aboutsummaryrefslogtreecommitdiffgithub
path: root/novice/simulator/debugger.test.ts
blob: d729cbca86c2d431b330e2b86c3194f1fb4af6fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { Debugger } from './debugger';
import { getIsa } from '../isa';
import { FakeIO } from './helpers.test';

// for programs with ONE section, generate store() calls with
// storify() { ./novice.js asm "$1"; xxd -p "${1%.asm}.obj" | awk '{ print substr($1,9) }' | sed 's/\(.\{4\}\)/\1\n/g' | head -n -1 | awk '{ printf "dbg.store(0x%04x, 0x%s);\n", 0x3000 + NR - 1, $1 }'; }; storify meme.asm

describe('debugger', () => {
    let io: FakeIO;
    let dbg: Debugger;

    beforeEach(() => {
        io = new FakeIO();
    });

    describe('lc-3 programs', () => {
        beforeEach(() => {
            dbg = new Debugger(getIsa('lc3'), io);
        });

        describe('prints 3 bangs', () => {
            beforeEach(() => {
                dbg.store(0x3000, 0x2007); // ld r0, bang
                dbg.store(0x3001, 0x54a0); // and r2, r2, 0
                dbg.store(0x3002, 0x14a3); // add r2, r2, 3
                dbg.store(0x3003, 0x0c03); // loop brnz done
                dbg.store(0x3004, 0xf021); // out
                dbg.store(0x3005, 0x14bf); // add r2, r2, -1
                dbg.store(0x3006, 0x0ffc); // br loop
                dbg.store(0x3007, 0xf025); // done halt
                dbg.store(0x3008, 0x0021); // bang .fill '!'
                dbg.store(0x3009, 0xd000); // .fill 0xd000
            });

            it('errors on negative breakpoint', () => {
                expect(() => {
                    dbg.addBreakpoint(-0xf);
                }).toThrow('negative');
            });

            it('errors on oversized breakpoint', () => {
                expect(() => {
                    dbg.addBreakpoint(0x10000);
                }).toThrow('large');
            });

            it('errors on dupe breakpoints', () => {
                dbg.addBreakpoint(0x3000);
                expect(() => {
                    dbg.addBreakpoint(0x3000);
                }).toThrow('0x3000');
            });

            it('obeys breakpoints', () => {
                dbg.addBreakpoint(0x3004);

                return dbg.cont().then(() => {
                    expect(dbg.getPc()).toEqual(0x3004);
                    expect(dbg.isHalted()).toBe(false);
                    expect(io.stdout).toEqual('');
                    return dbg.cont();
                }).then(() => {
                    expect(dbg.getPc()).toEqual(0x3004);
                    expect(dbg.isHalted()).toBe(false);
                    expect(io.stdout).toEqual('!');
                    return dbg.cont();
                }).then(() => {
                    expect(dbg.getPc()).toEqual(0x3004);
                    expect(dbg.isHalted()).toBe(false);
                    expect(io.stdout).toEqual('!!');
                    return dbg.cont();
                }).then(() => {
                    expect(dbg.getPc()).toEqual(0x3008);
                    expect(dbg.isHalted()).toBe(true);
                    expect(io.stdout).toEqual('!!!');
                });
            });

            it('disassembles an instruction with operands', () => {
                expect(dbg.disassembleAt(0x3005)).toEqual('add r2, r2, -1');
            });

            it('disassembles an instruction with no operands', () => {
                expect(dbg.disassembleAt(0x3007)).toEqual('halt');
            });

            it('returns null for non-disassemblable words', () => {
                expect(dbg.disassembleAt(0x3009)).toBe(null);
            });

            it('disassembles the whole dang thing', () => {
                expect(dbg.disassembleRegion(0x2ffe, 0x300b)).toEqual([
                    [0x2ffe, 0x0000,      0, "nop"],
                    [0x2fff, 0x0000,      0, "nop"],
                    [0x3000, 0x2007,   8199, "ld r0, 7"],
                    [0x3001, 0x54a0,  21664, "and r2, r2, 0"],
                    [0x3002, 0x14a3,   5283, "add r2, r2, 3"],
                    [0x3003, 0x0c03,   3075, "brnz 3"],
                    [0x3004, 0xf021,  -4063, "out"],
                    [0x3005, 0x14bf,   5311, "add r2, r2, -1"],
                    [0x3006, 0x0ffc,   4092, "brnzp -4"],
                    [0x3007, 0xf025,  -4059, "halt"],
                    [0x3008, 0x0021,     33, null],
                    [0x3009, 0xd000, -12288, null],
                    [0x300a, 0x0000,      0, "nop"],
                    [0x300b, 0x0000,      0, "nop"],
                ]);
            });

            it('disassembles reversed operands', () => {
                expect(dbg.disassembleRegion(0x3004, 0x3002)).toEqual([
                    [0x3002, 0x14a3,   5283, "add r2, r2, 3"],
                    [0x3003, 0x0c03,   3075, "brnz 3"],
                    [0x3004, 0xf021,  -4063, "out"],
                ]);
            });
        });
    });
});