This subroutine takes a 2X2 tile of data from the given address and copies it to the screen co-ordinates at (x,y) - x and y in character addresses, where 0<=x<=31 and 0<=y<=23.
Note that this uses pushes and pops to move the data, using the fastest known data moving algorithm for the Z80. As a consequence, while active it uses ALL the registers, including alternates and IY and IX as well as the Stack Pointer SP. It is kind enough to put these back for the purposes of exiting the subroutine though - ZX BASIC uses that register quite extensively.
Also, interrupts are disabled while the copying is happening. Considering that the stack pointer is likely pointing either at the screen or the tile data, an interrupt would be disastrous. If interrupts were enabled when the SUB is called, it should re-enable them again on exit.
Note the data format is across the tile - 2 bytes for the top row, then 2 bytes for the second row...and so on until there are 2 bytes for the 16th row. Then two bytes for the top two attributes, and 2 for the bottom. It uses 36 bytes of data, starting at the address given.
' Routine to place a 16 pixel by 16 pixel "Tile" onto the screen at character position x,y from adddress given. ' Data must be in the format of 16 bit rows, followed by attribute data. ' (c) 2010 Britlion, donated to the ZX BASIC project. ' Thanks to Boriel, LCD and Na_than for inspiration behind this. ' This routine could be used as the basis for a fast sprite system, provided all sprites can be in 4 character blocks. ' It can also be used to clean up dirty background (erase sprites), or put backgrounds from tiled blocks onto a screen. ' Note the comments about Self Modifying code should be ignored. This has been updated with IX+n methods, which overall are faster than accessing and changing the code. ' (They would have to be accessed to change the memory anyway - may as well just access them directly.) SUB putTile(x AS UBYTE, y AS UBYTE, graphicsAddr AS UINTEGER) ASM JP pt_start ptstackSave: defb 0,0 pt_start: ld a,i push af ; SAVE interrupt status. ; Routine TO SAVE the background TO the buffer DI ; we really, really, REALLY can NOT be having interrupts WHILE the stack AND IX AND IY are pointed elsewhere. PUSH IX PUSH IY ;ld HL, 65535 ; Self modifying CODE should LOAD this with the graphics address. LD D,(IX+9) LD E,(IX+8) EX DE,HL ;; PRINT sprites routine LD (ptstackSave), SP ; SAVE Stack Pointer LD SP,HL ; now SP points AT the start of the graphics. ; This FUNCTION returns the address into HL of the screen address ld a,(IX+5) ; LOAD in x - note the Self Modifying value ld IYH, a ; SAVE it ld l,a ld a,(IX+7) ; LOAD in y - note the Self Modifying value ld IYL, a ; SAVE it ld d,a AND 24 add a,64 ld h,a ld a,d AND 7 rrca rrca rrca OR l add a,2 ; Need TO be TO the right so backwards writing pushes land properly. ld l,a ; SO now, HL -> Screen address, AND SP -> Graphics. Time TO start loading. POP BC ; Row 0 POP DE ; row 1 EX AF,AF' POP AF ; row 2 EX AF,AF' EXX POP BC ; row 3 POP DE ; row 4 POP HL ; row 5 EXX ; All right. We're loaded. Time to dump! LD IX,0 ADD IX,SP ; SAVE our stack pointer into IX LD SP,HL ; point AT the screen. PUSH BC ; row 0 INC H LD SP,HL PUSH DE ; row 1 INC H LD SP,HL EX AF,AF' PUSH AF ; row 2 INC H LD SP,HL EXX PUSH BC ; row 3 EXX INC H LD SP,HL EXX PUSH DE ; row 4 EXX INC H LD SP,HL EXX PUSH HL ; ROW 5 EXX ; We're empty. Time to load up again. LD SP,IX POP BC ; ROW 6 POP DE ; ROW 7 EX AF,AF' POP AF ; ROW 8 EX AF,AF' EXX POP BC ; ROW 9 POP DE ; ROW 10 POP HL ; ROW 11 EXX ; AND we're loaded up again! Time to dump this graphic on the screen. LD IX,0 ADD IX,SP ; SAVE SP in IX INC H LD SP,HL PUSH BC ; ROW 6 INC H LD SP,HL PUSH DE ; ROW 7 DEC HL DEC HL ; Aha. Snag. We're at the bottom of a character. What's the next address down? ld a,l AND 224 cp 224 jp z,ptSameThird3 ptNextThird3: ld de,1760 AND a sbc hl,de jp ptAddrDone3 ptSameThird3: ld de,32 AND a adc hl,de ptAddrDone3: INC HL INC HL LD SP,HL EX AF,AF' PUSH AF ; ROW 8 INC H LD SP,HL EXX PUSH BC ; ROW 9 EXX INC H LD SP,HL EXX PUSH DE ; ROW 10 EXX INC H LD SP,HL EXX PUSH HL ; ROW 11 EXX ; Okay. Registers empty. Reload time! LD SP,IX POP BC ; ROW 12 POP DE ; ROW 13 EXX POP BC ; ROW 14 POP DE ; ROW 15 POP HL ; Top Attrs EXX EX AF,AF' POP AF ; Bottom Attrs EX AF,AF' ; AND the last dump TO screen INC H LD SP,HL PUSH BC INC H LD SP,HL PUSH DE INC H LD SP,HL EXX PUSH BC EXX INC H LD SP,HL EXX PUSH DE EXX ; Pixels done. Just need TO DO the attributes. ; So set HL TO the attr address: ld a,IYL ;ypos rrca rrca rrca ; Multiply by 32 ld l,a ; Pass TO L AND 3 ; Mask with 00000011 add a,88 ; 88 * 256 = 22528 - start of attributes. ld h,a ; Put it in the High BYTE ld a,l ; We get y value *32 AND 224 ; Mask with 11100000 ld l,a ; Put it in L ld a,IYH ; xpos adc a,l ; Add it TO the Low BYTE ld l,a ; Put it back in L, AND we're done. HL=Address. INC HL ; we need TO be TO the right of the ATTR point AS pushes write backwards. INC HL ; attr LD SP,HL EXX PUSH HL ; top row EXX LD HL,34 ; we need TO move down TO the NEXT row. We already backed up 2, so we add 34. ADD HL,SP LD SP,HL EX AF,AF' ; bottom row PUSH AF ptNextSprite2: ; done. Cleanup. LD SP,(ptstackSave) ; put our stack back together. ; done all 4 final clean up POP IY POP IX POP AF ; recover interrupt status JP PO, pt_nointerrupts EI ; Okay. We put everything back. IF you need interrupts, you can GO with em. pt_nointerrupts: END ASM END SUB
Will copy a tile of data to print position 10,10 from address at label sprite.