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:
parent
9000245002
commit
26529f77ab
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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");
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user