Category Archives: Audio

5 year anniversary! And happy new year 2023

It feels crazy to think that today it’s been a whopping 5 years since the project got rebooted to its current iteration! Sorry again for slow progress between late summer and the Christmas holidays, a lot of things going on so we have had to juggle between different things and try to prioritise them. But past couple months have been again quite productive, and we have some nice updates to share:

File system improvements

One important feature that was added to NativeFS has been ability to create directories using DirCreate, something that not only installer applications, but also some apps generally depend on. It’s still not perfect (for example extracting the THINK Pascal installer archives made with SmallerInstaller causes the directories to get a bit haywire), but basic use cases such as creating Desktop & Trash folders seems to work.

Also, some groundwork has been laid to support deletion of files, as the AVL tree routines for node deletion were added – but actual deletion of file system items is still a work-in-progress task, which needs to be done carefully and is not to be rushed considering errors and bugs in this code might cause a bit headache to the end-users.

And also, when doing test runs on some Installer VISE installers, we found out that the NativeFS DRVR’s Control handler had used incorrect csCodes for the logical/physical drive icons (we were using 0x16 and 0x17, while correct ones were 0x15 and 0x16), this tiny mistake caused the whole installer to crash…oops! Another quite important bug fix was adding missing IODone call to the NativeFS DRVR’s Status handler, as that caused basically the entire Device Manager to hang…

List Manager multi-select

Not really a tough feature to implement, but one that has been a TODO for long time, list manager multi-selection has been finally implemented. A very good test case for this has been ResEdit, as it supports multi-selection both in the 2-dimensional icon grid, and in the 1-dimensional list view

List multi-select in action in ResEdit

SeedCFill & CalcCMask

Also as part of filling some missing traps, SeedCFill and CalcCMask were implemented. Like the aforementioned list multi-selection, ResEdit again was a wonderful test case for this, as the color editor PACK 1 in it uses these for the color flood-fill tool:

SeedCFill & CalcCMask in ResEdit’s PACK 1 color pixmap editor

Technically, the SeedCFill and CalcCMask actually call SeedFill and CalcMask, and to do that, they first map the color pixmap passed to the routine to a 1-bit image by using a custom color search proc to a temporary bitmap, which replaces matching colors with 0 and non-matching with 1 (i.e. doing floodfill on certain color gets flooded only to exactly matching pixels, while being bound by any other colors)

Application zone teardown updates & fixes

A bunch of fixes and missing features were added to the InitApplZone & SetApplBase, which makes doing transfer/shell launch to other apps way more reliable:

  • Font caches are flushed in SetApplBase and InitApplZone. Sometimes recently used font might be left cached in the application zone, and doing Launch/Chain to new app might accidentally use the cached (but not obliterated from memory) font, causing random crashes.
  • All color grafports, palettes, and VBL tasks located in application zone are disposed/closed

These are quite important fixes, as the aim is to provide soon the much-promised “generic” MACE runtime, which would contain an application shell (i.e. Finder replacement) to reliably run applications from

Communications Toolbox foundation

Weirdly, the Comm Toolbox has a bunch of very non-related routines included, which some applications seem to depend on. The first one we encountered was some Installer VISE installers using the CountDITL selector to get number of items in dialog item list, which the designers of the toolbox interestingly put as part of the Communications Toolbox. Also, the dispatcher mechanism they use is a very non-standard one (they basically pass parameter block pointer in A0 register, which contains the actual arguments of the routine – which might vary in size and number depending on the API call – and expecting result in D0 register). Although there’s just this one call implemented, the foundation for Comm Toolbox was done so that other routines can be added as needed later.

SoftFPU support

Even though Pekka is working hard on the 6888x FPU support, we did interesting experiment to see how well the SoftFPU control panel would work on MACE. Basically, that utility hooks as the F-line exception handler, using SANE calls to the actual math, and passing those results as emulated FPU instruction results. So, emulation inside emulation, kind of. Getting this done was actually quite easy, as the SANE emulation already covers so many calls, so we just had to hook up the F-line exceptions, and implement two missing SANE FP68K selectors (FNEXTX and alternate FX2X), and we were able to run a test app written in THINK Pascal with native 68881/68882 code generation with correct results:

SoftFPU running on MACE

In the above test application, the Compile Options dialog shows that 68881/68882 codegen is active, the code contains math for 9 / 2, and the Text window shows the result of 4.5 (in scientific formatting, as the Dec2Str routine still has some unfinished features)

Some updates to get Civilization 2 to work…almost

One cool game we also have been wanting to play for a long time is legendary Civilization 2, and we took a small steps in getting closer to being able to play that game someday soon:

  • Time Manager’s “hidden” microseconds trap (A093). This one was a bit hard to find documentation for, but we were able to produce close-enough results to keep Civilization 2 happy, which uses it for some reason. Basically the call returns 64-bit microseconds counter, with low 32 bits in D0, and high 32 bits in A0 register.
  • Added DriverGestalt (43) status call to NativeFS, and dgDeviceType (‘devt’) to it, which Civilization 2 seems to use to detect the media type of volume it was launched from (to see if it was started from the CD-ROM or Hard Disk)
  • CopyPalette trap handler (only partial implementation, but seems to be enough for Civ2 startup)
  • Added some more fake process manager data to GetProcessInformation results, in this case basically faked the processAppSpec by using HGetVol to get active directory, and CurApName from low mem…really dirty, but luckily enough again for Civ2. When process manager is someday added, this hack will be replaced by a proper implementation

With these done, we did get the game to progress quite far already:

There are still some big issues in the game, such as missing for some reason the entire backdrop images from the start-up menus, fonts looking ugly because truetype is not implemented (bitmap fonts have the pixelation on non-exact sizes), and color picture recording has still missing features causing city screens to crash.

Newer Technology GURU: “GUide to Ram Upgrades”

One other nice application that we were also experimenting is NewerTech’s GURU, which not only at the time was nice tool for checking out what RAM/VRAM upgrade options you had for Macs, but also doubles as nice mini-encyclopdia of various Macs, clones – and even printers – with their specs and nice color icons included.

GURU running on MACE

To get GURU running, there were a couple things needed:

  • We had again to temporarily tweak the GetProcessInformation’s processSignature (which sadly also WarCraft 2 uses, so both this app and that game need a proper implementation for this
  • Interestingly, the tiny and very harmless-looking popup menu triangles (seen in the screenshot on right-hand side) were actually 16-bit RLE-compressed pixmap images, so we had to implement both the 16-bit pixel translator, and the RLE decompressor in the Color QD Picture bits opcode handler
  • Also, the About Box in GURU used TESetText for styled textfield, which was previously a TODO, so that had to be implemented (it basically just replaces existing styles with a single style run)

MacWrite (finally) works now!

We also took some time to dig into other long-time known issues, one of which the MacWrite crashing on newline issue. Interestingly, two separate bugs did surface:

  • The biggest issue was that doing post/pre-increment of registers in middle of execution was not handled properly, in MacWrite’s case the offending instruction was ADDA.W (A0)+,A0, where value from memory pointed was read, A0 was supposed to be incremented by the word size (2 bytes), and the result added to A0 register. The bug caused the increment to get missed before adding the value to A0, causing it to be 2 bytes too low. This caused a bunch of havok in MacWrite, as the app used its own data structures to keep style data for rows, and this made – among memory corruption – the font size to go to a whopping 255 pixels when adding a new line on keyboard
  • The “justify” text alignment was one of rare cases where spExtra of GrafPorts was used to do sub-pixel (fixed-point) spacing of text, so we didn’t notice before Font Manager was missing a byteswap in handling of spExtra, which caused the cached spExtra to get totally crazy values
  • A rare case of resource map not being initialized properly surfaced on document saving, which was fixed
  • And also, document saving in MacWrite seems to depend on IUSetIntl trap (to apparently write localization data to the document), that trap had to be implemented

With these fixes, MacWrite finally works:

MacWrite works now!

Strategic Conquest

Although most of the features we had tested in Strategic Conquest were working, the map drawing had glitches: polygon lines were missing. We did some investigation about this, and found out that MapPoly trap only mapped the polygon points, but totally forgot to do the mapping for polyBBox. This in turn caused the polygon drawing to assume points were out of range, and fail to render anything in most of cases. With this fixed, the map seems to work fine now (at least in the B&W version):

Strategic Conquest MapPoly works now!

Arkanoid has sound!!!

One issue that was also mysterious for long time was why Arkanoid had no audio, while (almost) every single other black & white game/application had sound. After some researching, we found out that being super smart (and bizarre), instead of the standard VLB queue item (like 99.9% of games), or the not-so-common Lvl1DTVIACA1 interrupt handler (which Fokker Triplane does), Arkanoid actually hooks directly to the AutoInt1 vector on the 68K exception vector list, which normally would be handled by the Mac OS to invoke the VBL interrupts. After adding a level of indirection to the AutoInt1 vector (and adding possibility to later add other interrupt handlers to this level), Arkanoid sounds works perfectly! Sadly we did not have time to get a nice video capture of this for this update 😦

THINK Pascal zone check fixed

Another long-time issue that had been bothering us was that THINK Pascal kept saying in debug mode all the time that all zones were damaged, so we finally took time to dig into the THINK Pascal code and look for their zone check code and see what it was doing. Luckily, the only part that was failing was the trailer check, which checked that the bkLim block was supposed to be always 12 bytes in 24-bit mode, was getting triggered because when GrowZone was adjusting the zone trailer, it accidentally wrote 0 (zero) as trailer size, instead of the expected 12 (ironically InitZone did set it up properly). Although this bug had no functional effects to anything else but the zone check, the THINK Pascal was way more happier now, and looks like the LightsBug debugger is working perfectly, zone browser now included!

LightsBug working nicely after zone checks pass

Other bug fixes

There were a couple bugs/missing features that were fixed, with varying effects:

  • Fixed mask overflow in 32-bit pixel data stretching blitter, and implemented TODOs for some 32-bit pixel data colorization variables – These caused black buttons in some F/A Hornet 2.0 dialogs
  • ScrollRect passed wrong offsetPt to ShieldCursor – this caused some cursor artifacts (this bug did not manifest itself until after the actual software cursor code was added recently)
  • The WDEF 0 (standard window defproc) did not offset the grow box drawing correctly when a window grafport was offset to a non-zero origin (like in PhotoShop) – this caused the bottom-right growbox to get drawn in wrong position on the desktop)
  • 1-bit cursor data saving was missing from SetCCursor – This caused the cursor update to not get triggered if doing InitCursor or SetCursor after doing SetCCursor
  • Fixed issue in RestoreBits, where the menu bar was invalidated in RestoreBits even though it was restored, which in turn caused recursive call to DrawMenuBar when had a menu highlighted (the kMBDFMessageHilite message in MBDF code called RestoreBits which in turn did InvalMenuBar call, which caused the DrawMenuBar to get triggered from the second HiliteMenu call inside the DrawMenuBar) – Fixes the issue of MenuBarPattern code getting stuck in infinite recursion in Civilization, as it tried to double-patch itself from the recursed DrawMenuBar call
  • Added missing call to SystemEvent in GetNextEvent, which caused some events, such as key press events, to not get passed properly to Desk Accessories – Fixes the issue of Key Caps DA not receiving text input
  • Allow BlockMove to receive negative lengths. After researching how the List Manager in some 68k Mac OS versions handle the case of having zero rows but non-zero columns, it seems that they do pass negative lengths (maybe on purpose) to BlockMove with no side effects, and BlockMove ignores those – Fixes the unncessary negative length assertion in ResEdit with empty resource files
  • vRefNum case was missing for GetWDInfo calls in NativeFS, which caused ResEdit to not be able to open files from root directory of a volume
  • Do proper setup of stack pointer on application launch to BufPtr, because this was missing (and SP was left in boot-time default), it cut amount of available memory to apps down to 75% of total RAM! This also surfaced another bug that the startup-time boot grafport (used to draw any stuff during INIT loading etc) was not closed, so any code assuming that all PortList entries were valid were randomly crashing
  • Added style support for TEDelete, so using backspace key in SimpleText works now

What next?

It’s always hard to estimate what will be done and when, but there are still some key goals we’re aiming in the near future:

  • To get the public binaries finally updated after we have time to figure out not only a nice, cheap and practical CDN or other way update the files
  • When electricity prices go down (which got crazy during winter, so had to turn off most of extra computer equipment), work on automating the builds for different platforms, instead of manually building everything (even though CMake does a lot of the hard lifting)
  • Get Sound Manager 3.x to work, so we can start putting out the color version too
  • Try to make sure ReserveMem & NewHandle combos work as expected, right now this is causing THINK Pascal to run out of memory – this is something we want to make sure works, before we move to:
  • 32-bit memory manager. Something that’s needed not just to get more than 8 megabytes of accessible memory, but also to get some picky games (such as Doom) to run, AND to allow:
  • Direct memory access support, for avoiding the overhead of virtual memory interface, potentially speeding up emulation quite a lot. This requires 32-bit memory model to work efficiently.
  • Update build system to include creation of the System file from resource scripts – it’s still being handled as binary file that’s edited using native resource tools, which is not only cumbersome, but also makes it difficult to have variants of the System file for different system types – when we at some point do add process manager support, the way for example desk accessories and fonts are handled needs adjustment, as in process manager-aware (System 7 and later) those are external files instead of ones in System like in System 6 and earlier
  • Work on the so-many missing features (which do also cause known bad behaviour or crashes)
  • Debug and research the problems and crashes which we know about, but haven’t yet figured out what is causing them (such as F/A Hornet and F117A drawing issues, ball missing bug in Loony Labyrinth, timing issues in Monkey Island and Prince of Persia 2…)
  • Prepare a Discord server for public access
  • Many, many, ….many other things

We will as always update our progress on this blog, hopefully a bit more frequently. Your support and encouragement has been very inspiring, and we’re trying as soon as possible to share more and more of the stuff we’ve been making with you.

Full list of changes since last post

2023-02-15 14:56:38 +0200 • Fix 24-bit zone trailer & add bkLim validation
2023-02-15 03:45:35 +0200 • Add dummy ADBOp trap (for SoftWindows)
2023-02-14 21:47:21 +0200 • Expose & use low-level AutoInt1 vector for VBLs
2023-02-13 23:34:21 +0200 • Add LMKbdType and use it for Gestalt & SysEnvirons
2023-02-13 00:27:16 +0200 • Also adjust polyBBox in MapPoly
2023-02-12 17:40:45 +0200 • Style support for TEDelete
2023-02-12 17:39:11 +0200 • Disable font width table logging in debug mode
2023-02-08 23:48:12 +0200 • Merge branch 'master' of
2023-02-08 23:47:43 +0200 • Remember to close the startup-time GrafPort
2023-02-08 06:12:54 +0200 • Merge branch 'master' of
2023-02-08 06:12:50 +0200 • Handle TEMeasure start in middle of style run
2023-02-08 06:12:02 +0200 • Don't return random value if FindFolder fails
2023-02-08 06:11:09 +0200 • Some cleanup in HighLevelFSDispatch
2023-02-08 06:10:30 +0200 • Don't ignore opIFore and opIBack
2023-02-06 02:47:11 +0200 • Reset stack pointer A7 in non-multitasking Launch
2023-02-06 00:48:02 +0200 • Handle vRefNum case in GetWDInfo
2023-02-05 23:15:40 +0200 • Implement IUSetIntl selector for IntlUtils Pack6
2023-02-05 22:29:59 +0200 • Fix rare case of res map handle not initialized
2023-02-05 22:26:45 +0200 • Fix ADDA.W/L postincrement same as destination bug
2023-01-30 12:28:55 +0200 • Fix byteswap bug on spExtra handling in FMSwapFont
2023-01-29 01:26:40 +0200 • Add dummy stub for HMRemoveBalloon (for Excel 3.0)
2023-01-28 08:35:17 +0200 • Implement TESetText styled textfield case
2023-01-28 00:35:35 +0200 • Some process manager placeholder hacks for GURU2.6
2023-01-28 00:34:03 +0200 • Use 16-bit RLE decoder for PICT packtype 3
2023-01-28 00:33:37 +0200 • Add 16-bit RLE decompressor for QD Pictures
2023-01-28 00:32:33 +0200 • Add 16bit direct to indexed color pixel translator
2023-01-26 21:06:19 +0200 • Merge branch 'master' of
2023-01-26 21:06:17 +0200 • Fix 68k Bxx opcode with 32-bit extension
2023-01-26 01:07:16 +0200 • Fix return value of Microseconds trap
2023-01-25 14:45:34 +0200 • Hack face processAppSpec in GetProcessInformation
2023-01-25 13:52:55 +0200 • Partial CopyPalette (0xAAA1) trap implementation
2023-01-25 03:32:02 +0200 • Add dummy Lower/Strip/Upper/StripUpperText handler
2023-01-25 03:01:56 +0200 • Handle 'devt' driver gestalt in NativeFS
2023-01-25 03:00:19 +0200 • Return controlErr on non-support NFS Control codes
2023-01-25 02:41:18 +0200 • Add DriverGestaltParam & dummy handler in NativeFS
2023-01-25 02:39:21 +0200 • Fix broken return from NativeFS Status handler
2023-01-25 02:38:02 +0200 • Implement Microseconds trap (A093) for Time Mgr
2023-01-23 03:10:20 +0200 • Expose some rsc manager stuff for SetResourceSize
2023-01-23 03:09:21 +0200 • Merge branch 'master' of
2023-01-23 03:08:20 +0200 • Log also 68k program counter in trap debug msgs
2023-01-23 03:07:21 +0200 • Update TimeManager.c
2023-01-23 03:06:42 +0200 • Work on SetResourceSize
2023-01-17 22:25:49 +0200 • F-line handler for M68kHelper glue module
2023-01-17 22:04:05 +0200 • Implement FNEXTX and alternative FX2X selector $10
2023-01-17 22:01:38 +0200 • Fix f-line dispatcher installation in 68k cpu code
2023-01-17 00:27:06 +0200 • Add CountDITL, and a VERY bizarre A08B dispatcher
2023-01-16 14:06:31 +0200 • Fix invalid icon csCode in NFS + add assert checks
2023-01-16 13:54:23 +0200 • Fix byteswap bug
2023-01-16 13:41:18 +0200 • Remove VBL tasks in app zone in InitApplZone
2023-01-16 13:34:27 +0200 • Dispose palettes in app zone in InitApplZone
2023-01-16 13:22:09 +0200 • Close all open grafports in InitApplZone
2023-01-16 13:21:38 +0200 • Flush font caches in InitApplZone
2023-01-16 13:02:15 +0200 • Flush font caches in SetApplBase
2023-01-16 13:01:45 +0200 • Add SLCheckApplZoneAddress, for app zone cleanup
2023-01-16 13:01:04 +0200 • Detect use of ResErrProc in resource manager
2023-01-16 13:00:29 +0200 • Add (partial) font cache flushing in Font Dispatch
2023-01-16 12:59:34 +0200 • Implement FCMPL selector in SANE FP68K trap
2023-01-09 23:32:54 +0200 • Implement QDExtensions's GDeviceChanged selector
2023-01-09 23:22:39 +0200 • Implement QDExtensions's OffscreenVersion selector
2023-01-08 23:17:28 +0200 • Added AVL Tree node deletion routines
2023-01-08 13:49:24 +0200 • Implement SeedCFill + CalcCMask QD traps
2023-01-06 16:57:46 +0200 • Fix handling of zero & negative BlockMove lengths
2023-01-06 11:24:27 +0200 • Check VCB flags in SetFileInfo/SetCatInfo
2023-01-01 17:46:21 +0200 • List Mgr's LClick multiselect (shift+cmd) support
2022-12-29 11:06:57 +0200 • Implement RsrcMapEntry trap handler
2022-12-20 00:02:35 +0200 • Don't return mouseMovedMessage unless app4 bit set
2022-12-18 14:05:14 +0200 • Implement SysEdit trap handler
2022-12-18 14:04:49 +0200 • Fix issue of GetNextEvent not calling SystemEvent
2022-12-17 00:25:55 +0200 • Fix unneeded invalidate of menu bar in restorebits
2022-12-14 21:29:16 +0200 • Save 1-bit cursor data in SetCCursor
2022-12-14 16:21:12 +0200 • Fix wDrawGIcon incorrect offset in nonzero origin
2022-12-13 21:49:42 +0200 • Proper fix for horizontal stretch pixel masking
2022-08-24 02:02:08 +0300 • Fix ShieldCursor offset in ScrollRect
2022-08-22 23:21:44 +0300 • Improve relative mouse mode host cursor behaviour
2022-08-22 22:11:12 +0300 • Merge branch 'master' of
2022-08-22 22:11:02 +0300 • Add dummy MIDI driver module (for MIDI Manager)
2022-08-22 22:09:16 +0300 • Fixes to 32-bit direct-to-8bit color QD blitter
2022-08-22 22:08:03 +0300 • Fix mask overflow in 32-bit horizontal CQD stretch
2022-08-21 13:30:13 +0300 • Fix sound manager header
2022-08-05 03:58:52 +0300 • Add empty dummy MIDIGetClients selector
2022-08-05 02:58:50 +0300 • Refactor filesystem AVL tree code & add validation
2022-08-05 02:57:45 +0300 • Cleanup SoundDispatch & add SndDispVersion handler
2022-08-01 17:28:51 +0300 • Merge branch 'master' of
2022-08-01 17:28:46 +0300 • Implement DirCreate file manager trap
2022-07-31 23:09:20 +0300 • Re-export traps & selectors for status update

Sound Manager 2.x Support

It’s again been quite a bit of time since last update, as we’ve been busy on doing other stuff for the latter half of 2021, and the winter – but now in spring, we again had time to work on the emulator. During the past couple weeks, Toni spent time finishing the first round of Sound Manager 2 research, so we were able to move to implementation phase last week. And after first week of coding, we already have a handful of test applications with (mostly) working Sound Manager 2 support – although, as planned, it is at moment limited only to applications running in the “classic” emulation mode. Here’s a short video demonstration of the new audio capabilities:

Sound Manager 2.x tests on M.A.C.E.

In the video, the following test applications are shown:

  • Civilization, with fully working sound output (exclusively with bufferCmds through ‘snd ‘ resources).
  • Prince of Persia 1, also fully working (interestingly, even without Sound Manager, PoP1 was able to *mostly* output audio with Sound Driver, but looping sounds (door opening) caused hang in that mode because of the way it used the Device Manager routines. With Sound Manager, none of those issues are present and all seems work fine (again, using bufferCmd commands).
  • Bard’s Tale was a bit different test case for us, as it uses soundCmd combined with noteCmd/restCmd etc commands to play the music. But it also works now, apparently fully.
  • Railroad Tycoon has also fully working sound support, again using bufferCmd with ‘snd ‘ resources like Civilization 1. This game has interestingly its own sound queuing system, separate from Sound Manager command queues – which allows funny way of queueing multiple sounds if invoked rapidly after each other, which eventually get played in turn after each other. Also this behaviour seems to match fully how audio in the game works on real Macs.
  • Pararena 1.3 also has now working sounds
  • And Pipe Dream finally also got sound and music
  • Finally, Loom also has working sound & music, although the Mac Plus/Classic style behaviour of music does sound a bit “fishy”. We checked that the music does sound same way at least on minivmac, although it appears to have been meant to be played using 4-channel output, while the “classic” hardware is limited to only one channel at once. This game also uses soundCmd/noteCmd to adjust playback frequency of samples, like Bard’s Tale. Also, there is still at least one known race condition issue in playback, which appears to actually be identical to a deadlock that happens also in minivmac, if the game is run under that emulator on faster than 1x speed.

Also, there’s working button click sounds in Speedometer system info window, and we’re working on getting sound to work also in Bolo soon (it uses apparently older style selectors for SoundDispatch at least in one case, which we don’t handle yet, so we need to improve that next).

All of the features of Sound Manager 2 haven’t been implemented yet, as we’ve been adding them “on demand” as we’ve been trying out various test applications. Some of features not yet implemented include:

  • Compressed sound decompression/compression
  • At moment, only ‘snth’ 5 (sampledSynth) synthesizer is supported (the squareWaveSynth and waveTableSynth still need to be added)
  • Sound Manager command timing is supposed to the drift-free Extended Time Manager tasks, but they for now default to regular TMTask behaviour. This will cause songs played using noteCmd/restCmd/etc, that depend on command duration for timing, to drift off a bit (at least in Bard’s Tale and Loom)
  • And as mentioned before, only Sound Manager 2.x for “classic” mode was implemented, so the color mode does not yet have audio. But that will follow up later as Sound Manager 3.x compatibility after we finish research on it (we plan to implement Component Manager emulation as first step on way to SM3 compatibility, which both allows us to provide Sound Component capabilities, and also allows in future other APIs that depend on Components to be implemented – such as QuickTime and Internet Config)

Other fixes

There are also a number of other improvements, of which some are listed here:

  • MDEF 0 now supports scrolling in popup menus, which allows the Machine Record selection popup to be opened in Speedometer, when screen size is smaller than the menu
  • Fixed issue in “classic” QuickDraw PICT color translation mapping, which was causing black credits screen in Railroad Tycoon
  • GhostWindow low-memory global handling was added – this fixes most of the issues in SuperPaint 1
  • ScalePt/FixDiv had a couple bugs, which caused at least two separate issues in MacDraw (related to calculation of object coordinates on the document)
  • Color search proc support and a couple color picker package traps were added (for HSV conversion) were added to allow Solarian 2 to work
  • Rounded windows (WDEF 1) finally have the close box (and color support) – this allows for example the about screen in Glider 2 & 3 to be closed 🙂
  • Allocation of WDCBs through OpenWD now check for existing working directories, to not flood the WDCB table and run out of memory. This happened at least with certain applications using the “Mercutio” MDEF, which on each message did SysEnvirons call, which in turn opened a WD in the system folder
  • A bunch of other important fixes
  • And finally, as first step of getting FPU support, Pukka has started implementation of the 68881/2 emulation, so we will eventually be able to run applications requiring the math co-processor instructions (such as Specular Logomotion and Pixar Typestry)

Full list of changes since last post

2022-04-12 23:42:44 +0300 • Fix typo in Bolo JSON config                      
2022-04-12 23:29:35 +0300 • Fix signed/unsigned comparison in FMSeek          
2022-04-12 23:28:33 +0300 • Tweak test apps for sound tests + add BoloSounds  
2022-04-12 22:06:17 +0300 • Fix classic QD color-to-mono translation mapping  
2022-04-12 21:31:19 +0300 • Implement rateCmd in snth 4101 (classic SM2)      
2022-04-12 21:24:47 +0300 • Fix uninitialized SndChannelPtr in SndPlay        
2022-04-12 00:48:08 +0300 • Add scrolling support to popup menu case of MDEF 0
2022-04-11 17:00:05 +0300 • Fix disposal of channel if SndNewChannel fails    
2022-04-11 16:45:48 +0300 • Some PrimeTime prep for extended timer mgr support
2022-04-11 16:43:27 +0300 • Fix timer task deactivation in RmvTime            
2022-04-11 16:42:37 +0300 • Fix setting of channel idle state from quietCmd   
2022-04-11 16:41:42 +0300 • Swap new buffer in note/freqCmd if already playing
2022-04-11 14:55:02 +0300 • Fix duration passed to timer in waitCmd           
2022-04-11 14:53:59 +0300 • Setup synthesizer state correct in noteCmd/freqCmd
2022-04-11 14:52:36 +0300 • Fix bug in checking whether timer was active      
2022-04-11 00:34:59 +0300 • Implement emptyCmd in snth 4101 (classic SM2)     
2022-04-11 00:33:56 +0300 • Fix sample rate conversion bug in soundCmd        
2022-04-09 19:10:16 +0300 • Handle noteCmd/freqCmd in snth 4101 (classic SM2) 
2022-04-09 00:02:14 +0300 • Implement waitCmd (classic SM2)                   
2022-04-09 00:02:01 +0300 • Implement restCmd in snth 4101 (classic SM2)      
2022-04-08 23:37:15 +0300 • Handle soundCmd in snth 4101 (classic SM2)        
2022-04-08 20:23:59 +0300 • Fix SM2 locking (dispose channel during playback) 
2022-04-08 20:21:14 +0300 • Fix  end-of-buffer handling in snth 4101 VBL Task 
2022-04-08 20:19:38 +0300 • Set correct state in quietCmd handler in snth 4101
2022-04-08 20:18:56 +0300 • Clear busy flag correctly at end of snth VBL Task 
2022-04-08 13:01:33 +0300 • Fix size mismatch when setting channelIdle flag   
2022-04-08 12:47:08 +0300 • Implement flushCmd handling (classic SM2)         
2022-04-08 12:46:50 +0300 • Implement SndDoImmediate (classic SM2)            
2022-04-08 12:25:55 +0300 • Implement freeCmd in snth 4101 (classic SM2)      
2022-04-08 12:25:41 +0300 • Fix a couple byteswap bugs in channel flags       
2022-04-08 12:23:09 +0300 • Implement SndDisposeChannel (classic SM2)         
2022-04-08 04:18:00 +0300 • Implement callbackCmd (classic SM2)               
2022-04-08 04:12:18 +0300 • Handle quietCmd in snth 4101                      
2022-04-08 03:56:40 +0300 • Implement resumeCmd handling                      
2022-04-08 03:47:29 +0300 • Handle tickleCmd in snth 4101 (classic SM2)       
2022-04-08 03:36:14 +0300 • Implement basic snth 4101 buffering (classic SM2) 
2022-04-08 03:34:44 +0300 • Implement Timer1 & tickleCmd sending (classic SM2)
2022-04-07 03:49:16 +0300 • Handle bufferCmd in 'snth' 4101 (classic SM2)     
2022-04-07 03:47:59 +0300 • Handle high-level pauseCmd (SM2 classic variant)  
2022-04-07 02:08:33 +0300 • Implement command timer & dequeueing (classic SM2)
2022-04-06 20:25:16 +0300 • Implement queueing in SndDoCommand (SM2 variant)  
2022-04-06 19:21:39 +0300 • Handle initCmd in 'snth' 5 (SM2 "classic" variant)
2022-04-06 18:21:17 +0300 • Implement availableCmd and versionCmd in 'snth' 5 
2022-04-06 18:08:38 +0300 • Implement quietCmd handler                        
2022-04-06 17:58:52 +0300 • SndAddModifier mostly implemented (SM2.x variant) 
2022-04-06 02:17:47 +0300 • Implement SndPlay 'snd ' format 1 resource parsing
2022-04-06 01:33:48 +0300 • Fix typo in previous commit                       
2022-04-06 01:33:08 +0300 • Initialize Sound Manager (and fix byteswap bug)   
2022-04-06 01:17:33 +0300 • Use SndAddModifier in SndNewChannel               
2022-04-06 01:14:29 +0300 • Add SndAddModifier trap (dummy stub at moment)    
2022-04-06 01:13:07 +0300 • SndNewChannel proto implementation (SM2.x variant)
2022-04-06 01:11:00 +0300 • Add snth 0x1005 (mac plus variant of sampledSynth)
2022-04-06 01:10:33 +0300 • Update sound manager headers with new information 
2022-04-03 22:31:59 +0300 • fix constants                                     
2022-04-03 21:32:09 +0300 • base for fpu                                      
2022-03-23 00:27:09 +0200 • Reinforce IODone to handle weirdness of SimCity1.3
2022-03-20 05:02:17 +0200 • Add missing GhostWindow check to FrontWindow trap 
2022-03-19 03:47:11 +0200 • Fix ScalePt special case byteswap bug             
2022-03-19 00:31:53 +0200 • Better fix for FixDiv overflow case               
2022-03-19 00:29:30 +0200 • Set MemErr to noErr when MoreMasters succeeds     
2022-03-18 04:39:24 +0200 • Avoid weird C bug causing FixDiv fail in release  
2022-03-08 03:14:17 +0200 • Add missing music file to PipeDream test app      
2022-02-17 22:40:14 +0200 • Re-use existing WDCB when possible (not MF-aware) 
2022-02-17 15:24:34 +0200 • Allow skip of selector high byte in some selectors
2022-02-17 15:20:12 +0200 • Fix overflow in iteration of WDCB list            
2022-01-12 02:43:01 +0200 • Implement _DelSearch (0xAA4C) Color Manager Trap  
2022-01-11 04:02:15 +0200 • Implement FADDL selector for FP68K in SANE        
2022-01-11 04:01:28 +0200 • Implement HSV2RGB in Pack12 (color picker package)
2022-01-10 00:25:46 +0200 • Implement RGB2HSV in Pack12 (color picker package)
2022-01-09 21:08:25 +0200 • Add dummy SndChannelStatus to SoundDispatch       
2022-01-09 08:56:43 +0200 • Call searchProc in _Color2Index trap when needed  
2022-01-09 08:56:05 +0200 • Implement _AddSearch (0xAA3A) Color Manager Trap  
2021-12-01 15:15:21 +0200 • Add dummy GNEFilter for modal dialogs             
2021-12-01 15:14:06 +0200 • Support kSpecialCaseGNEFilterProc mixedmode calls 
2021-11-03 05:54:14 +0200 • Fix handling of MP lock flag in MMSetSize24       
2021-11-03 05:52:18 +0200 • Fix measuring memory purge range in memory manager
2021-10-29 13:09:10 +0300 • Add Uninvited to test app JSON configs            
2021-10-27 14:52:29 +0300 • Fix calculation of offset to font name in 'ictb'  
2021-10-27 14:33:29 +0300 • Fix opOrigin PICT opcode (disposed wrong region)  
2021-10-27 13:54:33 +0300 • Fix typo in the Hornet JSON test app config file  
2021-10-27 13:31:04 +0300 • Add F/A-18 Hornet 2.0.1 to test app configs       
2021-10-27 13:30:38 +0300 • Fix SysEnvirons, use 'proc' instead of 'cput'     
2021-10-27 13:29:04 +0300 • Handle special palettes with bit 14 set in flags  
2021-10-27 05:13:14 +0300 • Fix minor Palette Manager bugs                    
2021-10-27 04:18:39 +0300 • Add ADB Manager & dummy CountADBs/GetADBInfo traps
2021-10-27 03:57:20 +0300 • Video DRVR: Handle status codes & add linear gamma
2021-09-26 20:45:07 +0300 • Disable SDL minimize/close keys (3rd party lib!!!)
2021-09-26 20:40:59 +0300 • Temp fix for return address of TRAP # instructions
2021-09-26 20:38:25 +0300 • Fix 32-bit overflow in ASR.W instruction          
2021-09-26 20:37:53 +0300 • Fix RTE instruction                               
2021-08-22 13:10:43 +0300 • Fix pattern masking bug in arithmetic rect blitter
2021-08-17 23:32:14 +0300 • Add dummy SRsrcInfo selector for Slot Manager     
2021-08-17 03:47:44 +0300 • Fix artifacting of outline/shadow color QD text   
2021-08-16 17:42:58 +0300 • Fix some Memory Manager type conversion warnings  
2021-08-16 17:42:03 +0300 • Add srcOr/srcXor/srcBic colorQD region blit modes 
2021-08-16 14:35:33 +0300 • Add missing WDEF 1 changes from previous commit   
2021-08-16 14:35:06 +0300 • WDEF 1 (rounded window) color support & GoAway box
2021-08-16 14:34:13 +0300 • Fix array overflow in the minilauncher hack proto 
2021-08-12 22:06:06 +0300 • Fix memory leak in DialogManager (Get)New(C)Dialog
2021-08-12 22:03:05 +0300 • Fix memory leak in CloseWindow (call CloseCPort)  
2021-07-29 19:48:49 +0300 • Add 1-to-2 bit non-colorizing pixel translator    
2021-07-13 23:42:11 +0300 • Sanity check ioNamePtr in _GetVol trap            
2021-07-13 23:41:02 +0300 • Add Bards Tale test app JSON configs              
2021-07-13 18:21:41 +0300 • Add Macintalk test app JSON configs               
2021-07-07 00:12:25 +0300 • Bump up version number                            
2021-07-07 00:12:14 +0300 • Fix VBL interrupt crash from start race condition 
2021-07-06 21:42:48 +0300 • Fix changes that broke .Sound DRVR #3             
2021-07-06 00:05:22 +0300 • Install dummy DRVR 4 at startup                   
2021-07-05 23:06:35 +0300 • Tweak Space Quest 1 config JSON (pass copy checks)
2021-07-05 23:06:02 +0300 • Add missing DskErr lowmem from previous commit    
2021-07-05 23:05:48 +0300 • Add dummy .Sony driver, allows DRVR 4 Status calls

Many fixes: DIVU.W, Bitfields, Time Manager; Prince of Persia 2…

A lot of has happened past week, perhaps thanks to the summer finally starting to be over, and us having more time for the project again. Here’s a breakdown of most important advances made last week:

Fixed the DIVU.W overflow calculation

A long-time bug in unsigned 16-bit division instruction was finally found, and fixed by Pukka! We accidentally calculated overflow using signed 32767 limit, while it should have been unsigned 65535. This immediately fixed highly noticeable glitches in two of the test applications, Vette! and Test Drive II: The Duel (both ironically of same racing simulation genre):

Before the fix, Test Drive II had noticeable warping and glitching in the road near the bottom, and the polygons in Vette! had sometimes incorrect tangents, causing them to get messy depending on the angle of polygon edge.

This also fixed the “Divide overflow” bug occurring in SoftPC, which was weirdly happening on certain times of day, which does indicate that some calculation related to the computer clock time was failing because of the incorrectly signaled overflow. Now SoftPC works quite reliably.

Bitfield instruction fixes

One recently added test application, Prince of Persia 2, used a lot of the bitfield instructions for the SHAP resource drawing on the screen (apparently RLE/LZ decoding), which originally had a really messy artifacts:

But after fixing the instructions, the levels finally looks as they should:

Time Manager fixes

Besides the bitfield instructions, Prince of Persia 2 also uses Time Manager heavily for timing, and that surfaced a couple nasty bugs in the code, which were fixed.

There are still so oddities to investigate, for example the intro sequence does not advance past second screen yet, and the death and level completion delays do not trigger during the gameplay.

Sadly these fixes do not (yet) fix the one special case in Speedometer, where Dhrystone test does not work properly because of some issues that may be related to Time Manager (it starts the same timer twice, and stops it before test, so it never runs).

Keyboard handling threading bug

There was also a nasty bug in keyboard handling, which had rather surprisingly managed to stay hidden for a quite long time; Basically, the SDL2 key events that were handled by the keyboard code on main thread had chance to accidentally modify data structures and especially the A0 register, while 68K was running in VBL interrupt handler, causing in some rare cases the A0 register value to get corrupted; This caused a lot of crashes in Prince of Persia 2, and is most likely the cause of a very rare crash in Wolfenstein 3D. The offending part code was made thread-safe, which fixed this particular crash completely.

Sound in SoftPC

This was a very minor fix, but still worth mentioning; the reason for why there was no sound in SoftPC was caused by the fact that the application was setting the sound driver volume from the SPVolCtl low memory global, which had been set to zero on startup. By improving the PRAM simulation to populate all low memory PRAM values with sensible default values, the sound works now nicely in SoftPC!

Dark Castle “_Random” bug

As Pukka guessed last Christmas, the reason for buggy behaviour of robots, and some other minor glitches, in Dark Castle was indeed related to calling of the “_Random” trap! Of the last weekend was spent on searching the bug in other places at first, such as robot behaviour code, until we noticed something strange.

In Apple documentation, and all references known to us, the QuickDraw random number generator is defined simply as a Pascal type function with the following signature (in IM: Mathematical and Logical Utilities, 3-51):

pascal short Random (void);

Which means, that the function takes no arguments, and returns a 16-bit signed integer on stack. But, however, the way Dark Castle used was rather odd:

This screenshot of ResEdit showing disassembly of the part of Dark Castle code calling _Random shows, that it actually ignores the result value at offset 000B60 by discarding it off stack! And later looking at the code, it was actually using value in D0 instead…so, a quick look at System 7 _Random trap handler gives some insight to this:

Here actually it is visible, that on real Macs, the trap trashes a number of registers, and has the side-effect of leaving result value in the D0 register (being a toolbox trap, the trap dispatcher does not clean up any of the registers, unlike it does for some of registers after calling OS traps).

So, to fix Dark Castle, we added extra code to set up the D0 register as the game expected it, and everything started to work perfectly.

Some other progress from these changes

A couple other test applications seemed to benefit of these changes too, although they still need a bit of work to make sense: Loony Labyrinth now loads correctly after bitfield instructions were fixed (and physics in it also work correctly after the DIVU.W fix), and Marathon now actually runs and is playable with the Time Manager fixes:

Interestingly both of these games still seem to suffer from having colors completely messed up, but besides this glitch, they appear to be playable and run quite nicely.

Bonus video

And to celebrate getting SoftPC finally to work properly, I recorded this short video of M.A.C.E. running SoftPC running David Murray’s (The 8-bit Guy) Planet X-3:

You can find more information on Planet X-3 here:

Full list of changes since last post

2020-09-09 23:29:07 +0300 • Merge branch 'master' of
2020-09-09 23:28:59 +0300 • Update CMake scripts to support Apple notarization
2020-09-09 21:14:01 +0300 • add missing addressing fixes
2020-09-09 20:32:11 +0300 • memory addressing fixes
2020-09-09 20:26:50 +0300 • Fix compilation error in QDColorStretchBits.c
2020-09-09 20:00:29 +0300 • Fix some warnings in StretchBits color blitters
2020-09-09 19:59:57 +0300 • Fix some warnings in CodeResourceWDEF1
2020-09-09 19:26:58 +0300 • Fix return value in C_PlotCIconHandle
2020-09-09 19:18:10 +0300 • Merge branch 'master' of
2020-09-09 19:16:59 +0300 • fixes for bitfield and divuw instructions
2020-09-07 04:23:28 +0300 • Tweak more Dark Castle CMake JSON configs
2020-09-07 00:04:11 +0300 • Simulate PRAM (SysParam) to make SoftPC sound work
2020-09-06 23:04:58 +0300 • Update MACE version in CMake config
2020-09-06 13:14:44 +0300 • Color pixeldouble & (disabled) CRT simulation
2020-09-06 03:45:52 +0300 • Update trap generator XLS file
2020-09-06 03:44:13 +0300 • Tweak Dark Castle JSON config settings
2020-09-06 01:52:48 +0300 • Hack Random result value in D0 to fix Dark Castle
2020-09-05 03:54:17 +0300 • Get correct tmCount in RmvTime for non-first tasks
2020-09-03 02:16:54 +0300 • Fix the conversion of positive count in PrimeTime
2020-09-02 19:30:04 +0300 • Fix threading issue with keyboard input module
2020-09-02 19:28:30 +0300 • Don't crash on purged handles in GetHandleSize
2020-09-02 19:27:42 +0300 • Fix a lot of Time Manager bugs & clean up the code
2020-09-02 19:23:45 +0300 • Fix typing of region handle to CMDoDrawControls
2020-08-31 23:01:40 +0300 • A simplified MakeFSSpec implementation, for POP2
2020-08-31 04:17:44 +0300 • Add missing EqualRect change in EmuUtils.h
2020-08-31 04:16:59 +0300 • Fix GetCCursor crash when ResLoad was FALSE
2020-08-31 04:16:08 +0300 • Add dummy SCSIDispatch for Loony Labyrinth
2020-08-30 21:32:44 +0300 • Add Prince of Persia 2 to test app JSON configs
2020-08-03 01:45:10 +0300 • Make NewGWorld's rowBytes to work w/ Color VETTE!
2020-08-01 20:08:42 +0300 • Record PICT2 opHiliteColor, opOpColor &opDefHilite
2020-08-01 19:45:53 +0300 • Record HiliteBit as opHiliteMode in PICT2 format

Some “Music” to entertain in the quiet period

The past few weeks have been a bit quiet for us, as we’ve been both busy doing important work and personal life stuff, so we haven’t had as much time recently to spend on this project. We will continue progress soon when each of us gets more spare time, but meanwhile here’s a bit of entertainment for meanwhile:

MS-BASIC 2.0 running “Music” example program in M.A.C.E.

As mentioned a few weeks ago, the audio output – which previously was not working in MS-BASIC – suddenly started working as Pukka fixed some 68k CPU bugs. At that time, I recorded a short video of the “Music” sample program running with audio included – and as there’s no other news to share at this time, we hope this “filler” video will keep you entertained for the time being!

Indiana Jones and the Last Crusade

Although we could make Indy launch already a while ago, for some reason we did not get any graphics to display. However, after Pukka fixed some more CPU bugs (including ror.l instruction), we suddenly got everything appear on the screen:

Lucasfilm Games logo

Indiana Jones title screen

Interestingly, the game uses Sound Driver to play intro music, *but* after intro completes, it switches to Sound Manager. As we don’t have yet Sound Manager implementation, we added some empty stubs that just return error for the caller, and it seems Indy is happy with that as we can now proceed in the game all the way until getting stuck on quests 🙂

Indy boxing

At this point we also noticed that we had accidentally handled the Style parameter of TextFace as 8-bit value instead of 16-bits as we should have, so the style calls had not been working until now that we fixed it. Indy uses these styles to adjust appearance of text on the UI buttons:

Game controls at bottom of the UI

Here’s also a short video of the intro (with audio), and few first minutes of the gameplay:

Indiana Jones and the Last Crusade running on MACE, intro with music and few first minutes of gameplay

Apache Strike

One of our favourite games, Apache Strike, is now partially working. The game has a weird way of checking machine memory by writing/reading value beyond RAM space, which had just to be handled with special case to not raise a page mapping error from virtual memory system. The music (it’s using Sound Driver) is working OK, and main menu is shown properly:

However, some unresolved CPU bug seems to be causing the 3D rendering to fail, with only vertical lines appearing on the screen. Also controls don’t work yet, but it might be because we don’t yet completely support moving the mouse location by writing to low memory cursor globals from applications.

Here’s a short video with just the main menu and the music playing in background:


Pukka had been working long on the disassembler, and we were finally able to get first version of it integrated with the emulator. Below is a simple partial disassembly of Tetris audio mixer VBL, executed from Xcode debugger command prompt:

Partial disassembly of the Tetris 6-channel audio mixer VBL handler

Although the disassembler itself still has some minor quirks to fix, there were some interesting bits we found about the Tetris mixer code in this disassembly:

  • Unlike Sound Driver, which has 4-channel mixer, Tetris uses a 6-channel mixer
  • The audio is downsampled from 22256 Hz to 11128 Hz by writing each sampled byte twice (the $7000(A6) at 00030C18 is supposed to be 2(A6) in disassembly)
  • The division of summed samples by 6 is done by pre-calculated lookup table (which has 6×256 = 1536 entries)
  • The pitch of channels is controlled by directly writing the fixed-point pitch into mixer code at addi.l instructions from 30BAA to 30BC8


With recent updates to Control Manager, and fixes to CPU emulation, we can now proceed in Tetris all the way to gameplay:

It’s not perfect yet, as clearing horizontal rows causes weird graphical corruption on screen – but it appears to be quite stable, music working and levels can be played through (although the graphical glitch makes it playing bit difficult). Here’s also a short video of the status of running Tetris in MACE at this point (with audio):

Tetris intro and one level running on MACE

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