mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2026-10-24 08:02:27 +00:00
59 lines
2.8 KiB
TypeScript
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]);
|
|
});
|
|
});
|