aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--README2
-rw-r--r--cpu/cpu.go12
-rw-r--r--cpu/opcodes.go91
-rw-r--r--nes/bus.go21
-rw-r--r--nes/nes.go6
6 files changed, 128 insertions, 8 deletions
diff --git a/.gitignore b/.gitignore
index 9006382..dabbf1b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,4 +15,6 @@
# fuck you emacs <3
*~
-.#* \ No newline at end of file
+.#*
+
+*.code-workspace \ No newline at end of file
diff --git a/README b/README
index 0cbac96..5db532a 100644
--- a/README
+++ b/README
@@ -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
diff --git a/cpu/cpu.go b/cpu/cpu.go
index fb673b7..113adf0 100644
--- a/cpu/cpu.go
+++ b/cpu/cpu.go
@@ -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)
diff --git a/nes/bus.go b/nes/bus.go
index d9fd521..07bbbf2 100644
--- a/nes/bus.go
+++ b/nes/bus.go
@@ -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
diff --git a/nes/nes.go b/nes/nes.go
index 0f2a754..7f36b1a 100644
--- a/nes/nes.go
+++ b/nes/nes.go
@@ -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()
}