-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcvpatch.asm
584 lines (528 loc) · 15.1 KB
/
cvpatch.asm
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
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
; ------------------------------------------------------------------------------
; CVPATCH V0.2
;
; Colecovision BIOS patches for Z180 retrocomputer.
; ------------------------------------------------------------------------------
; Directives / constants
INCLUDE "CVLOAD.INC"
AMERICA: EQU $0069 ; NMI frequency is 50Hz or 60Hz
POWER_UP: EQU $006E ; Colecovision power up routine
CV_STACK: EQU $73B9 ; Colecovision top of stack
VDP0SAV: EQU $73C3 ; VDP register 0 save value
VDP1SAV: EQU $73C4 ; VDP register 1 save value
PULSECNT1: EQU $73EB ; Spinner 1 pulse counter
JOY1SAV: EQU $73EE ; Joystick 1 save value
KEY1SAV: EQU $73F0 ; Keypad 1 save value
NMI_INT_VEC: EQU $8021 ; Vector for Coleco game nmi routine
ORG $5000
; Jump/init table
JP cvpatch ; $5000 Start Colecovision patch
DB 0 ; $5003 Autostart the Coleco patch from SCM if set to $55
JP outputChar ; $5004 Output character in register A to ASCI 0
; ------------------------------------------------------------------------------
; Patch Coleco ROM
; Some games (and BIOS) may directly call the internal address instead of using
; the jump table at the end of the Coleco BIOS therefore the internal addresses
; are patched with a jump to the new routine.
; ------------------------------------------------------------------------------
cvpatch: ; VDP I/O routines
LD DE,READ_REG
LD HL,$1D57
CALL patchHL
LD DE,FILL_VRAM
LD HL,$18D4
CALL patchHL
LD DE,WRITE_SPRITE
LD HL,$1C82
CALL patchHL
LD DE,WRITE_REG
LD HL,$1CCA
CALL patchHL
LD DE,WRITE_VRAM
LD HL,$1D01
CALL patchHL
LD DE,READ_VRAM
LD HL,$1D3E
CALL patchHL
; Game controller routines
LD DE,CONT_SCAN
LD HL,$114A
CALL patchHL
LD DE,DECODER
LD HL,$118B
CALL patchHL
IFNDEF SN76489
;Replace PSG I/O instructions: out ($ff),a --> nop
XOR A
LD ($0172),A ; Sound subroutines
LD ($0173),A
LD ($017B),A
LD ($017C),A
LD ($018D),A
LD ($018E),A
LD ($023D),A ; NOSOUND
LD ($023E),A
LD ($0241),A
LD ($0242),A
LD ($0245),A
LD ($0246),A
LD ($0249),A
LD ($024A),A
LD ($0335),A ; DOSOUND
LD ($0336),A
LD ($0354),A
LD ($0355),A
ENDIF
; The intended use of the 'AMERICA' byte is to differentiate
; between 50Hz PAL and 60Hz NTSC systems,
; games may or may not use this value for timing routines.
LD A,60
LD (AMERICA),A ; Set to 60Hz (VGA)
; Some games (e.g. Zaxxon) use following addresses in the READ_VRAM
; routine to get the VDP I/O port values.
LD A,VDPIO1
LD ($1D43),A
LD A,VDPIO0
LD ($1D47),A
; Prevent NMI call during initialisation.
XOR A
LD (VDP1SAV),A
; Don't autostart the patch program again from scm
LD ($5003),A
; patch end
; ------------------------------------------------------------------------------
; Additional system settings after scm has initialised the hardware
; Setting the clock at half speed will also half the baudrate
; This is adjusted in the baudrate divider setting but only works for some
; baudrates. Tested with 57600 baud (doesn't work with 115200 baud)
; Note: also adjust the baudrate in the scm configuration if needed.
; ------------------------------------------------------------------------------
IFDEF HALFSPEED
XOR A
OUT0 (Z180_CCR),A ; Divide the clock by 2
IN0 A,(Z180_CNTLB0)
LD B,A
AND $07 ; Baudrate divider is 1?
JR Z,endBaudrate
LD A,B
DEC A ; Adjust baudrate divider for ASCI 0
OUT0 (Z180_CNTLB0),A
endBaudrate:
ENDIF
; Set maximum memory and I/O wait states
LD A, $F0
OUT0 (Z180_DCNTL),A
; ------------------------------------------------------------------------------
; The propeller graphics card has no 50Hz or 60Hz vsync interrupt feature.
; This is emulated with a timer (used ROMWBW HBIOS timer code as example).
; ------------------------------------------------------------------------------
initTimer: LD A,IVT >> 8 ; Load interrupt vector table..
LD I,A ; .. high byte in I register
XOR A
OUT0 (Z180_IL),A ; .. low byte in Z180 IL register
LD HL,CPUKHZ*5/6 ; 50HZ = 18432000 / 20 / 50 / X, so X = CPU KHZ, *5/6 --> 60HZ
IFDEF VSYNC
DEC H ; Set the timer a little higher than 60HZ
ENDIF
OUT0 (Z180_TMDR1L),L ; Initialise timer 1 data register
OUT0 (Z180_TMDR1H),H
DEC HL ; Reload occurs *after* zero
OUT0 (Z180_RLDR1L),L ; Initialise timer 1 reload register
OUT0 (Z180_RLDR1H),H
LD A,$22 ; Set timer 1 interrupt / countdown bit..
OUT0 (Z180_TCR),A ; .. in Z180 timer control register
; ------------------------------------------------------------------------------
; End initialisation and start Coleco BIOS.
; Skip the first instructions at address $0000 so they can be patched with
; a trap or breakpoint vector that calls the SCM breakpoint routine.
; ------------------------------------------------------------------------------
EI
LD SP,CV_STACK ; First instructions of the Coleco BIOS
JP POWER_UP ; "
; ------------------------------------------------------------------------------
timer: LD (SPSAV),SP ; Save the stackpointer
LD SP,SPTIMER ; More stackspace needed for the extra code
PUSH AF
CALL inputChar ; Poll ASCI 0 for a new key
LD A,(charkey1)
OR A
JR Z, checkNewChar
LD A,(holdkey)
DEC A
LD (holdkey),A
JR NZ,endChar
LD (charkey1),A ; Release key after 100ms
checkNewChar: LD A,(charkey)
OR A
JR Z,endChar
IFDEF TESTKEY
CALL outputChar
ENDIF
CP 'P'
JR NZ,endCheckP
CALL $1FD6 ; NoSound
JP $200C ; Key P will invoke SCM
endCheckP: LD (charkey1),A
LD A,6 ; 6/60HZ --> 100ms
LD (holdkey),A ; Hold the key for 100ms
XOR A
LD (charkey),A ; Clear the received key
endChar:
XOR A
LD (CHECK_EI),A ; Set CHECK_EI instruction to NOP
IFDEF VSYNC
OUT0 (Z180_TCR),A ; Stop the count!
LD A,$22
OUT (VDPIO3),A ; Wait for vsync to reduce screen artifacts
OUT0 (Z180_TCR),A ; Resume the count
ENDIF
IN0 A,(Z180_TCR) ; Acknowledge Z180 timer interrupt
IN0 A,(Z180_TMDR1L) ; "
LD A,(VDP1SAV)
BIT 5,A ; VDP interrupt bit set?
CALL NZ, NMI_INT_VEC ; Yes, call NMI routine (ends with RETN)
LD A,$FB
LD (CHECK_EI),A ; set CHECK_EI instruction to EI
POP AF
LD SP,(SPSAV) ; Restore the stackpointer
EI
RET ; Don't use RETI in Z180 internal peripheral interrupt
; ------------------------------------------------------------------------------
; Re-enable interrupts if a Coleco replacement routine is not called from the
; NMI routine. The instruction at address CHECK_EI is either EI($FB) or NOP($00)
; ------------------------------------------------------------------------------
CHECK_EI: EI
RET
; ------------------------------------------------------------------------------
; Alternative input via Z180 internal ASCI 0.
; The ASCI port should be initialised already. If the system clock is divided
; by 2 then the baudrate is 57600. The input is intentionally unbuffered, only
; the latest received character is saved. Only the left controller is emulated
; with keyboard keys.
; ------------------------------------------------------------------------------
inputChar: PUSH AF
IN0 A,(Z180_STAT0)
PUSH AF
AND $70 ; Any errors?
JR Z,input1
IN0 A,(Z180_CNTLA0)
RES 3,A ; Clear error flag
OUT (Z180_CNTLA0),A
input1: POP AF
BIT 7,A ; Receive data register full?
JR Z,endInput
IN0 A,(Z180_RDR0)
; Check if the key is in the list
PUSH HL
AND $7F ; Mask ASCII
CP 'a'
JR C,input2
AND $5F ; Convert to upper case
input2: LD HL,keylist
repeatCheck: CP (HL)
JR Z,endCheck ; Key is in the list, update charkey
INC HL
JR NC,repeatCheck
LD A,(charkey) ; Key is not in the list, keep old value
endCheck: POP HL
LD (charkey),A
endInput: POP AF
RET
charkey: DB 0 ; Latest received char
charkey1: DB 0 ; Latest copy of char
holdkey: DB 0 ; Counter for debounce emulation
; ------------------------------------------------------------------------------
; outputChar is used for testing purposes.
; ------------------------------------------------------------------------------
outputChar: PUSH BC
IN0 B,(Z180_STAT0)
BIT 1,B ; Transmit data register empty?
POP BC
RET Z
OUT0 (Z180_TDR0),A
RET
; ------------------------------------------------------------------------------
; Coleco $1FDC-->$1D57 READ_REGISTER replacement routine.
; ------------------------------------------------------------------------------
READ_REG: IN A,(VDPIO1)
RET
; ------------------------------------------------------------------------------
; Coleco $1F82-->$18D4 FILL_VRAM replacement routine.
; ------------------------------------------------------------------------------
FILL_VRAM: DI
LD C,A
LD A,L
OUT (VDPIO1),A
LD A,H
OR $40
OUT (VDPIO1),A
LAB_18DD: LD A,C
OUT (VDPIO0),A
DEC DE
LD A,D
OR E
JR NZ,LAB_18DD
IN A,(VDPIO1)
JP CHECK_EI
; ------------------------------------------------------------------------------
; Coleco $1FC4-->$1C82 WR_SPR_NM_TBL replacement routine.
; * Use reversed sprite order to patch propeller emulator bug
; ------------------------------------------------------------------------------
WRITE_SPRITE: DI
LD IX,($8004)
PUSH AF
IFDEF SPRITE_FIX
LD B,A ; * Add number of sprites
incIX: INC IX ; *
DJNZ incIX ; *
DEC IX ; *
ENDIF
LD IY,$73F2
LD E,(IY+$00)
LD D,(IY+$01)
LD A,E
OUT (VDPIO1),A
LD A,D
OR $40
OUT (VDPIO1),A
POP AF
LAB_1C9A: LD HL,($8002)
LD C,(IX+$00)
IFDEF SPRITE_FIX
DEC IX ; * Reverse order
ELSE
INC IX
ENDIF
LD B,$00
ADD HL,BC
ADD HL,BC
ADD HL,BC
ADD HL,BC
LD B,$04
LD C,VDPIO0
LAB_1CAC: OUTI
;NOP ; VDP wait not required for emulator
;NOP
JR NZ,LAB_1CAC
DEC A
JR NZ,LAB_1C9A
JP CHECK_EI
; ------------------------------------------------------------------------------
; Coleco $1FD9-->$1CCA WRITE_REGISTER replacement routine.
; ------------------------------------------------------------------------------
WRITE_REG: DI
LD A,C
OUT (VDPIO1),A
LD A,B
ADD A,$80
OUT (VDPIO1),A
LD A,B
CP $00
JR NZ,LAB_1CDB
LD A,C
LD (VDP0SAV),A
LAB_1CDB: LD A,B
CP $01
JR NZ,LAB_1CE4
LD A,C
LD (VDP1SAV),A
LAB_1CE4: JP CHECK_EI
; ------------------------------------------------------------------------------
; Coleco $1FDF-->$1D01 WRITE_VRAM replacement routine.
; ------------------------------------------------------------------------------
WRITE_VRAM: DI
PUSH HL
PUSH DE
POP HL
LD DE,$4000
ADD HL,DE
LD A,L
OUT (VDPIO1),A
LD A,H
OUT (VDPIO1),A
PUSH BC
POP DE
POP HL
LD C,VDPIO0
LD B,E
LAB_1D14: OUTI
;NOP ; VDP wait not required for emulator
;NOP
JR NZ,LAB_1D14
DEC D
JP M,LAB_1D21
JR NZ,LAB_1D14 ; Keep the bug (should be: JR LAB_1D14)
LAB_1D21: JP CHECK_EI
; ------------------------------------------------------------------------------
; Coleco $1FE2-->$1D3E READ_VRAM replacement routine.
; ------------------------------------------------------------------------------
READ_VRAM: DI
LD A,E
OUT (VDPIO1),A
LD A,D
OUT (VDPIO1),A
PUSH BC
POP DE
LD C,VDPIO0
LD B,E
LAB_1D49: INI
;NOP ; VDP wait not required for emulator
;NOP
JP NZ,LAB_1D49
DEC D
JP M,LAB_1D56
JR NZ,LAB_1D49
LAB_1D56: JP CHECK_EI
; ------------------------------------------------------------------------------
; Coleco $1F76-->$114A CONT_SCAN - Read Controller Raw data routine.
; Following routine emulates the left controller with the keyboard.
; ------------------------------------------------------------------------------
CONT_SCAN: LD A,(charkey1)
OR A ; Is there a key pressed?
JR Z,readColeco
PUSH HL
LD HL,joytab
CALL conversion
LD (JOY1SAV),A
LD A,(charkey1)
LD HL,keytab
CALL conversion
LD (KEY1SAV),A
POP HL
RET
IFDEF GAMECTL
readColeco: IN A,($FC) ; Commands that were overwritten by patch jump
CPL ; "
JP $114D ; Continue in Coleco BIOS
ELSE
readColeco: LD A,$80 ; Set values for no input
LD (JOY1SAV),A
LD (KEY1SAV),A
RET
ENDIF
; ------------------------------------------------------------------------------
; Coleco $1F79-->$118B DECODER - Read Controller
; Input H: 0=left, 1=right
; L: 0=left fire, 1=right fire
; Output H: fire button status in bit 5
; L: joystick directions or keycode
; E: old pulse counter (only if L=0)
; ------------------------------------------------------------------------------
DECODER: LD A,H
OR A
JP NZ,ctlColeco ; Right controller
LD A,(charkey1)
OR A ; Is there a key pressed?
JR Z,ctlColeco
PUSH AF
LD A,L
CP $01
JR Z,readKeypad
LD BC,PULSECNT1
LD A,(BC)
LD E,A
XOR A
LD (BC),A
POP AF
PUSH HL
LD HL,joytab
CALL conversion
POP HL
JP $11A0 ; Continue in Coleco BIOS
readKeypad: POP AF
PUSH HL
LD HL,keytab
CALL conversion
POP HL
LD D,A
JP $11B2 ; Continue in Coleco BIOS
IFDEF GAMECTL
ctlColeco: LD A,L ; Commands that were overwritten by patch jump
CP $01 ; "
JP $118E ; Continue in Coleco BIOS
ELSE
ctlColeco: LD H,$00 ; Return values for no input
LD A,L
CP $01
JR Z,writeKeypad
LD L,$00
RET
writeKeypad: LD L,$0F
RET
ENDIF
; ------------------------------------------------------------------------------
; Convert keyboard ASCII key to Coleco key code.
; ------------------------------------------------------------------------------
conversion: CP (HL)
JR Z,convertKey
INC HL
INC HL
JR NC,conversion
LD A,$80 ; Key not in table
RET
convertKey: INC HL
LD A,(HL)
RET
; ------------------------------------------------------------------------------
; Patch (HL) with jump to (DE).
; ------------------------------------------------------------------------------
patchHL: LD A,$C3 ; Jump instruction
LD (HL),A
INC HL
LD (HL),E
INC HL
LD (HL),D
RET
; ------------------------------------------------------------------------------
; Conversion tables from keyboard ASCII code to Coleco controller raw code
; must be sorted in ascending order of keyboard code.
; ------------------------------------------------------------------------------
joytab: DB ',',$C0 ; Left fire
DB 'A',$88 ; Joystick left
DB 'D',$82 ; Joystick right
DB 'W',$81 ; Joystick up
DB 'X',$84 ; Joystick down
DB $FF,$80 ; End of table
keytab: DB '#',$89
DB '*',$86
DB '.',$C0 ; Right fire
DB '0',$85
DB '1',$82
DB '2',$88
DB '3',$83
DB '4',$8D
DB '5',$8C
DB '6',$81
DB '7',$8A
DB '8',$8E
DB '9',$84
DB $FF,$80 ; End of table
; List of acceptable keyboard characters, in ascending order
keylist: DB "#*,.0123456789ADPWX"
DB $FF
; ------------------------------------------------------------------------------
; Interrupt vector table. Must be aligned to page.
; ------------------------------------------------------------------------------
ALIGN 256
IVT: DW 0 ; 0 INT1
DW 0 ; 1 INT2
DW 0 ; 2 PRT0
DW timer ; 3 PRT1
DW 0 ; 4 DMA0
DW 0 ; 5 DMA1
DW 0 ; 6 CSIO
DW inputChar ; 7 ASCI0
DW 0 ; 8 ASCI1
DW 0 ; 9
DW 0 ; 10
DW 0 ; 11
DW 0 ; 12
DW 0 ; 13
DW 0 ; 14
DW 0 ; 15
; ------------------------------------------------------------------------------
; Stack space for extended interrupt routine.
; ------------------------------------------------------------------------------
SPSAV: DW 0 ; Saved stackpointer
DS 640 ; This should be enough stack space for any game
SPTIMER: