Jump to content
SpiceWare

DPC+ bB player0 graphics in other banks

Recommended Posts

NOTE: There's a bug in this version, get the latest version in Reply 6

 

Stationary graphics are defined as normal.  The other 4 images are defined using data statements in various banks.

 

700978832_ScreenShot2020-01-26at5_03_19PM.thumb.png.8643ae3472ca9049f645d446fa7cc8ad.png

 

 

Stationary

default_bas.thumb.png.9e5ef3c8352e58686dcd33e722ee6783.png

 

Moving up

default.bas_4.thumb.png.9023ca473ff6beb24ba8dea90e4e9490.png

 

Moving down

default.bas_3.thumb.png.d414b76664a34f78a86987c565d419a3.png

 

Moving left

default.bas_1.thumb.png.ff20a4d75dd96116b073e79f6b17b187.png

 

Moving right

default.bas_2.thumb.png.4129dc5ddd454918d61dc720c66c4af9.png

 

 

graphics_bank_test.zip

  • Like 3

Share this post


Link to post
Share on other sites
3 hours ago, SpiceWare said:

I was just scanning the entire site looking for what the usual ((>WALK) & $0f) | (((>WALK) / 2) & $70) does, I guess it just points to the graphics bank? does dasm allways assume it's in the first bank or why do you have to add this manually?

 

Could you walk me through what the & $0f does so I can learn something today? 🙂

 

Does it just turn of half the byte to make sure it's 0000 before adding the bank number????

 

Edited by Lillapojkenpåön

Share this post


Link to post
Share on other sites

Hmm - didn't notice that bB was RORG'ing each bank to have a different upper nybble, that makes things easier as you don't have to manually keep track of the bank to add.

  • player_up = $5088 - bank 3, needed to add $20
  • player_down = $5091
  • player_left = $7088 - bank 4, needed to add $30
  • player_right = $9088 - bank 5, needed to add $40
  • color_up = $b088 - bank 6, needed to add $50
  • color_down = $b093

 

The way that works is:

  1. lda #(>player_up & $0f) | ((>player_up / 2) & $70)
  2. lda #(>$5088 & $0f) | ((>$5088 / 2) & $70)
  3. lda #($50 & $0f) | (($50 / 2) & $70)
  4. lda #($00) | ($28 & $70)
  5. lda #($00) | ($20)
  6. lda #$20

 

  1. lda #(>player_left & $0f) | ((>player_left / 2) & $70)
  2. lda #(>$7088 & $0f) | ((>$7088 / 2) & $70)
  3. lda #($70 & $0f) | (($70 / 2) & $70)
  4. lda #($00) | ($38 & $70)
  5. lda #($00) | ($30)
  6. lda #$30

 

I also had the >( in the wrong order, should have been (>. It didn't cause a problem with graphics in the first 256 bytes of the bank, but would have as soon as the 2nd nybble was anything other than 0 - for example if player_up was at $5487, this would have happened:

  1. lda #>(player_up & $0f) + $20
  2. lda #>($5487 & $0f) | + $20
  3. lda #>($0007) + $20
  4. lda #($00) + $20
  5. lda #$20

but this is what we really need to happen:

  1. lda #(>player_up & $0f) | ((>player_up / 2) & $70)
  2. lda #(>$5487 & $0f) | ((>$5487 / 2) & $70)
  3. lda #($54 & $0f) | (($50 / 2) & $70)
  4. lda #($04) | ($28 & $70)
  5. lda #($04) | ($20)
  6. lda #$24

 

I've revised it again so you can now move the data statements around and not have to worry about which bank they end up in.

 

graphics_bank_test 4.zip

  • Like 2
  • Thanks 1

Share this post


Link to post
Share on other sites

This is brilliant 👍 Thanks for looking into this for me @SpiceWare it's much appreciated. I will download the program and have a good look at what changes you have made to achieve this. Very useful for when we are running tight on data space within the allocated memory area.

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, SpiceWare said:

Hmm - didn't notice that bB was RORG'ing each bank to have a different upper nybble, that makes things easier as you don't have to manually keep track of the bank to add.

I tried to point the colors to RAM awhile ago like this

 asm

    lda #<STACKbegin

    sta player0color

    lda #(>STACKbegin) & $0F

    sta player0color+1

end  

 

so I could just push colors to the stack

 

but in main the flashdata adress I guess keeps you from pointing anywhere else, do you know if it would be possible to change that line to allow or just exclusively be able to point to first of all RIOT RAM since it's fast to read and write to, or the stack or worst case copy from the not visable part of the color queue up to the visable part?

 

            flashdata+(RIOT[player0color+1]<<8)+RIOT[player0color],   //source

            queue+(fetcheraddr[46]<<8)+fetcheraddr[38],    //destination

 

  • Like 1

Share this post


Link to post
Share on other sites

Just a small update, so i have somewhat broken down the test program and realized the hi and lo player0 pointers don't need to be in the mainloop. As long as they're initiated at the start of the program, there is no need to keep updating within the mainloop other then when required by the asm code @SpiceWare provided.

 

here's the bas and bin

graphics_data_test.bas

graphics_data_test.bin

 

Edited by TwentySixHundred
Typo
  • Like 1

Share this post


Link to post
Share on other sites

Broken down further more i have removed the asm sub-routine from the mainloop. I feel for optimization of the mainloop it's important to remove any code when possible. Especially if this data is not needed to be called every frame and depending on the game concept ect. Having the asm sub-routine separate, feels to me like a function to call rather then bB code. It's a personal preference of program flow and structure, sort of like a mini kernel from my perspective. Anyway see below

graphics_data_test.bas

graphics_data_test.bin

Edited by TwentySixHundred
  • Like 1

Share this post


Link to post
Share on other sites

Actually now my brain is ticking about optimization, with my last example adding the goto command is most likely slower but rather cleans up structure the mainloop if that's your style. I will add by using this method it's most likely slower as you're fetching the graphics data from ROM rather then RAM. From my understanding of what @SpiceWare was explaining about the DPC+/ARM and BUS Stuffing is that graphics data is preloaded into RAM.

 

However with that in mind, i would say having the goto command fetch the sprite data from the same bank as your mainloop would be alot faster then bankswitching. Provided that's where you wanted to store the data and depending on when the data is fetched. So for instance, with a game like Stay Frosty if you wanted to update the player sprite when collecting the ice cubes it wouldn't be all that noticeable when bankswitching. Although a game that updates the sprite data frequently id say having that data stored with the same bank would be the faster option if desired.

 

I'm not a 100% sure and maybe @SpiceWare could chime in sometime to clarify that statement.

Share this post


Link to post
Share on other sites
4 hours ago, TwentySixHundred said:

so i have somewhat broken down the test program and realized the hi and lo player0 pointers don't need to be in the mainloop. As long as they're initiated at the start of the program, there is no need to keep updating within the mainloop

 

They are still as much in the mainloop as Darrell's example, you're just visually jumping to somewhere else which doesn't optimize but rather adds unnecessary jumps, if you mean the sprite that you moved to the init, it does nothing in your code other than set the playerheight to eight, it points to a sprite and color but is immediately overridden on the first frame when the pointers are set to something else in NOTFIRE. Darrell had it in the mainloop because it was the stand still sprite, so just a easy way to reset to that one when no joystick direction is pressed.

 

You can set the high pointers in the init tho if you merge the graphics and colors

 

You were never pointing to RAM only ROM, and you're not bankswitching when seting the pointers in one bank to a table in another, you're just storing a number to a variable so the speed is the same.

You also weren't using the last code Darrell posted, that let's you put the graphics in any bank without changing any numbers.

 

 

graphics_data_test2.bas

 

 

 

Edited by Lillapojkenpåön
  • Like 2

Share this post


Link to post
Share on other sites
11 minutes ago, Lillapojkenpåön said:

They are still as much in the mainloop as Darrell's example, you're just visually jumping to somewhere else which doesn't optimize but rather adds unnecessary jumps, if you mean the sprite that you moved to the init, it does nothing in your code other than set the playerheight to eight, it points to a sprite and color but is immediately overridden on the first frame when the pointers are set to something else in NOTFIRE. Darrell had it in the mainloop because it was the stand still sprite, so just a easy way to reset to that one when no joystick direction is pressed.

 

You can set the high pointers in the init tho if you merge the graphics and colors

 

You were never pointing to RAM only ROM, and you're not bankswitching when seting the pointers in one bank to a table in another, you're just storing a number to a variable so the speed is the same.

You also weren't using the last code Darrell posted, that let's you put the graphics in any bank without changing any numbers.

 

 

graphics_data_test2.bas 7.07 kB · 0 downloads

 

 

 

Yes i was aware later that adding the goto command was basically just moving the code outside the mainloop rather then optimizing. I think because i changed the standing sprite to a continuously moving sprite the hi lo pointers i hadn't noticed it was needed for the standing sprite.

 

Also thanks for clarifying that no matter what bank the data is stored in, it's not actually bankswitching. I was under the assumption you would need to in order to access that data from the bank. All good 👍

Share this post


Link to post
Share on other sites
10 hours ago, Lillapojkenpåön said:

I tried to point the colors to RAM awhile ago like this

 

 

I figured out how to do this by looking in the default.bas.asm file to see what it was doing.  For the color it did this:

.L032 ;  player0color:

	LDX #<playercolorL032_0
	STX player0color
	LDA #((>playercolorL032_0) & $0f) | (((>playercolorL032_0) / 2) & $70)
	STA player0color+1

 

for the graphics it did this:

.L031 ;  player0:

	LDX #<playerL031_0
	STX player0pointerlo
	LDA #((>playerL031_0) & $0f) | (((>playerL031_0) / 2) & $70)
	STA player0pointerhi
	LDA #8
	STA player0height

 

The DPC+ bB routines use some ARM/C code that you can find in bB/includes/custom/main.c:

  // fill color from player0, wrapping if necessary...
  //my_memcpy(queue+(dfhigh(0)<<8)+dflow(0),
  my_memcpy(queue+get32bitdf(0),
            flashdata+(RIOT[player0color+1]<<8)+RIOT[player0color], RIOT[player0y],
            RIOT[player0height]);

  //my_memcpy(queue+(dfhigh(2)<<8)+dflow(2),
  my_memcpy(queue+get32bitdf(2),
            flashdata+(RIOT[player0pointerhi]<<8)+RIOT[player0pointerlo], 0,
            RIOT[player0height]);

 

queue = Display Data RAM

flashdata = ROM

RIOT = 6507 ZP RAM

 

so those memory copy instructions can only copy data from ROM into RAM.  I found the RIOT to be interesting, apparently bB passes some, or all, of 6507 ZP RAM to the ARM routines as the ARM has no access to RIOT. I didn't see where this occurs, but didn't spend much time looking.

 

I also noticed bB is using a different version of my_memcpy, in Space Rocks it's this:

void my_memcpy(unsigned char* destination, unsigned char* source, int count)
{
	int i;
	for(i=0;i<count;i++)
		destination[i] = source[i];
}

 

while in bB it's this:

void my_memcpy(unsigned char* destination, unsigned char* source, int offset, int count)
{ 
        int i; //saves a few bytes
        for(i=0;i<count;i++)
                destination[(i+offset)&255] = source[i]&mask;
} 

 

So it lets you do an offset as a parameter, and mask the data while it gets copied.  Masking the data would let you smoothly move players on/off screen like I do in Stay Frosty 2.  It doesn't look like bB supports that for the players, though I could have easily missed something as I didn't analyze the routines in depth.

 

1 hour ago, TwentySixHundred said:

From my understanding of what @SpiceWare was explaining about the DPC+/ARM and BUS Stuffing is that graphics data is preloaded into RAM.

 

The way DPC+ was designed, there's 4K of Display Data in ROM that gets copied to Display Data RAM when the 2600 is powered up.  The data must be in RAM for performance reasons.  If you're writing a DPC+ game using assembly then the graphics are in place ready for use.  If you're writing a DPC+ game that uses ARM/C code then you can reuse the RAM however you want - that's what bB's DPC+ kernel is doing.  So what's going on is bB DPC+ is not using the Display Data RAM like I thought it was.

 

1 hour ago, TwentySixHundred said:

However with that in mind, i would say having the goto command fetch the sprite data from the same bank as your mainloop would be alot faster then bankswitching. Provided that's where you wanted to store the data and depending on when the data is fetched.

 

The actual copying of the sprite data occurs in the ARM/C Code.  It can see the full 32K of ROM, so there is no performance penalty if the data is in a different bank than the 6507 asm routine that sets player0pointerlo/hi.

 

 

  • Like 1
  • Thanks 2

Share this post


Link to post
Share on other sites
10 minutes ago, SpiceWare said:

The way DPC+ was designed, there's 4K of Display Data in ROM that gets copied to Display Data RAM when the 2600 is powered up.  The data must be in RAM for performance reasons.  If you're writing a DPC+ game using assembly then the graphics are in place ready for use.  If you're writing a DPC+ game that uses ARM/C code then you can reuse the RAM however you want - that's what bB's DPC+ kernel is doing.  So what's going on is bB DPC+ is not using the Display Data RAM like I thought it was.

 

 

The actual copying of the sprite data occurs in the ARM/C Code.  It can see the full 32K of ROM, so there is no performance penalty if the data is in a different bank than the 6507 asm routine that sets player0pointerlo/hi.

 

 

Thanks, that's making a lot more sense now and i'm starting to understanding how DPC+ uses the graphics data. So it makes complete sense there is no performance loss when the ARM/C code can see the full ROM as a whole. That explains why there is no bankswitching happening no matter where the sprite data is stored. Interesting stuff learning about what's going on in the background 👍

  • Like 1

Share this post


Link to post
Share on other sites
9 minutes ago, orange808 said:

Same as this, right?

 

 

 

Yep - I guess @TwentySixHundred hadn't seen that before as he posted this in another topic:

 

22 hours ago, TwentySixHundred said:

There is also the restriction within DPC+ i forgot to mention of storing graphics data. We are restricted to only being able to store 4k worth of data and haven't the ability to store any graphics data in other banks.

 

I'm not familiar with bB, and nobody else chimed in that a solution had already been found, so I offered to take a look to see if I could figure it out.    

 

I was planning to look into how to do the same for player1-9 tonight, but now I don't have to so thanks!  Instead, I'll be resuming work on the next entry for my CDFJ tutorial.

  • Like 2

Share this post


Link to post
Share on other sites
6 minutes ago, SpiceWare said:

I was planning to look into how to do the same for player1-9 tonight, but now I don't have to so thanks!  Instead, I'll be resuming work on the next entry for my CDFJ tutorial.

Well, drat. I was hoping that if you poked around in the DPC+ kernel innards that you would be tempted to create a CDFJ-based bB kernel to replace it. :P

  • Like 1
  • Thanks 1
  • Haha 1

Share this post


Link to post
Share on other sites
7 minutes ago, SpiceWare said:

 

 

Yep - I guess @TwentySixHundred hadn't seen that before as he posted this in another topic:

 

 

I'm not familiar with bB, and nobody else chimed in that a solution had already been found, so I offered to take a look to see if I could figure it out.    

 

I was planning to look into how to do the same for player1-9 tonight, but now I don't have to so thanks!  Instead, I'll be resuming work on the next entry for my CDFJ tutorial.

After having a skim read through, looks like i had must of missed that thread. However i do remember noticing the last two posts on Sunday (obviously because i liked them). Having said i think @Lillapojkenpåön was having some issues trying to work out the sprites side of things. So i think this thread was still valuable information to complete the last piece of the puzzle 👍 Also nothing still has been said about player1 and the virtual sprites 😉.

Share this post


Link to post
Share on other sites
9 minutes ago, Karl G said:

Well, drat. I was hoping that if you poked around in the DPC+ kernel innards that you would be tempted to create a CDFJ-based bB kernel to replace it. :P

I know it's a joke but yes please! 😀 That's probably too much to ask though lol

Share this post


Link to post
Share on other sites
17 minutes ago, Karl G said:

Well, drat. I was hoping that if you poked around in the DPC+ kernel innards that you would be tempted to create a CDFJ-based bB kernel to replace it. :P

 

My current plans are to finish the CDFJ Tutorial then resume work on SpiceC, which would be like bB DPC+ but use C instead of BASIC and CDFJ instead of DPC+.  I suspect most of the bB users would be able to figure out SpiceC as a lot of it is very similar.  Instead of bB's:

 if joy0up then p0y = p0y - 1
 if joy0down then p0y = p0y + 1
 if joy0left then p0x = p0x - 1
 if joy0right then p0x = p0x + 1

 

you'd write something like this:

    if (JOY0_UP)
        player_y[0] = player_y[0] - 1;

    if (JOY0_DOWN)
        player_y[0] = player_y[0] + 1;

    if (JOY0_LEFT)
        player_x[0] = player_x[0] - 1;
    
    if (JOY0_RIGHT)
        player_x[0] = player_x[0] + 1;

 

Though most C programmers would use the Increment and Decrement Operators as they take less typing:

    if (JOY0_UP)
        player_y[0]--;

    if (JOY0_DOWN)
        player_y[0]++;

    if (JOY0_LEFT)
        player_x[0]--;
    
    if (JOY0_RIGHT)
        player_x[0]++;

 

 

  • Like 4

Share this post


Link to post
Share on other sites
3 minutes ago, TwentySixHundred said:

Also nothing still has been said about player1 and the virtual sprites 😉.

 

That's covered in Reply 9, though it'd be easier to understand as an example in an actual program.  I'll go ahead and do that after work, I'll add a 2nd player that can be moved around with the right joystick.

  • Like 2

Share this post


Link to post
Share on other sites
10 minutes ago, SpiceWare said:

 

My current plans are to finish the CDFJ Tutorial then resume work on SpiceC, which would be like bB DPC+ but use C instead of BASIC and CDFJ instead of DPC+.  I suspect most of the bB users would be able to figure out SpiceC as a lot of it is very similar.  Instead of bB's:

 if joy0up then p0y = p0y - 1
 if joy0down then p0y = p0y + 1
 if joy0left then p0x = p0x - 1
 if joy0right then p0x = p0x + 1

 

you'd write something like this:

    if (JOY0_UP)
        player_y[0] = player_y[0] - 1;

    if (JOY0_DOWN)
        player_y[0] = player_y[0] + 1;

    if (JOY0_LEFT)
        player_x[0] = player_x[0] - 1;
    
    if (JOY0_RIGHT)
        player_x[0] = player_x[0] + 1;

 

Though most C programmers would use the Increment and Decrement Operators as they take less typing:

    if (JOY0_UP)
        player_y[0]--;

    if (JOY0_DOWN)
        player_y[0]++;

    if (JOY0_LEFT)
        player_x[0]--;
    
    if (JOY0_RIGHT)
        player_x[0]++;

 

 

So with SpiceC the whole program can be written in that C-like syntax or is assembly still needed? What compiler and tools are used? Would Cmake and a standard IDE like Visual Studio Code be fine? I really should have a good read through your tutorials, you probably have already answered these questions 👍

Share this post


Link to post
Share on other sites

I can't wait for SpiceC!!!!!!!!!🤤 An improved kernel for bB could just be a hack of the DPC+ kernel, 9 virtual player0 sprites for example, using the exact same code as for the nine player1's, if there isn't something keeping the kernel to only be in one bank?

 

and maybe one could change the six playfield reads and playfield color to some other updates more like the slick kernel?

14 minutes ago, SpiceWare said:

though it'd be easier to understand as an example in an actual program. 

here's some more source material 

 

  • Like 3

Share this post


Link to post
Share on other sites
3 minutes ago, TwentySixHundred said:

So with SpiceC the whole program can be written in that C-like syntax or is assembly still needed? What compiler and tools are used? Would Cmake and a standard IDE like Visual Studio Code be fine? I really should have a good read through your tutorials, you probably have already answered these questions 👍

 

It would be written in actual C.  No assembly required.  

 

SpiceC is not useable yet, so there is no tutorial for it.  I do have a few blog posts about it:

Do note that the forum upgrade messed up some of the formatting, and I've not gone back to fix the entries.

 

The C to ARM compiler is Linaro, you'll follow the instructions in Start Here, found in the General forum of the Harmony/Melody Club, to set up a Virtual Machine to use it.

  • Thanks 1

Share this post


Link to post
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.

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