commit 79259bafa261a97a03ace8f3b6fdf39306dfff49 Author: Zack Buhman Date: Thu Jun 15 17:08:20 2023 +0000 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f0c9b81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +main diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..078b006 --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +CFLAGS = -O3 -march=native -g -Wall -Wextra -Werror -Wpedantic -Wno-error=unused-parameter -Wno-error=unused-variable +CFLAGS += $(shell pkg-config --cflags sdl2) +CFLAGS += $(shell pkg-config --cflags freetype2) +LDFLAGS = $(shell pkg-config --libs sdl2) +LDFLAGS += $(shell pkg-config --libs freetype2) + +TARGET = +CC = $(TARGET)gcc +AS = $(TARGET)as +AS = $(TARGET)ar +LD = $(TARGET)ld +OBJCOPY = $(TARGET)objcopy +OBJDUMP = $(TARGET)objdump + +OBJS = fake6502.o main.o instruction.o mneumonic.o present.o text.o cpu.o +HEADERS = $(wildcard *.h) + +all: main + +%.o: %.c $(HEADERS) Makefile + $(CC) $(ARCH) $(CFLAGS) -c $< -o $@ + +main: $(OBJS) + $(CC) $(ARCH) $(LDFLAGS) $^ -o $@ + +clean: + rm -f *.o *.elf *.bin *.out *.imem *.hex + +.SUFFIXES: +.INTERMEDIATE: +.PHONY: all clean %.dump diff --git a/convert.py b/convert.py new file mode 100644 index 0000000..0773aff --- /dev/null +++ b/convert.py @@ -0,0 +1,19 @@ +import sys + +stdout = sys.stdout.buffer + +ix = 0 +for line in sys.stdin: + words = line.split() + for word in words: + if ';' in word: + break + else: + if word == '??': + print(f'?? at {ix:02x}', file=sys.stderr) + number = 0xff + else: + number = int(word, 16) + assert number < 256 + stdout.write(bytes([number])) + ix += 1; diff --git a/cpu.c b/cpu.c new file mode 100644 index 0000000..15e9b59 --- /dev/null +++ b/cpu.c @@ -0,0 +1,234 @@ +#include + +#include "cpu.h" +#include "opcodes.h" + +extern void reset6502(void); +extern void step6502(void); + +uint8_t cpu_memory[65536] = { + [0xfffc] = 0x0, + [0xfffd] = 0x0, +}; + +uint8_t read6502(uint16_t address) +{ + return cpu_memory[address]; +} + +void write6502(uint16_t address, uint8_t value) +{ + cpu_memory[address] = value; +} + +extern uint16_t pc; +extern uint8_t sp, a, x, y, status; + +cpu_history_t cpu_history[65536] = { 0 }; +int cpu_history_len; +int cpu_history_ix; + +void cpu_get_state(cpu_state_t * state) { + *state = (cpu_state_t){ + .a = a, + .y = y, + .x = x, + .pc = pc, + .sp = sp, + .status = status, + }; +} + +static inline void update_history(void) +{ + instruction_t instruction = decode_ins[cpu_memory[pc]]; + addressing_mode_t addressing_mode = addressing_modes[instruction.mode]; + + if (cpu_history_len < 65536) + cpu_history_len++; + + cpu_history[cpu_history_ix] = (cpu_history_t){ + .state = (cpu_state_t){ + .a = a, + .y = y, + .x = x, + .pc = pc, + .sp = sp, + .status = status, + }, + .pc_mem = { cpu_memory[pc], cpu_memory[pc+1], cpu_memory[pc+2] }, + .value = (addressing_mode.func)(pc), + }; + + cpu_history_ix = (cpu_history_ix + 1) & 0xffff; +} + +void cpu_reset(void) +{ + reset6502(); + + cpu_history_len = 0; + cpu_history_ix = 0; +} + +void cpu_step(void) +{ + update_history(); + step6502(); +} + +static uint8_t * mem = cpu_memory; + +uint16_t absolute(uint16_t _pc) +{ + uint16_t effective = ((uint16_t)mem[_pc + 2] << 8) | ((uint16_t)mem[_pc + 1] << 0); + + return effective; +} + +uint16_t absolute_indexed_indirect(uint16_t _pc) +{ + uint16_t effective = (((uint16_t)mem[_pc + 2] << 8) | ((uint16_t)mem[_pc + 1] << 0)) + + x; + + uint16_t indirect = ((uint16_t)mem[effective + 1] << 8) + | ((uint16_t)mem[effective + 0] << 0); + + return indirect; +} + +uint16_t absolute_indexed_with_x(uint16_t _pc) +{ + uint16_t effective = (((uint16_t)mem[_pc + 2] << 8) | ((uint16_t)mem[_pc + 1] << 0)) + + x; + + return effective; +} + +uint16_t absolute_indexed_with_y(uint16_t _pc) +{ + uint16_t effective = (((uint16_t)mem[_pc + 2] << 8) | ((uint16_t)mem[_pc + 1] << 0)) + + y; + + return effective; +} + +uint16_t absolute_indirect(uint16_t _pc) +{ + uint16_t effective = (((uint16_t)mem[_pc + 2] << 8) | ((uint16_t)mem[_pc + 1] << 0)) + + y; + + uint16_t indirect = ((uint16_t)mem[effective + 1] << 8) + | ((uint16_t)mem[effective + 0] << 0); + + return indirect; +} + +uint16_t accumulator(uint16_t _pc) +{ + return a; +} + +uint16_t immediate(uint16_t _pc) +{ + return (uint8_t)mem[_pc + 1]; +} + +uint16_t implied(uint16_t _pc) +{ + return 0; // ??? +} + +uint16_t program_counter_relative(uint16_t _pc) +{ + uint16_t effective = _pc + 2 + ((int8_t)mem[_pc + 1]); + + return effective; +} + +uint16_t stack(uint16_t _pc) +{ + uint16_t effective = (0x01 << 8) | (sp << 0); + + return effective; +} + +uint16_t zero_page(uint16_t _pc) +{ + uint16_t effective = (0x00 << 8) | ((uint16_t)mem[_pc + 1] << 0); + + return effective; +} + +uint16_t zero_page_indexed_indirect(uint16_t _pc) +{ + uint8_t base = x + ((uint8_t)mem[_pc + 1] << 0); + + uint16_t effective = (0x00 << 8) | base; + + uint16_t indirect = ((uint16_t)mem[effective + 1] << 8) + | ((uint16_t)mem[effective + 0] << 0); + + return indirect; +} + +uint16_t zero_page_indexed_with_x(uint16_t _pc) +{ + uint8_t base = x + ((uint8_t)mem[_pc + 1] << 0); + + uint16_t effective = (0x00 << 8) | base; + + return effective; +} + +uint16_t zero_page_indexed_with_y(uint16_t _pc) +{ + uint8_t base = y + ((uint8_t)mem[_pc + 1] << 0); + + uint16_t effective = (0x00 << 8) | base; + + return effective; +} + +uint16_t zero_page_indirect(uint16_t _pc) +{ + uint8_t base = ((uint8_t)mem[_pc + 1] << 0); + + uint16_t effective = (0x00 << 8) | base; + + uint16_t indirect = ((uint16_t)mem[effective + 1] << 8) + | ((uint16_t)mem[effective + 0] << 0); + + return indirect; +} + +uint16_t zero_page_indirect_indexed_with_y(uint16_t _pc) +{ + uint8_t base = ((uint8_t)mem[_pc + 1] << 0); + + uint16_t indirect = ((uint16_t)mem[base + 1] << 8) + | ((uint16_t)mem[base + 0] << 0); + + uint16_t effective = indirect + y; + + return effective; +} + +addressing_mode_t addressing_modes[16] = { + [A] = { .alen = 2, .olen = 2, .func = absolute }, + [AII] = { .alen = 2, .olen = 2, .func = absolute_indexed_indirect }, + [AIX] = { .alen = 2, .olen = 2, .func = absolute_indexed_with_x }, + [AIY] = { .alen = 2, .olen = 2, .func = absolute_indexed_with_y }, + [AI] = { .alen = 2, .olen = 2, .func = absolute_indirect }, + [ACC] = { .alen = 0, .olen = 1, .func = accumulator }, + [IMM] = { .alen = 1, .olen = 1, .func = immediate }, + [I] = { .alen = 0, .olen = 0, .func = implied }, + [R] = { .alen = 1, .olen = 2, .func = program_counter_relative }, + [S] = { .alen = 0, .olen = 2, .func = stack }, + [ZP] = { .alen = 1, .olen = 2, .func = zero_page }, + [ZPII] = { .alen = 1, .olen = 2, .func = zero_page_indexed_indirect }, + [ZPX] = { .alen = 1, .olen = 2, .func = zero_page_indexed_with_x }, + [ZPY] = { .alen = 1, .olen = 2, .func = zero_page_indexed_with_y }, + [ZPI] = { .alen = 1, .olen = 2, .func = zero_page_indirect }, + [ZPIY] = { .alen = 1, .olen = 2, .func = zero_page_indirect_indexed_with_y }, +}; diff --git a/cpu.h b/cpu.h new file mode 100644 index 0000000..95ac7c3 --- /dev/null +++ b/cpu.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include "instruction.h" + +extern uint8_t cpu_memory[]; + +void cpu_reset(void); + +void cpu_step(void); + +typedef struct cpu_state { + uint8_t a; + uint8_t y; + uint8_t x; + uint16_t pc; + uint8_t sp; + uint8_t status; +} cpu_state_t; + +typedef struct cpu_history { + cpu_state_t state; + uint8_t pc_mem[3]; + uint16_t value; +} cpu_history_t; + +extern cpu_history_t cpu_history[]; +extern int cpu_history_len; +extern int cpu_history_ix; + +typedef uint16_t (* mode_value_t)(uint16_t); + +typedef struct addressing_mode { + int alen; + int olen; + mode_value_t func; +} addressing_mode_t; + +extern addressing_mode_t addressing_modes[16]; + +void cpu_get_state(cpu_state_t * state); diff --git a/debug-table.c b/debug-table.c new file mode 100644 index 0000000..4b9dcd0 --- /dev/null +++ b/debug-table.c @@ -0,0 +1,30 @@ + + + for (int y = 0; y < 16; y++) { + for (int x = 0; x < 16; x++) { + int i = x | (y << 4); + instruction_t ins = decode_ins[i]; + if (x == 0) + fprintf(stdout, "|%5s |", opcode_string[ins.opcode]); + else + fprintf(stdout, "%6s |", opcode_string[ins.opcode]); + } + + fprintf(stdout, "\n"); + + for (int x = 0; x < 16; x++) { + int i = x | (y << 4); + instruction_t ins = decode_ins[i]; + if (x == 0) + fprintf(stdout, "|%5s |", mode_string[ins.mode]); + else + fprintf(stdout, "%6s |", mode_string[ins.mode]); + } + + fprintf(stdout, "\n"); + + for (int c = 0; c < 128; c++) { + fputc('-', stdout); + } + fputc('\n', stdout); + } diff --git a/fake6502.c b/fake6502.c new file mode 100644 index 0000000..d1ab49f --- /dev/null +++ b/fake6502.c @@ -0,0 +1,944 @@ +/* Fake6502 CPU emulator core v1.1 ******************* + * (c)2011 Mike Chambers (miker00lz@gmail.com) * + ***************************************************** + * v1.1 - Small bugfix in BIT opcode, but it was the * + * difference between a few games in my NES * + * emulator working and being broken! * + * I went through the rest carefully again * + * after fixing it just to make sure I didn't * + * have any other typos! (Dec. 17, 2011) * + * * + * v1.0 - First release (Nov. 24, 2011) * + ***************************************************** + * LICENSE: This source code is released into the * + * public domain, but if you use it please do give * + * credit. I put a lot of effort into writing this! * + * * + ***************************************************** + * Fake6502 is a MOS Technology 6502 CPU emulation * + * engine in C. It was written as part of a Nintendo * + * Entertainment System emulator I've been writing. * + * * + * A couple important things to know about are two * + * defines in the code. One is "UNDOCUMENTED" which, * + * when defined, allows Fake6502 to compile with * + * full support for the more predictable * + * undocumented instructions of the 6502. If it is * + * undefined, undocumented opcodes just act as NOPs. * + * * + * The other define is "NES_CPU", which causes the * + * code to compile without support for binary-coded * + * decimal (BCD) support for the ADC and SBC * + * opcodes. The Ricoh 2A03 CPU in the NES does not * + * support BCD, but is otherwise identical to the * + * standard MOS 6502. (Note that this define is * + * enabled in this file if you haven't changed it * + * yourself. If you're not emulating a NES, you * + * should comment it out.) * + * * + * If you do discover an error in timing accuracy, * + * or operation in general please e-mail me at the * + * address above so that I can fix it. Thank you! * + * * + ***************************************************** + * Usage: * + * * + * Fake6502 requires you to provide two external * + * functions: * + * * + * uint8_t read6502(uint16_t address) * + * void write6502(uint16_t address, uint8_t value) * + * * + * You may optionally pass Fake6502 the pointer to a * + * function which you want to be called after every * + * emulated instruction. This function should be a * + * void with no parameters expected to be passed to * + * it. * + * * + * This can be very useful. For example, in a NES * + * emulator, you check the number of clock ticks * + * that have passed so you can know when to handle * + * APU events. * + * * + * To pass Fake6502 this pointer, use the * + * hookexternal(void *funcptr) function provided. * + * * + * To disable the hook later, pass NULL to it. * + ***************************************************** + * Useful functions in this emulator: * + * * + * void reset6502() * + * - Call this once before you begin execution. * + * * + * void exec6502(uint32_t tickcount) * + * - Execute 6502 code up to the next specified * + * count of clock ticks. * + * * + * void step6502() * + * - Execute a single instrution. * + * * + * void irq6502() * + * - Trigger a hardware IRQ in the 6502 core. * + * * + * void nmi6502() * + * - Trigger an NMI in the 6502 core. * + * * + * void hookexternal(void *funcptr) * + * - Pass a pointer to a void function taking no * + * parameters. This will cause Fake6502 to call * + * that function once after each emulated * + * instruction. * + * * + ***************************************************** + * Useful variables in this emulator: * + * * + * uint32_t clockticks6502 * + * - A running total of the emulated cycle count. * + * * + * uint32_t instructions * + * - A running total of the total emulated * + * instruction count. This is not related to * + * clock cycle timing. * + * * + *****************************************************/ + +#include +#include + +//6502 defines + +#define FLAG_CARRY 0x01 +#define FLAG_ZERO 0x02 +#define FLAG_INTERRUPT 0x04 +#define FLAG_DECIMAL 0x08 +#define FLAG_BREAK 0x10 +#define FLAG_CONSTANT 0x20 +#define FLAG_OVERFLOW 0x40 +#define FLAG_SIGN 0x80 + +#define BASE_STACK 0x100 + +#define saveaccum(n) a = (uint8_t)((n) & 0x00FF) + + +//flag modifier macros +#define setcarry() status |= FLAG_CARRY +#define clearcarry() status &= (~FLAG_CARRY) +#define setzero() status |= FLAG_ZERO +#define clearzero() status &= (~FLAG_ZERO) +#define setinterrupt() status |= FLAG_INTERRUPT +#define clearinterrupt() status &= (~FLAG_INTERRUPT) +#define setdecimal() status |= FLAG_DECIMAL +#define cleardecimal() status &= (~FLAG_DECIMAL) +#define setoverflow() status |= FLAG_OVERFLOW +#define clearoverflow() status &= (~FLAG_OVERFLOW) +#define setsign() status |= FLAG_SIGN +#define clearsign() status &= (~FLAG_SIGN) + + +//flag calculation macros +#define zerocalc(n) {\ + if ((n) & 0x00FF) clearzero();\ + else setzero();\ +} + +#define signcalc(n) {\ + if ((n) & 0x0080) setsign();\ + else clearsign();\ +} + +#define carrycalc(n) {\ + if ((n) & 0xFF00) setcarry();\ + else clearcarry();\ +} + +#define overflowcalc(n, m, o) { /* n = result, m = accumulator, o = memory */ \ + if (((n) ^ (uint16_t)(m)) & ((n) ^ (o)) & 0x0080) setoverflow();\ + else clearoverflow();\ +} + + +//6502 CPU registers +uint16_t pc; +uint8_t sp, a, x, y, status; + + +//helper variables +uint64_t instructions = 0; //keep track of total instructions executed +uint64_t clockticks6502 = 0, clockgoal6502 = 0; +uint16_t oldpc, ea, reladdr, value, result; +uint8_t opcode, oldstatus; + +//externally supplied functions +extern uint8_t read6502(uint16_t address); +extern void write6502(uint16_t address, uint8_t value); + +//a few general functions used by various other functions +void push16(uint16_t pushval) { + write6502(BASE_STACK + sp, (pushval >> 8) & 0xFF); + write6502(BASE_STACK + ((sp - 1) & 0xFF), pushval & 0xFF); + sp -= 2; +} + +void push8(uint8_t pushval) { + write6502(BASE_STACK + sp--, pushval); +} + +uint16_t pull16() { + uint16_t temp16; + temp16 = read6502(BASE_STACK + ((sp + 1) & 0xFF)) | ((uint16_t)read6502(BASE_STACK + ((sp + 2) & 0xFF)) << 8); + sp += 2; + return(temp16); +} + +uint8_t pull8() { + return (read6502(BASE_STACK + ++sp)); +} + +void reset6502() { + pc = (uint16_t)read6502(0xFFFC) | ((uint16_t)read6502(0xFFFD) << 8); + a = 0; + x = 0; + y = 0; + sp = 0xFD; + status |= FLAG_CONSTANT; +} + + +static void (*addrtable[256])(); +static void (*optable[256])(); +uint8_t penaltyop, penaltyaddr; + +//addressing mode functions, calculates effective addresses +static void imp() { //implied +} + +static void acc() { //accumulator +} + +static void stk() { //stack +} + +static void imm() { //immediate + ea = pc++; +} + +static void zp() { //zero-page + ea = (uint16_t)read6502((uint16_t)pc++); +} + +static void zpx() { //zero-page,X + ea = ((uint16_t)read6502((uint16_t)pc++) + (uint16_t)x) & 0xFF; //zero-page wraparound +} + +static void zpy() { //zero-page,Y + ea = ((uint16_t)read6502((uint16_t)pc++) + (uint16_t)y) & 0xFF; //zero-page wraparound +} + +static void rel() { //relative for branch ops (8-bit immediate value, sign-extended) + reladdr = (uint16_t)read6502(pc++); + if (reladdr & 0x80) reladdr |= 0xFF00; +} + +static void abso() { //absolute + ea = (uint16_t)read6502(pc) | ((uint16_t)read6502(pc+1) << 8); + pc += 2; +} + +static void absx() { //absolute,X + uint16_t startpage; + ea = ((uint16_t)read6502(pc) | ((uint16_t)read6502(pc+1) << 8)); + startpage = ea & 0xFF00; + ea += (uint16_t)x; + + if (startpage != (ea & 0xFF00)) { //one cycle penlty for page-crossing on some opcodes + penaltyaddr = 1; + } + + pc += 2; +} + +static void absy() { //absolute,Y + uint16_t startpage; + ea = ((uint16_t)read6502(pc) | ((uint16_t)read6502(pc+1) << 8)); + startpage = ea & 0xFF00; + ea += (uint16_t)y; + + if (startpage != (ea & 0xFF00)) { //one cycle penlty for page-crossing on some opcodes + penaltyaddr = 1; + } + + pc += 2; +} + +static void ind() { //indirect + uint16_t eahelp, eahelp2; + eahelp = (uint16_t)read6502(pc) | (uint16_t)((uint16_t)read6502(pc+1) << 8); + eahelp2 = (eahelp & 0xFF00) | ((eahelp + 1) & 0x00FF); //replicate 6502 page-boundary wraparound bug + ea = (uint16_t)read6502(eahelp) | ((uint16_t)read6502(eahelp2) << 8); + pc += 2; +} + +static void indx() { // (indirect,X) + uint16_t eahelp; + eahelp = (uint16_t)(((uint16_t)read6502(pc++) + (uint16_t)x) & 0xFF); //zero-page wraparound for table pointer + ea = (uint16_t)read6502(eahelp & 0x00FF) | ((uint16_t)read6502((eahelp+1) & 0x00FF) << 8); +} + +static void indz() { // (indirect) + uint16_t eahelp; + eahelp = (uint16_t)(((uint16_t)read6502(pc++)) & 0xFF); //zero-page wraparound for table pointer + ea = (uint16_t)read6502(eahelp & 0x00FF) | ((uint16_t)read6502((eahelp+1) & 0x00FF) << 8); +} + +static void indy() { // (indirect),Y + uint16_t eahelp, eahelp2, startpage; + eahelp = (uint16_t)read6502(pc++); + eahelp2 = (eahelp & 0xFF00) | ((eahelp + 1) & 0x00FF); //zero-page wraparound + ea = (uint16_t)read6502(eahelp) | ((uint16_t)read6502(eahelp2) << 8); + startpage = ea & 0xFF00; + ea += (uint16_t)y; + + if (startpage != (ea & 0xFF00)) { //one cycle penlty for page-crossing on some opcodes + penaltyaddr = 1; + } +} + +static uint16_t getvalue() { + if (addrtable[opcode] == acc) return((uint16_t)a); + else return((uint16_t)read6502(ea)); +} + +static void putvalue(uint16_t saveval) { + if (addrtable[opcode] == acc) a = (uint8_t)(saveval & 0x00FF); + else write6502(ea, (saveval & 0x00FF)); +} + + +//instruction handler functions +static void adc() { + penaltyop = 1; + value = getvalue(); + result = (uint16_t)a + value + (uint16_t)(status & FLAG_CARRY); + + carrycalc(result); + zerocalc(result); + overflowcalc(result, a, value); + signcalc(result); + + #ifndef NES_CPU + if (status & FLAG_DECIMAL) { + clearcarry(); + + if ((a & 0x0F) > 0x09) { + a += 0x06; + } + if ((a & 0xF0) > 0x90) { + a += 0x60; + setcarry(); + } + + clockticks6502++; + } + #endif + + saveaccum(result); +} + +static void and() { + penaltyop = 1; + value = getvalue(); + result = (uint16_t)a & value; + + zerocalc(result); + signcalc(result); + + saveaccum(result); +} + +static void asl() { + value = getvalue(); + result = value << 1; + + carrycalc(result); + zerocalc(result); + signcalc(result); + + putvalue(result); +} + +static void bcc() { + if ((status & FLAG_CARRY) == 0) { + oldpc = pc; + pc += reladdr; + if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary + else clockticks6502++; + } +} + +static void bcs() { + if ((status & FLAG_CARRY) == FLAG_CARRY) { + oldpc = pc; + pc += reladdr; + if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary + else clockticks6502++; + } +} + +static void beq() { + if ((status & FLAG_ZERO) == FLAG_ZERO) { + oldpc = pc; + pc += reladdr; + if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary + else clockticks6502++; + } +} + +static void bit() { + value = getvalue(); + result = (uint16_t)a & value; + + zerocalc(result); + status = (status & 0x3F) | (uint8_t)(value & 0xC0); +} + +static void bmi() { + if ((status & FLAG_SIGN) == FLAG_SIGN) { + oldpc = pc; + pc += reladdr; + if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary + else clockticks6502++; + } +} + +static void bne() { + if ((status & FLAG_ZERO) == 0) { + oldpc = pc; + pc += reladdr; + if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary + else clockticks6502++; + } +} + +static void bpl() { + if ((status & FLAG_SIGN) == 0) { + oldpc = pc; + pc += reladdr; + if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary + else clockticks6502++; + } +} + +static void bra() { + oldpc = pc; + pc += reladdr; + if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary + else clockticks6502++; +} + +static void brk() { + pc++; + push16(pc); //push next instruction address onto stack + push8(status | FLAG_BREAK); //push CPU status to stack + setinterrupt(); //set interrupt flag + pc = (uint16_t)read6502(0xFFFE) | ((uint16_t)read6502(0xFFFF) << 8); +} + +static void bvc() { + if ((status & FLAG_OVERFLOW) == 0) { + oldpc = pc; + pc += reladdr; + if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary + else clockticks6502++; + } +} + +static void bvs() { + if ((status & FLAG_OVERFLOW) == FLAG_OVERFLOW) { + oldpc = pc; + pc += reladdr; + if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary + else clockticks6502++; + } +} + +static void clc() { + clearcarry(); +} + +static void cld() { + cleardecimal(); +} + +static void cli() { + clearinterrupt(); +} + +static void clv() { + clearoverflow(); +} + +static void cmp() { + penaltyop = 1; + value = getvalue(); + result = (uint16_t)a - value; + + if (a >= (uint8_t)(value & 0x00FF)) setcarry(); + else clearcarry(); + if (a == (uint8_t)(value & 0x00FF)) setzero(); + else clearzero(); + signcalc(result); +} + +static void cpx() { + value = getvalue(); + result = (uint16_t)x - value; + + if (x >= (uint8_t)(value & 0x00FF)) setcarry(); + else clearcarry(); + if (x == (uint8_t)(value & 0x00FF)) setzero(); + else clearzero(); + signcalc(result); +} + +static void cpy() { + value = getvalue(); + result = (uint16_t)y - value; + + if (y >= (uint8_t)(value & 0x00FF)) setcarry(); + else clearcarry(); + if (y == (uint8_t)(value & 0x00FF)) setzero(); + else clearzero(); + signcalc(result); +} + +static void dec() { + value = getvalue(); + result = value - 1; + + zerocalc(result); + signcalc(result); + + putvalue(result); +} + +static void dex() { + x--; + + zerocalc(x); + signcalc(x); +} + +static void dey() { + y--; + + zerocalc(y); + signcalc(y); +} + +static void eor() { + penaltyop = 1; + value = getvalue(); + result = (uint16_t)a ^ value; + + zerocalc(result); + signcalc(result); + + saveaccum(result); +} + +static void inc() { + value = getvalue(); + result = value + 1; + + zerocalc(result); + signcalc(result); + + putvalue(result); +} + +static void inx() { + x++; + + zerocalc(x); + signcalc(x); +} + +static void iny() { + y++; + + zerocalc(y); + signcalc(y); +} + +static void jmp() { + pc = ea; +} + +static void jsr() { + push16(pc - 1); + pc = ea; +} + +static void lda() { + penaltyop = 1; + value = getvalue(); + a = (uint8_t)(value & 0x00FF); + + zerocalc(a); + signcalc(a); +} + +static void ldx() { + penaltyop = 1; + value = getvalue(); + x = (uint8_t)(value & 0x00FF); + + zerocalc(x); + signcalc(x); +} + +static void ldy() { + penaltyop = 1; + value = getvalue(); + y = (uint8_t)(value & 0x00FF); + + zerocalc(y); + signcalc(y); +} + +static void lsr() { + value = getvalue(); + result = value >> 1; + + if (value & 1) setcarry(); + else clearcarry(); + zerocalc(result); + signcalc(result); + + putvalue(result); +} + +static void nop() { + switch (opcode) { + case 0x1C: + case 0x3C: + case 0x5C: + case 0x7C: + case 0xDC: + case 0xFC: + penaltyop = 1; + break; + } +} + +static void ora() { + penaltyop = 1; + value = getvalue(); + result = (uint16_t)a | value; + + zerocalc(result); + signcalc(result); + + saveaccum(result); +} + +static void pha() { + push8(a); +} + +static void phx() { + push8(x); +} + +static void phy() { + push8(y); +} + +static void php() { + push8(status | FLAG_BREAK); +} + +static void pla() { + a = pull8(); + + zerocalc(a); + signcalc(a); +} + +static void plx() { + x = pull8(); + + zerocalc(x); + signcalc(x); +} + +static void ply() { + y = pull8(); + + zerocalc(y); + signcalc(y); +} + +static void plp() { + status = pull8() | FLAG_CONSTANT; +} + +static void rol() { + value = getvalue(); + result = (value << 1) | (status & FLAG_CARRY); + + carrycalc(result); + zerocalc(result); + signcalc(result); + + putvalue(result); +} + +static void ror() { + value = getvalue(); + result = (value >> 1) | ((status & FLAG_CARRY) << 7); + + if (value & 1) setcarry(); + else clearcarry(); + zerocalc(result); + signcalc(result); + + putvalue(result); +} + +static void rti() { + status = pull8(); + value = pull16(); + pc = value; +} + +static void rts() { + value = pull16(); + pc = value + 1; +} + +static void sbc() { + penaltyop = 1; + value = getvalue() ^ 0x00FF; + result = (uint16_t)a + value + (uint16_t)(status & FLAG_CARRY); + + carrycalc(result); + zerocalc(result); + overflowcalc(result, a, value); + signcalc(result); + + #ifndef NES_CPU + if (status & FLAG_DECIMAL) { + clearcarry(); + + a -= 0x66; + if ((a & 0x0F) > 0x09) { + a += 0x06; + } + if ((a & 0xF0) > 0x90) { + a += 0x60; + setcarry(); + } + + clockticks6502++; + } + #endif + + saveaccum(result); +} + +static void sec() { + setcarry(); +} + +static void sed() { + setdecimal(); +} + +static void sei() { + setinterrupt(); +} + +static void sta() { + putvalue(a); +} + +static void stx() { + putvalue(x); +} + +static void sty() { + putvalue(y); +} + +static void stz() { + putvalue(0); +} + +static void tax() { + x = a; + + zerocalc(x); + signcalc(x); +} + +static void tay() { + y = a; + + zerocalc(y); + signcalc(y); +} + +static void tsx() { + x = sp; + + zerocalc(x); + signcalc(x); +} + +static void txa() { + a = x; + + zerocalc(a); + signcalc(a); +} + +static void txs() { + sp = x; +} + +static void tya() { + a = y; + + zerocalc(a); + signcalc(a); +} + + +static void (*addrtable[256])() = { +/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ +/* 0 */ imp, indx, imp, indx, zp, zp, zp, zp, stk, imm, acc, imm, abso, abso, abso, abso, /* 0 */ +/* 1 */ rel, indy, indz, indy, zpx, zpx, zpx, zpx, imp, absy, acc, absy, absx, absx, absx, absx, /* 1 */ +/* 2 */ abso, indx, imp, indx, zp, zp, zp, zp, stk, imm, acc, imm, abso, abso, abso, abso, /* 2 */ +/* 3 */ rel, indy, indz, indy, zpx, zpx, zpx, zpx, imp, absy, acc, absy, absx, absx, absx, absx, /* 3 */ +/* 4 */ imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, /* 4 */ +/* 5 */ rel, indy, indz, indy, zpx, zpx, zpx, zpx, imp, absy, stk, absy, absx, absx, absx, absx, /* 5 */ +/* 6 */ imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, ind, abso, abso, abso, /* 6 */ +/* 7 */ rel, indy, indz, indy, zpx, zpx, zpx, zpx, imp, absy, stk, absy, absx, absx, absx, absx, /* 7 */ +/* 8 */ rel, indx, imp, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* 8 */ +/* 9 */ rel, indy, indz, indy, zpx, zpx, zpy, zpy, imp, absy, imp, absy, abso, absx, absx, absy, /* 9 */ +/* A */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* A */ +/* B */ rel, indy, indz, indy, zpx, zpx, zpy, zpy, imp, absy, imp, absy, absx, absx, absy, absy, /* B */ +/* C */ imm, indx, imp, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* C */ +/* D */ rel, indy, indz, indy, zpx, zpx, zpx, zpx, imp, absy, stk, absy, absx, absx, absx, absx, /* D */ +/* E */ imm, indx, imp, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* E */ +/* F */ rel, indy, indz, indy, zpx, zpx, zpx, zpx, imp, absy, stk, absy, absx, absx, absx, absx /* F */ +}; + +static void (*optable[256])() = { +/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ +/* 0 */ brk, ora, nop, nop, nop, ora, asl, nop, php, ora, asl, nop, nop, ora, asl, nop, /* 0 */ +/* 1 */ bpl, ora, ora, nop, nop, ora, asl, nop, clc, ora, inc, nop, nop, ora, asl, nop, /* 1 */ +/* 2 */ jsr, and, nop, nop, bit, and, rol, nop, plp, and, rol, nop, bit, and, rol, nop, /* 2 */ +/* 3 */ bmi, and, and, nop, nop, and, rol, nop, sec, and, dec, nop, nop, and, rol, nop, /* 3 */ +/* 4 */ rti, eor, nop, nop, nop, eor, lsr, nop, pha, eor, lsr, nop, jmp, eor, lsr, nop, /* 4 */ +/* 5 */ bvc, eor, eor, nop, nop, eor, lsr, nop, cli, eor, phy, nop, nop, eor, lsr, nop, /* 5 */ +/* 6 */ rts, adc, nop, nop, stz, adc, ror, nop, pla, adc, ror, nop, jmp, adc, ror, nop, /* 6 */ +/* 7 */ bvs, adc, adc, nop, stz, adc, ror, nop, sei, adc, ply, nop, nop, adc, ror, nop, /* 7 */ +/* 8 */ bra, sta, nop, nop, sty, sta, stx, nop, dey, nop, txa, nop, sty, sta, stx, nop, /* 8 */ +/* 9 */ bcc, sta, sta, nop, sty, sta, stx, nop, tya, sta, txs, nop, stz, sta, stz, nop, /* 9 */ +/* A */ ldy, lda, ldx, nop, ldy, lda, ldx, nop, tay, lda, tax, nop, ldy, lda, ldx, nop, /* A */ +/* B */ bcs, lda, lda, nop, ldy, lda, ldx, nop, clv, lda, tsx, nop, ldy, lda, ldx, nop, /* B */ +/* C */ cpy, cmp, nop, nop, cpy, cmp, dec, nop, iny, cmp, dex, nop, cpy, cmp, dec, nop, /* C */ +/* D */ bne, cmp, cmp, nop, nop, cmp, dec, nop, cld, cmp, phx, nop, nop, cmp, dec, nop, /* D */ +/* E */ cpx, sbc, nop, nop, cpx, sbc, inc, nop, inx, sbc, nop, sbc, cpx, sbc, inc, nop, /* E */ +/* F */ beq, sbc, sbc, nop, nop, sbc, inc, nop, sed, sbc, plx, nop, nop, sbc, inc, nop /* F */ +}; + +static const uint32_t ticktable[256] = { +/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ +/* 0 */ 7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, /* 0 */ +/* 1 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 1 */ +/* 2 */ 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, /* 2 */ +/* 3 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 3 */ +/* 4 */ 6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, /* 4 */ +/* 5 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 5 */ +/* 6 */ 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, /* 6 */ +/* 7 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 7 */ +/* 8 */ 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, /* 8 */ +/* 9 */ 2, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5, /* 9 */ +/* A */ 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, /* A */ +/* B */ 2, 5, 2, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4, /* B */ +/* C */ 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, /* C */ +/* D */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* D */ +/* E */ 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, /* E */ +/* F */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7 /* F */ +}; + + +void nmi6502() { + push16(pc); + push8(status); + status |= FLAG_INTERRUPT; + pc = (uint16_t)read6502(0xFFFA) | ((uint16_t)read6502(0xFFFB) << 8); +} + +void irq6502() { + push16(pc); + push8(status); + status |= FLAG_INTERRUPT; + pc = (uint16_t)read6502(0xFFFE) | ((uint16_t)read6502(0xFFFF) << 8); +} + +uint8_t callexternal = 0; +void (*loopexternal)(); + +void exec6502(uint32_t tickcount) { + clockgoal6502 += tickcount; + + while (clockticks6502 < clockgoal6502) { + opcode = read6502(pc++); + status |= FLAG_CONSTANT; + + penaltyop = 0; + penaltyaddr = 0; + + (*addrtable[opcode])(); + (*optable[opcode])(); + clockticks6502 += ticktable[opcode]; + if (penaltyop && penaltyaddr) clockticks6502++; + + instructions++; + + if (callexternal) (*loopexternal)(); + } + +} + +void step6502() { + opcode = read6502(pc++); + status |= FLAG_CONSTANT; + + penaltyop = 0; + penaltyaddr = 0; + + (*addrtable[opcode])(); + (*optable[opcode])(); + clockticks6502 += ticktable[opcode]; + if (penaltyop && penaltyaddr) clockticks6502++; + clockgoal6502 = clockticks6502; + + instructions++; + + if (callexternal) (*loopexternal)(); +} + +/* +void hookexternal(void *funcptr) { + if (funcptr != (void *)NULL) { + loopexternal = funcptr; + callexternal = 1; + } else callexternal = 0; +} +*/ diff --git a/input-convert.py b/input-convert.py new file mode 100644 index 0000000..49f6ce9 --- /dev/null +++ b/input-convert.py @@ -0,0 +1,17 @@ +from itertools import chain + +input = """ +1721 +979 +366 +299 +675 +1456 +""" + +l = map(int, input.split()) + +f = lambda n: ((n & 0xff), ((n >> 8) & 0xff)) + +for n in chain.from_iterable(map(f, l)): + print(f"{n:02x}") diff --git a/instruction.c b/instruction.c new file mode 100644 index 0000000..cc8092d --- /dev/null +++ b/instruction.c @@ -0,0 +1,276 @@ +#include "instruction.h" +#include "opcodes.h" + +const struct instruction decode_ins[256] = { + {BRK, S}, + {ORA, ZPII}, + {O_INVALID, M_INVALID}, + {O_INVALID, M_INVALID}, + {TSB, ZP}, + {ORA, ZP}, + {ASL, ZP}, + {RMB0, ZP}, + {PHP, S}, + {ORA, IMM}, + {ASL, ACC}, + {O_INVALID, M_INVALID}, + {TSB, A}, + {ORA, A}, + {ASL, A}, + {BBR0, R}, + + {BPL, R}, + {ORA, ZPIY}, + {ORA, ZPI}, + {O_INVALID, M_INVALID}, + {TRB, ZP}, + {ORA, ZPX}, + {ASL, ZPX}, + {RMB1, ZP}, + {CLC, I}, + {ORA, AIY}, + {INC, ACC}, + {O_INVALID, M_INVALID}, + {TRB, A}, + {ORA, AIX}, + {ASL, AIX}, + {BBR1, R}, + + {JSR, A}, + {AND, ZPII}, + {O_INVALID, M_INVALID}, + {O_INVALID, M_INVALID}, + {BIT, ZP}, + {AND, ZP}, + {ROL, ZP}, + {RMB2, ZP}, + {PLP, S}, + {AND, IMM}, + {ROL, ACC}, + {O_INVALID, M_INVALID}, + {BIT, A}, + {AND, A}, + {ROL, A}, + {BBR2, R}, + + {BMI, R}, + {AND, ZPIY}, + {AND, ZPI}, + {O_INVALID, M_INVALID}, + {BIT, ZPX}, + {AND, ZPX}, + {ROL, ZPX}, + {RMB3, ZP}, + {SEC, I}, + {AND, AIY}, + {DEC, ACC}, + {O_INVALID, M_INVALID}, + {BIT, AIX}, + {AND, AIX}, + {ROL, AIX}, + {BBR3, R}, + + {RTI, S}, + {EOR, ZPII}, + {O_INVALID, M_INVALID}, + {O_INVALID, M_INVALID}, + {O_INVALID, M_INVALID}, + {EOR, ZP}, + {LSR, ZP}, + {RMB4, ZP}, + {PHA, S}, + {EOR, IMM}, + {LSR, ACC}, + {O_INVALID, M_INVALID}, + {JMP, A}, + {EOR, A}, + {LSR, A}, + {BBR4, R}, + + {BVC, R}, + {EOR, ZPIY}, + {EOR, ZPI}, + {EOR, ZPX}, + {O_INVALID, M_INVALID}, + {O_INVALID, M_INVALID}, + {LSR, ZPX}, + {RMB5, ZP}, + {CLI, I}, + {EOR, AIY}, + {PHY, S}, + {O_INVALID, M_INVALID}, + {O_INVALID, M_INVALID}, + {EOR, AIX}, + {LSR, AIX}, + {BBR5, R}, + + {RTS, S}, + {ADC, ZPII}, + {O_INVALID, M_INVALID}, + {O_INVALID, M_INVALID}, + {STZ, ZP}, + {ADC, ZP}, + {ROR, ZP}, + {RMB6, ZP}, + {PLA, S}, + {ADC, IMM}, + {ROR, ACC}, + {O_INVALID, M_INVALID}, + {JMP, AI}, + {ADC, A}, + {ROR, A}, + {BBR6, R}, + + {BVS, R}, + {ADC, ZPIY}, + {ADC, ZPI}, + {O_INVALID, M_INVALID}, + {STZ, ZPX}, + {ADC, ZPX}, + {ROR, ZPX}, + {RMB7, ZP}, + {SEI, I}, + {ADC, AIY}, + {PLY, S}, + {O_INVALID, M_INVALID}, + {JMP, AII}, + {ADC, AIX}, + {ROR, AIX}, + {BBR7, R}, + + {BRA, R}, + {STA, ZPII}, + {O_INVALID, M_INVALID}, + {O_INVALID, M_INVALID}, + {STY, ZP}, + {STA, ZP}, + {STX, ZP}, + {SMB0, ZP}, + {DEY, I}, + {BIT, IMM}, + {TXA, I}, + {O_INVALID, M_INVALID}, + {STY, A}, + {STA, A}, + {STX, A}, + {BBS0, R}, + + {BCC, R}, + {STA, ZPIY}, + {STA, ZPI}, + {O_INVALID, M_INVALID}, + {STY, ZPX}, + {STA, ZPX}, + {STX, ZPY}, + {SMB1, ZP}, + {TYA, I}, + {STA, AIY}, + {TXS, I}, + {O_INVALID, M_INVALID}, + {STZ, A}, + {STA, AIX}, + {STZ, AIX}, + {BBS1, R}, + + {LDY, IMM}, + {LDA, ZPII}, + {LDX, IMM}, + {O_INVALID, M_INVALID}, + {LDY, ZP}, + {LDA, ZP}, + {LDX, ZP}, + {SMB2, ZP}, + {TAY, I}, + {LDA, IMM}, + {TAX, I}, + {O_INVALID, M_INVALID}, + {LDY, A}, + {LDA, A}, + {LDX, A}, + {BBS2, R}, + + {BCS, R}, + {LDA, ZPIY}, + {LDA, ZPI}, + {O_INVALID, M_INVALID}, + {LDY, ZPX}, + {LDA, ZPX}, + {LDX, ZPY}, + {SMB3, ZP}, + {CLV, I}, + {LDA, AIY}, + {TSX, I}, + {O_INVALID, M_INVALID}, + {LDY, AIX}, + {LDA, AIX}, + {LDX, AIY}, + {BBS3, R}, + + {CPY, IMM}, + {CMP, ZPII}, + {O_INVALID, M_INVALID}, + {O_INVALID, M_INVALID}, + {CPY, ZP}, + {CMP, ZP}, + {DEC, ZP}, + {SMB4, ZP}, + {INY, I}, + {CMP, IMM}, + {DEX, I}, + {WAI, I}, + {CPY, A}, + {CMP, A}, + {DEC, A}, + {BBS4, R}, + + {BNE, R}, + {CMP, ZPIY}, + {CMP, ZPI}, + {O_INVALID, M_INVALID}, + {O_INVALID, M_INVALID}, + {CMP, ZPX}, + {DEC, ZPX}, + {SMB5, ZP}, + {CLD, I}, + {CMP, AIY}, + {PHX, S}, + {STP, I}, + {O_INVALID, M_INVALID}, + {CMP, AIX}, + {DEC, AIX}, + {BBS5, R}, + + {CPX, IMM}, + {SBC, ZPII}, + {O_INVALID, M_INVALID}, + {O_INVALID, M_INVALID}, + {CPX, ZP}, + {SBC, ZP}, + {INC, ZP}, + {SMB6, ZP}, + {INX, I}, + {SBC, IMM}, + {NOP, I}, + {O_INVALID, M_INVALID}, + {CPX, A}, + {SBC, A}, + {INC, A}, + {BBS6, R}, + + {BEQ, R}, + {SBC, ZPIY}, + {SBC, ZPI}, + {O_INVALID, M_INVALID}, + {O_INVALID, M_INVALID}, + {SBC, ZPX}, + {INC, ZPX}, + {SMB7, ZP}, + {SED, I}, + {SBC, AIY}, + {PLX, S}, + {O_INVALID, M_INVALID}, + {O_INVALID, M_INVALID}, + {SBC, AIX}, + {INC, AIX}, + {BBS7, R}, +}; diff --git a/instruction.h b/instruction.h new file mode 100644 index 0000000..518f961 --- /dev/null +++ b/instruction.h @@ -0,0 +1,8 @@ +#pragma once + +typedef struct instruction { + int opcode; + int mode; +} instruction_t; + +extern const struct instruction decode_ins[256]; diff --git a/main.c b/main.c new file mode 100644 index 0000000..daee81a --- /dev/null +++ b/main.c @@ -0,0 +1,29 @@ +#include +#include + +#include "cpu.h" +#include "present.h" + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + fprintf(stderr, "argc < 2\n"); + return -1; + } + + FILE * f; + size_t ret; + + f = fopen(argv[1], "r"); + const int program_location = 0x0200; + ret = fread(cpu_memory + program_location, 1, 0x10000 - program_location, f); + fprintf(stderr, "loaded %ld program bytes at %x\n", ret, program_location); + fclose(f); + + cpu_memory[0xfffc] = (program_location >> 0) & 0xff; + cpu_memory[0xfffd] = (program_location >> 8) & 0xff; + + cpu_reset(); + + return present_main(); +} diff --git a/mneumonic-strings.h b/mneumonic-strings.h new file mode 100644 index 0000000..3dd9eaf --- /dev/null +++ b/mneumonic-strings.h @@ -0,0 +1,120 @@ +#pragma once +// addressing modes + +#define S_A "a" +#define S_AII "(a,x)" +#define S_AIX "a,x" +#define S_AIY "a,y" +#define S_AI "(a)" +#define S_ACC "A" +#define S_IMM "#" +#define S_I "i" +#define S_R "r" +#define S_S "s" +#define S_ZP "zp" +#define S_ZPII "(zp,x)" +#define S_ZPX "zp,x" +#define S_ZPY "zp,y" +#define S_ZPI "(zp)" +#define S_ZPIY "(zp),y" +#define S_M_INVALID "?" + +#define S_ADC "ADC" +#define S_AND "AND" +#define S_ASL "ASL" +#define S_BBR0 "BBR0" +#define S_BBR1 "BBR1" +#define S_BBR2 "BBR2" +#define S_BBR3 "BBR3" +#define S_BBR4 "BBR4" +#define S_BBR5 "BBR5" +#define S_BBR6 "BBR6" +#define S_BBR7 "BBR7" +#define S_BBS0 "BBS0" +#define S_BBS1 "BBS1" +#define S_BBS2 "BBS2" +#define S_BBS3 "BBS3" +#define S_BBS4 "BBS4" +#define S_BBS5 "BBS5" +#define S_BBS6 "BBS6" +#define S_BBS7 "BBS7" +#define S_BCC "BCC" +#define S_BCS "BCS" +#define S_BEQ "BEQ" +#define S_BIT "BIT" +#define S_BMI "BMI" +#define S_BNE "BNE" +#define S_BPL "BPL" +#define S_BRA "BRA" +#define S_BRK "BRK" +#define S_BVC "BVC" +#define S_BVS "BVS" +#define S_CLC "CLC" +#define S_CLD "CLD" +#define S_CLI "CLI" +#define S_CLV "CLV" +#define S_CMP "CMP" +#define S_CPX "CPX" +#define S_CPY "CPY" +#define S_DEC "DEC" +#define S_DEX "DEX" +#define S_DEY "DEY" +#define S_EOR "EOR" +#define S_INC "INC" +#define S_INX "INX" +#define S_INY "INY" +#define S_JMP "JMP" +#define S_JSR "JSR" +#define S_LDA "LDA" +#define S_LDX "LDX" +#define S_LDY "LDY" +#define S_LSR "LSR" +#define S_NOP "NOP" +#define S_ORA "ORA" +#define S_PHA "PHA" +#define S_PHP "PHP" +#define S_PHX "PHX" +#define S_PHY "PHY" +#define S_PLA "PLA" +#define S_PLP "PLP" +#define S_PLX "PLX" +#define S_PLY "PLY" +#define S_RMB0 "RMB0" +#define S_RMB1 "RMB1" +#define S_RMB2 "RMB2" +#define S_RMB3 "RMB3" +#define S_RMB4 "RMB4" +#define S_RMB5 "RMB5" +#define S_RMB6 "RMB6" +#define S_RMB7 "RMB7" +#define S_ROL "ROL" +#define S_ROR "ROR" +#define S_RTI "RTI" +#define S_RTS "RTS" +#define S_SBC "SBC" +#define S_SEC "SEC" +#define S_SED "SED" +#define S_SEI "SEI" +#define S_SMB0 "SMB0" +#define S_SMB1 "SMB1" +#define S_SMB2 "SMB2" +#define S_SMB3 "SMB3" +#define S_SMB4 "SMB4" +#define S_SMB5 "SMB5" +#define S_SMB6 "SMB6" +#define S_SMB7 "SMB7" +#define S_STA "STA" +#define S_STP "STP" +#define S_STX "STX" +#define S_STY "STY" +#define S_STZ "STZ" +#define S_TAX "TAX" +#define S_TAY "TAY" +#define S_TRB "TRB" +#define S_TSB "TSB" +#define S_TSX "TSX" +#define S_TXA "TXA" +#define S_TXS "TXS" +#define S_TYA "TYA" +#define S_WAI "WAI" +#define S_O_INVALID "?" diff --git a/mneumonic.c b/mneumonic.c new file mode 100644 index 0000000..d32f73d --- /dev/null +++ b/mneumonic.c @@ -0,0 +1,124 @@ +#include "mneumonic-strings.h" +#include "opcodes.h" + +const char * opcode_string[] = { + [ADC] = S_ADC, + [AND] = S_AND, + [ASL] = S_ASL, + [BBR0] = S_BBR0, + [BBR1] = S_BBR1, + [BBR2] = S_BBR2, + [BBR3] = S_BBR3, + [BBR4] = S_BBR4, + [BBR5] = S_BBR5, + [BBR6] = S_BBR6, + [BBR7] = S_BBR7, + [BBS0] = S_BBS0, + [BBS1] = S_BBS1, + [BBS2] = S_BBS2, + [BBS3] = S_BBS3, + [BBS4] = S_BBS4, + [BBS5] = S_BBS5, + [BBS6] = S_BBS6, + [BBS7] = S_BBS7, + [BCC] = S_BCC, + [BCS] = S_BCS, + [BEQ] = S_BEQ, + [BIT] = S_BIT, + [BMI] = S_BMI, + [BNE] = S_BNE, + [BPL] = S_BPL, + [BRA] = S_BRA, + [BRK] = S_BRK, + [BVC] = S_BVC, + [BVS] = S_BVS, + [CLC] = S_CLC, + [CLD] = S_CLD, + [CLI] = S_CLI, + [CLV] = S_CLV, + [CMP] = S_CMP, + [CPX] = S_CPX, + [CPY] = S_CPY, + [DEC] = S_DEC, + [DEX] = S_DEX, + [DEY] = S_DEY, + [EOR] = S_EOR, + [INC] = S_INC, + [INX] = S_INX, + [INY] = S_INY, + [JMP] = S_JMP, + [JSR] = S_JSR, + [LDA] = S_LDA, + [LDX] = S_LDX, + [LDY] = S_LDY, + [LSR] = S_LSR, + [NOP] = S_NOP, + [ORA] = S_ORA, + [PHA] = S_PHA, + [PHP] = S_PHP, + [PHX] = S_PHX, + [PHY] = S_PHY, + [PLA] = S_PLA, + [PLP] = S_PLP, + [PLX] = S_PLX, + [PLY] = S_PLY, + [RMB0] = S_RMB0, + [RMB1] = S_RMB1, + [RMB2] = S_RMB2, + [RMB3] = S_RMB3, + [RMB4] = S_RMB4, + [RMB5] = S_RMB5, + [RMB6] = S_RMB6, + [RMB7] = S_RMB7, + [ROL] = S_ROL, + [ROR] = S_ROR, + [RTI] = S_RTI, + [RTS] = S_RTS, + [SBC] = S_SBC, + [SEC] = S_SEC, + [SED] = S_SED, + [SEI] = S_SEI, + [SMB0] = S_SMB0, + [SMB1] = S_SMB1, + [SMB2] = S_SMB2, + [SMB3] = S_SMB3, + [SMB4] = S_SMB4, + [SMB5] = S_SMB5, + [SMB6] = S_SMB6, + [SMB7] = S_SMB7, + [STA] = S_STA, + [STP] = S_STP, + [STX] = S_STX, + [STY] = S_STY, + [STZ] = S_STZ, + [TAX] = S_TAX, + [TAY] = S_TAY, + [TRB] = S_TRB, + [TSB] = S_TSB, + [TSX] = S_TSX, + [TXA] = S_TXA, + [TXS] = S_TXS, + [TYA] = S_TYA, + [WAI] = S_WAI, + [O_INVALID] = S_O_INVALID, +}; + +const char * mode_string[] = { + [A] = S_A, + [AII] = S_AII, + [AIX] = S_AIX, + [AIY] = S_AIY, + [AI] = S_AI, + [ACC] = S_ACC, + [IMM] = S_IMM, + [I] = S_I, + [R] = S_R, + [S] = S_S, + [ZP] = S_ZP, + [ZPII] = S_ZPII, + [ZPX] = S_ZPX, + [ZPY] = S_ZPY, + [ZPI] = S_ZPI, + [ZPIY] = S_ZPIY, + [M_INVALID] = S_M_INVALID, +}; diff --git a/mneumonic.h b/mneumonic.h new file mode 100644 index 0000000..5f1ccf1 --- /dev/null +++ b/mneumonic.h @@ -0,0 +1,7 @@ +#pragma once + +extern const char * opcode_string[]; +extern const char * mode_string[]; + +#define MAX_MODE_LEN (6) +#define MAX_OPCODE_LEN (4) diff --git a/opcodes.h b/opcodes.h new file mode 100644 index 0000000..82bda0c --- /dev/null +++ b/opcodes.h @@ -0,0 +1,121 @@ +enum opcode { + ADC, + AND, + ASL, + BBR0, + BBR1, + BBR2, + BBR3, + BBR4, + BBR5, + BBR6, + BBR7, + BBS0, + BBS1, + BBS2, + BBS3, + BBS4, + BBS5, + BBS6, + BBS7, + BCC, + BCS, + BEQ, + BIT, + BMI, + BNE, + BPL, + BRA, + 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, + PHX, + PHY, + PLA, + PLP, + PLX, + PLY, + RMB0, + RMB1, + RMB2, + RMB3, + RMB4, + RMB5, + RMB6, + RMB7, + ROL, + ROR, + RTI, + RTS, + SBC, + SEC, + SED, + SEI, + SMB0, + SMB1, + SMB2, + SMB3, + SMB4, + SMB5, + SMB6, + SMB7, + STA, + STP, + STX, + STY, + STZ, + TAX, + TAY, + TRB, + TSB, + TSX, + TXA, + TXS, + TYA, + WAI, + O_INVALID, +}; + +enum mode { + A, // Absolute a + AII, // Absolute Indexed Indirect (a,x) + AIX, // Absolute Indexed with X a,x + AIY, // Absolute Indexed with Y a,y + AI, // Absolute Indrect (a) + ACC, // Accumulator A + IMM, // Immediate # + I, // Implied i + R, // Program Counter Relative r + S, // Stack s + ZP, // Zero Page zp + ZPII, // Zero Page Indexed Indirect (zp,x) + ZPX, // Zero Page Indexed with X zp,x + ZPY, // Zero Page Indexed with Y zp,y + ZPI, // Zero Page Indirect (zp) + ZPIY, // Zero Page Indirect Indexed with Y (zp),y + M_INVALID, +}; diff --git a/present.c b/present.c new file mode 100644 index 0000000..c91c1c4 --- /dev/null +++ b/present.c @@ -0,0 +1,367 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "text.h" +#include "cpu.h" +#include "instruction.h" +#include "mneumonic.h" + +void render_memory(SDL_Renderer * renderer, int origin_col, int origin_row) +{ + int col = 0; + int row = 0; + + uint8_t mem_s[2]; + + int row_length = 8; + +#define MEM_COLOR 0xc8, 0xc8, 0xc9 + + col += 5; + for (int i = 0; i < row_length; i++) { + ull_base16(mem_s, 2, i); + render_text(renderer, origin_col + col, origin_row + row, + mem_s, 2, + MEM_COLOR); + col += 3; + } + + row += 1; + col = 0; + +#define MEM_COLOR_2 0xff, 0xff, 0xff + for (int ix = 0xee; ix < 0x100; ix++) { + if (col == 0) { + uint8_t mem_addr_s[4]; + ull_base16(mem_addr_s, 4, ix); + render_text(renderer, origin_col + col, origin_row + row, + mem_addr_s, 4, + MEM_COLOR); + col += 5; + } + + ull_base16(mem_s, 2, cpu_memory[ix]); + render_text(renderer, origin_col + col, origin_row + row, + mem_s, 2, + MEM_COLOR_2); + col += 3; + + if ((ix & 0x7) == 0x7) { + row += 1; + col = 0; + } + } + row += 1; + + for (int ix = 0; ix < 64; ix++) { + if (col == 0) { + uint8_t mem_addr_s[4]; + ull_base16(mem_addr_s, 4, 0x02dd + ix); + render_text(renderer, origin_col + col, origin_row + row, + mem_addr_s, 4, + MEM_COLOR); + col += 5; + } + + ull_base16(mem_s, 2, cpu_memory[0x02dd + ix]); + render_text(renderer, origin_col + col, origin_row + row, + mem_s, 2, + MEM_COLOR_2); + col += 3; + + if ((ix & 0x7) == 0x7) { + row += 1; + col = 0; + } + } +} + +void render_instruction(SDL_Renderer * renderer, int origin_col, int origin_row, + uint16_t pc, uint8_t * mem, uint16_t value) +{ + int col = 0; + instruction_t instruction = decode_ins[mem[0]]; + + uint8_t pc_s[4]; + ull_base16(pc_s, 4, pc); + render_text(renderer, origin_col + col, origin_row, + pc_s, 4, + MEM_COLOR); + col += 6; + + uint8_t mem_s[2]; + for (int ix = 0; ix < 3; ix++) { + if (addressing_modes[instruction.mode].alen >= ix) { + ull_base16(mem_s, 2, mem[ix]); + render_text(renderer, origin_col + col, origin_row, + mem_s, 2, + MEM_COLOR); + } + col += 3; + } + + col += 1; + +#define INS_COLOR 0xf0, 0xdf, 0xaf + + const char * opcode = opcode_string[instruction.opcode]; + const char * mode = mode_string[instruction.mode]; + + int opcode_len = strlen(opcode); + int mode_len = strlen(mode); + + render_text(renderer, origin_col + col, origin_row, + opcode, opcode_len, + INS_COLOR); + col += opcode_len + 1; + + render_text(renderer, origin_col + col, origin_row, + mode, mode_len, + INS_COLOR); + col += mode_len + 1; + +#define EFF_COLOR 0x7c, 0xb8, 0xbb + + int value_len = 2 * addressing_modes[instruction.mode].olen; + if (value_len) { + uint8_t value_s[value_len]; + ull_base16(value_s, value_len, value); + render_text(renderer, origin_col + col, origin_row, + value_s, value_len, + EFF_COLOR); + col += value_len + 1; + } +} + +void render_history(SDL_Renderer * renderer, int origin_col, int origin_row) +{ +#define HISTORY_COLOR 0xbb, 0x7c, 0xb8 + + int row = 0; + render_text(renderer, origin_col - 2, origin_row + row, + "history", 7, + HISTORY_COLOR); + row++; + + for (int i = 0; i < cpu_history_len; i++) { + int ix = (cpu_history_ix - (1 + i)) & 0xffff; + render_instruction(renderer, origin_col, origin_row + row++, + cpu_history[ix].state.pc, + cpu_history[ix].pc_mem, + cpu_history[ix].value); + if (row > 15) + break; + } +} + +void render_state(SDL_Renderer * renderer, int origin_col, int origin_row) +{ + uint8_t pc_s[4]; + uint8_t sp_s[2]; + uint8_t a_s[2]; + uint8_t x_s[2]; + uint8_t y_s[2]; + uint8_t status_s[8]; + + cpu_state_t state; + cpu_get_state(&state); + + ull_base16(pc_s, 4, state.pc); + ull_base16(sp_s, 2, state.sp); + ull_base16(a_s, 2, state.a); + ull_base16(x_s, 2, state.x); + ull_base16(y_s, 2, state.y); + for (int i = 0; i < 8; i++) { + status_s[i] = (((state.status) >> (7 - i)) & 1) ? '1' : '0'; + } + + int col = 0; + +#define HEADER_COLOR 0x77, 0xdd, 0x11 +#define VALUE_COLOR 0xff, 0xff, 0xff + + render_text(renderer, origin_col + col, origin_row, + "PC", 2, + HEADER_COLOR); + render_text(renderer, origin_col + col, origin_row + 1, + pc_s, 4, + VALUE_COLOR); + col += 2 + 3; + + render_text(renderer, origin_col + col, origin_row, + "A", 1, + HEADER_COLOR); + render_text(renderer, origin_col + col, origin_row + 1, + a_s, 2, + VALUE_COLOR); + col += 2 + 1; + + render_text(renderer, origin_col + col, origin_row, + "X", 1, + HEADER_COLOR); + render_text(renderer, origin_col + col, origin_row + 1, + x_s, 2, + VALUE_COLOR); + col += 2 + 1; + + render_text(renderer, origin_col + col, origin_row, + "Y", 1, + HEADER_COLOR); + render_text(renderer, origin_col + col, origin_row + 1, + y_s, 2, + VALUE_COLOR); + col += 2 + 1; + + render_text(renderer, origin_col + col, origin_row, + "SP", 2, + HEADER_COLOR); + render_text(renderer, origin_col + col, origin_row + 1, + sp_s, 2, + VALUE_COLOR); + col += 2 + 2; + + render_text(renderer, origin_col + col, origin_row, + "NV-BDIZC", 8, + HEADER_COLOR); + render_text(renderer, origin_col + col, origin_row + 1, + status_s, 8, + VALUE_COLOR); + col += 8; + + instruction_t instruction = decode_ins[cpu_memory[state.pc]]; + addressing_mode_t addressing_mode = addressing_modes[instruction.mode]; + uint16_t value = (addressing_mode.func)(state.pc); + + render_instruction(renderer, origin_row + 2, origin_col, + state.pc, + &cpu_memory[state.pc], + value); + + render_history(renderer, origin_col, origin_row + 4); +} + +void render(SDL_Renderer * renderer) +{ + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + render_state(renderer, 5, 3); + render_memory(renderer, 5 + 35, 3); + + SDL_RenderPresent(renderer); +} + +int present_main() +{ + SDL_Window * window; + SDL_Renderer * renderer; + + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); + + window = SDL_CreateWindow("cpu", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + 1500, + 1000, + SDL_WINDOW_SHOWN); + + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + + int ret = create_instruction_textures(renderer); + if (ret == -1) + return -1; + + SDL_Event event; + int got_event = 1; + int freerun = 0; + uint64_t ticks; + uint64_t ins_count; + uint64_t clocks; + extern uint64_t clockticks6502; + while (1) { + SDL_PumpEvents(); + while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) != 0) { + switch (event.type) { + case SDL_QUIT: + goto quit; + break; + case SDL_KEYDOWN: + got_event = 1; + if (event.key.keysym.scancode == SDL_SCANCODE_Q) + goto quit; + if (event.key.keysym.scancode == SDL_SCANCODE_SPACE) { + freerun = 0; + cpu_step(); + } + if (event.key.keysym.scancode == SDL_SCANCODE_RETURN) { + if (freerun == 0) { + ticks = SDL_GetTicks64(); + ins_count = 0; + clocks = clockticks6502; + freerun = 1; + } + } + break; + case SDL_MOUSEBUTTONDOWN: + got_event = 1; + break; + case SDL_WINDOWEVENT: + got_event = 1; + break; + default: + break; + } + } + + while (freerun == 1) { + cpu_state_t last_state; + cpu_get_state(&last_state); + + cpu_step(); + ins_count++; + + cpu_state_t state; + cpu_get_state(&state); + + if (last_state.pc == state.pc) { + freerun = 0; + printf("time %ju\n", SDL_GetTicks64() - ticks); + printf("ins %ju\n", ins_count); + printf("clocks %ld\n", clockticks6502 - clocks); + + FILE * f = fopen("memdump.bin", "w"); + assert (f != NULL); + int ret = fwrite(cpu_memory, 1, 0x10000, f); + assert (ret != -1); + fclose(f); + } + + got_event = 1; + + if ((ins_count % 160000) == 0) + break; + } + + if (got_event) { + render(renderer); + got_event = 0; + } + + //if (freerun == 1) + // SDL_Delay(1); + } + +quit: + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; +} diff --git a/present.h b/present.h new file mode 100644 index 0000000..88a030c --- /dev/null +++ b/present.h @@ -0,0 +1,3 @@ +#pragma once + +int present_main(void); diff --git a/test/ana.asm b/test/ana.asm new file mode 100644 index 0000000..20876fb --- /dev/null +++ b/test/ana.asm @@ -0,0 +1,9 @@ + ;; task: + ;; - there are two numbers, at addresses 0 and 1 + ;; - find the sum of the two numbers + ;; - store the result at address 2 + + LDA # h0 + ADC zp d0 + ADC zp d1 + STA zp d2 diff --git a/test/ana2.asm b/test/ana2.asm new file mode 100644 index 0000000..83f8432 --- /dev/null +++ b/test/ana2.asm @@ -0,0 +1,9 @@ + ;; task + ;; - there are an unknown quantity of numbers, anything between 1 and 99 (dec) + ;; - the `quantity` is stored at address 0 + ;; - the numbers are stored at addresses 1 to (1 + `quantity`) + ;; - add all numbers together + ;; - store the sum at address AF (hex) + + LDX # h5 + DEX i diff --git a/test/bra.asm b/test/bra.asm new file mode 100644 index 0000000..591d246 --- /dev/null +++ b/test/bra.asm @@ -0,0 +1,6 @@ + BRA r :there + NOP i + NOP i + NOP i +there: LDA # 0 + LDA # 1 diff --git a/test/foo.asm b/test/foo.asm new file mode 100644 index 0000000..193fce5 --- /dev/null +++ b/test/foo.asm @@ -0,0 +1,25 @@ +; task : +; - sum the following 5 numbers +; - save the result of the sum at memory address 7F +; (numbers are in base16 notation) +11 +18 +23 +25 +30 +; expected sum: A1 + +; program: + + ; mem = [0] * 128 + ; mem[0:5] = [0x11, 0x18, 0x23, 0x25, 0x30] +A2 00 ; LDX # 0 ; x = 0 +A9 00 ; LDA # 0 ; a = 0 + ; loop: ; while True: +75 00 ; ADC zp,x 0 ; a += mem[0 + x] +E8 ; INX i ; x += 1 +E0 05 ; CPX # 05 ; _ = (x == 5) +D0 F9 ; BNE r loop ; if _ == True: break +85 7F ; STA zp 7F ; mem[0x7f] = a + ; forever: : +4C 12 00 ; JMP a forever ; while True: pass diff --git a/test/instructions.asm b/test/instructions.asm new file mode 100644 index 0000000..155aa1c --- /dev/null +++ b/test/instructions.asm @@ -0,0 +1,14 @@ + LDA # ; load the value immediately after # into the A register + STA zp ; store the value of the A register into the address at zp + + ;; new instructions + + + LDX zp ; load the value at the address in zp into the X register + DEX i ; subtract 1 to the value of the X register, store the result in the X register + BNE r ; if the result of the last subtraction is not equal to zero, + ; - jump to a the specified location in the program + ; otherwise, + ; - continue to the next instruction immediately following this one + + ADC zp,x ; diff --git a/test/program.asm b/test/program.asm new file mode 100644 index 0000000..3cb8561 --- /dev/null +++ b/test/program.asm @@ -0,0 +1,122 @@ + ;; uint8_t * input_len = 8000; + ;; uint16_t * input = 8001; + ;; uint16_t ** input_j = 2; + ;; uint16_t ** input_k = 4; + ;; uint8_t * k_index = 0; + LDA # :_bin_input_start_l ;; + STA zp 4 ;; + LDA # :_bin_input_start_h ;; + STA zp 5 ;; *input_k = input + LDA # :_bin_input_size_h ;; + LSR A ;; + LDA # :_bin_input_size_l ;; + ROR A ;; + STA zp 0 ;; *input_len = _bin_input_size / 2 + STA zp 1 ;; *k_index = *input_len + + +_loopx: LDA # :_bin_input_start_l ;; + STA zp 2 ;; + LDA # :_bin_input_start_h ;; + STA zp 3 ;; *input_j = input + LDA zp 0 ;; (input_size) + TAX i ;; X = *input_len + +_loop: CLC i ;; /* 16-bit addition+comparison (lower byte) */ + LDY # 0 ;; + LDA (zp),y 2 ;; + ADC (zp),y 4 ;; A = ((uint8_t *)(*input_j))[0] + ((uint8_t *)(*input_k))[0] + PHP s ;; + CMP # e4 ;; + BNE r :_next ;; if (A != 0xE4) goto _next + + PLP s ;; /* 16-bit addition+comparison (upper byte) */ + LDY # 1 ;; + LDA (zp),y 2 ;; + ADC (zp),y 4 ;; A = ((uint8_t *)(*input_j))[1] + ((uint8_t *)(*input_k))[1] + CMP # 07 ;; + BEQ r :_found ;; if (A == 0x07) goto _found + +_next: CLC i ;; + LDA zp 2 ;; + ADC # 2 ;; + STA zp 2 ;; + LDA zp 3 ;; + ADC # 0 ;; + STA zp 3 ;; *input_j = *input_j + 2 + DEX i ;; + BNE r :_loop ;; if (--X != 0) goto _loop + + CLC i ;; + LDA zp 4 ;; + ADC # 2 ;; + STA zp 4 ;; + LDA zp 5 ;; + ADC # 0 ;; + STA zp 5 ;; *input_k = *input_k + 2 + LDY zp 1 ;; + DEY i ;; + STY zp 1 ;; *k_index = k_index - 1; + BNE r :_loopx ;; if (*k_index != 0) goto _loopx + +_not_found: JMP a :_not_found + +_found: LDY # 0 ;; copy **input_j and **input_k to mul24 `a` and `b` arguments + LDA (zp),y 2 ;; + STA zp 8 ;; + LDA (zp),y 4 ;; + STA zp b ;; + ;; + LDY # 1 ;; + LDA (zp),y 2 ;; + STA zp 9 ;; + LDA (zp),y 4 ;; + STA zp c ;; + ;; + LDA # 0 ;; + STA zp a ;; + STA zp d ;; + +;; multiply two 24-bit operands; 24-bit product +;; +;; r = a * b + + ;; uint24_t * a = [8, 9, a] + ;; uint24_t * b = [b, c, d] + ;; uint24_t * r = [10, 11, 12] +mul24: LDA # 0 + STA zp 10 + STA zp 11 + STA zp 12 + +_mul24_loop: LDA zp 8 ;; while (a) { + ORA zp 9 ;; + ORA zp a ;; + BEQ r :_mul24_ret ;; + + LDA # 1 ;; if (a & 1) + AND zp 8 ;; + BEQ r :_mul24_nand_1 ;; + + CLC i ;; r += b; + LDA zp b ;;; 7..0 + ADC zp 10 ;; + STA zp 10 ;; + LDA zp c ;;; 15..8 + ADC zp 11 ;; + STA zp 11 ;; + LDA zp d ;;; 23..16 + ADC zp 12 ;; + STA zp 12 ;; + +_mul24_nand_1: LSR zp a ;; a >>= 1 + ROR zp 9 ;; + ROR zp 8 ;; + + ASL zp b ;; b <<= 1 + ROL zp c ;; + ROL zp d ;; + + JMP a :_mul24_loop ;; } + +_mul24_ret: JMP a :_mul24_ret ;; return r diff --git a/test/test.asm b/test/test.asm new file mode 100644 index 0000000..031ec82 --- /dev/null +++ b/test/test.asm @@ -0,0 +1,106 @@ +;; input + +0D ; end of input +B9 06 ; 1721 +D3 03 ; 979 +6E 01 ; 366 +2B 01 ; 299 +A3 02 ; 675 +B0 05 ; 1456 + +;; program + +; start: + +A2 01 ; LDX # 01 +A0 01 ; LDY # 01 + +; loop: + +; add16: + 18 ; CLC i + B5 00 ; LDA zp,x 00 + 79 00 00 ; ADC a,y 00 + 85 AE ; STA zp AE + B5 01 ; LDA zp,x 01 + 79 01 00 ; ADC a,y 01 + 85 AF ; STA zp AF + +; sub16 + 38 ; SEC i + A9 E4 ; LDA # E4 + E5 AE ; SBC zp AE + D0 09 ; BNE r next + A9 07 ; LDA # 07 + E5 AF ; SBC zp AF + D0 03 ; BNE r next + +; goto multiply + 4C 41 00 ; JMP a multiply + +; next + E8 ; INX i + E8 ; INX i + E4 00 ; CPX zp 00 + D0 DB ; BNE r loop + A2 01 ; LDX # 01 + C8 ; INY i + C8 ; INY i + C4 00 ; CPY zp 00 + D0 D3 ; BNE r loop + 4C 0D 00 ; JMP a start + +; multiply + + B5 00 ; LDA zp,x 00 + 85 AE ; STA zp AE + B5 01 ; LDA zp,x 01 + 85 AF ; STA zp AF + + B9 00 00 ; LDA a,y 00 + AA ; TAX i + B9 01 00 ; LDA a,y 01 + A8 ; TAY i + + A9 00 ; LDA # 00 + 85 A8 ; STA zp A8 + 85 A9 ; STA zp A9 + 85 AA ; STA zp AA + + ; add16_24 @ 59 + 18 ; CLC i + A5 A8 ; LDA zp A8 + 65 AE ; ADC zp AE + 85 A8 ; STA zp A8 + + A5 A9 ; LDA zp A9 + 65 AF ; ADC zp AF + 85 A9 ; STA zp A9 + + A5 AA ; LDA zp AA + 69 00 ; ADC # 00 + 85 AA ; STA zp AA + + E0 00 ; CPX # 00 + F0 04 ; BEQ r inc_y ; -> 74 + CA ; DEX i + 4C 59 00 ; JMP add16_24 ; -> 59 + ; inc_y + C0 00 ; CPY # 00 + F0 05 ; BEQ r end ; -> 7d + CA ; DEX i + 88 ; DEY i + 4C 59 00 ; JMP add16_24 ; -> 59 + + ; end + 38 ; SEC i + A5 A8 ; LDA zp A8 + E5 AE ; SBC zp AE + 85 A8 ; STA zp A8 + + A5 A9 ; LDA zp A9 + E5 AF ; SBC zp AF + 85 A9 ; STA zp A9 + +; forever: + 4C 8a 00 ; JMP a forever ; 8a diff --git a/text.c b/text.c new file mode 100644 index 0000000..485817a --- /dev/null +++ b/text.c @@ -0,0 +1,170 @@ +#include +#include + +#include + +#include +#include FT_FREETYPE_H + +typedef struct glyph_texture { + SDL_Texture * texture; + int advance; + int bearing_x; + int bearing_y; +} glyph_texture_t; + +static int line_height; + +static glyph_texture_t glyph_textures[256] = {0}; + +const char * printable = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ "; + +int create_instruction_textures(SDL_Renderer * renderer) +{ + FT_Library library; + FT_Face face; + FT_Error error; + + error = FT_Init_FreeType(&library); + if (error) { + fprintf(stderr, "FT_Init_FreeType\n"); + return -1; + } + + error = FT_New_Face(library, "/usr/share/fonts/TTF/DejaVuSansMono.ttf", 0, &face); + if (error) { + fprintf(stderr, "FT_New_Face\n"); + return -1; + } + + assert(FT_IS_FIXED_WIDTH(face)); + + error = FT_Set_Pixel_Sizes(face, 0, 32); + if (error) { + fprintf(stderr, "FT_Select_Size: %s %d\n", FT_Error_String(error), error); + return -1; + } + + line_height = (face->height * face->size->metrics.y_ppem) / face->units_per_EM; + + SDL_Surface * surface; + SDL_Texture * texture; + + unsigned long c; + int ix = 0; + + while ((c = printable[ix++]) != 0) { + unsigned int glyph_index = FT_Get_Char_Index(face, c); + + error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); + if (error) { + fprintf(stderr, "FT_Load_Glyph: %ld %s %d\n", c, FT_Error_String(error), error); + return -1; + } + error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); + if (error) { + fprintf(stderr, "FT_Render_Glyph: %ld %s %d\n", c, FT_Error_String(error), error); + return -1; + } + + assert(face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY); + + surface = SDL_CreateRGBSurface(0, + face->glyph->bitmap.width, + face->glyph->bitmap.rows, + 32, + 0xFF000000, + 0x00FF0000, + 0x0000FF00, + 0x000000FF); + assert(surface != NULL); + SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND); + + SDL_LockSurface(surface); + + for (unsigned int y = 0; y < face->glyph->bitmap.rows; y++) { + for (unsigned int x = 0; x < face->glyph->bitmap.width; x++) { + uint8_t alpha = ((uint8_t *)face->glyph->bitmap.buffer)[face->glyph->bitmap.pitch * y + x]; + ((uint32_t *)surface->pixels)[(surface->pitch / 4) * y + x] = 0xFFFFFF00 + alpha; + } + } + SDL_UnlockSurface(surface); + + texture = SDL_CreateTextureFromSurface(renderer, surface); + + SDL_FreeSurface(surface); + + glyph_textures[c] = (glyph_texture_t){ + .texture = texture, + .advance = face->glyph->metrics.horiAdvance / 64, + .bearing_x = face->glyph->metrics.horiBearingX / 64, + .bearing_y = face->glyph->metrics.horiBearingY / 64, + }; + } + + return 0; +} + +void ull_base16(void * buf, size_t len, unsigned long long n) +{ + while (len > 0) { + uint8_t ni = n & 0xf; + n >>= 4; + uint8_t c; + + switch (ni) { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x6: + case 0x7: + case 0x8: + case 0x9: + c = '0' + (ni - 0x0); + break; + case 0xA: + case 0xB: + case 0xC: + case 0xD: + case 0xE: + case 0xF: + c = 'A' + (ni - 0xA); + break; + default: + assert(0); + } + + ((uint8_t *)buf)[--len] = c; + } +} + +void render_text(SDL_Renderer * renderer, int col, int row, + const void * buf, int len, + uint8_t r, uint8_t g, uint8_t b) +{ + uint8_t c; + assert(buf != NULL); + int x_origin = col * glyph_textures[((uint8_t *)buf)[0]].advance; + int y_origin = row * line_height; + int x_offset = 0; + + SDL_Rect rect; + + for (int ix = 0; ix < len; ix++) { + c = ((uint8_t *)buf)[ix]; + assert(c != 0); + glyph_texture_t * glyph = &glyph_textures[c]; + + SDL_QueryTexture(glyph->texture, NULL, NULL, &rect.w, &rect.h); + rect.x = x_origin + x_offset + glyph->bearing_x; + rect.y = y_origin - glyph->bearing_y; + + SDL_SetTextureColorMod(glyph->texture, r, g, b); + SDL_RenderCopy(renderer, glyph->texture, NULL, &rect); + + x_offset += glyph->advance; + } +} diff --git a/text.h b/text.h new file mode 100644 index 0000000..e9614fc --- /dev/null +++ b/text.h @@ -0,0 +1,8 @@ +#pragma once + +int create_instruction_textures(SDL_Renderer * renderer); +void ull_base16(void * buf, size_t len, unsigned long long n); + +void render_text(SDL_Renderer * renderer, int col, int row, + const void * buf, int len, + uint8_t r, uint8_t g, uint8_t b);