7th of April 2022 – a diversion to infinity!

Inspiration from: https://www.youtube.com/watch?v=c6B9tyUg3CE
From Ludum Dare 48, on itch.io: https://keepee.itch.io/infinity-dive
From the original idea: https://cutelifebot.github.io/sierpinski/


Originally posted to Vectrex Fans United in October 2021. I had this “idea” lying around for some time… and always thought it would be cool to do something similar on the Vectrex.

Yesterday I started for the first time a thought process on how to implement this… more or less a mindless doodling on paper.

After letting my mind rest for a few minutes… hours… I started Vide.


In general
The first step is always Prototyping!
No optimization what so ever.
This comes later, when the program “works”.

Protocol

Random thoughts and actions that happened when working on this little project…

In Vide I generated a project “InfinityDive” – with just an empty “hello world” program.


Some more trains of thought …

I started to think – what kind of functionalities are needed?

  • Vectorlists, that are scaleable, mutable with the ability to add/split/remove single vectors
  • a “zooming in”
  • a “moving” with joystick
  • split vectors in randomized “halves”
  • join vectors

Thus I created function dummies for those.
The thought in these firsts steps was, that the main loop would essentialy be:

mainloop
  JSR waitRecal
  JSR intensity_5f
  JSR Joy_Digital
  JSR displayLists
  JSR doZoomStep
  JSR doMove
  JSR doSplit
  bra mainloop

(leaving out the “join” vectors by now, since I don’t think they are needed anymore)

Than another train of thoughts started – how to implement the lines needed? It couldn’t be an “ordinary” vectorlist – since vectors needed to be split up, replaced, zoomed and moved around the screen.

And as it often happens I remembered stuff I did before and decided to reuse “objects”. They served me well in “Release” and in “Vectorblade” why should they be usable here?
Certainly the objects must differ from the other implementations… but it should work. The objects are held in memory (RAM) as a linked list, so “inserting” (or splitting) should be easy to accomplish. Since it is all RAM anyway, changing is also not a problem.

Next thing I did… I opened Vectorblade and copy/pasted the “enemyObject” list.
The structure changed to simply:

            struct   LineStruct 
                ds       MODE,1                       ; RESET to zero and reposition . draw line . end . 
                ds       Y_POS,1                      ; A 
                ds       X_POS,1                      ; B  
                ds       NEXT_OBJECT,2                ; 0 = last element
            end struct 

One line thus consists of 5 bytes.
1) a mode byte
I probably need a mode byte. Possible conceivable usages:

  • draw line
  • reset to 0
  • move

2) y_pos, x_pos
A delta position to draw a line to

3) next objects
Making this a (one way) linked list of objects

(Since I probably use a “pulu” to access the object information, in the back of my mind I think of pulling the “CC” register as the mode byte. If done correctly the mode switch is damn easy, just a beq, bmi, bvs… for the different modes, if I associate each mode with a flag!
And this is the ONLY way I can pull 3 different BYTE registers from a stack but this will possibly explored later in the optimzation part.)

Fundamental LIST functions

The functions taken from vectorblade stay essentially the same:
initObjectList()
newObject()
destroyObject()

and I added an additional functions to handle add objects to the list:
addObjectAtY()

initObjectList()
The objects are implemented such that I have “object” memory reserved in RAM (at the time of writing for 170 objects – a maximum of 170 lines). These objects are initialized to a linked list and are listed in a list called “list_empty_head”. These are all UNUSED objects.

newObject()
This function returns one object that is currently unused. Removes it from the “list_empty_head”, but does not add it to the “used” list yet. It returns (and sets the zero flag) if not successfull.

addObjectAtY()
Add such gotten object to the list_used_head (objectes in use, that are displayed) AFTER the object given in the Y-Register. If Y=0, than it replaces the head of the used list.

destroyObject()
Removes the given object from the “used” list, fixes the “used” list and adds the object to the “free” list.


Thoughts wandering…

In order to have a “clean” display – I think ALL vectors and moves must be done with the same scale.
(this also might open later possible optimization options)

I probably should have an “easy” way to include predefined vectorlists to the object scenario. Like the “original” (see video) did, when achieving certain scores. Also the START of the game should probably be some sort of vectorlist.

Thus I implemented a function:
insertVListToObjects()
This takes an ordinary vectorlists and converts it to an object list and inserts it into the list.

Hm. How to handle “moves”?
The above function added the list to the objects – but it starts at 0,0 and is thus off center!
Time to look and think about the “mode” byte.

After the thought process I have currently following implemented:
a function called addMovesToList().

As said above I probably must have the same scale for everything. Naturally that scale can not be 0x80 – that would be far too slow.
Also I think anything below 0x10 would be “wasted”. I’ll probably settle at something about 0x20 or so…
but that is not decided yet. Still – in order for a move to reach the screen edge… and doing so with a fixed scale, I need multiply moves.

The addMovesToList() adds a number of moves with the current scale to the object list. As of now this is done by adding two special objects:

  • a reset0Ref object + counter
  • a moveTo object, which is executed “counter” times

But this needs TWO objects instead of one – which feels a waste of (object) space – but it works.

(optimization thoughts:
As of now I only need two “modes”

  • continue draw
  • reset to zero and moveto

With a fixed scale large enough to reach the vectrex edge with 7 moves – I can squeeze all the information into one “nibble” (half byte).
If this is done correctly, than I can still use the CC register.
bits 4-7 must stay fixed (entireflag, IRQ(s) and half carry – can’t really be used).
but
bits 0-3 are more or less free usable, if I take the “N” flag as the only mode flag, like
N=0 continue
N=1 zero and move

Than I could use the flags in bit 0-2 as a counter (0-7) and this puts ALL information in that nibble.
EVEN if I later should have need to implement a mode for a move without resetting to 0 before, I can
use the special case N=1 with all other bits (0-2) set to zero – to indicate a single move without zeroing.
)


Implemented so far are most of above functions, except the ones needed for the “game”,
namely:
JSR doZoomStep
JSR doMove
JSR doSplit

are not implemented.

But the VLists can be added and moved and displayed. Displaying three “vectorlists” with a linecount of 165 with a scale of 30 works and is display in about 25000 cycles.
This is VERY unoptimized, since every:

  • move is a moveTo_d()
  • draw is a DrawLine_d()
  • reset is a Reset0Ref()

… about the slowest functions the BIOS has to offer :-).

to be continued…

Tagged on:

2 thoughts on “7th of April 2022 – a diversion to infinity!

  1. Peer

    Nice. I did not know Infinity Dive before. Took me quite a while to figure out the goal of the game and how to play. Likely because I am old. As usual, interesting write-up of yours. I especially like the non-digital paper with your notes and sketches. I work the same way. Always having a real piece of paper and a pen with me to note down ideas. Looking forward to reading more about your adventures.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.