From 26529f77ab852c7938e8dfea51a198c94009e61e Mon Sep 17 00:00:00 2001 From: Peter Evans Date: Fri, 29 Dec 2017 17:08:25 -0600 Subject: [PATCH] Implement jump table, labels in code --- include/mos6502.dis.h | 8 ++- include/mos6502.h | 3 ++ src/mos6502.c | 22 ++++++++ src/mos6502.dis.c | 121 ++++++++++++++++++++++++++++++++++-------- tests/mos6502.dis.c | 63 ++++++++++++++++------ 5 files changed, 177 insertions(+), 40 deletions(-) diff --git a/include/mos6502.dis.h b/include/mos6502.dis.h index 98d2561..31323f1 100644 --- a/include/mos6502.dis.h +++ b/include/mos6502.dis.h @@ -4,10 +4,14 @@ #include "vm_bits.h" #include "vm_segment.h" -extern int mos6502_dis_expected_bytes(vm_8bit); +extern bool mos6502_dis_is_jump_label(int); +extern int mos6502_dis_expected_bytes(int); extern int mos6502_dis_opcode(FILE *, vm_segment *, int); extern void mos6502_dis_instruction(FILE *, int); -extern void mos6502_dis_operand(FILE *, int, vm_16bit); +extern void mos6502_dis_jump_label(vm_16bit, int, int); +extern void mos6502_dis_jump_unlabel(int); +extern void mos6502_dis_label(FILE *, int); +extern void mos6502_dis_operand(FILE *, int, int, vm_16bit); extern void mos6502_dis_scan(FILE *, vm_segment *, int, int); #endif diff --git a/include/mos6502.h b/include/mos6502.h index addf2c3..4289a79 100644 --- a/include/mos6502.h +++ b/include/mos6502.h @@ -1,6 +1,8 @@ #ifndef _MOS6502_H_ #define _MOS6502_H_ +#include + #include "vm_bits.h" #include "vm_segment.h" @@ -111,6 +113,7 @@ typedef vm_8bit (*mos6502_address_resolver)(mos6502 *); */ typedef void (*mos6502_instruction_handler)(mos6502 *, vm_8bit); +extern bool mos6502_would_jump(int); extern int mos6502_cycles(mos6502 *, vm_8bit); extern int mos6502_instruction(vm_8bit); extern mos6502 *mos6502_create(); diff --git a/src/mos6502.c b/src/mos6502.c index eb4ff94..3d286d4 100644 --- a/src/mos6502.c +++ b/src/mos6502.c @@ -6,6 +6,7 @@ * stack, etc. */ +#include #include #include @@ -401,3 +402,24 @@ mos6502_read_byte(mos6502 *cpu) return byte; } + +/* + * Return true if the given instruction would require that we jump + * to somewhere else in the program. + */ +inline bool +mos6502_would_jump(int inst_code) +{ + return + inst_code == BCC || + inst_code == BCS || + inst_code == BEQ || + inst_code == BMI || + inst_code == BNE || + inst_code == BPL || + inst_code == BRK || + inst_code == BVC || + inst_code == BVS || + inst_code == JMP || + inst_code == JSR; +} diff --git a/src/mos6502.dis.c b/src/mos6502.dis.c index 3c956b2..9e35228 100644 --- a/src/mos6502.dis.c +++ b/src/mos6502.dis.c @@ -4,9 +4,14 @@ * Disassembly of the mos6502 machine code into an assembly notation. */ +#include + #include "mos6502.h" +#include "mos6502.dis.h" #include "mos6502.enums.h" +static vm_8bit jump_table[MOS6502_MEMSIZE]; + static char *instruction_strings[] = { "ADC", "AND", @@ -74,8 +79,10 @@ static char *instruction_strings[] = { * type. */ void -mos6502_dis_operand(FILE *stream, int addr_mode, vm_16bit value) +mos6502_dis_operand(FILE *stream, int address, int addr_mode, vm_16bit value) { + int rel_address; + switch (addr_mode) { case ACC: break; @@ -94,7 +101,11 @@ mos6502_dis_operand(FILE *stream, int addr_mode, vm_16bit value) case IMP: break; case IND: - fprintf(stream, "($%04X)", value); + if (jump_table[value]) { + mos6502_dis_label(stream, value); + } else { + fprintf(stream, "($%04X)", value); + } break; case IDX: fprintf(stream, "($%02X,X)", value); @@ -103,9 +114,12 @@ mos6502_dis_operand(FILE *stream, int addr_mode, vm_16bit value) fprintf(stream, "($%02X),Y", value); break; case REL: - // FIXME: we need some kind of table of jumps and branches - // we make, so that we can come up with some labels to use. - fprintf(stream, "(REL)"); + rel_address = address + value; + if (value > 127) { + rel_address -= 256; + } + + mos6502_dis_label(stream, rel_address); break; case ZPG: fprintf(stream, "$%02X", value); @@ -191,6 +205,7 @@ mos6502_dis_opcode(FILE *stream, vm_segment *segment, int address) vm_8bit opcode; vm_16bit operand; int addr_mode; + int inst_code; int expected; // The next byte is assumed to be the opcode we work with. @@ -201,6 +216,9 @@ mos6502_dis_opcode(FILE *stream, vm_segment *segment, int address) addr_mode = mos6502_addr_mode(opcode); expected = mos6502_dis_expected_bytes(addr_mode); + // The specific instruction we mean to execute + inst_code = mos6502_instruction(opcode); + // The operand itself defaults to zero... in cases where this // doesn't change, the instruction related to the opcode will // probably not even use it. @@ -233,25 +251,50 @@ mos6502_dis_opcode(FILE *stream, vm_segment *segment, int address) break; } - // Let's print out to the stream what we have so far. First, we - // indent by four spaces. - fprintf(stream, " "); - - // Print out the instruction code that our opcode represents. - mos6502_dis_instruction(stream, mos6502_instruction(opcode)); - - if (expected) { - // Let's "tab" over; each instruction code is 3 characters, so let's - // move over 5 spaces (4 spaces indent + 1, just to keep everything - // aligned by 4-character boundaries). - fprintf(stream, " "); - - // Print out the operand given the proper address mode. - mos6502_dis_operand(stream, mos6502_addr_mode(opcode), operand); + // If the stream is NULL, we're doing some kind of lookahead. + // Furthermore, if this is an instruction that would switch control + // to a different spot in the program, then let's label this in the + // jump table. + if (stream == NULL && mos6502_would_jump(inst_code)) { + mos6502_dis_jump_label(operand, address, addr_mode); } - // And let's terminate the line. - fprintf(stream, "\n"); + // It's totally possible that we are not expected to print out the + // contents of our inspection of the opcode. (For example, we may + // just want to set the jump table in a lookahead operation.) + if (stream) { + // Hey! We might have a label at this position in the code. If + // so, let's print out the label. + if (jump_table[address]) { + // This will print out just the label itself. + mos6502_dis_label(stream, address); + + // But to actually define the label, we need a colon to + // complete the notation. (We don't _need_ a newline, but it + // looks nicer to my arbitrary sensibilities. Don't @ me!) + fprintf(stream, ":\n"); + } + + // Let's print out to the stream what we have so far. First, we + // indent by four spaces. + fprintf(stream, " "); + + // Print out the instruction code that our opcode represents. + mos6502_dis_instruction(stream, inst_code); + + if (expected) { + // Let's "tab" over; each instruction code is 3 characters, so let's + // move over 5 spaces (4 spaces indent + 1, just to keep everything + // aligned by 4-character boundaries). + fprintf(stream, " "); + + // Print out the operand given the proper address mode. + mos6502_dis_operand(stream, address, addr_mode, operand); + } + + // And let's terminate the line. + fprintf(stream, "\n"); + } // The expected number of bytes here is for the operand, but we need // to add one for the opcode to return the true number that this @@ -266,3 +309,37 @@ mos6502_dis_scan(FILE *stream, vm_segment *segment, int pos, int end) pos += mos6502_dis_opcode(stream, segment, pos); } } + +void +mos6502_dis_jump_label(vm_16bit operand, int address, int addr_mode) +{ + int jump_loc = operand; + + if (addr_mode == REL) { + jump_loc = address + operand; + + if (operand > 127) { + jump_loc -= 256; + } + } + + jump_table[jump_loc] = 1; +} + +inline void +mos6502_dis_label(FILE *stream, int address) +{ + fprintf(stream, "ADDR_%d", address); +} + +inline void +mos6502_dis_jump_unlabel(int address) +{ + jump_table[address] = 0; +} + +inline bool +mos6502_dis_is_jump_label(int address) +{ + return jump_table[address] == 1; +} diff --git a/tests/mos6502.dis.c b/tests/mos6502.dis.c index 58cfeb3..740abf7 100644 --- a/tests/mos6502.dis.c +++ b/tests/mos6502.dis.c @@ -66,37 +66,51 @@ TestSuite(mos6502_dis, .init = setup, .fini = teardown); Test(mos6502_dis, operand) { - mos6502_dis_operand(stream, ABS, 0x1234); + mos6502_dis_operand(stream, 0, ABS, 0x1234); assert_buf("$1234"); - mos6502_dis_operand(stream, ABX, 0x1234); + mos6502_dis_operand(stream, 0, ABX, 0x1234); assert_buf("$1234,X"); - mos6502_dis_operand(stream, ABY, 0x1234); + mos6502_dis_operand(stream, 0, ABY, 0x1234); assert_buf("$1234,Y"); - mos6502_dis_operand(stream, IMM, 0x12); + mos6502_dis_operand(stream, 0, IMM, 0x12); assert_buf("#$12"); - mos6502_dis_operand(stream, IND, 0x1234); + + // For JMPs and JSRs (and BRKs), this should be a label and not a + // literal value. So we need to test both the literal and + // jump-labeled version. + mos6502_dis_operand(stream, 0, IND, 0x1234); assert_buf("($1234)"); - mos6502_dis_operand(stream, IDX, 0x12); + mos6502_dis_jump_label(0x1234, 0, IND); + mos6502_dis_operand(stream, 0, IND, 0x1234); + assert_buf("ADDR_4660"); // = 0x1234 + + // Let's undo our label above... + mos6502_dis_jump_unlabel(0x1234); + + mos6502_dis_operand(stream, 0, IDX, 0x12); assert_buf("($12,X)"); - mos6502_dis_operand(stream, IDY, 0x34); + mos6502_dis_operand(stream, 0, IDY, 0x34); assert_buf("($34),Y"); - mos6502_dis_operand(stream, ZPG, 0x34); + mos6502_dis_operand(stream, 0, ZPG, 0x34); assert_buf("$34"); - mos6502_dis_operand(stream, ZPX, 0x34); + mos6502_dis_operand(stream, 0, ZPX, 0x34); assert_buf("$34,X"); - mos6502_dis_operand(stream, ZPY, 0x34); + mos6502_dis_operand(stream, 0, ZPY, 0x34); assert_buf("$34,Y"); // These should both end up printing nothing - mos6502_dis_operand(stream, ACC, 0); + mos6502_dis_operand(stream, 0, ACC, 0); assert_buf(""); - mos6502_dis_operand(stream, IMP, 0); + mos6502_dis_operand(stream, 0, IMP, 0); assert_buf(""); - // FIXME: this is intentionally "broken" until we can implement a - // jump table - mos6502_dis_operand(stream, REL, 0x34); - assert_buf("(REL)"); + // Test a forward jump (operand < 128) + mos6502_dis_operand(stream, 500, REL, 52); + assert_buf("ADDR_552"); + + // Test a backward jump (operand >= 128) + mos6502_dis_operand(stream, 500, REL, 152); + assert_buf("ADDR_396"); } Test(mos6502_dis, instruction) @@ -218,3 +232,20 @@ Test(mos6502_dis, scan) " JMP ($1234)\n" ); } + +Test(mos6502_dis, jump_label) +{ + cr_assert_eq(mos6502_dis_is_jump_label(123), false); + + mos6502_dis_jump_label(123, 0, IND); + cr_assert_eq(mos6502_dis_is_jump_label(123), true); + + mos6502_dis_jump_unlabel(123); + cr_assert_eq(mos6502_dis_is_jump_label(123), false); +} + +Test(mos6502_dis, label) +{ + mos6502_dis_label(stream, 123); + assert_buf("ADDR_123"); +}