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:
parent
1942a75d9e
commit
32d08fbbc5
186
src/mos6502.dis.c
Normal file
186
src/mos6502.dis.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user