Apologies for the n00b question, but I really would love to have a bit of help understanding the bit of code at the top of this page:
Synchronising with the CRTC and display - CPCWiki (http://www.cpcwiki.eu/index.php/Synchronising_with_the_CRTC_and_display)
We're loading the b register with $F5 and immediately afterwards using the IN opcode which specifies the c register. I understand that it implies using the address held in the BC register pair. Why does the code not need to load the c register with any value before doing this? Presumably there could be garbage in the c register at this point? Or does it not matter when the address range begins at $F5xx
Thanks for any help understanding this!
kelp
The Z80 places BC on address bus when doing IN or OUT.On CPC , only high byte is decoded for PPI so you can have garbage in C.
ld B,#F5
.sync
in A,(C)
rra
jr nc,.sync
Will wait the start of the Vsync but logicaly you can do :
ld B,#F5
.sync
inc C,(C)
rr C
jr nc,.sync
Perfect, thank you for the very quick answer!
kelp
Actually, is your inc C,(C) correct in the 2nd example?Also, I do have another question. Is it ever useful to wait for HSYNC? Can you wait for a specific line of the display to make changes while the beam creates the image? Or can you only do this at the top of the screen, not an arbitrary location any line down the screen? I presume it uses up a lot of CPU time...
Quote from: kelp7 on 14:17, 07 February 14
Actually, is your inc C,(C) correct in the 2nd example?Also, I do have another question. Is it ever useful to wait for HSYNC? Can you wait for a specific line of the display to make changes while the beam creates the image? Or can you only do this at the top of the screen, not an arbitrary location any line down the screen? I presume it uses up a lot of CPU time...
I think this is a mistake and should be
in c,(c)
it's not possible to wait for hsync.
What you need to do is wait for a specific interrupt.
Then use NOPs to burn cycles until you get to the position you want.
Now you can change colours.
Like this:
http://cpctech.cpc-live.com/source/hwintdelay.asm (http://cpctech.cpc-live.com/source/hwintdelay.asm)
Look on Unofficial Amstrad WWW Resource (http://www.cpctech.org.uk) and look in the "source" section and under "source-interrupt".
Lots of examples here show various methods for waiting for using interrupts, and the delay example shows how you then burn cycles to get to a point after an interrupt.
Look for this too "Border colours indicating the interrupt position and how the vsync relates to the position".
If you use a ctc-ay hardware device you have programmable raster interrupts.
In addition you can have programmable raster interrupts on the amstrad plus.
More examples on my website show this. (no example for ctc-ay based interrupt yet).
I think that tells me everything I need to know! :) Thanks very much for your help both of you...
kelp
Actually, there is just one last question and I'll be happy :)
On the CPC, what is the trigger of the interrupt? Is this different for different Z80-based computers?
thanks!
Quote from: kelp7 on 17:34, 07 February 14
Actually, there is just one last question and I'll be happy :)
On the CPC, what is the trigger of the interrupt? Is this different for different Z80-based computers?
thanks!
The Gate-Array chip inside the CPC counts HSYNCs. When it counts 52 of them it triggers an interrupt and then resets the counter.
so each interrupt is 52 lines apart. There are 6 per frame. One coincides with the vsync.
The spectrum for example has a single interrupt happening at vsync time. That is it.
The Amstrad plus can have raster interrupts where you can say which line it will interrupt on. To use this you need to activate the special features first.
The CTC-AY hardware for the CPC has a timer chip inside it. You can define the frequency at which it interrupts. One of the triggers is HSYNC, so you can tell it to count x number of hsyncs and interrupt.
It depends on the hardware that is in the machine generally.
Thanks so much for your help arnoldemu (and for being patient with me!). This is a massive help for me!
Cheers
Kelp
Quote from: kelp7 on 21:09, 07 February 14
Thanks so much for your help arnoldemu (and for being patient with me!). This is a massive help for me!
Cheers
Kelp
no problem at all :)
If I can make my examples more friendly please tell me and I will add extra comments/information.
Quote from: kelp7 on 14:17, 07 February 14
Actually, is your inc C,(C) correct in the 2nd example?
my bad , typed to fast , lol , yep it is in C,(C)
Heh, no probs. I thought that might have been a mistake ;-)
Out of curiosity, is there any reason to use IN instruction rather than LD instruction. Seems you have to do more preparation with IN than if you just used LD A,($xxxx) ? More clock cycles?
Simply because LD and IN/OUT are not in same logical space, LD is for memory space (ram/rom) , IN/OUT for I/O space ("active" chips), they share data and address buses but behaves on a diffrent way (memory are just storage and other chips have a specific behaviour).Some computers like Plus series uses IO mapped in memory space but the "active" chips have to be fast.
I/O instructions are slower because Z80 has to 'wait' other chips that are slower than CPU.
Very interesting, thank you! That explains a lot.
Hi again,
I think i've come up with one more question on this subject ;D
When you consider the code of your main game loop, would you wait for vsync to begin and then carry out all the main game code. Or, a different scenario : would you perhaps wait for vsync to finish, carry out all operations that aren't related to the screen (such as key / joystick movements, calculating opponent AI etc) and then wait for vsync to begin and carry out all screen operations? What's the best method?
Thanks
Kelp
Quote from: kelp7 on 22:39, 03 March 14
When you consider the code of your main game loop, would you wait for vsync to begin and then carry out all the main game code. Or, a different scenario : would you perhaps wait for vsync to finish, carry out all operations that aren't related to the screen (such as key / joystick movements, calculating opponent AI etc) and then wait for vsync to begin and carry out all screen operations? What's the best method?
Usually best to avoid graphics routines during the frame time where the screen is displayed as otherwise you can get flicker or tearing.
If this isn't possible, you can double buffer - i.e. have two screens (one displayed, one hidden) and you draw to the hidden one and switch between them each frame.
Oh yeah definitely! That I understand without question. What I mean is, do you separate out your game code so that all the non-screen updating code happens during screen draw (when it doesn't matter) and then do all screen-updating code during vertical flyback ? Or do you just try and fit all of your game code into the vertical flyback?
I know this is probably a silly question!
Quote from: kelp7 on 09:04, 04 March 14
Oh yeah definitely! That I understand without question. What I mean is, do you separate out your game code so that all the non-screen updating code happens during screen draw (when it doesn't matter) and then do all screen-updating code during vertical flyback ? Or do you just try and fit all of your game code into the vertical flyback?
You wouldnt get a lot of graphics drawn during vsync, it doesnt take that long! :) Check the image halfway down this (http://www.grimware.org/doku.php/documentations/devices/crtc) page under the CRTC heading, Vsync lasts just 3 characters out of a frame that's 39 characters long (or high, if you prefer, I think of it as cpu time though).
But you dont need to restrict graphical operations on the visible screen to during vsync, you can do them whenever you like, so long as the beam isnt updating that part of the screen at that very moment. So if you have a 20 character high play area, then looking at that diagram of the screen I linked, you'd still have almost half the screen worth of cpu time where you could write anywhere you like to the play area and it wont be visible as flicker or tearing. After that though, you'd need to start considering Y ordering your sprite updates, double buffering, or whatever other approach suited your game.
Quote from: kelp7 on 09:04, 04 March 14
Oh yeah definitely! That I understand without question. What I mean is, do you separate out your game code so that all the non-screen updating code happens during screen draw (when it doesn't matter) and then do all screen-updating code during vertical flyback ? Or do you just try and fit all of your game code into the vertical flyback?
I know this is probably a silly question!
I use the hardware double buffering so I have an entire frame for all my game logic and drawing.
If I didn't use that, then I would draw my sprites from bottom to top (minimises tearing), and composite sprites to an off screen buffer before drawing.
EDIT: I do wait for vsync in my loop, but generally that is more for synchronisation rather than to decide when to draw.
I hope some of this helps. Please feel free to check out my website to see examples of some of these:
Unofficial Amstrad WWW Resource (http://www.cpctech.org.uk)
Thanks both of you! Yeah, that makes a lot more sense now and thanks for the links!
The time that should be more interesting is not the vsync time but the time where there are borders.
You can safely do screen updates during border time. In reality this reduces to the time you have during the bottom and top borders. Doing useful time during the side borders is possible but harder to code.
The time where the borders are active depends on the screen size (which can be altered by programming the crtc). The time is much longer than the vsync.
In addition, you can potentially do work during the graphics display if you work "behind" the screen refresh. This means waiting for the graphics to start drawing, then doing your drawing code. The time between vsync and the first graphical line is quite a bit of time. The "waiting" time could be used for ai, keyboard, sound updates that kind of thing.
But, to make this latter method work you need to do everything with 1 frame or you'll get flickering.
Okay, time to confess, (as I already have in private message to arnoldemu) : I am actually trying to code Z80 for another machine other than an Amstrad CPC. I should have stated this at the start of the discussion. I came here because there is little to no help anywhere else for the machine I am coding for. I'm pretty much on my own here. I'm programming the Sharp MZ-80A which I feel has been grossly under appreciated and never had the kind of games that I think it has the potential for. Sorry, I wasn't trying to deceive anyone but just felt a bit embarrassed that there was nowhere else to ask.
That said, should this thread perhaps be moved to the 'other retro' forum perhaps?
I still very much appreciate the help I have been given so far. It's all given me a deeper understanding of how to code around graphics / vblank etc. The MZ machine has no real graphics of its own, it is all text based. There is a character ROM which contains 255 different symbols (including standard ASCII of course) but this is not changeable. I am trying to work out if I can do some tricks with this machine to enable me to show graphics on the screen in a way that no-one has managed to do before for the MZ.
Luckily, I am working just with a text-based screen, therefore, of dimensions 40x25 so any screen updates are going to be pretty quick. For instance, the screen memory starts at $D000 and if you "LD ($D000),0" you instantly see display code 0 on the top-left corner. So I was hoping that there'd be enough speed on this 2Mhz machine to fill the screen with bytes or do entire screen displays within the time limit given during vertical flyback.
Perhaps, as arnoldemu has suggested privately to me, I need to update the screen behind the beam so that the change is only seen on the next frame. I guess this will require some precise code timings.
Anyway, if I can just get the synchronisation with Vsync working on this machine (it's in a memory mapped I/O address of $E002 at the most significant bit) and prove that it works then this could potentially open up the opportunity of just waiting for Vsync to begin, then burning lots of cycles with NOPs and attempting to alter the display scanline by scanline. That way I could really prove to do some interesting effects that have never been done before.
So thanks again for all your help. I think if I can just master programming this machine and do something great with it that hasn't been done before then perhaps I can move onto another machine, say, the Amstrad CPC ;)
Thanks
kelp
The problem is that you've got only char generator without changes ability.
Maybe you could achieve 80x50 graphics if there are four "quarter squares" in the character set ?
Or go for the sharp mz-800 ?
;)
You could potentially switch between 2 characters. 1 character is shown one frame, other the next etc.
It would flicker.
another possibility is changing colour, you could change colour and perhaps achieve 8 colours in a cell?
this may be possible.
Or even, say goodbye to the graphics, and just change colours?
Have you tried writing random numbers to these mapped i/o? Perhaps you can get it to do something which is not documented?
Is there a picture of all the characters available?
Ok it seems you have two character sets, 1 bit in the "colour" ram chooses this.
The other bits choose the foreground and background colour.
There are some "semi-graphics". The chunky graphics Xifos is talking about.
Potentially you could re-write the colour ram each line if you wanted to achieve some interesting effects.
Hmm, my understanding of the machine (which is only capable of displaying green & black, it's a green-screen display) is that there is an inverse video memory mapped I/O feature. This usually affects the entire screen in one go, so the whole screen goes black on green or green on black depending which I/O address you access. There is also a VGate address which allows you to turn the entire screen OFF but still keep drawing to it, then you can turn the entire screen ON again to see what was drawn (instantly). I think maybe if I can harness this VSync then I can burn NOPS to wait 8 scanlines and swap between inverse video modes, that's definitely something I want to try. Anyway, I won't take up any more of you guys' time on here with this, I know it's not very relevant :-) Thanks again so much for the help. I think I might go off and and start a blog or diary somewhere of my achievements as I go with this machine.
Lastly, to quote from a really good book I have on the machine:
INVERSE VIDEO FACILITY (MZ80A)
The serial video train in the MZ80A is presented to the screen in exactly the same way as in the MZ80K except that the serial video train is gated with a signal (1 or 0) from a 7474 toggle. The toggle uses CS E014 and A0 to switch its output from zero to one and from one to zero. This inverts the video train and so gives the facility to have the screen display as
1. Light characters on a dark background
2. Dark characters on a light background
(Light on dark being standard)
Reading address E014H gives standard video e.g. A=PEEK($E014)
Reading address E015H gives inverse video. e.g A=PEEK($E015)
(the $E014 & $E015 are memory mapped I/O address - kelp7)
it's not based on mz-700??
mz-800 seems better than the one you describe.
It came out before the MZ-700. It's a really basic machine but it was the 2nd computer I ever owned (after the ZX81), I think I may have given myself quite a challenge with it! :-) It's this one:
http://www.old-computers.com/museum/computer.asp?st=1&c=172 (http://www.old-computers.com/museum/computer.asp?st=1&c=172)
I love its design but wow is it ever not geared up for games. Hence the reason I'm trying to get used to how to synchronise CRT with game code because I think some things are actually possible with this machine, they've just never been attempted. (It doesn't really have much love as a computer, very small user-base too which doesn't help)
Also the 'graphic display' they mention on that site is wrong. It is only capable of a 'pseudo-graphic mode' of dimensions 80x50, that's only because of the special character set in the ROM which contains little squares, curves and lines etc (a bit like the default graphic characters you get on the keys on a commodore keyboard). One last word on the machine, if anyone is interested in any more about the computer, i have the manual on my mediafire account here:
http://www.mediafire.com/view/7fxd48ujn9t64ia/mz80amanual.pdf (http://www.mediafire.com/view/7fxd48ujn9t64ia/mz80amanual.pdf)
Ok looking at this you could play with the cursor, both resetting the timer, and also to set a different cursor rate?
You could perhaps generate sound using tape out (if the tape makes sound you can hear that is)
You mentioned normal/reverse so you could try and play with that too.
What is roll up/down?
What is VGATE?
May be interesting to poke that with values.
ok, you can double buffer by using roll? or does it always move down or is it instant?
You could potentially split the screen using roll too.
perhaps changing roll mid-line?
I think you'll need to try on your machine and write crazy programs that poke various values into different places at different times to see what you can achieve.
Potentially you could do 1 bit sampled sound? by changing the timer and outputting to the sound?
Ok, quite a few things to explore...
Yep, thanks arnoldemu for your help and enthusiasm with my questions. I shall have to just try things out. Funnily enough with the screen roll, I've been wondering that myself very recently. It's not something I've played with before but I had just the same question as you (is it an instant flick to the new screen memory area or does it actually roll the display). 1-bit sampled sound, very interesting, not something i've ever mucked about with (sound sampling). I'm not sure I'd know where to start but perhaps you have something on your source code site, I shall take a look.
The VGATE, by the way, is quite interesting. You can switch the entire display off and keep drawing to it, then turn the display on again once drawing is complete. I've seen this used in a BASIC example but not ML. It's something else I've been wondering about, perhaps it's not synchronised with the display hardware so you could switch the display off and on at different scanlines (if code is quick enough) to create interesting effects.
Anyway, you're absolutely right, I just need to write some crazy programs to just POKE all sorts of places and try out different things. Think I'll start a proper blog or website for all this development. One day I should come back to this thread with hopefully some results if anyone's interested :-)
Yes, with VGATE I was thinking, what if you turned this off for a few scanlines, would the display be moved?
If yes, then you could create pixel by pixel vertical scroll.
For basic 1-bit sound. Take a sound from PC, then decide what will be "1" and what will be "0" for each sample. Then store this, 1 bit per byte. In addition, you'll need to downsample so you can play it within the restrictions of the slower hardware.
Now you have a choice, you can either program an interrupt to trigger at a fixed rate (using the 8253) or you can do the cpc way, which is to write a sample to sound hardware, wait some time using nops and repeat. CPC has to do it this way because it's interrupts are not so flexible.
I don't know enough about playing sampled sound to even know where to start. I could convert someone else's code but i'm not sure I could start from scratch. I'm not sure I even understand the theory. I have to admit I'm no expert programmer anyway, I'm trying things out and learning as I go.
As for the interrupts on the MZ-80A, it's my current understanding that there aren't any (except for one interrupt that occurs at a 12 hour interval regarding the clock : The MZ has a proper realtime clock that you can set and presumably there is perhaps an AM/PM shift at 12 hours or something). I'd very happily be proven wrong on that though!
I am going to start playing around properly with the machine itself next week and will start writing up my experiments onto my existing website (sharpgames.atspace.com - very out of date now!). I'll let you know how I get on! Quite exciting really to be forging a new, possibly undiscovered, path on some old 8-bit hardware :-)
Actually, I am really interested in how sampled sound works, especially when there is only a speaker that can, i think, only create square waves over three octaves. When I got my first ever PC it was about 1987 and it was an Amstrad PC1512, this had no soundcard, it just had the PC speaker for beep sounds. Now, despite how basic that was, I had a copy of Wizball for this DOS machine and I distinctly remember the title screen having digitised / sampled music playing, I remember it very well because it was so impressive... to this day I wonder how they did that but I guess a speaker is a speaker, it just pushes it's cone outwards or inwards if you give it the right voltages (presuming here). The speaker in the MZ is just an average speaker too but i wonder just how much control you could possibly have over it really... obviously you can't affect amplitude as you couldn't on the PC speaker, no idea therefore how digitised music could occur without variations in amplitude !?
Quote from: kelp7 on 22:33, 07 March 14
Actually, I am really interested in how sampled sound works, especially when there is only a speaker that can, i think, only create square waves over three octaves. When I got my first ever PC it was about 1987 and it was an Amstrad PC1512, this had no soundcard, it just had the PC speaker for beep sounds. Now, despite how basic that was, I had a copy of Wizball for this DOS machine and I distinctly remember the title screen having digitised / sampled music playing, I remember it very well because it was so impressive... to this day I wonder how they did that but I guess a speaker is a speaker, it just pushes it's cone outwards or inwards if you give it the right voltages (presuming here). The speaker in the MZ is just an average speaker too but i wonder just how much control you could possibly have over it really... obviously you can't affect amplitude as you couldn't on the PC speaker, no idea therefore how digitised music could occur without variations in amplitude !?
The response of the speaker is not digital, i.e. it's not on or off, it takes time for it to move in/out.
Think that a 1 would move it in one direction, a 0 in the other.
If you played 1,0,1,0,1,0 what is the effect? Perhaps 1,1,0,0,1,1,0,0 gives a different effect.
You use this fact to make the sampled sound.
On the cpc, you can send the 1,0 to the sound chip (by using full volume or no volume), or you can send it to the cassette write (the reason is the cassette sound is mixed with the sound chip sound and sent to the internal speaker - this is why that also works).
So you need to both send the values at a specific rate AND decide what values to send.
The basic principle of sampling is that you capture the volume at a fixed rate (e.g. 40000 times per second), then you replay this back at that fixed rate.
Faster or slower will make it sound different.
Now on CPC you can do 4-bit quality samples because it has 16 volume levels in the sound chip. You can do 1 bit quality through the cassette.
If you take the speaker into account and it's response then you can make it sound higher quality.
I think there is an example of how to play 4-bit samples on my website. I don't think there is an example for 1-bit sound.
Wow, excellent. Thanks! So basically I have to decide what the 1 represents and what the 0 represents in the 1-bit sampling example? Do I basically pick a particular frequency for the 1 and another for the 0 ? I think on the MZ machine I can only tell it what frequency to produce from the speaker. Do I then just play the frequencies I've chosen at very small fractions of a second each? (obviously depending on the pattern of bits)
If you want to know more about making impressive sounding 1-bit music, you could do a lot worse than check out the World Of Spectrum forums, as there are quite a lot of very knowledgeable chaps there who are very used to doing the same on the Spectrum's beeper.
Good call! I should have thought of that! I guess because the Spectrum was one of the few popular machines I didn't really get to play around on much in my youth it didn't immediately spring to mind! Thanks for the tip!
Beepola !
Beepola :: Battle of the Bits Lyceum (http://battleofthebits.org/lyceum/View/Beepola/)
Quote from: arnoldemu on 09:48, 07 March 14
Yes, with VGATE I was thinking, what if you turned this off for a few scanlines, would the display be moved?
If yes, then you could create pixel by pixel vertical scroll.
You might be interested to know I have managed to get the machine up and running this evening and after a brief juddery start (the vertical hold on the CRT seemed to be jiggling about a bit, maybe because it hadn't been switched on for nigh on five years or so) I have taken the shortcut route and loaded up BASIC (rather than spend the evening getting back into using Avalon's ZEN assembler), ran a quick routine that poked a one followed by a zero into the VGate memory mapped address and, rather pleasingly, it doesn't seem to be synced at all with the display, there was quite a bit of flicker and tearing etc. So I would imagine this might mean I can turn the display on, burn some NOPs, and then turn it off to get partial displays etc :-) not sure if, like you say, the display would be shifted, rather i think it would be static and just a portion might be missing. Lots more to experiment with but am going to start up a diary on my website of my experiments and developments :-)
Quote from: kelp7 on 22:16, 14 March 14
You might be interested to know I have managed to get the machine up and running this evening and after a brief juddery start (the vertical hold on the CRT seemed to be jiggling about a bit, maybe because it hadn't been switched on for nigh on five years or so) I have taken the shortcut route and loaded up BASIC (rather than spend the evening getting back into using Avalon's ZEN assembler), ran a quick routine that poked a one followed by a zero into the VGate memory mapped address and, rather pleasingly, it doesn't seem to be synced at all with the display, there was quite a bit of flicker and tearing etc. So I would imagine this might mean I can turn the display on, burn some NOPs, and then turn it off to get partial displays etc :-) not sure if, like you say, the display would be shifted, rather i think it would be static and just a portion might be missing. Lots more to experiment with but am going to start up a diary on my website of my experiments and developments :-)
Good. Try changing it many times each line and many times on the screen and at different times on the same line, gives you maximum chance to see all potential effects.
Try the same with the other hardware registers.
Had a quick go last night at trying to detect Vsync, now would you say the following code I used is okay:
LOOP:
LD A,($E002)
AND 128
JP NZ,LOOP
Does that look correct? I was umm'ing and ahh'ing over the JP NZ or whether it should be JP Z.
As background : the MZ Vsync is detectable at bit 7 (MSB) of address $E002
Would it be better to use the method at the start of this discussion? Using a Rotate Left With Carry? (instead of rotate right as it's the MSB not the LSB on the Sharp). Is the rotate method the fastest code for this task? I was just writing the program off the top of my head partly to get used to the Zen assembler I have again and also get back into Z80 (after about 5 or so years away from it).
EDIT : Obviously I realise I should have used JR rather than JP anyway!!
Thanks
kelp
That code is fine.
Yes the rotate method would be slightly faster, but waiting for vsync is unlikely to be the bit of code that takes the longest.
As to if it's nz or z, depends on what the bit shows when vsync is active.
if you're not sure do this:
ld hl,buffer
ld bc,count
loop: ld a,(&e002)
and 128
ld (hl),a
inc hl
dec bc
ld a,b
or c
jr nz,loop
this will "sample"/collect the values into a buffer. you can then view the buffer. The most common value will be when vsync is not active, the least common when vsync is active.
if it's 1 when active, then you need to use JP Z to wait until it becomes 1, or if it's 0 when active, use JP NZ to wait until it becomes 0.
On CPC, it's 1/high/bit set when active. so when active, carry is set, when not active carry clear.
so JR NC, is waiting until carry becomes set and this means vsync is active.
That's an excellent idea, thanks, I will definitely be trying that out. Just out of interest, how does the "LD A,B" and "OR C" work? Is that part of detecting if the count's finished? Just trying to get my head 'round that bit. Loading A with the high order bits of BC and then OR'ing A with C. Perhaps I'll just dry run it myself and see how it works...
Okay, i understand the OR C part now. heh, sorry about that small moment of idiocy. You're basically checking that both B and C have reached zero...
Quote from: arnoldemu on 10:32, 20 March 14
this will "sample"/collect the values into a buffer. you can then view the buffer. The most common value will be when vsync is not active, the least common when vsync is active.
Well, thanks again arnoldemu. I've run that code just now on the machine and have some very interesting results in the buffer. I only have two values (as you would expect). It is either 00 hex or 80 hex. The 80 hex is the more common value! I counted around 434 recordings of 80H in the buffer with around 131 recordings of value 00H in the buffer. So I guess this means that the VSync is a little backward on this machine compared to the CPC! Thanks again for all the help. Much more to play around with now...
I'm not going to fill up your forum too much more with the non-CPC related stuff but just wanted to share one quick thing which I tried this morning. Have just tried the following code:
LD HL,&D000
LD DE,&D001
LD BC,&004F
LD (HL),01
LDIR
LOOP:
LD A,(&E002)
RLA
JR C,LOOP
LOOP2:
LD A,(&E002)
RLA
JR NC,LOOP2
LD A,(&E015)
LD A,(&E014)
JR LOOP
Basically this prints two lines of character 'A' to screen memory, waits for Vsync to happen, then waits for it to finish (so I know I'm on the top line of the display). Then I very quickly switch the inverse video mode on (by reading address &E015) and very soon after switch it back to standard video mode for the rest of the screen display. I wanted to see if I could turn one single scan-line into inverse video mode and then put the rest of the display back to standard video. This does sort of seem to happen. But what I witnessed was a very flickery line of 1-pixel height covering about 80 pixels length (10 letter 'A's). I don't know why it is flickering so much but am glad to see that the rest of the screen from vertical pixel line number 2 onwards are stable and in standard-video-mode. The only thing I can think of (regarding the flickering) is that the hardware simply doesn't like the display being changed more than once per scanline.
Anyway, thanks to everyone for all your help and discussions in this thread, this has put me on a good path to discovering new things with this machine!
Cheers
kelp
Quote from: kelp7 on 11:27, 23 March 14
I'm not going to fill up your forum too much more with the non-CPC related stuff but just wanted to share one quick thing which I tried this morning. Have just tried the following code:
LD HL,&D000
LD DE,&D001
LD BC,&004F
LD (HL),01
LDIR
LOOP:
LD A,(&E002)
RLA
JR C,LOOP
LOOP2:
LD A,(&E002)
RLA
JR NC,LOOP2
LD A,(&E015)
LD A,(&E014)
JR LOOP
Basically this prints two lines of character 'A' to screen memory, waits for Vsync to happen, then waits for it to finish (so I know I'm on the top line of the display). Then I very quickly switch the inverse video mode on (by reading address &E015) and very soon after switch it back to standard video mode for the rest of the screen display. I wanted to see if I could turn one single scan-line into inverse video mode and then put the rest of the display back to standard video. This does sort of seem to happen. But what I witnessed was a very flickery line of 1-pixel height covering about 80 pixels length (10 letter 'A's). I don't know why it is flickering so much but am glad to see that the rest of the screen from vertical pixel line number 2 onwards are stable and in standard-video-mode. The only thing I can think of (regarding the flickering) is that the hardware simply doesn't like the display being changed more than once per scanline.
Anyway, thanks to everyone for all your help and discussions in this thread, this has put me on a good path to discovering new things with this machine!
Cheers
kelp
Interesting that the display starts after end of vsync. There is no border? On CPC there are a few lines of top border before the display starts. The flicker you see comes because of the vsync wait code. It takes a few cycles for each time you check. This means the vsync comes at any time between the rotate instruction the jump relative and the load instruction. This is why it flickers because the position changes each frame. On CPC we use halt instruction to wait for interrupt which comes at a fixed position in the display and then use a number of fixed length instructions to get to the point we want. See if you can do that with interrupts. The length is down to the time for the two load instructions. Put some nops in there to make it longer. So it seems you can turn it on off as much as you want on a line. Try repeating the load instructions to switch back and forth perhaps to make a pattern. We do this with out instructions on CPC to make split raster's etc.
Keep going your finding out what state the vsync is, how long its active for, its position relative to the display and inverse can be turned on and off as you wish.
Quote from: arnoldemu on 17:56, 23 March 14
Interesting that the display starts after end of vsync. There is no border? On CPC there are a few lines of top border before the display starts. The flicker you see comes because of the vsync wait code. It takes a few cycles for each time you check. This means the vsync comes at any time between the rotate instruction the jump relative and the load instruction. This is why it flickers because the position changes each frame. On CPC we use halt instruction to wait for interrupt which comes at a fixed position in the display and then use a number of fixed length instructions to get to the point we want. See if you can do that with interrupts. The length is down to the time for the two load instructions. Put some nops in there to make it longer. So it seems you can turn it on off as much as you want on a line. Try repeating the load instructions to switch back and forth perhaps to make a pattern. We do this with out instructions on CPC to make split raster's etc.
Keep going your finding out what state the vsync is, how long its active for, its position relative to the display and inverse can be turned on and off as you wish.
Thanks arnoldemu, your support has been impeccable and has got me on the right track. I actually got a bit further on this afternoon and modified the routine to only wait for vsync to begin (not to bother waiting for it to finish) to see what would happen and it seemed to make no difference. Just waiting for it to start was enough to do what I needed. Then I added in a typical small loop (LD B,&FF, NOP, DJNZ back to the NOP) and I have actually managed to see something that no-one has ever seen on this machine before now. I had one complete full set of letter 'A' in inverse video and the next line on the screen was normal standard-video-mode letter 'A'. Well, mostly. It stretched into the 2nd line of A's a bit as well obviously as I haven't got the timing right yet. But weirdly the flickery bit was still there. It's not just a random flicker either, it has a sort of pulsing pattern to it, seems to go through a cycle of different patterns of flickery lines (1 pixel height) every 0.5 seconds! Rather strange but I'm gonna push this NOP loop around a bit and see what I get.
Thanks for all your help and hope I'm not 'misusing' your forum too much!
kelp
Oh yeah, and you're correct : there are no borders at all on this machine!
kelp
[EDIT] : also i re-read your part about the reason behind the flicker and I think that would make sense. I guess because I'm trying to maintain a stable image I have to make sure the very same set of events happen once every 50th second. Because the timing is out a bit each time I get the same pattern but it flickers as it changes each frame. Hmm, could be interesting to sort out but maybe you're right and interrupts may be the way to go. Unfortunately the interrupts in the MZ are handled by an 8253 timer chip, never played with one of those before and it looks complex!
Interestingly, things get better if I only wait for VSync to begin and not have a bit of code in there to wait for it to finish as well. Guess it's all down to timing, I've managed to get many splits horizontally at different parts of the screen just by having the right length of delay loop. Here is my first example of code (not yet commented / annotated) on my new site where I'm going to build up a nice library of routines:
Sharp MZ-80A Secrets (http://sharpgames.atspace.com/)
hoping to put graphics routines and sound / music routines up there and it should turn into a reasonable resource for this machine.
Also, I managed to splice a display character with another earlier on, so am starting to really see things that no-one's seen on the Sharp before (as far as I understand). I managed to show the top half of the letter 'A' with the bottom half of the letter 'B' underneath it. All stable too without any flickering...
Congratulations on your success and your new site, I notice you did not mention any help from sharp forums so I went looking for them and found this http://sharpusersclub.org/modules.php?op=modload&name=XForum&file=index (http://sharpusersclub.org/modules.php?op=modload&name=XForum&file=index)
And here someone has announced the uploading of his MZ 80k software library, maybe it would help you further http://www.vintage-computer.com/vcforum/showthread.php?35135-My-entire-Sharp-MZ-80K-software-library-now-online (http://www.vintage-computer.com/vcforum/showthread.php?35135-My-entire-Sharp-MZ-80K-software-library-now-online)
Thanks Steve. Yes, I was a member of the Sharp Users Club for a few years in recent times but time (plus house buying, plus birth of son) meant I couldn't keep up with it any longer. I have been on their website forum for a long time too and it was always slow moving but nowadays there's very little activity whatsoever, hence my wandering around other 8-bit forums which are far more active :-)
[EDIT] : Just trying out different layouts for the website at the moment, have annotated the first listing. Trying to make it look good (!) but am hand coding html in Notepad, heh...
Good to read how your investigations are going! good to hear you have also found a way to split characters!
Hey arnoldemu, I've just uploaded another routine to my site. In this routine I am using "RES 0,(HL)" and "SET 0,(HL)". Reading up on these commands suggests that actually, despite being quite dedicated commands, they use up quite a few clock cycles or T-States. Would it just be quicker for me to "LD A,0" and "LD ($E002),A" ? Or is there a neat way of switching a single bit in a byte on or off which I'm missing that might be quicker?
Thanks
kelp
[EDIT] : Also, is it better just to use "LD A,(&E002)" anyway rather than storing &E002 in HL and then using "LD A,(HL)" ?
Quote from: kelp7 on 09:29, 26 March 14
Hey arnoldemu, I've just uploaded another routine to my site. In this routine I am using "RES 0,(HL)" and "SET 0,(HL)". Reading up on these commands suggests that actually, despite being quite dedicated commands, they use up quite a few clock cycles or T-States. Would it just be quicker for me to "LD A,0" and "LD ($E002),A" ? Or is there a neat way of switching a single bit in a byte on or off which I'm missing that might be quicker?
Thanks
kelp
[EDIT] : Also, is it better just to use "LD A,(&E002)" anyway rather than storing &E002 in HL and then using "LD A,(HL)" ?
I think it would be better to use res/set to switch single bits.
Otherwise you need to read, set it and then write it back which would be slower.
I will compare it to cpc.
LD A,(&e002) uses 4 cycles.
res/set use 2 cycles.
So res/set would be faster *if* you have setup hl before and you're doing lots of these.
I would be interested to know how the instruction timings work on the mz80a.
There is one way to find this out:
inverse on
instruction to test
inverse off
then look at the size of the bar on the screen
repeat this for all other commands.
You should be able to see a rough correlation and in particular you should be able to see if they are all forced to be multiples of NOPs like on CPC.
This will also help you if you wanted for example to make changes 1 line at a time. On CPC we know it's 64 NOPs per line and each instruction is a multiple of a NOP. That means we can count instruction timings and know exactly how much time it takes.
Quote from: arnoldemu on 10:11, 26 March 14
I would be interested to know how the instruction timings work on the mz80a.
I'd be interested in this too.
Nice to see a Z80 computer being investigated - I'd not known anything about the MZ-80A before but it's good to see some new knowledge emerging from this thread.
Quote from: arnoldemu on 10:11, 26 March 14
This will also help you if you wanted for example to make changes 1 line at a time. On CPC we know it's 64 NOPs per line and each instruction is a multiple of a NOP. That means we can count instruction timings and know exactly how much time it takes.
Wow, okay, this is a nice big project for me. I will definitely be doing this! I guess I need to expand out my website a little bit and perhaps have categories (as you do on your own site), would you mind if I based the design of my site a little bit on yours? I'll probably keep the tables and screenshots but having a front page on the site with perhaps Sources, Timings and Other stuff (probably games when I get time to do some!).
One thing I've found is that the screen doesn't seem to like multiple changes on a single scanline (you get a lot of the flickering I was mentioning before) but this might just be down to wrong number of NOPs or whatever. I guess if I'm trying timings of single instructions this could be difficult to see 'visibly' as a line on the screen.... I'll let you know how I get on though!
[EDIT] : oh and I've just uploaded my code for character split
Quote from: redbox on 10:52, 26 March 14
I'd be interested in this too.
Nice to see a Z80 computer being investigated - I'd not known anything about the MZ-80A before but it's good to see some new knowledge emerging from this thread.
Thanks! Yeah, I've been interested in taking my m/l coding further for years now with this machine but could never quite find the time. I think it could turn out to be perhaps a little under-rated and hopefully I can make some games for it too which show what the machine *could* have had in its heyday.
What I'd really like is a really great emulator for the machine but I almost certainly don't have the coding prowess to produce one. The best I've found is Michael Franzen's multi-emulator but I'm still not convinced it emulates all the hardware (particularly as it is made to emulate various machines, not just Sharp).
Thanks
kelp
Struggling a bit with this at the moment. This machine can be a pain to code for with respect to the display. I really really wish it had some interrupts that were tied to the display as you mention the CPC does. I tried the instruction timing as you suggested but see absolutely nothing on the screen. So I tried experimenting a bit and wrote a routine that turned inverse on, then wrote 100H bytes to memory via a pre-setup LDIR (keep setting hl, bc and de in the loop) and then turn inverse off again. See a bar this time on the screen but it flickers. Changed the BC part of the LDIR up and down and get remarkably different results. I think if you don't have exactly the right amount of delay then the machine really doesn't like it.
But that's not the worst part, I think you probably only have enough time at 2mhz to change every alternate line. I suppose that's not such a big set back, I'm pleased just to be able to change anything on this machine's display really.
Anyway, I replaced the loop which sits in-between the inverse on / inverse off with several NOPs (typed in manually to the assembler). Can see a part of a line being turned on but it's very flickery. Ended up typing in 64 NOPs to compare with CPC and get one complete scanline in inverse and a large portion of the 2nd line (which flickers, the 1st line doesn't flicker however). Guess we're talking less than 64 NOPs on this machine for a scanline. It has some very strange properties though because, for instance, if I changed it to 63 NOPs I might not see any line at all! Certain values seem to draw nothing (perhaps it's somehow flickering or drawing in a way that sits the wrong side of the beam perhaps) and then perhaps 62 NOPs will bring most of the line back onto the screen again.
Hope what i've just written makes sense!
I wonder if I should start utilising the 8253 for its counter interrupts? Would that help me in any of these graphics tasks?
kelp
Take a look at the CTC-AY, this will give you all the interrupts you need.
Would love to but not programming an Amstrad at the mo :(
Looks nice though :)
Yes. It's an amazing device. Guess soon we'll have games using it. And the pricing is more than fair.
Yes try using the 8253.
Normally you would setup the appropiate counter with a value.
Set it to repeat mode. (not "one-shot").
Then enable the interrupt so it triggers the z80.
Now setup an interrupt. You may need to use interrupt mode 2 of z80, don't know.
Setup a int routine to do something so you know it's working.
change inverse on/off or something like that.
Quote from: kelp7 on 21:02, 31 March 14
Struggling a bit with this at the moment. This machine can be a pain to code for with respect to the display. I really really wish it had some interrupts that were tied to the display as you mention the CPC does. I tried the instruction timing as you suggested but see absolutely nothing on the screen. So I tried experimenting a bit and wrote a routine that turned inverse on, then wrote 100H bytes to memory via a pre-setup LDIR (keep setting hl, bc and de in the loop) and then turn inverse off again. See a bar this time on the screen but it flickers. Changed the BC part of the LDIR up and down and get remarkably different results. I think if you don't have exactly the right amount of delay then the machine really doesn't like it.
So are you saying that you start with a delay, try to turn inverse on in the middle of the screen, wait a bit, then turn it off a few scan lines later, and you get quite different results with minor adjustments to that initial delay, rather than say, the start and end of the inversion, flickery though it may be, moving a little later or earlier along the lines they are on? Where inverse is flickering, is it the whole scan line or part of it - are parts of the line constantly inverse, or not inverse with a flickery section in between?
I'm just wondering if perhaps inverse can only be set once per line, then I'd suppose it should be the whole line flickering, but if parts of a flickery line are stable as inverse or not, then it's sounding a bit weird if small changes to the initial delay produce quite different results.
Something else I'm wondering about your test code, since I've not used memory mapped IO before, but how does LD A,(&E015) *set* inverse on, rather than simply read the state of the mapped IO?
Quote from: Axelay on 10:49, 01 April 14
So are you saying that you start with a delay, try to turn inverse on in the middle of the screen, wait a bit, then turn it off a few scan lines later, and you get quite different results with minor adjustments to that initial delay, rather than say, the start and end of the inversion, flickery though it may be, moving a little later or earlier along the lines they are on? Where inverse is flickering, is it the whole scan line or part of it - are parts of the line constantly inverse, or not inverse with a flickery section in between?
In this particular case I turned inverse on first thing after waiting for vsync. So the inverse is happening at the top line of the screen. Then do a different sort of delay (using LDIR this time (instead of a decrement-counter loop) to write &100 bytes to memory somewhere) then turn inverse off. I do get some very strange effects. You're right that part of the line can be stable and then the end of the line (or the middle, depending on the delay) can be flickering.
Quote from: Axelay on 10:49, 01 April 14
I'm just wondering if perhaps inverse can only be set once per line, then I'd suppose it should be the whole line flickering, but if parts of a flickery line are stable as inverse or not, then it's sounding a bit weird if small changes to the initial delay produce quite different results.
Yep, this is the bit I don't fully understand. When I substituted my LDIR delay with just a bunch of NOPs manually typed in (rather than a loop burning NOPs because I wanted more accuracy in what I was seeing), I would get quite wildly varying results. Maybe I should just do all this again and take proper notes this time as to what happens! Apologies for my vagueness so far!
Quote from: Axelay on 10:49, 01 April 14
Something else I'm wondering about your test code, since I've not used memory mapped IO before, but how does LD A,(&E015) *set* inverse on, rather than simply read the state of the mapped IO?
Yeah, this is probably a little beyond me now too but essentially you can only read from that memory mapped I/O address, it's not a writeable address. When you read from that address the machine switches on inverse. When you read from the other address (&E014) it switches inverse off. I have a more detailed book at home which I could refer to which may explain this better. I'll post a bit of documentation up later on tonight....
and thanks arnoldemu, I think i'll have to dig further into 8253 and how interrupts work on this machine.
kelp
I realise I already posted the bit from the book I was talking about above earlier in this whole thread, here again is what it says (not much to go on as to why it's about reading those addresses rather than writing to them):
Quote from: kelp7 on 10:44, 06 March 14
Lastly, to quote from a really good book I have on the machine:
INVERSE VIDEO FACILITY (MZ80A)
The serial video train in the MZ80A is presented to the screen in exactly the same way as in the MZ80K except that the serial video train is gated with a signal (1 or 0) from a 7474 toggle. The toggle uses CS E014 and A0 to switch its output from zero to one and from one to zero. This inverts the video train and so gives the facility to have the screen display as
1. Light characters on a dark background
2. Dark characters on a light background
(Light on dark being standard)
Reading address E014H gives standard video e.g. A=PEEK($E014)
Reading address E015H gives inverse video. e.g A=PEEK($E015)
(the $E014 & $E015 are memory mapped I/O address - kelp7)
Quote from: Axelay on 10:49, 01 April 14
Something else I'm wondering about your test code, since I've not used memory mapped IO before, but how does LD A,(&E015) *set* inverse on, rather than simply read the state of the mapped IO?
it all depends on how the hardware decodes the signals on the z80 bus.
For an I/O instruction there will be an address in a15-a0, the data in d7-d0, iorq then rd for read or wr for write.
For a memory mapped instruction there will be address in a15-a0, data in d7-d0, then just rd/wr.
So, it's just different way to interpret the signal.
In addition since this operation is always "yes", you don't need to do a write. You just need to recognise a15-a0 and rd, or perhaps just a15-a0, so a read/write perform the same job.
Reading back the data from a device also depends on 1) if it stores the data 2) it decodes a read and puts the data onto the bus.
So reading to do a write may seem crazy but it's just a mapping of signals.
In fact, it may be possible to also do this:
LD BC,&E014
IN A,(C)
LD BC,&E015
IN A,(C)
@kelp7: Please try this it may do the same.
I already tried the IN instruction before now and didn't get a result. I'll try again to be sure I was doing it correctly but pretty certain the IN had no effect at all.
Quote from: arnoldemu on 09:26, 02 April 14
In fact, it may be possible to also do this:
LD BC,&E014
IN A,(C)
LD BC,&E015
IN A,(C)
@kelp7: Please try this it may do the same.
Just to confirm, this definitely does not work. So I guess this is down to how the machine is hardwired.
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.
@ kelp7 & arnoldemu: Thanks for those replies to my previous post. It's been a very busy week!
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!
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...
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.
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!
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)
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.
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 )
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.
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...!
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]
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).
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 (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).
Also, just started putting this together:
Sharp MZ-80A Secrets (http://sharpgames.atspace.com/useful.html) (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)
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.....
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.
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) :)
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.
All good, i've just found source/im2ex.asm (http://cpctech.cpc-live.com/source/im2ex.html) :-)
Thanks
Quote from: kelp7 on 11:59, 15 May 14
All good, i've just found source/im2ex.asm (http://cpctech.cpc-live.com/source/im2ex.html) :-)
Yes, also here (http://www.z80.info/1653.htm) and here (http://cpctech.cpc-live.com/docs/im2.html). Also, this (http://u6c87.blogspot.co.uk/2011/10/great-z80-interrupt-mode-2-debate.html) 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...!
Ok, nice one, thanks! I shall now bury myself in this task and will hopefully report back soon!
Just a quick one : Can the LDIR command be interruped by an interrupt?
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 (http://cpctech.cpc-live.com/docs/z80.html), yes.
Excellent, thanks :-)
Can you imagine the headache of having interrupts that aren't actually sync'd with anything? As that's what I'm going through right now.
I've not done any coding yet, just trying to get the theory sorted out in my head first. My first theory was that, at the beginning of my program, I would wait for vsync to begin and then set the 50hz interrupt clock counting down (initialise counter #2 with value &0276 which should divide a 31.5Khz input clock to 50hz). Then when the interrupt occurred, wait for vsync and do drawing to screen. Then set interrupt up again.
But if my interrupt code itself always waits for vsync, I wonder if I'm just going to get a lot of missed frames and flickering. Perhaps I should just reset the counter to &0276 the very moment the interrupt occurs? Arghh..... I think ... i'll just have to try some options out and see what happens....
(In fact I don't think I have to reset counter to &0276 as a 'rate generator' mode of counter always returns to the original value you gave it when it reaches zero. But I would have to reset the counter #3 to &0001 to get the interrupt to happen again, otherwise when counter #3 reaches zero it'll go straight through to &FFFF which is very unwanted)
Do exactly as you say, wait for a known position, e.g. vsync, and then setup the 8253.
Now you can rely on it being synced I hope.
As for the counter, there is probably a mode for the 8253 where you can set it to reset it's count and restart.
So it would count down to 0, reload and repeat?
Quote from: redbox on 13:40, 15 May 14
Yes, also here (http://www.z80.info/1653.htm) and here (http://cpctech.cpc-live.com/docs/im2.html). Also, this (http://u6c87.blogspot.co.uk/2011/10/great-z80-interrupt-mode-2-debate.html) 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...!
it's always consistent.
it doesn't do an &fffe ever.
it always takes the value on the bus and uses that as the basis.
So this is why the table is 257 bytes long. In addition because on the cpc the databus value is not fixed, we have to consider all 256 byte values are possible. Then because any is possible this means it can read from any table position so we need the same address each time. So that is why we fill it with a single byte and this is why the address is formed from the same byte for high and low.
Quote from: arnoldemu on 13:07, 21 May 14
Do exactly as you say, wait for a known position, e.g. vsync, and then setup the 8253.
Now you can rely on it being synced I hope.
Yes, I think that's the conclusion I came to in the end. I don't think I need to wait for vsync within the interrupt code itself. Just do it once at the start of the program and given that the rate generator should maintain the same frequency of counting (no matter what happens), i should be fine (I hope!).
The first thing I'll try will be one single basic change at the top of the screen I think and see if I can maintain it in a stable way with one single interrupt.
At a much later stage I envision setting up a variety of different interrupts to carry out minor changes at different vertical positions of the screen. That's really what will be the most useful in games. I guess it'll take some playing around with different 8253 settings to trigger each different position of the screen. So one interrupt would set up the next interrupt etc.
My other worry that occurred to me today was whether there might be slight differences between individual MZ-80As. Perhaps I would get it working on my MZ only to find that the interrupts which worked correctly (as triggered at vsync) might be slightly 'out' for another person's MZ. Horrible.... but.... worst case : I could provide an in-game menu that lets you 'calibrate' the graphics settings to your own MZ....
Quote from: kelp7 on 18:29, 21 May 14
At a much later stage I envision setting up a variety of different interrupts to carry out minor changes at different vertical positions of the screen. That's really what will be the most useful in games. I guess it'll take some playing around with different 8253 settings to trigger each different position of the screen. So one interrupt would set up the next interrupt etc.
Sounds cool, similar to vertical rupture... took the CPC coders a while to work out a way of doing that :)
Quote from: redbox on 20:06, 21 May 14
Sounds cool, similar to vertical rupture... took the CPC coders a while to work out a way of doing that :)
It's the only way I can think of to carry out lots of minor fx on the screen without taking tonnes of processor time away from the main game code ! :) Just have a sort of daisy chain of interrupts where each new interrupt sets up the timing for the next one...
By the way, I actually mean horizontal effects but in terms of their vertical position on the screen. So perhaps an inverse-mode line every 50th line of the display. That sort of thing. I'm not sure I can convince the machine to do anything that splits the screen on a horizontal way, heh, any time i've tried to split a single horizontal line into multiple fx it simply can't seem to handle it and flickers like mad!
Quote from: arnoldemu on 13:07, 21 May 14
As for the counter, there is probably a mode for the 8253 where you can set it to reset it's count and restart.
So it would count down to 0, reload and repeat?
I think that's only for the Mode 2 Rate Generator, here's a bit of info:
Intel 8253 - Wikipedia, the free encyclopedia (http://en.wikipedia.org/wiki/Intel_8253#Mode_0_.28000.29:_Interrupt_on_Terminal_Count)
So, my counter #3 of the 8253 is my interrupt trigger. I will set this to &0001 and it will reach &0000 when counter #2 is finished (the two are interlinked).
I don't believe it's a function of Mode 0 (the interrupt one) to reload itself with the initial value once it has reached zero. However my mode 2 rate generator (for counter #2) does do this.
Just for further info, this is how it's all wired up in the MZ-80A (ignore counter #0 which is for music / sound):
(http://i59.tinypic.com/14btft5.jpg)
(I realise that's not very clear, may scan it properly from my own manual if anyone's interested)
Just a small update : I did actually get an interrupt of my own working with the 8253 end of last week but with problems. I just wanted to do something basic to begin with, so I set up an interrupt which would print the letter 'B' to screen memory and then increment HL (which held the screen memory address) to move along the screen memory every frame. So it would take 20 seconds to fill the screen that way (I guess, 1000 characters at 50 a second). This would happen while an infinite loop of NOPs would just sit there doing nothing. The actual result was that I got a single 'B' on the screen in the expected place but then nothing more. I couldn't tell if the program had frozen / crashed or what was going on. I was using the stack during the interrupt to preserve my HL screen memory address as I was using HL in the interrupt itself to re-set up the 8253 then POP it back off the stack again at the end. My only conclusion is that maybe because I'd forgotten to set up the Stack Pointer perhaps this could have caused the issue (code being overwritten maybe). But alternatively, perhaps I just didn't set up the 8253 correctly. So tonight will be writing the same program again and put some debugging code in to print the counter values to the screen (to make sure they keep rolling over once the interrupt's occurred). I guess at least I did get one instance of the interrupt occurring successfully, so something was right in my code!
as with any interrupt handler make sure you push/pop the registers you are changing.
Also do an ei before the ret.
on mz80 you may need to do:
ei
reti
Perhaps you didn't setup the stack correctly, so make sure that it is in a good place in ram.
:)
Okay, that is all excellent advice :) thanks! I didn't realise I might need to execute an EI. I think I'm okay with the usual RET (as that is what they use in the Kernel when the RTC interrupt occurs). But, my problems are more difficult than that. I've just written the program again and this time the main program loop just dumps the contents of the counters to the screen. Counter number 1 is working correctly, it is very fast and re-loads its value at the end of each count automatically. But Counter number 2 (which is the one which should toggle between 1 and 0) doesn't re-load (or latch) its value. Annoyingly it looks as if it counts from 1 to 0 and then to 65535..... argh.... i will get to the bottom of this though. I even put in some specific 'latching' control word when programming Counter 2 but that doesn't seem to have had an effect as far as I can see.
I don't have any experience with the 8253, but I do read your findings with much interest. :)
So, what you described here corresponds to MODE 0 where the counter continues the count after it reaches zero:
http://www.intel-assembler.it/portale/5/Programming-the-Intel-8253-8354-pit/howto-program-intel-pit-8253-8254.asp (http://www.intel-assembler.it/portale/5/Programming-the-Intel-8253-8354-pit/howto-program-intel-pit-8253-8254.asp)
If you can you should change the counter mode.
Thanks for that. Yes, I can see now that maybe I just need to re-check the code I'm writing. My interrupt handler was set to re-load Counter 2 (the one that is mode 0) but maybe I was doing something wrong. Will try again this weekend :) Lots of testing to do with different counts etc I think, then I can work out exactly what's happening. I presume that I do have to use Mode 0 on counter #2 as that is the one which generates the interrupt. The output from counter #2 is AND'd with the interrupt mask, so I need to have a counter mode where the output is low all of the time except when the counter reaches zero.
Anyway, lots of playing around to do! Will report back (and will actually put my code in here too on my next posting if I'm still getting problems, always good to have other people checking the code!)
Okay, i've got it working. But not in the way that I want to. In the end, I went back to priming the counters by CALLing the kernel routine which sets the RTC. Then my program started working! I wrote a simple interrupt handler that would print the letter 'B' to the screen memory address currently held in HL, then INC HL and return (after setting Counter 2 back to '1' having become '0'). Counter 1 was set to a 50hz count. So every 50th of a second it printed a 'B' to the screen.
So it seems there is something I'm not doing in my own code which the kernel *is* doing. Cannot see what this is! They're doing one extra load of counter 1 with a strange value of &000A and I don't know why. Then later in the same routine they set it to the correct value for doing their 1Hz count for the RTC. Other than that, I wondered whether I needed to set the interrupt mask (which is bit number 2 of address &E002). So I put a SET command in for this bit at this address but my code didn't work.
Very weird. Guess there's something I'm missing. So that's the first thing I need to sort out. The 2nd thing is that reading up on Mode 0 of the 8253 : it seems to take 1 single clock count before it starts counting. So annoyingly if I set up a count at 50hz when I reset the counter (after it's reached zero) it'll take another clock cycle to start up again, so it's going to be more like 49hz....argh..... so my second problem is resolving this matter. I think maybe at some point I will be trying a different mode for Counter 2 (like MaV suggested above) and perhaps choose the Square Wave mode.
Still, need to sort out the fact that my own code doesn't work first and work out what the kernel is doing differently :)
Yeah, okay, I had stupid moment (not the last I'm sure and certainly not the first). I woke up this morning thinking about the fact that I hadn't actually used the DI or EI instructions hardly at all. I used 'EI' once at the end of my interrupt handler. But it seems the MZ doesn't start up with interrupts enabled. So the reason the kernel routine (for setting the RTC) allowed my own routine to work is because the kernel routine enables interrupts. Right, glad that's out of the way because now I am able to set up my own 8253 control words and counters and I've tested my usual print routine this morning and it functions exactly as I had always expected! Here's the program if anyone's interested:
LD SP,&4000 ; arbitrary address somewhere in RAM (doesn't matter for these purposes)
DI
LD HL,&1039 ; set up new interrupt handler address
LD DE,MYINT ; address of my new interrupt handler into DE
LD (HL),E ; by default the CPU jumps to &0038 on interrupt
INC HL ; this is in ROM on the MZ and always jumps to &1038
LD (HL),D ; 1038 is always a JP instruction
LD HL,&E007 ; load HL with control word address of 8253 chip
LD (HL),&74 ; Set counter #1 as a rate generator
LD (HL),&B0 ; Set counter #2 as an 'interrupt on terminal count'
DEC HL
LD (HL),&01 ; Least significant byte of counter value for counter #2
LD (HL),&00 ; Most significant byte of counter value for counter #2
DEC HL
LD (HL),&76 ; Least significant byte of counter value for counter #1
LD (HL),&02 ; Most significant byte of counter value for counter #1 (divide input clock of 31.5Khz down to 50hz)
EI
LD HL,&D000 ; first memory location of screen memory
LOOP: NOP
JR LOOP
MYINT: LD (HL),&02 ; print letter 'B' on screen in current screen memory location held in HL
INC HL
PUSH HL
LD HL,&E007 ; re-setup counter #2
LD (HL),&01
LD (HL),&00
POP HL
EI
RET
This all worked fine. All that I have to sort out now is the fact that, although I have set the clock to 50hz, the program took roughly a minute to fill the screen with the letter 'B'. This I can only attribute to the fact that Mode 0 of the 8253 means you have to wait a full clock cycle before the counter starts counting down again. So, as I said in my above post, it will essentially take an extra frame before it prints the 'B' again.
I'm hoping I can resolve this by switching Counter #2 to a Mode 3 counter which is a square wave. With this toggling the output on and off it should still produce the interrupt, then I have to work out the correct value for Counter #1 (which is used as Counter #2's input clock).
All good though so far!! :)
Phew, more playing around this afternoon with different 8253 modes. I thought I could get away with a square wave generator. But, of course, the output remains high for half the period of the input clock. So naturally, all of the time that the output is remaining high it triggers the interrupt. So I managed to fill the screen with 'B' in an extremely fast time (as fast as the interrupt can be triggered basically). Tried many combinations so far, even setting Counter #1 as a rate generator itself but can't quite get my head round this at the moment. There must be a combination of 8253 modes here which will just pulse 'high' every 50th of a second!!! Going to take quite some work to sort this out I think.
And I realise why it took roughly 60 seconds with my previous routine to fill the screen. I bet the 8253 takes one full clock cycle to start counting, and then because i've just set the counter to '1', it then counts the '1' for one clock cyle and then finally there's one more clock cycle when it is at zero? maybe!
Somehow I'll get a 50hz interrupt out of this thing if it kills me!!!!!
Just realised that if I use a square wave (or any other 8253 mode where the output is high for a long period) I could just set my interrupt handler to do what I want it to do and then point the interrupt vector to a new handler that just waits for the output to return low again and sets the original vector back up again :) getting the hang of this.....slowly....!
After much thinking I've realised that the above wouldn't be any good as it would tie-up any potential game program etc for too long per frame. So..... alternatives:
1) The first interrupt handler ends by executing a DI and therefore disabling all further interrupts. Then the game code has a part of its own main game loop that checks for the state of the square wave counter, once we know it's outputting a zero again (output gone low) we can re-enabled interrupts (EI)
OR
2) Go back to basics and just use the original set-up of a 'rate generator' on counter #1 and a 'interrupt on terminal count' on counter #2 and just try and come up with a working set of rates for counter #1 that produce what I'm after. Maybe daisy-chain some interrupt handlers and the very last interrupt handler (towards the end of the frame) then waits for vsync to begin and sets up the first 8253 values again ready to jump back to the 1st interrupt handler again as well.
Heh, not sure how clear that is but these are the only feasible solutions now I think!
(I realise I have been quite enthusiastically wordy in this thread recently, will probably just get down to the coding now and have something working next time I write in here :) )
Haven't yet been able to get the square wave generator of the 8253 working properly. In the end I've gone back to using a rate generator on Counter #1 to clock the Mode-0 "Interrupt-on-terminal-count" Counter #2. I've managed to get the correct rate for a frame by going for a 150hz clock into Counter #2. It seems that's what I need for my 'actual' 50hz screen frame rate. Using 150hz I now fill the screen with 1000 characters in just 20 seconds.
That was a good breakthrough, just working out how this 8253 chip does its counting! Basically, if I load Counter #2 with a value of '1' it first waits one full clock cycle (one 50hz) before starting to count, then it spends the next 50hz counting from '1' to '0' and finally one further 50hz once it's reached '0' before triggering the interrupt.
I've now been coding a screen changing interrupt (inverse mode on for eight scanlines and then off). This has been a REAL headache though. My code waited for vsync to begin and then triggered the 8253 counters with 150hz etc etc. But what I got was extreme flickering all the way across the screen. So my fixed height block of inverse colour just moved its vertical position all over the place. I got some very neat effects however by altering the amount of Hz on my rate counter. At one point I had achieved smooth scrolling (both up and down the screen) of this fixed height bar of inverse. Looks great but not really what I want.
It's going to be really really difficult to get this interrupt sync'd properly I think. In the end, I have stuck a 'wait for vsync' in the interrupt handler itself. Once I did this I could get a stable bar of inverse at the top of the screen where I first wanted it. The sad thing about this is that it wasn't actually stable until I actually changed the rate generator on Counter #1 to 366hz!!! This is crazy and will tie up any program I want to write. I actually wrote the object code out to a file and loaded the file into BASIC to see if it worked and created my inverse bar at the top of the BASIC screen. It did indeed. But you could also no longer use BASIC at all! It didn't respond to keypresses or anything.
I imagine this is all down to the fact that my interrupt is not sync'd properly with the start of the frame. So when I wait for vsync in my interrupt handler it may already have started drawing the full frame. So it has all that time to wait before then waiting for vsync to begin again before drawing my bar.
Need to get this 366hz down to the approx. 50hz somehow!
Arrghh... headache time. Well, the only thing I can come up with is to make sure that I trigger my interrupt as close to the end of the frame as possible so that when it carries out its wait-for-vsync it doesn't have much time to wait for this. I guess this will have to happen when I set up the counters in the first place. Make sure they don't finish counting down until we are very close to the end of the frame.
Oh well, onwards........
(also forgot to say that, obviously with the way the 8253 counters work, the 366hz is more like 122hz when the interrupt actually occurs. But still this is very nearly treble what I need anyway. Gotta get this 366hz down to 150hz so that there is plenty of time for the main software to still function! At the moment the 122hz interrupt basically happens so frequently there is no time for anything else, especially as the interrupt handler waits for vsync)
Kind of got this to work last night, all with interrupts.
It was a struggle though and I'm still at a confusing place with this but so far I have written a routine to turn on inverse-video mode for 8 scanlines using just the 8253 interrupt. The process was:
1. Set up the 8253 counters to a low value to launch interrupt handler #1 soon as possible. Spend the rest of the time waiting for key-press to exit this whole program.
2. Interrupt handler #1 waits for vsync then switches on inverse mode. Sets up 8253 counters for 8 scanline wait and sets up new vector to interrupt handler #2
3. Interrupt handler #2 switches inverse-video mode off and sets up 8253 counters and vector to re-trigger interrupt handler #1
Now, this sort of worked. I had to play around for ages with different values of counters to get what I needed. I even have, in the end, had to use a mixture of 8253 delays and manual delays (a very small 8-bit decrement counter). Strangely the 8 scanlines were very very flickery to begin with and did seem to extend way beyond 8 scanlines on certain frames. All very strange.
In the end, I got it stable (and only 8 scanlines high) by forcing interrupt handler #2 to launch interrupt handler #1 as soon as possible.
This is not a good thing and suggests that the flickering may have been caused by the 8253 not even being fast enough to carry out the changes I was asking of it within one frame. I can't believe this is the case though. The clock input to the 8253 is 31.5Khz. Even taking into account the fact that this is then divided down (by the effect of using a mode 0 counter triggered by a mode 2 counter), I should still be left with enough of a clock cycle which is faster than the usual 50hz of a frame.
Lots more experimenting to do. Next, I will try and do a second 8-scanline high bar of inverse somewhere else on the screen as well and see how it copes!
Great to read that you are making progress with the interrupts.
Assuming the built in monitor is outputting something close to 50hz.
The line time *may* be 15Khz. The 8253 has an input clock of 31.5hz so it's close to twice per line.
It's edge triggered, so a value of 1/0 would probably be 1 scanline.
So 4 is probably 8 scanlines.
This is all guesswork.
Do you know of the refresh rate and the line frequency of the internal monitor.
Is there a document that describes it?
Ah, that explains that then. There is no way it's possible to get the full 31.5Khz clock rate when you are using two inter-connected 8253 counters. I might get a third of that speed at its fastest.
Perhaps there might be the information you need here:
http://www.sharpmz.org/mz-80a/download/80asm.pdf (http://www.sharpmz.org/mz-80a/download/80asm.pdf)
And, as ever, always appreciate your advice arnoldemu :)
Quote from: arnoldemu on 13:52, 10 June 14
The line time *may* be 15Khz. The 8253 has an input clock of 31.5hz so it's close to twice per line.
According to google :) 15Khz divided by 300 is 50hz. There aren't 300 lines on the display though, only 200. You can't really access individual pixels on this machine but if you could then its resolution is 320 x 200
I shall be trying two sets of 8 pixel-high inverse bars tonight ;)
:) Success!!
I decided to just try and create three separate inverse-mode bars on the screen as quickly as the interrupt can possibly be launched (so that I could see how many scanlines between changes there were). I (eventually, see below) managed to get three bars of inverse on the screen and it looks like I get about 4 scanlines between each interrupt with the 8253 firing the interrupts as quickly as it can (with a touch of tearing and flickering but that can be sorted out).
However, initially I only saw two of my horizontal bars. I presumed that the first bar was happening so quickly that it was occurring within the vsync and therefore not part of the frame draw. So I changed the 8253 counter in my first interrupt handler (which set inverse mode on) to trigger the 2nd interrupt handler after a larger amount of delay (I put &0040 into counter #1). As expected I saw all three bars then but the really weird thing was that I had a big gap between my first bar and the other two bars.
Here's a mock-up of how it looked:
(http://i57.tinypic.com/bgry34.jpg)
I wasn't expecting this. I would have expected to see a very large bar of inverse-mode but not a large gap of non-inverse. (The delay was only to wait a while for top of screen and then switch inverse off just after the top). There was no delay in the 2nd interrupt handler to keep inverse-off going for any length of time.
My only conclusion is that the 8253 counters are the culprits. When I set the first interrupt handler to wait &0040 before triggering the 2nd interrupt handler, this probably worked okay and waited with inverse on for long enough to come out the other end of the vsync. Then when I set the counter in my 2nd handler back to &0002 it must have stayed on &0040 for another clock cycle. This is the only conclusion I can come to. I do believe when you load a new value into an 8253 counter it does take a full clock cycle to make the change. If anyone can come up with another reason then I'd be glad to hear of it!!!
But I really am progressing well now with this and it's mainly thanks to you guys and the help you've given!
Cheers
kelp
[EDIT : I forgot to say that I was loading the rate generator with a value of &0002 rather than just &0001. I wasn't sure if I could use &0001 or not as the 8253 rate generator mode counts from n to 1 and it never reaches 0. Perhaps this is something else I need to experiment with]
And just to finally confirm : You cannot put the value &0001 into a rate generator (8253 mode 2 counter), it simply does nothing (not even count at all as if it is frozen). I was hoping it might simply allow the full input clock rate (31.5khz), I presume I can only get half this perhaps (counting from &0002 to &0001).
One very last thing : I have corrected the timing issue caused by needing to wait for the 8253 to register a change to its counter values (and therefore have stopped that big blank section of screen between my 1st and 2nd inverse bars). Solution was obvious really. I was waiting for vsync and setting my counters to a bigger value than I normally would just to get past the end of vsync to the top of screen, then setting to lowest value to switch inverse off (but it would take full clock cycle of 8253 to do this). Instead, I have changed it now to wait for vsync, switch inverse on and then wait for vsync to finish before setting up lowest value counter!! This works great and I can get 4 scanlines of inverse at the top of the screen and four lines of non-inverse before the next interrupt occurs. Perfect! :)
(I suppose the only issue with this is all the waiting around for vsync status during which the main program can't do any processing. Swings & roundabouts really)
Just updated my website with the working code for a working piece of interrupt triggered inverse (along with screenshot), look right at the bottom of the page. :) (Will be tidying up the website properly soon)
Sharp MZ-80A Secrets (http://sharpgames.atspace.com/)
(quite a long bit of code but it takes the least amount of time away from main program code as it can). Got it working in BASIC as well which was good to see, will add an additional screenshot of that shortly.