BMOW title
Floppy Emu banner

Archive for March, 2009

Music Visualizer

One more demo: I wrote a music visualizer to show off the AY chip tunes, and also converted a bunch more AY songs for use with BMOW. Now there’s something cool to look at while you listen to that glorious 8-bit music!

The visualizer shows blue, yellow, and green bars for the three voices of the AY-3-8913. The bar’s horizontal position shows the voice’s frequency, and the bar’s height shows the amplitude. The fourteen hex numbers at the bottom are the real-time values of the AY control registers.

The Agent X II track that I used in my first audio demo shows this off pretty well. You can easily pick out which instrument is which voice, and see some of the auditory tricks used, just by watching the visualizer.

Here’s another AY tune running with the visualizer. This one’s called “Power M”, and was evidently written by a guy on speed. Just try to follow the voices! It’s all a blur. Forget it.

Incidentally, this is also the first BMOW demo to show any kind of animation, tame as it is.

Read 6 comments and join the conversation 

Spit and Polish

I know I’ve said this before, but I think BMOW is fast approaching a state of “done”. For the last week I’ve been polishing some old demos and making some new ones, for inclusion in the final demo ROM. Along the way, I also added a proper startup sound. Turn the machine on, and it announces itself to the world, loud and proud.

I also revisited the video hardware, and created a couple of new demos showing “new” video modes. These modes aren’t really new, of course. The hardware capability was always there, and I knew the modes were theoretically possible, but I’d never before actually taken the time to determine the necessary combination of register settings and video data interleaving to draw an image in every mode. My first target was the highest resolution mode possible for BMOW’s video system: 512×480, with two colors. It is truly any two colors, and not black and white, as this brown and tan Aztec Sun Stone image shows. Click the image to see a full-size version with greater detail.

I then created a second demo that makes use of mixed mode, with the screen split into three regions in 2 bits-per-pixel, 4 bits-per-pixel, and 8 bits-per-pixel modes. The same image of a flowering hedge is shown in all three regions, to demonstrate how the BMOW hardware trades color depth for horizontal resolution.

The 2bpp sample was a bit of a challenge, and was another mode I’d never actually tried before. It turned out that my image editor (GIMP) doesn’t really output 2bpp images, with 4 pixels packed per byte. Instead, it outputs an image in 4bpp format, that just happens to have only 4 colors in its palette instead of 16. Rather than hack the image data, I wrote a BMOW conversion routine to repack the 2bpp image data before it’s displayed.

The screenshot below shows the comparison of the three color depths, but to really appreciate the differences, click the image to see a higher-res screenshot. The 256 color region has rich, green, shiny leaves and a buttery yellow flower, but looks like a mosaic due to the low resolution. The 16 color region holds up well, given that most of the image is green anyway, but the flower becomes a solid yellow region. The flower turns green in the 4 color region, but thanks to the higher resolution and effective dithering, the leaves don’t look too bad.

If I’d chosen an image with a greater variety of colors, the discrepancy between the three would have been much more obvious. A grayscale palette test might be interesting too.

Read 1 comment and join the conversation 

Phantom Reads

I discovered an interesting BMOW hardware bug today– one that I’m amazed I never noticed before. It involves what I call “phantom reads” causing unexpected system behavior. I only stumbled across the problem by accident, while trying to troubleshoot why the audio system didn’t work in certain circumstances.

In brief, a phantom read operation is being performed on every cycle that memory isn’t explicitly written or read, and the result is thrown away. 99% of the time this is unimportant, because reading from memory or a device doesn’t have any side-effects. Except, in a few cases reads do have side-effects, and bad things happen due to phantom reads.

Every BMOW clock cycle, the CPU will be doing one of three operations:

  • reading from memory into a register
  • writing from a register into memory
  • moving data from one register to another

Memory and registers are on separate busses, connected by a bidirectional bus driver. For reads and writes, the CPU sets the required direction for the bus driver, and puts the desired address on the address bus, activating the needed memory chip or device. But what happens during register-to-register transfers? The bus driver is disabled, completely isolating the register bus from the memory bus. But some value will still be on the address bus, selecting a memory chip or device. What? I had to dig through the schematics and microcode to find the answer.

It turns out that during register-to-register transfers, the address bus will contain whatever’s in the general-purpose address register. Most of the time this will be the address of the most-recently referenced memory location, but because of the way the microcode works, it will sometimes be a combination of bytes from previous addresses and microcode temporary values. So in effect, during register-to-register transfers, a semi-random memory location ends up being read, and the result thrown away. This causes problems in two ways:

  • Interrupt flags for the keyboard, USB, and real-time clock are cleared as a side-effect when they are read. Normally this is a good thing, but a phantom read to one of these locations will clear the interrupt flag without ever servicing the interrupt.
  • Some write-only devices (audio, LCD, video registers) don’t actually distinguish between reads and writes, and perform a write whenever they’re accessed. I made the assumption that the software would only ever write to these locations, so I saved one wire by not bothering to connect the READ/WRITE line. A phantom read to one of these locations will perform a spurious write. Yikes!

So how can I fix this? At this point, I’m planning to leave the hardware as-is, and try to work-around it in software. Now that I know what to look for, I can predict what types of accesses are likely to cause problematic phantom reads, and avoid them by modifying the code slightly. I was able to do this fairly easily to solve the audio problem that led to this discovery in the first place. I’ll apply the same reasoning to some of the code where I’ve experienced intermittent crashes, and see what I can find.

If I had it all to do over again, I’d probably add a new control line from the CPU called MEMORY_ACCESS or something, that was asserted during any read or write operation. Then the address decoding logic could use that to disable all memory and devices when needed, making a clear distinction between intentional reads and phantom reads.

Read 8 comments and join the conversation 

Boot Menu 1.0

I’ve implemented the new memory map I described earlier, updated the software, and everything’s working great. This has allowed me to finally make some progress on my goal of a menu-driven bootloader, with a catalog of built-in demo programs from the expanded ROM space. Here’s a screenshot of an early version of the new bootloader, with a couple of demos installed:

On the whole, it works great, and I’m very happy with it. After selecting a built-in demo option, the selected program starts in about a second. Next, I’d like to expand on the bootloader menu concept a bit further.

  • Add a power-up sound, along the lines of the Amiga or Mac.
  • Design a better logo.
  • Dress up the boot screen with some more interesting graphics– a spinning box maybe? That’s an awful lot of plain text up there, and is kind of intimidating. Maybe you should have to press F8 to reveal “advanced boot options” like Windows.
  • Create a good video demo. Microchess is the only program that shows anything halfway-interesting graphically.

Not unexpectedly, there are also a pile of small problems to work through:

  • With the new bootloader, when BMOW has been turned off for several hours, it almost always crashes on bootup while drawing the BMOW logo. If I press the reset switch, it crashes again. After four or five resets, it works OK, and continues to work until the next lengthy shutdown. Sounds like the program is relying on uninitialized memory somewhere.
  • About one time in ten, it will crash while downloading a new program image with the bootloader. I think this is related to a feature I added to show the percent complete on the LCD during download.
  • All the demos I want to include won’t fit in 128K. The audio demo alone is 64K. I need to implement some kind of uncompression routine to extract compressed demos from ROM.
  • After booting a demo program, the first keypress will not be recognized. After that, keyboard input works fine.
  • There’s major video noise when navigating the boot menu. I need to delay the screen updates until the VBLANK period.
Be the first to comment! 

Memory Map

Recently I’ve been working on a revamped bootloader, turning it into a menu of cool built-in demos as well as its original purpose of downloading new programs from a remote PC. That’s led to some interesting tidbits, such as making the bootloader able to bootload itself. However, the biggest change to fall out of all this is a new memory map for BMOW.

You might be thinking that now is a strange time to consider rewriting the memory map, when the hardware is done and the machine is essentially finished, and you’d be right. Fortunately all of the relevant address decoding logic is contained in a single programmable GAL, and the memory and devices are connected to the address bus in a way that allows them to be located more-or-less anywhere in the address space, subject to some alignment requirements. The bulk of the work would be updating all the software I’ve written to work with a new memory mapping.

But why would I even consider a change like this? In the beginning, BMOW was conceived with a 16-bit address space, allowing for 64K of addressable memory:

Range Size Contents
$0000 – $3EFF 15.75K ROM
$3F00 – $3FFF 256 bytes memory-mapped devices
$4000 – $FFFF 48K RAM

Pretty simple. But at the last minute before I began hardware construction, I decided to expand the address space to 24 bits, allowing for 16MB of addressable memory. I added a third byte to the address bus called the bank byte, along with the existing low and high bytes. The instruction set was left mostly unchanged, however, so most instructions use 16-bit addresses, and implicitly reference the current bank. Thus the address space was divided into 256 banks of 64K each, and program references to different banks are fairly awkward (think x86 segment registers, etc). I left the first 64K bank mapped as it was originally planned, but added more RAM and eventually video RAM elsewhere in the address space. As a result, the current BMOW memory map looks like this:

Range Size Contents
$000000 – $003EFF 15.75K ROM
$003F00 – $003FFF 256 bytes memory-mapped devices
$004000 – $07FFFF 496K RAM
$080000 – $1FFFFF unused
$200000 – $207FFF 32K Video RAM
$208000 – $FFFFFF unused

A little odd, but it works. The problem I’m running into now with this memory mapping is that there’s only 16K of ROM. That’s not much space to fit a whole collection of cool built-in demos. Physically the ROM is actually 128K, so more space exists, it’s just not mapped anywhere into the address space. This is what ultimately motivated the change to the memory map that I’m contemplating now. I assembled a set of requirements and goals for the new memory map:

  • $000000 must be ROM, because that’s where the CPU begins after a cold boot.
  • The interrupt vector must be somewhere in the first three banks ($000000 – $02FFFF) or last two banks ($FE0000 – $FFFFFF), due to limitations on the constants I can generate in microcode, so some part of that range must be RAM.
  • The interrupt vector should be kept at its current location ($00FFFE-$00FFFF) if possible, to avoid the need for microcode changes.
  • ROM must be mapped to a 128K boundary.
  • RAM must be mapped to a 512K boundary.
  • ROM should ideally be mapped to a contiguous 128K address space, with no holes.
  • RAM should ideally be mapped to a contiguous 512K address space, with none of it masked by ROM occupying the same space.
  • Existing memory mappings should be changed as little as possible, to minimize the amount of software changes required.

A relatively logical mapping would have ROM at $000000 – $01FFFF, RAM at $F80000 – $FFFFFF, and devices somewhere like $020000 – $0200FF. However, I’d really like to avoid the microcode changes required if RAM is relocated, and I have some reason to suspect intermittent hardware glitches due to address bus signal noise if using bank $FF. Relocating RAM entirely from its current position would also require more software changes.

Instead, I’m planning to keep the existing mappings as they are, but add additional mappings for the same RAM and ROM at different address ranges, in order to meet the other requirements. So RAM and ROM will appear multiple times in the address space. This is a bit strange, and I may come to regret it, but I think it will solve my current problems with minimum impact. Essentially, ROM will be re-mapped to a currently unused address space in a contiguous 128K block, and RAM will be re-mapped to an unused contiguous 512K block. Existing microcode and hardware will continue to work as-is, but new software can take advantage of the new mappings to access additional ROM and previously-hidden RAM. The new memory mapping will look like this:

Range Size Contents
$000000 – $003EFF 15.75K ROM
$003F00 – $003FFF 256 bytes memory-mapped devices
$004000 – $07FFFF 496K RAM
$080000 – $0FFFFF 512K mirror of RAM
$100000 – $11FFFF 128K mirror of ROM
$120000 – $1FFFFF unused
$200000 – $207FFF 32K Video RAM
$208000 – $FFFFFF unused

On a side note, the BMOW site has been receiving dramatically more visitors in the past week, after it was picked up on Hack-a-Day and Reddit. Hello to the new readers!

Read 3 comments and join the conversation 

Final Touches

Today I installed the front panel LCD, and hooked up the all-important power light. Fitting the front panel LCD was a major pain in the ass, especially after the death of my Dremel. I cut a hole in the front panel using a pair of tin snips, but it came out looking pretty ragged. Oh well, it’s functional. I had to ditch my faithful 20×4 LCD that’s been with me practically since day one, since it was too tall to fit, and substituted a 24×2 LCD in its place. As you can see in the photo, it works fine, but will require an update to my LCD driver to make text word-wrap correctly.

Barring anything unforseen, that’s it for BMOW hardware. I’ll be packing away the soldering iron and wire-wrap tools now, and concentrating on the software end of things for a while.

Read 1 comment and join the conversation