port more apps
This commit is contained in:
parent
673f8605ce
commit
a317d8cce1
23 changed files with 5097 additions and 4 deletions
|
@ -1,3 +1,3 @@
|
|||
NAME = coremark-bench
|
||||
SRCS = $(shell find -L ./src/ -name "*.c")
|
||||
SRCS = $(shell find src/ -name "*.c")
|
||||
include $(AM_HOME)/Makefile
|
||||
|
|
3
hello/Makefile
Normal file
3
hello/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
|||
NAME = hello-am
|
||||
SRCS = hello.c
|
||||
include $(AM_HOME)/Makefile
|
13
hello/hello.c
Normal file
13
hello/hello.c
Normal file
|
@ -0,0 +1,13 @@
|
|||
#include <am.h>
|
||||
#include <klib-macros.h>
|
||||
|
||||
int main(const char *args) {
|
||||
const char *fmt =
|
||||
"Hello, AbstractMachine!\n"
|
||||
"mainargs = '%'.\n";
|
||||
|
||||
for (const char *p = fmt; *p; p++) {
|
||||
(*p == '%') ? putstr(args) : putch(*p);
|
||||
}
|
||||
return 0;
|
||||
}
|
3
litenes/Makefile
Normal file
3
litenes/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
|||
NAME = litenes
|
||||
SRCS = $(shell find src/ -name "*.c") $(ROM_SRC)
|
||||
include $(AM_HOME)/Makefile
|
16
litenes/src/common.c
Normal file
16
litenes/src/common.c
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include "common.h"
|
||||
|
||||
bool common_bit_set(long long value, byte position) { return value & (1L << position); }
|
||||
|
||||
// I could do this through non-void methods with returns in one copy,
|
||||
// but this variant is slightly faster, and needs less typing in client code
|
||||
#define M_common(SUFFIX, TYPE) \
|
||||
void common_set_bit##SUFFIX(TYPE *variable, byte position) { *variable |= 1L << position; } \
|
||||
void common_unset_bit##SUFFIX(TYPE *variable, byte position) { *variable &= ~(1L << position); } \
|
||||
void common_toggle_bit##SUFFIX(TYPE *variable, byte position) { *variable ^= 1L << position; } \
|
||||
void common_modify_bit##SUFFIX(TYPE *variable, byte position, bool set) \
|
||||
{ set ? common_set_bit##SUFFIX(variable, position) : common_unset_bit##SUFFIX(variable, position); }
|
||||
|
||||
M_common(b, byte)
|
||||
M_common(w, word)
|
||||
M_common(d, dword)
|
33
litenes/src/common.h
Normal file
33
litenes/src/common.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#include <am.h>
|
||||
#include <klib.h>
|
||||
#include <klib-macros.h>
|
||||
|
||||
typedef uint8_t byte;
|
||||
typedef uint16_t word;
|
||||
typedef uint32_t dword;
|
||||
|
||||
// Binary Operations
|
||||
bool common_bit_set(long long value, byte position);
|
||||
|
||||
// Byte Bit Operations
|
||||
void common_set_bitb(byte *variable, byte position);
|
||||
void common_unset_bitb(byte *variable, byte position);
|
||||
void common_toggle_bitb(byte *variable, byte position);
|
||||
void common_modify_bitb(byte *variable, byte position, bool set);
|
||||
|
||||
// Word Bit Operations
|
||||
void common_set_bitw(word *variable, byte position);
|
||||
void common_unset_bitw(word *variable, byte position);
|
||||
void common_toggle_bitw(word *variable, byte position);
|
||||
void common_modify_bitw(word *variable, byte position, bool set);
|
||||
|
||||
// Double Word Bit Operations
|
||||
void common_set_bitd(dword *variable, byte position);
|
||||
void common_unset_bitd(dword *variable, byte position);
|
||||
void common_toggle_bitd(dword *variable, byte position);
|
||||
void common_modify_bitd(dword *variable, byte position, bool set);
|
||||
|
||||
#endif
|
101
litenes/src/cpu-addressing.c
Normal file
101
litenes/src/cpu-addressing.c
Normal file
|
@ -0,0 +1,101 @@
|
|||
#include "cpu.h"
|
||||
#include "cpu-internal.h"
|
||||
#include "memory.h"
|
||||
|
||||
// CPU Addressing Modes
|
||||
|
||||
void cpu_address_implied() { }
|
||||
|
||||
void cpu_address_immediate() {
|
||||
op_value = memory_readb(cpu.PC);
|
||||
cpu.PC++;
|
||||
}
|
||||
|
||||
void cpu_address_zero_page() {
|
||||
op_address = memory_readb(cpu.PC);
|
||||
op_value = CPU_RAM[op_address];
|
||||
cpu.PC++;
|
||||
}
|
||||
|
||||
void cpu_address_zero_page_x() {
|
||||
op_address = (memory_readb(cpu.PC) + cpu.X) & 0xFF;
|
||||
op_value = CPU_RAM[op_address];
|
||||
cpu.PC++;
|
||||
}
|
||||
|
||||
void cpu_address_zero_page_y() {
|
||||
op_address = (memory_readb(cpu.PC) + cpu.Y) & 0xFF;
|
||||
op_value = CPU_RAM[op_address];
|
||||
cpu.PC++;
|
||||
}
|
||||
|
||||
void cpu_address_absolute() {
|
||||
op_address = memory_readw(cpu.PC);
|
||||
op_value = memory_readb(op_address);
|
||||
cpu.PC += 2;
|
||||
}
|
||||
|
||||
void cpu_address_absolute_x() {
|
||||
op_address = memory_readw(cpu.PC) + cpu.X;
|
||||
op_value = memory_readb(op_address);
|
||||
cpu.PC += 2;
|
||||
|
||||
if ((op_address >> 8) != (cpu.PC >> 8)) {
|
||||
op_cycles++;
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_address_absolute_y() {
|
||||
op_address = (memory_readw(cpu.PC) + cpu.Y) & 0xFFFF;
|
||||
op_value = memory_readb(op_address);
|
||||
cpu.PC += 2;
|
||||
|
||||
if ((op_address >> 8) != (cpu.PC >> 8)) {
|
||||
op_cycles++;
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_address_relative() {
|
||||
op_address = memory_readb(cpu.PC);
|
||||
cpu.PC++;
|
||||
if (op_address & 0x80)
|
||||
op_address -= 0x100;
|
||||
op_address += cpu.PC;
|
||||
|
||||
if ((op_address >> 8) != (cpu.PC >> 8)) {
|
||||
op_cycles++;
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_address_indirect() {
|
||||
word arg_addr = memory_readw(cpu.PC);
|
||||
|
||||
// The famous 6502 bug when instead of reading from $C0FF/$C100 it reads from $C0FF/$C000
|
||||
if ((arg_addr & 0xFF) == 0xFF) {
|
||||
// Buggy code
|
||||
op_address = (memory_readb(arg_addr & 0xFF00) << 8) + memory_readb(arg_addr);
|
||||
}
|
||||
else {
|
||||
// Normal code
|
||||
op_address = memory_readw(arg_addr);
|
||||
}
|
||||
cpu.PC += 2;
|
||||
}
|
||||
|
||||
void cpu_address_indirect_x() {
|
||||
byte arg_addr = memory_readb(cpu.PC);
|
||||
op_address = (memory_readb((arg_addr + cpu.X + 1) & 0xFF) << 8) | memory_readb((arg_addr + cpu.X) & 0xFF);
|
||||
op_value = memory_readb(op_address);
|
||||
cpu.PC++;
|
||||
}
|
||||
|
||||
void cpu_address_indirect_y() {
|
||||
byte arg_addr = memory_readb(cpu.PC);
|
||||
op_address = (((memory_readb((arg_addr + 1) & 0xFF) << 8) | memory_readb(arg_addr)) + cpu.Y) & 0xFFFF;
|
||||
op_value = memory_readb(op_address);
|
||||
cpu.PC++;
|
||||
|
||||
if ((op_address >> 8) != (cpu.PC >> 8)) {
|
||||
op_cycles++;
|
||||
}
|
||||
}
|
63
litenes/src/cpu-internal.h
Normal file
63
litenes/src/cpu-internal.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
#ifndef CPU_INTERNAL_H
|
||||
#define CPU_INTERNAL_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
typedef enum {
|
||||
carry_flag = 0x01,
|
||||
zero_flag = 0x02,
|
||||
interrupt_flag = 0x04,
|
||||
decimal_flag = 0x08,
|
||||
break_flag = 0x10,
|
||||
unused_flag = 0x20,
|
||||
overflow_flag = 0x40,
|
||||
negative_flag = 0x80
|
||||
} cpu_p_flag;
|
||||
|
||||
typedef enum {
|
||||
carry_bp = 0,
|
||||
zero_bp = 1,
|
||||
interrupt_bp = 2,
|
||||
decimal_bp = 3,
|
||||
break_bp = 4,
|
||||
unused_bp = 5,
|
||||
overflow_bp = 6,
|
||||
negative_bp = 7
|
||||
} cpu_p_bp;
|
||||
|
||||
typedef struct {
|
||||
word PC; // Program Counter,
|
||||
byte SP; // Stack Pointer,
|
||||
byte A, X, Y; // Registers
|
||||
byte P; // Flag Register
|
||||
} CPU_STATE;
|
||||
|
||||
extern CPU_STATE cpu;
|
||||
|
||||
extern byte CPU_RAM[0x8000];
|
||||
extern int op_value, op_address; // Arguments for current instruction
|
||||
extern int op_cycles; // Additional instruction cycles used (e.g. when paging occurs)
|
||||
|
||||
byte cpu_ram_read(word address);
|
||||
void cpu_ram_write(word address, byte data);
|
||||
|
||||
// Interrupt Addresses
|
||||
word cpu_nmi_interrupt_address();
|
||||
word cpu_reset_interrupt_address();
|
||||
word cpu_irq_interrupt_address();
|
||||
|
||||
// CPU Adressing Modes
|
||||
void cpu_address_implied();
|
||||
void cpu_address_immediate();
|
||||
void cpu_address_zero_page();
|
||||
void cpu_address_zero_page_x();
|
||||
void cpu_address_zero_page_y();
|
||||
void cpu_address_absolute();
|
||||
void cpu_address_absolute_x();
|
||||
void cpu_address_absolute_y();
|
||||
void cpu_address_relative();
|
||||
void cpu_address_indirect();
|
||||
void cpu_address_indirect_x();
|
||||
void cpu_address_indirect_y();
|
||||
|
||||
#endif
|
606
litenes/src/cpu.c
Normal file
606
litenes/src/cpu.c
Normal file
|
@ -0,0 +1,606 @@
|
|||
#include "cpu.h"
|
||||
#include "cpu-internal.h"
|
||||
#include "memory.h"
|
||||
#include "ppu.h"
|
||||
#include "common.h"
|
||||
|
||||
CPU_STATE cpu;
|
||||
|
||||
// CPU Memory
|
||||
|
||||
byte CPU_RAM[0x8000];
|
||||
|
||||
byte cpu_ram_read(word address) {
|
||||
return CPU_RAM[address & 0x7FF];
|
||||
}
|
||||
|
||||
void cpu_ram_write(word address, byte data) {
|
||||
CPU_RAM[address & 0x7FF] = data;
|
||||
}
|
||||
|
||||
static byte op_code; // Current instruction code
|
||||
int op_value, op_address; // Arguments for current instruction
|
||||
int op_cycles; // Additional instruction cycles used (e.g. when paging occurs)
|
||||
static unsigned long long cpu_cycles; // Total CPU Cycles Since Power Up (wraps)
|
||||
|
||||
static void (*cpu_op_address_mode[256])(); // Array of address modes
|
||||
static void (*cpu_op_handler[256])(); // Array of instruction function pointers
|
||||
static bool cpu_op_in_base_instruction_set[256]; // true if instruction is in base 6502 instruction set
|
||||
static char *cpu_op_name[256]; // Instruction names
|
||||
static int cpu_op_cycles[256]; // CPU cycles used by instructions
|
||||
|
||||
static const byte cpu_zn_flag_table[256] = {
|
||||
zero_flag,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
|
||||
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
|
||||
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
|
||||
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
|
||||
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
|
||||
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
|
||||
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
|
||||
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
|
||||
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
|
||||
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
|
||||
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
|
||||
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
|
||||
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
|
||||
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
|
||||
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
|
||||
negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,negative_flag,
|
||||
};
|
||||
|
||||
// Interrupt Addresses
|
||||
|
||||
word cpu_nmi_interrupt_address() { return memory_readw(0xFFFA); }
|
||||
word cpu_reset_interrupt_address() { return memory_readw(0xFFFC); }
|
||||
word cpu_irq_interrupt_address() { return memory_readw(0xFFFE); }
|
||||
|
||||
// Stack Routines
|
||||
|
||||
void cpu_stack_pushb(byte data) { memory_writeb(0x100 + cpu.SP--, data); }
|
||||
void cpu_stack_pushw(word data) { memory_writew(0xFF + cpu.SP, data); cpu.SP -= 2; }
|
||||
byte cpu_stack_popb() { return memory_readb(0x100 + ++cpu.SP); }
|
||||
word cpu_stack_popw() { cpu.SP += 2; return memory_readw(0xFF + cpu.SP); }
|
||||
|
||||
// CPU Instructions
|
||||
|
||||
void ____FE____() { /* Instruction for future Extension */ }
|
||||
|
||||
#define cpu_flag_set(flag) common_bit_set(cpu.P, flag)
|
||||
#define cpu_modify_flag(flag, value) common_modify_bitb(&cpu.P, flag, value)
|
||||
#define cpu_set_flag(flag) common_set_bitb(&cpu.P, flag)
|
||||
#define cpu_unset_flag(flag) common_unset_bitb(&cpu.P, flag)
|
||||
|
||||
#define cpu_update_zn_flags(value) cpu.P = (cpu.P & ~(zero_flag | negative_flag)) | cpu_zn_flag_table[value]
|
||||
|
||||
#define cpu_branch(flag) if (flag) cpu.PC = op_address;
|
||||
#define cpu_compare(reg) \
|
||||
int result = reg - op_value; \
|
||||
cpu_modify_flag(carry_bp, result >= 0); \
|
||||
cpu_modify_flag(zero_bp, result == 0); \
|
||||
cpu_modify_flag(negative_bp, (result >> 7) & 1);
|
||||
|
||||
|
||||
// CPU Instructions
|
||||
|
||||
// NOP
|
||||
|
||||
void cpu_op_nop() {}
|
||||
|
||||
// Addition
|
||||
|
||||
void cpu_op_adc() {
|
||||
int result = cpu.A + op_value + (cpu_flag_set(carry_bp) ? 1 : 0);
|
||||
cpu_modify_flag(carry_bp, !!(result & 0x100));
|
||||
cpu_modify_flag(overflow_bp, !!(~(cpu.A ^ op_value) & (cpu.A ^ result) & 0x80));
|
||||
cpu.A = result & 0xFF;
|
||||
cpu_update_zn_flags(cpu.A);
|
||||
}
|
||||
|
||||
// Subtraction
|
||||
|
||||
void cpu_op_sbc() {
|
||||
int result = cpu.A - op_value - (cpu_flag_set(carry_bp) ? 0 : 1);
|
||||
cpu_modify_flag(carry_bp, !(result & 0x100));
|
||||
cpu_modify_flag(overflow_bp, !!((cpu.A ^ op_value) & (cpu.A ^ result) & 0x80));
|
||||
cpu.A = result & 0xFF;
|
||||
cpu_update_zn_flags(cpu.A);
|
||||
}
|
||||
|
||||
// Bit Manipulation Operations
|
||||
|
||||
void cpu_op_and() { cpu_update_zn_flags(cpu.A &= op_value); }
|
||||
void cpu_op_bit() {
|
||||
cpu_modify_flag(zero_bp, !(cpu.A & op_value));
|
||||
cpu.P = (cpu.P & 0x3F) | (0xC0 & op_value);
|
||||
}
|
||||
void cpu_op_eor() { cpu_update_zn_flags(cpu.A ^= op_value); }
|
||||
void cpu_op_ora() { cpu_update_zn_flags(cpu.A |= op_value); }
|
||||
void cpu_op_asla() {
|
||||
cpu_modify_flag(carry_bp, cpu.A & 0x80);
|
||||
cpu.A <<= 1;
|
||||
cpu_update_zn_flags(cpu.A);
|
||||
}
|
||||
void cpu_op_asl() {
|
||||
cpu_modify_flag(carry_bp, op_value & 0x80);
|
||||
op_value <<= 1;
|
||||
op_value &= 0xFF;
|
||||
cpu_update_zn_flags(op_value);
|
||||
memory_writeb(op_address, op_value);
|
||||
}
|
||||
void cpu_op_lsra() {
|
||||
int value = cpu.A >> 1;
|
||||
cpu_modify_flag(carry_bp, cpu.A & 0x01);
|
||||
cpu.A = value & 0xFF;
|
||||
cpu_update_zn_flags(value);
|
||||
}
|
||||
void cpu_op_lsr() {
|
||||
cpu_modify_flag(carry_bp, op_value & 0x01);
|
||||
op_value >>= 1;
|
||||
op_value &= 0xFF;
|
||||
memory_writeb(op_address, op_value);
|
||||
cpu_update_zn_flags(op_value);
|
||||
}
|
||||
|
||||
void cpu_op_rola() {
|
||||
int value = cpu.A << 1;
|
||||
value |= cpu_flag_set(carry_bp) ? 1 : 0;
|
||||
cpu_modify_flag(carry_bp, value > 0xFF);
|
||||
cpu.A = value & 0xFF;
|
||||
cpu_update_zn_flags(cpu.A);
|
||||
}
|
||||
void cpu_op_rol() {
|
||||
op_value <<= 1;
|
||||
op_value |= cpu_flag_set(carry_bp) ? 1 : 0;
|
||||
cpu_modify_flag(carry_bp, op_value > 0xFF);
|
||||
op_value &= 0xFF;
|
||||
memory_writeb(op_address, op_value);
|
||||
cpu_update_zn_flags(op_value);
|
||||
}
|
||||
void cpu_op_rora() {
|
||||
unsigned char carry = cpu_flag_set(carry_bp);
|
||||
cpu_modify_flag(carry_bp, cpu.A & 0x01);
|
||||
cpu.A = (cpu.A >> 1) | (carry << 7);
|
||||
cpu_modify_flag(zero_bp, cpu.A == 0);
|
||||
cpu_modify_flag(negative_bp, !!carry);
|
||||
}
|
||||
void cpu_op_ror() {
|
||||
unsigned char carry = cpu_flag_set(carry_bp);
|
||||
cpu_modify_flag(carry_bp, op_value & 0x01);
|
||||
op_value = ((op_value >> 1) | (carry << 7)) & 0xFF;
|
||||
cpu_modify_flag(zero_bp, op_value == 0);
|
||||
cpu_modify_flag(negative_bp, !!carry);
|
||||
memory_writeb(op_address, op_value);
|
||||
}
|
||||
|
||||
// Loading
|
||||
|
||||
void cpu_op_lda() { cpu_update_zn_flags(cpu.A = op_value); }
|
||||
void cpu_op_ldx() { cpu_update_zn_flags(cpu.X = op_value); }
|
||||
void cpu_op_ldy() { cpu_update_zn_flags(cpu.Y = op_value); }
|
||||
|
||||
// Storing
|
||||
|
||||
void cpu_op_sta() { memory_writeb(op_address, cpu.A); }
|
||||
void cpu_op_stx() { memory_writeb(op_address, cpu.X); }
|
||||
void cpu_op_sty() { memory_writeb(op_address, cpu.Y); }
|
||||
|
||||
// Transfering
|
||||
|
||||
void cpu_op_tax() { cpu_update_zn_flags(cpu.X = cpu.A); }
|
||||
void cpu_op_txa() { cpu_update_zn_flags(cpu.A = cpu.X); }
|
||||
void cpu_op_tay() { cpu_update_zn_flags(cpu.Y = cpu.A); }
|
||||
void cpu_op_tya() { cpu_update_zn_flags(cpu.A = cpu.Y); }
|
||||
void cpu_op_tsx() { cpu_update_zn_flags(cpu.X = cpu.SP); }
|
||||
void cpu_op_txs() { cpu.SP = cpu.X; }
|
||||
|
||||
// Branching Positive
|
||||
|
||||
void cpu_op_bcs() { cpu_branch(cpu_flag_set(carry_bp)); }
|
||||
void cpu_op_beq() { cpu_branch(cpu_flag_set(zero_bp)); }
|
||||
void cpu_op_bmi() { cpu_branch(cpu_flag_set(negative_bp)); }
|
||||
void cpu_op_bvs() { cpu_branch(cpu_flag_set(overflow_bp)); }
|
||||
|
||||
// Branching Negative
|
||||
|
||||
void cpu_op_bne() { cpu_branch(!cpu_flag_set(zero_bp)); }
|
||||
void cpu_op_bcc() { cpu_branch(!cpu_flag_set(carry_bp)); }
|
||||
void cpu_op_bpl() { cpu_branch(!cpu_flag_set(negative_bp)); }
|
||||
void cpu_op_bvc() { cpu_branch(!cpu_flag_set(overflow_bp)); }
|
||||
|
||||
// Jumping
|
||||
|
||||
void cpu_op_jmp() { cpu.PC = op_address; }
|
||||
|
||||
// Subroutines
|
||||
|
||||
void cpu_op_jsr() { cpu_stack_pushw(cpu.PC - 1); cpu.PC = op_address; }
|
||||
void cpu_op_rts() { cpu.PC = cpu_stack_popw() + 1; }
|
||||
|
||||
// Interruptions
|
||||
|
||||
void cpu_op_brk() {
|
||||
cpu_stack_pushw(cpu.PC - 1);
|
||||
cpu_stack_pushb(cpu.P);
|
||||
cpu.P |= unused_flag | break_flag;
|
||||
cpu.PC = cpu_nmi_interrupt_address();
|
||||
}
|
||||
void cpu_op_rti() { cpu.P = cpu_stack_popb() | unused_flag; cpu.PC = cpu_stack_popw(); }
|
||||
|
||||
// Flags
|
||||
|
||||
void cpu_op_clc() { cpu_unset_flag(carry_bp); }
|
||||
void cpu_op_cld() { cpu_unset_flag(decimal_bp); }
|
||||
void cpu_op_cli() { cpu_unset_flag(interrupt_bp); }
|
||||
void cpu_op_clv() { cpu_unset_flag(overflow_bp); }
|
||||
void cpu_op_sec() { cpu_set_flag(carry_bp); }
|
||||
void cpu_op_sed() { cpu_set_flag(decimal_bp); }
|
||||
void cpu_op_sei() { cpu_set_flag(interrupt_bp); }
|
||||
|
||||
// Comparison
|
||||
|
||||
void cpu_op_cmp() { cpu_compare(cpu.A); }
|
||||
void cpu_op_cpx() { cpu_compare(cpu.X); }
|
||||
void cpu_op_cpy() { cpu_compare(cpu.Y); }
|
||||
|
||||
// Increment
|
||||
|
||||
void cpu_op_inc() {
|
||||
byte result = op_value + 1;
|
||||
memory_writeb(op_address, result);
|
||||
cpu_update_zn_flags(result);
|
||||
}
|
||||
void cpu_op_inx() { cpu_update_zn_flags(++cpu.X); }
|
||||
void cpu_op_iny() { cpu_update_zn_flags(++cpu.Y); }
|
||||
|
||||
// Decrement
|
||||
|
||||
void cpu_op_dec() {
|
||||
byte result = op_value - 1;
|
||||
memory_writeb(op_address, result);
|
||||
cpu_update_zn_flags(result);
|
||||
}
|
||||
void cpu_op_dex() { cpu_update_zn_flags(--cpu.X); }
|
||||
void cpu_op_dey() { cpu_update_zn_flags(--cpu.Y); }
|
||||
|
||||
// Stack
|
||||
|
||||
void cpu_op_php() { cpu_stack_pushb(cpu.P | 0x30); }
|
||||
void cpu_op_pha() { cpu_stack_pushb(cpu.A); }
|
||||
void cpu_op_pla() { cpu.A = cpu_stack_popb(); cpu_update_zn_flags(cpu.A); }
|
||||
void cpu_op_plp() { cpu.P = (cpu_stack_popb() & 0xEF) | 0x20; }
|
||||
|
||||
|
||||
// Extended Instruction Set
|
||||
|
||||
void cpu_op_aso() { cpu_op_asl(); cpu_op_ora(); }
|
||||
void cpu_op_axa() { memory_writeb(op_address, cpu.A & cpu.X & (op_address >> 8)); }
|
||||
void cpu_op_axs() { memory_writeb(op_address, cpu.A & cpu.X); }
|
||||
void cpu_op_dcm()
|
||||
{
|
||||
op_value--;
|
||||
op_value &= 0xFF;
|
||||
memory_writeb(op_address, op_value);
|
||||
cpu_op_cmp();
|
||||
}
|
||||
void cpu_op_ins()
|
||||
{
|
||||
op_value = (op_value + 1) & 0xFF;
|
||||
memory_writeb(op_address, op_value);
|
||||
cpu_op_sbc();
|
||||
}
|
||||
void cpu_op_lax() { cpu_update_zn_flags(cpu.A = cpu.X = op_value); }
|
||||
void cpu_op_lse() { cpu_op_lsr(); cpu_op_eor(); }
|
||||
void cpu_op_rla() { cpu_op_rol(); cpu_op_and(); }
|
||||
void cpu_op_rra() { cpu_op_ror(); cpu_op_adc(); }
|
||||
|
||||
|
||||
// Base 6502 instruction set
|
||||
|
||||
#define CPU_OP_BIS(o, c, f, n, a) \
|
||||
cpu_op_cycles[0x##o] = c; \
|
||||
cpu_op_handler[0x##o] = cpu_op_##f; \
|
||||
cpu_op_name[0x##o] = n; \
|
||||
cpu_op_address_mode[0x##o] = cpu_address_##a; \
|
||||
cpu_op_in_base_instruction_set[0x##o] = true;
|
||||
|
||||
// Not implemented instructions
|
||||
|
||||
#define CPU_OP_NII(o, a) \
|
||||
cpu_op_cycles[0x##o] = 1; \
|
||||
cpu_op_handler[0x##o] = ____FE____; \
|
||||
cpu_op_name[0x##o] = "NOP"; \
|
||||
cpu_op_address_mode[0x##o] = cpu_address_##a; \
|
||||
cpu_op_in_base_instruction_set[0x##o] = false;
|
||||
|
||||
// Extended instruction set found in other CPUs and implemented for compatibility
|
||||
|
||||
#define CPU_OP_EIS(o, c, f, n, a) \
|
||||
cpu_op_cycles[0x##o] = c; \
|
||||
cpu_op_handler[0x##o] = cpu_op_##f; \
|
||||
cpu_op_name[0x##o] = n; \
|
||||
cpu_op_address_mode[0x##o] = cpu_address_##a; \
|
||||
cpu_op_in_base_instruction_set[0x##o] = false;
|
||||
|
||||
// CPU Lifecycle
|
||||
|
||||
void cpu_init() {
|
||||
CPU_OP_BIS(00, 7, brk, "BRK", implied)
|
||||
CPU_OP_BIS(01, 6, ora, "ORA", indirect_x)
|
||||
CPU_OP_BIS(05, 3, ora, "ORA", zero_page)
|
||||
CPU_OP_BIS(06, 5, asl, "ASL", zero_page)
|
||||
CPU_OP_BIS(08, 3, php, "PHP", implied)
|
||||
CPU_OP_BIS(09, 2, ora, "ORA", immediate)
|
||||
CPU_OP_BIS(0A, 2, asla,"ASL", implied)
|
||||
CPU_OP_BIS(0D, 4, ora, "ORA", absolute)
|
||||
CPU_OP_BIS(0E, 6, asl, "ASL", absolute)
|
||||
CPU_OP_BIS(10, 2, bpl, "BPL", relative)
|
||||
CPU_OP_BIS(11, 5, ora, "ORA", indirect_y)
|
||||
CPU_OP_BIS(15, 4, ora, "ORA", zero_page_x)
|
||||
CPU_OP_BIS(16, 6, asl, "ASL", zero_page_x)
|
||||
CPU_OP_BIS(18, 2, clc, "CLC", implied)
|
||||
CPU_OP_BIS(19, 4, ora, "ORA", absolute_y)
|
||||
CPU_OP_BIS(1D, 4, ora, "ORA", absolute_x)
|
||||
CPU_OP_BIS(1E, 7, asl, "ASL", absolute_x)
|
||||
CPU_OP_BIS(20, 6, jsr, "JSR", absolute)
|
||||
CPU_OP_BIS(21, 6, and, "AND", indirect_x)
|
||||
CPU_OP_BIS(24, 3, bit, "BIT", zero_page)
|
||||
CPU_OP_BIS(25, 3, and, "AND", zero_page)
|
||||
CPU_OP_BIS(26, 5, rol, "ROL", zero_page)
|
||||
CPU_OP_BIS(28, 4, plp, "PLP", implied)
|
||||
CPU_OP_BIS(29, 2, and, "AND", immediate)
|
||||
CPU_OP_BIS(2A, 2, rola,"ROL", implied)
|
||||
CPU_OP_BIS(2C, 4, bit, "BIT", absolute)
|
||||
CPU_OP_BIS(2D, 2, and, "AND", absolute)
|
||||
CPU_OP_BIS(2E, 6, rol, "ROL", absolute)
|
||||
CPU_OP_BIS(30, 2, bmi, "BMI", relative)
|
||||
CPU_OP_BIS(31, 5, and, "AND", indirect_y)
|
||||
CPU_OP_BIS(35, 4, and, "AND", zero_page_x)
|
||||
CPU_OP_BIS(36, 6, rol, "ROL", zero_page_x)
|
||||
CPU_OP_BIS(38, 2, sec, "SEC", implied)
|
||||
CPU_OP_BIS(39, 4, and, "AND", absolute_y)
|
||||
CPU_OP_BIS(3D, 4, and, "AND", absolute_x)
|
||||
CPU_OP_BIS(3E, 7, rol, "ROL", absolute_x)
|
||||
CPU_OP_BIS(40, 6, rti, "RTI", implied)
|
||||
CPU_OP_BIS(41, 6, eor, "EOR", indirect_x)
|
||||
CPU_OP_BIS(45, 3, eor, "EOR", zero_page)
|
||||
CPU_OP_BIS(46, 5, lsr, "LSR", zero_page)
|
||||
CPU_OP_BIS(48, 3, pha, "PHA", implied)
|
||||
CPU_OP_BIS(49, 2, eor, "EOR", immediate)
|
||||
CPU_OP_BIS(4A, 2, lsra,"LSR", implied)
|
||||
CPU_OP_BIS(4C, 3, jmp, "JMP", absolute)
|
||||
CPU_OP_BIS(4D, 4, eor, "EOR", absolute)
|
||||
CPU_OP_BIS(4E, 6, lsr, "LSR", absolute)
|
||||
CPU_OP_BIS(50, 2, bvc, "BVC", relative)
|
||||
CPU_OP_BIS(51, 5, eor, "EOR", indirect_y)
|
||||
CPU_OP_BIS(55, 4, eor, "EOR", zero_page_x)
|
||||
CPU_OP_BIS(56, 6, lsr, "LSR", zero_page_x)
|
||||
CPU_OP_BIS(58, 2, cli, "CLI", implied)
|
||||
CPU_OP_BIS(59, 4, eor, "EOR", absolute_y)
|
||||
CPU_OP_BIS(5D, 4, eor, "EOR", absolute_x)
|
||||
CPU_OP_BIS(5E, 7, lsr, "LSR", absolute_x)
|
||||
CPU_OP_BIS(60, 6, rts, "RTS", implied)
|
||||
CPU_OP_BIS(61, 6, adc, "ADC", indirect_x)
|
||||
CPU_OP_BIS(65, 3, adc, "ADC", zero_page)
|
||||
CPU_OP_BIS(66, 5, ror, "ROR", zero_page)
|
||||
CPU_OP_BIS(68, 4, pla, "PLA", implied)
|
||||
CPU_OP_BIS(69, 2, adc, "ADC", immediate)
|
||||
CPU_OP_BIS(6A, 2, rora,"ROR", implied)
|
||||
CPU_OP_BIS(6C, 5, jmp, "JMP", indirect)
|
||||
CPU_OP_BIS(6D, 4, adc, "ADC", absolute)
|
||||
CPU_OP_BIS(6E, 6, ror, "ROR", absolute)
|
||||
CPU_OP_BIS(70, 2, bvs, "BVS", relative)
|
||||
CPU_OP_BIS(71, 5, adc, "ADC", indirect_y)
|
||||
CPU_OP_BIS(75, 4, adc, "ADC", zero_page_x)
|
||||
CPU_OP_BIS(76, 6, ror, "ROR", zero_page_x)
|
||||
CPU_OP_BIS(78, 2, sei, "SEI", implied)
|
||||
CPU_OP_BIS(79, 4, adc, "ADC", absolute_y)
|
||||
CPU_OP_BIS(7D, 4, adc, "ADC", absolute_x)
|
||||
CPU_OP_BIS(7E, 7, ror, "ROR", absolute_x)
|
||||
CPU_OP_BIS(81, 6, sta, "STA", indirect_x)
|
||||
CPU_OP_BIS(84, 3, sty, "STY", zero_page)
|
||||
CPU_OP_BIS(85, 3, sta, "STA", zero_page)
|
||||
CPU_OP_BIS(86, 3, stx, "STX", zero_page)
|
||||
CPU_OP_BIS(88, 2, dey, "DEY", implied)
|
||||
CPU_OP_BIS(8A, 2, txa, "TXA", implied)
|
||||
CPU_OP_BIS(8C, 4, sty, "STY", absolute)
|
||||
CPU_OP_BIS(8D, 4, sta, "STA", absolute)
|
||||
CPU_OP_BIS(8E, 4, stx, "STX", absolute)
|
||||
CPU_OP_BIS(90, 2, bcc, "BCC", relative)
|
||||
CPU_OP_BIS(91, 6, sta, "STA", indirect_y)
|
||||
CPU_OP_BIS(94, 4, sty, "STY", zero_page_x)
|
||||
CPU_OP_BIS(95, 4, sta, "STA", zero_page_x)
|
||||
CPU_OP_BIS(96, 4, stx, "STX", zero_page_y)
|
||||
CPU_OP_BIS(98, 2, tya, "TYA", implied)
|
||||
CPU_OP_BIS(99, 5, sta, "STA", absolute_y)
|
||||
CPU_OP_BIS(9A, 2, txs, "TXS", implied)
|
||||
CPU_OP_BIS(9D, 5, sta, "STA", absolute_x)
|
||||
CPU_OP_BIS(A0, 2, ldy, "LDY", immediate)
|
||||
CPU_OP_BIS(A1, 6, lda, "LDA", indirect_x)
|
||||
CPU_OP_BIS(A2, 2, ldx, "LDX", immediate)
|
||||
CPU_OP_BIS(A4, 3, ldy, "LDY", zero_page)
|
||||
CPU_OP_BIS(A5, 3, lda, "LDA", zero_page)
|
||||
CPU_OP_BIS(A6, 3, ldx, "LDX", zero_page)
|
||||
CPU_OP_BIS(A8, 2, tay, "TAY", implied)
|
||||
CPU_OP_BIS(A9, 2, lda, "LDA", immediate)
|
||||
CPU_OP_BIS(AA, 2, tax, "TAX", implied)
|
||||
CPU_OP_BIS(AC, 4, ldy, "LDY", absolute)
|
||||
CPU_OP_BIS(AD, 4, lda, "LDA", absolute)
|
||||
CPU_OP_BIS(AE, 4, ldx, "LDX", absolute)
|
||||
CPU_OP_BIS(B0, 2, bcs, "BCS", relative)
|
||||
CPU_OP_BIS(B1, 5, lda, "LDA", indirect_y)
|
||||
CPU_OP_BIS(B4, 4, ldy, "LDY", zero_page_x)
|
||||
CPU_OP_BIS(B5, 4, lda, "LDA", zero_page_x)
|
||||
CPU_OP_BIS(B6, 4, ldx, "LDX", zero_page_y)
|
||||
CPU_OP_BIS(B8, 2, clv, "CLV", implied)
|
||||
CPU_OP_BIS(B9, 4, lda, "LDA", absolute_y)
|
||||
CPU_OP_BIS(BA, 2, tsx, "TSX", implied)
|
||||
CPU_OP_BIS(BC, 4, ldy, "LDY", absolute_x)
|
||||
CPU_OP_BIS(BD, 4, lda, "LDA", absolute_x)
|
||||
CPU_OP_BIS(BE, 4, ldx, "LDX", absolute_y)
|
||||
CPU_OP_BIS(C0, 2, cpy, "CPY", immediate)
|
||||
CPU_OP_BIS(C1, 6, cmp, "CMP", indirect_x)
|
||||
CPU_OP_BIS(C4, 3, cpy, "CPY", zero_page)
|
||||
CPU_OP_BIS(C5, 3, cmp, "CMP", zero_page)
|
||||
CPU_OP_BIS(C6, 5, dec, "DEC", zero_page)
|
||||
CPU_OP_BIS(C8, 2, iny, "INY", implied)
|
||||
CPU_OP_BIS(C9, 2, cmp, "CMP", immediate)
|
||||
CPU_OP_BIS(CA, 2, dex, "DEX", implied)
|
||||
CPU_OP_BIS(CC, 4, cpy, "CPY", absolute)
|
||||
CPU_OP_BIS(CD, 4, cmp, "CMP", absolute)
|
||||
CPU_OP_BIS(CE, 6, dec, "DEC", absolute)
|
||||
CPU_OP_BIS(D0, 2, bne, "BNE", relative)
|
||||
CPU_OP_BIS(D1, 5, cmp, "CMP", indirect_y)
|
||||
CPU_OP_BIS(D5, 4, cmp, "CMP", zero_page_x)
|
||||
CPU_OP_BIS(D6, 6, dec, "DEC", zero_page_x)
|
||||
CPU_OP_BIS(D8, 2, cld, "CLD", implied)
|
||||
CPU_OP_BIS(D9, 4, cmp, "CMP", absolute_y)
|
||||
CPU_OP_BIS(DD, 4, cmp, "CMP", absolute_x)
|
||||
CPU_OP_BIS(DE, 7, dec, "DEC", absolute_x)
|
||||
CPU_OP_BIS(E0, 2, cpx, "CPX", immediate)
|
||||
CPU_OP_BIS(E1, 6, sbc, "SBC", indirect_x)
|
||||
CPU_OP_BIS(E4, 3, cpx, "CPX", zero_page)
|
||||
CPU_OP_BIS(E5, 3, sbc, "SBC", zero_page)
|
||||
CPU_OP_BIS(E6, 5, inc, "INC", zero_page)
|
||||
CPU_OP_BIS(E8, 2, inx, "INX", implied)
|
||||
CPU_OP_BIS(E9, 2, sbc, "SBC", immediate)
|
||||
CPU_OP_BIS(EA, 2, nop, "NOP", implied)
|
||||
CPU_OP_BIS(EC, 4, cpx, "CPX", absolute)
|
||||
CPU_OP_BIS(ED, 4, sbc, "SBC", absolute)
|
||||
CPU_OP_BIS(EE, 6, inc, "INC", absolute)
|
||||
CPU_OP_BIS(F0, 2, beq, "BEQ", relative)
|
||||
CPU_OP_BIS(F1, 5, sbc, "SBC", indirect_y)
|
||||
CPU_OP_BIS(F5, 4, sbc, "SBC", zero_page_x)
|
||||
CPU_OP_BIS(F6, 6, inc, "INC", zero_page_x)
|
||||
CPU_OP_BIS(F8, 2, sed, "SED", implied)
|
||||
CPU_OP_BIS(F9, 4, sbc, "SBC", absolute_y)
|
||||
CPU_OP_BIS(FD, 4, sbc, "SBC", absolute_x)
|
||||
CPU_OP_BIS(FE, 7, inc, "INC", absolute_x)
|
||||
|
||||
CPU_OP_EIS(03, 8, aso, "SLO", indirect_x)
|
||||
CPU_OP_EIS(07, 5, aso, "SLO", zero_page)
|
||||
CPU_OP_EIS(0F, 6, aso, "SLO", absolute)
|
||||
CPU_OP_EIS(13, 8, aso, "SLO", indirect_y)
|
||||
CPU_OP_EIS(17, 6, aso, "SLO", zero_page_x)
|
||||
CPU_OP_EIS(1B, 7, aso, "SLO", absolute_y)
|
||||
CPU_OP_EIS(1F, 7, aso, "SLO", absolute_x)
|
||||
CPU_OP_EIS(23, 8, rla, "RLA", indirect_x)
|
||||
CPU_OP_EIS(27, 5, rla, "RLA", zero_page)
|
||||
CPU_OP_EIS(2F, 6, rla, "RLA", absolute)
|
||||
CPU_OP_EIS(33, 8, rla, "RLA", indirect_y)
|
||||
CPU_OP_EIS(37, 6, rla, "RLA", zero_page_x)
|
||||
CPU_OP_EIS(3B, 7, rla, "RLA", absolute_y)
|
||||
CPU_OP_EIS(3F, 7, rla, "RLA", absolute_x)
|
||||
CPU_OP_EIS(43, 8, lse, "SRE", indirect_x)
|
||||
CPU_OP_EIS(47, 5, lse, "SRE", zero_page)
|
||||
CPU_OP_EIS(4F, 6, lse, "SRE", absolute)
|
||||
CPU_OP_EIS(53, 8, lse, "SRE", indirect_y)
|
||||
CPU_OP_EIS(57, 6, lse, "SRE", zero_page_x)
|
||||
CPU_OP_EIS(5B, 7, lse, "SRE", absolute_y)
|
||||
CPU_OP_EIS(5F, 7, lse, "SRE", absolute_x)
|
||||
CPU_OP_EIS(63, 8, rra, "RRA", indirect_x)
|
||||
CPU_OP_EIS(67, 5, rra, "RRA", zero_page)
|
||||
CPU_OP_EIS(6F, 6, rra, "RRA", absolute)
|
||||
CPU_OP_EIS(73, 8, rra, "RRA", indirect_y)
|
||||
CPU_OP_EIS(77, 6, rra, "RRA", zero_page_x)
|
||||
CPU_OP_EIS(7B, 7, rra, "RRA", absolute_y)
|
||||
CPU_OP_EIS(7F, 7, rra, "RRA", absolute_x)
|
||||
CPU_OP_EIS(83, 6, axs, "SAX", indirect_x)
|
||||
CPU_OP_EIS(87, 3, axs, "SAX", zero_page)
|
||||
CPU_OP_EIS(8F, 4, axs, "SAX", absolute)
|
||||
CPU_OP_EIS(93, 6, axa, "SAX", indirect_y)
|
||||
CPU_OP_EIS(97, 4, axs, "SAX", zero_page_y)
|
||||
CPU_OP_EIS(9F, 5, axa, "SAX", absolute_y)
|
||||
CPU_OP_EIS(A3, 6, lax, "LAX", indirect_x)
|
||||
CPU_OP_EIS(A7, 3, lax, "LAX", zero_page)
|
||||
CPU_OP_EIS(AF, 4, lax, "LAX", absolute)
|
||||
CPU_OP_EIS(B3, 5, lax, "LAX", indirect_y)
|
||||
CPU_OP_EIS(B7, 4, lax, "LAX", zero_page_y)
|
||||
CPU_OP_EIS(BF, 4, lax, "LAX", absolute_y)
|
||||
CPU_OP_EIS(C3, 8, dcm, "DCP", indirect_x)
|
||||
CPU_OP_EIS(C7, 5, dcm, "DCP", zero_page)
|
||||
CPU_OP_EIS(CF, 6, dcm, "DCP", absolute)
|
||||
CPU_OP_EIS(D3, 8, dcm, "DCP", indirect_y)
|
||||
CPU_OP_EIS(D7, 6, dcm, "DCP", zero_page_x)
|
||||
CPU_OP_EIS(DB, 7, dcm, "DCP", absolute_y)
|
||||
CPU_OP_EIS(DF, 7, dcm, "DCP", absolute_x)
|
||||
CPU_OP_EIS(E3, 8, ins, "ISB", indirect_x)
|
||||
CPU_OP_EIS(E7, 5, ins, "ISB", zero_page)
|
||||
CPU_OP_EIS(EB, 2, sbc, "SBC", immediate)
|
||||
CPU_OP_EIS(EF, 6, ins, "ISB", absolute)
|
||||
CPU_OP_EIS(F3, 8, ins, "ISB", indirect_y)
|
||||
CPU_OP_EIS(F7, 6, ins, "ISB", zero_page_x)
|
||||
CPU_OP_EIS(FB, 7, ins, "ISB", absolute_y)
|
||||
CPU_OP_EIS(FF, 7, ins, "ISB", absolute_x)
|
||||
|
||||
CPU_OP_NII(04, zero_page)
|
||||
CPU_OP_NII(0C, absolute)
|
||||
CPU_OP_NII(14, zero_page_x)
|
||||
CPU_OP_NII(1A, implied)
|
||||
CPU_OP_NII(1C, absolute_x)
|
||||
CPU_OP_NII(34, zero_page_x)
|
||||
CPU_OP_NII(3A, implied)
|
||||
CPU_OP_NII(3C, absolute_x)
|
||||
CPU_OP_NII(44, zero_page)
|
||||
CPU_OP_NII(54, zero_page_x)
|
||||
CPU_OP_NII(5A, implied)
|
||||
CPU_OP_NII(5C, absolute_x)
|
||||
CPU_OP_NII(64, zero_page)
|
||||
CPU_OP_NII(74, zero_page_x)
|
||||
CPU_OP_NII(7A, implied)
|
||||
CPU_OP_NII(7C, absolute_x)
|
||||
CPU_OP_NII(80, immediate)
|
||||
CPU_OP_NII(D4, zero_page_x)
|
||||
CPU_OP_NII(DA, implied)
|
||||
CPU_OP_NII(DC, absolute_x)
|
||||
CPU_OP_NII(F4, zero_page_x)
|
||||
CPU_OP_NII(FA, implied)
|
||||
CPU_OP_NII(FC, absolute_x)
|
||||
|
||||
cpu.P = 0x24;
|
||||
cpu.SP = 0x00;
|
||||
cpu.A = cpu.X = cpu.Y = 0;
|
||||
}
|
||||
|
||||
void cpu_reset() {
|
||||
cpu.PC = cpu_reset_interrupt_address();
|
||||
cpu.SP -= 3;
|
||||
cpu.P |= interrupt_flag;
|
||||
}
|
||||
|
||||
void cpu_interrupt() {
|
||||
if (ppu_generates_nmi()) {
|
||||
cpu.P |= interrupt_flag;
|
||||
cpu_unset_flag(unused_bp);
|
||||
cpu_stack_pushw(cpu.PC);
|
||||
cpu_stack_pushb(cpu.P);
|
||||
cpu.PC = cpu_nmi_interrupt_address();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long long cpu_clock() {
|
||||
return cpu_cycles;
|
||||
}
|
||||
|
||||
void cpu_run(long cycles) {
|
||||
cycles /= 3;
|
||||
while (cycles > 0) {
|
||||
op_code = memory_readb(cpu.PC++);
|
||||
if (cpu_op_address_mode[op_code] == NULL) {
|
||||
}
|
||||
else {
|
||||
cpu_op_address_mode[op_code]();
|
||||
cpu_op_handler[op_code]();
|
||||
}
|
||||
cycles -= cpu_op_cycles[op_code] + op_cycles;
|
||||
cpu_cycles -= cpu_op_cycles[op_code] + op_cycles;
|
||||
op_cycles = 0;
|
||||
}
|
||||
}
|
17
litenes/src/cpu.h
Normal file
17
litenes/src/cpu.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include "common.h"
|
||||
|
||||
#ifndef CPU_H
|
||||
#define CPU_H
|
||||
|
||||
byte cpu_ram_read(word address);
|
||||
void cpu_ram_write(word address, byte data);
|
||||
|
||||
void cpu_init();
|
||||
void cpu_reset();
|
||||
void cpu_interrupt();
|
||||
void cpu_run(long cycles);
|
||||
|
||||
// CPU cycles that passed since power up
|
||||
unsigned long long cpu_clock();
|
||||
|
||||
#endif
|
155
litenes/src/fce.c
Normal file
155
litenes/src/fce.c
Normal file
|
@ -0,0 +1,155 @@
|
|||
#include "fce.h"
|
||||
#include "cpu.h"
|
||||
#include "memory.h"
|
||||
#include "ppu.h"
|
||||
#include "psg.h"
|
||||
#include <klib.h>
|
||||
|
||||
static int frame_cnt;
|
||||
bool candraw() { return frame_cnt % 3 == 0; }
|
||||
|
||||
static uint32_t canvas[SCR_W * SCR_H];
|
||||
|
||||
void draw(int x, int y, int idx) {
|
||||
if (x >= 0 && x < SCR_W && y >= 0 && y < SCR_H && candraw()) {
|
||||
canvas[y * SCR_W + x] = palette[idx];
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char signature[4];
|
||||
byte prg_block_count;
|
||||
byte chr_block_count;
|
||||
word rom_type;
|
||||
byte reserved[8];
|
||||
} ines_header;
|
||||
|
||||
static byte *buf;
|
||||
static ines_header *fce_rom_header;
|
||||
|
||||
byte *romread(int size) {
|
||||
byte *ret = buf;
|
||||
buf += size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fce_load_rom(char *rom) {
|
||||
buf = (byte*)rom;
|
||||
fce_rom_header = (ines_header*)romread(sizeof(ines_header));
|
||||
|
||||
if (memcmp(fce_rom_header->signature, "NES\x1A", 4)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
mmc_id = ((fce_rom_header->rom_type & 0xF0) >> 4);
|
||||
|
||||
int prg_size = fce_rom_header->prg_block_count * 0x4000;
|
||||
|
||||
byte *blk = romread(prg_size);
|
||||
|
||||
if (mmc_id == 0 || mmc_id == 3) {
|
||||
// if there is only one PRG block, we must repeat it twice
|
||||
if (fce_rom_header->prg_block_count == 1) {
|
||||
mmc_copy(0x8000, blk, 0x4000);
|
||||
mmc_copy(0xC000, blk, 0x4000);
|
||||
}
|
||||
else {
|
||||
mmc_copy(0x8000, blk, 0x8000);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Copying CHR pages into MMC and PPU
|
||||
int i;
|
||||
for (i = 0; i < fce_rom_header->chr_block_count; i++) {
|
||||
byte *blk = romread(0x2000);
|
||||
mmc_append_chr_rom_page(blk);
|
||||
|
||||
if (i == 0) {
|
||||
ppu_copy(0x0000, blk, 0x2000);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fce_init() {
|
||||
cpu_init();
|
||||
ppu_init();
|
||||
ppu_set_mirroring(fce_rom_header->rom_type & 1);
|
||||
cpu_reset();
|
||||
}
|
||||
|
||||
static int gtime;
|
||||
|
||||
static inline int uptime_ms() {
|
||||
return io_read(AM_TIMER_UPTIME).us / 1000;
|
||||
}
|
||||
|
||||
void wait_for_frame() {
|
||||
int cur = uptime_ms();
|
||||
while (cur - gtime < 1000 / FPS) {
|
||||
cur = uptime_ms();
|
||||
}
|
||||
gtime = cur;
|
||||
}
|
||||
|
||||
// FCE Lifecycle
|
||||
|
||||
void fce_run() {
|
||||
gtime = uptime_ms();
|
||||
int nr_draw = 0;
|
||||
uint32_t last = gtime;
|
||||
while(1) {
|
||||
wait_for_frame();
|
||||
int scanlines = 262;
|
||||
|
||||
while (scanlines-- > 0) {
|
||||
ppu_cycle();
|
||||
psg_detect_key();
|
||||
}
|
||||
|
||||
nr_draw ++;
|
||||
if (uptime_ms() - last > 1000) {
|
||||
last = uptime_ms();
|
||||
printf("FPS = %d\n", nr_draw);
|
||||
nr_draw = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fce_update_screen() {
|
||||
frame_cnt++;
|
||||
if (!candraw()) return;
|
||||
|
||||
int idx = ppu_ram_read(0x3F00);
|
||||
uint32_t bgc = palette[idx];
|
||||
|
||||
AM_GPU_CONFIG_T cfg = io_read(AM_GPU_CONFIG);
|
||||
int xpad = (cfg.width - SCR_W) / 2;
|
||||
int ypad = (cfg.height - SCR_H) / 2;
|
||||
panic_on(xpad < 0 || ypad < 0, "screen too small");
|
||||
|
||||
io_write(AM_GPU_FBDRAW, xpad, ypad, canvas, SCR_W, SCR_H, true);
|
||||
|
||||
for (int i = 0; i < SCR_W * SCR_H; i ++) canvas[i] = bgc;
|
||||
}
|
||||
|
||||
extern unsigned char rom_mario_nes[];
|
||||
|
||||
int main(const char *rom_name) {
|
||||
ioe_init();
|
||||
|
||||
printf("Play: [%s] SELECT [%s] START [%s]\n"
|
||||
" [%s] [%s] [%s] A [%s] B [%s]\n",
|
||||
TOSTRING(KEY_UP), TOSTRING(KEY_SELECT), TOSTRING(KEY_START),
|
||||
TOSTRING(KEY_LEFT), TOSTRING(KEY_DOWN), TOSTRING(KEY_RIGHT),
|
||||
TOSTRING(KEY_A), TOSTRING(KEY_B));
|
||||
|
||||
fce_load_rom((void *)rom_mario_nes);
|
||||
fce_init();
|
||||
fce_run();
|
||||
return 1;
|
||||
}
|
24
litenes/src/fce.h
Normal file
24
litenes/src/fce.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#ifndef FCE_H
|
||||
#define FCE_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define FPS 60
|
||||
#define SCR_W 256
|
||||
#define SCR_H 240
|
||||
|
||||
void fce_update_screen();
|
||||
void draw(int x, int y, int idx);
|
||||
|
||||
static const uint32_t palette[64] = {
|
||||
0x808080, 0x0000BB, 0x3700BF, 0x8400A6, 0xBB006A, 0xB7001E, 0xB30000, 0x912600,
|
||||
0x7B2B00, 0x003E00, 0x00480D, 0x003C22, 0x002F66, 0x000000, 0x050505, 0x050505,
|
||||
0xC8C8C8, 0x0059FF, 0x443CFF, 0xB733CC, 0xFF33AA, 0xFF375E, 0xFF371A, 0xD54B00,
|
||||
0xC46200, 0x3C7B00, 0x1E8415, 0x009566, 0x0084C4, 0x111111, 0x090909, 0x090909,
|
||||
0xFFFFFF, 0x0095FF, 0x6F84FF, 0xD56FFF, 0xFF77CC, 0xFF6F99, 0xFF7B59, 0xFF915F,
|
||||
0xFFA233, 0xA6BF00, 0x51D96A, 0x4DD5AE, 0x00D9FF, 0x666666, 0x0D0D0D, 0x0D0D0D,
|
||||
0xFFFFFF, 0x84BFFF, 0xBBBBFF, 0xD0BBFF, 0xFFBFEA, 0xFFBFCC, 0xFFC4B7, 0xFFCCAE,
|
||||
0xFFD9A2, 0xCCE199, 0xAEEEB7, 0xAAF7EE, 0xB3EEFF, 0xDDDDDD, 0x111111, 0x111111
|
||||
};
|
||||
|
||||
#endif
|
3418
litenes/src/mario-rom.c
Normal file
3418
litenes/src/mario-rom.c
Normal file
File diff suppressed because it is too large
Load diff
41
litenes/src/memory.c
Normal file
41
litenes/src/memory.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
#include "memory.h"
|
||||
#include "cpu.h"
|
||||
#include "ppu.h"
|
||||
#include "psg.h"
|
||||
|
||||
byte memory_readb(word address) {
|
||||
switch (address >> 13) {
|
||||
case 0: return cpu_ram_read(address & 0x07FF);
|
||||
case 1: return ppuio_read(address);
|
||||
case 2: return psgio_read(address);
|
||||
case 3: return cpu_ram_read(address & 0x1FFF);
|
||||
default: return mmc_read(address);
|
||||
}
|
||||
}
|
||||
|
||||
void memory_writeb(word address, byte data) {
|
||||
// DMA transfer
|
||||
int i;
|
||||
if (address == 0x4014) {
|
||||
for (i = 0; i < 256; i++) {
|
||||
ppu_sprram_write(cpu_ram_read((0x100 * data) + i));
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (address >> 13) {
|
||||
case 0: return cpu_ram_write(address & 0x07FF, data);
|
||||
case 1: return ppuio_write(address, data);
|
||||
case 2: return psgio_write(address, data);
|
||||
case 3: return cpu_ram_write(address & 0x1FFF, data);
|
||||
default: return mmc_write(address, data);
|
||||
}
|
||||
}
|
||||
|
||||
word memory_readw(word address) {
|
||||
return memory_readb(address) + (memory_readb(address + 1) << 8);
|
||||
}
|
||||
|
||||
void memory_writew(word address, word data) {
|
||||
memory_writeb(address, data & 0xFF);
|
||||
memory_writeb(address + 1, data >> 8);
|
||||
}
|
15
litenes/src/memory.h
Normal file
15
litenes/src/memory.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#ifndef MEM_H
|
||||
#define MEM_H
|
||||
|
||||
#include "common.h"
|
||||
#include "mmc.h"
|
||||
|
||||
// Single byte
|
||||
byte memory_readb(word address);
|
||||
void memory_writeb(word address, byte data);
|
||||
|
||||
// Two bytes (word), LSB first
|
||||
word memory_readw(word address);
|
||||
void memory_writew(word address, word data);
|
||||
|
||||
#endif
|
30
litenes/src/mmc.c
Normal file
30
litenes/src/mmc.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include "mmc.h"
|
||||
#include "ppu.h"
|
||||
#include <klib.h>
|
||||
|
||||
#define MMC_MAX_PAGE_COUNT 1
|
||||
|
||||
static byte mmc_chr_pages[MMC_MAX_PAGE_COUNT][0x2000];
|
||||
static int mmc_chr_pages_number;
|
||||
|
||||
byte memory[0x10000];
|
||||
|
||||
byte mmc_read(word address) {
|
||||
return memory[address];
|
||||
}
|
||||
|
||||
void mmc_write(word address, byte data) {
|
||||
switch (mmc_id) {
|
||||
case 0x3: ppu_copy(0x0000, &mmc_chr_pages[data & 3][0], 0x2000); break;
|
||||
}
|
||||
memory[address] = data;
|
||||
}
|
||||
|
||||
void mmc_copy(word address, byte *source, int length) {
|
||||
memcpy(&memory[address], source, length);
|
||||
}
|
||||
|
||||
void mmc_append_chr_rom_page(byte *source) {
|
||||
assert(mmc_chr_pages_number < MMC_MAX_PAGE_COUNT);
|
||||
memcpy(&mmc_chr_pages[mmc_chr_pages_number++][0], source, 0x2000);
|
||||
}
|
8
litenes/src/mmc.h
Normal file
8
litenes/src/mmc.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include "common.h"
|
||||
|
||||
byte mmc_id;
|
||||
|
||||
byte mmc_read(word address);
|
||||
void mmc_write(word address, byte data);
|
||||
void mmc_copy(word address, byte *source, int length);
|
||||
void mmc_append_chr_rom_page(byte *source);
|
432
litenes/src/ppu.c
Normal file
432
litenes/src/ppu.c
Normal file
|
@ -0,0 +1,432 @@
|
|||
#include "ppu.h"
|
||||
#include "cpu.h"
|
||||
#include "fce.h"
|
||||
#include "memory.h"
|
||||
#include <klib.h>
|
||||
|
||||
//#define PROFILE
|
||||
//#define HAS_US_TIMER
|
||||
|
||||
PPU_STATE ppu;
|
||||
|
||||
static bool ppu_2007_first_read;
|
||||
static byte ppu_addr_latch;
|
||||
static byte PPU_SPRRAM[0x100];
|
||||
static byte PPU_RAM[0x4000];
|
||||
static bool ppu_sprite_hit_occured = false;
|
||||
static byte ppu_latch;
|
||||
|
||||
// PPU Constants
|
||||
static const word ppu_base_nametable_addresses[4] = { 0x2000, 0x2400, 0x2800, 0x2C00 };
|
||||
|
||||
// For sprite-0-hit checks
|
||||
static byte ppu_screen_background[264][248];
|
||||
|
||||
// Precalculated tile high and low bytes addition for pattern tables
|
||||
static byte ppu_l_h_addition_table[256][256][8];
|
||||
static byte ppu_l_h_addition_flip_table[256][256][8];
|
||||
|
||||
|
||||
// PPUCTRL Functions
|
||||
|
||||
word ppu_base_nametable_address() { return ppu_base_nametable_addresses[ppu.PPUCTRL & 0x3]; }
|
||||
byte ppu_vram_address_increment() { return common_bit_set(ppu.PPUCTRL, 2) ? 32 : 1; }
|
||||
word ppu_sprite_pattern_table_address() { return common_bit_set(ppu.PPUCTRL, 3) ? 0x1000 : 0x0000; }
|
||||
word ppu_background_pattern_table_address() { return common_bit_set(ppu.PPUCTRL, 4) ? 0x1000 : 0x0000; }
|
||||
byte ppu_sprite_height() { return common_bit_set(ppu.PPUCTRL, 5) ? 16 : 8; }
|
||||
bool ppu_generates_nmi() { return common_bit_set(ppu.PPUCTRL, 7); }
|
||||
|
||||
// PPUMASK Functions
|
||||
|
||||
bool ppu_renders_grayscale() { return common_bit_set(ppu.PPUMASK, 0); }
|
||||
bool ppu_shows_background_in_leftmost_8px() { return common_bit_set(ppu.PPUMASK, 1); }
|
||||
bool ppu_shows_sprites_in_leftmost_8px() { return common_bit_set(ppu.PPUMASK, 2); }
|
||||
bool ppu_shows_background() { return common_bit_set(ppu.PPUMASK, 3); }
|
||||
bool ppu_shows_sprites() { return common_bit_set(ppu.PPUMASK, 4); }
|
||||
bool ppu_intensifies_reds() { return common_bit_set(ppu.PPUMASK, 5); }
|
||||
bool ppu_intensifies_greens() { return common_bit_set(ppu.PPUMASK, 6); }
|
||||
bool ppu_intensifies_blues() { return common_bit_set(ppu.PPUMASK, 7); }
|
||||
|
||||
void ppu_set_renders_grayscale(bool yesno) { common_modify_bitb(&ppu.PPUMASK, 0, yesno); }
|
||||
void ppu_set_shows_background_in_leftmost_8px(bool yesno) { common_modify_bitb(&ppu.PPUMASK, 1, yesno); }
|
||||
void ppu_set_shows_sprites_in_leftmost_8px(bool yesno) { common_modify_bitb(&ppu.PPUMASK, 2, yesno); }
|
||||
void ppu_set_shows_background(bool yesno) { common_modify_bitb(&ppu.PPUMASK, 3, yesno); }
|
||||
void ppu_set_shows_sprites(bool yesno) { common_modify_bitb(&ppu.PPUMASK, 4, yesno); }
|
||||
void ppu_set_intensifies_reds(bool yesno) { common_modify_bitb(&ppu.PPUMASK, 5, yesno); }
|
||||
void ppu_set_intensifies_greens(bool yesno) { common_modify_bitb(&ppu.PPUMASK, 6, yesno); }
|
||||
void ppu_set_intensifies_blues(bool yesno) { common_modify_bitb(&ppu.PPUMASK, 7, yesno); }
|
||||
|
||||
// PPUSTATUS Functions
|
||||
|
||||
bool ppu_sprite_overflow() { return common_bit_set(ppu.PPUSTATUS, 5); }
|
||||
bool ppu_sprite_0_hit() { return common_bit_set(ppu.PPUSTATUS, 6); }
|
||||
bool ppu_in_vblank() { return common_bit_set(ppu.PPUSTATUS, 7); }
|
||||
|
||||
void ppu_set_sprite_overflow(bool yesno) { common_modify_bitb(&ppu.PPUSTATUS, 5, yesno); }
|
||||
void ppu_set_sprite_0_hit(bool yesno) { common_modify_bitb(&ppu.PPUSTATUS, 6, yesno); }
|
||||
void ppu_set_in_vblank(bool yesno) { common_modify_bitb(&ppu.PPUSTATUS, 7, yesno); }
|
||||
|
||||
|
||||
// RAM
|
||||
|
||||
word ppu_get_real_ram_address(word address) {
|
||||
if (address < 0x2000) { return address; }
|
||||
else if (address < 0x3F00) {
|
||||
if (address < 0x3000) { return address; }
|
||||
else { return address; }
|
||||
}
|
||||
else if (address < 0x4000) {
|
||||
address = 0x3F00 | (address & 0x1F);
|
||||
if (address == 0x3F10 || address == 0x3F14 || address == 0x3F18 || address == 0x3F1C)
|
||||
address -= 0x10;
|
||||
return address;
|
||||
}
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
byte ppu_ram_read(word address) {
|
||||
return PPU_RAM[ppu_get_real_ram_address(address)];
|
||||
}
|
||||
|
||||
void ppu_ram_write(word address, byte data) {
|
||||
PPU_RAM[ppu_get_real_ram_address(address)] = data;
|
||||
}
|
||||
|
||||
// 3F01 = 0F (00001111)
|
||||
// 3F02 = 2A (00101010)
|
||||
// 3F03 = 09 (00001001)
|
||||
// 3F04 = 07 (00000111)
|
||||
// 3F05 = 0F (00001111)
|
||||
// 3F06 = 30 (00110000)
|
||||
// 3F07 = 27 (00100111)
|
||||
// 3F08 = 15 (00010101)
|
||||
// 3F09 = 0F (00001111)
|
||||
// 3F0A = 30 (00110000)
|
||||
// 3F0B = 02 (00000010)
|
||||
// 3F0C = 21 (00100001)
|
||||
// 3F0D = 0F (00001111)
|
||||
// 3F0E = 30 (00110000)
|
||||
// 3F0F = 00 (00000000)
|
||||
// 3F11 = 0F (00001111)
|
||||
// 3F12 = 16 (00010110)
|
||||
// 3F13 = 12 (00010010)
|
||||
// 3F14 = 37 (00110111)
|
||||
// 3F15 = 0F (00001111)
|
||||
// 3F16 = 12 (00010010)
|
||||
// 3F17 = 16 (00010110)
|
||||
// 3F18 = 37 (00110111)
|
||||
// 3F19 = 0F (00001111)
|
||||
// 3F1A = 17 (00010111)
|
||||
// 3F1B = 11 (00010001)
|
||||
// 3F1C = 35 (00110101)
|
||||
// 3F1D = 0F (00001111)
|
||||
// 3F1E = 17 (00010111)
|
||||
// 3F1F = 11 (00010001)
|
||||
// 3F20 = 2B (00101011)
|
||||
|
||||
|
||||
// Rendering
|
||||
|
||||
void ppu_draw_background_scanline(bool mirror) {
|
||||
int tile_x;
|
||||
for (tile_x = ppu_shows_background_in_leftmost_8px() ? 0 : 1; tile_x < 32; tile_x ++) {
|
||||
// Skipping off-screen pixels
|
||||
if (((tile_x << 3) - ppu.PPUSCROLL_X + (mirror ? 256 : 0)) > 256)
|
||||
continue;
|
||||
|
||||
int tile_y = ppu.scanline >> 3;
|
||||
int tile_index = ppu_ram_read(ppu_base_nametable_address() + tile_x + (tile_y << 5) + (mirror ? 0x400 : 0));
|
||||
word tile_address = ppu_background_pattern_table_address() + 16 * tile_index;
|
||||
|
||||
int y_in_tile = ppu.scanline & 0x7;
|
||||
byte l = ppu_ram_read(tile_address + y_in_tile);
|
||||
byte h = ppu_ram_read(tile_address + y_in_tile + 8);
|
||||
|
||||
int x;
|
||||
for (x = 0; x < 8; x ++) {
|
||||
byte color = ppu_l_h_addition_table[l][h][x];
|
||||
|
||||
// Color 0 is transparent
|
||||
if (color != 0) {
|
||||
word attribute_address = (ppu_base_nametable_address() + (mirror ? 0x400 : 0) + 0x3C0 + (tile_x >> 2) + (ppu.scanline >> 5) * 8);
|
||||
bool top = (ppu.scanline % 32) < 16;
|
||||
bool left = (tile_x % 4 < 2);
|
||||
|
||||
byte palette_attribute = ppu_ram_read(attribute_address);
|
||||
|
||||
if (!top) { palette_attribute >>= 4; }
|
||||
if (!left) { palette_attribute >>= 2; }
|
||||
palette_attribute &= 3;
|
||||
|
||||
word palette_address = 0x3F00 + (palette_attribute << 2);
|
||||
int idx = ppu_ram_read(palette_address + color);
|
||||
|
||||
ppu_screen_background[(tile_x << 3) + x][ppu.scanline] = color;
|
||||
|
||||
draw((tile_x << 3) + x - ppu.PPUSCROLL_X + (mirror ? 256 : 0), ppu.scanline + 1, idx); // bg
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ppu_draw_sprite_scanline() {
|
||||
int scanline_sprite_count = 0;
|
||||
int n;
|
||||
for (n = 0; n < 0x100; n += 4) {
|
||||
byte sprite_x = PPU_SPRRAM[n + 3];
|
||||
byte sprite_y = PPU_SPRRAM[n];
|
||||
|
||||
// Skip if sprite not on scanline
|
||||
if (sprite_y > ppu.scanline || sprite_y + ppu_sprite_height() < ppu.scanline)
|
||||
continue;
|
||||
|
||||
scanline_sprite_count++;
|
||||
|
||||
// PPU can't render > 8 sprites
|
||||
if (scanline_sprite_count > 8) {
|
||||
ppu_set_sprite_overflow(true);
|
||||
// break;
|
||||
}
|
||||
|
||||
bool vflip = PPU_SPRRAM[n + 2] & 0x80;
|
||||
bool hflip = PPU_SPRRAM[n + 2] & 0x40;
|
||||
|
||||
word tile_address = ppu_sprite_pattern_table_address() + 16 * PPU_SPRRAM[n + 1];
|
||||
int y_in_tile = ppu.scanline & 0x7;
|
||||
byte l = ppu_ram_read(tile_address + (vflip ? (7 - y_in_tile) : y_in_tile));
|
||||
byte h = ppu_ram_read(tile_address + (vflip ? (7 - y_in_tile) : y_in_tile) + 8);
|
||||
|
||||
byte palette_attribute = PPU_SPRRAM[n + 2] & 0x3;
|
||||
word palette_address = 0x3F10 + (palette_attribute << 2);
|
||||
int x;
|
||||
for (x = 0; x < 8; x ++) {
|
||||
int color = hflip ? ppu_l_h_addition_flip_table[l][h][x] : ppu_l_h_addition_table[l][h][x];
|
||||
|
||||
// Color 0 is transparent
|
||||
if (color != 0) {
|
||||
int screen_x = sprite_x + x;
|
||||
int idx = ppu_ram_read(palette_address + color);
|
||||
|
||||
// FIXME: we do not distinguish bbg and fg here to improve performance
|
||||
if (PPU_SPRRAM[n + 2] & 0x20) {
|
||||
draw(screen_x, sprite_y + y_in_tile + 1, idx); // bbg
|
||||
}
|
||||
else {
|
||||
draw(screen_x, sprite_y + y_in_tile + 1, idx); // fg
|
||||
}
|
||||
|
||||
// Checking sprite 0 hit
|
||||
if (ppu_shows_background() && !ppu_sprite_hit_occured && n == 0 && ppu_screen_background[screen_x][sprite_y + y_in_tile] == color) {
|
||||
ppu_set_sprite_0_hit(true);
|
||||
ppu_sprite_hit_occured = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PPU Lifecycle
|
||||
|
||||
void ppu_run(int cycles) {
|
||||
while (cycles-- > 0) { ppu_cycle(); }
|
||||
}
|
||||
|
||||
static uint32_t background_time, sprite_time, cpu_time;
|
||||
#ifdef PROFILE
|
||||
#ifdef HAS_US_TIMER
|
||||
# define TIMER_UNIT "us"
|
||||
# define time_read(x) read_us(&x)
|
||||
# define time_diff(t1, t0) us_timediff(&t1, &t0)
|
||||
# define TIME_TYPE amtime
|
||||
#else
|
||||
# define TIMER_UNIT "ms"
|
||||
# define time_read(x) x = uptime()
|
||||
# define time_diff(t1, t0) (t1 - t0)
|
||||
# define TIME_TYPE uint32_t
|
||||
#endif
|
||||
#else
|
||||
# define time_read(x)
|
||||
# define time_diff(t1, t0) 0
|
||||
#endif
|
||||
|
||||
void ppu_cycle() {
|
||||
#ifdef PROFILE
|
||||
TIME_TYPE t0, t1, t2, t3, t4, t5;
|
||||
#endif
|
||||
|
||||
if (!ppu.ready && cpu_clock() > 29658)
|
||||
ppu.ready = true;
|
||||
|
||||
time_read(t0);
|
||||
cpu_run(256);
|
||||
time_read(t1);
|
||||
|
||||
ppu.scanline++;
|
||||
|
||||
if (ppu.scanline < SCR_H && ppu_shows_background()) {
|
||||
ppu_draw_background_scanline(false);
|
||||
ppu_draw_background_scanline(true);
|
||||
}
|
||||
|
||||
time_read(t2);
|
||||
cpu_run(85 - 16);
|
||||
time_read(t3);
|
||||
|
||||
if (ppu.scanline < SCR_H && ppu_shows_sprites()) {
|
||||
ppu_draw_sprite_scanline();
|
||||
}
|
||||
|
||||
time_read(t4);
|
||||
cpu_run(16);
|
||||
time_read(t5);
|
||||
|
||||
cpu_time += time_diff(t1, t0) + time_diff(t3, t2) + time_diff(t5, t4);
|
||||
background_time += time_diff(t2, t1);
|
||||
sprite_time += time_diff(t4, t3);
|
||||
|
||||
if (ppu.scanline == 241) {
|
||||
ppu_set_in_vblank(true);
|
||||
ppu_set_sprite_0_hit(false);
|
||||
cpu_interrupt();
|
||||
}
|
||||
else if (ppu.scanline == 262) {
|
||||
ppu.scanline = -1;
|
||||
ppu_sprite_hit_occured = false;
|
||||
ppu_set_in_vblank(false);
|
||||
|
||||
time_read(t0);
|
||||
fce_update_screen();
|
||||
time_read(t1);
|
||||
|
||||
#ifdef PROFILE
|
||||
uint32_t total = cpu_time + background_time + sprite_time + time_diff(t1, t0);
|
||||
printf("Time: cpu + bg + spr + scr = (%d + %d + %d + %d)\t= %d %s\n",
|
||||
cpu_time, background_time, sprite_time, time_diff(t1, t0), total, TIMER_UNIT);
|
||||
#endif
|
||||
cpu_time = 0;
|
||||
background_time = 0;
|
||||
sprite_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ppu_copy(word address, byte *source, int length) {
|
||||
memcpy(&PPU_RAM[address], source, length);
|
||||
}
|
||||
|
||||
byte ppuio_read(word address) {
|
||||
ppu.PPUADDR &= 0x3FFF;
|
||||
switch (address & 7) {
|
||||
case 2:
|
||||
{
|
||||
byte value = ppu.PPUSTATUS;
|
||||
ppu_set_in_vblank(false);
|
||||
ppu_set_sprite_0_hit(false);
|
||||
ppu.scroll_received_x = 0;
|
||||
ppu.PPUSCROLL = 0;
|
||||
ppu.addr_received_high_byte = 0;
|
||||
ppu_latch = value;
|
||||
ppu_addr_latch = 0;
|
||||
ppu_2007_first_read = true;
|
||||
return value;
|
||||
}
|
||||
case 4: return ppu_latch = PPU_SPRRAM[ppu.OAMADDR];
|
||||
case 7:
|
||||
{
|
||||
byte data;
|
||||
|
||||
if (ppu.PPUADDR < 0x3F00) {
|
||||
data = ppu_latch = ppu_ram_read(ppu.PPUADDR);
|
||||
}
|
||||
else {
|
||||
data = ppu_ram_read(ppu.PPUADDR);
|
||||
ppu_latch = 0;
|
||||
}
|
||||
|
||||
if (ppu_2007_first_read) {
|
||||
ppu_2007_first_read = false;
|
||||
}
|
||||
else {
|
||||
ppu.PPUADDR += ppu_vram_address_increment();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
default: return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
void ppuio_write(word address, byte data) {
|
||||
address &= 7;
|
||||
ppu_latch = data;
|
||||
ppu.PPUADDR &= 0x3FFF;
|
||||
switch(address) {
|
||||
case 0: if (ppu.ready) ppu.PPUCTRL = data; break;
|
||||
case 1: if (ppu.ready) ppu.PPUMASK = data; break;
|
||||
case 3: ppu.OAMADDR = data; break;
|
||||
case 4: PPU_SPRRAM[ppu.OAMADDR++] = data; break;
|
||||
case 5:
|
||||
{
|
||||
if (ppu.scroll_received_x)
|
||||
ppu.PPUSCROLL_Y = data;
|
||||
else
|
||||
ppu.PPUSCROLL_X = data;
|
||||
|
||||
ppu.scroll_received_x ^= 1;
|
||||
break;
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
if (!ppu.ready)
|
||||
return;
|
||||
|
||||
if (ppu.addr_received_high_byte)
|
||||
ppu.PPUADDR = (ppu_addr_latch << 8) + data;
|
||||
else
|
||||
ppu_addr_latch = data;
|
||||
|
||||
ppu.addr_received_high_byte ^= 1;
|
||||
ppu_2007_first_read = true;
|
||||
break;
|
||||
}
|
||||
case 7:
|
||||
{
|
||||
if (ppu.PPUADDR > 0x1FFF || ppu.PPUADDR < 0x4000) {
|
||||
ppu_ram_write(ppu.PPUADDR ^ ppu.mirroring_xor, data);
|
||||
ppu_ram_write(ppu.PPUADDR, data);
|
||||
}
|
||||
else {
|
||||
ppu_ram_write(ppu.PPUADDR, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
ppu_latch = data;
|
||||
}
|
||||
|
||||
void ppu_init() {
|
||||
ppu.PPUCTRL = ppu.PPUMASK = ppu.PPUSTATUS = ppu.OAMADDR = ppu.PPUSCROLL_X = ppu.PPUSCROLL_Y = ppu.PPUADDR = 0;
|
||||
ppu.PPUSTATUS |= 0xA0;
|
||||
ppu.PPUDATA = 0;
|
||||
ppu_2007_first_read = true;
|
||||
|
||||
// Initializing low-high byte-pairs for pattern tables
|
||||
int h, l, x;
|
||||
for (h = 0; h < 0x100; h ++) {
|
||||
for (l = 0; l < 0x100; l ++) {
|
||||
for (x = 0; x < 8; x ++) {
|
||||
ppu_l_h_addition_table[l][h][x] = (((h >> (7 - x)) & 1) << 1) | ((l >> (7 - x)) & 1);
|
||||
ppu_l_h_addition_flip_table[l][h][x] = (((h >> x) & 1) << 1) | ((l >> x) & 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ppu_sprram_write(byte data) {
|
||||
PPU_SPRRAM[ppu.OAMADDR++] = data;
|
||||
}
|
||||
|
||||
void ppu_set_background_color(byte color) {
|
||||
}
|
||||
|
||||
void ppu_set_mirroring(byte mirroring) {
|
||||
ppu.mirroring = mirroring;
|
||||
ppu.mirroring_xor = 0x400 << mirroring;
|
||||
}
|
41
litenes/src/ppu.h
Normal file
41
litenes/src/ppu.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
#ifndef PPU_H
|
||||
#define PPU_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
typedef struct {
|
||||
byte PPUCTRL; // $2000 write only
|
||||
byte PPUMASK; // $2001 write only
|
||||
byte PPUSTATUS; // $2002 read only
|
||||
byte OAMADDR; // $2003 write only
|
||||
byte OAMDATA; // $2004
|
||||
word PPUSCROLL;
|
||||
byte PPUSCROLL_X, PPUSCROLL_Y; // $2005 write only x2
|
||||
word PPUADDR; // $2006 write only x2
|
||||
word PPUDATA; // $2007
|
||||
|
||||
bool scroll_received_x;
|
||||
bool addr_received_high_byte;
|
||||
bool ready;
|
||||
|
||||
int mirroring, mirroring_xor;
|
||||
|
||||
int x, scanline;
|
||||
} PPU_STATE;
|
||||
|
||||
void ppu_init();
|
||||
|
||||
byte ppu_ram_read(word address);
|
||||
void ppu_ram_write(word address, byte data);
|
||||
byte ppuio_read(word address);
|
||||
void ppuio_write(word address, byte data);
|
||||
|
||||
bool ppu_generates_nmi();
|
||||
|
||||
void ppu_set_mirroring(byte mirroring);
|
||||
|
||||
void ppu_cycle();
|
||||
void ppu_copy(word address, byte *source, int length);
|
||||
void ppu_sprram_write(byte data);
|
||||
|
||||
#endif
|
54
litenes/src/psg.c
Normal file
54
litenes/src/psg.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
#include "psg.h"
|
||||
#include <klib.h>
|
||||
|
||||
static int p = 10;
|
||||
static int key_state[256];
|
||||
|
||||
#define KEYS \
|
||||
CONCAT(AM_KEY_, KEY_A), \
|
||||
CONCAT(AM_KEY_, KEY_B), \
|
||||
CONCAT(AM_KEY_, KEY_SELECT), \
|
||||
CONCAT(AM_KEY_, KEY_START), \
|
||||
CONCAT(AM_KEY_, KEY_UP), \
|
||||
CONCAT(AM_KEY_, KEY_DOWN), \
|
||||
CONCAT(AM_KEY_, KEY_LEFT), \
|
||||
CONCAT(AM_KEY_, KEY_RIGHT),
|
||||
|
||||
static int MAP[256] = {
|
||||
0, // On/Off
|
||||
KEYS
|
||||
255,
|
||||
};
|
||||
|
||||
byte psgio_read(word address) {
|
||||
// Joystick 1
|
||||
if (address == 0x4016) {
|
||||
if (p++ < 9) {
|
||||
return key_state[MAP[p]];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void psgio_write(word address, byte data) {
|
||||
static byte prev_write;
|
||||
if (address == 0x4016) {
|
||||
if ((data & 1) == 0 && prev_write == 1) {
|
||||
// strobe
|
||||
p = 0;
|
||||
}
|
||||
}
|
||||
prev_write = data & 1;
|
||||
}
|
||||
|
||||
void psg_detect_key() {
|
||||
while (1) {
|
||||
AM_INPUT_KEYBRD_T ev = io_read(AM_INPUT_KEYBRD);
|
||||
if (ev.keycode == AM_KEY_NONE) break;
|
||||
key_state[ev.keycode] = ev.keydown;
|
||||
}
|
||||
}
|
||||
|
||||
void psg_init() {
|
||||
key_state[0] = 1;
|
||||
}
|
20
litenes/src/psg.h
Normal file
20
litenes/src/psg.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#ifndef PSG_H
|
||||
#define PSG_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define KEY_A J
|
||||
#define KEY_B K
|
||||
#define KEY_SELECT U
|
||||
#define KEY_START I
|
||||
#define KEY_UP W
|
||||
#define KEY_DOWN S
|
||||
#define KEY_LEFT A
|
||||
#define KEY_RIGHT D
|
||||
|
||||
byte psgio_read(word address);
|
||||
void psgio_write(word address, byte data);
|
||||
void psg_init();
|
||||
void psg_detect_key();
|
||||
|
||||
#endif
|
|
@ -1,3 +1,3 @@
|
|||
NAME = micro-bench
|
||||
SRCS = $(shell find -L ./src/ -name "*.c" -o -name "*.cc")
|
||||
SRCS = $(shell find src/ -name "*.c" -o -name "*.cc")
|
||||
include $(AM_HOME)/Makefile
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
NAME := thread-os
|
||||
SRCS := thread-os.c
|
||||
NAME = thread-os
|
||||
SRCS = thread-os.c
|
||||
include $(AM_HOME)/Makefile
|
||||
|
|
Loading…
Reference in a new issue