-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmouse.s
535 lines (424 loc) · 12 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
;
; mouse.s
; Mouse driver for Apple //e Enhanced with mouse card (any slot), or Apple //c(+)
;
; Created by Quinn Dunki on 7/14/15.
; Copyright (c) 2014 One Girl, One Laptop Productions. All rights reserved.
;
.include "macros.s"
.include "switches.s"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Mouse clamping values. These are set to a convenient one-byte
; range, but you can change to suit your application. If you
; change them, also change the scaling math below (search for SCALING)
; Hardware produces values 0-1023 in both dimensions if not clamped
SCALE_X_IIE = $027f ; 640-1
SCALE_Y_IIE = $02e0 ; 736
; //c tracks much slower, so smaller clamps and no scaling works better
SCALE_X_IIC = $004f ; 8-1
SCALE_Y_IIC = $0017 ; 24-1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 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_VBLINT = %00001000
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 II ROM
; versions. This macro helps abstracts this for us.
.macro CALLMOUSE name
ldx #name
jsr WGCallMouse
.endmacro
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; WGEnableMouse
; Prepares the mouse for use
;
WGEnableMouse:
pha
SETSWITCH PAGE2OFF
; 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 ; Sorry //c, no scaling for you
lda #<SCALE_X_IIE
sta MOUSE_CLAMPL
lda #>SCALE_X_IIE
sta MOUSE_CLAMPH
lda #0
CALLMOUSE CLAMPMOUSE
lda #<SCALE_Y_IIE
sta MOUSE_CLAMPL
lda #>SCALE_Y_IIE
sta MOUSE_CLAMPH
lda #1
CALLMOUSE CLAMPMOUSE
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 #<SCALE_X_IIC
sta MOUSE_CLAMPL
lda #>SCALE_X_IIC
sta MOUSE_CLAMPH
lda #0
CALLMOUSE CLAMPMOUSE
lda #<SCALE_Y_IIC
sta MOUSE_CLAMPL
lda #>SCALE_Y_IIC
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
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 II, 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
; SCALING: If you change the clamps, change this division from
; 1024 to match your new values.
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
; Stub for your use
;
WGUndrawPointer:
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; WGDrawPointer
; Plots the mouse pointer at current location
; Stub for your use
;
WGDrawPointer:
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Mouse API state
;
; Useful things you can poll in your code:
WG_MOUSEACTIVE:
.byte 0
WG_MOUSEPOS_X:
.byte 39
WG_MOUSEPOS_Y:
.byte 11
WG_MOUSECLICK_X:
.byte $ff
WG_MOUSECLICK_Y:
.byte 0
; Internal state for the driver (no touchy!)
WG_MOUSE_STAT:
.byte 0
WG_MOUSEBG:
.byte 0
WG_APPLEIIC:
.byte 0
WG_MOUSE_JUMPL:
.byte 0
WG_MOUSE_JUMPH:
.byte 0
WG_MOUSE_SLOT:
.byte 0
WG_MOUSE_SLOTSHIFTED:
.byte 0
WG_MOUSE_BUTTON_DOWN:
.byte 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 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