catmeows wrote: ↑Fri Jul 15, 2022 4:07 pm
So imagine you have a 4KB buffer that has same layout as upper two thirds of screen. What if you copy 16 bytes on the left side and then just another 16 bytes right under ? It makes pointer arithmetic incredibly simple: for the next 16B you increase high byte of source by one and you increase high byte of destination by one.
Code: Select all
ld sp, ix
do some POPs
ld sp, iy
do some PUSHes
inc ixh
inc iyh
ld sp, ix
do some POPs
ld sp, iy
do some PUSHES
.... do it for 8 pixel lines
OK, so I took
@catmeows idea and ran with it - this copies the middle block of the screen to the top block (28 x 8 cells, centered horizontally).
Following the Spectrum's screen layout - and also working DOWN the left-hand side, then UP the right-hand side of each half-row cell - leads itself to some *surprisingly* simple pointer calculations!
Hopefully everything is explained clearly in the comments, I think it's pretty nifty how it all comes together!
Code: Select all
ORG 32768
; Push alternate registers + IY
EXX
PUSH BC
PUSH DE
PUSH HL
PUSH IY
do_forever:
; --- Interrupts! --- ;
; Interrupts on!
POP IY
PUSH IY
EI
; Wait for start of frame (to show border stripes)
HALT
; Disable interrupts to prevent corruption
DI
; --- Save SP --- ;
LD (restore_sp), SP ; [20]
; --- Set up src & dest --- ;
LD IX, $4802 ; from block 1, row 0, col 2
LD IY, $4002 +14 ; to block 0, row 0, col 2
; ===== BEGIN! ===== ;
block_start:
; START OF BLOCK
LD A, 8 ; [7] 8 cell rows in block
thr_loop:
OUT (254), A ; change border colour at start of each cell row, so we can see where beam is!
EX AF, AF' ; [4] store loop counter
; --- Transfer left-hand side --- ;
; Transfer 8 pixel rows, INCrementally
; Do 8 pixel rows
REPT 8
; Get row from source
LD SP, IX ; [10]
POP AF ; [10]
POP BC ; [10]
POP DE ; [10]
POP HL ; [10]
EXX ; [4]
POP BC ; [10]
POP DE ; [10]
POP HL ; [10]
; Put row at destination
LD SP, IY ; [10]
PUSH HL ; [11]
PUSH DE ; [11]
PUSH BC ; [11]
EXX ; [4]
PUSH HL ; [11]
PUSH DE ; [11]
PUSH BC ; [11]
PUSH AF ; [11]
; Move to next pixel row
INC IXH ; [8]
INC IYH ; [8]
ENDM
ORG $-4 ; skip back 4 bytes (the two INC instructions) to stay on bottom pixel row
; [1512] TOTAL for this unrolled loop
; --- Move to RHS --- ;
; To save time, DON'T return to the top pixel of the cell row! There is no need to do this.
; Instead, for the right-hand side, continue from the bottom pixel row (where IXH & IYH already are) and work upwards (DEC IXH & IYH)
; (This also means that when we finish, IXH & IYH will be be back where we started on the top pixel row,
; so to continue to the next cell row within the same block, we only have to adjust IXL & IYL) :)
; So we only need to move IXL & IYL across, and as a bonus,
; IYL is already where IXL wants to be! :)
LD A, IYL ; [8]
LD IXL, A ; [8]
; Then to calculate IYL, just +14
ADD A, 14 ; [7]
LD IYL, A ; [8]
; --- Transfer right-hand side --- ;
; Transfer 8 pixel rows DECrementally
; Do 8 pixel rows
REPT 8
; Get row from source
LD SP, IX ; [10]
POP AF ; [10]
POP BC ; [10]
POP DE ; [10]
POP HL ; [10]
EXX ; [4]
POP BC ; [10]
POP DE ; [10]
POP HL ; [10]
; Put row at destination
LD SP, IY ; [10]
PUSH HL ; [11]
PUSH DE ; [11]
PUSH BC ; [11]
EXX ; [4]
PUSH HL ; [11]
PUSH DE ; [11]
PUSH BC ; [11]
PUSH AF ; [11]
; Move to previous row
DEC IXH ; [8]
DEC IYH ; [8]
ENDM
ORG $-4 ; skip back 4 bytes (the two DEC instructions) to stay on top pixel row
; [1512] TOTAL for this unrolled loop
; --- Move down to next cell row --- ;
; IXH & IYH are back at top pixel of cell and therefore correct already :)
; Move IXL down/left
LD A, IXL ; [8]
ADD A, 32-14 ; [7]
LD IXL, A ; [8]
; Move IYL relative to it - saves a LD A, IYL
ADD A, 14 ; [7]
LD IYL, A ; [8]
; Loop
EX AF, AF' ; [4] retrieve loop counter
DEC A ; [4]
JP NZ, thr_loop ; [10]
; END OF BLOCK
; Restore SP
restore_sp EQU $+1
LD SP, 0 ; [10]
; === DONE === ;
OUT (254), A ; = 0
JP do_forever ; uncomment to watch the border stripes!
; Get stored alt registers back
POP IY
POP HL
POP DE
POP BC
EXX
; Enable interrupts
EI
; DONE!
RET