Bla
This commit is contained in:
parent
4a44443dc7
commit
f4279467e8
|
@ -25,11 +25,16 @@
|
|||
</configurations>
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="0c3b231e-0637-4ac1-8964-c60fc9e9e691" name="Changes" comment="Memory debugger">
|
||||
<list default="true" id="0c3b231e-0637-4ac1-8964-c60fc9e9e691" name="Changes" comment="Finished read-only memory debugger">
|
||||
<change afterPath="$PROJECT_DIR$/cpu/decoding.c" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/cpu/decoding.h" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/cpu/CMakeLists.txt" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/CMakeLists.txt" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/cpu/cpu.c" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/cpu.c" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/cpu/cpu.h" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/cpu.h" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/cpu/op.c" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/op.c" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/cpu/op.h" beforeDir="false" afterPath="$PROJECT_DIR$/cpu/op.h" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/debugger/debugger.c" beforeDir="false" afterPath="$PROJECT_DIR$/debugger/debugger.c" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/debugger/dialog.c" beforeDir="false" afterPath="$PROJECT_DIR$/debugger/dialog.c" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/debugger/dialog.h" beforeDir="false" afterPath="$PROJECT_DIR$/debugger/dialog.h" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/debugger/memory_view.c" beforeDir="false" afterPath="$PROJECT_DIR$/debugger/memory_view.c" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/debugger/memory_view.h" beforeDir="false" afterPath="$PROJECT_DIR$/debugger/memory_view.h" afterDir="false" />
|
||||
</list>
|
||||
|
@ -456,7 +461,7 @@
|
|||
<workItem from="1704501418104" duration="8204000" />
|
||||
<workItem from="1704569084127" duration="8903000" />
|
||||
<workItem from="1704582152049" duration="7863000" />
|
||||
<workItem from="1704660072645" duration="15399000" />
|
||||
<workItem from="1704660072645" duration="19497000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="Cpu opcodes implementation">
|
||||
<option name="closed" value="true" />
|
||||
|
@ -498,7 +503,15 @@
|
|||
<option name="project" value="LOCAL" />
|
||||
<updated>1704662439962</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="6" />
|
||||
<task id="LOCAL-00006" summary="Finished read-only memory debugger">
|
||||
<option name="closed" value="true" />
|
||||
<created>1704829582042</created>
|
||||
<option name="number" value="00006" />
|
||||
<option name="presentableId" value="LOCAL-00006" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1704829582042</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="7" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
|
@ -517,7 +530,8 @@
|
|||
<MESSAGE value="Added logging for operand decoding" />
|
||||
<MESSAGE value="Things" />
|
||||
<MESSAGE value="Memory debugger" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="Memory debugger" />
|
||||
<MESSAGE value="Finished read-only memory debugger" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="Finished read-only memory debugger" />
|
||||
</component>
|
||||
<component name="XDebuggerManager">
|
||||
<breakpoint-manager>
|
||||
|
|
|
@ -2,7 +2,9 @@ add_library(CPU
|
|||
cpu.c
|
||||
op.c
|
||||
memory.c
|
||||
cpu.h)
|
||||
cpu.h
|
||||
decoding.c
|
||||
decoding.h)
|
||||
|
||||
find_package(log.c)
|
||||
target_link_libraries(CPU log.c::log.c)
|
12
cpu/cpu.c
12
cpu/cpu.c
|
@ -5,6 +5,7 @@
|
|||
#include "cpu.h"
|
||||
#include "memory.h"
|
||||
#include "op.h"
|
||||
#include "decoding.h"
|
||||
|
||||
/*
|
||||
* =====================================================================================
|
||||
|
@ -139,14 +140,3 @@ void cpu_stack_pop_context(System *system) {
|
|||
pc += lo;
|
||||
system->cpu.program_counter = pc;
|
||||
}
|
||||
|
||||
char *operand_name(Operand *operand) {
|
||||
switch (operand->type) {
|
||||
case OPERAND_TYPE_ACCUMULATOR:
|
||||
return "Accumulator";
|
||||
case OPERAND_TYPE_IMMEDIATE:
|
||||
return "Immediate";
|
||||
case OPERAND_TYPE_ADDRESS:
|
||||
return "Address";
|
||||
}
|
||||
}
|
14
cpu/cpu.h
14
cpu/cpu.h
|
@ -19,25 +19,13 @@
|
|||
|
||||
#define CPU_STACK_ADDR 0x0100
|
||||
|
||||
enum OperandType {
|
||||
OPERAND_TYPE_ACCUMULATOR,
|
||||
OPERAND_TYPE_IMMEDIATE,
|
||||
OPERAND_TYPE_ADDRESS
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
word value;
|
||||
enum OperandType type;
|
||||
bool is_page_crossing;
|
||||
} Operand;
|
||||
|
||||
/**
|
||||
* Gets the name of the type of an operand, for logging.
|
||||
*
|
||||
* @param operand The operand
|
||||
* @return The name of the operand's type.
|
||||
*/
|
||||
char *operand_name(Operand *operand);
|
||||
//char *operand_name(Operand *operand);
|
||||
|
||||
/**
|
||||
* Gets a flag from the CPU registers.
|
||||
|
|
|
@ -0,0 +1,482 @@
|
|||
//
|
||||
// Created by william on 1/9/24.
|
||||
//
|
||||
|
||||
#include "decoding.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <log.h>
|
||||
|
||||
address decode_operand_addr(System *system, AddressingMode addr_mode, bool *page_crossing) {
|
||||
CPU registers = system->cpu;
|
||||
address operand_addr;
|
||||
|
||||
if (addr_mode == ADDR_MODE_ZERO_PAGE) {
|
||||
operand_addr = cpu_get_next_byte(system);
|
||||
} else if (addr_mode == ADDR_MODE_ZERO_PAGE_INDEXED_X) {
|
||||
operand_addr = (cpu_get_next_byte(system) + registers.x) & 0xff;
|
||||
} else if (addr_mode == ADDR_MODE_ZERO_PAGE_INDEXED_Y) {
|
||||
operand_addr = (cpu_get_next_byte(system) + registers.y) & 0xff;
|
||||
} else if (addr_mode == ADDR_MODE_ABSOLUTE) {
|
||||
operand_addr = cpu_get_next_word(system);
|
||||
} else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_X) {
|
||||
word addr = cpu_get_next_word(system);
|
||||
word new_addr = addr + registers.x;
|
||||
|
||||
*page_crossing = (addr & 0xff00) != (new_addr & 0xff00);
|
||||
|
||||
operand_addr = new_addr;
|
||||
} else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_Y) {
|
||||
word addr = cpu_get_next_word(system);
|
||||
word new_addr = addr + registers.y;
|
||||
|
||||
*page_crossing = (addr & 0xff00) != (new_addr & 0xff00);
|
||||
|
||||
operand_addr = new_addr;
|
||||
} else if (addr_mode == ADDR_MODE_INDIRECT_JUMP) {
|
||||
word addr = cpu_get_next_word(system);
|
||||
if ((addr & 0xff) == 0xff) {
|
||||
// Error in NES CPU for JMP op
|
||||
word result = mem_get_byte(system, addr);
|
||||
result += mem_get_byte(system, addr & 0xff00) << 8;
|
||||
operand_addr = result;
|
||||
} else {
|
||||
operand_addr = mem_get_word(system, addr);
|
||||
}
|
||||
} else if (addr_mode == ADDR_MODE_INDIRECT_X) {
|
||||
byte arg_addr = cpu_get_next_byte(system);
|
||||
|
||||
word addr = mem_get_byte(system, (arg_addr + system->cpu.x) & 0xff);
|
||||
addr += mem_get_byte(system, (arg_addr + system->cpu.x + 1) & 0xff) << 8;
|
||||
operand_addr = addr;
|
||||
} else if (addr_mode == ADDR_MODE_INDIRECT_Y) {
|
||||
byte arg_addr = cpu_get_next_byte(system);
|
||||
word addr = mem_get_byte(system, arg_addr) + (mem_get_byte(system, (arg_addr + 1) & 0xff) << 8);
|
||||
word new_addr = addr + registers.y;
|
||||
|
||||
*page_crossing = (addr & 0xff00) != (new_addr & 0xff00);
|
||||
|
||||
operand_addr = new_addr;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
log_trace("Operand address: %#02x, Addressing mode: %s", operand_addr, get_addr_mode_name(addr_mode));
|
||||
return operand_addr;
|
||||
}
|
||||
|
||||
Operand decode_operand(System *system, AddressingMode addr_mode) {
|
||||
Operand operand;
|
||||
|
||||
if (addr_mode == ADDR_MODE_ACCUMULATOR) {
|
||||
operand.type = OPERAND_TYPE_ACCUMULATOR;
|
||||
operand.value = 0;
|
||||
operand.is_page_crossing = false;
|
||||
} else if (addr_mode == ADDR_MODE_IMMEDIATE) {
|
||||
operand.type = OPERAND_TYPE_IMMEDIATE;
|
||||
operand.value = cpu_get_next_byte(system);
|
||||
operand.is_page_crossing = false;
|
||||
} else {
|
||||
operand.type = OPERAND_TYPE_ADDRESS;
|
||||
operand.value = decode_operand_addr(system, addr_mode, &operand.is_page_crossing);
|
||||
}
|
||||
|
||||
log_trace("Operand type: %s, value: %#02x", operand_name(&operand), operand.value);
|
||||
return operand;
|
||||
}
|
||||
|
||||
byte read_operand(System *system, Operand operand) {
|
||||
switch (operand.type) {
|
||||
case OPERAND_TYPE_ACCUMULATOR:
|
||||
return system->cpu.accumulator;
|
||||
case OPERAND_TYPE_IMMEDIATE:
|
||||
return (byte) operand.value;
|
||||
case OPERAND_TYPE_ADDRESS:
|
||||
return mem_get_byte(system, operand.value);
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
char *get_addr_mode_name(AddressingMode addr_mode) {
|
||||
switch (addr_mode) {
|
||||
case ADDR_MODE_ABSOLUTE:
|
||||
return "a";
|
||||
case ADDR_MODE_ABSOLUTE_INDEXED_X:
|
||||
return "a,x";
|
||||
case ADDR_MODE_ABSOLUTE_INDEXED_Y:
|
||||
return "a,y";
|
||||
case ADDR_MODE_ACCUMULATOR:
|
||||
return "A";
|
||||
case ADDR_MODE_IMMEDIATE:
|
||||
return "#";
|
||||
case ADDR_MODE_IMPLICIT:
|
||||
return " ";
|
||||
case ADDR_MODE_INDIRECT_X:
|
||||
return "(d,x)";
|
||||
case ADDR_MODE_INDIRECT_Y:
|
||||
return "(d,y)";
|
||||
case ADDR_MODE_INDIRECT_JUMP:
|
||||
return "(a)";
|
||||
case ADDR_MODE_RELATIVE:
|
||||
return "label";
|
||||
case ADDR_MODE_ZERO_PAGE:
|
||||
return "d";
|
||||
case ADDR_MODE_ZERO_PAGE_INDEXED_X:
|
||||
return "d,x";
|
||||
case ADDR_MODE_ZERO_PAGE_INDEXED_Y:
|
||||
return "d,y";
|
||||
}
|
||||
}
|
||||
|
||||
char *operand_name(Operand *operand) {
|
||||
switch (operand->type) {
|
||||
case OPERAND_TYPE_ACCUMULATOR:
|
||||
return "Accumulator";
|
||||
case OPERAND_TYPE_IMMEDIATE:
|
||||
return "Immediate";
|
||||
case OPERAND_TYPE_ADDRESS:
|
||||
return "Address";
|
||||
}
|
||||
}
|
||||
|
||||
char *get_op_code_name(byte op) {
|
||||
switch (op) {
|
||||
case 0x00:
|
||||
return "BRK";
|
||||
case 0x08:
|
||||
return "PHP";
|
||||
case 0x0b:
|
||||
case 0x2b:
|
||||
return "ANC";
|
||||
case 0x10:
|
||||
return "BPL";
|
||||
case 0x18:
|
||||
return "CLC";
|
||||
case 0x20:
|
||||
return "JSR";
|
||||
case 0x24:
|
||||
case 0x2c:
|
||||
return "BIT";
|
||||
case 0x28:
|
||||
return "PLP";
|
||||
case 0x30:
|
||||
return "BMI";
|
||||
case 0x38:
|
||||
return "SEC";
|
||||
case 0x40:
|
||||
return "RTI";
|
||||
case 0x48:
|
||||
return "PHA";
|
||||
case 0x4c:
|
||||
case 0x6c:
|
||||
return "JMP";
|
||||
case 0x50:
|
||||
return "BVC";
|
||||
case 0x58:
|
||||
return "CLI";
|
||||
case 0x60:
|
||||
return "RTS";
|
||||
case 0x68:
|
||||
return "PLA";
|
||||
case 0x70:
|
||||
return "BVS";
|
||||
case 0x78:
|
||||
return "SEI";
|
||||
case 0x84:
|
||||
case 0x8c:
|
||||
case 0x94:
|
||||
return "STY";
|
||||
case 0x88:
|
||||
return "DEY";
|
||||
case 0x90:
|
||||
return "BCC";
|
||||
case 0x98:
|
||||
return "TYA";
|
||||
case 0x9c:
|
||||
return "SHY";
|
||||
case 0xa0:
|
||||
case 0xa4:
|
||||
case 0xac:
|
||||
case 0xb4:
|
||||
case 0xbc:
|
||||
return "LDY";
|
||||
case 0xa8:
|
||||
return "TAY";
|
||||
case 0xb0:
|
||||
return "BCS";
|
||||
case 0xb8:
|
||||
return "CLV";
|
||||
case 0xc0:
|
||||
case 0xc4:
|
||||
case 0xcc:
|
||||
return "CPY";
|
||||
case 0xc8:
|
||||
return "INY";
|
||||
case 0xd0:
|
||||
return "BNE";
|
||||
case 0xd8:
|
||||
return "CLD";
|
||||
case 0xe0:
|
||||
case 0xe4:
|
||||
case 0xec:
|
||||
return "CPX";
|
||||
case 0xe8:
|
||||
return "INX";
|
||||
case 0xf0:
|
||||
return "BEQ";
|
||||
case 0xf8:
|
||||
return "SED";
|
||||
|
||||
case 0x01:
|
||||
case 0x05:
|
||||
case 0x09:
|
||||
case 0x0d:
|
||||
case 0x11:
|
||||
case 0x15:
|
||||
case 0x19:
|
||||
case 0x1d:
|
||||
return "ORA";
|
||||
case 0x21:
|
||||
case 0x25:
|
||||
case 0x29:
|
||||
case 0x2d:
|
||||
case 0x31:
|
||||
case 0x35:
|
||||
case 0x39:
|
||||
case 0x3d:
|
||||
return "AND";
|
||||
case 0x41:
|
||||
case 0x45:
|
||||
case 0x49:
|
||||
case 0x4d:
|
||||
case 0x51:
|
||||
case 0x55:
|
||||
case 0x59:
|
||||
case 0x5d:
|
||||
return "EOR";
|
||||
case 0x61:
|
||||
case 0x65:
|
||||
case 0x69:
|
||||
case 0x6d:
|
||||
case 0x71:
|
||||
case 0x75:
|
||||
case 0x79:
|
||||
case 0x7d:
|
||||
return "ADC";
|
||||
case 0x81:
|
||||
case 0x85:
|
||||
case 0x8d:
|
||||
case 0x91:
|
||||
case 0x95:
|
||||
case 0x99:
|
||||
case 0x9d:
|
||||
return "STA";
|
||||
case 0xa1:
|
||||
case 0xa5:
|
||||
case 0xa9:
|
||||
case 0xad:
|
||||
case 0xb1:
|
||||
case 0xb5:
|
||||
case 0xb9:
|
||||
case 0xbd:
|
||||
return "LDA";
|
||||
case 0xc1:
|
||||
case 0xc5:
|
||||
case 0xc9:
|
||||
case 0xcd:
|
||||
case 0xd1:
|
||||
case 0xd5:
|
||||
case 0xd9:
|
||||
case 0xdd:
|
||||
return "CMP";
|
||||
case 0xe1:
|
||||
case 0xe5:
|
||||
case 0xe9:
|
||||
case 0xed:
|
||||
case 0xf1:
|
||||
case 0xf5:
|
||||
case 0xf9:
|
||||
case 0xfd:
|
||||
return "SBC";
|
||||
|
||||
case 0x03:
|
||||
case 0x07:
|
||||
case 0x0f:
|
||||
case 0x13:
|
||||
case 0x17:
|
||||
case 0x1b:
|
||||
case 0x1f:
|
||||
return "SLO";
|
||||
case 0x23:
|
||||
case 0x27:
|
||||
case 0x2f:
|
||||
case 0x33:
|
||||
case 0x37:
|
||||
case 0x3b:
|
||||
case 0x3f:
|
||||
return "RLA";
|
||||
case 0x43:
|
||||
case 0x47:
|
||||
case 0x4f:
|
||||
case 0x53:
|
||||
case 0x57:
|
||||
case 0x5b:
|
||||
case 0x5f:
|
||||
return "SRE";
|
||||
case 0x4b:
|
||||
return "ALR";
|
||||
case 0x63:
|
||||
case 0x67:
|
||||
case 0x6f:
|
||||
case 0x73:
|
||||
case 0x77:
|
||||
case 0x7b:
|
||||
case 0x7f:
|
||||
return "RRA";
|
||||
case 0x6b:
|
||||
return "ARR";
|
||||
case 0x83:
|
||||
case 0x87:
|
||||
case 0x8f:
|
||||
case 0x97:
|
||||
return "SAX";
|
||||
case 0x8b:
|
||||
return "XAA";
|
||||
case 0x93:
|
||||
case 0x9f:
|
||||
return "AHX";
|
||||
case 0x9b:
|
||||
return "TAS";
|
||||
case 0xa3:
|
||||
case 0xa7:
|
||||
case 0xab:
|
||||
case 0xaf:
|
||||
case 0xb3:
|
||||
case 0xb7:
|
||||
case 0xbb:
|
||||
case 0xbf:
|
||||
return "LAX";
|
||||
case 0xc3:
|
||||
case 0xc7:
|
||||
case 0xcf:
|
||||
case 0xd3:
|
||||
case 0xd7:
|
||||
case 0xdb:
|
||||
case 0xdf:
|
||||
return "DCP";
|
||||
case 0xcb:
|
||||
return "AXS";
|
||||
case 0xe3:
|
||||
case 0xe7:
|
||||
case 0xef:
|
||||
case 0xf3:
|
||||
case 0xf7:
|
||||
case 0xfb:
|
||||
case 0xff:
|
||||
return "ISC";
|
||||
case 0xeb:
|
||||
return "SBC";
|
||||
|
||||
case 0x06:
|
||||
case 0x0a:
|
||||
case 0x0e:
|
||||
case 0x16:
|
||||
case 0x1e:
|
||||
return "ASL";
|
||||
case 0x26:
|
||||
case 0x2a:
|
||||
case 0x2e:
|
||||
case 0x36:
|
||||
case 0x3e:
|
||||
return "ROL";
|
||||
case 0x46:
|
||||
case 0x4a:
|
||||
case 0x4e:
|
||||
case 0x56:
|
||||
case 0x5e:
|
||||
return "LSR";
|
||||
case 0x66:
|
||||
case 0x6a:
|
||||
case 0x6e:
|
||||
case 0x76:
|
||||
case 0x7e:
|
||||
return "ROR";
|
||||
case 0x86:
|
||||
case 0x8e:
|
||||
case 0x96:
|
||||
return "STX";
|
||||
case 0x8a:
|
||||
return "TXA";
|
||||
case 0x9a:
|
||||
case 0xba:
|
||||
return "TSX";
|
||||
case 0x9e:
|
||||
return "SHX";
|
||||
case 0xa2:
|
||||
case 0xa6:
|
||||
case 0xae:
|
||||
case 0xb6:
|
||||
case 0xbe:
|
||||
return "LDX";
|
||||
case 0xaa:
|
||||
return "TAX";
|
||||
case 0xc6:
|
||||
case 0xca:
|
||||
case 0xce:
|
||||
case 0xd6:
|
||||
case 0xde:
|
||||
return "DEC";
|
||||
case 0xe6:
|
||||
case 0xee:
|
||||
case 0xf6:
|
||||
case 0xfe:
|
||||
return "INC";
|
||||
|
||||
case 0x02:
|
||||
case 0x12:
|
||||
case 0x22:
|
||||
case 0x32:
|
||||
case 0x42:
|
||||
case 0x52:
|
||||
case 0x62:
|
||||
case 0x72:
|
||||
case 0x92:
|
||||
case 0xb2:
|
||||
case 0xd2:
|
||||
case 0xf2:
|
||||
return "STP";
|
||||
case 0x04:
|
||||
case 0x0c:
|
||||
case 0x14:
|
||||
case 0x1c:
|
||||
case 0x1a:
|
||||
case 0x34:
|
||||
case 0x3a:
|
||||
case 0x3c:
|
||||
case 0x44:
|
||||
case 0x54:
|
||||
case 0x5a:
|
||||
case 0x5c:
|
||||
case 0x64:
|
||||
case 0x74:
|
||||
case 0x7a:
|
||||
case 0x7c:
|
||||
case 0x80:
|
||||
case 0x82:
|
||||
case 0x89:
|
||||
case 0xc2:
|
||||
case 0xd4:
|
||||
case 0xda:
|
||||
case 0xdc:
|
||||
case 0xe2:
|
||||
case 0xea:
|
||||
case 0xf4:
|
||||
case 0xfa:
|
||||
case 0xfc:
|
||||
return "NOP";
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// Created by william on 1/9/24.
|
||||
//
|
||||
|
||||
#ifndef NESEMULATOR_DECODING_H
|
||||
#define NESEMULATOR_DECODING_H
|
||||
|
||||
#include "../include/types.h"
|
||||
#include "../include/system.h"
|
||||
#include "cpu.h"
|
||||
|
||||
typedef enum {
|
||||
ADDR_MODE_ABSOLUTE, // a
|
||||
ADDR_MODE_ABSOLUTE_INDEXED_X, // a,x
|
||||
ADDR_MODE_ABSOLUTE_INDEXED_Y, // a,y
|
||||
ADDR_MODE_ACCUMULATOR, // A
|
||||
ADDR_MODE_IMMEDIATE, // #i
|
||||
ADDR_MODE_IMPLICIT, // Imp
|
||||
ADDR_MODE_INDIRECT_X, // (d,x)
|
||||
ADDR_MODE_INDIRECT_JUMP, //
|
||||
ADDR_MODE_INDIRECT_Y, // (d),y
|
||||
ADDR_MODE_RELATIVE, // label
|
||||
ADDR_MODE_ZERO_PAGE, // d
|
||||
ADDR_MODE_ZERO_PAGE_INDEXED_X, // d,x
|
||||
ADDR_MODE_ZERO_PAGE_INDEXED_Y, // d,y
|
||||
} AddressingMode;
|
||||
|
||||
enum OperandType {
|
||||
OPERAND_TYPE_ACCUMULATOR,
|
||||
OPERAND_TYPE_IMMEDIATE,
|
||||
OPERAND_TYPE_ADDRESS
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
word value;
|
||||
enum OperandType type;
|
||||
bool is_page_crossing;
|
||||
} Operand;
|
||||
|
||||
address decode_operand_addr(System *system, AddressingMode addr_mode, bool *page_crossing);
|
||||
|
||||
Operand decode_operand(System *system, AddressingMode addr_mode);
|
||||
|
||||
byte read_operand(System *system, Operand operand);
|
||||
|
||||
char *get_addr_mode_name(AddressingMode addr_mode);
|
||||
|
||||
char *operand_name(Operand *operand);
|
||||
|
||||
char *get_op_code_name(byte op);
|
||||
|
||||
#endif //NESEMULATOR_DECODING_H
|
466
cpu/op.c
466
cpu/op.c
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "op.h"
|
||||
#include "cpu.h"
|
||||
#include "decoding.h"
|
||||
|
||||
// Reference: https://www.nesdev.org/wiki/CPU_unofficial_opcodes
|
||||
// https://www.middle-engine.com/blog/posts/2020/06/23/programming-the-nes-the-6502-in-detail
|
||||
|
@ -60,130 +61,6 @@
|
|||
IS_UNOFFICIAL_OP_CODE_(op, line, 0x1b, ABSOLUTE_INDEXED_Y) \
|
||||
IS_UNOFFICIAL_OP_CODE_(op, line, 0x1f, ABSOLUTE_INDEXED_X)
|
||||
|
||||
char *addr_mode_name(AddressingMode addr_mode) {
|
||||
switch (addr_mode) {
|
||||
case ADDR_MODE_ABSOLUTE:
|
||||
return "Absolute";
|
||||
case ADDR_MODE_ABSOLUTE_JUMP:
|
||||
return "Absolute Jump";
|
||||
case ADDR_MODE_ABSOLUTE_INDEXED_X:
|
||||
return "Absolute Indexed X";
|
||||
case ADDR_MODE_ABSOLUTE_INDEXED_Y:
|
||||
return "Absolute Indexed Y";
|
||||
case ADDR_MODE_ACCUMULATOR:
|
||||
return "Accumulator";
|
||||
case ADDR_MODE_IMMEDIATE:
|
||||
return "Immediate";
|
||||
case ADDR_MODE_IMPLICIT:
|
||||
return "Implicit";
|
||||
case ADDR_MODE_INDIRECT_X:
|
||||
return "Indirect X";
|
||||
case ADDR_MODE_INDIRECT_JUMP:
|
||||
return "Indirect Jump";
|
||||
case ADDR_MODE_INDIRECT_Y:
|
||||
return "Indirect Y";
|
||||
case ADDR_MODE_RELATIVE:
|
||||
return "Relative";
|
||||
case ADDR_MODE_ZERO_PAGE:
|
||||
return "Zero Page";
|
||||
case ADDR_MODE_ZERO_PAGE_INDEXED_X:
|
||||
return "Zero Page X";
|
||||
case ADDR_MODE_ZERO_PAGE_INDEXED_Y:
|
||||
return "Zero Page Y";
|
||||
}
|
||||
}
|
||||
|
||||
address decode_operand_addr(System *system, AddressingMode addr_mode, bool *page_crossing) {
|
||||
CPU registers = system->cpu;
|
||||
address operand_addr;
|
||||
|
||||
if (addr_mode == ADDR_MODE_ZERO_PAGE) {
|
||||
operand_addr = cpu_get_next_byte(system);
|
||||
} else if (addr_mode == ADDR_MODE_ZERO_PAGE_INDEXED_X) {
|
||||
operand_addr = (cpu_get_next_byte(system) + registers.x) & 0xff;
|
||||
} else if (addr_mode == ADDR_MODE_ZERO_PAGE_INDEXED_Y) {
|
||||
operand_addr = (cpu_get_next_byte(system) + registers.y) & 0xff;
|
||||
} else if (addr_mode == ADDR_MODE_ABSOLUTE || addr_mode == ADDR_MODE_ABSOLUTE_JUMP) {
|
||||
operand_addr = cpu_get_next_word(system);
|
||||
} else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_X) {
|
||||
word addr = cpu_get_next_word(system);
|
||||
word new_addr = addr + registers.x;
|
||||
|
||||
*page_crossing = (addr & 0xff00) != (new_addr & 0xff00);
|
||||
|
||||
operand_addr = new_addr;
|
||||
} else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_Y) {
|
||||
word addr = cpu_get_next_word(system);
|
||||
word new_addr = addr + registers.y;
|
||||
|
||||
*page_crossing = (addr & 0xff00) != (new_addr & 0xff00);
|
||||
|
||||
operand_addr = new_addr;
|
||||
} else if (addr_mode == ADDR_MODE_INDIRECT_JUMP) {
|
||||
word addr = cpu_get_next_word(system);
|
||||
if ((addr & 0xff) == 0xff) {
|
||||
// Error in NES CPU for JMP op
|
||||
word result = mem_get_byte(system, addr);
|
||||
result += mem_get_byte(system, addr & 0xff00) << 8;
|
||||
operand_addr = result;
|
||||
} else {
|
||||
operand_addr = mem_get_word(system, addr);
|
||||
}
|
||||
} else if (addr_mode == ADDR_MODE_INDIRECT_X) {
|
||||
byte arg_addr = cpu_get_next_byte(system);
|
||||
|
||||
word addr = mem_get_byte(system, (arg_addr + system->cpu.x) & 0xff);
|
||||
addr += mem_get_byte(system, (arg_addr + system->cpu.x + 1) & 0xff) << 8;
|
||||
operand_addr = addr;
|
||||
} else if (addr_mode == ADDR_MODE_INDIRECT_Y) {
|
||||
byte arg_addr = cpu_get_next_byte(system);
|
||||
word addr = mem_get_byte(system, arg_addr) + (mem_get_byte(system, (arg_addr + 1) & 0xff) << 8);
|
||||
word new_addr = addr + registers.y;
|
||||
|
||||
*page_crossing = (addr & 0xff00) != (new_addr & 0xff00);
|
||||
|
||||
operand_addr = new_addr;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
log_trace("Operand address: %#02x, Addressing mode: %s", operand_addr, addr_mode_name(addr_mode));
|
||||
return operand_addr;
|
||||
}
|
||||
|
||||
Operand decode_operand(System *system, AddressingMode addr_mode) {
|
||||
Operand operand;
|
||||
|
||||
if (addr_mode == ADDR_MODE_ACCUMULATOR) {
|
||||
operand.type = OPERAND_TYPE_ACCUMULATOR;
|
||||
operand.value = 0;
|
||||
operand.is_page_crossing = false;
|
||||
} else if (addr_mode == ADDR_MODE_IMMEDIATE) {
|
||||
operand.type = OPERAND_TYPE_IMMEDIATE;
|
||||
operand.value = cpu_get_next_byte(system);
|
||||
operand.is_page_crossing = false;
|
||||
} else {
|
||||
operand.type = OPERAND_TYPE_ADDRESS;
|
||||
operand.value = decode_operand_addr(system, addr_mode, &operand.is_page_crossing);
|
||||
}
|
||||
|
||||
log_trace("Operand type: %s, value: %#02x", operand_name(&operand), operand.value);
|
||||
return operand;
|
||||
}
|
||||
|
||||
byte read_operand(System *system, Operand operand) {
|
||||
switch (operand.type) {
|
||||
case OPERAND_TYPE_ACCUMULATOR:
|
||||
return system->cpu.accumulator;
|
||||
case OPERAND_TYPE_IMMEDIATE:
|
||||
return (byte) operand.value;
|
||||
case OPERAND_TYPE_ADDRESS:
|
||||
return mem_get_byte(system, operand.value);
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void write_operand(System *system, Operand operand, byte value) {
|
||||
switch (operand.type) {
|
||||
case OPERAND_TYPE_ACCUMULATOR:
|
||||
|
@ -1230,344 +1107,3 @@ void process_op_code(System *system, byte op) {
|
|||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
char *get_op_code_name(byte op) {
|
||||
switch (op) {
|
||||
case 0x00:
|
||||
return "BRK";
|
||||
case 0x08:
|
||||
return "PHP";
|
||||
case 0x0b:
|
||||
case 0x2b:
|
||||
return "ANC";
|
||||
case 0x10:
|
||||
return "BPL";
|
||||
case 0x18:
|
||||
return "CLC";
|
||||
case 0x20:
|
||||
return "JSR";
|
||||
case 0x24:
|
||||
case 0x2c:
|
||||
return "BIT";
|
||||
case 0x28:
|
||||
return "PLP";
|
||||
case 0x30:
|
||||
return "BMI";
|
||||
case 0x38:
|
||||
return "SEC";
|
||||
case 0x40:
|
||||
return "RTI";
|
||||
case 0x48:
|
||||
return "PHA";
|
||||
case 0x4c:
|
||||
case 0x6c:
|
||||
return "JMP";
|
||||
case 0x50:
|
||||
return "BVC";
|
||||
case 0x58:
|
||||
return "CLI";
|
||||
case 0x60:
|
||||
return "RTS";
|
||||
case 0x68:
|
||||
return "PLA";
|
||||
case 0x70:
|
||||
return "BVS";
|
||||
case 0x78:
|
||||
return "SEI";
|
||||
case 0x84:
|
||||
case 0x8c:
|
||||
case 0x94:
|
||||
return "STY";
|
||||
case 0x88:
|
||||
return "DEY";
|
||||
case 0x90:
|
||||
return "BCC";
|
||||
case 0x98:
|
||||
return "TYA";
|
||||
case 0x9c:
|
||||
return "SHY";
|
||||
case 0xa0:
|
||||
case 0xa4:
|
||||
case 0xac:
|
||||
case 0xb4:
|
||||
case 0xbc:
|
||||
return "LDY";
|
||||
case 0xa8:
|
||||
return "TAY";
|
||||
case 0xb0:
|
||||
return "BCS";
|
||||
case 0xb8:
|
||||
return "CLV";
|
||||
case 0xc0:
|
||||
case 0xc4:
|
||||
case 0xcc:
|
||||
return "CPY";
|
||||
case 0xc8:
|
||||
return "INY";
|
||||
case 0xd0:
|
||||
return "BNE";
|
||||
case 0xd8:
|
||||
return "CLD";
|
||||
case 0xe0:
|
||||
case 0xe4:
|
||||
case 0xec:
|
||||
return "CPX";
|
||||
case 0xe8:
|
||||
return "INX";
|
||||
case 0xf0:
|
||||
return "BEQ";
|
||||
case 0xf8:
|
||||
return "SED";
|
||||
|
||||
case 0x01:
|
||||
case 0x05:
|
||||
case 0x09:
|
||||
case 0x0d:
|
||||
case 0x11:
|
||||
case 0x15:
|
||||
case 0x19:
|
||||
case 0x1d:
|
||||
return "ORA";
|
||||
case 0x21:
|
||||
case 0x25:
|
||||
case 0x29:
|
||||
case 0x2d:
|
||||
case 0x31:
|
||||
case 0x35:
|
||||
case 0x39:
|
||||
case 0x3d:
|
||||
return "AND";
|
||||
case 0x41:
|
||||
case 0x45:
|
||||
case 0x49:
|
||||
case 0x4d:
|
||||
case 0x51:
|
||||
case 0x55:
|
||||
case 0x59:
|
||||
case 0x5d:
|
||||
return "EOR";
|
||||
case 0x61:
|
||||
case 0x65:
|
||||
case 0x69:
|
||||
case 0x6d:
|
||||
case 0x71:
|
||||
case 0x75:
|
||||
case 0x79:
|
||||
case 0x7d:
|
||||
return "ADC";
|
||||
case 0x81:
|
||||
case 0x85:
|
||||
case 0x8d:
|
||||
case 0x91:
|
||||
case 0x95:
|
||||
case 0x99:
|
||||
case 0x9d:
|
||||
return "STA";
|
||||
case 0xa1:
|
||||
case 0xa5:
|
||||
case 0xa9:
|
||||
case 0xad:
|
||||
case 0xb1:
|
||||
case 0xb5:
|
||||
case 0xb9:
|
||||
case 0xbd:
|
||||
return "LDA";
|
||||
case 0xc1:
|
||||
case 0xc5:
|
||||
case 0xc9:
|
||||
case 0xcd:
|
||||
case 0xd1:
|
||||
case 0xd5:
|
||||
case 0xd9:
|
||||
case 0xdd:
|
||||
return "CMP";
|
||||
case 0xe1:
|
||||
case 0xe5:
|
||||
case 0xe9:
|
||||
case 0xed:
|
||||
case 0xf1:
|
||||
case 0xf5:
|
||||
case 0xf9:
|
||||
case 0xfd:
|
||||
return "SBC";
|
||||
|
||||
case 0x03:
|
||||
case 0x07:
|
||||
case 0x0f:
|
||||
case 0x13:
|
||||
case 0x17:
|
||||
case 0x1b:
|
||||
case 0x1f:
|
||||
return "SLO";
|
||||
case 0x23:
|
||||
case 0x27:
|
||||
case 0x2f:
|
||||
case 0x33:
|
||||
case 0x37:
|
||||
case 0x3b:
|
||||
case 0x3f:
|
||||
return "RLA";
|
||||
case 0x43:
|
||||
case 0x47:
|
||||
case 0x4f:
|
||||
case 0x53:
|
||||
case 0x57:
|
||||
case 0x5b:
|
||||
case 0x5f:
|
||||
return "SRE";
|
||||
case 0x4b:
|
||||
return "ALR";
|
||||
case 0x63:
|
||||
case 0x67:
|
||||
case 0x6f:
|
||||
case 0x73:
|
||||
case 0x77:
|
||||
case 0x7b:
|
||||
case 0x7f:
|
||||
return "RRA";
|
||||
case 0x6b:
|
||||
return "ARR";
|
||||
case 0x83:
|
||||
case 0x87:
|
||||
case 0x8f:
|
||||
case 0x97:
|
||||
return "SAX";
|
||||
case 0x8b:
|
||||
return "XAA";
|
||||
case 0x93:
|
||||
case 0x9f:
|
||||
return "AHX";
|
||||
case 0x9b:
|
||||
return "TAS";
|
||||
case 0xa3:
|
||||
case 0xa7:
|
||||
case 0xab:
|
||||
case 0xaf:
|
||||
case 0xb3:
|
||||
case 0xb7:
|
||||
case 0xbb:
|
||||
case 0xbf:
|
||||
return "LAX";
|
||||
case 0xc3:
|
||||
case 0xc7:
|
||||
case 0xcf:
|
||||
case 0xd3:
|
||||
case 0xd7:
|
||||
case 0xdb:
|
||||
case 0xdf:
|
||||
return "DCP";
|
||||
case 0xcb:
|
||||
return "AXS";
|
||||
case 0xe3:
|
||||
case 0xe7:
|
||||
case 0xef:
|
||||
case 0xf3:
|
||||
case 0xf7:
|
||||
case 0xfb:
|
||||
case 0xff:
|
||||
return "ISC";
|
||||
case 0xeb:
|
||||
return "SBC";
|
||||
|
||||
case 0x06:
|
||||
case 0x0a:
|
||||
case 0x0e:
|
||||
case 0x16:
|
||||
case 0x1e:
|
||||
return "ASL";
|
||||
case 0x26:
|
||||
case 0x2a:
|
||||
case 0x2e:
|
||||
case 0x36:
|
||||
case 0x3e:
|
||||
return "ROL";
|
||||
case 0x46:
|
||||
case 0x4a:
|
||||
case 0x4e:
|
||||
case 0x56:
|
||||
case 0x5e:
|
||||
return "LSR";
|
||||
case 0x66:
|
||||
case 0x6a:
|
||||
case 0x6e:
|
||||
case 0x76:
|
||||
case 0x7e:
|
||||
return "ROR";
|
||||
case 0x86:
|
||||
case 0x8e:
|
||||
case 0x96:
|
||||
return "STX";
|
||||
case 0x8a:
|
||||
return "TXA";
|
||||
case 0x9a:
|
||||
case 0xba:
|
||||
return "TSX";
|
||||
case 0x9e:
|
||||
return "SHX";
|
||||
case 0xa2:
|
||||
case 0xa6:
|
||||
case 0xae:
|
||||
case 0xb6:
|
||||
case 0xbe:
|
||||
return "LDX";
|
||||
case 0xaa:
|
||||
return "TAX";
|
||||
case 0xc6:
|
||||
case 0xca:
|
||||
case 0xce:
|
||||
case 0xd6:
|
||||
case 0xde:
|
||||
return "DEC";
|
||||
case 0xe6:
|
||||
case 0xee:
|
||||
case 0xf6:
|
||||
case 0xfe:
|
||||
return "INC";
|
||||
|
||||
case 0x02:
|
||||
case 0x12:
|
||||
case 0x22:
|
||||
case 0x32:
|
||||
case 0x42:
|
||||
case 0x52:
|
||||
case 0x62:
|
||||
case 0x72:
|
||||
case 0x92:
|
||||
case 0xb2:
|
||||
case 0xd2:
|
||||
case 0xf2:
|
||||
return "STP";
|
||||
case 0x04:
|
||||
case 0x0c:
|
||||
case 0x14:
|
||||
case 0x1c:
|
||||
case 0x1a:
|
||||
case 0x34:
|
||||
case 0x3a:
|
||||
case 0x3c:
|
||||
case 0x44:
|
||||
case 0x54:
|
||||
case 0x5a:
|
||||
case 0x5c:
|
||||
case 0x64:
|
||||
case 0x74:
|
||||
case 0x7a:
|
||||
case 0x7c:
|
||||
case 0x80:
|
||||
case 0x82:
|
||||
case 0x89:
|
||||
case 0xc2:
|
||||
case 0xd4:
|
||||
case 0xda:
|
||||
case 0xdc:
|
||||
case 0xe2:
|
||||
case 0xea:
|
||||
case 0xf4:
|
||||
case 0xfa:
|
||||
case 0xfc:
|
||||
return "NOP";
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
18
cpu/op.h
18
cpu/op.h
|
@ -16,24 +16,6 @@ enum op_code_base {
|
|||
OP_CODE_BASE_SBC = 0xe0
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
ADDR_MODE_ABSOLUTE, // a
|
||||
ADDR_MODE_ABSOLUTE_JUMP, // (a)
|
||||
ADDR_MODE_ABSOLUTE_INDEXED_X, // a,x
|
||||
ADDR_MODE_ABSOLUTE_INDEXED_Y, // a,y
|
||||
ADDR_MODE_ACCUMULATOR, // A
|
||||
ADDR_MODE_IMMEDIATE, // #i
|
||||
ADDR_MODE_IMPLICIT, // Imp
|
||||
ADDR_MODE_INDIRECT_X, // (d,x)
|
||||
ADDR_MODE_INDIRECT_JUMP, //
|
||||
ADDR_MODE_INDIRECT_Y, // (d),y
|
||||
ADDR_MODE_RELATIVE, // label
|
||||
ADDR_MODE_ZERO_PAGE, // d
|
||||
ADDR_MODE_ZERO_PAGE_INDEXED_X, // d,x
|
||||
ADDR_MODE_ZERO_PAGE_INDEXED_Y, // d,y
|
||||
} AddressingMode;
|
||||
|
||||
void process_op_code(System *system, byte op);
|
||||
char* get_op_code_name(byte op);
|
||||
|
||||
#endif
|
|
@ -36,19 +36,19 @@ void start_debugger(System *system) {
|
|||
int keycode;
|
||||
while ((keycode = getch()) != CTRL_KEY_EXIT) {
|
||||
if (keycode == KEY_UP) {
|
||||
memory_view_move_cursor(&view, 0, -1);
|
||||
memory_view_move_cursor(&view, 0, MEMORY_VIEW_DIRECTION_DOWN);
|
||||
}
|
||||
|
||||
if (keycode == KEY_DOWN) {
|
||||
memory_view_move_cursor(&view, 0, 1);
|
||||
memory_view_move_cursor(&view, 0, MEMORY_VIEW_DIRECTION_UP);
|
||||
}
|
||||
|
||||
if (keycode == KEY_LEFT) {
|
||||
memory_view_move_cursor(&view, -1, 0);
|
||||
memory_view_move_cursor(&view, MEMORY_VIEW_DIRECTION_LEFT, 0);
|
||||
}
|
||||
|
||||
if (keycode == KEY_RIGHT) {
|
||||
memory_view_move_cursor(&view, 1, 0);
|
||||
memory_view_move_cursor(&view, MEMORY_VIEW_DIRECTION_RIGHT, 0);
|
||||
}
|
||||
|
||||
if (keycode == CTRL_KEY_G) {
|
||||
|
|
|
@ -93,13 +93,13 @@ void memory_view_scroll(MemoryView *view, char direction) {
|
|||
}
|
||||
|
||||
void memory_view_move_cursor(MemoryView *view, char horizontal, char vertical) {
|
||||
if (horizontal == 1 && view->cursor_x == 0xf ||
|
||||
horizontal == -1 && view->cursor_x == 0) {
|
||||
if (horizontal == MEMORY_VIEW_DIRECTION_UP && view->cursor_x == 0xf ||
|
||||
horizontal == MEMORY_VIEW_DIRECTION_DOWN && view->cursor_x == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (vertical == 1 && view->cursor_y == 0xf ||
|
||||
vertical == -1 && view->cursor_y == 0) {
|
||||
if (vertical == MEMORY_VIEW_DIRECTION_RIGHT && view->cursor_y == 0xf ||
|
||||
vertical == MEMORY_VIEW_DIRECTION_LEFT && view->cursor_y == 0) {
|
||||
memory_view_scroll(view, vertical);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#define MEMORY_VIEW_DIRECTION_UP 1
|
||||
#define MEMORY_VIEW_DIRECTION_DOWN (-1)
|
||||
#define MEMORY_VIEW_DIRECTION_RIGHT 1
|
||||
#define MEMORY_VIEW_DIRECTION_LEFT (-1)
|
||||
|
||||
typedef struct memory_view {
|
||||
PANEL *panel;
|
||||
|
@ -25,16 +27,54 @@ typedef struct memory_view {
|
|||
char cursor_y;
|
||||
} MemoryView;
|
||||
|
||||
/**
|
||||
* Initializes a memory view for a system RAM.
|
||||
* The viewer base address will be set to 0x0000, and the cursor (0, 0).
|
||||
* The content of the memory will be printed on a new curses window.
|
||||
* @param view A pointer to the view to initialize
|
||||
* @param ram A pointer to the RAM
|
||||
*/
|
||||
void memory_view_init(MemoryView *view, ram ram);
|
||||
|
||||
/**
|
||||
* Prints the RAM content from the viewer base address.
|
||||
*
|
||||
* @param view
|
||||
*/
|
||||
void memory_view_print(MemoryView *view);
|
||||
|
||||
/**
|
||||
* Sets the viewer base address to the target address page (the first byte) and prints the RAM.
|
||||
*
|
||||
* @param view
|
||||
* @param target The target address to print
|
||||
*/
|
||||
void memory_view_goto(MemoryView *view, address target);
|
||||
|
||||
/**
|
||||
* Scrolls the base address up or down by steps of 0x10.
|
||||
*
|
||||
* @param view
|
||||
* @param direction The scroll direction
|
||||
*/
|
||||
void memory_view_scroll(MemoryView *view, char direction);
|
||||
|
||||
/**
|
||||
* Moves the cursor up, down, right or left.
|
||||
*
|
||||
* @param view
|
||||
* @param horizontal
|
||||
* @param vertical
|
||||
*/
|
||||
void memory_view_move_cursor(MemoryView *view, char horizontal, char vertical);
|
||||
|
||||
/**
|
||||
* Moves the cursor to a specific memory address.
|
||||
* The view will not be scrolled if the target address is not displayed.
|
||||
*
|
||||
* @param view
|
||||
* @param target
|
||||
*/
|
||||
void memory_view_set_cursor_addr(MemoryView *view, address target);
|
||||
|
||||
#endif //NESEMULATOR_MEMORY_VIEW_H
|
||||
|
|
Loading…
Reference in New Issue