Jump to content
IGNORED

Need help in fixing ripple in Dodgeball Kernel


tschak909

Recommended Posts

First thing I see is your cycle counts are wrong - lda (indirect),y take a minimum of 5 cycles, not 4.

LDA (LoaD Accumulator)
 
Affects Flags: S Z
 
MODE           SYNTAX       HEX LEN TIM
Immediate     LDA #$44      $A9  2   2
Zero Page     LDA $44       $A5  2   3
Zero Page,X   LDA $44,X     $B5  2   4
Absolute      LDA $4400     $AD  3   4
Absolute,X    LDA $4400,X   $BD  3   4+
Absolute,Y    LDA $4400,Y   $B9  3   4+
Indirect,X    LDA ($44,X)   $A1  2   6
Indirect,Y    LDA ($44),Y   $B1  2   5+
 
+ add 1 cycle if page boundary crossed




The other thing I see in the listing is this:
   1667  f7f8    PF1_2
   1668  f7f8        ff       .byte.b #%11111111
   1669  f7f9        00       .byte.b #%00000000
   1670  f7fa        00       .byte.b #%00000000
   1671  f7fb        00       .byte.b #%00000000
   1672  f7fc        00       .byte.b #%00000000
   1673  f7fd        e0       .byte.b #%11100000
   1674  f7fe        00       .byte.b #%00000000
   1675  f7ff        00       .byte.b #%00000000
   1676  f800        00       .byte.b #%00000000
   1677  f801        40       .byte.b #%01000000
   1678  f802        40       .byte.b #%01000000
   1679  f803        40       .byte.b #%01000000
   1680  f804        40       .byte.b #%01000000
   1681  f805        40       .byte.b #%01000000
   1682  f806        00       .byte.b #%00000000
   1683  f807        00       .byte.b #%00000000
   1684  f808        00       .byte.b #%00000000
   1685  f809        e0       .byte.b #%11100000
   1686  f80a        00       .byte.b #%00000000
   1687  f80b        00       .byte.b #%00000000
   1688  f80c        00       .byte.b #%00000000
   1689  f80d        00       .byte.b #%00000000
   1690  f80e        ff       .byte.b #%11111111



which is an example of where that + comes into play.

PF1_2 starts at address $F7F8. When you add the value in Y to that, if the high byte of the resulting address is different then you crossed a page boundary. So if Y > 8 the resulting address is $F8xx. $F7 != $F8, so you crossed the page making the lda (indirect),y take 6 cycles instead of 5.

 

It looks like your Y value maxes out at 28, so to prevent the page wrap make sure your data does not occupy the last 28 bytes of a page. Do that by adding align 256 statements in strategic locations such as:

   align 256
PF0_2
   .byte #%11110000
   .byte #%00010000
   .byte #%00010000
   .byte #%00010000
   .byte #%00010000
   .byte #%11110000
...
Link to comment
Share on other sites

These STA PFs don't seem to be where you think they are. They happen in the middle of the scan line:

	LDY TEMP		; 3	--
	STA GRP0		; 3	3
	TXA			; 2	5
	EOR BALLY2		; 3	8
	AND #$FC		; 2	10
	PHP			; 3	13
	LDA (PF0PTR),Y		; 4	17
	STA PF0			; 3	20
	LDA (PF1PTR),Y		; 4	24
	STA PF1			; 3	27
	LDA (PF2PTR),Y		; 4	31
	STA PF2			; 3	34
	INX			; 2	39
	LDA PLAYERY1		; 3	42
	SBC SCANLINE		; 3	45
	AND #$FE		; 2 	47
Link to comment
Share on other sites

These STA PFs don't seem to be where you think they are. They happen in the middle of the scan line:

That's normal - playfield doesn't move so you can update the registers mid-scanline provided you don't do so while they're being shown. In my code I normally put when those updates need to occur so when I'm revising the kernel I can tell right away if there will be problems or not. From Frantic:

KernelLoop:
        ; at this point the registers hold the following:
        ; A - graphics for player 1
        ; Y - enable for missile 1 & PF0 for right side of screen
        ; PF0 and PF1 have already been updated for left side of room
        ; GRP0 (on VDEL) has been preloaded with player 0 graphics
        ; BL (on VDEL) has been preenabled with missile data
        ;                  at cycle 73 
        sta GRP1                ; 3 76/0 - before 22 - also updates GRP0 & BL via VDEL
        lda #<DS_COLUP0         ; 2  2
        sta COLUP0              ; 3  5 - before 22
        lda #<DS_COLUP1         ; 2  7
        sta COLUP1              ; 3 10 - before 22
        sty ENAM1               ; 3 13 - before 22
        lda #<DS_EVENT_M0       ; 2 15 - bit 7 triggers kernel event
        sta ENAM0               ; 3 18 - before 22
        sbmi KernelEvent        ; 2 20 - 3 21 if taken
        SLEEP 3                 ; 3 23
        UPDATE_SPEECH           ; 5 28
        sty PF0                 ; 3 31 - PF0R, 28-49
        lda #<DS_PF2L           ; 2 33
        sta PF2                 ; 3 36 - PF2L, before 38
        ldy DS_PF0R_M1          ; 4 40 - any after sty PF0, loads for next line 
        lda #<DS_PF1R           ; 2 42
        sta PF1                 ; 3 45 - PF1R, 39-54
        lda #<DS_GRP0           ; 2 47 
        sta GRP0                ; 3 50 - any, on VDEL
        lda #<DS_PF2R           ; 2 52
        sta PF2                 ; 3 55 - PF2R, 50-65    
        lda #<DS_PF0L_BL        ; 2 57
        sta PF0                 ; 3 60 - PF0L, after 55
        sta ENABL               ; 3 63 - any, on VDEL
        lda #<DS_PF1L           ; 2 65
        sta PF1                 ; 3 68 - PF1L, 66 - 28
        lda #<DS_GRP1           ; 2 70
        jmp KernelLoop          ; 3 73


  • GRP1 - before cycle 22
  • COLUP0 - before cycle 22
  • COLUP1 - before cycle 22
  • ENAM0 - before cycle 22
  • ENAM1 - before cycle 22
  • PF0 left side - after 55 on prior scanline. also before 22 on current, not specified because 0-22 is already super busy
  • PF1 left side - between 66 (prior scanline) and 28
  • PF2 left side - between 0 and 38
  • PF0 right side - between 28 and 49
  • PF1 right side - between 39 and 54
  • PF2 right side - between 50 and 65
  • GRP0 - any cycle, it's on VDEL so update is delayed until GRP1 is written
  • ENABL - any cycle, it's on VDEL so update is delayed until GRP1 is written
  • Like 1
Link to comment
Share on other sites

So using Stella's debugger this is when the updates are occurring vs when they should occur for a symmetrical reflected playfield:

    LDA (PF0PTR),Y  ; 4 17
    STA PF0         ; 3 20 happens at 36, should be @0-22
    LDA (PF1PTR),Y  ; 4 24
    STA PF1         ; 3 27 happens at 44, should be @70-28
    LDA (PF2PTR),Y  ; 4 31
    STA PF2         ; 3 34 happens at 52, should be @60-38



I might be off a cycle or two on the 70 and 60 updates - those mean PF1 and PF2 can be updated on the prior scanline after TIA drew the second copy.
  • Like 1
Link to comment
Share on other sites

Also, be aware that you should use the BIT.w opcode (or some other inconsequential opcode for a 3-byte instruction) in place of the secondary branches when using (indirect),Y addressing in skipdraw code. Fixed loop:

 

.mainLoop:                      ; X     33
LDX #ENABL ; 2 35
TXS ; 2 37
LDA SCANLINE ; 3 40
TAX ; 2 42
LSR ; 2 44
LSR ; 2 46
LSR ; 2 48
STA TEMP ; 3 51
LDA PLAYERY0 ; 3 54
SBC SCANLINE ; 3 57
AND #$FE ; 2 59
TAY ; 2 61
AND #$F0 ; 2 63
BEQ .doP0 ; 2 65
LDA #$00 ; 2 67
.byte $2C ; 4BIT.w71
.doP0: ; X 66
LDA (PLAYERP0),Y ; 5 71
LDY TEMP ; 3 74
STA GRP0 ; 3 1
TXA ; 2 3
EOR BALLY2 ; 3 6
AND #$FC ; 2 8
PHP ; 3 11
LDA (PF0PTR),Y ; 5 16
STA PF0 ; 3 19
LDA (PF1PTR),Y ; 5 24
STA PF1 ; 3 27
LDA (PF2PTR),Y ; 5 32
STA PF2 ; 3 35
INX ; 2 37
LDA PLAYERY1 ; 3 40
SBC SCANLINE ; 3 43
AND #$FE ; 2 45
TAY ; 2 47
AND #$F0 ; 2 49
BEQ .doP1 ; 2 51
LDA #$00 ; 2 53
.byte $2C ; 4BIT.w57
.doP1: ; X 52
LDA (PLAYERP1),Y ; 5 57
STA WSYNC ; 3 --
STA GRP1 ; 3 3
TXA ; 2 5
EOR BALLY1 ; 3 8
AND #$FC ; 2 10
PHP ; 3 13
TXA ; 2 15
EOR BALLY0 ; 3 18
AND #$FC ; 2 20
PHP ; 3 23
INX ; 2 25
STX SCANLINE ; 3 28
CPX #232 ; 2 30
BCC .mainLoop ; 2
Link to comment
Share on other sites

 

That's normal - playfield doesn't move so you can update the registers mid-scanline provided you don't do so while they're being shown. In my code I normally put when those updates need to occur so when I'm revising the kernel I can tell right away if there will be problems or not.

 

Thanks!

I might not have been clear enough. I meant that he's storing too late, too close to the middle.

Link to comment
Share on other sites

1) know when you can safely update things to prevent shearing, document those cycle times in your source

For moveable objects (players, missiles, ball) you need to update them on or before cycle 22*. A comment as simple as 0-22 will do

For the playfield it depends on whether you're going for a symmetrical or asymmetrical playfield, and if you're using the reflected or repeated layout.

The TIA Timing diagram from Step 3 is arranged for a repeated playfield. I added some blue lines for a reflected playfield:
post-3056-0-25798800-1476195478_thumb.png

Do note that there is a minor delay for the update to occur - looking at the diagram it appears that an asymmetrical reflected playfield would be impossible as you'd have to update PF2 for the right side on cycle 49.3333. However, as seen in Stay Frosty, it works if PF2 is updated on cycle 48.

If you're going for symmetrical (like you are) then you have to update the registers before they get displayed for the left side, and after they've been displayed on the right. If you update them in the middle you get shearing.

 

 

2) always always always keep your kernel's cycle counts up to date so you can make sure #1 happens at the right time

If the cycle counts in your source had been up to date it would have been immediately obvious what was wrong. Instead, I had to build your project then step through the code via Stella to see that the counts were way off.

 

 

* for advanced 1 line kernels like the one in Draconian, you can turn on VDELP0 and VDELBL for the entire display which allows you to update GRP0 and ENABL at any time during the scanline.

  • Like 1
Link to comment
Share on other sites

Use Stella's debugger to help:

 

post-3056-0-40297500-1476201414_thumb.png

 

The oval shows the cycle times for each instruction. If your timing appears to be wrong, use these values to double check the counts you put in your source. If the values are right, then look for page boundary penalty. Those occur for many instructions, even branches.

 

The arrows point to the current scanline cycle count and the instruction that's going to run next (check the PC, also note the green box around the instruction). This means the prior instruction of STA PF0 ended on cycle 36, well after the cycle 22 it needed to be done by.

Link to comment
Share on other sites

Use Stella's debugger to help:

 

attachicon.gifScreen Shot 2016-10-11 at 10.55.15 AM.png

 

The oval shows the cycle times for each instruction. If your timing appears to be wrong, use these values to double check the counts you put in your source. If the values are right, then look for page boundary penalty. Those occur for many instructions, even branches.

 

The arrows point to the current scanline cycle count and the instruction that's going to run next (check the PC, also note the green box around the instruction). This means the prior instruction of STA PF0 ended on cycle 36, well after the cycle 22 it needed to be done by.

 

Yup, I have been using the debugger to help with this, in precisely this capacity.

 

It just takes me forever and a day to slowly work through trying to move instructions around to fit in timing... :) of all of this project, the kernel has proven to be the hardest part of any piece of software I've ever written, due to the timing sensitivity (then again, I never did, for example, disk drive controller firmware, which I imagine is far worse in some respects...)

 

-Thom

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