-
Notifications
You must be signed in to change notification settings - Fork 4
/
cpu.go
261 lines (226 loc) · 4.78 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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
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)
}
// im0data is pseudo Memory module be used when IM0 interrupt occurred.
type im0data struct {
start uint16
end uint16
data []uint8
base Memory
}
func newIm0data(pc uint16, d []uint8, base Memory) *im0data {
return &im0data{
start: pc,
end: pc + uint16(len(d)-1),
data: d,
base: base,
}
}
func (im0 *im0data) Get(addr uint16) uint8 {
if addr < im0.start || addr > im0.end {
// delegate to base Memory for out of range.
return im0.base.Get(addr)
}
return im0.data[addr-im0.start]
}
func (im0 *im0data) Set(addr uint16, value uint8) {
if addr >= im0.start && addr <= im0.end {
// invalid opepration, nothing to do.
return
}
// delegate to base Memory for out of range.
im0.base.Set(addr, value)
}
//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) flagPV() bool {
return cpu.AF.Lo&maskPV != 0
}
func (cpu *CPU) flagS() bool {
return cpu.AF.Lo&maskS != 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) fetch2() (l, h uint8) {
l = cpu.Memory.Get(cpu.PC)
cpu.PC++
h = cpu.Memory.Get(cpu.PC)
cpu.PC++
return l, h
}
func (cpu *CPU) fetch16() uint16 {
l := cpu.Memory.Get(cpu.PC)
cpu.PC++
h := cpu.Memory.Get(cpu.PC)
cpu.PC++
return (uint16(h) << 8) | uint16(l)
}
// fetchM1 fetches a byte for M1 cycle.
func (cpu *CPU) fetchM1() uint8 {
c := cpu.Memory.Get(cpu.PC)
cpu.PC++
// increment refresh counter
rc := cpu.IR.Lo
cpu.IR.Lo = rc&0x80 | (rc+1)&0x7f
return c
}
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)
}()
cpu.HALT = false
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() {
// try interruptions.
if cpu.Interrupt != nil && cpu.processInterrupt() {
cpu.Interrupt = nil
return
}
// execute an op-code.
cpu.executeOne()
}
func (cpu *CPU) processInterrupt() bool {
if cpu.Interrupt.Type == NMIType {
cpu.SP -= 2
cpu.writeU16(cpu.SP, cpu.PC)
cpu.PC = 0x0066
cpu.IFF2 = cpu.IFF1
cpu.IFF1 = false
return true
}
// check maskable interrupt.
if !cpu.IFF1 {
return false
}
switch cpu.IM {
case 0:
// Interrupt with IM 0
if len(cpu.Interrupt.Data) > 0 {
savedMemory := cpu.Memory
cpu.Memory = newIm0data(cpu.PC, cpu.Interrupt.Data, savedMemory)
cpu.executeOne()
cpu.Memory = savedMemory
cpu.IFF1 = false
}
return true
case 1:
// Interrupt with IM 1
cpu.SP -= 2
cpu.writeU16(cpu.SP, cpu.PC)
cpu.PC = 0x0038
cpu.IFF1 = false
return true
case 2:
// Interrupt with IM 2
if len(cpu.Interrupt.Data) > 0 {
cpu.SP -= 2
cpu.writeU16(cpu.SP, cpu.PC)
// The LSB of interruption vector is ignored in IM 2
cpu.PC = cpu.readU16(toU16(cpu.Interrupt.Data[0]&0xfe, cpu.IR.Hi))
cpu.IFF1 = false
}
return true
default:
return false
}
}
func (cpu *CPU) invalidCode(code ...uint8) {
cpu.warnf("detect invalid code, ignored: %X", code)
}