-
Notifications
You must be signed in to change notification settings - Fork 4
/
cpu.go
216 lines (190 loc) · 3.77 KB
/
cpu.go
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
package z80
import (
"context"
"log"
"sync/atomic"
)
const (
maskNone = 0x00
maskC = 0x01
maskN = 0x02
maskPV = 0x04
maskH = 0x10
maskZ = 0x40
maskS = 0x80
mask3 = 0x08
mask5 = 0x20
maskS53 = maskS | mask5 | mask3
mask53 = mask5 | mask3
)
// addrOff apply offset to address.
func addrOff(addr uint16, off uint8) uint16 {
return addr + uint16(int16(int8(off)))
}
func toU16(l, h uint8) uint16 {
return (uint16(h) << 8) | uint16(l)
}
func fromU16(v uint16) (l, h uint8) {
return uint8(v & 0xff), uint8(v >> 8)
}
// memSrc implements fetcher interface.
type memSrc []uint8
func (m *memSrc) fetch() uint8 {
if len(*m) == 0 {
return 0
}
var b uint8
b, *m = (*m)[0], (*m)[1:]
return b
}
func (cpu *CPU) failf(msg string, args ...interface{}) {
log.Printf("Z80 fail: "+msg, args...)
}
func (cpu *CPU) warnf(msg string, args ...interface{}) {
log.Printf("Z80 warn: "+msg, args...)
}
// not used for now
//func (cpu *CPU) debugf(msg string, args ...interface{}) {
// if !cpu.Debug {
// return
// }
// log.Printf("Z80 debug: "+msg, args...)
//}
func (cpu *CPU) flagC() bool {
return cpu.AF.Lo&maskC != 0
}
func (cpu *CPU) flagN() bool {
return cpu.AF.Lo&maskN != 0
}
func (cpu *CPU) flagH() bool {
return cpu.AF.Lo&maskH != 0
}
func (cpu *CPU) flagZ() bool {
return cpu.AF.Lo&maskZ != 0
}
func (cpu *CPU) readU16(addr uint16) uint16 {
l := cpu.Memory.Get(addr)
h := cpu.Memory.Get(addr + 1)
return toU16(l, h)
}
func (cpu *CPU) writeU16(addr uint16, v uint16) {
l, h := fromU16(v)
cpu.Memory.Set(addr, l)
cpu.Memory.Set(addr+1, h)
}
func (cpu *CPU) fetch() uint8 {
v := cpu.Memory.Get(cpu.PC)
cpu.PC++
return v
}
func (cpu *CPU) ioIn(addr uint8) uint8 {
if cpu.IO == nil {
return 0
}
return cpu.IO.In(addr)
}
func (cpu *CPU) ioOut(addr uint8, value uint8) {
if cpu.IO == nil {
return
}
cpu.IO.Out(addr, value)
}
// Run executes instructions till HALT or error.
func (cpu *CPU) Run(ctx context.Context) error {
var ctxErr error
var canceled int32
ctx2, cancel := context.WithCancel(ctx)
defer cancel()
go func() {
<-ctx2.Done()
ctxErr = ctx.Err()
atomic.StoreInt32(&canceled, 1)
}()
for {
if atomic.LoadInt32(&canceled) != 0 {
return ctxErr
}
cpu.Step()
if cpu.BreakPoints != nil {
if _, ok := cpu.BreakPoints[cpu.PC]; ok {
return ErrBreakPoint
}
}
if cpu.HALT {
break
}
}
return nil
}
// Step executes an instruction.
func (cpu *CPU) Step() {
// increment refresh counter
rc := cpu.IR.Lo
cpu.IR.Lo = rc&0x80 | (rc+1)&0x7f
// try interruptions.
oldPC := cpu.PC
if cpu.tryInterrupt() {
if cpu.IMon != nil {
cpu.IMon.OnInterrupt(cpu.InNMI, oldPC, cpu.PC)
}
return
}
// execute an op-code.
cpu.executeOne(cpu)
}
func (cpu *CPU) tryInterrupt() bool {
// check non-maskable interrupt.
if cpu.InNMI {
return false
}
if cpu.NMI != nil && cpu.NMI.CheckNMI() {
cpu.HALT = false
cpu.SP -= 2
cpu.writeU16(cpu.SP, cpu.PC)
cpu.PC = 0x0066
cpu.IFF2 = cpu.IFF1
cpu.IFF1 = false
cpu.InNMI = true
return true
}
// check maskable interrupt.
if cpu.INT == nil {
return false
}
d := cpu.INT.CheckINT()
if d == nil {
return false
}
switch cpu.IM {
case 1:
cpu.HALT = false
cpu.SP -= 2
cpu.writeU16(cpu.SP, cpu.PC)
cpu.PC = 0x0038
return true
case 2:
if n := len(d); n != 1 {
cpu.failf("interruption data should be 1 byte in IM 2")
if n == 0 {
return false
}
}
cpu.HALT = false
cpu.SP -= 2
cpu.writeU16(cpu.SP, cpu.PC)
cpu.PC = toU16(d[0]&0xfe, cpu.IR.Hi)
return true
}
// interrupt with IM 0
if len(d) == 0 {
cpu.failf("interruption data should be longer 1 byte in IM 0")
return false
}
cpu.HALT = false
ms := memSrc(d)
cpu.executeOne(&ms)
return true
}
func (cpu *CPU) invalidCode(code ...uint8) {
cpu.warnf("detect invalid code, ignored: %X", code)
}