mirror of
https://github.com/pevans/erc-c.git
synced 2024-11-24 14:32:08 +00:00
187 lines
4.2 KiB
C
187 lines
4.2 KiB
C
|
/*
|
||
|
* 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;
|
||
|
}
|