Category Archives: Audio

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: http://www.the8bitguy.com/product/planet-x3-for-ms-dos-computers/

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:

Disassembler

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

Tetris

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