News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu

VSync help! (Sharp MZ-80A !)

Started by kelp7, 09:49, 06 February 14

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

arnoldemu

Quote from: kelp7 on 08:15, 07 April 14

Just to confirm, this definitely does not work. So I guess this is down to how the machine is hardwired.
great, thanks for confirming that.

Axelay

@ kelp7 & arnoldemu: Thanks for those replies to my previous post.  It's been a very busy week!

kelp7

No problem :-) I'm still working on what delays etc are needed to create stable effects on this machine. Still seeing funny results but I do really need to start documenting this as I keep getting ideas I want to try and going off on tangents which helps nobody ! I find it strange that a certain amount of delay will cause the effect I'm trying to create to become stable on the screen with absolutely no flickering at all. I wonder if, depending on the delay, I'm starting to push the flickering part of the line out into the time when horizontal flyback is occurring perhaps! I presume this could be possible. I'm going to try arnoldemu's code which I used to buffer the vsync (to test what value determined vsync on/off) and use it instead to buffer the hsync at &E008. If I can even manage to test for hsync then maybe some of my troubles will be resolved!

kelp7

arnoldemu : I'm curious. On the CPC, if a game is written that takes advantage of the ability to change background colour scanline by scanline then obviously this has to happen (guaranteed) on every single frame (otherwise presumably you'd get horrible flickering). Do you set up an interrupt which occurs every frame? Does this slow the game action down noticeably?


[edit] : and presumably you only do a very small number of changes, you wouldn't, for instance, change the colour every single scanline or every other scanline even...

arnoldemu

Quote from: kelp7 on 08:09, 11 April 14
arnoldemu : I'm curious. On the CPC, if a game is written that takes advantage of the ability to change background colour scanline by scanline then obviously this has to happen (guaranteed) on every single frame (otherwise presumably you'd get horrible flickering). Do you set up an interrupt which occurs every frame? Does this slow the game action down noticeably?


[edit] : and presumably you only do a very small number of changes, you wouldn't, for instance, change the colour every single scanline or every other scanline even...
You are correct. For games it is faster to restrict colour changes to when the interrupts occur. This is six times per frame, approx 52 scan lines between. Here we can change mode and all the colours in the current mode. It is perfect for status panels. If we wanted to change colours more frequently then we are forced to use nop or other instructions which take a fixed time to get to the point where we want to change colours and that then takes time away from the game itself.


Changing mode takes few cycles. Changing colours can take 1 line. Using interrupts is quick. It is the burning of cycles for more frequent, or for a place where the interrupt doesn't happen is what takes the time. We normally choose an interrupt which is close then burn cycles after that if required.


You are correct that we must do our mode and colour changes every frame to keep it working.


On other systems you have more control over the position of the interrupts. This means to get to a particular position involves no burning of cycles because you trigger it to be exactly where you want it to be.


You may have more control with mz.

kelp7

#80
Thanks!


I hope I have more control on the MZ (certainly more than I initially thought when I started this). I'm starting to think I may lack the coding skills and experience necessary to fully hack this machine. For instance, trying to understand the 8253 gives me a headache, although I haven't put proper time aside to thoroughly go through how it works. I'm thinking of using the 8253 counter to set up an interrupt that happens every frame, could be difficult to do but maybe if I sync the EI instruction with the start of vsync it might work perhaps. Then I'd use that interrupt to do some quick changes to screen at the top of each frame (just after vsync) which doesn't take too much time and therefore not too much time away from any game code. All depends on whether the 8253 can have a very small counter amount (50th of a second or just less if i include wait-for-vsync code at the interrupt vector). We shall see!

kelp7

Also, another thing I haven't tried yet is simply just enabling EI and writing some code to display something on the screen at the appropriate vector, just to see if any interrupts do actually happen by default on the MZ! (Although it's my understanding that on power-on there is only a 12-hour interrupt for the real-time clock)

kelp7

Here's an interesting observation just carried out.


Just used arnoldemu's buffering technique to capture the hsync (instead of vsync), i.e:



LD HL,&4000
LD BC,&2000
LOOP: LD A,(&E008)
AND 128
LD (HL),A
INC HL
DEC BC
LD A,B
OR C
JR NZ,LOOP
RET



(The hsync is the most significant bit in the byte at E008)


What I saw was a kind of pattern. I actually ended up adding the code to wait for vsync at the top of this code just to ensure I was attempting to capture the hsync bits as soon after vsync as possible. Anyway, I see something like:



&4000 : 80
&4001 : 00
&4002 : 80
&4003 : 00
&4004 : 80
&4005 : 00
&4006 : 80
&4007 : 00
&4008 : 80
&4009 : 00
&400A : 00
&400B : 00
&400C : 00



And this pattern pretty much repeated over and over. Although sometimes there would be a collection of only four "80"s and a greater number of consecutive "00"s afterwards. It wasn't exactly the same all of the time. Just wondering what this means, does the hsync usually toggle quickly between on and off like this? Maybe it's meaningless and you can't really ascertain much from the hsync. Interesting results none-the-less.

kelp7

Just to say I'm still working on this stuff, at the moment I'm working on getting my head around using the 8253. My current impression of how it works within the Sharp MZ is that two of the three internal counters in the 8253 are sort of hard-wired to each other to create a realtime clock which generates an interrupt after 12 hours have elapsed to ascertain the switchover between AM and PM. I'm hoping that I can send a control word to the 8253 that will allow me to forget about one of the counters and just use the other counter as a fast one to enable interrupts to use with the screen. (Basically the fast counter is set to count for 1Hz and trigger a countdown on the 2nd counter, this is my impression of how it all works currently. The 2nd counter then counts down a second at a time and is initialised to a value of 43200). Hopefully, if i get good results, then I'll put them here (hoping people are still interested in all this  ;D  )

kelp7



Right, so I'm now much more clued up on the function of the 8253 in the MZ-80A !
One counter is used for music production (single PC speaker) and I haven't bothered to go any further with this yet.
The other two remaining counters are inextricably linked to each other. You cannot use the one without the other. Counter #1 happens to act as a 'rate generator' which is basically a divide-by-N counter. This has an input clock of 31.5Khz. This divides this input clock down to 1Hz and outputs that to the input clock of Counter #2 (which is initialised to 43200, the number of seconds in 12 hours). The output of counter #2 is hooked into the maskable interrupt. After 12 hours an interrupt occurs to switch from AM to PM or vice versa. Basically it's a realtime clock (which in BASIC is converted to hours, minutes and seconds and stored in a variable called TI$)


So... you cannot use counter #2 without counter #1 because counter #1 provides counter #2's clock input.
You cannot use counter #1 without also using counter #2 because if you did, you would basically not really be able to use it for anything. Its output goes straight to the clock input of counter #2.


This is fine though because 31.5Khz is clearly plenty of speed for me to start using interrupts with the screen to create fx


I managed to prove that I could speed up the realtime clock significantly just in BASIC by writing the following program:



10 POKE 57349,10
20 POKE 57349,10
30 PRINT TI$
40 GOTO 30



(You poke the same address twice because the counter is expecting you to do so. It expects least significant byte followed by most significant byte of the 16-bit count value. The address 57349 is memory mapped I/O for counter #1). Usually the values POKE'd in would be (in decimal) 12 followed by 123 (which creates the 16-bit decimal value of 43200). So i've decreased the values, arbitrarily, to '10'. This then creates a very fast count and the PRINT of TI$ shows the hours and minutes and seconds very quickly speeding up.


A strange thing happened however when I transported this idea to machine code using my Zen assembler. I simply carried out:



LD (&E005),&0A
LD (&E005),&0A



and then I had a bit of buffer code (as per arnoldemu's previous example in this thread) to read back the counter values. But it didn't seem to have changed the speed of the counter at all this time. It remained counting at 1Hz. I don't know if this is due to the Assembler environment protecting me from making changes to the 8253 as the assembler uses RST38 to breakpoint my code (so probably doesn't want me messing about with anything which might make changes to interrupts). Very odd though. Will have to write the source code out as object code and load it into the MZ-80A outside of the assembler environment to be sure, I think.

redbox

Quote from: kelp7 on 15:34, 13 May 14
This is fine though because 31.5Khz is clearly plenty of speed for me to start using interrupts with the screen to create fx

You've got the key, you've got the seeeeeeeeecreeettt  ;)

Hope this works eventually...!

kelp7

#86
Heh :)


Thanks!!


After a little more playing around last night I worked out a couple more things. There was no point in me trying to buffer to memory the results of reading counter #2 as the 2Mhz speed of the CPU is too quick to even notice the change in value. But that wasn't the main problem that resulted in what I was seeing described in my last post. I made a stupid assumption that when you switch the machine on it automatically kicks off the realtime clock straight away. Nope.... you have to CALL a kernel sub-routine to set the time at which point the control words are written to the 8253 etc etc and *then* it starts counting down. So... having written my code again with a little CALL at the top to set the clock off, everything was absolutely fine!! I've finally cracked it haha. God, I'm such an amateur. But as an amateur I think I'm doing okay :-) So, now I know how the 8253 works, no problems. I even managed to buffer counter #1 and saw all the values changing nicely recorded in memory. I also proved that I could speed up the count by sending new values to Counter #1 (and then reading the two bytes from this counter continually to the top-left of screen memory so could watch the changes happening live!).


I'm all set now to code a very quick timer countdown (hopefully around 50hz or something) to try and create an interrupt of my own on the screen!


[EDIT : Oh and I realise why my original tests were working okay in BASIC, because BASIC sets up this realtime clock ready to go without the user needing to do anything]

redbox

So by calling the kernel to get the RTC working, does this mean the 30khz interrupt isn't running until you do this?

I can't imagine that the interrupt isn't always running - there must be a jump to it somewhere in the RST code...?

I say this because of course with the CPC you can patch the 300hz interrupt jumpblock to redirect to your own code (and you can also query the interrupt to give you the 6 breaks per 50hz frame).

kelp7

#88
Absolutely. I think there is no interrupt unless you really want to use the RTC. By default the MZ is set into interrupt mode 1 when the kernel (or rather Monitor as it's actually named) is started up. So this jumps to address &0038 (which is set un-changeably to jump to address &1038) when an interrupt occurs. You have no interrupt unless the RTC has reached zero (zero seconds left in the 12 hour AM/PM change-over). The interrupt mask is wired into the output from Counter #2 of 8253.


Now, I could obviously be completely wrong about this. As I say, I'm an amateur really, just starting out with all this. (I've never programmed interrupts before at all). But if you look at page 173 of the MZ manual which I have uploaded here:


http://www.mediafire.com/view/7fxd48ujn9t64ia/mz80amanual.pdf


You will see the circuit block diagram of the 8253 (and also 8255). You'll see that it seems the interrupt mask is hard-wired to the output of Counter #2. (There's a little note saying "INTMASK").


My intention (to get my screen fx working) is to set Counter #2 to a value of one (so it only has to count once to generate my interrupt). Then I will set Counter #1 to a very fast (50hz presumably, if i can) count down. When this is finished counting it will signal Counter #2 to count down as well (from one to zero) and my interrupt will happen. My interrupt code will probably have a little bit to sync with Vsync and then do a few things at the top of the screen. I've also thought of keeping an index table of different Counter settings to ensure that one interrupt will set up the counter for perhaps another interrupt to change the screen again later....


(Hope what I'm saying makes sense!!)


[EDIT : also I say "un-changeably" above because address &0038 is in ROM not RAM. But you can actually switch out the Monitor to a much higher address (I think it's &C000) which makes it unusable as a kernel but that doesn't really matter I suppose if you're writing your own interrupt routines etc etc I hardly ever use the kernel sub-routines anyway so far).

kelp7

Also, just started putting this together:

Sharp MZ-80A Secrets (memory map sub-page of my site)

(for my own use mainly  :)  but will build it up a bit over time and maybe try and make it look better)

kelp7

So, last night I managed to create my first interrupt! Very basic but I just set the RTC to literally about 20 seconds left before the default interrupt occurs (for AM/PM switchover), changed the vector at $1038 to become a JP to my own code (which simply printed the character 'B' on the screen), left an infinite loop running which didn't do anything at all then waited 20 seconds. After 20 seconds my 'B' appeared :-) So that's all excellent. Next time you hear from me I will have my own specific interrupt set up to create a screen effect.....

arnoldemu

Nice.

BTW, you can use interrupt mode 2 in the same way as we use it on the amstrad.
You can then position your interrupt handler where you want without needing to move the rom.

redbox

Quote from: arnoldemu on 09:23, 15 May 14
BTW, you can use interrupt mode 2 in the same way as we use it on the amstrad.
You can then position your interrupt handler where you want without needing to move the rom.

I did wonder this (have been looking at interrupts lately)  :)

kelp7

#93
I actually didn't quite get how Mode 2 worked when I read up about it. Any easy to digest info on-line? (This is the one where you have a table of jump addresses, is that right?) That would be much more useful than my own interrupt code in Mode 1 manually setting up new interrupt positions!


[EDIT] : P.S. I don't actually need to move the ROM. The $0038 vector is in ROM but it jumps to $1038 which is in RAM. So I don't really have any qualms about using $1038 to jump to my own code elsewhere in RAM.

kelp7

All good, i've just found source/im2ex.asm :-)
Thanks

redbox

Quote from: kelp7 on 11:59, 15 May 14
All good, i've just found source/im2ex.asm :-)

Yes, also here and here.  Also, this is interesting.

I've seen it debated before that IM2 doesn't always jump to I*256+databus AND &FFFE as defined in the Z80 documentation, but sometimes just to I*256+databus.  Might be something to bear in mind...!


kelp7

Ok, nice one, thanks! I shall now bury myself in this task and will hopefully report back soon!

kelp7

Just a quick one : Can the LDIR command be interruped by an interrupt?


gerald

Quote from: kelp7 on 17:16, 18 May 14
Just a quick one : Can the LDIR command be interruped by an interrupt?
According to Some features of the Z80, yes.

kelp7

Excellent, thanks :-)

Powered by SMFPacks Menu Editor Mod