There are a variety of techniques for handling large maps. Here are the first few that come to mind:zanto wrote: ↑Mon Mar 29, 2021 8:05 pm When the screen loads up (e.g. after the map is just loaded), this is how I can get the bytes I need to display
1) I have to figure out what's the address of the byte on the top left tile within the camera, something like2) Then, I'd have to loop go get all bytes in that row, so 32 times.Code: Select all
first_byte = start + (cameray * map_width) + camerax
3) After that, I'd have to find the first byte of the next row. I can do that by adding the width to the first byte4) go back to (2). Rules 2, 3 and 4 will be executed 30 timesCode: Select all
first_byte_in_row = prev_first_byte + map_width
Then, after I scroll to the right, for example:
1) get the first byte inside the camera, like (1) above
2) add 256 to it, so we have the byte right on the top left part that is to the right of what's displayed on screen
3) get byte 1 and byte 2 so we have a 16x16 tile
4) go to the next row, by adding "map_width" to the address we found in 2
5) go back to (3). repeat 3,4,5 30 times.
I want to make sure this is the idea, because if so, it involves a bunch of things I still don't know how to do in ASM, like 16 bit operations and multiplication. I also want to make sure that it's feasible to do these things the way I thought without causing lag and stuff
Also, will this idea work if my map is RLE compressed?
1. Rather than storing a large map as a flat 2D array (which requires doing multiplication to find the correct offset inside, which can be quite slow if your map dimensions are not a power of 2 since the 6502 doesn't have a MUL instruction), instead use an array of pointers to other arrays. That way, no math at all is required for finding a specific entry - you just use one coordinate to find the desired pointer, then use the other coordinate as an index into that pointer. You also no longer need to know how large the map is, aside from knowing when to stop loading more data.
Code: Select all
maprow0: .db 1,2,3,4,...
maprow1: .db 5,6,7,8,...
maprow2: .db 9,10,11,12,...
map: .dw maprow0,maprow1,maprow2,...
getTile: ; with coordinates in X and Y registers
; multiply coordinate in X by 2, since pointers are 2 bytes long
TXA
ASL A
TAX
; copy pointer from "map" into temporary pointer variable in zeropage
LDA map,X
STA $FE
LDA map+1,X
STA $FF
; then just index into the resulting array and you have your map tile
LDA ($FE),Y
RTS
2. Divide the map into "meta-tiles", or groups of tiles that are frequently reused - for example, each byte in your top-level map could refer to an 8x8 group of actual nametable entries. As long as the meta-tile dimensions are a power of 2 (i.e. 2x2, 4x4, 8x8, 16x16), any multiplication or division will simplify to bit shifting, which the 6502 can do rather easily.
3. Use multiple levels of meta-tiles and apply compression to them. Since you'll typically be fetching map tiles in groups (rows or columns), you'll be fetching the same meta-tiles over and over again, so you can decompress one into RAM and keep it around until you need to look at a different one.
I'm sure there are more methods that are more appropriate for your use case, but this should at least point you in the right direction.