aboutsummaryrefslogtreecommitdiff
path: root/cpu/cpu.go
blob: b8b0a1ed425775cf5b1c2d6db3e18730af8d333c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package cpu

const (
	vectorNMI   uint16 = 0xFFFA
	vectorReset uint16 = 0xFFFC
	vectorIRQ   uint16 = 0xFFFE

	stackBase uint16 = 0x0100
)

type CPU struct {
	A, X, Y uint8
	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
	Jammed bool

	extraCycles int
}

func NewCPU(bus Bus) *CPU { // creates new cpu with bus
	return &CPU{Bus: bus}
}

func (c *CPU) read(addr uint16) uint8 {
	return c.Bus.Read(addr)
}

func (c *CPU) write(addr uint16, v uint8) {
	c.Bus.Write(addr, v)
}

func (c *CPU) read16(addr uint16) uint16 {
	lo := uint16(c.read(addr))
	hi := uint16(c.read(addr + 1))
	return hi<<8 | lo
}

func (c *CPU) push(v uint8) {
	c.write(stackBase+uint16(c.SP), v)
	c.SP--
}

func (c *CPU) pop() uint8 {
	c.SP++
	return c.read(stackBase + uint16(c.SP))
}

func (c *CPU) push16(v uint16) {
	c.push(uint8(v >> 8))
	c.push(uint8(v))
}

func (c *CPU) pop16() uint16 {
	lo := uint16(c.pop())
	hi := uint16(c.pop())
	return hi<<8 | lo
}

func (c *CPU) Reset() {
	c.A, c.X, c.Y = 0, 0, 0
	c.SP = 0xFD
	c.Status = FlagU | FlagI
	c.PC = c.read16(vectorReset)
	c.Cycles = 7
	c.Jammed = false
}

func (c *CPU) IRQ() {
	if c.Jammed {
		return
	}
	if c.flag(FlagI) {
		return
	}
	c.interrupt(vectorIRQ, false)
	c.Cycles += 7
}

func (c *CPU) NMI() {
	if c.Jammed {
		return
	}
	c.interrupt(vectorNMI, false)
	c.Cycles += 7
}

func (c *CPU) interrupt(vector uint16, brk bool) {
	c.push16(c.PC)
	status := c.Status | FlagU
	if brk {
		status |= FlagB
	} else {
		status &^= FlagB
	}
	c.push(status)
	c.setFlag(FlagI, true)
	c.PC = c.read16(vector)
}

// burns cycles without running anything, used while dma holds the cpu
func (c *CPU) Stall(cycles int) {
	c.Cycles += uint64(cycles)
}

func (c *CPU) Step() int {
	if c.Jammed {
		c.Cycles++
		return 1
	}
	before := c.Cycles

	opcode := c.read(c.PC)
	c.PC++

	entry := opcodeTable[opcode]
	op := c.resolveOperand(entry.mode)

	c.extraCycles = 0
	entry.exec(c, op)

	cycles := entry.cycles
	if entry.pageCrossPenalty && op.pageCrossed {
		cycles++
	}
	cycles += c.extraCycles
	c.Cycles += uint64(cycles)

	return int(c.Cycles - before)
}