CPC 8255 PIO and VBlank
category: general [glöplog]
Ok so I got my AY playback code to work on the CPC after switching around some of the AY regs (the datasheet claimed the volume regs to be R10-R12, seems like it's actually R8-R10)..
The problem now is that the playback rate appears to be nearly twice what it should be. I'm using a wait-for-vblank to get a playback rate of 50 Hz - or at least that's what's intended.
First I set up the 8255 like this:
ld b,$F7
ld a,$82 ; A as output, B as input
out (c),a
and before every call to my music update routine I do:
wait_vblank:
ld b,$F5
in a,(c)
rra
jr nc,wait_vblank+2
Any CPC coders that can see anything that's wrong/missing?
The problem now is that the playback rate appears to be nearly twice what it should be. I'm using a wait-for-vblank to get a playback rate of 50 Hz - or at least that's what's intended.
First I set up the 8255 like this:
ld b,$F7
ld a,$82 ; A as output, B as input
out (c),a
and before every call to my music update routine I do:
wait_vblank:
ld b,$F5
in a,(c)
rra
jr nc,wait_vblank+2
Any CPC coders that can see anything that's wrong/missing?
I'm no CPC coder, but shouldn't it be "in a,(b)" instead?
I mean, ld c,$F5...
Joghurt: this is one of the oddities of the use of the z80 in a cpc. So the code is correct.
mic: it looks like you are trying to use 'PPI Mode 1', meaning you handle half of port C as an input, which is bad since it's not wired for that. Try LD A,$86 instead of $82.
And this should be the default config of the 8255 as done by the system so you shouldn't even have to do it.
mic: it looks like you are trying to use 'PPI Mode 1', meaning you handle half of port C as an input, which is bad since it's not wired for that. Try LD A,$86 instead of $82.
And this should be the default config of the 8255 as done by the system so you shouldn't even have to do it.
One would think so, but all the CPC code samples I've found have been assigning the port number to B for some reason. The wait_vblank loop was actually taken from someplace else, and I'm hoping at least that the person who wrote it knew what he was doing.
Here is what happened :
Amstrad wired the address bits to the high byte of the bus, while the z80 spec says only the low byte is used for the in and out instruction. So you use C as the address but actually the whole BC reg pair is sent on the bus. And things work.
However, it makes it tricky to use OUTI, INI, OUTD, IND and their friends. They decrement B and you end up sending values to random places in the hardware.
And the Vblank loop is fine.
Amstrad wired the address bits to the high byte of the bus, while the z80 spec says only the low byte is used for the in and out instruction. So you use C as the address but actually the whole BC reg pair is sent on the bus. And things work.
However, it makes it tricky to use OUTI, INI, OUTD, IND and their friends. They decrement B and you end up sending values to random places in the hardware.
And the Vblank loop is fine.
That last comment was for Joghurt.
Anyway, I tried using $86 as the PIO setup value instead (as well as not writing any setup value at all), and now the playback rate is even faster than before.. :S
Anyway, I tried using $86 as the PIO setup value instead (as well as not writing any setup value at all), and now the playback rate is even faster than before.. :S
Mh, lloks like there is an error on my docs, $82 is the right value.
So this part of your code looks fine to me.
The volume regs are R8-10, so you must have selected the wrong datasheet for some reason.
If you have the cpc system still alive, you can try using "call $bd19" to wait for the vblank instead of your own code.
Oh, and you should probably add an HALT somewhere in your main loop, because if you detect the vbl, run your player very fast and check for the VBL again, it may still be in the same VBL and the bit won't be reset.
So this part of your code looks fine to me.
The volume regs are R8-10, so you must have selected the wrong datasheet for some reason.
If you have the cpc system still alive, you can try using "call $bd19" to wait for the vblank instead of your own code.
Oh, and you should probably add an HALT somewhere in your main loop, because if you detect the vbl, run your player very fast and check for the VBL again, it may still be in the same VBL and the bit won't be reset.
And to be on the safe side, also disable the system interrupts by putting an EI ; RET at $38 (interrupt vector). Or just use DI if you don't wan't to use the HALT instruction.
If you do something like this:
It will work, but...
The VSync signal lasts for quite some time (usually arround 14 scanlines, depending on the CRTC configuration). If the [i]playmusic[i] routine in the exemple above is too fast (eg. less than 14 scanlines), you will loop to the VSync test while the VSync is still active, hence the double-rate "bug".
Just add some DJNZ $ in your loop to make sure you don't catch the same VSync several time :)
Code:
; Init PPI
ld bc,$F782
out (c),c
waitvs
; wait until vsync is active
ld b,$F5
in a,(c)
rra
jr nc,waitvs+2
call playmusic
; loop forever
jr waitvs
It will work, but...
The VSync signal lasts for quite some time (usually arround 14 scanlines, depending on the CRTC configuration). If the [i]playmusic[i] routine in the exemple above is too fast (eg. less than 14 scanlines), you will loop to the VSync test while the VSync is still active, hence the double-rate "bug".
Just add some DJNZ $ in your loop to make sure you don't catch the same VSync several time :)
I went back to writing $82 to the 8255 and added a HALT to my loop, and now the playback rate sounds ok. Thanks.
The VBlank-loop looks ok to me, but I've got a thought...
Do you know, how fast your player routine is? Could it be, that the player routine is faster than the time needed for VBlank and because of that, VBlank bit is still active when the routine ends? Correct me if I'm wrong, but I think VBlank takes 16 lines (=1024us).
For example, if your routine took a bit over 900us, you'd be running it twice per VBlank.
Do you know, how fast your player routine is? Could it be, that the player routine is faster than the time needed for VBlank and because of that, VBlank bit is still active when the routine ends? Correct me if I'm wrong, but I think VBlank takes 16 lines (=1024us).
For example, if your routine took a bit over 900us, you'd be running it twice per VBlank.
Oops.. Grimmy got the answer already. :-) Sorry for the "double post".
hey Sparklite! Nice to see you're still alive! :)
Nope, I haven't looked at how much time it takes. Doing cycle calculations for 1200 lines of code isn't my idea of fun :P
But the song I was using for testing only uses 2 channels and no effects, so updating the player in that case probably doesn't take much time.
But the song I was using for testing only uses 2 channels and no effects, so updating the player in that case probably doesn't take much time.