Created
October 15, 2022 20:44
-
-
Save taylorza/a759f87a5aa4970005855705d8c43a09 to your computer and use it in GitHub Desktop.
ZX Spectrum Next - Mouse Example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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