Monday, October 26, 2026

A few additional features, and the future of this project.

         Since finishing the bitmap mode, I've been experimenting with a few other features for the project. These are mostly unfinished and still being tested, and I wouldn't count on them to work as is in the code. For reasons I'll explain later, I don't know at this point if these ever will be completed.

         The first feature I worked on implementing for a while is a affine transform background layer. This layer takes over the memory spaces normally used for the two background tile layers, and creates instead a single layer of 128 x 128 tile layer, which can then be stretched, rotated, or otherwise deformed by multiplication by an affine transformation matrix. This feature was directly inspired by the Mode 7 graphics on the SNES, which can be used for various pseudo-3D effects. I wanted to see if I could make this work, and I figured it would be possibly useful for certain games and graphical effects.

        The affine tile map is larger than the normal tile maps would be, and takes up the memory that both of them would. I did later figure out a more complex memory mapping that allowed one or two of the normal tile background layers to be used at the same time as the affine tile layer. This does come at the expense of using up most of the sprite graphics memory in the process, so I don't know how useful it would actually be in practice.

         The affine layer can apply a simple rotation, scaling, or skew to the entire tile layer. In order to get a perspective effect such as what you'd need for a racing game, the affine transform registers need to be updated with every horizontal scan line. The SNES and similar gaming systems either use a dedicated DMA driven by the horizontal blanking interrupt, or interrupt-driven code in the CPU to update these registers as needed. I didn't have an easy way to implement this that would work easily through my BASIC interpreter.

         Instead, I decided to try something more ambitious: creating my own custom bespoke coprocessor function in the FPGA. This is a somewhat unusual 16 bit custom purpose processor, intended to be triggered by various interrupt sources inside the FPGA to perform simple math and update affine transform registers as needed with every horizontal line. It also can update other registers such as the tile layer horizontal scroll registers for parallax scrolling, or the bitmap palette registers for increasing the limited color palette in certain bitmap modes.

        The design of this started out as one inspired by the Copper function from the Amiga computer chipsets, but evolved into an actual CPU with its own internal registers and an actual RISC-like instruction set. It's still a somewhat odd processor. No stack, limited indirection ability, and it can only address the first 128 bytes of the FPGA's memory space, but it has dedicated instructions for multiplication, sine and cosine, even division and square root through lookup tables, as well as flow control instructions which cause the coprocessor to wait for specific horizontal lines or screen states before continuing.

        Unfortunately, this feature is currently largely untested and probably unfinished, along with the affine transform background layer.  I haven't had time to do any significant work on this project in about a year. Other projects and general life issues have been taking higher priority, and the homebrew computer project has been gathering dust on a shelf in my workshop.

         Part of the problem has been the question that I've never been really able to answer, which is what this project would ultimately be for if it was ever really completed.  I had some possible ideas about having a custom circuit board made, and putting it in a commodore-64-like case with a mechanical keyboard and everything, but that seemed like a lot of cost and work for a one of a kind prototype. I've even considered the idea of mass-producing and selling these machines, but quickly realized that supporting that effort would be far more work than I wanted to put in to this project. The operating system on the MCU is extremely kludgy and needs a lot of re-writing to be usable as an actual commercial product, and small run circuit board production is still pretty expensive. Besides, I'd just be in direct competition with existing, established products like the Commander 64.

        I've also considered my original idea of putting a FPGA on a carrier board that can be plugged into a hobby breadboard 6502 computer to act as a GPU (and possibly other functions such as DMA, interrupt controller, address decoder, or other useful bits of glue logic). I even have some preliminary PCB designs with a FPGA and required support circuitry on a little PCB the size and shape of a 40 pin DIP chip that could plug right into a breadboard. This is still something I could pursue, if there was any interest in it. It would require a bit of redesign of the interface logic on the FPGA.

         Other than that, this has been a fun programming project over the years, and has taught me a lot more about FPGA programming in particular, but it's on indefinite hold until I get inspiration to start working on it again.

Additional feature: Bitmap mode


         With the basic graphics and sound functionality done, and plenty of logic space left in the FPGA, I started to think about what else I could make this chip do. Having a bitmap mode would be nice, as there are many things this could be used for that can’t be easily done with tile and sprite graphics. Could I squeeze a bitmap mode into this project?

        The first problem is that there were only four line buffers available, and I really didn’t want to add a fifth. Instead, I gave the graphics mode register some additional functionality, and attached some logic to it beyond simply enabling the individual engines. Previously the graphics chip had four individual engines in it (text, tile A, tile B, and sprite). I added a field for a fifth, but also ensured that only four would be permitted to be active at a time, and added mapping logic to rearrange the connections between the engines and the line buffers, so that each of the maximum of four enabled engines would still have its own line buffer.

        I added additional bits to the graphics mode register to control the resolution and color depth of the bitmap. Two resolutions are available: 160x120, and 320x240. There are also four color depths available: 2 color, 4 color, 16 color, or 256 color. I also added a separate bitmap palette area in memory which stores up to 16 colors, to be used as the palette in the lower color modes.

        The biggest challenge was where to store the memory required for bitmap modes. I still had about 24K of general purpose block RAM left in the FPGA, which was enough for some of the graphics modes. I could fit any color depth bitmap at 160x120, and 2 or 4 color modes at 320x240. For the higher color depth full resolution modes, I put in a feature to allow the bitmap to use other parts of the chip’s memory, disabling the sprite, tile, and text engines as needed to free up the space. A full resolution, full color bitmap image requires all of the other engines to be disabled, freeing up all of the available graphics memory for the bitmap. This was also where I added in the ability for the sprite bitmaps to draw from other parts of the memory, enabling up to 64K of memory to be available for sprites by disabling other graphics engines.

        The other problem was how to allow the external CPU to access all of this memory. To the external CPU, the FPGA appears as a 64K memory device. Essentially all of that 64K space was already spoken for, but here I wanted to add an additional 24K of addressable space. I handled this in a slightly inelegant way, adding a remap register to the control registers that allows the last 16K of the memory space to be remapped between three different regions of memory, making the new areas of memory available to the external CPU.

        With this new feature, I could recreate the original parrot image demo from a Basic program, loading the image from files on the SD card instead of having it hard-coded in the FPGA’s memory. 

        I could also now do a full-resolution Mandlebrot set demo.

 

        And at lower resolution or color depth, I can layer bitmap graphics on top of text, tiles, or sprites. I added a number of Basic commands to automate the drawing of lines, circles, rectangles, and other shapes, as well as to automatically handle memory address calculation and remapping.


 

Wednesday, October 21, 2026

A sample game: making Pac-Man from scratch

        With working graphics, sound, and a fast Basic interpreter, I needed a real demonstration program to show what the system could do. I decided to re-create a version of Pac-Man, similar to the original arcade game but with a few tweaks and modifications.


        First, I set about re-creating the game map, using one of the tile engines. The original playfield graphics were easy to find online, already in the form of 8x8 tiles which my tile engine code could easily work with. My tile engine only needed a few tiles to build up the entire maze, as the per-tile flip and rotate feature greatly reduced the amount of raw graphics needed.

 The dozen or so map tiles needed, in a simple tile editor program I created for this project.

        The shape of the map would not be the same, as the original Pac-Man game had a vertically oriented screen, while I was working with a conventional horizontally oriented screen. Rather than try to use the original playfield map, I decided to just create a new horizontally oriented map for my game. And to be extra fancy, I made the map slightly different every level with a bit of procedural generation. Most of the playfield map would stay the same, but I added a few dozen spots which change with each level. In all there would be several thousand slightly different playfields which the player might encounter while playing the game. The base maze outline was actually saved in a binary file on disk and loaded into memory as needed, as was the bitmap graphics for the tiles. These could have been stored in the Basic program itself in data statements, but that would have made the Basic program listing much longer.



One of many procedurally generated map screens

        After drawing in the walls of the playfield maze and filling in the level-specific custom details, the code places the four power pellets and then floods the remaining corridor areas with the regular pellets. This does mean that the number of pellets that need to be eaten varies slightly from level to level. It also avoids the level 256 kill screen present in the original arcade game.


        Next, I needed to create the sprites. All of the sprites in this game are 16x16 pixel, 256 color mode sprites. 16K of sprite graphics space gives me 64 different images to use, which was just barely enough for this game.

All of the sprite graphics needed for the game

        I did take advantage of the flip and rotate capabilities in my sprite engine to avoid needing graphics for each different direction of Pac-Man and the ghosts. I also used the sprite animation to cycle between sprite images to animate the ghosts. Later I added an optional 16 color sprite mode with per-sprite palettes, which could have been used to save memory by using the same basic sprite graphics for all 4 ghosts, and also increased the available sprite graphics space to 64K, but I didn't end up needing that for this project.

        Movement of Pac-Man unfortunately had to be done with the keyboard, since I have yet to add a joystick port to this machine. It’s good enough for demonstration purposes. I ended up coding a general movement routine that applies to all movable objects in the game which locks them to moving only along lines between the centers of tile spots and through open spaces in the maze, with some special logic for using the tunnels off the sides of the map. Pac-Man moves according to these limits, with a target direction value determined by the keyboard inputs.

 Moving around the maze and eating pellets.

        The ghosts are also locked to moving along open spaces in the maze, with some special case logic for entering and leaving the ghost house in the center. They all have a target location which they try to move towards. Initially, after leaving the ghost house, they are all in “scatter mode”, where each one moves towards a different corner of the maze. After some time in scatter mode, they switch to chase mode. In this mode each ghost has a slightly different behavior, having a target point which will be the player, a point in front of the player, a point behind them, or alternating between the player’s location and the far corner of the map. This is the same chase AI as in the original arcade game. After the player eats a power pellet, the ghosts change appearance and then attempt to move directly away from the player if possible. Finally, after a ghost has been “eaten”, its target location will be set to the center of the maze to return it to the ghost house.

The game gets much harder once the ghosts have been added.

        I also added a moving bonus item that bounces as it travels from one side of the arena to another, as was seen in Ms. Pac-Man. It just seemed more fun than just having it appear in the middle of the arena.
To recreate the sound of the original game, I found a You Tube video of a long gameplay with good audio, fed the audio into spectrum analyzer software, and then spent a while working out the exact frequency and timing of the various sounds. The cross-channel modulation features of my audio generation code were very useful here, allowing me to recreate the background siren sound, the rising whoops when the player eats a ghost or bonus item, and other complex sound effects with just a few lines of Basic. Some of the sound effects don’t exactly match the arcade game original, as the original arcade game used a custom sound generator chip with complex wavetables that I couldn’t easily reproduce. The original “waka” sound from eating a pellet is especially difficult to reproduce, and my code doesn’t get it exactly, but it works well enough for demonstration purposes.

Apologies for the handheld camera work. I haven't managed to get the video capture device to work with this setup yet.

        This game is missing several features of the original arcade game. There are no cutscenes. There’s no attract mode, running the program starts a new game instantly. And getting a game over simply locks the game up entirely, forcing the user to exit and then run the program again.


        It’s a pretty fun game. I find it actually fairly difficult, but I’m not sure if that’s due to my reflexes being poor, or if I’ve set the speed on the ghosts to be too high. The main thing I’m proud of is that the somewhat complex game logic runs smoothly at 60hz, despite still being essentially interpreted Basic code.


        The Basic source code is available here, but obviously this is only going to run on my own custom hardware, so it’s provided purely for reference purposes.


        While writing this code the main problem I ran into was the limited memory available for sprites. 16K is really not much space for bitmaps, especially when they’re being stored in full 256 color mode. I did later implement an option for 16 color mode to effectively double the space available for sprites, and also later added some options to potentially increase the available sprite bitmap space to as much as 64K by disabling the other graphics engines and using their memory for sprite bitmaps instead. Ultimately I’m limited by the small amount of memory available on the FPGA, and my choice to try and implement this graphics engine with a single chip solution. Adding an external memory chip would fix the limited memory availability, of course, but that would be an entirely different design than what I’m going for.


Sound generation

        With the graphics engine coming along well, I turned to the second major task this FPGA would need to do: audio generation. The typical method to get audio out of a retrocomputer project like this would be to use something like a AY-3-8910 sound synthesis chip, but I didn’t have any of those on hand and wanted to try making my own audio generator from scratch. The preferred method with modern sound generation is to just play back digitized sound clips, but I didn’t have enough block RAM left over on the FPGA for that, and it would put too much load on the external CPU anyway. Instead, I decided that I wanted to implement a fairly standard wavetable synthesis system, inspired by the AY-3-8910, the Commodore SID, and other classic sound generators.

      Those early classic sound chips were generally good for 3 channels of sound at most, with completely separate sound generation circuitry for each channel. I decided for a more ambitious design, a synthesizer capable of handing up to 16 channels simultaneously. To avoid having to have an unreasonable amount of logic for each channel, the audio engine would operate serially, cycling through the 16 channels and performing all the math required for each one at a time.

Audio engine block diagram

        The audio engine starts with a memory interface block, which loads all of the registers required for a channel from the audio control RAM. Each channel has an associated 32 byte section of the control ram, which is loaded into temporary registers which control the audio engine. After the required math has been run for that channel, 9 bytes will be written back to the audio RAM, containing data that needs to be updated with each cycle. As all 16 channels are processed, right and left channel output values are added to a cumulative sum, which is then sent to the I2S audio output block after all 16 channels are done. The entire cycle repeats on a 48khz clock.

        Each channel starts with a 24 bit phase register, which is updated by a 24 bit frequency control register to set the note frequency. 24 bits of frequency control allows a wide range of frequencies for each channel, giving a maximum frequency of 12mhz and a minimum of somewhere around 1/700hz. Being able to go to frequencies that low might seem pointless, but I wanted the channels to be also used as low-frequency oscillators for cross-channel modulation purposes. It also allows a full 9 octave note range with good precision.

        There is a cross-channel modulation function, which allows each channel's frequency, amplitude, or duty cycle to be adjusted by the output from another channel. This allows the user to set up complex effects such as sirens, warbling sounds, randomly varying notes, rising/falling noise to simulate waves or wind, etc., all without the external CPU having to do anything other than the initial register configuration. The resulting system has a lot of flexibility, and the 16 channels give a lot of spare channels to use as oscillators.

        Each channel can be set to output random noise, square waves, triangle/saw waves, sine waves, or one of four custom waveforms. The triangle/saw tooth wave generation logic supports smoothly transition between falling sawtooth, triangle, and rising sawtooth waves with a bit of clever math. The noise, square, triangle and saw waves are generated mathematically on the fly, while the sine wave and custom waveforms are stored in small lookup tables in memory. Each channel also has an independent ADSR envelope generator, which optionally shapes the volume output for each channel’s audio output.

        The left and right channel outputs from each channel are all summed together, and then output on an I2S port after all 16 channels have been updated. An external amplifier takes the I2S input and drives a speaker. I had originally experimented with a R2R DAC, but it didn’t work well. The I2S amp seems to be a cleaner solution anyway.

Devkits now with added I2S amp and speaker.
(and also a USB hub, 8 digit LED display, and battery backed RTC chip) 

        While I am not an audio expert by any means, the resulting sound synthesis seems good to me. Quite capable of generating the types of sound effects that you’d get out of a mid 80’s game system. I am sure that someone with more musical aptitude than I could do a lot with this audio generator.

Sunday, September 20, 2026

Creating a fast Basic interpreter

        For a retro boot-to-basic computer, I’d need an actual Basic interpreter. My first idea was to just grab an open-source implementation online somewhere and just port it over to my system, rather than reinvent the wheel myself. There had to be something suitable out there already, right?

        After a bit of searching, I decided to use Tony Wang’s MY-BASIC. This seemed idea –lightweight, packaged in a single code and header file, and easy to expand with hardware-specific commands. I copied it into my project, ported it over to deal with the idiosyncrasies of the MPLAB compiler, and added a handful of commands to make it work with the USB keyboard and text display. The trickiest part was converting it into a retro style line number dependent Basic instead of its more modern structured text style.


        This seemed to work great at first. I tried a few simple “Hello World” programs, and then coded a simple text mode Game of Life engine as a demo. This ran, but it was a lot slower than I expected, taking several seconds to calculate a single frame of the simulation. Well, that’s to be expected, I thought. Interpreted basic is slow, even on a modern 32 bit 200mhz CPU.


        The real problem came when I tried to write more complex programs. When I got to about 100 lines of code, the interpreter ran out of memory and crashed, even though most of the MCU’s 512KB of memory was set aside for it.

        It turned out that MY-BASIC is just not very memory-efficient. From looking through the code, there’s a lot of overhead to everything it does. It’s just not all that well optimized for microcontrollers with limited memory space like the one I was using.

        I looked at a few other options, but ultimately decided that I’d have to code my own Basic interpreter. One that was optimized for a lightweight memory footprint and high speed. It would turn out to be be my own unique dialog of Basic, as I looked at Commodore and Microsoft Basic for guidance but ended up reworking a few aspects of the language as I saw fit.

        The main guideline of my Basic interpreter would be to pre-calculate everything as much as possible. I was inspired by the tokenization pass that was performed by the Basic interpreter on Commodore machines, as well as the way that Python interpreters pre-process scripts into an easier to execute form. My Basic interpreter would scan every line of the program, first tokenizing them, and then processing them into a simple stack-orientated byte code, which would be what the actual execution process then ran.
The first step taken after the user types the RUN command, is actually to save the user’s entered program onto the attached SD card, clearing the memory space that would be taken up by the raw text. That file is then read back a line at a time, each line being processed by the tokenizer and parser.

        The tokenizer skims through each line, and breaks it down into a list of tokens. This is the part of the code that recognizes commands and functions, creates variable names and allocates space for variables as needed, and also recognizes strings and data and places those in specialized memory spaces.

        For example, the Basic line:

    b = 2*a*b+cb
will be broken down into the following list of tokens:
    [Index of variable b] [Assignment] [Integer 2] [Multiply operator] [Index of variable a] [Multiply operator] [Index of variable b] [Addition operator] [Index of variable cb]

        Each token is a 32 bit value, broken up into individual fields that can indicate token type (such as operator, variable, syntax, etc) and type within that token (such as type of operator, index of the variable, etc.). There is some complex logic in the tokenizer to distinguish different uses of syntax characters like equals signs and parentheses. The tokenizer will also catch many basic syntax errors and halt the process with an error message.

        After tokenizing, the list of tokens is handed to the parser. This code takes the list of tokens and converts it into a list of commands, which will be run by a virtual machine during runtime. This command language was chosen to be run as fast as I can get it to run, and also to take up minimal space in memory. Each command in the command list is a 32 bit value, which can have additional data for immediate parameters stored as part of the 32 bit command. Commands can also be followed by physical addresses in memory, as during the parsing process the addresses of variables, procedures, and operators are looked up and stored directly in the command list.

        Our previous Basic line, after going through the tokenizer and parser, will result in the following command list:

[Push variable contents to stack] [Address of variable a]
[Operation with immediate value 2] [Address of multiplication operator]
[Push variable contents to stack] [Address of variable b]
[Operation on top two values of stack] [Address of multiplication operator]
[Push variable contents to stack] [Address of variable ab]
[Operation on top two values of stack] [Address of addition operator]
[Pop top of stack to variable] [Address of variable b]

        This list of 14 instructions takes up 56 bytes. This is admittedly more than the 12 bytes of the raw Basic text, but it is much faster to execute than parsing the Basic itself every time that line needs to be run.
As an additional optimization, there is a final sweep through the code that identifies GOTO and GOSUB statements and replaces the associated line number with the address of that line in memory. This eliminated the need to search through the program list for the actual address every time a GOTO or GOSUB is encountered, although there still needs to be that capability for jumping to a line number that’s calculated at runtime.


        The resulting Basic interpreter is fast, running my Game of Life demo about 30 times faster than Tony Wang’s MY-BASIC. And it’s much more memory-efficient, I’ve written programs over 600 lines long without getting close to the memory limitations. I’d call it a success, for a part of the project that I never intended to develop from the start it’s working really well.

        The source code for the project can be found here.

Saturday, September 19, 2026

Connecting a CPU: A change of direction

         My original plan for this project was to pair it with a 6502 CPU system and build a simple 8 bit homebrew computer. But as I approached the point where I’d need an external system to run code on, I had several realizations.

        The first problem was that I didn’t have a 6502 computer system to connect it to. And while it wouldn’t be that hard to build one, it was a project that I wasn’t really sure I wanted to undertake.

        Because the second problem is that I have zero experience with 6502 assembly programming. My early programming experience was all on Z80 and 68000 machines, and even there I was using interpreted and compiled languages and not touching the assembly myself. I didn’t start doing low-level assembly programming until much later.

        And that leads to the third problem: programming 6502 machines isn’t nostalgic for me. A homebrew 6502 system isn’t my dream retro machine. I never really did any programming on them. The vast majority of my actual programming experience has been with Microchip PIC series microcontrollers, starting with the simple 8 bit ones back in the 90s, and leading up to the PIC32 devices I work with today. 

        And as it turned out, I had a PIC32MZ development kit board sitting around unused, left over from a previous project, and the development and debugging environment already set up and ready to use.
It is true that the resulting system isn’t going to be all that retro, with a modern (200mhz, 32 bit) CPU, and a modern FPGA. But it would be a lot easier for me to develop, and it would at least look and act like a retro boot-to-basic machine.

        I created code in the FPGA to make it act like a 64K memory, with internal division between control registers, tile and text maps, sprite data, graphical data, etc. In the Microchip code project I configured a chunk of the PIC32’s memory space to connect to the FPGA through the MCU’s external bus interface. Then I just needed to connect 16 address, 8 data, and various control signals between the two of them.
With some fairly simple code I was able to write to the FPGA, clear the memory space, and make a simple hello world program to draw text to the screen.

Connections between the MCU and the FPGA 

 

Wired up with a ribbon cable, and a simple 3D printed frame to hold everything stable 

        The next step was to connect a keyboard, using the USB Host capability of the PIC32 MCU, and create a simple text interface. This wasn’t terribly difficult to do, although I had a little difficulty getting key repeat timing just right. I also added a SD card interface, and wrote a rudimentary DOS for saving and loading files.



        At this point I had a simple “TV Typewriter” type interface. But to make it really useful, I would need to implement a scripting language – preferably Basic, since that matches the type of computer I was trying to imitate.

Designing the graphics engines: Text, Tile, and Sprite graphics.


         

        At this point, I had a simple FPGA program to output a HDMI video signal consisting of a single static image. This was a start, but to be useful it needed a lot more functionality.


        I would eventually need to code a CPU interface to allow the contents of the frame memory to be written externally, but first, I wanted to code the actual GPU function. My goal was not to have a simple bitmap display, as that would require the external CPU to do a lot of work to draw simple text and tile graphics. Instead, I wanted to build graphics engines to make the CPU’s job easier. The system I was imagining needed an easy-to-use text mode, as well as multiple tile and sprite layers for implementing simple games. I quickly came up with a design that would have four independent graphics engines: text, tile A, tile B, and sprite, running simultaneously to generate different layers of the final screen image.


        The 75K frame buffer took up far too much of the FPGA’s limited memory, so it would have to go. Instead, the individual engines would each have a dedicated line buffer, and would generate the image to the screen line-by-line as needed, racing the beam as it moved down the screen. I actually created two line buffers for each graphics engine, with one of them being written to while the other one was being read out to generate the video output. After each line the buffers would be swapped, so the most recently generated one would be output while a new line was being generated.


        The block RAMs on the T20F256C3 are each 512 x 10 bits, which fit my needs for this well enough. I was only using 240 locations out of each block, but the 10 bit width came in handy. Of those I was using 8 bits for the color value, while the remaining 2 bits were used to encode a layer priority value. When reading data from the four line buffers, the priority values from each would be compared, and the color value with the highest priority would be used as the color value sent to the palette memory. With this each sprite in the sprite engine could have a different layer priority, allowing them to appear in front of or behind the tile layers as desired. Likewise different parts of the tiles generated by each tile engine could have different layer priorities relative to the sprites and each other.

Simplified block diagram of the graphics pipeline.


        The text engine stores a 64x32 page of text in memory, although only 40x30 characters are visible on the screen at once. While it would have been nice to be able to implement 80 column text mode, I haven’t come up with a clean way to fit it into this existing architecture yet. Each character is stored as 4 bytes in memory: one byte for letter ID, one byte for foreground color, one byte for background color, and one ‘attribute’ byte which encodes various bits for cursors, underline, blinking, and per-character layer priority. This came out to 8K for the text map, plus 2k for the 256 character font, which might seem a lot for a simple text display, but I wanted it to have a lot of built-in functionality. I also much later added a feature to allow this space to be repurposed for other uses by disabling the text engine.

         A single byte in a general-purpose control register space allows the text display to be shifted up and down on a per-line basis, allowing smooth scrolling if the external CPU wants to implement that.


        The two tile engines are identical, other than pointing to different line buffers and reading from different memory banks in memory. Similar to the text engine, each 8x8 tile on the screen is stored as 4 bytes in memory. One byte selects the tile image ID, one selects the transparency color (pixels on the tile matching this color will have their layer priority set to zero, allowing other layers to show through), and then there are two bytes encoding various special features. The tile map encodes 64x32 tiles, requiring a total of 8K of memory, and another 8K of memory is set aside for the tile graphics.


        There’s a lot of fancy functionality built into the tile engines. Each tile’s image can be encoded in 256 color, or 16 color with a per-tile palette selection. Tiles can be individually flipped horizontally, vertically, rotated, or any combination of those. There is also an automatic animation function where a tile’s image can be cycled at a per-tile adjustable speed. Each tile can have an individually adjustable layer priority, so you can have effects where some tiles are in front of the other layers and some are behind them. There’s also a collision bitmask for detecting sprite-to-tile collisions, although that feature hasn’t been well tested at this point.


        There are also scrolling registers in the general control register space which allow each tile layer to be scrolled in X and Y on a per-pixel level. It’s also possible to generate parallax scrolling effects by having the external CPU change the X scroll register between scanlines.


        Finally, there’s the sprite engine. This handles up to 128 sprites on the screen at once, and is one of the more complex bits of coding in the project. Each sprite requires 8 bytes of RAM, requiring 1K of RAM for the 128 sprites. Initially I set aside 16K of RAM for the sprite graphics, which turned out to be a fairly severe limitation. I later added provisions to increase the sprite graphics memory up to 64K, by allowing the sprite engine to use other areas of memory when the tile or text engines were disabled.


        Sprites have 10 bits of X position and 9 bits of Y position. These are signed integers, which allows a sprite to be partially off the top or left side of the screen with a negative location. Sprites must be square, but can be 8, 16, 32, or 64 pixels wide. Like tiles, sprite images can be flipped horizontally, vertically, and/or rotated, and their images can be stored as 16 or 256 colors. There is also a feature to automatically animate sprites, rotating their image through several with adjustable timing, and a sprite-to-sprite and sprite-to-tile collision detection system.


        The code of the sprite engine is more complicated than the other engines. First there is a scan process that iterates through the data for the 128 sprites, reading the next sprite's data from the sprite data registers and passing it to the sprite math block.

       The sprite math block determines if this sprite is potentially present on the current horizontal line being drawn to the line buffer. If the sprite is to potentially be drawn, the math block generates a command to be sent to the blitter. This command includes source address in memory, destination address on the line, and information about how much and in what direction to increment the source data pointer between each pixel - required for sprites which might be flipped or rotated. The math block also takes into account the animation cycle, moving the data pointer between animation frames as needed. This information is all packed into a 64 bit command which is sent to a command FIFO.

        The command FIFO holds up to 16 blit commands. If the FIFO happens to be full - possible if multiple large sprites are handled at once - the scan state engine gets paused until there is a free spot in the FIFO. When there is a command ready in the FIFO, the blitter engine takes the command and copies the indicated pixels from the sprite bitmap memory to the line buffer.

        There is no hard-coded limit to the number of sprites per line. In theory, if you draw too many large sprites to the same line at once, the blitter will at some point run out of time and be unable to finish drawing all of them before the line buffers get swapped and sent to the screen. I’m not entirely sure where that point is yet, I need to do more testing.

        I figured that these four engines should be enough for a machine that would be more than comparable to any early 80’s 8 bit boot-to-basic or home game machine. The next step would be to connect an external CPU and actually write code to run it.