ZX81 Aquarium Makeover—Revamping Kimmie Fish in Assembly


A remake of my Kimmie Fish screen saver in assembly with additions.

While on vacation, I spent some relaxing time converting my Kimmie Fish BASIC program into something a bit more interesting. In addition to the normal speed improvements, I wanted to give it an aquarium like feeling with bubbles and depth. No color here, this is all stock ZX81 graphics. Only thing needed is the requisite 16K memory pack.

# A fine screen saver indeed.
Before digging into the display, let's look a bit at what all Aquarium does. There is a bit of subtlety to the program that you might not notice at first glance. For example, the fish start off at full speed. After that, their speed will vary to make the visuals a bit more random. The speed difference isn’t huge but is noticeable.

The bubbles range not just in size but speed as well. The little bubbles tend to move slowly. The larger bubbles tend to move quickly. In relation to each, they also take on some variability in speed. This keeps the screen moving and avoids becoming repetitive.

Aquarium Screenshot 1, 2024 by Steven ReidAquarium Screenshot 1, 2024 by Steven Reid

Although it may not be obvious at first, there is also depth in the scene. The tiny bubbles are in the back. The Kimmie fish are in the middle, overwriting any small bubbles. The large bubbles are in front and will overlap the fish. it doesn’t happen often, but is noticeable when it does.

You can actually break out of the program by pressing the SPACE key as normal. Each run of the program will generate different water and sand graphics. Sharing with friends, they liked the overall effect and I do as well.

# From simple to complex.
Converting original BASIC version wasn’t really an option. That version used string manipulation to determine what would display not he screen. That didn’t make sense here. Plus I had a lot more flexibility here due to the speed gains.

The main loop is actually straight forward (below), but the code is more complex than I expected it would be.

        call initialize
main_loop:
  call move_back_bubbles
  call move_fish
  call move_front_bubbles
  call copy_to_screen
  jr main_loop ; rinse and repeat

Reading above, you get an idea of how I managed the death. Each element is printed back to front, overwriting the previous graphics. This provided a sense of depth without a lot of trickery. The display is done in a buffer that is copied to the screen when done to avoid flickering. To keep the frame rate somewhat consistent, I used the frame counter for the delay. it’s not perfect but works.

# Making fish move.
To deal with the movement of the fish, I decided to treat them as sprites. The BASIC program used strings, but I have a lot more control here. I don’t actually do any string slicing. Instead, the routine that prints the character into the buffer checks if either the X or Y coordinates are out of range. If they are, it returns without printing anything.

By doing the bounds check, I could allow the X position to move outside of the screen until the fish was completely off screen. To avoid having to clear the buffer, the sprite has spaces to print over the end of the fish. This has the advantage of speeding up the program with minimal memory impact.

Aquarium Screenshot 2, 2024 by Steven ReidAquarium Screenshot 2, 2024 by Steven Reid

Once the fish is off screen, I do a quick call to reposition it (see below). It is a bit brute force as it just calls a random value and then checks if the fish is on the other fish. I could have allowed them to overlap, but decided I didn’t like the way the graphics displayed doing that.

get_fish_row:
  ld b,buffer_size-2
  call rnd ; from 1 to buffer_size-2
  dec a ; make from 0 to buffer_size-3
  ld b,a ; save new pos
  ; check if other+2 < top (top>e+2 or e+2 < top)
  ld a,e
  add a,2
  ld d,a
  cp b
  jr c,good_to_go
  ; check if top+2 < othertop
  ld a,b
  add a,2
  cp e
  jr nc,get_fish_row
  ; we are good!
good_to_go:
  ld a,b ; restore position
  ret

Funny enough, this was probably the hardest part to get right. I had to futz with tests a few times to makes sure they were working correctly. Funny how simple things can be hard.

# Adding speed.
The bubbles, much like the fish, are stored in an array. To mimic speed, I actually use two bytes to determine if the bubble moves. The first byte is the speed. The second is the sub-position.

To move the bubble, I first add the speed to the sub-position. If the sub-position rolls (sets the carry flag), I move the bubble. Otherwise, the program saves the sub-position and keeps going. I used the same logic for the fish as well. This allows a more organic movement. You can view this on YouTube as well.

Aquarium, Video, 2024 by Steven ReidAquarium, Video, 2024 by Steven Reid

This is different from how I managed speed in my ZX81 Rain program. In that one, I had a frame counter that I would increment. When that counter reached the speed, it would reset to zero and move the drop. This version doesn’t require reseting the variable.

One thing I realized afterwards is that I update the fish frame each print. This makes the slower fish look like they are swimming against a current. Reviewing the code, it wouldn’t have been a hard change to make either. Consider it a bug to correct in a future version.

# Improving and final thoughts.
Although I love the look of the aquarium, it could be more. My original idea was to add in different backdrop objects like rocks, plants or figures. I also thought about moving the water a bit with a pump at the top. As the program progressed, I felt those elements weren’t needed.

The fish code isn’t very optimized. My original goal was to add different fish types. The sprites would then be picked when adding the fish. The problem is I didn’t like the graphics I came up with. The whole left/right fish thing could be done completely different. End in the end, it was easier calculating them in separate code blocks.

The final result is pleasing and I’m happy with what I put together. The code is readable and should be easy enough to adjust in the future.



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