The Apple2 doesn't have sprites so the Apple2 mouse callbacks place a special character on the text screen to indicate the mouse position. In order to support the necessary character removing and redrawing the Apple2 mouse driver called the Apple2 mouse callbacks in an "unusual way". So far so (sort of) good. However the upcoming Atari mouse driver aims to support both "sprite-type" mouse callbacks as well as "text-char-type" mouse callbacks. Therefore the interface between mouse drivers and callbacks needs to be extended to allow the mouse callbacks to hide their different types from the mouse driver. The nature of this change can be seen best by looking at the Apple2 file modifications. The CBM drivers and callbacks (at least the current ones) don't benefit from this change.
460 lines
13 KiB
ArmAsm
460 lines
13 KiB
ArmAsm
;
|
|
; Driver for the Inkwell Systems 170-C and 184-C lightpens.
|
|
;
|
|
; 2013-07-01, Greg King
|
|
;
|
|
|
|
.include "zeropage.inc"
|
|
.include "mouse-kernel.inc"
|
|
.include "c128.inc"
|
|
|
|
.macpack generic
|
|
|
|
; ------------------------------------------------------------------------
|
|
; Header. Includes jump table.
|
|
|
|
.segment "JUMPTABLE"
|
|
|
|
HEADER:
|
|
|
|
; Driver signature
|
|
|
|
.byte $6d, $6f, $75 ; ASCII "mou"
|
|
.byte MOUSE_API_VERSION ; Mouse driver API version number
|
|
|
|
; Library reference
|
|
|
|
LIBREF: .addr $0000
|
|
|
|
; Jump table
|
|
|
|
.addr INSTALL
|
|
.addr UNINSTALL
|
|
.addr HIDE
|
|
.addr SHOW
|
|
.addr SETBOX
|
|
.addr GETBOX
|
|
.addr MOVE
|
|
.addr BUTTONS
|
|
.addr POS
|
|
.addr INFO
|
|
.addr IOCTL
|
|
.addr IRQ
|
|
|
|
; Mouse driver flags
|
|
|
|
.byte MOUSE_FLAG_EARLY_IRQ
|
|
|
|
; Callback table, set by the kernel before INSTALL is called.
|
|
|
|
CHIDE: jmp $0000 ; Hide the cursor
|
|
CSHOW: jmp $0000 ; Show the cursor
|
|
CDRAW: jmp $0000 ; Draw the cursor
|
|
CMOVE: jmp $0000 ; Prepare to move the cursor
|
|
CMOVEX: jmp $0000 ; Move the cursor to X co-ord.
|
|
CMOVEY: jmp $0000 ; Move the cursor to Y co-ord.
|
|
|
|
|
|
;----------------------------------------------------------------------------
|
|
; Constants
|
|
|
|
; This driver is for the 40-column screen.
|
|
|
|
XSIZE = 40
|
|
YSIZE = 25
|
|
|
|
SCREEN_WIDTH = XSIZE * 8
|
|
SCREEN_HEIGHT = YSIZE * 8
|
|
|
|
|
|
;----------------------------------------------------------------------------
|
|
; Global variables. The bounding box values are sorted so that they can be
|
|
; written with the least effort in the SETBOX and GETBOX routines; so, don't
|
|
; re-order them.
|
|
|
|
.rodata
|
|
|
|
; Default values for below variables
|
|
; (We use ".proc" because we want to define both a label and a scope.)
|
|
|
|
.proc DefVars
|
|
.word 0 ; XMin
|
|
.word 0 ; YMin
|
|
.word SCREEN_WIDTH - 1 ; XMax
|
|
.word SCREEN_HEIGHT - 1 ; YMax
|
|
.byte %00000000 ; Buttons
|
|
.endproc
|
|
|
|
.bss
|
|
|
|
Vars:
|
|
XMin: .res 2 ; X1 value of bounding box
|
|
YMin: .res 2 ; Y1 value of bounding box
|
|
XMax: .res 2 ; X2 value of bounding box
|
|
YMax: .res 2 ; Y2 value of bounding box
|
|
Buttons: .res 1 ; Button status bits
|
|
|
|
XPos: .res 2 ; Current lightpen position, X
|
|
YPos: .res 2 ; Current lightpen position, Y
|
|
|
|
OldPenX: .res 1 ; Previous HW-counter values
|
|
OldPenY: .res 1
|
|
|
|
INIT_save: .res 1
|
|
|
|
.data
|
|
|
|
; Default Inkwell calibration.
|
|
; The first number is the width of the left border;
|
|
; the second number is the actual calibration value.
|
|
|
|
XOffset: .byte (24 + 24) / 2 ; x-offset
|
|
|
|
; Jump to a function that puts a new calibration value into XOffset.
|
|
Calibrate: jmp $0000
|
|
|
|
|
|
.code
|
|
|
|
;----------------------------------------------------------------------------
|
|
; INSTALL routine. Is called after the driver is loaded into memory. If
|
|
; possible, check if the hardware is present.
|
|
; Must return a MOUSE_ERR_xx code in .XA.
|
|
|
|
INSTALL:
|
|
|
|
; Disable the BASIC interpreter's interrupt-driven sprite-motion code.
|
|
; That allows direct access to the VIC-IIe's sprite registers.
|
|
|
|
lda INIT_STATUS
|
|
sta INIT_save
|
|
lda #%11000000
|
|
sta INIT_STATUS
|
|
|
|
; Initiate variables. Just copy the default stuff over.
|
|
|
|
ldx #.sizeof (DefVars) - 1
|
|
@L0: lda DefVars,x
|
|
sta Vars,x
|
|
dex
|
|
bpl @L0
|
|
|
|
ldx VIC_LPEN_X
|
|
ldy VIC_LPEN_Y
|
|
stx OldPenX
|
|
sty OldPenY
|
|
|
|
; Call a calibration function through the library-reference.
|
|
|
|
lda LIBREF
|
|
ldx LIBREF+1
|
|
sta ptr1 ; Point to mouse_adjuster
|
|
stx ptr1+1
|
|
ldy #1
|
|
lda (ptr1),y
|
|
bze @L1 ; Don't call pointer if it's NULL
|
|
sta Calibrate+2 ; Point to function
|
|
dey
|
|
lda (ptr1),y
|
|
sta Calibrate+1
|
|
lda #<XOffset ; Function will set this variable
|
|
ldx #>XOffset
|
|
jsr Calibrate
|
|
|
|
; Be sure that the lightpen cursor is invisible and at the default location.
|
|
; It needs to be done here because the lightpen interrupt handler doesn't
|
|
; set the lightpen position if it hasn't changed.
|
|
|
|
@L1: sei
|
|
jsr CHIDE
|
|
|
|
lda #<(SCREEN_HEIGHT / 2)
|
|
ldx #>(SCREEN_HEIGHT / 2)
|
|
jsr MoveY
|
|
lda #<(SCREEN_WIDTH / 2)
|
|
ldx #>(SCREEN_WIDTH / 2)
|
|
jsr MoveX
|
|
cli
|
|
|
|
; Done, return zero.
|
|
|
|
lda #MOUSE_ERR_OK
|
|
tax
|
|
rts
|
|
|
|
;----------------------------------------------------------------------------
|
|
; UNINSTALL routine. Is called before the driver is removed from memory.
|
|
; No return code required (the driver is removed from memory on return).
|
|
|
|
UNINSTALL:
|
|
jsr HIDE ; Hide cursor on exit
|
|
lda INIT_save
|
|
sta INIT_STATUS
|
|
rts
|
|
|
|
;----------------------------------------------------------------------------
|
|
; HIDE routine. Is called to hide the lightpen pointer. The mouse kernel manages
|
|
; a counter for calls to show/hide, and the driver entry point is called only
|
|
; if the mouse is currently visible, and should get hidden. For most drivers,
|
|
; no special action is required besides hiding the lightpen cursor.
|
|
; No return code required.
|
|
|
|
HIDE: sei
|
|
jsr CHIDE
|
|
cli
|
|
rts
|
|
|
|
;----------------------------------------------------------------------------
|
|
; SHOW routine. Is called to show the lightpen pointer. The mouse kernel manages
|
|
; a counter for calls to show/hide, and the driver entry point is called only
|
|
; if the mouse is currently hidden, and should become visible. For most drivers,
|
|
; no special action is required besides enabling the lightpen cursor.
|
|
; No return code required.
|
|
|
|
SHOW: sei
|
|
jsr CSHOW
|
|
cli
|
|
rts
|
|
|
|
;----------------------------------------------------------------------------
|
|
; SETBOX: Set the lightpen bounding box. The parameters are passed as they come
|
|
; from the C program, that is, a pointer to a mouse_box struct in .XA.
|
|
; No checks are done if the lightpen is currently inside the box, that is the job
|
|
; of the caller. It is not necessary to validate the parameters; trust the
|
|
; caller; and, save some code here. No return code required.
|
|
|
|
SETBOX: sta ptr1
|
|
stx ptr1+1 ; Save data pointer
|
|
|
|
ldy #.sizeof (MOUSE_BOX) - 1
|
|
sei
|
|
|
|
@L1: lda (ptr1),y
|
|
sta XMin,y
|
|
dey
|
|
bpl @L1
|
|
|
|
cli
|
|
rts
|
|
|
|
;----------------------------------------------------------------------------
|
|
; GETBOX: Return the lightpen bounding box. The parameters are passed as they
|
|
; come from the C program, that is, a pointer to a mouse_box struct in .XA.
|
|
|
|
GETBOX: sta ptr1
|
|
stx ptr1+1 ; Save data pointer
|
|
|
|
ldy #.sizeof (MOUSE_BOX) - 1
|
|
@L1: lda XMin,y
|
|
sta (ptr1),y
|
|
dey
|
|
bpl @L1
|
|
rts
|
|
|
|
;----------------------------------------------------------------------------
|
|
; MOVE: Move the mouse to a new position. The position is passed as it comes
|
|
; from the C program, that is: X on the stack and Y in .XA. The C wrapper will
|
|
; remove the parameter from the stack on return.
|
|
; No checks are done if the new position is valid (within the bounding box or
|
|
; the screen). No return code required.
|
|
;
|
|
|
|
MOVE: sei ; No interrupts
|
|
jsr MoveY
|
|
|
|
ldy #$01
|
|
lda (sp),y
|
|
tax
|
|
dey
|
|
lda (sp),y
|
|
jsr MoveX ; Move the cursor
|
|
|
|
cli ; Allow interrupts
|
|
rts
|
|
|
|
;----------------------------------------------------------------------------
|
|
; BUTTONS: Return the button mask in .XA.
|
|
|
|
BUTTONS:
|
|
lda Buttons
|
|
ldx #>0
|
|
|
|
; Make the lightpen buttons look like a 1351 mouse.
|
|
|
|
asl a
|
|
asl SID_ADConv2 ; PotY
|
|
rol a
|
|
eor #MOUSE_BTN_RIGHT
|
|
and #MOUSE_BTN_LEFT | MOUSE_BTN_RIGHT
|
|
rts
|
|
|
|
;----------------------------------------------------------------------------
|
|
; POS: Return the lightpen position in the MOUSE_POS struct pointed to by ptr1.
|
|
; No return code required.
|
|
|
|
POS: ldy #MOUSE_POS::XCOORD ; Structure offset
|
|
|
|
sei ; Disable interrupts
|
|
lda XPos ; Transfer the position
|
|
sta (ptr1),y
|
|
lda XPos+1
|
|
iny
|
|
sta (ptr1),y
|
|
lda YPos
|
|
iny
|
|
sta (ptr1),y
|
|
lda YPos+1
|
|
cli ; Enable interrupts
|
|
|
|
iny
|
|
sta (ptr1),y ; Store last byte
|
|
rts
|
|
|
|
;----------------------------------------------------------------------------
|
|
; INFO: Returns lightpen position and current button mask in the MOUSE_INFO
|
|
; struct pointed to by ptr1. No return code required.
|
|
;
|
|
; We're cheating here, to keep the code smaller: The first fields of the
|
|
; mouse_info struct are identical to the mouse_pos struct; so, we'll just
|
|
; call _mouse_pos to initiate the struct pointer, and fill the position
|
|
; fields.
|
|
|
|
INFO: jsr POS
|
|
|
|
; Fill in the button state
|
|
|
|
jsr BUTTONS ; Will not touch ptr1
|
|
ldy #MOUSE_INFO::BUTTONS
|
|
sta (ptr1),y
|
|
rts
|
|
|
|
;----------------------------------------------------------------------------
|
|
; IOCTL: Driver-defined entry point. The wrapper will pass a pointer to ioctl-
|
|
; specific data in ptr1, and the ioctl code in .A.
|
|
; Must return an error code in .XA.
|
|
;
|
|
|
|
IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioctls, for now
|
|
ldx #>MOUSE_ERR_INV_IOCTL
|
|
rts
|
|
|
|
;----------------------------------------------------------------------------
|
|
; IRQ: IRQ handler entry point. Called as a subroutine, but in the IRQ context
|
|
; (so, be careful). The routine MUST return carry set if the interrupt has been
|
|
; 'handled' -- which means that the interrupt source is gone. Otherwise, it
|
|
; MUST return carry clear.
|
|
;
|
|
|
|
IRQ: jsr CMOVE
|
|
|
|
; Record the state of the buttons.
|
|
; Try to avoid crosstalk between the keyboard and the lightpen.
|
|
|
|
ldy #%00000000 ; Set ports A and B to input
|
|
sty CIA1_DDRB
|
|
sty CIA1_DDRA ; Keyboard won't look like buttons
|
|
lda CIA1_PRB ; Read Control-Port 1
|
|
dec CIA1_DDRA ; Set port A back to output
|
|
eor #%11111111 ; Bit goes up when button goes down
|
|
sta Buttons
|
|
bze @L0
|
|
lda #%11101111 ; (Don't change bit that feeds VIC-II)
|
|
sta CIA1_DDRB ; Buttons won't look like keyboard
|
|
sty CIA1_PRB ; Set "all keys pushed"
|
|
|
|
; Read the VIC-II lightpen registers.
|
|
|
|
@L0: lda VIC_LPEN_Y
|
|
cmp OldPenY
|
|
|
|
; Skip processing if nothing has changed.
|
|
|
|
beq @SkipY
|
|
sta OldPenY
|
|
|
|
; Subtract the height of the top border, so that the lightpen co-ordinate
|
|
; will match the TGI co-ordinate.
|
|
|
|
sub #50
|
|
tay ; Remember low byte
|
|
ldx #>0
|
|
|
|
; Limit the Y co-ordinate to the bounding box.
|
|
|
|
txa
|
|
cpy YMin
|
|
sbc YMin+1
|
|
bpl @L3
|
|
ldy YMin
|
|
ldx YMin+1
|
|
jmp @L4
|
|
|
|
@L3: txa
|
|
cpy YMax
|
|
sbc YMax+1
|
|
bmi @L4
|
|
ldy YMax
|
|
ldx YMax+1
|
|
|
|
@L4: tya
|
|
jsr MoveY
|
|
|
|
@SkipY: lda VIC_LPEN_X
|
|
cmp OldPenX
|
|
|
|
; Skip processing if nothing has changed.
|
|
|
|
beq @SkipX
|
|
sta OldPenX
|
|
|
|
; Adjust the value by the calibration offset.
|
|
|
|
sub XOffset
|
|
|
|
; Calculate the new X co-ordinate.
|
|
; The VIC-II register is eight bits; but, the screen co-ordinate is nine bits.
|
|
; Therefore, the VIC-II number is doubled. Then, it points to every other pixel;
|
|
; but, it can reach across the screen.
|
|
|
|
asl a
|
|
tay ; Remember low byte
|
|
lda #>0
|
|
rol a
|
|
tax ; Remember high byte
|
|
|
|
; Limit the X co-ordinate to the bounding box.
|
|
|
|
cpy XMin
|
|
sbc XMin+1
|
|
bpl @L1
|
|
ldy XMin
|
|
ldx XMin+1
|
|
jmp @L2
|
|
|
|
@L1: txa
|
|
cpy XMax
|
|
sbc XMax+1
|
|
bmi @L2
|
|
ldy XMax
|
|
ldx XMax+1
|
|
|
|
@L2: tya
|
|
jsr MoveX
|
|
|
|
; Done
|
|
|
|
@SkipX: jsr CDRAW
|
|
clc ; Interrupt not "handled"
|
|
rts
|
|
|
|
; Move the lightpen pointer to the new Y pos.
|
|
|
|
MoveY: sta YPos
|
|
stx YPos+1
|
|
jmp CMOVEY
|
|
|
|
; Move the lightpen pointer to the new X pos.
|
|
|
|
MoveX: sta XPos
|
|
stx XPos+1
|
|
jmp CMOVEX
|