Added another tile, mainly just to test pointer arithmetic at the moment. The screenshot is from the new Beebasm version...
The source code is also half presentable now so you might as well have a look and point out my mistakes before I repeat them...
Code: Select all
tilePointerL=$70 : tilePointerH=$71
tileOffsetL=$72 : tileOffsetH=$73
screenPointerL=$74 : screenPointerH=$75
tileCount=$76 : tileByteCount=$77 : mapColCount=$78 : mapRowCount=$79
OSBYTE=$FFF4 : OSWORD=$FFF1 : OSWRCH=$FFEE
ORG $3000
.start
; Select MODE4
LDA #22 : JSR OSWRCH : LDA #4 : JSR OSWRCH
; Set all logical colours to black (so we can't see)
LDX #LO(lightsOff) : LDY #HI(lightsOff) : LDA #$C : JSR OSWORD
LDX #LO(lightsOff+5) : LDY #HI(lightsOff+5) : LDA #$C : JSR OSWORD
; Initialise some counters
LDA #15 : STA mapRowCount
LDA #0 : STA tileCount
; Loop through each row of the map
.nextMapRow
; Load map row counter into X via A (for subsequent ASL)
LDA mapRowCount : TAX
; Double it (because 2 bytes per element) and transfer to Y
ASL A : TAY
; Get number of tiles in current row from precalculated table
LDA tblColCount-1,X : STA mapColCount
; Get screen address where current row starts from precalculated table
LDA tblRowStart-2,Y : STA screenPointerL
LDA tblRowStart-1,Y : STA screenPointerH
; Loop through each column in the row
.nextTile
; Draw the first row of 16 pixels using bitwise OR to mask the
; corners of each tile because they overlap. No need to store in
; the tilesheet because it's always the same
LDA #%00000001
LDY #0 : ORA (screenPointerL),Y : STA (screenPointerL),Y
LDA #%11000000
LDY #8 : ORA (screenPointerL),Y : STA (screenPointerL),Y
; Increment screen address. No need to worry about carry or
; character block boundary as first row is always even
INC screenPointerL
; Initialise counter
LDA #32 : STA tileByteCount
; Get start address of tile sheet
LDA #LO(tileSheet) : STA tilePointerL
LDA #HI(tileSheet) : STA tilePointerH
; Load tile counter into X and then increment ready for next
; iteration (keeps track of map location)
LDX tileCount : INC tileCount
; Load tile number for current map location
LDA tileMap,X
; Skip tile sheet offset arithmetic if tile number is 0
BEQ nextTileRow
; Otherwise multiply tile number (in A) by 32 (no. of bytes per tile)
; to produce tile sheet offset
STA tileOffsetL : LDA #0 : STA tileOffsetH : LDX #5
.nextShift
CLC : ASL tileOffsetH : ASL tileOffsetL : BCC tileOffsetNoCarry
INC tileOffsetH
.tileOffsetNoCarry
DEX : BNE nextShift
; Add offset to start adddresss of tile sheet to produce absolute
; address of tile data
CLC
LDA tilePointerL : ADC tileOffsetL : STA tilePointerL
LDA tilePointerH : ADC tileOffsetH : STA tilePointerH
; Loop through each row of pixels (except the last)
.nextTileRow
; Draw current row of 16 pixels using bitwise OR to mask the
; corners of each tile because they overlap.
DEC tileByteCount : LDY tileByteCount : LDA (tilePointerL),Y
LDY #0 : ORA (screenPointerL),Y : STA (screenPointerL),Y
DEC tileByteCount : LDY tileByteCount : LDA (tilePointerL),Y
LDY #8 : ORA (screenPointerL),Y : STA (screenPointerL),Y
; Skip screen address arithmetic if last row has been reached
LDA tileByteCount : BEQ endTile
; Increment low byte of screen address
INC screenPointerL
; Loop back to draw next row of pixels if screen address is not
; a multiple of 8 (which means we've crossed a character block
; boundary and may also need to carry)
LDA #7 : AND screenPointerL : BNE nextTileRow
; Carry to high byte of screen address if low byte is now zero
CMP screenPointerL : BNE screenPointerNoCarry
INC screenPointerH
.screenPointerNoCarry
; Add 312 to screen pointer because we have crossed a character
; block boundary and want to move down not right
CLC
LDA #LO(312) : ADC screenPointerL : STA screenPointerL
LDA #HI(312) : ADC screenPointerH : STA screenPointerH
; Loop back to draw next row of pixels
JMP nextTileRow
; The last row of pixels now draws itself because it's always
; blank and so we can just end this loop there
.endTile
; Increment low byte of screen address because blank rows still
; count. No need to worry about carry because mathemagic :D
INC screenPointerL
; Skip screen address arithmetic if this was the last column
DEC mapColCount : BEQ endMapRow
; Subtract 625 from screen address to bring us to the start of the
; next tile in the row
SEC
LDA screenPointerL : SBC #LO(625) : STA screenPointerL
LDA screenPointerH : SBC #HI(625) : STA screenPointerH
; Loop back to start next tile in the row
JMP nextTile
; End map row loop
.endMapRow
; Exit loop if last row reached
DEC mapRowCount : BEQ endMap
; Otherwise loop back and start the next row
JMP nextMapRow
; End outer map loop
.endMap
; Set logical colour 1 to reveal
LDX #LO(lightsOn) : LDY #HI(lightsOn) : LDA #$C : JSR OSWORD
; Back to BASIC (or whatever)
RTS
.tblColCount ; number of tiles in each row of the map (for speed)
EQUB 8,9,10,11,12,13,14,15,14,13,12,11,10,9,8
.tblRowStart ; screen address where each row of the map begins (for speed)
EQUW $77A4,$751E,$73D0,$714A,$6EC4
EQUW $6C3E,$6AF0,$686A,$65F4,$637E
EQUW $6240,$5FCA,$5D54,$5ADE,$59A0
.lightsOff ; parameter blocks for OSWORD &C (like VDU19)
EQUB 0,0,0,0,0
EQUB 1,0,0,0,0
.lightsOn
EQUB 1,2,0,0,0
.tileMap ; what it says on the tin
EQUB 0,0,1,1,1,0,0,0
EQUB 1,0,0,1,1,0,0,0,1
EQUB 0,1,1,0,1,0,0,0,0,0
EQUB 1,0,0,1,0,1,1,0,1,1,0
EQUB 0,0,1,0,1,0,0,0,0,1,0,1
EQUB 0,1,1,0,1,0,0,0,1,0,0,0,1
EQUB 0,0,1,1,1,0,0,0,1,1,1,0,0,0
EQUB 1,0,0,1,0,1,2,0,0,1,1,0,0,0,1
EQUB 0,1,1,0,1,2,2,0,0,0,0,0,1,0
EQUB 1,0,0,1,1,2,2,2,0,0,0,1,1
EQUB 0,0,0,1,2,2,2,0,0,0,1,1
EQUB 0,0,1,2,2,0,0,0,1,0,0
EQUB 0,0,1,1,2,2,2,0,1,0
EQUB 1,0,0,1,0,1,1,0,1
EQUB 0,1,1,0,0,1,0,0
.tileSheet ; technically not but misnomers are us. Rows in reverse order.
EQUW %0000000111000000
EQUW %0000011111110000
EQUW %0001111111111100
EQUW %0111111111111111
EQUW %1111111111111111
EQUW %0111111111111111
EQUW %1111111111111111
EQUW %0111111111111111
EQUW %1111111111111111
EQUW %0111111111111111
EQUW %1111111111111111
EQUW %0111111111111111
EQUW %1111111111111111
EQUW %0111111111111111
EQUW %0001111111111100
EQUW %0000011111110000
EQUW %0000000111000000
EQUW %0000011000110000
EQUW %0001111000111100
EQUW %0111000000000111
EQUW %1110101010101011
EQUW %0101110101010101
EQUW %1101111010101001
EQUW %0101111101010101
EQUW %1101111110101001
EQUW %0101111111010101
EQUW %1101111111101001
EQUW %0101111111110101
EQUW %1110111111111011
EQUW %0111001111100111
EQUW %0001110000011100
EQUW %0000011111110000
EQUW %0000000111000000
EQUW %0000011111110000
EQUW %0001111111111100
EQUW %0111111011001111
EQUW %1111111100011111
EQUW %0111110110110001
EQUW %1101111011100011
EQUW %0110111101000111
EQUW %1111010010001111
EQUW %0101100011010001
EQUW %1110110111100011
EQUW %0111011111000111
EQUW %1111101110001111
EQUW %0111110100011111
EQUW %0001111000111100
EQUW %0000011101110000
.end
SAVE "hex",start,end