| |
ChristopherJam
Registered: Aug 2004 Posts: 1409 |
Stablising raster IRQ with DMA
I was a little surprised not to see this method on codebase64, as it's really quite old. It's most useful at the top of the screen, where you can do the DMA forcing in the border, but for that very reason I find it a useful prelude to (e.g.) line crunch and other HW scrolling techniques.
The concept's fairly simple; you start a character row above the visible area, set an interrupt for a subsequent non-badline, and force a DMA to absorb the unknown number of cycles between then and end-of-row. Set $d011 back to $1b, and the screen appears as normal!
A simple example:
#define poke(addr,val) .(:lda #val:sta addr:.)
#define doke(addr,val) .(:lda #<val:sta addr:lda #>val:sta addr+1:.)
#define endIRQ pla:tay:pla:tax:pla:rti
#define ackIRQ pha:lda $d019:sta $d019:txa:pha:tya:pha
*= $0801-2
.word *+2 ; load address
.byt $0b,$08,$0a,$00,$9e,$32,$30,$36,$31,0,0,0 ; 10 sys2061
main:
poke($d021,$04)
sta 646
lda#147
jsr $ffd2
poke($d021,0)
poke($0400,1) ;something to see
poke($0428,2)
jsr IRQstart
loop:
inc $7e7 ; a nice untidy main loop to ensure instability!
bpl loop
jmp loop
IRQstart
poke($dc0d,$7f) ; kill CIA irq
sei
poke($01, $35) ; disable ROM
poke($d01a,$01) ; enable VIC irq
poke($d011,$1b) ; clear high bit of irq rasterline
poke($d012,$32) ; last invisible line
doke($fffe,irq1 ) ; set irq vector
cli
rts
irq1:
ackIRQ
poke($d020,$0f) ; this is unstable!
poke($d011,$1a) ; force partial badline before screen starts
poke($d020,$0b) ; this is stable ^_^
poke($d011,$1b) ; trigger normal re-fetch of first row of chars
poke($d012,$ff) ; set end-of-screen irq
doke($fffe,irq2 )
endIRQ
irq2:
ackIRQ
;ensure first row of chars is already being displayed when badline forced by irq1
poke($d011,$18)
poke($d012,$32 )
doke($fffe,irq1 )
endIRQ
(build with Fachat's xa)
Does anyone know why this technique is so rarely mentioned?
|
|
| |
Skate
Registered: Jul 2003 Posts: 494 |
@ChristopherJam: Codebase64 is still far from being a complete code/tutorial resource. Many many known tricks are not explained there. Probably that's why this was missing, too. Also this technique doesn't look that flexible since you must catch the interrupt at a fixed area. Still very useful though, thanks for sharing. |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Quote: @ChristopherJam: Codebase64 is still far from being a complete code/tutorial resource. Many many known tricks are not explained there. Probably that's why this was missing, too. Also this technique doesn't look that flexible since you must catch the interrupt at a fixed area. Still very useful though, thanks for sharing.
Which is good enough for setting up the initial timer, THEN use the timer to jitter compensate at any raster line on any frame.
(i'd choose a 63 cycle timer though...) |
| |
Flavioweb
Registered: Nov 2011 Posts: 463 |
Here is my version.
Works stable in PAL and NTSC.
(CA65 version, comile with cl65 -tc64 filename.asm)
; ------------------------------------------------------------------------------
rasterlogo1 = $34
; ------------------------------------------------------------------------------
.code
.org $07ff
; ------------------------------------------------------------------------------
; ------------------------------------------------------------------------------
; "sys" basic
; ------------------------------------------------------------------------------
.word *+2 ;loading bytes
basicstub:
.word @nextline
.word 2012 ; line number
.byte $9e ; sys
.byte <(((init / 1000) .mod 10) + $30)
.byte <(((init / 100 ) .mod 10) + $30)
.byte <(((init / 10 ) .mod 10) + $30)
.byte <(((init ) .mod 10) + $30)
.byte 0 ;end of line
@nextline:
.word 0 ;empty line == end basic code
init:
lda #$00
sta $d020
sta $d021
sta $3fff
; ------------------------------------------------------------------------------
;set up irq
; ------------------------------------------------------------------------------
setup: lda #$1b
sta $d011
lda #rasterlogo1
sta $d012
ldy #$00
sty $d015
sei
lda #$7f
sta $dc0d
sta $dd0d
lda #$35
sta $01
lda #<irq1
sta $fffe
lda #>irq1
sta $ffff
lda #$01
sta $d01a
lda $dc0d
lda $dd0d
lda #$01
sta $d019
cli
jmp *
; ------------------------------------------------------------------------------
; Routine in interrupt
; ------------------------------------------------------------------------------
irq1:
ldx #$08
dex
bne *-1
lda $d012
and #$07
ora #$18
sta $d011
cmp $00,x
cmp $00,x
cmp $00
inc $d021
inc $d021
inc $d021
inc $d021
inc $d021
inc $d021
ldy #$00
sty $d021
lda #$1b
sta $d011
asl $d019
rti
|
| |
enthusi
Registered: May 2004 Posts: 677 |
oh, and please add it to codebase in case you didnt already :) |
| |
chatGPZ
Registered: Dec 2001 Posts: 11386 |
and incase you do, remove that poke stuff... *please* =P |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1409 |
@Groepaz hehe. My assembler of choice for many many years was FASSEM; I got used to doing a lot of set up in BASIC, and old habits die hard ;) |
| |
Frantic
Registered: Mar 2003 Posts: 1648 |
@Christopher: Codebase is only a click away pokes or not! :)
http://codebase64.org/
...and more specifically:
http://codebase64.org/doku.php?id=base:interrupts#stable_timing
As someone said, there are still many areas to fill in on codebase, waiting for contributions. :) |
| |
zscs
Registered: Sep 2010 Posts: 49 |
I'm (an old:) new to C=64 coding since I haven't touched any asm code for 17.5 years. ...until last Saturday, when I found an old unfinished one-file demo on one of my workdisks. ;) A really interesting one, because I have already tried to implement 5-6 raster stabilisation methods to it but the raster is still unstable... I used methods that I found here in the forums and at Codebase64.org. (I can work only with Vice 2.2 and I use an old SMON $C000 for coding/experiments.) Some methods, like Hermit's D012 algorythm (example 1) hangs in Vice because the self-modifying code has a bug (or at least it seems it has a bug - I debugged the process with Vice's built-in monitor).
It's getting more and more frustrating to me but I don't want to give it up! :) Nevertheless, if I send a file to anyone who would like to take a look to my code, could he (you) please help me, what's wrong here? I can send a Vice Snapshot file in .vsf format. Well, as I mentioned, I just started to learn asm again so probably I did a stupid and easily fixable bug in my code. :)
So just drop me a mail to zscsoka at hotmail dot com, and I will send you a file.
Thanks a lot in advance!
BR,
zs |
| |
zscs
Registered: Sep 2010 Posts: 49 |
After 1.5 years I started to stabilize the raster again in my crappy intro. I think I totally got stucked. I tried all methods that I found here and at Codebase. Maybe I haven't noticed something which does not allow me to stabilise the raster. :)
Actually, I have a 2x2 sideborder scroll at the upper part of the screen, center part is a DXYCP scroller in a 16x16 character grid with a moving sprite logo behind of the scroller. (Problem: the sideborder scroll's sides are blinking, raster is not stable.)
Mainly I tried to use the very same method that I coded 19 years ago here (Unforeseen Open part, sideborder scroll with sideborder logo with text "Unforeseen"): Attempt One and Unforeseen Open [unfinished]
(Yes, I'm a lamer newbie again but I don't give the things up easily) :) |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Do you by any chance accidently have active sprites on the raster line you're stabalizing on? |
| |
zscs
Registered: Sep 2010 Posts: 49 |
Quote: Do you by any chance accidently have active sprites on the raster line you're stabalizing on?
Hm, it can happened, I'm going to check. Thanks for the tip! |
| |
zscs
Registered: Sep 2010 Posts: 49 |
Quote: Hm, it can happened, I'm going to check. Thanks for the tip!
It was long time ago when I tried to stabilize the raster in my very old intro that I mentioned here, earlier. 4 days ago (and almost 1.5 years of inactivity:) I tried to stabilize an another intro (that I coded 21 years ago:-) and I could fix it with the "IRQ with DMA" technique. I got the example from Codebase: http://codebase64.org/doku.php?id=base:stable_irq_with_dma
So yesterday I fent myself quite motivated, therefore I tried to stabilize the IRQ in my another intro and after 3 hours of experiments with the code changes, finally I could stabilize the raster there as well! :)
OFF: I will release the intro very soon. The only thing that I need to do, compress it with a "packer", create .d64 image and copy the file to the image and upload. (Nothing special actually, however the DXYCP that I coded (and without sprites) can be interesting I guess.) ;) ON. |
| |
dink Account closed
Registered: Mar 2012 Posts: 30 |
I was about to say, that poke macro sure makes the code 6502 code look a lot nicer. |
| |
Krill
Registered: Apr 2002 Posts: 2980 |
If i were to use such things, i'd prefer a "mov" macro-op, as inmov #$00, $d020 My 2 bitcents. |
| |
chatGPZ
Registered: Dec 2001 Posts: 11386 |
ab ins eck! |
| |
tlr
Registered: Sep 2003 Posts: 1790 |
sys ($ffd2)... ;) |
| |
Mace
Registered: May 2002 Posts: 1799 |
I tried the DMA method, but results vary.
How come that it sometimes jitters while with a different timing, it's stable?
And the single cycle I change, is at a point where I'd expect it to have no effect on the stabilization.
On second thought: could it be that I put the #$1b into $d011 too early afterwards?
.pc = $0810
sei
lda #$35 // switch off kernal
sta $01
ldx #$00
stx $d015 // switch off sprites
stx $3fff // clear garbage gfx
inx
stx $d01a // raster interrupt
lda #$7f // clear timers
sta $dc0d
sta $dd0d
lda #$1b // clear MSB of rasterline
sta $d011
lda #$33 // set triggering rasterline
sta $d012
ldx #<irq // set IRQ vectors
ldy #>irq
stx $fffe
sty $ffff
lda $dc0d // ack pending timers
lda $dd0d
lda #$01 // ack pending irq
sta $d019
cli
jmp * // infinite loop
irq:
sta saveA
stx saveX
sty saveY
ldx #$08
timeloop1:
dex
bne timeloop1
nop
nop
lda $d012 // DMA
and #$07
ora #$18
sta $d011
bit $eaea
bit $eaea
bit $eaea
bit $eaea
bit $eaea
bit $eaea
bit $eaea
bit $eaea
bit $eaea
bit $eaea
bit $eaea
bit $eaea
bit $eaea // jitters, change into bit $ea to stabilize
ldy #$c0
lda #$c8
sty $d021
sta $d021
lda #$1b
sta $d011
asl $d019
.label saveA = * +1
lda #$00
.label saveX = * +1
ldx #$00
.label saveY = * +1
ldy #$00
rti
|
| |
Flavioweb
Registered: Nov 2011 Posts: 463 |
Dma trick to stabilize raster is more useful if used to start a cia timer (reverse counter method).
This avoid to care about idle gfx and screen display.
But is better for no background gfx with sprites over rasterbars (or sprites with open sideborder) parts. |
| |
Mace
Registered: May 2002 Posts: 1799 |
That's what I was trying to do: open borders (using d020 instead of d016, to find the sweet spot). |
| |
Flavioweb
Registered: Nov 2011 Posts: 463 |
I never tried it, but i remember some trick like
LDX #$0B
STA $D016,X
with in .A the color.
If executed with right timing it should open border and put color on screen...
But i have not really tried it...
It's done using a 6502 bug that read and discard value from base address before add .X and write to the dest address.
You can give a try +) |
| |
tlr
Registered: Sep 2003 Posts: 1790 |
The classic way assuming 8 sprites and no badlines is to do a 46 cycles/raster line loop including an inc $d016/dec $d016 (or vice versa). That will self stabilize after a few raster lines.
I'm guessing from your comment you want the screen open though. |
| |
Mace
Registered: May 2002 Posts: 1799 |
Okay, will try with some actual sprites on the screen and with a few more lines than just one. |
| |
Rastah Bar Account closed
Registered: Oct 2012 Posts: 336 |
Quote: Which is good enough for setting up the initial timer, THEN use the timer to jitter compensate at any raster line on any frame.
(i'd choose a 63 cycle timer though...)
For the sake of completeness: you can also use the sprite-to-sprite and sprite-to-background collision detection registers to find the exact position of the beam. E.g., put 8 1-byte-wide sprites next to each other on top of 8 characters at a suitable x-position. Let a raster interrupt occur, read $d01f and the number of bits set tells you were you are. Then use a timer to keep track. This method can handle 0-8 cycle jitter values. |
| |
algorithm
Registered: May 2002 Posts: 705 |
Is it not just easier to use the double IRQ method or polling (that would take more raster lines to stabilize) but no requirement for forcing badlines or placing sprites to stabilize? |
| |
Mace
Registered: May 2002 Posts: 1799 |
Algo, it's the other way around.
For me it's easier to use the double IRQ, but for education sake I was trying other methods :-) |
| |
zscs
Registered: Sep 2010 Posts: 49 |
Quote: For the sake of completeness: you can also use the sprite-to-sprite and sprite-to-background collision detection registers to find the exact position of the beam. E.g., put 8 1-byte-wide sprites next to each other on top of 8 characters at a suitable x-position. Let a raster interrupt occur, read $d01f and the number of bits set tells you were you are. Then use a timer to keep track. This method can handle 0-8 cycle jitter values.
Hm, interesting method. Sounds a bit overkill but I like the idea. :) Would be good to add an example to codebase64.org |
| |
Rastah Bar Account closed
Registered: Oct 2012 Posts: 336 |
I may do that. I can post some example code here and let people comment on it first.
;In the main routine:
sei
lda $d01f ;Clear sprite-to-background collision register.
...
;Place 1 byte wide sprites at suitable x-pos on 8 characters.
...
;Set up raster IRQ for first line where the sprites are
...
lda #$ff ; Turn sprites on.
sta $d015
cli
... ; rest of main routine
IRQ
lda $d01f ; read collision register
;Sprites must be placed such that $d01f equals either %00000000 or %10000000 or %11000000 or %11100000 ... or ... %11111111 depending on the jitter value.
; Now create 1 cycle extra delay for every zero bit
; (Caution: branches should not cross a page boundary)
asl
bcc *+2
asl
bcc *+2
asl
bcc *+2
asl
bcc *+2
asl
bcc *+2
asl
bcc *+2
asl
bcc *+2
asl
bcc *+2
...
;Set timer
;Turn sprites off
...
rti
This is a bit clumsy since the main routine shouldn't touch the sprites until synchronization has been achieved.
Therefore, in my code I did not use a raster interrupt but just waited in the main routine for $d01f to become unequal to zero:
sei
...
;Place sprites
...
lda $d01f ;Clear sprite-to-background collision register
lda #$ff ; Turn sprites on
sta $d015
lda $d01f ; Wait for nonzero collision bits
beq *-3 ;
;Add delay depending on number of zero bits
asl
bcc *+2
...
...
;Set timer
;Continue main program
cli
rts |
| |
Oswald
Registered: Apr 2002 Posts: 5094 |
innovative method, but using a CIA timer is much less trouble. You dont have to waste so much resources, and are free to use sprites, calculating cycles to waste is simpler aswell. |
| |
Rastah Bar Account closed
Registered: Oct 2012 Posts: 336 |
Not sure I understand you. I use $d01f only once to find the exact beam position, then I set a CIA timer that is used from then on to keep track of where the beam is. |
| |
enthusi
Registered: May 2004 Posts: 677 |
Nice, once you know xpos exactly you can even busy loop to the next start of the line anyway |
| |
Mace
Registered: May 2002 Posts: 1799 |
"You can even busy loop to..." WHATYAMEAN?! :-) |
| |
chatGPZ
Registered: Dec 2001 Posts: 11386 |
Quote:using a CIA timer is much less trouble.
actually if you consider the setup it requires, the CIA timer stuff is the most involved method of them all. and it really only makes sense when you need to sync more than once per frame. in pretty much all other cases some other method is nicer (i usually use double irq, and occasional dma forcing) |
| |
Oswald
Registered: Apr 2002 Posts: 5094 |
my bad, I didnt read it fully, so I thought this would use merely the sprites for stabilization without timer.
gpz: if you make a demo, you can just copy paste the timer setup, and run it at the start, then you are set. Now for going like this imho it doesnt really matter how you init the timer. I use a routine by Ninja which does it in about 100 lines including irq setup
clever innovative method still, tho. easyer to code from scratch, than waiting for $d012 change and then add cycles accordingly. |
| |
MagerValp
Registered: Dec 2001 Posts: 1078 |
Quote: Quote:using a CIA timer is much less trouble.
actually if you consider the setup it requires, the CIA timer stuff is the most involved method of them all. and it really only makes sense when you need to sync more than once per frame. in pretty much all other cases some other method is nicer (i usually use double irq, and occasional dma forcing)
If you only stabilize once per frame and speedcode or busywait from thereon, then yes, double IRQ is all you need. It costs about 80 cycles though, so when you need to stabilize every N raster lines, there's a huge gain from using CIA timers since you can stabilize anywhere on a line. Hermit's init code on codebase64 is nice and compact and can be generalized to give you a 63 or 65 cycle counter, and it's smooth sailing after that. Conceptually I don't think it's much more complicated than the double IRQ, and not as crazy as putting a jmp instruction at $dc02-$dc04. |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1409 |
Mace wrote:
Quote:I tried the DMA method, but results vary.
How come that it sometimes jitters while with a different timing, it's stable?
And the single cycle I change, is at a point where I'd expect it to have no effect on the stabilization.
I just had a look, and the issue is you're not writing to $d011 until you're in the right hand border - too late to force DMA.
Try replacing
and #$07
ora #$18
sta $d011
with
and #$07
ora #$18
sta $d021
sta $d011
dec $d021
and you'll see where your d011 write is occurring. Drop timeloop1's iterations by two (ie ldx#$06) and it should be fine. |
| |
Rastah Bar Account closed
Registered: Oct 2012 Posts: 336 |
Quote: Quote:using a CIA timer is much less trouble.
actually if you consider the setup it requires, the CIA timer stuff is the most involved method of them all. and it really only makes sense when you need to sync more than once per frame. in pretty much all other cases some other method is nicer (i usually use double irq, and occasional dma forcing)
When I was learning to make a stable raster routine, I had some difficulty understanding some of the other methods (DMA forcing in particular). So I thought about a way to directly observe/measure the beam position.
For inexperienced coders (like me) this method could be a bit easier to visualize then some of the others, even though some of these other methods are more efficient, nicer, faster, etc. Once you understand one simple method, it becomes way easier to understand more complicated ones too.
Personally, I find it always interesting to learn about different methods to achieve the same thing. That is why I started my post with "For the sake of completeness". |
| |
robo
Registered: Nov 2012 Posts: 9 |
I have a question regarding the cia-timer method:
When setting $dc04 to #8, will the timer really count only from 7..1?
Because otherwise, hermit's Routine would fail (but I guess it does not...)
In the opposite, the Manual http://archive.6502.org/datasheets/mos_6526_cia.pdf
says, that CIA counts from the latched value (#8) to Zero. That would be 9 values, and so this setup not usable to correct a 7 cycle-jitter..
What am I missing? |
| |
Rastah Bar Account closed
Registered: Oct 2012 Posts: 336 |
Correct me if I'm wrong: If you set it to 8, the counter will go like this: 8,7,6,5,4,3,2,1,8,8,7,6,...
So instead of 0 it will take on the value 8. |
| |
robo
Registered: Nov 2012 Posts: 9 |
I already thought about something like that.
But with #8 the eor #7 will result in branch-offset = #0f.
That would be wrong, since there are only 7 adresses to branch to, so the Offset has to be 0..6. |
| |
robo
Registered: Nov 2012 Posts: 9 |
Color Bar, its exactly like you wrote.
I did a short test in vice by setting up a timer and watching the values in the monitor.
So, is it a bug or what is going on? |
| |
chatGPZ
Registered: Dec 2001 Posts: 11386 |
Quote:when you need to stabilize every N raster lines, there's a huge gain from using CIA timers since you can stabilize anywhere on a line. Hermit's init code on codebase64 is nice and compact and can be generalized to give you a 63 or 65 cycle counter, and it's smooth sailing after that. Conceptually I don't think it's much more complicated than the double IRQ, and not as crazy as putting a jmp instruction at $dc02-$dc04.
IMHO in pretty much all cases that justify using this timer method you'd also want to use the mentioned "ninja method" (best term ever :=)) for ACKing the interrupt, because every single cycle counts in these cases :) |
| |
Rastah Bar Account closed
Registered: Oct 2012 Posts: 336 |
Quote: Color Bar, its exactly like you wrote.
I did a short test in vice by setting up a timer and watching the values in the monitor.
So, is it a bug or what is going on?
Maybe you should check this thread:
http://csdb.dk/forums/?roomid=11&topicid=65658&showallposts=1
There the same thing is noticed by ready. and Copyfault. |
| |
Rastah Bar Account closed
Registered: Oct 2012 Posts: 336 |
Quote: Quote:when you need to stabilize every N raster lines, there's a huge gain from using CIA timers since you can stabilize anywhere on a line. Hermit's init code on codebase64 is nice and compact and can be generalized to give you a 63 or 65 cycle counter, and it's smooth sailing after that. Conceptually I don't think it's much more complicated than the double IRQ, and not as crazy as putting a jmp instruction at $dc02-$dc04.
IMHO in pretty much all cases that justify using this timer method you'd also want to use the mentioned "ninja method" (best term ever :=)) for ACKing the interrupt, because every single cycle counts in these cases :)
haha ... It just HAD to be a guy named "ninja" to invent this. Make crazy jumps and land perfectly every time :-) |
| |
Rastah Bar Account closed
Registered: Oct 2012 Posts: 336 |
Quoting Color Bar
Therefore, in my code I did not use a raster interrupt but just waited in the main routine for $d01f to become unequal to zero:
sei
...
;Place sprites
...
lda $d01f ;Clear sprite-to-background collision register
lda #$ff ; Turn sprites on
sta $d015
lda $d01f ; Wait for nonzero collision bits
beq *-3 ;
;Add delay depending on number of zero bits
asl
bcc *+2
...
...
;Set timer
;Continue main program
cli
rts
I peeked into my code and saw that I had put an "ora #$01" after the "beq *-3". It seems that reading $d01f clears it, but with some delay, so it can happen that you read $d01f while the beam is just before the sprites and then you branch back, read it again and the higher bits are set, but bit 0 is not. This should not happen if you read $d01f in the raster IRQ version (since you read $d01f only once then).
Or maybe the bits of $d01f are set with some delay. I do not really know. |
| |
MagerValp
Registered: Dec 2001 Posts: 1078 |
Quoting GroepazQuote:when you need to stabilize every N raster lines, there's a huge gain from using CIA timers since you can stabilize anywhere on a line. Hermit's init code on codebase64 is nice and compact and can be generalized to give you a 63 or 65 cycle counter, and it's smooth sailing after that. Conceptually I don't think it's much more complicated than the double IRQ, and not as crazy as putting a jmp instruction at $dc02-$dc04.
IMHO in pretty much all cases that justify using this timer method you'd also want to use the mentioned "ninja method" (best term ever :=)) for ACKing the interrupt, because every single cycle counts in these cases :)
Ninja's method is impractical in some cases, since there's effectively no vector if you need to switch between several handlers, and it clobbers low ram. But if you need every last cycle, go for it. |
| |
MagerValp
Registered: Dec 2001 Posts: 1078 |
Quoting roboI have a question regarding the cia-timer method:
When setting $dc04 to #8, will the timer really count only from 7..1?
As others pointed out, it's 8..0 (sort of), but consider setting dc04 to 62 or 64 instead. This effectively gives you free NTSC fixing and an inverted x counter that lets you stabilize anywhere on the line. |
| |
Rastah Bar Account closed
Registered: Oct 2012 Posts: 336 |
Quote: Color Bar, its exactly like you wrote.
I did a short test in vice by setting up a timer and watching the values in the monitor.
So, is it a bug or what is going on?
It't not a bug, it's a feature :-) |