Commented version of initgame.bas - of some version of it. 2022.12.16 I hope my comments will help, but it is a brain dump. If something doesn't seem to make sense, it probably doesn't. 100 REM ************************************* 120 REM * * 130 REM * ITEM HEX DECIMAL * 140 REM * ---- ------ ------- * 150 REM * XDL $00000 0 LEN:23 * 160 REM * PALETTE $00200 512 LEN:768 * 170 REM * BCBS $00500 1280 * 180 REM * SPRITESHEET $01E00 7680 LEN:24960 * 190 REM * VISIBLE $10C88 68744 * 200 REM ************************************* In old tutorials they would draw a RAM area layout. Example: https://www.atariarchives.org/dere/chapt04.php Look at the link, Figure 4-2, a RAM area layout was drawn for Player/Missile Graphics. The VBXE has 512K of RAM. How you lay out the memory is up to you. Keep in mind if the user has configured their VBXE to use RAMBO memory, then they are using the second half of the memory, 256K to supply memory to the system. I don't want to use that, they may have something important in that area. So, first, I decided to use the first 256k of RAM of the VBXE, and simply stay out of the second half. And then I needed to decide on reasonable locations for where I wanted to store some things needed by the VBXE. The VBXE is controlled by a disply list, called an XDL (extended display list). In this game I need to use the blitter, so I needed to store BCBs (blitter control blocks). I need to store spritesheets. Spritesheets are the animation frames for the sprites in the game. I keep them in memory to copy to the screen, during game play. I need to also allocate an area for the screen itself. I am using standard resolution, which is visible 320 x 192 with 256 colors. The first memory location of the screen, will draw the pixel 0,0 on the 320 x 192 screen. Say the memory location is 65536. Then 65536 is pixel 0,0. 65537 is pixel 0,1. 65538 is pixel 0,2. And so on. I put the XDL at memory location 0, the very first memory location. I also reserved 512 bytes for the XDL. The XDL is only 23 bytes. So, the logic here was to simply reserve a lot of memory for XDL in case anyone wants to create a longer, more complex XDL. The next memory location is for palette, and starts at byte 512. Palette doesn't have to go in memory, the VBXE does not directly read from it. The VBXE palette is configured by repeatedly poking 3 registers, eventually programming in a new palette. So why did I put it in VBXE memory? Just as a work area. I used it simply to BGET the palette from disk, so I could then use the memory location to do the pokes. I wanted the convenience and speed of using BGET to interface with the disk. Then I reserved an area for where I store the 21-byte BCB blocks. You may need a lot of BCB blocks in your game. They tell the blitter what to do. If programming in assembler you could create a BCB block just before you use it. For basic, I'd prefer to have them written in advance. There is a location for a spritesheet. This is the source of the animation frames. Finally there is the screen area. Let's talk about 6502 math. One byte (8-bits) can represent 256 values:0 - 255. If you add 33+47, each of the values is in the range of one byte, so you are doing 8-bit math. That may not seem so useful, but in reality 8-bit math is done all the time. Now say you need to add 1538+762, those values are in the range of 16-bit (2 byte). The range of possible values to address with 16-bits is 65536. Of course we know the 6502 as an 8-bit computer, but it has a 16-bit memory address bus, 65536(64k). Anyway, for numbers in the 16-bit range, you need 16-bit math - slower, more complicated. I said earlier the VBXE has 512K of ram. It takes 19-bits to address 512k (524288). Because it needs more than two bytes, 3 bytes are used to handle the 19-bit addresses of the VBXE. Using 3 bytes is even slower and more complicated than 2 bytes. However, the reality is that if you just consider your 320 x 192 screen (320 x 192 = 61440) that this screen memory is addressable within 16-bits. 61440 < 65536. As long as I do not cross a 65536 boundary, I can do only 16-bit math, using only two bytes, with the other bits assumed to not have changed. It's easier to undertand memory in hex. The XDL is located at $00000 or $0 00 00. I could have started the screen memory on the next boundary: $1 00 00. That would be so, that the $1 could always be assumed when doing screen calculations. I know I will never cross a 65536 boundary. So why did I actually start at $10C88 or $1 0C 88. It's because my sprites are drawn with blank lines. I actually do draw my sprites with pixel 0,0 starting at memory location $1 00 00. But those blank lines are drawn and I kicked the screen memory forward so that when I draw at $1 00 00, the first visible pixel, not my blank lines, draws at pixel 0,0. This may seem like adding complexity, but drawing a sprite surrounded by blank lines ensures when I draw the next frame of the animation that the old frame is erased by the new frame. As something is in motion the next frame is drawn a few pixels away from the old frame. One could blank out the old frame, then draw the new one. But that is actually more processor cycles, not less. With this setup, I only drawn new frames, becaus that new frame is surrounded by blank lines, that copy over and clear the surrounding pixels, it does its own cleanup of old frames. Now I will talk about the structure of initgame.bas. Basically it has the flow, starting at line 270, "Start Message" -> "Check VBXE Base address" -> "Clear VBXE Memory" -> "Load the XDL, display list" -> "Load the Palette" -> "Load the BCB (blitter control blocks)" -> "Load the SpriteSheet" -> "Send the end message" -> "Debug" The Debug area, I often write a trivial program, display the sprites on the screen and run test animation there. It should be blank now, it is just a scratch area for testing and viewing results. I will comment on each section in-line with the code below, just before each section begins. 210 REM NOTE: NOT REQUIRED TO KEEP PALETTE COPY 220 REM EQUATES 230 STRTMSG=1000:CHKVBXE=2000:CLEARVBXEMEM=3000 240 LDXDL=4000:LDPAL=5000:LDBCB=6000 250 LDSHEET=7000:ENMSG=8000:DBG=9000:VBMEM=32768 260 REM MAIN WORKFLOW 270 GOSUB STRTMSG 280 GOSUB CHKVBXE 285 MEMCONTROL=BASE+94:MEMBANK=BASE+95 290 GOTO CLEARVBXEMEM 300 GOSUB LDXDL 310 GOSUB LDPAL 320 GOSUB LDBCB 330 GOSUB LDSHEET 340 GOSUB ENMSG 345 GOSUB DBG 346 POKE 752,0 347 POKE MEMCONTROL,0 350 END 1000 REM START MSG 1010 GRAPHICS 0:POKE 752,1 1020 POSITION 8,10:? "LOADING ASSETS TO VBXE.";:POSITION 14,11:? "PLEASE WAIT."; 1999 RETURN COMMENTS ON CHECK the VBXE BASE ------------------------------- First of all, my code for checking the VBXE base has a potential issue. It'd be absurdly rare to hit it, so it is not a priority to change. Let me back track to that in a second. The VBXE has various memory locations that you use to control it, but they aren't the same for every VBXE. Essentially there are VBXE's configured for "D6" and some configured for "D7" apparently the choice is to avoid conflicts with other installed hardware. It means the actualy areas you POKE are different. You need to know where to poke the VBXE control valus, so you need to understand which base the user's VBXE is configured to use. The way I check with VBXE is I try to use it. I try the D6 locations to write out memory, if success, you have a D6 base. If not success, I try D7, if D7 writes are successful, then you have a D7. If none are successful, your VBXE is not installed or not working. However, I write 3 numbers to 3 byte locations and then do a membank switch and check if I still find them. I should not still find them, because I wrote them to a different bank. If I still find them, it means the bank switch failed. However, it could be a super rare coincidence that the second bank did have those 3 numbers, previously written. So, an upgrade to the logic could be to write different numbers to different banks, then check they are different. That would avoid the coincidence issue. oh well, maybe I'll improve it, but the reality is this will work 99.9999% of the time, so its way down the list on code to write to finish this game. 2000 REM GET VBXE BASE 2010 D6MEMCONTROL=54878:D6MEMBANK=54879 2020 POKE D6MEMCONTROL,128+8:REM $8000 4K, CPU ACCESS 2030 POKE D6MEMBANK,128:REM BANK 0 2040 POKE VBMEM,1:POKE VBMEM+1,2:POKE VBMEM+2,183:REM 3 CHECK VALUES 2050 POKE D6MEMBANK,128+33:REM CHANGE BANKS 2060 IF PEEK(VBMEM)=1 THEN IF PEEK(VBMEM+1)=2 THEN IF PEEK(VBMEM+2)=183 THEN GOTO 2090 2070 BASE=54784:POKE 203,6 2080 GOTO 2999 2090 REM IF YOU ARE HERE THEN D6 CHECK FAILED 2100 D7MEMCONTROL=55134:D7MEMBANK=55135 2110 POKE D7MEMCONTROL,128+8:REM $8000 4K, CPU ACCESS 2120 POKE D7MEMBANK,128:REM BANK 0 2130 POKE VBMEM,33:POKE VBMEM+1,12:POKE VBMEM+2,212:REM 3 CHECK VALES 2140 POKE D7MEMBANK,128+33:REM CHANGE BANKS 2150 IF PEEK(VBMEM)=33 THEN IF PEEK(VBMEM+1)=12 THEN IF PEEK(VBMEM+2)=212 THEN GOTO 2180 2160 BASE=55040:POKE 203,7 2170 GOTO 2999 2180 REM IF YOU ARE HERE THEN D7 CHECK ALSO FAILED 2190 GRAPHICS (0) 2200 POSITION 13,10:? "NO VBXE FOUND."; 2210 END 2999 RETURN CLEAR VBXE ========================================== Antic Magazine, December 1983, Harvey Branch https://www.atarimagazines.com/v2n9/Garbagecollector.html Using the ability of the Atari to locate strings at any memory location, we can clear memory at machine language speeds. I go through a bunch of banks, and clear them, because otherwise it is quite unpleasant looking at the garbage on the screen. 3000 REM CLEAR VBXE 3010 REM DIM ALL STRINGS BEFORE CALLING THIS 3030 POKE MEMCONTROL,128+8:REM $8000 4K, CPU ACCESS 3040 POKE MEMBANK,128:REM BANK 0 3050 POKE 143,INT(VBMEM/256):POKE 142,VBMEM-256*PEEK(143) 3060 POKE 144,PEEK(142):POKE 145,PEEK(143) 3070 DIM CLEAR$(4096):REM FAST STRING CLEAR 3080 FOR LP=0 TO 31 3090 POKE MEMBANK,128+LP:REM CLEAR 32 X 4K BANKS 3100 CLEAR$(1)=CHR$(0):CLEAR$(4096)=CHR$(0) 3110 CLEAR$(2)=CLEAR$ 3120 NEXT LP 3999 GOTO 300 XDL === The XDL controls the screen, it is so flexible, but I do not want that flexibility right now. What I want is to use the VBXE. This is the "Standard Resolution" mode from the S2: driver. I say copy and paste this and just use it. This gets you a 320x192 screen with 256 colors. That's all i have to offer on this subject - I copied this from the S2: driver. Recall that a standard Atari display list also started with blank lines for "off screen" lines that a TV might draw, this also has mode off for a while, then then turns on "standard resolution" for 192 lines, the 192 visible lines on your monitor. Key point of interest: lines 4070-4080, the 3 bytes representing the visible screen location. Change that to what you need for your memory layout. 4000 REM XDL 4010 REM LIKE S_VBXE.SYS SR MODE 4020 POKE MEMBANK,128:REM START AT VBXE MEM LOCATION $00000 4040 POKE VBMEM,116:REM $74 4050 POKE VBMEM+1,15:REM $0F MODE OFF 4060 POKE VBMEM+2,23:REM $17 REPEAT 24 4070 POKE VBMEM+3,136:REM $10C88 NEXT 3 bytes: START OF VISIBLE SCREEN 4080 POKE VBMEM+4,12:REM 4090 POKE VBMEM+5,1:REM END Location of screen 4100 POKE VBMEM+6,64:REM $40 4110 POKE VBMEM+7,1:REM $01 LOAD_OVL $140 4120 POKE VBMEM+8,248:REM LOAD_CHBASE $7C000 4130 POKE VBMEM+9,0:REM $00000 4140 POKE VBMEM+10,0 4150 POKE VBMEM+11,0 4160 POKE VBMEM+12,40 4170 POKE VBMEM+13,0:REM LOAD_MAP $028 4180 POKE VBMEM+14,0 4190 POKE VBMEM+15,0 4200 POKE VBMEM+16,7 4210 POKE VBMEM+17,7:REM MAP_ATTR 0,0, 8X8 4220 POKE VBMEM+18,17:REM 4230 POKE VBMEM+19,223 4240 POKE VBMEM+20,34 4250 POKE VBMEM+21,128:REM MODE SR 4260 POKE VBMEM+22,191:REM REPEAT 192 4999 RETURN 5000 REM PALETTE 5010 PSEL=BASE+69:CSEL=BASE+68:CR=BASE+70:CG=BASE+71:CB=BASE+72 5015 POKE CSEL,0:POKE PSEL,1 5020 OPEN #1,4,0,"D1:VBPAL.BIN" 5030 OFFSET=512 5040 BGET #1,VBMEM+OFFSET,768 5050 CLOSE #1 5060 POKE MEMBANK,128+0:REM BANK 0 5070 PKI=VBMEM+OFFSET 5080 FOR X=0 TO 767 STEP 3:PKV=PKI+X:POKE CR,PEEK(PKV):POKE CG,PEEK(PKV+1):POKE CB,PEEK(PKV+2):NEXT X 5999 RETURN BCB ==== Blitter Control blocks are always 21 bytes, to that isn't too bad to configure. Configuration items 1)The source address. Start with just one picture, load it somewhere in the VBXE memory. You know where you put it - now tell the BCB where you put it. That's the source address. 2) source step y, and source step x For source step Y, I put in 320 (1,64). For source Step X, I put in 1. Why? Because they seem to work, I could make no sense out of the instructions, but logically in an SR screen the next Y occurs after 320 bytes. The next X after 1 byte. 3) the destination address This is where you are drawing it within the visible screen area. 4) destination step y, destination step x, same values 320 and 1 5) height and width. Recall I have a spritesheet with many sprites on it. This is where I carve out 79 x 55 frame-so just the individual sprite from the spritesheet. 6) AND MASK - I'm just copying over the entire screen area with my sprite. This is a simple strategy I can do, because I have only the airplane on the VBXE overlay. If I were interfacing with other sprites and background elements - would require different strategy. 6000 REM LOAD BCBS 6005 REM SIMPLY FOR TESTING 6010 POKE BASE+65,0:POKE BASE+66,0:POKE BASE+67,0:REM SET XDL ADDRESS 6020 POKE BASE+64,3:REM ENABLE XDL, EXT COLOR 6030 POKE MEMBANK,128:REM BANK 0 6035 ADD1=0:ADD2=30 6040 FOR OFFSET=1280 TO 1385 STEP 21 6050 POKE VBMEM+OFFSET,ADD1:REM ADDRESS BYTE 1 6060 POKE VBMEM+OFFSET+1,ADD2:REM SOURCE ADDRESS BYTE 2 6070 POKE VBMEM+OFFSET+2,0:REM SOURCE ADDRESS BYTE 3 6071 ADD1=ADD1+80:IF ADD1>255 THEN ADD1=ADD1-256:ADD2=ADD2+1 6072 IF ADD1=222 THEN ADD1=223 6080 POKE VBMEM+OFFSET+3,64:REM SOURCE STEP Y BYTE 1 6090 POKE VBMEM+OFFSET+4,1:REM SOURCE STEP Y BYTE 2 6100 POKE VBMEM+OFFSET+5,1:REM SOURCE STEP X 6110 POKE VBMEM+OFFSET+6,0:REM DESTINATION ADDRESS BYTE 1 F378 62328 6120 POKE VBMEM+OFFSET+7,0:REM DESTINATION ADDRESS BYTE 2 6130 POKE VBMEM+OFFSET+8,1:REM DESTINATION ADDRESS BYTE 3 6140 POKE VBMEM+OFFSET+9,64:REM DEST STEP Y BYTE 1 6150 POKE VBMEM+OFFSET+10,1:REM DEST STEP Y BYTE 2 6160 POKE VBMEM+OFFSET+11,1:REM DEST STEP X 6170 POKE VBMEM+OFFSET+12,79:REM WIDTH BYTE1 6180 POKE VBMEM+OFFSET+13,0:REM WIDTH BYTE 2 6190 POKE VBMEM+OFFSET+14,55:REM HEIGHT 6200 POKE VBMEM+OFFSET+15,255:REM AND MASK 6210 POKE VBMEM+OFFSET+16,0:REM XOR MASK 6220 POKE VBMEM+OFFSET+17,0:REM COLLISION AND MASK 6230 POKE VBMEM+OFFSET+18,0:REM ZOOM 6240 POKE VBMEM+OFFSET+19,0:REM PATTERN FEATURE 6250 POKE VBMEM+OFFSET+20,0:REM CONTROL 6260 POKE BASE+80,0:POKE BASE+81,5:POKE BASE+82,0:REM SET BCB ADDRESS 6280 NEXT OFFSET 6999 RETURN SPRITESHEET =========== The spritesheet today now has 5 animation frames for plane with landing gear, 5 frames w/o landing gear, and a crashed plane. All I am doing here is loading it into memory. 7000 REM LOAD SPRITESHEET 7010 OPEN #1,4,0,"D1:SHEET.BIN" 7020 OFFSET=3584 7025 POKE MEMBANK,128+1 7030 BGET #1,VBMEM+OFFSET,512 7035 FOR I=2 TO 13 7040 POKE MEMBANK,128+I 7050 BGET #1,VBMEM,4096 7060 NEXT I 7080 POKE MEMBANK,128+14 7090 BGET #1,VBMEM,1536 7100 CLOSE #1 7999 RETURN 8000 REM ENDING MESSAGE 8010 GRAPHICS 0:POKE 752,1 8020 POSITION 13,10:? "DONE LOADING.";:POSITION 9,11:? "RUN ";:? CHR$(34);:? "D:RUNWAY180.BAS";:? CHR$(34); 8999 RETURN