Subscribe to with your favorite RSS Reader
January 29, 2018#53 Software

Video Update on C64 OS, #1

Post Archive Icon

I've been plugging away in the new year, trying to get the booter booting properly, and the ability to launch an app from an application bundle. So here's a quick video update of my progress so far.

What this video shows

The booter has run, it loads in all of the OS modules. It runs a drive detection routine and constructs a drive type to device number table in workspace memory.

The booter sets itself up as a standard app with a "quit app" jump table entry. Then it calls service routines to quit itself. This actually puts a vector to the "loadhome" service routine into the main event loop's EventLoopBreakVector. Typically this will cause the main event loop to end, and will jump to the routine to load the designated "home" application. But because we're just booting, the main event loop is not yet actually running. The quit app routine returns the pointer to loadhome in the X and Y registers. I refer to these in C64 OS now as a RegPtr. That's a standard 16 bit pointer, low byte in X, high byte in Y. The booter takes that returned pointer and jumps to it manually. This kicks off the load home routine.

Load home starts by allocating a page to be the application file reference (AppFileRef) for the about-to-be-launched app. It allocates new space because it needs to still have access to the AppFileRef for the about-to-be-quit app. Load Home then configures this new file reference with the device, partition and path to the currently designated home app's bundle. It then calls the loadapp service routine, with a pointer to the newly configured AppFileRef. In this way, the home app gets loaded just like any other app. And any app can load any other app directly, without needing to go back to the home app first. However, if you want to just quit the current app, there is a service routine that configures loading the home app for you. So it's very easy to leave your app and have the system return to the default home app.

The very first thing the loadapp routine does, is quit's the currently running app. In order to do this it jumps to the application's standard quit routine. The booter configured itself to have a standard quit jump table entry specifically so that things would go smoothly when loadapp tries to quit the booter. The booter's own quit routine doesn't need to do anything so it just RTSs.

Typically the quit routine gives the application one last chance to clean up, before it is forcibly removed from memory. It might be that it closes network connections, or saves open files to disk, or saves its own state back to its application bundle. In fact, that's the reason for maintaining the current app's AppFileRef, so it can use it during the quit phase to write data back to its own bundle.

After the previous app has completed it's quit routine, the current AppFileRef is deallocated and AppFileRef is pointed to the configured file reference for the app to be loaded. Then all application-level allocated memory pages are deallocated. Then the new app's menu file is loaded in, and the new menus are initialized. The menu initialization process deallocates the previous application's menu allocations. Memory allocations for the menu system are done at the system-level. So your application never needs to worry about setting up or taking down its own menus.

Then the application's main.o file is loaded in. And the pages that it occupies get marked as allocated (application-level.) Next, just as every application has a standard quit jump table entry, every application also has an initialization jump table entry. Load app then jumps to the freshly loaded main.o's init routine. At this early stage, I've setup app launcher as the default home app, but file manager will be next. App launcher's init routine is called. Typically it would configure its Toolkit widget UI, but I'm not there yet. It allocates space for a PETSCII image, which it then populates from a file which it finds as a resource in its own app bundle.

Next the init method sets up a screen layer struct which it pushes onto the screen layer stack. This defines its mouse and keyboard event handling routines, and its draw routine. The draw routine doesn't do much, except call textblit to copy its background PETSCII image to the screen. The mouse event routine is not yet forwarding to the Toolkit, but is instead just doing some primitive checks for where the clicks are happening. Clicking in the first column changes the foreground/background colors. Clicking in the middle of the screen changes the colors back.

Clicking in the second column puts the app into an infinite loop. This lets us test how the interrupt routine handles the situation where the app fails to return to the main event loop for a prolonged period of time. It kicks into action the CPU Busy animation, top left corner. We can see that working. It is animated by the interrupt routine. But as long as the the main event loop keeps running in a timely fashion, and events keep getting distributed, it continually updates a variable that prevents that IRQ handler from animating the CPU busy indicator. Pretty cool.

Once the app goes into an infinite loop, the main event loop is no longer running, and so events are no longer being distributed by their normal means. The interrupt handler is still updating the mouse and keyboard, though, and it's still buffering input events. The infinite loop code is scanning the key command buffer. As soon as there is a key command in the buffer it breaks the loop.

Most of these things are just a way for me to test various low-level features of the system. And things seem pretty stable. I'm making progress.