1
0
mirror of https://github.com/pevans/erc-c.git synced 2024-11-16 09:07:05 +00:00

Add new file for opcode disassembly

This commit is contained in:
Peter Evans 2017-12-27 21:21:11 -06:00
parent 1942a75d9e
commit 32d08fbbc5

186
src/mos6502.dis.c Normal file
View File

@ -0,0 +1,186 @@
/*
* mos6502.dis.c
*
* Disassembly of the mos6502 machine code into an assembly notation.
*/
#include "mos6502.h"
#include "mos6502.enums.h"
static char *instruction_strings[] = {
"ADC",
"AND",
"ASL",
"BCC",
"BCS",
"BEQ",
"BIT",
"BMI",
"BNE",
"BPL",
"BRK",
"BVC",
"BVS",
"CLC",
"CLD",
"CLI",
"CLV",
"CMP",
"CPX",
"CPY",
"DEC",
"DEX",
"DEY",
"EOR",
"INC",
"INX",
"INY",
"JMP",
"JSR",
"LDA",
"LDX",
"LDY",
"LSR",
"NOP",
"ORA",
"PHA",
"PHP",
"PLA",
"PLP",
"ROL",
"ROR",
"RTI",
"RTS",
"SBC",
"SEC",
"SED",
"SEI",
"STA",
"STX",
"STY",
"TAX",
"TAY",
"TSX",
"TXA",
"TXS",
"TYA",
};
/*
* Given a stream, address mode and 16-bit value, print the value out in
* the form that is expected given the address mode. The value is not
* necessarily going to truly be 16-bit; most address modes use one
* 8-bit operand. But we can contain all possible values with the 16-bit
* type.
*/
void
mos6502_dis_operand(FILE *stream, int addr_mode, vm_16bit value)
{
switch (addr_mode) {
case ACC:
break;
case ABS:
fprintf(stream, "$%04X", value);
break;
case ABX:
fprintf(stream, "$%04X,X", value);
break;
case ABY:
fprintf(stream, "$%04X,Y", value);
break;
case IMM:
fprintf(stream, "#$%02X", value);
break;
case IMP:
break;
case IND:
fprintf(stream, "($%04X)", value);
break;
case IDX:
fprintf(stream, "($%02X,X)", value);
break;
case IDY:
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)");
break;
case ZPG:
fprintf(stream, "$%02X", value);
break;
case ZPX:
fprintf(stream, "$%02X,X", value);
break;
case ZPY:
fprintf(stream, "$%02X,Y", value);
break;
}
}
/*
* This function will write to the stream the instruction that the given
* opcode maps to.
*/
void
mos6502_dis_instruction(FILE *stream, vm_8bit opcode)
{
int inst_code;
inst_code = mos6502_instruction(opcode);
// Arguably this could or should be done as fputs(), which is
// presumably a simpler output method. But, since we use fprintf()
// in other places, I think we should continue to do so. Further, we
// use a simple format string (%s) to avoid the linter's complaints
// about potential security issues.
fprintf(stream, "%s", instruction_strings[inst_code]);
}
/*
* This function returns the number of bytes that the given opcode is
* expecting to work with. For instance, if the opcode is in absolute
* address mode, then we will need to read the next two bytes in the
* stream to compose a full 16-bit address to work with. If our opcode
* is in immediate mode, then we only need to read one byte. Many
* opcodes will read no bytes at all from the stream (in which we return
* zero).
*/
int
mos6502_dis_expected_bytes(vm_8bit opcode)
{
int addr_mode = mos6502_addr_mode(opcode);
switch (addr_mode) {
// These are 16-bit operands, because they work with absolute
// addresses in memory.
case ABS:
case ABY:
case ABX:
case IND:
return 2;
// These are the 8-bit operand address modes.
case IMM:
case IDX:
case IDY:
case REL:
case ZPG:
case ZPX:
case ZPY:
return 1;
// These two address modes have implied arguments; ACC is
// the accumulator, and IMP basically means it operates on
// some specific (presumably obvious) thing and no operand
// is necessary.
case ACC:
case IMP:
return 0;
}
// I don't know how we got here, outside of foul magicks and cruel
// trickery. Let's fearfully return zero!
return 0;
}