Jump to content
IGNORED

Session 14: Playfield Wierdness


Recommended Posts

Ah okay, so there are some syntatic variations.  Some examples above show people using rol directly on variable names (rol PATTERN_SF1), but when I do that, it looks like it starts to rotate, but the screen then goes dark permanently.   I would think this would simply rotate PATTERN one bit to the left (a mod of an earlier example program from the website, only instead of increasing the value by 1, I'm trying to rotate the bits):.

 iny      
	
                cpy TIMETOCHANGE      ; has it reached our "change point"?

                bne notyet             ; no, so branch past
			  
                ldy #1                 ; reset speed count

				rol PATTERN
				
notyet

                lda PATTERN;
				sta PF1;

 

Edited by SavedByZero
Link to comment
Share on other sites

1 hour ago, SavedByZero said:

Ah okay, so there are some syntatic variations.  Some examples above show people using rol directly on variable names (rol PATTERN_SF1), but when I do that, it looks like it starts to rotate, but the screen then goes dark permanently.   I would think this would simply rotate PATTERN one bit to the left (a mod of an earlier example program from the website, only instead of increasing the value by 1, I'm trying to rotate the bits):.


 iny      
	
                cpy TIMETOCHANGE      ; has it reached our "change point"?

                bne notyet             ; no, so branch past
			  
                ldy #1                 ; reset speed count

				rol PATTERN
				
notyet

                lda PATTERN;
				sta PF1;

 


It's not a great idea to use UPPERCASE for variable names.

It is convention to reserve UPPERCASE for ROM registers/names, and CONSTANTS, and use lowercase or camelCase for variables.

For example, in the above snipped I can't tell if you have a bug because TIMETOCHANGE is a constant or a variable. In my code it would be a constant, written like that, and an immediately obvious bug  (cpy #TIMETOCHANGE).
You may like UPPERCASE, and of course you're free to use whatever you want, but conventions are there for a good reason.
It helps others read and understand your code - especially useful if you want help.


As to your problem, "rol" will rotate the bits in a byte, but it shifts the carry bit in to the low bit, too. So after 8 "rol" instructions, if you haven't managed your carry bit properly then you've got a blank byte, which could explain your blackness. You not only need to roll the bits, you also need to have the correct bit ready to put in that low bit (i.e., sitting in the carry).  But, it gets much worse than this. You CANNOT simply roll the bits in playfield bytes and expect things to work, because of the weirdness in the orientation/flipping of the playfield display. PF0 and PF2 are mirrored, PF1 is not. PF0 only uses the top 4 bits, too.  There's a diagram I did somewhere that should explain all this clearly.

 

Link to comment
Share on other sites

Oh yeah, I know I have way more work ahead of me than simply rotating left; this was mainly my introduction to those commands. I was trying to see if that command did what I expected it with PF1, since PF1 is the only playfield register that's remotely straightforward. I'll deal with the others once I know what I'm doing with the commands.  

 

Anyhow, I figured out the blacking out was because I had accidentally erased a line of code that stored a positive value in the accumulator for the first overscan line, sta WSYNC.

 

HOWEVER...I don't have a blank byte after 8 bits, now that I see it working...I have a full one. It starts as 00000001 and ends as 11111111...slowly filling with 1s as it rotates left...which was not what I expected, carry bit or no carry bit.  I would have expected the carry bit to remain 0 as all those initial 0s get rotated through it, no?

 

 

 

Edited by SavedByZero
Link to comment
Share on other sites

5 hours ago, SavedByZero said:

Oh yeah, I know I have way more work ahead of me than simply rotating left; this was mainly my introduction to those commands. I was trying to see if that command did what I expected it with PF1, since PF1 is the only playfield register that's remotely straightforward. I'll deal with the others once I know what I'm doing with the commands.  

 

Anyhow, I figured out the blacking out was because I had accidentally erased a line of code that stored a positive value in the accumulator for the first overscan line, sta WSYNC.

 

HOWEVER...I don't have a blank byte after 8 bits, now that I see it working...I have a full one. It starts as 00000001 and ends as 11111111...slowly filling with 1s as it rotates left...which was not what I expected, carry bit or no carry bit.  I would have expected the carry bit to remain 0 as all those initial 0s get rotated through it, no?

 

 

 

The carry bit is rolled onto D0 (least significant bit), and D& (the most significant bit) is rolled off INTO the carry.

You can't actually roll PF registers - they are write-only. I'm pretty sure.  You roll your ram variables, and then write the result to PF registers.

Edit: don't forget your carry bit is also affected by things such as compares.

 

Edited by Andrew Davie
Link to comment
Share on other sites

11 hours ago, Andrew Davie said:

You can't actually roll PF registers - they are write-only. I'm pretty sure.  You roll your ram variables, and then write the result to PF registers.

 

Ahh, now that would explain what I'm seeing. Thanks. Though oddly enough, rol pattern, followed by lda pattern and sta PF1 still has the same result.

 

BTW, in session 13, you declare a ram variable named PATTERN and later call "inc PATTERN" (so I know it wasn't a constant, I don't think? I know PATTERN referenced an address, but that 'inc' call did change the value at said address. But maybe I'm confusing ram variables with a different kind of declaration) -- I was copying that format when I used it.

 

EDIT^2: Okay, this finally worked:

                lda pattern;
                AND #%10000000
                bne dontclear
                clc;
dontclear
                rol pattern;
                
                
notyet
                lda pattern

So yay, I can now "scroll" the easiest part of the playfield. On to the harder stuff...

 

Edited by SavedByZero
Link to comment
Share on other sites

  • 1 month later...

Onto the "Wall" exercise.  Would anyone see immediately why, when I try to increase the y register or even load a value into it, it seems to be ignored? I would think this code would draw a solid pattern for a while (I initialized the playfields to 8, across the board, earlier in the code) and then only draw side walls.  However, it draws only side walls for the entire frame, as if it's never branching to "Notyet".  Why is it never branching to "Notyet"?

			 ldx #192                
			ldy #0                 ; start y at 0
Picture         
			iny       ;increase 1
            		cpy #50     ;compare y to 50
			bne Notyet  ; if not 50, skip this next part and don't change the playfield yet. 
			ldy #49;   ;once y hits 50, go here and use this to make sure it stays at 50 when it's increased next time. 
			lda #%00010000    ; change playfield to be mostly empty, except for the sides (mirror flag is set)
			sta pattern_pf0;
            		lda #%00000000
			sta pattern_pf1 
			sta pattern_pf2 
Notyet
			lda pattern_pf0;
			sta PF0;
			lda pattern_pf1
			sta PF1;
			sta PF2
			lda #1
			sta WSYNC              ; wait till end of scanline
            dex
            cpx #0	
            bne Picture   ;loop until it's time for the blanking period

 

 

 

Edited by SavedByZero
Link to comment
Share on other sites

7 hours ago, SavedByZero said:

Onto the "Wall" exercise.  Would anyone see immediately why, when I try to increase the y register or even load a value into it, it seems to be ignored? I would think this code would draw a solid pattern for a while (I initialized the playfields to 8, across the board, earlier in the code) and then only draw side walls.  However, it draws only side walls for the entire frame, as if it's never branching to "Notyet".  Why is it never branching to "Notyet"?


			 ldx #192                
			ldy #0                 ; start y at 0
Picture         
			iny       ;increase 1
            		cpy #50     ;compare y to 50
			bne Notyet  ; if not 50, skip this next part and don't change the playfield yet. 
			ldy #49;   ;once y hits 50, go here and use this to make sure it stays at 50 when it's increased next time. 
			lda #%00010000    ; change playfield to be mostly empty, except for the sides (mirror flag is set)
			sta pattern_pf0;
            		lda #%00000000
			sta pattern_pf1 
			sta pattern_pf2 
Notyet
			lda pattern_pf0;
			sta PF0;
			lda pattern_pf1
			sta PF1;
			sta PF2
			lda #1
			sta WSYNC              ; wait till end of scanline
            dex
            cpx #0	
            bne Picture   ;loop until it's time for the blanking period

 

 

 

Not sure yet what your problem is, but a few comments...

Firstly, you don't show the initial values of "pattern_pf0" and "pattern_pf1"

Secondly, this is not C/C++/C#, so you don't need semicolons on the end of lines... that's really just an empty comment you have there :)

WSYNC is a strobe so that means you don't write a value to it, you just write to it. So the "lda #1" before the "sta WSYNC" is superfluous.

When you decrement/increment a register or memory, the flags are set accordingly. So with "dex", when it gets to 0, the Z flag is set.

Thus you do not need the "cpx #0" - that has effectively already been done as far as that particular usage goes. Ditch it.


What I would suggest is using the stella emulator to step through this code.

Quick usage:  load the program, hit the tilde key and you get to the debugger.

type "break Picture" and then "run"

If things go well, you should see the program debugger stopped, and you can then "step" and watch instruction by instruction what's happening, and examine the contents of memory (e.g., "dump pattern_pf0") and see the registers.  Could be useful!

 

Edited by Andrew Davie
Link to comment
Share on other sites

11 hours ago, Andrew Davie said:

 

 

Not sure yet what your problem is, but a few comments...

Firstly, you don't show the initial values of "pattern_pf0" and "pattern_pf1"

Secondly, this is not C/C++/C#, so you don't need semicolons on the end of lines... that's really just an empty comment you have there :)

WSYNC is a strobe so that means you don't write a value to it, you just write to it. So the "lda #1" before the "sta WSYNC" is superfluous.

When you decrement/increment a register or memory, the flags are set accordingly. So with "dex", when it gets to 0, the Z flag is set.

Thus you do not need the "cpx #0" - that has effectively already been done as far as that particular usage goes. Ditch it.


What I would suggest is using the stella emulator to step through this code.

Quick usage:  load the program, hit the tilde key and you get to the debugger.

type "break Picture" and then "run"

If things go well, you should see the program debugger stopped, and you can then "step" and watch instruction by instruction what's happening, and examine the contents of memory (e.g., "dump pattern_pf0") and see the registers.  Could be useful!

 

Force of habit with the darn semicolons. 

Ah, okay -- I assumed that strobing meant any positive value over 0 would set wsync to true.

 

Initial playfield values, above all that in the once-only initialization area:

 lda #%11111111
 sta pattern_pf1          
sta pattern_pf0
sta pattern_pf2

I will definitely play with the debugger -- thanks.

Edited by SavedByZero
Link to comment
Share on other sites

  • 11 months later...

I apologize if this is the wrong thread to ask- but using the standard kernel with both player color kernels in bbasic, and pfcolors, have we figured out a way to remove the "stairstep" effect on the left side of the screen? On RT's page it says this is a known issue when combining the 3 kernels. It's really distracting.

 

Also, question 2. Without using superchip ram, what is the smallest size the playfield blocks can be?
 

Link to comment
Share on other sites

7 hours ago, freshbrood said:

Also, question 2. Without using superchip ram, what is the smallest size the playfield blocks can be?
 

A more generic answer - Bb may be different - but assume you have about 120 bytes you're willing to devote to holding a playfield in RAM. At the very least you need 5 bytes to hold 40 bits (across) of playfield. So, 120/5 = 24 playfield blocks vertical x 40 horizontal.  It's possible to have an asymmetric playfield and don't use the PF0 on either side, and then you have 32 bits of playfield per scanline. That only needs 4 bytes and therefore 120/4 = 30 playfield blocks vertical.

 

Having said that, you'd be hard-pressed to write the rest of your game using the 8 remaining RAM bytes.

 

  • Thanks 1
Link to comment
Share on other sites

7 hours ago, freshbrood said:

I apologize if this is the wrong thread to ask- but using the standard kernel with both player color kernels in bbasic, and pfcolors, have we figured out a way to remove the "stairstep" effect on the left side of the screen? On RT's page it says this is a known issue when combining the 3 kernels. It's really distracting.

 

Also, question 2. Without using superchip ram, what is the smallest size the playfield blocks can be?
 

It depends. You could make the PF blocks 1 scanline high. You could devote just 6 bytes to the Playfield, 3 for PF0, PF1, PF2 for scanline1 (Reflected) and 3 for scanline2 (PF0-PF2). Then you can cycle between the 2 RAM locations when drawing the playfield.

 

You could do this in even less RAM, it is only for example. If you want full control over the Playfield for both left and right side of the screen you need 6 (really 5, since only 4 bits are used in PF0) bytes. You only have 128 bytes available. If you try to make the largest size arena, (160w x 192h) is a common screen resolution for NTSC, (but technically you could draw the screen taller) then you could replicate each pf block of size (4w x 9h) in RAM using 126 bytes, leaving you 2 bytes for other things.

 

Of course, you could use RAM as pointers into the game ROM and use that to draw the screen. That would likely only require two bytes for a pointer into ROM and then copy the ROM data directly to the PF pixel registers.

  • Thanks 1
Link to comment
Share on other sites

6 hours ago, Andrew Davie said:

A more generic answer - Bb may be different - but assume you have about 120 bytes you're willing to devote to holding a playfield in RAM. At the very least you need 5 bytes to hold 40 bits (across) of playfield. So, 120/5 = 24 playfield blocks vertical x 40 horizontal.  It's possible to have an asymmetric playfield and don't use the PF0 on either side, and then you have 32 bits of playfield per scanline. That only needs 4 bytes and therefore 120/4 = 30 playfield blocks vertical.

 

Having said that, you'd be hard-pressed to write the rest of your game using the 8 remaining RAM bytes.

 

I figured. Not sure if you're familiar with the Ninja Kombat port I have in development, but memory space seems crucial to make it playable at all. I can live with chunky blocks. But is there a way to eliminate the waterfall/stairstep at least?

Link to comment
Share on other sites

1 hour ago, freshbrood said:

I figured. Not sure if you're familiar with the Ninja Kombat port I have in development, but memory space seems crucial to make it playable at all. I can live with chunky blocks. But is there a way to eliminate the waterfall/stairstep at least?

What you are seeing may be heavily dependent on the Bb kernel you are using, and so it limited by whatever that is doing.

 

But again, a more generic answer - if you are referring to the HMOVE blanks - the black lines at the start of every line where a HMOVE is being done - yes, it is in fact possible to get rid of these. I believe the solution is to do your HMOVE at exactly cycle 74 on the scanline. Really, these sorts of issues are good reasons to develop your own kernel.  If you're using Bb, perhaps the best bet is to put up with the limitations and concentrate on the gameplay.  Bb is wonderful and designed to pre-package stuff so you can do just that - gameplay - but if you start wanting tweaks and changes to the kernels, then you need to be doing things at a lower level and understand/modify the kernel code.  By the time you're doing that sort of thing, you're not too far from programming in assembler anyway.

  • Like 2
Link to comment
Share on other sites

It's not the HMOVE blanks he's talking about. In the bB standard kernel there's not enough cycles in the blank bit of the scanline, when you enable all of the kernel options he's talking about, so one of colour updates happens during the visible portion of the screen.

 

Freshbrood, this is probably the wrong place to be asking these bB questions, unless you're looking to make your own kernel with priorities that suit you better.

  • Like 2
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...