English Amiga Board

English Amiga Board (https://eab.abime.net/index.php)
-   Coders. General (https://eab.abime.net/forumdisplay.php?f=37)
-   -   OutRun: I started porting it! (https://eab.abime.net/showthread.php?t=116656)

reassembler 02 February 2024 15:41

Quote:

Originally Posted by jotd (Post 1667220)
If you share that part of the code I may be able to help you out.

No problem. It's just stolen code from the examples with Bare Metal Programming really. Could be a simple problem, just haven't got round to debugging it as its hardware only.

Code:


; In my setup I do:
    move.l        #CIAA_Handler,IRQ2(a0)        ; Set the new CIAA IRQ vector
    move.l        #CIAB_Handler,IRQ6(a0)        ; Set the new CIAB IRQ vector

;--------------------------------------------------------------------------------------------------
; Input & Keyboard Handling Routines
;--------------------------------------------------------------------------------------------------
KeyCode:        dc.b    0
    even

;--------------------------------------------------------------------------------------------------
; This handler is called each time CIAA receives a byte from the keyboard.
; The keyboard needs a 200uS handshake signal. The signal is started by
; this interrupt handler which sets up an alarm on CIAB so that the
; CIAB_handler is called at the end of the handshake period.
;
; Using a value of 4 TOD counts means that the ALARM interrupt fill fire
; at somewhere between 190uS and 254uS for PAL systems.
;--------------------------------------------------------------------------------------------------
CIAA_Handler:   
    MOVEM.L        d0-d1,-(a7)
    MOVE.B        CIAAICR,d0      ; Read source of CIAA IRQ
    BEQ.W          .NoIRQ          ; Not from CIAA!
    BTST          #3,d0          ; Check the SP bit
    BEQ.W          .NotKB          ; Not set? Was not keyboard IRQ

    MOVE.B        CIAASDR,d0      ; Read new value
    ROR.B          d0              ; Set the bits in right order
    NOT.B          d0              ; Account for active-low!
    MOVE.B        d0,KeyCode      ; Store new key code

    BSET.B        #6,CIAACRA      ; Change serial to output
    CLR.B          CIAASDR        ; Set the SP (KDAT) pin low.

    MOVEQ          #0,d1          ; Ensure top bits will be cleared
    MOVE.B        CIABTODHI,d1    ; Reading TODHI latches values
    SWAP          d1              ; Move to bottom byte of top word
    MOVE.B        CIABTODMID,d1  ; Read TOD middle byte
    ASL.W          #8,d1          ; Move to top byte bottom word
    MOVE.B        CIABTODLO,d1    ; Reading TODLO removes latch

    BSET.B        #7,CIABCRB      ; Writing to TOD will set alarm

    ADDQ.L        #4,d1          ; Calculate the number of TOD counts required

    MOVE.B        d1,d0          ; Make copy of the TODLO value
    ASR.W          #8,d1          ; Move TODMID to low byte
    MOVE.B        d1,CIABTODMID  ; Write to TOD
    SWAP          d1              ; Move TODHI to low byte
    MOVE.B        d1,CIABTODHI    ; Write to TOD
    MOVE.B        d0,CIABTODLO    ; Write TODLO to TOD
    MOVE.B        #$84,CIABICR    ; Enable TOD alarm interrupt

.NotKB     
    MOVE.W        #$0008,$DFF000+INTREQ  ; Acknowledge CIAA IRQ

.NoIRQ         
    MOVEM.L        (a7)+,d0-d1
    RTE

;--------------------------------------------------------------------------------------------------
; This handler is called each time the TOD of CIAB reaches the ALARM value.
; When this happens the keyboard handshake on CIAA has finished and the
; keyboard can send the next value.
;--------------------------------------------------------------------------------------------------
CIAB_Handler: 
    MOVEM.L    d0,-(a7)
    MOVE.B      CIABICR,d0              ; Read source of CIAB IRQ
    BEQ.W      .NoIRQ                  ; Not from CIAB!
    BTST        #2,d0                  ; Check the TOD bit
    BEQ.B      .NoIRQ                  ; Not set? Was not TOD counter IRQ

    BCLR.B      #6,CIAACRA              ; Change serial back to input.
    MOVE.B      #$04,CIABICR            ; Disable TOD alarm interrupt

.NoIRQ     
    MOVE.W      #$2000,$DFF000+INTREQ  ; Acknowledge CIAB IRQ
    MOVEM.L    (a7)+,d0
    RTE

; Called from my main loop!
Controls:
    move.b      KeyCode(PC),d0          ; Get the next key code
    beq        .end2                  ; No key pressed
    lea        cpu1_ram,a5

    cmpi.b      #KEY_LEFT,d0
    bne        .key_right
    add.w      #12,car_x(a5)
    bra        .end1
.key_right
    cmpi.b      #KEY_RIGHT,d0
    bne        .key_up
    sub.w      #12,car_x(a5)
    bra        .end1
.key_up
    cmpi.b      #KEY_UP,d0
    bne        .key_down
    move.w      #512,car_inc(a5)
    bra        .end1
.key_down
    cmpi.b      #KEY_DOWN,d0
    bne        .end1
    move.w      #0,car_inc(a5)
.end1
    ; Clear if you don't want the user to be able to hold the key
    ;clr.b      KeyCode
.end2
    rts

Quote:

Originally Posted by jotd (Post 1667220)
That's very difficult to handle that in a smart way, and a lot of pixels are drawn then overwritten... Unless you perform a ton of horrible computations to handle clipping between bobs (argh....)

Tell me about it. Also OutRun draws back to front (i.e. furthest away stuff first) as you'd kind of expect the original programmers to do, given they had some amazing sprite hardware at their disposal!

paraj 02 February 2024 16:06

I assume you have taken over vblank interrupt from system? Otherwise it will reset ciab tod every frame. Also TOD latch doesn't work if ALARM bit is set. There is also TOD alarm bug, but don't think it will affect this case (though I could be wrong).

For figuring out where time is spent use some kind of timer around interesting sections of code. If you have clearly defined high level "sections" e.g. "game logic", "c2p", "sprites" etc. it should be fairly easy to time the easy of the and report numbers on screen (or accumulate time for each time e.g. specific functions are called, and report once per frame). That should help you narrow down where difference vs UAE is (probably something CPU heavy as chipset timing is usually accurate enough).

If you have system available and KS2+ timer.device is fine and straightforward, if system is off CIAB TOD timer is good enough for rough estimates, otherwise just use a normal CIA timer (set reload value to $ffff, START+LOAD <run code> stop timer and read elapsed ticks).

reassembler 02 February 2024 16:20

Quote:

Originally Posted by paraj (Post 1667237)
I assume you have taken over vblank interrupt from system? Otherwise it will reset ciab tod every frame. Also TOD latch doesn't work if ALARM bit is set. There is also TOD alarm bug, but don't think it will affect this case (though I could be wrong).

If you mean, do I have my own VBlank routine running, then yes I do. It doesn't do much at the moment beyond updating the palette caching I mentioned (i.e. it updates colour registers based on what the main loop code previously queued).

Quote:

Originally Posted by paraj (Post 1667237)
If you have system available and KS2+ timer.device is fine and straightforward, if system is off CIAB TOD timer is good enough for rough estimates, otherwise just use a normal CIA timer (set reload value to $ffff, START+LOAD <run code> stop timer and read elapsed ticks).

System is off, so I'll use the CIA timer. It looks pretty straightforward.

jotd 02 February 2024 17:32

The code is very good and avoids the not satisfactory blocking delay loop which steals CPU cycles...

unless interrupt level 6 is not active or overwritten by something else like music player. It's interrupt level 6 which performs the handshake with proper timing. Check if the level 6 interrupt really calls CIAB_Handler

Quote:


Tell me about it. Also OutRun draws back to front (i.e. furthest away stuff first) as you'd kind of expect the original programmers to do, given they had some amazing sprite hardware at their disposal!
They had hardware sprite & zooming. Not the same league... Even the guys at Atari complained of the lack of this hardware when they designed Road Wars

Don_Adan 02 February 2024 17:45

I dont think that using asr.w and asl.w is very good code. only lsr.w lsl.w is good for me for this case. Same for using movem.l d0,-(sp). If no special tricks for CCR handling, this is not good code. Most assemblers with auto optimisations as default will be change this to move.l d0,-(sp). Perhaps at end movem.l will be replaced with move.l too. I dont know if this movem.l is necessary for handling ACK.

reassembler 02 February 2024 18:09

Either way, the expected keyboard behaviour, which works in UAE, as an example:
1/ Hold down left or right cursor. Scroll world left and right until depressed. It's responsive and 100% reliable.

On hardware I get:
1/ Hold down left or right cursor. World scrolls for a bit then stops. Then keypresses are intermittent or most likely not registered at all.

Is it possible that I'm punishing the CPU so much on hardware that something else is grabbing priority from the interrupt handler?! Is that even possible.

I guess some debug code on hardware will once again help. The last thing I expected, out of all the things to not work on hardware, was my borrowed keyboard routine!!

Toni Wilen 02 February 2024 20:05

Recent WinUAE versions log warning message if keyboard handshake is too short. UAE won't care if pulse is too short or too long, too many programs have CPU delay loops and different keyboard models have different handshake pulse requirements which forces UAE to accept everything.

Usual easy check: flash background color in both interrupts routines, do the flashes appear in correct order and in same frame?

btw, there is no need to write anything to CIASDR (Why some examples have it? It has never been needed), setting serial port outmode bit is enough (and is what KS ROM also does)

btw2, clear bit #7 of CIABCRB, at least when exiting your code to not confuse KS ROM routines too much.

btw3, CIAs are slow to access, reading and writing all TOD registers wastes time, it probably is more optiomal to keep static ALARM value and only reset the TOD every time you need to "start" the timer.

jotd 02 February 2024 21:18

interrupt 6 can't be delayed by other ones as it has highest priority. But if you're using move #$2700,SR each time you enter in a VBLANK that could delay (but not mask) cia 6. If you clear interrupt request with 7FFF and not $20 or $10 (relevant interrupt) then you could clear another interrupt request (but CIAs would keep interrupting so it doesn't apply here)

Color flashes are a good method, Toni is right (as always)

reassembler 05 February 2024 00:06

Thanks for your responses. For the keyboard handling, in the end I studied the WHDLoad code and wrote a version based off that.

It's far simpler than what I had before, and works perfectly on hardware.

I didn't get to the bottom of where the previous implementation was failing on hardware. I did try modifying the timing values, but to no avail.

Anyway, here's the working code based off of WHDLoad's Keyboard.s
Code:


move.l        #KeyboardInterrupt,IRQ2(a0)
move.w          #(INTF_SETCLR|INTF_INTEN|INTF_PORTS|INTF_VERTB),INTENA(a5)

...

SetupKeyboard:
        move.b    #142,(CIAATALO)                                        ; init timer-a (~200 µs)
        sf        (CIAATAHI)
        move.b    #$7f,(CIAAICR)                                        ; allow interrupts from the keyboard & timer-a
        move.b    #(CIAICRF_SETCLR|CIAICRF_SP|CIAICRF_TA),(CIAAICR)
        tst.b    (CIAAICR)                                              ; clear all ciaa-interrupt requests
        and.b    #~(CIACRAF_SPMODE),(CIAACRA)                          ; set input mode
        move      #INTF_PORTS,(CHIPBASE+INTREQ)                          ; clear ports interrupt       
        rts

KeyboardInterrupt:
        move.l    d0,-(a7)
        btst      #INTB_PORTS,(CHIPBASE+INTREQR+1)                                        ; check if keyboard has caused interrupt
        beq.b      .end   
        move.b    CIAAICR,d0                                                              ; timer-a
        btst      #CIAICRB_TA,d0
        beq        .cont           
        sf        CIAACRA                                                                ; set input mode (handshake end)
        bra.b      .end
.cont:               
        btst      #CIAICRB_SP,d0
        beq.b      .end
        move.b    CIAASDR,d0                                                              ; read keycode
        move.b    #CIACRAF_SPMODE|CIACRAF_LOAD|CIACRAF_RUNMODE|CIACRAF_START,(CIAACRA)    ; set output mode (handshake start)       
        not.b      d0
        ror.b      #1,d0                                                                  ; calculate rawkeycode
        move.b    d0,KeyCode                                                              ; Store new key code
.end:
        move.w    #INTF_PORTS,(INTREQ+CHIPBASE)
        tst.w      (INTREQR+CHIPBASE)                                                      ; to avoid timing problems on very fast machines we do another custom register access
        move.l    (a7)+,d0
        rte

Other than that I've been doing some general house-keeping. Removing the massive INCBINs for the sprites and other graphics - dynamically allocating memory and loading them from disk instead. The binary is now a manageable size.

Now that I have a working keyboard on hardware, I can start timing various blocks of code.

reassembler 08 February 2024 22:39

1 Attachment(s)
In a somewhat unexciting moment, the stats are in. See attachment.

Left Monitor: Real Amiga
Right Monitor: WinUAE

Big number bad. Small number good.

As you can see my road routine kicks ass because it's highly optimized. The sprite routine provides enough time to make a cuppa whilst it renders. The C2P is slower than I expected, although I dunno what I was expecting.

Inevitably, the sprite routine is mega slow because it does so much heavy lifting. And because I haven't started the second round of optimizations yet.

The good thing is that I can now feel good, slowly shaving numbers of these values as I improve the routines. I can also tweak UAE settings to more closely match performance on hardware potentially.

Other than that, I fixed the sprite layout bug that was annoyingly me tremendously. And got a few later stages of the game rendering for fun.

There won't be anything particularly sexy to report for a while other than numbers decreasing.

gingerbeardman 09 February 2024 00:07

> Other than that, I fixed the sprite layout bug that was annoyingly me tremendously.

You can't say that and not tell us what it was!?!

I look forward to seeing a series of decreasing numbers.

reassembler 09 February 2024 00:10

Quote:

Originally Posted by gingerbeardman (Post 1668576)
> Other than that, I fixed the sprite layout bug that was annoyingly me tremendously.

You can't say that and not tell us what it was!?!

As expected, a totally ridiculous bug of my own making. I wrote a routine that assumed an address register was previously set and it wasn't. Once found, very obvious. But there were approximately 100 obvious places to look before I found that one! Also the routine kind of half worked even though it was writing to the wrong area of memory.

tomcat666 09 February 2024 02:14

To get as close to real Amiga timings you need to tick both Cycle Exact boxes in the chipset tab on winuae. Without that winuae in AGA mode will fly compared to real Amiga, regardless of the accelerator.

reassembler 09 February 2024 08:51

Quote:

Originally Posted by tomcat666 (Post 1668586)
To get as close to real Amiga timings you need to tick both Cycle Exact boxes in the chipset tab on winuae. Without that winuae in AGA mode will fly compared to real Amiga, regardless of the accelerator.

I'm using those settings already on WinUAE. I imagine it's more a case of differences in FastRam timings. It's not like UAE can truly understand every combination of accelerator card that's out there. Either way it's not a problem, I just don't want to be lulled into a false sense of speed by not continuously testing on hardware. :)

aros-sg 09 February 2024 11:19

I wonder how racing games like Outrun would look if they used a Stardust 3d tunnel like fixed perspective + some scrolling/panning to create non-fixed-perspective illusion. Would this be hard to hack into cannonball engine just to see how it looks (basically assume ferrari is always in the middle of the "scene" horizontally and then pan towards were it really is)?

With that theoretically the whole background scenery / the track could be pre calculated or even be only some kind of pre-rendered animation. And you'd only have to handle the dynamic objects (cars) per frame. Yeah, there's still the thing with clipping (theoretically a car sprite could end up between scenery objects like trees) but maybe you could get aways with only some very simple fast/clipping were it's hard to notice if every once and then it is not 100 % correct. And/or have some scenery objects (like bushes between roads) handled like dynamic objects.

Maybe a bit more problematic for Outrun (two roads -> horizontally large "scenes" -> panning) than less complex racing games.

acidbottle 09 February 2024 11:50

well this is shaping up real nicely, so smooth.

is this project in any way in conjunction with the agermose AGA outrun port that is also ongoing? just wondering, not seen it mentioned.

derSammler 09 February 2024 12:09

Quote:

Originally Posted by aros-sg (Post 1668629)
I wonder how racing games like Outrun would look if they used a Stardust 3d tunnel like fixed perspective + some scrolling/panning to create non-fixed-perspective illusion.

Like Mega Race, more or less.

aros-sg 09 February 2024 12:26

Quote:

Originally Posted by derSammler (Post 1668636)
Like Mega Race, more or less.


From videos on YouTube it seems that game misses crucial horizontal scrolling/panning.

khph_re 09 February 2024 16:09

Quote:

Originally Posted by aros-sg (Post 1668629)
I wonder how racing games like Outrun would look if they used a Stardust 3d tunnel like fixed perspective + some scrolling/panning to create non-fixed-perspective illusion.

I've often thought you could render out track frames from wipeout and render them as an animation using this technique.

aros-sg 10 February 2024 08:40

Quote:

Originally Posted by khph_re (Post 1668692)
I've often thought you could render out track frames from wipeout and render them as an animation using this technique.


Something like Wipeout in theory may be better suited for this technique than Outrun (large required horizontal panning/shifting when there are two roads may end up being too much to look good if perspective is fixed), but otoh it seems in Wipeout there are also jumps/vertical movement, so that may not be good too.


All times are GMT +2. The time now is 21:13.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.

Page generated in 0.05785 seconds with 11 queries