HiUxLife: A Hires Life for the Unexpanded VIC-20.

Basic and Machine Language

Moderator: Moderators

Post Reply
Snial
Vic 20 Newbie
Posts: 14
Joined: Fri Jan 05, 2018 2:51 am
Location: UK
Occupation: firmware engineer

HiUxLife: A Hires Life for the Unexpanded VIC-20.

Post by Snial »

HiUxLife is a hi-resolution (160x160) version of Life for the Unexpanded VIC-20.

Image

I really wrote it in order to experiment with both 6502 coding (of which I haven't done a great deal) and as an experiment in coding a bitmapped program for an Unexpanded VIC-20. The .prg and .s file are zipped as an attachment here, but can also be downloaded from:

https://sites.google.com/site/libby8dev ... UxLife.prg

and

https://sites.google.com/site/libby8dev/home/HiUxLife.s

Usage:

Run the .prg, it generates a bitmapped random starting image and then runs life for successive generations displaying the generation on the bottom left-hand corner up to 99999 generations. If you press a key from 1..9,A..F it'll regenerate another random display with cells filled at a probability 15/16 down to 1/16. The default setting ( which is equivalent to pressing C) will run for over 3000 generations without reducing to simple oscillating patterns.

Description

As we know, it's (just) possible to fit a small machine code program with hi-resolution graphics on an Unexpanded VIC-20. By limiting the screen to 160x160 and using double-height characters we need 200 bytes for the video and 200*16 = 3200 bytes for the character set, implementing a full bitmapped frame buffer. In theory this leaves us with 4096-3200-200 = 696 bytes for the machine code, though this is not quite true as we'll see.

The unusual trick here is to move the video memory to $1000 even though we're using an unexpanded VIC-20. So, initially the .prg contains the initial line of BASIC which calls the machine code and the machine code is set to start after the beginning of where the video memory will be, $10c8.

Thus the memory map looks like this:

Code: Select all

;$1000            $10c8       $1380        $2000
;[Basic/Video:200][MCode:696b][Chrs:200*16=3200]
It's not quite true that we have to make do with 696b of code. With careful initialisation we could allow code to run into the bitmap area, but I don't exploit this in this program, instead the code here is 686 bytes long (the entire .prg is 886 bytes), leaving 10b spare.

Normally Life uses two whole arrays to represent the current and next generation, but HiUxLife just uses one array (the bitmap) plus a copy of the current row and previous row. This is possible because each new row of cells is dependant upon only its own row, the one above and the one below. Since the current one is being modified and the one above is already modified we must cache the original unmodified versions, but the row below hasn't been modified so this can be read directly from the screen.

In addition, the top row must also be saved at the beginning of a generation because it contains the row below when calculating the bottom row. The cached data is stored in the cassette buffer.

HiUxLife also makes a couple of other optimisations: the first byte of every cached row is also copied to the end in order to avoid having to perform special wrap-around tests or indexing. Also the 9 bytes surrounding the current byte's worth of cells is cached again so that it can index over the rows and thus save space in the calculations. Perhaps I ought to revisit this and do it a different way.

Currently each generation takes 4.2s to compute. The current generation is displayed at the bottom - by explicitly copying the bitmap from the character table in ROM (all the characters are really double-height in hires mode).
Attachments
HiUxLife.zip
(5.66 KiB) Downloaded 66 times
User avatar
Mike
Herr VC
Posts: 4816
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: HiUxLife: A Hires Life for the Unexpanded VIC-20.

Post by Mike »

Nice!

Doing a hires 160x160 Conway's Life on an unexpanded VIC-20 is quite a feat! :D
Snial wrote:HiUxLife also makes a couple of other optimisations: the first byte of every cached row is also copied to the end in order to avoid having to perform special wrap-around tests or indexing.
It still has quite funny semantics though when lifeforms straddle the sides: a 2x2 block transforms into a glider ... :wink: ... I'd suppose you also need to copy the *last* byte of each screen row to the position *before* the cached line so you have 22 bytes per row.

...

Welcome to Denial!

Cheers,

Michael
Snial
Vic 20 Newbie
Posts: 14
Joined: Fri Jan 05, 2018 2:51 am
Location: UK
Occupation: firmware engineer

Re: HiUxLife: A Hires Life for the Unexpanded VIC-20.

Post by Snial »

Thanks for the feedback.
I'd suppose you also need to copy the *last* byte of each screen row to the position *before* the cached line so you have 22 bytes per row.
Effectively, the code should already be doing that.

The code caches the previous and current row, but this is in turn cached into 3x3 bytes where the actual computation takes place:

Code: Select all

TopLeftByte    TopByte TopRightByte
   LeftByte CentreByte    RightByte
BotLeftByte    BotByte BotRightByte
The cell to be analysed is always bit 7 of CentreByte and on each pass the 3x3 bytes are shifted left 1 bit. So, after 8 bytes the right most bytes will be cleared, ready for the new right-most bytes to be copied. At the beginning of each row the left and centre bytes are actually initialised from the last two bytes in the prev, current and screen rows.

Code: Select all

prevRow+kCol-1 prevRow+kCol ---
currRow+kCol-1 currRow+kCol ...
(ptr),y=1      (ptr-3072),y=33 ...
So, initially e.g. prevRow+kCol is a copy of prevRow+0, so this means the first column is initialised with the first column and the one to the left of it, the last proper byte in the row.

Then at the beginning of each block of 8 cells the Right most bytes are copied from prevRow+colPos+1, currRow+colPos+1 and normally (ptr),y=161. This will work for the top and current rows all the way to kCols-1, because prevRow+(kCol-1) +1 is the 21st byte at offset +20.

However, it won't work for (ptr1),y=161 at the end of a row, because it should be looking at the beginning of that row, which it does by subtracting 12 from the high byte and providing an offset of 33 in y. Hence (ptr1-3072),y=33 = an offset of -3072+33 = -3039, which is the first byte of the row below.

So, it should be effectively doing as you suggest.

For example, half way down on Gen 59 we have the following pattern straddled across the right and left sides

Code: Select all

Appears
 **......
* **.....*
  *......*
 *.......*
*........

Equivalent

   **
 ** **
 *  *
 * *
  *

Should become:

   ***
 **  *
**  **
 * *
  *

Which appears as:

 ***.....
*  *.....*
  **....**
 *.......*
*........

And that's what happens. So, if a 2x2 can turn into a glider the bug is more subtle.

However, there could still be a bug. Did you notice this from the standard run, or did you press a key first? One thing that's worth noting is that there is sometimes the illusion that the top and bottom scans don't seem to generate properly, but this is because most of the time the top and bottom scans are showing different generations.
User avatar
Mike
Herr VC
Posts: 4816
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: HiUxLife: A Hires Life for the Unexpanded VIC-20.

Post by Mike »

I just run it with no special settings.

At some time I noticed a 2x2 block, split over the left and right side, was going downwards, i.e. behaved like a glider/spaceship, but it did not actually turn into one of the known small gliders. Of course it shouldn't do that, a 2x2 block is supposed to stay put.
Snial
Vic 20 Newbie
Posts: 14
Joined: Fri Jan 05, 2018 2:51 am
Location: UK
Occupation: firmware engineer

Re: HiUxLife: A Hires Life for the Unexpanded VIC-20.

Post by Snial »

Hi Mike,

Thanks for that - I re-ran from scratch and couldn't see that pattern up to 200 generations. I was running it under xvic (VICE). If someone comes across it with generation numbers then that'd be helpful.
User avatar
Mike
Herr VC
Posts: 4816
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: HiUxLife: A Hires Life for the Unexpanded VIC-20.

Post by Mike »

The pattern appears at generation 1412 and runs downwards until generation 1500 where it collides with a blinker on the left side.

But even before that there are also at least two gliders which do not survive the transition to the other side.
Snial
Vic 20 Newbie
Posts: 14
Joined: Fri Jan 05, 2018 2:51 am
Location: UK
Occupation: firmware engineer

Re: HiUxLife: A Hires Life for the Unexpanded VIC-20.

Post by Snial »

Hi Mike,

You were right, there was an issue at Generation 1412 as you say. I found the issue and corrected it.

The problem was that the assembler I use: mac2c64 didn't calculate

kBmEndColDn .equ 3200-kBmH+1

correctly. It should have resulted in the value 3041, which is the relative displacement from the bitmap byte at the beginning of the current row and the bitmap byte at the end of the next row. However, it calculated it as:

kBmEndColDn .equ 3200-kBmH-1

which is 3039, the displacement to the bitmap byte at the end of the previous row. It looks like it doesn't really assemble statements with multiple terms properly. By changing the code to:

kBmEndCol .equ 3200-kBmH
kBmEndColDn .equ kBmEndCol+1

the correct value is computed: $be1.

I also fixed a bug whereby pressing the key to start again with a different random bitmap failed to reset the generation count. The new version is 884 bytes long, leaving 12 bytes to spare :-) !
Attachments
HiUxLife.zip
(5.79 KiB) Downloaded 65 times
Post Reply