Sheddy Posted August 8, 2009 Share Posted August 8, 2009 I've been doing some experimenting, but I'm at a loss to understand what's going on here: Try the two programs in the attached zip file on a real XL/XE machine. press the joystick fire button! 1) s15.xex program. This works as expected. Nice colours down the screen and a raygun sound when firing 2) s64.xex program. Same, except the colours glitch and shift when firing. program 1 syncs an IRQ exactly to a scan line using the ~15KHz clock (using 1.79MHz also works). program 2 is using the ~64KHz Pokey clock. This doesn't sync exactly to a scan line (2 cycles out every line) But why is program 2 causing such a big problem? The DLI's are NMI's (Non Maskable) so should always run at the correct time regardless of what the IRQ is doing? They are obviously not though. Note that this behaviour doesn't show up on any emulators I've tried, even those that supposedly have cycle exact POKEY emulation. But it happens on my PAL 800XL and 130XE. I thought I was beginning to understand the POKEY timers, but now I'm not sure Maybe if an IRQ and NMI happen at exactly the same time the NMI doesn't get done? (I'd always heard to the contrary though) Here's the code. Yes, I know it can be made faster, but it is only an example. Unfortunately I have passed the 64KHz code off as a good way to do sample playback before, but it's not much good if it doesn't work on real hardware. ; IRQ sample playback ; NB XL/XE only ATARI=0 .include "equates.asm" ; variables *=$f0 sample .ds 2 sample_nybble .ds 1 sample_value .ds 1 sample_size .ds 1 table_index .ds 1 trigger .ds 1 *=$02e0 .byte <run,>run ; initial run address ; main code *=$2000 run lda #0 sta NMIEN ; turn off NMI's sei sta IRQEN ; disable IRQ's lda #128+32+16+2 ; bit 7 on - ram at $5000, bits 4&5 on - 130XE compatible sta PORTB ; bit 1 on - disable os & rom, bit 0 off - disable basic lda #<nmi ; setup custom NMI handler sta $fffa lda #>nmi sta $fffa+1 lda #<irq ; setup custom IRQ handler sta $fffe lda #>irq sta $fffe+1 lda #<dlist ; set up a display list sta DLISTL lda #>dlist sta DLISTH lda #2+32 ; enable normal width screen+screen dma sta DMACTL lda #128+64 sta NMIEN ; turn on NMI's - DLI's and VBI cli ; enable IRQ's ldx #0 run1 txa asl a sta colour_table,x ; create a table of colours dex bne run1 ; initialize sample play irq play jsr init ; initialize the sample to play wait1 lda TRIG0 ; any other stuff can go here, but... beq wait1 ; just wait until fire button is pressed wait2 lda TRIG0 bne wait2 beq play ; end of main code init sei lda #<sample_start sta sample lda #>sample_start sta sample+1 lda #[>sample_end->sample_start] sta sample_size ; sample size in 256 byte pages lda #0 ;1 ; 0=POKEY 64KHz, 1=15KHz sta AUDCTL lda #15 ;3 ; ~64KHz clock 16 = ~4Khz timer, ~15KHz clock 4 = ~4KHz sta AUDF1 ; in timer 1 lda #$f0 ; test - no polycounters + volume only sta AUDC1 lda #1 sta IRQEN ; enable timer 1 lda #0 sta sample_nybble ; initialize nybble ldx #$f8 stx sample_value ; an initial sample value (with no polycounters + volume only bit) lda #$70 wait3 cmp VCOUNT bne wait3 ; sync to a scanline lda #0 sta SKCTL lda #3 sta SKCTL ; test - reset pokey and polycounters sta STIMER ; start timers cli rts ; IRQ irq pha ; save a lda sample_value sta AUDC1 ; play sample ASAP to minimise DMA lag tya pha ; save y ldy #0 sty IRQEN ; reset interrupt lda #1 sta IRQEN ; re-enable only timer 1 eor sample_nybble ; switch between 0 and 1 (right and left nybble) sta sample_nybble beq irq1 lda (sample),y lsr a lsr a lsr a lsr a ; left nybble of sample bpl irq2 ; always branch irq1 lda (sample),y and #$0f ; right nybble of sample inc sample ; next lo byte of sample bne irq2 inc sample+1 ; next hi byte of sample dec sample_size ; check if at end of sample bne irq2 ; branch if not tya sta IRQEN ; end of sample - reset interrupt beq irq3 ; always branch irq2 ora #$f0 ; no polycounters+volume only bit irq3 sta sample_value ; save sample to play next irq pla tay pla ; restore y and a rti ; NMI nmi pha bit NMIST ; check for cause of interrupt bvs vbi ; branch if a VBI ; DLI dli txa pha ldx table_index lda colour_table,x sta WSYNC sta COLBK inc table_index pla tax pla rti ; VBI ; critical vbi vbi sta NMIRES ; reset interrupt txa pha ; save x tya pha lda #0 sta table_index ; reset table index sta COLBK ; tsx ; lda $104,x ; and #$04 ; bne vbiexit ; exit VBI if VBI interrupted an IRQ ; cli ; allow IRQ to interrupt from now on ; deferred VBI here vbiexit pla tay pla tax pla ; restore x and a rti ; display list with some random DLIS *=$2400 dlist .byte $70,$70,$70 .byte $8D+$40,<screen_start,>screen_start .byte $8D,$D,$8D,$8D,$8D,$8D,$8D,$8D,$8D,$8D .byte $8D,$8D,$8D,$8D,$8D,$8D,$8D,$8D,$8D,$8D .byte $8D,$D,$D,$8D,$8D,$D,$8D,$8D,$8D,$8D .byte $8D,$D,$8D,$8D,$8D,$8D,$8D,$8D,$8D,$8D .byte $8D,$D,$8D,$8D,$8D,$8D,$D,$8D,$8D,$8D .byte $8D,$D,$8D,$D,$8D,$8D,$D,$8D,$8D,$8D .byte $8D,$D,$8D,$8D,$8D,$D,$8D,$8D,$8D,$8D .byte $8D,$D,$8D,$8D,$D,$8D,$8D,$8D,$8D,$8D .byte $8D,$D,$8D,$D,$8D,$8D,$8D,$8D,$8D,$8D .byte $8D,$D,$D,$D,$D .byte $41,<dlist,>dlist ; sample data *=$2800 ; NB start on a 256 byte page boundary ; sample will stop playing at nearest page boundary if not a multiple of 256 bytes sample_start .incbin "raygun44.raw" ; 4KHz, 4-bit raw sample data sample_end ; screen data *=$8000 screen_start ; colour table *=$9000 colour_table samples.zip Quote Link to comment Share on other sites More sharing options...
Rybags Posted August 8, 2009 Share Posted August 8, 2009 In another thread, atariksi claimed you could mask System Reset (NMI) on 400/800 - I thought it was impossible without resorting to crashing the CPU but it isn't. I think it comes down to a couple of things: - Antic only holds the NMI line at (low) activate level for a certain period. If you can distract the CPU long enough, it can be missed. - IIRC, the 6502 itself has a "bug" such that if an NMI and IRQ occur at the same time, the NMI can actually be ignored. IRQs are level triggered, ie IRQ sources tend to hold the IRQ line low until the CPU services the interrupt and takes action to disable it. NMIs are edge triggered, ie they rely on the CPU acting upon a high->low transition of the NMI input. For example, on the C64 there's the old trick of masking out the RESTORE key. Normally it's not maskable in any way, but it can be disabled by triggering a Timer NMI, and having a custom routine that does an RTI without resetting the NMI status on the CIA. I haven't tried your program, will see if I can give it a go later. For your scanline exact IRQ, it might be desirable to set it up to occur at a different time so that it doesn't clash with DLIs. Quote Link to comment Share on other sites More sharing options...
Bryan Posted August 8, 2009 Share Posted August 8, 2009 Is there any chance that the IRQ service is introducing a longer delay than the usual instructions that are running? Here's the internals of an IRQ request: A sequential IRQ/DLI can take up to 13 cycles to get to the first instruction of the DLI. Is that more latency than your code is designed to deal with? Quote Link to comment Share on other sites More sharing options...
Rybags Posted August 8, 2009 Share Posted August 8, 2009 lda #$70 wait3 cmp VCOUNT bne wait3; sync to a scanline lda #0 sta SKCTL lda #3 sta SKCTL; test - reset pokey and polycounters sta STIMER That's not an exact align there for the timer, it could drift a couple of cycles either way. I'd change it a bit. Put the reset SKCTL and poly stuff before the wait loop. Do a STA WSYNC immediately before the STA STIMER. That should make the IRQ occur earlier in the scanline, might be enough to stop if interfering with the NMIs. If that don't work, I'd try different delay loops after the WSYNC until you hit the sweet spot. Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted August 8, 2009 Share Posted August 8, 2009 Sheddy... your post came right in time... Rybags/Miker know why... Quote Link to comment Share on other sites More sharing options...
atariksi Posted August 8, 2009 Share Posted August 8, 2009 ...sta PORTB ; bit 1 on - disable os & rom, bit 0 off - disable basic ... Your "rem" statement is wrong. That may explain the problem. >program 1 syncs an IRQ exactly to a scan line using the ~15KHz clock (using 1.79MHz also works). >program 2 is using the ~64KHz Pokey clock. This doesn't sync exactly to a scan line (2 cycles out every line) 64Khz should be out of sync since clocks are as follows: 1.789773 is color burst/2. 15.7Khz is exactly 1.789773/114. 64Khz is exactly 1.789773/28 (they would have to divide by 28.5 to get it divide evenly into scanline cycles). >But why is program 2 causing such a big problem? The DLI's are NMI's (Non Maskable) so should always run at the correct time regardless of what the IRQ is doing? They are obviously not though. Note that this behaviour doesn't show up on any emulators I've tried, even those that supposedly have cycle exact POKEY emulation. But it happens on my PAL 800XL and 130XE. I thought I was beginning to understand the POKEY timers, but now I'm not sure Actually, even at 1.79Mhz it should NOT work (cause glitches). You may just have tried a case where it was phase shifted enough to not cause conflict. Quote Link to comment Share on other sites More sharing options...
atariksi Posted August 8, 2009 Share Posted August 8, 2009 lda #$70 wait3 cmp VCOUNT bne wait3; sync to a scanline lda #0 sta SKCTL lda #3 sta SKCTL; test - reset pokey and polycounters sta STIMER That's not an exact align there for the timer, it could drift a couple of cycles either way. I'd change it a bit. Put the reset SKCTL and poly stuff before the wait loop. Do a STA WSYNC immediately before the STA STIMER. That should make the IRQ occur earlier in the scanline, might be enough to stop if interfering with the NMIs. If that don't work, I'd try different delay loops after the WSYNC until you hit the sweet spot. Sta STIMER will reset timers to zero for IRQ; what is initializing SKCTL accomplishing? True, he should base things on WSYNC so he knows the EXACT cycle when things start but his problem is the 64Khz mode since that's misaligned with scanline rate anyway. He needs to use some divisor (prime # or something) so that DLI triggerring and IRQ triggerring cycles never overlap assuming that's the entire issue here. Quote Link to comment Share on other sites More sharing options...
Rybags Posted August 8, 2009 Share Posted August 8, 2009 I wouldn't worry about SKCTL... maybe he just wants a known sequence of Random numbers for whatever reason. I'm going to have to play around with some of this stuff... although I suspect our answer lies in how the 6502 does Interrupt processing, I'm sure there's a known issue where it can ignore an NMI under certain circumstances. Quote Link to comment Share on other sites More sharing options...
Sheddy Posted August 8, 2009 Author Share Posted August 8, 2009 (edited) lda #$70 wait3 cmp VCOUNT bne wait3; sync to a scanline lda #0 sta SKCTL lda #3 sta SKCTL; test - reset pokey and polycounters sta STIMER That's not an exact align there for the timer, it could drift a couple of cycles either way. I'd change it a bit. Put the reset SKCTL and poly stuff before the wait loop. Do a STA WSYNC immediately before the STA STIMER. That should make the IRQ occur earlier in the scanline, might be enough to stop if interfering with the NMIs. If that don't work, I'd try different delay loops after the WSYNC until you hit the sweet spot. Sta STIMER will reset timers to zero for IRQ; what is initializing SKCTL accomplishing? True, he should base things on WSYNC so he knows the EXACT cycle when things start but his problem is the 64Khz mode since that's misaligned with scanline rate anyway. He needs to use some divisor (prime # or something) so that DLI triggerring and IRQ triggerring cycles never overlap assuming that's the entire issue here. yes, sorry, starting timers with the 64KHz clock at a certain point in a scanline is a red herring to the main concern. It should drift 2 cycles every scan line. The code is only in there because it was enough to help avoid the NMI's with the 15KHz and I just left it in there when changing the code to use the 64KHz clock. the reset SKCTL was just a test to see if polycounters made any difference to timers (if they do it is subtle) The main concern is why IRQ's can affect NMI's so badly? Keeping IRQ's from triggering at/near NMI's avoids the problem, but doesn't explain the problem. I don't think the IRQ latency can be it if the 15KHz works. If the NMI's aren't actually being ignored, maybe they are being delayed by the IRQ if they trigger at the same time? I'm still tending towards thinking Rybags is correct and this is a 6502C "feature". It is a shame if that is the case, as even with it getting interrupted by NMI's, sample playing with 64KHz clock sounds pretty good on real hardware, and only needs 1 channel. Why do you think the CPU based POKEY clock shouldn't work Atariksi? It can trigger at a specific point in the scan line as per the 15KHz clock. Or do you just mean the lax timing when starting the timer (no WSYNC etc.)? Edited August 8, 2009 by Sheddy Quote Link to comment Share on other sites More sharing options...
Rybags Posted August 8, 2009 Share Posted August 8, 2009 (edited) Just tried a couple of things, kinda distantly related. NMIST ($D40F) will reflect a requested NMI whether that NMI is enabled or not, e.g. you can setup a DList with DLIs in it and it will still be triggered within Antic and reflected in NMIST - of course, the 6502 isn't signalled. This is already known... technique has been described before "DLIs in polling mode". In polling mode, we typically store to NMIRES to clear the pending DLI. Despite NMIST reflecting a requested DLI, by enabling DLIs in NMIEN, the pending DLI will not trigger an NMI to the 6502... (ed) tested this just then, even a tight loop waiting for NMIST to trigger a DLI, then re-enabling DLIs won't trigger it. Despite NMIST holding a pending DLI, it seems a VBlank NMI resets that bit, ie - the OS won't get confused and JMP ($0200) instead of processing the VBI. Edited August 8, 2009 by Rybags Quote Link to comment Share on other sites More sharing options...
Bryan Posted August 8, 2009 Share Posted August 8, 2009 (edited) I wonder what it would do on an 800 without a 6502C. I see that you've taken over the FFFx vectors, though. Edited August 8, 2009 by Bryan Quote Link to comment Share on other sites More sharing options...
Sheddy Posted August 8, 2009 Author Share Posted August 8, 2009 I wonder what it would do on an 800 without a 6502C (if the PORTB stuff was removed). yes, that would be interesting. I did try the code with the OS and it has the same problem. I'll post it so someone can give it a go. Quote Link to comment Share on other sites More sharing options...
Rybags Posted August 8, 2009 Share Posted August 8, 2009 I'd get my 'scope onto the NMI line and see what's going on internally, but I've virtually trashed one machine in the last 24 hours... don't want to risk another just yet. Quote Link to comment Share on other sites More sharing options...
Bryan Posted August 8, 2009 Share Posted August 8, 2009 (edited) I took out the sta WSYNC to see where the DLI was being serviced. It's about 1/3 of the way across (well, that's where the COLBK change is)- unless you're playing sound- then it scoots around. Edited August 8, 2009 by Bryan Quote Link to comment Share on other sites More sharing options...
atariksi Posted August 8, 2009 Share Posted August 8, 2009 lda #$70 wait3 cmp VCOUNT bne wait3; sync to a scanline lda #0 sta SKCTL lda #3 sta SKCTL; test - reset pokey and polycounters sta STIMER That's not an exact align there for the timer, it could drift a couple of cycles either way. I'd change it a bit. Put the reset SKCTL and poly stuff before the wait loop. Do a STA WSYNC immediately before the STA STIMER. That should make the IRQ occur earlier in the scanline, might be enough to stop if interfering with the NMIs. If that don't work, I'd try different delay loops after the WSYNC until you hit the sweet spot. Sta STIMER will reset timers to zero for IRQ; what is initializing SKCTL accomplishing? True, he should base things on WSYNC so he knows the EXACT cycle when things start but his problem is the 64Khz mode since that's misaligned with scanline rate anyway. He needs to use some divisor (prime # or something) so that DLI triggerring and IRQ triggerring cycles never overlap assuming that's the entire issue here. yes, sorry, starting timers with the 64KHz clock at a certain point in a scanline is a red herring to the main concern. It should drift 2 cycles every scan line. The code is only in there because it was enough to help avoid the NMI's with the 15KHz and I just left it in there when changing the code to use the 64KHz clock. the reset SKCTL was just a test to see if polycounters made any difference to timers (if they do it is subtle) The main concern is why IRQ's can affect NMI's so badly? Keeping IRQ's from triggering at/near NMI's avoids the problem, but doesn't explain the problem. I don't think the IRQ latency can be it if the 15KHz works. If the NMI's aren't actually being ignored, maybe they are being delayed by the IRQ if they trigger at the same time? I'm still tending towards thinking Rybags is correct and this is a 6502C "feature". It is a shame if that is the case, as even with it getting interrupted by NMI's, sample playing with 64KHz clock sounds pretty good on real hardware, and only needs 1 channel. Why do you think the CPU based POKEY clock shouldn't work Atariksi? It can trigger at a specific point in the scan line as per the 15KHz clock. Or do you just mean the lax timing when starting the timer (no WSYNC etc.)? The CPU based clock (1.79Mhz) works as long as it's phase shifted away from the DLI. The 15Khz cannot be phase shifted from the tests that I did. Even if you delay STIMER, the 15Khz interrupt seems to occur at exact same spot on the scanline. Enclosed below is the IRQ-based code that locks out the System Reset NMI on Atari 400/800-- but it doesn't work if I use 15Khz timer regardless of phase shift. By the way, I see you are optimizing code by writing directly to ROM vector area, but then you mine as well do STA ZP1 and STX ZP2 rather than to PHA, TXA/PHA and reverse for exiting from DLI. ;*** ATIRQ400.asm: Atari Timer IRQ measured to exact cycles so no WSYNC is required by Krishna Software Inc. ;*** This plays music on voice #1 and uses IRQ timer interrupt on timer #4. Voice #0 is for constant tone. ;*** There are three CLKs which can be used by either 8 or 16 bit divisors as controlled by 53768. ;*** The 15.6999Khz CLK and 63.921Khz CLK can be turned off via 53775 but 1.78979Mhz CLK is always on. ;*** The 15.6999Khz CLK is 1.78979Mhz/114 and gives the scanline interrupt at end of scanline. ;*** The 63.921Khz CLK is 1.78979Mhz/28 and does not divide evenly into 29868 cycles/frame so cannot be used ;*** for color clock accurate or scanline accurate interrupts. This program also attempts to write IRQ vector ;*** directly at 65534/65535 by disabling ROM for XL/XE series (should have no effect on Atari 400/800). ;*** This program locks out SYSTEM RESET on Atari 400/800 where it's a NMI. TIMERFREQLSB = 53760 TIMERFREQMSB = 53762 WSYNC = 54282 VCOUNT = 54283 DOSVEC = 10 CASINI = 2 WARMSTART = 58484 VMIRQ = 534 ;hardware irq ptr ORG = 600h ;DW 0FFFFh ;DW StartAdr ;DW LastOffset-1 DB 0,3 ;# of sectors to load 1..255 DW ORG DW StartAdr Rts StartAdr: Lda #MyReset,L Sta CASINI Lda #MyReset,H Sta CASINI+1 Lda #0 Sta 580 Lda #2 Sta 9 Jmp WARMSTART MyReset: Lda #2 Sta 9 Lda #MyReset,L Sta CASINI Lda #MyReset,H Sta CASINI+1 Sei Lda #0 ;no VBIs nor DLIs for maximum performance Sta 54286 Sta 53774 ;disable all IRQs Sta 54272 ;turn off screen Sta 54017 ;disable ROM and BASIC for XL/XE series via PORTB Lda #TimerTwoIRQ,L ;general IRQ routine but we use only for timer #2 Sta VMIRQ Ldy #TimerTwoIRQ,H Sty VMIRQ+1 Sta 65534 Sty 65535 Cpy 65535 Beq ROMVectorSet Lda #IdleLoop,L Sta SelfModifyThis+1 ;Remove 5 cycles from background task (for Jmp [534]) Lda #IdleLoop,H Sta SelfModifyThis+2 ROMVectorSet: Lda #40 ;+40 for join channels 3,4; +80 for channels 1+2 @1.79Mhz Sta 53768 ;join channels at 1.79 Mhz Lda #160 Sta 53760 Lda #143 Sta 53761 Lda #175 Sta 53763 Lda #114-7 ;lsb 114-7 or 57-7 for two irqs/scanline Sta 53764 ;timer #2 freq = 1789790/[A+1] Lda #0 ;msb for rate divisor A Sta 53766 Lda #4 ;1,2,4=timer interrupts Sta 53774 ;enable IRQ #2 Lda #128 ;Sta 53248 ;unrem to show sprite using 0 CPU cycles and 0 DMA cycles Lda #144 ;Sta 53249 Lda #255 Sta 53261 Sta 53262 Sta 53256 Sta 53257 Lda #32 Sta 53275 Lda #10 Sta 53266 Lda #12 Sta 53267 Ldx #0 Ldy #4 ;load ack parameters Lda #65 NotMidScr: Cmp VCOUNT Bne NotMidScr ;CF=1 when A=65 Sta WSYNC Nop ;Nop CLI Sta 53769 ;start timer counter ;CLI ;Nop ;Nop ;Nop ;Nop ;Lda #34 ;Sta 54272 IdleLoop: ;(29868 cycles in NTSC frame - 9*262 for Refresh = 27510 cycles so to keep the IRQ ; stable, cycles must be aligned to 27510 including IRQ routine). For PAL frame, use ; 312*114 = 35568 - 312*9 = 32760 cycles.) ;(27510 factors to 2*5*7*3*131) Lda #0 Sta 53274 Lda 1535 ;Lda 53770 ;No longer random if bits 0,1 of 53775 = 0 ROL Sta 53274 ;Rol Sta 53762 ;make construction site noise Lda #48 Sta 53274 Lda #64 ;which of these two instruction to rem out depends on IRQ routine total cycles ;Sta 53274 Lda #96 Sta 53274 Lda #128 Sta 53274 Lda #144 Sta 53274 Lda #160 Sta 53274 Lda #176 Sta 53274 SelfModifyThis: Jmp Idle1 ;For Atari 400/800, this jmp and following NOP (5 cycles) will be removed since ROM vector not used Idle1: Nop Jmp IdleLoop ;*** H/W does jmp [65534] (7 cycles) and XL/XE OS does a CLD and JMP [534] for 2+5 = 7 cycles. ;*** Below routine 32 cycles+7+5 = 44 cycles on old OS (like on Atari 400/800). ;*** Optimized by using X/Y registers and not using it in main routine so PHA/PLA not needed. TimerTwoIRQ: ;Pha ;3 cycles ;Lda #255 ;Sta 53771 ;POTGO ;Nop Inc 53274 ;change register (like color for example) ;Lda #0 ;Sta 53774 ;Lda #4 ;Sta 53774 ;send ack to timer irq Stx 53774 Sty 53774 ;do ack Inc 1535 ;Nop ;Lda #96 Ror 53274 ;change register (like color for example) ;Pla ;4 cycles Rti ;6 cycles ;LastOffset: DW 2e0h,2e1h,ORG Quote Link to comment Share on other sites More sharing options...
Sheddy Posted August 8, 2009 Author Share Posted August 8, 2009 I'd get my 'scope onto the NMI line and see what's going on internally, but I've virtually trashed one machine in the last 24 hours... don't want to risk another just yet. ha, ha! sounds like you've been having fun. what are you up to now? (the interlace discovery is very cool BTW) I took out the sta WSYNC to see where the DLI was being serviced. It's about 1/3 of the way across (well, that's where the COLBK change is)- unless you're playing sound- then it scoots around. interesting. no way should the sound affect when the DLI is serviced that much here's the code using the OS, which should also work on an 800. Programs in the zip as before. If anyone can try and see if the colours glitch and wobble with s64.xex on a real 800 it'd be much appreciated. ATARI=0 .include "equates.asm" ; variables *=$f0 sample .ds 2 sample_nybble .ds 1 sample_value .ds 1 sample_size .ds 1 table_index .ds 1 *=$02e0 .byte <run,>run ; initial run address ; main code *=$2000 run sei lda #0 sta NMIEN ; turn off NMI's sta POKMSK sta IRQEN ; disable IRQ's lda #<irq ; use immediate IRQ vector (no other OS IRQ's will happen) sta VIMIRQ lda #>irq sta VIMIRQ+1 lda #<dlist ; set up a display list sta SDLSTL lda #>dlist sta SDLSTH lda #<vbi ; set up an immediate VBI sta VVBLKI lda #>vbi sta VVBLKI+1 lda #<dli ; set up a DLI sta VDSLST lda #>dli sta VDSLST+1 lda #128+64 ; turn on NMI's - DLI's and VBI sta NMIEN cli ; enable IRQ's ldx #0 run1 txa asl a sta colour_table,x ; create a table of colours dex bne run1 ; initialize sample play irq play jsr init ; initialize the sample to play wait1 lda TRIG0 ; any other stuff can go here, but... beq wait1 ; just wait until fire button is pressed wait2 lda TRIG0 ; bne wait2 beq play ; end of main code init lda #<sample_start sta sample lda #>sample_start sta sample+1 lda #[>sample_end->sample_start] sta sample_size ; sample size in 256 byte pages lda #0 ; ~64KHz POKEY clock ; lda #1 ; ~15KHz POKEY clock sta AUDCTL lda #15 ; ~64KHz clock divided by 16 = ~4Khz ; lda #3 ; ~15KHz clock divided by 4 = ~4KHz sta AUDF1 ; in timer 1 lda #0 sta sample_nybble ; initialize nybble sta AUDC1 lda #1 sta IRQEN ; enable timer 1 sta POKMSK ldx #$18 stx sample_value ; an initial sample value (with volume only bit) lda #$70 wait3 cmp VCOUNT bne wait3 ; sync to a scanline (only relevant when 15KHz) stx STIMER ; start timers rts ; IRQ irq pha ; save a lda sample_value sta AUDC1 ; play sample ASAP to minimise DMA lag tya pha ; save y ldy #0 lda #1 sty IRQEN ; reset interrupt sta IRQEN ; re-enable only timer 1 eor sample_nybble ; switch between 0 and 1 (right and left nybble) sta sample_nybble beq irq1 lda (sample),y lsr a lsr a lsr a lsr a ; left nybble of sample bpl irq2 ; always branch irq1 lda (sample),y and #$0f ; right nybble of sample inc sample ; next lo byte of sample bne irq2 inc sample+1 ; next hi byte of sample dec sample_size ; check if at end of sample bne irq2 ; branch if not tya sta IRQEN ; end of sample - reset interrupt sta POKMSK beq irq3 ; always branch irq2 ora #$10 ; turn on volume only bit irq3 sta sample_value ; save sample to play next irq pla tay pla ; restore y and a rti ; DLI dli pha txa pha ldx table_index lda colour_table,x sta WSYNC sta COLBK inc table_index pla tax pla rti ; VBI vbi lda #0 sta table_index ; reset table index sta COLBK jmp SYSVBV ; 58463 - do system vertical blank processes ; display list *=$2400 dlist .byte $70,$70,$70 .byte $8D+$40,<screen_start,>screen_start .byte $8D,$D,$8D,$8D,$8D,$8D,$8D,$8D,$8D,$8D .byte $8D,$8D,$8D,$8D,$8D,$8D,$8D,$8D,$8D,$8D .byte $8D,$D,$D,$8D,$8D,$D,$8D,$8D,$8D,$8D .byte $8D,$D,$8D,$8D,$8D,$8D,$8D,$8D,$8D,$8D .byte $8D,$D,$8D,$8D,$8D,$8D,$D,$8D,$8D,$8D .byte $8D,$D,$8D,$D,$8D,$8D,$D,$8D,$8D,$8D .byte $8D,$D,$8D,$8D,$8D,$D,$8D,$8D,$8D,$8D .byte $8D,$D,$8D,$8D,$D,$8D,$8D,$8D,$8D,$8D .byte $8D,$D,$8D,$D,$8D,$8D,$8D,$8D,$8D,$8D .byte $8D,$D,$D,$D,$D .byte $41,<dlist,>dlist ; sample data *=$2800 ; NB start on a 256 byte page boundary ; sample will stop playing at nearest page boundary if not a multiple of 256 bytes sample_start .incbin "raygun44.raw" ; 4KHz, 4-bit raw sample data sample_end ; screen data *=$8000 screen_start ; colour table *=$9000 colour_table samplesOS.zip Quote Link to comment Share on other sites More sharing options...
Sheddy Posted August 8, 2009 Author Share Posted August 8, 2009 (edited) The CPU based clock (1.79Mhz) works as long as it's phase shifted away from the DLI. The 15Khz cannot be phase shifted from the tests that I did. Even if you delay STIMER, the 15Khz interrupt seems to occur at exact same spot on the scanline. Enclosed below is the IRQ-based code that locks out the System Reset NMI on Atari 400/800-- but it doesn't work if I use 15Khz timer regardless of phase shift. By the way, I see you are optimizing code by writing directly to ROM vector area, but then you mine as well do STA ZP1 and STX ZP2 rather than to PHA, TXA/PHA and reverse for exiting from DLI. OK, I see what you mean now. (As an aside, according to Altirra emu author Avery Lee, when POKEY is initialized should relate to where that spot on the scanline is for 15KHz as there is nothing else that particularly syncs it) I don't understand how your code disables reset NMI yet, but it could well be related to the glitches I'm seeing. Yes, no hardcore optimizes have been done! purely example code. Edited August 8, 2009 by Sheddy Quote Link to comment Share on other sites More sharing options...
Rybags Posted August 8, 2009 Share Posted August 8, 2009 I've done something and I've made the DLIs miss... I think even VBI gets clobbered at times. The 16 KHz Timing should be able to be aligned to any cycle... I don't see why not. Pokey has no idea whatsoever what HCOUNT is, only Antic and GTIA bother to keep track. Remember too, emulators don't do Timers properly. Don't trust them for this, or any hardcore stuff for that matter. Another possibility though... could it be the case that it's important during what part of the scanline you actually switch AUDCTL to the 16 KHz mode, irrespective of what you do with STIMER down the track? Quote Link to comment Share on other sites More sharing options...
Bryan Posted August 8, 2009 Share Posted August 8, 2009 If NMI's are vulnerable, then wouldn't disk loading cause missed DLI's and VBI's? Do we have an example of a stable DLI-filled screen during loads? Quote Link to comment Share on other sites More sharing options...
Rybags Posted August 8, 2009 Share Posted August 8, 2009 atariksi: what Assembler, debugger, monitor were you testing with? I'm loading stuff up here in Atari Asm/Ed... I modified my own stuff, and came to the conclusion your theory was right. But... try something like this: lda $d20a eor seed; seed = just some number you choose tax eor #$7f tay lda #0 sta $d20f; put POKEY in INIT state wait1 dey bne wait1 lda #3 sta $d20f; POKEY back to normal txa eor #$44 tay wait2 dey bne wait2 wait3 dex bne wait3 lda #1 sta $d208; voices in 16 KHz mode sta $d209; STIMER... don't really need it. OK, there's probably some stuff in there not needed, but I'm able to randomize where the IRQs occur during a scanline with something like this. Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted August 8, 2009 Share Posted August 8, 2009 Bryan, I never had a DLI screen without flickering while loading... look at Alternate Reality f.e. Quote Link to comment Share on other sites More sharing options...
Sheddy Posted August 8, 2009 Author Share Posted August 8, 2009 The CPU based clock (1.79Mhz) works as long as it's phase shifted away from the DLI. The 15Khz cannot be phase shifted from the tests that I did. Even if you delay STIMER, the 15Khz interrupt seems to occur at exact same spot on the scanline. Enclosed below is the IRQ-based code that locks out the System Reset NMI on Atari 400/800-- but it doesn't work if I use 15Khz timer regardless of phase shift. By the way, I see you are optimizing code by writing directly to ROM vector area, but then you mine as well do STA ZP1 and STX ZP2 rather than to PHA, TXA/PHA and reverse for exiting from DLI. OK, I see what you mean now. (As an aside, according to Altirra emu author Avery Lee, when POKEY is initialized should relate to where that spot on the scanline is for 15KHz as there is nothing else that particularly syncs it) I don't understand how your code disables reset NMI yet, but it could well be related to the glitches I'm seeing. Yes, no hardcore optimizes have been done! purely example code. On the slower clocks when you hit STIMER doesn't matter much - it doesn't reset the base clock. The timer will count down only when the base clock changes. I didn't understand this until recently. Quote Link to comment Share on other sites More sharing options...
Rybags Posted August 8, 2009 Share Posted August 8, 2009 DLIs aren't used during disk loads all that much... they can tend to upset SIO. Only XL/XE OS allows them too. Missed VBIs aren't critical, although of course if there's DLIs running that rely on counters etc being reset, then you might get problems. I think with the method I've employed, we should be able to work out where our 16 KHz IRQs occur... that way we could avoid conflict. Of course, we can scrap all this random/seed loop stuff - I think it's probably a case of putting POKEY into 64 Khz mode, wait a little bit, then go into INIT state, do a known wait to a blank scanline then wait a little more. Then put Pokey into normal operation, finally switch to 15 KHz mode. Quote Link to comment Share on other sites More sharing options...
Bryan Posted August 8, 2009 Share Posted August 8, 2009 (edited) here's the code using the OS, which should also work on an 800. Programs in the zip as before. If anyone can try and see if the colours glitch and wobble with s64.xex on a real 800 it'd be much appreciated. EDIT: Okay, ran it on my 800 (Standard Rockwell R6502) and the glitches still happen. Edited August 8, 2009 by Bryan Quote Link to comment Share on other sites More sharing options...
Rybags Posted August 8, 2009 Share Posted August 8, 2009 I think I've got the whole situation worked out... Since the OS does it's clear chipset register routine at cold/warmstart, we'd likely have a situation where Pokey always gets initialized and put back into normal mode "in sync" with the same spot on the scanline. If you go through the OS, you see a loop similar to: LDA #0 TAX loop: sta $d000,x sta $d200,x sta $d400,x inx bne loop etc. During the loop of course, we're hitting $D40A. Further into the warmstart, the OS puts Pokey back into normal mode. I would suspect that in possibly 100% of cases, that always occurs at the same place in a scanline. It might be the case though, that with different revisions of the OS that the alignment would be slightly different. 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.