Jump to content
IGNORED

Drawing playfields from data statements


Grebnedlog

Recommended Posts

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.)

Link to comment
Share on other sites

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 by Grebnedlog
Link to comment
Share on other sites

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)

Link to comment
Share on other sites

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 by Grebnedlog
Link to comment
Share on other sites

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 by bogax
Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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 by bogax
Link to comment
Share on other sites

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).

Link to comment
Share on other sites

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 by theloon
Link to comment
Share on other sites

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.

Link to comment
Share on other sites

  • 4 weeks later...

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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 by bogax
Link to comment
Share on other sites

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
...

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

 

Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...