Let’s Have Fun With Interpreters and Bytecode VMs — Chapter 2

We can achieve this by keeping a map of addresses to labels, and then looping through the entries after we’ve disassembled the entire program:const disassemble = (program: Uint8Array): string => { const output: Map<string, string> = new Map(); let labels: Map<string, Token> = new Map(); for (let i = 0; i < program.length; i += 2) { const address = i + 0x200; const opcode = new Opcode( getOpcode(program[i], program[i + 1]), false ); if (opcode.label) labels.set(opcode.bytes.nnn.toString(), opcode.label); output.set( address.toString(), `${opcode.toString()} ;${address.toString(16)}` ); } Array.from(labels) .forEach(([address, labelToken]: [string, Token]) => { const disassembly = output.get(address); output.set(address, `${labelToken}..${disassembly}`); }); return Array.from(output.values()).join('..');};Let’s examine some portions of the pong2 rom:Looks about rightAwesome?.Let’s take a moment to congratulate ourselves and look through the pong disassembly..We can see subroutines and branches in (relative) action..We’ve also got the addresses where the opcodes occur to further help us break down what’s going on inside the program.If we were to reassemble the above program as is, we would get a playable pong.???..What’s the catch.Yeah, we’re not finished..Because our disassembler works… but only sometimes and by accident..Let’s take a look at the first few lines of disassembly from another program we’ve seen before, missile.c8:JMP :label-0x0219 ;200SKNE $D, 49h ;202SKEQ $3, $5 ;204SKNE $9, 4Ch ;206SKNE $5, 20h ;208MOV $2, 79h ;20aJSR :label-0x0044 ;20cMOV $1, 76h ;20eMOV $9, 64h ;210JSR :label-0x0057 ;212Uh oh..This first jump takes us to a non-even address space..What follows isn’t very pretty either, we apparently have two more jumps taking us into sub 0x200 memory space and an unusual four skip branches in a row?.That can’t be right.. More details

Leave a Reply