From 5abc208168225c0498ad111874956264d339418a Mon Sep 17 00:00:00 2001 From: ConradoBadenas Date: Fri, 6 Mar 2026 18:45:26 +0100 Subject: [PATCH 1/3] examples.maskedsprites --- examples/maskedsprites.bas | 161 +++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 examples/maskedsprites.bas diff --git a/examples/maskedsprites.bas b/examples/maskedsprites.bas new file mode 100644 index 000000000..a9e0d45b5 --- /dev/null +++ b/examples/maskedsprites.bas @@ -0,0 +1,161 @@ +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyright (C) 2026 Conrado Badenas +' +' Example for +' Print Masked (AND+OR) Sprites, version 2026.03.06 +' ---------------------------------------------------------------- + +#include + +#include + +CONST NUMMAXSPRITES as UByte = 10 +CONST NUMSPRITES as UByte = 7 +if NUMSPRITES>NUMMAXSPRITES then stop + +dim i,temp as Integer +dim pointx1,pointx2,pointy1,pointy2 as Integer 'for subtractions +dim x0(0 to NUMSPRITES-1) as UByte +dim y0(0 to NUMSPRITES-1) as UByte +dim x1(0 to NUMSPRITES-1) as UByte +dim y1(0 to NUMSPRITES-1) as UByte + +for i=0 to NUMSPRITES-1 + y0(i)=255:y1(i)=255:x1(i)=randomLimit(240) +next i:x1(0)=120 + +SetVisibleScreen(5):if GetVisibleScreen()<>5 then STOP +paper 1:ink 7:border 0:cls + +for i=0 to 127 + pointx1=randomLimit(255):pointy1=randomLimit(191) + pointx2=randomLimit(255):pointy2=randomLimit(191) + plot pointx1,pointy1:draw pointx2-pointx1,pointy2-pointy1 +next i + +CopyScreen5ToScreen7() +'print at 11,8;"Esto es Screen5" +SetDrawingScreen7() 'Bank7 is set at $c000 +'print at 13,8;"Esto es Screen7" +SetDrawingScreen5() 'Bank7 is still set at $c000 +'print at 15,8;"Esto es Screen5" + +ASM + xor a ; 4 Ts reseteamos el contador + ld (23672),a ; 13 Ts de FRAMES a 0 +END ASM + +' Empezamos con VisibleScreen = DrawingScreen = 5 +bucle: + + ' La nueva VisibleScreen está lista para que la ULA la saque por la tele + ' Pero vamos a esperar al principio de un frame para que no haya tearing + ' El 2 de luego significa que obtendremos 25 FPS como máximo + ASM + ld hl,23672 +wait: + ld a,(hl) ; A = contador de FRAMES + cp 2 ; 25 FPS como máximo + jr c,wait ; repetimos si A < 2 + xor a + ld (hl),a ; reseteamos el contador de FRAMES a 0 + END ASM + + ToggleVisibleScreen() + ' Ahora se puede modificar la DrawingScreen porque no está Visible + + ' Restauramos fondo en x0,y0 si está guardado en MaskedSpritesBackground + for i=0 to NUMSPRITES-1 + if y0(i)<>255 then RestoreBackground(x0(i),y0(i),MaskedSpritesBackground(i)) + next i + + ' Actualizamos x0,y0 y calculamos nuevos x1,y1 + for i=1 to NUMSPRITES-1 + temp=x1(i): x0(i)=temp + temp=temp-1 + if temp<0 then temp=240 + x1(i)=temp + temp=y1(i): y0(i)=temp + if temp=255 then temp=randomLimit(176) + if randomLimit(3)=0 then temp=temp+randomLimit(2)-1 + if temp<0 then temp=0 + if temp>176 then temp=176 + y1(i)=temp + next i + + 'Código especial para el prota + temp=x1(0): x0(0)=temp + temp=temp+1 -2*(in($dffe) bAND 1) +2*((in($dffe)>>1) bAND 1) '-noP+noO + if temp<0 then temp=240 + if temp>240 then temp=0 + x1(0)=temp + temp=y1(0): y0(0)=temp + if temp=255 then temp=randomLimit(176) + temp=temp -(in($fdfe) bAND 1) +(in($fbfe) bAND 1) '-noA+noQ + if temp<0 then temp=0 + if temp>176 then temp=176 + y1(0)=temp + + ' Guardamos fondo en MaskedSpritesBackground y dibujamos sprites en x,y + for i=NUMSPRITES-1 to 1 step -1 + SaveBackgroundAndDrawSprite(x1(i),y1(i),MaskedSpritesBackground(i),@enemigo0) + next i + SaveBackgroundAndDrawSprite(x1(0),y1(0),MaskedSpritesBackground(0),@prota0) + + ' Cambiamos el Set de Backgrounds para la siguiente iteración + ChangeMaskedSpritesBackgroundSet() + + ' Hemos terminado de dibujar + ' DrawingScreen actual está lista para ser mostrada ahora mismo + ' Cambiamos de DrawingScreen para el siguiente ciclo de dibujo + ToggleDrawingScreen() + + ' Ahora VisibleScreen = DrawingScreen y + ' lo que se dibuje se verá mientras se dibuja + +goto bucle + +stop + +prota0: +ASM + defb %11000000,%00000000,%00001111,%00000000;1 + defb %10000000,%00011111,%00000000,%11100000;2 + defb %00000000,%00100000,%00000000,%00111110;3 + defb %00000000,%01001110,%00000000,%11111000;4 + defb %00000000,%01011001,%00000001,%00001100;5 + defb %00000000,%01011010,%00000000,%00010100;6 + defb %00000000,%00000011,%00000000,%11100010;7 + defb %00000000,%01111100,%00000000,%00100010;8 + defb %00000000,%01111100,%00000000,%00100010;8 + defb %00000000,%00000011,%00000000,%11100010;7 + defb %00000000,%01011010,%00000000,%00010100;6 + defb %00000000,%01011001,%00000001,%00001100;5 + defb %00000000,%01001110,%00000000,%11111000;4 + defb %00000000,%00100000,%00000000,%00111110;3 + defb %10000000,%00011111,%00000000,%11100000;2 + defb %11000000,%00000000,%00001111,%00000000;1 +END ASM + +enemigo0: +ASM + defb %00000011,%00000000,%11000000,%00000000;1 + defb %00000011,%01111000,%11000000,%00011110;2 + defb %00000011,%01000000,%11000000,%00010000;3 + defb %00000001,%01010000,%10000000,%00010100;4 + defb %00000000,%01001000,%00000000,%00100010;5 + defb %00000000,%00000010,%00000000,%01110000;6 + defb %11100000,%00000111,%00000111,%11100000;7 + defb %11110000,%00000010,%00001111,%01000000;8 + defb %11110000,%00000010,%00001111,%01000000;8 + defb %11100000,%00000111,%00000111,%11100000;7 + defb %00000000,%00001110,%00000000,%01000000;6' + defb %00000000,%01110100,%00000000,%00011110;5' + defb %00000001,%01000000,%10000000,%00010000;4' + defb %00000011,%01010000,%11000000,%00010100;3' + defb %00000011,%01001000,%11000000,%00010010;2' + defb %00000011,%00000000,%11000000,%00000000;1 +END ASM + From bf9d867c83f137e58acf6253f2c14dfc7b9c5797 Mon Sep 17 00:00:00 2001 From: ConradoBadenas Date: Fri, 6 Mar 2026 18:51:20 +0100 Subject: [PATCH 2/3] stdlib.cb.maskedsprites --- .../arch/zx48k/stdlib/cb/maskedsprites.bas | 456 ++++++++++++++++++ 1 file changed, 456 insertions(+) create mode 100644 src/lib/arch/zx48k/stdlib/cb/maskedsprites.bas diff --git a/src/lib/arch/zx48k/stdlib/cb/maskedsprites.bas b/src/lib/arch/zx48k/stdlib/cb/maskedsprites.bas new file mode 100644 index 000000000..8a2feb376 --- /dev/null +++ b/src/lib/arch/zx48k/stdlib/cb/maskedsprites.bas @@ -0,0 +1,456 @@ +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyright (C) 2026 Conrado Badenas +' Ideas taken from +' https://github.com/boriel-basic/zxbasic/blob/main/src/lib/arch/zx48k/stdlib/memorybank.bas +' by Juan Segura (a.k.a. Duefectu), +' https://github.com/oisee/antique-toy/blob/main/chapters/ch16-sprites/draft.md +' by Alice Vinogradova (a.k.a. oisee), and +' https://youtu.be/nBHXtI1Y-xU?t=434 +' by Benjamín (a.k.a. RetrobenSoft) +' +' Print Masked (AND+OR) Sprites, version 2026.03.06 +' ---------------------------------------------------------------- + +#ifndef __CB_MASKEDSPRITES__ + +REM Avoid recursive / multiple inclusion + +#define __CB_MASKEDSPRITES__ + +#include +#include +#include + + +' ---------------------------------------------------------------- +' Set the visible screen (either in bank5 or bank7) +' and updates the system variable BANKM. +' Only works on 128K and compatible models. +' Parameters: +' Ubyte: bank number 5 or 7 +' ---------------------------------------------------------------- +SUB FASTCALL SetVisibleScreen(bankNumber AS UByte) +ASM + ; A = bankNumber 5 or 7 with screen to be visible + and %00000010 ; bit1 = 0/1 for bank5/7 + rla + rla ; bit3 = 0/1 for bank5/7 + ld c,a + ld hl,$5b5c ; BANKM system variable + ld a,(hl) ; Read BANKM + and %11110111 ; Reset bit3 + or c ; Set screen in bank5/7 + ld bc,$7ffd ; Memory Bank control port + di ; Disable interrupts + ld (hl),a ; Update BANKM system variable + out (c),a ; Set the screen + ei ; Enable interrupts +END ASM +END SUB + + +' ---------------------------------------------------------------- +' Returns the bank of visible screen (either 5 or 7) +' according to system variable BANKM. +' Only works on 128K and compatible models. +' Returns: +' UByte: bank 5 or 7 +' ---------------------------------------------------------------- +FUNCTION FASTCALL GetVisibleScreen() AS UByte + RETURN ((PEEK $5b5c bAND %1000)>>2) bOR 5 +END FUNCTION + + +' ---------------------------------------------------------------- +' Toggles the visible screen (from 5 to 7, or from 7 to 5) +' and updates the system variable BANKM. +' Only works on 128K and compatible models. +' ---------------------------------------------------------------- +SUB FASTCALL ToggleVisibleScreen() + SetVisibleScreen(2 bXOR GetVisibleScreen() ) + '2 bXOR 5/7 = 7/5 +END SUB + + +' ---------------------------------------------------------------- +' Copy contents of screen5 to screen7 (display file + attribs) +' Only works on 128K and compatible models. +' ---------------------------------------------------------------- +SUB CopyScreen5ToScreen7() + DIM b AS UByte + + b = GetBank() + if b<>7 then SetBank(7) + MemCopy($4000,$c000,$1b00) + if b<>7 then SetBank(b) +END SUB + + +' ---------------------------------------------------------------- +' Copy contents of screen7 to screen5 (display file + attribs) +' Only works on 128K and compatible models. +' ---------------------------------------------------------------- +SUB CopyScreen7ToScreen5() + DIM b AS UByte + + b = GetBank() + if b<>7 then SetBank(7) + MemCopy($c000,$4000,$1b00) + if b<>7 then SetBank(b) +END SUB + + +' ---------------------------------------------------------------- +' Set ScreenBufferAddr and AttrBufferAddr to screen5 +' Only works on 128K and compatible models. +' ---------------------------------------------------------------- +SUB FASTCALL SetDrawingScreen5() + SetScreenBufferAddr($4000)' ld (.core.SCREEN_ADDR),hl + SetAttrBufferAddr($5800)' ld (.core.SCREEN_ATTR_ADDR),hl +END SUB + + +' ---------------------------------------------------------------- +' Put screen7 at $c000 (in case it is not), and +' Set ScreenBufferAddr and AttrBufferAddr to screen7 +' Only works on 128K and compatible models. +' Returns: +' Bank7 is set at $c000, old bank is removed +' UByte: bank that was at $c000 (to restore it manually IYW) +' ---------------------------------------------------------------- +FUNCTION FASTCALL SetDrawingScreen7() AS UByte + DIM b AS UByte + + b = GetBank() + if b<>7 then SetBank(7) + SetScreenBufferAddr($c000)' ld (.core.SCREEN_ADDR),hl + SetAttrBufferAddr($d800)' ld (.core.SCREEN_ATTR_ADDR),hl + RETURN b +END FUNCTION + + +' ---------------------------------------------------------------- +' Toggle ScreenBufferAddr and AttrBufferAddr between screen5,7 +' Only works on 128K and compatible models. +' ---------------------------------------------------------------- +SUB FASTCALL ToggleDrawingScreen() + SetScreenBufferAddr($8000 bXOR GetScreenBufferAddr() ) + SetAttrBufferAddr($8000 bXOR GetAttrBufferAddr() ) + '$8000 bXOR $4000/$c000 = $c000/$4000 + '$8000 bXOR $5800/$d800 = $d800/$5800 +END SUB + + +' ---------------------------------------------------------------- +' MaskedSpritesBackgroundSet = 0 or 1 is the Set of Backgrounds +' +' MaskedSpritesBackground(i) is the Dir where Background i begins +' +' ChangeMaskedSpritesBackgroundSet() changes the Set of Backgrounds +' Returns: +' Byte: new value of MaskedSpritesBackgroundSet (IYW to use it) +' ---------------------------------------------------------------- +dim MaskedSpritesBackgroundSet as Byte = 0 +#define MaskedSpritesBackground(i) (56064+((i)*2+MaskedSpritesBackgroundSet)*48) +FUNCTION FASTCALL ChangeMaskedSpritesBackgroundSet() as Byte + MaskedSpritesBackgroundSet = MaskedSpritesBackgroundSet bXOR 1 + RETURN MaskedSpritesBackgroundSet +END FUNCTION + + +' ---------------------------------------------------------------- +' Save background and Draw sprite in screen +' Parameters: +' UByte: X coordinate (0:left to 240:right) +' UByte: Y coordinate (0:up to 176:down) +' UInteger: Dir where background will be saved +' UInteger: Dir where sprite image begins +' ---------------------------------------------------------------- +SUB FASTCALL SaveBackgroundAndDrawSprite(X as UByte, Y as UByte, backgroundDir as UInteger, spriteImageDir as UInteger) +ASM + PROC + LOCAL shiftsprite, spriteOK, loop0, loopA, loopB + LOCAL branchA, branchB, nextA, nextB + ; A = X + pop de ; returnDir + exx + pop bc ; B = Y + ld c,a ; C = X +; BEGIN code from https://skoolkid.github.io/rom/asm/22AA.html + rlca + rlca + rlca ; A = %c4c3c2c1c0c7c6c5 + xor b + and %11000111 + xor b ; A = %c4c3b5b4b3c7c6c5 + rlca + rlca + ld e,a ; E = %b5b4b3c7c6c5c4c3 + ld a,b + and %11111000 + rra + rra + rra ; A = %.0.0.0b7b6b5b4b3 + xor b + and %11111000 + xor b + ld d,a ; D = %.0.0.0b7b6b2b1b0 +; END code from https://skoolkid.github.io/rom/asm/22AA.html + ld hl,(.core.SCREEN_ADDR) + add hl,de + ex de,hl ; DE = screenDir where drawing will start + ld a,c; ; A = X + and 7 + jr z,spriteOK ; jump if X is a multiple of 8 (unlikely) + ; continue if sprite must be shifted +shiftsprite: + pop bc ; backgroundDir + exx + pop hl ; spriteImageDir + push de ; returnDir + push ix + ld ixh,16 ; 16 scanlines + ld ixl,a ; IXl = X MOD 8 = 1,2,...,7 +loopB: + ld a,(hl) ; mask1 + inc hl + ld c,(hl) ; graph1 + inc hl + ld d,(hl) ; mask2 + inc hl + ld e,(hl) ; graph1 + inc hl + push hl ; spriteImageDir + + ld hl,$FF00 ; H = 255 , L = 0 + ld b,ixl +loop0: + scf + rra ; SCF + RRA injects a 1 in bit7 of A + rr d + rr h + srl c ; ShiftRightLogical injects a 0 in bit7 of C + rr e + rr l + djnz loop0 + ld b,a + push hl ; H,L = mask,graph 3rd byte + push de ; D,E = mask,graph 2nd byte + push bc ; B,C = mask,graph 1st byte + exx + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 1st byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 2nd byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 3rd byte done + dec e + dec e + + inc d + ld a,d + and 7 + jr z,branchB ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + exx + pop hl ; spriteImageDir + dec ixh + jp nz,loopB + pop ix + ret +branchB: + ld a,e + add a,32 ; for 1 out of 8 values of D + ld e,a + jr c,nextB ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) + ld a,d + sub 8 + ld d,a +nextB: + exx + pop hl ; spriteImageDir + dec ixh + jp nz,loopB + pop ix + ret + +spriteOK: + pop bc ; backgroundDir + pop hl ; spriteImageDir + exx + push de ; returnDir + exx + push ix + ld ixh,16 ; 16 scanlines +loopA: + ld a,(de) ; screen + ld (bc),a; ; save + inc bc + and (hl); ; mask + inc hl + or (hl) ; graph + inc hl + ld (de),a ; 1st byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + and (hl); ; mask + inc hl + or (hl) ; graph + inc hl + ld (de),a ; 2nd byte done + dec e + + inc d + ld a,d + and 7 + jr z,branchA ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + dec ixh + jp nz,loopA + pop ix + ret +branchA: + ld a,e + add a,32 ; for 1 out of 8 values of D + ld e,a + jr c,nextA ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) + ld a,d + sub 8 + ld d,a +nextA: + dec ixh + jp nz,loopA + pop ix + ret + ENDP +END ASM +END SUB + + +' ---------------------------------------------------------------- +' Restore background in screen +' Parameters: +' UByte: X coordinate (0:left to 240:right) +' UByte: Y coordinate (0:up to 176:down) +' UInteger: Dir where saved background begins +' ---------------------------------------------------------------- +SUB FASTCALL RestoreBackground(X as UByte, Y as UByte, backgroundDir as UInteger) +ASM + PROC + LOCAL loopC, loopD, branchC, branchD, nextC, nextD + ; A = X + pop de ; returnDir + exx + pop bc ; B = Y + ld c,a ; C = X +; BEGIN code from https://skoolkid.github.io/rom/asm/22AA.html + rlca + rlca + rlca ; A = %c4c3c2c1c0c7c6c5 + xor b + and %11000111 + xor b ; A = %c4c3b5b4b3c7c6c5 + rlca + rlca + ld e,a ; E = %b5b4b3c7c6c5c4c3 + ld a,b + and %11111000 + rra + rra + rra ; A = %.0.0.0b7b6b5b4b3 + xor b + and %11111000 + xor b + ld d,a ; D = %.0.0.0b7b6b2b1b0 +; END code from https://skoolkid.github.io/rom/asm/22AA.html + ld hl,(.core.SCREEN_ADDR) + add hl,de + ex de,hl ; DE = screenDir where restoring will start + pop hl ; backgroundDir + exx + push de ; returnDir + exx + ld a,c; ; A = X + ld bc,$10FF ; B = 16, C = 255 (up to 255 LDIs do not change B) + and 7 + jr z,loopD ; jump if X is a multiple of 8 (unlikely) + ; continue if restoring 3 bytes per scanline +loopC: + ldi ; 16 Ts vs 7+7+6+4=24 Ts + ldi + ldi ; 3 bytes background restored to screen + dec de ; last LDI could have increased D if initially E=253... + dec e ; ...so DEC DE restores D in that case + dec e + + inc d + ld a,d + and 7 + jr z,branchC ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + djnz loopC + ret +branchC: + ld a,e + add a,32 ; for 1 out of 8 values of D + ld e,a + jr c,nextC ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) + ld a,d + sub 8 + ld d,a +nextC: + djnz loopC + ret + +loopD: + ldi ; 16 Ts vs 7+7+6+4=24 Ts + ldi ; 2 bytes background restored to screen + dec de ; last LDI could have increased D if initially E=254... + dec e ; ...so DEC DE restores D in that case + + inc d + ld a,d + and 7 + jr z,branchD ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + djnz loopD + ret +branchD: + ld a,e + add a,32 ; for 1 out of 8 values of D + ld e,a + jr c,nextD ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) + ld a,d + sub 8 + ld d,a +nextD: + djnz loopD + ret + ENDP +END ASM +END SUB + + +#endif + From d06a2856b3a655e5285cbdd12c2c125df9c15056 Mon Sep 17 00:00:00 2001 From: ConradoBadenas Date: Fri, 6 Mar 2026 18:52:24 +0100 Subject: [PATCH 3/3] stdlib.cb.maskedsprites --- .../arch/zxnext/stdlib/cb/maskedsprites.bas | 456 ++++++++++++++++++ 1 file changed, 456 insertions(+) create mode 100644 src/lib/arch/zxnext/stdlib/cb/maskedsprites.bas diff --git a/src/lib/arch/zxnext/stdlib/cb/maskedsprites.bas b/src/lib/arch/zxnext/stdlib/cb/maskedsprites.bas new file mode 100644 index 000000000..8a2feb376 --- /dev/null +++ b/src/lib/arch/zxnext/stdlib/cb/maskedsprites.bas @@ -0,0 +1,456 @@ +' ---------------------------------------------------------------- +' This file is released under the MIT License +' +' Copyright (C) 2026 Conrado Badenas +' Ideas taken from +' https://github.com/boriel-basic/zxbasic/blob/main/src/lib/arch/zx48k/stdlib/memorybank.bas +' by Juan Segura (a.k.a. Duefectu), +' https://github.com/oisee/antique-toy/blob/main/chapters/ch16-sprites/draft.md +' by Alice Vinogradova (a.k.a. oisee), and +' https://youtu.be/nBHXtI1Y-xU?t=434 +' by Benjamín (a.k.a. RetrobenSoft) +' +' Print Masked (AND+OR) Sprites, version 2026.03.06 +' ---------------------------------------------------------------- + +#ifndef __CB_MASKEDSPRITES__ + +REM Avoid recursive / multiple inclusion + +#define __CB_MASKEDSPRITES__ + +#include +#include +#include + + +' ---------------------------------------------------------------- +' Set the visible screen (either in bank5 or bank7) +' and updates the system variable BANKM. +' Only works on 128K and compatible models. +' Parameters: +' Ubyte: bank number 5 or 7 +' ---------------------------------------------------------------- +SUB FASTCALL SetVisibleScreen(bankNumber AS UByte) +ASM + ; A = bankNumber 5 or 7 with screen to be visible + and %00000010 ; bit1 = 0/1 for bank5/7 + rla + rla ; bit3 = 0/1 for bank5/7 + ld c,a + ld hl,$5b5c ; BANKM system variable + ld a,(hl) ; Read BANKM + and %11110111 ; Reset bit3 + or c ; Set screen in bank5/7 + ld bc,$7ffd ; Memory Bank control port + di ; Disable interrupts + ld (hl),a ; Update BANKM system variable + out (c),a ; Set the screen + ei ; Enable interrupts +END ASM +END SUB + + +' ---------------------------------------------------------------- +' Returns the bank of visible screen (either 5 or 7) +' according to system variable BANKM. +' Only works on 128K and compatible models. +' Returns: +' UByte: bank 5 or 7 +' ---------------------------------------------------------------- +FUNCTION FASTCALL GetVisibleScreen() AS UByte + RETURN ((PEEK $5b5c bAND %1000)>>2) bOR 5 +END FUNCTION + + +' ---------------------------------------------------------------- +' Toggles the visible screen (from 5 to 7, or from 7 to 5) +' and updates the system variable BANKM. +' Only works on 128K and compatible models. +' ---------------------------------------------------------------- +SUB FASTCALL ToggleVisibleScreen() + SetVisibleScreen(2 bXOR GetVisibleScreen() ) + '2 bXOR 5/7 = 7/5 +END SUB + + +' ---------------------------------------------------------------- +' Copy contents of screen5 to screen7 (display file + attribs) +' Only works on 128K and compatible models. +' ---------------------------------------------------------------- +SUB CopyScreen5ToScreen7() + DIM b AS UByte + + b = GetBank() + if b<>7 then SetBank(7) + MemCopy($4000,$c000,$1b00) + if b<>7 then SetBank(b) +END SUB + + +' ---------------------------------------------------------------- +' Copy contents of screen7 to screen5 (display file + attribs) +' Only works on 128K and compatible models. +' ---------------------------------------------------------------- +SUB CopyScreen7ToScreen5() + DIM b AS UByte + + b = GetBank() + if b<>7 then SetBank(7) + MemCopy($c000,$4000,$1b00) + if b<>7 then SetBank(b) +END SUB + + +' ---------------------------------------------------------------- +' Set ScreenBufferAddr and AttrBufferAddr to screen5 +' Only works on 128K and compatible models. +' ---------------------------------------------------------------- +SUB FASTCALL SetDrawingScreen5() + SetScreenBufferAddr($4000)' ld (.core.SCREEN_ADDR),hl + SetAttrBufferAddr($5800)' ld (.core.SCREEN_ATTR_ADDR),hl +END SUB + + +' ---------------------------------------------------------------- +' Put screen7 at $c000 (in case it is not), and +' Set ScreenBufferAddr and AttrBufferAddr to screen7 +' Only works on 128K and compatible models. +' Returns: +' Bank7 is set at $c000, old bank is removed +' UByte: bank that was at $c000 (to restore it manually IYW) +' ---------------------------------------------------------------- +FUNCTION FASTCALL SetDrawingScreen7() AS UByte + DIM b AS UByte + + b = GetBank() + if b<>7 then SetBank(7) + SetScreenBufferAddr($c000)' ld (.core.SCREEN_ADDR),hl + SetAttrBufferAddr($d800)' ld (.core.SCREEN_ATTR_ADDR),hl + RETURN b +END FUNCTION + + +' ---------------------------------------------------------------- +' Toggle ScreenBufferAddr and AttrBufferAddr between screen5,7 +' Only works on 128K and compatible models. +' ---------------------------------------------------------------- +SUB FASTCALL ToggleDrawingScreen() + SetScreenBufferAddr($8000 bXOR GetScreenBufferAddr() ) + SetAttrBufferAddr($8000 bXOR GetAttrBufferAddr() ) + '$8000 bXOR $4000/$c000 = $c000/$4000 + '$8000 bXOR $5800/$d800 = $d800/$5800 +END SUB + + +' ---------------------------------------------------------------- +' MaskedSpritesBackgroundSet = 0 or 1 is the Set of Backgrounds +' +' MaskedSpritesBackground(i) is the Dir where Background i begins +' +' ChangeMaskedSpritesBackgroundSet() changes the Set of Backgrounds +' Returns: +' Byte: new value of MaskedSpritesBackgroundSet (IYW to use it) +' ---------------------------------------------------------------- +dim MaskedSpritesBackgroundSet as Byte = 0 +#define MaskedSpritesBackground(i) (56064+((i)*2+MaskedSpritesBackgroundSet)*48) +FUNCTION FASTCALL ChangeMaskedSpritesBackgroundSet() as Byte + MaskedSpritesBackgroundSet = MaskedSpritesBackgroundSet bXOR 1 + RETURN MaskedSpritesBackgroundSet +END FUNCTION + + +' ---------------------------------------------------------------- +' Save background and Draw sprite in screen +' Parameters: +' UByte: X coordinate (0:left to 240:right) +' UByte: Y coordinate (0:up to 176:down) +' UInteger: Dir where background will be saved +' UInteger: Dir where sprite image begins +' ---------------------------------------------------------------- +SUB FASTCALL SaveBackgroundAndDrawSprite(X as UByte, Y as UByte, backgroundDir as UInteger, spriteImageDir as UInteger) +ASM + PROC + LOCAL shiftsprite, spriteOK, loop0, loopA, loopB + LOCAL branchA, branchB, nextA, nextB + ; A = X + pop de ; returnDir + exx + pop bc ; B = Y + ld c,a ; C = X +; BEGIN code from https://skoolkid.github.io/rom/asm/22AA.html + rlca + rlca + rlca ; A = %c4c3c2c1c0c7c6c5 + xor b + and %11000111 + xor b ; A = %c4c3b5b4b3c7c6c5 + rlca + rlca + ld e,a ; E = %b5b4b3c7c6c5c4c3 + ld a,b + and %11111000 + rra + rra + rra ; A = %.0.0.0b7b6b5b4b3 + xor b + and %11111000 + xor b + ld d,a ; D = %.0.0.0b7b6b2b1b0 +; END code from https://skoolkid.github.io/rom/asm/22AA.html + ld hl,(.core.SCREEN_ADDR) + add hl,de + ex de,hl ; DE = screenDir where drawing will start + ld a,c; ; A = X + and 7 + jr z,spriteOK ; jump if X is a multiple of 8 (unlikely) + ; continue if sprite must be shifted +shiftsprite: + pop bc ; backgroundDir + exx + pop hl ; spriteImageDir + push de ; returnDir + push ix + ld ixh,16 ; 16 scanlines + ld ixl,a ; IXl = X MOD 8 = 1,2,...,7 +loopB: + ld a,(hl) ; mask1 + inc hl + ld c,(hl) ; graph1 + inc hl + ld d,(hl) ; mask2 + inc hl + ld e,(hl) ; graph1 + inc hl + push hl ; spriteImageDir + + ld hl,$FF00 ; H = 255 , L = 0 + ld b,ixl +loop0: + scf + rra ; SCF + RRA injects a 1 in bit7 of A + rr d + rr h + srl c ; ShiftRightLogical injects a 0 in bit7 of C + rr e + rr l + djnz loop0 + ld b,a + push hl ; H,L = mask,graph 3rd byte + push de ; D,E = mask,graph 2nd byte + push bc ; B,C = mask,graph 1st byte + exx + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 1st byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 2nd byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + pop hl + and h ; mask + or l ; graph + ld (de),a ; 3rd byte done + dec e + dec e + + inc d + ld a,d + and 7 + jr z,branchB ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + exx + pop hl ; spriteImageDir + dec ixh + jp nz,loopB + pop ix + ret +branchB: + ld a,e + add a,32 ; for 1 out of 8 values of D + ld e,a + jr c,nextB ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) + ld a,d + sub 8 + ld d,a +nextB: + exx + pop hl ; spriteImageDir + dec ixh + jp nz,loopB + pop ix + ret + +spriteOK: + pop bc ; backgroundDir + pop hl ; spriteImageDir + exx + push de ; returnDir + exx + push ix + ld ixh,16 ; 16 scanlines +loopA: + ld a,(de) ; screen + ld (bc),a; ; save + inc bc + and (hl); ; mask + inc hl + or (hl) ; graph + inc hl + ld (de),a ; 1st byte done + inc e + + ld a,(de) ; screen + ld (bc),a ; save + inc bc + and (hl); ; mask + inc hl + or (hl) ; graph + inc hl + ld (de),a ; 2nd byte done + dec e + + inc d + ld a,d + and 7 + jr z,branchA ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + dec ixh + jp nz,loopA + pop ix + ret +branchA: + ld a,e + add a,32 ; for 1 out of 8 values of D + ld e,a + jr c,nextA ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) + ld a,d + sub 8 + ld d,a +nextA: + dec ixh + jp nz,loopA + pop ix + ret + ENDP +END ASM +END SUB + + +' ---------------------------------------------------------------- +' Restore background in screen +' Parameters: +' UByte: X coordinate (0:left to 240:right) +' UByte: Y coordinate (0:up to 176:down) +' UInteger: Dir where saved background begins +' ---------------------------------------------------------------- +SUB FASTCALL RestoreBackground(X as UByte, Y as UByte, backgroundDir as UInteger) +ASM + PROC + LOCAL loopC, loopD, branchC, branchD, nextC, nextD + ; A = X + pop de ; returnDir + exx + pop bc ; B = Y + ld c,a ; C = X +; BEGIN code from https://skoolkid.github.io/rom/asm/22AA.html + rlca + rlca + rlca ; A = %c4c3c2c1c0c7c6c5 + xor b + and %11000111 + xor b ; A = %c4c3b5b4b3c7c6c5 + rlca + rlca + ld e,a ; E = %b5b4b3c7c6c5c4c3 + ld a,b + and %11111000 + rra + rra + rra ; A = %.0.0.0b7b6b5b4b3 + xor b + and %11111000 + xor b + ld d,a ; D = %.0.0.0b7b6b2b1b0 +; END code from https://skoolkid.github.io/rom/asm/22AA.html + ld hl,(.core.SCREEN_ADDR) + add hl,de + ex de,hl ; DE = screenDir where restoring will start + pop hl ; backgroundDir + exx + push de ; returnDir + exx + ld a,c; ; A = X + ld bc,$10FF ; B = 16, C = 255 (up to 255 LDIs do not change B) + and 7 + jr z,loopD ; jump if X is a multiple of 8 (unlikely) + ; continue if restoring 3 bytes per scanline +loopC: + ldi ; 16 Ts vs 7+7+6+4=24 Ts + ldi + ldi ; 3 bytes background restored to screen + dec de ; last LDI could have increased D if initially E=253... + dec e ; ...so DEC DE restores D in that case + dec e + + inc d + ld a,d + and 7 + jr z,branchC ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + djnz loopC + ret +branchC: + ld a,e + add a,32 ; for 1 out of 8 values of D + ld e,a + jr c,nextC ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) + ld a,d + sub 8 + ld d,a +nextC: + djnz loopC + ret + +loopD: + ldi ; 16 Ts vs 7+7+6+4=24 Ts + ldi ; 2 bytes background restored to screen + dec de ; last LDI could have increased D if initially E=254... + dec e ; ...so DEC DE restores D in that case + + inc d + ld a,d + and 7 + jr z,branchD ; 7Ts no jump (7/8 times), 12Ts jump (1/8 times) + djnz loopD + ret +branchD: + ld a,e + add a,32 ; for 1 out of 8 values of D + ld e,a + jr c,nextD ; 7Ts no jump (7/8 times), 12 Ts jump (1/8 times) + ld a,d + sub 8 + ld d,a +nextD: + djnz loopD + ret + ENDP +END ASM +END SUB + + +#endif +