Keelan Lightfoot 1 рік тому
коміт
15ed7ad70d
9 змінених файлів з 921 додано та 0 видалено
  1. BIN
      331/U36.rom
  2. BIN
      331/U37.rom
  3. BIN
      331/U38.rom
  4. BIN
      331/U44.rom
  5. BIN
      331/U45.rom
  6. BIN
      331/U46.rom
  7. 61
    0
      331/map.json
  8. 3
    0
      go.mod
  9. 857
    0
      main.go






+ 61
- 0
331/map.json Переглянути файл

@@ -0,0 +1,61 @@
1
+{
2
+	"roms": [
3
+		{
4
+			"file": "U36.rom",
5
+			"start": "0x000000",
6
+			"end":   "0x003FFE",
7
+			"version": "3.30",
8
+			"device": "27C64",
9
+			"checksum": "0x52EA"
10
+		},
11
+		{
12
+			"file": "U44.rom",
13
+			"start": "0x000001",
14
+			"end": "0x003FFF",
15
+			"version": "3.31",
16
+			"device": "27C64",
17
+			"checksum": "0xD16B"
18
+		},
19
+		
20
+		{
21
+			"file": "U37.rom",
22
+			"start": "0x090000",
23
+			"end": "0x09FFFE",
24
+			"version": "3.30",
25
+			"device": "AT27C256R",
26
+			"checksum": "0x2500"
27
+		},
28
+			{
29
+			"file": "U45.rom",
30
+			"start": "0x090001",
31
+			"end": "0x09FFFF",
32
+			"version": "3.30",
33
+			"device": "AT27C256R",
34
+			"checksum": "0x8E92"
35
+		},
36
+		
37
+			{
38
+			"file": "U38.rom",
39
+			"start": "0x0A0000",
40
+			"end": "0x0AFFFE",
41
+			"version": "3.30",
42
+			"device": "AT27C256R",
43
+			"checksum": "0xE240"
44
+		},
45
+		{
46
+			"file": "U46.rom",
47
+			"start": "0x0A0001",
48
+			"end": "0x0AFFFF",
49
+			"version": "3.30",
50
+			"device": "AT27C256R",
51
+			"checksum": "0x0717"
52
+		}
53
+	],
54
+	"labels": [
55
+		{"address": "0x0034AA", "label": "CLEARSCREEN"},
56
+		{"address": "0x095BD8", "label": "SLEEP1SECOND"},
57
+		{"address": "0x0034C8", "label": "SETSEGMENTS"}
58
+	]
59
+}
60
+
61
+

+ 3
- 0
go.mod Переглянути файл

@@ -0,0 +1,3 @@
1
+module code.beefchicken.com/keelan/disasm4000sx
2
+
3
+go 1.20

+ 857
- 0
main.go Переглянути файл

@@ -0,0 +1,857 @@
1
+package main
2
+
3
+import (
4
+	"fmt"
5
+	"math"
6
+	"os"
7
+	//	"sort"
8
+	"strings"
9
+	//"code.beefchicken.com/68kdisasm"
10
+	"encoding/json"
11
+	"path/filepath"
12
+)
13
+
14
+//var externs = make([]int, 0)
15
+
16
+type romInfo struct {
17
+	Roms []struct {
18
+		File     string `json:"file"`
19
+		Start    string `json:"start"`
20
+		End      string `json:"end"`
21
+		Version  string `json:"version"`
22
+		Device   string `json:"device"`
23
+		Checksum string `json:"checksum"`
24
+	} `json:"roms"`
25
+	Labels []struct {
26
+		Address string `json:"address"`
27
+		Label   string `json:"label"`
28
+	} `json:"labels"`
29
+}
30
+
31
+type memBlock struct {
32
+	lowFile  string
33
+	highFile string
34
+	start    uint32
35
+	end      uint32
36
+}
37
+
38
+type output struct {
39
+	mnemonic string
40
+	operands string
41
+	comment  string
42
+}
43
+
44
+type call struct {
45
+	function     uint32
46
+	instrAddr    uint32
47
+	computedAddr uint32
48
+	targetAddr   uint32
49
+	native       bool
50
+}
51
+
52
+func main() {
53
+
54
+	if len(os.Args) < 2 {
55
+		fmt.Printf("Usage: %s <path to rom directory>\n", os.Args[0])
56
+		os.Exit(1)
57
+	}
58
+	romDir := os.Args[1]
59
+	romMapFilename := filepath.Join(romDir, "map.json")
60
+	info := &romInfo{}
61
+
62
+	b, _ := os.ReadFile(romMapFilename)
63
+
64
+	fmt.Printf("Loading ROM info from '%s'...\n", romMapFilename)
65
+	err := json.Unmarshal(b, &info)
66
+	if err != nil {
67
+		fmt.Printf("unable to load rom map from '%s': %v\n", romMapFilename, err)
68
+		os.Exit(1)
69
+	}
70
+
71
+	d := NewDisassembler()
72
+
73
+	for _, rom := range info.Roms {
74
+		var start, end uint32
75
+		var checksum int
76
+
77
+		_, err := fmt.Sscanf(rom.Start, "0x%X", &start)
78
+		if err != nil {
79
+			fmt.Printf("Unable to parse start address '%s' for rom '%s'!\n", rom.Start, rom.File)
80
+			os.Exit(1)
81
+		}
82
+		_, err = fmt.Sscanf(rom.End, "0x%X", &end)
83
+		if err != nil {
84
+			fmt.Printf("Unable to parse end address '%s' for rom '%s'!\n", rom.End, rom.File)
85
+			os.Exit(1)
86
+		}
87
+		_, err = fmt.Sscanf(rom.Checksum, "0x%X", &checksum)
88
+		if err != nil {
89
+			fmt.Printf("Unable to checksum '%s' for rom '%s'!\n", rom.Checksum, rom.File)
90
+			os.Exit(1)
91
+		}
92
+		file := filepath.Join(romDir, rom.File)
93
+		fmt.Printf("Loading '%s' (0x%06X-0x%06X)...\n", file, start, end)
94
+		d.AddRom(file, start, end, checksum)
95
+	}
96
+
97
+	for _, label := range info.Labels {
98
+
99
+		var address uint32
100
+		_, err := fmt.Sscanf(label.Address, "0x%X", &address)
101
+		if err != nil {
102
+			fmt.Printf("Unable to parse address '%s' for label '%s!\n", label.Address, label.Label)
103
+			os.Exit(1)
104
+		}
105
+		fmt.Printf("Adding label %s = 0x%06X\n", label.Label, address)
106
+		d.AddLabel(address, label.Label)
107
+
108
+	}
109
+
110
+	// look for the two kinds of header functions and queue
111
+	// the vm code up for disassembly
112
+	queue := []uint32{} //0xe6d // 0xa0435 <- week // 0x00001631 <- WRNO FIX // 0x0009324d
113
+	for _, block := range d.roms {
114
+		for i := block.start; i <= block.end-3; i++ {
115
+			inst := d.read32(i)
116
+			if inst == 0x4EB82144 || inst == 0x4EB82164 || inst == 0x4EB82102 {
117
+				queue = append(queue, i)
118
+			}
119
+		}
120
+	}
121
+
122
+	dests := make(map[uint32][]call)
123
+
124
+	// first pass to gather all of the JSR.68Ks
125
+	for _, addr := range queue {
126
+		externs, _ := d.disassemble(addr, nil, true)
127
+		for _, c := range externs {
128
+			if !c.native {
129
+				dests[c.targetAddr] = append(dests[c.targetAddr], c)
130
+			}
131
+		}
132
+	}
133
+
134
+	// disassemble with output
135
+	for _, vpc := range queue {
136
+		d.disassemble(vpc, dests, false)
137
+	}
138
+
139
+}
140
+
141
+type disassembler struct {
142
+	pc        uint32
143
+	labels    map[uint32]string
144
+	roms      []*memBlock
145
+	prog      []byte
146
+	caseStmts map[uint32]caseStmt
147
+}
148
+
149
+type caseStmt struct {
150
+	switchAddr uint32
151
+	keys       []byte
152
+}
153
+
154
+func NewDisassembler() *disassembler {
155
+	return &disassembler{
156
+		labels:    make(map[uint32]string),
157
+		roms:      make([]*memBlock, 0),
158
+		prog:      make([]byte, 0),
159
+		caseStmts: make(map[uint32]caseStmt),
160
+	}
161
+}
162
+
163
+func (d *disassembler) AddLabel(addr uint32, l string) {
164
+	d.labels[addr] = l
165
+}
166
+
167
+func addx(x, y uint16, xf *uint16) uint16 {
168
+	t := x + y + *xf
169
+	if int(t) < int(x)+int(y)+int(*xf) {
170
+		*xf = 1
171
+	} else {
172
+		*xf = 0
173
+	}
174
+	return t
175
+}
176
+
177
+func (d *disassembler) AddRom(file string, start uint32, end uint32, checksum int) error {
178
+
179
+	if len(d.prog) < int(end)+1 {
180
+		newProg := make([]byte, end+1)
181
+		copy(newProg, d.prog)
182
+		d.prog = newProg
183
+	}
184
+
185
+	b, err := os.ReadFile(file)
186
+	if err != nil {
187
+		return err
188
+	}
189
+
190
+	var cs uint16 = 0
191
+	var cs2 uint16 = 0
192
+
193
+	var xf uint16
194
+
195
+	for i := 0; i < len(b); i++ {
196
+		d.prog[int(start)+i*2] = b[i]
197
+		if i < len(b)-4 {
198
+			cs = addx(uint16(b[i]), cs, &xf)
199
+		}
200
+		cs2 += uint16(b[i])
201
+	}
202
+	cs = ^cs
203
+
204
+	internalStoredChecksum := uint16(b[len(b)-1])<<8 + uint16(b[len(b)-2])
205
+
206
+	fmt.Printf("%s INTERNAL CHECKSUM: GOT 0x%04X EXPECTED 0x%04X\n", file, cs, internalStoredChecksum)
207
+	fmt.Printf("%s EXTERNAL CHECKSUM: GOT 0x%04X EXPECTED 0x%04X\n", file, cs2, checksum)
208
+
209
+	// check if we've already loaded the pair for this rom
210
+	var block *memBlock
211
+	for _, rom := range d.roms {
212
+		if start&0xFFFFFFFE == rom.start&0xFFFFFFFE {
213
+			block = rom
214
+			if start < block.start {
215
+				block.start = start
216
+			}
217
+			if end > block.end {
218
+				block.end = end
219
+			}
220
+			break
221
+		}
222
+	}
223
+
224
+	if block == nil {
225
+		// this is the first ROM of this pair, so create the block
226
+		block = &memBlock{
227
+			start: start,
228
+			end:   end,
229
+		}
230
+		d.roms = append(d.roms, block)
231
+	}
232
+
233
+	if start%2 == 0 {
234
+		block.lowFile = file
235
+	} else {
236
+		block.highFile = file
237
+	}
238
+
239
+	return nil
240
+}
241
+
242
+func (d *disassembler) read8(addr uint32) byte {
243
+	return d.prog[addr]
244
+}
245
+
246
+func (d *disassembler) read16(addr uint32) uint16 {
247
+	return uint16(d.prog[addr])<<8 | uint16(d.prog[addr+1])
248
+}
249
+
250
+func (d *disassembler) read32(addr uint32) uint32 {
251
+	return uint32(d.prog[addr])<<24 | uint32(d.prog[addr+1])<<16 | uint32(d.prog[addr+2])<<8 | uint32(d.prog[addr+3])
252
+}
253
+
254
+func (d *disassembler) readFloat64(addr uint32) float64 {
255
+	return math.Float64frombits(d.read64(addr))
256
+}
257
+
258
+func (d *disassembler) read64(addr uint32) uint64 {
259
+	var ret uint64
260
+
261
+	ret |= uint64(d.prog[addr]) << 56
262
+	ret |= uint64(d.prog[addr+1]) << 48
263
+	ret |= uint64(d.prog[addr+2]) << 40
264
+	ret |= uint64(d.prog[addr+3]) << 32
265
+	ret |= uint64(d.prog[addr+4]) << 24
266
+	ret |= uint64(d.prog[addr+5]) << 16
267
+	ret |= uint64(d.prog[addr+6]) << 8
268
+	ret |= uint64(d.prog[addr+7])
269
+
270
+	return ret
271
+}
272
+
273
+func (d *disassembler) disassemble(start uint32, dests map[uint32][]call, silent bool) ([]call, error) {
274
+
275
+	externs := make([]call, 0)
276
+
277
+	signature := d.read32(start)
278
+
279
+	var headerInfo string
280
+
281
+	// 13fff = no branch
282
+	// 14000 = branch
283
+	// 14001 = branch
284
+
285
+	if signature == 0x4EB82102 {
286
+		if !silent {
287
+			stackMax := 0x17fff
288
+			vsp := stackMax + 1 - int(d.read16(start+4))
289
+			if vsp < 0x14000 {
290
+				vsp -= 0xc000
291
+			}
292
+
293
+			headerInfo = fmt.Sprintf("; A0 = $%06X; (A0) = $%04X; A6 = $%06X\n\n", 0x4000, vsp, vsp)
294
+
295
+			// A0 = A6
296
+		}
297
+		d.pc = start + 6
298
+	} else if signature == 0x4EB82144 {
299
+		if !silent {
300
+			a6 := 0
301
+			a1 := a6 + int(d.read16(start+4)) - 1
302
+			a0 := 0x4000 + (int(d.read8(start+6)) << 2)
303
+
304
+			//(a0) = a1
305
+			if a1 >= 0 {
306
+				headerInfo = fmt.Sprintf("; A0 = $%06X; (A0) = SP + $%X\n\n", a0, a1)
307
+			} else {
308
+				headerInfo = fmt.Sprintf("; A0 = $%06X; (A0) = SP - $%X\n\n", a0, -a1)
309
+			}
310
+		}
311
+		d.pc = start + 7
312
+	} else if signature == 0x4EB82164 {
313
+
314
+		//     movea.l    (SP)+,A5
315
+		//     move.w     (A5)+,D0w
316
+		//     move.w     (A5)+,D1w
317
+		//     moveq      #0x0,D2
318
+		//     move.b     (A5)+,D2b
319
+		//     lsl.w      #0x2,D2w
320
+		//     movea.w    #0x4000,A0
321
+		//     adda.w     D2w,A0
322
+		//     move.l     (A0),-(SP)=>DAT_00004000
323
+		//     move.w     A0w,-(SP)=>local_2
324
+		//     movea.l    A6,A1
325
+		//     adda.w     D0w,A1
326
+		//     subq.l     #0x1,A1
327
+		//     move.l     A1,(A0)=>DAT_00004000
328
+		//     movea.l    A6,A0
329
+		//     suba.w     D1w,A6
330
+		//     tst.w      D0w
331
+		//     beq.b      LAB_00002196
332
+		//     movea.l    A6,A1
333
+		//     lsr.w      #0x1,D0w
334
+		//     subq.w     #0x1,D0w
335
+		// LAB_00002190
336
+		//     move.w     (A0)+,(A1)+
337
+		//     dbf        D0w,LAB_00002190
338
+		// LAB_00002196
339
+		//     bra.w      FUN_00002000
340
+
341
+		if !silent {
342
+			d0 := uint32(d.read16(start + 4))
343
+			a6 := uint32(0)
344
+			a1 := a6 + d0 - 1
345
+			d1 := uint32(d.read16(start + 6))
346
+			a0 := 0x4000 + (int(d.read8(start+8)) << 2)
347
+
348
+			headerInfo = fmt.Sprintf("; P0 = $%04X; P1 = $%04X; P2 = $%02X\n", d.read16(start+4), d.read16(start+6), d.read8(start+8))
349
+
350
+			if d0 > 0 {
351
+				d0 = (d0 << 1) - 1
352
+			}
353
+
354
+			if a1 >= 0 {
355
+				headerInfo += fmt.Sprintf("; A0 = $%06X; (A0) = A6 + $%X; D1 = $%06X\n\n", a0, a1, d1)
356
+			} else {
357
+				headerInfo += fmt.Sprintf("; A0 = $%06X; (A0) = A6 - $%X; D1 = $%06X\n\n", a0, -a1, d1)
358
+			}
359
+		}
360
+		d.pc = start + 9
361
+	}
362
+
363
+	if !silent {
364
+
365
+		fmt.Printf("\n; ---------------- Begin %04X Function $%06X ---------------- \n\n%s", signature&0xFFFF, start, headerInfo)
366
+		for _, dest := range dests[start] {
367
+			fmt.Printf("; called from $%06X:$%06X\n", dest.function, dest.instrAddr)
368
+
369
+		}
370
+
371
+		fmt.Printf("\n%6s  %-14s  %-8s\t%-20s%s\n\n", "; addr", "bytes", "opcode", "operands", "comment")
372
+
373
+	}
374
+
375
+	var err error
376
+
377
+	for {
378
+
379
+		instPC := d.pc
380
+		inst := d.prog[d.pc]
381
+		d.pc++
382
+		o := output{}
383
+
384
+		if cs, ok := d.caseStmts[instPC]; ok {
385
+			if len(cs.keys) > 0 {
386
+				o.mnemonic = fmt.Sprintf("CASE")
387
+				o.comment = fmt.Sprintf("SWITCH $%06X", cs.switchAddr)
388
+				keysStr := make([]string, len(cs.keys))
389
+				for i, v := range cs.keys {
390
+					keysStr[i] = fmt.Sprintf("$%02X", v)
391
+				}
392
+				o.operands = strings.Join(keysStr, ", ")
393
+
394
+				d.pc += 1 + uint32(len(cs.keys))
395
+			} else {
396
+				o.mnemonic = fmt.Sprintf("DEFAULT")
397
+				o.comment = fmt.Sprintf("SWITCH $%06X", cs.switchAddr)
398
+			}
399
+			delete(d.caseStmts, instPC)
400
+		} else if inst == 0x01 {
401
+			o.mnemonic = "NOP"
402
+		} else if inst == 0x02 {
403
+			o.mnemonic = "INLINE"
404
+
405
+			if d.pc%2 != 0 {
406
+				d.pc++
407
+			}
408
+			//startPC := d.pc
409
+			machineCodeBytes := make([]byte, 0)
410
+			for {
411
+				machineCodeBytes = append(machineCodeBytes, d.prog[d.pc])
412
+				if d.read16(d.pc) == 0x4e75 {
413
+					machineCodeBytes = append(machineCodeBytes, d.prog[d.pc+1])
414
+					d.pc += 2
415
+					break
416
+				}
417
+				d.pc++
418
+			}
419
+
420
+			// 	bus := disasm.NewAddressBus(d.prog)
421
+			// 			doneBytes := 0
422
+			//
423
+			// 			for {
424
+			// 				s, n := disasm.Disassemble(disasm.M68K_CPU_TYPE_68000, int32(startPC+doneBytes), bus)
425
+			// 				fmt.Println(s)
426
+			// 				doneBytes+=int(n)
427
+			// 				if doneBytes == len(machineCodeBytes) {
428
+			// 					break
429
+			// 				}
430
+			// 			}
431
+
432
+			//fmt.Printf("%d % 02X\n",len(machineCodeBytes),machineCodeBytes)
433
+			o.operands = fmt.Sprintf("%d Bytes", len(machineCodeBytes)) //d.getOpr32())
434
+		} else if inst == 0x03 {
435
+			op := d.getOpr8()
436
+			if op <= 0x30 {
437
+				o, externs, err = d.handle2090(d.pc-2, op, externs)
438
+			} else {
439
+				err = fmt.Errorf("bad 0x03 instruction")
440
+			}
441
+		} else if inst >= 0x04 && inst <= 0x07 {
442
+			o.mnemonic = "PUSH.10"
443
+			o.operands = fmt.Sprintf("$%03X", d.getOpr10())
444
+		} else if inst >= 0x08 && inst <= 0x0F {
445
+			o.mnemonic = fmt.Sprintf("?_$%02X", inst)
446
+			op1 := d.getOpr16()
447
+			op2 := d.getOpr16()
448
+
449
+			if inst&(1<<2) == 0 && d.prog[d.pc] == 0 {
450
+				d.pc++
451
+				o.operands = fmt.Sprintf("$%04X, $%04X, $0", op1, op2)
452
+			} else {
453
+				o.operands = fmt.Sprintf("$%04X, $%04X", op1, op2)
454
+			}
455
+
456
+		} else if inst >= 0x10 && inst <= 0x17 {
457
+			o.mnemonic = "BR"
458
+			jd := int(d.getSignedOpr11())
459
+			o.operands = fmt.Sprintf("$%03X", jd)
460
+			o.comment = fmt.Sprintf("=> $%06X", uint32(int(d.pc)+jd))
461
+		} else if inst >= 0x18 && inst <= 0x1F {
462
+			o.mnemonic = "BRZ"
463
+			jd := int(d.getSignedOpr11())
464
+			o.operands = fmt.Sprintf("$%03X", jd)
465
+			o.comment = fmt.Sprintf("=> $%06X", uint32(int(d.pc)+jd))
466
+		} else if inst >= 0x20 && inst <= 0x3F {
467
+			o, externs, err = d.handle2090(d.pc-1, inst-0x20, externs)
468
+		} else if inst >= 0x40 && inst <= 0x7f {
469
+			o.mnemonic = "PUSH.6"
470
+			o.operands = fmt.Sprintf("$%02X", d.getOpr6())
471
+		} else if inst >= 0x80 && inst <= 0xFF {
472
+			w := []string{"B", "W", "L", "F"}[inst&0x03]
473
+
474
+			if (inst & 0x10) == 0 {
475
+				o.mnemonic = fmt.Sprintf("READ.%s", w)
476
+			} else {
477
+				o.mnemonic = fmt.Sprintf("WRITE.%s", w)
478
+			}
479
+
480
+			if inst >= 0xc0 {
481
+				o.operands = fmt.Sprintf("$%04X", d.getOpr16())
482
+			} else {
483
+				o.operands = fmt.Sprintf("$%02X", d.getOpr8())
484
+			}
485
+
486
+			if (inst & 0x10) == 0 {
487
+				if d.prog[d.pc] == 0 {
488
+					d.pc++
489
+				}
490
+			}
491
+		} else {
492
+			err = fmt.Errorf("bad instruction")
493
+		}
494
+
495
+		//	var hex string
496
+
497
+		// if !silent {
498
+		// 			if d.pc-instPC < 6 {
499
+		// 				hex = fmt.Sprintf("% 02X", d.prog[instPC:d.pc])
500
+		// 			} else {
501
+		// 				hex = fmt.Sprintf("% 02X...", d.prog[instPC:instPC+4])
502
+		// 			}
503
+		// 			fmt.Printf("%06X  % -14s  ", instPC, hex)
504
+		// 		}
505
+		//
506
+		// 		if err != nil {
507
+		// 			break
508
+		// 		}
509
+		//
510
+		// 		if !silent {
511
+		// 			var comment string
512
+		// 			if o.comment != "" {
513
+		// 				comment = "; " + o.comment
514
+		// 			}
515
+		// 			fmt.Printf("%-8s\t%-20s%s\n", o.mnemonic, o.operands, comment)
516
+		// 		}
517
+
518
+		if err == nil {
519
+			if !silent {
520
+				d.printInstr(instPC, o)
521
+			}
522
+		}
523
+
524
+		if err != nil {
525
+			if !silent {
526
+				fmt.Printf("Disassembly failed at $%06X\n", instPC)
527
+			}
528
+			break
529
+		}
530
+
531
+		if o.mnemonic == "RETURN" {
532
+			break
533
+		}
534
+
535
+	}
536
+	for i := range externs {
537
+		externs[i].function = start
538
+	}
539
+
540
+	if !silent {
541
+		if err != nil {
542
+			fmt.Printf("\n; ---------------- Disassemly Terminated: %v ---------------- \n", err)
543
+
544
+		} else {
545
+			fmt.Printf("\n; ---------------- End Function $%06X ---------------- \n\n\n", start)
546
+		}
547
+	}
548
+	return externs, err
549
+}
550
+
551
+func (d *disassembler) printInstr(instPC uint32, o output) {
552
+
553
+	// var hex, comment string
554
+	// 		if o.comment != "" {
555
+	// 			comment = "; " + o.comment
556
+	// 		}
557
+	//
558
+	// 		if d.pc-instPC < 6 {
559
+	// 			hex = fmt.Sprintf("% 02X", d.prog[instPC:d.pc])
560
+	// 		} else {
561
+	// 			hex = fmt.Sprintf("% 02X...", d.prog[instPC:instPC+4])
562
+	// 		}
563
+	// 		fmt.Printf("%06X  % -14s  ", instPC, hex)
564
+	//
565
+	//
566
+	// 		fmt.Printf("%-8s\t%-20s%s\n", o.mnemonic, o.operands, comment)
567
+
568
+	var hex, comment string
569
+	if o.comment != "" {
570
+		comment = "; " + o.comment
571
+	}
572
+
573
+	if d.pc-instPC < 6 {
574
+		hex = fmt.Sprintf("% 02X", d.prog[instPC:d.pc])
575
+		fmt.Printf("%06X  % -14s  %-8s\t%-20s%s\n", instPC, hex, o.mnemonic, o.operands, comment)
576
+	} else {
577
+		for i := instPC; i < d.pc; i += 5 {
578
+			if i == instPC {
579
+				hex = fmt.Sprintf("% 02X", d.prog[i:i+5])
580
+				fmt.Printf("%06X  % -14s  %-8s\t%-20s%s\n", i, hex, o.mnemonic, o.operands, comment)
581
+			} else {
582
+				endAddr := i + 5
583
+				if endAddr > d.pc {
584
+					endAddr = d.pc
585
+				}
586
+				hex = fmt.Sprintf("% 02X", d.prog[i:endAddr])
587
+				//fmt.Printf("%06X  % -14s  %-8s\n", i, hex, "")
588
+				fmt.Printf("%6s  % -14s  %-8s\n", "", hex, "")
589
+
590
+			}
591
+
592
+		}
593
+
594
+	}
595
+
596
+}
597
+
598
+func (d *disassembler) getOpr6() byte {
599
+	return d.prog[d.pc-1] & 0x3F
600
+}
601
+
602
+func (d *disassembler) getOpr8() byte {
603
+	v := d.prog[d.pc]
604
+	d.pc += 1
605
+	return v
606
+}
607
+
608
+func (d *disassembler) getOpr10() uint16 {
609
+	v := uint16(d.prog[d.pc-1]&0x03)<<8 | uint16(d.prog[d.pc])
610
+	d.pc += 1
611
+	return v
612
+}
613
+
614
+func (d *disassembler) getOpr11() uint16 {
615
+	v := uint16(d.prog[d.pc-1]&0x07)<<8 | uint16(d.prog[d.pc])
616
+	d.pc += 1
617
+	return v
618
+}
619
+
620
+func (d *disassembler) getSignedOpr11() int16 {
621
+	v := uint16(d.prog[d.pc-1]&0x07)<<8 | uint16(d.prog[d.pc])
622
+	d.pc += 1
623
+	if v&0x0400 != 0 {
624
+		return int16(v | 0xF800)
625
+	}
626
+	return int16(v)
627
+}
628
+
629
+func (d *disassembler) getOpr16() uint16 {
630
+	v := uint16(d.prog[d.pc])<<8 | uint16(d.prog[d.pc+1])
631
+	d.pc += 2
632
+	return v
633
+}
634
+
635
+func (d *disassembler) getOpr32() uint32 {
636
+	v := uint32(d.prog[d.pc])<<24 | uint32(d.prog[d.pc+1])<<16 | uint32(d.prog[d.pc+2])<<8 | uint32(d.prog[d.pc+3])
637
+	d.pc += 4
638
+	return v
639
+}
640
+
641
+func (d *disassembler) getOpr64() uint64 {
642
+	v := uint64(d.prog[d.pc]) << 56
643
+	v |= uint64(d.prog[d.pc+1]) << 48
644
+	v |= uint64(d.prog[d.pc+2]) << 40
645
+	v |= uint64(d.prog[d.pc+3]) << 32
646
+	v |= uint64(d.prog[d.pc+4]) << 24
647
+	v |= uint64(d.prog[d.pc+5]) << 16
648
+	v |= uint64(d.prog[d.pc+6]) << 8
649
+	v |= uint64(d.prog[d.pc+7])
650
+	d.pc += 8
651
+	return v
652
+}
653
+
654
+func (d *disassembler) handle2090(instAddr uint32, inst byte, externs []call) (output, []call, error) {
655
+	o := output{}
656
+	switch inst {
657
+	case 0x00:
658
+		o.mnemonic = "ADD.F"
659
+	case 0x01:
660
+		o.mnemonic = "SUB.F"
661
+	case 0x02:
662
+		o.mnemonic = "MUL.F"
663
+	case 0x03:
664
+		o.mnemonic = "DIV.F"
665
+	case 0x04:
666
+		o.mnemonic = "GT.F"
667
+	case 0x05:
668
+		o.mnemonic = "GTE.F"
669
+	case 0x06:
670
+		o.mnemonic = "EQ.F"
671
+	case 0x07:
672
+		o.mnemonic = "ADD.L"
673
+	case 0x08:
674
+		o.mnemonic = "SUB.L"
675
+	case 0x09:
676
+		o.mnemonic = "MUL.L"
677
+	case 0x0A:
678
+		o.mnemonic = "EQ.L"
679
+	case 0x0B:
680
+		o.mnemonic = "NEQ.L"
681
+	case 0x0C:
682
+		o.mnemonic = "AND.L"
683
+	case 0x0D:
684
+		o.mnemonic = "OR.L"
685
+	case 0x0E:
686
+		o.mnemonic = "NOT.L"
687
+	case 0x0F:
688
+		o.mnemonic = "MOD.L"
689
+	case 0x10:
690
+		o.mnemonic = "BSR.68K"
691
+		off := int(d.getOpr16())
692
+		o.operands = fmt.Sprintf("$%04X", off)
693
+
694
+		c := call{
695
+			instrAddr:    instAddr,
696
+			computedAddr: uint32(int(d.pc) - off),
697
+		}
698
+
699
+		var targetAddrStr string
700
+
701
+		if d.read16(c.computedAddr) == 0x4EF9 {
702
+			c.targetAddr = d.read32(c.computedAddr + 2)
703
+			targetAddrStr = fmt.Sprintf(" => $%06X", c.targetAddr)
704
+		} else {
705
+			c.targetAddr = c.computedAddr
706
+		}
707
+
708
+		label := d.labels[c.targetAddr]
709
+		if label != "" {
710
+			label = fmt.Sprintf(" (%s)", label)
711
+		}
712
+
713
+		var native string
714
+		if d.read32(c.targetAddr) != 0x4EB82144 && d.read32(c.targetAddr) != 0x4EB82164 {
715
+			native = " (NATIVE)"
716
+			c.native = true
717
+		}
718
+
719
+		o.comment = fmt.Sprintf("=> $%06X%s%s%s", c.computedAddr, targetAddrStr, label, native)
720
+
721
+		externs = append(externs, c)
722
+	case 0x11:
723
+		o.mnemonic = "SUBSP.B"
724
+		o.operands = fmt.Sprintf("$%02X", d.getOpr8())
725
+	case 0x12:
726
+		o.mnemonic = "ADDSP.B"
727
+		o.operands = fmt.Sprintf("$%02X", d.getOpr8())
728
+	case 0x13:
729
+		o.mnemonic = "_2300"
730
+	case 0x14:
731
+		o.mnemonic = "RETURN"
732
+	case 0x15:
733
+		o.mnemonic = "SWITCH"
734
+		switchAddr := d.pc - 1
735
+		tpc := d.pc
736
+		for d.prog[tpc] != 0 {
737
+			caseAddr := tpc
738
+			op := d.read16(tpc)
739
+			tpc += 2
740
+			numKey := int(op & 0xF000 >> 12)
741
+			recLen := int(op & 0xfff)
742
+			keys := make([]byte, numKey)
743
+			for i := 0; i < numKey; i++ {
744
+				keys[i] = d.read8(tpc + uint32(i))
745
+			}
746
+			d.caseStmts[caseAddr] = caseStmt{
747
+				switchAddr: switchAddr,
748
+				keys:       keys,
749
+			}
750
+			tpc += uint32(recLen)
751
+		}
752
+		d.caseStmts[tpc] = caseStmt{
753
+			switchAddr: switchAddr,
754
+		}
755
+	case 0x16:
756
+		o.mnemonic = "_2362"
757
+		op := d.getOpr16() & 0xfff
758
+		a0 := 0x4000 + ((op & 0x0C00) >> 8)
759
+
760
+		o.comment = fmt.Sprintf("A0 = $%06X; (A0) = A6 + $%X\n\n", a0, 0)
761
+
762
+		o.operands = fmt.Sprintf("$%04X", op)
763
+	case 0x17:
764
+		o.mnemonic = "_23d2"
765
+		op := d.getOpr16() & 0xfff
766
+		op2 := d.getOpr16()
767
+		o.operands = fmt.Sprintf("$%04X, $%04X", op, op2)
768
+	case 0x18:
769
+		op := d.getOpr16()
770
+		o.mnemonic = []string{"BLS.B", "BLS.W", "BLS.L", "BLS.W"}[op&0xc000>>14]
771
+		o.operands = fmt.Sprintf("$%04X", op&0x3fff)
772
+		o.comment = fmt.Sprintf("=> $%06X", int(d.pc)+int(op&0x3fff))
773
+
774
+	case 0x19:
775
+		o.mnemonic = "_23fc"
776
+		op := d.getOpr16()
777
+		op2 := d.getOpr16()
778
+		o.operands = fmt.Sprintf("$%04X, $%04X", op, op2)
779
+	case 0x1A:
780
+		op := d.getOpr16()
781
+		o.mnemonic = []string{"BGT.B", "BGT.W", "BGT.L", "BGT.W"}[op&0xc000>>14]
782
+		o.operands = fmt.Sprintf("$%04X", op&0x3fff)
783
+		o.comment = fmt.Sprintf("=> $%06X", int(d.pc)+int(op&0x3fff))
784
+	case 0x1B:
785
+		o.mnemonic = "PUSH.F"
786
+		v := d.getOpr64()
787
+		fv := math.Float64frombits(v)
788
+		o.operands = fmt.Sprintf("$%016X", v)
789
+		o.comment = fmt.Sprintf("float = %f", fv)
790
+	case 0x1C:
791
+		o.mnemonic = "PUSH.W"
792
+		op := int(d.getOpr16())
793
+		o.operands = fmt.Sprintf("$%04X", op)
794
+	case 0x1D:
795
+		o.mnemonic = "PUSH.L"
796
+		op := int(d.getOpr32())
797
+		o.operands = fmt.Sprintf("$%08X", op)
798
+	case 0x1E:
799
+		o.mnemonic = "PUSH.W"
800
+		o.operands = "$0000"
801
+	case 0x1F:
802
+		o.mnemonic = "DISP"
803
+		strlen := uint32(d.getOpr8())
804
+		str := d.prog[d.pc : d.pc+strlen]
805
+		o.operands = fmt.Sprintf(`"%s"`, string(str))
806
+		d.pc += strlen
807
+	case 0x20:
808
+		o.mnemonic = "NEG.W"
809
+	case 0x21:
810
+		o.mnemonic = "LT.F"
811
+	case 0x22:
812
+		o.mnemonic = "LTE.F"
813
+	case 0x23:
814
+		o.mnemonic = "NEQ.F"
815
+	case 0x24:
816
+		o.mnemonic = "DIV.L"
817
+	case 0x25:
818
+		o.mnemonic = "LT.L"
819
+	case 0x26:
820
+		o.mnemonic = "GT.L"
821
+	case 0x27:
822
+		o.mnemonic = "LTE.L"
823
+	case 0x28:
824
+		o.mnemonic = "GTE.L"
825
+	case 0x29:
826
+		o.mnemonic = "FLOAT2LONG"
827
+	case 0x2A:
828
+		o.mnemonic = "LONG2FLOAT"
829
+	case 0x2B:
830
+		o.mnemonic = "COMPARE"
831
+		op := d.getOpr16()
832
+		o.operands = fmt.Sprintf("$%04X", op)
833
+	case 0x2C:
834
+		o.mnemonic = "NOTCOMPARE"
835
+		op := d.getOpr16()
836
+		o.operands = fmt.Sprintf("$%04X", op)
837
+	case 0x2D:
838
+		o.mnemonic = "SUBSP.W"
839
+		op := d.getOpr16()
840
+		o.operands = fmt.Sprintf("$%04X", op)
841
+	case 0x2E:
842
+		o.mnemonic = "ADDSP.W"
843
+		op := d.getOpr16()
844
+		o.operands = fmt.Sprintf("$%04X", op)
845
+	case 0x2F:
846
+		o.mnemonic = "XORL.L"
847
+	case 0x30:
848
+		o.mnemonic = "TRAP1"
849
+		strlen := uint32(d.getOpr8())
850
+		str := d.prog[d.pc : d.pc+strlen]
851
+		o.operands = fmt.Sprintf("\"%s\"", string(str))
852
+		d.pc += strlen
853
+	default:
854
+		return o, externs, fmt.Errorf("unrecognized 2090 instruction $%02X", inst)
855
+	}
856
+	return o, externs, nil
857
+}