1
0
mirror of https://github.com/pevans/erc-c.git synced 2024-11-01 04:04:28 +00:00

Implement jump table, labels in code

This commit is contained in:
Peter Evans 2017-12-29 17:08:25 -06:00
parent 9000245002
commit 26529f77ab
5 changed files with 177 additions and 40 deletions

View File

@ -4,10 +4,14 @@
#include "vm_bits.h" #include "vm_bits.h"
#include "vm_segment.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 int mos6502_dis_opcode(FILE *, vm_segment *, int);
extern void mos6502_dis_instruction(FILE *, 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); extern void mos6502_dis_scan(FILE *, vm_segment *, int, int);
#endif #endif

View File

@ -1,6 +1,8 @@
#ifndef _MOS6502_H_ #ifndef _MOS6502_H_
#define _MOS6502_H_ #define _MOS6502_H_
#include <stdbool.h>
#include "vm_bits.h" #include "vm_bits.h"
#include "vm_segment.h" #include "vm_segment.h"
@ -111,6 +113,7 @@ typedef vm_8bit (*mos6502_address_resolver)(mos6502 *);
*/ */
typedef void (*mos6502_instruction_handler)(mos6502 *, vm_8bit); typedef void (*mos6502_instruction_handler)(mos6502 *, vm_8bit);
extern bool mos6502_would_jump(int);
extern int mos6502_cycles(mos6502 *, vm_8bit); extern int mos6502_cycles(mos6502 *, vm_8bit);
extern int mos6502_instruction(vm_8bit); extern int mos6502_instruction(vm_8bit);
extern mos6502 *mos6502_create(); extern mos6502 *mos6502_create();

View File

@ -6,6 +6,7 @@
* stack, etc. * stack, etc.
*/ */
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -401,3 +402,24 @@ mos6502_read_byte(mos6502 *cpu)
return byte; 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;
}

View File

@ -4,9 +4,14 @@
* Disassembly of the mos6502 machine code into an assembly notation. * Disassembly of the mos6502 machine code into an assembly notation.
*/ */
#include <stdbool.h>
#include "mos6502.h" #include "mos6502.h"
#include "mos6502.dis.h"
#include "mos6502.enums.h" #include "mos6502.enums.h"
static vm_8bit jump_table[MOS6502_MEMSIZE];
static char *instruction_strings[] = { static char *instruction_strings[] = {
"ADC", "ADC",
"AND", "AND",
@ -74,8 +79,10 @@ static char *instruction_strings[] = {
* type. * type.
*/ */
void 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) { switch (addr_mode) {
case ACC: case ACC:
break; break;
@ -94,7 +101,11 @@ mos6502_dis_operand(FILE *stream, int addr_mode, vm_16bit value)
case IMP: case IMP:
break; break;
case IND: case IND:
fprintf(stream, "($%04X)", value); if (jump_table[value]) {
mos6502_dis_label(stream, value);
} else {
fprintf(stream, "($%04X)", value);
}
break; break;
case IDX: case IDX:
fprintf(stream, "($%02X,X)", value); 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); fprintf(stream, "($%02X),Y", value);
break; break;
case REL: case REL:
// FIXME: we need some kind of table of jumps and branches rel_address = address + value;
// we make, so that we can come up with some labels to use. if (value > 127) {
fprintf(stream, "(REL)"); rel_address -= 256;
}
mos6502_dis_label(stream, rel_address);
break; break;
case ZPG: case ZPG:
fprintf(stream, "$%02X", value); fprintf(stream, "$%02X", value);
@ -191,6 +205,7 @@ mos6502_dis_opcode(FILE *stream, vm_segment *segment, int address)
vm_8bit opcode; vm_8bit opcode;
vm_16bit operand; vm_16bit operand;
int addr_mode; int addr_mode;
int inst_code;
int expected; int expected;
// The next byte is assumed to be the opcode we work with. // 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); addr_mode = mos6502_addr_mode(opcode);
expected = mos6502_dis_expected_bytes(addr_mode); 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 // The operand itself defaults to zero... in cases where this
// doesn't change, the instruction related to the opcode will // doesn't change, the instruction related to the opcode will
// probably not even use it. // probably not even use it.
@ -233,25 +251,50 @@ mos6502_dis_opcode(FILE *stream, vm_segment *segment, int address)
break; break;
} }
// Let's print out to the stream what we have so far. First, we // If the stream is NULL, we're doing some kind of lookahead.
// indent by four spaces. // Furthermore, if this is an instruction that would switch control
fprintf(stream, " "); // to a different spot in the program, then let's label this in the
// jump table.
// Print out the instruction code that our opcode represents. if (stream == NULL && mos6502_would_jump(inst_code)) {
mos6502_dis_instruction(stream, mos6502_instruction(opcode)); mos6502_dis_jump_label(operand, address, addr_mode);
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);
} }
// And let's terminate the line. // It's totally possible that we are not expected to print out the
fprintf(stream, "\n"); // 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 // 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 // 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); 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;
}

View File

@ -66,37 +66,51 @@ TestSuite(mos6502_dis, .init = setup, .fini = teardown);
Test(mos6502_dis, operand) Test(mos6502_dis, operand)
{ {
mos6502_dis_operand(stream, ABS, 0x1234); mos6502_dis_operand(stream, 0, ABS, 0x1234);
assert_buf("$1234"); assert_buf("$1234");
mos6502_dis_operand(stream, ABX, 0x1234); mos6502_dis_operand(stream, 0, ABX, 0x1234);
assert_buf("$1234,X"); assert_buf("$1234,X");
mos6502_dis_operand(stream, ABY, 0x1234); mos6502_dis_operand(stream, 0, ABY, 0x1234);
assert_buf("$1234,Y"); assert_buf("$1234,Y");
mos6502_dis_operand(stream, IMM, 0x12); mos6502_dis_operand(stream, 0, IMM, 0x12);
assert_buf("#$12"); 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)"); 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)"); assert_buf("($12,X)");
mos6502_dis_operand(stream, IDY, 0x34); mos6502_dis_operand(stream, 0, IDY, 0x34);
assert_buf("($34),Y"); assert_buf("($34),Y");
mos6502_dis_operand(stream, ZPG, 0x34); mos6502_dis_operand(stream, 0, ZPG, 0x34);
assert_buf("$34"); assert_buf("$34");
mos6502_dis_operand(stream, ZPX, 0x34); mos6502_dis_operand(stream, 0, ZPX, 0x34);
assert_buf("$34,X"); assert_buf("$34,X");
mos6502_dis_operand(stream, ZPY, 0x34); mos6502_dis_operand(stream, 0, ZPY, 0x34);
assert_buf("$34,Y"); assert_buf("$34,Y");
// These should both end up printing nothing // These should both end up printing nothing
mos6502_dis_operand(stream, ACC, 0); mos6502_dis_operand(stream, 0, ACC, 0);
assert_buf(""); assert_buf("");
mos6502_dis_operand(stream, IMP, 0); mos6502_dis_operand(stream, 0, IMP, 0);
assert_buf(""); assert_buf("");
// FIXME: this is intentionally "broken" until we can implement a // Test a forward jump (operand < 128)
// jump table mos6502_dis_operand(stream, 500, REL, 52);
mos6502_dis_operand(stream, REL, 0x34); assert_buf("ADDR_552");
assert_buf("(REL)");
// Test a backward jump (operand >= 128)
mos6502_dis_operand(stream, 500, REL, 152);
assert_buf("ADDR_396");
} }
Test(mos6502_dis, instruction) Test(mos6502_dis, instruction)
@ -218,3 +232,20 @@ Test(mos6502_dis, scan)
" JMP ($1234)\n" " 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");
}