Jump to content
IGNORED

IRQ SIO trackloader by HARD


Heaven/TQA

Recommended Posts

in the source code archive of HARD I found the SIO IRQ loader of Cool Emotion and Joyride. And in the tools section I got a generic "BSIO" (background sio loader).

 

it installes a IRQ chain (8 levels) for loading via POKEY timers sectors from disc while main CPU remains free and leaves 2 channel for music free...

 

Now before I post the code I run into following issue.

 

I have an NMI at $fffa,$fffb triggering VBI/DLI if wanted while at $fffe,$ffff is the IRQ handler of the loader hooked in.

 

Now the theoretical question (I did not solved it yet):

 

When I place a RMT music replayer in the NMI the SIO IRQ stops to load as soon the music starts to play. Assume that there is no memory conflict... what could be the reason?

 

I know that IRQ have lower priority than NMI of course but what could happen when VBL "overrides" one of the timer IRQ steps?

 

I will post later an example code.

 

(yeah I know trackloaders are out of style but I don't care :D never did one in the past so I need to use one).

 

ps. Oswald of Resource did it the otherway around... using loader in main CPU mode while FX are run in timer IRQ on C64... did someone ever tried that on A8?

 

pss. of course I patched the RMT to use only 2 channels and not touching $d208.

Edited by Heaven/TQA
  • Like 1
Link to comment
Share on other sites

ok... here is one example which works... as it does not use NMI.

 

start the exe file... insert an ATR image and press start...

 

plasma & music start to play while it loads sectors until music buffer gets overwritten...

 

source code should be clear...

 

 

 
;--- Entry points ---;
; org $0800
 
BSIO   JMP INIRQ
       JMP SIO
       JMP SIOVBI
 
;--- Initialize IRQ ---;
 
INIRQ  SEI
       LDA #< IRQ
       STA $fffe
       LDA #> IRQ
       STA $ffff
       CLI
       RTS
 
;--- Interval timer VBI ---;
 
SIOVBI 
BIT TIMEN
       BPL SVBX
       LDA TIMCNT
       ORA TIMCNT+1
       BEQ SVBX
       LDA TIMCNT
       BNE SVB1
       DEC TIMCNT+1
SVB1   DEC TIMCNT
       LDA TIMCNT
       ORA TIMCNT+1
       BNE SVBX
       JMP JTIMER
SVBX  
RTS
 
TIMEN  .byte 0
TIMCNT .word 0
 
;--- Set timer interval ---;
 
SETVBX LDA #0
       STA TIMEN
       STY TIMCNT
       STX TIMCNT+1
       DEC TIMEN
       RTS
 
;--- Interval timer routine ---;
 
JTIMER LDA #ERROR
       STA STATUS
       JMP RETURN
 
;--- IRQ main entry point ---;
 
IRQ    PHA
       LDA IRQST
       AND #$20
       BNE NOSIR
       LDA #$DF
       STA IRQEN
       LDA POKMSK
       STA IRQEN
VSERIN JMP $FFFF
NOSIR  LDA IRQST
       AND #$04
       BNE NOTIM4
       LDA #$FB
       STA IRQEN
       LDA POKMSK
       STA IRQEN
VTIMR4 JMP $FFFF
NOTIM4 LDA IRQST
       AND #$10
       BNE NOODN
       LDA #$EF
       STA IRQEN
       LDA POKMSK
       STA IRQEN
VSEROR JMP $FFFF
NOODN  LDA IRQST
       AND #$08
       BNE NOHAND
       LDA #$F7
       STA IRQEN
       LDA POKMSK
       STA IRQEN
VSEROC JMP TASK3
NOHAND PLA
       RTI
 
;--- Enable timer IRQ ---;
 
TENABL STA VTIMR4+1
       STY VTIMR4+2
       LDA #3
       STA SSKCTL
       STA SKCTL
       LDA DLYLO,X
       STA AUDF3
       LDA DLYHI,X
       STA AUDF4
       LDA #$28
       STA AUDCTL
       LDA POKMSK
       ORA #$04
       STA POKMSK
       STA IRQEN
       RTS
 
DLYLO  .byte $80,$00,$80,$80,$80
DLYHI  .byte $00,$05,$00,$00,$00
 
;--- Enable sending IRQ ---;
 
SENDEN STA VSEROR+1
       STY VSEROR+2
       LDA #$07
       AND SSKCTL
       ORA #$20
       STA SSKCTL
       STA SKCTL
       JSR COMINI
       LDA #$C7
       AND POKMSK
       ORA #$10
       STA POKMSK
       STA IRQEN
       RTS
 
;--- Enable receiving IRQ ---;
 
RECVEN STA VSERIN+1
       STY VSERIN+2
       LDA #$07
       AND SSKCTL
       ORA #$10
       STA SSKCTL
       STA SKCTL
       STA SKRES
       JSR COMINI
       LDA #$C7
       AND POKMSK
       ORA #$20
       STA POKMSK
       STA IRQEN
       RTS
 
;--- Common initialization part ---;
 
COMINI LDA #< B192
       STA AUDF3
       LDA #> B192
       STA AUDF4
       LDA #$28
       STA AUDCTL
       RTS
 
;--- Disable sending & receiving ---;
 
SENDDS LDA POKMSK
       AND #$C3
       STA POKMSK
       STA IRQEN
       LDA #3
       STA SSKCTL
       STA SKCTL
       LDA #$A0
       STA AUDC3
       STA AUDC4
       RTS
 
;--- Main entry point ---;
 
SIO    LDA #0
       STA DSTATS
       LDA #NCOMHI
       STA PBCTL
       JSR SENDDS
       LDA #$FF
       STA TIMEN
 
;--- Some initialisation ---;
 
SIOSUB LDA #< TASK0
       LDY #> TASK0
       LDX #0
       JMP TENABL
 
;--- Task 0: ---;
;  - Setup command frame;
;  - Lower COMMAND line.
 
TASK0  STX TASK0X+1
       STY TASK0Y+1
 
       LDA #$31
       STA COMBUF
       LDA #$52
       STA COMBUF+1
       LDA DAUX1
       STA COMBUF+2
       LDA DAUX2
       STA COMBUF+3
       CLC
       LDA #< COMBUF
       STA BUFRLO
       ADC #4
       STA BFENLO
       LDA #> COMBUF
       STA BUFRHI
       STA BFENHI
 
       LDA #NCOMLO
       STA PBCTL
 
       LDA #< TASK1
       LDY #> TASK1
       LDX #1
       JSR TENABL
 
TASK0X LDX #0
TASK0Y LDY #0
       PLA
       RTI
 
;--- Task 1: ---;
;  - Initiate sending command frame.
 
TASK1  STX TASK1X+1
       STY TASK1Y+1
 
       LDA POKMSK
       AND #$FB
       STA POKMSK
       STA IRQEN
 
       LDA #SUCCES
       STA STATUS
       LDA #< TASK2
       LDY #> TASK2
       JSR SENDEN
       LDY #0
       STY CHKSUM
       STY CHKSNT
       LDA (BUFRLO),Y
       STA SEROUT
       STA CHKSUM
 
TASK1X LDX #0
TASK1Y LDY #0
       PLA
       RTI
 
;--- Task 2: ---;
;  - Output Data Needed ISR
;  - for sending command frame
 
TASK2  TYA
       PHA
 
       INC BUFRLO
       BNE NOWRP0
       INC BUFRHI
NOWRP0 LDA BUFRLO
       CMP BFENLO
       LDA BUFRHI
       SBC BFENHI
       BCC NOTEND
       LDA CHKSNT
       BNE RELONE
       LDA CHKSUM
       STA SEROUT
       LDA #$FF
       STA CHKSNT
       BNE CHKDON
 
RELONE LDA POKMSK
       ORA #$08
       STA POKMSK
       STA IRQEN
       BNE CHKDON
 
NOTEND LDY #0
       LDA (BUFRLO),Y
       STA SEROUT
       CLC
       ADC CHKSUM
       ADC #0
       STA CHKSUM
 
CHKDON PLA
       TAY
       PLA
       RTI
 
;--- Task 3: ---;
;  - Transmit done ISR
;  - for sending command frame
 
TASK3  LDA CHKSNT
       BEQ FOOEY
 
       LDA POKMSK
       AND #$C7
       STA POKMSK
       STA IRQEN
 
       STX TASK3X+1
       STY TASK3Y+1
       LDA #< TASK4
       LDY #> TASK4
       LDX #2
       JSR TENABL
 
TASK3X LDX #0
TASK3Y LDY #0
FOOEY  PLA
       RTI
 
;--- Task 4: ---;
;  - Initiate Wait for ACK
 
TASK4  STX TASK4X+1
       STY TASK4Y+1
 
       LDA POKMSK
       AND #$FB
       STA POKMSK
       STA IRQEN
 
       LDY #< CTIME
       LDX #> CTIME
       JSR SETVBX
       JSR WINIT
       LDA #0
       STA CHKSUM
       STA BUFRFL
       LDA #SUCCES
       STA STATUS
       LDA #< TASK5
       LDY #> TASK5
       JSR RECVEN
 
       LDA #NCOMHI
       STA PBCTL
 
TASK4X LDX #0
TASK4Y LDY #0
       PLA
       RTI
 
;--- Task 5: ---;
;  - Serial Input Ready ISR
;    for reading ACK
;  - Analyse ACK
 
TASK5  STY TASK5Y+1
       STX TASK5X+1
 
       JSR ISRSIR
       BCS TASK51
       JMP TASK5X
 
TASK51 LDY #0
       LDX #0
       JSR SETVBX
       LDA POKMSK
       AND #$DF
       STA POKMSK
       STA IRQEN
 
       JSR CHKACK
       BCC ACKREC
 
       JSR RETURN
 
ACKREC LDY #$C0
       LDX #$01
       JSR SETVBX
       JSR WINIT
       LDA #0
       STA CHKSUM
       STA BUFRFL
       LDA #SUCCES
       STA STATUS
       LDA #< TASK6
       LDY #> TASK6
       JSR RECVEN
 
TASK5X LDX #0
TASK5Y LDY #0
       PLA
       RTI
 
;--- Task 6: ---;
;  - Serial Input Ready ISR
;    for reading COMPL
;  - Analyse COMPL
 
TASK6  STY TASK6Y+1
       STX TASK6X+1
 
       JSR ISRSIR
       BCS TASK61
       JMP TASK6X
 
TASK61 LDA POKMSK
       AND #$DF
       STA POKMSK
       STA IRQEN
 
       JSR CHKACK
       BCS DERR
 
       JSR LDPNTR
       LDA #0
       STA CHKSUM
       STA BUFRFL
       LDA #SUCCES
       STA STATUS
       LDA #< TASK7
       LDY #> TASK7
       JSR RECVEN
 
       LDA #NCOMHI
       STA PBCTL
       BNE TASK6X
 
DERR   JSR RETURN
 
TASK6X LDX #0
TASK6Y LDY #0
       PLA
       RTI
 
;--- Task 7: ---;
;  - Serial Input Ready ISR
;  - for reading sector data
 
TASK7  STY TASK7Y+1
 
       JSR ISRSIR
       BCC TASK7Y
 
       STX TASK7X+1
       LDY #0
       LDX #0
       JSR SETVBX
       LDA POKMSK
       AND #$DF
       STA POKMSK
       STA IRQEN
 
       JSR RETURN
 
TASK7X LDX #0
TASK7Y LDY #0
       PLA
       RTI
 
;--- Return from SIO ---;
 
RETURN JSR SENDDS
       LDY STATUS
       BMI RETX
 
       LDA DBUFLO
       CLC
       ADC #$80
       STA DBUFLO
       BCC RET1
       INC DBUFHI
RET1   LDA DBUFHI
; sta $d01a
       CMP #$D0
       BNE RET2
       LDA #$D8
       STA DBUFHI
RET2   INC DAUX1
       BNE RET3
       INC DAUX2
RET3   LDA DAUX1
       CMP #$68
       BNE RET4
       LDA DAUX2
       CMP #$01
       BNE RET4
       LDA #$6B
       STA DAUX1
RET4   LDA DLSECL
       CMP DAUX1
       LDA DLSECH
       SBC DAUX2
       BCC RETX
 
       JMP SIOSUB
 
RETX   STY DSTATS
       RTS
 
;--- Setup ACK/COMPL buffer ---;
 
WINIT  CLC
       LDA #< TEMP
       STA BUFRLO
       ADC #1
       STA BFENLO
       LDA #> TEMP
       STA BUFRHI
       STA BFENHI
       LDA #$FF
       STA NOCKSM
       RTS
 
;--- Check Acknowledge ---;
 
CHKACK LDA STATUS
       CMP #SUCCES
       BNE BAD
       LDA TEMP
       CMP #ACK
       BEQ GOOD
       CMP #COMPL
       BEQ GOOD
       LDA #ERROR
       STA STATUS
BAD    SEC
       RTS
GOOD   CLC
       RTS
 
;--- Serial Input Ready common ---;
 
ISRSIR LDA SKSTAT
       STA SKRES
       BPL IERR
       AND #$20
       BEQ IERR
       LDA BUFRFL
       BEQ NOTYET
       LDA SERIN
       CMP CHKSUM
       BEQ SRETRN
IERR   LDY #ERROR
       STY STATUS
SRETRN SEC
SUSUAL RTS
 
NOTYET LDA SERIN
       LDY #0
       STA (BUFRLO),Y
       CLC
       ADC CHKSUM
       ADC #0
       STA CHKSUM
       INC BUFRLO
       BNE NTWRP1
       INC BUFRHI
NTWRP1 LDA BUFRHI
       CMP #$D0
       BNE NSKPD0
       LDA #$D8
       STA BUFRHI
NSKPD0 LDA BUFRLO
       CMP BFENLO
       LDA BUFRHI
       SBC BFENHI
       BCC SUSUAL
       LDA NOCKSM
       BEQ GOON
       LDA #0
       STA NOCKSM
       BEQ SRETRN
GOON   LDA #$FF
       STA BUFRFL
       CLC
       BCC SUSUAL
 
;--- Load buffer pointer ---;
 
LDPNTR CLC
       LDA DBUFLO
       STA BUFRLO
       ADC #$80
       STA BFENLO
       LDA DBUFHI
       STA BUFRHI
       ADC #$00
       CMP #$D0
       BNE LDP1
       LDA #$D8
LDP1   STA BFENHI
       RTS

trackloader.zip

  • Like 4
Link to comment
Share on other sites

SIO operations will get data overrun (ie fail) if any NMI runs too long or SEI is used to mask IRQs for too long.

An RMT 2 voice song playback would be sure to do this periodically, 4 voice even more often.

 

The problem with doing stuff by NMI is that you can't just CLI part way through since that can allow IRQs to trigger twice (bad thing).

Since you have main loop CPU control you could just do RMT playback there. Just have the VBI maintain the RTCLOK counter and use the low byte as a compare to see if an update is needed.

 

Alternatively, you could have multiple DLIs about 2-3 scanlines apart. Check in each one if the I flag is set on the saved P entry on the stack (like how the OS Stage 2 VBlank does it) - if it's clear then you can CLI and disable NMIs temporarily then run RMT. Multiple DLIs give any in progress IRQ the chance to finish.

 

Another technique to trigger an IRQ on demand is the SEROC type - though with SIO going on it mightn't be useful.

In the VBlank, enable the SEROC IRQ type then just exit (RTI etc). You then use the SEROC vector to get control. Since you're inside an IRQ handler you know that there aren't other IRQs being serviced and once you clear the SEROC IRQ it's safe to do a CLI and run the RMT player.

Link to comment
Share on other sites

yeah.... i thought of overrun... problem is now... that when putting everything into main-loop like in the first example... I need to keep sure that the rmt is always played at 50hz...

 

now think of an routine that needs more time than 1 frame... it gets more challenging to get the music played back in correct speed... that's what I am struggling right now... HARD mostly inlined the player and took care an FX could be spreaded over frames...

Link to comment
Share on other sites

yeah.... i thought of overrun... problem is now... that when putting everything into main-loop like in the first example... I need to keep sure that the rmt is always played at 50hz...

 

now think of an routine that needs more time than 1 frame... it gets more challenging to get the music played back in correct speed... that's what I am struggling right now... HARD mostly inlined the player and took care an FX could be spreaded over frames...

???

The solution would be to keep the "routine" not to use longer CPU time than 1 frame.

As always. On the C64, particular Rescue on Fractalus, screens get drawn in more than 1 frame. In a weird sight of things, it saves CPU time AND moves things on the screen , suggesting higher framerates to the viewer.

Edited by emkay
Link to comment
Share on other sites

Another idea - buffer the RMT generated audio data, ie 8 AUDC/F values + the AUDCTL value per buffer entry. Maintain a buffer with enough for 10-15 VBlank cycles.

Run RMT in the main loop, the only criteria needs to be if the buffer is full, if full then skip the playback call.

 

Use the VBlank to play back the audio data stored in the buffer. This should be a quick process so shouldn't hurt in progress SIO stuff.

Extra advantage is you should be left with CPU to spare if some effects or other case requires lots of processing intermittently.

Link to comment
Share on other sites

Another idea - buffer the RMT generated audio data, ie 8 AUDC/F values + the AUDCTL value per buffer entry. Maintain a buffer with enough for 10-15 VBlank cycles.

Run RMT in the main loop, the only criteria needs to be if the buffer is full, if full then skip the playback call.

 

Use the VBlank to play back the audio data stored in the buffer. This should be a quick process so shouldn't hurt in progress SIO stuff.

Extra advantage is you should be left with CPU to spare if some effects or other case requires lots of processing intermittently.

 

cool... yeah... never thought of buffering... but 128 bytes should be enough... as 2 channels are possible which would be 4 bytes per frame...

 

Link to comment
Share on other sites

Rybags described the problem... If you manage to have fx plus RMT running plus IRQ sio transfer in less than 1 frame... No problem...

 

If you have a dynamic fx which is not deterministic in cycle usage... Like 3D calculations... And take more time than 1 Frame... You can not run music in that loop because it slows down as well as it does not get triggered to play at the same time...

 

Now Sio is also tricky because you have disk head moving where you gain more cycles as there is no data transmission etc.

 

Putting fx or music into VBI as usual will not work as shown... It will override IRQ for loading....

 

Solutions could be injecting RMT playback several times

In your fx so it gets played back once a frame so you split your fx over frames... But problem see above with non deterministic running time....

 

But ring buffering the music data sounds good!

Link to comment
Share on other sites

If you have a dynamic fx which is not deterministic in cycle usage... Like 3D calculations... And take more time than 1 Frame... You can not run music in that loop because it slows down as well as it does not get triggered to play at the same time...

???

Then split the 3D calculations?

Ring Buffer makes things more fluent, but it costs even more CPU cycles, making "the whole thing" slower.

Link to comment
Share on other sites

I'd recommend chaining the stage 2 VBI off of the end of the IRQ handler. Have all of the IRQ handlers jump to a common RTI instruction, and if stage 1 VBI sees that it has interrupted an IRQ handler (I flag set), patch the RTI instruction to JMP to the stage 2 VBI handler instead. This then allows RMT to run at a software interrupt level higher than the main loop but lower than hardware interrupts.

 

Using the serial output complete IRQ as a software interrupt is another way to do it, but IIRC it does not work in Atari800WinPlus: enabling the SEROC IRQ after the fact won't fire an IRQ.

 

The Joyride loader has some issues with reliability since it does not do retries -- I've had it fail at a red screen on real hardware with a real 1050 drive. The OS retries commands up to 12 times and device errors at least once. Besides transient failures, some SIO2PC adapters actually rely on this for high speed support since the UART on the PC side may only be able to handle one speed at a time.

 

Link to comment
Share on other sites

Wasn't there an SIO loader around that ran from VBlank?

From memory the VBI handled the new command scheduling and end of frame was handled within the IRQs.

That allowed mainline code to run freely, short DLIs. Anything frame time-dependant could either be done in Stage 2 VBlank or waiting on counter/flags.

Link to comment
Share on other sites

I never have seen chained loader in the wild except in 5 demos and the mentioned games (cool emotion, joyride, total daze, energy zine #1 and #2).

 

Energy zine #1 plays 2 channel mpt music plus digi drums while loading the main part. Source code can be found at jaskier website... But it's not IRQ based.

Link to comment
Share on other sites

Retry logic is fairly simple. Where you might have problems which even the OS occasionally gets is with hangups part way through a data frame.

Generally you need timeout for things like missing IRQs for data coming in or out. And of course have the IRQs and other parts of the system to check for stuff like framing errors.

Then you need to know when an operation should be aborted, and you'd want a suitable delay before trying again.

 

e.g. with a read sector operation that has problems halfway through there's no point at all retrying right away as the drive will still be sending data and have no idea there's been a problem - so you want to wait for the amount of time the block of data would have taken to come over before trying again.

  • Like 1
Link to comment
Share on other sites

just for people not knowing what we are talking about:

 

https://youtu.be/p69z3zytU6I?t=3m1s

 

this animation and music is played while next part is being loaded from disc...

 

https://youtu.be/wHNoMSzKqhM?t=15s

 

game being loaded while mother ship is on screen including sounds.

 

https://youtu.be/wUycREr3vs0?t=2m39s

 

streaming data while playing and having sound fx

 

 

music plus zooming while loading...

 

http://jaskier.atari8.info/#

 

in the "old work" section you find "Energy zine"... all relevant loaders... this one is sample playing music pro tracker while loading.

 

 

Eidolon... see the disc activity icon in left corner...

 

 

Ballblazer I guess same... animation while loading...

Edited by Heaven/TQA
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...