|
Post by gtoal on Nov 12, 2017 20:41:03 GMT -5
Am I right in thinking that the 50Hz timer used for frame timing actually generates interrupts?
If so, would this work? instead of programs drawing vectors directly to the screen, they would *always* add the draw commands to a display list buffer, and then on the timer interrupt, code would draw that display list to the screen and RTI.
The display list would either have to be double-buffered or updated atomically in some way so that a partial command was never executed.
I'm not suggesting a multi-tasking OS - there would be only one foreground process as now - just that it would not do any direct drawing and all drawing would be done in the timer interrupt code.
This would create a system similar to, say, the Asteroids hardware, or if you go back far enough, a PDP9/15 with a VT15 vector terminal. It would decouple game activity speed from frame refresh speed...
Having created such a mechanism, the best coders for Vectrex could optimise the display list code and then everyone would benefit!
Graham
|
|
|
Post by gauze on Nov 12, 2017 23:51:29 GMT -5
remember that schematic for the Logic Board I posted before? check how pin 3 /IRQ on the CPU is wired ...
|
|
|
Post by gtoal on Nov 13, 2017 4:29:11 GMT -5
remember that schematic for the Logic Board I posted before? check how pin 3 /IRQ on the CPU is wired ... OK, so FIRQ is connected to the sound card, and NMI and IRQ only go to the edge connector. I guess that means that what I'm asking about would only be possible using a custom cartridge No - wait a minute - it just means it can't be done under a timer interrupt. However you can still test the timer register, do all your graphics from the said displaylist and return to the code. You would have to write the procedures that did the drawings (ie adding to the displaylists) as coroutines... harder, but doable... G
|
|
|
Post by Malban on Nov 13, 2017 4:49:32 GMT -5
Yes and no. VIA and its Timer can generate interrupts. For the 50Hz WaitRecal the interrupt itself is not used. The generation of interrupts can be switched on/off (on the VIA side) - per default they are switched off.
Nonetheless the interrupt flag is set accordingly (even without interrupt generation). This is what the WaitRecal is checking... Whether the interrupt flag for T2 timer occured - if so the 50Hz border was reached (either by your code before the WaitRecal or within the wait loop inside the routine).
There actually ARE games which "calibrate" using interrupts - most notably are the imager games. They are controlled by the Interrupt generated by the imager (Joytsick port 1 button 4 -> CA1 line). [BTW FIRQ is connected to Joyport 0 button 4]
For "non optimized" drawing such a vector pipeline might be feasable. For optimized drawing I very much doubt that it will be of much use... my reasoning: - often one uses different drawing routines (with mode information, pattern information, etc) - vectors can be drawn with different scales (and with same scales) - vectors can be drawn with different intensities
A global vector routine should support these cases - which slows it down for all drawings where these features are not needed.
Moreover in a global vector drawing there also vector "moves" must be made. Often times the move time can be used for short additional calculations (even in "C"). This would not easily be possible. One could write a dispatcher for these "in move" calculations - but than again goes the overhead of that dispatcher.
From my own experience - if you are going for performance - you must have tight control over the moves and vectors you draw and move and draw only the things necessary for the current vectorlist you are processing. "generalizations" slow things down.
The BIOS is great at generalization - and much can be done with it. But even the BIOS features (dunno...) 10 different drawing routines for different vector types (not counting the moves).
Still - I think it would be possible... at least as a "showcase"...
Malban
PS Had a look at the board schematics. IRQ only connected to cartridge "seems" wrong. VIA can defenitly generate interrupts - otherwise Imager games would not work (CA1 interrupt).
|
|
|
Post by gauze on Nov 13, 2017 8:22:26 GMT -5
my interpretation is the "cpu irq bus" is tied to +5V and is always high so it's impossible to have any IRQ in that context anyway. You are right this might be "wrong" Malban. on VIA6522 it shows its /IRQ tied to RAM, but not which pin(s) from Address or Data pins (assuming it's not /CS, /WE, VCC/VSS|GND) so my assumption would be you set some write some bit and VIA gets interrupted that way, but address or data bit? I might just be asking a question here
|
|
|
Post by thomas on Nov 13, 2017 11:02:50 GMT -5
Doesn't 'bedlam' use a vbl interrupt routine for game code, too ? I seem to remember this but am not certain, it's 2 years now since I disasm'ed it..
the Asteroids display hw isn't really decoupled from the game - it slows down once it needs to show 'too many' life ships. Eventually it crashes, even, which is why marathon gamers need to stay below 2xx lives. So a forced break once in a while to eat/drink/go to the restroom while the extra lives are killed off..
|
|
|
Post by gtoal on Dec 24, 2018 2:14:42 GMT -5
I've been thinking about this again, having been working on a "No BIOS" program this weekend. In terminal mode, and for the embedded ARM-hosted emulations, Thomas is already doing something similar in the VecFever - with the 6809-side code which handles the drawing for whichever processor is actually running the game (Presumably his code to draw the display file is all non-BIOS code. The entire BIOS RAM should be at our disposal apart from the interrupt vectors.) So a very crude sketch of a Vectrex supervisor might look something like this:
--------------
PROGRAM START:
disable interrupts
set interrupt vector (CBF5 or CBF8) to cause jump to "MAIN LOOP"
set timer for now + 1/50 sec; enter user code with t2 interrupts enabled
MAIN LOOP:
disable interrupts
reset timer 2 for now + 1/50 sec
draw display file
return to user code with t2 interrupts enabled
USER CODE:
main code loop as normal. draw routines replaced by code to build display file. No Bios calls.
If the program needs to exit, jmp [FFFE]... --------------
If the drawing can be done in less than 1/50 sec, there is time left over for the game code to run. If it completes within the same 1/50 sec, all is well. If not, the last frame will be repeated on the screen without flicker. If the drawing takes longer than 1/50 sec, there is no time left for the game code to run and it will return to the drawing code immediately, with the result that the game appears to freeze. This can be worked around by having the supervisory loop extend the drawing period beyond 1/50 sec if it detects this happening, but it should walk the delay period back down to 1/50 sec as soon as the drawing is simplified enough to take place within that time. Ideally you do not want games to generate more graphics than can be displayed within one frame cycle but if it does happen it should degenerate gracefully and recover if possible. It is up to the programmer to write code that on average runs at full speed. Occasional missed updates (duplicated frames) may be allowed but if it happens on a continuous basis, the game is not acceptable (unless it is a low animation game such as chess, scrabble etc)
|
|
|
Post by D-Type on Dec 24, 2018 4:30:01 GMT -5
Yes, I keep thinking about this sort of thing as well, if you're using the 6809 just as a Vector Generator and running the game on another processor then having a VL buffer could be quite neat.
You could then use a multi-tasker on the other processor if you wanted as it would have the power to manage it. With a VL frame buffer, it'd be like programming a C64 with sprites only!
Another thought I had was to create a separate cart with another 6809 on it, so the Vectrex could be the Vector Generator and the other 6809 would be the game processor, then you'd still be using 6809 skills to create the game. Then you could put that 6809 on an FPGA and swap it for 6502 or Z80 and you could go on forever.
Then I try to stop thinking about it because don't have the time to do any of this before I retire, and that seems quite a way away. The Forth community is full of oldies spending their time making cool stuff!
|
|
|
Post by Malban on Dec 24, 2018 6:44:49 GMT -5
If you are using only Vectrex hardware - I don't see the advantage. If you are using additional hardware - it might be worth a look. Personally I very much prefer original vectrex hardware (no extra processor, extra RAM/ROM is ok though :-) - dunno where exactly I draw the line). Its a "personal" thing I guess - on the one hand I very much like the things Thomas did with "his" static binary compilation, and it surely is a cool thing for the vectrex. But on the other hand for me it feels a bit like "cheating".
---
That said... - to point 1 "I don't see the advantage". Why use an interrupt at all (and all the trouble that might come with it)? Especially if you have to check if the time you needed for drawing was exceeded? Just check the interrupt flag - if expired than it has exceeded and you react on it - if not, wait in a loop till the display round is over. - and that is EXACTLY what the WaitRecal function does. Everthing else is more or less the organization how you program your "game". Feel free to put all vectors you want to draw every round to a RAM location and draw them at the end. It will NEVER be efficient though. The "trick" to be efficient with the drawing is - "run" your game code WHILE the vectors are drawn or being positioned. And you will not be able to do that if your drawing code does only that -> drawing.
If you are dead set on programming a "dispatcher" of some sort - I would suggest the exact opposite of what you are saying. - write your game code in little dispatchable pieces, of say 20, 50 or 100 cycles each - while your drawing code runs "idle" - dispatch those code pieces (depending on the cycles your wait loop lasts)
If you are drawing (not moving) these dispatches must be cycle exact the same every round (otherwise the vectors will shake a bit).
---
That also said... One other truth remains: - you will never be able to program a universal dispatcher or drawing system that is more performant than specialized code for the game you want to run
The BIOS is "universal" but slow. There probably (with more ROM space now available) can be faster versions of "universal" drawing routines.
But I dare to say, that no "BIOS" you (or anyone else) can come up with will be as fast as the specialized routines in Karl Quappe, Release, Thomas games, Kristofs games. The routines (and the intermediate usage of idle time) we came up with are pretty damn fast.
Therefor - if you have a neat game idea, I'd suggest to program the game as easily as you can (BIOS for starters), than optimize. If the optimization is not good enough seek help, learn more - perhaps it is impossible, perhaps not.
---
On the other hand - if this is a philosophical debate - and you want to be able to "simulate" a seperate vector drawing unit, yes kepp going. It will probably work. Also multitasking will work.
The thing is it will not be efficient, only a proof of concept. For games with little graphic needs it will work.
Regards
Malban
|
|
|
Post by gtoal on Dec 24, 2018 11:36:38 GMT -5
As to the 'why'... primarily to simplify the environment for new coders. Instead of redrawing the whole area every time, you would just place your sprite on the screen once and then modify its x/y location to move it. Also you get flicker-free graphics when these conditions are true: graphics can be drawn in < 1/50s AND (graphics + gameplay) > 1/50s. The system redraws the display for you while your logic is catching up. To handle that under the default model involves complex code structuring as described above by malban. In this model you do still slow the game down (about the same as before) but you don't flicker. Looks like a win to me!
There are two ways the display list can be updated: the first and obvious one is to double-buffer, but in that case you either have to copy the entire buffer *or* you have to generate the entire buffer afresh each time which defeats one of the advantages of this method. The other way is to modify the data structure in situ, which means being careful with the order of updates and deletes, and making some operations such as setting a new x and y atomic. This is how the VT15 worked and how I'd prefer to do it. I think it can be done with careful ordering and maybe not require true semaphores.
Once a basic implementation of this worked, you could increase the frequency of the interrupt, while doing less graphics on each interrupt, possibly up to the level of true multitasking while the beam is drawing as suggested by Malban above. But even without that I think the simplified model might appeal t coders.
And at that point you might want to structure user code into more than one process, so that different game actions were implemented by actual threads, eg a process for each ghost in a pacman style game.
Also writing a multitasking OS is fun and really someone should have tried it by now :-)
|
|
|
Post by gtoal on Dec 24, 2018 15:55:17 GMT -5
Malban - the example of chess is a perfect example of how this structuring helps make coding easier for a new Vectrex coder... you set up the display file to draw the current board, then the 'thinking' code can do the game state search in the natural way without having to break out of the search tree frequently to draw screen updates. In fact the programmer should be able to completely forget about a 50Hz 'game loop' with computation in small segments, and program actions sequentially like a terminal program.
The more I think about this the more it seems worth trying. Just need to work out how to do a register context swap from C :-) It shouldn't be a big project, I bet the main code for a demo will be less than 100 lines.
OK, I've said all I'm going to on this subject. If I post again it'll be after I've got a working demo. First I need to think of a suitable game to make it worth the effort...
|
|
|
Post by D-Type on Dec 24, 2018 16:46:43 GMT -5
I have a MPE 6809 Forth with a built in multi-tasker, the multi-tasker is probably less than 100 lines. It's the cooperative type, you could set to have one task drawing and others running the game code. I've not tried using it yet, as it's commercial. Alternatively, to find out more about how to make a simple multi-tasker in Forth, try here: www.bradrodriguez.com/papers/mtasking.html Yes, it's Forth, but it's pretty low level and it's the concepts that are most important. Brad's example is coded in about 60 lines of assembler and Forth at the end of the article. I have some chess games I'd like to to port to 6809 Vectrex, initially via terminal display, maybe then via vector monitor. One is a 6809 version of Sargon I extracted out of a FLEX package, the other two are a simple Forth chess program I found and one called Brainless chess, also in Forth, but a bit more serious, it has a rating of about 2300 and was written by one of the DeepMind AlphaZero team that beat Stockfish recently. Brainless is probably never going to work, 1.5MHz will probably be too slow and 1k RAM may also be a problem. Sargon needs 2k, unless I can hack it to use less, or make a RAM expansion. Simple chess might work easily. But even for a chess program, the draw routine will still take lots of CPU, it would be best to blank the screen a la ZX80/81 or at least shrink it during calculation.
|
|
|
Post by gtoal on Dec 24, 2018 23:58:25 GMT -5
|
|
|
Post by gtoal on Dec 25, 2018 0:06:13 GMT -5
A stray thought: surely this C environment doesn't implement signal, longjumps etc etc? It didn't even occur to me to consider those. Are they supported? I.e. can we handle timer interrupts with clean C code and no embedded asm???
|
|
|
Post by Malban on Dec 25, 2018 3:58:06 GMT -5
I dont see why you can not program interrupts in C. Thing is you enabled only VIA INTERRUPTS at least you must also enable interrupts for the microprocessor. Some CC flag ... dunno by heart. Im at my mothers place - being XMas and all :-)... Malban PS scroll to "Interrupt" at vectrexc.malban.de/documentation/gcc-6809-documentation
|
|