This commit is contained in:
Zack Buhman 2023-06-15 17:08:20 +00:00
commit 79259bafa2
26 changed files with 2843 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.o
main

31
Makefile Normal file
View File

@ -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

19
convert.py Normal file
View File

@ -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;

234
cpu.c Normal file
View File

@ -0,0 +1,234 @@
#include <assert.h>
#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 },
};

42
cpu.h Normal file
View File

@ -0,0 +1,42 @@
#pragma once
#include <stdint.h>
#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);

30
debug-table.c Normal file
View File

@ -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);
}

944
fake6502.c Normal file
View File

@ -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 <stdio.h>
#include <stdint.h>
//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;
}
*/

17
input-convert.py Normal file
View File

@ -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}")

276
instruction.c Normal file
View File

@ -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},
};

8
instruction.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
typedef struct instruction {
int opcode;
int mode;
} instruction_t;
extern const struct instruction decode_ins[256];

29
main.c Normal file
View File

@ -0,0 +1,29 @@
#include <stdint.h>
#include <stdio.h>
#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();
}

120
mneumonic-strings.h Normal file
View File

@ -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 "?"

124
mneumonic.c Normal file
View File

@ -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,
};

7
mneumonic.h Normal file
View File

@ -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)

121
opcodes.h Normal file
View File

@ -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,
};

367
present.c Normal file
View File

@ -0,0 +1,367 @@
#include <stdint.h>
#include <assert.h>
#include <stdbool.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <SDL.h>
#include <SDL_events.h>
#include <SDL_rect.h>
#include <SDL_render.h>
#include <SDL_surface.h>
#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;
}

3
present.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
int present_main(void);

9
test/ana.asm Normal file
View File

@ -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

9
test/ana2.asm Normal file
View File

@ -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

6
test/bra.asm Normal file
View File

@ -0,0 +1,6 @@
BRA r :there
NOP i
NOP i
NOP i
there: LDA # 0
LDA # 1

25
test/foo.asm Normal file
View File

@ -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

14
test/instructions.asm Normal file
View File

@ -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 ;

122
test/program.asm Normal file
View File

@ -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

106
test/test.asm Normal file
View File

@ -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

170
text.c Normal file
View File

@ -0,0 +1,170 @@
#include <stdint.h>
#include <assert.h>
#include <SDL_render.h>
#include <ft2build.h>
#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;
}
}

8
text.h Normal file
View File

@ -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);