Character Study. Dan’s MEGA65 Digest for May 2025.
(Audio) Character Study. It’s upgrade season in the MEGA65 community! We partied hearty for the 10th anniversary last month, and the result is an all-new stable release of the MEGA65 platform packed with features and bug fixes, a bug fix update to the C64 core, and a new version of the M65Connect app. We also finished up the Screenful of BASIC compo, with 26 entries!
For this month’s feature article, I’m starting my series on MEGA65 character graphics. It’s a big subject that we’ll cover over multiple issues. We’ll probably take breaks to cover other subjects just to mix things up.
Here we go!
MEGA65 NewsMEGA65 platform v0.97 released!It’s here! Over 14 months in the making, the new MEGA65 platform release v0.97 is now available. The MEGA65 team recommends that all owners upgrade to this version.
Sign in to Filehost with your registered owner account, then download the release package appropriate for your mainboard version:
Upgrade instructions are in the latest User Guide. Remember to copy the SD card files to your SD card, including the .M65 system software files and the MEGA65.ROM file.
Also remember to install the core in slot 1, then also in slot 0. Upgrading slot 0 is easy if you previously upgraded slot 0 to v0.96: press Mega + , (comma) from the core selection menu to start the process. If you have an older core in slot 0, you’ll need to start v0.97 from slot 1 then immediately press and hold No Scroll to re-open the newer version of the core selection menu, then press Mega + , and proceed.
We reviewed the changelogs back in the March Digest. Check out all the new stuff you’re getting.
C64 Core v5.2 released! Various C64 cartridges tested with the C64 core.
While you’re updating cores, be sure to pick up C64 core v5.2. This maintenance release fixes several bugs, including the HDMI display bug that has blocked a minority of users with a set of vertical bars. It also adds support for COMAL 80, Simons BASIC, Waterloo Structured BASIC, Mikro Assembler, and BMP-Data Turbo 2000 as CRT files.
Huge thanks to everyone testing and contributing fixes to the C64 core!
M65Connect v2.4 released!M65Connect, the essential desktop companion app for the MEGA65, has an update also! Version 2.4 includes richer support for subdirectories, creating new D81 disk images, and many usability improvements. Whether you connect to your MEGA65 via Ethernet or JTAG adapter, or just move a microSD card between your MEGA65 and your PC, M65Connect is the easiest way to manage your files and perform other tasks.
Starting with M65Connect v2.4, macOS no longer complains that the app is “corrupt,” thanks to a long-awaited bug fix in a library that the app uses. As with many open source apps, the macOS version is “unsigned,” and the first time you run it will be blocked by the operating system. In macOS Sequoia, open the app, dismiss the message with the “Done” button, then open the Privacy & Security panel in macOS settings and scroll down to find the button to give it permission to run (“Open Anyway”).
Thanks as always to Tayger for this great tool.
Screenful of BASIC Compo 2025 results! The Screenful of BASIC Compo 2025 compilation disk menu.
From February to April, the Screenful of BASIC Compo 2025 challenged us to write a game, demo, or tool using just an 80x25 screenful of BASIC 65 code. There were 26 fabulous entries.
I put together a compilation disk with all of the entries and a browsable menu. Try them all!
After a friendly round of voting, three winners came out on top:
Want to know more about compact BASIC coding techniques? Check out The 8 Bit Theory’s YouTube video explaining his Screenful Compo entry, “Screenful Crawler,” a 3D maze in just 16 lines.
Several entries have already had updates and expanded versions published to Filehost, so be sure to check those out as well.
Featured FilesMore amazing things recently uploaded to Filehost:
Don’t forget that you can follow the MEGA65Files bot on Mastodon or your favorite federated social media service. The bot posts about both new files and updates to existing ones. If you don’t have a fediverse account, you can also follow the bot’s page in your RSS reader.
Why character graphics? Some pixels of the MEGA65 start-up banner.When you first switch on your MEGA65, it shows a screen with a displayable area 640 pixels wide and 200 pixels tall, capable of displaying up to 4,096 possible colors. The MEGA65’s VIC-IV video chip determines the color of each pixel by reading graphics data from memory, and generates a video signal that draws the final image to the screen, many times per second.
If we were designing a graphics system, how would we design the data that describes the image drawn to the screen? One possibility would be to store the color of each pixel. This screen is 640 x 200 = 128,000 pixels. With 4,096 possible colors, each pixel needs to be stored as 12 bits. 128,000 x 12 = 1,536,000 bits = 192,000 bytes, or 187.5 kilobytes. That’s a lot of memory to dedicate to graphics data. Even if we could find a place to put it in the MEGA65’s RAM, it would require substantial computational power to update this memory to draw or animate objects, or scroll a playfield.
Pixel graphics: 12 bits represent one pixel.The most obvious way to reduce the memory demand of the graphics system is to reduce the quality of the image. Using a pixel resolution that’s 320 pixels wide instead of 640 cuts the needed memory by half. Reducing the number of possible colors to 256 (a *bit depth* of 8) cuts it by a third. Naturally, these changes diminish the amount of graphics information we can display. Instead of 80 columns of text, we can only display 40 columns. A program that displays a photograph has to settle for less detail or less color accuracy. Space aliens are chonkier.
There are other ways a graphics system can reduce its capabilities without making such obvious sacrifices in image quality—and this is where things get interesting. Many computer applications do not need the ability to set every pixel on the screen to any possible color at any time. By limiting the system’s capabilities to just what common applications need, a graphics system can use less memory to describe the full display, and still produce high quality images.
Character graphics: 16 bits represent 64 pixels.Character graphics are one such scheme. You probably already have a sense of how this works from typing letters, numbers, and symbols at the `READY.` prompt. In the default text mode, each of these symbols — let’s call them *characters* — is a small image, 8 pixels wide and 8 pixels tall. On our 640 x 200 pixel screen, 80 of these characters fit on a row, and 25 rows fill the screen. The graphics system knows what each character looks like from a *character set* stored in memory, containing the images for each of 256 possible characters, including a blank character to represent empty space. By assigning each character a *screen code* from 0 to 255, we can describe the entire screen in just 80 x 25 = 2,000 bytes of memory.
The PETSCII uppercase character set.This scheme uses a *monochromatic* character set: each character cell is assigned a single foreground color, and the image in the character set describes which pixels should be set to this color. The graphics system uses another 2,000 bytes to describe attributes of each character cell, including five bits to set the foreground color. Each cell can use a different foreground color for its foreground pixels. All other pixels share a common background color for the entire screen, set in a VIC register.
Of course, five color bits can only represent up to 32 colors, not 4,096. Just as the graphics system uses a character set of 256 character patterns, it also uses a palette of 32 colors. Each palette entry is assigned a 12-bit color setting. The MEGA65 starts with a system character set of PETSCII symbols and a system palette of 32 pre-defined colors.
The default MEGA65 system palette.This character graphics scheme uses very little memory compared to the 12-bit pixel scheme, and allows for much faster updates. In exchange, it trades away flexibility in what images can be represented. The entire screen *must* consist of repeated patterns of 8 pixel x 8 pixel characters selected from a set of 256, only a single foreground color can be used for each character, and colors must be selected from a set of 32.
This trade-off is obviously worth it for text applications, such as the BASIC screen editor, a word processor, or a BBS terminal program. Less obvious, at least at first, is that character graphics are also useful for graphical displays for some games. The default PETSCII character set includes glyphs that can serve as game graphics on their own, such as the heart, diamond, spade, and club for playing card suits. Some symbols are designed to be combined with others in adjacent cells to draw larger objects. Commodore programmers and artists have been exploiting the default character graphics mode for decades, with spectacular results.
Monstro Giganto, by The Pirates of Zanzibar.Screen memory Memory layout for an 80x25 screen (2,000 characters).Both screen memory and color memory organize characters by row, starting in the top-left corner and going down the screen. For the default 80 x 25 text mode, each row is 80 bytes, so the first row left-to-right is represented by screen codes in positions 0 to 79, the second row is positions 80 to 159, and so on.
You can tell the VIC-IV chip to use any address within the first 384 KB of memory as the start address of screen memory. This is a special feature! The Commodore 64’s VIC-II, in contrast, has restrictions on which memory can be used as screen memory. With the VIC-IV, the starting address for screen memory is stored as a 28-bit address, in a register called SCRNPTR at addresses $D060 to $D063.
Here is a simple BASIC program that reads the SCRNPTR register to get the starting address of screen memory, then draws all 256 characters from the default character set in a nice rectangle:
10 SC=WPEEK($D060) + WPEEK($D062)*6553620 FOR Y=0 TO 330 FOR X=0 TO 6340 POKE SC+Y*80+X,Y*64+X50 NEXT X60 NEXT Y
WPEEK() is a special form of PEEK() that reads two bytes of memory and interprets them as a 16-bit value stored in little endian order. Line 10 reads all four bytes, and assembles them into a 32-bit number, in this case the address stored in SCRNPTR.
You can change the location of screen memory at any time by updating this register. This can be used for switching between multiple displays stored in different regions of memory, or for techniques such as screen scrolling.
Try this. Set the SCRNPTR address to $6000:
WPOKE $D060,$6000:WPOKE $D062,0
You are now looking at the contents of memory at address $6000, interpreted as screen codes by the VIC-IV chip, one byte per character. If you did this on Xemu, the screen is probably filled with @ symbols, which use a screen code of 0. On the MEGA65, this could be all manner of crazy characters, whatever happens to be in memory at that location.
Memory at address $6000, interpreted as screen memory, in Xemu (left) and on the MEGA65 (right).If you did this just after switching on the computer, you may also see that the characters in the top left corner have retained the colors of the rainbow bar of the welcome banner. The VIC keeps screen codes and color codes in separate areas of memory, and so far all you have done is tell the VIC to look somewhere else for screen codes.
Another thing to notice: the screen editor’s READY. prompt and cursor have followed you to the new screen location. It’s cool that this seems to work, but note that not every feature of the KERNAL, screen editor, and BASIC know how to do this. Some features of the KERNAL still believe screen memory is where it originally put it. Reset the screen editor, including its SCRNPTR, by holding Run/Stop and tapping Restore. Or just reset the computer.
Low and high resolution text modesThe Commodore 64’s VIC-II is capable of text in 40 columns and 25 rows. This was extended for the Commodore 65’s VIC-III to also support 80 columns. The VIC-III can switch between these two modes. In fact, this is how GO64 mode works: it downshifts the VIC-III into its VIC-II compatibility mode, including adjusting the screen to 40 x 25 text mode.
The 640-pixel wide 80-column text mode is controlled by the H640 register, bit 7 of address $D031. You can switch to 320-pixel wide 40-column text by clearing this bit:
CLRBIT $D031,7
Naturally, 40-column text uses less screen memory than 80-column text. It’s still using the same screen memory location of SCRNPTR, but only reading the first 1,000 bytes to describe the 40 x 25 screen. The memory layout is similar, organized by rows of 40 instead of 80.
Once again, the KERNAL doesn’t know you have changed modes, and still thinks the display is 80 characters wide. Re-enable 80-column mode:
SETBIT $D031,7
The MEGA65 VIC-IV can also double the vertical resolution from 200 pixels tall (25 rows) to 400 pixels (50 rows). This is controlled by the V400 register, bit 4 of $D031. Try enabling it:
SETBIT $D031,3
If you’ve tried the screen editor’s 80 x 50 mode before, this might look familiar—except there’s a bunch of garbage on the bottom half of the screen. Also, if you try typing multiple lines, it scrolls at line 25. What’s going on?
You guessed it: the KERNAL doesn’t know you enabled V400 mode in the video chip. The VIC-IV is now reading 4,000 bytes of screen memory starting at the screen editor’s internal location. This space is not meant to hold so many screen codes, and the KERNAL is using the latter 2,000 bytes for something else. Restore sanity by disabling V400 mode:
CLRBIT $D031,3
But wait, we know that the screen editor does support an 80 x 50 mode. Press Esc then 5 to enter this mode, and notice that the screen is nice and clean, and you can use the full space. How does this work?
We can get a hint by inspecting the SCRNPTR register while this mode is active:
PRINT HEX$(WPEEK($D060) + WPEEK($D062)*65536)
Notice the address that it prints. Switch back to 80 x 25 mode (Esc then 8) and enter the command again.
The screen editor uses a different SCRNPTR address for 80 x 50 mode, switching to a location with more space to use as screen memory. It also keeps track of which mode you selected, so it knows how many rows and columns it should be using in this space. It does not check the H640 and V400 registers in case some smart aleck changed them, so it is easily fooled if you don’t switch screen editor modes properly.
Can the VIC-IV display text 40 columns wide and 50 rows tall? Only one way to find out.
Color memoryEach character on the screen has a corresponding color code assigned to it. The code refers to a color defined in a palette. In the text modes we’ve seen so far, the assigned color becomes the foreground color for the character: referring to the character set images, the VIC draws an “on” pixel in the character’s color, and an “off” pixel in the background color.
Programming color memory is a bit weird.
The VIC-IV maintains a whopping 32 kilobytes of color memory from address $FF8.0000 to $FF8.7FFF. By default, color memory starts at the beginning of this range. The memory layout is the same as screen memory. An 80 x 25 text screen uses the first 2,000 bytes of this range for color data.
PRINT CHR$(147);"A" : rem: Put an A in the cornerPOKE $FF80000,4 : rem: Make it purple.
You can adjust the starting address of color memory, but only within this range. The COLPTR register at $D064 to $D065 stores a 16-bit offset to the start of color memory. Think of it as the last four hexadecimal digits after “FF8.”
WPOKE $D064,$07D0 : rem: Color memory starts at FF8.07D0POKE $FF807D0,7 : rem: Make the A yellow.WPOKE $D064,$0000 : rem: Color memory starts at FF8.0000. : rem: A is purple again.
Buffer switching Switching between two views.Imagine a program that maintains two screens of 80 x 25 text. Such a program would determine two locations in memory for the 2,000 bytes of screen memory per screen, and set SCRNPTR to the starting address of the screen to be displayed. The program would also determine two locations in color memory for 2,000 bytes per screen, and set COLPTR to the current screen’s color data.
By changing the VIC pointers, a program can make a big change to the display using very little CPU time. While the user is looking at one screen, the program draws the next screen in the other set of memory, then at the last millisecond, it changes the pointers. As soon as the pointers have changed, the VIC immediately starts drawing from the new screen data. From the user’s perspective, the screen switch is instantaneous, and they never see any of the computation that goes into updating screen and color memory.
There is a teensy amount of time after updating SCRNPTR and before updating COLPTR, such that the VIC is drawing from the second screen’s screen codes and the first screen’s color codes. If this is a concern for your program, you can use a VIC raster interrupt to make sure the switch happens while the VIC is drawing the top or bottom border. By the time the raster beam gets to drawing characters, both pointers are updated.
Color memory windows Other ways to access the top of color memory, for VIC-II and VIC-III compatibility.The Commodore 64 only supports a 40 x 25 character graphics screen, and so only ever needs 1,000 bytes of color memory. The VIC-II keeps these bytes among the I/O registers, at addresses $D800 to $DBFF (that’s dee-bee-eff-eff).
The Commodore 65 introduced 80-column mode, requiring 2,000 bytes of color memory. To keep this data among the registers and stay compatible with the C64, the VIC-III has a one-bit register, CRAM2K, bit 0 of $D030, that switches the upper I/O registers to act as more color RAM, extending the range: $D800 to $DFFF.
To make it possible to access all 2,000 bytes of color memory without hiding I/O registers, the C65 also exposes the same memory at $1F800 to $1FFFF. This is outside the first 64 KB of memory, so a program has to use the 4502’s indirect addressing modes to access it. (A program could also use the 4502’s MAP feature to associate 16-bit addresses with 28-bit addresses. Personally, I only use MAP for executing CPU instructions in upper memory, and use indirect addressing for data. You have to be careful with the MAP feature to not accidentally un-MAP your program while it is running.)
The MEGA65 supports all of this. $D800 and $1F800 are the beginnings of two equivalent windows into the color memory starting at $FF8.0000. If the first 1,000 or 2,000 bytes of color memory are all you need, you can use these legacy methods, perhaps for the added convenience of $D800 being in the first bank. Remember that 80 x 50 text mode uses 4,000 bytes of color memory, which is more than these windows can see.
There’s one rather large caveat: these windows always reflect the top of color memory at $FF8.0000, not where COLPTR is pointing. If you set COLPTR to $0800, $D800 and $1F800 will continue to represent memory at $FF8.0000, and won’t be able to reach any of the memory that the VIC is using to paint color on the screen. The windows don’t move, which limits their usefulness.
If you’re not using the windows, there’s a neat trick you can do here. By setting COLPTR to $0800, the windows point to memory that’s not being used, from $FF8.0000 to $FF8.07FF. With this setting, you can use the windows as regular memory for other purposes. (If you need to be really technical, this memory is slightly slower to access than other memory in banks 0 through 5, but your program might not notice.)
Color and character attributesEach byte of color memory stores eight bits of information. I mentioned that five of these bits select the color, with the ability to select any of the first 32 colors in the palette. The remaining three bits apply interesting text effects to the character.
The color bits are 0, 1, 2, 3, and 6. Another way to think of it is, bits 0 to 3 select one of 16 colors, and bit 6 selects either the first 16 colors (0 to 15) or the second 16 colors (16 to 31).
PRINT CHR$(147);"A" : rem: Put an A in the cornerPOKE $FF80000,%00000100 : rem: purple (4)POKE $FF80000,%00001111 : rem: light grey (15)POKE $FF80000,%01000000 : rem: "guru meditation" red (16)
C65 documentation and the MEGA65 documentation describe bit 6 as “bold.” The idea is for a program to set colors 16 to 31 to be “bold” versions of colors 0 to 15, when such an effect is desired. Neither the C65 nor the MEGA65 have a boldface text effect, it’s just using a different set of colors.
Bit 4 causes the character to blink in a steady pattern. With only this attribute set, the VIC alternates between two states: draw “on” pixels in the foreground color, or draw all pixels in the background color.
Bit 5 reverses the character. With a monochrome character set, this means that “off” pixels are drawn in the character’s foreground color, and “on” pixels are drawn with the background color.
Bit 7 underlines the character. The bottommost line is drawn with all “on” pixels, regardless of how the character appears in the character set.
Things get a little dicey when you turn on more than one character attribute at a time. This was so confusing to me at first that I filed it as a bug years ago, and even submitted a “fix” to make it more intuitive. I eventually discovered that Commodore explicitly intended it this way, so I removed the “fix” in favor of a backwards-compatible feature request that we have not yet implemented.
For starters, “reverse” does not take effect if the “bold” palette is selected. There is no way to reverse a character and also set it to a color from 16 to 31.
“Blink” has a different behavior if any other attribute is set. With no other attributes set, “blink” causes the foreground pixels to alternate on and off. With any other attribute set, including the “bold” attribute, “blink” causes the attributes to blink on and off, and the character stays visible. Blink + underline: blink just the underline. Blink + reverse: alternate normal and reverse. Blink + bold: alternate between the two sets of colors.
Here’s a program that demonstrates every attribute combination and color:
10 PRINT CHR$(147); :rem (Notice the semicolon.)20 FOR Y=0 TO 1530 PRINT "ABCDEFGHIJKLMNOP ";Y40 FOR X=0 TO 1550 POKE $1F800+Y*80+X,Y*16+X60 NEXT X70 NEXT Y
I don’t like how this makes certain combinations inaccessible, especially the ability to blink a character that’s using the “bold” color set between visible and invisible. But I eventually convinced myself that Commodore had good intentions, and if we want to add more control over the attribute behaviors, we need to preserve Commodore’s design as the default. At least it’s quirky.
The blink + reverse effect resembles the blinking cursor of the screen editor. It’s interesting to notice that this is not actually how the blinking cursor works. Just as on the C64, the C65 default character set consists of 128 characters, followed by their reversed equivalents, for a total of 256 characters. The cursor blink is actually modifying the screen code, switching between the reversed and non-reversed variants of the character under the cursor. That seems like a waste considering there’s a blink + reverse mode that achieves a similar effect. The cursor could just set these attributes, and the default font could contain 128 more useful characters.
Unfortunately, an attribute-based cursor would be constrained by the unusual way that attributes combine. If an attribute-based cursor were above a character using the “bold” palette, the cursor would try to set “bold” and “reverse,” which cancels the “reverse” effect. Earlier versions of the MEGA65 ROM attempted this, and we had to go back to the screen code-based cursor because users noticed that the cursor would vanish when using the upper 16 colors.
A look ahead: video modesIn this Digest, we introduced the main idea behind character graphics: representing a screen as a coarse grid of characters, instead of a fine grid of pixels. Restricting the flexibility of the display to a common category of applications saves memory and compute time, an essential resource on microcomputers. We saw how the default text mode of the MEGA65 displays a grid of 80 x 25 characters, selected from a set of 256 characters, each displayed in one of 32 colors. Programs can manage display memory using pointer registers. We also took a brief look at how the text display can be switched between two horizontal resolutions (320 pixels or 640 pixels) and two vertical resolutions (200 pixels or 400 pixels), each combination fitting a different number of characters on the screen.
Vintage video systems typically support multiple video modes to give programs the opportunity to choose their own tradeoffs of capabilities and costs. Over several Digest articles this year, we’ll explore more features of the MEGA65’s character graphics system. We’ll see that many of these features involve switching modes that adjust specifications and change the way screen and color memory are interpreted by the VIC.
Next month, we’ll dive deeper into character sets and color palettes, both of which can be completely customized by a program. We’ve visited custom character sets a few times in previous Digest issues, and it’s about time we lay all the cards out on the table. We’ll also play around with some multi-color character graphics modes from the VIC-II and VIC-III, and work our way up to the crown jewel of MEGA65 VIC-IV character graphics, Super Extended Attribute Mode.
If you’re as excited about this series as I am, please consider supporting this Digest. Visit: ko-fi.com/dddaaannn
So much to do, so little time! See you next month.
— Dan
MEGA65: The First Ten Years. Dan’s MEGA65 Digest for April 2025.
(Audio) MEGA65: The First Ten Years. MEGAVOXL, by MirageBDTen years ago, Paul Gardner-Stephen and the Museum of Electronic Games and Art announced a joint effort to recreate the Commodore 65. The MEGA65 would be an open-hardware open-source product, based on the original Commodore designs and prototypes, manufactured in Germany and sold worldwide. Instead of fading into obscurity as a historical footnote, the Commodore 65 would achieve new potential as a modern day platform for recreational computing, a revival both familiar and new to thousands of people looking for a way to reconnect with the creativity, stimulation, comfort, and digital sovereignty of the 8-bit era.
In this special edition of the Digest, we’ll review the first ten years of the MEGA65 project through published media, articles, videos, and conference talks. Take your time to click through the links below and experience the project through the eyes of the people that built it and the community around it.
We also have some exciting announcements to celebrate the anniversary! Don’t miss this one!
The Great MEGA65 Egg Hunt begins This one-of-a-kind original MEGA65 prototype can be yours!Breaking news from Detlef “deft” Hastik:
Having used citations and celebrated events from the history of computer nerds, and being fans of the book (not the movie!) Ready Player One, we have hidden an Easter egg in every MEGA65! The first person to find it will win an insane piece of MEGA65 history: the very first working and beautifully shaped MEGA65 prototype, shown in 2015 at AMIGA 30 in Neuss/Germany, six months after the project was officially announced by Paul Gardner-Stephen (AU) and Detlef Hastik (DE). This unicorn computer has an insurance value of over 15K USD, is signed by Dave Haynie, RJ Mical, Chris Hülsbeck, Dino Dini and other famous people, sports an unprinted (!) original C65 keyboard inside an SLS fabricated C65 case (yes, it’s made with lasers!) and - it also works!
Prepare for an unforgiving hunt through the depths of the MEGA65, through time, and of course, through space! There can be only one person to enter the nerd throne and win this nerd’s dream machine. Will it take days, weeks, months - or even years to solve the mind bending puzzles left by the creators and not discovered for years? Will you be the one to earn the crown?
Friends (and friends of friends) of the MEGA65 core team are not allowed to participate. Be prepared to be questioned. A time stamp will decide who the lucky winner is in case several people find the egg. It is allowed to build clans and search in groups :)
That’s right, this one-of-a-kind piece of MEGA65 history can be yours—if you know where to look! This multi-step trail has been under our noses for years and has not yet been discovered independently. Best of luck to everyone!
RJ Mical admiring the MEGA65 prototype, at AMIGA 30 in Neuss/Germany, October 2015.Intro Disk #4 Intro Disk #4Every MEGA65 comes bundled with a large collection of software produced by the community, browsable by a menu that opens automatically when you first turn on the computer. The Intro Disk collection has added more titles each year since the MEGA65’s debut in 2022, and it’s time to expand it again!
Intro Disk #4 is now available for download. MEGA65 owners, sign in to Filehost and use this link for a version with extra licensed content. The new archive has an all-new menu system by MirageBD with music by Hein, and adds 72 new titles and dozens of articles about projects you can download and install. This brings the total size of the Intro Disk collection to 264 games, demos, and tools.
The entire collection has been revised and tested with the upcoming v0.97 release core, ROM, and system software. ID4 should work with release v0.96, but if you notice issues, I recommend installing the test version of the upcoming release (as discussed in last month’s Digest).
Huge thanks to Gurce, who has spearheaded the Intro Disk project from the very beginning. Thanks to his efforts curating, building, and testing the collection, everyone can experience what the MEGA65 has to offer, right out of the box.
Screenful of BASIC Compo, last call!If you’ve been waiting until the last minute to enter the Screenful of BASIC Compo, congratulations! It is now the last minute! The itch.io game jam page will accept entries until 11:59 pm GMT, April 22, 2025.
After the deadline, all entries will be revealed for download, and a friendly round of voting will begin. Try out all the entries for yourself, cheer on your fellow competitors in the comments, and boost your favorites.
If you have any last minute questions or technical issues with your entry, be sure to ask in the Discord or the Forum64.de thread. Go go go!
More from deft!Another note from deft:
But what’s this? Something stirs behind the curtain - quiet talks, shifting shapes, and a name long thought dormant may soon rise to breathe new life into an extraordinary machine. Could something we all hold dear be wearing new clothes in the future? Those threads are being woven in fractures between past and present, by hands both trusted and unexpected. Something is cooking. What’s on the menu? Nothing is confirmed. Everything is possible. Watch this space.
Commodore: The Final Years The Commodore 65, developer unit #23, rev 2A mainboard. Owned by Jim Brain, photographed at Vintage Computer Festival Midwest 2024, next to MEGA65 materials.At the Consumer Electronics Show of 1985 in Las Vegas, Nevada, Commodore Business Machines announced two new computers: the Commodore 128 and the Commodore LCD. The C128 went on to sell 2.5 million units over the next four years. The CLCD was cancelled before it was released.
That same year, Commodore began developing the 4502 CPU, later rebranded as the CSG 65CE02. Originally intended as a more efficient CMOS version of the 6502, the final design of the 4502 was faster and more featureful, compatible with the documented 6502 instruction set, with new instructions and enhancements for making use of more memory. The new chip would be a drop-in replacement for the 6502, at least in some applications.
Chip designer Bill Gardei conceived of a new home computer using the new CPU, an 8-bit successor to the C64 and C128 that would eventually be known as the Commodore 65. The C65 would feature the 4502 (packaged with other components as the “4510”), along with an enhanced VIC-III (4567) graphics chip to follow the VIC-II from the C64 and C128. Like the C128, the C65 would run existing C64 software and support most C64 peripherals. And the faster CPU, improved graphics, and target price would beat the new home game console everyone was talking about: the Nintendo Entertainment System.
By 1989, Commodore needed the C65 to fill out the low end of its home computer line, something that would capitalize on the popularity of the seven-year-old C64, and be less expensive than the Amiga 500 (1987). Three people were tasked with delivering the new system in time for the 1989 holiday season. Gardei continued to work on the chipset. Fred Bowen developed the KERNAL ROM and BASIC 10 language, emboldened by a luxurious 256 KB of ROM space and a budget to hire contractors. Paul Lassa designed the motherboard and hardware layout. Despite the team’s best efforts, hopes for a 1989 release of the C65 were dashed by issues with the test chips coming back from the Commodore Semiconductor Group. Only a few prototype systems had been assembled by August 1989, and many bugs remained.
The delays gave Lassa an opportunity to work on a new feature: a DMA chip capable of copying large regions of memory at high speed. Lassa even managed to sneak in Amiga-like features for drawing large graphical objects using the chip. Gardei and others worked on improving the 4502’s compatibility with C64 software, made difficult by efficiency improvements that caused legacy programs to run with incorrect timing. By the time chip designs stabilized, 1990 had come and gone.
While there was still some residual momentum within Commodore to launch the C65 in 1991, North American sales teams were no longer interested, and work had already begun on a low-cost Amiga that would be released as the Amiga 600 the following year. Commodore leadership de-staffed the already under-staffed teams, and the C65 project was cancelled. In the throes of financial difficulty, Commodore began selling off the contents of its warehouses in 1993, including an unknown number of C65 prototypes, estimated to be fewer than 200. By mid-1994, Commodore was out of business.
Paul’s Commodore 65 Paul's Commodore 65, March 2010The Commodore 65 prototypes drifted among dealers and enthusiasts, mostly as curiosities, considering there was no software written for the unreleased platform. On the outside, the prototypes looked nearly finished, with the familiar wedge-style case, keyboard, and built-in floppy drive. Inside, the prototypes varied, with inconsistent versions of the mainboard and chips. The KERNAL ROM was incomplete, no meaningful software existed other than a few simple in-house demos, and no documentation was publicly available at the time.
One C65 prototype made it into the hands of engineer Paul Gardner-Stephen. Despite the lack of commercial software, Paul made regular use of the machine, learning what he could through experimentation and writing his own programs. Over the next sixteen years, the historical value of the prototype machine eventually exceeded Paul’s ability to care for it, and he sold it to a collector in the year 2010.
For the lucky few that owned them, the C65 was a glimpse into an alternate history with one more generation of 8-bit home computers. Paul conceived of a new 8-bit based on his experience with the C65. By 2010, FPGA technology had become fast enough and inexpensive enough for a single person to develop such a computer, without the large scale chip fabrication facilities used by Commodore developers to iterate on the original designs.
His C65 now in other hands, Paul started work on the “C65GS,” a new computer with the same spirit as the C65 and some degree of backwards compatibility. Paul originally expected the project to take the form of an “accelerator” board that would connect to vintage Commodore 64 hardware. The C65GS would be built using an off-the-shelf FPGA development kit, so early stages of the project could proceed without fabricating any parts.
Paul joins MEGA MEGA demo, April 2015The Museum of Electronic Games & Art (MEGA), a German non-profit organization dedicated to computer engineering education, creativity, and preservation, already had some experience recreating vintage computer history by the time they heard of Paul’s project. In 2011, MEGA members recreated Tennis for Two (1958), the early video game milestone played on an oscilloscope. In 2013, MEGA debuted their recreation of the ENTEX Adventure Vision (1982) with its unique spinning mirror display. These projects produced research documentation, emulators, tools, and prototypes that would join MEGA’s traveling exhibits that also included preserved hardware and modern demo software.
MEGA was exploring a potential project based on the Commodore 65 when they discovered Paul’s C65GS developer blog. MEGA and Paul joined forces in 2014, and relaunched the project in April 2015 as the MEGA65. The new computer would put Paul’s FPGA-based design into historically accurate reproductions of the Commodore 65 case with keyboard and built-in 3-1/2" floppy drive, as well as modern conveniences such as digital video output and Ethernet connectivity.
Keyboard cases and community The second MEGA65 prototype, January 2016MEGA made steady progress on the keyboard case, exhibiting generations of prototypes in 2015 and 2016. The earliest prototypes were already measurement-exact reproductions of the C65 case and keyboard. Meanwhile, FPGA development was well under way. Paul and Detlef established the project with open source and open hardware licenses, and began the process of building a community of volunteer developers and testers. Paul published regular updates to his developer blog. As part of his work as a researcher and lecturer at Flinders University in Australia, Paul used the MEGA65 as a learning platform for his students. MEGA began promoting the project to solicit developer interest, and to stoke the imagination of the vintage computing community.
MEGAPhone: the big picture MEGAPhone prototype, June 2019It’s easy to assume that the warm feelings we have for personal computers from our childhoods are purely nostalgia. Perhaps we only feel this way about these piles of junk because we grew up with them. But most people I know in this hobby cite another reason for the attraction: simplicity. Modern computing is built upon huge stacks of software. The lowest layers wrangle the unfathomable complexity of electrons coursing through switching circuits, and upper layers make the computer easier to program and easier to use. Over time, the depth and complexity of these layers have grown to alienate a computer’s owner from its power, ceding control of our data-driven personhood to software and service companies. Even when this exchange has obvious benefits—you didn’t have to code that web browsing program to use it—we get the creeping sense that our modern computers aren’t entirely ours. The technological centerpieces of our lives are merely rented, not owned, and don’t always act in our best interests.
In contrast, vintage computers are knowable, stable, comfortable. They do what you tell them to, and don’t do anything else. There are no background processes, there’s no unsolicited network activity, there are no operating system updates being installed remotely by the manufacturer, and there’s no user telemetry data being sent out to who knows where by something you didn’t know you installed years ago. It doesn’t take rose-tinted glasses to see this has some benefit over modern commercially-available computing products, something valuable that we may have lost.
Could modern-day personal computing learn from its ancestry? Could we restore the powers and freedoms we once had without sacrificing utility, or are the tradeoffs we’ve made for modern conveniences inevitable? Could the implicit principles of early personal computing be applied intentionally to the design of a modern computer? To a smartphone?
The MEGAPhone attempts to answer to that question. Funded by grants from the NLnet Foundation, MEGAPhone is the modern smartphone reimagined through the lens of the MEGA65 and Commodore 8-bit computers. It’s literally the same platform, running the MEGA65 FPGA core and ROM on portable hardware, extended with discrete mobile communication components. The “apps” are just programs, and you can write your own phone apps in BASIC 65 or machine code. The MEGAPhone architecture takes personal security and privacy to its strongest useful conclusion, mobile computing not beholden to a single manufacturer or provider, accessible and extensible by its user.
Pre-series, case molds, and DevKits Pre-series MEGA65 units, September 2019The MEGA65 hit another milestone towards wide distribution with the “pre-series” prototypes in 2019, a short run of complete units with early revision (R2) mainboards. These were sold privately to team members to raise money for the next phase of the project.
December 28, 2019: Paul presents the MEGAPhone at 36C3, the Chaos Communication Congress for 2019: “Creating Resilient and Sustainable Mobile Phones.”
The MEGA65 DevKit, June 2020The final version of the MEGA65 would use an injection molding process to produce the replica plastic case and keycaps. Unlike short-run processes like the vacuum shaping used for the pre-series cases, injection molding requires a significant up-front tooling cost, carving the shape of the plastic into reusable metal molds capable of surviving the injection process. MEGA turned to the growing community of enthusiasts for support, raising over 69,000 EUR in donations.
While the final cases were being developed, MEGA wanted to get the nearly-completed mainboards into the hands of eager developers. They produced a limited run of feature-complete MEGA65 DevKit models, with cases made from clear laser-cut acrylic panels. These would be the first MEGA65s sold to the public. The 100 units sold out quickly.
Picking up steam Hibernated 1: Director's Cut, by Stefan Vogt, published by poly.playThe MEGA65 project had permission from Commodore intellectual property licensor Cloanto to distribute the original Commodore 65 prototype KERNAL ROM with the computer. The code dumped from the C65 prototype ROM chips was incomplete, and it seemed clear that it wouldn’t provide the best experience for MEGA65 owners in its original form.
The team formed two projects to address this. One group dug out the original Commodore 65 KERNAL source code from archives, wrote a custom assembler compatible with its vintage syntax, and began fixing bugs, completing features, and adding useful extensions. Another group began work on an all-new ROM, a clean-room implementation of the KERNAL intended to be API compatible with software written in BASIC 10 or using C65 KERNAL APIs, using no original Commodore IP so that it could be fully open-source licensed. The “closed ROM,” “open ROM,” and the original C65 prototype ROM would be included with the MEGA65, with an easy way for users to switch between them. The closed ROM was the most complete option by the time of launch, and it was chosen as the default experience.
Meanwhile, the community began writing and polishing a professional-quality user’s guide, to be printed and included in the box. The guide included a complete reference for the closed ROM’s built-in BASIC with the latest extensions, now called “BASIC 65” to differentiate it from the original C65’s BASIC 10. The typesetting was designed to resemble an idealized form of the original Commodore 64 manuals, and contributors spent many months editing and testing the material.
By September 2021, the MEGA65 was finally ready for its first production run. Manufacturing and distribution partner Trenz Electronic opened for pre-orders, and sold out the first batch within a few days.
Production models Intro Disk #1Thousands of people joined the queue for the new computer, excited that the project was close to achieving its lofty goals—and perhaps eager for something to do at home during the continuing global health crisis. Unfortunately, that same crisis disrupted supply chains for electronic components and other materials, and Trenz had to delay delivery of the first batch by several months. Subsequent delivery dates would be difficult to predict, dependent entirely on availability of components.
Work continued on the FPGA core and KERNAL ROM projects. The MEGA65 was designed to be upgradeable, such that everyone who would receive hardware would continue to benefit from improvements. The first batch of computers shipped with what would be known as v0.9 of the platform.
Also included with every computer: an SD card populated with dozens of programs written by early developers, DevKit owners, and team members. When a new owner first turns on the MEGA65, they are greeted by the Intro Disk menu, a colorful animated display with jaunty music. The SD card also bundled many Commodore 64 games and demos, which the user could run from the Commodore 65’s C64 compatibility mode.
I received my MEGA65 in May of 2022. To help new owners get over some of the rough patches in the early experience while the team worked to fix them, I wrote a Welcome Guide with advice and explanations. The project needed help staying in contact with people waiting on pre-orders and others who had expressed interest, so I started Dan’s MEGA65 Digest, a monthly email newsletter and read-aloud podcast. My hope was to get more information about the MEGA65 into various forms of online media, improve the ability to find the project through search engines, and keep people informed of project activity as we bootstrapped the community.
The second batch 45GS02 Quick Reference mousepadThe first delivery batch in May 2022 consisted of 400 units. Another 400 units shipped eight months later, in 2023. The supply chain crisis was ongoing, with limited FPGA supply going to the world’s biggest customers and leaving smaller projects on hold. As word spread about the project, more people got in line, but many who pre-ordered in late 2021 were still waiting.
With more computers in the hands of developers, the MEGA65 was coming into its own, both as a capable 8-bit home computer with attractive features for games and demos, and as an FPGA platform for alternate computer chipsets. One of the most influential FPGA projects was the C64 core, a highly accurate recreation of the Commodore 64 based on a similar project for the MiSTer FPGA platform. The C64 core was especially welcomed by MEGA65 owners who were hoping to use it as a C64 while waiting for the community to produce more MEGA65 software. The original Commodore 65 team never fully overcame design choices that limited compatibility with existing C64 software, and some speculate that this was a contributing factor that led to the C65 getting cancelled. With the C64 core, the MEGA65 could reconfigure itself with a C64 chipset and run nearly all C64 software with high fidelity.
Then came the first MEGA65 accessories. Publisher poly.play released several MEGA65 games as deluxe boxed editions. Paul designed a four-player joystick adapter for the cartridge port, and Jim Happel assembled some to distribute along with a disk of four-player games. Boutique vintage computer clothier Sew Ready produced a dust cover for the C65/MEGA65. I designed a machine code quick reference for the CPU that you can purchase as a mousepad or a poster. Paul also started work on an expansion board, a DIY kit that would sit inside the MEGA65 case and provide additional hardware features. (The expansion board is still a work in progress.)
We declared a new release of the MEGA65 platform—specifically the MEGA65 core, ROM, and system software—as version 0.95 in late 2022, in time to be installed at the factory on second-batch machines. Of course, owners of first-batch machines could upgrade to v0.95 for free. To accompany the release, we finished a 2nd edition of the User Guide that was used for the next printing of bundled manuals, and also made it available via print-on-demand so owners of the 1st edition could upgrade this as well.
General availability The MEGA65 retail boxKeeping a hardware project perpetually available at low volumes requires some flexibility when it comes to components. The MEGA65 is not a mass-produced appliance, and neither MEGA nor Trenz can purchase and store thousands of identical parts just to keep using the same mainboard design. In 2023, Trenz notified the team that components of the R3A revision of the mainboard used in the first 800 MEGA65s shipped would soon become unavailable. Paul updated the board design with newer replacement parts. He also took the opportunity to fix a few issues we encountered with R3A boards, such as unreliable Real-Time Clock chips. The R6 mainboard revision would be used for MEGA65s going forward, at least until the next component end-of-life notice.
Some members of the community expressed concern that the hardware revision meant that some MEGA65s were different than others, and certain products might only work with one revision or another. This was mostly noticed with the FPGA core projects, which needed to be updated by their authors for the newer board. Eventually, nearly all alternate cores were updated for both mainboard revisions. (As of this writing, only a few arcade cores remain to be updated.) More controversially, the R6 mainboard included a few extra features in addition to repairs. At the request of the community, Trenz Electronic began selling bare mainboards for anyone wishing to upgrade.
By 2024, the supply chain crisis abated. All remaining pre-orders for MEGA65s were fulfilled, and Trenz Electronic upgraded the MEGA65 to in-stock status in their store. Today, Trenz assembles computers in small batches to keep new orders for the MEGA65 on a short delivery timeline.
The Next 10 Years The thing that excites me most about the MEGA65 is that I keep meeting new people who are also excited about it. Trenz Electronic continues to receive new orders, and sometimes people tell me when they place their orders after seeing the MEGA65 at a conference or in a YouTube video. I’ve met people who had never heard of it before, people who heard rumors about it in the early days, and people who were on the verge of buying one and decided to take the plunge. Kids love it, at least the ones attending vintage computing conventions. They play the games, but they have just as much fun flipping through the User Guide and trying out the BASIC commands.
A healthy recreational computing community has multiple layers, each one grown from and supported by the one below it. Initially, these layers form around the platform itself: hardware hackers, FPGA core developers, KERNAL coders, technical writers. Then new people try it out, and find purchase to explore their interests and share their results: programming learners, game makers, demosceners, tool builders, media creators. As people are inspired by the passions of others, they contribute their own passions back, giving everyone more ways to enjoy and engage. People come and go, but the surface area continues to expand. This, more than anything, is what prevents technological abstractions from alienating users: there are people at every layer willing to help other people learn and explore.
What I most like about the MEGA65 specifically is how its layered design welcomes a community with diverse backgrounds and interests. The authentic Commodore-style BASIC is more accessible than ever, for running programs and for learning how to write new ones. Programmers both novice and expert can explore the undiscovered country of the architecture, pushing this alternate 8-bit timeline forward. And Commodore enthusiasts can access a previously inaccessible piece of history, use it with vintage peripherals and software, and summon more platforms with FPGA cores.
The community at the FPGA layer has been surprisingly active in the last couple of years. The MiSTer2MEGA65 framework and the excellent work of the MiSTer community draw a clear path toward someday having a complete Commodore computer museum that fits on your desk, in an authentic Commodore keyboard-wedge form factor. C65/MEGA65 and C64 cores exist today, PET and VIC-20 cores have public test releases, and there’s a great deal of interest in a potential Amiga core as well. There are cores for the ZX Spectrum, Game Boy, and TI-99/4A, with an Apple IIe core also in development, not to mention a dozen arcade cabinet cores. The MEGA65 has huge potential as an FPGA learning tool, as well.
Specific to the MEGA65 platform (that’s the core, ROM, system software), the MEGA65 Steering Committee is marshaling the project on a steady path to a v1.0 release, with an emphasis on feature quality and API stability. Development will continue beyond v1.0, using a high bar for backwards compatibility of changes, while continuing to track the interests of the community as it evolves and grows. As the current ROM lead, I’m especially interested in formalizing more of the KERNAL API surface and building hooks for third-party extensions. (Inner layers beget outer layers.) And we have tons of ideas for improving the non-platform parts of the user experience: on-screen interfaces, documentation, PC tools, networking.
MEGAPhone research will contribute features back into the MEGA65 platform. It may even be hardware you can purchase someday. The MEGA65 hardware will continue to adapt to shifts in global conditions, as resources allow, so the community can continue to grow.
And what was that “new clothes” thing deft was on about? 🤔
To ensure the growth and longevity of the platform and the community, we must continue to pump energy through the layers, inviting more people with diverse interests and enthusiasm. It’s why I write this Digest.
If you find this Digest useful, please consider becoming a supporter. Visit: ko-fi.com/dddaaannn
Congratulations to the MEGA65 team on an incredible ten years!
— Dan
What’s New in v0.97. Dan’s MEGA65 Digest for March 2025.
(Audio) What's New in v0.97. Messages printed by the MEGA65 on an Okimate 10 printer.We’re getting ready to release the next major update to the MEGA65 firmware and system software, and you can help! We’re still finishing up the last few fixes before declaring a release candidate, but you can grab the latest development versions of the core and ROM and try them out. The more information we have about what works and what doesn’t, the stronger this release will be.
In this Digest, we’ll review installation and testing instructions, and look through the changelogs for things that could use some exercise. Soon, we will declare a release candidate, get all hands on deck to test for a bit, then announce the new version. The result will become release package version 0.97, the new recommended stable platform for all MEGA65 users.
But first, even more new stuff!
MEGA65 NewsScreenful of BASIC 2025 compo reminder! A kaleidoscopic design from one of my Screenful 2025 entries.
The Screenful of BASIC 2025 compo is open and accepting submissions. Thanks to everyone who has entered so far! Remember that the deadline is April 22, 2025, 11:59 pm GMT.
I managed to get a couple of entries in. They only took me about an afternoon each. I tried to pack a lot of code into the small amount of space, but that’s hardly necessary. Poems don’t have to be dense to be effective.
Give it a try, and when you’re ready, upload your entry to itch.io and submit it to the Screenful game jam.
MEGASPUTUM v1.2 for Maniac Mansionkibo and M3wP continue to evolve MEGASPUTM, their LucasArts adventure game player, currently capable of running the Amiga version of Maniac Mansion. The newly released v1.2 of MEGASPUTM adds a really cool feature: the ability to switch between the Amiga version’s sound and music, and the Commodore 64’s SID-based sound and music.
Installing the data files from the Amiga and C64 versions of Maniac Mansion can be a challenge, so M3wP has made it easier with a new utility included with the player. Provide the original Amiga ADF and Commodore 64 D64 disk image files for the game, and the utility will install the appropriate files onto the D81 disk images, directly on your MEGA65.
Huge thanks to kibo and M3wP for this fun update, and for continuing to work on the project!
MiSTer2MEGA framework update The MiSTer2MEGA FPGA framework
sy2002 and MJoergen have updated the MiSTer2MEGA framework, upon which multiple alternate cores are based, to repair issues with R6 boards, HDMI output, and the joystick swapping feature. Authors of alternate cores will need to re-build and re-release the cores to pick up the fixes.
muse has already started the process of updating the arcade cores, starting with Bank Panic and Burnin’ Rubber. There is also now an R6 version of the Burnin’ Rubber core.
Featured Files Mega Chase, by shad0wfax.
Mega Chase by shad0wfax is an asymmetric 1-player or 2-player space battle game. Player One controls a powerful laser cannon capable of destroying ships in a single blast. Player Two (or the computer) pilots their ship, trying to dodge the laser. If the ship is hit before time runs out, Player One is victorious, otherwise Player Two escapes to fight another day. The game features original quad-SID music, multicolor sprites, custom text fonts, and sampled sound effects, and is written in BASIC.
muse’s arcade core of the month! Dig Dug (Namco, 1982) is now available for both R3 and R6 mainboards. Dig Dug was voted the 6th most popular arcade game of all time by the Killer List of Videogames, and was the 36th highest grossing according to sources gathered by Wikipedia. See the Github repo of the core for installation instructions, as usual.
Meteor Defense by Nuwanda puts you in charge of defending your city against a barrage of meteors. 80-column character based graphics give this challenging shooter extra charm.
Mars, 2049 AD by fredrikr is a short dialog-based text adventure game, written for PunyComp 2024 using the Inform 6 programming language and the PunyInform library. This MEGA65 version uses the Ozmoo interactive fiction player. You can visit the game’s itch.io page, or rate it at IFDB.org.
How to get the latest development core and ROMYou can download the latest development core and ROM at the following links. The core builds include the system files to copy to the SD card, as well as the core file to install from the core menu.
We covered installation steps and tips last year when we were testing v0.96, so I won’t repeat those. The User’s Guide, 2nd edition also has improved instructions for installing cores and system files.
The ROM changes in this release are expected to all be functional with the v0.96 core. If you can, please test the development core and ROM together, but if it’s more convenient for you, you can test the ROM separately. (This is not always the case for release testing, it just happens to be the case this time.)
If you don’t yet own a MEGA65 and want to help test the ROM in the Xemu emulator, the ROM FAQ explains how to use the latest ROM patch file. Please use the latest “future next stable” release of Xemu.
Remember that you can have multiple ROMs installed on your SD card. The ROM whose file is named MEGA65.ROM is used by default. To install a secondary ROM, give its file the name MEGA652.ROM, then hold the 2 key during boot. You can install up to eight ROMs this way with the v0.96 core; the latest development core expands this to ten ROMs, one for each digit key.
The development core includes changes to the Configuration interface. If you don’t want to make the development core your default core, you can access a non-default core’s interface by booting to the core selection menu (hold the No Scroll key while switching on the computer), selecting the core, then immediately pressing and holding the Alt key to open its version of the Configuration menu.
Pay attention to the Discord for updates and other testing instructions. Please file bugs in Github:
Core changes in v0.97 RC The new Configuration option to disable disk noise, in the v0.97 release candidate.New features for everyday use:
New features for programmers:
I flag: it uses a separate mechanism so that the program’s use of the I flag is represented accurately.Hardware support:
The v0.97 RC core also has improvements in these areas:
rts #... instructionThere are currently known issues with mounting disks from the Freezer in the latest build. Also, formatting a new SD card doesn’t correctly offer to populate the SD card with the system software. We’re working on fixing these, and will hold this release until they are fixed.
See the mega65-core changelog for more details and links to resolved issues.
ROM changes in v0.97 RCThese ROM changes include corresponding changes in the latest User’s Guide PDF, as appropriate. Feel free to report documentation issues, if you find any.
New features for everyday use:
MOUNT command.DIR U12, to list the contents of the SD card, now accepts a pattern matching string, similar to using DIR with DOS devices. All wildcard characters are supported. For example: DIR "M*",U12DIR command has a new P flag that pauses long directory listings at each screenful: DIR P, DIR P,U12TYPE command has a new P flag that pauses long TYPE output at each screenful, for displaying text files: TYPE "FILENAME",PNew features for BASIC programmers:
%1010 The new DECBIN("...") function converts a string of 0 and 1 to a number. STRBIN$(n) converts a number to a string of 0 and 1 characters.PRINT USING %-style patterns now support left zero padding for positive values in float and integer patterns: %07.2F %05DRWINDOW(3) returns the screen height in rows, regardless of window size, similar to how RWINDOW(2) returns the screen width in columns.JOY() now reports up to five game controller buttons, as defined by the 5-button protocol and as implemented by several modern game controllers and adapters. The C64GS controller reports two buttons; Commotron and several others report three buttons; mouSTer and Unijoysticle report five buttons. This protocol works with both R3 and R6 mainboards.JOY() to believe buttons 4 and 5 are pressed. The “next” version of Xemu adds the ability to disable mouse emulation: open the context menu, Input Devices, Disable mouse emulation. Xemu does not yet have a way to emulate joystick buttons 2-5.JOY(3) reports the combined status of joysticks in either port, allowing for a program to easily support a single joystick in either port without calling JOY() twice.CHAR now accepts a 28-bit charset address argument. This was previously limited to 20-bit addresses.This is mostly useful for CHAR ...,$FF7E000, which tells CHAR to use the custom charset defined by FONT and CHARDEF statements when drawing text to the graphics screen. Use $FF7E800 for lowercase.RPALETTE() now accepts a screen number of -1 to refer to the primary system palette (palette 0). It also accepts -2, -3, and -4 for the other three system palettes (palettes 1-3). Support for using the other system palettes from BASIC is planned for a future release.ESC R (PRINT CHR$(27)+"R"). Use ESC N to re-enable. This allows the PRINT command to write a character to the right-most column without pushing other lines downward. Note that you must still use ESC M (PRINT CHR$(27)+"M") to disable screen scrolling to PRINT a character in the bottom-right corner without scrolling the screen. (Use ESC L to re-enable scrolling.)sprenv400 (high-res vertical, per sprite) and sprh640 (high-res horizontal, system-wide). MOVSPR accepts 10-bit X and Y coordinates, and will correctly truncate coordinates in low-res modes. We will be adding a mode for matching the sprite resolution to the screen resolution in a future release. It will always be supported for a BASIC program to set these registers directly. We will also add support for a hires mouse pointer in a future release.BSAVE now has a “raw” mode, symmetric with the BLOAD “raw” mode, that omits the two-byte start address header normally used for PRGs. This is essential for saving SEQ files, which typically don’t have such a header.AND, OR, XOR, and NOT operate on 16-bit integer values, and were previously restricted to the range of possible signed 16-bit integers [-32,768, 32,767]. It is often convenient to operate on the range of unsigned 16-bit integers [0, 65,535], but these operators were rejecting operands above 32,767. With this update, these operators now accept operands in the range [-32768, 65535]. They are still treated like 16-bit integers and produce a result in the signed 16-bit integer range.New and improved KERNAL routines; see the “KERNAL Jump Table” appendix of the Compendium:
SAVEFL: a SAVE variant that can do a “raw” save, similar to the BSAVE BASIC commandGETIO: Read the current input and output devicesGETLFS : Read file, device, secondary addressKEYLOCKS: Read/set keyboard locksADDKEY: Add a character to the soft keyboard input bufferSETBNK: Reimplemented with 28-bit address support, using a backwards compatible API.VECTOR: KEYSCAN vector allows programs to intercept keyboard input.Important changes to existing features:
INT() function now always rounds toward negative infinity, also known as the floor function. This is a change to the behavior when the argument is negative and has a fractional part: INT(-1.5) = -2, INT(-1) = -1. All versions of Commodore BASIC, and many other BASICs derived from Microsoft BASIC, define INT() as “floor,” and other parts of BASIC depend on this definition internally. This was inadvertently changed starting at ROM 920177 to mean “round toward zero,” breaking dependent behaviors. Restoring this also fixes issues with trigonometric functions and other features.RENUMBER command was executed without a space between the command name and the first argument (e.g. RENUMBER100), the command would omit the space between keywords and line numbers in the resulting program (“crunch” mode). This feature remains, but now requires a C flag to prevent accidental activation: RENUMBER C 100 Note that “crunch” mode does not change any other whitespace in the program.VECTOR routine now copies 56 bytes of vector table data, not 32. Future changes to the vector table will use a different routine, to keep this API compatible with existing programs.Stuff that already existed but has now been documented in the latest User’s Guide PDF:
VOL command accepts two parameters, a volume for the right stereo channel SIDs, and a volume for the left stereo channel SIDs: VOL 9,9MOUSE command was intended by Fred Bowen to accept arguments to adjust the location of the “hot spot” on the mouse pointer sprite, as well as set the pointer’s initial screen position. These arguments were incorrectly documented and also were not functioning correctly. The features have been fixed, using the original intended argument order: MOUSE ON,port,sprite,hotspot-x,hotspot-y,location-x,location-ySIN(), COS(), and TAN() accept an argument in units of radians. The previously undocumented variants SIND(), COSD(), and TAND(), respectively, accept an argument in units of degrees.FONT accepts a built-in font ID as a parameter: A, B, or C. It also supports D, E, and F, which are the same as A, B, and C, respectively, set to lowercase mode. (Mega + Shift toggles between uppercase and lowercase, as usual, unless disabled by the appropriate escape code.)The v0.97 RC ROM also has improvements in these areas:
MONITORCHARVSYNCMOUNTPOT()CURSOR ON mode was leaving behind stray blinked-on cursors when the position changed.PRINT USINGJOY(1) no longer reports a spurious signal when top row keys are pressed.RMOUSEDEC()CONTFOR X& = ...MOVSPRDS / DS$ disk error codesBLOAD and BVERIFY and Attic RAMVAL()MOUSE ONSee the ROM changelog for more details.
Thanks to everyone who is available to help test this release. Even if you only have a moment to install it and run through a few games and demos, it’d be really appreciated.
Also, huge thanks to everyone who has contributed to the platform in the last year and a half! Every bug fixed makes the MEGA65 better for everyone, now and into the future.
Remember, you can support this Digest and my other work. Visit: ko-fi.com/dddaaannn
— Dan
Dan’s MEGA65 Digest for February 2025. A new BASIC code competition for the MEGA65 10th anniversary!
(Audio) Screenful of BASIC 2025 compo. An example entry for the Screenful of BASIC 2025 compo.On April 22, 2015, the Museum of Electronic Games & Art and Paul Gardner-Stephen announced the MEGA65 project to the world. Paul also announced the project on his development blog, where he had been documenting his work on a “C65GS” FPGA bitstream, on which the MEGA65 would be based. The MEGA65 has been a thing for ten years!
To help celebrate the ten-year anniversary of the MEGA65, ZeHa of Dr. Wuro Industries is throwing a short-form BASIC competition—and you’re invited! We want as many entries as possible, so power up your MEGA65 and crack open your User’s Guide. No experience necessary!
I’ll keep this Digest brief so you have extra time to start playing with code. We’ll go over how to enter the compo, check out some recent Featured Files, and take a brief look at a fun and obscure feature of the VIC-IV video chip: high resolution text fonts.
MEGA65 NewsThe Screenful of BASIC 2025 compoIt’s time for a good ol’ fashioned short-form BASIC competition!
The twist: Your program must be written in BASIC 65, and the program listing must fit entirely in one screen of 80 x 25 text, including space for the READY. prompt at the bottom of the screen. Someone should be able to type your program in from a single screenshot, and type LIST to see the entire program on screen. No data files allowed, one screen of code is all you get!
The deadline: Entries must be submitted by 11:59 pm GMT, April 22, 2025.
How to submit: Go to the itch.io page for the compo, create an itch.io account, and follow the instructions. Upload a D81 disk image containing a single PRG file of your program.
Pick your favorites: Public voting will open from April 23 to April 30. All entries will be available for download from itch.io. Results will be announced on May 1st.
Your program can do anything you’d like, and use any feature of the MEGA65. Just don’t destroy someone’s files or commit any crimes. Submissions should be family friendly in content. Multiple submissions are allowed, if you’re feeling productive.
Most MEGA65 users are running the latest stable release package v0.96, including ROM 920395. If your program requires a more recent ROM beta version, be sure to say so in the description of your entry. (We’re close to releasing v0.97 with all of the changes, but it won’t go out before the compo deadline.)
By submitting an entry to the competition, you assert that you have copyrights to the code, and you are assigning the Museum of Electronic Games & Art (MEGA) a non-exclusive right to distribute your program globally. Your entry will be hosted on itch.io, and may also be included in a compilation disk distributed on Filehost or bundled with the MEAG65 on the SD card as part of the “Intro Disk” anthology.
For complete rules and submission instructions, visit the Screenful of BASIC 2025 compo home page on itch.io. Many thanks to ZeHa for hosting this event!
BASIC compo tips For BASIC To One-Liners, by Holger Weßling.
Clever coders have devised many ways to cram a lot of BASIC into a little space, just as a personal challenge or for a short-form coding competition like Screenful. Figuring out what works and what doesn’t is part of the fun. If you’d like a bit of inspiration, there’s a book I can recommend. For Basic to One-Liners by Holger Weßling is packed with time-tested tips and tricks that apply to many dialects of microcomputer BASIC, with a focus on the Commodore 64 and short-form BASIC competitions. ZeHa himself is mentioned in the book!
We’ve covered BASIC programming topics in past issues of this Digest. In particular, check out October 2023: Robot Finds Kitten, part 1 and July 2024: Let’s Paint for some ideas.
For the purposes of short-form compos, the biggest difference between BASIC 65 and other Commodore BASICs is the breadth of graphics, sound, and music commands available. The language is mostly the same. I’d watch out for an unusual fact about BEGIN-BEND blocks from BASIC 7, where everything after the BEND on the same line is considered part of the block. If you discover any more language oddities, please mention them in the Discord or on Forum64.de.
Featured Files “Hunt the Wumpus on the Mega65!” from the YouTube channel Coding with Culp.
Hunt the Wumpus by Dave Culp revives the classic computer game in BASIC 65. Traverse the dungeon, watch for clues, and take the Wumpus by surprise—before it surprises you. Don’t miss Dave’s video about his game on his YouTube channel, Coding with Culp.
It seems like muse has a new arcade core for us every month! I won’t hold him to that schedule, but I will very much enjoy Bank Panic (1984) from Sega. This western-themed reflex action game features colorful cartoon graphics and a unique user interface. This bank has so many doors! R6 owners will prefer the VGA video output. We’re waiting on a video fix for R6 boards in the MiSTer2MEGA framework that affects HDMI output of some arcade cores. I needed to use the horizontal position adjustment feature in the menu system, but then it looked great over VGA on my screen. As usual, see the Github repo for installation instructions.
WarGames simulator, originally by Andy Glenn, ported to BASIC 65 by Mr. Jones. Recreate scenes from the classic movie WarGames (1983) and avert Global Thermonuclear War. The game features a narrative hint mode if you don’t remember the movie.
BasicTracker by ZeHa is a simple, intuitive music composition program with a tracker-like interface. Compose for six simultaneous voices, each with six possible instruments. Enter two octaves worth of notes using letters and numbers, move the cursor with the cursor keys, and follow on-screen instructions for the rest. There’s no way to save or export song data, this is just for fun.
Tall Character Mode The font Hack converted to a MEGA65 high resolution font, by M3wP.When you first turn on the computer, it starts up with a text screen 80 character wide and 25 characters tall, where each character is 8 pixels wide and 8 pixels tall, for a resolution of 640 x 200. There are two other text mode resolutions you can access directly from this screen: press Esc then 4 to enter 40 x 25 text mode, Esc then 5 to enter 80 x 50 text mode, and Esc then 8 to return to 80 x 25 text mode. All of these modes use 8 x 8 characters.
The MEGA65 has another way to draw text—and it’s practically a secret. Tall Character mode (TCR mode) doubles the vertical pixel resolution without changing the dimensions of the screen. Each character becomes 8 pixels wide and 16 pixels tall, making the text sharper and easier to read. TCR mode needs a 8x16 font to do this, and the MEGA65 does not have any TCR fonts built in. But there’s an easy way to load such fonts yourself.
Xemu users, note that TCR support was very recently added to the next branch (as of version 20250216001821), so download the latest if you want to try this in the emulator.
Loading TCR font files with the FreezerM3wP has been experimenting with this feature recently, and produced TCR conversions of several modern fixed width fonts. Download M3wP’s conversions of the fonts Courier, Xirod, Hack, and Noto.
These fonts are distributed in the .TCR file format, a binary format that matches how this data appears in the MEGA65’s memory. To load a .TCR file into the MEGA65:
.TCR file to your SD card.L key. Select the .TCR file.F3 to resume, and enjoy!These are lowercase ASCII character sets only, and naturally are missing PETSCII graphics symbols. They contain ASCII symbols such as underscore (_) and tilde (~).
The Oldschool PC Font Resource An IBM PC font on the MEGA65.Inspired by M3wP’s example, I spent an afternoon playing with The Oldschool PC Font Resource, a large collection of high fidelity reproductions of system fonts for IBM PCs, PC compatibles, and various BIOSes and devices. I wrote a script to convert the 8x16 bitmap fonts to TCR files, and put together a font browser in BASIC:
Mount this disk image, then RUN "*" to play with the font browser.
A PC font normally has 256 unique characters in it, with the usual cast of ASCII characters in the first half and some familiar PC graphics characters and symbols in the second half. I attempted to relocate the ASCII-style characters to the corresponding screen codes used for PETSCII, such that ASCII text could be displayed without changes. I put some of the PC symbol characters (like the smiley faces) at useful but arbitrary locations.
There are two TCR files for each font: a 128-character version and a 256-character (“all characters”) version. The 128-character version uses the first 128 characters of the PC font, then repeats them in “reverse” video for the second 128 characters, such that the screen terminal cursor blinks correctly. When the screen terminal’s cursor is over a letter, it blinks between the letter and its reverse, so both need to be present in the character set.
A PC font organized into a 128-character screen code layout, with reverses.The 256-character version of the TCR font uses all 256 characters of the PC font, with no reverses. This makes more symbols available, at the expense of not working correctly with the screen terminal. If you’re writing your own program, you could implement a new cursor using the reverse character attribute instead of dedicated reverse characters. (We tried upgrading the screen terminal to do this, but ran into issues with how the VIC-III defines character attributes. That’s a story for another day.)
A PC font organized into a 256-character screen code layout, without reverses.See the mega65-pcfonts Github repo for documentation, loose TCR files, and the scripts I used to put it all together. Or just enjoy the demo disk.
Using a TCR font from a programTo use a TCR font in your own program (or AUTOBOOT.C65 startup sequence), the TCR file must be on a D81 disk image. You can use the usual PC tools to make such an image, or you can do it on the MEGA65 with the BSAVE command, like so:
READY prompt, BSAVE the character generator buffer: BSAVE "MYFONT.TCR",P($FF7E000) TO P($FF7EFFF)The PCFONTS.D81 disk has the TCR files on the disk, so you can copy those with the COPY command. Note that the PC fonts are released under a Creative Commons license with attribution and “share-alike” provisions. See the licensing terms in the README.md file.
In your program, load the font file into memory, and activate the TCR mode:
In BASIC:
10 BLOAD "MYFONT.TCR",P($FF7E000)20 CLRBIT $D054,530 SETBIT $D07A,4
To return to 8x8 PETSCII:
FONT command to do this.980 CLRBIT $D07A,4990 FONT C1000 END
How TCR fonts workIn the VIC-IV TCR mode, character set data is read directly from the character generator memory at $FF7E000-$FF7EFFF (4 KB). The screen terminal does not switch between “uppercase + graphics” and “lowercase + uppercase” text modes. There is only one text mode. Whereas a PETSCII character set has a set of 256 characters for each text mode, TCR mode uses the same amount of memory for a single mode, with twice as many pixels per character.
TCR character set data is interpreted as a single set of 256 characters of 8 x 16 resolution, using interleaving lines from charset positions n and n+256. For example, screen code 1 is normally a lowercase “a” character. Its image is made of alternating lines of the data at positions 1 and 257 in the character set, like so:
Byte Charset address---------------------------........ FF7E008........ FF7E808........ FF7E009........ FF7E809........ FF7E00A..xxxx.. FF7E80A.....xx. FF7E00B.....xx. FF7E80B..xxxxx. FF7E00C.xx..xx. FF7E80C.xx..xx. FF7E00D..xxxxxx FF7E80D........ FF7E00E........ FF7E80E........ FF7E00F........ FF7E80F
In memory, the lines look like this:
........ FF7E008........ FF7E009........ FF7E00A.....xx. FF7E00B..xxxxx. FF7E00C.xx..xx. FF7E00D........ FF7E00E........ FF7E00F........ FF7E808........ FF7E809..xxxx.. FF7E80A.....xx. FF7E80B.xx..xx. FF7E80C..xxxxxx FF7E80D........ FF7E80E........ FF7E80F
There’s a fun way to get an intuition for how this works. The original 8x8 PETSCII font keeps uppercase letters in the first half and lowercase letters in the second half. Without loading a TCR font, enable TCR mode by setting the CHARY16 register: SETBIT $D07A,4 The VIC-IV interleaves the uppercase and lowercase PETSCII characters at double the vertical resolution.
The 8x8 PETSCII character set with 8x16 TCR mode enabled, illustrating how TCR character data is interleaved.
Character graphics are a big subject on the MEGA65, and I’m planning a series of Digest feature articles for later this year to cover as much of it as I can.
If you like this kind of thing, please consider supporting the Digest. Your patronage makes this Digest possible—and makes it free for everyone. Visit: https://ko-fi.com/dddaaannn
Now get cracking on that screenful!
— Dan
Greetings MEGA65 fans! I am updating the hosting situation of this newsletter. Hopefully this will be automatic for nearly everyone, and you don’t need to do anything. Here’s what you need to know.
As I mentioned in last month’s Digest, I am updating the hosting situation of this newsletter. Based on many recommendations from friends and my experiences with other newsletters, I have chosen Buttondown for email subscriptions. They’re featureful, reasonably priced, and best of all, you don’t need to create an account to be an email subscriber.
In most cases, you don’t have to do anything in response to this change. Here are the details.
Email:
Podcast:
RSS feed readers and the Substack app:
Substack paid subscriptions:
All issues of the Digest are on my website: dansanderson.com/mega65/ For now, I will leave copies of past issues on Substack’s website to preserve links. New issues will be posted exclusively to my website, and distributed by email and these feeds.
Thank you for your patience as I work out the kinks in the new tools! Please let me know if you have any questions, concerns, or feedback.
— Dan
Disassembling Crossroads, part 2. Dan’s MEGA65 Digest for January 2025.
(Audio) Disassembling Crossroads, part 2. The creatures of Crossroads, illustration from Compute!'s Gazette, December 1987.In last November’s Digest, we started a project to dissect the classic C64 title Crossroads, published by Compute!’s Gazette magazine in December 1987. We got a handle on how the game works by playing it, and by inspecting the running game state in Retro Debugger, a C64 emulator with memory visualization features. In this issue, we’ll get up close and personal with the Crossroads machine code, using a tool called a disassembler. Combined with what we’ve learned, and some world knowledge of Commodore programs, we can begin to produce human-readable assembly language source code for the game.
Crossroads was so popular that Compute! commissioned a sequel, Crossroads II: Pandemonium, which was published in December 1988. I did some searching to see if anyone has disassembled the Crossroads games already, and discovered an excellent disassembly of Crossroads II by Github user hyphz. For the sake of the exercise, I did not look closely at this disassembly at first, but I do expect the original Crossroads to be similar in many ways.
We won’t do a full disassembly of Crossroads in a single newsletter, but we’ll answer some of the most important questions. Before we do that, let’s see what everyone else is up to!
MEGA65 NewsPET core pre-release The Commodore PET core, running on the MEGA65.
Olaf “Rhialto” Seibert is working on a Commodore PET core for the MEGA65, and has invited everyone to help test a pre-release version. The Filehost download includes core builds for R3 and R6 boards, and a copy of the necessary ROM files that you install on the SD card in a folder named pet/.
Do keep in mind that this is a pre-release and may be missing features. Check out the Commodore PET core on Github to see source files and file bugs. Many thanks to Rhialto for this exciting project!
Gaplus arcade core Gaplus arcade game, by Namco
muse continues to deliver arcade core goodness! This month, it’s Gaplus, the third game in the Galaga series from Namco. As usual, you will need to download the Gaplus core, locate MAME ROMs on the Internet, then follow the set-up instructions to produce the files for the SD card. The Gaplus core is available for both R3 and R6 mainboards.
I encountered an issue with the R6 version when using HDMI video where after a few seconds the game video becomes a static screen of vertical bars. This does not occur over VGA, or with the version for the R3 mainboard. A fix is in progress.
Goodwell’s QR code generator The 8 Bit Theory on developing QR code generators for Commodore computers.
Goodwell, aka The 8 Bit Theory on YouTube, recently attempted an experiment to write a QR code generator for multiple vintage Commodore computers in multiple languages, including BASIC 2, BASIC 7, C, 6502 assembly language—and BASIC 65 for the MEGA65. The video about the experience discusses implementation techniques and performance characteristics of these implementations. Definitely check it out, it’s worth watching the whole thing.
Goodwell uploaded the MEGA65 version to Filehost, and is continuing to refine it. It might even get added to the MEGA65 Intro Disk menu system, to make it easier to share URLs in the menu text. The Github repo for the QR code generator has all of the versions of the experiment, as source code.
Don’t miss Goodwell’s follow-up video about the MEGA65’s technical design, based on his early experiences developing for the computer.
MegaMage by SirGeldi MEGAMage, by SirGeldi.
Hot off the presses! SirGeldi on Forum64.de has posted MEGAMage, a Roguelike adventure game written entirely in BASIC. Adorable character graphics and fast map scrolling enhance the gameplay. Even better, SirGeldi built the tools used to develop the graphics, map, and object and monster metadata for the game on the MEGA65 in BASIC, and has included these tools on the disk!
Download the .zip file from SirGeldi’s message post, unpack it, and transfer the D81 disk image to your MEGA65. To start the game, mount the disk image, then:
RUN "MEGAMAGE"
Even more Featured Files! Soccer, by SirLazarus
Soccer, by SirLazarus, is a new soccer-themed paddle game. It is written in BASIC, and requires the latest ROM beta version 920409 because it uses recently added features. (This ROM update will be included in the next stable release package.) Grab your paddle controllers and check it out!
Jotto 2020, by ToneDeF, is a word puzzle game similar to Mastermind. It plays something like Wordle, though its invention pre-dates Wordle by a year. ToneDeF originally wrote this for other micros, and made this new version for the MEGA65 in BASIC.
Pac-man in BASIC demo, by xlar54. This playable demo sports many features of the arcade classic, including music, sound, and polished graphics. xlar insists this is just a demo, but only a few features are missing (most notably power pellets). And yes, this too is implemented entirely in BASIC 65.
Opal Dance, by lochmana, is a colorful puzzle and maze game. Match colored pieces by walking into them, but think before you move! Each match of two pieces replaces the first piece with a wall, and removes the second piece. Clear as much of the level as you can.
Mega65 Perpetual Calendar, by gcastel (MiddleAgedCoder on Discord). Generate calendars for any month of any year. Developed for BASIC 65 with Eleven, you can get the source code from Github.
Xemu keyboard layout reference for Windows, by Discord user Digger. Windows users, keep this handy app open alongside the Xemu emulator to remember how to replicate MEGA65 keystrokes on the PC keyboard. See also the project homepage (in German), and the README file (in English) included with the download.
mouSTer back in stockmouSTer, the USB-to-Commodore DB9 adapter for modern USB mice and game controllers, is back in stock after a long hiatus. This is one of the best ways to use a modern USB mouse with the MEGA65. It even works with wireless mice that use USB dongles. Get one while you can!
See RetroHax’s update from December 30, 2024 for details.
Digest administrative noteI am considering migrating the Digest to a new email newsletter service. As Substack evolves their business model and practices, I want to make sure the Digest is hosted on services that remain open and compatible with the values of the MEGA65 project. I’ll let everyone know in advance before I make changes.
The change should be fully automatic, including migrating donations made through the Substack interface as paid subscriptions. Hopefully I can migrate the read-aloud podcast feed URL automatically as well, but this will depend on how your podcast app is subscribed. I’ll try to figure it out and let you know. If anyone has any feedback on this change, please let me know. I know a couple of you are using the Substack app to read it, which should be the only case affected by a migration.
You can always find all issues of the Digest on my website and subscribe to it using an RSS feed reader. You can also become a patron of the Digest at ko-fi.com/dddaaannn.
The art of disassemblyA quick recap: A computer’s CPU performs instructions encoded as bytes in the computer’s memory. This machine code is difficult to work with as a list of numbers, so when we humans want to write a program, we use a programming language, and a tool that converts that language to machine code for the computer to execute. Assembly language is a human language that most closely resembles the machine code, with human-readable names for each machine code instruction. The corresponding conversion tool is called an assembler.
There’s a simple assembler built into the MEGA65’s machine language monitor. You can start the monitor by entering the MONITOR command at the READY prompt. Try doing this now, and use the A command to assemble this short program:
MONITORA1600 LDA #$01STA $D020LDA #$00STA $D021RTS
After the last line, press Return on a line by itself to stop the assembler.
As you type each line, the monitor converts the instruction to the corresponding machine code. It stores the machine code at the requested address in memory, and also prints the address and machine code bytes to the screen, as hexadecimal values.
A 1600 A9 01 LDA #$01A 1602 8D 20 D0 STA $D020A 1605 A9 00 LDA #$00A 1607 8D 21 D0 STA $D021A 160A 60 RTS
The monitor can also reverse this process, to show you which instructions are represented by bytes in memory. This process is known as disassembly. Use the D command followed by the address:
D1600
Sure enough, the program you just entered is right where you put it, at address $1600. There’s also a bunch of other stuff after the last instruction you typed starting at address $160B, maybe garbage data or something left behind by another program. These bytes may or may not represent machine code instructions, but the disassembler diligently tries to interpret them as instructions either way. These instructions are what the CPU would perform if you told it to treat that memory as code.
The number of bytes used to encode an instruction differs depending on the instruction. Some instructions, like RTS, are encoded as a single byte. Others, like LDA #$01, use two bytes. Still others such as STA $D020 require three bytes. Anything trying to interpret bytes as machine code—whether that’s a disassembler or the CPU itself—needs to know the address of the first byte of the first instruction, or it could get very confused.
Try using the disassemble (D) command again, this time with a starting address of $1601:
D1601
The monitor tries to disassemble some of the memory of the program you entered, but the result looks nothing like the program. $1601 is the address of the second byte of the LDA instruction, $01, which to the disassembler (and the CPU) looks like a version of the ORA instruction that uses two bytes. At address $1603, the next byte $20 looks like a three-byte JSR instruction. Coincidentally, the next incorrect instruction gets the disassembler back on track, aligned with the correct first instruction byte at address $1607. But by now, a CPU trying to execute this code will get very lost, and probably crash.
Disassemblers are not magic, and they need help from the operator—that’s you—to convert machine code back into human-readable assembly language. The disassembly process requires research, maybe some guessing and checking, taking notes, and labeling addresses and values once you’ve figured out what they mean. A modern disassembly tool can make this process surprisingly easy.
Introducing Ghidra Ghidra logo.Ghidra is a powerful disassembler and reverse engineering multi-tool. It supports many types of CPU machine code, including the 6502, and supports extensions to add more. Ghidra was originally developed by the United States National Security Agency (NSA), and has been released as open source and free of cost.
I want to credit two YouTube videos on the topic of using Ghidra for reverse engineering Commodore 64 programs: Introduction to Ghidra by David Youd, and How to start a new C64 project in Ghidra by Jarkko Lehti. These are worth watching when you get a chance, and we’ll be using Lehti’s configuration files in a moment.
Setting up GhidraAs of this writing, the latest version of Ghidra is v11.2.1. It’s a Java-based application, and it requires a recent version of the Java runtime (21 or later), which you may need to install or upgrade. Ghidra will tell you if your Java is too old. Properly installed, Windows users can double-click on ghidraRun.bat to start the program, and macOS and Linux users invoke the ghidraRun shell script.
As usual, macOS will refuse to run the scripts and tools until you have blessed them in the Privacy & Security panel. You probably know the drill by now. Attempt to start ghidraRun, dismiss the complaint, open macOS Settings: Privacy & Security, scroll down, click Open Anyway. If you use Ghidra for other stuff later, you’ll need to similarly bless some internal components, which you can do in the Terminal app on the command line:
xattr -cr ./Ghidra/Features/FileFormats/os/mac_arm_64/lzfsexattr -cr ./Ghidra/Features/Decompiler/os/mac_arm_64/decompilexattr -cr ./Ghidra/Features/Decompiler/os/mac_arm_64/sleighxattr -cr ./GPL/DemanglerGnu/os/mac_arm_64/demangler_gnu_v2_*
When you run Ghidra for the first time, it will ask for the path to the Java home directory. This will depend on your operating system. On macOS using Java 23 from Oracle, the path is:
/Library/Java/JavaVirtualMachines/jdk-23.jdk/Contents/Home
Ghidra opens its main window, from which you create or select a project, and open tools.
Creating the project Ghidra's project window, with no active project.Let’s start a new project using the crossroads.prg file that we extracted from the disk image in last month’s Digest.
Create a new project:
Next, import the crossroads.prg file:
crossroads.prg, then Select File To Import.... button next to Language. Select “6502,” which is at the top of the list for this version of Ghidra. Click OK.Leave everything else at default settings, then click OK. Another window opens with some information about the file. Dismiss this with OK.
The Ghidra file import dialog. The Ghidra language select dialog.Finally, double-click on the crossroads.prg file in the project to open it in the CodeBrowser tool. The first time you do this, Ghidra will offer to analyze the file. Say Yes. Leave the default set of analyzers selected, then click Analyze. The analysis doesn’t produce spectacular results just yet, but it’s usually worth doing.
Here is crossroads.prg in all its glory, in the CodeBrowser window. These are the same bytes we saw in the hex dump, but now they’re listed one per line. We will use this interface to massage the data as we learn what each part of it means.
Commenting the codeLet’s start by telling Ghidra the C64 memory address where this data gets loaded. Because this is a PRG file, it begins with two bytes that are not loaded into memory. We could have gone through an extra step to remove these bytes before importing the data, but for our purposes, it’s enough to just take these into account when telling Ghidra where everything lives.
This data gets loaded into a single block of memory that starts at address $0801. Minus the two PRG header bytes, we can tell Ghidra to start counting addresses at $07FF:
0000.07ff. Click OK.Close the Memory Map window.
The Memory Map toolbar, with the blue cross button: Move a block to another addressNotice that the data is now lined up with the memory addresses. The first byte of the BASIC bootstrap is at $0801, and the two-byte end-of-BASIC terminator is at $080B. (We peeked at this in the previous Digest.)
Let’s add a comment describing this data. Right-click on the line for $0801 to open the context menu. Select Comments > Set EOL Comment… In the window that opens, type a useful description, such as: “Start of BASIC bootstrap.” You may wish to add a similar comment on $080C, for “End of BASIC bootstrap.”
The BASIC bootstrap of Crossroads, aligned and commented.Initiating disassemblyThe BASIC bootstrap uses the `SYS` command to invoke machine code starting at decimal address 2300. Ghidra even shows the “2300” characters next to the byte listing, guessing that we might be interested in what these look like as ASCII characters. In hexadecimal, this address is $08FC.
Navigate to this address in the byte listing. Here’s a quick way: press “G” (for “go”), then enter the address in hexadecimal (08fc).
Start the disassembly at this address: press “D” (for “disassemble”).
Our first disassembly, starting at $08fc.Ghidra has just done a bunch of work for us! It took our word for it that the bytes starting at $08fc are machine code: $a9 $ff represent the `LDA #$ff` instruction, which Ghidra spells `LDA #0xff`, and updated the code listing accordingly. It proceeded to the next instruction, and kept going as long as it was reasonably certain that the CPU would treat those bytes as instructions as it executed.
Scroll down, and notice that Ghidra stopped disassembly at address $0cab, where it found an RTS instruction. The bytes starting at $0cac may be more CPU instructions, but Ghidra doesn’t know this for sure. In fact, we can see evidence that it may be data and not instructions: the bytes from $0cb1 to $0cba spell out “CROSSROADS” in ASCII. Ghidra doesn’t know this is more likely to be PETSCII and not ASCII, but it displays the ASCII letters for our benefit, just in case.
Keep scrolling, down to $0dc5. Ghidra found more code! Starting with the first address we gave it, Ghidra decoded each instruction, and when it found a branching instruction or subroutine call, it continued disassembly at that location, filling everything in. It assigned some temporary labels for things that look like functions, such as FUN_0dc5. As we try to understand what the program does, we can replace these labels with more intuitive names. In the end, we’ll have a fully human-readable assembly language program. (Or as human readable as assembly language gets, anyway.)
Ghidra found what might be a function definition at $0dc5.Ghidra made similar attempts to highlight data locations referenced in the code, assigning labels such as `DAT_08b0`. Everything referenced by something else is annotated with the cross references. For example, the byte at address $08b0 is read by an instruction at address $0a7a, so it lists `0a7a(R)` (“R” for “read”) next to $08b0.
We can add our own labels. Let’s do this for our entry point, $08fc:
08fc.start. Because this is the entry point of the program, select the “Entry Point” property. Click OK.Importing C64 symbolsGhidra has invented labels for all of the data and code referenced by the disassembled code. The addresses within the imported data range have blue labels and cross references.
Where the code references an address not in the imported range, Ghidra also generated a label, and colored it red. Consider the first few instructions of the program, starting at $08fc:
LDA #0xffSTA DAT_d40eSTA DAT_d40f
This loads $ff into the accumulator, and stores that value to addresses $d40e and $d40f. If these were just memory locations, we would assume that these are variables used by the game for storing game state, like the player’s position. In this case, we can apply some knowledge of how the Commodore 64 works: addresses in the range $d000-$dfff are input-output registers that control, or are controlled by, the computer’s hardware. We can look up what these registers do in a Commodore 64 technical reference.
In this case, these registers belong to the SID sound chip, specifically the frequency of the oscillator on voice #3. We don’t yet know why the program does this, but we do know that’s what it does. The next two instructions and a bit more knowledge of C64 programming gives us a hint:
LDA #0x80STA DAT_d412
The technical reference says $d412 is the SID voice #3 control register. Setting bit 7 like this tells it to use the “noise” waveform. Crossroads is certainly a noisy game, but it’s probably setting this up to use as a random number generator, or at least a seed for one. We’ll have to keep our eye out for something that samples a value from voice #3.
Now that we’ve identified DAT_d40e et al., we could update the labels to something that describes their functions. To do this, you would right-click on the label and select Edit Label… (or move the cursor onto the label and press “L”).
Considering that the C64 I/O registers always do the same thing in every program, it would be nice if Ghidra could automatically recognize all of them and give them useful names. The following is one way to do this, based on the files supplied by Jarkko Lehti to accompany his YouTube video on the subject.
Download these C64 symbol files:
The .txt files tell Ghidra to apply certain labels to certain addresses when they appear in the code. The .bin files fill in the memory ranges themselves in the Ghidra project, so you can get some idea of what’s actually there in the C64’s memory.
In your project, the IO and KERNAL data will exist as overlays. An overlay is a portion of memory that may contain different things at different times, based on system state. With the Commodore 64, I/O registers and KERNAL ROM occupy addresses that can also refer to RAM, based on the memory banking register. Ghidra can only see that an instruction accesses an address, not the memory map of the computer at the time the instruction is called. Telling Ghidra that these memory regions are overlays accounts for the possibility that a given reference to an address may refer either to I/O and ROM, or the underlying RAM.
To create the IO overlay:
IO_d000.bin, click Add To Program.IO, and set the Base Address to d000. Click OK in the Options dialog, then click OK in the Add To Program dialog. Dismiss the Import Results window.To create the KERNAL overlay, repeat these steps with the kernal_e000.bin file, using a Block Name of KERNAL and a Base Address of e000.
Now we can import the symbols that describe addresses in these regions. Ghidra is a highly scriptable application environment, and we can use a script that comes with Ghidra to import the .txt symbols files.
To import the symbols:
ImportSymbolsScript.py. Double-click it to run it.IO.txt file. Click “Go baby go!”KERNAL.txt file, and again for the RAM.txt file.Ghidra now knows about the I/O registers via an overlay with sensibly named symbols. Ghidra still doesn’t know which references refer to RAM and which refer to I/O. We have to tell it.
Go (“G”) back to the STA instruction at $08fe. Its destination is still the red symbol DAT_d40e. Click on it, then press “R” to edit the reference. In the pop-up window, double-click the reference, then change the dropdown menu that says “RAM:” to “IO:”. Click Update, and close the reference editor. The symbol has changed to D40E_FRQ_3_LO. That’s… better.
Code near the entry point with I/O registers labeled.It’s unusual for a C64 game to un-bank the I/O registers, so it’s a little inconvenient that each label needs to be applied like this. I haven’t found a good way to automatically declare all references to be for the `IO` overlay. One possibility might be to add `IO_d000.bin` as a non-overlay region, then edit the `IO.txt` symbols file to remove the `IO::` part of each address. For now, I like keeping `IO` as an overlay, because it accurately describes how the C64 works.
I did find a reasonably fast way to update all of the labels manually using Ghidra’s search feature. In the Search menu, select Program Text… Prepare a search for DAT_d in All Fields of the Listing Display, and click Search All. A window opens with the search results, and clicking on a result causes the Listing display to jump to that location. I got 38 results, and it only took me a few minutes to update all of the references to use the IO labels. It’s not the most fun I’ve ever had with a computer, but it didn’t take so long that I regretted not automating it.
Getting some answersNow that we have at least some idea of what we’re looking at, let’s try to answer a few questions that will help us understand how Crossroads works. These are just questions that occurred to me, in the order I thought to ask them. They may not be the best questions, or in the best order. But let’s see what happens.
Does the program call the KERNAL jump table?This is an easy one. A call to the KERNAL jump table would look like a JSR to a hexadecimal address beginning with an f. Ghidra will have generated a label like FUN_ffd2 prior to me updating it to use the KERNAL overlay’s symbols, so just like we used a search of DAT_d to find I/O register references, we can search for FUN_f to find calls to KERNAL routines. I get no results from this search, so I’m fairly confident that Crossroads does not call KERNAL routines. Alas, we don’t get to use our KERNAL overlay. Maybe next time.
Does the program use KERNAL vectors?As we’ve discussed in previous issues of the Digest, most games drive their game loop using CPU interrupts. There are two ways to set up an interrupt handler: replace the KERNAL’s interrupt handlers in $fffa-$ffff, or use a KERNAL vector to add a custom subroutine to the KERNAL’s interrupt handler. Let’s check the latter option first, because it’s more common for simple C64 games.
The designers of the Commodore ROM wanted people to use the VECTOR KERNAL call to read and update the KERNAL vector table, and that’s still a best practice for MEGA65 programs. Even early on, C64 programmers skipped over that part of the manual, and just wrote to the C64 vector table at $0314-$0333. We can search for the string 0314 to find any direct references to the IRQ vector.
Yup, there it is. The instructions at $0ba8-$0bb1 are writing address $11c4 to the KERNAL IRQ vector $0314-$0315.
LDA #0xc4STA 0314_IRQ_VECTOR_LOLDA #0x11STA 0315_IRQ_VECTOR_HI
This appears to be the only vector touched, and it is only set in this one location in the code that Ghidra has disassembled. Let’s go to the address being written to the vector and check it out: “G”, then 11c4.
Hoo boy, Ghidra hasn’t disassembled this yet! This is because the main routine, which sets up the game and installs the IRQ vector, doesn’t actually call the IRQ handler directly. We have a bit more work to do. Press “D” to start disassembly from this address. Also, press “L” to add an entry point label, with a name such as irq.
We’ll have to re-do our searches for I/O register labels and KERNAL calls now that we have more disassembled code. That’s just how it goes. I found 8 more I/O registers, but still no KERNAL calls.
Does the program do any C64 CPU bank switching?I mentioned that most games leave I/O registers banked to $d000-$dfff, the initial state of the C64. We can double-check that by looking for references to the C64 bank switching register, address $0001.
A search for DAT_0001 does produce two results in the start-up routine, so it’s worth taking a closer look. Starting at address $09f9, the routine sets this to $73, or binary %01110011. According to C64 technical documentation, this setting makes sure the C64 character ROM—the graphics data for the PETSCII font—is visible at $d000-$dfff instead of the I/O registers. It’s not that way for long, though. Just below, at address $0a10, the banking register gets set to $77, or binary %01110111, which switches the I/O registers back in.
While the character ROM is visible, a short loop copies the first 472 bytes of PETSCII graphics data to address $2000.
LDX #0x0LAB_09ff:LDA DAT_d000,XSTA 0x2000,XLDA DAT_d0ec,XSTA 0x20ec,XINXCPX #0xecBNE LAB_09ff
Now that we know what this does, we can rename the LAB_09ff label to something like copy_char_loop, and add explanatory comments to the bank switching instructions. We can also relabel DAT_0001 while we’re at it.
Does the program use any self-modifying code?From our perspective as reverse engineers, one of the scariest things a program can do is modify its own code. The program is entirely within its right to do this—code is just data in memory—but it means that the disassembler may not be seeing the code in the form it is executed.
To find places where the program might be modifying itself, we have to look for attempts to store values in locations that Ghidra believes are inside subroutine code. I bet there’s a way to get Ghidra to make a list of such places, but I haven’t found one yet. I did notice, however, that Ghidra refers to such addresses by the name of the function, plus an offset, like this example near the entry point:
LDA #0x0STA FUN_0b4a+1LDA #0x28STA FUN_0b4a+2
These address expressions appear in a grey color. From visual inspection, I found only a few examples of this. Thankfully, they were all short subroutines that were easy to understand, and all of the self-modifications were to addresses for LDA and STA instructions.
Several of these appear to be simple utility functions for using addresses from variables together with indirect addressing. For example, FUN_0b4a is simply the STA instruction in absolute Y-indexed addressing mode, followed by an RTS. To use this routine, the program writes an address into the STA instruction, sets the A and Y CPU registers, then calls the subroutine:
FUN_0b4a STA 0x0000,Y RTS
This probably saved the developer some headache, using a single self-modifying mechanism instead of having every subroutine modify its own instructions. The initialization routine near the entry point uses this mechanism as part of a loop to fill some memory with zeroes.
Is there any more code?We found the entry point and the IRQ vector routine, and we identified self-modifying code and confirmed that it’s not doing something crazy like dynamically writing JMP instructions to memory at run time. It looks like Ghidra has found most of the code in the program. Are we safe to conclude that the remaining unidentified regions are static data, or perhaps initialized variable space?
This requires a bit of judgement, a bit of guesswork, and a willingness to change our minds later, but we can start to form some conclusions from a visual inspection of Ghidra’s analysis. Here’s what I see so far:
For now at least, I feel confident that these regions are static data and not code, and that Ghidra has found all of the code in the program.
What’s in the data?We can learn more about the suspected data regions by double-clicking on the cross-references and digging around the code that refers to them. We can also make some guesses about how the data is encoded, and try to decode it to see if we recognize any patterns, similar to how we identified text strings with Ghidra’s built-in ASCII decoding.
I’m especially interested to find the character graphics data that gives Crossroads its special flair. This is likely to take the form of monochrome character pixel data, where each byte represents a row of 8 pixels, and consecutive bytes are stacked, usually in a square of 8 rows. If we visualized the bytes in suspicious data regions this way, would the Crossroads cast of characters jump out at us?
Ghidra has extensive features for visualizing data structures. Once we have a guess as to how data should be interpreted, we can paint data types onto the bytes and see if they make sense. For example, to visualize a byte as a binary number:
??, its hex value, and its ASCII value.byte type. Ghidra updates ?? to db.Right-click again, navigate to Data, and now select the Settings… option that has appeared now that the value has a type. Change Format to “binary” and Padding to “padded.” Click OK. Ghidra updates the visualization to a padded binary value: 01000100b.
The Data Settings dialog, as it appears on macOS.Let’s try this for a chunk of the data region $080d-$08f9. (We can always undo it later.) Click-drag with the mouse, or move the cursor to the first line and hold Shift while cursoring down, to select lines. Use the Data context menu to make them all bytes displayed as padded binary.
083d ff db 11111111b ; ########083e 8f db 10001111b ; # ####083f ff db 11111111b ; ########0840 3c db 00111100b ; ####0841 7e db 01111110b ; ######0842 7e db 01111110b ; ######0843 ee db 11101110b ; ### ###0844 87 db 10000111b ; # ###
It’s a little difficult to make out when displayed as 0’s and 1’s, but I see some familiar faces. Going eight bytes at a time, I see two frames of animation for each of an enemy missile ($080d), a bullet ($081d), the Human/player ($082d), the Purple Rubberhead ($083d), and more. I have to squint to see the rest, but if each of the nine enemies gets 16 bytes starting at $083d, it feels safe to say these are graphics tiles up to at least $08cd.
How does the program set up character graphics data?We have a rough idea that the game keeps its graphics assets starting at $080d, and establishes its custom character font at $2000 with some combination of this data and PETSCII characters from character ROM. We’ve already found where the set-up routine ($08fc) copies the character ROM to $2000. What else does it do?
It’s relatively easy to see that a big chunk of the set-up routine is messing around in the $2000 memory region. Starting from the beginning, it sets a few registers, then immediately dives into the work of rendering the character data, with various loops and STA instructions indexing into this memory.
The characters of Crossroads walk in four directions, with two frames of animation. We only found two frames of animation in the graphics data, with the characters facing to the right. Can you guess where it gets the graphics for the other three directions?
The Purple Rubberhead character set, rendered from two 8x8 frames.To achieve the full effect, the set-up loop takes the 8x8 right-facing creature graphics and writes it into character graphics memory. Then it flips each byte horizontally and writes the left-facing copy. Then it rotates it 90 degrees one way for up-facing, and 90 degrees the other way for down-facing.
Moreover, the second step of each character’s animation is drawn across two character tiles, for that slick grid-breaking animation. The creature takes up two characters on the 40x25 screen for this half step, and no other creature or item is allowed in either tile at this moment. The set-up routine renders these half-steps dynamically from the 8x8 pixel data, using bit shifting instructions to produce half-tile images. I see groups of four bit shifting instructions at $0a5d and $0a6d that might be involved in this process.
Just after setting up the character data at $2000, the program sets VIC register $d018 to $18. This establishes how the VIC uses memory, and indeed, it sets the character memory pointer to $2000. It also sets the screen memory location to $0400. The VIC-II can only see 16 KB of the address space at a time, and which 16 KB is selected by a register at $dd00. Crossroads does not appear to change this register from its default setting.
Future possibilities with GhidraThat’s a good start, and plenty for a single newsletter. Ghidra is a very powerful tool and I’m still learning about all it can do.
Ghidra is highly extensible, and there is substantial potential for extensions and scripts to support C64 and MEGA65 development and reverse engineering. In his video, Jarkko Lehti is using his own Ghidra fork with a few nice features such as PETSCII string visualization.
When I was squinting at the character set data, I was wishing for a bit of tool support for visualizing bitfields as graphics, much like how Retro Debugger lets you view memory contents in different ways. I wonder if Ghidra can do custom inline data visualizers like this, or if a feature like this is already available.
The annotated disassembly can be exported in multiple formats, but none of them resemble something that could be re-assembled by a tool such as Acme assembler. A custom export tool or post-processing script would be handy for that, and would be easy to write.
Of course, the thought naturally occurs to someday use Ghidra to disassemble MEGA65 machine code programs. It’d take some effort, but it is possible to write an extension to provide 45GS02 CPU support to the disassembler. Ghidra has an entire language for specifying new processors called SLEIGH.
It’d also be good to generate symbols files and overlays for all of the MEGA65’s I/O register personalities. I already have symbol generator scripts that could be tweaked to produce something that Ghidra can import.
In practice, most MEGA65 machine code programs that exist today have friendly authors that have open sourced their code. It’s still fun to think about.
I chose Crossroads for this demonstration for a few reasons. For one, it really was one of my favorite games, so I was excited to play with it. But also, I had a suspicion that its internals were straightforward enough to understand through disassembly. As fast and seamless as Crossroads is to play, you only need to know a few things about C64 programming to start to make guesses about how it works just from looking at it.
Many C64 games and demos use data compression techniques to limit disk space and loading times. This can make disassembly more difficult, because you can’t just load the PRG into Ghidra directly. One possible technique is to run the program in Retro Debugger, let it decompress everything into memory, then take a snapshot of the memory of the running program. You can then apply disassembly techniques to the snapshot. I suspected that Crossroads did not use compression for code, and felt comfortable just loading the PRG file directly into Ghidra.
If you’re interested in writing programs for the MEGA65, it’s worth studying C64 programs. The Commodore 65 was designed as a true technological successor to the C64, and many C64 programming techniques apply to C65 and MEGA65 programs. The original source code for many C64 programs have been lost to time, but we can use tools like Retro Debugger and Ghidra to pull them apart and reconstruct their secrets.
There’s one more tool that we can use to reverse engineer C64 programs—and it’s sitting right beside you. The MEGA65 can run many C64 programs in its GO64 mode, and you can use the MEGA65 core’s serial debugger, along with a powerful frontend like m65dbg, to analyze a running program. We’ll have to revisit this in a future Digest.
Onward into the new year!
— Dan
Santa’s Souped-Up MEGA65. Dan’s MEGA65 Digest for December 2024.
(Audio) Santa's Souped-Up MEGA65. Santa's Xmas Caper (1990) for the Commodore 64, by Zeppelin Games.Hey everyone! I hope you’re all ready for some end-of-the-year relaxation, maybe some time off work, spent with family, in whatever temperature extreme you’re experiencing in your hemisphere. I also hope you’ll get some time to play with your MEGA65, because you’re gonna need it!
There are so many project announcements this month that I’ve decided to dedicate this entire issue to new downloads, to celebrate everyone’s talent and enthusiasm, and to give thanks for our wonderful community. We will finish our Crossroads disassembly project in January.
MEGA65 NewsSidPlay65 SidPlay65 by LightTangent.
There’s a new way to make music with your MEGA65, thanks to MEGA65 developer LightTangent! SidPlay65 is a MEGA65 program that re-implements the SIDPlayer music system from Compute! Publishing. It plays single-SID and dual-SID songs, and supports some advanced SIDPlayer format features like colorful PETSCII banner displays and karaoke lyrics.
SIDPlayer was one of several major tentpole products for the Commodore 64 from Compute!,along with Crossroads and the hit word processing package SpeedScript. The initial version of SIDPlayer appeared as a type-in in the book All About the Commodore 64, Volume Two by Craig Chamberlain. It included rudimentary music editing software, and a player routine that could be embedded in your BASIC or machine language programs. Chamberlain revised the system and published the Enhanced SIDPlayer in the book COMPUTE!’s Music System for the Commodore 128 & 64, this time with a companion floppy disk instead of type-in code.
The SIDPlayer system was a huge hit in the United States, and C64 owners produced thousands of songs and shared them on online services like Delphi and Quantum Link. Some SIDPlayer songs were written to be played on two SID chips simultaneously for a total of six simultaneous voices, made possible by either having two C64s, hacking a C64 to add a second SID chip, or using the SID Symphony Stereo Cartridge from Dr. Evil Laboratories. Today, this legacy lives on in the Compute!’s Gazette SID Collection (CGSC). You can download the whole set, or browse and play the collection in a web browser with the website DeepSID.
LightTangent disassembled the original SIDPlayer program, and used the findings to write SidPlay65. Many thanks to LightTangent for this ambitious project!
Full-color full-motion video demos by MirageBD HyperBallad by Bjork, as rendered on the MEGA65 by MirageBD.
MirageBD has been researching methods for streaming full-color full-motion video from the SD card. So far, Mirage has produced two brilliant demos: HyperBallad (1995), the Björk music video masterpiece, and Duel, a scene from the movie Electric Dreams (1984). Both demos launch from a D81 disk image, and stream data directly from a large file on the SD card.
Important note: Running these demos requires a fast SD card, very likely an SD card faster than the one that was included with your MEGA65. As lydon explained on Discord, the bundled cards are optimized for a longer lifespan, at the expense of some speed. If these demos glitch out after the first couple of seconds, consider getting a SanDisk Ultra brand microSD card.
COPA65 by Bananzi COPA65 by Bananzi. (Image from the manual.)
Ever wanted to copy and paste text in the MEGA65 screen editor? Now you can, with this clever extension by Bananzi! COPA65 adds a text clipboard to the built-in editor experience, as well as both keyboard and mouse methods for selecting text.
With COPA65 installed, simply press Ctrl + C to wake it up, then press one of the command keys. For example, use the C command (press Ctrl + C, then press C) to set the start of a selection, move the cursor to the end of the selection, then use the C command again to copy the text. Finally, move the cursor to where you wish to paste, then use the V command.
Other commands can quickly select characters, words, and lines, and can insert pasted text instead of overwriting. See the detailed manual included with the download for instructions on installation and use.
COPA65 requires ROM beta version 920398 or later, which is when we formalized the extension mechanism being used. This is newer than the ROM included in release package v0.96. You can update to the latest ROM beta version, or wait for the v0.97 release package, which hopefully will be out soon.
Really amazing work, Bananzi!
Midnight Mega by nobruinfo Midnight Mega by nobruinfo.
Bruno (nobruinfo on Discord) has been working on a powerful file browser, which he calls Midnight Mega. You can use Midnight Mega to browse your SD card, mount disk images, and view and copy files.
Midnight Mega is still in a beta testing release, and it is recommended to back up your data regularly. Please report any issues you find, and join the discussion on the #midnight-mega channel of the MEGA65 Discord. Also note that it only supports disk images and the internal floppy drive; it does not yet support external IEC drives.
Gurce disassembles Sea Wolf Gurce’s version of Sea Wolf for the Commodore 64, modified to support four players.
My series on disassembling Crossroads is directly inspired by Gurce, who recently disassembled another classic C64 game, Sea Wolf. In this submarine battle game, you (and an optional second player) use paddle controls to maneuver submarines to fire missiles at battleships. The game’s lineage goes all the way back to the electro-mechanical arcade game Periscope by Sega (1966), followed by a similar electro-mechanical version by Midway called Sea Devil. Midway launched a video arcade version under the name Sea Wolf in 1976. Commodore International produced a home version for the VIC-20 and C64 in 1982.
Gurce made two videos about his disassembly project. Part 1 introduces the game and the project, and presents a few findings. The extended part 2 presents the entire project, with live demos and slides. You can read Gurce’s complete notes and the final disassembly on Github. With disassembly in hand, Gurce was able to modify the game to support four players, with two paddles on each of the two joystick ports.
Disassembling classic games is a great way to learn about programming for the C64 and the MEGA65. What game would you disassemble?
Featured Files Cornucopia! Stargate (1981).
muse has given us another arcade core! Stargate is the 1981 sequel to the arcade classic Defender, a fast-paced horizontal shooter. As with other arcade cores, you will need to search for ROM data files and follow the installation instructions. The core is available for both R3 and R6 mainboards.
Fireplace is a short BASIC demo by Urban Lindeskog, featuring PETSCII flames and crackling sound effects. Drag your MEGA65 out to the living room and run this for the holidays to keep your family warm.
RobH developed an example of sprite to character collision detection in BASIC, then followed up with a Lode Runner-like ladder climbing demo. These demos show off how to implement several game mechanics with this technique, including a player sprite, walls, collectibles, ladders, and doors. Both include well-commented BASIC source code.
Alpha Burst is a brief graphics-only demo by Drex, with brilliant colors and a borderless display. Run this on real hardware for full effect.
Disk Doctor Mega by tundra/C is a low-level disk viewer and editor for both physical floppy disks and virtual D81 disk images. Inspect your disk tracks and sectors, and potentially recover data from damaged disks.
Unelite Part 1 by mk9 is a text adventure game with PETSCII graphics. Use the joystick to select options in this branching sci-fi narrative.
45GS02 Quick Reference, and other media 45GS02 quick reference, by dddaaannn (that’s me).
I brought a few gifts to the party as well. You can still buy the 45GS02 quick reference as a mousepad or a poster. If you’d prefer a digital copy, you can now download the 45GS02 Quick Reference PDF for free. I also uploaded the BASIC65 Quick Reference PDF from the User’s Guide for similar convenience.
When I took my MEGA65s to computer shows earlier this year, I made some paper items to keep at the table to help show it off. I have uploaded these designs to Filehost, in case anyone else wants to use them. The MEGA65 Conference Table standee design is designed to be printed professionally on a 11" x 17" sign, and has facts and figures about the computer. The MEGA65 Demo Booklet prints in landscape orientation on 8-1/2" x 11" paper, suitable for spiral binding on the top long edge, and features diagrams, screenshots, and very brief instructions intended for someone unfamiliar with the computer to try it out in just a few minutes. Accompany this with an SD card pre-loaded with the mentioned disk images for full effect.
Santa called his elves together
To soup up his old sleigh
So Rudolph and the other reindeer
Could rest on Christmas dayHe’s got a million miles to travel
And to do it in one day
And that’s why Santa Claus
Has a souped-up Santa sleighGot a rocket burnin’ mighty quick
Turnin’ Santa’s souped-up sleigh
Comes in like a streak of light
And it goes out the same wayWhen you hear that rocket roar
You know Santa’s on his way
But he’ll be back again next year
In his souped-up Santa sleigh— “Santa’s Souped-up Sleigh,” from the film Melvin & Howard, dir. Jonathan Demme
Have a great holiday! See you next year.
— Dan
One of my all-time favorite games for the Commodore 64—and I know I’m not alone in this—is Crossroads, the single-screen maze shoot’em’up by Steve Harter, published in Compute!’s Gazette magazine as a type-in program in December 1987. The game features dozens of enemies of a variety of types and colors, all fighting each other in a cacophony of attacks and explosions. You’re dropped into the fray to collect items called “spars,” which provide you some protection against attacks, and which the enemies also consume. The game supports one or two players on joysticks, and both players battle for survival simultaneously.
Some people like to say that typing in programs from magazines and books helped them learn how to write programs of their own. I can see how that might be true for BASIC programs, though I can’t honestly say I learned anything from type-ins when I was in grade school. For programs written in languages other than BASIC, Compute! offered no way to learn. In this interview with Steve Harter by Kirk Israel, Steve says he wrote Crossroads in assembly language, but Compute! never published assembly language listings for full programs in magazines. Instead, Compute! published such programs as columns of numbers, along with a helper program that assisted with keying these values directly into memory. The most you could learn from these type-ins was data entry, and perseverance.
(As an aside: I’m aware of only two cases where Compute! published full assembly language program listings for the C64, both as books: the LADS assembler and the SpeedScript word processor. Steve wrote Crossroads using LADS.)
Over the next two issues of this Digest, we’ll crack open the original Crossroads program and see what we can learn, using two modern reverse engineering tools for your PC. This month, we’ll take a look at Retro Debugger, a C64 emulator with real-time memory visualization features. To keep this newsletter to a reasonable length (😬) we’ll discuss just enough to get started, and ask a few specific questions of interest about the game.
As always, let us begin with some MEGA65 news!
New Filehost feature: high score tables
Filehost has a fun new feature for competitive MEGA65 gaming: high score tables! When you upload your game to Filehost, you can choose to enable “Highscore submission.” This allows players to submit their high scores by clicking the plus ("+") button on your game’s Filehost detail page, uploading a screenshot as evidence. All score submissions must be approved by you before appearing on the page. You will receive a message when a player submits a score for your game.
Check out the high score table on Classy as an example.
Thanks as always to Tayger for the Filehost innovations! Everyone go out and rack up those high scores!
Galaga, Ghosts’n’Goblins, and Xevious now available for R6 boards; TI-99/4A now available for R3 boards
Over the last year or so, muse has given us alternate cores for the arcade games Galaga, Ghosts’n’Goblins, and Xevious, originally released for the R3 mainboard. Now, thanks to Robert Jaremczak (rjaremczak), there are R6 versions for these cores: Galaga R6, Ghosts’n’Goblins R6, and Xevious R6. See the original installation instructions for the R3 cores, which also apply to the R6 versions.
zeldin has updated the TI-99/4A core several times since its initial release, and as of version 1.3, the core is available for the R3 mainboard as well as R6. Go grab the latest, and be sure to file any bugs you find.
I had a few people respond to last’s month’s Digest to point out that core availability is currently a sticking point with regards to mainboard revisions, with some cores only available for R3 and others only for R6. Core developers must put in additional effort to support both mainboards, and not everybody owns both boards for testing. If you’re working on a core and need assistance building it for a board revision that you do not own, please reach out to the MEGA65 community on Discord or by email. We will eagerly connect you with resources for building and testing your core on both revisions.
Thank you Robert and zeldin for working on cores!
Last call for R3 Real-Time Clock replacements (Grove RTC)
Back in 2022, we noticed that a small but significant percentage of R3 mainboards had faulty Real-Time Clock (RTC) chips. Paul quickly put together a workaround involving a core update and an inexpensive RTC breakout board and Grove connector. This requires a soldering iron to put together, but no expertise: it’s just four wires. Nevertheless, I wanted to make sure that everyone who needed one could get one easily, so I built a bunch of them and made them available for the cost of shipping. The MEGA65 team also supported this effort financially out of their own pockets.
I will be ending my distribution offer at the end of 2024. I have about a dozen units remaining, and am willing to send them out to anyone who needs them. If you have an R3 mainboard and believe your Real-Time Clock is faulty, please follow these instructions for testing your RTC and requesting a unit. I will email you with instructions to send me the shipping cost by PayPal, then get one out to you, while supplies last.
Please do not request one if you don’t need one. Only a fraction of the MEGA65s delivered in 2022 have this issue. Follow the instructions to test your MEGA65 before requesting a replacement. If you discover you need one later than the end of this year, consider ordering the parts and cooking one up yourself. These are easy to build!
The sega-adapter is great actually
This isn’t news so much as just boasting, but last month I mentioned the sega-adapter project as a fun way to connect Sega Genesis-style game controllers to Commodore computers. Since I sent newsletter, I had some boards printed, got the parts together, and assembled a few. Chat, I can heartily recommend it, especially when used with the 8bitDo Retro Receiver for Sega Genesis ($16 on Amazon). The 8bitDo Retro Receiver pairs with almost any modern Bluetooth game controller, including Nintendo, Playstation, and older Xbox controllers (but not newer Bluetooth LE Xbox controllers). 8bitDo also makes a Bluetooth Sega Genesis gamepad; be sure to get the M30 Bluetooth model, not the “M30 2.4G wireless” model. I got my Nintendo Switch Pro controller to be a 3-button game controller for my MEGA65!
The sega-adapter is a pretty simple project, with the following parts:
A 9-pin male connector, for the incoming Sega Genesis controller signal (such as the 8bitDo Retro Receiver)
A PIC microcontroller
A capacitor
Header pins, and a “pigtail” cable for the 9-pin female connector that goes to the MEGA65 (or what have you)
Optional header pins and jumper to switch between C64 and Amiga modes—or you can just use solder to set it to C64 mode permanently
That’s it. If you buy the exact parts recommended by the project, everything goes together using a soldering iron, and something to apply crimping force to the pigtail’s ribbon cable. You also need an inexpensive PIC microcontroller programmer such as a generic “PICKit3” device; I bought one for $33 on Amazon. I got the boards from OSHPark and parts from Digikey, for a total unit cost of about $16 each for a batch of ten adapters. The recommended 9-pin connectors were the most expensive parts, and you might be able to find cheaper ones in different form factors.
I’d never worked with PIC microcontrollers before! For software, I only needed the free version of Microchip MPLAB X IDE and the pre-built release of the sega-adapter .hex file.
To program the PIC, put it on a breadboard, then connect five of the wires of the PICKit3 to pins of the PIC, as described in the pinout diagrams (which I’m including below). Leave the sixth wire disconnected. You also need to apply about 5 volts of DC power across the PIC power pins. The PICKit3 does not supply power the microcontroller, and it needs to be powered to accept programming. You can use a little battery box, a wall wart of appropriate voltage, or a bench power supply.
I’m not selling these, I’m just showing off. It was a fun afternoon project, with a satisfying result, and because I made ten I have my holiday gifts all sorted.
Playing Crossroads
The first step to reverse engineering anything is to understand what the thing does. We can learn a lot about Crossroads just from playing it.
Archive.org has complete scans of Compute!’s Gazette magazine, and Crossroads is in issue 54, December 1987. An amusingly nostalgic way to acquire the Crossroads program would be to enter the “MLX” data entry program listed in the magazine, then use MLX to enter the 6,676 hexadecimal numbers for the game. (That’s 5,935 program bytes, plus error checking digits.) I recommend simply downloading the Compute!’s Gazette cover disk image archive. The disk image for the December 1987 issue is named 1987-12.d64. This disk image works great with the C64 core for the MEGA65. (Alas, I could not get it to run in GO64 mode. Maybe later we can figure out why.)
We can learn some useful things about Crossroads even before we run it. If we browse the disk directory, we can see that there are many files from this issue of the magazine, but the game likely consists of a single PRG file named CROSSROADS.
LOAD "$",8LIST
We can load this file from disk into memory, using the ,8,1 arguments to LOAD to tell it to use whatever memory start address that CROSSROADS prefers:
LOAD "CROSSROADS",8,1
The program loads, and we’re back at the READY. prompt. Because the game didn’t do anything fancy to start automatically, we can guess that the program has loaded to the start of BASIC memory, and probably begins with a BASIC bootstrap program that invokes the machine code, like we’ve done for our own programs. Let’s confirm this:
LIST
Sure enough, there is a one-line BASIC program in memory:
10 SYS2300
Connect a joystick to port 2 (first player), then start the program with the RUN command. The game starts without any further disk activity, which confirms our hypothesis that this is a single-file game.
If you’ve never played Crossroads before, take some time to enjoy it! Shoot bullets at monsters by pressing the fire button, and collect the white spinning “spars” to gain shields and progress to the end of the level. Connect a second joystick and invite a friend!
Observing the game
As you play, try to make some guesses about how the game works, and come up with some questions you would like answered. Your own questions and hypotheses will guide your reverse engineering process.
Here are just a few things I noticed that might be worth exploring:
Program states. The game starts with an “attract mode” where a bunch of enemies duke it out on a maze without a player present, and with a scrolling message across the top of the screen. Like many vintage arcade games, attract mode appears very similar to gameplay, and likely reuses the game’s logic with minor changes. The game starts the first level when the player presses the fire button. Each level starts with an introductory animation where creatures appear with special graphical and sound effects. The players are the last to arrive. Then play begins. A level ends when a player collects a number of spars, at which point the game pauses until a key is pressed, and the game restarts at the next level. The game ends when both players are out of lives.
Creature properties. Creatures and players have a number of shields that protect them from attacks. Each attack deducts a shield, and the creature is destroyed when attacked without shields. A creature or a player gains a shield by picking up a spar. The player’s shield count appears at the top of the screen, under the “S.” Other creatures also have shield counts, but these are not visible. Player actions are controlled by joystick inputs; creature actions are controlled by algorithms. Each creature is of one of nine types, and the type describes their action patterns, reactions to collisions with bullets and other creatures, and the initial number of shields.
Character graphics. The maze and the creatures appear to occupy a grid in the same shape as the C64’s 40 x 25 character mode, and every element appears to be the size of a single character. It’s likely that at least some of these graphical elements are using VIC-II character graphics, where the glyphs of some letters, numbers, and symbols in the PETSCII typeface have been replaced with these designs. Each element is of a single color and high resolution, 8 x 8 pixels per character.
Creature half-steps. Even though creatures are the size of a single grid tile, they appear to traverse the screen in half-tile steps! If creatures are implemented with character graphics, the game must be doing something interesting to allow a creature to appear this way.
Sprites. There appear to be too many creatures and bullets on the screen at one time for the game to be using the eight VIC sprites to draw them. While advanced techniques like sprite multiplexing (which we discussed previously) could use sprites to draw more than eight objects at a time, there’s nothing in the design of this game that prevents more than eight objects from appearing on the same scan line, so this seems unlikely. However, there is an explosion effect that occurs when a creature takes damage, and this appears to overlay the character graphics. This suggests that this effect might be done with sprites.
Sound. The soundscape of Crossroads consists entirely of sound effects that represent activity on the screen: creatures appearing at the start of the level, player bullets firing, enemy bullets firing, shields absorbing damage, creatures and bullets being destroyed, and spars getting collected. There’s so much going on early in the level that each sound may be difficult to pick out, and it’s a bit easier after the screen is mostly clear. I’ll be interested to see how sounds are allocated to the three SID channels, especially when there are more than three events causing sound at a time.
In that interview with the developer that I mentioned earlier, Steve confirms that the creatures use character graphics, and the explosions use sprites. Steve also mentions that the sprite images for the explosion animations are generated randomly.
Examining the PRG
We want to use modern PC tools to examine the program, so we need to extract the PRG file from the disk image. Graphical tools like DirMaster (Windows only) or DroiD64 (Java, multi-platform) work nicely, or you can use the c1541 command included with the VICE emulator:
c1541 1987-12.d64 -read crossroads crossroads.prg
You can use a tool called a hex viewer to examine the bytes of the PRG file. I like a terminal-based hex viewer called hexyl, and there are many others to choose from. We won’t spend more than a minute staring at raw bytes, but we can use this to get our bearings. Here are the first few dozen bytes of the PRG file:
00000000 01 08 0b 08 0a 00 9e 32 33 30 30 00 00 00 00 00 |.......2300.....|00000010 44 aa aa 11 00 00 00 00 11 aa aa 44 00 00 00 00 |D..........D....|00000020 00 3c 3c 00 00 00 00 00 00 3c 3c 00 00 00 38 38 |.<<......<<...88|00000030 30 7f be 68 ce ec 38 38 30 7f 76 30 30 38 ff 8f |0..h..880.v008..|...
As we’ve seen before, a PRG file starts with two bytes that specify the load address for the program. In this case, the first two bytes are $01 and $08, specifying the Commodore 64 BASIC start address of $0801 hexadecimal, or 2049 decimal. The PRG address bytes are not written to memory, they only tell the BASIC command LOAD "CROSSROADS",8,1 where to put the rest of the data.
The subsequent bytes describe the one-line BASIC program we saw earlier:
$0b $08: The address of the next line. In this case, it’s the end-of-program marker: $080b.
$0a $00: The line number. $000a = 10 decimal.
$9e: The C64 BASIC token for SYS.
$32 $33 $30 $30: PETSCII for 2300.
$00: End of line.
$00 $00: End of program. If you count from $0801, you’ll see this is at address $080b, from the first bytes of the first line.
These 12 bytes start at address 2049, which means the rest of the program data starts at address 2061 ($080d). Interestingly, the SYS command uses address 2300 ($08fc). The data starting at address 2061 might be code, or it might be something else. We don’t know yet. We only know that the byte at address 2300 is the beginning of a machine code instruction, and that it’s the first instruction executed by the program.
It’s amusing to compare this hex dump to the listing in the magazine. (Well I think it’s amusing.) Note that Compute!’s Gazette program listings have nine bytes on a line, but only the first eight bytes are part of the program. The ninth byte is a checksum, which the MLX program uses to verify that you entered the line correctly. If it didn’t do this, entering these programs would be a nightmare, as opposed to merely a nuisance.
Introducing Retro Debugger
If programming is the art of telling a computer what to do, debugging is the art of figuring out why it’s not doing what you meant. One of the best ways to do that is to peek inside the computer’s memory as it steps through your instructions, and try to line up the evolving state of the computer with your own mental model. This process is often so opaque and frustrating that I sometimes wish I could just rip the top off the chips and look inside as the program is running. Thanks to high fidelity emulators running on modern computers, this fantasy is now a reality.
Retro Debugger by Marcin Skoczylas is an emulator for the C64, Atari XL/XE, and Nintendo Entertainment System, with some powerful real-time debugging tools. As the emulator runs, you see the C64 screen in one window, and use other windows to render the memory in different useful ways: as hex values, as machine code instructions, as graphics in common VIC-supported formats, and as a color-coded memory map. All of this is updated as the emulation runs at full speed, and you can pause execution and manipulate system state as a program runs.
Let’s use Retro Debugger to inspect Crossroads, and follow up on a few of our assumptions about how the game does graphics.
Setting up Retro Debugger
Download the latest Retro Debugger release for your platform (Windows, macOS, or Linux). You also need the original C64 ROM and 1541 DOS code. Retro Debugger opted not to include these in the distribution, but you can get them directly by installing the VICE emulator. You’ll have to dig around the VICE folder for the appropriate .bin files. On my Mac, I found them here:
./VICE.app/Contents/Resources/share/vice/C64/./VICE.app/Contents/Resources/share/vice/DRIVES/
Retro Debugger needs the ROM files to be in a single folder, with specific filenames. Create a new folder for these (such as inside the Retro Debugger folder), make duplicates of the files from VICE, and rename them as shown:
.../C64/basic-901226-01.bin -> basic
.../C64/chargen-901225-01.bin -> chargen
.../C64/kernal-901227-03.bin -> kernal
.../DRIVES/dos1541-325302-01+901229-05.bin -> dos1541
.../DRIVES/dos1541ii-251968-03.bin -> dos1541II
When you start Retro Debugger for the first time, you need to tell it where to find the ROM files. Open the Settings menu, select C64, then Select C64 ROMs folder. Navigate to the folder you created with the ROM files in it. Retro Debugger loads the ROMs and starts the emulation. If it complains, make sure the ROM files are in the right place and have been renamed as expected.
Behold, the insides of the C64! You can see the CPU bouncing around the instructions for blinking the cursor at the READY prompt, and all of the memory and register updates that go with it. With the “C64 Screen” window active, you can interact with the C64 as you would with VICE, typing BASIC commands and whatnot.
Retro Debugger starts with some of its tools visible, but there are many more. With the cursor still at the READY prompt, open the VIC Editor menu, and select C64 Charset. The C64 Charset window opens. At this point, you should see what the VIC is currently using for its character set, which at the moment is standard C64 PETSCII. Resize the window to make the characters easier to see.
On my computer, I need to make the text bigger to be useful. Some windows, like the C64 Screen and Charset windows, have fixed-sized content, and you can drag the window corners to resize. In other windows, to increase the text size, right-click on the window, then adjust the Font Size slider. These windows can also be resized to display more information. The app will remember your window layout between sessions.
Retro Debugger is based on VICE, and has all of its debugging features. To pause the emulator, press F10 (or open the Code menu, then select Pause). While paused, press F10 repeatedly to advance by one instruction at a time. Press F11 to resume full speed. You can also set breakpoints on instructions, set watchpoints on memory locations, and can navigate to instructions that access specific memory addresses. You can even rewind the emulation to the instruction that last accessed a memory address.
For more information on features and keyboard shortcuts, see docs/README-C64-65XE-NES_Debugger.txt, included with the program.
Playing Crossroads in the emulator
There are two ways to get Crossroads running in the emulator. One method is to open the D64 disk image, then type the BASIC commands to load and run the game, as you would on a C64. Because Crossroads is a single PRG file, you can also just open the PRG file that we extracted earlier. Opening the PRG is often faster, especially if you need to reset the program.
Open the File menu, then select Open. Select the crossroads.prg file. This will load and run the game immediately, so prepare your speaker or headphones volume for the game’s violent introduction.
Next, open the Settings menu, then Joystick #2, and select a configuration for the joystick. With the joystick set to “Keyboard,” the cursor keys move the stick, and the right Alt key is the fire button. Or you can connect any PC-compatible game controller. If a controller is connected, its device name will appear in the menu.
Select the C64 Screen window, then press fire to start a game. Good luck!
Investigating character graphics
We have a hypothesis about how Crossroads uses character graphics. Let’s see if we can confirm this hypothesis in Retro Debugger.
When you started the game, it installed a new character set and told the VIC to use it. The Charset window now shows new graphics tiles: a limited set of PETSCII letters and numbers, and nearly all of the graphics for the game. You can see four types of maze wall, the “spar,” and various images for each of the creature types and bullets facing all four directions. Each of these are unique tiles of 8-by-8 pixels, and the tiles can be placed anywhere on the 40-by-25 tile grid.
We’ve made a discovery! The spar appears to be animating directly inside the Charset window! While Crossroads animates creatures using multiple static tile definitions, it animates the spar simply by continuously updating its single tile definition. This conserves charset tiles, at the expense of some CPU time.
If we squint, we can start to see how the game achieves its “half step” effect for creatures. When a creature is standing fully on a tile position, it is drawn as a single tile based on which direction it is facing. When a creature is walking between two tile grid spaces, it occupies both space, with a tile for each half of the image. There is a tile pair for each of the four directions. Each creature has two animation frames, and it appears that Crossroads stores both frames as single tiles, and uses only the second frame for the half-step tiles, for a total of 16 tile definitions per creature.
How the VIC finds character sets
The Charset window determines the memory location of the character set data that the VIC is using based on hardware registers, then draws the data it finds as characters. We can use some Commodore 64 knowledge to figure out where in memory this character set is stored.
The way the VIC-II accesses memory is a little bit complicated. The VIC can only access one of the four 16 KB banks at a time. Additionally, the C64 architecture makes a special accommodation so the VIC can access the PETSCII character set from an upper bank of ROM even when a lower bank is selected. This configuration for how the VIC sees memory is managed by the second of the C64’s two CIA chips. This is controlled by the lowest two bits of the CIA data register at $DD00.
In Retro Debugger, select the C64 Memory window. With this window selected, you can use either the Page Up/Page Down keys or your mouse’s scroll wheel to browse memory. You can also jump directly to an address by pressing Ctrl+G (or on macOS, Command+G), then typing the address in hexadecimal. Use this to jump to the register address $DD00.
Tip: Be careful what you type when the Memory window is active. If the cursor is on a memory value, typing hexadecimal digits will modify memory. When you type Ctrl+G (or Command+G) to go to an address, double-check that the cursor has moved into the address column before typing the address.
With Crossroads running, the register at $DD00 is set to $C7, so the lowest two bits are %11. According to C64 technical documentation, this points the VIC at bank 0, addresses $0000-$3FFF. It also tells the VIC to see the PETSCII character set at addresses $1000-$1FFF, hiding the RAM at these addresses from the VIC. This only hides the RAM from the VIC, not the CPU.
Still in the C64 Memory window, jump to register $D018 (53272 decimal). This register tells the VIC where to find screen memory within the selected bank, and where to find the character set. With the game running, this appears to be set to $19. This value is interpreted as follows:
$19 = %0001 1001
The upper four bits specify the location of screen memory, as a multiple of 1024 ($0400) bytes. %0001 = 1, so screen memory starts at address $0400. This is an offset from the beginning of the selected bank, and in this case the bank is 0, so the final address is $0400.
The next three bits specify the location of character memory, as a multiple of 2048 ($0800) bytes. %100 = 4, so character memory starts at address $2000.
The last (least significant) bit is ignored.
Be aware that the character memory offset gets described in manuals and online resources in different ways. I just described it the way the C64 wiki does: a three-bit number times 2048. Other books, including the MEGA65 manual, describe it as the upper three bits of a four-bit number whose last bit is 0, times 1024. The math works out the same either way.
With the selected CIA configuration, a program could tell the VIC to use the PETSCII character set from ROM by setting the three bits to %010 = 2 times 2048 = address $1000. Crossroads tells the VIC to look at $2000 instead, which is RAM where Crossroads can install its custom character set.
Let’s confirm our understanding. In the C64 Memory window, go to address $2000.
The C64 Memory window doesn’t know what the values in memory mean, but it tries to help us out by displaying them in four different ways: as hex bytes, as PETSCII characters, as character graphics data, and as sprite graphics data. In this case, we’re looking at character graphics data, so that view makes the most sense, and the other views are mostly nonsense. Scroll down to address $21F8 to see the spar glyph data being animated by the program.
Cheating at Crossroads
The C64 Memory window lets us peek into the computer’s memory as it is running, and also lets us change memory as we like. If we knew where to look, we could change properties of the game, such as the number of lives the player has. So how do we know where to look?
Locating variables is such a common task that the debugger has a tool just for this. Open the C64 menu, then select the C64 Memory Monitor. (This is a new tool, not the C64 Memory window that we’ve been using.) Resize this innocuous-looking little window to make it bigger.
Start a new single-player game by pressing the fire button on the joystick configured for port #2. The level clears, creatures spawn in, and finally the player arrives.
The player starts with 4 lives. We can know for sure that somewhere in the computer’s memory is a representation of this value, and this representation will change when the player loses a life. We don’t know where it is, or how the value is represented.
Let’s take a guess that the number of lives is stored as a byte, encoded as a non-negative integer. In other words, if we search the computer’s memory for a value of 4, there’s a chance that this might be how it stores the number of lives. This is just a guess! We can start with this, and refine our guess if it doesn’t work out.
With the emulation still running, in the C64 Memory Monitor, click the Capture button. Retro Debugger takes a snapshot of all of memory, and a giant list of memory addresses and values appears. Next, set the filter settings:
Check the box next to “Changed.” The list updates to show only the memory that differs from the captured snapshot in the current state of the emulation.
Check the box next to “Matches prev.” Click the value box, type 04, then press Enter. (It’s important to press Enter in this field, or the value won’t stick.) The Memory Monitor now lists all of the memory locations that were set to $04 when you clicked the Capture button, but currently contain something else.
Finally, check “Matches current,” and set its value to $03.
The list narrows to just the memory addresses that were 4, and are now 3. You may see more than one row, but likely only a few. Of all 65,536 memory addresses in the computer, these are the only ones that meet the criteria. This list updates in real-time as the emulation runs, and some rows will flicker in and out as memory changes.
Select the C64 Screen window again, and use your gamepad or the keyboard to move the player into harm’s way until they run out of shields and lose a life. The number of lives shown in the top display reduces to 3. Press F10 to pause the emulation.
One of the addresses that meet the filter criteria in the Memory Monitor is $0011. Let’s take another guess that this is player 1’s life count. This is a good guess for at least two reasons. It’s an address in the Zero Page (addresses $0000-$00FF), which is a common place to put variables. It’s also the only row that doesn’t flicker while the emulation is running.
On the row showing address $0011, click the “Memory” button. This updates the C64 Memory window to highlight that location. As expected, its value is currently $03. Now change this value: select the Memory window, then type 07. Press the F11 key to unpause the emulator.
Hmmm, the number of player lives on the screen still shows “3.” Was our guess incorrect? Try killing off the player one more time. Success! The life count now reads “6,” one less than the value we set. Double-check that the value at address $0011 is now $06.
When we dig deeper into the machine code next month, we should expect to find instructions that manipulate address $0011, and can probably assume that the code is related to the number of lives of player 1. We can use that information and some inductive reasoning to determine the purpose of the surrounding code, and figure out the rest of the program.
We got a bit lucky with our guesses in this example, though truthfully many games store the number of player lives this way. What if our guesses were wrong, and the number of lives wasn’t stored as a non-negative integer in a byte value? One possible strategy would be to locate the code that updated the “4” on the screen to a “3.” We could use the Memory Monitor against the screen codes for those characters, or we could locate the screen memory address and ask Retro Debugger to tell us which instruction last changed it. It might be possible to figure out from nearby code where it reads the value that it displays, and how the value is interpreted.
More things to try
Retro Debugger comes packed with plenty of toys for messing with the state of the emulator. Browse the menus and experiment! Most menu items open a new window with interesting features. Here are just a few ideas.
Locate that first instruction. Recall that the BASIC bootstrap invokes the machine code at address 2300, or $08FC in hexadecimal. Pause the emulator by pressing F10. Select the C64 Disassembly window, type Ctrl+G (or Command+G for macOS) to go to address $08FC. We can see that the first few instructions initialize some hardware registers: $D40E, $D40F, $D412. You can look up these addresses in C64 technical documentation to see what they do. Researching all of the code this way is a bit tedious, but it’s fun to see the code sitting right there in memory. We’ll use a more powerful tool for investigating this code in next month’s Digest.
Watch the SID chip make sound. Open the C64 menu, then the C64 SID sub-menu, then the C64 SID tool. The C64 SID window opens, showing information about the SID chip’s three sound voices. (Check out one of the earliest Digest issues for an introduction to the SID chip.) Keep this window open, play the game for a bit, and watch the SID waveforms as the enemies duke it out.
Draw on the screen. Open the VIC Editor menu, and select Show VIC Editor. This window resembles the screen, and by default shows the tile grid and the pixel grid. This is a powerful tool for updating the VIC data in real time, and there’s quite a bit of documentation of its features in the text file. For now, select the C64 VIC Editor window, then left-click on an empty space on the Crossroads maze. It fills in with a character. Try making a wall of characters in the maze. Another discovery! The characters block the path of creatures, and the player! It is likely that the game code reads screen memory directly to detect whether a creature is adjacent to a wall.
Watch for sprites. The VIC Editor highlights sprites with a red box. If you zoom out in this window with the mouse scroll wheel, you can see that Crossroads keeps the sprites off screen when not in use. Play the game to cause explosion effects as shields and creatures are destroyed. Notice that the red boxes appear around the explosions in progress, then retreat out of view when no longer needed.
We’ve only scratched the surface of what Retro Debugger can do, and we’ve already learned quite a bit about how Crossroads works. We’ll be able to apply this knowledge as we start investigating the code.
Next month, we’ll introduce Ghidra, a powerful tool for inspecting machine code. With some effort and ingenuity, we’ll be able to produce a complete human-readable assembly language source listing for the game—the listing that Compute!’s Gazette refused to print.
Before you ask: no, there is currently no real-time memory visualization tool like Retro Debugger for the MEGA65 or the Xemu emulator. Yes, it would be cool. LGB tells me more work is needed on Xemu before it can support real-time memory visualization. In the short term, we’re more likely to see investment in the serial debugging protocol and tools like m65dbg, which can be used with both the Xemu emulator and with real MEGA65 hardware over a JTAG connection.
This Digest is made possible by an amazing group of supporters. Only supporters are allowed to complain when the feature article isn’t specifically about the MEGA65. To become a supporter, visit: ko-fi.com/dddaaannn
More next month!
— Dan
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
Everyone who owned a Commodore 64 had at least one joystick, a game controller consisting of a lever that could be pushed in eight directions and a single action button. Games based on this controller typically involved the player using the stick to move an avatar—a spaceship, a person, a creature—as well as pushing the action button to activate a weapon against some enemies. The button on this kind of joystick was almost universally referred to as the “fire” button for how often it was used to launch missiles or shoot bullets.
Then came the Nintendo Entertainment System with its gamepad controller, a plastic rectangle with a directional thumb pad, two action buttons, and two more business-like buttons intended for pausing the game and accessing menus. NES games, especially Super Mario Bros. and The Legend of Zelda, popularized a new game design language based on the two action buttons. While Mario-like platform games were made for the C64 and its one-button joystick, expecting the player to shove the joystick forward (“up”) to jump, every generation of gamers that followed the NES expected to be able to jump in a platform game by pressing one of the two action buttons.
The vast majority of C64 games used the one-button joystick protocol. But there have been several attempts, both vintage and modern, to bring multi-button gaming to the C64, and to other computers with 9-pin joystick ports. This is of particular interest to modern retro game developers who want to use multi-button game design conventions while coding for the C64 and MEGA65.
In this Digest, we’ll explore a bit of the history of 9-pin game controllers, and investigate several methods used to implement multi-button schemes. We’ll look at the fundamentals of game controller electronics, and game controller programming. We’ll also take a closer look at several modern game controllers and controller adapters you can buy today for your MEGA65.
But first news!
More MEGA65 parts available separately
Last month, we noticed that Trenz Electronic is now making the MEGA65 mainboard available for preorder as a standalone part. Trenz has since added more MEGA65 parts to their store: the MEGA65 keyboard, the plastic case, the floppy drive, and the power supply. The keyboard is a sophisticated part, including the mechanical key switches, keycaps, cable, metal frame, and LEDs. The case includes other plastic bits such as the ALPS floppy drive eject button and reset button cap. You could, in theory, assemble a full MEGA65 from the spare parts available.
Math wizards may notice that the cost of the parts all together add up to more than the cost of a complete MEGA65. I can’t speak for Trenz, but this jibes with my limited understanding of the microeconomics of short-run niche electronics. Without economies of scale, Trenz has to account for labor, logistics, and potential lossage to support the individual items, even when pulling from the same stock they use to assemble full units. As deft puts it, “Imagine buying a car one piece at a time.”
I’m grateful that they’re making these available, so we have potential replacement parts, as well as options for other projects.
“Should I upgrade my mainboard?”
Did you receive your MEGA65 in 2022, or buy it second-hand from someone who did? Then your computer has the revision 3 (R3) mainboard. Starting in 2024, Trenz Electronic began shipping MEGA65s with the revision 6 (R6) mainboard, which has several changes from R3. If you’re unsure which MEGA65 mainboard you have, you can double-check which one you have in the MegaInfo panel: open the Freezer (hold Restore for one second, then release), then press Help. It’ll say which one you have next to “MEGA65 Model.”
Should owners of the R3 board upgrade to the R6? Probably not. It is unlikely that a difference between the R3 and R6 mainboards would be worth the cost of a new mainboard to most R3 owners. The differences may not even be noticeable.
There are a few notable categories of changes between the R3 and R6 boards: bidirectional cartridge and joystick I/O lines, an added SDRAM chip, and electrical fixes.
Bidirectional cartridge lines. The original R3 mainboard supports the vast majority of cartridges made for the Commodore 64, which provide program data as input to the computer. It will also support cartridges made for the MEGA65 in the future. The cartridge port changes in the R6 board apply to a few electronically fancy cartridges that communicate with the computer using both input and output. For example, the MSSIAH MIDI interface cartridge works with the C64 core, and requires the R6 mainboard. EasyFlash 3 is also known to require the R6. Notably, an EasyFlash 1 cartridge can be both read from and written to by an R3 MEGA65. That’s how we’ve been testing the MEGA65 cartridge protocol.
Bidirectional joystick port lines. The original R3 mainboard also supports the vast majority of game controller peripherals for the C64, which provide input to the computer about how the user is manipulating the controller. A few unusual peripheral designs use the data lines for both input and output, to implement more buttons or other features with proprietary protocols. The R6 mainboard adds support for such protocols. See this month’s feature article for more on game controller protocols.
SDRAM chip. The R6 adds an SDRAM chip that is intended to broaden the possibilities for alternate FPGA cores. It is possible that someday there could exist an alternate core that requires the R6 and doesn’t run on the R3. Personally, I think it’s worth tempering these expectations. I’ve discussed this with a bunch of people familiar with MiSTer-style core development, and they have all estimated that the most compelling alternate cores for the MEGA65—including an Amiga core—should fit the original R3 board. There may never be an alternate core that requires the SDRAM, even though it was added for this purpose. No core exists today that requires an R6, and I’m not aware of anyone working on one.
Most importantly, the MEGA65 core does not use the new SDRAM component. All software written for the MEGA65 will run on both R3 and R6 mainboards, now and in the foreseeable future. There are no plans to treat the SDRAM chip as a memory expansion for the MEGA65.
Minor fixes. Lastly, the R6 board implements some minor fixes for known issues. If you are noticing these issues on an R3 board, you may be able to use inexpensive workarounds, such as an HDMI repeater or a replacement Real-Time Clock. If you’re not noticing these issues, you won’t notice the fixes with an upgrade. This category also includes a few changes to accommodate the availability of components; these changes don’t affect the functionality of the computer.
I know I’ve spent a lot of money over the years indulging my FOMO by buying hobby computers “just in case” I want them later, and it’d be hypocritical of me to say an R6 is not a worthy upgrade to an R3 when I own one of each. But given that a new mainboard is more than half the cost of a new MEGA65, it’s worth resisting the temptation to think other people have all of those things and you have none of them. Really, R3 and R6 are mostly identical.
Portland Retro Gaming Expo 2024
I took the MEGA65 to the Portland Retro Gaming Expo in Portland, Oregon, mere weeks after getting back from Chicago. PRGE is another massive show, but with a different vibe from VCF Midwest. As the name implies, the show is dominated by vintage gaming, with free-to-play arcade and gaming console exhibits, gaming tournaments, several concurrent tracks of talks and panels, meet-and-greet booths with voice acting and YouTube celebrities, and a massive vendor floor with multiple eras of console games and related merchandise.
PRGE has always featured vintage home computing in its line-up, but representation of the home 8-bits on the vendor floor has waned as our vintage hardware has become more collectible. This year, PRGE established a “home computing” exhibit to keep some of our favorite computers in the show. My MEGA65 booth joined several large displays of Texas Instruments, Commodore, Atari, and Apple vintage equipment, as well as more obscure Z80-based computers. I sat closest to the tournament area, where competitors tore through Street Fighter and Rivals of Aether as commentators hyped up the crowd. I was also across from the big surprise of the show, a large display by The Last Blockbuster, with Blockbuster video iconography, merch, and racks of VHS tapes.
My MEGA65 table was much larger and more welcoming to visitors than it was at VCF Midwest. I’m grateful to Hans, the show runner, for gifting me two tables, and for lending me a big screen TV. And I got to hang up the banner! Even in this back corner of the show floor, the MEGA65 drew a lot of attention, and I spent nearly all of the first day on my feet, answering questions, shaking hands, and handing out cards and mousepads. Several people from press outlets wanted interviews. If the fire alarm hadn’t gone off in the middle of the day, I may never have had a break for lunch. The second day was quieter, but I still had quite a few kids stop by for a round of Unicone or First Shot.
It was especially gratifying to see young people take an interest in the MEGA65. Some of them already knew what it was and were excited to see it in person, some dragged their parents over to the table for a look, and several sat down at the computer for long stretches of time to peruse the demo disks and write short programs. I talked to MEGA65 owners and wannabe owners, and plenty of people simply intrigued by an 8-bit style computer that seemed familiar but unlike anything they’d seen before. I got to introduce many people to the stories of both the Commodore 65 and the MEGA65.
New TI-99/4A core
My very first computer was a Texas Instruments TI-99/4A, and now thanks to zeldin, I can recreate those childhood memories! Mega99 is a TI-99/4A core, written from scratch exclusively for the MEGA65. The core needs the ROM files from the MAME project (ti99_4a.zip, ti99_fdc.zip, and ti99_speech.zip) as well as the provided mega99sp.bin file in the root of the SD card. Flash the core to a spare slot in the core selection menu.
As of this writing, the core is only available for the R6 boards. This is not a matter of hardware capacity, and hopefully zeldin can produce a build for R3 owners in the future.
Be sure to read the README.md file for installation and usage instructions. The core matches the 99/4A CPU timing by default, and can also run in a turbo mode. And get yourself the TI-99/4A User’s Guide to remind yourself how this thing works! Hint: the Mega key is the FUNC key, and many text entry features use it.
Thank you zeldin for this excellent and nostalgic core!
Featured Files
Here are some recent demos uploaded to Filehost worth checking out:
Shodan, by Drex.
3D functions and Patterns, by adorigatti.
HyperBallad, by MirageBD.
Joytest65
This month’s Digest is all about joystick and paddle programming, so I made a joystick and paddle tester app to go with it. It includes documentation and assembly language source code.
The 9-pin connector
The C64 was one of several early computers that adopted the Atari joystick port standard from the Atari 2600 (1977), and supported the use of Atari game controllers to play C64 games. I’m most familiar with the classic Atari CX40 joystick, but there were many others, and everyone has a favorite. Commodore used the Atari-style controller connector on everything from the VIC-20 to the Amiga, and they intended the C65 to have two 9-pin peripheral ports, just like the C64. The MEGA65 has two 9-pin peripheral ports on the left-hand side.
Nintendo released the NES in the USA with a proprietary controller connector and more buttons, and the game console arms race that followed produced more custom connectors, more buttons, and more evolutions in game design language. Commodore made two attempts to follow suit in their final years, both using new protocols over the original Atari-style connector: the C64GS and the Amiga CD32. These protocols were backwards compatible with older Commodore computers, and the new consoles also supported the one-button controllers and ran compatible games.
Commodore also made several types of computer mice that used the 9-pin connector, especially the 1351 mouse for the 8-bit computers, and the Amiga mouse. As you probably know by now, your MEGA65 supports both types of mice, using built-in adapter logic to make the Amiga mouse appear to software as a 1351. See the User’s Guide, 2nd edition, for information on how to configure the mouse emulation mode. We won’t cover the mice protocols in this issue of the Digest.
What follows is a description of how Atari and Commodore computers use the 9-pin connector for game controllers. Other 8-bit computers and early game consoles used the connector, but not necessarily the same wiring or protocols.
Joystick fundamentals
A standard 9-pin one-button joystick tells the computer that the action button is being pressed by connecting pin #6 to pin #8 (the ground pin, labeled “GND”). This doesn’t require any fancy electronics, it’s typically just a button connected to these two pins. When the player presses down on the button, two pieces of metal in the button make contact, completing an electronic circuit. When the player releases the button, a mechanical spring-like action separates the two pieces of metal, breaking the circuit and signaling to the computer that the button was released. This kind of button is also known as a momentary switch.
Pins #1, #2, #3, and #4 work similarly, representing the joystick directions up, down, left, and right, respectively, when connected to pin #8. The joystick indicates a diagonal direction by completing two circuits simultaneously, such as up and left. Typically, there are no fancy electronics involved here either. The joystick is built such that when the user pushes on the stick, it closes one or two momentary switches wired to those pins.
Within the MEGA65, these pins are connected to the Complex Interface Adapter (CIA) chip, which translates these electronic signals into register values that can be read by a program. Try this: connect a joystick to port 2, and run the following simple program:
10 PRINT PEEK($DC00)20 GOTO 10
The program prints the number 127 repeatedly. Move the joystick and press the fire button. The number changes depending on which buttons are pressed.
This register value is a bit field, where each button is assigned to a bit. If the button is not pressed, the bit is 1 (“high”). If the button is pressed, the bit is 0 (“low”). This is why the register value is 127 when no buttons are pressed: all of the bits associated with the buttons are “held high” until they are “pulled low” by the button presses. (The register has additional bits not connected to the joystick pins.)
The one-button 8-direction joystick protocol can be summarized as follows:
Buttons connect pin to GND; CIA bit = 0 when pressed
Pin #1: Up, CIA bit 0
Pin #2: Down, CIA bit 1
Pin #3: Left, CIA bit 2
Pin #4: Right, CIA bit 3
Pin #6: Fire, CIA bit 4
Pin #8: Ground (GND)
%01111111 = 127%01111110 = 126: up%01111010 = 122: up and left%01101010 = 106: up, left, and fire
It is the program’s responsibility to read these register values, do the math, then change the game state appropriately, such as to move the player sprite around the screen.
Like the C64, the MEGA65 has two CIA chips. Only the first CIA is used to read the joystick ports. And yes, these are the same CIA chips that provide timers and other features.
The CIA and port 1
Register $DC00 represents joystick port 2. To read the status of joystick port 1, you read register $DC01.
C64 owners remember this one: with a joystick connected to port 1 and the cursor at the READY prompt, you can get junk to show up on the screen just by jiggling the stick around. This is no longer true with the MEGA65 in MEGA65 mode (as of release v0.96), but you can still reproduce this in GO64 mode or the C64 core. Try it!
The C64 uses the CIA chip connected to the joystick ports for multiple purposes. This includes reading the keyboard. When you type on the keyboard, it pulls down the CIA bits in certain patterns, just like a joystick on port 1 does. This is why, when the C64 is expecting keyboard input, a joystick in port 1 moving around can seem like typing. Even though you can prevent this effect in code for a game, many C64 games simply assumed that most people would keep their joystick connected to port 2.
The MEGA65 is better behaved because its keyboard handling uses a different mechanism that is not as reliant on the CIA chip. A couple of specific functions still test the CIA for specific key presses, but in general, port 1 joystick events are much less likely to interfere with the keyboard.
Reading the joystick from BASIC 65
BASIC 65 provides a handy function for reading the state of the joysticks. The JOY(port) function returns a value that represents joystick activity on the requested port (1 or 2). Instead of a bit field, the JOY() function returns a number from 0 to 8, where 0 means the joystick is in the center, and 1 through 8 are the eight directions counting clockwise from “up.” When the button is pressed, it adds 128 to this value. See the User’s Guide for a table and example program.
10 PRINT JOY(2)20 GOTO 10
As of ROM beta version 920397, the JOY() function can also read both joystick ports simultaneously, and report a merged result. To request this, use a port number of “3”: JOY(3). This is intended solely to allow a game to work with a single joystick in either port, so the player doesn’t have to change their joystick connections to play. It does not return a useful result when two joysticks are connected and moved simultaneously. If your game uses two joysticks, use JOY(1) and JOY(2) separately. This feature will be in the next MEGA65 stable release.
A machine code program can read the state of the joystick by accessing the CIA registers and interpreting the bits directly. For best results, such a program must take interrupts into account if it leaves the KERNAL interrupt handler active.
The KERNAL interrupt handler messes with the CIA registers many times per second to process certain keyboard events. A program wanting a clean read of the CIA register must disable interrupts to prevent the handler from messing up the result.
The program has to tell the CIA chip to ignore the state of the keyboard when reading the joystick. I won’t cover how this works in detail, just know that the way to do this is to write the value $FF to the data register ($DC00 or $DC01) before reading it.
Here’s the complete sequence for a clean read of port 2 in assembly language:
sei ; Disable interruptslda #$ffsta $dc00 ; Lock out the keyboard lines on port 2lda $dc00 ; A = port 2 pin statecli ; Re-enable interrupts
Paddle pairs and potentiometers
In addition to joysticks, the Atari controller standard also supports a paddle controller. A paddle uses a dial-like wheel instead of a joystick. A game can detect how far the wheel is turned by reading more registers. This controller is called a “paddle” after the seminal home computer game Pong, which used such a controller to manipulate a paddle in a virtual game of ping pong.
Each joystick port supports two paddles, each with its own dial and fire button. The MEGA65, Commodore 64, and Atari 2600 all have two joystick ports, for up to four paddle players. The two dial-like controls are also used together by peripherals such as light pens or mice, where the two inputs represent movement along X and Y axes.
The paddle protocol is different from the joystick protocol. Electronically, the fire buttons are wired to joystick port pins #2 for the first paddle and #3 for the second paddle, connecting them to pin #8 (GND) when pressed. A program can read these pins using the CIA data registers, just like reading the joystick. The register bits read 0 when the button is pressed.
The paddle dial itself is a potentiometer, also known as a variable resistor or “pot.” Instead of simply connecting or disconnecting the path between two pins like the fire button, the potentiometer forms an electrical connection between two pins through an amount of electrical resistance that the computer can measure. When the player turns the knob, the amount of resistance changes, indicating the position of the dial. The first paddle on a port connects signal pin #5 (“POTX”) to pin #7, where the computer provides a voltage of +5V. The second paddle on the port uses signal pin #9 (“POTY”) and pin #7.
The paddle protocol:
Potentiometers connect pin to +5V
Buttons connect pin to GND; CIA bit = 0 when pressed
Pin #2: Paddle A button, CIA bit 1
Pin #3: Paddle B button, CIA bit 2
Pin #5: Paddle A analog (POTX)
Pin #7: +5V
Pin #8: Ground (GND)
Pin #9: Paddle B analog (POTY)
Resistor values and register values
Strangely, Commodore used a different potentiometer in their own paddles compared to the ones by Atari, and the two paddle types produce different numbers when accessed from a Commodore program. It is generally understood, and some have measured, that Commodore paddles use a potentiometer rated at 470 kΩ (kilo-ohms), and Atari paddles are rated at 1 MΩ (mega-ohms), with the measured resistance range a bit less but on that order of magnitude. This means that when an Atari paddle is connected to a Commodore, turning the knob goes from the minimum value to the maximum value in half the rotational distance (half the “throw”) than an official Commodore paddle. In practice, this can be both good and bad: shorter throw can result in faster play, but with less accuracy and a large “dead zone” where the resistance is above the maximum recognized by the computer.
I wanted to know the actual resistance value range recognized by the MEGA65, so I started by measuring the resistance of my Commodore and Atari paddles with an ohmmeter. What I discovered was a bit of a surprise.
I took some ohmmeter measurements of my Commodore and Atari paddles, and also tested the values returned by the BASIC 65 POT() function, which returns a value from 0 to 255 based on the dial setting. Both paddles had a minimum resistance in the fully clockwise position, then increased counter-clockwise to their maximum. The Commodore paddles measured a minimum of 44 Ω to a maximum of 166 kΩ. The POT() function returned a value of 220 at the maximum, not the full 255. Atari paddles had a higher resistance of 124 Ω at minimum to 782 kΩ at maximum. Here, the POT() function returned the full 255 after only a third of a turn, with a whopping dead zone of two thirds of a rotation, returning 255 for that portion.
So I opened the cases of both sets of paddles, and discovered that my Commodore paddles have a potentiometer rated at 200 kΩ, far less than the 470 kΩ that others have reported. My Commodore paddles also have a capacitor in parallel with the signal lines, which smoothes out the signal and shouldn’t affect the DC resistance, but is notable because it is absent from other paddles. I asked some friends to open theirs and they all reported 470 kΩ pots, so I must have some weird ones.
While it’s reasonable in the abstract that a Commodore program should be able to detect the full range of motion of the Commodore paddle knob, it may have turned out to be unreasonable in practice. A game expecting the full value range to represent a full turn of the knob might be unplayable with Atari paddles, and vice versa. Perhaps Commodore decided to split the difference at some point and changed to a 470 kΩ potentiometer, and I just happen to have an earlier model of the paddles.
Of course, it's possible that a previous owner of my paddles replaced the pots with the unusual values and these didn't come from Commodore.
Reading paddles from BASIC 65
As with joysticks, BASIC 65 has a function for reading paddles: POT(paddle) returns a paddle position as a number between 0 and 255, and adds 256 if the paddle’s button is pressed. The paddles on joystick port 1 are #1 and #2, and the paddles on joystick port 2 are #3 and #4.
10 PRINT POT(1), POT(2), POT(3), POT(4)20 GOTO 10
The MEGA65 paddle registers
The MEGA65 has dedicated circuitry and registers for reporting the four paddle values. A MEGA65 machine code program can read these values directly from registers.
$D620: Port 1, paddle X
$D621: Port 1, paddle Y
$D622: Port 2, paddle X
$D623: Port 2, paddle Y
The fire buttons on the two paddles work just like joystick buttons, and can be read directly from the CIA data register for the appropriate port.
That's all a MEGA65 programmer needs to know. On the C64, the process is a little more elaborate, and it's interesting to know why.
Reading paddles the old-fashioned way
The CIA chip only knows how to handle digital signals (1’s and 0’s), and can’t read a resistance value (an analog value in a range). Instead, it gets the help of another chip in the chipset more familiar with converting between analog and digital signals: the SID sound chip.
To read a paddle value, a program first tells the CIA to connect the SID to one of the joystick ports. The program must then wait briefly before reading the value from a SID register. The SID needs a small amount of time to read the resistance value and update the register. During this time, the SID charges an internal capacitor that is connected to the signal and voltage lines of the controller. Once it is charged, the SID flips to a different circuit that discharges it. The SID determines the resistance of the paddle controller from the amount of time it takes to perform this charge-discharge process. To convert this to a value between 0 and 255, the SID needs 512 CPU cycles. At the end of this time, the SID updates its register value.
512 machine cycles at a rate of 1 MHz takes about 1/1950th of a second. That doesn't sound like a lot, but it can be a long wait for an action game, over 10% of the time it takes to draw a frame of graphics. Of course, the program is welcome to do other things during this time. A game loop could set the CIA select bits at the beginning of the frame, then read the paddle values near the end of the game logic, adding a busy-wait at the end as needed.
The SID can only be connected to one joystick port at a time. To read paddles from both ports, a program must connect to the first port, wait the 512 cycles, read the SID registers, then connect to the second port, wait another 512 cycles, and read the SID registers again. A game that reads all four paddles may or may not need to be clever to use the waiting time wisely, depending on how much time the game needs per frame.
If the program leaves the SID connected to a single port and takes over the hardware interrupt vectors (such that the KERNAL interrupt handler isn’t messing with the CIA connections), it can skip the wait and just read the SID register at its convenience. The register will contain the last stable value.
The C64 Wiki page on paddles has a diagram of the resistor-capacitor circuit that the SID is using to measure the resistance value in the controller. I was confused at first because the diagram makes it look like the capacitor is provided by the paddle controller, outside of the joystick port. This is not the case: the SID provides the capacitor part of the RC circuit. The capacitor present in my Commodore paddles bridges the +5V and signal lines, and is there for smoothing purposes and is not part of the RC timing circuit. Other paddles don’t have the smoothing capacitor.
The C64 way to read paddles from machine code
A C64 machine code program must perform the full procedure to read an analog value from the SID chip: select the port to connect the SID chip, wait briefly, then read the result from a SID register.
To select the port, set bits 6 and 7 of register $DC00: for joystick port 1, set bit 6 = 1 and bit 7 = 0; for joystick port 2, set bit 6 = 0, bit 7 = 1. This is the same address as the CIA data register for reading joystick pins on port 2, but you use this address even when connecting the SID to joystick port 1. That's just how the CIA chip is wired.
The program then waits briefly for the SID to get the value. On a C64, this relatively easy: perform a busy-wait loop for some number of iterations. Based on a reference table of cycle counts per instruction, you probably only need 103 or so repetitions of a loop, but it doesn’t hurt to wait longer. The C64 CPU runs at 1 MHz, so 512 CPU cycles is sufficient.
(The MEGA65 normally runs its CPU at 40 MHz. To reproduce the SID reading procedure, you have two options: temporarily down-clock the CPU to 1 MHz for the busy-wait, or perform the busy-wait loop 40 times. Note that instruction cycle counts differ on the 45GS02 CPU than on the 6502.)
After the delay, the values of the two paddles can be read from SID registers $D419 and $D41A. The ROM code that implements the BASIC POT() function takes an additional step to debounce the register value, which just means to wait until two consecutive reads of the register return the same value.
Here is code for the complete procedure, reading the state of both paddles on port 2:
; Disable interrupts sei ; Down-clock MEGA65 CPU to 1 MHz ; FAST $D031.6 -> 0 lda #$40 trb $d031 ; Connect SID to port 2 analog inputs lda #%10000000 sta $dc00 ; Wait for SID to read, ~512 1 MHz cycles ldx #$00- inx bne - ; Read port 2 paddles, with debounce- lda $d419 cmp $d419 bne - sta paddle2a- lda $d41a cmp $d41a bne - sta paddle2b ; Reset MEGA65 CPU to 40 MHz ; FAST $D031.6 -> 1 lda #$40 tsb $d031 ; Re-enable interrupts cli; ...paddle2a: !byte $00paddle2b: !byte $00
Two buttons—and why not a third
The Commodore 64 Games System (1990), aka the C64GS, repackaged the C64 into a keyboard-less console box. To enhance the console’s gaming potential, the Cheetah Annihilator joystick had two buttons: a trigger button in the joystick grip, and a second button that could be detected separately in the base. This two-button protocol worked over the 9-pin connector and could be supported by any C64 game that knew about it. Only a few games were written to support it.
Math wizards may notice that all nine of the joystick port pins have been assigned: up, down, left (or paddle A button), right (or paddle B button), paddle B (“POTY”), fire, +5V, GND, and paddle A (“POTX”). That’s nine. So how is the second button connected?
In the two-button protocol, the second button uses paddle A’s analog line (pin #9, “POTX”). Unlike the other joystick buttons that connect their respective pins to pin #8 (GND, “pulled low”), this button connects pin #9 to pin #7 (+5V, “pulled high”), with a current-limiting resistor to protect the sensitive CIA and SID chips. (One recommended resistance value I’ve seen is 270-330 ohms.) To a program, a button press looks like a paddle dial flipping between its minimum and maximum positions.
The Cheetah Annihilator only had two action buttons, but if someone were to add a third button to such a controller, it would have an easy way to connect: just use the other paddle line (pin #5, “POTY”). This three-button protocol was uncommon in vintage joysticks, but is generally accepted by controller modders and homebrew controller makers, because it’s a natural extension of the two-button protocol, and doesn’t require fancy electronics beyond the button and safety resistor.
The three-button protocol:
Pin #1: Up, CIA bit 0
Pin #2: Down, CIA bit 1
Pin #3: Left, CIA bit 2
Pin #4: Right, CIA bit 3
Pin #5: Button C, via POTX
Pin #6: Button A, CIA bit 4
Pin #7: +5V
Pin #8: Ground (GND)
Pin #9: Button B, via POTY
To read the complete state of a three-button controller, simply combine the techniques for joysticks and paddles. Here’s an example of reading all buttons from port 2 in BASIC:
10 J = JOY(2)20 D = J AND 128 : REM: THE DIRECTION30 A = J > 127 : REM: BUTTON A40 B = POT(3) < 128 : REM: BUTTON B50 C = POT(4) < 128 : REM: BUTTON C
Reading the additional buttons from machine code is similar to reading the paddle values. A C64 program must still connect the SID to the port, wait for the SID to sense the button value, then read the value from the SID register. A MEGA65 program can just read the paddle registers.
Five buttons??
An 8-way joystick with two or three action buttons brings Commodore gaming one step forward in 1980’s game design language. Math wizards may notice that the NES gamepad has not three but four buttons: A, B, Start, and Select. Can we support all four with minimal changes to the protocol?
5plusbuttonsJoystick, by crystalct, specifies a five-button protocol using a neat trick: button #4 triggers “up” and “down” simultaneously, and button #5 is “left” and “right.” A normal joystick can’t activate these opposing directions at the same time, so they uniquely identify the buttons. This scheme has the disadvantage of not being able to use buttons #4 and #5 simultaneously with the joystick—pressing button #4 and “up” at the same time would only register the button—but they can still plausibly be used as Start and Select buttons, which aren’t typically combined with other buttons during play.
crystalct has published the schematic and board design for a joystick that supports this protocol, and you can order the board from PCBWay. It would be easy to hack a NES-style gamepad to support this with just a few wires. The two buttons are easy to detect with the CIA data registers, though they won’t work with the current version of BASIC 65’s JOY() function, which interprets the directional buttons to direction values only.
The project includes a C64 joystick test program also useful for trying out three-button controllers (PRG download). It works with the C64 core; I had difficulty running it with the MEGA65 core in GO64 mode. crystalct also personally modded dozens of games to add multi-button features—though none use the 4th and 5th buttons, as far as I can tell.
More buttons and bidirectional protocols
Game consoles that followed the NES had seven or eight buttons in addition to the directional pad. How far can we go, using the same 9-pin joystick port?
The protocols we’ve discussed so far could all be considered passive protocols: the controller can report the state of the joystick and buttons to the computer by mechanically completing circuits across the connector pins, and the computer does all the work to interpret these input signals. To be able to report more sophisticated inputs using the same pins, we need an active protocol, where the computer not only reads signals from the controller, but also sends signals to the controller. Perhaps it’d be more accurate to call these bidirectional protocols, because the output signals could be manipulating otherwise “passive” digital electronics in the controller, such as a key matrix. These days, microcontrollers are cheap, and modern game controllers contain tiny computers of their own, capable of an active dialog with the host computer.
The Amiga CD32 game controller provided seven buttons: red, blue, green, and yellow action buttons, left and right shoulder buttons, and one menu button. It used the same 9-pin connector, and did so in a way that allowed the CD32 to support all previous Commodore-compatible passive controllers and allowed the CD32 controller to be used as a passive two-button controller on other Commodores. This required the computer to treat pin #5 as an output pin to select either active or passive mode: the CD32 would hold this pin low relative to the +5V line to request the active protocol—something other Commodores wouldn’t do. When in active mode, the computer sends pulses to the controller on pin #6. With each pulse, the CD32 controller rotates through the seven buttons, reporting the status of each button on pin #9. This mechanism is called a shift register and is typically implemented with a common shift register chip in the controller.
While the backwards-and-forwards compatibility of this design is impressive, it rules out the ability for C64 programs to support a CD32 controller, because the C64 cannot treat pin #5 as an output. Perhaps someday we could add a CD32 controller mode to the MEGA65 core, but there just aren’t many CD32 controllers around. Would be a fun VHDL experiment, though.
Sega game consoles also used a 9-pin connector, though they did not use Commodore-compatible pin assignments. The Sega Master System started with a passive two-button controller protocol, and the Sega Genesis (aka Mega Drive) added more buttons (first 3+1 then 6+1) with an active protocol: one pin was a dedicated output from the console that marched through two, four, or eight cycles, alternating high and low, and the definition for each of six input pins changed for each cycle. The full state table is a bit complex, trying to be backwards compatible with previous standards. (This appears to be based on timing? I got a bit lost trying to read about it.)
I always feel obligated to mention: Do not connect a Sega Master System or Sega Genesis controller directly to a Commodore. The incompatible wiring protocol could damage the delicate CIA chip. The MEGA65 is a bit more robust in this regard, but the MEGA65 core won’t assign the pins in a way that matches the controller.
These newer controllers weren’t the first to take advantage of bidirectional communication to support more buttons. The ColecoVision Hand Controller sported the usual 8-way joystick, and also a number pad with twelve buttons. Surprisingly, this protocol only uses seven of the nine pins, and the computer selects between one-button joystick mode or number pad mode by connecting one of two pins to power. In number pad mode, each button is represented as a combination of the pin signals. For example, when the “5” button is pressed in this mode, pins #2 and #3 are pulled low. When the “4” button is pressed, pins #1, #2, and #3 are pulled low. This means that only one button can be recognized reliably at a time. If “4” and “5” are both pressed, only “4” is recognized.
None of these protocols work with the Commodore 64, and due to the lack of a prevalent commercial implementation, there is no real consensus on a common protocol for Commodore 8-bit computers that supports more than three buttons. One glimmer of hope is the in-progress Protovision Protopad, a project that intends to produce an eight-button controller (four action, two shoulder, two menu) with support for both the passive three-button protocol and a proprietary eight-button protocol, selectable by software. The active protocol uses an elegant and fast multiplexing technique compatible with the C64’s CIA bidirectional capabilities. It’s not clear if or when the hardware part of this project will come to fruition, but the Protopad Development Guide defines the protocol. It’s a fun read. (Well I think it’s fun.)
This brings us back to the topic of MEGA65 mainboard revisions. A Commodore-compatible active protocol game controller would use bidirectional communication over the joystick port data lines, and therefore would require an R6 mainboard. That is, if such a controller existed. I’m rooting for the Protopad, but in the meantime, I’m focusing on passive protocol controllers, which work with both R3 and R6 mainboards.
Modern game controllers and adapters
You can find plenty of vintage Commodore-compatible one-button 9-pin game controllers at flea markets, computer conventions, and online auction websites. Many are still in working order, and hobbyists refurbish and re-sell them. You can also get new 9-pin game controllers ranging from hand-assembled hobbyist projects to limited run Kickstarters to fully manufactured products.
There are also quite a few hobbyist electronics projects to make adapters for non-Commodore game controllers, both new and vintage, so that they work as Commodore 9-pin game controllers. A few of these implement the three-button protocol, or have a way to switch between multiple protocols. You can buy some of these pre-assembled; others are only available as schematics, and require ordering the parts separately and assembling them with a soldering iron.
The following are just a few examples of useful and interesting game controllers and adapters.
Game controllers
RetroGameBoyz gamepads. Mike of retrogameboyz.com has been making new game controllers for vintage computers since 2017. Mike offers a wide variety of styles, platforms, and configurations. My favorites are the NES-style game pads, which include 9-pin connectors, long cables, and attractive decorative faceplates. Mike even makes a MEGA65-themed game pad! This one supports the one-button protocol, and wires the second action button to “up,” which is used as “jump” in many C64 platform games. I’m working with Mike—well, I’ve asked Mike and he’s doing all the work—to produce a custom MEGA65-themed controller that supports the three-button protocol, for future MEGA65 multi-button games.
The Commotron wireless gamepad. The Commotron Gamepad Turbo 2000 Super is a fully assembled gamepad with two unique properties. For one, it’s wireless, with a dongle and a range of six meters. And for two, it supports six remappable button layouts including the an “up” button in addition to the three-button protocol. It’s currently sold out in the Commotron store, but they’re taking pre-orders for more, and third-party sellers still have some.
The Commotron powers on into a mode compatible with the three-button protocol described above, where button “1” is the primary fire button. I couldn’t find the instructions for the Commotron online, so for the benefit of future web searchers:
Buttons: directional pad (up, down, left, right), Map, Auto; and four action buttons, starting right-most going counterclockwise: 1, 2, 3, and “up.”
Button 1 is the primary fire button, for one-button games. In C64 mode, buttons 2 and 3 are POTX and POTY, respectively. The “up” button triggers the “up” direction signal, good for “jump” in one-button platform games.
The controller takes two AAA batteries. The dongle connects to the joystick port and is powered by the computer.
Both the controller and the dongle have a light on them:
Off: no power, or in standby mode.
Blinking: waiting to connect.
On: connected.
To connect, press the Auto button.
To select a preset, hold the Map button and press a direction:
Left: Commodore/ZX Spectrum mode (the default). POTX and POTY connect to +5V when active, per the three-button protocol.
Up: Amiga and Atari ST mode. These computers connect these pins to GND when active.
Right: Atari 7800 mode. Buttons 1 and 2 are “right” fire; button 3 is “left” fire.
You can swap the functions of button 1 and the “up” button. To swap, hold Map and press Down.
You can enable auto-fire to have the buttons trigger repeatedly while held down. To toggle auto-fire mode, press Auto. To change the fire rate, hold Map and press an action button:
Button 1: 3 clicks per second
Button 2: 5 clicks per second
Button 3: 8 clicks per second
Button “up”: 13 clicks per second
Neo-Atari controllers. The modern-day company that owns the Atari trademark has made a largely successful play for our nostalgia-filled hearts with recreations of vintage consoles with varying degrees of authenticity and modernity. This includes new versions of the classic 9-pin game controllers, including the CX40+ joystick, the CX30+ paddles, and the CX78+ two-button gamepad. (We didn’t get the Atari CX78 in the USA back in the day, so this is news to me!) Wireless models of the CX40+ and the CX78+ are available for pre-order, and will include both 9-pin and USB-A wireless dongles, so you can use them with emulators on a PC and with vintage and retro hardware. I haven’t tested this model of paddles yet and would expect them to support the Atari resistance range.
Hyperkin 9-pin controllers. Hyperkin makes a series of retro gaming consoles, as well as game controller peripherals for consoles old and new. Two of their controllers are compatible with Commodore and Atari computers. The Hyperkin Trooper most resembles the classic Atari CX40, with the fire button duplicated for left-handed and right-handed play. The Hyperkin Ranger is a gamepad-style controller with one fire button, and even includes a single paddle wheel in a unique design. The Ranger also supports left-handed and right-handed play with a switch. Both the Trooper and the Ranger are in stock at Amazon.com.
Note: The Ranger’s paddle wheel is useless on Commodores. The potentiometer covers the full Atari range, 0 Ω to 1 MΩ. Less than a fifth of the throw registers between 0 and 255 with the SID, worse than an Atari paddle. I can barely get a middle value to register in my joystick-paddle tester, and can’t imagine using this paddle knob for Commodore gaming.
Ultimate ArcadeR Classic. The ArcadeR is a one-button protocol 9-pin joystick built to last. It uses arcade-quality parts and is designed to be repairable and moddable, including auto-fire options. The joystick has quite a bit of travel, for better and for worse, and the switches make satisfying click sounds.
Controller adapters
Retro Rewind C64 Genesis adapter. This pre-assembled adapter converts a Sega Genesis controller to a C64/Amiga controller. It supports both the one-button + “up” button configuration and a C64GS-style two-button configuration, selectable by a jumper (a little metal piece you push onto some pins on the adapter). Another jumper changes which pin is used for the second action button: POTX for “C64” mode and POTY for “Amiga” mode. Use this with a vintage Sega Genesis controller, or combine it with the 8BitDo wireless Genesis-compatible controller for cable-free multi-button gaming.
sega-adapter. If you’re handy with a soldering iron, the sega-adapter project is a Sega Genesis controller adapter with some cool features. It supports both the one-button + “up” protocol and the three-button protocol, and you can switch between them by holding the Start button. This project was tested extensively with the 8BitDo wireless Genesis-compatible controller. The microcontroller code is open source, so you can customize it with your own features. Note the special instruction to invert the 2nd and 3rd button signals to comply with the C64 version of the three-button protocol.
mouSTer. I would put the mouSTer multi-function USB 9-pin adapter at the top of my list for lots of reasons, except they’re sold out everywhere, and not available as a DIY board either. This clever versatile device is the best way to connect a modern USB mouse to the MEGA65 or other Commodores, and it also adapts USB gamepads and joysticks with support for the three-button protocol, as well as crystalct’s 5plusbuttonsJoystick protocol. It’s highly configurable: to upload new configuration, put a configuration file on a USB stick and shove the stick into the adapter. I love it, but yeah, it’s currently unavailable.
Unijoysticle 2. This bare board project connects to both joystick ports simultaneously, and pairs with any modern Bluetooth game controller. The C64 model supports the three-button protocol and the 5plusbuttonsJoystick protocol. Normally for sale on Tindie, this one is also out of stock, though designs are available under an open hardware license so you can make your own.
SNES controller adapters. snes2c64 and PadSwitcher64 are two projects with interesting takes on multi-button support. snes2c64 adapts the six-button SNES controller to a three-button C64 controller, using a programmable Arduino Nano for custom button mapping and auto-fire options. PadSwitcher64 connects to both 9-pin joystick ports to support all six SNES buttons with a proprietary passive protocol.
I’m most excited about the possibility of the three-button protocol for MEGA65 games. It’s relatively easy to find pre-assembled controllers, easy to implement in software and modified hardware, compatible with both R3 and R6 mainboards, and is the closest thing we have to an implemented standard for multi-button Commodore-compatible game controllers. I’ll let you know what Mike and I come up with for controllers and software support. If you can get your hands on a Commotron, or are handy enough to build a sega-adapter, those are also good options.
Give joytest65 a try and let me know how it works for your game controllers.
Happy gaming!
— Dan
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
It’s a busy month! I just got back from the Vintage Computer Festival Midwest (VCFMW), the large-and-getting-larger-every-year vintage computer show. This was my first visit at this particular show, and it was great fun. I bring back a few photos, a few stories, and too much stuff from the free table.
But first, the news.
Catching up on pre-orders; mainboards now available separately
Trenz Electronic is making steady progress on shipping all of the pending MEGA65 preorders, on schedule to get everyone taken care of by the end of the calendar year. A new pre-order for a MEGA65 placed today should have a lead time of only a few months. If you placed a pre-order and have received an email with an invoice, be sure to submit payment promptly. Invoiced but unpaid orders expire within a few weeks, to allocate inventory to others waiting on a pre-order. (Thankfully, you can just re-make your expired order, and you’ll receive it fairly soon.)
With this milestone comes some exciting news! Trenz Electronic is now accepting preorders for MEGA65 mainboards without the enclosure, keyboard, or floppy drive. This is great for electronics projects, custom enclosures, or just keeping a spare. You can make your own keyboard with some effort, and the floppy drive is a standard IDE drive and cable. The standalone mainboards are expected to ship in mid-2025.
Many owners of the 2022 edition of the MEGA65 (mainboard revision R3A) asked for the ability to replace the mainboard with the latest hardware revision (R6), and it’s great that Trenz is providing this option. This is just my opinion, but if you simply want a MEGA65 with the revised hardware, you might get a better result buying a complete new computer, and selling your 2022 edition on the secondary market. The mainboard is more than half the cost of the computer, and unless you want to keep the spare R3 board, you’re unlikely to recoup that cost after an upgrade. Of course, there are no guarantees on the secondary market, but it’s worth giving it a thought. Personally, I own two MEGA65s and a spare board, a Nexys dev board—and dozens of other computers. But you might not want to follow my example.
Speaking of owning lots of computers, Trenz has lifted the one-per-person limitation on MEGA65 pre-orders. If you want to outfit a classroom, hand out MEGA65s at parties, or just make a cool fort out of the boxes, now is your chance!
Thanks as always to everyone at Trenz Electronic for their partnership on the MEGA65 project.
Filehost messaging feature
Filehost, our community file and information sharing board, has a new feature! You can now receive private notifications from the system, and send and receive private messages with other users. If you have a pending notification or incoming message, a red dot will appear on the user icon in the upper right corner.
To receive incoming notifications and messages, you must enable them: go to User menu > User Settings, check “Allow to receive messages from users” and “Inform me about comments on my entries,” then click Submit.
To access your messages: User menu > Messages.
To start a new private conversation with another user: click the plus sign next to Subjects, select the user, enter a subject, then enter the first message. Only users who have enabled incoming messages appear in the list of users.
To add to an existing conversation: select the subject from the list, then scroll to the bottom of the conversation and enter the next message.
Huge thanks to Tayger for this new feature, and all of his work on this essential community resource!
New C64 core documentation
Boris has been working on extended documentation for the C64 core. The docs are now “feature complete,” with coverage of every feature and common task, including how to install JiffyDOS or use the built-in RAM expansion unit (REU). Be sure to send Boris your feedback and thanks, via Discord or email. Thank you, Boris!
Recent Filehost uploads
A few recent Filehost uploads for you to try:
Quadratic Equation Solver, by fredrikr, an equation root finder
3D 4-in-a-row, by fredrikr, a 4x4x4 two-player tile placement game
Pelota, by SirLazarus, a single-player paddle game; requires a recent ROM beta
Hang the DJ, by mk9, a Hangman-style word game
romlister, by nobruinfo, a small utility to list which ROMs you have on your SD card
Greetings from Chicagoland!
I just flew back from Vintage Computer Festival Midwest and boy are my arms tired. Literally. I took my MEGA65, printed table displays, a bunch of backup equipment, and bundles of 45GS02 mousepads to give away. I use this weatherproof hard case when I travel with my MEGA65, and I always carry it onto the plane and put it in the overhead bin. For this trip, the less delicate stuff went into a large checked bag. Who knew 100 mousepads would be so heavy! Thankfully, everything made it there and back safely.
The event took place at the Renaissance Schaumburg Convention Center Hotel, in Schaumburg, Illinois, in the Chicagoland area. The show consumed an entire expo floor and a separate presentation space for talks. This venue is also a nice hotel, and many exhibitors and visitors stayed here or in a nearby Embassy Suites. I didn’t take enough photos to do justice to the scale of this show, but hopefully you can get a sense of it.
I prepared a bunch of table tchotchkes, more than I actually needed, including a colorful banner, an informational standee, a “demo” booklet that someone could use to launch and browse software on the SD card, and various boxed software and accessories. I didn’t have a good way to hang the banner, so it ended up being more of a tablecloth, but it still added some color. I also made business cards with a full-color photo on one side and links to resources on the other. And a custom t-shirt to wear, because why not.
Ladies and gentlemen: the Commodore 65
My table included my MEGA65, Jim Happel’s MEGA65, and two original Commodore 65 prototypes owned by Jim Brain. After having been on the MEGA65 project for two years, it was a real thrill to be able to see a C65 prototype and the MEGA65 side by side. It was also the best conversation starter: most passersby knew the C64 and never heard of the C65, so I could just point to the C65 first, then tell the rest of the story. Several people knew of the C65 and were excited to see it in person, taking selfies with the computer. I took a bunch of photos myself for future reference.
I tried to spend some time with Jim Brain’s copies of the demo disks, both on the C65 and on the MEGA65. It was a sobering reminder of how the C65 prototypes were not ready to be software platforms that the demo disks were hard-coded to specific versions of the ROM. Disks were generally not working on the C65. I could list directories but not load files. I got the IFF slideshow disk to work on the MEGA65—and of course I forgot about the topless woman until it showed up right as some young kids were walking up to the table.
One especially fun find was a message that Fred Bowen posted to the comp.sys.cbm Usenet group on September 17, 1993, subject line, “What’s a C-65???” Fred’s message puts the date of the liquidation sale around July 1993, and claims the prototypes numbered around 50. (According to Brian Bagnall’s Commodore: The Final Years, “By 1991, Commodore had at least 205 pilot production C65 machines. ‘We had a room full of them at one point,’ says [chip designer Bill] Gardei. ‘If you counted all the variants, 205 would not be an unreasonable guess.’”) In the message that follows, “Grapevine” refers to The Grapevine Group Inc. of Suffern, New York, one of two companies known to have acquired and re-sold the prototypes.
From: fred@cbmvax.cbm.commodore.com (Fred Bowen)Newsgroups: comp.sys.cbmSubject: Re: What’s a C-65???Date: 17 Sep 93 14:56:25 GMT…
Apparently Grapevine picked up some C65’s during Commodore’s liquidation of its warehouse/storage areas a couple of months ago.
What is a C65? A product that almost made it out the door. What you’re seeing are “alpha” pilot-production units. There were only something like 50 units made, and a dozen or more of these were distributed around engineering, system developers, etc. Obviously, they were never meant to be sold to the public, but…
CPU CSG65CE02, running at 3.54MHzRAM 128K, expansion to ~8MBROM 128K, including C65 mode (v10.0), C64 (v2.2) mode, & DOSVideo CSG4567 “VIC-III”I/O Built-in DMA/blitterBuilt-in 6511-type UART, supports MIDI data rate.All C64 ports: parallel, expansion, etc.new RAM expansion port, accessible by DMA and video chips.
I guess that’s enough for now. But be warned- these boards are very out of date-the ROMs, PLA’s, etc. are pre-pre-preALPHA, there are absolutely NO spare parts (so if it breaks, that’s it!), all systems are PAL (but they display okay on regular 1084-type monitors in analog mode). Oh, and there aren’t any manuals. I’m surprised that Grapevine can sell them as “new” equipment. Buyer beware, and all that.
All things considered, not a bad machine, though. I still use mine :-)
At some point during the conference, some guy I’d never met showed up at the table and started opening the C65 case. I politely but firmly discouraged him from touching it without Jim Brain’s permission. The guy smiled and introduced himself as Jim Brain. 😅 We were having some difficulty with non-functioning keys on one of the C65s, and Jim noticed a small break in the keyboard ribbon cable that will need repair. Thankfully, and amazingly, Jim has two C65s, so we temporarily swapped the keyboards to form a fully functioning unit. Jim was careful to keep track of the fact that we swapped the keyboards, so they could be reunited with their original cases later.
The C65 prototype is held together with one screw in the front-right of the case near the floppy drive. The rest of the case is all plastic tabs, to make it easy to open and repair. I got to see and photograph the internals of both machines. Developer unit #23 contained the revision 2A mainboard, and a memory expansion module.
As impressive as the C65 is as a historical artifact, I’m relieved to realize that I don’t want to own one. Not that that was ever an option, with working units selling on eBay for $30,000. A recent auction for a broken one ended north of $12,000. The prototype plastic is weird, the keyboards are mushy compared to a good condition production C128, and the working ones barely work and are difficult to maintain. I’m grateful for collectors like Jim for keeping them alive as long as possible and sharing them with the world. And I’m grateful for the MEGA65 for not only letting me access a piece of this history in my own home, but for being a satisfying instrument of recreational computing in its own right.
Reactions
Quite a few people saw the READY. prompt and wanted to type their own GOTO 10 program. Several young kids took the time to write a longer program, though they did it all from memory and didn’t bother exploring the manual. I ended up standing in front of the table and doing guided demos for most of the time. First Shot, Maniac Mansion, and Unicone attracted attention. Nobody bothered with the “demo” booklet, even with encouragement. I’m still glad I made the demo book: the full-color screenshots made it something you could flip through in seconds, and get a glimpse without having to wait for software to load.
I had stacks of the mousepads on the table. At first I was concerned that if I gave away the mousepads for free that they would disappear quickly, and wouldn’t provide much promotional value, so I had intended to maybe sell them at cost, and even made a little for-sale sign for it. I figured out within the first few hours that this wasn’t going to be a problem. I took down my for-sale sign and just arbitrarily handed mousepads to people that seemed even remotely interested in programming. At the end of the last day, I ran around the floor looking for coders I admired by reputation, and used mousepad gifts as excuses to say hi.
I had many good conversations with many people with a wide variety of interests and backgrounds, and I handed out many business cards. Greetz to all the new newsletter subscribers!
Expo highlights
Many of the Commodore exhibits were colocated together in a common section. There ended up being so much diversity in the exhibits that you almost couldn’t tell they were related by platform. I was delighted that my table was next to the brilliant CityXen, who were showing off “Whackadoodle,” their C64-powered whack-a-mole game with giant colorful light-up arcade controller, along with a miniature version with custom handheld controllers.
June of the excellent Nybbles & Bytes was also in my area, demoing and distributing her unique C128 game, Nybbles: Legend of the Drunken Snake. When you first start playing, Nybbles looks like a traditional snake game, until just moments later you eat something unusual and things get utterly wild. The game uses two displays, and manipulates both the VIC-II and the VDC in ways I have never seen before. I waited to the end of the show to buy one to give others a chance, and I did indeed end up with one of the last copies to be distributed on floppy disk. Be sure to subscribe to the Nybbles & Bytes YouTube channel for future updates and technical details about the game.
Right around the corner, I met Craig of BitBinders, maker of 1581 clone floppy disk drives. I was especially impressed by his unique dual drive units, available in both vertically and horizontally arranged variants. I bought one of the horizontal units immediately. Craig already knew about the MEGA65, and we discussed the un-produced Commodore 1565 external drive, and how a modern clone would make a great MEGA65 accessory (along with the expansion board that provides the 1565 mini-DIN connector).
There isn’t enough space to show all the photos of cool and unusual computers I saw throughout the show, but I had such a strong reaction to this one I have to include it. I’ve wanted a Heathkit Hero 1 robot kit all of my life, having only known it through advertisements in magazines. This is the first time I’ve ever seen a Hero in person. The Hero Jr., on its 40th anniversary, was sharing a table with two Androbot Topo robots.
VCFMW is as much a swap meet as a share-out, and about half of the show floor was for sale. Many exhibitors had new and vintage items for sale, and VCFMW also held an auction of unusual items donated by exhibitors to help fund the event. What really gives the convention a pervasive electric air is the ominous entity known as the Free Table. People leave stuff to give away, and other people take stuff they like, a waxing and waning of trash and treasure throughout the convention. The table is largely unsupervised, which gives the added thrill of worrying that something that isn’t free might get taken. (This has happened.) The table this year was well labeled, with a useful partition between the free table and the next table over, so it was pretty chill.
I knew I was flying home, and I’m kind of at my limit for hardware anyway, so pretty much nothing on the free table interested me. I saw some ’90s-era Microsoft software in original boxes pass across the table. My big finds were all books, where at least one person was shedding a nerd’s bonanza of famous titles on computer science and programming. I grabbed what I could carry, and left behind some of my favorite books just because I know I already owned them. I was glad to see the good ones eventually found new owners.
Of course, with the bundles of mousepads, booth decor, and equipment, my luggage was already quite full and quite heavy. The books pushed my checked bag over the weight limit, and ended up costing me a $100 heavy bag fee. So not exactly free. Still a decent haul.
I owe huge thanks to Jim Happel (jim_64 on Discord) for making my VCFMW trip possible. Jim encouraged me to make the trek in the first place, and provided logistical, technical, and moral support throughout the experience. Jim also introduced me to many excellent people in the community whom I’ve known only by reputation online and I now consider friends. This was such a good time, I’m eager to visit the other annual shows around the country as time and budget allows, with my MEGA65 in its carrying case.
Thanks to everyone for your kind words and feedback on EasyAsm v0.1. I’ve only received a couple of bug reports—which just means you’re not trying hard enough to break it! I hope to start a revision as soon as I’m through this very busy month. I’m working on the draft of this year’s community survey, I’m dropping off a kid at college, and I still have the Portland Retro Gaming Expo in a couple of weeks.
Special thanks to all of my supporters, who are making my promotional tours and other projects possible. If you like what I’m doing, please consider becoming a supporter. Visit: ko-fi.com/dddaaannn
Cheers!
— Dan
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
There’s tons of new stuff for you to download and try this month! kibo has launched a new implementation of a classic graphic adventure game engine capable of playing Maniac Mansion. The group Megastyle has released several new MEGA65 titles. And I’m launching a project of my own: an on-device assembly language programming tool for the MEGA65, called EasyAsm.
Much to discuss, let’s get started!
SCUMM v2 player, by kibo
You can now play the LucasArts graphic adventure-comedy game Maniac Mansion directly on your MEGA65!
kibo made a new implementation of the SCUMM v2 game engine for the MEGA65 capable of playing the classic game, which he calls "MegaSPUTM." This hugely impressive project uses the game data files from the Amiga version, and supports full color graphics, digitized sound, and multiple input devices.
To play Maniac Mansion, you need to acquire the original game data files. If you own the disks, you can use Amiga disk management tools to extract the .LFL files. MegaSPUTM works with both the English and German versions of the data files.
The full procedure takes a few minutes. I got it up and running like so:
Download kibo’s SCUMM v2 engine from Filehost. Expand the .zip archive to produce D81 disk image files mm1.d81 and mm2.d81.
Retrieve the .LFL files from the rooms/ folder of the first disk of Maniac Mansion for the Amiga. If your media is in the form of an ADF disk image, there are several tools you can use. I found this cute browser-based ADF reader, which does everything locally on your computer and doesn’t require uploading data to a remote server. It’s a bit obnoxious that you have to select and “download” each .LFL file individually, but it works, and doesn’t require installing anything.
Use a D81 disk image tool to place the .LFL files from the first ADF onto mm1.d81. Here too you have your choice of tools. I like droid64, a Java UI application. If you use DirMaster and possibly some others, you may need to rename the files to use lowercase .lfl before adding them to the disk. (droid64 will lowercase it in transit.)
Repeat steps 2 and 3 for the 2nd game disk and mm2.d81.
Transfer mm1.d81 and mm2.d81 to the root of your MEGA65’s SD card.
Now on the MEGA65, MOUNT and BOOT the mm1.d81 disk image. Crank those speakers, and use a joystick or a mouse!
The original Maniac Mansion game included a printed booklet entitled “Nuke’m Alarms Owner’s Disarmament Quick Reference Guide,” which was needed early in the game. Similar to SCUMM VM and Disney's own re-release of Maniac Mansion, this is not needed when playing the game with MegaSPUTM.
C programmers, don’t miss the source code, which is chock full of examples of how to do common and interesting things on the MEGA65 in C.
New titles from Megastyle
The group Megastyle has not one, not two, but three releases for the MEGA65 this month!
Mega Invaders (itch.io link), an homage to Space Invaders, is a MEGA65 original with all of the nostalgic arcade game graphics and sounds you crave. Be sure to switch to PAL video mode before running the game. Joystick in port 2.
Omega Race (itch.io link) is a direct port of the VIC-20 space war game to the MEGA65. Nearly all of the VIC-20 code is intact, with only small patches required to get it to run.
In Skramble, also a VIC-20 port, you fly over a landscape, destroying enemies. Check out the blog article about the original game for the impressive technical details of how an unexpanded VIC could render such large levels.
Don’t miss all the great C64 and Amiga titles in Megastyle’s catalog. Their Empire Strikes Back remake is one of my all-time C64 faves.
Snake65, by piramania
Snake65 by piramania is a modern take on a classic. It features six levels of challenging snake gameplay and charming music, and is written entirely in BASIC 65. Use W, A, S, and D on the keyboard as controls.
Mondrian Simulator, by Drex
Mondrian Simulator, by Drex, is a MEGA65 demo that generates artwork in the style of abstract artist Piet Mondrian. Press S to start a meditative slideshow.
MEGA65 Command Line Tools v1.0
The MEGA65 project includes a collection of command-line tools for development and testing, using a JTAG adapter to remotely control a MEGA65 from a PC. These tools have recently acquired new features for transferring files, ROMs, and programs over Ethernet, as well as a revised tool for core management. The M65Connect desktop application uses these tools behind the scenes, and I use these tools regularly as part of a cross-development workflow.
You can now download a formal release package of the latest command-line toolkit, version 1.0. Get it for Windows, macOS, and Linux. The tools in this package:
m65 : A multi-tool for testing, debugging, and interacting with the MEGA65 over a JTAG/UART serial adapter.
mega65_ftp : Transfer files to and from the MEGA65 SD card, via Ethernet or JTAG/UART.
etherload : Send programs and ROMs to the MEGA65’s memory over Ethernet for testing.
coretool : Convert MEGA65 bitstreams to cores, and manage core metadata. Requires Python 3.
romdiff : A binary patch generator, used for producing ROM patches.
Mac users, remember to remove the “quarantine” attribute of the tools before using them: xattr -d com.apple.quarantine m65.osx
New list of alternate cores
Looking for alternate cores available for the MEGA65? Boris Schneider-Johne has taken over maintaining the cores list from sy2002, and it now has an easy-to-remember address. Visit cores.mega65.org to be redirected to the new list. Thanks to Boris for preparing and maintaining this documentation!
September conferences
A quick update on those September conferences in the USA: I still plan to be at the Vintage Computer Festival Midwest, September 7-8 in Schaumburg, Illinois. For the first time in the show’s history, VCFMW received more talk proposals than available slots, and my proposal was not selected this year. I’m grateful that they have a presentation track at all, with a full-time volunteer staff to make quality recordings for YouTube. I have confirmed that we’ll have a table—and we might even have a real Commodore 65 next to the MEGA65!
I splurged on getting some fun booth display items printed professionally, something I’ve always wanted to try. The cloth banner and table signage turned out very nicely. I also placed a bulk order of 45GS02 CPU quick reference mousepads.
I, my MEGA65, and my mousepads will also be at the Portland Retro Gaming Expo, September 28-29 in Portland, Oregon. Come say hi, if you can!
Introducing EasyAsm
EasyAsm is a new assembly language programming tool for the MEGA65. EasyAsm runs entirely on the computer, with no need for a separate PC. It is designed to work with the existing MEGA65 environment, to give you as much control and understanding of the computer as possible, and to take advantage of the MEGA65’s existing programming workflow. EasyAsm provides a powerful subset of the syntax and features of the Acme cross-assembler, so that you can use it with the assembly language programming examples in the documentation and in this Digest, as well as for larger programs.
Download EasyAsm from Filehost.
See the Github repo for full documentation and source code, and to file bug reports and feature requests.
Note: Assembling to disk requires a recent ROM beta version, 920401 or later. This will also work in the upcoming v0.97 MEGA65 platform release.
I was inspired to write EasyAsm while playing with vintage on-device assemblers for the Commodore 64. Many of these used the C64’s built-in BASIC line editor for entering assembly language source code, giving the programmer a familiar environment for managing code without the overhead of a proprietary text editor application or file management system. I realized that not only was this possible on the MEGA65, the MEGA65 could do it better and more intuitively thanks to its powerful memory management architecture, and a little-known feature of the BASIC: Edit mode.
An important note
Save your work to disk, early and often.
Writing a program for a microcomputer using the microcomputer itself comes with the inherent risk that a bug in your program will interfere with your programming environment. EasyAsm preserves your source code in memory while you are testing your program, but this cannot be guaranteed to work if the program does something unexpected.
By design, EasyAsm does not force you to save your work to disk before testing your program. Please remember to do this yourself.
With this announcement, I am releasing the first version of EasyAsm, which I’m calling “version 0.1.” I have tested it to the best of my ability, but it may still have bugs, including bugs that may lose data. If you find any bugs, please report them!
A canonical first program
To start an EasyAsm session, mount and boot the EASYASM.D81 disk image:
MOUNT "EASYASM.D81"BOOT
Try entering the following program at the OK prompt. To enter the blank line on line 20, type 20 then press Shift + Space, then press Return.
10 !TO "SIMPLE", RUNNABLE2030 INC $D02040 RTS
Type LIST to see the listing.
Before doing anything else, save this source file to disk:
DSAVE "SIMPLE.S"
Next, press the Help key to open the EasyAsm menu.
Select option #1: Assemble and Test. (Press the 1 key.) EasyAsm assembles the program, then runs it. In this case, the program you entered does two things: it changes the color of the border (inc $d020), then ends the program (rts).
Type LIST again. The source code is in memory, and you can continue to make changes to the program.
Let’s make a small improvement. The register at hexadecimal address $D020 controls the border color, but this is not obvious from the program listing. It’d be better to assign a label to this value, so it’s easier to see what’s going on. Insert the label definition with line 25, and replace line 30:
25 BORDER = $D02030 INC BORDER
Save the source file to disk again, this time using the @ symbol to tell DSAVE to replace the existing file.
DSAVE "@SIMPLE.S"
Press Help then 1 to assemble and run the program again, to confirm that it still works.
Using Edit mode
Edit mode is very similar to the usual BASIC mode. You can type any BASIC command at the prompt, and it’ll do it right away. The main difference is that when you type a numbered line, instead of treating it as BASIC code, Edit mode treats it as PETSCII text. The DSAVE and DLOAD commands also have special behavior in Edit mode, operating on sequential files (type SEQ) instead of program files (type PRG). You can use Edit mode to write any kind of text file, like a (very) simple word processor. With EasyAsm, you can use it to write assembly language programs.
Editing works just like in BASIC. When you enter a new line, the program updates based on the line number, either inserting the line in sequence or replacing an existing line of that number. To delete a line, enter just the number, then press Return. All of the built-in tools for line editing, such as the AUTO and RENUMBER commands, are available in Edit mode. See the User’s Guide for information about these commands.
Type DIR to see the list of files on the disk. Notice that the SIMPLE.S file you created is of type SEQ.
Type NEW to clear program memory. Type LIST again to confirm that the source code is no longer in memory. Then re-load the source file:
DLOAD "SIMPLE.S"
Type LIST. The source file is the same—but the line numbers have changed!
1000 !TO "SIMPLE", RUNNABLE10101020 BORDER = $D0201030 INC BORDER1040 RTS
This is a notable difference between Edit mode and BASIC mode: Edit mode does not include the line numbers when it saves the file. This is usually what you want when editing a text file, and for EasyAsm, the line numbers don’t affect how the program is assembled. However, it does mean that if you try to organize your code using groups of line numbers like you might in a large BASIC program, that organization effort is not saved between sessions.
You’ll want to get familiar with the RENUMBER command, which can insert a range of unused line numbers in the middle of your source code. For example, to renumber all lines from 1030 onward so that they start at number 2000 counting by 10s, thereby leaving a gap of available line numbers between 1030 and 2000:
RENUMBER 2000,10,1030-
EasyAsm enables Edit mode automatically when you boot the disk. To disable Edit mode and return to BASIC mode, use this command: EDIT OFF To re-enable Edit mode: EDIT ON
Remember that when you're in BASIC mode, the prompt says "READY," and when you're in Edit mode, the prompt says "OK." Take care to notice which mode you are in before typing in lines of a program, and before loading or saving source files. If you type a line of assembly language source while in BASIC mode, it will attempt to interpret the line as BASIC code, and produce incorrect results. And if you try to DSAVE the source file while in BASIC mode, it’ll save as a PRG file.
Assembling to disk
With the SIMPLE program’s source code in memory, press Help to open the EasyAsm menu. Now select option #2: Assemble to Disk. EasyAsm assembles the program, but instead of running it, it creates a new PRG file on disk, named "SIMPLE". You specify the name of the program file using the !to directive in the code.
10 !TO "SIMPLE", RUNNABLE
Type DIR to see the SIMPLE program file of type PRG alongside the SIMPLE.S source file of type SEQ.
I like to use a .S suffix for source code filenames, then omit the suffix for the program name. Take care to not use the same name for the source file and the program file, so that EasyAsm does not overwrite your source file with the program file.
Let’s try out the new program. Switch back to BASIC mode, then load and run the SIMPLE program, like any other program:
EDIT OFFDLOAD "SIMPLE"RUN
The program changes the border color, then exits.
In a previous Digest, we used the Acme cross-assembler to make a program like this, using a lengthy preamble to produce a runnable program:
!cpu m65!to "hello.prg", cbm* = $2001!8 $12,$20,$0a,$00,$fe,$02,$20,$30,$3a,$9e,$20!pet "$2014"!8 $00,$00,$00inc $d020rts
This would work in EasyAsm just as well: EasyAsm supports the cbm output type similar to Acme. But EasyAsm has a feature that makes this easier. When you tell the !to directive to make a runnable file, EasyAsm generates this preamble for you, including setting the program counter. In fact, this cbm listing and the runnable version you typed produce exactly the same output.
The PRG file contains your program and only your program. Give it to your friends so they too can change the colors of their borders!
How to stop a running program
When you assemble-and-test your assembly language program, EasyAsm attempts to recreate the memory layout that your program will see when it runs from disk. To do this, it first copies your source code out of program memory, so it can replace it with the assembled program for testing. EasyAsm watches for the program to exit using the rts instruction, then copies the source file back into program memory so you can continue to edit it.
That’s nice, but it’s not always possible—or even desired. A typical machine code program never exits. A broken machine code program might get stuck before it can exit.
Try entering a slightly different program:
10 !TO "FOREVER", RUNNABLE2030 LOOP:40 INC $D02050 JMP LOOP
Assemble and run the program. This program changes the border color repeatedly, in an infinite loop. It does not exit.
To interrupt this program and return to BASIC, hold Run/Stop and press Restore. The program stops, and the MEGA65 Monitor starts. Type X then press Return to exit the Monitor and return to the OK prompt.
Now type LIST.
Uh oh, there’s something wrong with the first line! EasyAsm did not see the program exit with the rts instruction, so it didn’t get a chance to restore the source file. The assembled instructions are still in program memory, having overwritten the source.
When this happens—and it will happen often—use menu option #9 to tell EasyAsm to bring back the source file. Press Help, then press 9. Type LIST again to see the restored source.
How EasyAsm uses memory
EasyAsm tries to maintain a minimal memory footprint while you are editing your source code, and while your program is running. This allows you to use all the tools at your disposal for editing, and allows your program to use most of the computer, while still retaining a useful on-device workflow.
Of course, EasyAsm has to live somewhere. This is what EasyAsm needs:
EasyAsm reserves the memory ranges $1E00-$1EFF (256 bytes of bank 0) and $8700000-$87FFFFF (1 megabyte of Attic RAM). If your program overwrites any of this memory, you will need to reload EasyAsm, and any stashed source code data may not be recoverable.
EasyAsm reserves the right to overwrite $50000-$5FFFF (all 64KB of bank 5) when you invoke EasyAsm. Your program can use this memory while it is running, but the state of this memory may change when EasyAsm is running. Overwriting this memory may inhibit EasyAsm’s ability to return to the OK prompt on rts.
EasyAsm will refuse to assemble to addresses $1E00-$1EFF when assembling to memory, or when assembling a runnable program to disk (because the bootstrap routine may use it in the future). This restriction does not apply when assembling to disk in cbm mode.
If your program needs to use $1E00-$1EFF for its own purposes, that’s entirely fine. Just be aware that it will interfere with the EasyAsm workflow.
What’s next for EasyAsm?
I’m fairly confident that I could have used this initial version of EasyAsm to write many of the programs I’ve written for the MEGA65 so far. There are a few features I want to add to really make it feel like a complete assembler:
!binary: embed a file of binary data into a program (such as graphics or sound assets)
!source: use multiple source files to describe a program (such as with reusable libraries), and to produce programs larger than Edit mode’s 44 KB memory limit for a single source file
Inspect an annotated source listing with program counter and byte values next to the original source lines (Acme’s “report” feature)
Inspect a list of label definitions (Acme’s “symbol list” feature)
I have a roadmap of other feature ideas that I’m considering, though they’ll take more substantial work and I want to see if anyone actually uses EasyAsm first. 😇 To me, the most important of these would be macros, a feature of Acme and other assemblers that makes programs easier to write, test, and manage. It's possible to write substantial programs without macros—the MEGA65 ROM source has no macros in it at all—but macros are a powerful way to organize code, reuse common structures, and mitigate some of the fragility of the low-level language.
As clever as Edit mode is, it would be nice to have a more modern IDE-like text editor as an option. I have plenty of ideas and opinions, and would love to try implementing them. That said, I intentionally designed EasyAsm so that it could be integrated into other projects, so someone else could implement supplemental tools.
I wrote EasyAsm from scratch in pure assembly language. I learned a lot about assembly language from this experience, and will be milking this for content writing about this in future episodes of the Digest.
I included a few sample programs on the D81 disk image based on previous Digest and blog articles. Try loading their source files and assembling them. The longest file on the disk right now is my assembly language implementation of Robot Finds Kitten, 35 KB of source that assembles in six seconds.
Want to see continued development of EasyAsm, or my other projects like this Digest? Please consider becoming a patron. Visit: ko-fi.com/dddaaannn
I hope you try EasyAsm, and I hope it makes it easier to get started with assembly language programming. Let me know how it goes!
— Dan
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
I feel like painting today. Let’s paint!
Oh, uh, but first…
The Silent Enigma
The Silent Enigma is a new demo by Gurce, based on the song by Anathema. Gurce wrote the demo in BASIC 65 with the Eleven IDE, with assembly language helper routines in Mega Assembler and Acme, and an extended version of grim-fandango’s MEGAPLOT library.
The current version of the demo requires customized versions of the MEGA65 core and ROM to run on MEGA65 hardware. Gurce discovered a need for a new feature of the KERNAL for combining BASIC and machine code routines, and also discovered a core bug while getting it to work. We’re working on getting these improvements into the core platform. In the meantime, Gurce is distributing modified core and ROM files with the demo.
Check out retroComb’s video debuting The Silent Enigma, along with an interview with Gurce. And don’t miss the Eleven and assembly language source files included on the disk!
My talk at PaCommEx NW is online
My talk about the MEGA65 at Pacific Commodore Expo Northwest 2024, Lessons from My First Two Years with the MEGA65, is now on YouTube. My thanks to Robert Bernardo for producing the event, to Stephen Jones and SDF.org for sponsoring, and to everyone who attended. We had three MEGA65s on the floor this year, including a live unboxing of a freshly delivered unit!
C64 core v5.1 released
The C64 core for the MEGA65, one of the best things you can do for your favorite computer, has a new update. Version 5.1 adds support for the R6 mainboard, the board in all MEGA65s being delivered this year, and can take advantage of the R6 board’s bidirectional expansion port lines for Kung Fu Flash, MSSIAH, and freezer cartridge support. This update also includes improvements for all mainboards, including the ability to share the MEGA65’s Real-Time Clock with GEOS, using an appropriate driver.
Don’t forget that the MEGA65 has a new feature that can select the C64 core automatically when a C64 cartridge is installed. If your MEGA65 was delivered this year, your firmware is already up to date with this feature. If you have an older MEGA65, you can update to the v0.96 release in “slot 0” to get this feature. See the User’s Guide, 2nd edition, for upgrade instructions.
See the C64 core release notes for details on all of the changes.
retroCombs MEGA65 Video User’s Guide
retroCombs is producing a video series based on the MEGA65 User’s Guide, 2nd edition. Steven is going chapter by chapter, reviewing the material, and presenting it in an entertaining and easy-to-understand fashion. It’s the perfect video companion to the Guide, and a useful resource in its own right, with additional tips not mentioned in the book. Steven just released the video for chapter 4, and intends to complete the series for the entire book.
Subscribe to retroCombs to make sure you don’t miss a video!
Canvas
I need a canvas to paint on. Something 320 pixels wide and 200 pixels tall is good enough to start. I just want to use the 32 built-in colors for now, so a bit depth of 5 is plenty. (Five binary bits can express up to 32 values: 2 x 2 x 2 x 2 x 2 = 32.) I’ll use screen number 0.
SCREEN 0,320,200,5
Yes, that’s fine. I can’t see what I’m typing now because the text is behind the painting, but I can hold Run/Stop and tap Restore to get back to the text screen.
Blank canvases are intimidating. Whenever I buy a new notebook, I always scribble nonsense on the first page, so I’m not afraid to make mistakes on the other pages. I’ll take a swipe with some white paint (color 1).
PEN 1LINE 20,30,300,180
I can specify locations on the canvas using numbers, one for the distance in pixels across from the left (the X coordinate), and one for the distance down from the top (the Y coordinate). With a 320 x 200 screen, coordinate X=0 Y=0 is the top-left corner, and X=319 Y=199 is the bottom-right corner. I drew my line from the position X=20 Y=30 to X=300 Y=180.
The 32 colors of the built-in palette are listed in the MEGA65 User’s Guide, 2nd edition, appendix E. The first 16 of these are also written on the fronts of the number keys on the keyboard from 1 to 8, in two rows: colors 0 through 7 are the top row (“Blk” through “Yel”), and colors 8 through 15 are the bottom row (“Orng” through “L Gry”).
I need to switch back to the canvas to see my white line. I’ll set my screen 0 to be both the canvas I’m drawing on and the canvas I’m looking at.
SCREEN SET 0,0
I notice that typing SCREEN 0,320,200,5 again will not switch back to the existing canvas, it’ll open a new blank one. Here’s a way to clear an already-opened canvas, by filling it with color #6 (blue):
SCREEN CLR 6
Flipping back and forth between the text screen and the canvas is a bit cumbersome. I think I’ll put all of my painting commands into a BASIC program. This way, the MEGA65 can recreate my picture by following my painting instructions, and I can save my program to a floppy disk.
10 SCREEN 0,320,200,520 PEN 130 LINE 20,30,300,170DSAVE "LINE"RUN
I can press Run/Stop + Restore to get back to text mode, make changes to the program, and RUN it again to see the result.
If I wanted to give this program to someone else, they might enjoy being able to exit back to text mode by pressing a key. Here’s a simple way to do that:
9998 GETKEY A$9999 SCREEN CLOSE 0
In addition to lines, BASIC has commands for drawing dots, and drawing shapes of several kinds, both filled and unfilled. I’m just messing around, changing the numbers to see how they change the picture.
40 PEN 250 DOT 4,460 DOT 5,570 DOT 6,480 DOT 7,590 DOT 8,4100 PEN 3110 BOX 180,10,240,50120 BOX 200,30,260,70,1130 PEN 4140 CIRCLE 50,150,24150 CIRCLE 70,130,24,1160 PEN 5170 ELLIPSE 180,150,50,20180 ELLIPSE 160,130,50,20,1190 PEN 6200 POLYGON 250,100,40,40,5210 POLYGON 270,120,40,40,5,,,,1
Of course, the User’s Guide has more information on how these commands work, including some additional interesting features.
Here is a simple picture I made.
5 BORDER 010 SCREEN 0,320,200,520 PEN 27:BOX 0,0,319,30,130 PEN 28:BOX 0,31,319,60,140 PEN 29:BOX 0,61,319,90,150 PEN 30:BOX 0,91,319,120,160 PEN 0:CIRCLE 220,280,200,170 BOX 200,60,240,60,230,90,210,90,180 BOX 200,48,240,48,250,60,190,60,190 BOX 210,42,213,42,213,48,210,48,1100 PEN 7:BOX 222,65,227,65,226,72,223,72,1110 PEN 11:CIRCLE 213,36,4,1120 CIRCLE 223,33,3,1130 CIRCLE 232,31,4,1
Computer art
Wherever there’s a number in a computer program, there could also be some computation that produces a number. This can turn math formulas, variables, and flow control structures into their own kind of computational paintbrush.
I want to try a simple grid pattern with regularly spaced lines. Instead of writing out all of the coordinates for each line, I’ll have the MEGA65 figure it out for me.
5 BORDER 010 SCREEN 0,320,200,520 PEN 130 FOR X=0 TO 319 STEP 2040 LINE X,0,X,19950 NEXT X60 FOR Y=0 TO 199 STEP 2070 LINE 0,Y,319,Y80 NEXT Y
I can tell the computer how to calculate a number. Or, I can just let the computer pick a number at random. The computer can use its own imagination to paint a picture, in accordance with my instructions. (well actually computers don’t have imaginations and random numbers aren’t really random and whatever it’s fun)
5 BORDER 010 SCREEN 0,320,200,520 PEN RND(1)3230 LINE RND(1)320,RND(1)200,RND(1)320,RND(1)*20040 GOTO 20
BASIC 65 has some mathematical functions that can help paint interesting pictures, if I can remember enough geometry.
5 BORDER 010 SCREEN 0,320,200,520 FOR Z=0 TO 430 PEN RND(1)3240 R=RND(1)40+2050 CX=RND(1)200+6060 CY=RND(1)80+6070 FOR A=0 TO 2π STEP 2π/3080 LINE CX,CY,CX+COS(A)R,CY+SIN(A)R90 NEXT A100 NEXT Z
Those symbols on line 70 are the Greek letter pi (π), which I typed with Shift + up-arrow. In BASIC 65, this is the geometric constant pi, defined as the circumference of a circle divided by its diameter. Someone check my math.
Graphing calculator
I keep my MEGA65 on my desk and sometimes just use it as a simple calculator, typing a question mark ? followed by an arithmetic expression at the READY. prompt to get a quick answer. I wonder if I could make the MEGA65 into a graphing calculator.
1 DEF FN Y(X) = SIN(X9)+1.52 XL = -3 : XH = 33 YL = -1 : YH = 44 REM ======================5 BORDER 010 SCREEN 0,320,200,520 REM ==== DRAW AXES30 PEN 1240 IF 0 < XL OR XH < 0 THEN 7050 V = 0:L = XL:H = XH:SC = 320:GOSUB 1000:XC = C60 LINE XC,0,XC,19970 IF 0 < YL OR YH < 0 THEN 10080 V = 0:L = YL:H = YH:SC = 200:GOSUB 1000:YC = 199-C90 LINE 0,YC,319,YC94 IF XC > 351 THEN XC = 35195 IF YC > 192 THEN YC = 192100 CHAR 0,YC+1,1,1,2,STR$(XL)110 CHAR 39-LEN(STR$(XH)),YC+1,1,1,2,STR$(XH)120 CHAR XC/8+1,0,1,1,2,STR$(YH)130 CHAR XC/8+1,191,1,1,2,STR$(YL)140 REM ==== DRAW FUNCTION150 PEN 7160 PX = 0170 V = FN Y(XL):L = YL:H = YH:SC = 200:GOSUB 1000:PY = SC-C180 FOR X = XL TO XH STEP (XH-XL)/160190 V = X:L = XL:H = XH:SC = 320:GOSUB 1000:NX = C200 V = FN Y(X):L = YL:H = YH:SC = 200:GOSUB 1000:NY = SC-C210 LINE PX,PY,NX,NY220 PX = NX : PY = NY230 NEXT X999 END1000 REM ==== SCALE V TO C WITHIN RANGE (L,H) BY SC1010 C = (V-L)/(H-L)SC1020 IF C > SC THEN C = SC1030 IF C < 0 THEN C = 01040 RETURN
Huh… That’s interesting…
I think I got the gist of this right. The first few lines define the function to be plotted, and the domain (XL to XH) and range (YL to YH) to use for the graph.
The subroutine starting on line 1000 scales a value V to screen coordinate C within value range (L,H) scaled by SC. For example, to convert an X value to a horizontal screen coordinate, I set V to the X value, L to XL, H to XH, and SC to 320 (the pixel width of the screen). The subroutine returns with C set to the horizontal screen coordinate.
190 V = X:L = XL:H = XH:SC = 320:GOSUB 1000:NX = C
I use this subroutine for every such conversion. Importantly, I need to invert the vertical coordinate that comes back from this, because math graphs go up as Y values increase, but screen coordinates go down. So PX = C, and PY = SC-C.
To keep this simple, I only draw a zero-axis if the zero is within the range/domain, and I don’t bother with tick marks. I use the CHAR command to print the range/domain values close to the zero-axis lines, if appropriate, making sure they stay on screen.
To draw the function itself, I calculate a starting point based on its value for the leftmost point on the screen, FN Y(XL), then loop over 160 evenly spaced points between XL and XH. For each iteration, I draw a line from the previous point to the next. PX/NX and PY/NY are the previous and next horizontal and vertical screen coordinates.
Real graphing calculators are pretty sophisticated. They need to be to produce something useful under all circumstances. For example, I just allow a graph that goes off the top or bottom of the screen to squash up against the edge. The LINE command doesn’t mind receiving fractional coordinates, but it will complain if I give it values outside of the screen. This could be improved by calculating exactly where the graph crosses the edge. But I’m painting here, not building something useful.
Also my sine graphs have a bit of a cowlick in some spots, for some reason. I’m guessing this is hitting up against the range limits of BASIC 65 floating point numbers. Math is hard. 🤷 Moving on…
Paintbrush
Painting with code is fun, but this computer can do more than use hand-entered numbers or numbers derived from math and randomness. I can also write a program that changes numbers interactively based on keys I press on the keyboard. Here’s the simplest version of this idea:
10 SCREEN 0,320,200,520 X=100:Y=10030 PEN 140 DOT X,Y50 GETKEY A$60 IF A$=CHR$(17) AND Y<199 THEN Y=Y+170 IF A$=CHR$(29) AND X<319 THEN X=X+180 IF A$=CHR$(145) AND Y>0 THEN Y=Y-190 IF A$=CHR$(157) AND X>0 THEN X=X-1100 GOTO 40
This operates similarly to our Robot Finds Kitten experiment from a while back. The program maintains a cursor position in the X and Y variables. It waits for a keypress, then compares it to the PETSCII codes for the cursor keys. After it confirms the cursor position won’t leave the screen, it changes the cursor position, then draws a dot. I can move the dot around the screen, and it leaves a trail, like an Etch-a-Sketch.
I could also make this controllable with a joystick, with almost no changes:
10 SCREEN 0,320,200,520 X=100:Y=10030 PEN 140 J=JOY(2)50 DOT X,Y60 IF J=5 AND Y<199 THEN Y=Y+170 IF J=3 AND X<319 THEN X=X+180 IF J=1 AND Y>0 THEN Y=Y-190 IF J=7 AND X>0 THEN X=X-1100 GOTO 40
Whoa! Uh, slow down there, partner:
55 SLEEP 0.01
This only supports four directions of movement, and actually feels like it sticks when I accidentally press the joystick in a diagonal direction. The JOY() function returns different values for diagonals. (See the User’s Guide.) This should work.
60 IF (J=4 OR J=5 OR J=6) AND Y<199 THEN Y=Y+170 IF (J=2 OR J=3 OR J=4) AND X<319 THEN X=X+180 IF (J=8 OR J=1 OR J=2) AND Y>0 THEN Y=Y-190 IF (J=8 OR J=7 OR J=6) AND X>0 THEN X=X-1
It’d be nice if I had more control over the paintbrush behavior. Maybe if it doesn’t draw unless I press the button?
50 IF J AND 128 THEN DOT X,Y:J=J AND 127
JOY() returns a value 0 through 8 representing the joystick direction, plus 128 if the fire button is pressed. I’m using a bitwise AND both to test the button on its own, and to erase the button part of the value for the rest of the comparisons.
OK that works, but now I can’t really see what I’m doing. Before, it was always drawing, so I could usually see where it will draw next when I move. Now it’s only drawing when the button is pressed. I need some kind of cursor on the display that doesn’t draw into the image itself.
On Commodores, that’s what sprites are for. We covered sprites, yeah? I’m just going to use the built-in arrow sprite for now:
15 SPRITE 0,145 MOVSPR 0,X+24,Y+50
Remember that the sprite coordinate system is not the same as the screen coordinate system inside the border. The upper corner for a sprite is X=24, Y=50, so I need to add these values to the MOVSPR command.
That’s pretty good… Looks like there might be a bug in sprite coordinates over bitplanes. We’ll have to look into that.
That arrow cursor reminds me, I could also write this to use the mouse. This simplifies the code quite a bit because the KERNAL does its own range checking on the mouse position. My program just needs to read the mouse position, check for the button press, and draw a dot there if it is pressed. I still need to offset the mouse coordinates when I draw, this time in the other direction because X and Y are set to sprite coordinates by the RMOUSE command.
10 SCREEN 0,320,200,520 MOUSE ON,130 PEN 140 RMOUSE X,Y,B50 IF B THEN DOT X-24,Y-5060 GOTO 40
That’s neat! The dots are a bit spread apart though. I wonder if I can combine this with the technique in the graphing calculator to get a smoother line while the button is pressed.
10 SCREEN 0,320,200,520 MOUSE ON,130 PEN 140 PX=-150 RMOUSE X,Y,B60 IF B<>128 THEN PX=-1:GOTO 5070 X=X-24:Y=Y-5080 IF PX=-1 THEN 10090 LINE PX,PY,X,Y100 PX=X:PY=Y110 GOTO 50
Hmmm…
41 SPRITE 0,1,151 GET A$:IF A$<"1" OR A$>"9" THEN 6052 PEN VAL(A$)53 SPRITE 0,1,VAL(A$)
Maybe…
90 CIRCLE X+RND(1)10-5,Y+RND(1)10-5,RND(1)*5,1
And what about…
42 SC=161 SC=SC-162 IF SC>0 THEN 7063 SOUND 1,RND(1)*65535,564 SC=50
Oh, here’s an idea…
90 LINE PX,PY,X,Y91 LINE 320-PX,PY,320-X,Y92 LINE PX,200-PY,X,200-Y93 LINE 320-PX,200-PY,320-X,200-Y
That was fun!
Did I forget to include screenshots of my paintings? Ah, well. I suppose you can always just type these in and see what happens.
If you liked this one, please consider supporting the Digest with a contribution. I hope to continue making more of these, and your support goes a long way to making that possible. Visit: ko-fi.com/dddaaannn
Happy painting! See you next month.
— Dan
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
The Summer of MEGA65 begins! The latest delivery batch is in progress, and many preorder holders are receiving their new favorite computer.
If you’re new to this Digest, welcome! Here you’ll find news about the MEGA65 and community projects, and interactive feature articles of things you can try for yourself. Read the Digest while next to your MEGA65 and PC for the best experience.
Also, every Digest has a read-aloud audio edition. Click the audio player at the top of the email or website, or subscribe to “Dan’s MEGA65 Digest” in your podcast player. I don’t know how many people listen to it, but I enjoy making it.
In this Digest, we’ll start taking a look at the MEGA65 KERNAL, the main operating system of the computer. We’ll build off of last month’s discussion of interrupts and the CPU memory map, and try writing a MEGA65 version of a classic KERNAL extension: a desktop time-of-day clock.
Let’s get started!
Batch #3 is arriving!
MEGA65 home computers are now arriving with their new owners! Trenz Electronic has begun sending out the third manufacturing batch, and will continue to fulfill pre-orders steadily. Congrats and welcome to everyone receiving a new bundle of joy!
Back in January, we were able to confirm with Trenz that this manufacturing batch will be large enough to cover all preorders placed up to that point. I continue to use that as a conservative estimate. New preorders placed in the last few months may need to wait a bit longer—or maybe not. For all we know, Trenz may be able to make quick work of another batch and get everyone taken care of. Rest assured that everyone involved in this project wants you to have a MEGA65 as soon as possible.
Of course, you can still order the MEGA65 if you haven’t already. Tell your friends!
MEGA65 at the Pacific Commodore Expo Northwest, June 22-23, 2024
If you’re near Seattle, Washington, USA this month, I will be presenting the MEGA65 at the Pacific Commodore Expo Northwest, June 22-23, 2024. Admission is free. The space is cozy and filled with Commodores, and this year we have access to additional space for presentations. I’ll have my MEGA65 at a table all weekend for people to try.
I gave a talk on the MEGA65 at last year’s PaCommEx NW that went reasonably well, despite being hastily planned. Here’s hoping that I’ll have this year’s talk figured out in time. 😬
MEGA65 at the Vintage Computer Festival Midwest, September 7-8, 2024
I’m working up plans to be at the Vintage Computer Festival Midwest, September 7-8, 2024, at the Schaumburg Convention Center in Schaumburg, Illinois near Chicago. This large show attracts enthusiasts from all over the USA and Canada, and is a regular pilgrimage for collectors, computer clubs, YouTubers, and makers of modern retro computers and peripherals. Admission is free.
This year, I’ll be collaborating with Jim Happel (jim_64) on a MEGA65 table. I’ve also applied for a speaking slot, though those won’t be announced until next month, so, fingers crossed. 🤞
MEGA65 at the Portland Retro Gaming Expo, September 27-29, 2024
My MEGA65 and I will be at the Portland Retro Gaming Expo, September 27-29, 2024, in Portland, Oregon, USA. PRGE is one of the largest vintage video gaming shows in the Pacific Northwest, and this year they’re doing a home computing exhibition. Come for the interactive volunteer-run computer exhibits, then stay for the weekend of talks, playable arcade games, and the vendor floor. I’m not doing a talk at this one, but I’ll have my own table in the home computing area.
PRGE is a ticketed event at the Oregon Convention Center. If you’re visiting from out of town, get your hotel rooms reserved early—and make sure to leave time to explore Portland!
Lala The Magical (preview)
Majikeyric is previewing a new game for the MEGA65! Lala the Magical (preview) is an adorable puzzle platformer, with parallax scrolling areas and tons of enemies and collectibles. It’s based on the PC DOS game by the Mojon Twins.
In the ancient ruins (province of Badajoz) lives Lala with her teacher. She is learning magic but still has a long way to go.
“Tell me about the Sky Palace” – asks Lala, and her teacher tells her again about the three Power Gems hidden inside. “I’d love to see them”, but Lala’s teacher would say no… “It’s too dangerous, and powerful magic is required to traverse the palace”.
You can play the preview with a joystick. Sound, music, and polish are in progress. I’m looking forward to the final product!
Wonder Boy core
muse continues his series of arcade cores with Wonder Boy. This colorful side-runner will have you running, jumping, and even skateboarding across the landscape, collecting fruit and dodging creatures.
This core is available for both R3 (2022) and R6 (2024) MEGA65s. Be sure to use the correct version for your system, and follow the installation instructions.
Arcade cores use the MEGA65’s FPGA to fully recreate arcade game chipsets with a high degree of accuracy. Thanks to muse for his continued dedication to the arcade core library!
The role of the KERNAL
In computer architecture, the kernel is the center of a computer’s operating system. It manages access to hardware such as the keyboard, storage devices, and peripherals, as well as other common facilities. Programs—including the rest of the operating system—access these facilities by interacting with the kernel.
The Commodore KERNAL has played this role in Commodore 8-bit computers all the way back to the Commodore PET. The proper name, spelled with an “A” and all uppercase, is credited to an honest misspelling of the word “kernel” by developer Robert Russell that made its way into the VIC-20 programmer’s manual. While the KERNAL has evolved substantially across the line all the way up to the Commodore 65, its role in the computer and many of its key features and interfaces have remained largely the same since 1977.
In total, a Commodore computer’s operating system consists of the KERNAL, the BASIC interpreter, and the screen editor. The machine code for the operating system is etched into a ROM chip, wired to be the first thing the CPU sees when it wakes up. If the KERNAL detects that a C64-style cartridge is connected, it passes control to the cartridge. Otherwise, the KERNAL starts the screen editor, from which the user can load and run programs, type other commands, or start programming in BASIC. These three components tend to share some responsibilities, and the distinctions between them can be a bit blurry in some places, but that’s the general idea.
(There’s an interesting historical exception to the start-up procedure. The Commodore 64 was designed with support for cartridges made for the Commodore MAX, aka “Ultimax” or “VC-10.” An Ultimax cartridge connects directly to the 6510 chip’s I/O lines in a way that causes the CPU to go directly to the cartridge for its first instructions. The Ultimax did not have its own operating system: it relied on the connected cartridge for everything.)
Programming with the KERNAL
There are three main ways the KERNAL participates in the execution of programs: KERNAL subroutine calls, the KERNAL IRQ handler, and vectors.
KERNAL subroutines
A program can call subroutines in the KERNAL code to perform tasks and interact with the KERNAL’s systems. Here’s a simple machine code program that writes a short message to the screen by calling the KERNAL subroutine BSOUT:
bsout = $ffd2 lda #'H' jsr bsout lda #'I' jsr bsout lda #13 jsr bsout
The BSOUT routine writes a single character to the current output device, which by default is the screen editor. The program sets the accumulator to the character to be written as a PETSCII code, then calls BSOUT with the JSR instruction to address $FFD2. The subroutine does all of the work to write the character, then returns.
The screen editor does a lot of work here. It maintains a text cursor—the same one that you type with at the READY prompt—that has a position on the screen, and a draw state including the text color and other attributes. When it receives a character from the program via BSOUT, the screen editor figures out what changes to make to screen and character attribute memory, moves the cursor position, and scrolls the screen up if the text runs off the bottom. (Other PETSCII codes change the draw state or cursor position in other ways.)
The screen editor is a full-fledged built-in application that behaves like an input/output terminal. Your program writes to and reads from this terminal using KERNAL subroutines.
The KERNAL subroutine jump table
$FFD2 is not actually the memory location of the BSOUT subroutine. Instead, the KERNAL keeps a JMP instruction at that address that goes to the actual subroutine code. Each KERNAL subroutine available to programs has one of these, in a fixed memory location called a jump table.
The jump table provides a critical layer of compatibility across different versions of the KERNAL. Your program needs an explicit address to where it can jump, to invoke the subroutine. When the KERNAL developers inevitably need to relocate the subroutine code to a different address in a new version of the operating system, they can just update the address in the jump table, and your program (and everyone else’s) will continue to run correctly because the jump table itself doesn’t move.
I recently added an appendix to the MEGA65 Compendium describing all of the KERNAL subroutines and how to use them, including jump table addresses, CPU register arguments, and example programs. Check it out, and let me know what you think!
The KERNAL raster IRQ
As we covered in detail in last month’s Digest, you can program the VIC video chip to tell the CPU when its raster beam is at a given vertical location on the screen. This triggers an interrupt that causes the CPU to call a special kind of subroutine known as an interrupt handler. A typical use of the vertical raster beam interrupt is to have a chunk of program code that gets called many times per second at regular intervals. This allows the program to be organized as if it is two programs running side by side: a main program that runs normally, and a short secondary program kicked along by the interrupt.
The KERNAL uses this raster interrupt in this way. It installs an interrupt handler that pays regular attention to KERNAL subsystems while the user’s program is running. The KERNAL IRQ handler drives behaviors of the screen editor such as blinking the cursor and checking for special keyboard combinations like Mega + Shift, and behaviors of BASIC such as sprite move animations (from the MOVSPR command) and music playback (from the PLAY command).
Most programs can just take for granted that the KERNAL is doing its own thing with the raster IRQ. As we saw last month, only one IRQ handler can be installed at a time, so a program that wants to take over the IRQ handler must disable the KERNAL. But the KERNAL has a way it can share its IRQ handler with a program, so they can both play…
KERNAL vectors
The KERNAL generously offers programs the ability to replace or extend some of the KERNAL’s own subroutines. In these cases, when the KERNAL wants to call one of its own subroutines, it looks up the address in a table. This table is initialized to the address of the KERNAL’s version of the subroutine, so without changes, everything behaves normally. A program can replace that address with an address of its own, so the KERNAL calls the program’s routine instead. This indirect address is called a vector, because of how it points to where the KERNAL will go.
A program can replace a KERNAL routine by re-pointing a vector, but this is a heavy task in most cases. Much more common is to extend the existing KERNAL routine with additional functionality. It works like this:
Read the address of the original KERNAL routine from the vector table.
Write that address into a JMP instruction at the end of the program’s custom routine.
Re-point the vector to the beginning of the custom routine.
Now when the KERNAL calls the vector, control goes to the custom routine. The custom routine does some custom things, then jumps to the KERNAL’s original routine. The KERNAL routine still does the heavy lifting, and the custom routine just adds a bit of extra functionality.
This is how the KERNAL can share its IRQ handler. In the middle of the KERNAL’s IRQ handler, it calls a vector that points right back at the rest of the IRQ handler. A program can use the vector to insert its own routine that gets called as part of the KERNAL’s raster IRQ. This way, the program can combine the KERNAL IRQ and its custom IRQ routine, all running alongside its main program code.
Custom vectors remain installed when your program returns to BASIC. This allows a program to extend KERNAL behaviors used by BASIC and the screen editor. The custom IRQ vector is especially fun for this. A game written in BASIC can invoke a small amount of machine code to install an IRQ vector that plays a SID music file, and this music plays in the background while BASIC runs the rest of the game. You could even listen to SID music while writing your BASIC program, with the playback routine running alongside the screen editor!
Note that Run/Stop + Restore will reset KERNAL vectors. There are ways to prevent this, but we won’t cover them here for now. In general, this is a good thing, so you can get the system back to normal when a bug in a vector routine gets out of control.
The VECTOR accessor
The KERNAL keeps its vector table in its own private memory location. To read from or write to the vector table, a program calls a KERNAL subroutine that copies the table to or from the program’s memory. The full procedure for installing an extension vector routine is:
Call VECTOR, telling it to copy the vector table to program memory.
Copy the original vector address from the table into a JMP instruction at the end of the custom routine.
Write the address of the custom routine to the program’s copy of the vector table.
Call VECTOR again, telling it to copy the vector table from program memory into KERNAL memory.
Some Commodore programmers are accustomed to reading and writing custom vector addresses directly from internal KERNAL memory, without using the VECTOR routine. For the MEGA65, it is important for programs to use the VECTOR accessor routine, and to not depend on the internal KERNAL memory address. Just like how the jump table protects your program from KERNAL subroutines jiggling around between ROM versions, the VECTOR accessor protects your program from the KERNAL vector table jiggling in the same way.
We’ll try an example below. See the entry for VECTOR in the “KERNAL Jump Table” appendix of the Compendium for a complete explanation, along with a description of the vector table.
A bit more about the MAP
Last month, we briefly considered the CPU memory map, just enough to understand that the KERNAL assigns itself into the $E000-$FFFF address range so it can take over the IRQ handler address stored at $FFFE-$FFFF. We ditched the KERNAL by zeroing out the memory map, so all 16-bit addresses referred to RAM in the MEGA65’s first (well, zeroth) 64K memory bank.
I will continue to avoid a complete description of the MAP register—see the “Memory” chapter of the Compendium for that—but it’s worth knowing a bit more about how the KERNAL uses MAP when integrating with it using jump table entries and vectors.
Mapping 16-bit addresses to 28-bit addresses
Recall that the CPU sees 64KB of memory at a time, using 16-bit addresses $0000 to $FFFF. The MEGA65 has a much larger 28-bit address space, $000.0000 to $FFF.FFFF. The memory map assigns 8KB regions of the 16-bit space to parts of the 28-bit space, and a program can reconfigure this map with the CPU instructions map and eom.
There are actually a few ways the CPU can read and write memory using full 28-bit addresses without changing the map. Crucially, the CPU can only execute machine code instructions via 16-bit addresses. In other words, to execute code somewhere in the MEGA65’s memory, the memory map must have an 8KB region mapped to that location. Instructions like JMP use the 16-bit address into the memory map to find that code.
Two KERNAL maps
When the MEGA65 starts, it runs BASIC, the screen editor, and the KERNAL all together to power the familiar READY prompt, blinking cursor, program line editor, and library of BASIC commands. The operating system code that powers all of this lives in bank 3. There’s a lot of it, so in this mode, most of the memory map points to bank 3. Specifically, $2000 to $BFFF refer to $3.2000 to $3.BFFF, and $E000 to $FFFF refer to $3.E000 to $3.FFFF.
If the operating system is hogging most of the 16-bit addresses, where does our program code live? The KERNAL has two tricks up its sleeve, one for BASIC, and one for machine code.
A BASIC program lives in RAM in bank 0, starting at address $0.2001. The BASIC interpreter uses the CPU’s ability to read from 28-bit addresses to access BASIC program code. The machine code for the interpreter lives in memory starting at $3.2000, and is mapped in to 16-bit addresses starting at $2000. The interpreter itself reads the BASIC code from MEGA65 RAM directly using 28-bit addresses starting at $0.2000. (You’ll get used to it.)
A typical standalone assembly language program, the kind we start with the RUN command, starts with a short BASIC program that consists of a SYS command and the address of the beginning of the machine code, such as $2014. The machine code lives right next to the BASIC starter code, in bank 0 from $0.2014 onward. The CPU can’t execute this machine code when the BASIC memory map is active. So how does SYS get to it?
The answer: SYS changes the map. In the new map, only $E000 to $FFFF are mapped to $3.E000 to $3.FFFF. This is the KERNAL, without the BASIC interpreter or the screen editor.
In both maps, the unmapped region $C000 to $DFFF connects to I/O registers and whatnot, using features of the memory system that we won’t discuss here. See the “Memory” chapter for details.
So what?
Understanding how the KERNAL uses the memory map is important when writing software that depends on the KERNAL.
If your machine code program is a game or demo that doesn’t need the KERNAL for anything, it can map out the KERNAL, take over the interrupt handlers, and run the whole show. The tradeoff is, if you want to do something like read a file from disk, you’ll have to provide your own driver routines that interface directly with hardware registers.
If your program calls KERNAL subroutines, or if it installs vectors but does not return to BASIC, the SYS map will work just fine. The KERNAL routines live in $E000 to $FFFF, and this is present in both the BASIC map and the SYS map. The KERNAL’s internal memory is in the $0000-$15FF region, in bank 0.
If your program installs vector routines then returns to BASIC, those routines must live in a region that’s accessible from the BASIC map, not just the SYS map. This means your vector routines must live within $1600 to $1EFF.
The vector routines themselves can do something fancy like disable interrupts, change the map, do something with code elsewhere in memory, then restore the map and re-enable interrupts before continuing on. See that “Memory” chapter again if you want to try to figure that out.
Project: a desktop clock
It’s 10 o’clock at night, the kids and spouse have gone to bed, and you finally have a bit of time to play with your MEGA65. You flip through the User’s Guide, scroll through some old Digests, and decide to try a little BASIC program. You type a few lines, run the program, get a satisfying result, then write some more code. Everything is going great. Then you look up and it’s 3 o’clock in the morning! And you have an important work meeting in five hours! If only you had a little clock display in the corner of the MEGA65’s screen, you wouldn’t be in this mess.
Let’s try extending the KERNAL with an IRQ routine that prints the current time of day in the corner of the screen. This program has two parts: an installer that sets up the IRQ vector to point to a clock printing routine, and the clock routine itself.
To keep the memory management simple, we will assemble everything starting at address $1600, leaving out the BASIC preamble of a traditional program assembled to $2001. This complicates the user instructions a bit: the user must load the program with the BLOAD command, and start it with SYS $1600. But this turns out to be somewhat convenient. If the user resets the KERNAL vectors with Run/Stop + Restore, they can re-install the clock by running SYS $1600 again.
Here’s the full listing:
!cpu m65!to "clock.prg", cbm!symbollist "clock.lst"* = $1600vector = $ff8dscrnptr = $d060attrmem = $d800rtc = $fd7110 ; RTC registers at $0ffd7110rtc_mb = $0f ; Read vector table into memory sec ldx #vectable jsr vector ; Copy the iirq vector to custom_irq's ; jmp instruction lda vectable sta custom_irq_return+1 lda vectable+1 sta custom_irq_return+2 ; Write custom_irq address to iirq lda #custom_irq sta vectable+1 ; Install updated vector table clc ldx #vectable jsr vector ; Return to BASIC rtscustom_irq: lda #rtc_mb sta $ff lda #^rtc sta $fe lda #>rtc sta $fd lda #
Things to notice:
sta custom_irq_return+1 : The jmp $0000 instruction at the end of the custom IRQ routine assembles to three bytes, one for the jmp instruction and two for the address. The installer copies the original KERNAL IRQ vector to the address portion of this instruction.
$0ffd7110 : The Real-Time Clock can be read from hardware registers starting at $0FFD7110. There are six byte registers: seconds, minutes, hours, day-date, month, year (from the year 2000). Each value is a two-digit Binary Coded Decimal (BCD), where each 4-bit nibble represents one decimal digit. This might seem like a crazy way to store a number, but it’s quite useful when the end goal is to display it as a decimal number. To read the left digit alone, shift right four times: lsr…. To read the right digit alone, mask out the upper four bits: and #$0f.
and #$7f : The hours value needs its top bit masked out for some reason.
adcq scrnptr : The address of screen memory where the clock should be drawn is calculated by adding 72 to the starting address of screen memory, read from the VIC SCRNPTR register ($D060-$D063). This is a 28-byte pointer, and using the Q virtual register to calculate column 72 allows this to support any text screen configuration, including 80x50 mode. Of course, on a 40x25 screen, an offset of 72 would put it on the second row. This could be improved by detecting the screen width. See the SCRORG KERNAL routine for one way to do this.
sta attrmem+72,x : To make the clock stand out, it is drawn with character attributes for reverse mode and a white color. The VIC makes the first 2KB of attribute memory visible at $D800, which is sufficient to reach the top half of the screen. See the “Memory” chapter for an explanation of attribute memory. (Naturally, the “72” in this statement would also have to change to support the clock on the first row in 40-column mode.)
Here’s a pre-assembled version of the clock program if you want to play with it:
A reminder of the instructions:
To load the clock: BLOAD "CLOCK"
To start the clock: SYS $1600
Run/Stop + Restore stops the clock. SYS $1600 again to bring it back.
Did you enjoy this Digest? Consider supporting the Digest with money! Visit: ko-fi.com/dddaaannn
Oh, and be sure to send me a photo of your MEGA65 set-up! I might feature it in a future issue.
Cheers!
— Dan
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
At least once before in this Digest I’ve said something about how there’s a way to write a program to perform precisely timed actions, but the actual technique would have to wait until a future issue. In this issue, we’ll start looking into this, with a focus on synchronizing a program with a particularly useful hardware feature: the raster beam.
To do this effectively, we’ll introduce an important programming paradigm supported directly by a feature of the CPU, called interrupts. To get that to work, we’ll also take a brief look at how to uninstall the KERNAL operating system by changing the system’s memory map.
Any news?
Trenz Electronic is busily assembling new MEGA65s, still on track for delivering the next batch in the next few weeks. The Discord and Forum64 board have been quieter than usual, with everyone starting new projects or resting up from previous ones. I can’t wait for the rush of new people joining and asking questions!
If you have a project in progress that you’d like to see featured in the Digest, let me know! You can also announce your project in the #announcements channel on the Discord, and upload your work in progress to Filehost for others to try. Beginners welcome! Some of the coolest stuff we’ve seen for the MEGA65 has been written in BASIC by people like you just trying things out.
The time keepers
Every computer contains a component that generates a clock signal, a fast and precisely timed electronic pulse that drives the digital devices in the computer. Like turning the crank of a music box, these pulses advance the internal mechanisms of each device through their various stages to perform computations, and to generate signals of their own. The clock signal also synchronizes the devices with each other, so they can communicate with one another over their electronic connections.
MEGA65 programs can take advantage of three specific devices driven by the system clock to execute code with precise timing: the CPU, the VIC video chip, and the CIA chip.
The CPU
The CPU uses the clock signal to perform the machine code instructions of a program. Each instruction requires a certain number of cycles to perform, where each cycle takes a fixed amount of time based on the clock. For example, the lda #$ff instruction, which loads the byte value $ff from the program code into the accumulator register, takes two CPU cycles to complete. The cycle cost of an instruction depends on the instruction and addressing mode, but it’s typically between 1 and 6 cycles, and as many as 14 cycles for fancier operations like the 45GS02 Q-register instructions.
In its default MEGA65 mode, the MEGA65 CPU performs instructions at 40.5 million cycles per second, or 40.5 megahertz (MHz). The MEGA65 can also underclock its cycle rate to emulate a Commodore 64 (1 MHz), Commodore 128 (2 MHz), or Commodore 65 (3.5 MHz).
In theory, you can analyze the duration of a section of machine code by looking up the cycle costs of each instruction and adding them together. Commodore 64 programmers sometimes do this to optimize small sections of code, especially for advanced video effects that require precise coordination between the 1 MHz CPU and the VIC video chip. This is less necessary with the MEGA65’s 40.5 MHz CPU, which outpaces most of the signal frequencies that a program might care about.
A program can pause for a period of time by executing instructions and ignoring the results, simply to burn through cycles. This could be a series of instructions in memory, a loop with a counter, or a loop that exits when a hardware register changes. This technique is known as busy-waiting because the CPU can’t stop executing instructions: it has to twiddle its thumbs to keep busy—and also to figure out when to stop waiting and continue the program.
Try these examples of busy-waiting in BASIC:
5 REM -- PAUSE BRIEFLY BY DOING NOTHING IN A LOOP10 BORDER 020 FOR A=1 TO 2000030 NEXT A40 BORDER 15 REM -- WAIT FOR JOYSTICK FIRE BUTTON, PORT 110 BORDER 020 IF (JOY(1) AND 128)=0 THEN 2030 BORDER 1
The VIC
The VIC video chip uses the system clock to generate a video signal with the precise timing expected by the display hardware. Such protocols are derived from old cathode-ray tube (CRT) displays that draw the entire screen with a beam of electrons. This raster beam sweeps across the screen in a fixed pattern of horizontal rows, or raster lines, from left to right, top to bottom. The video signal controls the intensity of the beam, and the beam leaves pixels in its wake that form the complete image. Once it reaches the bottom, the beam returns to the top of the screen, and the process repeats, many times per second.
The MEGA65 connects to modern VGA analog or HDMI digital displays. Internally, it attempts to recreate the display parameters of one of two vintage analog video modes, either PAL or NTSC, so that vintage software that depends on these parameters will run properly. The PAL video mode draws 312 raster lines 50 times per second, also known as a refresh rate of 50 Hz. NTSC draws 262 lines 60 times per second, or a refresh rate of 60 Hz. A full image consists of two interlaced sets of lines, so the actual image size and frame rate is twice as many lines and half the frequency: PAL video is 625 lines tall with a frame rate of 25 Hz.
Synchronizing a program’s behavior with the raster beam is a powerful way to perform advanced visual effects. By changing VIC control parameters at specific times during the drawing of the screen, a program can break away from some of the VIC’s numerical limitations. This is the basis for one of the most coveted of Commodore graphical techniques, called sprite multiplexing, where the VIC’s eight hardware sprites are reused in different parts of a single screen.
The CIAs
The Complex Interface Adapter (CIA) chip is a multi-purpose chip that manages device and serial communications, and also has a high-precision countdown timer feature. Each CIA chip contains two timers, and the MEGA65 (like the Commodore 64) has two CIA chips, each wired up a bit differently. The CIA counts pulses at a rate of 1 MHz, and your program can set a timer interval up to 65,535 of these pulses, for a delay of up to approximately 0.065 seconds. For example, to set a timer for 1/60th of a second, your program would set the timer value to 16,666.
We’ll save a complete discussion of the CIA chip for another time. For now, suffice it to say that you can use both busy-waiting and interrupt-based techniques with the CIA high-precision timers.
Other clocks
There are two other kinds of clock that are worth knowing about, but are less useful for high-precision timing. Each CIA chip contains a time-of-day (TOD) clock that counts tenths of seconds, seconds, minutes, and hours, up to a 24-hour period. In the Commodore 64, this clock powers the TI and TI$ BASIC variables, and is reset to midnight when the computer is switched off. The MEGA65 contains a separate battery-backed Real-Time Clock (RTC) chip that can remember the time of day and calendar date even when power is disconnected. BASIC 65 uses the RTC chip for the TI$ and DT$ variables, and it uses the CIA TOD clock for the TI variable.
Just to complete the picture, here’s a simple BASIC program that busy-waits on the TI special variable for three seconds. Notice that the TI variable’s value changes over time, even though the BASIC program itself isn’t changing it.
5 REM -- WAIT 3 SECONDS10 BORDER 020 T=TI30 IF (TI-T)<3 THEN 3040 BORDER 1
Finding the beam
The VIC video chip generates a signal for the raster beam to draw onto the display, expecting the raster beam to follow a fixed pattern on a regular interval. The VIC keeps track of where the raster beam is located, and a program can access this information by reading a hardware register.
One way to synchronize a program’s behavior with the VIC’s raster beam is to busy-wait on its location. You can do this from BASIC 65, with the VSYNC command:
10 BORDER 020 VSYNC 10030 BORDER 140 VSYNC 20050 GOTO 10
VSYNC pauses the BASIC program and busy-waits for the raster beam to reach a given vertical position (or raster line). The vertical position number can be between 0 and 311 in PAL video mode, or 6 and 261 in NTSC mode, where smaller numbers are further up the screen. Try adjusting the numbers on lines 20 and 40 in this example program. (Beware that there’s currently a bug that causes VSYNC to hang with a value less than 6 in NTSC mode.)
Here is an equivalent program in assembly language. Because the raster beam vertical position can be larger than 255, it takes up more than one byte of space across two register addresses. Bit 7 of register $D011 is high (1) if the raster beam is beyond line 255.
vicii_rcl = $d012vicii_rch = $d011 ; bit 7border = $d020loop: lda #0 ; black sta border lda #100- cmp vicii_rcl bne - bit vicii_rch bmi - ; rch is high, not our stop lda #1 ; white sta border lda #200- cmp vicii_rcl bne - bit vicii_rch bmi - ; rch is high, not our stop jmp loop
$D011/$D012 are the VIC-II raster position registers. Indeed, this example will work on a Commodore 64 as well as the MEGA65. The MEGA65’s VIC-IV is also capable of doubling the vertical resolution of the screen, a mode known as “V400,” which uses twice as many raster lines. You can see this mode in action by activating the 80x50 text mode: press Esc then 5. (Press Esc then 8 to return to 80x25 text mode.) In V400 mode, the $D011/$D012 VIC-II registers and the VSYNC command continue to use the raster position as if it were in the non-doubled mode, so the bottommost VIC-II raster position in PAL video mode is still “312,” even if V400 is active. If you want the actual mode-specific VIC-IV raster position, an 11-bit value, see the FNRASTERLSB register at $D052 (lower 8 bits) and FNRASTERMSB at $D053.0-2 (upper 3 bits).
Note that while the raster vertical position corresponds to pixel Y-coordinates, the beam starts well above the top edge of the usual background area inside the colored border—because the raster beam also draws the border! The outermost raster positions are outside of the visible area.
Visualizing time
The ability to synchronize our program with the raster beam gives us a clever way to visualize the duration of a section of program code—using the border color! Try this:
10 N=3020 DIM FB(N):FB(0)=0:FB(1)=160 FOR I=2 TO N70 FB(I)=FB(I-2)+FB(I-1)80 NEXT I
This program allocates an array of 30 numbers, then fills it with the Fibonacci sequence. To see this in action, run the program, then type: PRINT FB(5)
We can get a sense of how fast this code runs using a combination of VSYNC and BORDER, like so:
10 N=3020 DIM FB(N):FB(0)=0:FB(1)=130 BORDER 040 VSYNC 10050 BORDER 160 FOR I=2 TO N70 FB(I)=FB(I-2)+FB(I-1)80 NEXT I90 GOTO 30
The Fibonacci code is the same, but now it runs repeatedly in a loop. For each repetition, the program changes the border color to black, synchronizes to raster line 100, changes the border to white, then performs the Fibonacci fill. When control loops back to line 30, the border is changed back to black. In other words, the border is only white for the time it takes to fill the array. The height of the white bars in the border represent how far the raster beam travels during the fill operation. The VSYNC command makes sure this happens when the raster beam is at the same spot on the screen every time, to hold the white bars steady.
Try changing N in line 10 to another number, such as N=5 or N=40. This changes the size of the array, and therefore changes the amount of time it takes to fill it with Fibonacci numbers.
Caution: This next step causes the screen to flicker. Change line 10 to N=80, then run the program. Press Run/Stop to stop the flickering.
Why does the screen flicker when N=80? Here’s what’s happening:
The raster beam reaches position 100, the border changes to white, then the Fibonacci algorithm runs while the raster beam travels.
In the time it takes to calculate 80 Fibonacci numbers, the raster beam reaches the bottom of the screen, returns to the top, then goes just beyond position 100.
The Fibonacci fill completes, and the program changes the border back to black—but by now the raster beam has made it all the way through the screen and has drawn an entirely white border.
The program waits for the raster beam to reach 100 again, which requires the raster beam to travel through almost a full screen with the border color set to black. With each screenful alternating between black and white, the border flickers.
Visualizing code duration using the border color is especially helpful when writing games and demos. To animate graphics smoothly, a game must update all of the screen data in less than the time it takes to draw a single screen, so the updated data is ready for the next pass of the raster beam. If the border flickers, it means the computation is taking up more than one screen’s worth of raster travel, and the programmer has some work to do.
Wherefore interrupts?
A typical machine code program is a sequence of instructions performed one at a time by the CPU in the order the instructions are given. Branching instructions can change the flow of control to other instructions in memory, but otherwise the CPU just marches down the list.
An interrupt is an event in a computer that needs immediate attention from the software. The event can come from a peripheral, such as a key press or incoming serial communication, or an internal programmable device that isn’t the CPU, such as the VIC or CIA chip. (As a special case, an interrupt event can also be triggered directly by the software with the brk instruction, but we’ll ignore this for now.)
When an interrupt occurs, the CPU completes the current instruction (however many cycles are remaining), stops executing the program, then calls a software routine called an interrupt handler. A typical interrupt handler does a small amount of work to respond to the event, then resumes the main program where it left off.
Interrupts allow a program’s code to be organized as if it were two programs: a main program that runs as if it is unaware that special events are occurring, and a short program that runs whenever a special event occurs. Without interrupts, a program would have to be written in such a way as to check for the special condition periodically, in between doing everything else it needs to do. This can delay the program’s ability to respond to the event, or even cause it to miss the event entirely.
Here’s a quick demonstration in BASIC of what could go wrong in this scenario. The program loop does some “work” that takes time, in this case an empty loop, but imagine it’s doing something important. Once the program has a free moment, it checks to see if the fire button of the joystick in port 1 is pressed; if not, it goes and does some more work. It’s pretty difficult to get this program’s attention with the fire button. Only by holding the button down will it eventually notice and respond.
10 BORDER 020 REM -- DO SOME "WORK"30 FOR A=1 TO 5000040 NEXT A50 REM -- CHECK FOR FIRE BUTTON IN JOYSTICK PORT 160 IF (JOY(1) AND 128)=0 THEN 3070 BORDER 1
You can imagine a hypothetical computer that triggers an interrupt when the fire button is pressed, halting the “work” to respond immediately. Because interrupts are built into the hardware, only a few kinds of events are wired into the CPU interrupt system. A typical program would use one of the timing sources (VIC or CIA) to trigger an interrupt at fixed intervals many times a second, and the interrupt handler would check for things like joystick input and react accordingly. This is how the KERNAL moves sprites or plays music concurrently with a BASIC program, or blinks the cursor while waiting for keyboard input at the READY. prompt. The steady drumbeat of interrupts allows the program (or the KERNAL) to respond to user input quickly, and progress animation and music smoothly.
Custom interrupt handlers are exclusively the domain of machine code programs. While BASIC programs benefit from how the KERNAL uses interrupts, they can’t use interrupts directly. In most cases, the best a BASIC game program can do is keep the game loop logic short and fast, so it can respond to joystick input as soon as possible. So maybe don’t loop 50,000 times between checks for the fire button.
BASIC 65’s sprite COLLISION feature behaves like an interrupt handler, but it’s actually implemented by the BASIC interpreter, not the CPU’s interrupt system: the interpreter just checks for sprite collisions before executing each BASIC statement.
IRQs and NMIs
The 6502 family of CPUs supports two kinds of interrupt: an interrupt request (IRQ), and a non-maskable interrupt (NMI). Some events trigger IRQs, and others trigger NMIs.
The difference between IRQs and NMIs is that certain CPU operations can temporarily disable IRQs, and a program can disable and re-enable IRQs as needed, while NMIs cannot be disabled. Disabling IRQs is like putting a “Do Not Disturb” sign on a hotel room door: it says that the main program is working on something that should not be interrupted, and interrupt requests should be ignored for the time being. NMIs are more like a hotel fire alarm: they must be handled immediately, no matter what. Both kinds of interrupt are necessary for a fully functioning system, but most programs can get away with only dealing with IRQs.
The following are possible sources of IRQ interrupts in the C64 and MEGA65:
VIC raster beam location
VIC sprite collision
Timers on CIA #1
The brk instruction
The following are possible sources of NMI interrupts in the C64 and MEGA65:
Timers on CIA #2
RS-232 serial communication
Pressing the Restore key
Yes, pressing the Restore key triggers the NMI handler! This is how Run/Stop + Restore works: the KERNAL’s NMI handler tests whether both keys are pressed, then resets BASIC and declines to continue the interrupted program. This works even if the program has disabled IRQs.
The CPU supports one interrupt handler for IRQs and one handler for NMIs. It’s up to the handler code to figure out the specific cause of the interrupt, and react appropriately.
The address of each interrupt handler is stored as two bytes, in little-endian order (low byte first), at a fixed 16-bit address: the IRQ handler address is stored at $FFFE-$FFFF, and the NMI handler address is stored at $FFFA-$FFFB. The MEGA65’s default memory map installs KERNAL ROM code in this location, and the KERNAL uses its own interrupt handlers, so you can’t just POKE new addresses here. One way that a program can install custom interrupt handlers is to uninstall the KERNAL, by changing the memory map.
It is possible to leave the KERNAL in place and ask it to call custom code during its own IRQ handler. We’re still formalizing and documenting KERNAL integration techniques, so this is best left alone for now when writing MEGA65 programs. This is probably the most popular KERNAL integration technique on the C64, so we’ll try to finish this soon. (Hint: use the “VECTOR” KERNAL routine.)
For the rest of this Digest, we’ll focus on the raster beam IRQ, and assume that the program will uninstall the KERNAL to install its own handler. We’ll save other interrupt types for another time.
Interrupt handlers
When the CPU encounters an IRQ, it finishes the current instruction, then pushes the 16-bit address of the next instruction onto the stack (two bytes). It also pushes the CPU flags onto the stack, as a single byte. It then disables IRQs, then jumps to the address stored in the IRQ vector.
The first thing the interrupt handler needs to do is preserve any additional CPU state that might get clobbered by the rest of the handler. The main program expects everything about the CPU to be just as it left it, including the values in other registers. For example, if the handler needs to use the accumulator (and it probably does), it needs to push the previous accumulator value to the stack beforehand, and restore it afterward.
For most kinds of IRQ interrupt, the device that triggers the interrupt stays in the “interrupt” state until it is told that the interrupt is handled. The program must interact with the device via a hardware register to acknowledge the interrupt, so that it doesn’t re-trigger once the interrupt handler resumes the program. Remember that an IRQ can have multiple causes, so if your program enables more than one cause, it will need to determine the cause and handle it appropriately.
When the interrupt handler is complete, it calls the rti instruction to resume the paused program. This restores the CPU flags and next code address from the stack (thereby re-enabling interrupts), and continues where it left off. This is kind of like how jsr jumps to a subroutine and rts returns from it, with minor differences. For example, normal subroutines don’t stash and restore the CPU flags.
irq_handler: ; Preserve the Accumulator, X, Y, and Z registers ; on the stack. pha phx phy phz ; Test for and acknowledge the IRQ cause... ; Do something fun... ; Restore the CPU registers, pulling them off the ; stack in the reverse order they were pushed. plz ply plx pla ; Resume the program rti
Enabling and disabling IRQs
The CPU keeps track of whether IRQs should be handled or ignored using the “IRQs disabled” CPU status flag (I). To disable IRQs, a program calls the sei instruction (“set IRQ disabled”). To re-enable IRQs, a program calls the cli instruction (“clear IRQ disabled”). I know, this set/clear definition feels like a double-negative; just think of how interrupts being enabled is the default case (0 or clear), and being disabled is the special case (1 or set).
The CPU will automatically disable IRQs when calling either the IRQ or NMI interrupt handler, so an IRQ handler can’t re-trigger while it is executing, nor can a handler be interrupted by something else. Interrupts get re-enabled automatically when the rti instruction restores the previous status flags from the stack. It’s possible to get fancy with this, and some advanced techniques make unusual exits from the interrupt handler or manipulate the stack.
Interrupts are also disabled automatically while updating the 45GS02 memory map registers. This is important because memory map adjustments require several instructions, and interrupting these instructions could be disastrous if the incomplete memory map leaves either the interrupt handler code or handler vectors in an inconsistent state.
This is an important hint for how a program and its interrupt handlers can communicate with each other. If the main program is updating the state of the system, and it’d be bad if the interrupt handler sees the state only partially updated, the main program should disable interrupts (sei), update the state, then re-enable interrupts (cli). Remember: with interrupts enabled, the handler could be called between any two instructions, including between an lda and an sta. Modern-day programmers might compare disabling interrupts to a “global lock” on memory and CPU state, albeit a simple one.
Setting up hardware interrupt handlers
Let’s try disabling the KERNAL and setting up our own interrupt handler. This requires a few steps.
We’re not going to cover memory mapping in detail in this issue. To learn more about it, download the latest version of The MEGA65 Compendium and check out the “Memory” chapter. For now, we just need to know a few things:
The MEGA65 has an address space of 28 bits, with addresses numbered from $000.0000 to $FFF.FFFF. Some of these addresses go to memory, some go to hardware registers, and some are unassigned.
The CPU sees 64KB of the MEGA65’s memory at a time, using 16-bit addresses numbered from $0000 to $FFFF.
The CPU maintains a memory map that assigns 8KB chunks of the 16-bit address space to the 28-bit address space. A program can adjust this memory map using CPU instructions.
The MEGA65 KERNAL ROM code lives in the 28-bit address space from $2.0000 to $3.FFFF. While the KERNAL is running, it switches between a few memory maps to do various things. Most importantly, when interrupts are enabled, the 28-bit addresses $3.E000 to $3.FFFF are mapped to 16-bit addresses $E000 to $FFFF, which includes the IRQ and NMI vectors as well as the handler routines that they point to.
To take this over for our own purposes, we need to reset the memory map so that $E000 to $FFFF points to RAM at $0.E000 to $0.FFFF. The memory map consists of eight bytes of information. Without going into detail, we can accomplish our task by setting four of these bytes to zero. To do this, set the A, X, Y, and Z registers to zero, then use the map instruction, followed by the eom (“End Of Map”) instruction:
lda #0 tax tay taz map ; Disable IRQs and update the MAP register eom ; End MAP adjustments, re-enable IRQs
The map instruction transfers A, X, Y, and Z to the first four bytes of the MAP register. Simultaneously, the map instruction also disables IRQs (similar to sei). After the first map and before the eom, the program can call map a second time to set the second four bytes of the MAP register if needed, or otherwise set up any additional state needed by IRQ handlers in the new memory map. The eom instruction signifies that the MAP setting is complete, and re-enables IRQs.
The map and eom instructions are specific to the 65CE02 CPU on which the 45GS02 is based, so you'll want to use an assembler that supports them, like Acme assembler. If your assembler of choice only supports CPUs up to the 65C02, you can use the aug instruction for map, and nop for eom. If your assembler only supports 6502 instructions, get a better assembler.
The KERNAL keeps the VIC raster interrupt active for its own purposes, so we need to either disable this or set it up the way we want. In general, it’s a best practice to disable all sources of interrupt that your program doesn’t recognize when installing custom interrupt handlers.
Let’s use the gap between map and eom to install our own interrupt handlers, and disable the KERNAL’s raster interrupt. Even though we’re not discussing NMIs for now, we need an NMI handler because we’re replacing all of the KERNAL’s handlers. (We’ll look at a more thorough way to do this another time.)
hw_nmi_vec = $fffahw_irq_vec = $fffevicii_irqmask = $d01aciaa_d = $dc0dciab_d = $dd0d lda #0 tax tay taz map ; (disables interrupts) ; Set the NMI handler vector lda #nmi_handler sta hw_nmi_vec+1 ; Set IRQ handler vector lda #irq_handler sta hw_irq_vec+1 ; Disable all VIC IRQs lda #0 sta vicii_irqmask ; Disable all CIA IRQs, and acknowledge any pending timers lda #$7f sta ciaa_d sta ciab_d lda ciaa_d lda ciab_d ; Set up the interrupts we care about... eom ; (re-enables interrupts) ; Main program...- bra - ; (Just an empty busy-loop for now.)irq_handler: pha ; Test for and acknowledge the IRQ cause... ; Do something fun... pla rtinmi_handler: rti
Raster interrupts
The VIC chip can trigger an IRQ when the raster beam is at a requested position. Setting this up requires two simple steps.
First, the program must tell the VIC what raster position to use. As with the BASIC 65 VSYNC command, the range of this value depends on the video mode: 0 to 311 for PAL, and 6 to 261 for NTSC. The program sets the desired location with the lower eight bits in register $D012, and the ninth bit as bit 7 of register $D011.
Astute readers may notice that this is the same register we used to read the raster position for busy-waiting! The VIC is clever enough to re-use this register location in this way: when you read the register, it returns the current raster position. When you write to the register, you set the raster interrupt position.
The second step is to set a flag that says the program wants the VIC to trigger the raster IRQ. Set bit 0 of register $d01a to 1 to enable the raster IRQ.
vicii_rcl = $d012vicii_rch = $d011 ; bit 7vicii_irqmask = $d01a ; Enable raster interrupt at position 200 lda #200 sta vicii_rcl lda vicii_rch ; clear bit 7 of $d011 and #$7f sta vicii_rch lda #$01 sta vicii_irqmask
Within the IRQ handler, you can confirm that it was the raster IRQ that triggered the interrupt by testing bit 0 of register $D019. To acknowledge the interrupt and prevent the IRQ handler from re-triggering, write a 1 to this bit. This is another register that has unusual read and write behavior: reading it tests the IRQ trigger status, and writing a 1 to a bit “unlatches” the trigger.
vicii_irq = $d019irq_handler: pha ; Test for raster IRQ lda vicii_irq bit #$01 beq + ; not raster IRQ ; Acknowledge raster IRQ lda #$01 ; write a 1 to bit 0 sta vicii_irq ; Do something fun...+ pla rti
I promised to limit this discussion to raster interrupts, but it won’t take more than two sentences to discuss the VIC-II’s other interrupt capabilities. The VIC can also trigger an IRQ when a sprite collides with the background, or with another sprite. To use these, simply use bits 1 and 2, respectively, of the $d019 and $d01a registers. Oh, and the VIC also uses an IRQ for managing input from a light pen, but I don’t have one of those so I can’t tell you anything about it.
As mentioned earlier, the VIC-IV uses twice as many raster lines in V400 mode as otherwise, but maintains the non-doubled raster line count in the $D011/$D012 registers. The raster vertical position IRQ uses the same value range, regardless of the V400 mode. There is not currently a way to set a more precise raster IRQ in V400 mode.
Racing the beam
In the time that it takes the raster beam to draw a single line, a 1 MHz Commodore 64 can execute about 64 cycles worth of instructions. (Check my math: PAL draws 312 lines 50 times per second; the C64 executes 1 million CPU cycles per second; 1,000,000 / (312 x 50) = 64.1. NTSC draws 262 lines 60 times per second; 1,000,000 / (262 x 60) = 63.6.) Just eyeballing the IRQ handler I wrote above, which doesn’t even do anything useful, it looks like I’ve already spent 26 cycles. A typical C64 interrupt handler will not complete until the raster beam is on the next line.
With high-speed C64 graphics programming, if each CPU cycle represents 1/64th of a line, every cycle matters, and getting the code to synchronize with the raster beam position can be challenging. Remember how I said that when the CPU receives the interrupt request from the VIC, it finishes the current instruction before calling the handler? The current instruction could have several cycles remaining, and the raster beam will have advanced across the screen for some distance before the handler is called. Moreover, the interrupt could be happening at any time during any instruction, so the runoff is likely to vary every time. On a C64, a simple raster IRQ handler that changes the background color results in a flickering fringe at the transition point. C64 programmers can use complex techniques to synchronize the CPU with the raster beam, typically by setting a raster interrupt just before the line that is needed, then performing tests and busy-waiting for very specific amounts of time to account for minute differences between C64 models and other conditions.
The MEGA65 doesn’t have that problem. At 40.5 MHz, the MEGA65 executes approximately 2,600 cycles per raster line, or less than a pixel per CPU instruction. In most cases, the small amount of cycle slop just before the interrupt handler is called won’t impact a program. A bit of fringing can still appear if you’re changing the border color, because the border is so close to the beginning of the raster line. In this case, just be sure to change the border color as early as possible in the handler routine.
Also note that if the interrupt handler exits before the beam has left the line that caused the IRQ to trigger, the VIC won’t re-trigger the IRQ for the same line. The VIC only triggers a vertical raster interrupt at the beginning (left-most position) of the line.
Raster chaining
If you want the raster IRQ to trigger once per frame in the same location, you can just leave the IRQ mask bit set, and leave the raster location registers alone. This is handy for simple game loops that stay in sync with the screen refresh.
Earlier, we were discussing special effects like multi-color borders and sprite multiplexing that involve changing VIC parameters at multiple raster positions. To do this with interrupts, you set the next raster position within the interrupt handler. After the handler returns, the VIC is ready to trigger it again at the new position. Of course, this means that your handler must test which position triggered the interrupt. It can do so by reading the raster position, as before.
Another option is for the interrupt handler to also update the IRQ handler vector address to point to a different routine to be called for the next raster interrupt. This avoids having to test the raster position. As long as all of the raster handlers correctly advance the raster interrupt position and handler address, you can set up distinct handlers for each raster position.
Here’s an example of using the first technique to draw the white border bar from 100 to 200:
vicii_border = $d020irq_handler: pha ; Test for raster IRQ lda vicii_irq bit #$01 beq + ; not raster IRQ ; Acknowledge raster IRQ lda #$01 ; write a 1 to bit 0 sta vicii_irq ; White border bar from 100 to 200 lda vicii_rcl cmp #101 bcs ++ ; This is the raster interrupt for line 100. ; Set the raster interrupt position to 200. lda #200 sta vicii_rcl lda #1 ; white sta vicii_border bra + ; This is the raster interrupt for line 200. ; Set the raster interrupt position to 100.++ lda #100 sta vicii_rcl lda #0 ; black sta vicii_border+ pla rti
The VIC-IV raster X position
The traditional VIC-II raster position register and corresponding IRQ trigger are based on the vertical position of the beam, how far up or down the beam is. If you’re careful to consider the video mode, you can think of this as a raster Y coordinate, in correspondence with the VIC screen coordinates.
The MEGA65’s VIC-IV has an extra trick up its sleeve: it also reports and can trigger the IRQ on the raster horizontal position, as a raster X coordinate. This works similarly to the VIC-II vertical position: you can read the X position from a register, write an X position to this register to set the IRQ trigger, and enable and acknowledge the IRQ by setting bits in other registers.
To protect backwards compatibility, this IRQ is behind a master switch for all MEGA65-specific IRQ triggers that is off by default. To unlock new MEGA65 IRQs (currently just this one), set $D07A bit 6. The X position is a 14-bit value, readable at $D050 (lower eight bits) and $D051.0-5 (upper six bits). You write the IRQ trigger position to the same register. To enable the raster X IRQ, set bit 4 of $D01A. To acknowledge the raster X IRQ in the request handler, write a 1 to bit 4 of $D019.
This IRQ will trigger whenever the raster beam reaches the requested horizontal position, once for every raster line when this is enabled. To target a specific raster X-Y coordinate, leave the horizontal IRQ disabled, trigger a vertical IRQ for the Y coordinate, then in that handler, enable the horizontal IRQ for the X coordinate and return. The next interrupt will be at the requested position on the line. Remember that the beam travels while the handler is executing, so keep it tight.
Playing SID music
Let’s end with a musical experiment!
SID files are the Commodore community’s method of creating and preserving music designed to be played by a Commodore 64 through its SID sound chip. Perhaps surprisingly, a typical SID file isn’t just data about the musical notes to be played, like a piano roll for a player piano. Instead, it contains 6502 machine code—the player mechanism itself! (I guess the SID is the piano? You get it.)
The playback code consists of two subroutines. One subroutine initializes the SID chip, and is expected to be called once before starting the song. The other subroutine plays the actual music, and is expected to be called many times per second, with precise timing. This makes it easy for a game or demo to play the SID file simply by loading it into memory, calling the initialization routine, then calling the playback subroutine from an interrupt handler. The music plays concurrently with whatever else the program does.
The complete SID file specification has matured over multiple generations to cover many interesting cases. For our simple experiment, we can note just a few things: the file header tells us where in memory to load the file, and also provides the addresses of the initialization routine and the playback routine in memory when loaded appropriately. SID files expect a C64 memory map, so getting arbitrary SIDs to play on the MEGA65 can be tricky. (When you play a SID via M65Connect—did you know you can play SIDs via M65Connect?—it uploads a tiny player routine that runs in GO64 mode, which gets around this issue.)
Here’s an easy one to try that works with the MEGA65 memory map:
Download Frogger.sid.
Download FROGGERSID.d81 (contains frogger.sid).
If we open Frogger.sid in a hex editor and compare it to the SID file format spec, we can see:
It’s a PSID file (not an RSID), version 2.
The data region starts at offset $7C in this file.
The load address is in the first two bytes of the data region, and not the header. This address is $5000.
The initialization routine starts at $5000.
The play routine starts at $59F0.
The play routine expects to be called 60 times per second.
It seems like we should be able to load this file starting at address $5000 - $7C = $4F84, then call the subroutines at $5000 and $59F0 appropriately. Let’s forget about interrupts for a second and try this in BASIC!
10 BLOAD "FROGGER.SID",P($4F84)20 SYS $500030 BORDER 040 VSYNC 10050 BORDER 160 SYS $59F070 GOTO 30
This program loads the SID file such that the data region starts at $5000, calls the initialization routine, then runs the main loop in lines 30-70. The loop uses VSYNC in two ways: it makes sure that we’re calling the play routine as often as the screen updates, and it uses the border timing trick to see how long the playback routine takes to execute. If you want to use this tune in a BASIC game, the white bar tells you how much runtime is available for game logic and graphics.
Maybe this tune sounds familiar! And if you’re in PAL mode, maybe it sounds a bit too slow. If your display supports it, open the Freezer (hold Restore for one second then release), press V to switch video modes, then press F3, then re-run the program to hear the difference. The song plays as originally intended in the NTSC video mode, which updates the screen 60 times per second, and slower in the PAL video mode, at 50 times per second.
Music playing at the wrong speed is very familiar to Commodore enthusiasts sharing game software between the USA and Europe, where the two different video standards were used. Many C64 games use the raster interrupt for every aspect of their game loop including music playback, because it’s much more convenient to use a single interrupt for all timing purposes than to try to accommodate different intervals for graphics and music. One fix for this would be to use both a raster IRQ (50 Hz or 60 Hz, depending on video mode) and a CIA timer IRQ (set to 60 Hz), and write the interrupt handler to detect which device triggered the IRQ and perform either graphics or sound updates accordingly.
If you’d like to try playing this SID file from assembly language, I recommend using the feature of your assembler to include frogger.sid as binary data into your program, then have the program start by copying the data into the correct memory location. Everything else should look similar to the interrupt handling code from earlier. In a later Digest, we’ll revisit the subject of interrupts, and look at how to use the CIA chip to play music correctly in either video mode.
By this time next month, new MEGA65s may be on their way to their new homes! Huge thanks to everyone who has been reading this Digest in anticipation of future ownership. The new User’s Guide contains everything you need to get started, and don’t miss my MEGA65 Welcome Guide with photos and additional tips. Join the Discord and let us know it arrived safely, and don’t forget to register for ownership status in both the Discord and on Filehost.
Everything I do for the MEGA65 project, including this Digest, is made possible by supporters like you! If you’d like to support the Digest, visit: ko-fi.com/dddaaannn.
Happy computing!
— Dan
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
[Did you know: All issues of the Digest have an audio version! Search for “Dan’s MEGA65 Digest” in your favorite podcast app, or check out the audio player at the top of each issue. — Dan]
There are two methods for making sound and music with the MEGA65, as it is currently implemented. The first method is the four SID chips, programmable devices that generate waveforms with requested parameters using analog electronic components. We took a dive into the SID chips back in—November 2022?? How long have I been doing this?? …
The MEGA65 can produce sound another way. Pulse-Code Modulation (PCM) describes a waveform as a sequence of values over time, literally the shape of the desired waveform as a series of high and low numbers, as if drawn on a graph. The computer feeds these numbers into a device called a Digital-Analog Converter (DAC) that produces the waveform in that shape, as if rapidly changing the position of a speaker membrane according to each value. The MEGA65 has four DACs, and these waveforms are mixed with the rest of the audio system to produce the stereo audio output of the computer.
With PCM, a computer can reproduce real-world sounds captured by a microphone, such as human speech or musical instruments. Today, we take this extremely for granted: modern computers generate pretty much all sound using PCM waveforms. We used to call this “digitized sound,” in contrast with “synthesized sound.” Now we just call it “sound.” While PCM gives a computer program much more control over the generated sound, the trade-off is memory: relative to the memory sizes of 1980’s computers, PCM sound data takes a huge amount of space, depending on the length and quality of the sounds.
In this issue, we’ll look at how to control the MEGA65’s DACs to play digitized sound, as well as techniques for wrangling sound data for use in your programs. As usual, we’ll spend a bit too much time nerding out on theory and file formats.
Are you ready? Here we go.
Featured Files
Bomb’em All by btoschi, an explosive action game for two to four players. Drop bombs, pick up items, and break through walls while trying to trap your opponents in the blast zone. The game supports the Four Fun joystick adapter for four joysticks, or can be played with a mix of joystick and keyboard controls.
BASIC Star Galactica by jim_64, a space battle adventure. Destroy the Cylons and protect the fleet—and the future of humanity. Don’t miss the downloadable, printable disk label and manual.
fredrikr has prepared the fourth in his series of text adventure game bundles for the MEGA65, featuring modern classics from the interactive fiction community. This pack includes a variety of games released from 1995 to 2023, all playable on the MEGA65 thanks to the Ozmoo Z-machine player. (See the Digest from October 2022 for more on Ozmoo and MEGA65 adventure gaming.)
Another arcade core from muse! In Ghosts’n Goblins (1985), you are brave knight Sir Arthur, on a quest to save the Princess Prin-Prin. Don your armor—and take care not to lose it—while fighting waves of zombies, giants, demons, and other monsters. This classic from Capcom is considered one of the best video games of all time—and one of the most difficult. As with the other arcade cores, you will need to find the game ROM online, and follow the instructions to install it.
In Stranded, a graphical adventure game by Magnus Heidenborn, your boat has crashed and washed ashore a deserted island. Magnus wrote Stranded for the Commodore 64, specifically the modern TheC64 clone. Gurce ported it to the MEGA65, and added original music. I especially appreciate the novel keyboard-based linear travel and exploration mechanic, which works around common issues with point-and-click adventure games. Check it out!
Expansion board progress
Paul is making progress on the MEGA65 expansion board project. As we reviewed in a previous issue, this project intends to produce an internal hardware expansion that adds component video output, a Commodore user port, a tape port, and a port for the never-released 1565 external floppy drive that was originally intended for the Commodore 65. The goal is for these expansion boards to be made entirely out of printed circuit boards (PCBs) and common components with open source designs, so anyone can order and assemble one from any PCB fabricator.
The latest prototype includes a way to connect an ESP32 wireless Internet module (!), and routes a path for the optional JTAG adapter to a firmly mounted microUSB port accessible through the back of the computer. It also completes the 1565 disk drive port. Hopefully someday we’ll be able to make a modern recreation 1565 drive with a plastic case that matches the MEGA65.
KLF is gonna rock you
Here is a visual representation of the waveform of the first few notes of “3 am Eternal” by The KLF, performed by soul singer P. P. Arnold:
Storing and replicating this waveform exactly as it was performed would require a device and a storage medium that could capture the infinitesimal changes of air pressure in the listener’s ear over time. Digital storage media is neither infinite nor infinitesimal, so we have to make some tradeoffs when digitizing audio. How can we represent this waveform as a collection of numbers, then use those numbers to reproduce an approximation of the waveform on a loudspeaker?
Sample rate
Pulse-code modulation describes the height of a waveform measured periodically, or sampled, over time. We can’t measure every infinitesimal point in time, so we have to choose a sample rate that says how often we take a sample. For audio, a typical sample rate is on the order of kilohertz, or thousands of times per second. A smooth curve in the original waveform is approximated by a stair-step pattern in the digitized waveform.
The sample rate for digital audio is a lot like the screen resolution of a digital image. The actual image of an object as seen by the eye could have infinitesimal detail, but to capture it and represent it on a screen, it must be digitized into pixels. The smaller and closer together the pixels, the more detail can be captured and represented. You can think of sample rate as the “horizontal resolution” of the waveform.
Bit depth
We can’t measure the height of each waveform sample infinitesimally either, so this too needs a digital compromise. For example, we could store each sample as 8 bits (one byte) of data. This would give us 256 possible values for the sample. If the measured sample is between two adjacent values, it gets rounded—or quantized—to the closest value. The number of storage bits per sample is the bit depth of the sample (or sample depth). You can think of bit depth as the “vertical resolution” of the waveform.
Sample depth can also be understood by analogy with digital images. Each pixel of an image is stored with a digital value that represents its color. The number of possible colors it can use is limited by the bit depth of the color value. Naturally, the more colors the computer can use for a pixel, the more accurately it can represent the original image.
Signed vs. unsigned samples
Conceptually, an audio waveform is a variance in the regular air pressure of a silent room, wiggling positive and negative in a pattern until stabilizing back to zero. You can see the middle line on these waveform diagrams where this level lies. Based on this, waveform samples could be represented as a signed number, with some negative numbers and some positive numbers around a baseline of zero. Or, it could be represented as an unsigned number, where the baseline is the middle value of a range of positive numbers.
A signed 16-bit sample has a range of -32,768 to +32,767. It is usually stored as a two’s complement value, $8000 (-32,768) up to $FFFF (-1), then $0000 (0) up to $7FFF (32,767). An unsigned 8-bit sample has a range of 0 to 255, where 128 is the baseline of the waveform. It is stored simply as its byte value, $00 (0) to $FF (255). If you’re messing with sample data, you need to know both its bit depth and whether it is signed or unsigned to interpret it correctly.
Size vs. audio quality
Both sample rate and bit depth explain how the amount of data relates to the quality of the reproduction of the original sound. The faster we sample, the more samples we collect, and the more accurate the data is to the original sound. The higher the bit depth of the sample, the more memory is required for each sample, and the more accurate the sample is to the waveform.
Before there were large hard drives, fast Internet transmission speeds, and data compression algorithms tuned for audio, digital music could only be practically distributed on optical fixed-storage compact discs (or “CDs,” as we called them). We purchased these discs at retail establishments with names such as “Tower Records,” and ordered them from printed catalogs to be delivered to our home via postal service from Columbia House at an introductory cost of 10 discs for 1 penny. A typical audio CD stored 74 minutes of uncompressed stereo sound data, using a sample rate of 44.1 kHz and a depth of 16 bits.
We can calculate the amount of sample data on an audio CD with a simple multiplication: 74 minutes x 60 seconds per minute x 44,100 samples per second x 2 bytes per sample x one sample for each stereo channel (2) = 783,216,000 bytes, or almost 747 MiB. Old-timers may remember that a CD-ROM, which uses the same type of disc for storing computer files, has a capacity of 650 MiB. CD-ROMs use the rest of the space for error correction and addressing, so computers can find files and make sure they read them correctly. When an audio CD player reads a sample incorrectly, it just pretends nothing happened and keeps going, so it can use more space for samples.
For perspective, at CD quality, the MEGA65’s 8 megabytes of Attic RAM can contain 45 seconds of audio. As we’ll soon see, we’re actually limited to 64 kilobytes at a time, which would last us 0.35 seconds at 44.1 kHz 16-bit. We’ll need to make some tradeoffs in quality and duration to make practical use of digitized sound.
The following are the durations of 64 kilobytes of sample data at various sample rates and bit depths:
4000 Hz, 8-bit: 16.384 seconds
4000 Hz, 16-bit: 8.192 seconds
8000 Hz, 8-bit: 8.192 seconds
8000 Hz, 16-bit: 4.096 seconds
11025 Hz, 8-bit: 5.944 seconds
11025 Hz, 16-bit: 2.972 seconds
16000 Hz, 8-bit: 4.096 seconds
16000 Hz, 16-bit: 2.048 seconds
MEGA65 Audio DMAgic
Imagine we had a way to control the speaker membrane directly from a MEGA65 program using a hardware register. What would the program have to do to play back a short digitized sound? The program would write each PCM sample to the register, and it would have to do it at a consistent rate that matches the sample rate. If the sound was digitized at 8 kHz, this program would have to write sample data to the register 8,000 times per second, at even intervals. This is entirely possible with a 40 MHz CPU, but getting the timing right would be a significant programming challenge, especially if the program does other things besides play the sound.
Thankfully, the MEGA65 can help us with this. Reading a series of values from one memory location and writing them to another is the bread and butter of the Direct Memory Access (DMA) chip, a purpose-built chip in the Commodore 65 that the development team called “DMAgic.” DMA is typically used to copy one region of memory to another location very quickly, or fill a region with a value. Normally it does this as quickly as possible. The MEGA65 has enhanced DMAgic with a mechanism for playing audio samples to the DAC at a programmable sample rate, controlled with hardware registers. Your program can even do other things while the sample is playing: playback happens concurrently with program execution.
Because sample timing is managed for us by audio DMA, we don’t need to worry about our program being fast or accurately timed. You can even use audio DMA from BASIC!
Audio DMA specs and techniques
For audio DMA to work its “DMAgic,” a few things must be true:
The CPU must be running at 40 MHz. (This is the default.)
Sample data must be in main memory, not Attic RAM.
Samples must use a bit depth of 16, 8, or 4. Values can be signed or unsigned.
The playback region cannot exceed 64 KB.
At first glance, this seems a bit limiting, but it’s actually quite powerful with a few tricks. For example, while sample data must be in the first 384 KB of main memory during playback, your program can store its samples in the 8 MB of Attic RAM, then use a DMA copy operation to install it in main memory to be played.
4-bit samples are stored in an interesting way: the DMA must advance through memory by at least one byte per sample, so 4-bit samples use either the top four bits or the bottom four bits of each byte, and you tell audio DMA which bits to use when you play the sound. This means you can have two 4-bit sounds stored in the same bytes, one in the top bits and another in the bottom bits.
When you play a sound, you can tell audio DMA to stop at the end, or loop back to the beginning automatically and continue playing. Looping is great for short repeating waveforms, or long repeating patterns like drum beats or atmospheric sounds.
With a bit of cleverness, you can play sample data larger than 64 KB by taking advantage of the looping feature. Your program can copy new sample data into the memory region as the sound is being played, so when the playhead loops back to the beginning, it starts playing the new data.
Your program specifies the sample rate to use the play the samples. If you want the samples to sound like the original recording, you play the samples at the rate they were digitized. You can also play the samples at a slower or faster rate to change the pitch and speed of the sample, like spinning a record player at a faster or slower speed. —Oh, uh, records were another way to distribute music before CDs, as analog waveforms etched into grooves on vinyl discs. Sounds were reproduced mechanically by vibrating a needle called a stylus, by running it across the etched groove at a steady speed. Because retro is cool, vinyl album sales have seen a rapid resurgence recently that is expected to grow in 2024, while sales of CDs have dropped in favor of online music streaming.
A synthetic example
Before we figure out how we get recorded sound data into the MEGA65, let’s try out the audio DMA system with some made-up numbers. We’ll use BASIC here, but the procedure is the same in assembly language: we’re just writing to memory and registers. (Try porting this example to assembly language!)
The waveform
To start, let’s draw a simple “sawtooth” waveform into some memory. This will just be the numbers 0 to 255 across 256 bytes of memory, which we will play looped.
10 FOR X=0 TO 255 : POKE $50000+X,X : NEXT X
Channel registers
Each of the four audio DMA channels has 16 bytes of registers. Channel 0’s registers are at addresses $D720 to $D72F, channel 1’s are at $D730 to $D73F, channel 2’s are at $D740 to $D74F, and channel 3’s are at $D750 to $D75F. I’ll refer to each register by its offset $0 through $F. Just know that the registers have the same layout for all four channels.
Switch off channel 0 while we’re setting it up, by writing a 0 to register $0:
20 POKE $D720,0
Enabling the audio DMA system
The audio DMA system must be enabled before it can be used. This is a global setting, address $D711 bit 7:
30 SETBIT $D711,7
Sample addresses
The starting address of the sample is a 24-bit value, across registers $1 to $3. We also want to set the channel 0 playhead, a 24-bit value across registers $A to $C. (Remember, these are all Little Endian.) The starting address is used for looping: when the playhead reaches the end, it’ll jump back to the start address.
40 POKE $D721,$00:POKE $D722,$00:POKE $D723,$05 : REM SAMPLE STARTS AT $5.000050 POKE $D72A,$00:POKE $D72B,$00:POKE $D72C,$05 : REM SET PLAYHEAD TO BEGINNING
The end of the sample is stored as a 16-bit value equal to the lower 16 bits of the last address, in registers $7 to $8. In our case, the end address is $5.00FF, so the register value is $00FF.
60 POKE $D727,$FF:POKE $D728,$00
Channel volume
Each audio DMA channel gets two volume settings, with the ability to pan the channel’s output to the Left DAC or the Right DAC in different proportions.
Channels 0 and 1 are considered “left” channels, and channels 2 and 3 are considered “right” channels. The $9 register in each channel’s register bank sets the volume of the signal that it outputs to its device, either the Left DAC or the Right DAC. The register value is between 0 and 255, with 255 being the loudest.
Another set of volume registers allow for each channel to also output to the opposing DAC. Registers $D71C and $D71D set the output of channels 0 and 1, respectively, to the Right DAC. Registers $D71E and $D71F set the output of channels 2 and 3, respectively, to the Left DAC.
This can get a little confusing because the user can subsequently pan the Left DAC or the Right DAC using the Audio & Volume tool in the Freezer, and can even flip the stereo field entirely. If you want to experiment with this, I recommend opening the Freezer tool and setting the “Left Digi” volume up in the left output channel and all the way down in the right output channel, and the “Right Digi” volume down in the left and up in the right, so you can hear clearly through stereo speakers which DAC is being used.
Let’s just set channel 0 to output evenly to both DACs for now. This sawtooth wave is a bit obnoxious, so I'm using a low volume level:
70 POKE $D71C,$22 : POKE $D729,$22
Headphone warning: If you are listening to your MEGA65 with headphones connected directly to the 3.5mm jack, I recommend reducing the master volume in the Audio & Volume tool while you are testing digital audio samples. Incorrect sample data or erroneous register values could cause unexpectedly loud sounds. I prefer to use a set of speakers with an independent volume knob set to a low setting. (Headphones tend not to have their own volume control.)
Sample rate
The sample rate (frequency) is stored as a 24-bit value in registers $4 to $6. This one is a bit weird, but it’ll make sense, I promise.
The value is the 24-bit proportional value of the frequency relative to the CPU speed. For example, if the sample rate is 16 kHz, then the register value is 16 kHz divided by 40 MHz (a ratio), multiplied by 2 to the 24th power minus 1 (the largest 24-bit value). Watch those units when doing the math! The CPU is much faster than the sample rate, so you should get a very small ratio.
Here are the register values for some useful sample rates, pre-calculated for your convenience:
4 kHz : 1,662 ($00067E)
8 kHz : 3,324 ($000CFC)
16 kHz : 6,648 ($0019F8)
32 kHz : 13,296 ($0033F0)
For our experiment, we actually want to do something special with the frequency. We have a very short sawtooth waveform, capable of playing a musical note. The middle “A” key on a piano would play this pattern 440 times per second (440 Hz). We have 256 samples, so to achieve a middle A pitch, we need a sample rate of 440 x 256 = 112640 Hz = 112.64 kHz. Proportional to the 40 MHz CPU speed, the 24-bit value is: 47,244 decimal, or $00B88C (that’s “zero zero Baker eight eight Charlie”).
Someone check my math.
80 POKE $D724,$8C:POKE $D725,$B8:POKE $D726,$00 : REM SAMPLE RATE 112.64 KHZ
Sample sign, bit depth, looping, and playback
Finally, we can trigger the sample, with a few last settings in register $0. This register uses the following bits as flags:
Bit 7: Play it.
Bit 6: Loop it. We want this to loop our very short sawtooth wave.
Bit 5: Set this to 0 if the sample value is signed. We’re using unsigned values in this example, so we set this to 1.
Bits 1-0: The bit depth: 11=16 bits, 10=8 bits (our choice), 01=upper 4 bits, 00=lower 4 bits.
90 POKE $D720, $80 + $40 + $20 + $02100 GETKEY A$ : REM WAIT FOR A KEYPRESS110 POKE $D720,0 : REM SWITCH IT OFF! SWITCH IT OFF!
Try it out!
Run this to hear the tone. It’s a bit obnoxious, so press a key to switch it off. If you accidentally press Run/Stop with the sound still playing, use POKE $D720,0 to disable the sound.
You can experiment with different waveforms by writing different values to $5.0000-$5.00FF. Try these:
10 FOR X=0 TO 255 : POKE $50000+X,255(SGN(X-128)+1) : NEXT X10 FOR X=0 TO 255 : POKE $50000+X,X-(X-128)(X>128) : NEXT X10 FOR X=0 TO 255 : POKE $50000+X,127SIN(X/2553.1415)+128 : NEXT X
Converting audio files to raw PCM data
This little experiment to play triangle, sawtooth, and sine waveforms is fun, but not particularly impressive considering that the SID chips can already generate these waveforms on their own. To unleash the true power of audio DMA, we need PCM sample data for more interesting sounds. The MEGA65 does not have a built-in audio digitizer (at least not yet), so we must turn to our trusty modern computers.
There are many ways to record, produce, or otherwise acquire sound files with a modern computer and an Internet connection. Your computer’s operating system probably includes a free sound recording or editing program, and so does your mobile phone. Sound editing software like Audacity is free, and you can also download free sound files from FreeSound.org. Whatever software you’re using, be sure to edit your sound file to trim it to just the length you want your MEGA65 program to play.
Audacity
Our goal is to convert the sound file to raw PCM sample data at an appropriate sample rate, and at a bit depth supported by the MEGA65 audio DMA. Audacity can do all of this in a single export step:
File menu, Export Audio.
Format: “Other uncompressed files”
Channels: Mono
Sample Rate: 8000 Hz or higher, or select “Other” and enter any rate in Hertz
Header: “RAW (header-less)”
Encoding: “Signed 16-bit PCM” or “Unsigned 8-bit PCM”
Click Export.
Remember that sample rate and bit depth affect data size and sound quality, so choose wisely.
Other modern sound production software may not have all of the same export options. Some of the more expensive software packages on my computer can’t believe I would ever want to export at a sample rate lower than 44.1 kHz. If your software doesn’t export raw PCM sample data, or doesn’t offer the desired sample rate or bit depth, export a .wav file with high quality settings, then use Audacity for the final conversion to raw PCM data with the desired parameters.
It’s important to use the highest quality settings for every processing step until the last one. Because each digital audio file is an approximation of the original waveform, the conversion tool has to use some math to assume the gaps between the samples when converting from one sample rate to another. The more data that goes into that process, the better.
While preparing this Digest, I had occasional issues with Audacity trying to boost the volume of my samples, causing clipping of the signal in some cases that added loud pops to the final sound effect. Notably, this clipping only happened on real hardware and not in Xemu, so be sure to test your samples.
ffmpeg
Audacity is fine, but I like command line tools so I can automate build workflows. The tool for this is ffmpeg, an essential free media conversion tool for video and audio. It’s similar to how we used ImageMagick to convert still images back in a previous Digest. ffmpeg is available for all platforms. On a Mac using Homebrew, install this with: brew install ffmpeg
ffmpeg accepts most audio files as input, including .wav files. Specify the input file with the -i parameter. It’ll detect everything it needs to know about the input data from the file itself.
For the output file, we want something special, so we need to tell it what we want. The -f option selects the bit rate, selected from this list:
s16le : signed 16-bit Little Endian
u16le : unsigned 16-bit Little Endian
u8 : unsigned 8-bit
To convert the file to a different sample rate, use the -ar option, and provide the value in Hertz. To convert a stereo sound to monophonic, specify -ac 1.
ffmpeg -i klf.wav -f u8 -ar 8000 -ac 1 klf.raw
You now have a raw PCM data file. Assuming it’s small enough, you can now copy this to a D81 disk image, and transfer the disk image to your MEGA65. See a previous Digest for more information on how to manage files and disk images.
Playing the digitized sound
The BASIC program above can be modified to play an arbitrary sample file. Replace line 10 with a BLOAD command that loads your sample file to address $50000:
10 BLOAD "KLF.RAW",P($0050000)
You will need to adjust the sample length (line 60) and the sample rate (line 80) as appropriate for your data. You might want to remove the loop flag ($40) from the playback command (line 90). Only include the “unsigned” flag ($20 on line 90) if the sample data is unsigned.
Can you hear the sound, but it’s noisy or crackling? Double-check the “unsigned” bit. Using the unsigned bit when the sample data is signed (and vice versa) will cause transitions from negative to positive values to pop, in a bad way.
What about 4-bit sample data?
None of the audio processing programs I tried offer export of 4-bit samples. How can we make 4-bit samples, to take advantage of this feature of audio DMA?
Compared to something as complex as re-sampling at different sample rates, converting from an unsigned 8-bit sample to an unsigned 4-bit sample is quite easy. We just lop off the lower four bits, equivalent to dividing by 16. Here’s a simple Python program that does this:
INPUT_FILE = 'klf_8b.raw'OUTPUT_FILE = 'klf_4b.raw'data_input = open(INPUT_FILE, 'rb').read()with open(OUTPUT_FILE, 'wb') as outfile: for i in range(len(data_input)): samp = int(data_input) // 16 outfile.write(samp)
This takes the file klf_8b.raw containing unsigned 8-bit samples and produces the file klf_4b.raw containing unsigned 4-bit samples, with each sample in the lower four bits. We can modify our playback program to use this by setting the appropriate flags in the $0 register:
90 POKE $D720, $80 + $20 : REM PLAY IT ONCE, UNSIGNED, 4-BIT LOW
Audio DMA requires that each sample be at separate memory locations, so merely converting a sample from 8-bit to 4-bit doesn’t actually save any space. The space savings comes from storing two 4-bit samples on top of each other, and telling audio DMA to play either the bottom four bits or the top four bits. We can modify this Python script to store two samples together in this way:
INPUT_FILE_ONE = 'klf_8b.raw'INPUT_FILE_TWO = 'rockyou_8b.raw'OUTPUT_FILE = 'klf_4b.raw'data_one = open(INPUT_FILE_ONE, 'rb').read()data_two = open(INPUT_FILE_TWO, 'rb').read()# Pad the shorter sample with zeroes, so they are the same lengthif len(data_one) < len(data_two): data_one += b'\x00' * (len(data_two) - len(data_one))elif len(data_two) < len(data_one): data_two += b'\x00' * (len(data_one) - len(data_two))with open(OUTPUT_FILE, 'wb') as outfile: for i in range(len(data_one)): samp_one = int(data_one[i]) // 16 samp_two = int(data_two[i]) // 16 result = samp_two * 16 + samp_one outfile.write(bytes([result]))
This takes the two 8-bit sample files klf_8b.raw and rockyou_8b.raw, converts them both to 4-bit, then writes them to a single file klf_4b.raw with klf in the lower bits and rockyou in the upper bits.
To tell the playback program which sample to play, set bits 1 and 0 of $D720 accordingly:
90 POKE $D720, $80 + $20 : REM PLAY IT ONCE, UNSIGNED, 4-BIT LOW90 POKE $D720, $80 + $20 + $01 : REM PLAY IT ONCE, UNSIGNED, 4-BIT HIGH
I have a vivid memory of hearing The KLF’s “3 a.m. Eternal” played on my childhood Amiga 500. Like the MEGA65, the Amiga can play short digitized sounds over four audio DACs. Most Amiga owners didn’t have enough memory to store several minutes of digitized sound, so another kind of music was born. A MOD file encodes music as short digitized sounds played in a pattern of frequencies and timings, saving memory by re-using samples throughout the song. The KLF’s music was largely sample-based and repetitive to begin with, and they even produced some of it on home computers like the Apple II and Atari ST. MOD files can come close to reproducing the original songs within the memory limitations of a vintage computer.
Your MEGA65 includes a MOD player on the Intro Disk, called “Manche,” that uses audio DMA to play sound samples. I found one person’s attempt at a MOD of “3 a.m. Eternal” and was able to play it in Manche on my MEGA65. I can’t say this particular MOD is my favorite remix, but it’s been fun to revive this memory.
Here are a few MOD arrangements of KLF songs for you to enjoy:
Download: 3ametern.mod
Download: whattime.mod
Download: doctorin.mod
Put these in the root of your SD card, then start Intro Disk #1. Open the Music menu, select Manche, and press Return to run the program. Within Manche, press F1, type one of the filenames, then press Return to load the file. Press Space to start playback.
I also made this demonstration disk with the waveform generator program from this issue, as well as a program demonstrating the same sound at different sample rates and bit depths.
This Digest is made possible with support from justified ancients, like you! To contribute, visit ko-fi.com/dddaaannn.
See you next month!
— Dan
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
Spaceships. Aliens. Marios. Goombas. Bullets. Fireballs. Mouse pointers, text cursors. Any of these could be a sprite, a feature of a computer graphics system dedicated to things that move. The sprite capabilities of the Commodore 64’s VIC-II chip super-charged video games and user interfaces beyond a single screenful of character text or a bitmap image. The MEGA65 includes support for VIC-II hardware sprites, and has sprite-related BASIC commands that make them easy to use in your programs.
In this Digest, we’ll review the VIC-II sprite system’s capabilities, try out the sprite features added to Commodore BASIC for the C128, C65, and MEGA65, and step through a development workflow for using sprites in BASIC games. And we’ll try putting these pieces together to make a simple arcade game.
But first…
The User’s Guide, 2nd edition, now available
You can now buy a spiral-bound printed copy of the MEGA65 User’s Guide, 2nd edition!
This new edition has been updated substantially from the 1st edition printing from two years ago. It covers the upcoming v0.96 release, with instructions for new features like Ethernet file transfer, and revised information on important topics like upgrading the firmware and using disks. The BASIC reference has been updated with corrections, polish, and material on new features. And there are handy new appendices on screen codes and system colors.
Whether you have the 1st edition and want to upgrade, or don’t yet have a MEGA65 and just want a useful book to go with Xemu, getting the 2nd edition in print is a great way to enhance your MEGA65 experience.
I wrote a FAQ with more information, including what’s happening with the manuals bundled with new MEGA65s. Let me know if you have any questions.
Release testing update
The v0.96 release package has been in public testing for a month, and we’ve been chasing down issues and polishing it up for factory installation on all of the new MEGA65s. This process should be complete a week or so after you read this. Many thanks to everyone who has contributed to the testing effort!
What happens next: the v0.96 release package will be made official and sent to Trenz for the factory installation on new machines. It will be declared the new stable release for R3A and R6 mainboards, and made available on Filehost. Everyone with the “retail” MEGA65 will be encouraged to upgrade.
DevKit and Nexys board owners will need to wait, just a bit. In order to meet the assembly schedule, we have had to defer preparing new cores for these boards until after this release. The plan is to immediately start work on these deferred tasks, and issue a v0.97 update within a couple of months that includes support for these boards. You’re always welcome to help test along the way, just be aware of known issues with slot 0 flashing on the older boards.
Upgrade party at lydon’s place! 🎉
New Intro Disks!
Gurce has put a ton of work into preparing the new software bundle for the SD card that will ship with the new MEGAs. Current owners are familiar with the “Intro Disk” menu that starts when you first switch on the computer, as well as other bonus goodies that come on the pre-installed memory card. The new bundle includes all of that and much more, a total of 191 (one hundred and ninety one!) menu entries. And that’s not even counting the disk menus themselves, with useful information and musical accompaniment. This compilation starts all new MEGA65 owners off with plenty to do on their first day. Huge thanks to Gurce for his meticulous work on this project, and of course to everyone who has written software for the MEGA65.
You can download the new software bundle on Filehost. (There’s a separate download for registered owners with a more complete version of GEOS.) If you don’t yet have a MEGA65, you can try it out in Xemu—or save it as a surprise for when your MEGA65 arrives!
New on Filehost
Don’t miss these new titles on Filehost:
Rocket Delivery Service, by fredrikr, a new game for one or two players. Navigate your rocket man to pick up and deliver packages, for cash! Each player can use a joystick, or player one can use the keyboard while player two uses a joystick. Written entirely in BASIC!
MJoergen and sy2002 are updating their vintage platform cores for use on the new R6 mainboard. The ZX Spectrum core v1.2 is now available, and there is a beta test version of the C64 core v5.1 (link goes to Discord) to be released soon. The C64 core update also includes a few fixes for all mainboards, and support for using the MEGA65’s Real-Time Clock with the Commodore 64 version of GEOS.
muse has yet another arcade core, and he keeps picking my favorites! Elevator Action (1983), a spy thriller set in a multi-level top secret facility. Infiltrate the building from the roof, avoid the armed goons, collect the secret files, and escape through the parking garage. As with the other arcade cores, you must locate the arcade ROMs yourself, so be sure to follow the installation instructions. Hold the button to jump.
Amiga fans: geehaf has published an ADF disk image utility that can use the MEGA65’s physical floppy drive to make Amiga disks from ADF files, and vice versa. This is a beta release of the tool, so take care to back up important data.
Exploring sprites
The hardware sprites of the VIC-II chip should be familiar to anyone who has written a program for the Commodore 64. The Commodore 64 Users Guide that came with the machine included a chapter on sprite graphics with a type-in demo. C64 programmers may also remember drawing out pictures on graph paper, hand-coding them into decimal numbers, typing them into DATA statements, and messing about with dozens of arcane POKE statements. In later computers and later versions of BASIC, Commodore added a BASIC sprite subsystem with access to most of the VIC-II’s sprite features, driven by a new set of commands. No POKE-ing necessary.
From here on, when I say “sprites,” I’m referring to the built-in sprite features of the VIC-II chip that have carried forward from the Commodore 64 to the MEGA65. Experienced game programmers would want me to point out that the general term “sprite” could apply to any kind of moveable graphics object, and there are, in fact, other ways to accomplish sprite-like effects on the MEGA65. We’ll have to cover those in later issues of the Digest.
The best way to get to know Commodore sprites is to try out the commands! The new v0.96 release version of the MEGA65 ROM includes pre-defined sprite images for a mouse pointer and a simple line pattern, so you can get started without drawing any graphics. If you’re using the previous release, you can still switch on the sprites, you’ll just see a blob of pixels that represents uninitialized memory contents.
Getting a sprite onto the screen
The VIC chip can keep track of up to eight sprites at a time, numbered 0 through 7. Each sprite has a set of modes and flags, including a flag to enable or disable the sprite. You manage these flags with the SPRITE command.
To turn on sprite 0:
SPRITE 0,1
Nothing appears to happen because the sprite starts located off the screen. Let’s move it roughly to the middle of the screen so we can see it (X=160, Y=120):
MOVSPR 0,160,120
While many of BASIC 65’s sprite commands originate with BASIC 7 and the Commodore 128, there are subtle differences. For example, on the C128, sprites are numbered 1 through 8. I won’t describe every difference here, just be aware that they’re not exactly the same on both computers.
Pixels and colors
In the default sprite mode (sometimes called “high resolution” mode), each sprite is 24 pixels wide and 21 pixels high, using a single sprite-assignable color for each “on” pixel (bit 1) and treating each “off” pixel (bit 0) as transparent. This is encoded as three bytes per row (8 bits per byte x 3 bytes = 24 pixels), for a total of 63 bytes (21 rows x 3 bytes per row) per sprite image.
You can set the color of a sprite with the SPRITE command. You can omit any argument you don’t want to change by specifying just a comma. The color value refers to the palette entry, in this case the default system palette. To turn sprite 0 red (palette entry 2):
SPRITE 0,,2
On the MEGA65, you can change the system palette using the PALETTE COLOR command. Each palette entry can be any of 4096 possible colors, with a red, green, and blue component each between 0 and 15. This changes the palette entry for all uses of the color on the screen, not just sprites. To replace the red color at entry 2 with a bright green:
PALETTE COLOR 2,12,15,4
To restore the default palette:
PALETTE RESTORE
Multicolor mode
If you’re willing to use a lower sprite resolution of 12 double-wide pixels per row instead of 24 single-wide pixels, you can set a sprite to multi-color mode, for two more additional colors. The two new colors must be shared by all sprites that are in multi-color mode. In this mode, sprite pixels are encoded as bit pairs: 00 for transparent, 10 for the sprite color, and 01 and 11 for the two shared colors.
Let’s try setting sprite 0 to multi-color mode, using a value of 1 for the seventh argument to SPRITE:
SPRITE 0,,,,,,1
If you’re using a ROM beta version, the default mouse pointer image of sprite 0 might not look that impressive in multi-color mode. Try enabling sprite 1 and moving it to the middle of the screen, then put it in multicolor mode. The default image of sprite 1 is designed to illustrate multicolor mode, with a repeating pattern of 00, 01, 10, and 11 bit pairs. (If you’re using release v0.95’s ROM 920377 or earlier, you’re in luck: the garbage data that’s usually there typically has an interesting combination of bit pairs.)
To set the two shared colors for the multicolor sprites, use the SPRCOLOR command, with the two palette entry numbers:
SPRCOLOR 4,5
Double width, double height
Each sprite can be displayed normal size, double width, double height, or both double width and double height. The double modes don’t give you more pixels: instead, they stretch the existing sprite design in the requested dimension.
These modes can be selected per sprite, and are the fifth and sixth arguments to the SPRITE command, respectively. Try the combinations of 0 and 1 for these arguments:
SPRITE 0,,,,1,1
Sprite priority
Each sprite can be set to appear above the character or bitmap layer, or below it. When above, you can see the character or bitmap through the sprite’s transparent pixels.
Use the cursor keys to move the text cursor to where the sprite is on the screen, then type some characters underneath it. On a blank line, set the fourth argument to SPRITE to 0 or 1, and see what happens:
SPRITE 0,,,1
When sprites overlap each other, lower numbered sprites are always in front of higher numbered sprites. This order cannot be changed. Use SPRITE to make sure two sprites are active, then use MOVSPR to locate them so that their images overlap.
SPRITE 1,1MOVSPR 1,160,120
Overlapping sprites is a common way to draw a high resolution graphics object using more than one color: just use more than one sprite to represent the object, and allow pixels of lower sprites to shine through the transparent pixels of upper sprites.
Location, location, location
A sprite can be located at any pixel location on the screen. A sprite’s location is specified as coordinates on the screen, where the sprite’s top-left corner is placed at that location. Coordinates extend to locations under the screen border, and a sprite that overlaps the border will be obscured. This allows a sprite to glide off the screen smoothly until it disappears. (Note that double-width sprites won’t fit completely under the left border.)
To place a sprite exactly in the top-left corner of the screen just inside the border, set its coordinates to X=24 Y=50. If a sprite is in its normal display mode (with its double-width double-height features turned off), you can place the sprite’s bottom-right corner exactly at the bottom-right corner of the screen with coordinates X=321 Y=229.
The VIC-II sprite coordinate system is independent of the resolution of the underlying screen mode. The MEGA65’s default 80-column text screen is actually 640 pixels wide. VIC-II sprite coordinates refer to the same locations regardless of the MEGA65 screen resolution. If you switch to the 40-column text mode, the sprites will remain where they are.
Sprite coordinates are the same for both NTSC and PAL video modes. Even though these modes use different dimensions for the full screen, the dimensions inside the border are the same.
We’ve already seen how the MOVSPR command can set a sprite to specific coordinates. This command has several other useful features. To move a sprite to a relative distance from its current location, use a + or - and an offset for the coordinate value (X, then Y):
MOVSPR 0,+20,-10
The MOVSPR command can animate the movement of a sprite at a given speed. This animation happens asynchronously to BASIC program execution, similar to the PLAY command for music: the animation occurs in the background while the rest of your code continues to run. To move a sprite from one coordinate pair to another, use the TO keyword, and provide a speed as a number of pixels per frame. Remember that PAL mode refreshes 50 frames per second, while NTSC mode refreshes 60 frames per second, so the movement speed differs based on video mode.
MOVSPR 0,100,100 TO 250,200,5
Note that the sprite needs to hit the end coordinate almost exactly for the sprite to stop. If the speed is not a multiple of the differences between the starting and ending coordinates, the sprite might overshoot its destination and wrap around the screen until it finally lands where it was told. If there’s a chance of this, you may need to resort to a loop that tests RSPPOS() and stops the sprite with another MOVSPR command when it is close enough.
A sprite can be set to move continuously, with no fixed end coordinate. To do this, provide an angle as a number of degrees, with 0 degrees being “up” and proceeding clockwise, so 90 is right, 180 is down, and so forth. Follow this with a # symbol and the speed.
MOVSPR 0,135#1
The sprite will continue to move at this angle and speed until you set its speed to 0 with another MOVSPR command, or until you switch it off with the SPRITE command.
MOVSPR 0,0#0
When a moving sprite leaves the screen, it will continue to move until its position wraps around to the other side of the screen. If you want to test the position of the moving sprite, such as to stop its motion after it is off screen, use the RSPPOS() function. This takes the sprite number, and an argument identifying the parameter to return: 0 for the X position, 1 for the Y position, 2 for the speed.
Here’s a short program that starts the sprite moving to the right, then stops it either when a key is pressed or the sprite leaves the screen:
10 SPRITE 0,120 MOVSPR 0,100,10030 MOVSPR 0,90#140 GET A$50 X=RSPPOS(0,0)60 IF A$="" AND X < 345 THEN 4070 MOVSPR 0,0#0
Collision detection
The VIC chip can report when two sprites have non-transparent pixels on top of each other, or on top of the character or bitmap layer. Your program can use this to detect collisions between sprites, or between a sprite and the background, such as to know when a player’s bullet hits an alien ship, or when the player’s ship crashes into a mountain.
BASIC 7 on the Commdoore 128 introduced an interesting mechanism for handling sprite collisions. To tell BASIC that you care about sprite collisions, you use the COLLISION command to indicate the type of collision (1 = sprite on sprite, 2 = sprite on background), followed by a line number of your BASIC program. At some later point, when the VIC detects the requested collision type, BASIC will stop whatever it is doing and jump to the given line number as if it were a GOSUB subroutine. Your code at this line number should do whatever it needs to do to react to the collision, then use the RETURN command to resume where the program left off.
Your handler subroutine is called for any collision of the requested type. Your code will need to use other tests to determine which collision took place. The BUMP() function reports which sprites were involved in a collision since the last call to BUMP(). You can also use the RSPPOS() function to test individual sprite locations to further determine where the collision took place. See the User’s Guide for information on these functions.
Try to guess what this program does, then type it in and run it:
100 SCNCLR110 X=0120 SPRITE 0,1130 MOVSPR 0,100,100140 MOVSPR 0,90#1150 CURSOR 70,6160 PRINT "XXX"170 COLLISION 2,220180 GET A$190 IF A$="" AND X=0 THEN 180200 MOVSPR 0,0#0210 END220 PRINT "BLAMMO!"230 X=1240 RETURN
The VIC’s built-in collision detection is not always the most reliable way to detect game events. Depending on the shapes of your sprites and other factors, you may prefer a different method to detect collisions, such as by testing sprite positions relative to a hitbox. To do this, instead of a COLLISION handler, you’d make repeated tests of RSPPOS() in your game’s main loop.
Another caveat: There is a known issue with the VIC-IV core where sprite collisions are not detected with bitmap graphics (the BASIC 65 SCREEN system). Collisions are limited to sprite-to-sprite and sprite-to-text for now. Of course, there’s quite a bit you can do graphically in text mode using PETSCII graphics, and custom character sets with the CHARDEF command.
Other sprite features?
If you grew up with a Commodore 64, these sprite features probably feel quite familiar. The C128 sprite commands, and the C65 and MEGA65 additions, are handy, but the sprites themselves mostly look like they did on the VIC-II.
For the VIC-III video chip in the Commodore 65, Commodore added no new features to sprites. Sprite colors use the VIC-III system palette, and you can adjust the colors of the system palette to any of 4096 possible colors. Otherwise, they’re basically the same as they’ve always been.
The MEGA65’s VIC-IV adds advanced sprite features, such as a full-color mode with actual-size pixels, four palette banks, a 64-pixel width monochromatic mode, variable pixel height, and high resolution sprites that use the actual pixel width and coordinate system of a 640-wide (80-column) display. BASIC 65 does not yet support any of these features with the BASIC sprite commands. You can adjust VIC-IV registers directly with POKE statements, but this may interfere with the behavior of the BASIC commands. I’m currently working to extend BASIC 65 with official support for VIC-IV sprite features, so stay tuned for updates!
Managing sprite image data
Playing with the built-in sprites is fun and all, but for our own programs, we obviously want to draw our own sprite images.
Using the Sprite Editor
Did you know that the Commodore 128 has a built-in sprite editor? The C128’s SPRDEF command opens a sprite editor that directly manipulates the BASIC sprite image data in memory. For whatever reason, Commodore removed SPRDEF for BASIC 10. The good news is, the MEGA65 has its own sprite editor, “SpritEd,” built into the Freezer.
Hold Restore for one second then release to open the Freezer, then press S to start the sprite editor. This utility edits sprite image data at the BASIC sprite locations in memory.
A word of warning. SpritEd is currently unfinished software. Up to platform release v0.95, SpritEd crashes the machine if you press the Up Arrow key. I discovered this while writing this article, and I committed a fix to prevent this that will be in release v0.96. I recommend upgrading to release v0.96 before doing any serious work in SpritEd.
SpritEd is missing a bunch of its intended features, such as the ability to load and save images to disk. Even without these features, it is about as useful as the C128’s SPRDEF tool, and can edit sprite image data in memory. We’ll see how to get sprite image data from memory into your program in the next section.
Press the Help key for a list of keyboard controls. Briefly, these are as follows:
To select a drawing tool: P for single pixels, L for a line, X for a box, Shift-X for a filled box.
Press Space to draw with the selected tool. Del uses the tool with the background component, such as to erase a pixel.
To erase the entire sprite: Ctrl+N.
To select a “component” (color) to draw with: + and - rotate through the choices. In monochrome mode, this is just the background (transparent) and foreground. In VIC-II double-width multi-color mode, there are two additional components for the shared sprite colors.
To change the color of the currently selected component: 0 through 9, A through F assigns one of the sixteen palette entries to the component.
To select other sprites: comma (,) and period (.) rotate through the eight sprites.
To change the sprite mode: * rotates through monochrome, VIC-II multicolor, and VIC-IV multicolor modes. Remember that BASIC does not yet support VIC-IV multicolor mode directly, so this last mode won’t be useful until we finish the new features.
To see what your sprite looks like when expanded horizontally or vertically: use H and V to toggle these modes.
SpritEd supports drawing with a mouse connected to port 1. I discovered a small bug where the mouse can’t draw in the rightmost column, which should be easy to fix in a future version.
To exit SpritEd, press F11 to make sure the current sprite is saved to memory, then press F3 to exit back to the Freezer menu. Each sprite image is saved to memory automatically when you flip between the sprites, so F11 is only necessary on the current sprite, if it was modified. (That feels like something else we can improve pretty easily. Just know that’s how it works for now.)
Storing sprite image data on disk
The gorgeous sprite graphics we created in SpritEd are now in memory, but they’ll disappear forever if we don’t get them out of memory and onto disk. BASIC 65 makes this easy: SPRITE SAVE "filename" saves all eight sprite images to a file on disk. SPRITE LOAD "filename" loads them back in.
This provides a POKE-free workflow for managing sprites for BASIC programs:
Create sprite images in the SpritEd utility. Press F11, then F3 to exit back to the Freezer menu, then F3 again to resume BASIC.
Execute the SPRITE SAVE "filename" command to save all eight sprite images to a file.
Then in the program, use the SPRITE LOAD "filename" command to load these images.
Animating sprite images
For a game like Space Invaders, a ship can be a single sprite image whose only animation is to move around the screen, such as with the MOVSPR command. For a game like Pac Man, the player sprite should open and close its mouth, which means updating the sprite’s image data. We can’t exactly load a new Pac Man from disk each time he opens and closes his mouth, so we need another way to update sprite data within the program.
The SPRSAV command copies sprite data between sprite slots and BASIC string variables. It takes two arguments: the source and the destination. One or both of these can be a number between 0 and 7 to refer to the sprite image. If the other is a named string variable, the variable is used as either the source or destination.
For example, to copy the image data from sprite 2 to the string variable C$:
SPRSAV 2,C$
To copy that image to sprite 5:
SPRSAV C$,5
Combined with SPRITE LOAD, you can use SPRSAV to load all of your sprite patterns into string variables, then store them back into sprite slots during game play as needed.
You can also use string variables and SPRSAV along with disk operations as an alternative to SPRITE LOAD. For example, you can store more than eight patterns in a single file. Each pattern is 64 bytes in length. See any good reference on disk commands for more information.
Putting it together
Sprite Attack is a simple game I wrote to demonstrate how the sprite features can be used together. Get it here:
Try it out with a joystick in port 2. It’s written entirely in BASIC, so type LIST to see the program listing. A few notes:
I used SpritEd to draw eight multicolor sprites: the player’s spaceship, the player missiles, the space alien death ray, four frames of an explosion animation, and the space alien itself. I exited SpritEd and the Freezer, then used the command SPRITE SAVE "SPRATK.DAT" to save the sprite data to disk. The program starts by loading this file with SPRITE LOAD.
I keep the player’s X position in a variable so I can fine-tune the speed that the player moves. Pixel locations are always integers, but sometimes you want a sprite to move in non-integer increments, such as 1.8 pixels per frame. The variable holds the non-integer location, and this value is rounded to the nearest pixel when I pass it to the MOVSPR command.
The game loop tests for joystick movement, adjusts the variable, and updates the player location with MOVSPR. Because the player only moves left and right, I use +0 as the Y coordinate argument to MOVSPR so that I don’t have to remember the ship’s vertical position after initializing it.
I use the VSYNC command to synchronize gameplay with the frame rate. This makes it easier to tune the movement speeds, and it prevents the sprites from “tearing” by making sure the location doesn’t change while the sprite is being drawn.
Player missiles use the movement animation system. The RSPPOS() with a second argument of 2 returns the current speed of a sprite, so I can test whether the missiles are moving. If the button is pressed and the missiles aren’t moving, I set their position to just above the player ship and set them moving in an upward motion, then I activate the missiles sprite. I also test once per loop if the missiles have gone off the top of the screen, and if so, I stop their motion by setting their speed to zero.
The alien also uses movement animation. If the space alien is ever stationary (speed is 0), the program chooses a new random location, and calls MOVSPR with the current location as the origin and the random location as the destination. This gives the alien an evasive behavior that still gives the player a chance to target it with missiles.
A collision handler detects when the missiles are in contact with the alien. When the alien is hit, the routine moves the explosion sprite to the alien’s location, disables the missiles sprite, then moves the alien sprite to another random starting point above the screen. It plays out the explosion animation using SPRSAV to copy in the explosion patterns we saved into string variables earlier, pausing between each frame. This causes all gameplay to pause while the explosion finishes. A more sophisticated game would handle explosion animations in the main game loop, so other events like player movement can take place during the animation.
This game is a little unfair—to the alien. Try extending this program to have the alien shoot back at the player, using its death ray in sprite 2.
VIC hardware sprites and the BASIC 65 sprite commands are a fun and easy way to code video game graphics. Combine them with PETSCII graphics to draw level maps, backgrounds, and informational displays, and you have all of the elements of a great looking game. Be sure to share your games with other MEGA65 owners by uploading them to Filehost!
There are some advanced sprite techniques I wanted to mention here, but they’ll have to wait for another issue. You can help make more issues possible by supporting the Digest on Ko-Fi.
As always, happy coding!
— Dan
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
It’s time! We have a shipping schedule for the next batch of MEGA65 computers, and a candidate for the next platform release. Everyone is invited to help with testing, so let’s get to it!
Shipping update!
Trenz Electronic has announced that the next batch of MEGA65 computers will ship on June 1st, 2024.
We currently expect that all preorders up to this point will be included in this batch. If you need to make adjustments to your preorder before then, contact sales@trenz-electronic.de.
Once we clear the preorders, remaining stock will become available for purchase in the Trenz Electronic store, while supplies last.
Please bear with us while we work through any last minute issues that might change these estimates. And thank you so much for your patience! I can’t wait for you to receive your MEGA65!
v0.96 release candidate now available to test!
To make the new delivery schedule, we need to hand over the gold masters of the next release of the MEGA65 firmware and system software, as well as the updated User’s Guide, to Trenz Electronic by January 31st. Release v0.96 of the firmware, ROM, and system software will be installed at the factory on all MEGA65s about to ship. Of course, it will also be available as a free upgrade for all existing MEGA65s. This next release has a ton of new features and bug fixes—and we need testers!
If you have a MEGA65, you can install the release candidate on your machine to help test. If you don’t have a MEGA65 yet, you can still help by testing the new version of the ROM with an updated version of the Xemu emulator.
Testing the release candidate on a MEGA65
To begin, be sure to back up your SD card, or use an alternate SD card, before proceeding with testing. I usually just move the SD card to my PC, then copy all of the files from the SD card into a folder on my desktop. This doesn’t back up the configuration or Freeze states, but personally I don't keep important freeze states around long term. Alternatively, you could also use a disk imaging utility to back up both the hidden configuration partition and the files partition.
If you’re using a fresh SD card for testing, remember to prepare the card first using the MEGA65’s SD Card Utility. Insert the card in your MEGA65, hold the Alt key while switching on the computer, then select the utility from the menu and follow the prompts. This erases all data on the card.
Here’s how to get the files for the release candidate:
Release v0.96 RC core and system software. Select the latest build that begins with mega65r3-r-0.96, such as mega65r3-r-0.96-build-5.7z. The “r3” refers to all MEGA65s delivered up to this point. The MEGA65s shipping in June will be known as “r6.” Yes, we’re skipping R5 after all: we found one small non-electrical change needed for the board during R5 testing.
Release v0.96 RC ROM. Sign in with your Filehost account with your owner code redeemed to access this. Download the most recent version: 920391.bin.
MEGA65 command line tools: Windows, Mac, or Linux. These will be bundled with, and be the basis for, an upcoming version of the M65Connect app, and are generally useful on their own for the new Ethernet file transfer feature.
The MEGA65 User’s Guide, 2nd edition (PDF).
Unpack the mega65r3-r-0.96-build-5.7z archive. Transfer the .cor file to the root of your SD card. Open the sdcard-files folder, and copy its contents (.M65 files) to the root of the SD card as well.
Rename 920391.bin to mega65.rom, and transfer it to the root of your SD card.
Install the SD card in your MEGA65. With the power switched off, hold No Scroll then switch on the computer. This opens the core selection menu. Hold the Ctrl key and press a number to select an appropriate core slot for the new core, then follow the prompts to select the .cor file and flash the core. See the User’s Guide for more information about upgrading cores.
Using both the release candidate and the previous stable release with one MEGA65
I recommend keeping both the release candidate and the previous stable release installed during testing. If you find any issues with the release candidate, it will be important to determine if those issues also exist with the previous stable release, or if they are new issues introduced by a recent change.
Choose one or the other to be the default for your computer. Flash the core you wish to be the default to core slot 1, and name the default ROM as mega65.rom on the SD card. For the non-default release, flash the core to slot 2, and name the ROM file as mega652.rom. To start the non-default release (whichever one you chose), hold No Scroll while switching on the computer to open the core selection menu, then press and hold the number 2 until you see the READY. prompt (and probably a bunch of 2’s). This selects core slot 2 and also selects mega652.rom.
There’s one tricky bit with a dual installation: you can only have one set of system software on the SD card. I have not had any issues running the new system software with the previous stable core and ROM, so my recommendation is to go ahead and use the release candidate system software as the system files on your SD card.
It’s important to note that the release candidate ROM only works with the release candidate core. If you accidentally select the release candidate ROM (version 920391) and the previous stable release core, typing won’t work correctly and you’ll see the cursor disappear. If this happens, switch off the computer, hold No Scroll while switching it back on, then select a matching core and ROM. You can use the previous ROM (version 920377) with the new core, just not the other way around.
You can confirm that you’re running the release that you intend with the new MEGAINFO utility. Open the Freezer—hold the Restore key for a second, then release—then press the Help key. This is a feature of the new system software, and it works with either release as long as the new release’s system software is on the SD card.
Testing the candidate ROM in Xemu
If you do not have a MEGA65, you can still help test the ROM in the Xemu emulator. Get these files:
Release v0.96 RC ROM patch. Follow the instructions in the ROM FAQ to put together the MEGA65 ROM 920391 using the free C64 Forever download from Cloanto.
Xemu “next” release. Look at the bottom of the page for “The ‘future next stable’ still unstable (“next” branch) builds” for your platform. Download and install.
The MEGA65 User’s Guide, 2nd edition PDF.
Run xmega65, and when prompted for a ROM, use the new ROM.
Just as the release candidate ROM (version 920391) depends on the release candidate core, it also depends on the “next” version of Xemu. If you try to use the new ROM with the previous stable version of Xemu, you will notice problems with typing.
What to test
This release is the culmination of 15 months of improvements and fixes across the board. Changes to the ROM are listed in the ROM change log. Changes to the core are numerous and we’re still assembling the core change log, but here are a few notable features:
Ethernet file transfer. See the updated User’s Guide or this wiki page for instructions on how to transfer files between your PC and the MEGA65 using just an Ethernet cable. Notice that this requires flipping a small switch on the mainboard, so you will need to open your MEGA65’s case.
Hardware accelerated typing. You should notice that typing at the READY. prompt and in most apps is more accurate, especially for fast typists. This should not affect MEGA65 software in any other way.
Improvements to mouse handling, with smoother motion and support for mouse and paddles in port 2.
Coming soon: New core flashing capabilities—and a way to flash slot 0. The special core slot 0 controls aspects of the MEGA65 boot process, and it is intentionally more difficult to flash so you always have the factory-installed MEGA65 core available. With this release, there will be a new way to update the core in slot 0 to claim the benefits of improvements made to the boot process. As of this writing, this feature is not yet in the release candidate. Stay tuned to the Discord for an announcement of when and how to test this.
Coming soon: Configurable cartridge booting. Flashing this core to slot 0 enables the ability to configure the boot process to use a specific core when a type of Commodore cartridge is connected. This is primarily so you can assign C64 cartridges to the C64 core, instead of the less capable GO64 mode of the MEGA65 core and ROM. This change also introduces a new cartridge signature and boot protocol for MEGA65 cartridges, though you’ll need an EasyFlash 1CR cartridge and a test image to test it.
In addition to testing new features, we want to know about anything that worked in the last stable release and appears to have stopped working in the new release candidate (called “regressions” in software engineering parlance). This includes, but is not limited to:
Your favorite MEGA65 games, apps, and utilities
BASIC commands and features
The core menu, core flashing, configuration, “on-boarding,” and the SD card utility
The Freezer, and features accessible from the Freezer menu
Procedures and information described in the User’s Guide, 2nd edition
Anything that worked in GO64 mode with the previous stable release
How to report issues
There are two ways to report any bugs you find. We have an active thread on the Discord for collecting release candidate bugs. Look for the platform-testing forum. Alternatively, you can file issues directly to the Github repo for the core, the repo for the ROM, or the repo for the User’s Guide. If you’re not sure where the bug goes, file it in the core repo, and we’ll triage.
If you find something that doesn’t look right, try to reproduce it in the previous release. File a bug either way, and make sure to note whether this only happens in the release candidate, or if it happens in both versions.
Many thanks to everyone who is available to test! We’re on a tight schedule, so anything you can do before the end of January would be a huge help.
About that User’s Guide…
Every MEGA65 includes a printed version of the MEGA65 User’s Guide, a book that includes set-up instructions, common procedures, an introduction to BASIC programming, and a BASIC command reference. We printed a large quantity of the 1st edition, and everyone who has received a MEGA65 so far has this edition. We can’t afford not to use the remaining stock, so we will be including copies of the 1st edition with some MEGA65s that will ship in June.
The User’s Guide has been revised with new material on new features like Ethernet file transfer, and a renewed focus on non-developer activities like using virtual disk images and upgrading the firmware. The entire Guide has been polished with corrections, and the BASIC reference has been updated to include the most recently added features. We’re calling this the “2nd edition,” and we will be printing new Guides to include with MEGA65s after the stock of the 1st edition is depleted.
Do you want a printed copy of the 2nd edition? I know I do! I’m working on setting up the User’s Guide, 2nd edition with a print-on-demand service, so anyone who wants an updated printed Guide can get one. Whether you have the 1st edition and want to upgrade, or you’re just enjoying Xemu and want a book to go with it, you’ll be able to order a copy.
I’ll share the link to the purchase page in the next issue of this Digest. We’re using the release testing period to catch any last minute bugs in the Guide, and to make sure it matches the new slot 0 flashing procedure when it is finished. In the meantime, you can always download the latest draft PDF of The MEGA65 User’s Guide. (And report bugs!)
The 8-Bit Guy, and me!
YouTuber David Murray, aka “The 8-Bit Guy,” made a video about the Commodore 65 last December, borrowing Bo Zimmerman’s vintage C65 prototype to try it out. At the end of the video, David mentioned the MEGA65, mostly to say that he knew about the project but didn’t know anything about it. There were multiple attempts to get a review unit to David that fizzled out for one reason or another. When I saw that, I thought to myself, what can I do to help? I suppose I could… just go down to Texas and bring my MEGA65 with me. So I did!
Behold, the 8-Bit Guy’s video about the MEGA65, with special guest, me!
David is a co-founder of the Commander X16 project, a retro fantasy hardware project with similar interests to the MEGA65. Based on the 65C02 CPU, the X16 has a Commodore-like architecture and uses a Commodore-derived KERNAL and BASIC, while updating other aspects of the design to use readily available components and interfaces. Check out the X16 FAQ for details, and give the in-browser emulator a try.
Many thanks to David for hosting me and for appreciating the MEGA65. Thanks also to my Ko-fi supporters and to the MEGA65 team who helped make this possible.
Yes, your support of this Digest on Ko-fi is already making new things happen! If you would like to support the Digest and my involvement with the MEGA65 team, please visit: ko-fi.com/dddaaannn
Back again next month with more news, and more fun things to do with your favorite computer. Cheers!
— Dan
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
Our robotfindskitten adventure continues! In part 1, we introduced the robotfindskitten experience, and described tools and techniques for building an rfk game in BASIC 65. In part 2, we started building a similar toolkit in assembly language, starting with KERNAL routines, memory access techniques, and screen memory registers. This month, we complete the toolkit, and I present my own attempt at an assembly language version of the game.
But first, a whole bunch of new stuff!
R5 main board in testing!
The new R5 main board test units have arrived and Paul has started the “bring-up” process, adapting the FPGA core to the design changes. With some minor corrections to the assembly, this should resemble the main boards that will ship with new MEGA65s going forward, including all pending pre-orders. Many thanks to Paul, Trenz Electronic, and the hardware testing team for the work they are doing.
Unicone, by deathy
deathy has another new game release! In Unicone, you are chasing a unicorn that poops ice cream. Move your ice cream cone left and right to catch falling ice cream scoops dropped by the unicorn. A fun game in the tradition of Kaboom! (or its lesser known ancestor, Avalanche), Unicone features high resolution graphics, sampled sounds, a wide variety of control schemes, and a unique ice cream balancing mechanic for extra challenge in later levels. Download Unicode from Filehost.
Want to see how it works? deathy has generously released the C source code to Unicone using an open source license, and the assets using a Creative Commons license. The code builds with the Calypsi C cross-compiler, a modern retro development suite by hth313 that recently added support for the MEGA65’s 45GS02 CPU. Check it out!
The Ghosts of Blackwood Manor, by Stefan Vogt
Stefan Vogt, the author of the adventure games Hibernated and The Curse of Rabenstein, has a new adventure out for multiple platforms including the MEGA65. The Ghosts of Blackwood Manor is an interactive horror game with three possible outcomes, and each outcome fills out the story.
And of course, Ghosts is getting another gorgeous boxed release from poly.play! Pre-order the boxed release, and get it digitally right now for a donation of your choice.
Gurce’s BASIC 65 Dev Vlogs
Gurce has been doing a series of live stream development vlogs coding a game from scratch in BASIC 65, using the Eleven programming environment. The game, currently titled “Way of the Imploding Foot” (or just “MegaFoot” for short), is a side-view fighting game, featuring low resolution block character graphics, animated fighting characters, and parallax scrolling.
You can start with episode 1, and subscribe to find out about new live streams. Also check out the Github repository for the game, or just try the latest D81 disk image. When browsing the repo, be sure to locate the .ELPC files (such as FOOT.ELPC), which contain the code in Eleven syntax viewable as a text file on a PC.
Gurce is welcoming contributions on this project! If you’d like to try implementing a feature in BASIC using Eleven, let Gurce know on the Discord.
Discord upgrade!
The MEGA65 Discord is the MEGA65 community’s real-time meeting spot, a great place to ask questions, show off your projects, and meet other MEGA65 enthusiasts. Thanks to MEGA65 Discord moderator KiDra, the Discord now has a fresh new structure and some really cool new features! Here are just a few highlights:
New section layout. Channels and resources are now organized in sections based on how you engage with the project, such as regular use, programming, and platform development. Click or tap a section title to collapse sections you use less often.
Discord forums. Sections now include Discord forums in addition to text chat channels. Forums are especially useful for asking technical questions: each question stays visible in a list, and answers and discussions stay organized by topic. Once you have the answer you need, you can close the discussion, and it stays in Discord search history for others to find.
Voice/video rooms. Sometimes it’s just easier to talk, you know? Hop into a voice room to start an audio or video chat. Show off your work, collaborate on a project, or just hang out.
The Pin-Board. All of the most important community resources in one well-organized section! Announcements, official project status, and links to resources.
Events. Want to organize a club meeting, schedule an online conference, or throw a party? Let everyone know by posting an event! Everyone will be able to express interest in your event and request an automated reminder when the event draws near.
Self-service posting roles. To help keep the announcements and events channels clean and on-topic, we ask that anyone who wishes to post to them read a brief description of how they are used, in the #role-setup channel. Acknowledging the description grants permission to post, automatically.
Starred posts. Did someone post something you think more people should see? Give it a ⭐️ reaction emoji! If enough people star the post, it will be advertised in the #starred-posts channel. This works from any public channel.
Project channels. These discussion channels for specific MEGA65 projects by the community can be used for collaboration, on-going user support, or just dev logging and rubber ducking by the project developers. Want a channel for your project? Propose one in the #signup channel.
We will continue to evolve and improve the Discord based on the needs of the community. If you have any feedback or questions on the new layout and features, feel free to discuss it in the #mega65 channel, or contact anyone on the admin team. Thanks again to KiDra for setting all of this up!
Bad Apple… in BASIC?!?
As a follow-up to MEGApple, MirageBD’s high fidelity Bad Apple demo for the MEGA65, Nobato has a new version, this time in BASIC65! Bad Basic65 Apple Demo comes on multiple D81 disk images and can swap disks automatically from the SD card. The music is an original SID arrangement, using BASIC65 PLAY statements.
Boot B65APPL1.D81 to start. It takes a few minutes to load and decompress data into upper RAM—this is all in BASIC, remember—but once it begins, it runs smoothly, start to finish.
Don’t miss the Little Bad Basic65 Apple bonus disk, also included in the archive!
45GS02 assembly language mousepads and posters
A couple of weeks ago, I mentioned that I designed a 45GS02 assembly language quick reference, and you can purchase this design as a mousepad or a poster. Many thanks to everyone who has ordered a mousepad or poster so far! Proceeds go to supporting this Digest and my other work on the MEGA65 project.
I especially like how the mousepad turned out. It’s fun to look at, I can keep it on my desk as a mousepad, and it’s actually useful as a quick reference for MEGA65 assembly language programming. I just used it a couple of days ago to remind myself how the stack-related instructions work.
I’m using Zazzle to make and distribute these. They ship worldwide, and the shipping rates seem reasonable, only about 10 euros to ship the mousepad to Germany. I’ve had one report that there were technical issues trying to pay with PayPal. They also support credit cards. If you have issues, let me know, and I can try to figure it out.
The mousepad is a perfect companion for our robotfindskitten project. Speaking of which…
Finishing robotfindskitten in assembly language
To complete our robotfindskitten program in assembly language, we need to accept keyboard input, generate random numbers, de-duplicate choices, pause to animate the (very simple) ending sequence, and select from the list of item descriptions.
Accepting keyboard input from the KERNAL
A common way for a program to accept keyboard input is another KERNAL routine, getin at $FFE4. This loads a PETSCII code into the Accumulator if a key has been pressed, or 0 if not. This isn’t necessarily the best method for using the keyboard as a game controller, because it includes typing features like key repeat. But it’s easy to use, and for rfk, the built-in key repeat is reasonable behavior.
getin = $ffe4; ...- jsr getin beq - cmp #17 beq cursor_down ; ...cursor_down: ; ...
In older versions of the ROM, the getin routine uses CIA registers to scan the keyboard. With the latest beta test version of the core and ROM that I mentioned last month, getin uses a new typing event queue implemented in hardware for greater accuracy. Your program will get the same result from either version: the pre-conditions and post-conditions of getin have not changed.
A program that doesn’t use the KERNAL can access the new hardware registers directly, or can implement its own keyboard scanner based on the CIA chips. See a Commodore 64 reference for information on how to do that.
getin also supports features like the function key macros and input redirection, which don’t apply to a game but could come in handy for other types of programs. getin and chrout are both part of the KERNAL’s input-output system, of which the screen and keyboard are only two possible devices.
Chaos
We need a way to select randomized locations and appearances for the items. In BASIC, we used the RND() function for this purpose. What can we do from assembly language?
The MEGA65 has a dedicated hardware register that produces random values. To use it, a program must loop to wait for a “not ready” flag to clear, then read the generated random number from a register. The act of reading the number restarts the process, setting the “not ready” flag while it works on a new number. Internally, the number generator is using a fluctuating oscillator to generate values, and it needs a brief amount of time between reads to produce good results.
random = $d7efrandom_ready = $d7fe ; bit 7; ...- bit random_ready bmi - lda random ; A is a random number between 0 and 255
Important note: If you test your software in the Xemu emulator, be sure to use the “next” branch release. As of this writing, the random number register implementation is not in the latest stable release, and the ready-wait loop will never exit.
The distribution of values returned by this register is even across all possible byte values 0 to 255. Because this distribution is over a range sized by a power of two, you can reduce the range to a smaller power of two by ignoring some of the bits. For example, to get a random value from an even distribution of 0 to 63, use the and instruction to set the upper two bits to 0:
and #%00111111 ; A is a random number between 0 and 63
If the range size is not a power of two, it is feasible to simply “re-roll” for a number within the desired range. This causes all picks outside the range to be distributed evenly across the desired range, so it does not skew the results. Of course, this can take an arbitrary number of tries to roll a value in range, so you can speed this up by combining both techniques: get a number 0 to 255, zero out the unused bits, then test whether the result is in range and re-roll if not.
To get a random number larger than 255, fetch two random numbers and treat them as a single 16-bit number. You can use similar techniques to narrow the range.
Another option is to implement a pseudorandom number generator in your own code. Such algorithms range in power and complexity and can be difficult to get right, but applications like simple games can often get by with something as simple as a linear-feedback shift register (LFSR). Pseudorandom number algorithms generate a sequence of numbers based a starting number, known as the seed. It can be an advantage during testing to use a known seed to get a predictable result. For the final program, you can use another source of randomness, such as the hardware random number register, to determine the seed.
If you’d like to try this, read the Wikipedia page on LFSRs carefully. See also this video from Retro Game Mechanics Explained on how the game Super Mario Bros. 3 nerfs its card shuffling algorithm by misusing an LFSR, apparently accidentally.
Avoiding duplicates
When we discussed random selection in BASIC last month, we put in some extra effort to store all previous choices in an array, and re-rolled the dice when we accidentally chose an already-selected character, location, or description. This ensured that no two items would be placed on the same location, which would not only appear as fewer items on the screen but might cause the kitten to be hidden by another item. Depending on how it was implemented, the robot might touch the location and only see the Non-Kitten Item even if the kitten was also at the same location. Ensuring diverse characters and descriptions also maintains variety in the gameplay, to avoid the disappointment of seeing the same description more than once in a game.
The assembly language version of this isn’t different, it’s just a bit more cumbersome than in BASIC. I allocated some array-like memory within the program’s memory space for this purpose, like so:
num_items = 20 ; max 127item_description: !fill num_items*2item_screen_code: !fill num_itemsitem_x: !fill num_itemsitem_y: !fill num_items
(Alternatively I could have put this on the base page, or elsewhere between $1700 and $19FF, which is available for program use.)
I wrote three assembly language routines that detect whether the value at position X is equal to the value at any position 0 through X-1, the idea being that as I fill the array from position 0 upward, I can check each item against the previous items. I needed three routines because each value is stored in a different way: characters are stored as one-byte screen codes, descriptions are stored as two-byte addresses (which we’ll see more of later in this article), and coordinates are stored as coordinate pairs in two separate lists.
Here’s the simplest of the three routines I came up with that checks for duplicate screen codes:
addrl = $00addrh = $01vall = $02is_unique_byte: ; Y,Z: starting address of byte set ; X: index of last item ; Returns: Carry Set if item at X appears elsewhere in the set cpx #0 beq @is_unique sty addrl stz addrh txa tay lda (addrl),y sta vall- dey lda (addrl),y cmp vall beq @is_not_unique cpy #0 bne -@is_unique clc bra @end@is_not_unique sec@end rts
A few notes:
The routine accepts the starting address of item_screen_code in the Y and Z registers. I could have hard-coded the address into the routine, but this way I could potentially re-use it with a different array, or in another program.
It uses the Carry flag as the return value, where Carry is set if a duplicate is found. The routine is not responsible for choosing new random values or updating any memory. It’s important to separate responsibilities between routines to make code easier to write, test, and re-use.
It uses base page variables and indirect indexing (via the Y register) to access the array and remember the value under test. 6502-style CPUs have very few registers, so programs must rely on the base page (or the stack) to wrangle variables.
With the Acme assembler, a symbol whose name starts with an @ character is a “cheap local:” it is only valid between two global symbols (such as is_unique_byte). This way I can re-use these symbol names in my other duplicate test routines.
These routines were challenging to get right! The algorithm isn’t complicated, but one errant instruction can cause all kinds of havoc. I found it useful to insert a brk instruction early in my program so that it would open the MEGA65 Monitor. From here, I would test these routines by writing values into the arrays, setting the registers, calling the routines, and inspecting the Carry flag.
To do this, I needed to determine the raw addresses of the routines and the arrays assigned by the assembler. You can tell Acme to generate a report of all of the addresses it used throughout the program, using the -r command line argument:
acme -r rfk_report.txt rfk.asm
The monitor commands r and m inspect registers and memory, respectively. To update values, you can move the cursor up to the lines that these commands print, change a value, then press Return. (Remember that addresses and values are in hexadecimal.) To call a subroutine, use the j command. See my tutorial on the MEGA65 Monitor, as well as appendix M of the manual, for more information.
Avoiding other bad choices
There are two more cases of bad item placement worth considering, at least briefly.
The robot also appears on the playfield along with the items. We need to make sure that an item doesn’t land on the robot’s starting position. My solution is crude but effective: I put the robot in the upper left corner of the playfield, then limit item placement to be within a one-tile border. This way, there is never an item where the robot starts, and the robot can move unimpeded around the entire edge of the playfield.
With items placed entirely at random within the border, it is possible, however unlikely, that four items might surround a fifth item. And if that fifth item is the kitten, then the robot will never be able to find it! Tragedy! I admit, I did not bother to solve for this edge case in my implementations, but it’s worth thinking about how this might be solved. One way is to test a chosen item location for surrounding items, and re-roll if a bad case is noticed. A complete solution would have to handle extremely unlikely cases like 19 items forming a wall around the kitten, and would probably involve path finding algorithms or other magicks.
A simpler solution is to only place items on odd numbered rows and columns. For example, within an 80-column width, pick a number from 0 to 38, double it, then add one. This produces a column coordinate from 1 to 77 and guarantees at least a one-space gap between items. Do the same for the row coordinate, and the robot is assured a clear path to every item.
Calm
One of the responsibilities of the CIA chips is to keep track of time. For coarse-grained timing, the CIA has a built-in “time of day” clock that counts up by tenths of a second. If your main loop executes faster than ten times per second, you can count upwards by tenths of a second simply by testing for when register $DC08 changes. The bottom four bits are a “binary-coded decimal” value between 0 and 9, representing the tenths-of-seconds value of the CIA’s clock. This can be a bit crude because you might be testing the register just before it ticks, so the actual delay might be off by as much as one tenth of a second.
todtenths = $dc08wait_briefly: lda todtenths sta last_todtenths- lda todtenths cmp last_todtenths beq - sta last_todtenths- lda todtenths ; Wait two ticks to pause between 1/10th ; and 2/10ths of a second cmp last_todtenths beq - rtslast_todtenths: !byte $00
You can achieve a more precise wait loop delay using a feature of the VIC video chip. The VIC uses a finely-tuned high speed mechanism to describe the entire screen to a connected display many times per second, one line at a time from top to bottom, known as the raster scan. The current raster line number is stored in the RC register, as nine bits: bits 0 through 7 are at address $D012, and bit 8 of the number is stored as bit 7 at address $D011. In the same way that you can wait for the CIA TOD tenths register to change, you can wait for these register values to change for much shorter durations.
wait_very_briefly: ; Wait for bit 8 of the raster count to flip negative then positive, ; for a delay between 1 and 2 frames.- bit $d011 bpl -- bit $d011 bmi - rts
The tricky bit (because there’s always a tricky bit) is that the number of raster lines and the rate at which the screen is drawn differs depending on the video mode of the MEGA65. Back in the day, PAL and NTSC were competing standards for analog video signals, used for both transmission and rendering on cathode ray tube (CRT) displays. Commodore sold computers with different VIC chips in different regions to be compatible with the technical standard used in the region for televisions. As a result, Commodore games that used raster-based timing ran differently depending on which VIC chip was in the machine. A game might animate slower or faster than was intended, or it might play music at a different speed. In other cases, a game would only function correctly with one kind of VIC chip, and just trip over itself when running with mismatched VIC timing.
The MEGA65’s VIC-IV chip can generate PAL-like or NTSC-like video signals, as separate modes. Some MEGA65 owners are using displays (vintage or modern) that only support one of the modes. I asked everyone about their display modes in the 2023 survey, and 81% of the MEGA65 owners that responded have their video mode set to PAL by default, vs. 18% set to NTSC. Modern digital displays can often support both modes, but only 56% of owners were confident that their display could do this. A program can force the MEGA65 into one mode or the other using registers, but it’s best to leave it in the mode selected by the user in the configuration or Freezer menu.
The upshot is this:
In PAL mode, the RC register counts up to 312 raster lines, at a rate of 50 times per second (50 Hz).
In NTSC mode, the RC register counts up to 263 raster lines, at a rate of 60 times per second (60 Hz).
The actual image size is twice this many lines, fully refreshed at half these rates. The full image is drawn interlaced, with odd lines and even lines alternating with each refresh. 50 Hz or 60 Hz is the refresh rate; 25 Hz or 30 Hz is the frame rate.
It is possible for a program to achieve precision timing independently of video mode using the CIA chips and a system feature known as interrupt handling. Interrupts can also be used for raster-based timing. High speed games are most likely to use raster-based interrupts for timing because it makes it easier to control screen updates, and they will either live with the consequences of the different video modes, or use sophisticated techniques to work around them. Interrupts are a big topic, so we’ll leave that for another Digest.
robotfindskitten doesn’t need precise timing for anything, so raster timing is overkill in this case. I use CIA TOD tenths to add a simple delay to my found-kitten experience, and otherwise rely exclusively on keyboard events (and delays introduced by the key repeat handler) to drive the gameplay logic.
Data
We’re almost done! We have all the tools we need to display messages, choose and place items, animate the robot, move the robot based on keyboard input, and check for item interactions and the win condition. We just need a way to store, select, and display the 400+ possible item descriptions.
We’ve already seen how to add the bytes of a text message to the program in Acme, and to assign a label to its address:
message: !pet "\"I pity the fool who mistakes me for kitten!\", sez Mr. T.",0
What we need is a way to select from a table of messages by an index. For my robotfindskitten implementation, I used a Python script that reads the NKI descriptions from a text file, and generates !pet directives for all of them, with labels such as i1, i2, and so on. The script also generates an address look-up table with all of the generated labels in it. This puts the starting address of each description into memory at regular two-byte intervals. The program can find the description for a given index by multiplying the index by two (the asl instruction will do the trick), adding it to the address of the lookup table (nki_table), then reading the description start address from that location. Each description ends with a 0 byte, so the message printing routine knows where to stop.
nki_desc_count_mask = $01ffnki_desc_count = 401nki_table:!16 i0,i1,i2,i3,i4,i5,i6,i7!16 i8,i9,i10,i11,i12,i13,i14,i15; ...i0: !pet "\"I pity the fool who mistakes me for kitten!\", sez Mr. T.",0i1: !pet "That's just an old tin can.",0i2: !pet "It's an altar to the horse god.",0; ...
Here’s the full procedure for assigning a unique description to a Non-Kitten Item, using the techniques we’ve discussed:
For each item, use the random number register to generate 16 bits (two bytes).
Apply a bitmask to cut down this 16-bit value to a 9-bit value, the smallest mask that would account for the maximum number of descriptions (512 > 401).
Compare the result to the actual maximum number of descriptions, and re-roll until the selected number is within range. This is the description index.
Multiply the index by two to get the offset into the lookup table. Add this to the lookup table’s starting address.
Read the description starting address from the lookup table at that location.
Store the description address in the item_description array, then test whether it is a duplicate. If so, start again from step 1.
Using tools and scripts to generate data embedded in a machine code program like this is an essential skill, especially for games. Some assemblers have powerful directives that make embedding and manipulating data easy, such as Acme’s !binary directive that pulls in binary data from a file. Kick Assembler’s inline scripting capabilities are especially impressive. I find it useful to know a modern scripting language like Python so I can write my own data conversion tools that generate either assembly code or binary data that can be embedded in my programs.
If you don’t want to write your own scripts to generate the NKI description table, you can borrow the source code for my version, linked below.
The final robotfindskitten program
Here are my final versions of robotfindskitten for the MEGA65, in both BASIC and assembly language:
rfk-bas.prg : robotfindskitten in BASIC 65; BASIC source code in petcat format
rfk-asm.prg : robotfindsktiten in MEGA65 assembly language; assembly language source code for the Acme assembler
I don’t claim that either of these are the best possible versions of this program. If you find ways that either of these can be improved, or if you have used different designs or techniques in your program, please let me know!
Whew! That’s a whirlwind of programming topics for three Digest issues. Hopefully this is enough for you to get started writing a robotfindskitten of your own, or extending these ideas to making other kinds of games. For example, you could add a system of walls or a maze generator to make locating items more challenging. Or, you could add other non-player robots that move when the player does, attempting to interfere with the search. Maybe when a non-player robot encounters an item, the item gets relocated on the playfield! It’d be a much less zen experience, but more like a game.
Huge thanks to everyone who has jumped in to support the Digest on Ko-fi so far! We’re off to a good start to funding the Digest through 2024. If you would like to become a supporter, visit: https://ko-fi.com/dddaaannn
See you next year!
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
Last month’s Digest introduced robotfindskitten, a programming exercise that unites several major concepts of game programming: updating the display, reading user input, generating random values, timing events, and including and manipulating large amounts of static game data. I offered examples of each of these tasks in BASIC 65, and proposed that these could be used to make a robotfindskitten experience for the MEGA65.
In this issue, I want to start reviewing these topics again in assembly language. Without BASIC’s help, the program will need to turn to hardware registers and low-level programming techniques to achieve similar effects. Some of these topics are too large for a single newsletter, so we’ll take this in two parts. I’ll try to keep things simple by limiting this to just the needs of a robotfindskitten program. Applications that require higher speed or more memory may need more sophisticated techniques.
This month’s Digest will focus on using the KERNAL, printing messages, and drawing characters to the screen—barely scratching the surface of the MEGA65’s graphics capabilities. Next month, we’ll finish robotfindskitten in assembly language with random values, user input, item descriptions, and a simple animation delay.
Shipping update
The work continues to finalize the new R5 main board hardware for the next delivery batch of computers. Getting the test hardware has taken longer than anticipated, and we are now expecting manufacturing lead times to put the batch #3 delivery in early 2024.
Importantly, the team has decided to proceed with the full verification process for the new design, and not skip any steps just to accelerate the schedule. The MEGA65 is manufactured in small volumes in a not-for-profit operation, so we can’t afford to rush the process and risk having to re-make and replace hardware. We want every computer delivered to be as high in quality as possible.
Some pre-orders have been pending for a very long time now, and we thank you for your patience! If you have a pending pre-order and need to make changes, contact Trenz Electronic customer support.
Tristam Island
Tristam Island, by Hugo Labrande, is a new text adventure game for multiple platforms, including the MEGA65. You can get the deluxe boxed edition, from publisher poly.play for 35 EUR. The deluxe edition includes the game on 3.5" floppy disk and on microSD card, a hint book, immersive props such as a rock sample and a postcard, and more. You can also get the digital-only edition for $3.99 USD.
Thanks to Hugo for the great game and for supporting the MEGA65, and to poly.play for publishing fun collectible boxed software for our favorite platform!
Updated ZX Spectrum core
Did you know that you can turn your MEGA65 into a ZX Spectrum? You can, with the ZX Spectrum core! This core just received a major overhaul to use the MiSTer2MEGA65 framework, and now works with modern displays.
Download the ZX Spectrum core from Filehost, then follow these detailed instructions for set-up and enjoyment. The core expects certain files in specific locations on the SD card, and uses ESXDOS v0.8.8 (not v0.8.9) for SD card access. It can load .tap and .trd files.
Once again thanks to sy2002 and MJoergen for their amazing work on setting up the MEGA65 for retro core success!
More arcade cores!
muse continues the great work of porting arcade game cores to the MEGA65. Bombjack (1984) (installation instructions) and Bosconian (1981) (installation isntructions) are both available.
The complete list of alternate cores for the MEGA65 so far:
Ports and enhancements by MJoergen and sy2002:
Commodore 64 v5
ZX Spectrum v1.0
Game Boy v0.8
Ports and enhancements by muse (shoestring):
Galaga v0.5.1
Bosconian v0.5.0
Xevious v0.5.0
Bombjack v0.5.0
Along with a MEGA65 development core in slot 1 and a factory-installed stable core in slot 0, that’s more cores than there are core slots on a MEGA65! Just keep the .cor files on your SD card and flash them as needed.
Conference talks
MEGA65 enthusiasts have been giving talks at computer conferences this year, and several of them now have video online. Check these out!
Oliver Graf (lydon) at Vintage Computing Festival Berlin 2023
Jim Happel (jim_64) at Commodore Retro eXpo 2023
Dan Sanderson (dddaaannn) at Pacific Commodore Expo Northwest 2023
If you have given a presentation on the MEGA65, even just to your local computer club, and there’s video online, please let me know so I can feature it here!
robotfindskitten in assembly language
Let’s take a look at how you might implement robotfindskitten in MEGA65 assembly language. This month will focus on using the KERNAL API and manipulating the display. Next month, we’ll cover the remaining topics to get a robotfindskitten program working, similar to what we did in BASIC last month.
As before, I’ll be using the Acme assembler. Here’s a reminder of the starter code for an assembly language program that assembles to a PRG file that can be loaded and run. The !8 ... values describe a BASIC bootstrap program that invokes the first assembly language instruction.
!cpu m65!to "rfk.prg", cbm* = $2001!8 $12,$20,$0a,$00,$fe,$02,$20,$30,$3a,$9e,$20!pet "$2014"!8 $00,$00,$00start: ; Program code will go here.
Introducing the KERNAL
Every Commodore computer has a built-in operating system powered by machine code etched into the computer’s Read Only Memory (ROM). You see this code running as soon as you turn on the computer: the READY prompt, the blinking cursor, and the BASIC interpreter and all of the BASIC commands are all built-in code. Included in this code is a collection of machine code routines and subsystems for accessing hardware such as the keyboard, screen, and disk drives. In computer architecture terms, these routines are known as the kernel, the centerpiece of the system used by other components. Commodore employee Robert Russell originally misspelled the word “kernel” as “KERNAL” in the documentation, and this became a nickname for the software. For the sake of tradition, I’ll continue to refer to the Commodore kernel as the KERNAL, using uppercase letters.
A program can call KERNAL routines by way of a jump table, a list of jmp instructions built into the ROM at a fixed memory location. Each jmp instruction redirects to the actual location of the routine elsewhere in memory. The jump table exists to give programmers peace of mind that each jmp instruction will stay at a consistent address for all future revisions of the KERNAL. When changes in the KERNAL code inevitably push an internal routine to a new location, the jump table is updated accordingly, so programs that use the routine continue to function without needing an update.
Here’s a useful example. The KERNAL maintains a system for printing PETSCII codes to the screen. As we’ve seen before, these codes manipulate a cursor that determines the location of the next printable character, and maintains other properties such as the text color and display attributes. The BASIC PRINT command uses this system to display strings of codes and characters. Machine code programs can also access this system using a routine called chrout (also called bsout in some documentation), available via the jump table entry at $FFD2.
chrout = $ffd2 lda #147 ; clear the screen jsr chrout lda #65 ; the letter "A" jsr chrout
The chrout routine has a pre-condition that the PETSCII code to print is in the accumulator (CPU register “A”). The effect of calling the routine is to print the PETSCII code at the current cursor position. If the code is a printable character, it plots the character, moves the cursor, and performs other effects like scrolling the display as needed. If the code is some other PETSCII code, such as code 147 to clear the screen and move the cursor to the top-left corner, that code takes effect.
The jump table is part of the Application Programming Interface, or API, of the KERNAL. The API definition also includes the pre-conditions and post-conditions of each routine, and other important behavioral characteristics about what the routine does. All KERNAL jump table entries designed for the C64 and C128 are valid on the MEGA65—with some changes. While neither the C65 nor the MEGA65 have introduced new table entries, future versions of the MEGA65 ROM could extend the table further without changing any of the existing entries.
It’s important to remember that the MEGA65 ROM is an active construction zone, and only some of the KERNAL APIs borrowed from previous Commodores are considered officially supported for now. Some things that C64 programmers may be used to aren’t official APIs, even if they appear to function today. When the dust has settled on the major bug fixes, the MEGA65 team will formalize more of the KERNAL API surface, adding documentation about supported features to the manuals and building automated tests to ensure that the APIs work properly. The KERNAL jump table is officially supported, as are certain specific facts about the memory layout, such as $2001 being the start address for BASIC programs.
Once a machine code program is running, it is not obligated to use the KERNAL. In fact, many games and larger utilities jettison the KERNAL entirely, installing custom interrupt handler routines, defining a custom memory map, and manipulating the computer entirely through hardware registers. For such a program, the only API surface is the hardware itself, and there is no need to integrate with the KERNAL except to launch the program. If the program does use KERNAL routines and systems, it must honor the KERNAL’s documented pre-conditions, and stay out of its way.
Printing a short message using the KERNAL
The following code uses the KERNAL chrout routine to clear the screen and print a short message:
lda #147 ; clear the screen jsr chrout ldx #0- lda message,x ; read a byte from message, offset by X beq + ; if it's a zero, we have reached the end of the message jsr chrout ; output the byte inx ; increment X bra - ; loop back+ rtsmessage:!pet 14,"robotfindskitten",0 ; Acme directive to generate PETSCII ; bytes
This example uses the “X indexing” addressing mode to access characters in the message stored in program memory. lda message,x takes the address of the message, via the assembler symbol message, and adds the value in the X register to it. The X register is one byte, with a range of possible values of 0 to 255. This limits the length of the message to 254 PETSCII codes and one 0 byte to indicate the end of the message.
There’s another way to print short messages, using another KERNAL routine. The primm routine at $FF7D will print a null-terminated message, with the message bytes immediately following the jsr instruction. It’s like having a PRINT command for assembly language!
primm = $ff7d jsr primm!pet 14,"robotfindskitten",0 ; Program continues...
It seems like a magic trick for the message bytes to be sitting in the middle of the code like this. How does the CPU know to skip over the message to the next instruction? The primm routine is quite clever. When the CPU encounters a jsr instruction, it remembers the address just after the instruction, so it can pick up where it left off when the subroutine exits with the rts instruction. The primm routine assumes that this address is the starting byte for the message and starts printing. When primm finds the 0, it updates the return address to just after the 0, then issues the rts. The CPU is blissfully unaware that primm has been messing with the return address and proceeds to execute the rest of the program. Pretty slick!
Note that primm is also limited to messages of 254 characters. To print a longer message, we need a different strategy.
Printing longer messages
In the code above, message is a symbol whose value is determined by the assembler and inserted into the code where the word appears. Wouldn’t it be great if we could update the value of the message symbol as the program executes, like a variable? The value of message is written into the machine code, and this code is just bytes in memory. One option is to update the address bytes within the code by writing to the appropriate memory locations, then execute the code. This technique, known as self-modifying code, is quite common, and not that difficult with a bit of care.
The following routine accepts the low byte and high byte of the message address in the Y and Z registers, and prints the message until it encounters a 0 byte. It relies on the knowledge that the lda $0000,x instruction assembles to three bytes: one for the instruction code, and two for the address. It uses the X register as before, but this time it tests whether X wraps around from 255 to 0, then increments the high byte of the address and continues printing. This gives us a maximum message length of 65,535 characters, which is more than enough.
ldy #intro_message jsr print_long_message; ...print_long_message: ; Y,Z = address sty .load_selfmod+1 stz .load_selfmod+2 ldx #0.load_selfmod lda $0000,x beq .end jsr chrout inx bne .load_selfmod inc .load_selfmod+2 bra .load_selfmod.end rtsintro_message:!pet 147,14,5,27,"8" ; clear, lowercase, white, 80x25!pet "robotfindskitten in MEGA65 assembly language",13!pet "Based on the game by the illustrious Leonard Richardson (C) 1997, 2000",13!pet "Press Return to start.",0
Changing the cursor location
Another useful KERNAL routine for printing is the plot routine, at $FFF0. plot can do two things: it can either move the cursor to given coordinates, or it can report the coordinates of the cursor’s location. You use the carry flag to choose between the two actions. To move the cursor, clear the carry flag (clc), then set the X register to the column number and the Y register to the row number.
plot = $fff0 clc ldx #12 ldy #1 jsr plot
To ask plot to report the current location, set the carry flag (sec) before calling it. Instead of changing the cursor position, plot will overwrite the values in the X and Y registers with the current cursor location.
A word of caution: When the KERNAL print system prints a character in the bottom-right corner of the screen, it assumes that more text needs to appear on the following line, and scrolls the text display to make room. When plotting characters for game graphics, this probably isn’t what you want. A simple albeit unsatisfying way to avoid this issue is to never print a character in the bottom-right corner.
Accessing screen memory
You could make a robotfindskitten game using just the KERNAL print system: use plot and chrout with PETSCII codes to draw items, and erase and draw the robot character as it moves across the screen. Most games don’t use the KERNAL print system to plot characters at all. Instead, they write directly to screen memory, color memory, and registers in the VIC-IV chip. This avoids issues like the KERNAL scrolling the text display, at the expense of doing other things manually, such as calculating memory locations for screen coordinates, and managing character attributes and colors.
If your program uses the KERNAL print routines, it’s best to let the KERNAL manage screen memory as much as possible. The KERNAL uses internal variables to keep track of some screen properties, and these need to be consistent with its own understanding of the state of the video hardware. If you change the screen mode or relocate screen memory via VIC-IV registers without telling the KERNAL, actions performed by PETSCII codes, such as clearing the screen, may not function correctly.
Handling screen memory is an essential skill for assembly language programmers. It’s also a deep topic. So let’s limit this discussion of screen memory to a few salient facts. To tell the KERNAL to set the screen mode to 80 x 25 text, print the Escape + 8 key sequence with chrout, as if you were typing it at the READY prompt:
lda #27 ; Escape jsr chrout lda #'8' ; PETSCII "8" jsr chrout
If you’re not using the KERNAL, you can select the screen mode with flag registers at location $D031. To get 80 x 25, clear the V400 register (bit 3) and set the H640 register (bit 7). Without the KERNAL, you will need to write your own message printing routines that write directly to screen memory; you can’t use chrout.
The starting address of screen memory is in a VIC-IV register known as SCRNPTR, a 28-bit value stored at $D060-$D063, least significant byte first. The KERNAL will set this when you print the escape sequence for the screen mode. If you’re not using the KERNAL, you can set this to any address you like in the first 384K of memory by writing its address into the registers.
The KERNAL’s preferred memory location for a 40 x 25 or 80 x 25 screen is address $0800. Strictly speaking, this is not a documented fact: a program that relies on the KERNAL’s setting must read it from SCRNPTR, and never assume its value. For propriety, the examples below will read the SCRNPTR value instead of assuming it, but—at least for now—I’m also relying on the fact that the address is within bank 0 (addresses $0000 to $FFFF) so we can use 16-bit addressing modes and don’t have to worry about accessing upper memory. Note that this won’t work with 80 x 50 text mode, because the KERNAL relocates screen memory to bank 1 for this mode.
As with BASIC’s T@&() special array, screen memory contains screen codes that represent the characters in the character set. The codes are organized in columns then rows, from top-left to bottom-right: the first 80 bytes are the top row, the next 80 bytes are the next row, and so on, for 80 x 25 = 2,000 bytes.
The following example uses the self-modifying code technique to store a value in screen memory. The sta instruction can only operate on 16-bit addresses, so it uses the lower two bytes of SCRNPTR, and assumes the upper bytes are zero (an address in bank 0).
scrnptr = $d060 ; Use self-modification to write to the (16-bit, bank 0) ; SCRNPTR address lda scrnptr sta store_screen_selfmod+1 lda scrnptr+1 sta store_screen_selfmod+2 lda #1 ; Screen code for the letter Astore_screen_selfmod sta $0000
Color memory
In the regular text mode, color memory stores the foreground color and attributes for each character on the screen, organized in the same way. One way to access color memory is starting at address $D800.
There’s one issue, though. By default, only the first 1K of color memory is visible here, from $D800 to $DBFF. That’s not enough for all 2,000 characters of a 80 x 25 display! This is solved by yet another of the MEGA65’s many modes. The CRAM2K register, bit 0 of $D030, swaps the registers from $DC00 to $DFFF with the remaining color memory. To do this, it hides other hardware registers normally at those locations, such as the CIA chip registers. The KERNAL expects to see the registers here and not color memory, so you must clear the bit before calling KERNAL routines. (The KERNAL IRQ is smart enough to stash and restore your CRAM2K setting, so you do not need to disable interrupts. That’s a topic for another time.)
cram2k = $d030color_mem = $d800 lda #%00000001 ; Enable CRAM2K tsb cram2k lda #5 ; Paint the A green sta color_mem lda #%00000001 ; Restore registers trb cram2k
From coordinates to screen memory addresses
Given screen coordinates of column X and row Y, the offset into screen memory for that coordinate is Y times 80, plus X. The CPU doesn’t have an instruction that can multiple any two numbers, but it does have a quick and easy way to multiply a number by two: it can shift the bits of a number to the left, using the Arithmetic Shift Left instruction (asl). If it’s not clear that shifting the bits of a number to the left multiply it by two, write out a binary number, then write a zero at the end. For example, the binary number %0110 is 2 + 4 = 6 in decimal. Write a zero at the end, and it becomes %01100, which is 4 + 8 = 12 in decimal. Just as writing a zero at the end of a decimal (base ten) number multiplies the number by ten, writing a zero at the end of a binary (base two) number multiplies it by two.
Using a combination of left-shift and addition operations, we can calculate the screen memory address for a given set of coordinates in three steps:
Calculate the row offset from the Y coordinate, stored in a memory variable (row_offset), using bit shift and addition operations to multiply Y by 80.
Add the base address and the row offset, stored in the address part of a sta ...,x instruction (self-modifying code).
Use X-indexing with the X coordinate, to determine the final address and set the screen code.
row_offset: !byte 0,0plot_char: ; Pre-condition: ; - A is the screen code ; - X register is the X coordinate ; - Y register is the Y coordinate taz ; Stash the screen code in Z, so ; we can use A for other things lda scrnptr sta store_screen_selfmod+1 lda scrnptr+1 sta store_screen_selfmod+2do_plot: ; - Z is the value to store in memory ; Calculate Y * 80 into row_offset memory variable lda #0 sta row_offset+1 ; reset row_offset's high byte tya sta row_offset ; store the row number as row_offset's low byte asl ; x2 asl ; x4 adc row_offset ; x5 sta row_offset asw row_offset ; x10 asw row_offset ; x20 asw row_offset ; x40 asw row_offset ; x80 ; Add row_offset to store_screen_selfmod+1 lda store_screen_selfmod+1 clc adc row_offset sta store_screen_selfmod+1 lda store_screen_selfmod+2 adc row_offset+1 ; add with carry sta store_screen_selfmod+2store_screen_selfmod: stz $0000,x rts
To update color memory, the only difference in this routine would be to use a different base address, and set CRAM2K while updating memory. I added a do_plot label above so I could share the common code between the two routines.
color_char: ; Pre-condition: ; - A is the color ; - X register is the X coordinate ; - Y register is the Y coordinate taz ; Stash the color code in Z, so ; we can use A for other things lda #color_mem sta store_screen_selfmod+2 lda #%00000001 ; Enable CRAM2K tsb cram2k jsr do_plot lda #%00000001 ; Restore registers trb cram2k rts
Base Page Indirect Addressing
Each machine code instruction that accesses memory can operate in one or more addressing modes, ways for the CPU to figure out what address to use. We’ve seen three addressing modes in the examples so far:
Immediate mode: use the value given in the instruction. This example loads the number 7 from the instruction into the accumulator:
lda #7
Absolute mode: use the 16-bit address given in the instruction. This example loads the value stored at address $20CF into the accumulator:
lda $20cf
Absolute X-indexed mode: take the given 16-bit address, then add the value from the X register to it, and use that as the address. This example sets the X register to $2F, then loads the value stored at the address calculated as $20CF plus the value in the X register ($20CF + $2F = $20FE) into the accumulator:
ldx #$2f lda $20cf,x
The X register can contain a value from 0 to 255, which limits the range of the X-indexed addressing mode. Earlier, I used self-modifying code to get around this problem, allowing for performing calculations on the entire address. The following example rewrites the 16-bit address portion of an instruction that’s using absolute mode addressing, then executes the modified instruction:
lda #$cf sta .selfmod+1 lda #$20 sta .selfmod+2.selfmod: lda $0000
Writing the address directly into an instruction like this is only really practical if only one instruction needs the address, and the modifying code knows exactly which address to modify. In many cases, it’d be better if we could store the address in one memory location like a variable, then tell the instructions where to find it.
Base Page Indirect Addressing mode can do exactly that. This example stores a 16-bit address at memory location $10-$11, then accesses it using the Y register as an index:
lda #$cf sta $10 lda #$20 sta $11 ldy #0 lda ($10),y
So why didn’t I do this earlier? Well, there’s a catch: the address must be written to the base page, a region of 256 bytes that gets special treatment. Commodore 64 programmers know this as the zero page, within the addresses $0000 to $00FF. The CPU uses special forms of its instructions to access these addresses faster and with more compact code than absolute 16-bit addresses, so the base page is a great place to put variables. It’s so useful, in fact, that the KERNAL claims all of the zero page for its own use. If your program uses the KERNAL, you must protect the zero page so that it’s the way the KERNAL left it by the time you call a KERNAL routine.
With the original 6502 CPU, the base page is always at address $0000 (hence the name “zero page”). The 65CE02 CPU (on which the MEGA65’s 45GS02 is based) adds a feature that lets you use any 256-byte page of the first 64K of memory as the base page. The B register holds the top two nibbles (two hexadecimal digits) of the address of the base page. Your program can stake out its own base page and set the B register to that location to use it. I recommend page B = $16 ($1600 to $16FF), which is reserved for use by your program.
You must change B back to $00 before calling a KERNAL routine. The base page being set to $00 is a pre-condition of all KERNAL routines. (The B register is preserved along with other registers during interrupt handling, so you don’t have to worry about confusing the KERNAL IRQ.)
To set the B register, put the desired value in the accumulator, then use the tab instruction (Transfer A to B). If you need to read the B register, transfer it back to the accumulator first, with tba. The requirement to go through A to set or read B sometimes requires a bit of register wrangling.
Another register consideration to keep in mind: Base Page Indirect Addressing always uses an index register. The lda instruction supports using the Y or Z registers as indexes for this mode. If you just want to use the address directly, you must set the index register to 0.
Here’s another version of the long message printer using Base Page Indirect addressing, preserving the zero page for the KERNAL and using page $16 for the program’s base page. Try to follow which register has which value:
print_long_message_2: ; Y,Z = address lda #$16 ; A = $16 tab ; B = $16 sty $00 ; $1600-$1601 = the message address stz $01 ldy #0 ; Y = 0.loop lda ($00),y ; Load a character from the message address + Y beq .end ; If the value is 0, end. tax ; Stash the character in X so we can use A to set B. lda #0 ; A = 0 tab ; B = 0 txa ; A = the character jsr chrout lda #$16 ; A = 16 tab ; B = 16 iny ; Y = Y + 1 bne .loop ; If Y hasn't rolled over to 0, continue. inc $01 ; Y rolled over, so increment the high byte of ; the message address. bra .loop ; Continue..end lda #0 tab rts
This is not necessarily better than the earlier version using self-modifying code, especially with all the register flipping required. But it illustrates the use of base page variables as address pointers.
The 45GS02 CPU has another Base Page Indirect addressing mode, and it’s super useful: 32-bit Base Page Indirect Addressing lets you use a 32-bit address instead of a 16-bit address. With the Acme assembler in MEGA65 mode, this is indicated using square brackets instead of parentheses. Only the Z register is supported as the index in this mode.
lda #$16 tab ; Store the address $0001.F800 at base page address $00-$03 lda #$00 sta $00 lda #$F8 sta $01 lda #$01 sta $02 lda #$00 sta $03 ; Store the value 7 at $0001.F800 lda #7 ldz #0 sta [$00],z
Notice that other addressing modes do not support 32-bit addresses, so we can’t use the self-modifying code method to access upper memory, at least not in the same way. We can adapt the second version of the printing routine to be able to print long messages from anywhere in the MEGA65’s memory with minimal changes, but we can’t adapt the first version as easily.
Earlier, I made a potentially unsafe assumption that the KERNAL used an address between $0000 and $FFFF for SCRNPTR in 80 x 25 mode, and I just ignored the upper bytes of the SCRNPTR address. With 32-bit base page indirect addressing, I don’t have to do that: I can just copy the entire address from SCRNPTR to a four-byte base page variable, then use 32-bit indirect addressing to access screen memory.
Incidentally, upper memory addresses $1.F800 through $1.FFFF are another way to access the first 2K of color memory, with no need to set the CRAM2K flag. In my implementation of robotfindskitten, I use base page $16 for variable storage, and base page indirect addressing for plotting characters with the full four-byte SCRNPTR address and for plotting colors to 1.F800. I set the B register to $16 at the beginning of the program, and wrap my KERNAL calls to set B to $00 before the call and set it back to $16 afterward.
As you can see, there are multiple ways to accomplish certain tasks. For high speed applications, you may need to make your choices based on the amount of CPU time each operation takes, measured in CPU cycles. Instruction cycle counts are listed in the manual. robotfindskitten is not speed critical, so I just went with what made my code easiest for me to understand.
That’s a good start. With the KERNAL output routines and screen memory access, we have the tools to replicate BASIC’s PRINT, T@&(), and C@&() facilities in assembly language. Next month, we’ll look at another important KERNAL facility for accepting keyboard input, and some snazzy MEGA65 hardware features for generating random numbers and measuring time. We’ll close by designing data structures to access all of the item descriptions, and prevent two items from landing on the same place on the screen.
For comparison and enjoyment, here’s my BASIC version of robotfindskitten. It’s faithful to Leonard Richardson’s version, which is not particularly creative of me, but hopefully it serves as a useful reference implementation.
Happy coding! See you next month.
— Dan
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
A new month, a new feature, a new game, a new demo, and a coding exercise that really brings the room together. Let’s dig in!
Available to test: New keyboard scanner
We’re getting closer to having a complete release candidate, with features being finalized and bug fixes piling in. There’s one new feature that’s near and dear to my heart, and I’m thrilled to be able to share it with anyone up for early beta testing.
The very first thing I noticed when I got my MEGA65 is how the typing experience felt nostalgic. Even with the new mechanical key switches and the 40 MHz CPU, the ability for the computer to recognize key presses felt exactly like a Commodore 64 did back in the day: sluggish and imprecise. I like nostalgia as much as anybody, but I type much faster today than I did when I was nine years old. I wished my MEGA65 could handle fast typing more like a modern computer, so I could enjoy on-device composition without slowing myself down. It was one of the first feature requests that I filed with the MEGA65 team.
Paul and company started working on an idea for fast typing, and an early version of the chipset support required made it into the core last year. The feature wasn’t wired up to the KERNAL ROM, and only a few built-in applications used it. I brought it up again with the team this summer, and we had many design discussions and tried several ideas. I learned a bit of FPGA coding, and even built a high-speed Linux PC just to build and test new cores and ROMs.
I’m proud to present the all-new MEGA65 hardware-accelerated keyboard scanner. Using the latest development core and ROM beta release, you can now enjoy a typing experience that is more accurate and more reliable for fast typists throughout the BASIC screen editor and many applications. These changes will be in the v0.96 release, and you can test it today and file any bugs you find. I also wrote a test plan and detailed description of how it works.
I plastered this all over the instructions, but I'll repeat it here: the latest ROM beta versions (920387 or later) require the latest development core. If you use the newer ROM with an older core, typing won't work. You can safely use older ROMs with the newer core, so you can revert to the legacy keyboard scanner at any time if you encounter any issues just by going back to an older ROM (920386 or earlier). Also, the latest ROM betas will not work with the Xemu emulator until emulation for the new core feature has been added (hopefully soon), so stick with 920386 or earlier in Xemu for now.
For me, the difference with the enhanced typing quality is like night and day. On-device programming and other typing applications are much more usable, and I just enjoy using the computer much more, confident that I won’t have to struggle with missed keystrokes. Try it out, and let me know what you think!
Classy, by deathy
Classy, by deathy, is a new match-3 game for the MEGA65. Careful with this one: it’s addictive! A joystick in either port or the keyboard sets you on your tile swapping journey. If the game is still running, it means you still have moves remaining. Keep looking!
MEGApple, by MirageBD
The MEGA65 gets its Bad Apple!!
MEGApple is MirageBD’s MEGA65 version of the popular demo challenge: recreate the shadow-art music video of the Japanese pop song, “Bad Apple!!” The original video was a collaboration of animators on the Japanese website Nico Nico Douga, based on the pop remix by nomico of a track from the video game Touhou Fantasy Land: Lotus Land Story. Over a decade later, it persists as a popular meme to recreate the music video in unusual media, including (but not limited to) retro computers.
To run MEGApple, download and expand the archive, then copy the files to the root of your SD card. Start your MEGA65, then type: MOUNT "MEGAPPLE.D81":RUN "*" The program on the D81 disk image loads the data from the other larger files.
Xevious, new arcade core by muse
muse has a new arcade cabinet core for us! Xevious (1982) and Super Xevious (1984) are early vertical scrolling shooter games by Namco. Fly your Solvalou starship over the planet’s surface to stop the Xevious forces by both land and sky. muse’s version supports a one-button joystick with a clever adaptation: press the button quickly to shoot at airborne enemies, and hold the button to drop bombs on turrets.
As with the Galaga core, the installation instructions include searching for and downloading the original Xevious ROM files, running a Python script to validate the files and prepare a folder that you copy to your MEGA65’s SD card with the path /arcade/xevious, then installing the core in an available core slot. See the Discord announcement for additional notes.
robotfindskitten
“Yet another zen simulation.” — robotfindskitten.org
In robotfindskitten, you are a robot, who finds a kitten.
robotfindskitten was originally written by Leonard Richardson in 1997 for DOS, as his submission to the robotfindskitten contest put on by the webzine “Nerth Pork.” It took first prize. There were no other entries, but there didn’t need to be. The theme had found its perfect expression on the first try.
In the decades that followed, many have made their own versions of the simulation, on many interactive platforms. And now, it is your turn.
Richardson’s version depicts the robot with a single colored ASCII character on a monospaced field that spans the screen. The field is populated sparsely with other colored ASCII characters that represent many Non-Kitten Items (NKIs), and one kitten. To the robot, all items, including the kitten, appear as random colored characters. The operator uses cursor keys to move the robot around the field. When the robot comes into contact with an item, a message is printed describing the item. If the item is the kitten, the experience ends in joyful celebration.
You can play robotfindskitten in your browser. Or, you can install a version for a modern terminal for Linux (sudo apt install robotfindskitten) or macOS (brew install robotfindskitten) or many other platforms. (Some versions are survived only by their archived screenshots.) Or you can build the Linux version from source. Or you can check out the Ultimate robotfindskitten Fan Site, by Leonard himself.
Or you can make your own. Here are some techniques that you might use to rise to this challenge in BASIC 65. Next month, we’ll look at how to do similar things in assembly language.
Display
You will need to control what is displayed on the screen, so the operator can see the robot and the items.
To clear the screen of all text, you can use the SCNCLR command. To set the color of text to be printed (from 0 to 31): COLOR number. Similarly, to change the color of the border or background: BORDER number or BACKGROUND number. To set the position of where the next text will be printed: CURSOR column, row. To print a string of text at the current cursor position: PRINT string.
One word of caution with PRINT: if the string you are printing extends beyond the right edge of the screen, the screen below that point will scroll down by one line. This probably isn’t what you want in this exercise, so take care with the length and position of your strings.
To plot a single character at a set of coordinates, you can assign the screen code of the character to the T@& array, and its color to the C@& array. These are two dimensional arrays, with the first index referring to the column and the second index referring to the row. T@&(0,0)=1 plots the letter A to the leftmost topmost position. (1 is the screen code for the A character. Notice that screen codes are not PETSCII codes.) C@&(0,0)=5 paints it green. You can read screen codes and colors from these arrays as well: IF T@&(0,0)=1 THEN ... and so forth.
Recall that PETSCII codes in strings give you further control of the display via the PRINT command. PRINT CHR$(14) switches the display to the lowercase character set. Be aware that not all PETSCII graphics characters are available in the lowercase set.
Here is a short program that clears the screen, switches to lowercase, and plots all of the available screen codes:
100 COLOR 1110 SCNCLR120 PRINT CHR$(14)130 FOR R=0 TO 3140 FOR C=0 TO 63150 T@&(C,R)=R*64+C160 NEXT C170 NEXT R180 CURSOR 0,5
Input
To accept keyboard input, use either GET variable or GETKEY variable. These both take a variable name as their parameter. Given a string variable (such as A$), the key being pressed is loaded into the variable as a single-character string. Given a number variable (such as A), it is loaded as a PETSCII code. GETKEY pauses until a key is pressed. GET will not wait: if no key is pressed, it assigns an empty string or a zero, and moves on. If you want GET or GETKEY to provide the PETSCII code as a number, be sure to use a byte type variable, so that the number is in the range 0 to 255: GET K&.
10 GETKEY K&20 PRINT K&30 GOTO 10
Alternatively, you can accept joystick input using the JOY() function. The function takes the port number as an argument (1 or 2), and returns a number 0 through 8 indicating the state of the joystick. 0 says the joystick is centered. All other numbers indicate a direction, starting with 1 for “up” and going clockwise: 2 for “up-right,” 3 for “right,” and so forth.
Chaos
The items are assigned random characters, colors, and positions, so you’ll need a way to generate randomness. The RND() function generates numbers using a pseudo-random number generation algorithm. The function takes an argument that determines the “seed” for the algorithm. For most purposes, an argument of 1 suffices: RND(1). The function evaluates to a random fractional number between 0 and 1. To generate numbers within a range of integers, multiply the result by the size of the range, then take the INT() of the result to lop off the fractional part. INT(RND(1)*80) generates a random integer between 0 and 79.
If you want the range of possible values to start at a value other than zero, do the inner multiplication by the size of the range, then add the starting value. INT(RND(1)*77)+1 generates a random integer between 1 and 77.
The following program plots randomly colored random characters when one of the cursor keys is pressed. See if you can figure out what the variables CL, CH, RL, and RH do.
100 SCNCLR110 CL=0:CH=79:RL=0:RH=24120 GET K&130 IF K&<>17 AND K&<>29 AND K&<>145 AND K&<>157 THEN 120140 IF K&=17 THEN RL=12 : REM CURSOR DOWN150 IF K&=29 THEN CL=40 : REM CURSOR RIGHT160 IF K&=145 THEN RH=12 : REM CURSOR UP170 IF K&=157 THEN CH=40 : REM CURSOR LEFT180 C=CL+INT(RND(1)(CH-CL))190 R=RL+INT(RND(1)(RH-RL))200 T@&(C,R)=INT(RND(1)256)210 C@&(C,R)=INT(RND(1)16)220 GOTO 110
Like the Commodore 64, a positive argument generates a new number based on the previously generated number. Unlike the Commodore 64, the algorithm is initially seeded from the MEGA65’s Real-Time Clock during boot, so you are unlikely to see the same sequence twice with RND(1).
Tip: For RFK, you probably want to prevent randomly selected values from repeating. A simple way to avoid this is to keep track of previously selected values, such as in an array, and scan the array each time a new value is chosen. If it’s in the array, restart the process to pick another value. Once a unique value is found, add it to the end of the array. In my version, I used this technique to avoid two items from appearing at the same location, and to ensure that each item uses a unique PETSCII character and a unique description.
Calm
To animate the joyful celebration at the end, you’ll need a way to pause between frames of an animation. The SLEEP seconds command pauses the program for the given number of seconds. The number can be fractional.
10 FOR X=1 TO 520 PRINT CHR$(211);30 SLEEP 0.640 NEXT X50 PRINT
Data
Each Non-Kitten Item in robotfindskitten gets assigned a description at random when the item is created. One way to put a large list of descriptions in a BASIC program is with DATA statements. These statements don’t do anything when encountered by control flow. Instead, they make values available to the READ statement, which finds the next unread value and puts it in a variable.
A traditional BASIC technique is to read the number of elements from the first DATA statement, DIMension an array of that size, then READ that many elements into the array. The program can then access any string from the array by its number index. This is a bit wasteful. It keeps two copies of the list in memory: one in the BASIC program listing, and another in variable memory. It also introduces a delay at the beginning of the program to load all of the elements into the array. The primary advantage is that each element can be accessed instantly by index.
In BASIC 65 (and BASIC 7 for the Commodore 128), there’s a better way: use sequential line numbers for the DATA statements, then use the RESTORE command to set the READ pointer to a given line number. RESTORE accepts a number expression for the line number, so you can select a string programmatically by number.
10 READ N20 K=INT(RND(1)*N)+130 RESTORE 1000+K40 READ NK$50 PRINT NK$60 GETKEY A$70 GOTO 201000 DATA 51001 DATA "THAT'S JUST AN OLD TIN CAN."1002 DATA "IT'S AN ALTAR TO THE HORSE GOD."1003 DATA "A BOX OF DANCING MECHANICAL PENCILS. THEY DANCE! THEY SING!"1004 DATA "IT'S AN OLD DUKE ELLIGTON RECORD."1005 DATA "A BOX OF FUMIGATION PELLETS."
A DATA statement has a special syntax to allow for string values that contain almost any character: values are separated with commas, and a value that starts with a double-quote contains every character (including commas) up to the next double-quote. This requires special handling if you want a string value to contain a double-quote character. Because NKI descriptions only use letters, numbers, and punctuation, one solution is to use a special character as a substitute for double-quotes, then replace it in code:
41 FOR I=1 TO LEN(NK$)42 IF MID$(NK$,I,1)="£" THEN MID$(NK$,I,1)=CHR$(34)43 NEXT I1000 DATA 61006 DATA "IT'S A DVD OF £CROUCHING MONKEY, HIDDEN KITTEN£, REGION ENCODED FOR THE MOON."
You can use the original list of 406 NKI descriptions, or write your own. The truly patient can type all of these into DATA statements by hand, a zen experience of its own. Or you can use one of these that I made for you:
nkis_upper.bas / nkis_upper.prg : uppercase character set
nkis_lower.bas / nkis_lower.prg : lowercase character set
Structure
Your program will have at least a few phases of operation, such as one to decide on the locations and appearances of the items, and another where the player is moving the robot. Each phase will be a section of lines in your program. BASIC executes lines in a given section to produce the experience of that phase. To transition from one phase to another, the program directs BASIC to GOTO a line in the appropriate section.
I based my RFK experience on Richardson’s version, which can be described as four phases:
Introduction. Display an intro message, then wait for a keypress. When a key is pressed, proceed to phase 2.
Initialization. Set up and draw the items on the screen, set the initial robot position. When complete, proceed to phase 3.
Game loop. Listen for player input, move the robot, display NKI descriptions. When the robot finds the kitten, proceed to phase 4.
Ending. Animate the joyful experience, then wait for a keypress. When a key is pressed, return to phase 1.
My program code also ends with the NKI descriptions as a fifth section of data statements. Control never reaches this section, because DATA statements do not need to be performed like other commands. (The READ statement in an earlier section accesses this data.)
One of the best things about BASIC is how it makes it easy to write and test each section separately. To start a section from the READY. prompt, provide the first line number to the RUN command:
RUN 300
You can temporarily add a STOP statement in the program to cause execution to return to the READY. prompt at that point. You can use commands to inspect the state of program variables, such as: ?NK$ When you are ready to resume execution of the program, type the CONT command. You can even assign new values to variables while paused, and it’ll use the new values when you continue the program. Don’t forget to remove the STOP statements when you no longer need them.
When you’re finished
As amusing as it would be for everyone to upload their RFK experiences to Filehost, that might not be the most polite use of the site. Let’s do this instead: send me a photo or screenshot of your robotfindskitten experience. I will try to feature a selection of submitted photos in an upcoming issue of the Digest.
If you have any questions about BASIC programming or would like to discuss the exercise, let’s meet up in the #basic channel of the Discord chat. I’m usually there.
Next month, I’ll share my BASIC RFK implementation, and we’ll look at similar tools for making an RFK game in assembly language. See you then!
— Dan
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
The MEGA65 Community Survey 2023 is now complete! It opened on August 14, ran for three weeks, and received 509 submissions. A HUGE thank you to everyone who submitted!
Let’s do a quick update, then onto the results!
Release testing update
We do not yet have a formal release candidate for the upcoming v0.96 release, which is intended to be the factory installed release for the next delivery batch. We are still waiting on test hardware for the revision 5 main board to arrive. Once it does, we will clean up the changes and document a formal test process that you can try on your own computer.
In the meantime, we’re sharing out early test plans for some major features. Before I get you all excited about early availability, please know that what’s currently available is not the release candidate. By definition, development versions are riskier than release candidates, and do not necessarily represent what will be in the release candidate.
In particular, do not attempt to flash a development core to slot 0 unless you own a JTAG adapter and know how to use it to recover from a broken state. Keep a stable core in slot 0, and use any other slot for the development core. We’re working on making it safer to upgrade slot 0 without a JTAG adapter, but this is not yet ready for testing by people that don’t actually have one.
While it is possible to have multiple MEGA65 cores installed and switch between them, this is not the case for system software files (.M65 files) on the SD card. We haven’t noticed any issues using newer system software with the earlier stable core, but keep this in mind while troubleshooting issues.
Here are the development builds of the platform components:
mega65-core development builds; the mega65r3 build is for production units and DevKits
mega65-tools development builds; macOS versions can be built from the development branch of the repo
Latest ROM beta release (owner registration required); patch files (free access, patching instructions)
Available to test: Ethernet file transfer
You are invited to help test Ethernet file transfer, a feature in the development version of the core and tools. See these instructions for testing Ethernet-based file transfer. You’ll want to be familiar with your PC’s command line interface, as the file transfer tools are currently only available in command line form.
A few things to notice:
The core download includes a new system software file, ETHLOAD.M65, so be sure to copy that file to the SD card.
This feature exposes the MEGA65 to be controlled remotely by another computer over a network. As a safety measure, the feature is locked by a DIP switch on the main board. You will need to open the case to set DIP switch #2 to the “on” position.
You can connect your MEGA65 to your local network router, or directly to a PC using an Ethernet cable. You provide the file transfer tool with an available IP address on your local network, so you will need to know how your local network or PC networking settings are configured. Typically, the IP address of your computer’s network interface and the subnet mask together indicate the IP address range for the local network. When using a router, you will need to configure your router to reserve an IP address for the MEGA65, so that it doesn’t assign it to another device. (The MEGA65 doesn’t support DHCP directly.)
You enable a remote control session on the MEGA65 by pressing the Shift + Pound key combination. The power light will blink to indicate that it is listening to the network. You can then run the mega65_ftp command-line tool to transfer files.
Give it a go, and ask questions in the Ethernet Tools Testing thread on Discord.
Available for review: User’s Guide, 2nd edition
The next delivery batch will include a fresh printing of the User’s Guide. We’ve taken this opportunity to overhaul the Guide, update it with new information for the v0.96 release and R5 main board, and do a cover-to-cover editing pass over the whole thing including the BASIC reference. The BASIC quick reference card has also been updated, and there’s a snazzy new cover image. There are a few remaining to-do items related to features that are still in progress.
Download the latest User’s Guide PDF, file documentation issues on Github, and discuss in the #manual channel on the Discord.
MEGA65 Community Survey 2023 Results
The following report attempts to summarize results while preserving the anonymity of respondents. Free text responses will be kept private and will not be quoted verbatim in this report. I will be summarizing based on my own judgement, and I will withhold outliers to preserve privacy. Please know that even if your opinion doesn’t seem represented in the report, your free text responses will still be read by the MEGA65 team.
I will attempt to base conclusions on numerical data where possible. Interpretation of free text responses will inevitably be biased. This bias reflects on me and not the opinions of the MEGA65 team. Similarly, I designed the survey and am solely responsible for flaws and biases in the way questions were structured and worded. This will be pretty casual.
Owners and non-owners
The survey asked respondents whether they currently own a MEGA65, have one on preorder, are still considering placing an order, are not considering an order, or may have previously owned a MEGA65 and no longer do. Owners were shown additional questions about the configuration of their machine. Non-owners were given additional opportunities to explain their interests and concerns.
This is a control question for interpreting other responses, and is not a useful indicator of a single population on its own. The relative sizes of the categories only describe the response pool, not the MEGA65 community at large. The survey was promoted in various channels that tend to skew variously towards owners (such as the Digest, Discord) or non-owners (such as Commodore user groups), and non-owners were encouraged to reply, with the rough guidance that anyone who has heard about the MEGA65 is invited.
When the survey was sent, approximately 900 MEGA65s had been delivered. This helps extrapolate potential population counts from a sample of the owner category. 218 respondents said they own a MEGA65, which is 24% of all owners. In contrast, there is no good way to extrapolate answers from a sample of non-owners to a larger population.
For some questions asked of all respondents, ownership status has a natural influence on responses. Someone is more likely to engage with community resources after they have received their computer. For example, 86% of current owners said they are aware of the Xemu emulator, compared to 58% of respondents who have not yet placed an order.
I will try to separate results by ownership status where there appears to be significant difference. This does not intend to imply that responses from any particular group are more or less important. There are many non-owners that make major contributions to the community, as well as owners that have not yet engaged with the project. Grouping is merely a crude way to control for differences in sampling.
What’s interesting about the MEGA65
The survey offered a list of possible reasons why someone might be interested in the MEGA65 project, and asked for zero or more selections. Raw data from multi-select questions can be difficult to interpret, especially when the question is phrased like a buffet of ideas or desires. The survey could have asked respondents to rank these, or pick a primary reason, but that felt too limiting.
Many respondents are interested in collecting vintage computers, reproducing the experience of the Commodore 65 prototype, and leveraging the MEGA65’s FPGA to reproduce the experiences of other Commodore 8-bit computers. The MEGA65 project’s goal of staying connected to history has always been a balancing act between recreating an incomplete, unreleased, and ultimately canceled design, and completing and extending that design to be practical and fun to use in the 21st century. As invigorating as it is to see the MEGA65 as an opportunity to design a new 8-bit computer, it is important to the community that the MEGA65 project values historical preservation.
Several of the options discuss programming, which is also repeated later in the survey. Many respondents want to run MEGA65 software written by others, write programs for their own enjoyment, and/or write programs for others to use. I’m personally gratified to see 19% said they want to use the MEGA65 as a platform for learning how to program computers in general. I’m also excited to see 19% said they want to learn FPGA programming. There’s a lot of potential for the MEGA65 to be an all-in-one FPGA learning platform, especially with the MiSTer2MEGA65 template as a starting point.
We can try to relate interest in recreating Commodore computers and interest in programming. 68% selected at least one of the Commodore recreation options. 72% selected at least one programming-related option. 50% of all respondents selected at least one option in both of the re-creation and programming categories.
This section included a free-text prompt for mentioning other interests and motivations. Many took this opportunity to elaborate on their interest in using the MEGA65 as a Commodore 64, and potentially other Commodore computers, with several people appreciating that new hardware is more available and less likely to fail than vintage hardware. Others gave details about their interests in programming, with some specifically interested in FPGA development. Quite a few people praised the project for its nostalgic feel, and called out wanting to participate in the MEGA65 community.
The survey asked follow-up questions to people who said they don’t use their MEGA65 or are not interested in ordering. The most common concerns are the price being too high and software being too scarce. (The survey phrased this as “Not enough software,” which was intended as “Not enough new third-party MEGA65-platform software.”) I won’t share specifics on these questions publicly due to low response rates in these categories, but all individual concerns will be seen by the team.
Platform versions
The survey asked owners to identify when they received their computers, to determine which delivery batch they received. We know that batch 1 (from mid-2022) and batch 2 (from early 2023) each delivered 400 units. Of these, 35% of batch 1 owners and 16% of batch 2 owners responded to the survey. This might indicate that batch 2 recipients are less engaged with our communication channels than batch 1 recipients.
(This is not to be confused with the proportions within the responses. Among respondents who said they owned a MEGA65, 64% said batch 1, and 29% said batch 2.)
I asked owners how often they use their MEGA65s. Batch 2 owners are slightly more likely to be using their computers more frequently than batch 1 owners, which would make sense if batch 1 owners have had more time to move on to other projects.
“More than once per week:” 19% of batch 1, 23% of batch 2
“A few times per month:” 37% of batch 1, 32% of batch 2
“Less often than once per month, but occasionally:” 29% of batch 1, 37% of batch 2
“I used it some when I first got it, but I don’t use it any more:” 13% of batch 1, 6% of batch 2
The MEGA65 platform (core, ROM, and system software) is upgradable, and the MEGA65 team continues to improve the platform and issue new releases to be installed at the factory for each delivery batch, and made available to everyone as upgrades. Batch 1 owners started out with Release v0.9 and were invited to upgrade to Release v0.95, which was the default for batch 2. We’ve tried to make upgrading as easy a process as possible, but it’s not clear how easy it is to learn that new versions are available, or learn how to upgrade.
I’m pleased to see that 72% of owners have upgraded their MEGA65 at least once. Naturally, more batch 1 owners upgraded than batch 2 owners, because batch 2 owners started with the current stable release. Quite a few batch 2 owners reported having upgraded, likely to help beta test new features. Excellent!
17% of batch 1 owners report having not upgraded. The survey didn’t ask why they did not upgrade, but given the benefits of upgrading, it’s worth taking this as an indication that these owners may not know about the newer releases, or may not know how to upgrade.
Everyone who said they have upgraded was asked what they have for their default core and MEGA65 ROM. Options included the MEGA65 stable release cores and ROMs, newer test versions of each, non-MEGA65 cores, and MEGA65-compatible ROMs such as the C65 prototype ROMs or the MEGA65 Open ROM project. In the following charts, I have included people who said they did not upgrade (and so were not asked which core or ROM they are running) in the results for the stable releases based on their reported delivery batch.
21% of owners admitted that they do not know which core or ROM (or both) that they have installed. I would guess many of these might be people who were not in a position to check their version numbers when they took the survey. This could also suggest that we can do a better job making this information easier to find. While it’s an authentic retro experience to not know or care about the chipset and ROM version of a vintage Commodore, it’s important that we help everyone keep up with updates so that they get the most out of their machines.
The survey also asked which cores everyone has installed, regardless of slot. 54% of owners have the C64 for MEGA65 core installed. Once again aggregating responses from people who said they have never upgraded:
Video and audio configuration
Owners were asked several questions about their video and audio configuration, and peripherals they are using.
81% reported using the PAL video mode as their primary mode. Of these, 17% reported that their display only supports PAL.
Similarly, 18% reported using the NTSC video mode as their primary mode. Of these, 24% reported that their display only supports NTSC.
56% of owners reported that their display supports both PAL and NTSC display modes, so they can run software that switches to either mode. 24% reported that they do not know whether their display supports both modes.
The survey asked which video and audio outputs you used primarily. Some displays support more than one input. (I actually keep both DVI and VGA connected so I can switch between them as needed.)
I asked everyone to name the make and model of their display, as free-text. Not everyone knew their make or model, but many attempted to answer. I was hoping to compile a list of recommendations of displays that support both PAL and NTSC. Not surprisingly, almost everyone was using a different model of display.
The most reported single model was the Dell P1917S, a 19" 5:4 flat panel LCD display that has both DVI and VGA inputs and supports both PAL and NTSC, and you can still buy it new. It doesn’t have built-in sound, so get a pair of powered computer speakers with a 3.5 mm audio input. Note that the Dell soundbar for this model is USB audio only and won’t work with the MEGA65. I have used this model, as well as a vintage Dell 2001FP with an older soundbar with a 3.5 mm audio jack. I can report that the P1917S handles the VGA signal much better than the 2001FP, and the P1917S doesn’t freak out about sound over HDMI either the way the 2001FP does for DVI video.
I also asked about video capture equipment. Only a few people said they use it, primarily for making or streaming video of their MEGA65. Generic HDMI-to-USB capture devices seem to work just fine. I use an Elgato Cam Link for this purpose, and I got a ClonerAlliance Box Pro based on a recommendation from retroCombs which I also like a lot.
Peripherals and supplies
I asked everybody (not just owners) about what’s in their closet, in an attempt to survey what people can be expected to own that would extend their MEGA65 experience.
In general, lots of respondents have joysticks, and quite a few have mice and paddles. 11% reported not having any of these.
Of the paddle owners, 57% have Commodore paddles, and 41% have Atari paddles. Great job knowing what paddles you have! It was only last year that I learned there was a difference. As a kid I used Atari paddles with my Commodore and didn’t understand why my paddles had about half a turn of dead space.
Many respondents, 68%, own a vintage 5-1/4" Commodore disk drive. I included a free-text field for other IEC devices, and plenty of people reminded me that SD2IEC devices are popular. Zero respondents reported owning a vintage dual drive unit, such as the Commodore 4040.
I asked how many floppy disks you own, of any type and for any purpose. 62% said they have more than 20. 14% have none. I intended this as a rough measure of how many people might use the built-in floppy drive. Of course, the drive could still be used with a future purchase of MEGA65 software distributed on 3-1/2" floppy.
Connectivity and development hardware
88% of overall respondents said they could connect a MEGA65 to their local network via Ethernet from where they use a MEGA65, or from where would use one if they had one. Those new networking features of the MEGA65 will be a game changer for transferring files without extracting the SD card, and for doing cross-development. Anyone without convenient access to an Ethernet connection might be able to use a Wifi-ethernet bridge.
29% of owners have a JTAG adapter for their MEGA65. 11% of owners have a UART serial adapter. JTAG and UART serial are powerful tools for programmers doing cross development and contributing to the MEGA65 platform, and some have used them just to transfer files back and forth between their PC and their MEGA65. The new Ethernet-based tools can move files and programs at much faster speeds, and are likely to eliminate the need to buy extra hardware for most people. JTAG is still needed for testing bitstreams, and JTAG/UART are still useful for remote debugging tasks, at least until someone makes a similar feature that works over Ethernet.
Small but non-zero percentages of respondents own a MEGA65 DevKit, and/or a Nexys or Wukong FPGA development board, all of which are alternative ways of running the MEGA65 platform.
Software
The survey asked how many MEGA65 software titles have you downloaded off of the Internet. It asked this of all respondents, not just MEGA65 owners, because some people use the Xemu MEGA65 emulator as a temporary or permanent substitute for MEGA65 hardware.
12% of owners say they have never downloaded MEGA65 software. 71% of non-owners have never downloaded software, which makes sense. The non-owner sub-categories break down as you would expect, with greater degrees of interest in the project correlating with more software downloaded.
Retro software publisher poly.play currently prints two commercial titles for the MEGA65: Hibernated 1 by Stefan Vogt, and Showdown by Badger Punch Games. 14% of respondents have purchased at least one of these. 6% of respondents have purchased both titles. 40% knew about them but did not purchase either, and 45% did not know about one or both.
Xemu
The Xemu MEGA65 emulator by Gábor Lénárt (LGB) is the definitive PC emulator for the MEGA65 platform. 74% of respondents knew there was an emulator before taking the survey. Of the people who said they knew about Xemu, 48% said they use it.
There are several reasons people use Xemu, so the survey asked about them.
Selecting just for people who already own a MEGA65, the proportions of uses don’t actually change much except, of course, for respondents stating they use Xemu because they don’t own hardware.
The survey gave everyone an opportunity to provide feedback on Xemu. We collected dozens of excellent suggestions and feedback in a wide variety of categories. Xemu is a well-loved tool and a cornerstone of the MEGA65 project, and we’re all grateful to LGB for working on it!
Documentation
Every MEGA65 ships with a first edition printed User’s Guide. 28% of owners use it regularly, 44% refer to it occasionally, and 26% rarely or never use it.
The User’s Guide is just one of a set of books in progress that form the complete documentation for the project. The latest versions are available as PDF files, free for anyone to download. The survey asked all respondents how often they use the PDFs.
When I first got my MEGA65 in May of 2022, I wrote Dan’s MEGA65 Welcome Guide as a supplement to the official documentation, intending to patch over the gaps between the intended experience and the first delivery batch that I knew would be fixed in later versions. It ended up being a generally useful resource for many. 62% of respondents were aware of the Welcome Guide.
The survey asked about what medium you prefer your documentation. My personal preference would be professionally printed manuals, and the MEGA65 documentation was set up to maybe someday become a multi-volume printed set, similar to the printed User’s Guide. I wanted to gauge interest in crowdfunding a professional print run. Crowdfunding can be tricky with a small population, especially one that’s difficult to reach. Print-on-demand services are easy to use with no up-front or on-going monetary investment, and they have significant advantages for global fulfillment, but they’re lower quality than what I have in mind. My favored option for printing would require a minimum print run.
In the survey, I proposed a hypothetical version of this project where an “entire set” (with the actual content to be determined) might cost $150 USD, and individual volumes might cost $35 USD. I asked everyone their preference: the entire set, a couple of volumes, print-on-demand, or digital only.
If the responses are representative of the preferences of everyone who will own a MEGA65 by the end of 2023 and enough of them could be reached to announce a crowdfunding campaign and at least half of them backed the project, we could make fancy books a reality. It’d be a limited print run, followed up by a secondary print-on-demand version available in perpetuity. I like these odds well enough. Even if the crowdfunding campaign never happens, investing in content will improve the digital versions and we can easily do an official print-on-demand offering when we feel they’re in a state worth printing.
I’m also going to try to get a web version of the documentation working. No promises—it’ll be a chunk of work to clear up the LaTeX for web export—but it’s a worthy goal.
Dan’s MEGA65 Digest
If you’re reading this, you’re probably aware of my monthly email newsletter, Dan’s MEGA65 Digest. I initially announced the survey in the Digest, so it’s not surprising that 60% of respondents were aware of the Digest before the survey. I’m delighted to see that 39% of respondents were unaware, because that means two things: 1) there are more people out there who might be interested, and 2) our efforts to promote the survey reached beyond people already aware of the Digest.
Of the people aware of the Digest, 67% read the text version by email. 15% go to my website directly, and a few use RSS or the Substack website. It’s good to see that I could migrate the Digest to another newsletter provider if needed and probably nobody would miss it.
Did you know that there’s an audio podcast version of the Digest? 42% of people familiar with the Digest did not know. 18% listen to at least some issues by audio. 51% of listeners use the Substack service to do so (because clicking on the email takes you there), and 37% of listeners use a podcast app. I enjoy making the audio version and I’m grateful that some find it entertaining.
Thank you all for the generous comments in the free-text field! Some people asked for more advanced technical topics, and others asked for less advanced technical topics. 😊 Several people mentioned interest in a print anthology, or just a printed zine. I’m not sure I could fill a print zine each month—at least not without collaborators.
Community resources
62% of all respondents are aware of the MEGA65 Filehost, an essential community resource. 91% of owners who responded to the survey know about it.
Of those who are aware, almost everyone has used the Files section to browse and download files. 47% of respondents who own a MEGA65 have redeemed their owner code, which is surprisingly low! You need to redeem your owner code to access release packages containing the closed ROM (though there are other ways to get the closed ROM). The survey asks about other features of Filehost, and it generally looks like people use them proportional to the amount of material posted in those categories. No surprises here.
83% of all respondents are aware of the MEGA65 Discord, and 54% have an account. Of those that have an account, 29% visit it daily.
76% of all respondents are aware of the MEGA65 section of Forum64.de, and 33% have an account. Of those that have an account, 18% visit it daily.
25% of all respondents have both a Discord account and a Forum64 account. 75% of them visit the Discord at least once a month. 28% of them (7% of all respondents) visit the Discord daily.
Programming
The majority of respondents have more than 10 years of experience writing computer programs. The survey purposefully did not make a distinction between professional vs. hobbyist programming: the survey used phrases like “writing computer programs” to be inclusive of both.
79% of all respondents said they are interested in writing their own programs for the MEGA65. Those that expressed interest were presented additional questions about programming tools.
Of those that want to program, 75% are interested in assembly language, and 75% are interested in BASIC 65.
The survey offered a free-text box to express interest in other programming languages. Python was mentioned multiple times. In my experiences with Python and with retro computing, I’ve seen many people wonder aloud if MicroPython could be ported to a vintage computer. In those discussions, the quality of C compilers that target retro platforms has been mentioned as an issue, so I wonder if it’s a topic worth revisiting in the context of llvm-mos. Pascal also got multiple mentions, which is more likely to be built from scratch for the MEGA65 by an enterprising hobbyist.
The survey listed a bunch of popular cross-development tools and let everyone pick their favorites. Visual Studio Code, CBM prg Studio, Kick Assembler, and cc65 were top of mind for most people. Several people mentioned C64 Studio in the free-text responses.
I know people that love cross development, writing programs for the MEGA65 using a modern computer. I also know people that love on-device development, preferring to shut off the modern computer and use the MEGA65 as its own programming environment. The survey asked for your preference, and results were split three ways: on-device development, cross development, and “depends on the project” all got roughly the same proportion of responses.
Demographics
65% of respondents are between 45 and 54 years old. Remaining respondents are roughly split between 35-44 years old and 55 years or older, with only a few respondents in other age groups.
The MEGA65 community is international, with strong contingents in Germany and the United States.
Discussion about the MEGA65 on Forum64.de is almost entirely in the German language, and making MEGA65 programming resources available in the German language is a frequent topic of discussion. For the survey, I asked two questions about spoken language: in what language(s) do you prefer to socialize online, and in what language(s) do you prefer to consume technical information?
86% of everyone who answered these questions answered both questions with the same language or languages. 80% of these said English. 7% said they preferred German exclusively for both. 10% offered both English and German as their answer in both categories.
14% of respondents gave different answers for preferred languages for socializing vs. technical information. Of those that gave different answers, 85% said English was preferable or at least acceptable for consuming technical information. 13% preferred technical information in German.
Danish, Dutch, English, Finnish, French, German, Hungarian, Italian, Norwegian, Polish, Portuguese, Scandinavian, Spanish, and Swedish were all mentioned at least once. I won’t draw any conclusions due to sample size, but it’s exciting to list them all out!
The survey asked for everyone’s preferred social media service. This is a bad question. Outside of the Digest, the survey was promoted almost exclusively on social media, and so response rates are most likely to correspond to visibility of the survey announcements on those platforms. Nevertheless, it’s good to know that Facebook and X.com (née Twitter) are still important ways to reach people.
Despite how the survey was promoted, 22% of respondents say they do not use a social networking service. There was some ambiguity in the question about whether Discord qualifies as social media. The survey did not include it in the list, but it included a free-text prompt for other services and many used it to mention Discord. Combining social media non-users and users of services the survey did not list, that’s 28% of respondents.
Other vintage and modern-retro computers
Nearly every respondent has owned or used a Commodore 64 in their lifetime. 76% of respondents currently own a Commodore 64. Only 2% of respondents have never owned or used a Commodore computer.
45% of respondents own an SD2IEC device. 35% own an Ultimate 1541 (any model). Of potential interest to future MEGA65 cartridge authors, 21% of respondents own an EasyFlash 3, and 10% own an EasyFlash 1 or 1CR (purchase link).
34% own, have preordered, or intend to order at least one of several retro-style modern computers that the survey listed. The breakdown between the models isn’t interesting, mostly corresponding to the current availability of those machines.
Modern computers
53% of respondents use a Windows computer. 22% use an Apple laptop or desktop. 20% use a Linux laptop or desktop.
Of the Windows users, 44% are using Windows 11, and 52% are using Windows 10. Several people are using Windows 7.
Of the Apple users, 73% have an Apple Silicon (M1 or M2) processor, and 26% have an Intel processor. 53% are using macOS 13 Ventura, the latest stable release. 31% are on the macOS 14 Sonoma public beta. 13% are using older versions of macOS.
I did not ask about Linux distributions or variants, nor did I ask about Intel vs. ARM for Linux or Windows. Tablets (iOS, Android), BSD variants, Windows Subsystem for Linux, AmigaOS 4.1, and MorphOS were represented in the minority replies.
MEGA65 project feedback
The survey concluded with an invitation to provide free-text feedback to the MEGA65 project. I will try to summarize common themes and provide my own commentary based on my experiences with the project in the last year. This will naturally reflect my own biases.
Enthusiasm. Nearly everyone took this opportunity to express words of encouragement and praise for the project. Thank you all for your kindness and generosity! It is gratifying to know there are so many people out there who believe in the MEGA65 project and are enthusiastic about the community and what everyone has accomplished so far.
Price and shipping cost. As of this writing, the MEGA65 production model, with the mechanical keyboard, injection molded plastic case, and built-in 3.5" floppy disk drive, has a retail price of 666.66 € ($722.38 USD), not including taxes or shipping costs. The total cost to buy one delivered to where I am in the United States is 790.20 € ($856.79 USD). This price alienates some people who are interested in the project, due either to the value proposition or the ability to pay. Less expensive options include running the MEGA65 core on a Nexys Artix A7 FPGA development board, or running the MEGA65 ROM in the Xemu emulator. The central FPGA chip dominates the price, and burning the chipset design into cheaper ASICs would require several orders of magnitude more people interested. (And of course, ASIC-based computers would not be upgradeable.) Counterintuitively, eliminating the floppy drive does not meaningfully reduce the cost, and new plastic case shapes would require an up-front investment in new moulds.
Shipping delays and shipping updates. Many who have preordered a MEGA65 have experienced shipping waits longer than one year. Estimating shipping dates has been nearly impossible over the past two years due to the unavailability of essential components, and a reliable estimate is only possible after all parts have been acquired by our assembly and distribution partner Trenz Electronic. Trenz has been great about responding to customer service inquiries with rough guesses, but it is not currently a regular practice to send periodic emails to pre-order customers during the process. Some survey respondents expressed concern about the lack of proactive order status communication. Due to privacy laws in Germany, Trenz Electronic is not allowed to share their list of pre-order customers with the project team. The best we’ve been able to offer so far is to use other communication channels to encourage everyone to subscribe to the newsletter or follow project announcement channels on the Discord, YouTube, or Paul’s developer blog. It has been a challenge to make sure pre-order customers are aware of these resources.
Sources of technical information. The MEGA65 has a substantial amount of technical documentation available, but this can be difficult to notice given how that information is stored and disseminated. Survey respondents pointed out that having information spread over PDF manuals, a Confluence Wiki, and Filehost articles is inconvenient, and it’s unclear where to find each kind of information. Sometimes the easiest way to get an answer to a technical question is to ask the Discord chat, but answers to questions previously asked are difficult to find, and both the community and its discussion archives are only available to Discord users. Discord is an ephemeral medium and does not function as a technical resource, at least not a passive one, and it’s unreasonable to expect all users to read every discussion. Moreover, none of these resources are reliably indexed by search engines, either by design (Discord cannot be crawled; Confluence blocks crawlers) or by unintended technical limitations. Multiple survey respondents wished for a more traditional open-access web forum, and some are disappointed that only a subset of the community participates on Forum64.de.
Better software. Many MEGA65 owners want to use their computer to run games and applications written by others. Without a large library of quality titles (for some relative definition of “quality”), the MEGA65 is mostly useful for learning to program, writing programs from scratch, or running C64 software via the C64 core (or GO64 mode). Some respondents emphasized that they would be willing to pay for quality MEGA65 software. Some respondents said they are waiting for the software library to grow before investing in a purchase.
German-language technical resources. Multiple respondents used the free-text feedback field to emphasize the need for German-language technical information. There is a German MEGA65 handbook that is highly valued. The topic of German-language video tutorials, livestreams, and classes comes up occasionally on Forum64.de.
Technical issues. Several respondents mentioned some of the common technical issues we have experienced in the first year, both in the electrical hardware and in the core and ROM. The new main board revision will help with some of these, and the core and ROM are active projects that welcome contributors. Many respondents expressed gratitude for everyone who is contributing fixes, and are excited to see the work continue.
Feature areas. Many respondents were excited about the potential for new features, including networking applications, D64 disk image support, more alternate cores, and more on-device programming environments and tools. Several respondents wished for more programming tutorials and recipes. Several respondents expressed an interest in a more compact version of the keyboard case without the floppy drive. Others reminded us that some owners of R3A boards would be interested in buying a bare R5 board as an upgrade. (I considered asking about interest in an R5 upgrade as a survey question, but we don’t know how much such an upgrade would cost. We don’t realize economies of scale in the hundreds of units, so it doesn’t help to forecast sales.)
More community events and publications. Respondents expressed an interest in more live streams like the MEGAVISION live stream that took place earlier this year. Conferences were also mentioned. There were multiple suggestions for a print magazine.
Reach
One of the biggest questions I wanted to answer with the survey was, how effective have we been at connecting the MEGA65 community with the project, and how can we improve? Communities around projects tend to form layers of engagement, and we want to foster the growth of all of those layers and reach as many people as possible that would benefit from the project. We have known issues with making resources available in search engines and providing information to pre-order customers. We can only hope that as people get more interested, they look for community resources more aggressively.
The first test was simply, how many people can we get to take the survey? 509 respondents feels downright miraculous considering that only 900 people currently own a MEGA65 (DevKit or production model). We aren’t privy to the number of pending pre-orders but we believe it is over 1,000. The survey promotion reached quite a few people who are interested enough in the project to take the survey but have not yet pre-ordered, but it’s impossible to guess the size of that population from the survey alone.
Our limited ability to reach out to the community makes it difficult to promote resources like Dan’s MEGA65 Digest, and difficult to promote community projects that require financial support. A small number of free-text responses in the survey ask for things that already exist, which suggests that we could be doing more to promote the resources we already have.
The survey asked about several specific community resources, projects, and concepts, and made a distinction between “I know about that resource but don’t use it” vs. “I didn’t know about that resource.” These replies are a useful indicator of how well information about the project has disseminated. The following numbers are shown as percentages of owners. Owners are more likely to be familiar with resources, and have a known population size as a basis for the sample. This is not meant to exclude non-owners from consideration, only to have a basis for comparison.
10% of owners replied that they did not know about the JTAG or UART serial adapter.
4% did not know about the DevKit.
13% did not know about the Nexys FPGA dev board.
34% did not know abut the Wukong FPGA dev board.
20% did not know about Hibernated 1 by Stefan Vogt. The numbers were similar for Showdown by Badger Punch Games.
13% were unaware of the Xemu emulator.
5% were unaware of the PDF manuals.
16% were unaware of the MEGA65 Welcome Guide.
22% were unaware of Dan’s MEGA65 Digest.
8% were unaware of Filehost.
7% were unaware of the MEGA65 Discord.
14% were unaware of Forum64.de.
Conclusions and next steps
The MEGA65 is an open source volunteer-run project. It doesn’t have development resources so much as interested parties. Nevertheless, many people who work on the project want the project as a whole to succeed, and want to work on important things, so seeing which tasks can have the most impact can influence contributors.
I can’t speak for anyone else, but personally, I’m more motivated to work on information visibility. This was already my area of focus with the Digest. I have ideas for making our information easier to find in search engines, and I hope to use my web development background to improve our web-based resources and community tools. I also want to evolve the Digest to make it more visible and interesting to a larger audience. The Digest is our primary tool for disseminating news and project status—including best available information on shipping updates—and I want everyone who is interested in the project (at any level) to at least be aware of it.
I’m excited to see that we might be able to afford a high-quality print run of the manuals, if we can finish them. Print books are a fun addition to the retro experience, and they’re one of my favorite things to make. It’s good to be reminded by the data that many people would prefer browser and e-reader-friendly resources, and hopefully we can produce both print and web resources using the same content.
There’s an opportunity for German content creators to fill a need. Bloggers, YouTubers, and live streamers capable of producing tutorials and walkthroughs in German—at any level of technical complexity or production value—could have a big impact. I’ve seen people ask for German-language BASIC programming introductions on Forum64.de. You don’t need to know a ton about the internals of the machine to help others with their first steps, and simply blogging your own learning adventure can attract a grateful audience. We can also consider crowdfunding a professional German translation of the manuals once they are reasonably complete.
There’s no shortage of interest in improving the platform or the hardware, just a shortage of time. I believe our open source processes are in decent shape, so if anyone is interested in helping out, please reach out on the Discord. Bug reports are always welcome, even if you are unable to submit bug fixes.
As for growing the software library, the number of owners is expected to more than double by the end of the year. It’ll be exciting to see what everyone comes up with!
That’s it for this year’s survey! Thanks again to everyone who responded. Let me know if you have any feedback on the survey itself or this report. We might do this again next year.
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
Hey all! I’m keeping this issue short, because there’s something more important for you to do instead of reading.
The MEGA65 Community Survey 2023
The MEGA65 Community Survey 2023 is now open. This survey is for anyone even a little bit interested in the MEGA65, whether you own one, have preordered, are still considering it, or don’t intend to order but still enjoy seeing what’s going on. This includes anyone not subscribed to this Digest, so please share this link.
Responses are anonymous. We will share aggregate anonymized results in a future Digest. Full-text replies will be kept confidential to the MEGA65 Steering Committee.
We’re targeting 400 responses for this year’s survey. If everyone subscribed to the Digest responds, we’ll hit that easily. (Please only take the survey once, so we get accurate counts.)
Here’s that link again: please take the survey. Your response will go a long way to guiding the future of the MEGA65 project. Thank you!
New Galaga core!
After you have completed the survey, it’s time to hit the arcade! Run don’t walk to the Galaga core for the MEGA65 by muse. This isn’t just a version of Galaga for the MEGA65, this is Galaga, a port of the original arcade hardware architecture running directly on the MEGA65 FPGA. The core even supports connecting a vintage VGA monitor turned on its side to fully replicate the arcade experience.
Installation requires a few steps, including fetching the original Midway Galaga ROM from the Internet (using the link provided) and running a Python script. See muse’s installation instructions.
When starting the core, give it a minute to go through the arcade hardware test routine. The core displays its keyboard controls briefly; press the spacebar to dismiss. Press 5 to insert a quarter, then press 1 to start a one-player game. You can control the game via a joystick in port 1, or use the keyboard: A and Z move left and right, and the Cursor Up key fires. The P key pauses. As with the C64 core, press the Help key to open a settings menu.
Huge thanks to muse for this effort, and to all of the original implementors of the core. (And to Midway too, but they already have all of my lunch money.)
R4 is now R5
Last month I mentioned that there will be a new revision of the main board that will start shipping with the next delivery batch, known as R4. Due to restricted availability of a component, the board needs one more revision. This gives us an opportunity to further refine the cartridge interface before the revised board goes into production, so we’re taking that opportunity. The core needs a corresponding adjustment, so the board is now known as “R5” to distinguish between the core builds. R4 boards will not be manufactured. The next delivery batch will receive R5 boards.
The cartridge interface enhancements will make it possible to support fancier utility cartridges. As with the other revisions, only time will tell what differences will actually be notable in practice. The most important reason to know which main board revision you have is to download the correct versions of core files.
Pudding Mountain Miner 65
I said I’d keep this short, but I can’t resist including a bit of BASIC fun.
Of all the Commodore 64 games that could have been a staple of my childhood, an unlikely contender was “Pudding Mountain Miner” by Charles Brannon. Brannon was an editor for Compute!’s Gazette magazine, and originally wrote “Pudding Mountain Miner” as a short type-in to appear in newspapers promoting the magazine when it was getting started. He wrote up an explainer in the April 1985 issue of Gazette, in the “Horizons” column.
The game is a one-button shooter where you’re a fighter pilot dropping bombs on Pudding Mountain, digging for buried treasure. If you hit the dollar sign, you win the big bucks. If you hit the mantle underneath the pudding, it’s game over.
I liked Miner because it was fun and cute, and also because it was only 17 lines of BASIC code. I don’t think I ever bothered to save it to disk. I would just type it in any time I wanted to play it.
Here’s the program listing converted to a form where you can use it with petcat to generate a PRG in case you don’t want to type it in (but what’s the fun in that?). This version runs just fine with the MEGA65’s GO64 mode.
100 v=(peek(0)=76):w=40+18v:t=1024-6656v:c=55296+16896v:s=53281+16402v 110 c$=chr$(147):printc$:pokes,1-26v:for i=0tow-1:q=22w+i 115 poket+q,160:pokec+q,7:next 120 s$=chr$(32)+chr$(158)+chr$(18)+chr$(188)+chr$(146)+chr$(156)+chr$(185) 130 s$=s$+chr$(31)+chr$(175):q=rnd(1)(w-7)+3+22w:poket+q,164:pokec+q,5 140 fori=0tow-1:forj=0to7rnd(1)+3:q=(21-j)w+i:poket+q,160:pokec+q,2:next:next 150 printchr$(142);chr$(19);:y%=4rnd(1)+1:fori=1toy%:print:next:x=0 160 l$=chr$(157):prints$;l$;l$;l$;:x=x+1:geta$:ifa$=""andx164thenfori=0to255:pokec+q,i:poket+q,i:next:printc$;"you lost":goto230 220 fori=1to50:poket+q,32+132f:f=1-f:next:printc$;"you won! ";b;"bombs" 230 print:print"press "chr$(18);"return";chr$(146);" to play again" 240 geta$:ifa$<>chr$(13)then240 250 run
Brannon’s article gives a line-by-line description of how the program works. I’ll try not to repeat the whole analysis, but I do want to notice a few things.
For starters, it’s difficult to read. Brannon wrote this to appear in a newspaper ad, so he used Commodore BASIC’s abilities to combine multiple statements on a single line and omit spaces to cram all of the code into a dense rectangle. C64 programmers are accustomed to omitting spaces because it speeds up the program slightly when running at 1 MHz, and it uses slightly less BASIC memory. At the MEGA65’s 40 MHz, omitting spaces makes no measurable difference and isn’t worth doing.
Early Commodore BASICs lack built-in commands for graphics and sound, so programs like games tend to contain many POKE statements. As a result, very few lines of code say what they mean, at least to a reader that didn’t write the program. To work backwards from an obscure statement like POKET+Q,160, you have to recognize that this actually says POKE T + Q, 160, that the variables T and Q are forming an address, that T is assigned the number 1024 under some circumstances, and that this is the start of C64 screen memory. Then you know to look up a screen code table to determine that the value 160 represents a blank space. This statement is probably writing a blank space somewhere on the screen determined by the Q variable.
I’m not criticizing the way the program is written. If anything, I’m criticizing the way programs had to be written for the Commodore 64, at least under some circumstances. Of course, Brannon intentionally wrote this program to take up as little printed space as possible, so it is designed, crunched, and organized in an atypical way.
One of the most interesting parts of the program is this bit in the very first line: V=(PEEK(0)=76) When this runs on a Commodore 64, the V variable is set to 0. Some simple arithmetic determines how the other variables on this line are set: W is set to 40, T is set to 1024, C is 55296, S is 53281. 40 is the number of characters that fit on a single line of a C64’s screen, so W probably stands for “width,” and sure enough that’s what it appears to mean elsewhere in the code.
But what’s up with the PEEK(0)? It turns out that this program also works on the VIC-20! When this runs on VIC-20 hardware, PEEK(0) evaluates to 76, so the comparison PEEK(0)=76 evaluates to true, so the V variable is set to -1, which is how “true” is represented in Commodore BASIC. This affects the settings for the other variables: W=40+18*V becomes 22, which is the screen width for the VIC-20.
This game takes advantage of the similarities between the C64 and VIC-20, along with some intentional design choices, to scale its display to fit the computer it’s running on. This poses a question: what’s the smallest change we can make to this program so that it runs on the MEGA65 in 80-column mode?
On the MEGA65, the BASIC syntax is the same, the PETSCII character and screen codes are the same, and the VIC-II registers are present at the same addresses by default. Screen memory is relocatable and not at the same location by default, so we’ll have to do something about that. Color memory (whose location is stored in the C variable) needs to be accessed at a different memory location if we want to access the full 80 column’s worth.
Let’s just try this and see what happens:
100 w=80:t=$0800:c=$1f800:s=$d021
(I’m using hexadecimal values here because I’m used to thinking of these addresses in hex, and BASIC 65 supports hexadecimal value literals.)
I had read through the whole program to make sure this would work and was still surprised that it did. There’s only one issue: it’s way too fast! We need to slow down the MEGA65 processor to make it playable. Thankfully, this is possible with a BASIC 65 command.
105 speed 1
It’s the nature of this game that the denser character width makes it more difficult to hit the target, but it works! No other changes are necessary.
Consolidating this into a single BASIC listing that works on all three machines would be straightforward—or not so straightforward if you want to preserve the compactness of the original. I’ll leave that as an exercise.
That’s it! Next month should be action-packed: we’re about to open public beta testing of the next major platform release. I’m trying to do my part and fix as many bugs as I can before the deadline. Of course, this is an open project, so you can get involved with testing right away if you like. Inquire in the Discord.
Go take the survey, and tell your friends. See you next month!
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
In last month’s Digest, we introduced cross development, the practice of writing MEGA65 programs using a modern PC. We looked at several tools for writing programs in the BASIC 65 language and in 45GS02 assembly language, and we welcomed a new BASIC-like compiled language with MEGA65 support called XC=BASIC.
Let us take one step further into the world of compiled languages. The C programming language is one of the most widely used programming languages in computing history, suitable for everything from microcomputers to mainframes. It was the language that built the Unix operating system—or maybe it was the other way around. Fifty years later, C is still in widespread use, for better and for worse.
As fun as it is to write in assembly language, larger programs benefit from a language with a bit more structure to manage the inherent complexity of software. There are several cross-development tool chains for writing Commodore programs in C, and these can also be used to write programs for the MEGA65. In this issue, we will try using one of these tools to write our first C program, and walk through the concepts involved. We’ll look briefly at other tools and resources for getting started with C programming for microcomputers. And we’ll consider a much newer language vying to be the C of the next fifty years, called Rust.
Featured Files
We’ve been talking about coding tools lately, so let’s do an all-tools edition of Featured Files!
The Coffeebreak Compiler by TOS22. Matthias wanted a comfortable way to hack on assembly language code directly on his MEGA65 that he could extend with modern conveniences. The result is a lightly structured custom language and IDE that accepts inline assembly code and produces machine code programs, similar to ubik’s Eleven for BASIC. Press the Help key to browse the built-in documentation and tutorials.
MEGA65-Forth alpha by carthibar. MEGA65-Forth is a project to build an interpreter and interactive environment of the Forth programming language for the MEGA65. This is an early release with some features still in progress, but enough has been implemented to start learning the language. It is based on FIG Forth 1.1. Check out the source code and release notes on Github, as well as the Getting Started documentation.
bf65 by dddaaannn (that’s me!). The experimental minimalist programming language known as Brainfuck was invented by Urban Müller in 1993 as an attempt to design a language with as small a compiler as possible that is still Turing complete. The language is famous for its funny name and for having only eight parameterless instructions. I was motivated to write bf65 when I had the idea to take advantage of the MEGA65’s built-in BASIC editor as the editor for bf65 programs, including the ability to combine BASIC and bf65 code in the same program. The D81 disk image includes the bf65 interpreter and several demo programs. See documentation and source code on Github.
C64 for MEGA65 V5 is official!
We’ve mentioned the Commodore 64 core for the MEGA65 a few times in this Digest, and with good reason. For many owners, the potential of the MEGA65 to be an FPGA-based multi-Commodore platform is a major attraction, a chip-level recreation of an entire line of vintage computers that share a non-PC keyboard layout, made from all-new components and modern hardware conveniences. The Commodore 64 core by MJoergen and sy2002 is a huge step toward this potential.
C64 for MEGA65 version 5 is now officially released. For installation instructions, see the May issue of the Digest, and the excellent documentation.
Don’t miss the epic video trailer for the new release!
MEGAVision video now available
The first ever MEGAVision live streaming event took place on July 1st and was a huge success! Hosted by RetroCombs, Gurce, and lydon, MEGAVision featured nine short talks about MEGA65 projects. Watch the replay online, then visit the MEGAVision wiki page for links to resources for everything discussed in the presentation. I was happy to present my bf65 toy language interpreter at the event.
This was so successful that we’re excited to do another one sometime soon. Start thinking about topics you’d like to know more about, or topics you’d maybe like to talk about at the next MEGAVision. If you have an idea for a talk, send a message to Gurce or lydon on Discord.
Huge thanks to the organizers, to everyone who gave a talk, and to everyone who attended!
R4 mainboard announced
The MEGA65 project continues to invest in improving every aspect of our favorite computer. On June 30, Paul posted an in-depth article to his developer blog describing some of the recent improvements made to the design of the MEGA65 main board. The next revision of the main board, known as R4, will start shipping with the next delivery batch later this year.
Several of the improvements in the R4 board are electrical. There’s a fix for an HDMI back-power issue, improved audio quality from the 3.5 mm jack, a new Real-Time Clock (RTC) unit, and improved RF shielding for the major components. The design has also been reworked to use one fewer FPGA chip. These changes resolve some minor issues, but they otherwise do not affect the operation of the computer.
Two changes enhance the capability of the system slightly. There is now additional RAM hardware that can be used by FPGA cores, enabling porting of a larger variety of MiSTer cores in the future. Also, the joystick ports are now bi-directional, a capability used by a few peripherals like the Protovision Protopad and the Commodore version of the AtariLab science kit that I had as a kid.
Nothing about the MEGA65 platform changes with this revision. Any program written for the MEGA65 will run on both R3 and R4 boards. While the RAM hardware is intended to enable broader development of alternate cores, no core currently exists that needs it, and it’ll be a while before one does. Moreover, future cores that don’t need the extra RAM are unlikely to use it, so that they will work on all MEGA65s.
Nevertheless, it’s natural to wonder: will MEGA65 owners with the R3 main board be able to buy the R4 board as a replacement? Hopefully someday! The first priority is to fulfill all of the remaining preorders for complete MEGA65s. Beyond that, it’s not obvious that Trenz Electronic can feasibly stock spare parts, especially pre-assembled main boards with expensive FPGAs. So far, preorders have been fulfilled in batches, and it’ll take time to figure out the costs, risks, and logistics of switching to an in-stock distribution model. I know I’d love to be able to someday buy MEGA65 parts for upgrades, repairs, and building new machines with custom configurations.
For now, I’m excited by how the R4 main board is an investment in the MEGA65’s continued success. More than anything else, it means we will all get more out of our computers, no matter which version we have.
A single-file C program
A compiler is a tool that takes a computer program written in some programming language and generates an equivalent program for the target platform’s machine code. You can write a program in the C programming language, then use a C compiler to generate the equivalent machine code program for the MEGA65.
As with other development tools, a compiler could be a program that runs on the target device directly, like how Mega Assembler runs on the MEGA65. There is not yet a C compiler that runs natively on the MEGA65, but it is possible to use a cross development workflow as we did in the previous issue of the Digest. You create a source file on our PC, then use a cross compiler to generate a MEGA65 PRG file.
As much as I want to, I can’t fit a full C language tutorial in this Digest. Nevertheless, let’s start with a small C program, so we have something to play with:
int main() { puts("IT'S A C PROGRAM!\n"); }
Put this in a file named hello.c. This is our source file.
Most C programs consist of multiple source files, with filenames ending in .c for definitions and .h for declarations (“header” files). Exactly one .c file defines a function named main(), which is where the program begins. A source file can #include header files to learn about functions provided by other modules. In this case, #include brings in the declarations from a module of the C standard library, including a declaration for the puts() function used by the main() function. Refer to any good book on C programming for an explanation of how C programs are organized.
An ideal compiler targeting the MEGA65 would know all about the inner workings of the MEGA65’s CPU, so it can use all of the CPU’s features in its machine code. So far, nobody has written a C compiler that targets the MEGA65 specifically. Instead, we’ll take advantage of the MEGA65’s Commodore lineage and use a cross compiler that targets earlier Commodore computers. Several Commodore cross compilers can be adjusted to generate programs that run on the MEGA65.
Installing llvm-mos
llvm-mos by Daniel Thornburgh and John Byrd is a cross compiler toolchain for target platforms based on the 6502 CPU. It provides a version of Clang for compiling C programs. Thanks to Mikael Lund (wombat on the Discord), llvm-mos supports building programs for the MEGA65. It supports generating code for CPUs in the 6502 lineage up to the 65C02, which will run fine on a MEGA65.
Visit the llvm-mos Github page, then scroll down to find the download link for your PC’s operating system. As usual, macOS requires an additional step to allow unsigned command line tools to run. A complete procedure for macOS users:
Download llvm-mos for macOS.
tar xzf llvm-mos-macos.tar.xz
xattr -d com.apple.quarantine llvm-mos/bin/* Ignore the list of “No such xattr” warnings.
Verify that the mos-mega65-clang command line tool works: ./llvm-mos/bin/mos-mega65-clang --help
As noted in the llvm-mos documentation, you can add the bin/ folder to your command path for convenient access. macOS users, heed the warning that doing so may conflict with other versions of the Clang compiler you may have installed. The Apple Xcode Command Line Tools provide another version of Clang, and you may have this installed if you use Homebrew even if you don’t otherwise use Xcode. You do not need to have llvm-mos on your command path to use it: simply provide the full path to the llvm-mos commands when using them.
You can compile the hello.c example program with the following command:
./llvm-mos/bin/mos-mega65-clang -Wall -Os -o hello.prg hello.c
This produces the file hello.prg that you can run in Xemu or on your MEGA65, as we discussed in the previous issue. It loads to the top of BASIC memory with a BASIC bootstrap header, similar to the one we used for our assembly language program.
The equivalent program
So far, a compiler sounds a lot like an assembler: it takes a source file and produces a PRG file that does what we want. What’s different is how the tool decides what machine code to generate. When we write a program in assembly language, we must say exactly which machine code instructions the target machine’s CPU should execute. Assembly language mnemonics like LDA represent machine code instructions in a somewhat-human-readable syntax, and the assembler translates the assembly language to machine code, one instruction at a time.
When you write a program in a compiled language, the compiler reads through your program, then decides which machine code instructions will do what you intend. You can ask llvm-mos to show you the assembly language program that it came up with for your C program with this command:
./llvm-mos/bin/mos-mega65-clang -Os -o hello.s -Wl,--lto-emit-asm hello.c
This generates an assembly language listing as the file hello.s. Open this in a text editor to see the result. It is quite large because it includes the code for the puts() function from the standard library.
To get a better idea of what compilers can do, consider a simpler program that doesn’t involve a call to a large function:
int square(int num) { return num * num; }
int main(void) { return square(5); }
There’s a website called Compiler Explorer, aka “GodBolt.org,” that can show C code and the compiled assembly code side by side directly in your browser. It supports many compilers and target CPUs, including llvm-mos and the MEGA65, so you can see the result in 6502 assembly language.
Follow this link to see one possible version of square() and main() produced from this C code.
In this example, you can see how llvm-mos converts the square() function into several dozen instructions. The labels that begin with two underscores, such as __rc0, refer to addresses in the base page that llvm-mos sets up to be used as temporary variables by the generated code. Later in the listing is the implementation for the main() function that sets up the argument (5), calls the square() function, and ends with the result in a variable. The C language requires that the main() routine returns a number, which is used by some operating systems as a result code from a program. The MEGA65 just ignores this return value.
But that’s just one possible assembly language program. Check out this version with the -Os flag enabled.
Given the -Os flag, llvm-mos generates different assembly language code for the same C program. It’s much shorter and much faster than the previous version. How can a compiler look at the same program and produce two drastically different results?
In the first version, llvm-mos took our C program literally: we wrote it as two functions, so it generated code for two functions. In this new version, llvm-mos studied the C source code, realized that this program always returns the number 25, so it produced machine code that returns the number 25. Both of these assembly language programs are equivalent to the original C program: they both return 25.
An optimizing compiler uses many complex techniques to calculate the smallest and fastest machine code possible that is equivalent to your original program. The -Os flag enables all of llvm-mos’s optimization rules. Optimization is typically smart enough that you can leave it enabled for nearly all purposes. It’s not always perfect—I can’t explain why llvm-mos generates a few instructions for the square() function in the optimized version even though it never calls them—but it’s a huge benefit.
Just as modern chess computers can play chess better than any human, modern compilers can write machine code so efficient that it’s almost not worth writing in assembly language at all. We still write assembly language programs for the same reason we still play chess: for fun.
CMake, mega65-libc, and mega65-llvm-template
Running the mos-mega65-clang command is fine enough for simple one-file experiments. When your project grows to multiple source files or involves third-party libraries, you will want a build management tool. CMake and Ninja are popular with the modern C crowd. (I still use GNU Make, but I’m old school.)
One third-party library you’ll want to consider early on is mega65-libc, the MEGA65 project’s own set of utility functions. This library provides functions that access files from a disk or the SD card, access memory, power a console-like user interface, and more. The library is useful enough for things like debugging that you pretty much always want to include it in your project, even if you don’t intend to use most of it. The compiler will only generate code for the functions that you use.
Thanks to kibo, there’s an easy way to get everything you need to set up your MEGA65 C project, including CMake and mega65-libc. mega65-llvm-template is a Github project template that you can either use to create a new Github repository, or just download to try it out. See the template’s documentation for instructions.
MEGA65 C tips and tricks
Before you dive into a good book on the C language, it’s important to note that llvm-mos has a few limitations when it comes to standard C. For starters, it does not support the float or double fractional number types. The 6502 CPU does not have built-in support for floating point math, and llvm-mos does not provide a floating point math library. If you’re ambitious, you could consider third-party assembly language routines for this purpose, such as “Floating Point Routines for the 6502” by Steve Wozniak, from the magazine Dr. Dobb’s Journal in August 1976.
llvm-mos provides a stripped-down version of the C standard library implemented for Commodore computers, using C64-style kernel calls for things like printing messages. The implementation is incomplete, and some functions may not work correctly on the MEGA65. It’s worth trying, just don’t expect everything to work.
If your C program returns from main(), either with the return statement or by allowing control to reach the end of the function, the program llvm-mos generates will attempt to return to BASIC. This doesn’t always work with the MEGA65 kernel. I sometimes get weird display artifacts, false BASIC errors, or crashes when returning from main(). Consider ending your program with an infinite loop (while(1);) and not relying on exiting to BASIC.
In C, character values ('x') and string values ("xyz") are treated as ASCII byte values. These are neither PETSCII values nor Commodore screen codes, so you will need to account for this when defining and printing text messages to the screen. Using uppercase ASCII letters in your C source code along with the MEGA65’s default uppercase character mode will print uppercase letters.
If you’re writing a game—or pretty much anything, honestly—your program will need to access registers and memory addresses directly, similar to POKE and PEEK from BASIC, or lda and sta in assembly language. This is possible in C, taking care to use C’s pointer dereferencing features correctly. Use an appropriate value type for bytes (such as uint8_t from stdint.h), and be sure to refer to an I/O register as volatile to prevent the optimizer from taking shortcuts. mega65-libc’s memory.h provides useful macros and functions for accessing registers, so it looks cleaner in your code and you don’t have to remember the details:
// From mega65-libc:
... POKE(0xD020, 5); uint8_t joy2 = PEEK(0xDC00);
// Without mega65-libc: ((volatile uint8_t )(0xD020)) = 5; uint8_t joy2 = ((volatile uint8_t )(0xDC00));
Programs generated by llvm-mos start by unmapping the BASIC ROM to make more room for program memory. The program code starts at address $2001, including the BASIC bootstrap program. The space beyond the end of the program up to address $CFFF is used for memory managed by the C language, a total of 44 kilobytes for both program and memory. Memory allocated by the program using the C standard library malloc() function, a mechanism known as the heap, starts just after the program and “grows upward” toward higher addresses. The stack, used for keeping track of function calls and local variables, ends at address $CFFF and “grows downward” toward lower addresses. The I/O registers remain in $D000-$DFFF, and the kernel ROM in $E000-$FFFF.
Sometimes you’ll want the ability to write assembly language code inside your C code. Clang supports the GCC inline assembly syntax, expecting to output 6502 assembly language for llvm-mos’s assembler. This requires giving the optimizer some hints about what the assembly code does, so it can make decisions about using CPU registers and relocating code. The following example from mega65-libc takes an unsigned char variable from the C code and calls a “Hypervisor trap” with a bit of assembly language, informing the optimizer that this code must be executed in this location (volatile) and that it uses the accumulator ("a"). Notice how the assembly language text is a single C string with newline delimiters (\n).
unsigned char the_char; void debug_msg(char m) { // Write debug message to serial monitor while (m) { the_char = *m; asm volatile("lda the_char\n" "sta $d643\n" "clv" ::: "a"); m++; } // ... };
A sophisticated program might want to install interrupt handlers that call C functions, such as a raster interrupt handler that is called once per frame. This is an advanced technique that requires disabling interrupts, setting the address of the function, and updating the MAP register to disable the kernel. I won’t include a complete example here, but here’s one way to get the address of a C function as two bytes, using volatile once again to make sure the optimizer keeps the function even if it isn’t called by the rest of the program.
volatile void do_frame(void) { // ... }
// ... unsigned char addrlow = (long)do_frame & 0xff; unsigned char addrhigh = ((long)do_frame >> 8) & 0xff;
Enable compiler warnings. The C language gives you plenty of opportunity to make mistakes, and Clang is very smart about noticing potential issues in your code. The command line flags -Wall -Wextra -Wpedantic tell the compiler to go crazy with the constructive feedback. To enable this using the mega65-llvm-template, add this to the file CMakeLists.txt:
target_compile_options(hello.prg PRIVATE -Wall -Wextra -Wpedantic )
Learning more about C
The most famous book for learning the C programming language is The C Programming Language by Brian W. Kernighan and Dennis M. Ritchie. Ritchie invented the C language, and this concise book is a computer science classic.
My favorite book for learning C is C Programming: A Modern Approach, 2nd edition, by K. N. King. It’s excellent for self-learning with high quality and well tested examples and exercises. I’ve literally read this book cover to cover and have done nearly all of the exercises, and I still go back to it to remind myself of language intricacies.
I wouldn’t be surprised if many of the examples from these books worked on the MEGA65, but I would be surprised if they all did. If you’re new to the C language, I would recommend starting with a C compiler for a modern computer first: Xcode for macOS, GNU C for Linux, Visual Studio Community for Windows. You will only need a subset of C’s features to write substantial programs for the MEGA65.
There are several other C compilers that work reasonably well for MEGA65 development. cc65 is popular with C64 programmers and has a long history. It is used by the MEGA65 project in many places. KickC is another, using the venerable Kick Assembler for generating machine code. Calypsi by hth313 is a C and assembly development toolkit for several vintage computers. Calypsi’s MEGA65 support is in early stages, but it already supports 45GS02 instructions, 64-bit integers (long long), and floating point math.
I chose llvm-mos for this Digest because it takes advantage of the LLVM ecosystem to have the most complete implementation of the C language and the best optimizer. With a built-in MEGA65 configuration, it’s also just the easiest to get running for MEGA65 projects.
Other llvm-mos tools
The LLVM project reinvents the way cross compilers are built: instead of a single compiler dedicated to a single language and target system, LLVM is divided into separate layers for the language, the optimizer, and the target. llvm-mos is a target backend for Commodore computers, and is able to take advantage of a suite of cutting edge tools and language frontends. Because it is based on LLVM, llvm-mos is able to power multiple language compilers, and use the state-of-the-art optimizer from the LLVM core.
The llvm-mos package you just installed includes not only the C compiler called Clang, but also a C++ compiler Clang++. Start writing C++ programs for your MEGA65 today with the mos-mega65-clang++ command!
The clang-tidy command is a tool known as a static analyzer. It can examine your C code, find bugs, and recommend improvements. Programming IDEs like Visual Studio Code can be configured to run such tools automatically to highlight problems as you are coding. Tell VSCode to use the language server included with llvm-mos, called clangd.
You can find all of these and more in the bin/ folder of llvm-mos.
Rust
Take everything you’ve just learned about C and throw it in the bin! The new hotness that all the kids are on about is Rust.
fn _main(_argc: isize, _argv: const const u8) -> isize { let mut rng = sid::SIDRng::new(c64::sid()); for offset in 0..80 * 25 { let character = [77u8, 78u8].choose(&mut rng).copied().unwrap(); unsafe { mega65::DEFAULT_SCREEN .add(offset) .write_volatile(character) }; } println!("HELLO MEGA65 FROM RUST"); 0 }
Rust is an important new language that intends to be just as powerful as C while making it easier to write programs that are more secure and have fewer bugs. I won’t go into detail on Rust for this Digest—I’m still learning it myself—but I wanted to take a moment to highlight the work wombat has done to bring Rust programming to the MEGA65. See wombat’s MEGAVision talk for a brief video overview.
rust-mos by Mariusz Krynski is a version of the Rust compiler customized for Commodore microcomputers. It requires llvm-mos, with modified installation instructions. Setting up the tool chain is somewhat involved, so read the README file carefully, or consider using a pre-made Docker image if you’re familiar with how to do that. wombat’s mos-hardware is a Rust “crate” with libraries and examples for interfacing with MEGA65 hardware. There’s also a project template: mos-hardware-template.
For learning the Rust language, start with Learn Rust, the official online book and other resources. We’re still figuring out the best way to write Rust programs for the MEGA65, so be sure to join us in the #rust channel on the MEGA65 Discord.
Compared to assembly language, languages like C and Rust provide abstractions that make programs easier to write and understand. They are essential tools for producing large programs whose complexity can be difficult or expensive to manage. Even without perfect compilers that take full advantage of the MEGA65 CPU and memory system, the tools currently available provide plenty of power for MEGA65 development. I still try to write in assembly language occasionally to feel a sense of camaraderie with early programmers that didn’t have these tools, but it’s good to know that they’re just an arm’s reach away.
Happy coding!
— Dan
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
There’s something wholesome about writing a program on a vintage computer. Such a computer was designed to give equal attention to programs written by their operator and programs written by software companies, and included everything you would need to get started writing such programs. Compared to modern software development, the constraints of on-device retro coding can be comforting and inspiring. When all you have is a Commodore, some graph paper, and a reference manual, you can concentrate on creating programs and solving problems without distraction.
Professional software companies didn’t always do it this way. Larger companies often used other computers to produce Commodore software, such as a mainframe computer with larger storage, computation, and multi-user collaboration capabilities. They would write code using languages like C, and use specialized tools that combined the efforts of programmers and artists into data that could be written to a disk and run on a Commodore. Known as cross development, this workflow gave these companies a competitive advantage: they could work faster, collaborate better, reuse code across projects, and even develop their programs for multiple kinds of computers at the same time.
Today, many hobbyists use modern computers to write programs for vintage machines. There are so many good cross-development tools at our disposal that we can’t cover them all in one Digest. This month, we’ll consider the advantages of cross development, and survey a few tools you can use to write MEGA65 programs in BASIC and assembly language. We’ll highlight recent developments in these tools made specifically for MEGA65 programming. And we’ll introduce an exciting new cross-development language that has just added MEGA65 support: XC=BASIC.
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
The MEGA65’s VIC-IV video chip has multiple graphics modes. Each mode and feature pulls ideas from some point in vintage computing history: terminal-style text output, character graphics, hardware sprites, palette banks, full-screen scrolling, and even Amiga-style blitter objects and graphics-optimized DMA operations. Several VIC-IV modes are fully backwards compatible with the VIC-II of the Commodore 64 and 128, and the VIC-III of the Commodore 65.
In today’s Digest, we’ll be looking at the VIC-III bitplane graphics mode. While this mode doesn’t show off all of the MEGA65’s capabilities, it’s one of the more fun modes to use with BASIC 65 thanks to its library of 31 drawing commands and functions. In Featured Files, we’ll see a new game that takes advantage of BASIC 65’s graphics system to draw vector art. We’ll also try using a built-in feature to display full-screen high color photographs and illustrations.
Featured Files
This month’s Features Files are all brand new, hot off the presses!
Onion Cake and the Hungry Dinosaurs, by Gurce. This short comedy adventure game combines a text command interface with gorgeous full-screen vector art by Gurce’s sister Ayca and original music. You get to watch the BASIC code fill in the image as it loads each scene, and the code cleverly uses the MEGA65’s memory and DMA features to cache the render so they display instantly on subsequent visits.
Gurce wrote a MEGA65 vector art editing tool, called VART, to develop the image format used by the game. Both the game and the editor are written in the Eleven programming environment.
Onion Cake and the Hungry Dinosaurs is inspired by another MEGA65 game, Escape from Onion Cake by MrZaadii, from his Mega65 Games Pack from 2018. MrZaadii’s game also uses bitplane graphics with pixel art, pre-dating some of the newer graphics features that were added by the MEGA65 team.
Don’t miss Gurce’s behind-the-scenes presentation of the making of Onion Cake.
Old Mine Hoist by GeirS. This addictive one button game has you lowering a box of supplies down a perilous mine shaft. Struts on the mineshaft walls and barrels of dynamite risk damaging your payload. How far can you get?
Rescue Inc. by SirLazarus. An original solitaire strategy game written in BASIC 65. Launch probes to locate missing battleships in the dark ocean waters. Try to find all the ships with as few probes as possible.
C64 for MEGA65 V5 alpha releases
In last November’s Digest we mentioned the Commodore 64 core for the MEGA65 by MJoergen and sy2002, the best way to run Commodore 64 software on MEGA65 hardware. The core completely reconfigures the FPGA for a precise recreation of the Commodore 64 chipset, capable of running software with a very high degree of accuracy and compatibility. The latest stable release is version 4, with support for D64 disk images on the SD card, joystick port peripherals, built-in RAM expansion, and multiple display modes for modern displays.
The C64 for MEGA65 project is ramping up for a major upgrade with release 5. sy2002 has been posting alpha releases to the Discord, and you’re invited to help test! As of V5 alpha 22, the core supports:
Running virtual cartridges as CRT files from the SD card
Running Commodore 64 PRG files directly from the SD card
Connecting IEC devices to the MEGA65’s IEC port, including external disk drives and printers
Connecting C64 hardware cartridges to the MEGA65’s expansion port, with support for EasyFlash 1 cartridges
Installing a licensed JiffyDOS ROM for use with IEC disk drives
As this is an alpha testing release, it comes with a few caveats and has known issues. Be sure to read through the list before installing an alpha release.
For simpler C64 hardware cartridges, you can turn off the MEGA65, connect the cartridge, hold the No Scroll key while turning it on, then select the C64 core from the core selection menu. Remember that if you allow it to boot with the MEGA65 core, it’ll just try to run the cartridge software in its C64 mode, which is not the same as the C64 core and is less likely to be successful. lydon is working on improving the MEGA65 core such that the boot-up process can be configured to automatically select different cores for different types of hardware cartridges.
For EasyFlash and some fancier cartridges, the core selection menu won’t work at all with the cartridge connected. To get this to work, you have to do something we would have considered unthinkable in the days of static-sensitive PLA chips: start with the cartridge not connected, boot with No Scroll, select the C64 core, then connect the cartridge and press the reset button on the side of the machine.
EasyFlash 1 or 1CR works with the latest alpha release. EasyFlash 3 does not yet work. You can buy an EasyFlash 1CR from Daniel Mantione (dmantione in the MEGA65 Discord). Be sure to also order a Protoparts enclosure from Daniel (or from Protoparts directly) to ensure the best fit in the MEGA65 cartridge port.
When using the IEC port with the C64 core to connect disk drives, keep in mind that the C64 core always uses device number 8 for the virtual drive for D64 disk images. Set your external drive to any other device number for use with the C64 core. You can use this set-up to copy D64 disk images to physical floppy disks and vice versa.
Whether you’re installing the C64 core’s latest stable release V4 or trying out the V5 alpha release, the installation process is the same:
Download the C64-for-MEGA65 release that you want to install, and unpack the archive.
Copy the .cor file to the root of your MEGA65 SD card.
Make a folder in the root of the SD card named c64.
Copy the file named c64mega65 to the c64 folder. Also take this opportunity to copy any D64, CRT, and C64 PRG files you want to try to the c64 folder as well.
Reinstall the SD card in your MEGA65.
With the MEGA65 turned off, hold the No Scroll key, then turn it on. This opens the core selection menu.
Select a core slot to overwrite with the core, then hold Ctrl and press the number for that slot. Follow the prompts to install the C64M65~1.cor file.
If you’re juggling multiple versions of the C64 core, be sure to use the c64mega65 file that comes with that version. This file saves your menu preferences, and the file format has changed between V4 and V5.
Visit the #c64-core channel in the MEGA65 Discord for alpha release downloads, release notes, and troubleshooting issues.
Saying hello to BASIC 65 bitmap graphics
Gurce’s Onion Cake and the Hungry Dinosaurs and MrZaadii’s original Escape from Onion Cake both use the bitmap graphics system built in to BASIC 65. This subsystem allows for rendering high resolution pixel perfect images using BASIC commands. Bitmap graphics are implemented as a separate graphics mode from the text mode you see when writing programs, so you can only see either the text screen or a graphics screen on the display at one time. There are 31 commands and functions you can use to manipulate screens and draw figures in your BASIC programs.
Because the graphics screen hides the text screen, this isn’t easy to experiment with from the READY. prompt. Instead, you can use a short program like this one:
10 SCREEN 320,200,220 PEN 130 LINE 25,25,295,17540 GETKEY A$50 SCREEN CLOSE
In this example, line 10 opens a new screen that is 320 pixels wide and 200 pixels tall, with a palette of four colors (a “bit depth” of 2). Line 20 sets the color used by subsequent drawing commands (the “pen” color) to palette entry 1 (the second of four colors), in this case using the default palette’s color of white. (See the PALETTE command for a way to change the palette colors.) Line 30 draws a line using the pen color from screen coordinate (25,25) to (295,175), a diagonal stripe.
Line 40 pauses until the user presses a key, and line 50 closes the screen and returns to text mode. This is a useful recipe when you’re experimenting with graphics commands. If your program exits without closing the screen, the MEGA65 will stay in graphics mode and you won’t be able to see the READY. prompt. This can happen if there is an error in your code that stops the program before it closes the screen. If your program appears to get stuck showing a graphics screen, hold Run/Stop and press Restore to reset the display to text mode and a fresh READY. prompt.
The graphics system supports widths of either 320 or 640 pixels, heights of either 200 or 400 pixels, and color bit depths from 1 (two colors on screen at a time) to 8 (256 colors). You can have up to 16 colors (bit depth 4) with a 640x400 screen, and up to 256 colors (bit depth 8) for the other possible resolutions. It is possible to allocate multiple screens and switch between them, though this limits the possible sizes and bit depths due to available memory. Each color palette entry can be any of 4,096 possible colors, with 16 possible values of each of the red, green, and blue components.
It’s worth noting that the MEGA65 video hardware is capable of 24-bit color. The BASIC 65 graphics system only uses a subset of the MEGA65’s graphics capability.
As tempting as it is to tour all 31 commands of the graphics system in this Digest, for now I will leave it to you to discover them in your User’s Guide. Look for SCREEN, BOX, CHAR, CIRCLE, DOT, ELLIPSE, LINE, PAINT, PALETTE, PEN, POLYGON, and SCNCLR, for starters.
Saving the graphics screen to disk
BASIC 65 includes a command for saving the currently active bitmap graphics screen to disk. The SAVEIFF command takes a filename, and optional drive and unit numbers. It only works with a graphics screen active, so it’s best to make it part of the program.
35 SAVEIFF "LINE-EXAMPLE"
The image is saved to disk in the IFF-ILBM file format, a bitmap graphics file format common to Amiga computers. The file appears on the disk as a PRG file, but it’s actually just the IFF-ILBM data, without the two-byte address header of an actual PRG file.
IFF-ILBM files use a simple form of data compression to reduce file sizes. The image produced by the program above of a single line on a blank screen at 320x200x2 occupies six blocks on disk to store 1,502 bytes. More complex images, or higher resolutions or color bit depths, will require more space.
Loading the graphics screen from disk
A file saved with SAVEIFF can be loaded back to the active graphics screen with another command: LOADIFF. As before, the graphics screen must be active.
10 SCREEN 320,200,220 LOADIFF "LINE-EXAMPLE"30 GETKEY A$40 SCREEN CLOSE
Due to how multi-color bitmap graphics are stored, the color bit depth of the active screen must match the file. Loading an image that was saved from a smaller resolution than the active screen (for example, loading a 320x200 image onto a 640x400 screen) will simply load the image into the upper-left corner of the screen, leaving the rest of the screen intact.
SAVEIFF includes the current palette settings with the image data in the IFF-ILBM file. You do not need to restore the palette separately in your program. Notice that the current palette will be overwritten by LOADIFF.
Exploring the IFF-ILBM file format
The IFF-ILBM file format is a standard format developed by Electronic Arts in cooperation with Commodore in 1985. IFF, or “Interchange File Format,” is what is known as a container (or “envelope”) format, capable of representing multiple chunks of data of many kinds. Amiga fans mostly know IFF files as still images of the inner format ILBM, or “Interleaved Bitmap.” The IFF-ANIM format was a variant for storing multiple ILBM images as an animation in a single IFF envelope, also popular on the Amiga. As a kid, I only knew that IFF files contained either stills or animations, and you could make them with Deluxe Paint.
Here is the beginning of the IFF-ILBM file created by the BASIC program above, as hexadecimal values:
00000000 46 4f 52 4d 00 00 05 de 49 4c 42 4d 42 4d 48 44 |FORM....ILBMBMHD|00000010 00 00 00 14 01 40 00 c8 00 00 00 00 02 00 01 00 |.....@..........|00000020 00 00 00 00 01 40 00 c8 43 4d 41 50 00 00 00 0c |.....@..CMAP....|00000030 00 00 00 ff ff ff ff 00 00 00 ff ff 42 4f 44 59 |............BODY|00000040 00 00 05 9a d9 00 d9 00 d9 00 d9 00 d9 00 d9 00 |................|
An IFF file contains one or more chunks. Each chunk has a four-byte type ID and a four-byte size, followed by that many bytes of data. The SAVEIFF command creates a single outer chunk of type FORM, so it begins with those uppercase letters encoded as ASCII (46 4f 52 4d). The size is a signed 32-bit integer in Big Endian format (largest digits first), so 00 00 05 de represents a size of hexadecimal $05de, or 1,502 bytes. It’s not obvious from looking at it, but this is actually a bug in SAVEIFF: the actual data size is 1,494 bytes, but it is erroneously including the eight bytes of the FORM header in this size. Because of this bug, some tools will complain about an “unexpected end of input file” when reading this.
According to the IFF specification, a FORM chunk is a record with an inner type and one or more inner chunks. In this case, the inner type is ILBM, so those are the next four bytes (49 4c 42 4d). The inner chunks have types BMHD, CMAP, and BODY, which are part of the ILBM specification for still images. You can see these identifiers in the hex dump, along with their four-byte sizes and data.
Reading along with the ILBM specification for the $00000014 (20) bytes of the BMHD chunk, we can see the following properties of the image:
01 40 00 c8: The image is $0140 = 320 pixels wide, and $00c8 = 200 pixels tall.
00 00 00 00: The image starts at coordinate (0,0) on the screen.
02: It has a color bit depth of 2.
00: It does not use the mask feature of the ILBM format.
01: The data uses Run-Length Encoding for compression (type 1).
00: This is unused.
00 00: This is related to the mask feature, not used here.
00 00: This file describes its aspect ratio as 0:0 ($00 $00). This is not actually a valid aspect ratio. Some tools will complain, and just assume a ratio of 1:1.
01 40 00 c8: The intended screen size for the image is 320x200, the same size as the image.
The CMAP inner chunk describes the color palette. With a bit depth of 2, there are four possible colors in the image, each described as one byte for each of the red, green, and blue components, $0000000c (12) bytes in total.
Color 0: red = $00, green = $00, blue = $00, aka black
Color 1: red = $ff, green = $ff, blue = $ff, aka white
Color 2: red = $ff, green = $00, blue = $00, aka bright red
Color 3: red = $00, green = $ff, blue = $ff, aka cyan
The IFF-ILBM format supports RGB component values from 0 to 255 (8 bits). The MEGA65 only supports component values from 0 to 15 (4 bits). LOADIFF uses the most significant four bits of each RGB component value when setting the palette, effectively rounding each number down to the nearest 16.
The spec describes the RLE algorithm that is used for the BODY chunk. For example, $d9 $00 represents forty zeroes, and you can see this repeated in the excerpt above. The decoding algorithm will build out 320x200 bits for each of the four bitplanes. There’s a bit in each bitplane for each pixel of the image. Take the pixel’s bit from each of the bitplanes, starting with the lowest bit in the first bitplane, to get the pixel’s color number.
ILBM is useful for Amiga and MEGA65 BASIC bitmap graphics because these systems represent the screen as bitplanes in memory, so it’s easy to save the image just by compressing memory, and load it again just by decompressing it. The MEGA65 has other graphics modes that don’t use bitplanes.
Do you believe in ImageMagick?
Most modern graphics software does not support the vintage IFF-ILBM image format. To use an image saved with SAVEIFF, it must be converted to a modern format, such as PNG.
ImageMagick is the ultimate software suite for converting and manipulating image data in a wide variety of formats. It’s free, packed with features, and runs on most operating systems. You typically use ImageMagick from the command line, which makes it great for automation scripts. See the ImageMagick Downloads page for installation instructions for your operating system. On a Mac using Homebrew, install this with: brew install imagemagick
In most cases, you can convert images between formats using the magick convert command:
magick convert line-example.iff line-example.png
There’s a slight problem when trying this with files made with the MEGA65’s SAVEIFF command. Those SAVEIFF bugs about the FORM chunk length and 0:0 aspect ratio cause the inner workings of ImageMagick to believe that the conversion failed. When I run the magick convert command on a SAVEIFF file, I get these messages, and no PNG file:
ilbmtoppm: warning - illegal aspect ratio 0:0, using 1:1ilbmtoppm: input is a 2-plane ILBMilbmtoppm: Unexpected end of input fileconvert: delegate failed 'ilbmtoppm' '%i' > '%o'' @ error/delegate.c/InvokeDelegate/1924.convert: no decode delegate for this image formatILBM' @ error/constitute.c/ReadImage/781.convert: no images defined `line-example-test.png' @ error/convert.c/ConvertImageCommand/3342.
As shown in these messages, ImageMagick delegates IFF-ILBM conversion to a tool called ilbmtoppm, part of another software suite called NetPBM that’s included with ImageMagick. When I run the ilbmtoppm tool directly, it complains about the issues with the file, but successfully converts the IFF-ILBM file to the PPM format:
ilbmtoppm line-example.iff >line-example.ppm
The PPM format is supported by some modern software, and can be further converted to other formats by ImageMagick.
magick convert line-example.ppm line-example.png
The PPM file can also be converted back to IFF-ILBM format with the SAVEIFF errors corrected:
magick convert line-example.ppm line-example-fixed.iff
Converting images to be displayed on a MEGA65
You can reverse the conversion process to prepare any image, including high color photographs, as an IFF-ILBM file, so it can be displayed on your MEGA65 with the LOADIFF command in a BASIC program.
For this to work, it needs to not only be in IFF-ILBM format, but also be the right size and color depth for a BASIC 65 screen. You can try adjusting your image to 320x200 and 256 colors (8-bit color) using image editing software, or you can let ImageMagick do it for you.
magick convert photo.jpg -resize 320x200! +dither -colors 256 -depth 8 photo.iff
This command converts photo.jpg to photo.iff with the following options:
-resize 320x200! : Resize the image to 320 pixels wide and 200 pixels high. The ! tells ImageMagick to stretch the original image to fit this aspect ratio, which may or may not be what you want. ImageMagick has other options for resizing that can crop or extend the image instead. See any ImageMagick tutorial for examples.
+dither : Disable dithering. Dithering is a technique for representing color and value gradations in digital images, using noise patterns and just a few colors. In this case, we’re converting a JPEG, which may introduce compression artifacts. Disabling dithering prevents these artifacts from being preserved as dithered patterns in the final image. If you’re converting from another format, you may be able to leave dithering enabled.
-colors 256 -depth 8 : Set the color depth to 8, with the maximum of 256 colors.
I had some issues with ImageMagick and NetPBM that were resolved by updating to the latest versions. If you already have these tools installed from a while ago, consider updating. With Homebrew: brew update && brew upgrade
Four-Byte Burger
In 1985, Jack Haeger created an illustration of a hamburger falling through the air, with a 3-1/2" floppy disk for the hamburger patty. Jack painted the image using an early version of the Graphicraft art software for the Commodore Amiga, one of the first works of art created on the machine. At the time, Graphicraft lacked any way to save an image to disk or export it to print, so Jack photographed the screen with a 35mm film camera. Presumably, Jack eventually turned off the machine, and the digital record of the work was lost forever. The photograph appeared in the Graphicraft manual and the first issue of Amiga World magazine.
In 2023, YouTuber Stuart Brown aka Ahoy recreated “Four-Byte Burger” as digital art. Ahoy described his research and his process in a 30-minute video, including the realization that Jack must have drawn the image sideways then rotated the photograph to give it more height, as shown by the CRT scan lines running up and down the photo. Ahoy concluded that Jack used just fewer than 32 colors, and the original image was 320x200 in size.
Ahoy published a PNG file of his recreation, and by popular demand also produced an IFF-ILBM file of the same image for potential display on an Amiga. Naturally, I wanted to display it on my MEGA65, so I loaded it onto a D81 disk image and wrote an appropriate LOADIFF program, with a screen 320x200x5.
I was a bit surprised that it rendered as mustard and ketchup colored garbage instead of the actual image. So I peeked into the hex dump as we did above. Everything looked as I expected it to, until I got to the compression type field of the BMHD chunk, which was $00 instead of $01. According to the ILBM spec, this indicates that the BODY data uses no compression at all, and not Run-Length Encoding. So I looked at the ROM code for LOADIFF, and sure enough, LOADIFF always assumes the BODY region uses RLE, and ignores the compression type field. (That should be another easy fix in a future version of the ROM.)
There’s an easy workaround: use ilbmtoppm to convert the IFF-ILBM file to PPM, then use ppmtoilbm to convert it right back.
ilbmtoppm four-byte-burger.iff >four-byte-burger.ppmppmtoilbm four-byte-burger.ppm >four-byte-burger-fixed.iff
With “Four-Byte Burger,” the resulting file was naturally smaller, and had the compression field set to $01. It loaded fine on the MEGA65. If you have an IFF image file of a compatible size and color depth and it still isn’t loading correctly with LOADIFF, this is an easy workaround to try.
One more tip: If you find an IFF-ILBM file and want to know its dimensions and color depth, use the magick identify command:
magick identify four-byte-burger.iff
IFF MegaShow65
Want to display a set of IFF-ILBM images as a slideshow? Check out IFF MegaShow65 by Nobato. This clever program can browse and display multiple IFF image files on a disk. It uses BASIC 65’s graphics system and LOADIFF command behind the scenes, reading the BMHD section of the file to determine the resolution and color depth.
The BASIC graphics system originated at Commodore, and retains a vintage feel. Even though it doesn’t use the MEGA65’s capabilities to their fullest, it’s a ton of fun to play with and use from programs, much easier than using bitmap graphics on a Commodore 64.
Dirty old Onion Cake has kidnapped you. You know that it is your own fault since you have not written enough MEGA65 software. Better get started!
— Dan
Get full access to Dan’s MEGA65 Digest at m65digest.substack.com/subscribe
PETSCII is a set of 256 values that can be printed to the screen of a Commodore computer. Most of these codes refer to characters, such as letters, numbers, punctuation, and Commodore’s unique set of graphics glyphs that can be typed from the keyboard. Other codes are control codes that manipulate the state of the screen and printing system, such as to change the color of subsequently printed text. The PETSCII character set is part of what gives Commodore computers their distinctive style. Drawing pictures by typing PETSCII characters and codes is one of the first things everyone does with a Commodore, and there is a long tradition of crafting murals of PETSCII art for demos, games, and computer bulletin boards.
If you’re reading this, you’re probably pretty familiar with the capabilities of PETSCII on a Commodore 64. In this Digest, we’ll review the PETSCII character set, and see how the MEGA65 can learn a trick or two from Commodores that predate the 64. We’ll also take a look at PETSCII control codes that are newer than the C64, and see how we can make special use of PETSCII strings on the MEGA65.
Featured Files
Here’s more stuff you can download and try on your MEGA65 today!
M3wP Solitaire by M3wP. Every computer needs some card game fun, and this Solitaire game has it in spades! Connect a mouse to port 1 and play the classic Klondike Solitaire with rich multi-color playing card graphics. You won’t be able to resist just one more hand.
Don’t have a vintage Commodore 1351 mouse or Amiga mouse? Get the mouSTer adapter and connect a modern USB mouse. Make sure the mouse mode is set correctly in the MEGA65 Configuration menu (hold Alt while turning on the computer, then select 1) to match the mouSTer configuration. If you’re one of the lucky ones to receive a modern Amiga Tank Mouse from their Kickstarter campaign, that also works with the MEGA65.
Lemonade for two by ubik. An innovative two-player version of the microcomputer classic business strategy game. Use your limited funds to buy supplies based on the weather forecast, sell lemonade in your neighborhood, and leverage your profits into expanding your business. Out-match your opponent and win it all!
Procedurally Drawn Galaxy by grim_fandango. This high resolution graphics demo renders a gorgeous spiral galaxy. Once you’ve admired the image, check out the BASIC program listing to see how it’s done.
The lost glyphs of the PET
The PETSCII text encoding standard was originally invented for the Commodore PET computer in 1977. It uses 8-bit code points for 256 possible values, of which 192 represent typeable characters. Some characters are assigned to multiple code points; the actual number of distinct typeable characters is 128. The remaining 64 code points are used for control codes, or otherwise have no effect when printed to the screen.
While PETSCII was based on the ASCII code standard in use by most computers by that time, the Commodore PET had its own ideas about how to make the best use of the code space. An unexpanded PET did not have a bitmap graphics mode, so PETSCII extended the character set with graphics symbols that could be used to draw boxes, diagrams, and game images. PETSCII actually describes two character sets: one with mixed case letters and some graphics symbols (the “text” character set), and another with only uppercase letters and more graphics symbols (the “graphics” character set). On Commodore machines, the graphics character set is the default. This is why you sometimes see ASCII messages mis-printed on Commodore machines with lowercase letters as uppercase and uppercase letters as graphics symbols. The user can toggle between character sets as needed by pressing the Commodore key and the Shift key together. (This is Mega + Shift on the MEGA65.)
The PETSCII codes for the 128 typeable characters map to screen codes that describe what actually appears on a location on the screen. There’s a second set of screen codes that represent those characters in reverse video mode, for a total of 256 possible screen codes in a character set. With two character sets, the complete definition of a Commodore typeface has 512 possible glyphs, of which only the first 256 or second 256 can be on the screen at one time.
Fun fact: The PET adds a one-pixel vertical space between each line when the display switches to “text” mode, to give it more of an ASCII terminal feel. You can see this in the image above, which I produced in an emulator with a program that prints the PETSCII control code to switch to this mode. (It’s also possible to use the text character set on a PET without the vertical gaps.)
The C64 typeface
The original Commodore PET typeface was designed with the PET’s built-in monochrome display in mind. These glyphs were used in 40-column and 80-column PET models. The same glyphs were also used in the VIC-20. To reduce costs, the VIC-20 did not have a built-in display and was designed to be connected to the color television set already in your living room. To make the text legible on blurry TVs, the VIC-20 only supported 22 columns of text and stretched the PET glyphs extra wide.
Commodore sought to return to a 40-column text display for the Commodore 64, but there was a problem: the PET glyphs were too thin and spindly to be readable on a TV at narrow character widths. So Commodore redesigned the character set with thicker vertical lines. Nearly every occurrence of a single pixel with space on either side was doubled in some form.
The result is the Commodore typeface we know and love, used unchanged all the way up to the MEGA65. It looks pretty great in 80 columns on a modern display.
The missing glyphs
The redesign met its goal, but at a cost. Several PET glyphs simply did not survive being made thicker.
For example, the glyph set includes vertical lines of every width on both the left and right side of the tile. Take a look at your MEGA65 keyboard and you can see a few of these are typeable by holding down Mega and pressing keys like L, N, and M. On the PET, CBM+G and CBM+M were vertical lines one pixel wide. Starting with the C64, they are now two pixels wide, redundant with CBM+H and CBM+N, respectively.
Some PET glyphs have been deleted entirely. A fine-grained 1-pixel checkerboard pattern was replaced with a duplicate of the 2-pixel checkerboard pattern. Dotted and line pattern glyphs just became extra empty spaces.
Early MEGA65 contributor Markus Neeb (X2themax on the Discord, aka MPBBS) noticed the missing glyphs. At the MEGA eV open house of August 2022, he proposed a radical idea: restore the missing PET glyphs for the MEGA65. All of the restorations only replace glyphs that are redundant with others elsewhere in the typeface, and because they are both redundant and obscure, they likely haven’t even been used by MEGA65 software. The restored glyphs are authentic to Commodore history, and are demonstrably useful on modern displays in both 40-column and 80-column modes.
It’s intriguing to imagine how re-introducing these glyphs enhances the character of the platform. I know I would have used the fine checker, dotted, and lined pattern tiles all the time if I had them—and a sharp enough display to support them—back in the day.
The MEGA65 Steering Committee decided against modifying the default ROM with these changes, primarily because it’s actually quite easy for any program that wants to use these glyphs to install them. It’s even easier on the MEGA65 than on previous Commodore computers. BASIC programs can just use the CHARDEF command, and machine code programs merely have to write the new glyphs to a memory location.
The following are CHARDEF statements that restore the missing PET glyphs. Feel free to use them in your programs, or in your AUTOBOOT.C65 file.
10 chardef $42,$10,$10,$10,$10,$10,$10,$10,$1011 chardef $43,$00,$00,$00,$ff,$00,$00,$00,$0012 chardef $60,$aa,$00,$aa,$00,$aa,$00,$aa,$0013 chardef $65,$80,$80,$80,$80,$80,$80,$80,$8014 chardef $67,$01,$01,$01,$01,$01,$01,$01,$0115 chardef $c2,$ef,$ef,$ef,$ef,$ef,$ef,$ef,$ef16 chardef $c3,$ff,$ff,$ff,$00,$ff,$ff,$ff,$ff17 chardef $e0,$ff,$00,$ff,$00,$ff,$00,$ff,$0018 chardef $e5,$7f,$7f,$7f,$7f,$7f,$7f,$7f,$7f19 chardef $e7,$fe,$fe,$fe,$fe,$fe,$fe,$fe,$fe20 chardef $15e,$aa,$55,$aa,$55,$aa,$55,$aa,$5521 chardef $160,$aa,$00,$aa,$00,$aa,$00,$aa,$0022 chardef $165,$80,$80,$80,$80,$80,$80,$80,$8023 chardef $167,$01,$01,$01,$01,$01,$01,$01,$0124 chardef $1de,$55,$aa,$55,$aa,$55,$aa,$55,$aa25 chardef $1e0,$ff,$00,$ff,$00,$ff,$00,$ff,$0026 chardef $1e5,$7f,$7f,$7f,$7f,$7f,$7f,$7f,$7f27 chardef $1e7,$fe,$fe,$fe,$fe,$fe,$fe,$fe,$fe
PETSCII control codes
The ASCII standard includes codes that do something other than print a character when sent to a terminal or teletype. These codes cause the terminal device to perform some other action, such as to move the cursor (or print head) to the beginning of the next line, or signal the operator by ringing a bell.
PETSCII also includes non-printing code points that, when printed, perform an effect, such as:
Change the color or style of subsequently printed characters
Move the cursor position (potentially scrolling the terminal-like text display)
Switch the display between the uppercase and lowercase character sets
Enable or disable the ability for the user to switch between the uppercase and lowercase character sets
Clear the screen
Ring the terminal bell
Control codes can be typed directly into the screen using a key sequence. This is a great way to experiment with control codes and see their effects immediately. For example, hold Ctrl and press 3 to change the printing color to red. Ctrl + 2 changes it back to white. Printing is very similar to typing in this way: it causes the effect of typing each code to occur, in the order the codes appear in the string.
Your User’s Guide has a good reference of PETSCII character and control codes, and their keyboard equivalents, in Appendix B.
Including control codes in strings
The CHR$() function takes a PETSCII code as a number and evaluates to a one-character string for that code. You can concatenate this string with other strings.
PRINT "IT'S "+CHR$(28)+"VERY"+CHR$(5)+" COOL"
The CHR$() function works for all 256 PETSCII codes, but it’s not especially convenient: you have to use a table to look up the control code numbers, and type quite a few characters to spell it all out.
Thankfully, the Commodore screen editor has an alternate way to type control codes into strings just by typing the key sequences that cause their effect. The screen editor knows that you intend to include the code in the string, and not have it take effect immediately, when it is in quote mode. The editor enables quote mode when you type the first double-quote (") character of a string, and disables it when you type the closing double-quote character.
You may have seen vintage magazine program listings represent these typeable codes using labels, like so:
PRINT "IT'S {red}VERY{white} COOL"
Try typing this command, entering Ctrl+3 for {red} and Ctrl+2 for {white}. They appear as reversed characters inside the string.
Quote mode takes some getting used to. It’s easy to forget that cursor movement keys get quoted, so if you try to cursor left while typing a string, it quotes the cursor-left PETSCII codes instead. A few codes intentionally do not get quoted, such as pressing Return. If you need such a code in a string, the only way is to use the CHR$() function (CHR$(13) for Return).
There is one other way to access quote mode. When you press Shift+INST/DEL, the editor inserts a space at the cursor location and does not move the cursor. It also enters quote mode only for the inserted spaces, so you can type a control code if you need to. This quote mode effect happens even if the cursor is not located inside double-quotes! That’s not particularly useful, but it’s a tradeoff for the fact that the screen editor doesn’t try to understand what you are typing until you press Return.
If you find that you’re in quote mode and you don’t want to be, press Esc then O (the letter O).
It’s useful to know that the screen editor will interpret whatever is on the screen when you press Return, including reversed characters inside double-quotes, no matter how those characters were typed. Technically, you can type a quoted {red} code while not in quote mode by turning on reverse mode and typing the £ (pound) symbol, which is what it would look like if you typed Ctrl+3 while in quote mode. I’ve never done this in regular practice—I typically use the insert method to type quoted codes when modifying an existing line—but it’s fun to know that it works.
Try this:
Type: PRINT " HELLO{wht}", without pressing Return.
Cursor back to the space before HELLO.
Enter Ctrl+9 to enter reverse mode, then type £.
Press Return.
Print modes and colors
The Commodore 65 supports text modes for underlining, flashing text, and reverse text. These modes can be combined.
To enable underlining, press Ctrl+B. To enable flashing text, press Ctrl+O. One way to enable reverse text is Ctrl+R, though as we just saw, this is also available as Ctrl+9 aka “Rvs on.” There are PETSCII codes to disable each of these individually, and if you’re just typing them onto the screen you can press Esc then O to disable all three modes. Reverse can also be disabled with Ctrl+0 (zero) aka “Rvs off.”
Underline on: Ctrl+B, CHR$(2)
Underline off: CHR$(130)
Flashing on: Ctrl+O, CHR$(15)
Flashing off: CHR$(143)
Reverse on: Ctrl+R or Ctrl+9 aka “Rvs on”, CHR$(18)
Reverse off: Ctrl+0 aka “Rvs off”, CHR$(146)
The printing system keeps track of the text color as its own mode, using that color for printing characters. The blinking cursor is drawn in the current text color. C64 fans know the color changing keys quite well: Ctrl or Mega plus a number key 1 through 8 accesses sixteen colors, as labeled on the number keys themselves. The corresponding PETSCII codes are scattered a bit (white is 5, red is 28, green is 30, etc.), so you’ll just want to look those numbers up in the User’s Guide if you need them.
On the MEGA65, you can access a system palette of 32 colors for text printing. All 32 colors can be used on the same screen simultaneously. To access them using PETSCII control codes, there is a print mode for that: Ctrl+D causes the 16 color codes to refer to colors 0-15, and Ctrl+A causes them to refer to colors 16-31.
Access colors 0-15: Ctrl+D, CHR$(4)
Access colors 16-31: Ctrl+A, CHR$(1)
Don’t forget that you can change an entry in the 32-color system palette to any of 4,096 colors using the PALETTE COLOR command. You can also change which color is used for printing with the FOREGROUND command (or the COLOR command). See the User’s Guide for more information.
Moving the cursor
It was very common for Commodore 64 BASIC programs to position text on the screen using PETSCII control codes that move the cursor. To print the message "HELLO WORLD" in the center of the 40x25 screen, a program might print the Home code to move the cursor to the top left corner, followed by 12 cursor-down codes and 14 cursor-left codes, then the message.
Home cursor: Home, CHR$(19)
Cursor down: Cursor down, CHR$(17)
Cursor right: Cursor right, CHR$(29)
Cursor up: Cursor up, CHR$(145)
Cursor left: Cursor left, CHR$(157)
BASIC 65 has a dedicated command for positioning the cursor: CURSOR col,row This may be more useful for positioning text on the screen, depending on what you are trying to print.
The terminal display supports tab column positions, similar to ASCII teletypes (and old typewriters). I won’t describe the whole system here. You can experiment with the Tab key (9) and “tab set/clear” (24) code to see what they do.
Screen effects
Pretty much every Commodore 64 BASIC program I ever wrote started by clearing the screen. This felt so powerful, the first step in my program taking control of the machine. The reversed heart character of the quoted clear-screen PETSCII code resonates as nostalgically for me as the blue-bordered home screen.
Press Shift+Clr/Home to clear the screen. Press it in quote mode to insert the PETSCII control code into a string, such that it clears the screen when that part of the string is printed.
As we mentioned last month, the text screen can display either the uppercase character set or the lowercase character set. Each set can have 256 unique glyphs, but only one set can appear on the screen at one time. The default character set keeps uppercase letters and graphics characters in the first set, and lowercase and uppercase letters in the second set.
You can toggle between the two character sets interactively by pressing the Mega and Shift keys together. (Try it!) This toggle effect is not a PETSCII code, and in fact supersedes many program functions so you can do this while a program is running. If your program intentionally uses one set or the other, you can print PETSCII codes to select the set, as well as to disable the Mega+Shift behavior.
Select uppercase: Ctrl+N, CHR$(15)
Select lowercase: CHR$(142)
Enable Mega+Shift: Ctrl+K, CHR$(11)
Disable Mega+Shift: Ctrl+L, CHR$(12)
Old terminals had a special code that caused a bell to ring, such as to alert an operator at the end of a long job, possibly on a printer in another room. Newer screen terminals support the bell code, mostly using it to indicate an error. The Commodore 65 also has a bell! Press Ctrl+G to hear it.
Patterns in control codes
PETSCII codes 1 through 26 are typeable as Ctrl plus a letter of the alphabet A through Z. Ctrl+S is overridden as an equivalent to No Scroll; code 19 can be entered with the Home key. Ctrl+M is Return, and Ctrl+T is Delete, so these can’t be quoted; use CHR$() for these. As quote mode characters, these codes appear as reversed versions of their corresponding letters. (This pattern extends to other codes and PETSCII characters, though they’re less easy to remember.)
In most cases where a code is usefully paired with another, one code is in the range 0-32 and the other is the same code plus 128 ($80), similar to shifted characters. For example, code 2 enables underlining, and code 130 ($82) disables it. Not every code has a useful pair, and for whatever reason the shift enable and disable codes are simply 11 and 12.
The Commodore 65 assigns several PETSCII codes to keys that have no behavior when printed, such as the function keys. These codes are only useful when reading a keypress with the BASIC GETKEY command. In some of these cases, the corresponding Ctrl keys perform behaviors in the screen editor that do not occur when the code is printed. For example, there is no way to print a PETSCII code with the same effect as typing Ctrl+P.
Escape codes
The screen editor has some advanced features that go beyond the available PETSCII control codes. This extended set is accessible by pressing Esc followed by another key. See the appendix in your User’s Guide for a complete list. (Be sure to download the latest PDFs, which include minor updates to this appendix specifically.)
Only a few escape codes are useful from PRINT. You activate them by printing the PETSCII code for Esc, which is CHR$(27), followed by the appropriate character. As we saw last month, Esc followed by the character 5 switches to 80x50 mode in the latest version of the ROM. You can activate this mode from a BASIC program by printing these codes:
PRINT CHR$(27)+"5"
Esc then M disables scrolling of the text display. This can be useful within a program to PRINT on the bottommost line of the screen. Esc then L re-enables scrolling.
PRINT CHR$(27)+"M{home}{24 down}YOUR SCORE: ",SC
Programmable function keys
Your MEGA65 includes a row of function keys, numbered F1 through F14. The seven keys act as the odd numbers; hold Shift and press one to access the even numbers. Technically, there are sixteen function keys, with the last two being the Help key and the Run key, respectively. (Press Shift + Run/Stop to act as Run.)
By now, you or your cat may have pressed these keys in the BASIC editor and noticed that they do various things. Some are super useful, and others are not obviously helpful. I know I’ve tried pressing the Help key on a Commodore 128, only to be disappointed that it just shouts HELP back at me and doesn’t appear to do anything else.
In the BASIC editor, the purpose of the function keys is to type strings of PETSCII codes. These strings are programmable! When you press a function key, it types its string as if you typed it out manually. For example, the default definition of the Help key is a string of five codes: HELP followed by the Return key. On a blank line, this enters the BASIC command HELP, which displays information about the most recent error encountered by your BASIC program. (If your program didn’t recently encounter an error, the HELP command prints nothing.)
To see a list of all of the function key definitions, type the KEY command:
KEY
Knowing what we know about PETSCII codes, we can see how the default definitions of the function keys work. Several definitions use CHR$(27) to refer to the Esc key, entering escape codes. Others use CHR$(13) to type the Return key to enter commands. Some definitions use quoted control codes to type the equivalent key sequences.
Notice that key sequences that control the BASIC editor don’t work with the PRINT command, but they do work in function key definitions. For example, function key F9 is defined as a synonym for Ctrl + P, which scrolls a line of a BASIC program listing onto the screen from the bottom. Paired with the default definition of F11, Ctrl + V, these keys are a handy way to browse a BASIC program listing, and more convenient than using the control sequences directly.
To redefine a function key, use the KEY command with arguments: the function key number, a comma, and the string definition. For your convenience, this is how the KEY command displays the current definitions, so you can cursor up to one of those lines, make a change, and press Return to register the new definition.
It can be really handy to define function keys for project-specific tasks. For example, say you’re troubleshooting a subroutine on line 3500 that has different behaviors depending on the value of the X variable, and you want to try it with different values. You can define the function keys F1 and F3 as follows:
KEY 1,"X="KEY 3,":GOSUB 3500"+CHR$(13)
Now you can test the subroutine quickly: press F1, enter a value for X, then press F3.
Once you have function key settings that you like, you can save them to a file on disk, then load them again later. These commands are KEY SAVE "filename" and KEY LOAD "filename", respectively. Save your project-specific definitions on disk with your other project files. You can even have multiple definition files and load them as needed.
KEY SAVE "MYPROJ-KEYS"
Control codes in petcat
In the December 2022 issue of the Digest, I mentioned a handy tool for developing MEGA65 BASIC programs on your PC called petcat. This tool is distributed with the VICE suite of Commodore emulators. It converts between PETSCII characters and an ASCII text-based syntax, so your programs can include all of the possible characters even though your PC doesn’t have a Commodore keyboard.
For the more unusual PETSCII characters and control codes, petcat has a label syntax using curly brackets, similar to what we used earlier for codes like {red}. You can type these labels directly into your text file, and petcat will generate the equivalent PETSCII code when it makes the PRG file.
To my knowledge, there isn’t an existing prose document that describes all of the petcat syntax. So I wrote one. See my new petcat syntax reference in the MEGA65 Wiki. Feedback welcome!
PETSCII is a defining feature of Commodore computers, providing a ton of display power that’s easy to use in BASIC programs. I hope it inspires you to do something great!
This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit m65digest.substack.com
A Rorschach Test on Fire. Dan’s MEGA65 Digest for March 2023.
This month, we have featured files, featured features, new hardware, and a classic programming exercise to play with. Let’s get started!
Featured Files
Get this month’s Featured Files from Filehost and keep them on your SD card for showing off to your friends!
LUMA by Shallan50k. Shift the components around on a playfield to fire lasers at targets. Use a joystick in port 2: select a piece, hold the button, then push it in a direction. Try to solve each puzzle in as few moves as possible.
Wave Hero by GeirS. A watersports-themed one-button runner with smooth gameplay, a gorgeous color palette, and authentic SID sounds. Use just the button of a joystick in port 2. This adaptation of a C64 game includes source code written in Millfork.
Mandelbrot Explorer 65 by lydon, with assembly source. The fractal so famous there’s a song about it. See also Mega-Mandelbrot by Liquidream, in BASIC.
An external floppy disk drive for the MEGA65
In addition to the 3-1/2" floppy disk drive built into the MEGA65, the computer supports connecting a vintage Commodore 1581 external floppy drive to the IEC port. These drives are difficult to obtain, with working units listing for upwards of $400 USD on eBay. If you want a second drive for your MEGA65, there’s a new option!
Foenix Retro Systems is taking pre-orders for a new Commodore-compatible 3-1/2" floppy drive, the FNX1591. Like the 1581, it supports double-density (DD) disks, for 720KB of storage per disk. The FNX’s firmware can be updated over USB, and it can store multiple firmware options that can be selected by a switch. Another switch can set the IEC unit number. The drive uses a modern power supply and power switch, and sports two IEC connectors for daisy-chaining devices.
The FNX1591 is intended to be compatible with all computers that work with the 1581, including the MEGA65. You can preorder one today for $275 USD + $45 USD flat rate shipping. Delivery is estimated for April 2023.
The drive’s resin-printed case is designed to match the Foenix F256K, an all-new 65C02-based wedge-style computer. The F256K is not currently open for orders, but the F256 Jr, a low-cost bare-board version, is for sale for only $199 USD. If your MEGA65 needs a friend, you can’t go wrong with an F256 Jr.
An embroidered dust cover for the MEGA65
Sew Ready, a maker of boutique dust covers for vintage Commodore equipment out of Norfolk, United Kingdom, now offers an embroidered dust cover for the MEGA65! Grey with stylish red piping and a rainbow “65” logo, this cover protects your MEGA65 (or your Commodore 65, if you have one) while leaving the ports exposed, so you can keep it connected and covered at the same time. £22.99 plus shipping. While you’re there, get matching covers for all your Commodore gear.
Looking for other dust cover options? Some large-size keyboard covers like this one tend to fit the MEGA65, and the cheap ones are practical. I found a generic stretchy keyboard cover that just fits, though it won’t protect it from things set on top of it or cats walking across it. For something more durable, lydon found this acrylic keyboard cover that is sized correctly, and works well with these clear elastic buffers. Others have found acrylic display stands that, with the right dimensions, can do the trick and look pretty stylish.
Want to mount your MEGA65 to the wall? 8-Bit Resurgence (COREi64 in the Discord) made a video on making 3D-printed presentation brackets.
Recently added features of the ROM
As I mentioned last month, the MEGA65 ROM is getting new updates. February saw a series of updates that promoted features that were contributed over the last year, using a new testing process to eliminate bugs and confirm backwards compatibility with existing MEGA65 software. See the ROM FAQ for a reminder on how to obtain and install the latest beta release.
The following new features have been added to beta releases since the last release package, up to the most recent ROM version as of this writing, ROM 920383. Many thanks to Bit Shifter for these contributions, and for all of his work on the ROM over the years!
80x50 text mode
The MEGA65 operating environment starts up in a text mode with 80 columns and 25 rows. You can change the environment’s display mode to 40 columns and 25 rows by pressing the Esc key followed by the 4 key. To switch back to 80x25, press Esc then 8.
Starting with ROM 920382, you can switch the operating environment to 80 columns and 50 rows, plenty of screen space for BASIC program code and directory listings. To switch to this mode, press Esc then 5.
Be advised that some software might not run correctly if started with the computer in a state other than the default boot state. Technically, this is true for anything you can change about the state of the computer, and is not particular to the new 80x50 mode. Some programs would also be confused if you start them in 40x25 mode. When writing a program, it’s best to not assume any undocumented default state: either test the state, or set it to be as you need it.
To set the text screen mode from a BASIC program, use the PRINT command to emit the PETSCII codes for Esc (27) and the appropriate number character. You’ll probably want to also emit the code to clear the screen (147).
PRINT CHR$(27)+"5"+CHR$(147)
MOUSE ON default sprite
BASIC 65 has built-in support for the mouse. When you enable the mouse with MOUSE ON, BASIC 65 uses the first sprite of its sprite system (sprite 0) as a mouse cursor. In previous versions of the ROM, the BASIC program had to provide its own sprite image for the cursor. You can still provide a custom image, but now you don’t have to: the default image for sprite 0 is an arrow-shaped mouse cursor.
If you have a compatible mouse (or a modern USB mouse and the amazing mouSTer adapter), try this command to see the BASIC mouse pointer in action:
MOUSE ON
In a BASIC program, you can read the position and button state of the mouse into variables using the RMOUSE command. See your User’s Guide for more information.
In addition to the mouse sprite, the other seven sprites are now set a simple default pattern, so you can experiment with the BASIC sprite system without first loading image data.
CHARDEF 512
The CHARDEF command in BASIC 65 can replace the glyph of a screen code with a custom pattern. (We played with CHARDEF a bit in the September 2022 issue of the Digest.) In previous versions of the ROM, CHARDEF could only modify the first 256 screen codes in the uppercase character set. Now you can use CHARDEF with all 512 screen codes, numbered 0 through 511.
Remember that your program can only display either the uppercase character set or the lowercase character set on the screen at one time. To switch to the lowercase set, PRINT PETSCII code 14:
PRINT CHR$(14)
Surprisingly, there is no PETSCII code to switch back to the uppercase character set. If you need to switch back and forth, it’s better to use the VIC register at $D018 (53272). For the default character set on the Commodore 65, the lower four bits should be 4 for uppercase, or 6 for lowercase.
POKE $D018,PEEK($D018) AND $F0 OR $04POKE $D018,PEEK($D018) AND $F0 OR $06
If your program depends on the character set not changing, you may want to disable the ability for the user to toggle between the two sets by pressing Mega+Shift. Print PETSCII code 8 to disable it (and PETSCII code 9 to re-enable).
PRINT CHR$(8)
MONITOR PC handling and the BRK instruction
In last month’s Digest we explored the MEGA65 Monitor as an essential tool for learning machine language, understanding how the computer works, and troubleshooting machine language programs. With the latest ROM, the Monitor just got even more useful.
We saw how you can start the Monitor by typing the MONITOR command at the READY. prompt. The MEGA65 will also activate the Monitor when a machine language program executes the BRK instruction. This is super handy for investigating a problem with the state of the computer from inside your program: just add a BRK instruction and run your program, and it’ll stop at exactly that spot and open the Monitor so you can inspect registers and memory.
With the latest ROM, the Monitor has been improved so it knows exactly where the BRK instruction left off. You can now continue your program from the Monitor using the G command without arguments, assuming you did not modify the program counter while you were in the Monitor.
Run/Stop+Restore enters the Monitor
Anyone who has written a machine language program knows how it feels when a bug in the program causes it to get stuck, ignoring all input, appearing to do nothing—or worse. For these cases, Commodore provided us an escape hatch: hold Run/Stop and press Restore. This triggers an interrupt that tells the CPU to stop what it’s doing and hand control to the kernel.
Traditionally, the kernel would handle this interrupt by clearing the screen and displaying a READY. prompt. This isn’t particularly useful when troubleshooting a machine language program. The latest ROM does something a bit smarter: it opens the Monitor, with the program counter set to wherever you interrupted it, as if there had been a BRK instruction at that location. In many cases, you can even resume your ML program after interruption, using the G command!
With the new ROM installed, try assembling the infinite border color program from last month’s Digest, then execute it to enter an infinite loop. Press Run/Stop+Restore to break into the Monitor. Then enter the G command to resume the fireworks.
An 80x50 Mandelbrot Set
Rendering the Mandelbrot set is a rite of passage coding exercise, similar to implementing Conway’s Game of Life. lydon’s Mandelbrot Explorer 65 and Liquidream’s Mega-Mandelbrot show off the MEGA65’s color bitmap graphics capabilities. To celebrate the launch of 80x50 BASIC text mode, let’s try a low-res version!
The Mandelbrot set is the set of complex numbers c for which the function f(z+1) = f(z)^2 + c does not diverge to infinity when iterated from z = 0. In other words, take a complex number c, then starting from zero, repeatedly add the complex number and square the result. If the results run off to infinity, the number c is not in the set. If the results never run off—maybe they bounce around locally or converge toward zero—then c is in the set.
The most famous image of the Mandelbrot set represents the points on the complex plane where c = x + iy, where i is the imaginary constant defined as i^2 = -1, centered on the origin (0, 0) and zoomed in fairly close. Points that are in the set appear in the image as black, forming bulbous round shapes.
A simple computer program can draw the image as a bitmap by testing every pixel (x, y) as a coordinate on the complex plane, starting the function at the point and iterating until either it starts to look like the value is diverging, or a maximum number of iterations is reached. If the value is still near the starting point after the maximum iterations, the program concludes it is in the set. Traditionally, the program assigns diverging (non-member) points a color based on the number of iterations it took to detect divergence, giving the figure a glowing effect that shows how points near members of the set take longer to diverge.
(See also Dr. Holly Krieger’s excellent explanation in The Mandelbrot Set episode of Numberphile.)
Let’s write a BASIC program to draw this using 80x50 text mode. First, adjust the color palette to provide a gradient of colors for the non-set points. You can experiment with different values to adjust the colors to your taste.
10 PALETTE COLOR 0,0,0,020 FOR N=1 TO 15:PALETTE COLOR N,N,0,4-(N-4):NEXT N
A BASIC program can set the screen mode by printing the PETSCII code for the ESC key (27) then the character for 5, similar to if you typed it.
100 PRINT CHR$(27)+"5"
To draw the pattern, the program visits each character position once, using a nested FOR loop. For this program, we’ll set every character on the screen to a solid rectangle, then set the color based on our calculations. The BASIC 65 special array T@&() gives us access to the characters on the screen, and the special array C@&() gives us access to the colors. These arrays are indexed with a row and column number, and you can read or assign to these values, similar to other arrays.
110 FOR K=0 TO 79120 FOR R=0 TO 49130 T@&(K,R)=160
The program fills the screen with solid rectangles (screen code 160). The rest of the code will test the point for set membership and set its color accordingly.
The first thing we need to do is determine the actual x/y coordinates on the complex plane for each position on the screen. The y-axis should be in the center, so the x coordinate is 0 when the column number K is 40. Similarly, the x-axis should also be centered, so the y coordinate is 0 when the row number R is 25. We also need to scale down the coordinate ranges to be much smaller, so we can see something interesting. These calculations for X and Y should produce interesting results:
140 X=(K-40)0.06150 Y=(R-25)0.06
These variables represent the complex number c = x + iy that corresponds with the coordinate on the screen. We need to keep track of the iteration value, a complex value stored in the variables ZX and ZY. We also need to keep track of the number of iterations, in variable N. Initialize these variables to zero.
160 ZX=0:ZY=0:N=0
BASIC 65 does not have built-in support for complex numbers or the imaginary constant i, but it turns out we don’t need it. We can calculate the new values of ZX and ZY using separate equations for the real and imaginary parts. Remembering that i squared is -1, the square of a complex number can be figured as follows:
(x + iy)^2x^2 + ixy + ixy + (iy)^2x^2 + 2ixy + (-1)y^2(x^2 - y^2) + i(2xy)
To complete the equation, add the components of the original complex value, X and Y. The complete BASIC code for updating the ZX and ZY variables is as follows:
170 ZX=ZXZX-ZYZY+X180 ZY=2ZXZY+Y
Each time we iterate this function, we can determine whether the function is diverging by testing whether the updated point is far away from where it started. We’re using starting points that are very close to the origin, so to keep the math simple, we can take the distance of the new point from the origin. This distance, known as the magnitude of the complex number c = x + iy, can be found using the Pythagorean Theorem, the square root of the quantity x^2 + y^2.
In BASIC 65, the square root function is spelled SQR(), so we get our magnitude like so:
190 A=SQR(ZXZX+ZYZY)
We’ve just completed an iteration, so we need to increment the iteration counter N:
200 N=N+1
Now we can test whether we’re done iterating. If the magnitude of the value hasn’t gotten large enough to conclude that the function is diverging, and we haven’t yet reached the maximum number of iterations, we continue iterating.
210 IF A<=2 AND N<80 THEN 170
After iteration is complete, we determine the color of the screen location based on the number of iterations. If we hit the maximum, we conclude that the point is in the Mandelbrot set, so the square should be black. Otherwise it is some other color based on the number of iterations. With a maximum iteration count of 80, one way to convert this to a color value between 0 and 15 is to divide it by 5.
220 C=0:IF N=80 THEN 240230 C=N/5+1240 C@&(K,R)=C250 NEXT R260 NEXT K
That’s it! RUN this program and watch it draw a pattern that roughly resembles the Mandelbrot Set image. 80x50 is a low resolution, but it’s close to the first picture of the Mandelbrot Set, published by Robert W. Brooks and Peter Matelski in 1978.
Mandelbrot Set, you’re a Rorschach Test on fireYou’re a day-glo pterodactylYou’re a heart-shaped box of springs and wireYou’re one badass f*g fractalAnd you’re just in time to save the daySweeping all our fears awayYou can change the world in a tiny way— “Mandelbrot Set,” by Jonathan Coulton
Peace and love,
— Dan
This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit m65digest.substack.com
I spent six years of my early childhood on my Commodore 64, between ages 6 and 12. I wrote many programs, all using the built-in BASIC language, learning everything I could from reading the Commodore 64 Programmer’s Reference Guide and typing in program listings from Compute!’s Gazette magazine. I learned a lot from writing BASIC programs, but the one lesson I took away over and over again was this: to make anything really cool, like Marble Madness or Skyfox, I’d need to know machine language.
Machine language felt like dark magic. In Compute!’s Gazette, all the games with the cool graphics and high speed action were printed as columns of numbers, a pages-long incantation that if you typed it correctly would conjure a video game. The Programmer’s Reference had a chapter on machine language that I stared at endlessly trying to make sense of it, but it only made a passing reference to the fact that I needed additional software to write it. If I were older or had a friend with more experience, I would have known what utility cartridge to ask my parents for as a birthday gift, but it was too much for my kid self to figure out all by myself in a basement.
In fairness, Compute!’s Gazette did publish the occasional machine language coding tool, like Fast Assembler in the January 1986 issue. The companion disk to the issue even included the source code for Fast Assembler itself, the first time I had ever seen a complete machine language program listing. I remember making a small change and running the assembler on its own code, which produced a new version of the assembler that printed my name instead of its own. But that’s as far as I got.
In this issue of the Digest, we’re going to answer the questions I had when I was a kid. What is machine language? What is an assembler? And what in the name of Chuck E. Cheese is hexadecimal? We’ll also look at ways you can start learning machine language right now with your MEGA65.
Featured Files
It’s time once again for Featured Files, where we highlight stuff you can download from the Filehost and try with your MEGA65 today!
Mega Wizards, by RBJeffrey. A game for one to four players, using the joysticks. Use your wizardly wiles to bounce the magic ball, take out your enemies, and defend your crystal. Three and four players require a four-player joystick interface.
WORDUP by geehaf. A faithful variant of the popular daily word game Wordle, playable on your MEGA65. Guess the secret five-letter word in six or fewer tries, using hints produced by each guess.
xmas65 by MirageBD. A holiday greeting for the MEGA65 community with an impressive graphical effect and a rockin’ MOD-style backing track.
New Intro Disk!
The intro disk is the first thing you see when you turn on the MEGA65 for the first time, a bundle of games, demos, and music ready to play and show off your new computer to your friends. Thanks to Gurce, there’s now a second intro collection for your enjoyment!
Intro Disk #02 goes beyond the bounds of a single D81 disk image with dozens of titles and music files, all browsable from a colorful menu. Unpack the zip file and copy the files to your SD card, then boot the INTRO2.D81 disk image.
There are a lot of files for this one. I had success keeping the files in a subfolder on the SD card. You may want to move the MODS/ subfolder to the root to make these music files easier to play with the new version of the Manche MOD player, included on the intro disk.
What’s new with the ROM
The MEGA65 ROM is the built-in program code that powers the kernel, the BASIC programming language, and the operating environment. It’s what Commodore would have burned into a physical ROM chip had they released the Commodore 65 as a product. In the case of the MEGA65, the ROM is actually a file on the SD card, loaded during boot by the firmware.
Commodore canceled the C65 project before finishing the original C65 ROM. The MEGA65 team has made significant investments into finishing planned features, fixing bugs, and generally making improvements in the spirit of the original Commodore design. The latest MEGA65 ROM is much more useful thanks to these efforts. Release package version 0.9, which shipped with the first batch of MEGA65s in May of last year, bundled the ROM with the version ID of 920287. The most recent release package as of this writing is version 0.95, which shipped with the second batch last October, with ROM 920377.
The work continues on improving all aspects of the MEGA65 platform, including the ROM. You are invited to help test new “beta” releases of the ROM, starting with version 920378, now available. If you own a MEGA65 and are signed in to Filehost with your account, you can download the latest beta ROM and install it on your SD card. If you do not own a MEGA65, you can put the ROM together for use with the Xemu MEGA65 emulator, using the original C65 ROM, the MEGA65 ROM patch file, and the M65Connect app (also available on Filehost). (The C65 ROM is available for free for personal use as part of C64 Forever Free Express Edition from Cloanto.)
If you discover an issue with any version of the ROM, please report it using the Issues tab of the mega65-rom-public Github repo. Check out the new MEGA65 ROM FAQ article with answers to common questions and instructions on how to download and use the latest ROM. Also, be sure to join the #closed-roms channel on the Discord for updates.
Addressing the computer
One of the first things I learned to do with the Commodore 64 was this command:
POKE 53280,4
This command still works today on your MEGA65. (Try it!) If you haven’t memorized what these numbers mean, it’s difficult to tell what this command does just from looking at it. Newer Commodores added an equivalent BASIC command that better describes its function, also available on the MEGA65:
BORDER 4
The POKE command takes two numbers: an address and a value. It instructs the CPU to send the value to whatever device inside the computer is wired to that address. The effect this has depends entirely on what’s there. In the case of 53280, the address is connected to the VIC video chip, specifically the part that controls the color of the screen border. The different values represent different colors.
In many cases, the device at an address remembers the last sent value, and the CPU can retrieve it with another command:
PRINT PEEK(53280)
The POKE command and PEEK() function are most often associated with computer memory, a device whose sole purpose is to remember the most recent value sent to an address for as long as the computer is powered on. Here’s an address on the MEGA65 connected to memory:
POKE 6144,255PRINT PEEK(6144)
Most addresses are connected to memory chips. Some are connected to other devices, like video and sound generators, disk drives, the keyboard, joysticks, and so on. Non-memory addresses are known as registers, or more specifically I/O registers for their purpose of reading input from and writing output to the devices that live there. Not all registers accept new values. For example, the CPU can read a register connected to a joystick port to see which way the joystick is being pushed, but there is no way to send a value to the joystick.
There are 256 possible values you can send to or read from an address, numbered 0 to 255. The meaning of the value depends entirely on how it is used. It can be a color, a character of text, a set of pixels on the screen, the pitch or volume of a sound, a piece of a larger number, or anything else that can be represented by a value.
Bits, bytes, and binary
Internally, the computer represents a value as one or more digital electronic signals. This fundamental unit of digital data is known as a bit, with two possible values, typically represented by the numbers 0 and 1. Bits can be combined to represent more possible values: two bits can represent four values, 00, 01, 10, or 11; three bits can represent eight values. The value you send to or read from an address is eight bits, also known as a byte, with 256 possible values. The MEGA65 CPU mostly deals with byte-sized values, which is why it’s called an “8-bit computer.”
It’s very common to describe bit patterns as numbers—specifically, integers counting up from 0—even when they don’t represent numbers in the data. The binary number system puts these bit patterns in order. The first six binary numbers are 0, 1, 10, 11, 100, and 101, equal to the decimal numbers 0 through 5.
Just as decimal place values are powers of ten (1, 10, 100, 1000), each binary place value is a power of two (1, 2, 4, 8). You can convert a decimal number to its binary equivalent by setting the bits whose place values add to the number to 1. To convert from binary to decimal, add the place values where bits are set.
Addresses are numbered starting from 0 and counting up, and it should be no surprise that the computer uses bits to represent addresses. A Commodore 64 uses 16 bits (two bytes) for addresses, for 65,536 possible addresses. The MEGA65 uses 28 bits for addresses.
Because the MEGA65’s 45GS02 CPU is an evolution of the Commodore 64’s 6510 CPU, MEGA65 machine language programs mostly work with 16 address bits, and use other features of the 45GS02 CPU to complete the address. It’s kind of like telling a taxi driver where to go using just the house number and street name, and assuming the driver knows the city, state, and country. See the chapter on memory in the manual for more information about how this works.
Hexadecimal to the rescue!
We use decimal numbers in our daily lives, but it gets unwieldy when working with computer programs. Computers are designed around binary, so all of the useful value ranges are convenient in binary—and confusing in decimal. 255 feels like a weird stopping point for a byte, but in binary it’s just the largest eight-digit value: 11111111. As more bits get involved, the possibilities get even more difficult to track. Why is 65,536 an important number? How much is 1,048,576? What is at address 268,251,168?
POKE 268251168,3
Writing programs using binary numbers would quickly drive us mad. A 28-bit address would literally be twenty-eight 1’s and 0’s. We need a concise way to represent these numbers that doesn’t make them more confusing. That’s where hexadecimal comes in.
Hexadecimal is a numbering system that uses sixteen possible values per digit, so a single hex digit completely represents all possible values of four bits. A byte value can be represented by two hex digits instead of eight binary digits.
Earlier we saw that the border color address is 53280 in decimal. It looks like a random number, if you haven’t memorized it. In binary, a hint of a pattern emerges, but it’s still difficult to manage: 1101000000100000 To convert this to hexadecimal, split the bits into four-bit groups, then find the hex digit that represents each group. 53,280 in hexadecimal is D020.
To avoid confusing a hex number with a decimal number, it is often written with a dollar sign in front: $D020. This convention is common to microcomputer programming. (Modern languages use different conventions for hex numbers, such as 0xd020 in Python.)
Commodore 64 BASIC only knows decimal notation for numbers, so C64 programmers are well practiced at converting between bit values and decimal notation. MEGA65 BASIC can handle hexadecimal numbers directly, so you don’t need to bother. Just use the dollar sign notation:
POKE $D020,$04
This makes it easy to use MEGA65 BASIC as a hexadecimal calculator. To convert a hexadecimal number to decimal, simply print the value:
PRINT $D020
To see the hexadecimal representation of any number or expression, use the HEX$() function:
PRINT HEX$(53280)
The MEGA65 I/O registers are in the address range $D000 – $DFFF. In hexadecimal, all of these addresses start with a D. This is easier to understand than using decimal notation for the range: 53248 – 57343.
Most books on this subject discuss at length how to convert between hexadecimal and decimal. This is fine for getting accustomed to the idea. In practice, as long as your programming tools support hexadecimal notation, you rarely need to convert between hexadecimal and decimal. It’s more convenient to leave addresses and values in hexadecimal, and understand how hex digits represent bit patterns in groups of four. Adding one hexadecimal number to another takes some practice—but you always have your handy MEGA65 to help you.
The CPU always follows instructions
The CPU’s job is to perform the instructions of a machine language program. When it’s not running the instructions of your program, it is running the instructions of the MEGA65 kernel to blink the cursor, perform BASIC commands, access disk drives, and so forth. If it doesn’t look busy, it’s busy waiting.
Machine language instructions manipulate memory, interact with I/O registers, perform simple calculations, and make decisions about what instructions to perform next. There are surprisingly few things the CPU knows how to do. Most of its power comes from being able to perform very many instructions very quickly.
The CPU reads its instructions from bytes in memory. It keeps track of which address has its next instruction using an internal register called the program counter (PC). The CPU reads the instruction from the address stored in the program counter, then does it. By the end of the instruction, the program counter contains the address of the next instruction to perform, and the process repeats.
Those columns of numbers in a Compute!’s Gazette magazine program listing are machine language instructions (and data) for the program. As you type them in, Compute!’s “MLX” data entry program stores those values at their corresponding addresses. (MLX magazine listings also include a checksum value as the last number in the line, so it can catch your typing mistakes.)
To start a typical Compute!’s Gazette program, you use the BASIC SYS command with the address of the first instruction. SYS sets the CPU’s program counter to that address, and the program takes it from there.
You can write your own machine language routine in a similar way, setting values in memory that represent instructions then calling the routine with SYS. Try entering these commands on your MEGA65:
POKE $1800,$EEPOKE $1801,$20POKE $1802,$D0POKE $1803,$60SYS $1800
If you mis-type any of these numbers, your computer might do something strange. You can always restart the computer and try again.
A human-friendly machine language
Yes, EE 20 D0 60 is a machine language program. You’re not expected to know what it means, nor are you expected to write programs that way. It’s possible to do so, in the same way that I used to sit in that basement drawing pixel art on graph paper and converting it to byte values for my DATA statements, but it’s slow going. (I tried this recently as an exercise for a different microcomputer. It’s fun, the first time.)
When most people say they’re writing a machine language program, most often they’re actually using assembly language, a programming language that is roughly similar to the computer’s machine language, along with a tool that converts it to machine code called an assembler. Assembly language consists of the same instructions that the CPU knows how to perform, just spelled out in a way that’s easier to read and write.
Here’s that short machine language routine again, this time written as assembly language instructions:
INC $D020RTS
The first instruction tells the CPU to increase the value at address $D020 by one. The second instruction returns from the subroutine. As we’ve seen, address $D020 controls the border color. Each time you SYS to the first instruction, the border color changes, and control returns back to BASIC.
Registers, instructions, and addressing modes
The CPU contains a little bit of memory of its own to use as scratch paper while performing computations. The MEGA65’s 45GS02 CPU has four general purpose registers, each the size of a byte: an accumulator (often referred to as just A), and registers X, Y, and Z. The program counter (PC) is an example of a special purpose register, holding the address of the current instruction.
The CPU maintains eight one-bit registers, called flags or just status registers (SR), that indicate useful aspects of the machine’s operation. For example, the Zero flag is set after a math operation results in zero, which is useful for countdowns or comparing whether two numbers are equal.
The CPU reserves a small area of memory to use as a data structure called a stack. Much like a stack of cards, you can push new values onto the stack, and pull the last pushed value off of it. The CPU uses the stack to remember its place when calling a subroutine, like the SYS command does, so it can return to that place when the subroutine performs the RTS instruction.
The CPU gives special treatment to a single page of memory, 256 bytes starting at an address that ends in $00. It can access this memory faster than other locations, so it’s useful for storing variables that change frequently. In the Commodore 64 (and the 6502/6510 CPU), this is known as the zero page because it is always located at address $0000. In the MEGA65, the 45GS02 CPU can change which page it uses for this purpose, so it is known as the base page. The location of the base page is stored in a register (B).
Nearly all CPU instructions manipulate a value in working memory, a value at an address, or both. Here are just a few examples:
LDA $1900 : load the value from address $1900 into the accumulator
LDX #$FF : load the value $FF into the X register
STA $D020 : store the value in the accumulator to address $D020
ADC #$1A : add the value $1A to the value in the accumulator
AND $C901 : set the accumulator to just the 1 bits that are present in both the accumulator and the value at address $C901
BNE $180C : set the program counter to $180C (“branch to”) if the last math operation resulted in something other than zero
JMP $18FF : set the program counter to $18FF (“jump to”)
JSR $1900 : remember the address of the next instruction, start executing the subroutine at address $1900, then return to this location when the subroutine performs RTS
PHA : push the accumulator value onto the stack
PLA : pull the most recently pushed value from the stack, and set the accumulator to that value
TXA : swap the values in the accumulator and X register
Instructions that access addresses often have variants for different addressing modes that describe how the address is calculated. As shown in the examples above, some instructions can read a value from an address provided with the instruction, or it can read the value provided with the instruction itself (which is just reading it from the memory that contains the program code).
Addressing modes are a powerful concept, and understanding them is critical for performing certain tasks effectively. The MEGA65 45GS02 CPU has many addressing modes. I recommend learning a few common ones to get started, then read about the rest once you’ve tried writing a few short programs.
The MEGA65 manuals describe the 45GS02 instruction set and addressing modes in detail. The 45GS02 supports all of the instructions of the 6502, so descriptions of the 6502 instruction set (another good one) that you can find online and in books also apply to the MEGA65.
The MEGA65 machine language monitor
The chapter on machine language in The Commodore 64 Programmer’s Guide makes a brief mention of a software tool called “64MON,” and uses it to introduce machine language concepts. I know they did not intend this to be cruel, but it was an insurmountable hurdle for me as a kid that Commodore did not include 64MON or anything like it with the computer.
64MON was a machine language monitor, a tool for examining and interacting directly with the CPU and memory of a microcomputer. An ML monitor is a gateway to accessing the raw power of a machine, so useful that many microcomputers had one built in. The earliest models of the Apple II even booted directly into an ML monitor, instead of BASIC. Commodore added a built-in monitor to their computers starting with the C16. The MEGA65 includes an all-new monitor, written by Bit Shifter.
To start the MEGA65 ML monitor from the READY. prompt, type the MONITOR command:
MONITOR
The monitor accepts commands similar to the BASIC environment. For example, to exit back to BASIC, type X then press Return. The monitor uses the BASIC screen editor, so you can cursor up to previous lines to repeat or modify commands.
The first thing the monitor displays is the contents of the CPU’s working memory: the program counter, the status register, the accumulator, and so on. These values aren’t useful when the monitor first starts with the MONITOR command, but you can examine them at any time with the R command.
I won’t describe all of the monitor’s uses and features here, but I do want to mention two of its primary functions: examining and changing memory values, and assembling and disassembling machine language instructions.
Try this command:
M1800
The M command takes an address in hexadecimal (with or without the $), then displays a block of memory values in hexadecimal. If you POKE’d that short program into memory earlier, you’ll see the byte values at the beginning of this list, along with a bunch of junk values:
1800 EE 20 D0 60 8D 41 6A A9 01 8D 40 6A 20 32 52 20 ................>1810 ...
The command to change values in memory is the > that appears at the beginning of one of these lines. This is not a coincidence: you can move the cursor up to any line printed by the M command, change a value, then press Return to modify the memory.
The monitor knows how to assemble instructions directly into memory, using their assembly language mnemonics. To start the assembly process, use the A command followed by a starting address and the first instruction, then press Return. It converts the assembly instruction to machine code, stores it in memory, then prompts for the next instruction. Press Return without an instruction to end assembly.
Try typing A 1800 INC $D020, followed by Return. Then enter RTS on the next line, and finally enter a blank line. Notice that the assembler displays the machine code byte values that it came up with as you type: EE 20 D0 are the bytes for INC $D020, and the 60 byte means RTS.
To see the assembly instructions stored at an address, use the D command (for “disassembly”) followed by the address. Try this now: D 1800
. 1800 EE 20 D0 INC $D020. 1803 60 RTS. 1804 8D 41 6A STA $6A41. 1807 A9 01 LDA #$01...
The instructions you entered appear at the top. Notice that the disassembly continues into the junk data region. The disassembler doesn’t know that those values are meaningless, so it just displays what those bytes would be as assembly instructions, even though it’s nonsense.
To execute a subroutine from the monitor, use the J command (for “jump”) followed by an address. When the CPU encounters the RTS instruction, it returns control back to the monitor, just as it does with SYS in BASIC. Note that if you write a subroutine that never reaches the RTS instruction, control will never return to the monitor. In most cases, you can hold Run/Stop and press Restore to return to the READY. prompt without losing your program in memory.
Here’s a variation of the border color subroutine without an RTS instruction. Use the monitor to assemble it at address $1800, then run it:
INC $D020JMP $1800
For more information on the ML monitor’s features, see the manual. I also wrote a tutorial on the MEGA65 Monitor as part of a series on introductory assembly language.
Mega Assembler
The machine language monitor is super useful for understanding how machine language programs work, and for troubleshooting programs and examining memory. Even so, you would not write large programs this way. A full-fledged assembler application gives you essential tools for managing large amounts of code.
Mega Assembler by grubi is an assembler for the MEGA65. It includes a source code editor with built-in help features, and can assemble and run your programs, and save them to disk. Check out the example programs included on the disk image to get a feel for how it works.
One of the most useful features of an assembler is its ability to assign labels to the many numbers that appear in a machine language program. Here’s a version of the border color program for Mega Assembler that is easier to read than the raw instructions you entered into the monitor. (Mega Assembler needs the lines indented as shown.)
BORDER=$D020 *=$1800START INC BORDER JMP START
Next steps
The MEGA65 manuals are the best and most complete reference for the MEGA65, but they do not yet contain introductory material for machine language programming. Many books about machine language programming for the 6502/6510 CPU and the Commodore 64 apply to the MEGA65. The memory locations are different, and the MEGA65’s 45GS02 has more advanced features, but C64 programming books are still a valuable resource.
Check Archive.org for scans of popular assembly language books, such as:
Machine Language for Beginners by Richard Mansfield
Machine Language for the Commodore 64 and Other Commodore Computers by Jim Butterfield
Compute!’s Machine Language Routines for the Commodore 64
Despite my struggles with it as a small child without the benefit of the Internet, the chapter of the Commodore 64 Programmer’s Guide on machine language is a good introduction to the topic.
With a built-in machine language monitor, a freely available assembler, and access to hundreds of websites and PDFs, it’s so much easier to learn how to write machine language programs for the MEGA65 today than it was for the Commodore 64 in 1986. I hope you give it a try, if only to get a sense of how the computer works, on its own terms.
See you next month!
This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit m65digest.substack.com
I think many of us were first attracted to the MEGA65 project for its hardware: the Cherry MX keyswitches in an authentic Commodore layout, the precision recreation of the Commodore 65 injection molded case, the 3-1/2" floppy drive—not to mention modern conveniences like HDMI video out and an SD card slot. It has taken nine years of hard work and persistence of vision to bring this project to the point of being something everyone can purchase and enjoy.
In this Digest, we’ll look at the recent history of the MEGA65 hardware, including the Nexys FPGA development board on which most of the firmware was written. We’ll take a tour of peripherals that work with the MEGA65, both vintage and new. And we’ll look at hardware experiments in progress that may provide a glimpse of the MEGA65’s future.
But first!
Resources for new owners
The latest batch of MEGA65s is being delivered, and it’s been super exciting to see all of the messages and photos from proud new owners in the Discord. If you’re a new owner, welcome to the world of personal computing!
We have many more resources for getting started with your MEGA65 than we did a year ago. There’s a new documentation landing page, which has links to the latest version of the User’s Guide.
Last year I wrote a MEGA65 Welcome Guide intended to help new owners get up and running. I’ve been keeping it up to date to be useful whether you received your MEGA65 last May or this January.
You’ve already found this Digest. You might also enjoy the back issues, available on the website.
That documentation landing page is part of the MEGA65 Wiki, a relatively new resource that we’re still building. If you’d like to contribute to the wiki, reach out to Gurce on the Discord chat server. You can also read and post articles on Filehost, another great resource.
Featured Files
It’s the Digest’s first recurring segment! Here at Featured Files, we look at cool stuff that has been posted to the MEGA65 Filehost. Download and try these on your MEGA65 today!
Mega Sisters, by Endurion. A version of the 1987 Commodore 64 classic The Great Giana Sisters, this multi-stage adventure pays homage to familiar side-scrolling platformers while adding a few twists of its own. Endurion, aka Georg Rottensteiner, is the author of C64 Studio, a Commodore cross-development IDE for Windows. You can download the source code for this MEGA65 game. Use a joystick in port 2; move up to jump.
Battle Sparrow, by gurce. A vertical scrolling space shooter in the vein of Warhawk for the C64 (1986), this action game was written entirely in MEGA65 BASIC with PETSCII graphics, vibrant colors, and a rockin’ soundtrack. Use a joystick in port 2.
Barnsley Fern Fractal, by heath8041. This elegant BASIC program renders the Barnsley fern at a resolution of 640x400. The disk image includes several programs to render the fractal in different ways. Run it yourself, or watch Heath-man’s video.
The MEGA65 hardware
When you pre-order a MEGA65 from Trenz Electronic, you can expect to receive a faithful recreation of the unreleased Commodore 65 personal computer, with a few modern improvements. The first step of this experience is opening the rainbow-colored “retail” cardboard packing box. Inside the box is the MEGA65 computer, with its authentic injection-molded plastic case, keyboard with authentic layout and keycap designs using modern mechanical keyboard switches, and a vintage new-old-stock 3-1/2" floppy drive.
The ports on the back and side of the computer are a mix of modern and familiar: a barrel-jack power connector, a C64-style cartridge port, an IEC serial port, VGA and HDMI video output, an ethernet jack, a microSD card slot, stereo audio output, and two DE-9 ports for joysticks, paddles, and mice.
Inside the MEGA65 is a circuit board populated with chips and connectors. Compared to its vintage ancestors, the MEGA65’s board is surprisingly spare, with less than a handful of devices that you might call chips and lots of empty green space. Also surprising are what appear to be extra unused connectors for diagnostic devices, additional stereo audio outputs, and unspecified peripherals. The keyboard is a separate device connected to the main board with a ribbon cable, as is the floppy drive. The main board also sports a removable full-size SD card pre-populated at the factory with software, and a CR1220 battery holder for the Real-Time Clock.
At the center of the MEGA65 is the Xilinx Artix-7 FPGA chip, a modern device that can be programmed to behave like other digital logic devices. Importantly, the FPGA replicates these behaviors electronically, as if these digital designs were etched in silicon. The MEGA65 firmware (or core) describes a complete chipset that in 1991 would have been a dozen permanently-etched chips arrayed across the board.
The MEGA65 has two smaller FPGA devices on the board, an Altera MAX10 for various electronic functions, and a Lattice device for the keyboard. There are three FPGA devices in total in the MEGA65.
This is the MEGA65 in its official form. It’s what you buy from Trenz Electronic, and it’s what the MEGA65 team does its best to support with regular testing and improvement of the firmware and system software. The MEGA65 has taken other forms in the past, and the expandability of the machine and open licenses of the designs are meant to encourage experimentation. Naturally, you install such expansions at your own risk.
From Nexys to MEGA
The MEGA65 as we know it went through multiple stages of hardware development over the years. Most of the MEGA65 core was built on the Digilent Nexys A7-100T FPGA trainer board, a bare circuit board that electronics designers use to evaluate the Artix-7’s capabilities and learn how to use it. The circuit board is “bare” in the sense that it doesn’t come with its own plastic case, but it is actually quite crowded with features, with connectors and hardware interfaces of various kinds, push-buttons, and a set of seven-segment LED numeric displays. Important to the MEGA65 project, the trainer board includes VGA video output, monaural audio output, a USB port for a PC keyboard, and a microSD card connector.
While the Nexys board is not an officially supported configuration, the MEGA65 team still provides downloads of the MEGA65 core for the Nexys A7. You can buy a Nexys board for $350 on Amazon, install the MEGA65 core, set up the microSD card, and put together a capable MEGA65 rig. This configuration lacks the eight megabyte RAM expansion of the MEGA65, but this is a documented variation, and many software titles can run without it.
Note that the Nexus A7 board is also known as the “Nexys4DDR,” which is the name you will see in some documentation. This is not the same as the older board known as the “Nexys 4,” which was also used for MEGA65 development at one point.
You can program the Artix-7 FPGA using the m65 command-line tool provided by the MEGA65 project, or with Vivado ML: Standard Edition, the free version of the official professional FPGA development suite from Xilinx. Connect your PC to the “PROG UART” microUSB port on the board.
Support for the USB host port is limited and doesn’t work with USB hubs. You can connect a standard USB PC keyboard to this port, though be warned that fancier keyboards tend to act like USB hubs, so be sure to look for something cheap. A PC keyboard does not have the MEGA65 key layout, but all of the MEGA65 keys are mapped to PC equivalents.
For serious tinkerers, you can enhance your Nexys board with the DM65PIC MEGA65 widget board, designed by Dieter Penner. The widget board connects to the Nexys’s PMOD ports and provides connectors for an authentic Commodore 64 keyboard, a Commodore 65 keyboard (if you’re lucky enough to have one), and two DE-9 ports for joysticks, paddles, or mice. You’ll have to get a board fabrication shop to make it from the Eagle design files, and depending on which shop you use you may need to solder some or all of the surface mount components. You’ll also have to program the microcontroller using the software in the repo. It’s a significant project, but can make for a satisfying build. See sy2002’s demo video to see the widget board in action.
While you’re at it, whip up a 3D-printed case for the Nexys using community-provided design files. I don’t have my own 3D printer, so I use 3D printing services like ShapeWays. It’s pretty amazing how we can send homemade design files for things like plastic shapes and circuit boards to fabrication services, and have them in our hands weeks later, for not much money.
Because the Nexys board was the only way to develop for the MEGA65 project for so long, there are excellent set-up instructions in the Developer Guide. Also check out the article and video by RetroCombs: Install the MEGA65 on a Nexys4 or A7 FPGA (2021).
Board revisions
Between the Nexys trainer board and the MEGA65, there were two revisions of the main board. It is occasionally useful to understand the board revision identifiers, especially when locating the firmware release for your computer.
There were early main board designs known as “R1” and “R2.” These were not distributed, so you won’t see these in the wild.
In the year 2020, the MEGA65 project produced and sold 100 “DevKit” units, with cases made from laser cut acrylic. The DevKit main board is revision “R3.” It has four slots for firmware cores, and includes eight megabytes of additional RAM (known as “attic RAM” or “HyperRAM”). DevKits came bundled with USB JTAG hardware for testing new cores and doing cross-development of software. You can only flash a new core to slot 0 of a DevKit using the Vivado software.
The latest main board in today’s MEGA65 is known as revision “R3A.” The differences from the R3 board are minor: there are eight core slots instead of four, and there are some minor electrical changes. The R3A board can only be flashed by the m65 tool and not the Vivado software suite, but you can flash a new core to slot 0 without Vivado.
From an architecture standpoint, the Nexys, R2, R3, and R3A boards only differ with regards to the Attic RAM: whether it’s present or absent, how much there is, and how fast it performs. The core and system software intend to continue supporting these differences, and software that uses Attic RAM can test for its presence to run cleanly on all boards.
You’ll see these board revision identifiers in the names of the different versions of the MEGA65 Core Release Package on Filehost:
nexys4ddr-widget is the latest firmware for Nexys boards, with widget board support.
mega65r2 is for the unreleased R2 board.
mega65r3 is for the DevKit (R3) and retail (R3A) MEGA65. You probably want this one.
Peripherals
The MEGA65 tries to be compatible with some vintage Commodore peripherals and their modern counterparts. Here’s a quick rundown of a few that I’ve tried. If you have your own experiences or recommendations, be sure to post them to the #peripherals channel in the Discord.
Joysticks, gamepads, and paddles
Vintage joysticks meant for Commodore and Atari computers work well with the MEGA65. They connect to the DE-9 ports on the left side of the computer.
If you’d like to use a Sega Genesis gamepad controller, be sure to use a C64 Genesis adapter. Sega Genesis controllers use DE-9 connectors, but use different wiring that can damage a Commodore. It’s not obvious how much damage it might do to a MEGA65, but it’s mis-wired regardless and I’m not willing to try it to find out.
I prefer a modern gamepad controller wired for the C64. They look and feel like Nintendo controllers, and they wire the second button to “up,” which is used to jump in platform games.
For a modern joystick, I like the ArcadeR. It puts an arcade-quality stick and buttons in a featureful, mod-able case, and has options like auto-fire and two-button support. Another option is the Hyperkin Trooper, a good choice if you’re looking for the vintage shape without roughing up vintage equipment.
The Hyperkin Ranger has a gamepad form factor, and also includes a single wheel for paddle games. Specifically for paddles, I recommend looking for vintage Atari paddles on eBay. They appear to be plentiful, inexpensive, and in good condition, at least for now.
Mice
The MEGA65 supports the Commodore 1351 mouse, the Amiga mouse, and anything that can act like either of these. The 1351 tends to be more reliable.
The MEGA65 must be configured to recognize either the 1351 or the Amiga mouse. Hold the Alt key while turning on your MEGA65 to start the Configuration tool, then set each port to the desired mouse type under the “Input” tab. Connecting an Amiga mouse while in 1351 mode may interfere with the behavior of the keyboard, so if you’re having difficulty, just unplug the mouse and check your settings.
The mouSTer is a clever modern device that lets you connect a USB mouse to a DE-9 Commodore port. It’s quite versatile, and you can set a variety of options by connecting a USB drive containing a config file. The mouSTer can be configured to emulate either a 1351 or an Amiga mouse, so make sure it matches your MEGA65’s settings. I use my mouSTer with an inexpensive Logitech M185 wireless mouse. If I’m not desperate for the Commodore tank mouse look and feel, I prefer my Logitech.
I’m looking forward to trying modern retro mice recreations like the Tank Mouse when I get one.
Other peripherals
The MEGA65 has an IEC serial port, most often used on Commodores with external disk drives. You can use a vintage external 1581 drive with the MEGA65 in C65 mode. Other IEC devices don’t work so well with C65 mode at the moment due to known timing issues inherited from the Commodore 65 ROM. C64 mode (type GO64) does better in some cases. I was able to get my Blue Chip D12 daisy wheel serial printer to work in C64 mode. Improving IEC device support in C65 mode is on the to-do list for the ROM.
The FTDI JTAG adapter is a peripheral that mounts to the primary JTAG port on the main board. With your PC connected to it with a mini-USB cable, you can install bitstreams, send programs, transfer files, and debug issues. If you don’t need to install bitstreams, you can use a cheaper and easier-to-find USB serial device connected to the UART pins of the JTAG connector. USB serial communication is great for cross-development and debugging.
Work is in progress to make it possible to transfer files and programs, and perform other cross-development tasks like debugging, using the MEGA65’s ethernet port, with no need for a separate adapter. If you’re interested in helping test this feature, inquire on the Discord. There’s a test core and new versions of the command-line tools that demonstrate this capability.
To round out this discussion of peripherals, we might as well mention the Real-Time Clock replacement. This is a peripheral that uses the Grove connector on the main board. The replacement RTC was our workaround solution for the small percentage MEGA65s that shipped with faulty real-time clock chips. The latest firmware detects when the replacement RTC is connected and communicates with it over the Grove connector.
Paul’s prototypes
One of the biggest hardware challenges for the MEGA65 project is the limited availability of FPGA chips. Trenz Electronic can only acquire so many of the FPGAs at time, which limits their ability to build and deliver computers. Even the price of a Nexys trainer board has gone up $80 in the last year. We all hope this shortage is temporary, but it may be a few years before we see supply return to previous levels.
MEGA65 founder and hardware designer Paul Gardner-Stephen is experimenting with a prototype for a lower cost keyboard that doesn’t need its own FPGA. The “DIY keyboard” project intends to be a drop-in replacement for the current MEGA65 keyboard, using the same Cherry MX switches and keycaps, and fitting perfectly in its case with no noticeable difference to the user. It can also be a standalone C65 keyboard for the Nexys board, connected to a PMOD port. You can use the open hardware design files to send away for a bare board, and install your own switches and keycaps. As pictured above, MEGA65 owner mpryon completed his Nexys rig with a DIY keyboard based on Paul’s prototype design.
It’s too early to say whether the DIY keyboard will become a more polished product for sale. It’s easy to imagine it being sold as a kit with a set of official MEGA65 keycaps, or adapted via microcontroller to be a USB keyboard with a C65 layout for use with the Xemu emulator. You can follow Paul’s progress on the DIY keyboard on his developer blog: October 2022, November 2022, December 2022
Paul is also working on a prototype for a port expansion board that adds component video output, a Commodore user port, a tape port, and a port for the unreleased C1565 external floppy disk drive. The board would mount inside the MEGA65, and place the new ports through the punch-outs already prepared in the back of the plastic case.
Why add a port for an unreleased Commodore peripheral? So you can recreate the peripheral, of course! It’s just an idea for now, but the 8-pin mini-DIN connector for the C1565 external floppy drive could support an actual vintage prototype drive, or an all-new project to recreate the drive with a case that matches the MEGA65. At the very least, the port would make the MEGA65 more authentic, matching the similar port on the Commodore 65.
As with the DIY keyboard, there are not yet plans to produce the expansion board in quantity. You may be able to make your own from open designs at some point. Paul is also dev-blogging progress on the expansion board: October 2022, January 1, 2023, January 6, 2023.
For several years, the MEGA65 website advertised not only an upcoming reproduction of the Commodore 65, but also a mobile phone based on the same technology. Today, the website focuses on the first project, which, obviously, is now in full swing and in the hands of hundreds of happy owners. Is the mobile phone project still a thing?
Let this be a lesson: never bet against a project with strong principles. The MEGAphone is still in active development, and making good progress. With a grant from the NLNet Foundation, the MEGAphone seeks to reclaim one of the most important technological spaces in the daily lives of billions of people.
The idea is this: The sheer complexity of modern computers alienates us from the technology upon which our digital lives depend. It’s not enough for a smartphone operating system to be open source in theory, as Android claims to be. To achieve the benefits of this transparency—security, privacy, control in the hands of the user—the technology has to be strictly modular, and simple enough for a computer hobbyist to understand. The Commodore 65 is one of the last practical computers to fit this description. What if there were a smartphone based on the Commodore 65?
The MEGAphone is a proof-of-concept portable device with mobile telephony, Internet, and a touch screen interface. It uses the MEGA65 core and ROM, and can run MEGA65 software, including software written by the phone’s owner. Everything is open source, and any owner can replace any component on their phone as easily as they can on the MEGA65.
To learn more about the project’s goals, watch video of Paul’s 2019 talk, Creating Resilient and Sustainable Mobile Phones. For a glimpse of recent progress, see his recent dev-blog entries about the MEGAphone: September 7, 2022, September 14, 2022, September 20, 2022.
The MEGA65 hardware released in 2022 is a huge milestone for the project and for vintage computer preservation in general, the culmination of nine years of work to realize the vision of Commodore’s designers and engineers with modern components and techniques. The result is a multi-layered hobbyist platform that will continue to see revisions, adaptations, and expansions into the future.
If you’d like more information on any of these projects, or if you discover something interesting or build something cool, be sure to let the community know in the Discord.
Until next time!
— Dan
This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit m65digest.substack.com
One of the most satisfying things you can do with a vintage computer is to turn it on and start writing a program. With nothing but your curiosity, a little persistence, and maybe a book and a pad of scratch paper, you can craft a program that solves a problem, performs a task, produces a work of art, tells a story, or generates an interactive experience.
In this Digest, we’ll look at a few ways to get starting writing programs for the MEGA65 using the built-in BASIC 65 programming language. This won’t be a BASIC tutorial—there are hundreds of vintage books on that subject, and you already have the MEGA65 User’s Guide. There are additional chapters for beginners in the MEGA65 Complete Compendium. Instead, we’ll focus on several ways to create and run BASIC programs with your MEGA65, and with your PC.
But first!
Batch 2 shipments have begun!
Trenz Electronic has announced that batch 2 shipping has begun, and some have already received their MEGAs! The next 400 recipients should receive shipping notices in the next few weeks, followed soon after by receipt of your very own MEGA65.
Batch #3 is expected to deliver in the third quarter (July-September) of 2023. It’s too early to say how many units will ship in the next batch. Here’s hoping that FPGA supply will be flowing more freely next year.
Has your shipping address changed?
For those of us awaiting delivery of a pre-ordered MEGA65: If your shipping address has changed since you placed the pre-order, send an email to: sales@trenz-electronic.de
Changing the shipping address in your Trenz Electronic website account settings will not update shipping addresses on pending orders. You must send them email to update the address on a pre-order.
I know many of us have been waiting a long time for our MEGA65s. If your address needs updating, take care of it soon to ensure smooth delivery.
Digest feeds
You’re probably reading this Digest delivered as email via Substack. If you clicked through to the audio version with the play icon at the top of the email, you’re probably listening to it using a web browser on your desktop computer, tablet, or smartphone. You can read past and current issues, as well as manage your email subscription, on the Substack website: m65digest.substack.com Substack has a phone app, and it includes a built-in audio player.
Alternatively, you can follow the Digest using your favorite feed reader. The Substack website has an embedded RSS feed, and you can usually just copy the website address into your feed reader to subscribe. If your reader needs the exact feed URL, it’s just this: m65digest.substack.com/feed
I’m pleased to announce that the audio version of the Digest is now available in your podcast player! Search for “Dan’s MEGA65 Digest” in Apple Podcasts or the Pocket Casts directory. I’ve also found that some (but not all) podcast players can subscribe directly to the Substack RSS feed as if it’s a podcast.
Finally, just to make sure we’re not too dependent on a single platform, I post a copy of the Digest to my personal website. dansanderson.com/mega65 has links to all of my MEGA65 projects, along with every issue of the Digest. You can follow the RSS feed on that page for just the Digest, or follow the one on my homepage for everything I post. I sometimes write about MEGA65 topics on my main blog.
Regardless of how you follow the Digest, thanks for being here!
Entering commands and programs
Like the Commodores that precede it, the MEGA65 starts up with a blinking cursor at the READY. prompt. You can enter a command by typing the name of the command followed by arguments, and execute the command immediately by pressing the Return key. For example, here’s a command to turn the color of the screen border to black:
BORDER 0
You can use the same blinking cursor to write a program, a collection of commands on numbered lines. If you type the BORDER command after a line number then press Return, it does not change the border color, but instead stores the command to be run as part of the program.
10 BORDER 0
Here’s a short program that prompts for a temperature in degrees Fahrenheit, then displays the equivalent temperature in degrees Celsius. Try typing these lines into your MEGA65:
100 PRINT "ENTER A TEMPERATURE IN DEGREES FAHRENHEIT:"110 INPUT F120 C=(F-32)*5/9130 PRINT140 PRINT F;" DEGREES FAHRENHEIT IS ";C;" DEGREES CELSIUS."
If you’ve written a program on a Commodore before, this should feel familiar. The BASIC screen editor allows you to type whatever you want anywhere on the screen. When you press Return, it checks whether the line with the cursor on it begins with a line number, and if so, it stores the contents of that line in BASIC program memory. If there’s already a line in memory with that line number, BASIC replaces the old line with the new one.
It’s important to remember that what’s on the screen does not necessarily represent the program stored in memory. You can think of the screen as a sort of scratch pad on which you can type whatever you like. Only when you press Return on a line will BASIC try to do something with what you typed.
To see the lines of the actual program stored in memory, use the LIST command.
LIST
If you notice a mistake or something you’d like to change, you can move the cursor up to the line displayed on the screen, type new text over the old text, then press Return to replace the line in memory.
BASIC keeps the program in line number order, but you can enter the lines in any order you like. This is handy for inserting a line between two other lines: pick a number between the numbers of the existing lines, and enter the new line with that number. This is why BASIC programmers tend to number their lines by tens (10, 20, 30…), to make it easy to insert a line in the middle of the program (15).
To delete a line, enter the line number with no text after it. To delete a range of numbered lines, use the DELETE command followed by the range:
DELETE 1000-1500
To erase the entire program from memory, use the NEW command. If you do this accidentally and want to bring the deleted program back, don't panic, just type:
NEW RESTORE
Saving programs to disk
Your program is in the computer’s memory, but it is not saved to permanent storage automatically. (Maybe that’s obvious in the context of vintage computers, but we get accustomed to modern conveniences sometimes.) If you turned off the MEGA65 now, the program would be forgotten.
Hopefully by now you’ve had a chance to mount a D81 disk image and load a program from it. You can also use the Freeze menu to create an empty disk image on your SD card. Try this now: hold Restore for a second then release to open the Freeze menu. Press 0 to mount a disk to device 0 (unit 8). At the top of the list of disk images is the option to create a “NEW D81 DD IMAGE.” Select this, then give the disk a name. I like to call my work disks “WORK” to make it seem like I’m doing something important.
The new disk image is now mounted. Press F3 to resume to BASIC. (Be careful not to press F5 to reset if you have a program in memory!)
With a BASIC program in memory, save it to your disk with the DSAVE command. Put the filename in double-quotes:
DSAVE "TEMPCONV"
Confirm that your program is on the disk by listing the disk directory:
DIR
As written, the DSAVE command will fail if there is already a file with that name on the disk. It’s a common practice to save your work periodically with different filenames suffixed with increasing numbers, so you can revert back to an older version if necessary. (Commodores don’t have built-in revision control systems!) Over a few days, you might end up with "TEMPCONV2", "TEMPCONV3", "TEMPCONV17" on your disk, until you’ve decided which version is the final version.
Alternatively, you can live dangerously and overwrite the same file each time you save. To do this, prepend the filename with an “at” symbol (@). (The symbol is not considered part of the filename.)
DSAVE "@TEMPCONV"
You can now turn off your MEGA65. The next time you turn it on, you can use the MOUNT command with your work disk name followed by .D81 to mount it, then the DLOAD command to load your program back into memory:
MOUNT "WORK.D81"DLOAD "TEMPCONV"
Listing long programs
Eventually your program will be many lines long. The LIST command will try to list your entire program, and only the bottommost 25 rows will be on the screen at the end. How can we navigate a long program listing?
(If you want to experiment with this without writing a long program, MOUNT "MEGA65.D81" then DLOAD "AUTOBOOT.C65" to load the demo disk’s menu program, which is written in BASIC. If you disabled opening the menu on start-up, this file has been renamed: use DLOAD "MENU" instead.)
As with previous Commodore BASICs, the LIST command accepts a range of line numbers. This is a slightly cumbersome but precise way to look at a few lines at a time, if you know their numbers.
LIST 100-300 : rem List lines 100 through 300LIST -300 : rem List all lines up to 300LIST 1000- : rem List all lines starting from 1000LIST 250 : rem List only line 250
When listing many lines, you can tap the No Scroll key to pause the listing. You can press Run Stop to terminate a paused listing, or No Scroll again to resume. The MEGA65 is so fast in its default 40 MHz mode that this isn’t a practical way to peruse a listing. You could use the SPEED 1 command to set it to 1 MHz and slow it down, but be warned: there’s a bug where the MEGA65 crashes when you Run Stop in slow mode! I don’t recommend this method at all for this reason—but I mention it because it’s interesting.
The MEGA65 LIST command has an option to display a program listing one screenful at a time:
LIST PLIST P 100-300
Press a key at each page to advance to the next one, or press Run Stop to abort at the current page.
There’s a third method to browse long listings, and it’s my favorite: press F9 and F11 to scroll down and up by a line. (If you’ve redefined your function keys, you can use Ctrl-P and Ctrl-V instead.) I like this method because the screen editor remains active, so you can start typing inside the listing as soon as you see what you want to change. Notice that the screen does not have a modern-style scrollback buffer, even though it seems like it does: it’s actually reading lines from BASIC memory to fill in the scrolling display.
With lots of text on the screen, remember that you can get new blank lines to type on by running the cursor off the bottom of the screen, or pressing Shift + Clr Home to clear the screen entirely. You can also clear just from the cursor’s line to the bottom of the screen by pressing the ESC key followed by the @ key.
The Eleven programming environment
The BASIC 65 screen editor is thoroughly retro and thoroughly Commodore, and it picked up some nice features along the way from the Commodore 128, Commodore 65, and a few added by the MEGA65 team. But perhaps you want a coding environment that’s a bit more modern—without leaving your MEGA65.
The Eleven programming environment, by Stephen Kleinert (username ubik), reinvents the BASIC 65 editing experience. It provides a modern-style code editor and extensions to the BASIC 65 language to make it easier to use for large projects. It dispenses with line numbers, and instead uses labels for flow control (such as goto statements). Variable names can be longer than two characters, and preprocessor directives allow for efficient definition of constants without taking up variable space.
The brilliant part is that Eleven compiles your program into plain BASIC 65 code. It generates line numbers, replaces labels, renames variables, strips comments, and minifies syntax. The result is a compact BASIC 65 program that you can save to disk and give to your friends, with no bulky runtime library or other baggage. BASIC 65 is a powerful enough language; Eleven just adds modern documentation and editing conveniences, making BASIC programs easier to write and maintain.
A copy of Eleven was included on your SD card, and you can download the latest version from Filehost. Here’s how to give it a try:
MOUNT "ELEVEN.D81"
BOOT
Enjoy reading the documentation directly in the editor. Use cursor keys to scroll.
Press F1 to load a program.
Type dizzy.el and press Return. Scroll through the program listing to see how it works.
Press F5 to compile. The program compiles and starts running. Press any key to exit to the READY. prompt.
LIST to see the compiled BASIC 65 program.
BOOT to return to the Eleven editor, with the last-saved project loaded.
Eleven will insist that you save your changes before compiling, so you won’t lose anything if your program crashes the machine.
Stephen has released Eleven to the MEGA65 project with an open source license. There may be on-going improvements in the future.
Writing BASIC 65 programs on your PC
Coding directly on the MEGA65 with the printed User’s Guide at your side is a cozy, comfortable experience, free of Internet-y distractions. But sometimes—just sometimes—I want to use modern tools to develop retro software, even if I’m writing in BASIC.
Full disclosure: I’m a developer tools nerd. I like storing text source files in a Git repository, running things through multiple levels of processing, and automating it all together with Makefiles. One of my favorite ways to write MEGA65 BASIC is on my PC.
I use a command-line tool called petcat that converts a text file containing BASIC code into a PRG file that I can run on my MEGA65 or in the Xemu MEGA65 emulator. petcat comes with the VICE Commodore emulator suite, which I have installed for all of my non-MEGA65 Commodore projects. VICE doesn’t provide a MEGA65 emulator (that’s what Xemu is for), but petcat does know about BASIC 65 commands.
That temperature conversion program looks like this in a petcat text file, literally the same text but in lowercase:
100 print "enter a temperature in degrees fahrenheit:"110 input f120 c=(f-32)*5/9130 print140 print f;" degrees fahrenheit is ";c;" degrees celsius."
To convert this to a MEGA65 PRG file, use this shell command:
petcat -w65 -o tempconv.prg -- tempconv.bas
You can test the PRG file in Xemu by dragging it into a running Xemu window, or by starting Xemu with appropriate command-line options. If you happen to have a JTAG USB adapter, you can beam this PRG file directly to your MEGA65 with the m65 command-line tool or with the M65Connect app (search for “M65Connect” on Filehost).
petcat labels
You might notice that a text file on a PC doesn’t seem to be capable of representing everything that might go in a BASIC program. For example, there are characters on the MEGA65 keyboard that are not present on a PC keyboard, like the Up Arrow symbol. How would a petcat program print the graphical glyphs on the front of the keys, like the heart shape on the S? What about the PETSCII control codes, like Ctrl-3 that changes the print color to red?
petcat represents as many characters as it can with ASCII equivalents. All of the right-hand glyphs that you access by holding Shift and pressing a letter are simply the capital letters. The PETSCII symbols for pound, Up Arrow, Back Arrow, and pi are represented by ASCII characters with the same encoded value: \, ^, _, and ~, respectively.
Nearly everything else appears in a petcat listing as a label inside curly brackets, such as {red} for Ctrl-3. For example:
10 print "{clr}{red}SSS {wht}mega65 {red}SSS{wht}"
The left-hand glyphs that you access by holding Ctrl and pressing a letter are labels like {CTRL-A}. A few codes that are typed with the Mega key look like {CBM-@}. Other PETSCII control sequences have named labels like {clr} or {stop}.
I won’t clutter this Digest with a complete list. I’ll just say that petcat also knows how to convert a PRG file back into text. If you’re unsure of what label to use for a character or control code, you can create a small program with the character in it using the MEGA65 or Xemu, then run petcat in reverse to see what it looks like.
petcat -65 -o myprog.bas -- myprog.prg
CBM PRG Studio 4
If your PC runs Windows, there’s a compelling new option for MEGA65 cross development. CBM PRG Studio is a much loved integrated development environment for writing programs for Commodore computers. Starting with version 4.0, it includes support for the MEGA65.
I don’t normally use Windows, so I’m not deeply familiar with CBM PRG Studio’s features. But it took me no time at all to figure out how to use it to write a MEGA65 BASIC program and build the PRG file. It looks like a compelling IDE for Kick Assembler, as well. Give it a try!
Managing D81 disk images on a PC
Maybe you’ve written a program on your MEGA65 and saved it to a D81 disk image, and now you want to extract the PRG file on your PC. Or perhaps you’ve written a PRG on your PC, and want to put it in a D81 disk image for the MEGA65. We’ve got tools for that!
Windows users should go straight to DirMaster, a powerful disk image management tool that supports many formats, including D81. You can create new images, open existing ones, copy files to and from images, and much more.
Mac and Linux users will prefer droiD64, a desktop app similar to DirMaster. It has fewer features, but I like it because it’s fast and works on all operating systems. It requires a Java 11 runtime, such as OpenJDK or the Oracle Java runtime. I wrote a bit more detail about droiD64 in a recent blog post. Check that out for a technical recommendation on how to improve the start-up script that comes with droiD64.
Of course, I did say I was a developer tools nerd, so naturally my build scripts use command-line tools to create D81 image files. VICE comes with a tool called c1541, and cbmconvert (download) is another option. Personally, I write my own D81 build logic in Python with the d64 Python library. Most people will get by just fine with DirMaster or droiD64.
It’s the end of the year, and I hope everyone is able to take some time off, disconnect from the Internet a bit, and spend some quality time with your MEGA65, and maybe family and friends too if you get around to it. When you get back online at the end of your break, don’t forget to share your BASIC programs by uploading them to Filehost!
I hope you’re all doing well this holiday season, and I hope to see you again in 2023.
— Dan
This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit m65digest.substack.com
[Did you know: All issues of the Digest have an audio version! Check out the player at the top of the email or Substack post. — Dan]
The Commodore 64's Sound Interface Device (SID) chip was ahead of its time. While other personal computers were limited to simple beeps, the SID provided features comparable to professional sound synthesizers, with support for multiple kinds of sound waveforms, three-voice polyphony, and advanced filters and effects. Musician-programmers and game developers pushed the SID's capabilities to the extreme, finding clever ways to drive the chip to produce multi-instrumental arrangements. To this day, artists continue to contribute to a vast legacy of SID-based music.
The MEGA65's audio subsystem includes four SID chips, alongside more modern sound capabilities. In this Digest, we will scratch the surface of how the SID works, and how to produce sound and music with the SIDs from BASIC. We'll also introduce a new music demo written for the MEGA65 that takes full advantage of all four SID chips. MEGA65 music is a big subject, and we'll cover more music topics in future issues of the Digest.
But first, some news!
New firmware release
The new release bundle for the firmware and system software has completed testing and is now officially declared "stable." The new batch of MEGA65 computers being delivered by the end of the year 2022 will have this release installed at the factory. Anyone who received their MEGA65 computer earlier this year is encouraged to upgrade.
Release bundles now have version numbers to make them easier to understand. The new release is known as release v0.95, and includes the MEGA65 core dated October 2022 (20221012.18,93d55f0) and MEGA65 ROM 920377. The previous release has been retroactively labeled v0.9.
I have updated the MEGA65 Welcome Guide to describe the upgrade procedure, and to welcome the new batch of owners. You can download the release v0.95 bundle from Filehost. Sign in with your account, make sure you have redeemed your owner code, then download the release package:
If you have any questions about upgrading, ask for help in the Discord!
Real-Time Clock replacement program
The MEGA65 has a built-in Real-Time Clock (RTC) chip that keeps track of the date and time. If you haven't yet, you'll want to open your MEGA65 case and install a battery of type CR1220 on the main board. See the Welcome Guide for instructions with photos.
It was discovered earlier this year that a minority of MEGA65 computers contained faulty RTC chips, estimated to affect about 20% of computers. Faulty RTCs count the seconds too slowly or not at all. Starting with release v0.95 of the system software, the Freeze menu includes a diagnostic utility that knows how to test for a faulty RTC. (Open the Freeze menu and press the Help key to start it.)
If your MEGA65 has a slow or stopped clock, you can request a replacement part that connects to the main board. For instructions on how to run the diagnostic utility, and how to request a replacement unit, see this article on Filehost:
Thanks to the generosity of the MEGA65 community, we are able to offer this replacement part free of charge. We're asking anyone with the ability to do so to donate money to cover shipping costs. If you need a replacement and cannot afford to donate, please request one anyway. We want everyone who needs a replacement RTC to have one. Just make sure your RTC fails the test with a fresh battery before requesting the part.
I am personally assembling these replacement parts in my basement! Many thanks to the MEGA65 team and community donors for making this fix possible.
C64 core release 4
The MEGA65 is not just a MEGA65. Thanks to its FPGA-based architecture, it can be reconfigured to behave like other computers. By far the most thorough transformation available today is the Commodore 64 core by MJoergen and sy2002, based on the MiSTer core. This core provides a PAL-mode C64 experience with a high degree of compatibility, and has been tested thoroughly with even the most intense games and demos. The “C64 for MEGA65” earns its place in slot 2 of everyone’s computers.
They’ve just announced a new release 4 of the C64 core, which adds RAM Expansion Unit emulation and writable D64 disk images. Many aspects of the core can be configured, especially display settings, and starting with this release you can save your settings to the SD card.
Download the C64 core from Filehost and install it from the core selection menu, similar to how you upgraded the MEGA65 firmware.
The sound of ones and zeroes
The simplest way for a computer to make sound is to send ones and zeroes to a speaker. A "one" pushes the diaphragm of the speaker all the way out; a "zero" pulls it all the way in. If the computer sends alternating ones and zeroes fast enough, we perceive the pressure wave generated by the diaphragm as sound.
The faster the computer alternates the ones and zeroes, the higher the pitch of the sound we perceive. The slower the alternations, the lower the pitch. We can describe the pitch of the sound in terms of the frequency of the repetitions, expressed as the number of repetitions per second, or Hertz (Hz). A frequency of 880 Hz is higher in pitch than a frequency of 660 Hz.
The more forcefully the computer pushes the diaphragm of the speaker, the louder the sound. This is known as the amplitude of the wave, or the volume of the sound. The computers sending ones and zeros to a speaker are beeping as loud as they can: the difference between the "one" and the "zero" is as much as it can push the speaker. If you're lucky, such a computer has a volume knob connected that reduces the amplitude of the signal electronically before it reaches the speaker.
With only ones and zeroes at its disposal, it might seem like this simple computer can only make one kind of sound at different pitches, or no sound at all. In fact, it can change the nature of the sound in one other way. When it sends a "one" for a small fraction of a second then sends a "zero" for that same amount of time, and repeats this pattern at a given frequency, the computer generates a special kind of pulse wave known as a "square wave." The computer can also differ how long it holds the "one" relative to the "zero," known as the pulse width. The pulse width affects the timbre of the sound, a musical term referring to the nature of the tone independent of its frequency or amplitude.
It's worth pausing here to admire what creative coders have done with these simple pulse waveforms. By rapidly adjusting the frequency and width of these pulses in complex patterns, computers can generate music that sounds, at least to a generous observer, like a band of instruments playing a tune.
Introducing the SID
Pulses are a crude digital way to make noise. By the time of personal computers, musicians had already spent decades developing sophisticated analog sound synthesis techniques. To break free of pulse waves, computers needed analog devices that they could control digitally, entire chips dedicated to sound and music.
Commodore's solution was the MOS 6581 Sound Interface Device, now known the world over as simply "the SID chip." The SID brought sound synthesis features previously found only in professional musical instruments to home computing, and became a defining characteristic of Commodore computers. For later models of the C128 and C64C, Commodore used an improved version with the part number 8580 with minor differences from the 6581. Audiophiles are divided as to which version sounds best. SID designer Bob Yannes went on to found the Ensoniq synthesizer company with an expanded version of the SID in its instruments.
The SID can generate four distinct types of sound wave: pulse waves of multiple pulse widths, triangle waves, sawtooth waves, and a randomized "noise" waveform. A single SID chip has three independent signal generators, capable of three simultaneous "voices" with separate wave and volume controls that can be combined for effects or used as separate instruments. (The 8580 can even merge the waveform types, but this is seldom used.)
The SID can trigger tones programmed with a volume pattern known as an "envelope” that can reproduce behaviors of musical instruments and other sound sources, such as a bowed violin or a plucked guitar. The signal generators can be synchronized and modulated with each other, and the combined output can be filtered by frequency.
The SID made the Commodore 64 popular with musicians, and composers produced a long legacy of SID music for games and demos. Third-party hardware such as the SID Symphony cartridge gave the Commodore a second SID chip, and you can find many six-voice dual-SID compositions in the archives. Today, hobbyist-made devices to add a second SID chip to a C64 are widely available. The vintage SID chips themselves are getting increasingly difficult to find, though hobbyists have recreated them as FPGA-based devices that slot in to vintage and retro hardware.
The MEGA65's audio subsystem includes four SIDs as part of its FPGA architecture, for a total of twelve independent voices. The MEGA65 produces stereo audio, and by default places SIDs 1 and 2 panned slightly to the right, and SIDs 3 and 4 slightly to the left. You can adjust the mix yourself from the Freeze menu: hold Restore for a second then release, then press the A key to select the audio mixer utility.
The MEGA65 also has separate hardware for playing 16-bit digitized audio samples, and room to add a Yamaha OPL FM synthesizer in a future firmware update. Altogether, MEGA65 audio represents multiple generations of microcomputer sound technology.
Sound and music with BASIC 65
MEGA65 BASIC makes it easy to include sound and music in your programs with simple commands. BASIC reserves two SID chips (six voices) for sound effects and the other two SIDs (the other six voices) for music, so effects and music never compete for resources.
All sound occurs simultaneously with the action of your program: the commands return control immediately, so your program can continue to drive animations and game logic while the music plays. This powerful feature is unlike any other BASIC, which, if they have sound commands at all, typically wait for the sound to finish playing before proceeding to the next command.
While these commands do not provide access to all of the features of the SID chips, they're a quick and easy way to add sound and music to your BASIC programs.
The SOUND command
SOUND voice, frequency, duration, sweep-dir, sweep-lower-frequency, sweep-size, waveform, pulse-width
The SOUND command plays a single sound effect. When you provide the voice number (1-6), a frequency (in Hz), and a duration, it plays a square wave:
SOUND 1,3520,90
The duration is in a unit called a "jiffy." The length of a jiffy depends on the MEGA65's video mode: in PAL mode, it's 1/50th of a second, and in NTSC mode it's 1/60th of a second. (We'll discuss this oddity a bit later.) So a duration of 90 in NTSC video mode would sound the tone for one and a half seconds.
The SOUND command can apply fun frequency sweeping effects, good for sirens, whoops, and rumbles. The sweep is described by three parameters. The first parameter is the direction of the sweep: 0 for up, 1 for down, and 2 to wobble up and down. Next is a second frequency lower than the first, defining a range that the sound will sweep between. The last sweep parameter is a duration of each repeat of the sweep. Larger numbers produce faster sweeps and more repetitions over the duration of the sound effect.
Experiment with these numbers to see what sounds you get.
SOUND 1,3520,90,0,2640,50
The command accepts additional parameters to select a different waveform or set the pulse width. If you want to use one or both of these parameters without a frequency sweep, provide empty values (just commas) for the sweep parameters.
For the waveform parameter, select 0 for a triangle wave, 1 for a sawtooth wave, 2 for a pulse wave, and 3 for noise. If you select the pulse wave (the default), you can set a pulse width in the range 0-4095.
Here's a non-sweeping triangle wave:
SOUND 1,7040,90,,,,0
This produces low noise:
SOUND 1,880,90,,,,3
A noise waveform with frequency sweeping might sound like an airplane engine sputtering:
SOUND 1,4000,45,2,2000,900,3
Try the pulse waveform with different pulse widths to see how it affects the sound of a pulse wave:
SOUND 1,4000,45,2,2000,90,2,200 SOUND 1,4000,45,2,2000,90,2,800 SOUND 1,4000,45,2,2000,90,2,2500
The PLAY command
PLAY voice-1, voice-2, voice-3, voice-4, voice-5, voice-6
While you could generate musical melodies using a series of SOUND commands, this isn't convenient. Instead, BASIC 65 has a featureful music system, and it's based entirely on strings—strings of text, that is.
The PLAY command accepts up to six string parameters, one for each voice reserved for music. Each string describes a sequence of notes and directives to be performed by the voice. Sequences for separate voices are performed simultaneously, so you can generate harmonies and rhythms.
This command plays a simple melody using a piano-like sound:
PLAY "CEG"
These are the notes C, E, and G, played in sequence with a single voice. To play them simultaneously as a chord, we give one note to each voice in separate strings:
PLAY "C","E","G"
The directives and notes in a PLAY string are evaluated in order from left to right. A directive affects all subsequent notes played by the voice, until another directive changes the effect. For example, the O directive sets the octave with a number in the range 0-6:
PLAY "O3 CDEFGAB O4 CDEFGAB O5 C"
PLAY will ignore spaces in the string, so you can organize the symbols for clarity.
Each note is a letter in the diatonic C major scale: A, B, C, D, E, F, or G. The letter can be preceded by modifiers: the # symbol to make it sharp or the $ symbol to make it flat. For example, $B is B-flat. A note can also be the letter R to specify a rest.
PLAY "O4 RGG $BRRRGG $BRRRG$B O5 $ERDRCR CR O4 $B"
Duration and tempo
A note is a quarter note by default. You can precede it with a letter to set its length: W for whole note, H for half note, Q for quarter note, I for eighth note, and S for sixteenth note. Any of these followed by a dot (.) extends the duration by half: a dotted quarter note is the same duration as three eighth notes. For example, S#F is an F-sharp sixteenth note.
The note duration persists for the voice like a directive, so you only need to specify it once in a run of notes with similar durations.
PLAY "O4 S#F#G#F#G#F"
You can adjust the tempo for the entire song using the TEMPO command. Its sole parameter is a number in the range 1-255, where larger values are faster. You can think of this value in terms of beats per minute divided by ten: TEMPO 12 sets the tempo at 120 bpm. (The User’s Guide defines this value as the denominator in a fraction of seconds for a whole note, which breaks my brain.)
TEMPO 12
Instruments and envelopes
So far we've been playing a piano-like instrument. This is defined by a combination of the SID waveform type and an "envelope" that describes the volume curve for each note. The envelope consists of four segments, during which the volume increases (the attack), abates (the decay), holds (the sustain), then fades out (the release). This is what differentiates instrumental notes from the solid tones we produced with the SOUND command.
BASIC 65 starts with 10 default instrument envelopes, numbered from 0 to 9: piano, accordion, calliope, drum, flute, guitar, harpsichord, organ, trumpet, and xylophone. To change the instrument being used, issue the T directive followed by the number.
PLAY "T6 O4CECGCO5C" PLAY "T8 O4CECGCO5C" PLAY "T9 O4CECGCO5C"
Some of the instruments are more convincing than others, and may require some experimentation to fit your song.
You can overwrite any of the ten instrument slots with your own definition. See the ENVELOPE command in the User's Guide for more information.
Looping, waiting, and silencing
The L directive tells the PLAY command to loop to the beginning of the string. This is great for background music that needs to play indefinitely. Remember to loop all voices for a piece of music in the same place—or loop them in different places for interesting effects! For instance, you can have a short loop for drums and a long loop for verses and choruses.
Issuing a new PLAY command with a new sequence for a voice that's currently playing will stop the current sequence and start the new one. If you want a voice to continue playing while you issue a new sequence to a different voice, leave an empty parameter.
PLAY "CEGBGEL" :rem a loop for voice 1 PLAY ,"EDCDEGE" :rem a sequence for voice 2, does not interrupt voice 1
If you want your program to wait until a PLAY sequence has completed, use the RPLAY(n) function to test whether a voice is currently sounding. It takes the voice number as a parameter, and returns 1 if that voice is making sound, or 0 otherwise.
10 PLAY "O4CECGCO5C" 20 BORDER 0 30 IF RPLAY(1) THEN 30 40 BORDER 6
To silence all voices, issue the PLAY command without parameters. You will probably use this often when debugging games with background music, as the music will keep playing after the program halts with an error.
PLAY
Learning more about PLAY
When you first turned on your MEGA65, it loaded the demo disk menu with some jaunty background music. This menu is a BASIC program, and the music is produced by the PLAY command. Run the menu, exit to BASIC, then LIST the program to find the PLAY statements.
The User's Guide describes a few more features of the PLAY command not covered here. The PLAY command can actuate the ring modulation feature of the SID, an effect that involves combining multiple voices in a complex way, so you'll want to read more about that. PLAY can also enable and disable the filter on the SID; filter parameters can be set with the FILTER command.
Remember that PLAY sequences are BASIC strings. This means you can assemble and manipulate these strings as values before passing them as arguments to PLAY. This also means they are constrained by the BASIC limit of 255 characters per string. If you run out of string, you might consider breaking your composition into parts, and using RPLAY() to wait for each part to finish before PLAYing the next part.
The best way to learn is to experiment! Remember that you can always reset the computer and try again.
What's PAL got to do with it?
Earlier, we said the SOUND command measures duration differently depending on which video mode your MEGA65 is in: PAL or NTSC. Why would the video mode have anything to do with playing sound?
Programs that play music are sensitive to timing. They need to execute code with strict regularity to make sure the music keeps up with the beat. Commodore games and demos typically tie their playback loops to another part of the architecture that keeps strict time: the video chip. Using raster interrupts, they set up their code to trigger each time the video chip refreshes the screen. The screen refreshes an exact number of times per second, so this is a good way to advance to the next step in a music composition with strict regularity. It's like turning the cylinder of a music box at a steady pace.
There's just one problem with tying playback to the video refresh rate: the PAL and NTSC video standards used different rates! Commodore made different versions of their computers for the analog video standards in the countries where they were sold, PAL in Europe and NTSC in the United States and Japan. (In countries that used a third standard called SECAM, Commodore sold PAL computers with video adapters.) PAL refreshed the screen at 50 Hz, and NTSC refreshed at 60 Hz. In many cases, games and demos written for one video standard played too slowly, too quickly, or not at all on a computer built for the other standard. It’s possible to write a program that works similarly for both, but this requires additional effort, and few people had both kinds of computers to test with.
The MEGA65 outputs a modern digital video signal that supports multiple refresh rates. For compatibility with older software, the MEGA65 continues to honor PAL and NTSC as separate video modes, including their raster timings. Many modern digital displays will honor both modes, but some of us have older displays that get confused by one or the other. You may or may not remember setting your default video mode when you turned on your MEGA65 for the first time.
You can check which mode you're in, and change it, in the Freeze menu: hold Restore for a second then release, then locate the Video setting (PAL50 or NTSC60). Press the V key to switch between them. If a game or demo isn't running correctly for you and your display supports both modes, try switching modes and running the program again.
PAL vs. NTSC survey!
Can the display you use with your MEGA65 support both PAL and NTSC video modes, or just one or the other? Developers want to know!
MEGA65 musician and demo coder deathy has set up a one-question survey that asks which video modes your set-up supports. Please take a moment to answer it. It'll be a big help to MEGA65 game and demo developers!
Xanadu
Syntax 2022, the 16th annual demo party in Melbourne, Australia, took place this month, and the MEGA65 was there! deathy and MEGA65 contributor Gurce, both members of the BAS demo group, produced “Xanadu,” a two-part tribute to the late Olivia Newton-John.
Part one is written in assembly language and features an arrangement of the Newton-John classic using all four SID chips. Part two offers another take, this time rendered entirely in BASIC with the PLAY command.
Download the D81 disk image and run it on your MEGA65 today!
There's plenty more to say about music on the MEGA65, but it'll have to wait for future issues. Until next time, thanks for listening!
This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit m65digest.substack.com
Personal computers in the early 1980s ignited the home video gaming industry with their ability to display animated color graphics. Even so, the games with the best images were the ones with no graphics at all. Text adventure games told tales of fantasy, mystery, and suspense with you as the protagonist, rendering entire worlds using only the written word and your imagination.
The effectiveness of the medium and the limited hardware requirements have made text games the longest living and most widely supported form. Archivists, hobbyists, and game authors have reverse engineered and ported vintage text games to modern computers, and evolved the form into the modern day. Thanks to members of our community who have continued that work, you can play a vast legacy of interactive fiction—everything from classic games to new releases—on your MEGA65.
Hibernated 1: Director’s Cut
A Cold Dark Place You wake up. A feeling of nausea grips you as you slowly regain control of your senses. You should open your eyes.
open eyes Blurred shapes slowly morph into a clear picture. It takes a moment for you to find your way back to reality. Has the Polaris-7 reached its destination? Still a bit shaky on your legs you climb out of the hypersleep tube.
Hibernation Chamber The room is lit by a gentle blue light. A glance through the porthole reveals nothing but the endless vastness of the Centaurus constellation. Proxima Centauri must be very close now. But there is no planetary orbit in sight. It seems an incident has interrupted your journey to Proxima C1. The only exit is starboard.
The status monitor of the hypersleep tube flashes intrusively.
examine monitor If this information is correct, then you have spent nearly 20 years in hypersleep. During this time a distance of about 4.1 light-years was covered. Proxima Centauri is 4.24 light-years away from Sol, which confirms the assumption that you haven't reached the target system yet. What's going on here?
In Hibernated 1 by Stefan Vogt, you play Olivia Lund, a space-faring explorer whose long voyage to Alpha Centauri is interrupted by a mysterious alien vessel. With your artificial intelligence companion Io, you must board the vessel and learn its secrets to break free of its tractor beam.
To play a text adventure game, you describe the actions of your character in abbreviated English commands. The story proceeds with each action, so you can take your time to read descriptions for clues and work out solutions to puzzles. In Hibernated 1, you navigate around spaceships (yours and theirs) with commands like starboard, port, fore, and aft. You can also examine objects to investigate your surroundings.
starboard
Lower Corridor Flashing red warning lights are reflected on the palladium glass. From here, you're able to reach every section of the ship. The hibernation chamber is port, while your private area is starboard. The docking bay lies aft.
The passage fore, leading to the Polaris-7 command unit, is barred by a massive security door. It divides the narrow corridor into two halves.
examine lights Looks like we have a problem.
Some objects can be taken, carried, and used at various points in the story. Others are fixed in place, and you must remember where they are and come back to them when needed. When playing text adventures, it is critical to take notes and draw maps in a paper notebook so you don’t lose your way.
aft
Docking Bay You stand in a desolate hall. This is the section where raw materials and samples of foreign origin must be stored, according to the Terran Alliance directive. Fore you get back to the corridor. An airlock aft allows you to leave the Polaris-7.
A laser rifle is leaning against the wall.
You also notice your spacesuit.
take rifle Taken.
take spacesuit Taken.
wear spacesuit You are now wearing the spacesuit.
Stefan originally released Hibernated 1 for the Commodore 64 and several other systems in 2018. In 2021, he rewrote the game with a revised story and puzzles, and published the “Director’s Cut” for a whopping twenty-eight (28) microcomputer platforms. You can even play it on a modern computer. Hibernated 1: Director’s Cut is available for the MEGA65, with an 80-column text display and the ability to save your progress to disk.
You can download Hibernated 1 from 8bitgames.itch.io. The download is free of charge; donations to the developer are welcome.
For an authentic vintage software experience, you can purchase a physical boxed edition of Hibernated 1 for the MEGA65 from publisher poly.play. The physical edition comes on a 3-1/2" floppy disk that you can use with your MEGA65’s floppy drive, in a gorgeous display box designed to resemble the games we used to buy in computer stores. And just like Infocom games back in the day, the box is packed with extras.
The physical edition of Hibernated 1: Director’s Cut qualifies as the first commercial title for the MEGA65. If boxed software appeals to you, you might also be interested in the second qualifying title, Showdown by Badger Punch Games, a two-player action game for the MEGA65 available in a display box from poly.play and as a download from badgerpunch.itch.io.
What is the Z-machine?
The most famous name in text adventures is Infocom, an American software company founded in 1979. Their most famous game was Zork, a treasure hunting adventure that begins in a boarded up cottage in the woods. Infocom published 36 original text adventures over ten years, including several Zork sequels and an adaptation of The Hitchhiker’s Guide to the Galaxy by Douglas Adams.
Key to Infocom’s early success was the ability to publish versions of their games for multiple microcomputer platforms simultaneously. All games were written in a custom programming language that ran on a common engine known as the Z-machine (Z for Zork). Infocom made versions of the Z-machine for every major microcomputer from the Apple II to the Amiga. This effectively erased the difference between each type of computer as far as the game’s Z-machine code was concerned: the story could be written once, compiled to Z-code, then interpreted by each platform’s Z-machine to play the game. When a new kind of computer came to market, Infocom simply built a new Z-machine for it, then re-released their entire catalog.
The longevity of this strategy continues to this day, over 40 years later. After Infocom ceased operations, hobbyists reverse engineered and documented the Z-machine standard. It’s a fun coding exercise to build your own Z-machine that can run Infocom story files, and implementations exist for nearly every modern computer. This is how Stefan released Hibernated 1: Director’s Cut for twenty-eight computers simultaneously: it’s a game written for Z-machines.
The Z-machine is a popular target for modern authors thanks to its ubiquity. The annual Interactive Fiction Competition gets new entries in this format every year, and you can find plenty more via the Interactive Fiction Database. Be sure to look for game files with names with endings such as .z3, .z5 or .z8. (There are several other formats used for interactive fiction, including Glulx, TADS, Twine, and more.)
Ozmoo
Ozmoo is a Z-machine player by Johan Berntsson and Fredrik Ramsberg for the MEGA65, Commodore 64, Commodore 128, and Commodore Plus/4. It’s a modern take on the idea, capable of taking advantage of each machine’s capabilities to their fullest extent, especially when extra memory is available. Ozmoo can run pretty much any game written for the Z-machine, including nearly all Infocom games and most modern Z-machine games.
Infocom experimented with sound effects and still graphics for a few of their games. Ozmoo supports the ones with sound effects, but not the ones with graphics (“v6” files).
The best way to get Ozmoo games for your MEGA65 is with Ozmoo Online, a web-based utility that can convert any compatible Z-machine game file into a playable D81 disk image. Ozmoo is free and open source, and you can learn more about it from the Ozmoo Github repository.
Fredrik, known as fredrikr on the Discord, has done a ton of great work to make interactive fiction available to the MEGA65 community. For starters, Ozmoo Online has every classic Infocom game available for download. Simply select one of the provided story files from the menu instead of uploading one.
Be sure to select MEGA65 as the platform, and “81” as the build mode. I also recommend enabling the scrollback buffer, a handy feature of Ozmoo that works well with the MEGA65’s “AtticRAM” extended memory.
Fredrik has packaged the two Infocom games with sound files as a separate MEGA65 download. You can find it on Filehost:
If you’re playing Infocom games, you’ll also want the original game manuals. You can download PDF scans of these from infodoc.plover.net. Some of the manuals contain information required to finish the games.
The interactive fiction community has been developing new Z-machine games for decades. Fredrik has bundled a selection of the best of these, using Ozmoo for the MEGA65. These are also on Filehost:
The MEGA65 Z-code Text Adventure Collection Pack #1
The MEGA65 Z-code Text Adventure Collection Pack #2
Game Book Collection
Need more? Try these text adventures written by MEGA65 owners, including two by Fredrik himself:
The Job, by fredrikr
Vacation Gone Awry, by fredrikr
Arthur’s Day Out, by WauloK
Brotquest 2, by EgonOlson71
Writing a Z-machine game
Infocom used their own custom language called “ZIL” to write their games. Contemporary authors use a family of languages called Inform, specifically Inform 6 and Inform 7. A newer language called Dialog is also worth a look, especially when writing games for microcomputers.
Inform 6
Inform 6 is a programming language for writing Infocom-style adventure games for the Z-machine. Typically, you use it with the Inform 6 standard library, which implements a wide range of useful object properties (doors, keys, containers, food) and player verbs (open, unlock, put in, eat). The Inform 6 compiler is a command-line tool that takes an Inform 6 source file and produces a Z-machine game file.
Here’s a tiny excerpt of how a bit of the Docking Bay in Hibernated 1 might be implemented. (This is not the actual code. Among many things, I’ve omitted code that replaces “north” and “south” with shipboard directions “fore” and “aft.”)
Object docking_bay "Docking Bay" with description "You stand in a desolate hall. This is the section where raw materials and samples of foreign origin must be stored, according to the Terran Alliance directive. Fore you get back to the corridor. An airlock aft allows you to leave the Polaris-7." n_to lower_corridor, s_to airlock, has light;
Object rifle "laser rifle" docking_bay with name 'rifle' 'laser' 'gun', initial "A laser rifle is leaning against the wall.", description "The rifle supports two modes: paralyze and disintegrate.";
Even though Inform 6 is intended to build to the Z-machine format, its standard library was written with modern computers in mind, and tends to produce game files too large to run on vintage microcomputers. Ozmoo co-author Johan Berntsson produced PunyInform, a replacement for the Inform 6 standard library optimized for low memory computers like the Commodore 64. You use PunyInform with the Inform 6 compiler.
macOS and Linux users can download the Inform 6 source bundle with everything you need, including the Inform 6 compiler, standard library, and PunyInform. The Interactive Fiction Archive also has pre-built versions of Inform 6 for many platforms, library add-ons, and plenty of example code. I recommend using Visual Studio Code to write Inform 6 games: there are extensions available that allow you to compile and play your game directly in the editor.
The definitive book about the Inform 6 language is The Inform Designer’s Manual by Inform’s creator Graham Nelson. It’s exceptionally well written and includes general information about interactive fiction and game design, as well as a tour of the language and its library. I consider it a classic text, and recommend it to anyone with even a casual interest in the subject. Read it online, or buy it in hardcover or paperback.
Inform 7
Inform 7 is a newer language and authoring system for narrative game writing. It’s a radical departure from Inform 6, using a logical programming paradigm and a natural language syntax, so source code reads like English prose. Inform 7 also includes a friendly authoring application where you can write an Inform 7 game, test it, read high quality documentation, and even develop automated tests that put your game through its paces.
Here’s how that Docking Bay example would be written in Inform 7 (again, not the actual code):
Docking Bay is a room. "You stand in a desolate hall. This is the section where raw materials and samples of foreign origin must be stored, according to the Terran Alliance directive. Fore you get back to the corridor. An airlock aft allows you to leave the Polaris-7."
The Docking Bay is south of the Lower Corridor. The Docking Bay is north of the Airlock.
A laser rifle is in the Docking Bay. Understand "rifle" and "laser" and "gun" as the laser rifle. "A laser rifle is leaning against the wall." The description of the laser rifle is "The rifle supports two modes: paralyze and disintegrate."
Inform 7 is designed to be accessible to authors without a computer programming background, but it doesn’t skimp on computational power or expressiveness. Some programmers prefer Inform 6 because it looks more like a programming language. Most new language innovation is happening with Inform 7.
The only reason I didn’t give Inform 7 top billing in this section is that games made with Inform 7 tend to run slowly on microcomputers. The MEGA65 version of Ozmoo can handle simple Inform 7 games with the “Inform 7 XL Stack” option enabled in Ozmoo Online, but they tend to be too slow to play.
For making Z-machine games for the MEGA65, stick with Inform 6. If you also want your game to run on smaller machines like the Commodore 64, use PunyInform instead of the Inform standard library.
Dialog
Dialog is another modern take on writing interactive fiction by Linus Åkesson. It borrows ideas from Inform 7 and the Prolog programming language, with an emphasis on minimalism, clarity, and performance. Dialog brings some of the best language design ideas to games that can be played on vintage microcomputers with Z-machine players. Like Inform, Dialog includes a rich standard library that makes starting new stories easy.
Here’s my novice attempt at describing the Docking Bay in Dialog:
(room ) (name ) Docking Bay (look *) You stand in a desolate hall. This is the section where raw materials and samples of foreign origin must be stored, according to the Terran Alliance directive. Fore you get back to the corridor. An airlock aft allows you to leave the Polaris-7. (from * go #north to #lower-corridor) (from * go #south to #airlock)
(item ) (name ) laser rifle (descr ) The rifle supports two modes: paralyze and disintegrate. (appearance ) A laser rifle is leaning against the wall. (dict ) rifle laser gun ( is #in #docking-bay)
Dialog includes a command-line compiler and a handy interactive debugger. The software bundle includes pre-built tools for Windows and Linux, and it’s easy to build them from source on macOS. Linus also has his own alternative to the Z-machine, which he calls the Å-machine, optimized for converting Dialog games to playable web pages and Commodore 64 disk images.
Adventures in BASIC
I’ve always been interested in how computers can be used to tell stories. One of my favorite books as a kid was Creating Adventure Games on Your Computer by Tim Hartnell (1983, Ballantine Books). In fewer than 200 pages, Hartnell covers game design, BASIC coding techniques, and several fully playable adventure games that you can type in, play, and modify.
I love the title of the kid’s book Creating a Database Adventure Game by Steve Rodgers and Marcus Milton (1985, Aladdin Books). It’s a genuine insight that adventure games are like explorable databases: tables of values that describe the state of the world, and long strings of text that become the game’s narrative based on the player’s actions.
Write Your Own Adventure Programs by Jenny Tyler and Les Howarth (1983, Usborne Publishing) is another, with excellent visualizations of how BASIC programs are structured. Both of these books use a grid structure to represent the game world, with each cell a location with potential exits in the four cardinal directions (north, east, south, west).
Compute!’s Guide to Adventure Games by Gary McGath (1984, Compute! Books) only dedicates a few chapters at the end to authorship, but it includes useful advice on structured programming and a short type-in. Most of the book is a catalog of the state of adventure games of the era, summarizing their stories and reviewing their features. Infocom gets its own chapter.
Of all the books on BASIC that came out of the 1980s, the ones about adventure games were some of the most informative and inspirational. They described how to organize large programs, how to design and implement data structures beyond simple variables, and how to build a game engine as much as a game. And they showed how evocative text games can be, even with no graphics, simple command parsers, and short text descriptions. You can read these vintage BASIC books and others on Archive.org.
I was going to write up a bunch of tips for coding adventure games in MEGA65 BASIC for this Digest, but we’re running a little long. Instead, here’s a brief example of a technique for representing a world map that’s common to most vintage BASIC books on the subject: the travel table.
Let’s say our game takes place on the Polaris 7, Olivia’s ship from Hibernated 1. At the beginning of the game, Olivia can access five rooms: the Hibernation Chamber, the Lower Corridor, the Private Area, the Docking Bay, and the Airlock. Olivia can navigate these rooms by traveling fore, aft, starboard, or port, according to the entrances and exits to each room.
For this example, we’ll go with the simplest possible command parser. We’ll use an INPUT statement to accept the command, and compare the value against the four directional commands. If a command is recognized, it is converted into a number from 0 to 3 in the C variable, and processing continues on line 380. Otherwise it is rejected with a message, and control loops back to the INPUT statement.
100 DIM CM$(4):FOR I=0 TO 3:READ CM$(I):NEXT 110 DATA FORE,AFT,STARBOARD,PORT
300 INPUT C$ 310 PRINT 320 C=-1:FOR I=0 TO 3 330 IF C$=CM$(I) THEN C=I 340 NEXT I 350 IF C<>-1 THEN 380 360 PRINT "I DON'T UNDERSTAND." 370 GOTO 300
The travel table is a two-dimensional array, with one row for each room, and one column for each movement direction. The value of a cell represents the exit for that room in that direction. If the value is a room number, that is the destination of the exit. If the value is 0, then there is no exit in that direction. We reserve room #0 for this purpose, so room numbering starts at #1. For example, the Docking Bay has an exit fore to the Lower Corridor (room #2). Fore is direction 0, so the Docking Bay’s zero-th column in the travel table is 2.
We initialize the TT(room, direction) array by reading the map data from DATA statements. For convenience, we put the room names on the same lines in the DATA statements and read them into the RO$(room) array.
120 DIM TT(6,4):DIM RO$(6) 130 FOR I=1 TO 5 140 FOR J=0 TO 3:READ TT(I,J):NEXT J 150 READ RO$(I) 160 NEXT I 180 DATA 0,0,2,0,"HIBERNATION CHAMBER" 190 DATA 0,4,3,1,"LOWER CORRIDOR" 200 DATA 0,0,0,2,"PRIVATE AREA" 210 DATA 2,5,0,0,"DOCKING BAY" 220 DATA 4,0,0,0,"AIRLOCK"
Olivia begins the game in the Hibernation Chamber, room 1. The PL variable stores her location. At the top of the game loop, we tell the player where they are, and which directions from the current room have exits—that is, are non-zero in the travel table.
230 PL=1 240 PRINT CHR$(13)+"YOU ARE IN THE "+RO$(PL)+"." 250 PRINT "EXITS: "; 260 FOR I=0 TO 3 270 IF TT(PL,I)<>0 THEN PRINT CM$(I)+" "; 280 NEXT I 290 PRINT
Now we can finish processing the command. With a valid direction command in the C variable, we can determine whether there is an exit from the player’s current location in that direction by consulting the travel table. If not, we inform the player. Otherwise, we move the player to that location, then return to the top of the game loop.
380 IF TT(PL,C)<>0 THEN 410 390 PRINT "YOU CAN'T GO THAT WAY." 400 GOTO 240 410 PL=TT(PL,C) 420 GOTO 240
That’s a complete albeit not very interesting game. It might be more interesting if there’s an object that the player can find to open the security door fore of the Lower Corridor. We might update the room description routine at the top of the game loop to announce whether the object is in the player’s current location. It might also describe the security door when the player is in the Lower Corridor, and whether it is open or closed. A new command could allow the player to pick up the object, when appropriate. When the player opens the security door, the game logic would update the travel table to allow passage between the Lower Corridor and what lies beyond.
50 Years of Text Games, by Aaron Reed
This isn’t MEGA65 specific but I can’t close this out without recommending 50 Years of Text Games by Aaron Reed, a historical deep dive on the genre and its many unusual twisty passages. Well researched and compellingly written, Reed originally published this as a serial newsletter, then crowdfunded a book version. The project raised over $640,000.
The Kickstarter has ended, but there is a limited number of extra print copies available for pre-order. If you’re still reading this Digest, you’ll love Aaron’s book.
The MEGA65 might be my favorite text adventure gaming platform. With the distraction-free 80-column display and quality mechanical keyboard, I can shut off the Internet and immerse myself in an interactive story. And the large amount of memory, fast disk access, and ability to store a giant library on an SD card mean I can play modern text games without the limitations of other microcomptuers.
I hope you give at least a few of these games a try, and maybe write one of your own. Happy adventuring!
This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit m65digest.substack.com
First off, thank you all so much for the outpouring of support for this Digest idea. I didn't expect to see so many of you subscribe to a newsletter sight unseen. I really hope it'll be an enjoyable use of your attention.
And boy did we time this right, because we get to discuss big news in the first issue!
Batch #2 is shipping soon
MEGA's assembly and distribution partner Trenz Electronic has parts in hand for the next batch of 400 computers, and is scheduled to assemble and deliver them by the end of the 2022 calendar year. Soon there will be 800 MEGA65s in the hands of new owners, not counting the 100 Dev Kits.
All batch #2 machines will ship with updated factory-installed firmware and system software, including fixes and improvements made since batch #1 shipped back in May. Batch #2 will include the printed User’s Guide from the first print run. If you receive a batch #2 machine, you'll want to download the most recent PDF as a supplement. I'll try to keep the Welcome Guide up to date as well.
Trenz is planning these fulfillment batches based on the availability of parts, especially the Xilinx FPGA chips. The MEGA65 team has done a great job mitigating risk in the supply chain, with unusual pieces like the injection molded cases and floppy disk drives well stocked. (We were all surprised when the first batch was delayed due to a shortage of cardboard, of all things.) We can all look forward to continued fulfillment of pre-orders through next year.
Help with testing for batch #2
The MEGA65 community made a huge push to test the factory-installed firmware and system software for batch #1. We're doing it again for the batch #2 release candidate—and you can help!
The closer you can get your MEGA65 to the final configuration, the better for testing. If you own a MEGA65, a Dev Kit, or a Nexys FPGA board, jump in the release-test forum on the Discord. Check out the release candidate verification home page with links to downloads and instructions for testing and reporting bugs. You can install the latest core in slot 1 of the core management utility, and prepare a new internal SD card with the latest system software and bundled disk images.
If you're one of the lucky ones to have both a batch #1 MEGA65 and a JTAG programming interface, you can install the candidate core in the special "slot 0," which is how batch #2 machines will be configured. Overwriting slot 0 requires opening your case to flip a tiny switch, as well as running some fancy commands. We need slot 0 testers, so if this sounds like you, please give it a try. See the wiki page for instructions.
If you don’t yet have a MEGA65, you can still help test the ROM using the Xemu emulator. You’ll need to use the C65 ROM diff files along with a copy of the original Commodore 65 ROM version 910828 (which you can find online or on the C64 Forever CD-ROM) to produce the mega65.rom file.
Thanks to everyone who can help out!
What's the latest firmware, anyway?
The core team and contributors have been improving the MEGA65 firmware and system software (including the MEGA65 version of the Commodore 65 ROM code) continuously since batch #1 shipped. So what's considered the "latest" version, and should you upgrade?
There are two major release packages for the system software: the stable release package, and the experimental release package.
The stable release package contains the most recent set of software that has been through a testing phase, which today is the factory-installed set from batch #1. It includes MEGA65 core version 20220109.11,1586ad4 and the MEGA65 ROM version 920287. When we've completed verification of the batch #2 software set, that set will become the new stable release. You can download this from the MEGA65 R3 Release Package page on Filehost. (You'll need a Filehost account registered with your owner code to access it, because it contains the licensed ROM.) Stable releases have their own version number to represent the entire set of software, with the current release known as version 0.9.
The experimental release package is the bleeding edge, built regularly from the latest changes committed to the source repo. As of this writing, the experimental package download contains core version 20220717.12-develo-3253c5d. Contributors do their best to keep everything in working order when they submit changes. For additional assurance that changes maintain backwards compatibility, they are held in this "experimental" state while we test them and put them through their paces. You can download the experimental release core and system software from the Experimental Release MEGA65 R3 page on Filehost.
Unlike the stable package, the experimental release package does not contain the ROM. As of now, the latest ROM is 920377, and is available from the C65/MEGA65 Kernal ROM page on Filehost.
The batch #2 verification build has its own download page and may be slightly newer than the experimental release during the test phase.
Which release should I use?
Which release you choose to install on your own MEGA65 is a matter of taste. Everyone is encouraged to install the latest stable release. For batch #1 owners, this just means that we'll throw an upgrade party when batch #2 is finalized. If you don't want to hassle with frequent upgrades of experimental releases, and the occasional downgrade when issues are discovered, stick with the stable release and watch for announcements. If you'd like to preview new features, get the latest bug fixes, and help test new contributions, consider installing the latest experimental release. I've had a good time keeping the latest experimental release on my MEGA65 all year, though I've also been paying close attention to bug reports.
One of the coolest things about the MEGA65 is that it's easy to switch between multiple versions of these components. You can have multiple cores in flash memory, you can have multiple ROMs on the SD card, and you can easily switch between them when you boot the machine. This is not quite true for the auxiliary system software in those .m65 files on the SD card. If you find yourself switching between stable releases, you might want more than one SD card prepared with the appropriate files. The differences in the files between experimental releases tend to be minor enough to just use one SD card.
As of right now, my personal recommendation is for batch #1 owners to proceed with upgrading to the experimental or batch #2 verification release set. Since batch #1 shipped, newer releases have fixed noticeable bugs and improved the overall quality of life for MEGA65 owners. You'll also be helping with the verification effort by upgrading now. When the batch #2 testing phase completes, that'll be a good time to install the new stable release as your default core (in slot 1) and default ROM (with filename mega65.rom).
As anyone who has done software release management professionally can tell you, release verification is an expensive process. We’re gradually building out automated testing solutions to reduce the time between stable releases, though this is challenging when custom hardware is involved. I'm grateful to everyone who tests experimental releases. You make continuous improvement possible!
For instructions on how to upgrade your MEGA65's firmware and system software, see the User's Guide included with the machine, as well as the Welcome Guide.
Upcoming type-in zine, call for submissions
ZeHa of Dr. Wuro Industries (known as ZeHa on the forums, drwuro on Discord) is producing Megazine, a printed zine of type-in BASIC programs for the MEGA65, and is calling for submissions. If you've always wanted to see one of your programs featured in magazines like Compute!'s Gazette, now is your big chance!
Megazine is looking for BASIC programs with a focus on fun, such as games, animations, or demos. Keep it short, and keep it readable enough for beginner programmers to understand. (I remember doing a type-in as a kid and encountering the word FORK in the program and wondering what it did. It took me a moment to realize it was just a FOR statement and a variable named K, with the space removed.) Also please only submit unreleased programs.
When the zine was originally announced, the submission deadline was set at September 30. This will probably be extended, though a new date has not yet been determined as of this writing. Check out the original forum announcement for submission details and updates.
Character graphics made easy
One of the easiest ways to get started with programming games on the MEGA65 is with character graphics. Your BASIC program plots colored letters and symbols on the screen, checks whether the joystick or fire button is pushed, and decides on new positions for player, enemy, and environment objects. Do that in a loop and you've got a game.
Just like with the Commodore 64, you can replace the glyphs used for each letter or symbol with your own custom designs. And starting with ROM version 920347, MEGA65 BASIC makes this super easy!
The CHARDEF command takes the character screen code whose glyph you want to replace, and eight numbers that describe the new 8x8 pixel image, one number for each row. Try this at the READY. prompt:
CHARDEF 0,$0C,$1C,$18,$FF,$18,$3C,$66,$C3
If you get a SYNTAX ERROR, double-check that you're using the latest version of the ROM. (ROM 920347 came after the batch #1 v0.9 release.)
Now type the "at" symbol (@) a few times.
The first 0 is the screen code for the "at" symbol. Screen codes 1 through 26 are the letters A through Z. See this table for all the screen codes. CHARDEF supports the first 256 screen codes in the uppercase PETSCII mode. (This will be updated to support all 512 codes, uppercase and lowercase PETSCII, in an upcoming release of the ROM.)
Each of the numbers following the screen code describes a row of the character glyph.
MEGA65 BASIC's built-in support for hexadecimal notation comes in really handy here, because each hexadecimal digit describes exactly four pixels.
Many of the games I wrote as a kid featured this classic "brick wall" glyph. Look at the diagram below. What hexadecimal numbers describe this pattern? What CHARDEF command would change the letter A to appear as a brick wall?
CHARDEF __, $__, $__, $__, $__, $__, $__, $__, $__
To restore the glyphs back to the MEGA65 PETSCII font, use the FONT command, with the letter C as an argument:
FONT C
There's also a FONT A, which replaces some lowercase glyphs with ASCII punctuation not available in PETSCII, and FONT B which resembles a DOS-like font. Try them! (Remember you can toggle between uppercase and lowercase modes with Mega+Shift.)
With CHARDEF in your BASIC programs, you're well on your way to making some fun games. Be sure to submit yours to Megazine!
That'll do it for this month. Best of luck to all the testers, and congrats to everyone about to receive a new MEGA65!
This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit m65digest.substack.com
The party was already in full swing when I got my MEGA65 back in March of 2022. I got mine as part of the first shipment of production units, the ones with the sweet injection molded cases, but 100 others already had the acrylic-encased Dev Kit beta units from the previous year. Many more were eagerly running the MEGA65 core on Nexys FPGA boards or testing the waters with the Xemu emulator. By the time batch #1 shipped, it included multiple disk images packed with demonstrations and games from an already thriving community.
A flurry of essential firmware and ROM updates followed the March delivery, and it was important to keep up with developments. I was lucky to have just started a sabbatical from my software job and had the time to read through daily discussions on the MEGA65 Discord. I knew other newly minted MEGA65 owners that didn't have that kind of time and were struggling to dig into their new machines. For them and others, I wrote the MEGA65 Welcome Guide, a supplement to the already excellent documentation that tried to synthesize information about recent changes and practical advice, stuff too temporary for the official manual but still useful to the first 400 owners.
And the new stuff keeps coming. I've been keeping track of all of the amazing things this growing community has been doing with the MEGA65 all year, from boxed retail games to music demos to tools and libraries to YouTube videos and tutorials. There was so much being posted, I built the @MEGA65Files Twitter bot as a way for people to be notified of new software and articles posted to the Filehost repository. As of this writing, the Filehost has nearly 200 downloadable files and over 60 articles.
By the end of 2022, there will be 800 MEGA65s in the wild—not counting the 100 Dev Kits—and the pace of development is likely to increase. With more people and more activity, it won’t be sufficient to assume that everyone is reading the Discord chat. I think it's time for a newsletter.
Whether you already have a MEGA65, have preordered one from a future batch, are jumping in with a Nexys board or the emulator, or are just curious about the MEGA65 project and its community, this Digest will keep you up to date. Firmware updates, shipping status, new software releases, game jams, tips and tricks, and maybe the occasional interview. Things you can do with your MEGA65 right now. It’ll be short, interactive, and free.
There are two things you can do to help:
Subscribe. You can get the Digest by email, RSS reader, or the Substack app.
Tell people about it. We want everyone who is interested in the project to know there's a newsletter, so they don't miss out.
Look for the first post in mid-September.
Thanks all! GO 65!
Thanks for reading Dan’s MEGA65 Digest! Subscribe for free to receive new posts.
This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit m65digest.substack.com