Category Archives: Toolbox

Sound works!

The past few weeks turned out rather interesting. After getting the basic Device Manager API written, we needed to have some simple test case to try it out – and as mentioned in the previous post, Sound Driver was an interesting potential use case. So that’s what we did…

The “Classic Mac” sound hardware

To get sound output abstracted in the emulator, we needed a bunch of stuff to support it, which includes:

  • Host platform abstraction for sound device: This allows us to create a SDL output buffer, and handle transfer to sound data from emulator to the host.
  • Hardware abstraction for sound: This separates “Classic Mac” type sound from future “Sound Manager” type generic audio output, and also ties the “Classic Mac” VIA to the audio output to control square wave generator, volume, alternative sound buffer, etc.
  • Vertical retrace simulation: More about this below
  • And also the Sound Driver DRVR

Vertical retrace interrupt simulation

One challenge in the old “Classic Mac” type computers is, that the sound output is deeply tied into the video signal generation, and especially the timings depend a lot on the frequency this generator works. In attempt to match this hardware as closely as possible, we added a secondary thread to simulate vertical retrace interrupts. As some might know, simulating hardware interrupts using threads is very tricky: On real hardware, interrupts are non-preemptive (except when allowed by the status register, as VBL interrupt handler does to keep Ticks low-mem variable in sync), but on multithreading system main thread could at any time pre-empt the interrupt thread.

To prevent this, we devised a locking scheme, which attempts to ensure that the interrupt service never gets cut off by main thread. At this moment it’s not a problem, as there is not yet actual 68K code running on main thread, but that we need to prepare for that in future, especially when VBL service routines written in 68K code might interrupt 68K code running on main thread!

In any way, the interrupt simulator currently reads the full sound buffer on each simulated VBL interrupt, and outputs the result through the hardware abstraction layer. The biggest challenge here at moment is keeping the SDL’s audio thread and VBL thread in sync, as the frequency of SDL sound output needs to match *exactly* the rate at which VBL thread is providing the sound data. It appears to be quite OK at the moment though, with occasional minor ticks here and there – currently the biggest source of breaks in audio appears to actually be the Xcode debug console, which seems to not be very thread-friendly…

Square-wave generator test

One neat feature of “Classic Mac” sound hardware is the built-in square-wave generator. Absent from later macs, this feature was very practical way of generating sound output without using almost any CPU resources. Controlled by the VIA, it was the first sound feature we implemented:

A simple square-wave sound test (sorry for low volume)

The Sound Driver

With sound hardware emulation in place, we could finally implement the Sound Driver. It took a day to decipher the old “.Sound” DRVR code, but after understanding how it works, we were able to whip up a C implementation which has equivalent functionality. In the process, we finalized an initial implementation for Vertical Retrace Manager.

Examining the original .Sound driver

After adding four-tone support, we needed a way to test this, so we wrote a test song by hard-coding it in C-language using set of frequencies as described in Inside Macintosh II-237 “Sound Driver” chapter, and used FTSynthRec to feed them to the virtual “.Sound” DRVR combined with a custom VBL task:

MacEmu four-tone synthesizer test

Device Manager

On the road to file system

The major motivation for working on Device Manager at this point was the goal of getting file system eventually up and running. As planned earlier, we will implement file system driver as a “virtual” DRVR resource, which we will mount the file system with, and using it requires naturally the Device Manager.

Interestingly, the Device Manager API overlaps with File Manager’s calls, which means that certain traps (Open/Write/Read/Close) can be called for both files using the FCB IDs (positive ioRefNums), or device drivers using the driver reference numbers (negative ioRefNum). Besides those four traps, Device Manager has a set of calls which only apply to device drivers, and File Manager has a lot of calls which are only valid for FCBs. At this point the goal is to implement the core I/O functions.

Other benefits

Besides the (planned future) loading of file system DRVR and file system support, there are a few other benefits of implementing the device Manager:

  • We can also implement the famous Sound Driver, which a lot of early Mac games used (in combination with direct sound hardware access)
  • At some point, implement the standard mac Printer/Modem serial ports (which can be mapped with the Platform abstraction to any character-based serial physical or virtual input/output devices)

68K Emulator integration

The CPU update

At this point, Pukka had been working hard to update the 68K emulator to work with the new environment. With 64-bit support, cross-platform standard C and POSIX compatibility as requirements, we had already discussed how to interface the emulator with the virtual memory system.

First integration test

To make a simple test case for the 68K emulator, we created a short 68K assembly program which we wrote manually to the memory using the C code below, and ran the CPU:

First successful 68K emulator integration test

Quite simple test, but it not only displayed that the 68K emulator worked, but also that our trap dispatcher was also working as intended! Which meant that the calls from 68K to native C code worked, arguments got translated correctly, return value got transferred to 68K stack and it just…worked!

…Which was a bit ironic, as soon after this we noticed that we forgot to allocate space on stack for the return value of Random trap (0x558F = SUBQ.L #2,A7). The MOVEA._L -1,A0 and MOVE.L A0,-(A7) was supposed to put return address for RTS but somehow it worked! I guess we got lucky this time…

Drawing PICTures

Now that the source transfer modes were added for drawing text, a logical next step was adding support for drawing the most basic bitmap-only PICT resources. For this purpose, we added a very simple test PICT resource into the Fake ROM resources, which we used to test this feature.

More than a bitmap

What makes the basic QuickDraw PICT format different from many other image formats is the fact, that it is actually not a pure bitmap container, but rather a recording of drawing commands, which can be played back at any time. Basically this allows majority of QuickDraw commands to be “recorded” into a picture, and later played back. Even better, these pictures can be “played” also on other devices besides screen, such as printers! This allows a picture with geometric shapes to have a rasterized on-screen representation, but appear as a sharp image on for example a laser printer. Furthermore, these pictures can be scaled during playback to a target rectangle, allowing them to adapt to different screen/device sizes as appropriate. Of course, all this makes displaying a simple scanned bitmap photo of a cat a bit more complex than one would expect.

A simple PICT image

Attempting to create the monochrome old-style PICT resource was a bit challenging on modern Macs, so we ended up transferring the photo on the reliable good old PowerBook G3, and exporting to resource from there using old version of PhotoShop.

Playback time!

So basically what we needed to display the cat picture, was just take the PICT resource and do the following:

  1. Read the picture size and picFrame from the header
  2. Save copy of content of current GrafPort
  3. Set up defaults for the GrafPort (including new clipping region)
  4. Initialize a playback state record
  5. Execute each picture opCode from picture data one by one
  6. Dispose playback state and restore original GrafPort state

Even in this simple format, the following opcodes were required for the test PICT: Version, LongComment, PackBitsRect, and EndOfPicture.

Type 1 picture opcodes encountered in the cat PICT

All other opcodes were trivial to implement, except PackBitsRect which ended up using UnpackBits trap, so we had good excuse for implementing it also at this point. With unpacking implemented, the drawing ended up with a simple call to StdBits, which was was already previously added for text drawing, and we got the cat quickly on the screen:

First appearance of the cat PICT

As clipping was already implemented, we were able to goof around by drawing the PICT in funky clipping region (with some text added for fun):

Cat PICT drawn masked by another, larger region, which was later framed with FrameRgn using different sizes and patterns to create gradient edge

More regions, and text clipping

Region recording

Previously, during the spring, we had added the basic region operations, which already allow us to do a bunch of cool stuff, but next up on the list was implementing region recording. This neat feature QuickDraw allows the program to open a new output region for “recording”, to which certain QuickDraw “FrameXXX” operations will output inversion points into. This includes StdLine, StdOval, StdRect, StdRgn, and RoundRects.

The article on MacGUI, which was mentioned earlier when discussing regions, also contained a neat screenshot of results of OpenRgn/CloseRgn operation with accompanying Pascal source code. We made adaption of this code for the C Toolbox testbed, so we could have a picture to compare to, to see that the results would match ones expected on a real Mac.

Again, first attempt rarely works…

At the same time, we were working on adding “source” transfer mode support to the region-clipping blitter, as transferring text to screen uses those to handle moving the text from offscreen buffer to the screen (previously we only implemented the non-clipped blitter). After getting this to work, there was also a nice benefit that non-scaled CopyBits can also be implemented easily soon, although only for cases where source and destination bitmaps are different (ScrollRect and window manager will use same source and destination, forcing inverted blitting direction depending on their locations).

Region recording works now, as does the text clipping

Styled text

A few days earlier, we got the non-styled direct-to-screen text drawing working, but we also need to support all the other cases. At this point, we want to have the clipping and styling support, but we’ll leave the scaling to some point in the future.

As styled text required an offscreen buffer for styling, we started by adding that. Interesting feature of QuickDraw FMSwapFont mapping is, that as any styles can be combined together, the FOut record contains a set of styling attributes which tells the text rendering how to adjust the final appearance of the font, such as bolding amount, how much slanted the italic style is, how deep the shadow is, etc.

Well, as usual, the first run didn’t go quite the way it was supposed to

After a few days to debugging, bug-fixing, more debugging, and even more bug-fixing, we go the text to render properly:

The final, correct styles

For reference, styles from PowerBook G3 running Mac OS 9. Note the difference in outlined/shadowed text.

An interesting discovery was, that it appears that “Classic”-type QuickDraw and Color QuickDraw handle the outline styles a bit differently. This may be due to the way outline/shadow styled text is combined during drawing, as the “inside” of outlined text is drawn over the shadow/outline using srcXor mode. Another interesting point is, that even Color QuickDraw falls back to the “old” type drawing if the GrafPort is old-type monochrome port (i.e. high-order bit of rowBytes is zero)

Even more combinations of styles and drawing modes!

Fonts and text drawing

Font Manager and the notorious FMSwapFont

As mentioned earlier, the next target is to get QuickDraw text rendering to work. For this purpose, we’ve been working on important part required by it, The Font Manager. The most important interface between QuickDraw and Font Manager is the FMSwapFont trap, which takes the FMInput record, and outputs an FMOutput record. On a real Mac, the font substitution has a bunch of special cases which we luckily don’t need to worry about; for example, we don’t need to worry about choosing right font to display the disk-switch alert. However, there are a number of substitutions we need to support:

  • Substitute non-present font to default font
  • Choosing nearest-size font strike to substitute a non-present (scaled) size of a font
  • FOND tables
  • Fallback to old FONT resources in case requested FOND does not exist (using the familiar family * 128 + size formula)
  • Etc.

Getting the strike loaded and on the screen

Majority of time spent for this milestone so far was investigating and implementing the quirks of the previously mentioned FMSwapFont trap. After finding the correct (or near-correct) strike, we finally got a FONT resource which we could work on. Of course, this meant that as first step dump the raw font bitmap on the screen buffer byte-by-byte 🙂

12 pixel “Chicago” font dumped on the screen. Tight fit, huh?

Of course getting the actual text to output, each of those characters would have to be individually masked and blitted to the correct location. For the first test, the original QuickDraw luckily appears to have “optimized” way to blitting non-clipped, non-scaled, non-styled srcOr-mode text directly to the screen, without having to go through offscreen buffer for styling and clipping. Implementing this was also pretty straightforward, resulting in the following tidy output using different fonts:

First successful text output, with various fonts in the fake ROM

Back from the summer break

Still alive and kicking!

It’s now about four months since the last update, but we’re back now from the holidays. Not much has happened during this time, the plan is to next add text drawing support, so foundation for Font Manager was laid briefly out, and Resource Manager is being reworked to allow calls to GetResource or ROM fonts soon.

Trap generator

The biggest change at this point in the recent days has been the work on automatic trap glue code generator. So far, all trap definitions have been added manually into the C headers and the glue code written by hand for each of them, but now we’re taking the bullet and actually implementing the generator responsible for creating those files.

For this task, we did careful consideration of a bunch of ways to handle generating this glue code, and ended up with using AWK scripts combined with basic unix shell script calling those AWK scripts. As all our Toolbox trap definitions are stored in a CSV file, which we generate from the master Excel file, the nature of AWK as line-by-line text processing tool fits pretty well.

C glue code generated by the trap generator
ProcInfo defines generated by the trap generator

At this point, we also tweaked further the argument formats, to make them better suited to the future MixedMode calling, such as converting arguments which were accidentally added as value-types into proper pointers (such as Pattern arguments in QuickDraw, etc)

Other geometric shapes

The “Atkinson” circle algorithm

After the groundwork done for rectangle drawing, the rest of geometric shapes were pretty easy to add. To make sure the results of drawing operations would look exactly like on a real Mac, the Midpoint circle algorithm was implemented as closely to what Atkinson used as possible

https://en.wikipedia.org/wiki/Midpoint_circle_algorithm

More information about the Midpoint (“Atkinson”) circle algorithm

The fun thing is, that a bunch of QuickDraw primitives actually share this method, so not only did this allow drawing Circles, it also enabled drawing ovals (basically stretched circles, thus this is why overly long framed ovals on Mac tend to have holes on their silhouette), and the infamous round rectangles. Also drawing arcs benefits from this, but we won’t be needing them at least for some time so we won’t touch them at this point.

Ovals, circles, and round rectangles drawn with random pen modes and with random patterns

Here’s also a neat video of round rectangle clipping:

Round rectangles getting clipped in the last week’s region test

Let there be regions

What are regions exactly?

Short answer: The most important part which made Mac user interface possible.

Long answer: The regions, in the form as they are used in the original Mac, were invented by Bill Atkinson as solution to solving the problem of creating effective algorithms for masking drawing commands with arbitrary bitmap shapes, and performing geometric logical operations with them. There is a good article on macGUI, which gives a better backstory and technical explanation about what regions are from the user’s (or programmer’s) point of view:

http://basalgangster.macgui.com/RetroMacComputing/The_Long_View/Entries/2010/8/29_Regions.html

Region operations simulated on paper, with inversion points indicated with dots (bottom-right region is not related to the other operations on the paper)

Regions in the QuickDraw

One interesting feature of regions is, that when a region is rectangular, it will always have fixed size (10 bytes), and its bounding box can be used as in rectangular clipping. This helps reducing sometimes drawing in complex regions to trivial rectangular blitting operations, which are much faster than ones requiring region masking for each scanline:

A simple rectangular clipping case, which does not require regions

However, the need for actual region operations comes up very quickly, when wanting to do anything that ends up being non-rectangular. For example, the test case we used at this point, was just to get union of two rectangles into region, and fill that with a solid pattern.

Region decompression and re-compression

In memory, regions are stored in sort of compressed format, with each scanline containing horizontal indices of the inversion points on that scanline. However, during operations, they need to be manipulated as points, with horizontal and vertical coordinate. To achieve this, the region is decompressed during the actual operations into a point buffer, where points from both source regions are added as needed by the actual operation. The resulting points from these operations are then sorted, and re-compressed into a new region. At this point, we do the sorting with slow bubble sort, which will be replaced by a more efficient Quicksort later.

Funny thing about InsetRgn is, that it takes a bit more complex way of performing the operation: For insetting, the region is actually getting inset twice, once for both axis, but the horizontal and vertical coordinates are flipped for the second inset, so the horizontal inset is actually ran *twice*.

Drawing the regions

After the complexity of geometric operations on regions, doing the actual drawing with them is pretty straightforward. The region image blitter is pretty similar to the rectangular blitter, but it takes up to three regions as arguments, which get expanded into horizontal scanline buffers during the drawing. Each of these buffers is updated on each vertical coordinate, and together they form a bitmask, which can be simply ANDed together and used to mask the transfer of source bits to the destination.

First attempt, not really working yet

Second attempt, little better but still fishy

After a few missteps, we finally got the region clipping to work nicely: