Jump to content
  • entries
    657
  • comments
    2,699
  • views
    899,712

Take 3


SpiceWare

1,222 views

Once again, Frantic is being rebooted :-o

 

While working on Stay Frosty 2, Thomas Jentzsch suggested redoing the kernel to use VDELBL. It puts the ball on vertical delay with updates synced to GRP1. It's a clever idea - but due to how SF2 is designed that would have required me to draw the snowman using player(sprite) 1 instead of player 0, which would cause problems in the horizon due to object priorities:

blogentry-3056-0-26710700-1390242348_thumb.png

 

I thought it could be useful for Frantic and Timmy, as their sprite usage is way more flexible, so I added a note to the top of their source files to look into it when I resumed work on those projects.

 

The kernel after the prior reboot was this:

KernelLoopNoWSYNC:
        ; at this point the registers hold the following:
        ; A - graphics for player 0
        ; X - enable for missile 0
        ; Y - enable for missile 1 & PF0 for right side of screen
        ; PF0 and PF1 have already been updated for left side of room
        ; GRP1 (on VDEL) has been preloaded with player 1 graphics
        ;                  at cycle 76/0
        sta GRP0                ; 3  3 - before 25 - updates GRP1 too via VDEL
        lda #<DS_COLUP0         ; 2  5
        sta COLUP0              ; 3  8 - before 25
        lda #<DS_COLUP1         ; 2 10
        sta COLUP1              ; 3 13 - before 25
        stx ENAM0               ; 3 16 - before 25
        sty ENAM1               ; 3 19 - before 25
        lda #<DS_EVENT_BL       ; 2 21 - bit 7 triggers kernel event
        sta ENABL               ; 3 24 - before 25
TESTB:  bmi KernelEvent         ; 2 26 - 3 27 if taken
HM:     sty PF0                 ; 3 29 - PF0R, 28-49
        lda #<DS_PF2L           ; 2 31
        sta PF2                 ; 3 34 - PF2L, before 38
        ldy DS_PF0R_M1          ; 4 38 - any after sty PF0, loads for next line 
        lda #<DS_PF1R           ; 2 40
        sta PF1                 ; 3 43 - PF1R, 39-54
        lda #<AMPLITUDE         ; 2 45 - any
        sta AUDV0               ; 3 48 - any
        lda #<DS_PF2R           ; 2 50
        sta PF2                 ; 3 53 - PF2R, 50-65
        ldx DS_PF0L_M0          ; 4 57
        stx PF0                 ; 3 60 - PF0L, after 55
        lda #<DS_GRP1           ; 2 62
        sta GRP1                ; 3 65 - any, on VDEL
        lda #<DS_PF1L           ; 2 67
        sta.w PF1               ; 4 71 - PF1L, 66 - 28
        lda #<DS_GRP0           ; 2 73
        jmp KernelLoopNoWSYNC   ; 3 76/0
 

 

An untested rewrite is this:

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 1 graphics
        ; BL (on VDEL) has been preenabled with missile data
        ;                  at cycle 73
        sta GRP1                ; 3 76/0 - before 25 - updates GRP0 & BL via VDEL
        lda #<DS_COLUP0         ; 2  2
        sta COLUP0              ; 3  5 - before 25
        lda #<DS_COLUP1         ; 2  7
        sta COLUP1              ; 3 10 - before 25
        sty ENAM1               ; 3 13 - before 25
        lda #<DS_EVENT_M0       ; 2 15 - bit 7 triggers kernel event
        sta ENAM0               ; 3 18 - before 25
        sbmi KernelEvent        ; 2 20 - 3 21 if taken
        lda #<AMPLITUDE         ; 2 22
        sta AUDV0               ; 3 25
        sty PF0                 ; 3 28 - PF0R, 28-49
        lda #<DS_PF2L           ; 2 30
        sta PF2                 ; 3 33 - PF2L, before 38
        ldy DS_PF0R_M1          ; 4 37 - any after sty PF0, loads for next line
        lda #<DS_PF1R           ; 2 39
        sta PF1                 ; 3 42 - PF1R, 39-54
        lda #<DS_GRP0           ; 2 44
        sta GRP0                ; 3 47 - any, on VDEL
        lda #<DS_PF2R           ; 2 49
        sta PF2                 ; 3 52 - PF2R, 50-65
        lda #<DS_PF0L_BL        ; 2 54
        sta PF0                 ; 3 57 - PF0L, after 55
        sta ENABL               ; 3 60
        SLEEP 3                 ; 3 63
        lda #<DS_PF1L           ; 2 65
        sta PF1                 ; 3 68 - PF1L, 66 - 28
        lda #<DS_GRP1           ; 2 70 - any, on VDEL
        jmp KernelLoop          ; 3 73
 

There's a few differences

  • loop starts at cycle 73 instead of 76/0
  • Updates for GRP0 and GRP1 have been swapped
  • Updates of M0 and BL have been swapped
  • A, instead of X, is used for reading datastream DS_PF0L_BL

 

These changes help us out in a couple ways:

  • the ball object no longer gets sheared (not an issue for Frantic, but could be for future games that utilize this kernel)
  • there's 2 extra free cycles, which will potentially allow me to reduce the number of scanlines needed to reposition the players (currently player 0 takes 6, player 1 takes 5). Fewer scanlines means the players could be reused more frequently (reducing the occurance of flicker) and the 6507 code would be smaller (saving precious ROM).

6 Comments


Recommended Comments

Looks interesting. I believe you could eliminate one of your fast fetch registers, and maybe free up some ram? Here is a mod of the "untested" code you had above. I put stars by the lines of interest, and some definitions below which are probably explain more then I can do in English.

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 1 graphics
        ; BL (on VDEL) has been preenabled with missile data
        ;                  at cycle 73
        sta GRP1                ; 3 76/0 - before 25 - updates GRP0 & BL via VDEL
        lda #<DS_COLUP0         ; 2  2
        sta COLUP0              ; 3  5 - before 25
        lda #<DS_COLUP1         ; 2  7
        sta COLUP1              ; 3 10 - before 25
        sty ENAM1               ; 3 13 - before 25
        dey                     ; 2 15 - bit 7 triggers kernel event              *****   same cycles, one less fast write register
        sty ENAM0               ; 3 18 - before 25                                *****
        sbeq KernelEvent        ; 2 20 - 3 21 if taken                            *****   Y=0, leave loop
        lda #<AMPLITUDE         ; 2 22
        sta AUDV0               ; 3 25
        sty PF0                 ; 3 28 - PF0R, 28-49
        lda #<DS_PF2L           ; 2 30
        sta PF2                 ; 3 33 - PF2L, before 38
        ldy DS_PF0R_M1_M0       ; 4 37 - any after sty PF0, loads for next line   *****   now also holds marker for branch/no branch, and ENAM0 value
        lda #<DS_PF1R           ; 2 39
        sta PF1                 ; 3 42 - PF1R, 39-54
        lda #<DS_GRP0           ; 2 44
        sta GRP0                ; 3 47 - any, on VDEL
        lda #<DS_PF2R           ; 2 49
        sta PF2                 ; 3 52 - PF2R, 50-65
        lda #<DS_PF0L_BL        ; 2 54
        sta PF0                 ; 3 57 - PF0L, after 55
        sta ENABL               ; 3 60
        SLEEP 3                 ; 3 63
        lda #<DS_PF1L           ; 2 65
        sta PF1                 ; 3 68 - PF1L, 66 - 28
        lda #<DS_GRP1           ; 2 70 - any, on VDEL
        jmp KernelLoop          ; 3 73


CONTINUE_LOOP = %1000
EXIT_LOOP     = %0000

;missile select:           dey
M1_OFF_M0_OFF = %001  ;  1 ---> 0
M1_ON__M0_OFF = %010  ;  2 ---> 1
M1_ON__M0_ON  = %011  ;  3 ---> 2
M1_OFF_M0_ON  = %100  ;  4 ---> 3

PF0_FULL = $F0
PF0_NONE = $00

aRamValue1 = ( PF0_FULL | CONTINUE_LOOP | M1_ON__M0_OFF )  ;  %1111 1010  write to PF0, keep looping, and write to M1 and M0

aRamValue2 = ( PF0_NONE | EXIT_LOOP | M1_OFF_M0_OFF )      ;  %0000 0001  exit loop as 1 ---> 0

Link to comment

Clever!

 

I suspect it won't work for this though as the eliminated DS_EVENT_BL does other things that you don't see in this snippet. First its used to select which event was triggered:reposition-0, reposition-1 or kernel-loop-is-done

 

KernelEvent:                    ; 3 21
        ; Y has PF0 for right of screen, needs to be written 28-49 if not end-of-kernel
        ; A holds the Kernel Event 
        ; IF   A{6} = 1 then reposition Player 1
        ; ELIF A{5} = 1 then reposition Player 0
        ; ELSE exit display kernel loop
        asl                     ; 2 23
        sbpl CheckRepo0         ; 2 25
        jmp Reposition1         ; 3 28
        
NotRepo0:                       ; 3 31
        jmp KernelDone          ; 3 34
        
CheckRepo0:                     ; 3 26
        asl                     ; 2 28
        sbpl NotRepo0           ; 2 30
        ; start of repositon player 0 routine
        lda #<DS_PF2L           ; 2 30
        sta PF2                 ; 3 33 - PF2L, before 38         

Then it's used during the reposition event to hold additional values:

Reposition1:                    ;   28        
        ; data stored in streams (relative to trigger index)
        ; DS_GRP1 = must have 0, GPR1 must be udpated due to VDELP0 and VDELBL
        ; DS_EVENT_BL + 0 = reposition trigger
        ; DS_EVENT_BL + 1 = HMP1 & missile 0 enable for scanline event + 1
        ; DS_EVENT_BL + 2 = HMP0 & missile 0 enable for scanline event + 2, HMP0 = $8x so player 0 doesn't move
        ; DS_COLUP1 + 0 = already used, sets color to white for visible Missile1
        ; DS_COLUP1 + 1 = <address for jmp()
        ; DS_COLUP1 + 2 = NUSIZ1
Based on that, the event scanlines would be this:
  • event triggered
  • set HMP1 and address for jmp()
  • set HMP0 (so player 0 doesn't move) and NUSIZ1
  • jmp() for reposition
  • extra scanline to get back in sync with main kernel
So it'd take 5 scanlines to reposition a sprite. I've been rethinking the above and will probably change it to this:

        ; DS_GRP1 = must have 0, GPR1 must be udpated due to VDELP0 and VDELBL
        ; DS_EVENT_BL + 0 = reposition trigger
        ; DS_EVENT_BL + 1 = HMP1 & missile 0 enable for scanline event + 1
        ; DS_COLUP1 + 0 = already used, sets color to white for visible Missile1
        ; DS_COLUP1 + 1 = < address for jmp()
        ; do jump now so RESP is done on EVENT+2, scanline 3 of reposition
        ; DS_EVENT_BL + 2 = HMP1, set to $8x so next repo event doesn't move player 1
        ; DS_COLUP1 + 2 = NUSIZ1
        ; if possible, jump back to kernel loop now
Main change is instead of updating HMP0 it updates HMP1 twice. That lets me strobe RESP1 a scanline sooner and might allow me to jump back to the kernel without needing an extra scan line (because of the strict timing for the HMOVE on cycle 73) to get back in sync with the kernel.
Link to comment
Guest
Add a comment...

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