+Random Terrain Posted October 5, 2012 Share Posted October 5, 2012 So can any of this be used with the regular version of bB? Like a 23 row superchip playfield? If so, can it be used to draw different sections of the screen by randomly selecting from little predetermined chunks of data and do something similar to this: http://www.atariage....maze-test-2011/ random_maze_test_16x16x16_with_scroll_32x23_2011y_08m_21d_2001t.bin (Hit the reset switch to create different mazes.) Quote Link to comment Share on other sites More sharing options...
Grebnedlog Posted October 5, 2012 Author Share Posted October 5, 2012 (edited) So can any of this be used with the regular version of bB? Like a 23 row superchip playfield? I see no reason why it couldn't be used with any kernel version. The routine isn't doing anything that's specific to the DPC+ kernel. The only reason I picked that one is because that's the kernel I'm working with at the moment, and because I think it's greatest usefulness would be with drawing hi-res playfields that eat up a lot of ROM space. If so, can it be used to draw different sections of the screen by randomly selecting from little predetermined chunks of data and do something similar to this: http://www.atariage....maze-test-2011/ random_maze_test_16x16x16_with_scroll_32x23_2011y_08m_21d_2001t.bin (Hit the reset switch to create different mazes.) Again, I don't see why not. For instance, you could just define offsets that point to the various regions of the maze, then randomize which segment of the array it reads during the pfpixel routine. For instance, when using this method each row of playfield pixels is defined by 4 bytes, meaning that each data statement can contain 64 playfield rows (or one 64 row x 32 column playfield). So, if you were using the optional superchip 23 visible rows (23 x 32) display, you could maybe break each maze down into 4 segments of 16 rows (or 8 segments of 8 rows, etc), then do a rand command in conjunction with "current_row" to pick the starting point of the segment you want to draw. And that's just one of many ways to do it. I see no reason to even be limited to a single data statement to draw a semi-random "maze-style" playfield. You could always look up data segments in multiple arrays for your maze pieces, in whatever way you see fit. All this routine does is draw whatever binary image is fed into it. Edited October 5, 2012 by Grebnedlog Quote Link to comment Share on other sites More sharing options...
bogax Posted October 5, 2012 Share Posted October 5, 2012 I see no reason to even be limited to a single data statement to draw a semi-random "maze-style" playfield. You could always look up data segments in multiple arrays for your maze pieces, in whatever way you see fit. All this routine does is draw whatever binary image is fed into it. How could you select amongst data statements with a parameter? Only thing I can think of is a case statement. (or IF statements) Quote Link to comment Share on other sites More sharing options...
Grebnedlog Posted October 5, 2012 Author Share Posted October 5, 2012 (edited) How could you select amongst data statements with a parameter? Only thing I can think of is a case statement. (or IF statements) Yeah, that's what I meant. You could do it with cases or conditionals. It was sort of a broad question (whether this could be adapted for random mazes). The short answer is "of course it can", but it can be done in almost too many ways to describe. In addition to randomizing the pointer, you could also assign another byte of RAM that points to different IF statements. For instance: dim current_row = a dim current_col = b dim current_byte = c dim current_bit = d dim lo_pointer = e dim print_row = f dim chunk_start = g dim maze_ref = h ... WritePFChunk for current_row = 0 to 7 lo_pointer = current_row * 4 for current_col = 0 to 31 current_byte = current_col / 8 + lo_pointer current_bit = current_col & 7 print_row = chunk_start + current_row on maze_ref goto MazeData1 MazeData2 MazeData3 MazeData4 MazeData1 if mazes1[current_byte] & setbits[current_bit] then pfpixel current_col print_row goto Continue WritePFChunk MazeData2 if mazes2[current_byte] & setbits[current_bit] then pfpixel current_col print_row on goto Continue WritePFChunk MazeData3 if mazes3[current_byte] & setbits[current_bit] then pfpixel current_col print_row on goto Continue WritePFChunk MazeData4 if mazes3[current_byte] & setbits[current_bit] then pfpixel current_col print_row on Continue WritePFChunk next next How he arrives at the value of "maze_ref" is completely up to the programmer, of course. It could perhaps increment with the levels or stages that a player advances through, or could itself be randomly assigned. EDIT: Boy, this forum software really messes up the spacing inside CODE tags. Edited October 5, 2012 by Grebnedlog Quote Link to comment Share on other sites More sharing options...
bogax Posted October 5, 2012 Share Posted October 5, 2012 (edited) WritePFChunk for current_row = 0 to 7 lo_pointer = current_row * 4 print_row = chunk_start + current_row for current_col = 0 to 31 current_bit = current_col & 7 current_byte = current_col / 8 + lo_pointer on maze_ref goto MazeData1 MazeData2 MazeData3 MazeData4 MazeData1 current_byte = Mazes1[current_byte] : goto Continue_WritePFChunk MazeData2 current_byte = Mazes2[current_byte] : goto Continue_WritePFChunk MazeData3 current_byte = Mazes3[current_byte] : goto Continue_WritePFChunk MazeData4 current_byte = Mazes4[current_byte] Continue_WritePFChunk if current_byte & setbits[current_bit] then pfpixel current_col print_row on next next Edit: removed the final goto (it's redundant) Edited October 6, 2012 by bogax Quote Link to comment Share on other sites More sharing options...
Grebnedlog Posted October 6, 2012 Author Share Posted October 6, 2012 WritePFChunk for current_row = 0 to 7 lo_pointer = current_row * 4 print_row = chunk_start + current_row for current_col = 0 to 31 current_bit = current_col & 7 current_byte = current_col / 8 + lo_pointer on maze_ref goto MazeData1 MazeData2 MazeData3 MazeData4 MazeData1 current_byte = Mazes1[current_byte] : goto Continue_WritePFChunk MazeData2 current_byte = Mazes2[current_byte] : goto Continue_WritePFChunk MazeData3 current_byte = Mazes3[current_byte] : goto Continue_WritePFChunk MazeData4 current_byte = Mazes4[current_byte] : goto Continue_WritePFChunk Continue_WritePFChunk if current_byte & setbits[current_bit] then pfpixel current_col print_row on next next Yes that is better, thanks. Quote Link to comment Share on other sites More sharing options...
+Random Terrain Posted October 6, 2012 Share Posted October 6, 2012 Do you think one of you guys could write a small working program based on what has been posted above and maybe explain how it works so I can adapt your text and program for the bB page? I'm sure more than I would like to learn how to do this to save space in the last bank for sprites. As it is right now, most of it is very far above my head, so there's no way I could explain it myself or write a working example program. Thanks. Oh yeah, and about posting code here, this online code cleaner by kisrael can help with that: alienbill.com/2600/basic/aabbcc.cgi Quote Link to comment Share on other sites More sharing options...
bogax Posted October 13, 2012 Share Posted October 13, 2012 (edited) What do routines like this really need to do? The examples here are writing chunks. If you don't need to do things on a per bit basis it's a lot easier (and faster) to do bytes or rows. I think if you were doing things in bB it's probably better to do your own version of pfpixel, especially if you're doing individual bits, but it would have to be taylored to the kernal/ kernal options. (well, maybe not strictly speaking, but that would certainly be preferable) And, of course, a little asm could help a lot. It seems just barely possible to get some purely bB version of the routines here to work (in Stella) but they take a lot of cycles and they don't really do much. Edited October 13, 2012 by bogax Quote Link to comment Share on other sites More sharing options...
Grebnedlog Posted October 13, 2012 Author Share Posted October 13, 2012 What do routines like this really need to do? The examples here are writing chunks. If you don't need to do things on a per bit basis it's a lot easier (and faster) to do bytes or rows. I agree, it would be nice to think of other applications for this. My application was very specific and limited (loading hi-res playfields for DPC+ games without using the graphics bank). I guess that bit arrays might also work pretty well for storing a bunch of scrollable playfield data too, but not using the specific pf-drawing routine I wrote. That works fine for drawing a static playfield before UI starts, but it's too cycle intensive to nest in any sort of looping gameplay. In general, I guess the application would be that storage of any binary data that requires >256 values. But I'm thinking binary values would probably be most useful for storing playfield and sprite pixel states -- and, as far as I know, there is no "playerpixel" command for writing to player sprites, so storage of playfield data is probably the most applicable thing for most kinds of Atari games. As for including this on Random Terrain's bB page, what exactly is it that needs to be up there? Is it just a brief explanation of how to structure a bit array and read values from it? If that's the case, are we sure that the method we've come up with on this thread is the simplest/most-efficient one? It works well, but I'm just wondering about other ways to do it (and other ways to demonstrate it). Quote Link to comment Share on other sites More sharing options...
+Gemintronic Posted October 13, 2012 Share Posted October 13, 2012 (edited) You could demonstrate using bit arrays for a collision mask. Some games might "flash" playfield blocks on and off and thus wouldn't work with the usual collision routines. Or, if the programmer used the hi-res playfield he may want to represent one block as more than one playfield pixel. Using a collision mask made up of a bit array would solve those problems. Edited October 13, 2012 by theloon Quote Link to comment Share on other sites More sharing options...
bogax Posted October 13, 2012 Share Posted October 13, 2012 The conclusion I'm coming to is that it would be best to write some utilities in asm that could be included in and called from a bB program to do some of this stuff. In the present case of picking out a bit from a data statement and then calling pfpixel you end up duplicating in bB stuff that pfpixel then does any way. Also the setbits data statment duplicates data in the kernal. Then too there's stuff you could do in asm that you can't do with bB. Quote Link to comment Share on other sites More sharing options...
RevEng Posted October 13, 2012 Share Posted October 13, 2012 Agreed. I think the arm assembly routine to do this would be the equivalent of a simple memcpy() Someone wanting this might suggest it to batari. Quote Link to comment Share on other sites More sharing options...
iesposta Posted November 11, 2012 Share Posted November 11, 2012 I can so use this routine for my DK 2600. Thanks for making and sharing it! My tests work! The only thing I can add is for a complete, highest res playfield, it has to be broken into three data sets. And you don't need "include div_mult..." unless it makes better binaries? Move the NTSC to the top so DASM calculates bytes free. current_row can only go up to 64 (0 to 63), I guess it has to do with 256 bytes of data at a time. A max res playfield in bB 1.1d is 0 thru 176. The first line is a double, not single (see example .bas [bB oddity]), and the last line is not displayed (used when vertical scrolling). So it is chunk_start=0 current_row 0 to 63, then chunk start = 64 current_row 0 to 63, then chunk_start = 128 for current_row 0 to 48 end rows. Also, how can I change ....xxx..x.x.x.x.x...... Into %00001110,%01010101,%00000000,%00000000 I did it by hand line by line... You said find and replace, but I don't get it. What are you finding? It is different every 8 digits!? I can find the . and repace it with 0, and find the x and replace it with 1, but then what? You have to add a % at the beginning and then "move over 8 and add a ,% three times" - repeat 176 more times. Here are my test files: DrawPlay4.bas.bin DrawPlay4.bas Quote Link to comment Share on other sites More sharing options...
bogax Posted November 15, 2012 Share Posted November 15, 2012 I'm curious, if you're drawing a playfield from data statements, would you optimize for code size or speed or would it depend on what you're doing ? Quote Link to comment Share on other sites More sharing options...
iesposta Posted November 15, 2012 Share Posted November 15, 2012 In using the DPC+ kernel, drawing a playfield from a data statement would use the current bank to store the data and not waste the "graphics" bank, so you can have more sprites and animation. Also it is slow and will send the scan line count off, so it is only good to use once before the main loop. Until I find another way, like an extended graphics bank or an inline assembly routine, it is the only way I can get 4 high res playfields in batari Basic. Quote Link to comment Share on other sites More sharing options...
bogax Posted November 16, 2012 Share Posted November 16, 2012 In using the DPC+ kernel, drawing a playfield from a data statement would use the current bank to store the data and not waste the "graphics" bank, so you can have more sprites and animation. Also it is slow and will send the scan line count off, so it is only good to use once before the main loop. Until I find another way, like an extended graphics bank or an inline assembly routine, it is the only way I can get 4 high res playfields in batari Basic. Sounds like, in your case, might as well optimize for code size. I doubt it can be made fast enough. You're really doing something akin to a block transfer and pfpixel is the wrong tool for the job. You need somthing like pfpixel that will do whole bytes. With the default kernel that's possible even from Bb but I don't know enough about Harmony or DPC+ to do a byte at a time (assuming it's possible short of rewriting the kernel). Here is my attempt to speed things up. This code compiles but is otherwise UNTESTED Basically it's got the bits unrolled and uses constants instead of a setbits table. The data statements are referenced with a pointer. This costs a few cycles per pixel but shortens the code and presents the possibility of paramterizing the transfer so that it could be done in small chunks. I didn't do that because I don't know how small the chunks would need to be. You could probably do a few rows at a time and it would probably take several seconds to do the whole thing. I also wasted a few cycles per pixel to get rid of redundant pfpixel calls With this code the data would be divided in to 4 equal pieces of 44 rows each (named L4_0 - L4_3) dim current_row = a dim current_col = b dim byte_col = c dim current_byte = d dim ds_index = e dim print_row = f macro Pixel_ON_macro asm LDA #(1) LDY #(2) LDX #0 JMP pfpixel end end WritePFChunk print_row = 0 ds_index = 0 DS_LOOP current_row = 0 ROW_LOOP current_col = 0 for byte_col = 0 to 4 current_byte = current_row | byte_col on ds_index goto DCASE0 DCASE1 DCASE2 DCASE3 CONT_WRITE_PF if current_byte & $80 then gosub PIXEL_ON current_col = current_col + 1 if current_byte & $40 then gosub PIXEL_ON current_col = current_col + 1 if current_byte & $20 then gosub PIXEL_ON current_col = current_col + 1 if current_byte & $10 then gosub PIXEL_ON current_col = current_col + 1 if current_byte & $08 then gosub PIXEL_ON current_col = current_col + 1 if current_byte & $04 then gosub PIXEL_ON current_col = current_col + 1 if current_byte & $02 then gosub PIXEL_ON current_col = current_col + 1 if current_byte & $01 then gosub PIXEL_ON current_col = current_col + 1 next print_row = print_row + 1 current_row = current_row + 4 if current_row < 173 then goto ROW_LOOP ds_index = ds_index + 1 if ds_index < 4 then goto DS_LOOP return DCASE0 current_byte = L4_0[current_byte] : goto CONT_WRITE_PF DCASE1 current_byte = L4_1[current_byte] : goto CONT_WRITE_PF DCASE2 current_byte = L4_2[current_byte] : goto CONT_WRITE_PF DCASE3 current_byte = L4_3[current_byte] : goto CONT_WRITE_PF PIXEL_ON callmacro Pixel_ON_macro current_col print_row Quote Link to comment Share on other sites More sharing options...
iesposta Posted November 17, 2012 Share Posted November 17, 2012 I couldn't get the above code to work. Quote Link to comment Share on other sites More sharing options...
+Random Terrain Posted November 17, 2012 Share Posted November 17, 2012 I couldn't get the above code to work. That's because he forgot to run his code through this first: The Atari Age Batari BASIC Code Cleaner If you don't use that, your indentation gets stripped away and the code becomes useless. Quote Link to comment Share on other sites More sharing options...
iesposta Posted November 17, 2012 Share Posted November 17, 2012 Well, that... but I typed it in line by line. Quote Link to comment Share on other sites More sharing options...
+Random Terrain Posted November 17, 2012 Share Posted November 17, 2012 Well, that... but I typed it in line by line. You shouldn't have to. A copy and paste is quicker and easier. Did you add the proper indentations and not indent what shouldn't be indented? Quote Link to comment Share on other sites More sharing options...
bogax Posted November 17, 2012 Share Posted November 17, 2012 (edited) I see one goof right off. The byte_col loop should only go to 3 All so, the addressing mode in the macro is wrong. The macro doesn't work any way. I could have sworn I'd done that before but... Here's some code that's tested. Print_row/a, current_col/b are referenced in hex in the asm so if they're dimmed different the asm will have to be changed. It may not work anyway in DPC+ I don't know how that's set up. As for the formatting I'll see if I can attach some files. (they're just this code not a complete program that's actually going to run) One with the asm and one with out. So try this dim print_row = a dim current_col = b dim current_row = c dim byte_col = d dim current_byte = e dim ds_index = f WritePFChunk print_row = 0 for ds_index = 0 to 3 for current_row = 0 to 172 step 4 current_col = 0 for byte_col = 0 to 3 current_byte = current_row | byte_col on ds_index goto DCASE0 DCASE1 DCASE2 DCASE3 BYTE_DONE next print_row = print_row + 1 next next return DCASE0 current_byte = L4_0[current_byte] : goto CONT_WRITE_PF DCASE1 current_byte = L4_1[current_byte] : goto CONT_WRITE_PF DCASE2 current_byte = L4_2[current_byte] : goto CONT_WRITE_PF DCASE3 current_byte = L4_3[current_byte] CONT_WRITE_PF if current_byte & $80 then gosub PIXEL_ON current_col = current_col + 1 if current_byte & $40 then gosub PIXEL_ON current_col = current_col + 1 if current_byte & $20 then gosub PIXEL_ON current_col = current_col + 1 if current_byte & $10 then gosub PIXEL_ON current_col = current_col + 1 if current_byte & $08 then gosub PIXEL_ON current_col = current_col + 1 if current_byte & $04 then gosub PIXEL_ON current_col = current_col + 1 if current_byte & $02 then gosub PIXEL_ON current_col = current_col + 1 if current_byte & $01 then gosub PIXEL_ON current_col = current_col + 1 goto BYTE_DONE PIXEL_ON asm LDA $D7 LDY $D6 LDX #0 JMP pfpixel end edit: attaching a file didn't work for me with http://pastebin.com/QDUgwAG8 without http://pastebin.com/XUknWmpK edit: put the for-next loops back in Edited November 17, 2012 by bogax Quote Link to comment Share on other sites More sharing options...
+Random Terrain Posted November 17, 2012 Share Posted November 17, 2012 Here is a post about converting the playfield data that VbB creates: http://www.atariage.com/forums/topic/185796-playfield-upgrade-request-for-batari-basic/#entry2340664 Quote Link to comment Share on other sites More sharing options...
iesposta Posted November 18, 2012 Share Posted November 18, 2012 Nice clean code there, bogax. Still can't get the asm one to work. (And I did use a, b, c, d, e, f) Your routine takes 105 more bytes than the OP, but one less variable: dim current_row = l dim current_col = m dim current_byte = n dim current_bit = o dim lo_pointer = p dim print_row = q dim chunk_start = r ; adjust this to draw the playfield at a different playfield Y. chunk_start = 0 bkcolors: $00 end pfcolors: $44 end WritePFChunk for current_row = 0 to 63 lo_pointer = current_row * 4 for current_col = 0 to 27 current_byte = current_col / 8 + lo_pointer current_bit = current_col & 7 print_row = chunk_start + current_row if L1_1[current_byte] & setbits[current_bit] then pfpixel current_col print_row on next next chunk_start = 64 for current_row = 0 to 63 lo_pointer = current_row * 4 for current_col = 0 to 27 current_byte = current_col / 8 + lo_pointer current_bit = current_col & 7 print_row = chunk_start + current_row if L1_2[current_byte] & setbits[current_bit] then pfpixel current_col print_row on next next chunk_start = 128 for current_row = 0 to 48 lo_pointer = current_row * 4 for current_col = 0 to 27 current_byte = current_col / 8 + lo_pointer current_bit = current_col & 7 print_row = chunk_start + current_row if L1_3[current_byte] & setbits[current_bit] then pfpixel current_col print_row on next next chunk_start=0 data L1_1 %00000000,%00000000,%00000000,%00000000 ... Quote Link to comment Share on other sites More sharing options...
bogax Posted November 18, 2012 Share Posted November 18, 2012 Your routine takes 105 more bytes than the OP, but one less variable: I was going for speed not code size. you've switched to 28 columns. Here's a shorter version of the previous code. dim print_row = a dim current_col = b dim current_bit = c dim current_byte = d dim ds_index = e dim byte_ptr = f WritePFChunk print_row = 0 current_col = 0 for ds_index = 0 to 3 for byte_ptr = 0 to 175 current_bit = $80 on ds_index goto DCASE0 DCASE1 DCASE2 DCASE3 DCASE0 current_byte = L4_0[byte_ptr] : goto BIT_LOOP DCASE1 current_byte = L4_1[byte_ptr] : goto BIT_LOOP DCASE2 current_byte = L4_2[byte_ptr] : goto BIT_LOOP DCASE3 current_byte = L4_3[byte_ptr] BIT_LOOP if current_byte & current_bit then pfpixel current_col print_row on current_col = current_col + 1 current_col = current_col & $1F current_bit = current_bit / 2 if current_bit then goto BIT_LOOP if byte_ptr & $03 = 3 then print_row = print_row + 1 next next return Quote Link to comment Share on other sites More sharing options...
iesposta Posted November 18, 2012 Share Posted November 18, 2012 That compiles to 135 bytes smaller! I only need 28 colums for my game, I was planning to use the remaining 4 bits for sound or something. What can I change in the code below to do only 28 colums? $1C doesn't work Some of the logic I can't follow like "if byte_ptr & $03 then print_row = print_row + 1" Also, to draw another playfield, it won't clear what already has been drawn (in DPC+) so I have to add "pfpixel current_col print_row off" Thank you. I was going for speed not code size. you've switched to 28 columns. Here's a shorter version of the previous code. dim print_row = l dim current_col = m dim current_bit = n dim current_byte = o dim ds_index = p dim byte_ptr = q WritePFChunk print_row = 0 current_col = 0 for ds_index = 0 to 3 for byte_ptr = 0 to 175 current_bit = $80 on ds_index goto DCASE0 DCASE1 DCASE2 DCASE3 DCASE0 current_byte = L1_0[byte_ptr] : goto BIT_LOOP DCASE1 current_byte = L1_1[byte_ptr] : goto BIT_LOOP DCASE2 current_byte = L1_2[byte_ptr] : goto BIT_LOOP DCASE3 current_byte = L1_3[byte_ptr] BIT_LOOP if current_byte & current_bit then pfpixel current_col print_row on current_col = current_col + 1 current_col = current_col & $1F current_bit = current_bit / 2 if current_bit then goto BIT_LOOP if byte_ptr & $03 = 3 then print_row = print_row + 1 next next Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.