Heaven/TQA Posted November 23, 2015 Share Posted November 23, 2015 (edited) 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 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 November 23, 2015 by Heaven/TQA 1 Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted November 23, 2015 Author Share Posted November 23, 2015 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 4 Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted November 23, 2015 Author Share Posted November 23, 2015 IRQ loader is written by Tamas Bene of HARD. 2 Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted November 23, 2015 Author Share Posted November 23, 2015 and if I do this: .proc NMI bit $d40f bpl VBL jmp dummy_dli dliv equ *-2 VBL sta vbl_a+1 stx vbl_x+1 sty vbl_y+1 ; inc cloc jsr rmt_player+3 vbl_a lda #0 vbl_x ldx #0 vbl_y ldy #0 dummy_dli rti .endp SIO loader stops somewhere in the middle... trackloader.xex Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted November 23, 2015 Author Share Posted November 23, 2015 btw... Eidolon, Rescue on Fractalus, 7 Cities of Gold esp. is using custom loader. Quote Link to comment Share on other sites More sharing options...
Rybags Posted November 23, 2015 Share Posted November 23, 2015 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. Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted November 23, 2015 Author Share Posted November 23, 2015 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... Quote Link to comment Share on other sites More sharing options...
emkay Posted November 23, 2015 Share Posted November 23, 2015 (edited) 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 November 23, 2015 by emkay Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted November 23, 2015 Author Share Posted November 23, 2015 emkay... yeah... sure keep everything in 50 fps... but if not... trouble starts Quote Link to comment Share on other sites More sharing options...
Rybags Posted November 23, 2015 Share Posted November 23, 2015 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. Quote Link to comment Share on other sites More sharing options...
emkay Posted November 23, 2015 Share Posted November 23, 2015 emkay... yeah... sure keep everything in 50 fps... but if not... trouble starts What could it be that uses steady 50Hz? for graphics? Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted November 23, 2015 Author Share Posted November 23, 2015 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... Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted November 23, 2015 Author Share Posted November 23, 2015 gfx does not hurt if it slows done 1-2 frames due to loading... but music... human ear detects any cycle slowness... Quote Link to comment Share on other sites More sharing options...
emkay Posted November 23, 2015 Share Posted November 23, 2015 (edited) gfx does not hurt if it slows done 1-2 frames due to loading... but music... human ear detects any cycle slowness...Ofcourse... But what's exactly the problem? Playing Sound at 2 channels and loading stuff via SIO should always work. Edited November 23, 2015 by emkay Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted November 23, 2015 Author Share Posted November 23, 2015 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! Quote Link to comment Share on other sites More sharing options...
emkay Posted November 23, 2015 Share Posted November 23, 2015 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. Quote Link to comment Share on other sites More sharing options...
phaeron Posted November 23, 2015 Share Posted November 23, 2015 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. Quote Link to comment Share on other sites More sharing options...
Rybags Posted November 24, 2015 Share Posted November 24, 2015 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. Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted November 24, 2015 Author Share Posted November 24, 2015 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. Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted November 24, 2015 Author Share Posted November 24, 2015 Phaeron.... Thanks. So basically the RMT player would be in stage 2? And I think of how to handle retries. Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted November 24, 2015 Author Share Posted November 24, 2015 when comparing to the OS routines... why not simply implement the "retry" functions on error, too? http://wiki.strotmann.de/wiki/Wiki.jsp?page=Atari%20800%20ROM%20OS%20Source%20Listing Quote Link to comment Share on other sites More sharing options...
Rybags Posted November 24, 2015 Share Posted November 24, 2015 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. 1 Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted November 24, 2015 Author Share Posted November 24, 2015 (edited) 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 November 24, 2015 by Heaven/TQA Quote Link to comment Share on other sites More sharing options...
xxl Posted November 24, 2015 Share Posted November 24, 2015 (edited) and Mazezam: http://a8.fandal.cz/detail.php?files_id=6638 and DeathChaseXE: http://a8.fandal.cz/detail.php?files_id=6859 or --- opss not IRQ Edited November 24, 2015 by xxl Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted November 24, 2015 Author Share Posted November 24, 2015 (edited) xxl... yeah... I want to use IRQ loader... or how do you use xBIOS to load "in background"? Edited November 24, 2015 by Heaven/TQA Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.