1
0
mirror of https://github.com/cc65/cc65.git synced 2024-11-19 06:31:31 +00:00
cc65/libsrc/c64/mou/c64-joy.s
Oliver Schmidt 4065cb1983 Harmonized interface between mouse drivers and callbacks.
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.
2014-01-15 22:47:59 +01:00

449 lines
13 KiB
ArmAsm

;
; Driver for a "joystick mouse".
;
; Ullrich von Bassewitz, 2004-03-29, 2009-09-26
; 2010-02-08, Greg King
;
; The driver prevents the keyboard from interfering by changing the
; keyboard's output port into an input port while the driver reads its
; controller device. That disables a wire that is left active by the
; Kernal. That wire is used by the STOP-key to break out of BASIC
; programs -- CC65 programs don't use that feature. The wire is shared
; by these keys: STOP, "Q", Commodore, Space, "2", CTRL, Left-Arrow, and
; "1". I listed them, in order, from bit 7 over to bit 0. The
; rightmost five keys can look like joystick switches.
;
; The driver prevents the mouse/joystick from interfering by "blinding"
; the keyboard scanner while any button/switch is active. It changes
; the input port into an output port, then stores all zero-bits in that
; port's latch. Reading from an output port sees the bitwise-AND of the
; latch and the input signals. Therefore, the scanner thinks that eight
; keys are being pushed at the same time. It doesn't know what to do
; about that condition; so, it does nothing. The driver lets the
; scanner see normally, again, when no buttons/switches are active.
;
.include "zeropage.inc"
.include "mouse-kernel.inc"
.include "c64.inc"
.macpack generic
; ------------------------------------------------------------------------
; Header. Includes jump table
.segment "JUMPTABLE"
HEADER:
; Driver signature
.byte $6d, $6f, $75 ; "mou"
.byte MOUSE_API_VERSION ; Mouse driver API version number
; Library reference
.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_LATE_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 coord
CMOVEY: jmp $0000 ; Move the cursor to Y coord
;----------------------------------------------------------------------------
; Constants
SCREEN_HEIGHT = 200
SCREEN_WIDTH = 320
.enum JOY
UP = $01
DOWN = $02
LEFT = $04
RIGHT = $08
FIRE = $10
.endenum
;----------------------------------------------------------------------------
; 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
; reorder them.
.bss
Vars:
YPos: .res 2 ; Current mouse position, Y
XPos: .res 2 ; Current mouse position, X
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 mask
; Temporary value used in the int handler
Temp: .res 1
.rodata
; Default values for above variables
; (We use ".proc" because we want to define both a label and a scope.)
.proc DefVars
.word SCREEN_HEIGHT/2 ; YPos
.word SCREEN_WIDTH/2 ; XPos
.word 0 ; XMin
.word 0 ; YMin
.word SCREEN_WIDTH ; XMax
.word SCREEN_HEIGHT ; YMax
.byte 0 ; Buttons
.endproc
.code
;----------------------------------------------------------------------------
; INSTALL routine. Is called after the driver is loaded into memory. If
; possible, check if the hardware is present.
; Must return an MOUSE_ERR_xx code in a/x.
INSTALL:
; Initialize variables. Just copy the default stuff over
ldx #.sizeof(DefVars)-1
@L1: lda DefVars,x
sta Vars,x
dex
bpl @L1
; Be sure the mouse cursor is invisible and at the default location. We
; need to do that here, because our mouse interrupt handler doesn't set the
; mouse position if it hasn't changed.
sei
jsr CHIDE
lda XPos
ldx XPos+1
jsr CMOVEX
lda YPos
ldx YPos+1
jsr CMOVEY
cli
; Done, return zero (= MOUSE_ERR_OK)
ldx #$00
txa
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 = HIDE ; Hide cursor on exit
;----------------------------------------------------------------------------
; HIDE routine. Is called to hide the mouse pointer. The mouse kernel manages
; a counter for calls to show/hide, and the driver entry point is only called
; if the mouse is currently visible and should get hidden. For most drivers,
; no special action is required besides hiding the mouse cursor.
; No return code required.
HIDE: sei
jsr CHIDE
cli
rts
;----------------------------------------------------------------------------
; SHOW routine. Is called to show the mouse pointer. The mouse kernel manages
; a counter for calls to show/hide, and the driver entry point is only called
; if the mouse is currently hidden and should become visible. For most drivers,
; no special action is required besides enabling the mouse cursor.
; No return code required.
SHOW: sei
jsr CSHOW
cli
rts
;----------------------------------------------------------------------------
; SETBOX: Set the mouse bounding box. The parameters are passed as they come
; from the C program, that is, a pointer to a mouse_box struct in a/x.
; No checks are done if the mouse is currently inside the box, this 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 mouse bounding box. The parameters are passed as they
; come from the C program, that is, a pointer to a mouse_box struct in a/x.
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 a/x. 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
sta YPos
stx YPos+1 ; New Y position
jsr CMOVEY ; Set it
ldy #$01
lda (sp),y
sta XPos+1
tax
dey
lda (sp),y
sta XPos ; New X position
jsr CMOVEX ; Move the cursor
cli ; Allow interrupts
rts
;----------------------------------------------------------------------------
; BUTTONS: Return the button mask in a/x.
BUTTONS:
lda Buttons
ldx #$00
rts
;----------------------------------------------------------------------------
; POS: Return the mouse 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 ; Done
;----------------------------------------------------------------------------
; INFO: Returns mouse 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 will just
; call _mouse_pos to initialize the struct pointer and fill the position
; fields.
INFO: jsr POS
; Fill in the button state
lda Buttons
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 a/x.
;
IOCTL: lda #<MOUSE_ERR_INV_IOCTL ; We don't support ioclts for now
ldx #>MOUSE_ERR_INV_IOCTL
rts
;----------------------------------------------------------------------------
; IRQ: Irq handler entry point. Called as a subroutine but in 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
; Avoid crosstalk between the keyboard and a joystick.
ldy #%00000000 ; Set ports A and B to input
sty CIA1_DDRB
sty CIA1_DDRA ; Keyboard won't look like joystick
lda CIA1_PRB ; Read Control-Port 1
dec CIA1_DDRA ; Set port A back to output
eor #%11111111 ; Bit goes up when switch goes down
beq @Save ;(bze)
dec CIA1_DDRB ; Joystick won't look like keyboard
sty CIA1_PRB ; Set "all keys pushed"
@Save: sta Temp
; Check for a pressed button and place the result into Buttons
ldx #$00 ; Assume no button pressed
and #JOY::FIRE ; Check fire button
beq @L0 ; Jump if not pressed
ldx #MOUSE_BTN_LEFT ; Left (only) button is pressed
@L0: stx Buttons
; Check left/right
lda Temp ; Read joystick #0
and #(JOY::LEFT | JOY::RIGHT)
beq @SkipX ;
; We will cheat here and rely on the fact that either the left, OR the right
; bit can be active
and #JOY::RIGHT ; Check RIGHT bit
bne @Right
lda #$FF
tax
bne @AddX ; Branch always
@Right: lda #$01
ldx #$00
; Calculate the new X coordinate (--> a/y)
@AddX: add XPos
tay ; Remember low byte
txa
adc XPos+1
tax
; Limit the X coordinate 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: sty XPos
stx XPos+1
; Move the mouse pointer to the new X pos
tya
jsr CMOVEX
; Calculate the Y movement vector
@SkipX: lda Temp ; Read joystick #0
and #(JOY::UP | JOY::DOWN) ; Check up/down
beq @SkipY ;
; We will cheat here and rely on the fact that either the up, OR the down
; bit can be active
lsr a ; Check UP bit
bcc @Down
lda #$FF
tax
bne @AddY
@Down: lda #$01
ldx #$00
; Calculate the new Y coordinate (--> a/y)
@AddY: add YPos
tay ; Remember low byte
txa
adc YPos+1
tax
; Limit the Y coordinate to the bounding box
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: sty YPos
stx YPos+1
; Move the mouse pointer to the new X pos
tya
jsr CMOVEY
; Done
@SkipY: jsr CDRAW
clc ; Interrupt not "handled"
rts