Skip to content

Instantly share code, notes, and snippets.

@taylorza
Created October 15, 2022 20:44
Show Gist options
  • Save taylorza/a759f87a5aa4970005855705d8c43a09 to your computer and use it in GitHub Desktop.
Save taylorza/a759f87a5aa4970005855705d8c43a09 to your computer and use it in GitHub Desktop.
ZX Spectrum Next - Mouse Example
SLDOPT COMMENT WPMEM, LOGPOINT, ASSERTION
DEVICE ZXSPECTRUMNEXT
CSPECTMAP "mousedemo.map"
org $8000
MouseMinX equ 0
MouseMaxX equ 319
MouseMinY equ 0
MouseMaxY equ 255
main:
nextreg 7,0 ; Run at 3.5Mhz
nextreg $a, 1 ; Set mouse DPI
; 0 - Low DPI
; 1 - Default
; 2 - Medium DPI
; 3 - High DPI
call 0x0daf ; ROM CLS
call initSprite ; Initialize mouse cursor sprite
di
nextreg $c0, (ivt & %11100000) | 1 ; setup vector table, enable Next IM2
nextreg $c4, %00000001 ; enable ULA interrupts
nextreg $c5, %00000000 ; disable CTC interrupts
nextreg $c6, %00000000 ; disable UART interrupts
ld a, high ivt ; high byte of the interrupt vector table
ld i, a ; loaded into the I register
; Init Mouse - Read starting mouse position
ld bc, $fbdf
in a, (c) ; A = starting mouse X
ld (mouseX), a ; Store starting mouse X
ld bc, $ffdf
in a, (c) ; A = starting mouse Y
ld (mouseY), a ; Store starting mouse Y
im 2 ; switch CPU to interrupt mode 2
ei ; enable interrupts
gameLoop
halt ; Wait for new frame
; Update mouse cursor
; This could be done in the interrupt handler
; But is done here as a demonstration of using the coordinates
nextreg $34, 0 ; Select the cursor sprite
ld a, (cursorX) ; Get mouse cursor X Least significant byte (LSB)
nextreg $35, a ; Set sprite X - LSB
ld a, (cursorX + 1) ; Get mouse cursor X Most significant bit (MSb)
and %00000001 ; Ensure high bits are all 0
nextreg $37, a ; Set sprite X - MSb
ld a, (cursorY) ; Get mouse cursor Y LSB
nextreg $36, a ; Set sprite Y LSB
jp gameLoop
border db 0
cursorX dw 160
cursorY dw 128
mouseY db 0
mouseX db 0
mouseBtn db 0
mouseWheel db 0
initSprite
; Setup cursor pattern
ld bc, $303b ; Select pattern
xor a ; 0 - mouse cursor pattern
out (c), a
ld hl, sprite_cursor ; HL = Pointer to pattern data
ld bc, $ff5b ; B = 255 pattern bytes, C = Pattern memory port
otir ; Write pattern data to pattern memory
; Setup cursor sprite
nextreg $34, 0 ; Select sprite 0
nextreg $38, %10000000 ; Visible + Pattern 0
; Enable Sprites
nextreg $15, %01000011 ; Sprite 1 in front, S-L-U, Over border, visible
ret
;------------------------------------------------------------------------------
; ULA Interrupt Handler - Updates mouse coordinates and calls default ULA ROM
ulahandler:
nop
push af
push bc
push de
push hl
ld bc, $fadf ; Mouse Wheel and buttons
in a, (c) ; Read mouse data
ld b, a ; Save button and wheel data in B
rrca ; Shift wheel reading to lower bits
rrca
rrca
rrca
and %00001111 ; Mask wheel value
ld (mouseWheel), a ; Store wheel value
ld a, b ; Restore wheel and button data from B
and %00000111 ; Mask buttons
xor %00000111 ; Invert bits 1-pressed, 0-not pressed
ld (mouseBtn), a ; Store button state
; Process mouse X movement
ld bc, $fbdf ; MouseX Port
in a, (c) ; A = New mouse X
ld hl, mouseX ; Point to the previous mouse X
ld b, (hl) ; B = Previous mouse X
ld (hl), a ; Store new mouse X
sub b ; A = Delta mouse X
or a ; Check if delta mouse = 0
jp z, .checkMoveY ; Skip check Y if no change in X
ld hl, (cursorX) ; HL = Current cursorX
ld e, a ; DE = Delta mouse X --|
add a ; Get sign bit into CF |- Sign extend Delta into DE
sbc a ; Extend CF through A |
ld d, a ; Sign extend into D --|
add hl, de ; Update cursorX by delta X
; Clamp cursor X to MouseMinX and MouseMaxX
ld de, MouseMinX ; Prepare to compare HL (cursorX)
or a ; Clear CF
sbc hl, de ; MouseMinX - HL
jp m, .clipMinX ; If M (minus) flag is set then HL is less than MouseMinX
add hl, de ; Restore X
ld de, MouseMaxX ; Prepare to compare HL (cursor)
or a ; Clear CF
sbc hl, de ; HL-MouseMaxX
jp m, .restoreX ; HL <= MaxMouseX save the new cursorX
ld hl, MouseMaxX ; Clamp HL to MouseMaxX
jp .saveCursorX
.restoreX
add hl, de ; Restore CursorX in HL
jp .saveCursorX ; Save the new X location
.clipMinX
ld hl, MouseMinX ; Clip X coordinate to MouseMinX
.saveCursorX
ld (cursorX), hl ; Store the latest X coordinate
; Process mouse Y movement
.checkMoveY
ld bc, $ffdf
in a, (c) ; A = New mouse Y
ld hl, mouseY ; Point to the previous mouse Y
ld b, (hl) ; B = Previous mouse Y
ld (hl), a ; Store new mouse Y
sub b ; A = Delta mouse Y
or a ; Check if delta mouse = 0
jp z, .doneMoveY ; Skip if no change in Y
ld hl, (cursorY) ; HL = Current cursorY
ld e, a ; DE = Delta mouse Y --|
add a ; Get sign bit into CF |- Sign extend Delta into DE
sbc a ; Extend CF through A |
ld d, a ; Sign extend into D --|
or a ; Clear CF
sbc hl, de ; Update cursorY by delta Y
; Clamp cursor Y to MouseMinY and MouseMaxY
ld de, MouseMinY ; Prepare to compare HL (cursorY)
or a ; Clear CF
sbc hl, de ; HL-MouseMinX
jp m, .clipMinY ; If M (minus) flag is set then HL is less than MouseMinY
add hl, de ; Restore HL
ld de, MouseMaxY ; Prepare to compare HL (cursor)
or a ; Clear CF
sbc hl, de ; HL-MouseMaxY
jp m, .restoreY ; HL <= MaxMouseY save the new cursorY
ld hl, MouseMaxY ; Clamp HL to MouseMaxY
jp .saveCursorY
.restoreY
add hl, de ; Restore CursorY in HL
jp .saveCursorY ; Save the new Y location
.clipMinY
ld hl, MouseMinY ; Clip Y coordinate to MouseMinY
.saveCursorY
ld (cursorY), hl ; Store the latest Y coordinate
.doneMoveY
pop hl
pop de
pop bc
pop af
;call $38 ; Run ROM Handler
ei
reti ; Return from Interrupt
;------------------------------------------------------------------------------
; Default Interrupt Handler - Does nothing but reenable interrupts and return
; This is not called because all the associated interrupt have been disabled
inthandler:
ei ; Reenable interrupts
reti ; Return from Interrupt
;------------------------------------------------------------------------------
; Interupt Vector Table
align 32
ivt:
dw inthandler ; 0 - line interrupt
dw inthandler ; 1 - uart0 rx
dw inthandler ; 2 - uart1 rx
dw inthandler ; 3 - ctc 0
dw inthandler ; 4 - ctc 1
dw inthandler ; 5 - ctc 2
dw inthandler ; 6 - ctc 3
dw inthandler ; 7 - ctc 4
dw inthandler ; 8 - ctc 5
dw inthandler ; 9 - ctc 6
dw inthandler ; 10 - ctc 7
dw ulahandler ; 11 - ula <--- Our handler for ULA Interrups
dw inthandler ; 12 - uart0 tx
dw inthandler ; 13 - uart1 tx
dw inthandler ; 14
dw inthandler ; 15
sprite_cursor
db $0, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3
db $0, $0, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3
db $0, $ff, $0, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3
db $0, $ff, $ff, $0, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3
db $0, $ff, $ff, $ff, $0, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3
db $0, $ff, $ff, $ff, $ff, $0, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3
db $0, $ff, $ff, $ff, $ff, $ff, $0, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3
db $0, $ff, $ff, $ff, $ff, $ff, $ff, $0, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3
db $0, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $0, $e3, $e3, $e3, $e3, $e3, $e3, $e3
db $0, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $0, $e3, $e3, $e3, $e3, $e3, $e3
db $0, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $0, $e3, $e3, $e3, $e3, $e3
db $0, $ff, $ff, $ff, $0, $0, $0, $0, $0, $0, $0, $0, $e3, $e3, $e3, $e3
db $0, $ff, $ff, $0, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3
db $0, $ff, $0, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3
db $0, $0, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3
db $0, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3, $e3
;------------------------------------------------------------------------------
; Stack reservation
STACK_SIZE equ 100
stack_bottom:
defs STACK_SIZE * 2
stack_top:
defw 0
;------------------------------------------------------------------------------
; Output configuration
SAVENEX OPEN "mousedemo.nex", main, stack_top
SAVENEX CORE 2,0,0
SAVENEX CFG 6,0,0,0
SAVENEX AUTO
SAVENEX CLOSE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment