aboutsummaryrefslogtreecommitdiff
path: root/nes
diff options
context:
space:
mode:
authorcrecher <crecher@crecher.net>2026-10-13 01:13:13 +0200
committercrecher <crecher@crecher.net>2026-10-13 01:13:13 +0200
commit99cae5a5a37f41f878a84857361eda47910303ba (patch)
tree31c8665ca991f95e09e93bb2e6ec91d81d2427ff /nes
parent96ff6d8eda2ba434682909f4ff4ce350d78b2273 (diff)
downloadgones-99cae5a5a37f41f878a84857361eda47910303ba.tar.gz
gones-99cae5a5a37f41f878a84857361eda47910303ba.zip
sprites!!!!!!
Diffstat (limited to 'nes')
-rw-r--r--nes/bus.go9
-rw-r--r--nes/controller.go53
-rw-r--r--nes/dma.go19
-rw-r--r--nes/nes.go11
4 files changed, 91 insertions, 1 deletions
diff --git a/nes/bus.go b/nes/bus.go
index af4f242..4f6c720 100644
--- a/nes/bus.go
+++ b/nes/bus.go
@@ -24,6 +24,10 @@ func (n *Console) Read(addr uint16) uint8 {
return n.RAM[addr&0x7FF]
case addr < 0x4000: // PPU registers, mirrored every 8 bytes
return n.PPU.ReadRegister(addr)
+ case addr == 0x4016: // controller 1
+ return n.controllers[0].read()
+ case addr == 0x4017: // controller 2
+ return n.controllers[1].read()
case addr >= 0x4020: // cartridge
return n.Cart.Mapper.CPURead(addr)
default:
@@ -37,6 +41,11 @@ func (n *Console) Write(addr uint16, v uint8) {
n.RAM[addr&0x7FF] = v
case addr < 0x4000:
n.PPU.WriteRegister(addr, v)
+ case addr == 0x4014: // oam dma
+ n.oamDMA(v)
+ case addr == 0x4016: // strobe is shared by both controllers
+ n.controllers[0].setStrobe(v&1 != 0)
+ n.controllers[1].setStrobe(v&1 != 0)
case addr >= 0x4020:
n.Cart.Mapper.CPUWrite(addr, v)
}
diff --git a/nes/controller.go b/nes/controller.go
new file mode 100644
index 0000000..51afe20
--- /dev/null
+++ b/nes/controller.go
@@ -0,0 +1,53 @@
+package nes
+
+// bit order matches the order the console reads them out in
+const (
+ ButtonA uint8 = 1 << iota
+ ButtonB
+ ButtonSelect
+ ButtonStart
+ ButtonUp
+ ButtonDown
+ ButtonLeft
+ ButtonRight
+)
+
+// standard controller, it is just an 8 bit shift register
+// https://www.nesdev.org/wiki/Standard_controller
+type controller struct {
+ buttons uint8 // what is held right now
+ shift uint8 // what the cpu is reading out
+ strobe bool
+}
+
+// while strobe is high the shift register keeps copying the buttons
+func (c *controller) setStrobe(on bool) {
+ c.strobe = on
+ if on {
+ c.shift = c.buttons
+ }
+}
+
+func (c *controller) setButtons(b uint8) {
+ c.buttons = b
+ if c.strobe {
+ c.shift = b
+ }
+}
+
+// returns one button per read, A first. with strobe high it stays on A
+func (c *controller) read() uint8 {
+ bit := c.shift & 1
+ if !c.strobe {
+ // ones get shifted in, so reads after the 8th return 1
+ c.shift = c.shift>>1 | 0x80
+ }
+ // upper bits are open bus, which is $40 here since that is the high byte
+ // of the address that was read
+ return 0x40 | bit
+}
+
+// port is 0 for controller 1 and 1 for controller 2
+func (n *Console) SetButtons(port int, buttons uint8) {
+ n.controllers[port].setButtons(buttons)
+}
diff --git a/nes/dma.go b/nes/dma.go
new file mode 100644
index 0000000..ebdbcea
--- /dev/null
+++ b/nes/dma.go
@@ -0,0 +1,19 @@
+package nes
+
+// im too lazy to write an explanation so heres the nesdev page
+// https://www.nesdev.org/wiki/DMA#OAM_DMA
+func (n *Console) oamDMA(page uint8) {
+ base := uint16(page) << 8
+ for i := uint16(0); i < 256; i++ {
+ n.PPU.WriteRegister(0x2004, n.Read(base+i))
+ }
+ n.dmaPending = true
+}
+
+func (n *Console) dmaStallCycles() int {
+ stall := 513
+ if n.CPU.Cycles&1 == 1 {
+ stall++
+ }
+ return stall
+}
diff --git a/nes/nes.go b/nes/nes.go
index 79580b6..5d96e8f 100644
--- a/nes/nes.go
+++ b/nes/nes.go
@@ -11,7 +11,10 @@ type Console struct {
PPU *ppu.PPU
Cart *cart.Cartridge
RAM [0x800]uint8
- // APU, controllers once they exist
+
+ controllers [2]controller
+ dmaPending bool
+ // APU once it exists
cycles uint64
}
@@ -25,6 +28,12 @@ func New(c *cart.Cartridge) *Console {
func (n *Console) Step() int {
cycles := n.CPU.Step()
+ if n.dmaPending {
+ n.dmaPending = false
+ stall := n.dmaStallCycles()
+ n.CPU.Stall(stall)
+ cycles += stall
+ }
n.cycles += uint64(cycles)
for i := 0; i < cycles*3; i++ {
n.PPU.Tick()