A Present On The ZX81 For VC3 2024


My entry for Vintage Computing Christmas Challenge 2024.

Another year and another Vintage Computing Christmas Challenge (VC3 for 2024). I rushed a bit to get mine in so I do feel it wasn’t as short as possible. I did take a somewhat unique route to solving the problem which makes the build more interesting. I made an auto-run version for Present so you could see it in action.

# This year’s VC3.

For 2024 lthe challenge was to display a Christmas present. To add to the challenge, the present had different characters used in the corners, top and sides. To top it off, a bow was added.

Part of my problem was that the ZX81 doesn’t have all the characters used. Namely the backslash and exclamation mark. For my version, I had to replace those with ones I did have. For the slashes, I used graphic characters. Instead of an exclamation mark, I used a colon. Fortunately, the rules allowed this substitution.

All characters can be replaced with another character if this does NOT influence the code. E.g. backslash on the C64 gets replaced by chr$(205). Or on the right-hand side I replaced the big O by a smaller one, as it looks nicer, but it didn't influence the code. At any time, I could replace the o with O or the C64 Backslash with its counterpart on the C64.

The harder part was deciding how to create the present.

# My Approach.

For my entry, I decided to go with ZX81 BASIC even though I from the start that it wouldn’t be the smallest due its limitations. Working late at night, I went through various options in my head but didn’t like the methods I came up with.

Playing around with progressive prints, I landed on a way to build the present using loops. With that working, I added a pair of loops to basically print each quadrant of the present.

I used a series of loops to basically build the square. This allowed a bit of symatry to the program and it looks neat when building out. The code ultimately looks like this before I started to compress anything.

 
1 FOR X=1 TO 10 STEP 9
2 FOR Y=1 TO 10 STEP 9
3 FOR B=1 T0 8
4 PRINT AT 0,9;"\O/";AT X+B,Y;":";AT X+B,Y+9;":";AT X,Y;"+";AT X+9,Y;"+";AT X,Y+B;"-+";AT X+9,Y+B;"-+"
5 NEXT B
6 NEXT Y
7 NEXT X

I will say that it probably took a couple of hours to get the code right. For as small as it is, I kept trying different options which took longer than I planned. Once it was working, I started refactoring to reduce memory consumption.

Due to the peculiarities of the ZX81, I could make a smaller version of the binary by not using numbers. This is due to the way the ZX81 stores floating point numbers. I actually have a version that is 1 byte smaller, but it is super slow to run. Converting LEN STR$ PI (which is 9) into a variable sped the program back up at the cost of that byte. Worth it.

Present, ZX81 ScreenShot, 2024 by Steven ReidPresent, ZX81 ScreenShot, 2024 by Steven Reid

# Counting things up.

With the program complete, I had a binary of of about 1078 bytes. Much of that is overhead as the ZX81 saves its state with the program. The system variables and screen add up to about 910 bytes of code. This meant that my memory optimized program came to about 168 bytes.

Although it worked, it wasn’t close to the smallest it could be. Other dialects could remove extra lines, something the ZX81 wasn’t good at. Once the competition was over, I realized that could have taken a different approach that would have yielded a smaller file.

This version would use some math to eliminate a loop as well as reduce the character size. With some memory optimization, the final BASIC code size was 111 bytes. A decent optimization, but still over twice as large as the BBC Micro version which was 53 bytes. Still, my reduced version (below without memory optimizations) was much slower and not nearly as interesting to watch.

1 PRINT TAB 8;"\O/"
2 FOR Y=-9 TO 9
3 FOR X=-9 TO 9
PRINT "-+"(1+(X=9*SGN X)+2*NOT Y=9*SGN Y);
4 NEXT X
5 PRINT
6 NEXT Y

# Would Assembly have helped?

Of course, I could get even smaller using Z80 machine code. I decided to hack together a few versions to see how far I could get. This burned quite a few more hours as I tried out different hacks to see how small I could get the code.

After about dozen or so versions, I got down to about 51 bytes of code. This was less than the BASIC size of the BBC, but was about 10 bytes larger than the smallest assembly version which was 41 bytes. Finding 10 bytes would be hard pressed on the ZX81.

I did use a a couple of ROM routines to help reduce some code. The print return call at CALL $0b87 and the print character at RST $10. But the screen display of the ZX81 ultimately became the limiting factor.

        ; get ready to print
  ld hl,(d_file) ; 3 - get screen location
  ld bc,9 ; 3 - get to position on screen
  add hl,bc ; 1 - move to position
  ; 7 bytes

  ; print bow - \O/ = $86,$34,$06
  ld (hl),$86 ; 2 - load \
  inc hl ; 1
  ld (hl),$34 ; 2 - load O
  inc hl ; 1
  ld (hl),$06 ; 2 - load /
  ; 8 bytes (15)

  ; print present
  ld c,19 ; 2 - y loop
  ld e,1 ; 2 - row pattern
  ; 4 bytes (19)

ylp:
  call $0b87 ; 3 - print return ROM routine
  ld hl,$0e00 ; 3 - box line ': '
  ; 6 bytes (25)

ty:
  dec e ; 1 - subtract 1
  jr nz,dox ; 2 - if not 0, then print box (: :)
  ld e,9 ; 2 - otherwise, reset e
  ld hl,$1516 ; 3 - ribbon line '+-'
  ; 8 bytes (33)

dox:
  ld d,1 ; 2 - column pattern
  ; 2 bytes (34)

  ld b,19 ; 2 - x loop
xlp:
  ld a,l ; 1 - set a to char in l
  dec d ; 1 - subtract 1
  jr nz,draw ; 2 - of not 0, print char
  ld d,9 ; 2 - otherwise, reset d
  ld a,h ; 1 - set a to char in h
  ; 9 bytes (44)

draw:
  rst $10 ; 1 - print char
  djnz xlp ; 2 - next x
  dec c ; 1
  jr nz,ylp ; 2 - next y
  ; 6 bytes (50)

  ret ; 1
  ; 1 bytes (51)

If you can find any additional optimizations, let me know.

# A lot of fun.

In the end, the challenge was a lot of fun. I liked my entry, even if not the smallest. I learned a lot from looking at what others were doing. The wild entries were fun to watch.

I didn’t realize how much I enjoyed trying to find different ways to do things. I even dug through the ZX81 ROM again. Explains why my Gem Quest program still isn’t done as I spend more time refactoring code than finishing the game.

Sigh. At least I got to code a bit.

Happy New Year!



Comments on this article:

No comments so far.

Write a comment:

Type The Letters You See.
[captcha image][captcha image][captcha image][captcha image][captcha image][captcha image]
not case sensitive