Files
8bitworkshop/test/unit/teststackwalk.ts
T

59 lines
2.8 KiB
TypeScript

import assert from "assert";
import { describe, it } from "mocha";
import { disassemble6502 } from "../../src/common/cpu/disasm6502";
import { disassembleZ80 } from "../../src/common/cpu/disasmz80";
import { walkStack } from "../../src/common/stackwalk";
// Memory with code poked in, and a stack, for walking by hand.
function machine(arch: string) {
const mem = new Uint8Array(0x10000);
const disassemble = arch === '6502'
? (a: number) => disassemble6502(a, mem[a], mem[a + 1], mem[a + 2])
: (a: number) => disassembleZ80(a, mem[a], mem[a + 1], mem[a + 2], mem[a + 3]);
return { mem, target: (sp: number) => ({ arch, sp, read: (a: number) => mem[a], disassemble }) };
}
describe('Stack walk', function () {
it('finds 6502 JSRs, skipping pushed data', function () {
const { mem, target } = machine('6502');
// $8000 main: JSR $9000 (foo); $9000 foo: ... JSR $A000 (bar)
mem.set([0x20, 0x00, 0x90], 0x8000);
mem.set([0x20, 0x00, 0xa0], 0x9010);
// stack from $1FF down: main's return ($8002), a PHA'd byte, foo's return ($9012)
mem.set([0x12, 0x90, 0x55, 0x02, 0x80], 0x1fb);
const frames = walkStack(target(0xfa), 0xa005);
assert.deepStrictEqual(frames.map(f => f.pc), [0xa005, 0x9010, 0x8000]);
assert.ok(frames.slice(1).every(f => f.matched));
});
it('prefers a Z80 return address whose CALL goes to the routine it is in', function () {
const { mem, target } = machine('z80');
// $0100 main: CALL $0200 (foo); $0150: CALL $0300 (other); $0200 foo: CALL $0400
mem.set([0xcd, 0x00, 0x02], 0x100);
mem.set([0xcd, 0x00, 0x03], 0x150);
mem.set([0xcd, 0x00, 0x04], 0x210);
// stack at $F000: foo's return ($0213), a stale return from other ($0153),
// main's return ($0103)
mem.set([0x13, 0x02, 0x53, 0x01, 0x03, 0x01], 0xf000);
const frames = walkStack(target(0xf000), 0x0405);
assert.deepStrictEqual(frames.map(f => f.pc), [0x0405, 0x0210, 0x0100]);
});
it('takes an RST, and a call it cannot match when nothing better is near', function () {
const { mem, target } = machine('z80');
// $0100: RST 38H; $0038: JP (HL) -- the call goes on somewhere we can't see
mem[0x100] = 0xff;
mem.set([0x01, 0x01], 0xf000);
const frames = walkStack(target(0xf000), 0x5000);
assert.deepStrictEqual(frames.map(f => [f.pc, f.matched]), [[0x5000, undefined], [0x0100, false]]);
// near enough to $38 to be in the routine there
assert.strictEqual(walkStack(target(0xf000), 0x0050)[1].matched, true);
});
it('stops where there are no more calls', function () {
const { target } = machine('z80');
assert.deepStrictEqual(walkStack(target(0xf000), 0x1234).map(f => f.pc), [0x1234]);
assert.deepStrictEqual(walkStack({ ...target(0xf000), arch: 'arm32' }, 0x1234).map(f => f.pc), [0x1234]);
});
});