-
Notifications
You must be signed in to change notification settings - Fork 10
/
mouse.s
673 lines (539 loc) · 14.7 KB
/
mouse.s
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
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
;
; mouse.s
; Routines for handling the mouse
;
; Created by Quinn Dunki on 8/15/14.
; Copyright (c) 2014 One Girl, One Laptop Productions. All rights reserved.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ProDOS ROM entry points and constants
;
PRODOS_MLI = $bf00
ALLOC_INTERRUPT = $40
DEALLOC_INTERRUPT = $41
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Mouse firmware ROM entry points and constants
;
; These mouse firmware entry points are offsets from the firmware
; entry point of the slot, and also indirect.
SETMOUSE = $12
SERVEMOUSE = $13
READMOUSE = $14
CLEARMOUSE = $15
POSMOUSE = $16
CLAMPMOUSE = $17
HOMEMOUSE = $18
INITMOUSE = $19
MOUSTAT = $0778 ; + Slot Num
MOUSE_XL = $0478 ; + Slot Num
MOUSE_XH = $0578 ; + Slot Num
MOUSE_YL = $04f8 ; + Slot Num
MOUSE_YH = $05f8 ; + Slot Num
MOUSE_CLAMPL = $04f8 ; Upper mouse clamp (LSB). Slot independent.
MOUSE_CLAMPH = $05f8 ; Upper mouse clamp (MSB). Slot independent.
MOUSE_ZEROL = $0478 ; Zero value of mouse (LSB). Slot independent.
MOUSE_ZEROH = $0578 ; Zero value of mouse (MSB). Slot independent.
MOUSTAT_MASK_BUTTONINT = %00000100
MOUSTAT_MASK_MOVEINT = %00000010
MOUSTAT_MASK_DOWN = %10000000
MOUSTAT_MASK_WASDOWN = %01000000
MOUSTAT_MASK_MOVED = %00100000
MOUSEMODE_OFF = $00 ; Mouse off
MOUSEMODE_PASSIVE = $01 ; Passive mode (polling only)
MOUSEMODE_MOVEINT = $03 ; Interrupts on movement
MOUSEMODE_BUTINT = $05 ; Interrupts on button
MOUSEMODE_COMBINT = $0f ; Interrupts on VBL, movement and button
; Mouse firmware is all indirectly called, because
; it moved around a lot in different Apple ][ ROM
; versions. This macro helps abstracts this for us.
.macro CALLMOUSE name
ldx #name
jsr WGCallMouse
.endmacro
CH_MOUSEPOINTER = 'B'
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; WGEnableMouse
; Prepares the mouse for use
;
WGEnableMouse:
pha
SETSWITCH PAGE2OFF
; Warning!
; We're relying on the fact that the 65C02 treats undefined opcodes as NOPs here!
; (in the same way that ProDOS 2.4.2 does)
sec
.byte $C2, $01 ;REP #$01 ; clear carry flag on 65816
bcc WGEnableMouse_IIGS
; Find slot number and calculate the various indirections needed
jsr WGFindMouse
bcs WGEnableMouse_Error
; Note if we're a //e or //c, because mouse tracking and interrupts are different
lda $fbb3
cmp #$06
bne WGEnableMouse_Error ; II or II+? Sorry...
lda $fbc0
bne WGEnableMouse_IIe
lda #1
sta WG_APPLEIIC
WGEnableMouse_IIe:
; Install our interrupt handler via ProDOS (play nice!)
jsr PRODOS_MLI
.byte ALLOC_INTERRUPT
.addr WG_PRODOS_ALLOC
bne WGEnableMouse_Error ; ProDOS will return here with Z clear on error
; Initialize the mouse
stz WG_MOUSEPOS_X
stz WG_MOUSEPOS_Y
stz WG_MOUSEBG
CALLMOUSE INITMOUSE
bcs WGEnableMouse_Error ; Firmware sets carry if mouse is not available
CALLMOUSE CLEARMOUSE
lda #MOUSEMODE_COMBINT ; Enable combination interrupt mode
CALLMOUSE SETMOUSE
; Set the mouse's zero postion to (1,1), since we're in text screen space
stz MOUSE_ZEROH
lda #0
sta MOUSE_ZEROL
lda #1
CALLMOUSE CLAMPMOUSE
lda #0
CALLMOUSE CLAMPMOUSE
; Scale the mouse's range into something easy to do math with,
; while retaining as much range of motion and precision as possible
lda WG_APPLEIIC
bne WGEnableMouse_ConfigIIc
lda #$7f ; 640 - 1 horizontally
sta MOUSE_CLAMPL
lda #$02
sta MOUSE_CLAMPH
lda #0
CALLMOUSE CLAMPMOUSE
lda #$e0 ; 736 vertically
sta MOUSE_CLAMPL
lda #$02
sta MOUSE_CLAMPH
lda #1
CALLMOUSE CLAMPMOUSE
bra WGEnableMouse_Activate
WGEnableMouse_IIGS:
lda #$20 ; JSR
sta WGEnableGSMouse
sta WGDisableGSMouse
bra WGEnableMouse_Activate
WGEnableMouse_Error:
stz WG_MOUSEACTIVE
WGEnableMouse_done: ; Exit point here for branch range
pla
rts
WGEnableMouse_ConfigIIc: ; //c's tracking is weird. Need to clamp to a much smaller range
lda #$4f ; 80 - 1 horizontally
sta MOUSE_CLAMPL
lda #$00
sta MOUSE_CLAMPH
lda #0
CALLMOUSE CLAMPMOUSE
lda #$17 ; 24 - 1 vertically
sta MOUSE_CLAMPL
lda #$00
sta MOUSE_CLAMPH
lda #1
CALLMOUSE CLAMPMOUSE
WGEnableMouse_Activate:
lda #1
sta WG_MOUSEACTIVE
cli ; Once all setup is done, it's safe to enable interrupts
bra WGEnableMouse_done
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; WGDisableMouse
; Shuts off the mouse when we're done with it
;
WGDisableMouse:
pha
SETSWITCH PAGE2OFF
lda WG_MOUSEACTIVE ; Never activated the mouse
beq WGDisableMouse_done
WGDisableGSMouse:
bit GsDisableMouse ; self-modified to JSR when appropriate
lda MOUSEMODE_OFF
CALLMOUSE SETMOUSE
stz WG_MOUSEACTIVE
lda #MOUSEMODE_OFF ; Disable VBL manually
CALLMOUSE SETMOUSE
; Remove our interrupt handler via ProDOS (done playing nice!)
lda WG_PRODOS_ALLOC+1 ; Copy interrupt ID that ProDOS gave us
sta WG_PRODOS_DEALLOC+1
jsr PRODOS_MLI
.byte DEALLOC_INTERRUPT
.addr WG_PRODOS_DEALLOC
jsr WGUndrawPointer ; Be nice if we're disabled during a program
WGDisableMouse_done:
pla
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; WGCallMouse
; Calls a mouse firmware routine. Here's where we handle all
; the layers of indirection needed to call mouse firmware. The
; firmware moved in ROM several times over the life of the
; Apple ][, so it's kind of a hassle to call it.
; X: Name of routine (firmware offset constant)
; Side effects: Clobbers all registers
WGCallMouse:
stx WGCallMouse+4 ; Use self-modifying code to smooth out some indirection
; This load address is overwritten by the above code, AND by the mouse set
; up code, to make sure we have the right slot entry point and firmware
; offset
ldx $c400 ; Self-modifying code!
stx WG_MOUSE_JUMPL ; Get low byte of final jump from firmware
php ; Note that mouse firmware is not re-entrant,
sei ; so we must disable interrupts inside them
jsr WGCallMouse_redirect
plp ; Restore interrupts to previous state
rts
WGCallMouse_redirect:
ldx WG_MOUSE_JUMPH
ldy WG_MOUSE_SLOTSHIFTED
jmp (WG_MOUSE_JUMPL)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; WGFindMouse
; Figures out which slot (//e) or port (//c) the mouse is in.
; It moved around a lot over the years. Sets it to 0 if no mouse
; could be found
; OUT C: Set if no mouse could be found
WGFindMouse:
SAVE_AX
ldx #7
WGFindMouse_loop:
txa ; Compute slot firmware locations for this loop
ora #$c0
sta WGFindMouse_loopModify+2 ; Self-modifying code!
sta WGFindMouse_loopModify+9
sta WGFindMouse_loopModify+16
sta WGFindMouse_loopModify+23
sta WGFindMouse_loopModify+30
WGFindMouse_loopModify:
; Check for the magic 5-byte pattern that gives away the mouse card
lda $c005 ; These addresses are modified in place on
cmp #$38 ; each loop iteration
bne WGFindMouse_nextSlot
lda $c007
cmp #$18
bne WGFindMouse_nextSlot
lda $c00b
cmp #$01
bne WGFindMouse_nextSlot
lda $c00c
cmp #$20
bne WGFindMouse_nextSlot
lda $c0fb
cmp #$d6
bne WGFindMouse_nextSlot
bra WGFindMouse_found
WGFindMouse_nextSlot:
dex
bmi WGFindMouse_none
bra WGFindMouse_loop
WGFindMouse_found:
; Found it! Now configure all our indirection lookups
stx WG_MOUSE_SLOT
lda #$c0
ora WG_MOUSE_SLOT
sta WG_MOUSE_JUMPH
sta WGCallMouse+5 ; Self-modifying code!
txa
asl
asl
asl
asl
sta WG_MOUSE_SLOTSHIFTED
clc
bra WGFindMouse_done
WGFindMouse_none:
stz WG_MOUSE_SLOT
sec
WGFindMouse_done:
RESTORE_AX
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; WGMouseInterruptHandler
; Handles interrupts that may be related to the mouse
; This is a ProDOS-compliant interrupt handling routine, and
; should be installed and removed via ProDOS as needed.
;
; IMPORTANT: This routine is NOT MLI-reentrant, which means MLI
; calls can NOT be made within this handler. See page 108 of the
; ProDOS 8 Technical Reference Manual if this feature needs to be
; added.
;
WGMouseInterruptHandler:
cld ; ProDOS interrupt handlers must open with this
SAVE_AXY
CALLMOUSE SERVEMOUSE
bcc WGMouseInterruptHandler_regard
jmp WGMouseInterruptHandler_disregard
WGMouseInterruptHandler_regard:
php
sei
lda PAGE2 ; Need to preserve text bank, because we may interrupt rendering
pha
SETSWITCH PAGE2OFF
ldx WG_MOUSE_SLOT
lda MOUSTAT,x ; Check interrupt status bits first, because READMOUSE clears them
and #MOUSTAT_MASK_BUTTONINT
bne WGMouseInterruptHandler_button
lda MOUSTAT,x
and #MOUSTAT_MASK_MOVEINT
bne WGMouseInterruptHandler_mouse
jmp WGMouseInterruptHandler_VBL
WGMouseInterruptHandler_mouse:
jsr WGUndrawPointer ; Erase the old mouse pointer
; Read the mouse state. Note that interrupts need to remain
; off until after the data is copied.
CALLMOUSE READMOUSE
ldx WG_MOUSE_SLOT
lda MOUSTAT,x ; Movement/button status bits are now valid
sta WG_MOUSE_STAT
lda WG_APPLEIIC
bne WGMouseInterruptHandler_IIc
; Read mouse position and transform it into screen space
lsr MOUSE_XH,x
ror MOUSE_XL,x
lsr MOUSE_XH,x
ror MOUSE_XL,x
lsr MOUSE_XH,x
ror MOUSE_XL,x
lda MOUSE_XL,x
sta WG_MOUSEPOS_X
lsr MOUSE_YH,x
ror MOUSE_YL,x
lsr MOUSE_YH,x
ror MOUSE_YL,x
lsr MOUSE_YH,x
ror MOUSE_YL,x
lsr MOUSE_YH,x
ror MOUSE_YL,x
lsr MOUSE_YH,x
ror MOUSE_YL,x
lda MOUSE_YL,x
sta WG_MOUSEPOS_Y
bra WGMouseInterruptHandler_draw
WGMouseInterruptHandler_IIc: ; IIc tracks much slower, so don't scale
lda MOUSE_XL,x
sta WG_MOUSEPOS_X
lda MOUSE_YL,x
sta WG_MOUSEPOS_Y
WGMouseInterruptHandler_draw:
jsr WGDrawPointer ; Redraw the pointer
bra WGMouseInterruptHandler_intDone
WGMouseInterruptHandler_disregard:
; Carry will still be set here, to notify ProDOS that
; this interrupt was not ours
RESTORE_AXY
rts
WGMouseInterruptHandler_button:
CALLMOUSE READMOUSE
ldx WG_MOUSE_SLOT
lda MOUSTAT,x ; Movement/button status bits are now valid
sta WG_MOUSE_STAT
bit WG_MOUSE_STAT ; Check for rising edge of button state
bpl WGMouseInterruptHandler_intDone
lda WG_MOUSE_BUTTON_DOWN
bne WGMouseInterruptHandler_intDone
WGMouseInterruptHandler_buttonDown:
; Button went down, so make a note of location for later
lda #1
sta WG_MOUSE_BUTTON_DOWN
lda WG_MOUSEPOS_X
sta WG_MOUSECLICK_X
lda WG_MOUSEPOS_Y
sta WG_MOUSECLICK_Y
WGMouseInterruptHandler_intDone:
pla ; Restore text bank
bpl WGMouseInterruptHandler_intDoneBankOff
SETSWITCH PAGE2ON
bra WGMouseInterruptHandler_done
WGMouseInterruptHandler_VBL:
CALLMOUSE READMOUSE
ldx WG_MOUSE_SLOT
lda MOUSTAT,x ; Movement/button status bits are now valid
sta WG_MOUSE_STAT
bmi WGMouseInterruptHandler_intDone
stz WG_MOUSE_BUTTON_DOWN
bra WGMouseInterruptHandler_intDone
WGMouseInterruptHandler_intDoneBankOff:
SETSWITCH PAGE2OFF
WGMouseInterruptHandler_done:
RESTORE_AXY
plp
clc ; Notify ProDOS this was our interrupt
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; WGUndrawPointer
; Unplots the mouse pointer at current location
; Side effects: Clobbers BASL,BASH
;
WGUndrawPointer:
pha
lda #$80
jsr renderPointer
pla
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; WGDrawPointer
; Plots the mouse pointer at current location
;
WGDrawPointer:
pha
lda #0
jsr renderPointer
pla
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; WGPointerDirty
; Updates the background behind the mouse pointer without
; modifying it's current render state. Assumes pointer is not
; currently visible
;
WGPointerDirty:
pha
lda #%11000000
jsr renderPointer
pla
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; renderPointer
; Performs mouse-pointer-related rendering
; A: 0=draw, %10000000=undraw, %11000000=recapture background
;
renderPointer:
SAVE_AXY
sta renderPointerMode
lda BASL ; Need to preserve BAS, because we may interrupt rendering
pha
lda BASH
pha
lda PAGE2 ; Need to preserve text bank, because we may interrupt rendering
pha
lda WG_MOUSEACTIVE
beq renderPointer_done ; Mouse not enabled
ldx WG_MOUSEPOS_Y
cpx #24
bcs renderPointer_done ; Mouse out of range (vertically)
lda TEXTLINES_L,x ; Compute video memory address of point
sta BASL
lda TEXTLINES_H,x
sta BASH
lda WG_MOUSEPOS_X
cmp #80
bcs renderPointer_done ; Mouse out of range (horizontally)
lsr
clc
adc BASL
sta BASL
lda #0
adc BASH
sta BASH
lda WG_MOUSEPOS_X ; X even?
ror
bcs renderPointer_xOdd
SETSWITCH PAGE2ON
bit renderPointerMode ; Draw or undraw?
bpl renderPointer_draw
bvc renderPointer_undraw
renderPointer_draw:
lda (BASL) ; Save background
cmp #CH_MOUSEPOINTER ; Make sure we never capture ourselves and leave a "stamp"
beq renderPointer_drawSaved
sta WG_MOUSEBG
renderPointer_drawSaved:
bit renderPointerMode ; Recapture or draw?
bvs renderPointer_done
lda #CH_MOUSEPOINTER ; Draw the pointer
sta (BASL)
bra renderPointer_done
renderPointer_undraw:
lda WG_MOUSEBG
beq renderPointer_done ; No saved background yet
sta (BASL)
bra renderPointer_done
renderPointer_xOdd:
SETSWITCH PAGE2OFF
bit renderPointerMode ; Draw or undraw?
bpl renderPointer_drawOdd
bvc renderPointer_undraw
renderPointer_drawOdd:
lda (BASL) ; Save background
cmp #CH_MOUSEPOINTER ; Make sure we never capture ourselves and leave a "stamp"
beq renderPointer_drawOddSaved
sta WG_MOUSEBG
renderPointer_drawOddSaved:
bit renderPointerMode ; Recapture or draw?
bvs renderPointer_done
lda #CH_MOUSEPOINTER ; Draw the pointer
sta (BASL)
bra renderPointer_done
renderPointer_done:
pla ; Restore text bank
bpl renderPointer_doneBankOff
SETSWITCH PAGE2ON
bra renderPointer_doneBAS
renderPointer_doneBankOff:
SETSWITCH PAGE2OFF
renderPointer_doneBAS:
pla ; Restore BAS
sta BASH
pla
sta BASL
RESTORE_AXY
rts
renderPointerMode:
.byte 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Mouse API state
;
WG_MOUSEACTIVE:
.byte 0
WG_MOUSE_STAT:
.byte 0
WG_MOUSEBG:
.byte 0
WG_APPLEIIC:
.byte 0
WG_MOUSE_JUMPL:
MousePixX: ;(GS-overload) Mouse pixel position 0-639 (word)
.byte 0
WG_MOUSE_JUMPH:
.byte 0
WG_MOUSE_SLOT:
MousePixY: ;(GS-overload) Mouse pixel position 0-191 (word)
.byte 0
WG_MOUSE_SLOTSHIFTED:
.byte 0
WG_MOUSE_BUTTON_DOWN:
MouseBtn0: ;(GS-overload) Previous btn0 state used for edge detect
.byte 0
WG_MOUSEPOS_X:
MouseTxtX: ;(GS-overload) Mouse text position 0-79
.byte 39
WG_MOUSEPOS_Y:
MouseTxtY: ;(GS-overload) Mouse text position 0-23
.byte 11
WG_MOUSECLICK_X:
MouseDownX: ;(GS-overload) Mouse click pos 0-79
.byte $ff
WG_MOUSECLICK_Y:
MouseDownY: ;(GS-overload) Mouse click pos 0-23
.byte 0
MouseMaxX:
.word 80*8 ;(GS-extension) Mouse max X = 640
MouseMaxY:
.word 24*8 ;(GS-extension) Mouse max Y = 192
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ProDOS system call parameter blocks
;
WG_PRODOS_ALLOC:
.byte 2
.byte 0 ; ProDOS returns an ID number for the interrupt here
.addr WGMouseInterruptHandler
WG_PRODOS_DEALLOC:
.byte 1
.byte 0 ; To be filled with ProDOS ID number