Jump to content

SpiceWare

+AtariAge Subscriber
  • Content Count

    16,912
  • Joined

  • Last visited

  • Days Won

    10

Posts posted by SpiceWare


  1. 2 hours ago, pvmpkin said:

    One issue I come up against each time is purely having instructions in the wrong place and getting unexpected results.

     

    One part of PosObject is very time critical and could be thrown off based on where the routine ends up in memory:

    DivideLoop
            sbc #15        ; 2  2 - each time thru this loop takes 5 cycles, which is 
            bcs DivideLoop ; 2  4 - the same amount of time it takes to draw 15 pixels
            eor #7         ; 2  6 - The EOR & ASL statements convert the remainder        
    

     

    The issue is the BCS instruction.  All 6502 branch instructions include this note:

    Quote

    A page boundary crossing occurs when the branch destination is on a different page than the instruction AFTER the branch instruction.

     

    The page is the upper byte of an address.  If you have dasm create a listing you can see the address for the DivideLoop and the EOR instruction.  For this:

        216  f003				   DivideLoop
        217  f003		       e9 0f		      sbc	#15	; 2  2 - each time thru this loop takes 5 cycles, which is
        218  f005		       b0 fc		      bcs	DivideLoop	; 2  4 - the same amount of time it takes to draw 15 pixels
        219  f007		       49 07		      eor	#7	; 2  6 - The EOR & ASL statements convert the remainder
    

    the address of DivideLoop = $F003, the EOR = $F007 so the page for both is $F0.

     

    For this:

        216  f0fc				   DivideLoop
        217  f0fc		       e9 0f		      sbc	#15	; 2  2 - each time thru this loop takes 5 cycles, which is
        218  f0fe		       b0 fc		      bcs	DivideLoop	; 2  4 - the same amount of time it takes to draw 15 pixels
        219  f100		       49 07		      eor	#7	; 2  6 - The EOR & ASL statements convert the remainder
    
    

    the DivideLoop is on page $F0, but the EOR is on page $F1, so there's a 1 cycle penalty. That means the expected 5 cycles is now 6 cycles  each time you go thru the loop, which will cause the objects to have the wrong positions.

     

    If the address of DivideLoop is $xxfc, $xxfd, $xxfe, or $xxff (where xx is any page) then the penalty will occur.

    • Like 1

  2. No.

     

    If you need the X position you need to use a byte of RAM to keep track of it.  Then instead of using HMPx and HMOVE to shift the object left/right you use a subroutine like PosObject to reposition the object on every frame.

     

    Here's the subroutine from Step 4 of my tutorial:

    ;===============================================================================
    ; PosObject
    ;----------
    ; subroutine for setting the X position of any TIA object
    ; when called, set the following registers:
    ;   A - holds the X position of the object
    ;   X - holds which object to position
    ;       0 = player0
    ;       1 = player1
    ;       2 = missile0
    ;       3 = missile1
    ;       4 = ball
    ; the routine will set the coarse X position of the object, as well as the
    ; fine-tune register that will be used when HMOVE is used.
    ;===============================================================================
    PosObject:
            sec
            sta WSYNC
    DivideLoop
            sbc #15        ; 2  2 - each time thru this loop takes 5 cycles, which is 
            bcs DivideLoop ; 2  4 - the same amount of time it takes to draw 15 pixels
            eor #7         ; 2  6 - The EOR & ASL statements convert the remainder
            asl            ; 2  8 - of position/15 to the value needed to fine tune
            asl            ; 2 10 - the X position
            asl            ; 2 12
            asl            ; 2 14
            sta.wx HMP0,X  ; 5 19 - store fine tuning of X
            sta RESP0,X    ; 4 23 - set coarse X position of object
            rts            ; 6 29

     

    Here's how you'd use it to set the positions of the players:

    ;===============================================================================
    ; PositionObjects
    ; --------------
    ; Updates TIA for X position of all objects
    ; Updates Kernel variables for Y position of all objects
    ;===============================================================================
    PositionObjects:
            ldx #1              ; position objects 0-1: player0 and player1
    POloop        
            lda ObjectX,x       ; get the object's X position
            jsr PosObject       ; set coarse X position and fine-tune amount 
            dex                 ; DEcrement X
            bpl POloop          ; Branch PLus so we position all objects
            sta WSYNC           ; wait for end of scanline
            sta HMOVE           ; use fine-tune values to set final X positions

     

    In Step 11 I added the ball object, the missiles are added in Step 12. In Step 11 I talked about how the X values for the ball and missiles are 1 off from the players, and updated the comment for PosObject to reflect this:

    ;===============================================================================
    ; PosObject
    ;----------
    ; subroutine for setting the X position of any TIA object
    ; when called, set the following registers:
    ;   A - holds the X position of the object
    ;   X - holds which object to position
    ;       0 = player0
    ;       1 = player1
    ;       2 = missile0
    ;       3 = missile1
    ;       4 = ball
    ; the routine will set the coarse X position of the object, as well as the
    ; fine-tune register that will be used when HMOVE is used.
    ;
    ; Note: The X position differs based on the object, for player0 and player1
    ;       0 is the leftmost pixel while for missile0, missile1 and ball 1 is
    ;       the leftmost pixel:
    ;           players     - X range is 0-159
    ;           missiles    - X range is 1-160
    ;           ball        - X range is 1-160
    ; Note: Setting players to double or quad size will affect the position of
    ;       the players.
    ;===============================================================================

     

     

     

    • Like 1

  3. After writing my games I tend not to play them often as I get too burned out by all the play-testing that occurs during the development.  

     

    With everything going on with COVID-19 I've needed something to take my mind off things, so dusted off Draconian and have been playing a few rounds each day.  Blasting everything away sure seems to help!

     

    I've specifically been playing the Δ Quadrant Sectors(winners of the level design contest) and have been averaging around 250K for the past week.  Today I did quite a bit better - even managed to make it to Sector 17 which is when the sectors start to repeat, but with a higher difficulty.

     

    IMG_0817.thumb.jpg.65e7a2f8f490043a1e36ac84a9ed49e7.jpg

    • Like 7

  4. I've done some work on Stella, a preliminary fix for the DPC+ jitter issue that occurs in some games due to various versions of the DPC+ drivers. I plan to do some more work on that, but have put it on the back burner as my mind's too distracted.

     

    I don't play my games that often as I get burned out on them during development, but it's been long enough that I've been playing a few rounds of Draconian (port of Bosconian) each day - it's easy to zone out while blasting the enemy.  Specifically I've been playing the Δ Quadrant Sectors, which are the winners of the level design contest.  

     

    While playing I twice managed to take out a formation by blowing up a station as the formation flew over it.  Also saw a rare bug - one of the enemy ships became stationary.

    • Like 4

  5. On 7/10/2019 at 7:22 AM, Marcus Englert said:

    Hi,

    why is Epic Adventure (and EMR II) not running on my Harmony Cartridge (PAL50, newest firmware)? I get the the blue startup circle but this is it....

    Any help much appreciated.

    Thanks

    Marcus

     

    Harmony Encore?  Epic Adventure is using a version of the DPC+ driver that doesn't work correctly on the Encore.  The driver is the first 3K of the ROM, this version has the driver replaced with one that works on the Encore.

     

    epicadv22_encore.bin

     

    • Like 1

  6. 1 hour ago, stephena said:

    @SpiceWare, I just realized that we don't have a ROM properties entry for Stay Frosty 2 in Stella.  As such, we can't make an exception for this game to use Joystick by default.  Did you ever release the ROMs for this game?  If so, please provide them, as well as any info you want in the database.  We're planning a new release by the end of the coming week, so it would be great to get this updated before then.

     

    Here's the ROMs, they were released here for the High Score Club.

     

    SF2_20131217_RC8_NTSC.bin

     

    SF2_20131217_RC8_PAL.bin

     

    Properties:

    "Cart.MD5" "541cac55ebcf7891d9d51c415922303f"
    "Cart.Manufacturer" "SpiceWare - Darrell Spice Jr."
    "Cart.ModelNo" "SW-05"
    "Cart.Name" "Stay Frosty 2"
    "Cart.Note" "AtariAge Holiday Greetings 2014"
    "Cart.Rarity" "Homebrew"
    "Controller.Left" "JOYSTICK"
    "Display.Phosphor" "YES"
    ""
    
    "Cart.MD5" "791c88eca9836af8c34bf32b07cb58a7"
    "Cart.Manufacturer" "SpiceWare - Darrell Spice Jr."
    "Cart.ModelNo" "SW-05"
    "Cart.Name" "Stay Frosty 2 (PAL60)"
    "Cart.Note" "AtariAge Holiday Greetings 2014"
    "Cart.Rarity" "Homebrew"
    "Controller.Left" "JOYSTICK"
    "Display.Format" "PAL60"
    "Display.Phosphor" "YES"
    ""

     

     

    • Thanks 1

  7. With this:

    while(getline (input,line))
    {
      while(line.length() == 0)
      {
        getline (input,line);   //get next line if line is empty (skip spaces between lines)
      }
      lines.push_back(line);
    }

     

    you'll likely have problems if there are empty lines are at the end of the file. I would change it to:

    while(getline (input,line))
    {
      if (line.length() > 0)
        lines.push_back(line);
    }

     

    • Thanks 1

  8. 29 minutes ago, TwentySixHundred said:

    Ok so i have thrown this together quickly for the purpose to test this and have made the program deliberatly only update DFxFRACINC line during the main loop.


    That uses driver 5f80b5a5adbe483addc3f6e6f1b472f8:

    $ dd count=3 bs=1024 if=jitter.bin | md5
    3+0 records in
    3+0 records out
    3072 bytes transferred in 0.000034 secs (90738746 bytes/sec)
    5f80b5a5adbe483addc3f6e6f1b472f8
    

     

    18 minutes ago, SpiceWare said:

    * at the moment Stella correctly emulates 3 of the 4 drivers.

     

     

    At the moment driver 5f80b5a5adbe483addc3f6e6f1b472f8 is the one that Stella does not correctly emulate. With that driver if you leave out the DFxFRACINC then on real hardware the display is stable, but in Stella it jitters.

    • Like 2
    • Thanks 1

  9. 41 minutes ago, TwentySixHundred said:

    Interesting, how would we know exactly what driver is being used?

     

    That's what I'm working on.  I'm currently on step 1:

    1. Find as many examples of DPC+ games as I can. It's time consuming, which is why I asked for help.
    2. The driver is the first 3K of the game, I use the MD5 value of that 3K to identify the driver
    3. Figure out how each driver behaves on real hardware (stable vs jitter, and Encore compatibility).
    4. Update Stella's DPC+ Jitter emulation to match*
    5. Update Stella's debugger* to show the MD5 value and Jitter/Encore status
    6. Work my way thru the private Harmony and Stay Frosty 2 forums to figure out release dates for each version
    7. Update Stella's debugger* to show the release date

     

    * at the moment Stella correctly emulates 3 of the 4 drivers.

     

    * plan to show this info in the Description box of the CartridgeDPC+ tab.

     

    497942994_ScreenShot2020-03-13at10_42_21AM.thumb.png.434e38511220f48fc4ffa5e58b55985c.png

     

    • Like 1

  10. 11 hours ago, TwentySixHundred said:

    Thanks, so i am guessing DF4FRACINC is the only line that needs to be updated every drawscreen. This is interesting because once the colours are pushed to the TIA i would think they only need updating once unless the colour is changed ect? Does this have anything to do with how DPC+ stores the graphics data into RAM?

     

    No, if you're using a version of the DPC+ driver that exhibits the jitter issue then DF0-DF3FRACINC need to be updated as well.  If you're using the multicolor background feature then DF6FRACINC would also need to be updated before each use of drawscreen.

     

    The issue is due to how the fractional data fetchers were implemented in the driver, the specifics of which I'm not familiar with as source for the driver is not available.

    • Like 1

  11. 32 minutes ago, satyrsfaction said:

    ok, let me know if that helped. Thanks!!!

     

    That did it.  

     

    Scanlines aren't consistent when moving from room to room, that can cause jitter on some TVs.  

     

    You can see it in Stella using the debugger.  Move to an exit, such as the one at the bottom then enter Stella's Debugger by pressing the ` key

     

    1402764868_ScreenShot2020-03-12at5_20_41PM.thumb.png.dd22aa90eab22c455509aa5321f34bb3.png

     

    Click the I/O tab, check the DOWN direction for the joystick, then click Frame +1 a few times until you see the Scanline Count change from 262 to something else. 

     

    1621721756_ScreenShot2020-03-12at5_20_55PM.thumb.png.e60b3ef1b41ac0a0a6c3bc8c20e4f14f.png

     

     


  12. On 3/5/2020 at 5:49 PM, satyrsfaction said:

    OK, i put the lines in every time I call drawscreen. Did it help?

     

    On 3/5/2020 at 8:22 PM, TwentySixHundred said:

    Yep that's a huge difference and on the right track, only a very small amount of jitter. It's probably something to do with the collision detection code now 👍

     

    The playfiled no longer jitters, but the colors used to draw the playfield does. Looking at this fetcher 4 is used for playfield colors, so adding DF4FRACINC before the call to drawscreen should fix it:

     DF0FRACINC = 43 : DF1FRACINC = 43 : DF2FRACINC = 43 : DF3FRACINC = 43
     DF4FRACINC = 50
     drawscreen

     

    • Thanks 1

  13. We're trying to get a handle on the screen jitter issue that can occur in some DPC+ games if the Fractional Data Fetcher Increments are not reset on every frame.  There's a number of versions of the DPC+ driver floating around, some of them exhibit jitter while some of them do not.  Stella also selectively emulates the jitter issue, but is not aware of all the versions of the driver so whether a given ROM jitters or not in Stella may not match what's seen on real hardware.

     

    To that end I've written a script file that'll find all the unique DPC+ driver versions when provided a set of ROMs.  So far I've found 4 DPC+ driver versions. What I need now is more DPC+ games to see if I can find any other versions so we can make Stella emulate them correctly.  Please add ROMs or links to DPC+ games in this topic.

     

     

     

     

    If you're curious about this issue you can run these versions of Doom Patrol that have been merged with the 4 DPC+ drivers I've found so far. The hexadecimal numbers in the filenames are the MD5 value of the DPC+ driver used in that ROM.  If you'd like more info about this check my recent blog entry (and the comments) DPC+ Jitter.

     

    This one jitters on real hardware, and in Stella

    DP_17884ec14f9b1d06fe8d617a1fbdcf47.bin

     

     

    This one is stable on real hardware, incorrectly jitters in Stella.

    DP_5f80b5a5adbe483addc3f6e6f1b472f8.bin

     

     

    This one is stable on real hardware, and in Stella.

    This DPC+ driver is also NOT compatible with the Harmony Encore, the other 3 are.

    DP_8dd73b44fd11c488326ce507cbeb19d1.bin

     

     

    This one jitters on real hardware, and in Stella.

    DP_b328dbdf787400c0f0e2b88b425872a5.bin

    • Like 1

  14. If you check the source in Part 2 you'll see I set REFP0 3 times.

    During Vertical Blank for the Sun:

            lda FrostyX
            cmp CelestialX
            bcc .facingLeft
            lda #%00001000
            sta REFP0
    


    After the horizon is drawn for Frosty:

            lda FrostyControl
            lsr
            lsr
            sta REFP0   ; set Frosty facing left or right
    

     

    And before drawing the score:

            lda #0
            sta HMP0        
            sta REFP0       ; reset in case Frosty was facing left
    

     

    For Frosty the left/right is calculated during OverScan and saved in bit 5 of the RAM location labeled FrostyControl (hence the use of LSR, LSR):

    FrostyControl     ds 1 ; 76543210
                           ; 5 = 0 facing right, 1 = facing left
                           ; 43210 - 0 to 31, jump height
    
    OverScanCode
    ...
            lda FrostyControl
            and #%11011111     ; set facing right 
            sta FrostyControl
    ...
            lda FrostyControl
            ora #%00100000     ; set facing left
            sta FrostyControl
    

     

    • Like 1

  15. 41 minutes ago, pvmpkin said:

    Do you have an example of how this is achieved in code, how one kernel has isolated instructions (for example unique REFP0) from another ?


    There's an unfinished series covering the development of Stay Frosty in my blog.  I used to have all my blog entries flagged with categories to make it easy to find things, but sadly the last forum upgrade eliminated categories. But if you go to the overview of my blog and click on page 5 you'll see parts 1 & 2.  On page 4 you'll find parts 3-9. On page 2 you'll find part 10. 

    Part 2 should help with what you're looking for.  At this point in its development you can move Frosty around the screen:

     

    frosty071104_dbg_c6a31540.thumb.png.a5c112f9b51b1f68f25ddf50aca2320a.png

     

    If you use Stella's Developer Key for Fixed Debug Colors mode (COMMAND-COMMA on Mac, ALT-COMMA on Linux & Windows) you can see how the screen is drawn:

    frosty071104_dbg_c6a33c92.thumb.png.8ca6415fa29fcc109444d12924ad1116.png

     

    By the red color you can see the sun, Frosty, and parts of the score & lives are all drawn using player0. The sun moves across the screen and always looks at Frosty, which is done by setting REFP0.  Likewise Frosty faces left/right based on the direction you're moving, once again by setting REFP0.  REFP0 is cleared before drawing the score.

     

    Note that the source doesn't cleanly break apart the kernels. The Horizon Kernel is basically this bit of code:

    .HorizonLoop        
            sta WSYNC
            cpx #8                        ; 2
            bcs .skipHills                ; 2  4
            lda HillColor,x
            sta COLUPF
            lda Hill0,x                   ; 4  8
            sta PF0                       ; 3 11
            lda Hill1,x                   ; 4 15
            sta PF1                       ; 3 18
            lda Hill2,x                   ; 4 22
            sta PF2                       ; 3 25
    .skipHills     
            txa
            sec
            sbc CelestialY
            adc #CELESTIALHEIGHT
            bcc .skipCelestialDraw
            tay
            lda (CelestialImagePtr),y     ; 
    .celestialDraw        
            sta GRP0                      ;
            dex                           ; 2 44
            bpl .HorizonLoop              ; 2 46

     

     

    Once it finishes it does some prep for the game kernel:

            inx
            stx WSYNC
            stx CTRLPF ; repeated playfield for game screen
            stx COLUBK
            stx PF0
            stx PF1
            stx PF2
            lda FrostyControl
            lsr
            lsr
            sta REFP0   ; set Frosty facing left or right
            lda FrostyX
            ldx #0
            jsr PosObject
            
            lda #0
            ldx #148



    The game kernel is this bit:

    .testloop        
            sta WSYNC
            sta GRP0
            sty COLUP0
            cpx FrostyNoseY
            php
            pla
            sta ENAM1
            txa
            sec
            sbc FrostyY
            adc #FROSTYHEIGHT
            bcc .skipDrawTL
            tay
            lda (FrostyImagePtr),y
    .frostyDraw        
            pha
            lda (FrostyColorPtr),y
            tay
            pla
            dex
            bne .testloop
            beq .score
    .skipDrawTL      
            lda #0
            beq .frostyDraw

     

    and so on for the prep for the score and the score kernel.



     

     

    • Like 1

  16. The kernel is the portion of your code that updates TIA in real time to draw the visible portion of the display. Updating TIA to position players during vertical blank is not part of the kernel, though it is prep for the kernel.

     

    The kernel is often broken up into multiple kernels that handle specific parts of the screen. A game like Combat has 2 kernels:

    845610130_Combat(1977)(Atari).thumb.png.b2a55ebcac6921f1db24d069eb47ac49.png

     

    Stay Frosty has multiple kernels:

    2021738249_StayFrosty(SpiceWare).thumb.png.947c8dd15a05078ba3396e1dca53ea71.png

     

    The Game Kernel in Stay Frosty is actually broken up even more with kernels that repeat going down the screen. Besides drawing the screen they also do some prep work:

    • Air Kernel - draws snowman, resets TIA collision registers, repositions player1 (multiple fireballs), draws tops of fireballs
    • Ice Kernel - draws snowman, ice blocks, bottoms of fireballs
    • Platform Kernel - draws snowman, platforms, saves TIA collision registers which get processed during overscan

     


  17. Sure, you write multiple kernels to draw the screen. It's normally easier* to put the score at the bottom for this.  Instead of:

    • Vertical Blank = game logic, position players, etc.
    • Kernel = draw entire screen
    • Overscan = additional game logic

     

    You'd do this:

    • Vertical Blank = game logic, position players, set REFP0, etc. for the game kernel
    • Game Kernel = draw large upper portion of the screen, where the action takes place
    • Prep for Score Kernel = use 2-3 scanlines to position players, set REFP0 etc. for the score kernel
    • Score Kernel = draw small lower portion of the screen, where the score is shown
    • Overscan = additional game logic

     

    * The reason it's easier is the routines to position the players take a variable number of scanlines depending on the X position. That'll cause jitter if done in the middle of the visible portion of the screen. You can work around this, but it complicates things when you're learning the 2600.

    • Like 2

  18. 14 minutes ago, JeffJetton said:

     

    And, as an aside, some computers like the ZX80 and (when in "fast" mode) the ZX81 would routinely drop the video signal logic when it came time for any sort of intensive calculations.

     

    The 40 column display on the C= 128 would blank out if you switched it from 1 to 2 MHz mode. I wrote an IRQ routine that would turn on 1 MHz mode while the visible 200 scanlines were drawn, then turn on 2 MHz mode for the 62 scanlines that made up Overscan and Vertical Blank.  Basically giving you 324 scanlines worth of processing per frame, about a 23% speed boost. It also worked when running C=64 software on a 128.

     

     

    Quote

    Another option is to simply reduce the number of scanlines used for the visual part of the game, rather than get rid of them all together. That would give you more time in the overscan and/or vertical blank for "thinking", at the expense of a more "letterbox" image. Didn't a lot of carts use that trick?

     

    I've not reviewed the games looking for that, but I did point out once that Draconian used fewer scanlines than my other games.

     


  19. 5 hours ago, stephena said:

    The changes you made in that link to CartDPCPlus.cxx are exactly what I was referring to above.  There is an older mask ($F0000) and a newer mask ($F00FF).  If I manually set the code to use the old mask, it works fine.  The default is to use the new mask, of course.

     

    @SpiceWare, where did the idea come from to change this mask?  I always assumed it was because of a change in the DPC+ driver behaviour, but now I'm not sure what the actual logic was???

     

    Don't recall where the idea came from, it was almost 5 years ago.  Change was done to match what was seen on real hardware when the DFxFRACINC registers were not updated in Doom Patrol.  I just finished doing some research that suggests Stella's DPC+ driver could be changed to this as there's actually 2 versions of Epic Adventure, one that runs on the Harmony Encore:

    CartDPCPlus.cxx

    string md5 = MD5::hash(image, 3_KB);
    if(md5 == "8dd73b44fd11c488326ce507cbeb19d1" || md5 == "5f80b5a5adbe483addc3f6e6f1b472f8")
      myFractionalLowMask = 0x0F0000;

     

     

     

    The Encore version uses the same driver that Space Rocks and Stay Frosty 2 use, so maybe it would make better sense to swap the mask values in the header and C files, then check for the DPC+ driver used in Doom Patrol.

    CartDPCPlus.hxx

        uInt32 myFractionalLowMask{0x0F0000};


    CartDPCPlus.cxx

      // DPC+ driver used in Doom Patrol 0.5 exhibits jitter on real hardware if the DFxFRACINC registers are not updated
      if(MD5::hash(image, 3_KB) == "17884ec14f9b1d06fe8d617a1fbdcf47")
        myFractionalLowMask = 0x0F00FF;


    The research is in this blog post:

     

×
×
  • Create New...