Monthly Archives: February 2018

Trap Dispatcher, native interface, and Memory Manager

Although having graphical output in the framebuffer is neat, and for a graphical system such as Mac a required element, the most critical components of the entire Toolbox emulation are definitely the Memory Manager and Trap Dispatcher:

Trap dispatcher

In a rather clever way, Macintosh system programmers used the A-line (line 1010) handler in the 68K CPU to implement all the Toolbox API calls. How this works is that any 68K instruction, which is of the hex form Axxx (from A000 to AFFF), triggers an A-line exception in the CPU. In this situation, the CPU looks up the exception handler routine address from vector table at index #10 (32-bit address at low memory location 0x28), sets up the exception frame, and calls the exception handler.

On the Mac, the trap dispatcher is responsible for handling these A-line exceptions. The actual A-line opcodes, which triggered the trap dispatcher, are divided to OS traps and Toolbox traps based on bit 11, which are handled differently based on their type. More detailed information about how trap dispatcher works is available in Part 3A of the Mac Almanac II at http://www.mac.linux-m68k.org/devel/macalmanac.php

Native interface

As the Toolbox will be implemented in C, another challenge is interfacing the emulated 68K code with C, especially due to the requirement that a 68K application might want to add their own trap handlers, and also call existing native handlers from the 68K code! To solve this, luckily Apple already did a lot of groundwork by introducing the Mixed Mode Manager in the early 1990s, to help interfacing 68K code with the PowerPC code when Macs switched from 68K to PowerPC CPUs. In this emulator, we will attempt to implement the native calls using Mixed Mode-type UPPs, adding the C calls as a completely new “emulator” ISA type (in addition to Apple’s predefined 68K and PowerPC ISAs).

Memory Manager

With the previously added 32 kilobyte RAM block, we could start work on the Memory Manager. As stated in the goals, the first phase aims for 24-bit compatibility, as the “Classic Mac” macs are anyway limited to 24-bit addressing due to the 68000 CPU’s 24-bit bus, and a lot of software from that era is also not 32-bit clean. The groundwork for Memory Manager was done in such a way, that adding 32-bit support next to the 24-bit implementation should be relatively easy. At this point, we don’t yet have a working trap dispatcher, or a 68K CPU for that matter, but rather use direct C method calls to the required Toolbox API calls.

Output from the DumpZone call of one of the first 24-bit memory manager C prototypes

Laying out the Foundation

The very first step was to figure out how to handle the most core abstraction of the emulation; how to handle memory interfacing. The Mac has a very strict memory map layout, with 68000 vectors residing in the beginning of address space, and all Toolbox low memory globals following them mixed with trap dispatcher routine addresses.

In a pure 32-bit environment, a cheap option might be just to allocate a contiguous area of memory to use as the emulated RAM, and offset the accesses so that emulated address 0x0 (zero) would be actually first byte of this memory block, and access any memory addresses from native code by just offsetting the native pointers. However, the recent transition to 64-bit causes new kind of headache; emulated pointers would be 32-bit while native pointers would require a double the space of 64 bits, not only requiring the offset, but also conversion from different data storage size.

To solve this, a “virtual” memory mapping scheme was devised, in which the emulated RAM would be divided into a number of “blocks”, each of which would have its own handler routine, which would handle the memory access. An additional benefit of this is, that for example VIA space can be mapped to a different handler, which would immediately trigger changes to emulated VIA registers without need to poll them from the generic system RAM; additionally, emulated “fake” ROM space could be implemented to have read-only access.

With this vision, we got a very simple Cocoa-based test app running, which did not yet emulate a real Mac address space, but rather a fixed 32KB RAM and 38KB VRAM for 640×480 monochrome buffer, with a static predefined test image (actually converted to C header with GraphicConverter!) shown in the video below:

A simple 640×480 monochrome framebuffer with static pre-defined raw image being scrolled

Mapping the goals

For a project with a scope as large as this one has, setting goals and planning how to get there is very important. Here’s a rough outline of what we actually want to achieve:

  • Some level of compatibility with all apps ranging from early apps for 128K Mac in 1984 to the last PowerPC apps in early 2000’s
  • Ability to run apps either:
    • Classic-style seamless integration with any host operating system desktop. This would be for any apps/games which use windows and adapt correctly to various desktop sizes.
    • Full-screen exclusive mode. Mostly for full-screen games/apps, and apps designed for fixed-resolution screens such as most of the early games.
  • Sound emulation either through low-level Classic-type sound hardware (often used in early monochrome games), and high-level Sound Manager emulation (for games/apps from the later times).
  • Provide a one-click solution to play any compatible favourite game as a pre-built Mac OS X .app package.

With these goals in mind, this project is divided to various phases, each of which can be considered as kind of a milestone:

  • Phase 1: Motorola 68000 CPU emulation, with “Classic Mac”-type hardware emulation with monochrome-only QuickDraw on 512×342 screen in non-seamless mode, 24-bit Memory Manager, “Sound Driver”-compatible hardware sound emulation and file system emulation, System 6.0.7/7.1 level Toolbox API compatibility.
  • Phase 2: Later 68020/30/40 emulation, with Slot Manager-type video device emulation, 32-bit Color QuickDraw and Sound Manager emulation, better System 7.1/7.5.x API compatibility. Optionally 32-bit Memory Manager support.
  • Phase 3: PowerPC emulation, Mac OS 8-level Toolbox API’s (Appearance, Game Sprockets, Networking).
  • Phase 4: Mac OS 9-level Toolbox API’s, OpenGL compatibility.

The Phase 1 and 2 seem to be most realistic, but if (when!) we actually reach those milestones, we can revisit the plans and see how feasible the rest of the goals look at that point.