diff options
| author | crecher <crecher@crecher.net> | 2026-10-12 15:44:15 +0200 |
|---|---|---|
| committer | crecher <crecher@crecher.net> | 2026-10-12 15:44:15 +0200 |
| commit | 780b1e886f6676efedc732b67328f5fbdbe87dc4 (patch) | |
| tree | ed9c99db91cbee374e31cea24f6ad34848dc47af | |
| parent | 6c49575d1cacd5126b73506cc30430643310396b (diff) | |
| download | gones-780b1e886f6676efedc732b67328f5fbdbe87dc4.tar.gz gones-780b1e886f6676efedc732b67328f5fbdbe87dc4.zip | |
waiter waiter more comments please
| -rw-r--r-- | .gitignore | 4 | ||||
| -rw-r--r-- | README | 2 | ||||
| -rw-r--r-- | cpu/cpu.go | 12 | ||||
| -rw-r--r-- | cpu/opcodes.go | 91 | ||||
| -rw-r--r-- | nes/bus.go | 21 | ||||
| -rw-r--r-- | nes/nes.go | 6 |
6 files changed, 128 insertions, 8 deletions
@@ -15,4 +15,6 @@ # fuck you emacs <3 *~ -.#*
\ No newline at end of file +.#* + +*.code-workspace
\ No newline at end of file @@ -1,5 +1,7 @@ nes emulator in go +im trying to make it somewhat commented so yeah :D + TODO: emulator base - [x] cpu passing nestest @@ -13,6 +13,16 @@ type CPU struct { SP uint8 // stack pointer PC uint16 // program counter Status uint8 // status flags + // status flags are NV1B DIZC + // |||| |||| + // |||| |||+- Carry + // |||| ||+-- Zero + // |||| |+--- Interrupt Disable + // |||| +---- Decimal + // |||+------ (no cpu effect, check https://www.nesdev.org/wiki/Status_flags#The_B_flag for a good explanation) + // ||+------- (no cpu effect, always pushed as 1) + // |+-------- Overflow + // +--------- Negative Bus Bus Cycles uint64 @@ -21,7 +31,7 @@ type CPU struct { extraCycles int } -func NewCPU(bus Bus) *CPU { +func NewCPU(bus Bus) *CPU { // creates new cpu with bus return &CPU{Bus: bus} } diff --git a/cpu/opcodes.go b/cpu/opcodes.go index 13f9612..ea6e1b9 100644 --- a/cpu/opcodes.go +++ b/cpu/opcodes.go @@ -28,6 +28,7 @@ func init() { } } + // load accumulator def(0xA9, "LDA", ModeImmediate, 2, false, ldaExec) def(0xA5, "LDA", ModeZeroPage, 3, false, ldaExec) def(0xB5, "LDA", ModeZeroPageX, 4, false, ldaExec) @@ -37,18 +38,21 @@ func init() { def(0xA1, "LDA", ModeIndirectX, 6, false, ldaExec) def(0xB1, "LDA", ModeIndirectY, 5, true, ldaExec) + // load X def(0xA2, "LDX", ModeImmediate, 2, false, ldxExec) def(0xA6, "LDX", ModeZeroPage, 3, false, ldxExec) def(0xB6, "LDX", ModeZeroPageY, 4, false, ldxExec) def(0xAE, "LDX", ModeAbsolute, 4, false, ldxExec) def(0xBE, "LDX", ModeAbsoluteY, 4, true, ldxExec) + // load Y def(0xA0, "LDY", ModeImmediate, 2, false, ldyExec) def(0xA4, "LDY", ModeZeroPage, 3, false, ldyExec) def(0xB4, "LDY", ModeZeroPageX, 4, false, ldyExec) def(0xAC, "LDY", ModeAbsolute, 4, false, ldyExec) def(0xBC, "LDY", ModeAbsoluteX, 4, true, ldyExec) + // store accumulator def(0x85, "STA", ModeZeroPage, 3, false, staExec) def(0x95, "STA", ModeZeroPageX, 4, false, staExec) def(0x8D, "STA", ModeAbsolute, 4, false, staExec) @@ -57,26 +61,48 @@ func init() { def(0x81, "STA", ModeIndirectX, 6, false, staExec) def(0x91, "STA", ModeIndirectY, 6, false, staExec) + // store X def(0x86, "STX", ModeZeroPage, 3, false, stxExec) def(0x96, "STX", ModeZeroPageY, 4, false, stxExec) def(0x8E, "STX", ModeAbsolute, 4, false, stxExec) + // store Y def(0x84, "STY", ModeZeroPage, 3, false, styExec) def(0x94, "STY", ModeZeroPageX, 4, false, styExec) def(0x8C, "STY", ModeAbsolute, 4, false, styExec) + // transfer accumulator to X def(0xAA, "TAX", ModeImplied, 2, false, taxExec) + + // transfer x to accumulator def(0x8A, "TXA", ModeImplied, 2, false, txaExec) + + // transfer accumulator to Y def(0xA8, "TAY", ModeImplied, 2, false, tayExec) + + // transfer y to accumulator def(0x98, "TYA", ModeImplied, 2, false, tyaExec) + + // transfer stack pointer to x def(0xBA, "TSX", ModeImplied, 2, false, tsxExec) + + // transfer x to stack pointer def(0x9A, "TXS", ModeImplied, 2, false, txsExec) + // push accumulator def(0x48, "PHA", ModeImplied, 3, false, phaExec) + + // pull accumulator def(0x68, "PLA", ModeImplied, 4, false, plaExec) + + // originally personal homepage, now PHP: Hypertext Preprocessor + // totally not push processor status (SR) def(0x08, "PHP", ModeImplied, 3, false, phpExec) + + // pull processor status (SR) def(0x28, "PLP", ModeImplied, 4, false, plpExec) + // add with carry def(0x69, "ADC", ModeImmediate, 2, false, adcExec) def(0x65, "ADC", ModeZeroPage, 3, false, adcExec) def(0x75, "ADC", ModeZeroPageX, 4, false, adcExec) @@ -86,7 +112,9 @@ func init() { def(0x61, "ADC", ModeIndirectX, 6, false, adcExec) def(0x71, "ADC", ModeIndirectY, 5, true, adcExec) + // subtract with carry def(0xE9, "SBC", ModeImmediate, 2, false, sbcExec) + def(0xEB, "SBC", ModeImmediate, 2, false, sbcExec) def(0xE5, "SBC", ModeZeroPage, 3, false, sbcExec) def(0xF5, "SBC", ModeZeroPageX, 4, false, sbcExec) def(0xED, "SBC", ModeAbsolute, 4, false, sbcExec) @@ -95,6 +123,7 @@ func init() { def(0xE1, "SBC", ModeIndirectX, 6, false, sbcExec) def(0xF1, "SBC", ModeIndirectY, 5, true, sbcExec) + // and (with accumulator) def(0x29, "AND", ModeImmediate, 2, false, andExec) def(0x25, "AND", ModeZeroPage, 3, false, andExec) def(0x35, "AND", ModeZeroPageX, 4, false, andExec) @@ -104,6 +133,7 @@ func init() { def(0x21, "AND", ModeIndirectX, 6, false, andExec) def(0x31, "AND", ModeIndirectY, 5, true, andExec) + // or with accumulator def(0x09, "ORA", ModeImmediate, 2, false, oraExec) def(0x05, "ORA", ModeZeroPage, 3, false, oraExec) def(0x15, "ORA", ModeZeroPageX, 4, false, oraExec) @@ -113,6 +143,7 @@ func init() { def(0x01, "ORA", ModeIndirectX, 6, false, oraExec) def(0x11, "ORA", ModeIndirectY, 5, true, oraExec) + // exclusive or (with accumulator) def(0x49, "EOR", ModeImmediate, 2, false, eorExec) def(0x45, "EOR", ModeZeroPage, 3, false, eorExec) def(0x55, "EOR", ModeZeroPageX, 4, false, eorExec) @@ -122,48 +153,63 @@ func init() { def(0x41, "EOR", ModeIndirectX, 6, false, eorExec) def(0x51, "EOR", ModeIndirectY, 5, true, eorExec) + // bit test def(0x24, "BIT", ModeZeroPage, 3, false, bitExec) def(0x2C, "BIT", ModeAbsolute, 4, false, bitExec) + // arithmetic shift left def(0x0A, "ASL", ModeAccumulator, 2, false, aslExec) def(0x06, "ASL", ModeZeroPage, 5, false, aslExec) def(0x16, "ASL", ModeZeroPageX, 6, false, aslExec) def(0x0E, "ASL", ModeAbsolute, 6, false, aslExec) def(0x1E, "ASL", ModeAbsoluteX, 7, false, aslExec) + // logical shift right def(0x4A, "LSR", ModeAccumulator, 2, false, lsrExec) def(0x46, "LSR", ModeZeroPage, 5, false, lsrExec) def(0x56, "LSR", ModeZeroPageX, 6, false, lsrExec) def(0x4E, "LSR", ModeAbsolute, 6, false, lsrExec) def(0x5E, "LSR", ModeAbsoluteX, 7, false, lsrExec) + // rotate left def(0x2A, "ROL", ModeAccumulator, 2, false, rolExec) def(0x26, "ROL", ModeZeroPage, 5, false, rolExec) def(0x36, "ROL", ModeZeroPageX, 6, false, rolExec) def(0x2E, "ROL", ModeAbsolute, 6, false, rolExec) def(0x3E, "ROL", ModeAbsoluteX, 7, false, rolExec) + // rotate right def(0x6A, "ROR", ModeAccumulator, 2, false, rorExec) def(0x66, "ROR", ModeZeroPage, 5, false, rorExec) def(0x76, "ROR", ModeZeroPageX, 6, false, rorExec) def(0x6E, "ROR", ModeAbsolute, 6, false, rorExec) def(0x7E, "ROR", ModeAbsoluteX, 7, false, rorExec) + // increment def(0xE6, "INC", ModeZeroPage, 5, false, incExec) def(0xF6, "INC", ModeZeroPageX, 6, false, incExec) def(0xEE, "INC", ModeAbsolute, 6, false, incExec) def(0xFE, "INC", ModeAbsoluteX, 7, false, incExec) + // decrement def(0xC6, "DEC", ModeZeroPage, 5, false, decExec) def(0xD6, "DEC", ModeZeroPageX, 6, false, decExec) def(0xCE, "DEC", ModeAbsolute, 6, false, decExec) def(0xDE, "DEC", ModeAbsoluteX, 7, false, decExec) + // increment x def(0xE8, "INX", ModeImplied, 2, false, inxExec) + + // decrement x def(0xCA, "DEX", ModeImplied, 2, false, dexExec) + + // increment y def(0xC8, "INY", ModeImplied, 2, false, inyExec) + + // decrement y def(0x88, "DEY", ModeImplied, 2, false, deyExec) + // compare (with accumulator) def(0xC9, "CMP", ModeImmediate, 2, false, cmpExec) def(0xC5, "CMP", ModeZeroPage, 3, false, cmpExec) def(0xD5, "CMP", ModeZeroPageX, 4, false, cmpExec) @@ -173,40 +219,79 @@ func init() { def(0xC1, "CMP", ModeIndirectX, 6, false, cmpExec) def(0xD1, "CMP", ModeIndirectY, 5, true, cmpExec) + // compare with X def(0xE0, "CPX", ModeImmediate, 2, false, cpxExec) def(0xE4, "CPX", ModeZeroPage, 3, false, cpxExec) def(0xEC, "CPX", ModeAbsolute, 4, false, cpxExec) + // compare with y def(0xC0, "CPY", ModeImmediate, 2, false, cpyExec) def(0xC4, "CPY", ModeZeroPage, 3, false, cpyExec) def(0xCC, "CPY", ModeAbsolute, 4, false, cpyExec) + // branch on carry clear def(0x90, "BCC", ModeRelative, 2, false, bccExec) + + // branch on carry set def(0xB0, "BCS", ModeRelative, 2, false, bcsExec) + + // branch on not equal (zero clear) def(0xD0, "BNE", ModeRelative, 2, false, bneExec) + + // branch on equal (zero set) def(0xF0, "BEQ", ModeRelative, 2, false, beqExec) + + // branch on plus (negative clear) def(0x10, "BPL", ModeRelative, 2, false, bplExec) + + // branch on minus (negative set) def(0x30, "BMI", ModeRelative, 2, false, bmiExec) + + // branch on overflow clear def(0x50, "BVC", ModeRelative, 2, false, bvcExec) + + // branch on overflow set def(0x70, "BVS", ModeRelative, 2, false, bvsExec) + // jump def(0x4C, "JMP", ModeAbsolute, 3, false, jmpExec) def(0x6C, "JMP", ModeIndirect, 5, false, jmpExec) + + // jump subroutine def(0x20, "JSR", ModeAbsolute, 6, false, jsrExec) + + // return from subroutine def(0x60, "RTS", ModeImplied, 6, false, rtsExec) + + // return from interrupt def(0x40, "RTI", ModeImplied, 6, false, rtiExec) + // clear carry def(0x18, "CLC", ModeImplied, 2, false, clcExec) + + // set carry def(0x38, "SEC", ModeImplied, 2, false, secExec) + + // clear interrupt disable def(0x58, "CLI", ModeImplied, 2, false, cliExec) + + // set interrupt disable def(0x78, "SEI", ModeImplied, 2, false, seiExec) + + // clear decimal def(0xD8, "CLD", ModeImplied, 2, false, cldExec) + + // set decimal def(0xF8, "SED", ModeImplied, 2, false, sedExec) + + // clear overflow def(0xB8, "CLV", ModeImplied, 2, false, clvExec) - def(0xEA, "NOP", ModeImplied, 2, false, nopExec) + // break / interrupt def(0x00, "BRK", ModeImplied, 7, false, brkExec) + // no operation + def(0xEA, "NOP", ModeImplied, 2, false, nopExec) def(0x1A, "NOP", ModeImplied, 2, false, nopExec) def(0x3A, "NOP", ModeImplied, 2, false, nopExec) def(0x5A, "NOP", ModeImplied, 2, false, nopExec) @@ -235,6 +320,7 @@ func init() { def(0xDC, "NOP", ModeAbsoluteX, 4, true, nopExec) def(0xFC, "NOP", ModeAbsoluteX, 4, true, nopExec) + // JAM is sometimes also referred to as HLT or KIL def(0x02, "JAM", ModeImplied, 2, false, jamExec) def(0x12, "JAM", ModeImplied, 2, false, jamExec) def(0x22, "JAM", ModeImplied, 2, false, jamExec) @@ -248,8 +334,7 @@ func init() { def(0xD2, "JAM", ModeImplied, 2, false, jamExec) def(0xF2, "JAM", ModeImplied, 2, false, jamExec) - def(0xEB, "SBC", ModeImmediate, 2, false, sbcExec) - + // illegal / undocumented instructions def(0xA7, "LAX", ModeZeroPage, 3, false, laxExec) def(0xB7, "LAX", ModeZeroPageY, 4, false, laxExec) def(0xAF, "LAX", ModeAbsolute, 4, false, laxExec) @@ -1,10 +1,27 @@ package nes +// CPU memory map is: +// Address range | Size | Device +// -------------------------------------------------- +// $0000-$07FF | $0800 | 2 KB internal RAM +// $0800-$0FFF | $0800 | Mirror of $0000-$07FF +// $1000-$17FF | $0800 | Mirror of $0000-$07FF +// $1800-1FFF | $0800 | Mirror of $0000-$07FF +// $2000-2007 | $0008 | PPU registers +// $2008-$3FFF | $1FF8 | Mirrors of $2000-$2007 (repeats every 8 bytes) +// $4000-4017 | $0018 | APU and I/O registers +// $4018-$401F | $0008 | APU and I/O functionality thats normally disabled (CPU test mode) +// $4020-$FFFF | $BFE0 | Unmapped, available for cartridge +// - $6000-$7FFF | $2000 | Usually cartridge RAM (if present) +// - $8000-$FFFF | $8000 | Usually cartridge ROM (if present) + +// wow i put way too much effort into making that table + func (n *Console) Read(addr uint16) uint8 { switch { - case addr < 0x2000: + case addr < 0x2000: // internal RAM and mirrors return n.RAM[addr&0x7FF] - case addr >= 0x4020: + case addr >= 0x4020: // cartridge return n.Cart.Mapper.CPURead(addr) default: return 0 @@ -27,7 +27,11 @@ func (n *Console) Step() int { } func (n *Console) StepFrame() { - const cyclesPerFrame = 29780 // 29780 ~ 1789773 / 60.1 + // 29780 is roughly one frame: + // ~ 1789773 cycles per sec + // ~ 60.1 fps + // 1789773 / 60.1 = 29779.916... + const cyclesPerFrame = 29780 for done := 0; done < cyclesPerFrame; { done += n.Step() } |