Jump to content
IGNORED

VBLANK - Why this code don't work ? HELP


khryssun

Recommended Posts

Here is a mysterious bug in the VBLANK routine in the program below.

The VBLANK routine burns 36 scanlines instead of 37 !

 

See the comments I put into the code for more explanations,

if you have an answer to this riddle, it's welcome...

This bug drives me crazy ;)

 

 


              processor 6502 

              include "vcs.h" 

              include "macro.h" 

               

              SEG 

              ORG $F000 



Reset 



 ; Clear all 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      ; Set Background

              



 ; 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------------ Start

              LDA  #43          

              STA  TIM64T      



Wait_VBLANK_End

              LDA INTIM                 

              BNE Wait_VBLANK_End       

              STA WSYNC         



 ; Count 37 Scanlines------------ End



 ; In fact the code above (between ---Start & ---End)

 ; burns only 36 Scanlines !!! 

 ; In theory it should burns 37 Scanlines :

 ; 43*64 cycles = 2752 cycles

 ; 2752 cycles / 76 cycles per Scanline = 36.21

 ; Waiting the end of the current scanline drawn  

 ; with STA WSYNC should lead to 37 Scanlines. Right ?

 ; 

 ; In practice it's WRONG : it burns only 36 !?!

 ; Use DOS version of Z26 and press ALT 9 to display

 ; the number of scanlines drawn : 

 ; you'll see 261 instead of 262...

 ;

 ; Replace this part of code with basic following code :

 ;

 ;      LDX 37

 ; Vertical_Blank  

 ;      STA WSYNC 

 ;      DEX 

 ;      BNE Vertical_Blank 

 ;

 ; And it will work (total = 262 scanlines)

 ; Where is the bug in the first code ???

                                                 

              LDA #0 

              STA VBLANK       ; Enable TIA Output

                               

 ; Display 192 Scanlines 

       

              LDY #192         ; 192 Scanlines

Picture                          

              STA WSYNC        

              DEY              ; End of Picture ?

              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 





              ORG $FFFA 



Interrupt_Vectors 



              .word Reset      ; NMI 

              .word Reset      ; RESET 

              .word Reset      ; IRQ 



      END 



Link to comment
Share on other sites

I received this answer from Erik Mooney (on the stella list) that solve the problem.

 

I post it, it may be helpful to other programmers.

 

Many thanks Erik ;-)

 

> - Storing 43 into TIM64T burns 43*64 = 2752 cycles

 

Not exactly.  Based on a quick test in PCAEWin's debugger, when you

store 43 into TIM64T, it *immediately* decrements to 42, then waits

64 cycles before decrementing to 41, 40, and so on. Storing 43 to  

TIM64T

actually makes INTTIM reach 0 at 1+42*64 = 2689 cycles later, or only

35.38 scanlines later.

 

Either store 44 instead of 43 to TIM64T (which is how the sample code

from Nick Bensema's original "How To Draw A Playfield" does it,  

although

the comments are misleading), or use BPL instead of BNE (which will

make it wait an extra 64 cycles until the 0 in INTTIM decrements to  

$FF.)

 

(At least I believe this is the answer, although I could be wrong. :) )

Link to comment
Share on other sites

And here's the explanation of this by Chris Wilkson :

 

(BTW, Erik you were right on how it works.  Here's why.)

 

The timer is in fact decremented immediately.  But that's not what causes the confusion.  Your code checks to see if the timer is zero and branches as soon as this happens.  

But remember that the RIOT's timer ends at the *end* of the zero count, not the beginning.

 

Thanks all ;)

Link to comment
Share on other sites

  • 1 month later...

You could say the timer will read zero during the last 'interval' cycles

 

Note that in case the timer is read after it timed out it will never become zero again and code will loop infinite.

 

An alternative method for reading the timer is by testing the timer interrupt bit which is set when the timer times out. The A register is left untouched and It is supported by z26 too!

 

WSLP

bit INTIM+1 ;test timer interrupt flag

bpl WSLP ;branch timer still counting

sta WSYNC ;complete last scanline

 

Personally I use INTIRQ instead of INTIM+1. But INTIRQ is not yet defined in vcs

Link to comment
Share on other sites

INTIRQ (or any of the IRQ versions of the timer start registers) is completely useless on the 2600 or 7800, because the IRQ output from the 6532 is not connected to anything. (FWIW, only the 7800 even has the 6502 IRQ line connected, and that only comes from the cartridge slot.)

Link to comment
Share on other sites

Note that in case the timer is read after it timed out it will never become zero again and code will loop infinite.  

 

no, it will indeed become zero again: after the timer expired, it continues counting at the system clock's frequency (regardless of which TIMxT register was written to). this allows you (to some extent) to calculate how much time exceeded since the timer expired. doesn't really matter here though, since you don't want to miss the moment the timer expired in your waitvblank loop...

 

Personally I use INTIRQ instead of INTIM+1. But INTIRQ is not yet defined in vcs

 

the register is defined in vcs.h, but it's called TIMINT

Link to comment
Share on other sites

no, it will indeed become zero again: after the timer expired, it continues counting at the system clock's frequency

 

The datasheet say it will continue to count to a maximum of -255T which would be 1 and not zero.

 

This situation should never happen. But when writting new code and you're code locks-up for a unknown reason. This might something to look for. It's just something to keep in mind when programming.

 

the register is defined in vcs.h, but it's called TIMINT

 

Thanks. I was using the wrong vcs.h which didn't include it.

Link to comment
Share on other sites

The datasheet say it will continue to count to a maximum of -255T which would be 1 and not zero.

 

ah, you're right! i should have read it more closely. this explains a certain bug in robotfindskitten 7800 :)

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