Jump to content
Andrew Davie

Session 21: Sprites

Recommended Posts

It's time to begin our understanding of sprites.

 

What are sprites? By now, sprites are well-known in the gaming industry. They are small, independantly movable objects which are drawn by hardware anywhere over the top of playfield graphics. The Atari 2600 was the first console to introduce general-purpose sprites - back in the day they were called 'player missile graphics'. It was the Commodore 64 which introduced the term 'sprites', which we know and love.

 

The Atari 2600 has two 'players', two 'missiles' and a 'ball' - all of these are sprites, and each has various parameters which can be adjusted by the programmer (position, size, colour, shape, etc). We're going to concentrate, this session, on the 'players' and how they work.

 

Player graphics have much finer resolution than playfield graphics. Each player is 8 pixels wide, and each pixel in a player is just a single TIA colour-clock in width. In other words, the pixels in player graphics are a quarter of the width of the pixels in playfield graphics. The graphics of each player are controlled by a single 8-bit TIA register. The register for player 0 (the first player) is GRP0 (standing for 'Graphics, Player 0') and the register for the second player is GRP1. When you write data to either of these registers you change the visuals of the relevant player sprite being drawn on the screen.

 

Just like playfield graphics, the player graphics registers only hold a single 'line' of data. If you do not modify the data on-the-fly (that is, changing it every scanline), then the TIA just displays the same data on every scanline. So kernels using sprite graphics typically modify these player graphics registers constantly.

 

Surprisingly, though player sprites can be (effectively) positioned anywhere on the screen, they do NOT have position registers. Most more modern machines (Nintendo, C64, etc) provided an x,y coordinate which was used to position a sprite on the screen. The Atari 2600 is a much more primitive beast.

 

The horizontal position of a player sprite is controlled by writing to a 'reset position' register (RESP0 for sprite 0 and RESP1 for sprite 1). When you write to these registers, you cause the hardware to begin drawing the relevant sprite... immediately! This is very strange and a bit hard to get used to at first. To move a sprite horizontally to any x-position on a scanline, one has to make sure that the RESP0 write happens just before the position on the scanline at which you want the sprite to appear. Since the 6502 is running at 1/3 of the clock speed of the TIA, this makes it incredibly difficult to write to RESP0 at exactly the right time. For every cycle of 6502 time, three pixels (cycles of TIA time) pass. So it's only possible to position sprites (through RESPx writes) with an accuracy of 1 6502 clock period, or in other words three TIA pixels.

 

To facilitate fine-positioning of sprites, the TIA has additional registers which allow the sprite to be adjusted in position by a few pixels. We are not going to cover that this session - but instead we'll have a look at how sprite graphics are written, how the course RESPx registers are used, and how sprite colours are controlled. Fine positioning of sprites is an art in itself, and many solutions have been proposed on the [stella] list. We'll get to that in a session or two, but for now, let's stick with the basics.

 

The sample kernel shows a fully working sprite demo.

 

There are very few additions from our earlier playfield demos...

 



               lda #$56

               sta COLUP0

               lda #$67

               sta COLUP1



 

 

In our initialisation (before the main frame loop) the above code is initialising the colours of the two player sprites. These are random purplish colours. You may also change the colour on-the-fly by rewriting it every scanline. Remember, though - you only have 76 cycles per scanline - so there's only so much you can cram into a single line before you run out of 'space'.

 



MiddleLines     



               SLEEP 20



               sta RESP0



               SLEEP 10

               sta RESP1



               stx GRP0               ; modify sprite 0 shape

               stx GRP1



               sta WSYNC

               inx



               cpx #184

               bne MiddleLines



 

 

The above code sample is the 'guts' of our sprite demo. It doesn't do a lot of new stuff. You should already be familiar with the SLEEP macro - it just causes a delay of a certain number of 6502 cycles. The purpose of the SLEEP macros here is to delay to a position somewhere in the middle of the scanline - you may play with the values and see the effect on the positioning of the sprites.

 

Immediately after each SLEEP, there's a write to RESPx for each of the player sprites. This causes the TIA to begin drawing the appropriate player sprite immediately. And what will it draw?

 

               stx GRP0               ; modify sprite 0 shape

               stx GRP1

 

Since, in this kernel, the x register is counting the scanline number, that is also the value written to both of the graphics registers (GRPx) for the player sprites. So the graphics we see will change on each scanline, and it will represent a visual image of the scanline counter. This should be pretty evident by the attached image.

 

That's pretty much all there is to getting sprites up and running. There are a few interesting things we need to cover in the coming sessions, including sprite size, sprite repeating, priorities, buffered sprite drawing, drawing specific images/shapes and lots of other stuff. But now you have the basics, and you should be able to do some experimenting with what you see here.

 

Exercises

 

[1] Modify the kernel so that the colour of the sprite is changed every scanline. How many cycles does this add to your kernel? How many cycles total is each of your lines taking now?

 

[2] Instead of using the scanline to write the shape of the sprite, load the shape from a table. Can you think how it would be possible to draw (say) a mario-shaped sprite anywhere on the screen? This is tricky, so we'll devote a session or more to vertical positioning.

 

[3] What happens when you use more than 76 cycles on a line - how will this code misbehave?

 

See you next time!

post-214-1061438390_thumb.png

kernel21.zip

Share this post


Link to post
Share on other sites

[1] Modify the kernel so that the colour of the sprite is changed every scanline.  How many cycles does this add to your kernel?  How many cycles total is each of your lines taking now?

You'd need to add "stx COLUP0 and stx COLUP1" to the middle lines

They take 3 cycles each adding 6 cycles to each line.

Each line would then be 55 cycles in length except for the last one

which would be 54 because the branch is not taken ;)

[3] What happens when you use more than 76 cycles on a line - how will this code misbehave?

The screen rolls.

Share this post


Link to post
Share on other sites

More exercises...

 

[4] The picture shows sprites over the 'border' areas at top and bottom, yet the code which draws sprites is only active for the middle section. Why is this happening? How would you prevent it?

 

[5] Move the SLEEP and RESPx code outside the middle loop - place this code BEFORE the loop. What differences would you expect to see? Is the result surprising?

Share this post


Link to post
Share on other sites
[4] The picture shows sprites over the 'border' areas at top and bottom,  

yet the code which draws sprites is only active for the middle section.  

Why is this happening? How would you prevent it?

 

The sprites spill over the border because we forgot to clean the GRP0/1 registers. The TIA gives priority to the sprites so that they are drawn on top of the playfield. I guess we'll learn to play with this as we plow ahead. To fix this we have to clear the GRP registers before drawing the top and bottom playfield bars.

 

 

[5] Move the SLEEP and RESPx code outside the middle loop - place this code BEFORE the  

loop. What differences would you expect to see? Is the result surprising?

 

In my case I moved the SLEEP and RESPx code after the setting of the PFx registers and just before the MiddleLines label. The player graphics shifted a little bit to the right. I guess this happened because some processing time was spent clearing the PFx registers.

 

 

This might be a bit obvious but I am going to ask anyway:

 

Stella Programmer's guide says:  

 

RESP0 (RESP1, RESM0, RESM1, RESBL)

These addresses are used to reset players, missiles and the ball. The object will begin its serial graphics at the time of a horizontal line at which the reset address occurs. No data bits are used

 

So storing a value in RESPx is only to set it off? Can I use any value for this then?

Share this post


Link to post
Share on other sites

[1] Modify the kernel so that the colour of the sprite is changed every scanline.  How many cycles does this add to your kernel?  How many cycles total is each of your lines taking now?

 

It takes 3 cycles per write to a colour register (eg: stx COLUP1), but it takes two or more additional cycles if you want to load a specific colour. The variation in time depends on the addressing mode you use to load the colour (eg: an immediate value = 2 cycles, but loading indirectly through a zero page pointer to a memory location, indexed by the y register, would take 6 cycles!).

 



   lda #34         ; 2

   sta COLUP1      ; 3





   lda (colour),y  ; 6

   sta COLUP1      ; 3



 

 

[2] Instead of using the scanline to write the shape of the sprite, load the shape from a table.  Can you think how it would be possible to draw (say) a mario-shaped sprite anywhere on the screen?  This is tricky, so we'll devote a session or more to vertical positioning.

 

This really is too tricky to answer here. Future sessions will cover this problem thoroughly, as its fundamental to drawing sprites in your game.

 

 

[3] What happens when you use more than 76 cycles on a line - how will this code misbehave?

 

Remember that the TIA and the TV beam are in synch. The timing is such that precisely 76 cycles of 6502 time, or 228 cycles of TIA time, correspond to *exactly* one scanline on the TV. Currently we've been using "sta WSYNC" to synchronise our kernel to the start of every scanline. This isn't necessary IF our code makes sure that our kernel lines take EXACTLY 76 cycles to execute.

 

But since the above code DOES use "sta WSYNC", a 3 cycle instruction, we really only have 73 cycles per line available for other processing. If we exceed these 73 cycles, then that pushes the "sta WSYNC" past the point at which it's on the current scanline and onto the point where it's really on the NEXT scanline. And if it happens on the NEXT scanline, it will operate as expected (and that, as we know, is by halting the 6502 until the start of the NEXT scanline).

 

So essentially, if our code exceeds 76 cycles, then each scanline will actually be two scanlines deep! And instead of sending, say, 262 scanlines per frame, we'd be sending 524. Most TVs cannot cope with this and they will, as noted, 'roll'. I just wanted you to understand WHY.

 

[4] The picture shows sprites over the 'border' areas at top and bottom, yet the code which draws sprites is only active for the middle section. Why is this happening? How would you prevent it?  

 

A good lesson in how the TIA works. The TIA registers hold whatever you put into them, until you next put something in to them. So after our last write to the sprite registers, the TIA keeps displaying the same shape for sprites, on each scanline, until we write again. So what we're really seeing in those border areas is the last write (which is actually at the bottom of the changing shape area of sprites) repeated on the bottom, and then on the top again, until we start writing sprite shapes again.

 

The solution is to write 0 to GRP0 and GRP1 when we've finished drawing our sprites - and, of course, on initialisation of the system.

 

 

[5] Move the SLEEP and RESPx code outside the middle loop - place this code BEFORE the loop. What differences would you expect to see? Is the result surprising?

 

Barring minor timing changes which will cause the positions to shift slightly, the effect I was trying to show was that it is not necessary to rewrite the RESPx registers every scanline. You only need to position your sprites once each, and they will remain in that position until you reposition them. By moving the reposition outside the loop, we've freed up extra cycles in the kernel code for each scanline.

 

Positioning sprites to any arbitrary horizontal position is quite complex, and usually takes at least one whole scanline to do in a generic fashion. This is why games which use multiple sprites rarely allow those sprites to cross over each other, and also the reason why you see distinct 'bands' of sprites in other games - the gaps between the bands is where the horizontal movement code is doing its stuff.

Share this post


Link to post
Share on other sites

I noticed that the ball can be streched out 2, 4 or 8 pixels wide.

Is it possible to stretch it out 4, and then turn it off after three have been drawn, to make it just three wide?

Share this post


Link to post
Share on other sites
Is it possible to stretch it out 4, and then turn it off after three have been drawn, to make it just three wide?

Theoretically maybe, practically no.

Share this post


Link to post
Share on other sites
[4] The picture shows sprites over the 'border' areas at top and bottom, yet the code which draws sprites is only active for the middle section. Why is this happening? How would you prevent it?  

 

The solution is to write 0 to GRP0 and GRP1 when we've finished drawing our sprites - and, of course, on initialisation of the system.

 

A lazier method might be to just set the playfield priority (by setting the 3rd bit of CTRLPF at the same time we choose a mirrored playfield) so that the playfield overlaps the sprites.

 

However, I guess this could cause problems later on with collision detection, since the sprite will constantly be colliding with the playfield, and collisions with other objects could then happen "underneath" the playfield.... but by the time that this is important, I imagine we'll only have GRP0/1 set for short periods anyways, rather than all the time.

 

Barring minor timing changes which will cause the positions to shift slightly, the effect I was trying to show was that it is not necessary to rewrite the RESPx registers every scanline.  You only need to position your sprites once each, and they will remain in that position until you reposition them.

 

Is it possible to do the RESPx writes during VBLANK'ing or overscan? Or even during VSYNC? I don't think I've seen any code that actually does anything during VSYNC... is this a "special" time where the TIA should be left alone or is it fair game?

 

--Zero

Share this post


Link to post
Share on other sites

I hope I'm not jumping ahead, but I'm a little curious about the VDELPx registers... in the Stella guide, it says they are supposed to be used to delay the display of a sprite (or the ball, in the case of VDELBL) by one line... the idea being that you can use this to get scanline resolution even if you're only updating the screen every second line (ie, in order to gain an extra 76 cycles, we only update on odd scanlines, but we need to start a sprite on an even scanline on occaision, so we write a 2 into VDELP0 during VBLANK to force it to draw one scanline later). This makes complete sense to me.

 

My question is about what happens if you toggle this on and off on a per-scanline basis instead of per-frame (which is what I think they had in mind). From the "Vertical Delay" circuit they show on page 30, it seems that there are two seperate registers for each player, with one being for the delayed version of the player (Although I am unclear as to how this second register gets loaded... is the TIA always keeping a delayed copy whether I need it or not?), and a switch saying which should be displayed on that line.

 

So, if I write into GRP0, and then set VDELP0, it will delay that GRP0 data until the next line. However, if I change GRP0 during the horizontal blank, the TIA would then be holding two seperate copies of sprite data (for a single sprite!), and would display whichever was specified by VDELP0. If I time it right, can I change VDELP0 and hit RESP0 to get both bytes of sprite data to appear on the same line in different positions? Could I fiddle with COLUP0 to convince it to draw each in a different color? (ie, instead of trying to hit RESP0, just draw both sprites at the same position with two different colors in order to get 2-color sprites).

 

Even if this is possible, I'm guessing that it would be extremely tricky to time it properly to keep this "double sprite" going for more than one scanline, let alone adding a second sprite or missiles/balls. There's probably much more efficient ways to multiplex sprites, but this one seemed to stand out to me.

 

--Zero

Share this post


Link to post
Share on other sites

Andrew Davie wrote

[2] Instead of using the scanline to write the shape of the sprite, load the shape from a table. Can you think how it would be possible to draw (say) a mario-shaped sprite anywhere on the screen? This is tricky, so we'll devote a session or more to vertical positioning.

 

Well, it's not Mario, but he's famous ;)

 

Player0_Pac_Man.jpg

 



              processor 6502 

              include "vcs.h" 

              include "macro.h" 

               
;-------------------------------------------------------------
;-                    Start of code here                     -
;-------------------------------------------------------------



              SEG 

              ORG $F000 



Reset 


;-------------------------------------------------------------
;-   Clear RAM, TIA registers and Set Stack Pointer to #$FF  -         
;-------------------------------------------------------------

       

              SEI

              CLD	

              LDX #$FF

              TXS

              LDA #0

Clear_Mem

              STA 0,X

              DEX

              BNE Clear_Mem	



              LDA #$80        ; Blue

              STA COLUBK      ; Background Color

              LDA #$1E        ; Yellow     

              STA COLUP0      ; Player 0 Color

 
;-------------------------------------------------------------
;-                    Start to Build Frame                   -
;-------------------------------------------------------------    



Start_Frame 



        

     ; Start of Vertical Blank 



              LDA #2 

              STA VSYNC 



              STA WSYNC 

              STA WSYNC 

              STA WSYNC        ; 3 scanlines of VSYNC



              LDA #0 

              STA VSYNC            

               

      

     ; Start of Vertival Blank

     ; Count 37 Scanlines



              LDA  #43         ; 2 cycles

              STA  TIM64T      ; 3 cycles


;>>>> Free space for code starts here : 2793 cycles Free

              

              

      
;>>>> Free space for code ends here



Wait_VBLANK_End

              LDA INTIM                ; 3 cycles

              BNE Wait_VBLANK_End      ; 3 cycles

              STA WSYNC        ;3 cycles  Total Amount = 19 cycles

                               ; 2812-19 = 2793; 2793/64 = 43.64 (TIM64T)



              LDA #0 

              STA VBLANK      ; Enable TIA Output

                               

     

     ; Display 192 Scanlines with Player 0

       	

              SLEEP 36         ; Player X = +/- Middle Screen

              STA RESP0        ; Set Player 0 (X)  

              LDY #192         ; 192 Scanlines

              LDX #0

Picture  

              CPY #110         ; Position Y reached ?

              BPL No_Drawing   ; No = Continue

              CPX #14          ; 14 Lines of Sprite Datas Drawn ?

              BEQ No_Drawing   ; Yes = Stop

              LDA Sprite_Data,X                        

              STA GRP0                               

              INX

No_Drawing                        

              STA WSYNC        

              DEY              ; 192 Scanlines drawn ?

              BNE Picture      ; No = Continue

               


;-------------------------------------------------------------
;-                       Frame Ends Here                     -
;-------------------------------------------------------------

     

              LDA #%00000010 

              STA VBLANK       ; Disable TIA Output 





     ; 30 Scanlines of Overscan

       

              LDX #30 

Overscan        

              STA WSYNC 

              DEX 

              BNE Overscan 



              JMP Start_Frame  ; Build Next Frame 




;-------------------------------------------------------------
;-                         Demo Datas                        -
;-------------------------------------------------------------



Sprite_Data

              .byte #%00011000

              .byte #%00111100

              .byte #%01111110

              .byte #%01101110

              .byte #%11111111

              .byte #%11111000

              .byte #%11100000

              .byte #%11111000

              .byte #%11111111

              .byte #%01111110

              .byte #%01111110

              .byte #%00111100

              .byte #%00011000

              .byte #%00000000


;-------------------------------------------------------------
;-                     Set Interrup Vectors                  -
;-------------------------------------------------------------





              ORG $FFFA 



Interrupt_Vectors 



              .word Reset      ; NMI 

              .word Reset      ; RESET 

              .word Reset      ; IRQ 



      END 

Share this post


Link to post
Share on other sites

Hi Cybergoth

 

 

I see no NO_ILLEGAL_OPCODES in the khryssun's code. So why I still get the error?

 

And dasm does not show the error when I compile my programs.

 

 

Here the whole message when I compile khryssun's Pac-Man:

unresolved symbol list

NO_ILLEGAL_OPCODES

1 unresolved symbol

Share this post


Link to post
Share on other sites
[1] Modify the kernel so that the colour of the sprite is changed every scanline. How many cycles does this add to your kernel? How many cycles total is each of your lines taking now?

 

post-3513-1125098736_thumb.png

 

Changing the colors every scanline added 6 cycles to my kernel. The lines in my MiddleLines section are taking 51 cycles now.

 

 

[2] Instead of using the scanline to write the shape of the sprite, load the shape from a table. Can you think how it would be possible to draw (say) a mario-shaped sprite anywhere on the screen? This is tricky, so we'll devote a session or more to vertical positioning.

 

post-3513-1125098705_thumb.png

 

[3] What happens when you use more than 76 cycles on a line - how will this code misbehave?

 

Mario and Luigi were stretched, Mario is caught in the left/right border, and the screen flickers.

Edited by Sheldon Sims

Share this post


Link to post
Share on other sites
[4] The picture shows sprites over the 'border' areas at top and bottom, 

yet the code which draws sprites is only active for the middle section. 

Why is this happening? How would you prevent it?

 

 

The sprites spill over the border because we forgot to clean the GRP0/1 registers. The TIA gives priority to the sprites so that they are drawn on top of the playfield. I guess we'll learn to play with this as we plow ahead. To fix this we have to clear the GRP registers before drawing the top and bottom playfield bars.

 

I just added the following code after my MiddleLines section and before my Bottom8Lines section:

 

    ldy #0; load 0 into accumulator
   sty GRP0; clear graphics in sprite 0
   sty GRP1; clear graphics in sprite 1

 

Since the GRP0 and GRP1 registers retain their settings until they are written to again, there is no need to repeat these changes for the Top8Lines section.

 

I guess I could have used lda and sta to avoid the unnecessary use of another register (Y).

 

post-3513-1125101823_thumb.png

 

[5] Move the SLEEP and RESPx code outside the middle loop - place this code BEFORE the  

loop. What differences would you expect to see? Is the result surprising?

 

 

In my case I moved the SLEEP and RESPx code after the setting of the PFx registers and just before the MiddleLines label. The player graphics shifted a little bit to the right. I guess this happened because some processing time was spent clearing the PFx registers.

 

Same here. My graphics just shifted to the right a little with this change.

 

post-3513-1125102541_thumb.png

Edited by Sheldon Sims

Share this post


Link to post
Share on other sites

[1] Modify the kernel so that the colour of the sprite is changed every scanline.

 

I took the exercise literally this time, and made the colors change every scanline.

 

post-3513-0-69713100-1525853036.png

 

 

Share this post


Link to post
Share on other sites
On 8/21/2003 at 4:59 AM, Andrew Davie said:

 

[2] Instead of using the scanline to write the shape of the sprite, load the shape from a table. Can you think how it would be possible to draw (say) a mario-shaped sprite anywhere on the screen? This is tricky, so we'll devote a session or more to vertical positioning.

Hi Folks,

 

Well I've been following this rather excellent series of tutorials (thanks to all involved) and even bought the book!

 

Anyway, I've been working through the second challenge and have hit a bit of an odd bug which I can't seem to fix. My code should display a pacman(ish) figure based on data held in a look-up table. For some vertical positions it works wonderfully like this.

 

Working.thumb.png.5fc6489c48ed88c7d689109b9ff32fa6.png

 

But for others I'm getting a shadowy effect like this...

 

Problem.thumb.png.20182174f3d85a3ebf9c9559af08f904.png

 

Having traced my code with the Stella debugger the problem seems to lie with an unexpected result from the SBC opcode in here...

MiddleLines
                stx COLUP0      ;3 cycles - Player 0 the colour associated with X for this scanline only
                stx COLUPF      ;3 cycles - Do the same for the playfield. Will look like we are on LSD!
                SLEEP 20                  ;20 cycles
                sta RESP0                 ;3 cycles
                txa                       ;2 cycles - Load A with the scanline number
                sec                       ;2 cycles - clear the carry flag
                sbc sprite_y              ;3 cycles Subtract the Y position of the sprite

                bmi NotDrawSprite         ;2/3 cycles - If the result is negative then it is too early to draw the sprite
                cmp #9                    ;2 cycles   - Check if we are past the end of the sprite
                bpl NotDrawSprite         ;2/3 cycles - Yes so too late to draw the sprite
                tay                       ;2 cycles   - Y now = the line of the sprite we want to draw
                lda pacman_sprite,y       ;4 cycles A = the sprite data to show on this scanline
                sta GRP0                  ;3 cycles - And send it to GRP0
NotDrawSprite


                sta WSYNC
                inx

                cpx #184
                bne MiddleLines

I've attached the full asm and bin files in the hope that someone will take pity on a 6502 newbie and explain what the problem is and what I can do about it.

 

Many thanks


Simon

 

 

kernel.bin kernel_q2.asm

Edited by KrazyKattapilla

Share this post


Link to post
Share on other sites

Overflow on subtraction is usually handled by checking if the carry flag is clear.

When you do comparisons (and subtractions) don't use "bmi" and "bpl"

Use "bcc" and "bcs". Given V1 and V2 then if you do "sec / lda V1 / sbc V2" your branching would be...
BCC is the same as "less than" (that is, branch taken if V1 < V2)

BCS is the same as greater than or equal (branch taken if V1 >= V2)
In the case where you have something in accumulator, that's just "V1" in the above checks.

If you do a "cmp" it's the same as a "SEC/SBC" for the point of view of checking.
So, change your "bmi" and "bpl" branches appropriately :)


 cmp #9
 bcs accumulator_greater_or_equal_to_9 ;(A>=9, 9<=A)
 bcc accumulator_less_than_9 ; (A<9, 9>A)

 lda v1
 sec

 sbc v2

 bcc overflow_v2_greater_than_v1 ; (V1<=V2 ... V2>=V1)
 bcs no_overflow_v2_less_than_or_equal_v1 ;(V2<=V1 ... V1>=V2)

A "cmp" is pretty much doing a subtraction without actually doing the subtraction - but it sets the Carry properly based on the result so you can then branch on the carry just as if you had actually done the subtraction



 

Edited by Andrew Davie
  • Like 1

Share this post


Link to post
Share on other sites

Hi Andrew,


Thanks for this - very much appreciated. The move away from BPL and BMI didn't help on this occasion but I now have new toys in my 6502 toolbox so all is good. Out of interest is there a time you would recommend using BPL and BMI or are they best avoided?

 

I think I have tracked down the source of my problem. I've tried to illustrate it below with a couple of screen-grabs from Stella's debugger...

Before.thumb.png.86b8bb7723d38dc678efa2ab420f82cb.png

 

Here you can see the system state immediately before the SBC at instruction F065 is executed. The accumulator holds $14 and ram address 81 holds $09. Also the carry flag is set. So I'd expect the result of the SBC to be that A contains $0B. However...

After.thumb.png.ba4ee43d903a4e4635393d2c091520f6.png

 

What actually happens is that the accumulator contains $05. It's probably a coincidence but if the $14 held in A was treated as decimal 14 then the subtraction would be correct.

 

Interestingly this problem isn't consistent. For example, if I load another Rom first and then my binary then things seem to work happily.


Any ideas on how to push this forward are much appreciated

 

Simon

 

 

 

 

 

kernel_q2b.asm kernel_q2b.bin

Edited by KrazyKattapilla

Share this post


Link to post
Share on other sites
2 hours ago, KrazyKattapilla said:

Out of interest is there a time you would recommend using BPL and BMI or are they best avoided?

They are used in signed comparisons, but I have never ever needed/used signed comparisons, so there's that.

But they are very useful when loading values - the flag is set based on the top bit of whatever you load to A/X/Y.

 

So, "lda playerState / bmi deadPlayer" - quick way to check the top bit is set (or clear, with bpl)

 

Share this post


Link to post
Share on other sites
5 hours ago, Andrew Davie said:

They are used in signed comparisons...

Cheers...

 

5 hours ago, Andrew Davie said:

You're not in decimal mode are you?  You do have "cld" somewhere in your startup...?

Possibly - checking the Stella options I had various settings randomized on startup. Putting the "cld" after the code to clear RAM and the TIA registers seems to have done the trick.

 

Thanks for all your help.

 

Simon

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