-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathtraceApplecorn.go
294 lines (270 loc) · 8.06 KB
/
traceApplecorn.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
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
package izapple2
import (
"fmt"
)
/*
See:
https://github.com/bobbimanners/Applecorn
Chapter 45 https://raw.githubusercontent.com/bobbimanners/Applecorn/main/Manuals/Acorn%20BBC%20Micro%20User%20Guide.pdf
http://beebwiki.mdfs.net/Category:MOS_API
http://beebwiki.mdfs.net/OSBYTEs
http://mdfs.net/Docs/Comp/BBC/Osbyte00
*/
type traceApplecorn struct {
a *Apple2
skipConsole bool
osbyteNames [256]string
call mosCallData
wasInKernel bool
}
type mosCallData struct {
api uint16
a, x, y uint8
skipLog bool
}
const (
applecornKernelStart uint16 = 0xc000 // Code above this is out of BBC territory
applecornTraceAreaStart uint16 = 0xd000
applecornNoCaller uint16 = 0xffff
applecornRomTitle uint16 = 0x8009
)
func newTraceApplecorn(skipConsole bool) *traceApplecorn {
var t traceApplecorn
t.skipConsole = skipConsole
t.osbyteNames[0x02] = "select input device"
t.osbyteNames[0x03] = "select output device"
t.osbyteNames[0x7c] = "clear escape condition"
t.osbyteNames[0x7d] = "set escape condition"
t.osbyteNames[0x7e] = "ack detection of ESC"
t.osbyteNames[0x7f] = "check for end-of-file on an opened file"
t.osbyteNames[0x80] = "read ADC channel"
t.osbyteNames[0x81] = "read key with time lim"
t.osbyteNames[0x82] = "read high order address"
t.osbyteNames[0x83] = "read bottom of user mem"
t.osbyteNames[0x84] = "read top of user mem"
t.osbyteNames[0x85] = "top user mem for mode"
t.osbyteNames[0x86] = "read cursor pos"
t.osbyteNames[0x8b] = "set filing system options"
t.osbyteNames[0xDA] = "clear VDU queue"
return &t
}
func (t *traceApplecorn) connect(a *Apple2) {
t.a = a
}
func (t *traceApplecorn) inspect() {
if t.a.dmaActive {
return
}
if !t.a.mmu.altMainRAMActiveRead {
// We want to trace only the activity on the Acorn memory space
return
}
pc, sp := t.a.cpu.GetPCAndSP()
if pc == 0x8000 {
activeROM := t.getTerminatedString(applecornRomTitle, 0)
regA, _, _, _ := t.a.cpu.GetAXYP()
fmt.Printf("BBC MOS call to $%04x LANGUAGE(ROM=\"%s\", A=%02x)\n", pc, activeROM, regA)
} else if pc == 0x8003 {
activeROM := t.getTerminatedString(applecornRomTitle, 0)
service, _, regY, _ := t.a.cpu.GetAXYP()
switch service {
case 4: // OSCLI
address := t.a.mmu.peekWord(0xf2 + uint16(regY))
command := t.getTerminatedString(address, 0x0d)
fmt.Printf("BBC MOS call to $%04x SERVICE_OSCLI(ROM=\"%s\", A=%v, \"%s\")\n",
pc, activeROM, service, command)
case 6: // Error
address := t.a.mmu.peekWord(0xfd)
faultNumber := t.a.mmu.Peek(address)
faultMessage := address + 1
faultString := t.getTerminatedString(faultMessage, 0)
fmt.Printf("BBC MOS call to $%04x SERVICE_ERROR(ROM=\"%s\", A=%v, #=%v, \"%s\")\n",
pc, activeROM, service, faultNumber, faultString)
case 7: // OSBYTE
pA := t.a.mmu.Peek(0xef)
pX := t.a.mmu.Peek(0xf0)
pY := t.a.mmu.Peek(0xf1)
fmt.Printf("BBC MOS call to $%04x SERVICE_OSBYTE%02x(ROM=\"%s\", A=%v, pX=%02x, pY=%02x)\n",
pc, pA, activeROM, service, pX, pY)
case 8: // OSWORD
pA := t.a.mmu.Peek(0xef)
fmt.Printf("BBC MOS call to $%04x SERVICE_OSWORD%02x(ROM=\"%s\", A=%v)\n",
pc, pA, activeROM, service)
case 9: // *HELP
address := t.a.mmu.peekWord(0xf2 + uint16(regY))
command := t.getTerminatedString(address, 0x0d)
fmt.Printf("BBC MOS call to $%04x SERVICE_HELP(ROM=\"%s\", A=%v, \"%s\")\n",
pc, activeROM, service, command)
default:
fmt.Printf("BBC MOS call to $%04x SERVICE(ROM=\"%s\", A=%v)\n",
pc, activeROM, service)
}
}
inKernel := pc >= applecornKernelStart
if !t.wasInKernel && inKernel && pc >= applecornTraceAreaStart {
regA, regX, regY, _ := t.a.cpu.GetAXYP()
s := "UNKNOWN"
skip := false
// Page 2 vectors
switch pc {
case t.vector(0x0208): // OSCLI vector
pc = 0xfff7
case t.vector(0x020A): // OSBYTE vector
pc = 0xfff4
case t.vector(0x020C): // OSWORD vector
pc = 0xfff1
case t.vector(0x020E): // OSWRCH vector
pc = 0xffee
case t.vector(0x0210): // OSRDCH vector
pc = 0xffe0
case t.vector(0x0212): // OSFILE vector
pc = 0xffdd
case t.vector(0x0214): // OSARGS vector
pc = 0xffda
case t.vector(0x0216): // OSBGET vector
pc = 0xffd7
case t.vector(0x0218): // OSBPUT vector
pc = 0xffd4
case t.vector(0x021A): // OSGBPB vector
pc = 0xffd1
case t.vector(0x021C): // OSFIND vector
pc = 0xffce
case t.vector(0xfffe): // BRK vector
pc = 0xfffe
}
// Jump area
switch pc {
case 0xffb9:
s = "OSDRM(?)"
case 0xffbc:
s = "VDUCHR(?)"
case 0xffbf:
s = "OSEVEN(?)"
case 0xffc2:
s = "OSINIT(?)"
case 0xffc5:
s = "OSREAD(?)"
case 0xffc8:
ch := ""
if regA >= 0x20 && regA < 0x7f {
ch = string(regA)
}
s = fmt.Sprintf("OSNWRCH(A=%02x, '%v')", regA, ch)
skip = t.skipConsole
case 0xffcb:
s = "OSNRDCH()"
skip = t.skipConsole
case 0xffce:
if regA == 0 {
s = fmt.Sprintf("OSFIND('close',HANDLE=%v", regY)
} else {
filenameAddress := uint16(regX) + uint16(regY)<<8
filename := t.getTerminatedString(filenameAddress, 0x0d)
s = fmt.Sprintf("OSFIND('open',FILE='%s')", filename)
}
case 0xffd1:
s = "OSGBPB(?)"
case 0xffd4:
s = "OSBPUT(?)"
case 0xffd7:
s = "OSBGET(?)"
case 0xffda:
s = "OSARGS(?)"
s = fmt.Sprintf("OSARGS(HANDLE=%v,A=%02x)", regY, regA)
case 0xffdd:
controlBlock := uint16(regX) + uint16(regY)<<8
filenameAddress := t.a.mmu.peekWord(controlBlock)
filename := t.getTerminatedString(filenameAddress, 0x0d)
s = fmt.Sprintf("OSFILE(A=%02x,FILE='%s')", regA, filename)
case 0xffe0:
s = "OSRDCH()"
skip = t.skipConsole
case 0xffe3: // This fallbacks to OSWRCH
ch := ""
if regA >= 0x20 && regA < 0x7f {
ch = string(regA)
}
s = fmt.Sprintf("OSASCI(A=%02x, '%v')", regA, ch)
skip = t.skipConsole
case 0xffe7: // This fallbacks to OSWRCH
s = "OSNEWL()"
skip = t.skipConsole
case 0xffec: // This fallbacks to OSWRCH
skip = t.skipConsole
s = "OSNECR()"
case 0xffee:
ch := ""
if regA >= 0x20 && regA < 0x7f {
ch = string(regA)
}
s = fmt.Sprintf("OSWRCH(A=%02x, '%v')", regA, ch)
skip = t.skipConsole
case 0xfff1:
xy := uint16(regX) + uint16(regY)<<8
switch regA {
case 0: // Read line from input
lineAddress := t.a.mmu.peekWord(xy)
maxLength := t.a.mmu.Peek(xy + 2)
s = fmt.Sprintf("OSWORD('read line';A=%02x,XY=%04x,BUF=%04x,MAX=%02x)", regA, xy, lineAddress, maxLength)
default:
s = fmt.Sprintf("OSWORD(A=%02x,XY=%04x)", regA, xy)
}
case 0xfff4:
s = fmt.Sprintf("OSBYTE('%s';A=%02x,X=%02x,Y=%02x)", t.osbyteNames[regA], regA, regX, regY)
case 0xfff7:
xy := uint16(regX) + uint16(regY)<<8
command := t.getTerminatedString(xy, 0x0d)
s = fmt.Sprintf("OSCLI(\"%s\")", command)
case 0xfffe:
address := t.a.mmu.peekWord(0x100+uint16(sp+2)) - 1
faultNumber := t.a.mmu.Peek(address)
faultMessage := address + 1
faultString := t.getTerminatedString(faultMessage, 0)
s = fmt.Sprintf("BRK(number=%v,message='%s')", faultNumber, faultString)
}
if s == "UNKNOWN" && t.skipConsole {
// Let's also skip not known calls
skip = true
}
t.call.api = pc
t.call.a = regA
t.call.x = regX
t.call.y = regY
t.call.skipLog = skip
if !skip {
fmt.Printf("BBC MOS call to $%04x %s ", pc, s)
}
}
if t.wasInKernel && !inKernel && !t.call.skipLog {
// Returning from the call
regA, regX, regY, _ := t.a.cpu.GetAXYP()
s := ""
switch t.call.api {
case 0xfff1: // OSWORD
cbAddress := uint16(t.call.x) + uint16(t.call.y)<<8
switch t.call.a {
case 0: // Read line from input
lineAddress := t.a.mmu.peekWord(cbAddress)
line := t.getTerminatedString(lineAddress, '\r')
s = fmt.Sprintf(",line='%s',cbaddress=%04x,lineAddress=%04x", line, cbAddress, lineAddress)
}
}
fmt.Printf("=> (A=%02x,X=%02x,Y=%02x%s)\n", regA, regX, regY, s)
}
t.wasInKernel = inKernel
}
func (t *traceApplecorn) getTerminatedString(address uint16, terminator uint8) string {
s := ""
for {
ch := t.a.mmu.Peek(address)
if ch == terminator {
break
}
s += string(ch)
address++
}
return s
}
func (t *traceApplecorn) vector(address uint16) uint16 {
return t.a.mmu.peekWord(address)
}