I am still going strong with “Telengard”. I transcoded nearly the complete game – only a few lines are remaining. Although I started up Vecci – I have as yet not implemented any graphics into the game.
The code alone right now is about 37kB (bin file) (again: no sound, no graphics, no intro…).
The pure game code is quite huge – but this also includes (which is not needed with a “normal” computer BASIC environment) the floating point routines, which also nearly take up about 8kB.
This logically means, that the “final cartridge” will be at least 64kB – I hope I can fit all that is planned into that space.
The route I wanted to follow is the same as with Akalabeth – it should fit into 64kB so that a normal PB6 banked cartridge can hold the complete game. The “advanced” version will be a “vectorblade compatible” flash PCB, which will enable saving the game. Well that is my goal anyway.
Changes done since last report:
- changed “C” code to be “bankable”…
- fixed bug in floating point routines
- added all “specials” (Pit, Throne, Cube, Fountain etc…)
- added all Treasure
- added “number” input
- generalized display()
- generalized input()
- cleaned up the complete string package
A few words to each of the above…
Changed “C” code to be “bankable”…
Before, the project was a “normal” one bank C project, I changed the complete project to be “bankable”. More or less what is needed for that is explained: here – including a general code cleanup.
Fixed bug in floating point routines
This was a bug done by myself when porting the MS BASIC floating point routines. The “int()” function was only half working. From a certain size on (> about 100000), the conversion resulted in an internal error.
Added all “specials” (Pit, Throne, Cube, Fountain etc…) / added all Treasure
This was just “work” transcribing 100th of lines of BASIC code.
Added “number” input
A “generalized” input routine to be reused to enter arbitrary numbers (1-65535) including info text and min/max values, like:
n = inputNumber("ENTER SOME NUMBER\x80", 1, 10000);
The number is entered via joystick (left/right choses digit, up down increases decreases digit).
Generalized display() / input()
Look at following code section:
This section “explains” what I mean by “generalizing”. Usually with vectrex code you do not (can not) code like above.
You can not just “print()” something on the screen, you can not just “pause()” and also cannot just “testForButton()”. Because the vectrex screen needs to be actively redrawn every 1/50 of a second.
But the above code is very clear in what it is supposed to mean, easy to understand and actually easy to write, especially if you transcribe “BASIC” to (Vectrex-) “C”.
The trick (or generalization) I did, was to make sure I only have ONE single position where the screen is drawn, namely a function:
In that function all normal display round functions are called (including joystick/button handling).
Also everything game related is drawn on the screen with the function “drawMap()” and messages (received with printMessages() and than buffered) are drawn with displayMessages().
With that fully working in place a pause function can simply be realized with:
… or the input function:
All this seems easy and logical. But somehow it never occured to me before to code like that.
… and if you have not either… than take a hint :-).
… and the next BASIC program you transcode… will be so much easier!
Cleaned up the complete string package
The whole “string” package is a mess. The gist of the problem is, that the gcc6809 does not support ellipsis paramters (like in printf(char* fmt, …) ).
Because of that and the fact that in simple “C” you can not overload functions with different sets of parameters, makes all the string conversion functions a mess.
For various reasons I need string format functions for:
- insert an integer: like “your strength increases by %i”, value
(this with 8bit signed/unsigned, 16 bit and even 32 bit)
- insert a string: like “you cast the %s spell”, spellname[i]
- quite a few variations thereof
For that I needed to code:
- itoa(): integer to ascii (8bit)
- ltoa(): integer to ascii (16bit)
- lltoa(): integer to ascii (32bit)
- combinations with signed / unsigned and with spaces instead of “front” zeros and to shorten the string if spaces are in front
Additionally: “each needed format variant”
- _fs(): format a string into a string
- _fi(): format a (8bit) int into a string
- _fi_s(): format a (8bit) int into a string (delete leading spaces)
- _fi_s_s(): format a (8bit) int into a string (delete leading spaces and signed int)
- _fsi(): format a string into a string and an integer (8bit) into a string (in that order)
- _fsi2(): format an integer (8bit) into a string and a string into a string (in that order)
- _fl(): format a (16bit) int into a string
- _fl_s(): format a (16bit) int into a string (delete leading spaces)
- _fll(): format a (32bit) int into a string
- _fll_s(): format a (32bit) int into a string (delete leading spaces)
Yes – certainly some parts can be reused. But overall this is really, really ugly. But this is more or less only internally ugly. “User” wise it is usually just one format function to use:
Any “single” function that could take care of it all – would probably be a “hell” to set up and to use.
The way it is – at least it is readable…
Sprites, as seen above… I started “converting” sprite images.
I started of by using “pixelated” vector graphic, as in:
I find the look quite “retro” and I like it (although the line count is TERRIBLE).
The alternative would be to “straighten” the lines, as in:
Which also looks ok.
But actually – personally I think I prefer the “retro” (pixel) style.
Straightened lines look much better IMO.
I don’t know if the program structure/flow lends itself, but why not make the screen updates on 50Hz interrupt? It just feels so much more elegant to do it that way, once you get it working. (Of course, I love the benefit of having the screen refreshed whilst I stop my program via the terminal, change stuff and continue, but that’s an acquired taste!)
a) The thing is with an interrupt driven approach. If the screen refresh takes longer than 1/50 of a second – the program gets stuck. If you can’t ensure its ALWAYS drawn in less than 1/50 of a second, you build yourself a time bomb.
… or you would need to write an adaptable interrupt routine, one which measures its own “time need” and ensures that the next interrupt is e.g. 5000 cycles away….
b) For the dragon – yes, the lines look also good – for most of the other sprites… not so good (imho)
Yes, if you can’t guarantee everything gets drawn in time, it hangs. You would need to do lots of testing and possibly prioritise (i.e. skip) drawing items or do the cycle check, both of which are awkward. Personally, I’d prefer to keep 5% of cycles in my pocket than risk missing the 1/50th slot, so I still like the interrupt approach. I also like the cycle counter in the ISR idea though, I’ll have a think about that.
Regarding interrupt-driven screen updates, I was thinking about this a bit more and I think a simple flag set by the non-interrupt code would solve the problem. The interrupt handler would read and reset the flag. If it hadn’t been set by the non-interrupt code, the handler could simply RTI or gracefully degrade the display, perhaps not print some stuff or interlace it. Clearly interrupt-driven display refresh is more useful for a program where processing could take more than 20ms, but I like the concept of graceful display degradation better than overrunning the 20ms timer, even for regular games, it’s just more elegant. Maybe the non-interrupt code could increment a counter, which in turn could degrade the display more in a controlled way. But that’s getting a bit complicated.
You are right, this is a working and cool concept.
More or less this is what I do on the PiTrex baremetal front. It works great – but it not the same.
Speed and RAM restrictions count heavily on the vectrex side.
There IS overhead doing it the interrupt way – (buffering of data… being careful not to use vectors generated to RAM – which might be gone by the time the interrupt kicks in etc).
But for “easy” beginner stuff this could be fun!