• Updated 2023-07-12: Hello, Guest! Welcome back, and be sure to check out this follow-up post about our outage a week or so ago.

Writing an Application that Runs in Mac OS System 1 Using CodeWarrior Gold 11

David Cook

Well-known member
Note: This is only going to be of interest to programmers trying to write code that works on the Mac’s earliest operating systems. I am posting this information so that it doesn’t get lost to time.

Apple Macintosh System 1 (aka 0.97) released on January 24, 1984. Metrowerks CodeWarrior Gold 11 released on December 31, 1996, which is about 13 years later. By then, the last 68K Macintosh, the PowerBook 190, had been discontinued. For some reason, I thought it would be a good idea to use CodeWarrior 11 to write an application that could run on a Mac 128K with System 1.

Tiny Transfer is my little application for sending files back and forth over a serial cable. Its purpose is to make it easier when repairing, testing, and playing with my old computers. Thus, Tiny Transfer needs to work on all Macs and all Mac OSes.

There are a number of challenges to writing an application for Macintosh System 1.0:
  • System 1 officially only runs on the Macintosh 128K, Macintosh 512K and (God forbid) the Macintosh XL. You’ll need to obtain one of these machines and recap it. Or, use Mini vMac with a bespoke build.
  • System 1 is officially limited to 400K disks (*cough* not trying to start the debate about 800K floppy drive support). Floppy Emu is a lifesaver here. Not only does it make it easier to move the development app over, but it saves wear and tear on the mechanical floppy drive.
  • System 1.0 only contains the calls in Inside Macintosh volume I, II, and III. Oh, wait. Not even that. Page I-4 says Inside Macintosh covers the system dated May 2, which is System 1.1
  • It gets worse. Page I-6 explains that the term “[Not in ROM]” does not mean “[On System Disk]”. Believe it or not, many routines were provided by the compiler to build into the application itself.
  • There is not a mechanism for detecting unimplemented traps, because no traps yet existed that weren’t implemented. In fact, the unimplemented trap term isn’t defined until late 1986, where is it called ‘UnimplCoreRoutine’ on page V-316. Adding to the difficulty, the trap table is condensed and partially interleaved to save memory. Thus, checking for a nil or missing OS trap will instead find a completely different trap (from the ToolBox) in that parking space. This is by far the biggest problem in developing applications for the Mac 128K and Mac 512K.
  • System 1.0 has bugs in both the System and the Finder. Is the fault in your program or the System?
  • Only MacsBug 5.x or earlier works [need to verify]. The syntax is different than the 6.x series and the capabilities are limited.
That being said, with enough determination you can get a decent application fully working.

First Crash: Compiler startup code

Metrowerks apparently did not expect System 1 would be targeted by their development environment. I guess System 1 was no longer a commercially large platform in 1996? Compilers insert startup code that runs before your code when your application launches. This startup code initializes global variables and even patches a few traps. Using MacsBug, I determined that a crash was happening before my code even started.

Fortunately, Metrowerks provides the source for the startup code, and they even provide the project file to let you build a custom version. Looking through the source code, we can spot a couple of interesting items.
  • CodeWarrior uses a kinda cool custom PackBits routine for compressing/decompressing your global variables and tables. Steve @bigmessowires might enjoy reviewing it, based on his write-up of FC8 compression (https://www.bigmessowires.com/2016/05/06/fc8-faster-68k-decompression/). It is faster and smaller to initialize your global variables inline with their declaration rather than in SetUp routines. The compiler’s startup code then simply loads all the globals at once from a compressed DATA resource.
1716516721051.png
  • CodeWarrior updates the application jump references for your code and flushes the CPU instruction cache. Oh, the 68000 doesn’t have a CPU cache? System 1 doesn’t have a trap to call for flushing? No problem, CodeWarrior checks to make sure the trap exists. But, the 64K ROM’s space-saving table overlaps with a completely different trap, which does exist. CodeWarrior calls the wrong trap. Blam.
  • CodeWarrior calls MoveHHi, which doesn’t exist in System 1.
  • CodeWarrior patches LoadSegment and UnloadSegment. The Segment traps allow for changing the jump tables as code segments are loaded and moved. But, back then, in the 64K ROMs, you could only patch a trap to the system heap, not the application heap. See Inside Macintosh I-86/87 and II-384
1716516741614.png
The startup code needs extensive rewrites to be fully compatible with a 64K ROM. Instruction flush is unnecessary and MoveHHi is optional, so we can skip those on 64K ROMs. The Segment calls could be patched in the System heap with a jump to the application code. But they are only needed if your code has segments beyond ID #1. CodeWarrior has an option to merge them all into the first segment, thus eliminating the need for the patches.

Startup patch.GIF

Rather than a major rewrite, I simply check for a 64K ROM after initializing the globals and skip over all the other stuff. This allows it to work on the earliest machines without sacrificing this functionality in later systems. Set the linker to a single segment so the patches to the Segment traps aren’t needed. Add the custom startup lib and set the startup configuration to custom.

I've attached my customized Appl68KStartup.c source file in case anyone else needs it.

Second Crash: MemError() isn’t set

Like any good defensive Macintosh programmer, I call MemError() to check for errors after making a memory manager call. I assumed, wrongly, that the returned ptr or handle is garbage if MemError() reported an error. In that case, I set the pointer to nil. That exercised a piece of code that always assumes there is enough memory for a little allocation at initialization time. There was enough memory, but MemError was a lie. My dereference of a pointer that I set to nil resulted in a crash.

MemError is not actually set by the Memory Manager in the 64K ROMs. Instead, Pascal compiler ‘glue code’ grabs the D0 return value and stuffs it into the MemError global variable, such that the following application call to MemError() works. The CodeWarrior developers weren’t unaware of that (or didn’t bother), as Apple fixed this in the 128K ROMs. See IV-79.
MemError doesnt work on 64K rom.GIF
As a developer, you can just just check whether a handle or ptr is not nil when returned. If it is nil, set MemError yourself.

Third Crash: Traps are less tolerant

Usually, I check whether a pointer is nil or a port is open before making a system call with it. I missed one spot where my code asks the serial port if there are incoming bytes. This worked in later systems, because the OS checked the port parameter and noticed it wasn’t open. In System 1, the call just crashes.

The correct fix is to make sure you don’t call the System with a closed port value.

Fourth Crash: WaitNextEvent?

Okay, this actually happened in System 3.3 when I thought using an emulated HD20 would provide more space to exercise my application. The Hard Disk 20 init startup provides a 400K/800K floppy and HD20 driver, along with the hierarchical file system (HFS).

My application started up, showed the menu bar, and then crashed after about 4 seconds with a bomb box. It was system error 28, which says that the heap and stack overlapped. I assumed a loop was filling memory or recursion was happening on a callback.

While stepping through the code with MacsBug, it showed my application calling WaitNextEvent, not GetNextEvent. What? That’s only available with Multifinder or System 7.

Surprise! The OS trap for HFSDispatch (implemented by the Hard Disk 20 init) has the same final digits as WaitNextEvent. Checking the smushed-together 64K ROM trap table wrongly indicates WaitNextEvent is implemented, when it’s actually the HFSDispatch patch. Calling the wrong routine with unexpected parameters every loop eventually fills up memory via the stack.

Macintosh Technical Note #158 provides the solution: WaitNextEvent is never supported on 64K ROMs.

WaitNextEvent.GIF

Fifth Crash: HOpenResFile and HCreateResFile

Don’t get fooled by the letter ‘H’ -- these are not HFS traps. That is, they are not part of the HD20 Init or MacPlus ROM that brought HFS to the Macintosh. You need to check for the existence of these traps specifically, not the existence of HFS. CodeWarrior has nice glue code that does this work for you, and falls back to OpenRFPerm and CreateResFile. However, the glue code fails on the 64K ROMs due to the condensed trap table, which is seems to be consistent source of crashes.

To work around this, just check for the 64K ROMs. If so, use SetVol or HSetVol and then call OpenResFile or CreateResFile directly, rather than using the CodeWarrior glue code.

Sixth Crash: I was wrong. StripAddress in glue code

CodeWarrior’s glue code for HOpenResFile needs more than just a check for 64K ROMs. It also calls StripAddress, which wasn’t available until System 4. So, add that check.

Seventh Problem: SetVol nil name is not the same as an empty name

I had difficulty opening a system file on the second drive, because I passed the empty string (“”) instead of nil for the name of the volume in SetVol. This works fine in later systems, but instead resulted in accessing the system file on the first drive in System 1.0. [Editor’s note: I’m not 100% sure on this one, as enough code changed that this needs to be independently verified.]

Eighth Problem: FileFilter paramBlock is garbage

SFGetFile has the option for your code to review all files before displaying them to the user. System 1.0 has a nasty bug where the parameter with the file info actually points to System code, not file data. I spent a day working this out. They fixed this bug in System 1.1. You need to detect that you are using system 1.0 (see ‘STR ’ 0) and just say ‘yes’ to every file, or I guess just provide a limited file type list instead of a filter routine.

Ninth Problem: Finder comments

This has its own write up in the Tiny Transfer manual. In brief, Apple hates file comments. Macintosh Technical Note #29 describes the hash algorithm to get the comment ID on MFS disks from the DeskTop file. What they don’t tell you is that this was not their first choice. Finder 1.0 with System 1.0 generates a random unique ID for the comment resource, and starts the comment with four bytes indicating the file ID. When the user chooses Get Info, Finder 1.0 goes through all the comments looking for the resource that contains the file ID in it. This is a slow algorithm. So, Apple ditched it for the hash algorithm in Finder 1.1g. Apple also changed the resource structure so the comments are straight Pascal strings. They don’t start with file IDs.

Unfortunately, the difference in IDs and resource contents means all Finder 1.0 comments can’t be read by all other Systems. And Finder 1.0 can’t read comments made by all other Systems. Effectively, this was the first of many times when file comments were effectively lost by Apple.

Tenth Problem: Alert with a Button ID Other Than 1

I noticed this in System 6, but it likely affects other versions of the operating system. If you call Alert (NoteAlert, StopAlert, CautionAlert), but the control with ID 1 is not the OK button, then you’ll get a slightly humorous rounded rectangle around the whatever item is ID 1. Simply make the OK button the first ID.

Eleventh Problem: MultiFinder ‘mstr’ Resources

The original Finder provides a file list for an application to open those files when the application starts up. In the original systems, the Finder goes away as the application launches. Because the Finder did not run at the same time as the application, there was no need for a mechanism for the Finder to open files in an already running application.

Starting in System 7, the Finder can open files at any time by sending AppleScript application events to the running application.

That leaves us with the MultiFinder era, circa System 5.x-6.x. Using OS events and short-circuiting certain traps, MultiFinder simulates the user clicking on the application’s File menu, choosing Open, and then picking the file from the standard file dialog in the application. This works well as most applications had this UI pattern. For applications with menus of a different name, they could include ‘mstr’ resource strings that provided the name of the File menu and Open command. Or, they could include ‘mst#’ resource strings with multiple different menu names to try.

Apple’s technical documentation indicates that ‘mstr’ 103 is the name of the Open menu item. However, the documentation doesn’t note that you must also include ‘mstr’ 102 for the name of the File menu. That is, you must include both strings for the mechanism to work.


I know this posting is a massive brain dump. However, I couldn't find most of these answers searching the Internet. Now, hopefully, other people can.

- David
 

Attachments

  • Appl68KStartup.c (ROM fix).hqx
    32.9 KB · Views: 6

MOS8_030

Well-known member
Nice summary!
Andy Hertzfeld and Steve Capps were geniuses to shoehorn all that functionality into the original Macintosh software.
 

David Cook

Well-known member
Nice summary!
Andy Hertzfeld and Steve Capps were geniuses to shoehorn all that functionality into the original Macintosh software.

Absolutely. I am amazed that the critical functionality needed to create a complete application existed in the very first release of the System. Most of my workarounds only had to do with glue code or detecting HFS. I can only imagine how exhausted they were in January 1984.

It is bittersweet to consider how far ahead the Macintosh was. I think Apple made three mistakes over the next decade.
1. It took too long to release a color Macintosh. The Apple II had color and was released in 1977. The Macintosh II was released in 1987. The lack of color limited the total market size.

2. It took too long to ship a low-end Macintosh. The LC was a smash hit in 1990. The lack of an inexpensive Mac before that again limited the total market size. This impacted the entire ecosystem of software, hardware, books, magazines, and so on.

3. Progress on the System software slowed. They had (in hindsight) an 11 year lead on Windows 95. System 7 was a wonderful step forward but came out in 1991. After that, it seems like all their system engineers were working at Taligent, and that was the end of that.
 

MOS8_030

Well-known member
The 128K-512K Macs were realistically expensive tech demos. They lacked the memory and applications to be truly useful.
Imho, the Plus was the first truly useful Mac.
But you're right, the small screen and lack of color hurt the Mac.
As far a low-end Mac I think it really took until ~1990 or so for the available hardware/components to develop to a point where they could offer a cheaper, yet still worthwhile Mac.
A lot of early Mac decisions were driven by the fact that Apple was still making most of their money from the Apple II.
Profit margins were much higher on the Apple II.
And yeah, they definitely squandered or at least were slow to develop the huge early lead they had with the OS and the UI.
 

Snial

Well-known member
Hi David,

Thanks for the write-up. I wrote a simple System 1 application in MacAsm last year just to have fun with the environment and see how small I could squeeze my application (a Morse code tutor) into: the answer was about 1kB. I think I fudged my way through some of the gotchas you've listed here, just trying something else whenever I had a problem (I certainly remember the Alert Button ID =1 issue).


Absolutely. I am amazed that the critical functionality needed to create a complete application existed in the very first release of the System. Most of my workarounds only had to do with glue code or detecting HFS. I can only imagine how exhausted they were in January 1984.
90 hours a week and lovin' it. https://www.folklore.org/90_Hours_A_Week_And_Loving_It.html
I don't think I could have coped in my 20s!

It is bittersweet to consider how far ahead the Macintosh was. I think Apple made three mistakes over the next decade.
1. It took too long to release a color Macintosh. The Apple II had color and was released in 1977. The Macintosh II was released in 1987. The lack of color limited the total market size.
Didn't Mac development get a bit disorganised after Jobs got sacked? There were a few attempts at a proper Mac successor (like Big Mac), but it took the big personality of Jean Louis Gassée to plug the power vacuum and provide a vision for a new Mac beyond the legacy concept of an appliance. I think also, though, GUIs played a big part in colour Macs taking so long. A colour Mac would need at least 88kB to 175kB of video, which would have soaked up all the memory bandwidth of a 8MHz Mac (for 4-bit colour/ 8-bit colour) and CPU power chucking around the graphics, so you'd either need some combination of: a faster CPU, much better bus design, much faster RAM, separate video RAM, hardware assist. The Amiga and Atari ST got away with it, because their useful colour modes were low resolution (and of course the Amiga had massive HW graphics assist) and earlier 8-bit computers could do colour, because they never did any blitting (just plots, lines and basic fills).

2. It took too long to ship a low-end Macintosh. The LC was a smash hit in 1990. The lack of an inexpensive Mac before that again limited the total market size. This impacted the entire ecosystem of software, hardware, books, magazines, and so on.
Totally true. I first used Macs at my UK University (UEA) from late 1986 and they were as revolutionary (and controversial) as EVs have been in the past decade. They were the future and I knew nothing else would do, but I couldn't afford a Mac until I bought a Performa 400 in early summer 1993.
3. Progress on the System software slowed. They had (in hindsight) an 11 year lead on Windows 95. System 7 was a wonderful step forward but came out in 1991. After that, it seems like all their system engineers were working at Taligent, and that was the end of that.
Also true. But also, the original OS architecture made it hard to radically overhaul (everything in Supervisor mode; Switcher / Multifinder both being pretty crude, 24-bit addressing limitations) and brilliant engineers all pulling in different directions. I'd argue that it was their desire to switch to RISC (first with the M88K) that forced OS development to be accelerated from the end of the 80s.


The 128K-512K Macs were realistically expensive tech demos. They lacked the memory and applications to be truly useful.
I agree about the 128K, but I found the 512K Macs very usable. Through 1985 to mid-1986, 512K competed very well with any existing 8-bitter; the newly emerging Atari-ST and Amiga; and all of the IBM PC/XT/AT models for application complexity (partly thanks to the Segment manager). Lack of hard disk support was the biggest problem. It didn't feel like a toy to me.

But you're right, the small screen and lack of color hurt the Mac.
As far a low-end Mac I think it really took until ~1990 or so for the available hardware/components to develop to a point where they could offer a cheaper, yet still worthwhile Mac.
Agreed.

A lot of early Mac decisions were driven by the fact that Apple was still making most of their money from the Apple II.
Profit margins were much higher on the Apple II.
That's a good insight. I hadn't considered the politics of the Apple ][ holding back the Mac.
 

David Cook

Well-known member
I can't find the video. But, I remember recently seeing a video where the sales/marketing team talked about their marching orders. This was the aha moment for me. Apple already had the K-12 school market. They had quite a bit of the home market. They wanted to break into business. (Hence, the Apple III, the Lisa, and then the Mac).

But it really wasn't the right product. Either the CIO's wanted the IBM logo, or they wanted cheap computers (IBM clones).

This is also why Apple courted Microsoft so much, for 'business' applications Word and Multiplan. Apple mistakenly sold off their database product because existing database companies said they wouldn't bother porting to Mac if they had to compete with Apple's database.

Apple's management artificially constrained the Apple II line (such as the low clock speed in the IIgs) and artificially constrained the Mac line (higher price, no color) to differentiate them from each other. Unfortunately, they should have done the opposite, making the Mac the smooth upgrade path for all the kids growing up on Apple II computers.

Oh well. I still love my old Macs.
 

NJRoadfan

Well-known member
Apple managed to get a color 640x200 GUI running on the Apple IIgs in 1986.... with 256k of RAM (barely, 512k was more reasonable)....and 2.8Mhz of CPU. Apple's focus at the time was certainly on the Macintosh. They couldn't outright abandon the Apple II simply because, unlike the Macintosh, it was a cash cow paying the bills at Apple!

Apple had a big chunk of the business market prior to the release of the IBM PC. This was mostly because of the existence of VisiCalc. They completely blew this lead by screwing up the Apple /// (partly because of Job's insistence on no fans causing heat issues). The Lisa was a solid effort, just mired by a ridiculously high price.
 

Arbee

Well-known member
Apple managed to get a color 640x200 GUI running on the Apple IIgs in 1986.... with 256k of RAM (barely, 512k was more reasonable)....and 2.8Mhz of CPU. Apple's focus at the time was certainly on the Macintosh. They couldn't outright abandon the Apple II simply because, unlike the Macintosh, it was a cash cow paying the bills at Apple!

Ironically for this thread, Apple II users have always blamed the IIgs' slow speed on Sculley wanting to protect the Mac. Apple engineers later said they had approval to run the IIgs at 7.16 MHz but couldn't make it work. Bill Mensch's famous hand-drawn chip mask wouldn't run anywhere near that fast, and fast 65816s weren't available until 1989 after Nintendo paid to have the chip redesigned with then-modern tools. A 7.16 MHz IIgs would've been on par with early color Macs in terms of responsiveness due to the 65816's better instructions-per-clock but the 640x200 resolution would've been limiting enough to keep DTP on the Mac.
 

David Cook

Well-known member
You can really see why they abandoned the interleaved trap tables. They're painful.

Yes, they did most things correctly, in terms of future proofing. I'd say traps and machine global variables were the biggest challenges to moving the OS forward.
 

Snial

Well-known member
Apple already had the K-12 school market <snip> a bit of the home market. They wanted to break into business <snip>.
They had a bit of the business market thanks to VisiCalc and there were a number of business applications. They were already in the creative market (e.g. the Alpha Syntauri synth).
<snip> wasn't the right product.<snip> wanted the IBM logo, or <snip> (IBM clones). <snip> Apple courted Microsoft so much, for 'business' applications Word and Multiplan. <snip>
Clones started to appear in November 1982, so they'd been out for 1.3 years before the Mac and possibly were starting to eclipse IBM PC sales by then:


However, despite the annoyingly US-centric nature of the table (e.g. no ZX Spectrum, nor BBC micro, despite selling better than some of the computers listed) and literally no reference to CP/M (!!!?!) I think it's fair to say that the period around 1984 was very much in transition. It was a transition from the 8-bit market to the 16-bit market and a boom time for home computers (which became powerful enough to do 8-bit business applications).

1716817905527.png

And the primary problem for all the legacy 8-bit and home computer companies was how to manage these transitions. What technologies would emerge? Apple's goal with the Mac, correctly, was to observe that the vast majority of people couldn't use a traditional DOS-based computer and only a tiny % actually owned one (certainly in the US, more people in the UK already owned - cheap - computers).

So, it was hard to navigate the whole process. The IBM PC took a traditional approach whereby it didn't matter if ordinary people couldn't use a DOS-based computer, office users only needed to know how to use the applications managers decided. Boot: then type WS at the A> prompt and you're in WordStar. Or a bit later type LOTUS (maybe) and you're in Lotus-1-2-3. Only a subset of people needed to actually understand DOS commands and they were given the job of IT support.

But Apple and for that matter all other microcomputer companies couldn't copy that. People weren't going to stick their computers alongside IBM PCs (why have 2 systems?) and they couldn't easily switch to PCs without crashing their existing market (which is partly what killed Commodore) and they couldn't just ignore business uses (because people wanted to do word processing etc at home too, as they started to afford printers and disk drives).

All computer companies faced this dilemma. In the UK, for example, Sinclair produced a 68K-based business computer in 1984 called the QL. I had one, the 4 integrated packages were great for the day (Word Processor, Spreadsheet, Database and Charts), but the rush to get it out and the choice of an underpowered 68008 killed it. Acorn had our educational market (like Apple, no coincidence with fruity names beginning with 'A'), but their amazing 2MHz, 6502, BBC Micro in 1982 couldn't fight off insurgent PCs into schools in the late 80s and neither could their GUI, ARM-based wonderkind Archimedes struggled despite being much faster than contemporary PCs (ARM only survived thanks to Apple's investment for the Newton). Commodore got squeezed into the games and graphics/frame-grabber market (with the Amiga) while Atari got squeezed into the games and MIDI musical computer with the ST.

It's the same story at the upper end: Sun made a name with high-powered 680x0 workstations; joined by SGI (for their 3-D graphics pipeline) which they managed to compete well with for 1.5 decades thanks to their RISC revolution, but eventually got squeezed out as PCs crossed their power/price envelope.

The fact that the Mac alone survived despite Apple's numerous mistakes argues well that it was, in a way, the right product. It was visionary enough, fast enough, serious enough, fun enough and had enough flexibility to see it though iterations that provided enough business support while being able to excel in graphics, music, video, education and the arts for the next 40 years. The Apple ][ or /// family couldn't have done that, so as cruel as it seemed at the time to deprecate the ][, it was the only choice Apple could make.

<snip> Unfortunately, they should have done the opposite, making the Mac the smooth upgrade path for all the kids growing up on Apple II computers.
And maybe they could have done that. They could have provided a Mac with a 320x200 x 3 bpp (or a 192x 120 display x 8 bpp) from the start as well as the standard 512x342. But a colour GUI Mac would have been released even later (probably 1985) and would have been more expensive, but looked underpowered or overvalued compared with the Atari ST and Amiga. So, I think Apple's constraints for the 1984-1985 timeframe would still have forced a monochrome Mac on the market and the prior work on the Lisa forced it to be a mini-Me style computer. In this case, for Apple they had a first-mover disadvantage.

Oh well. I still love my old Macs.
As we all do :) !
 

Arbee

Well-known member
The Mac was absolutely the right product, and in hindsight they could've done a much better bridge machine. The IIgs was famously originally conceived as the "IIx/"Golden Gate", basically a Mac Plus and an enhanced IIe (with a 65816 to be able to access the entire 4 MB of RAM) bolted together. I can't imagine Apple selling a machine like that anywhere near cheap enough, but it would've been a much cleaner path forward for users than rewriting the entire Toolbox for another processor.

Chasing down one more sub-thread, the lack of a fan causing the /// to fail is an urban legend. The hardware designers have said the problem was cheap edge connectors with tin contacts that oxidized if you looked at them funny on the RAM board plugged into the motherboard. The recall replaced the connectors with gold-plated contacts and those machines were as reliable as Apple IIs. People want to drag Jobs into everything, but sometimes he actually was innocent. :LOL:
 

siddhartha

New member
Did you ever solve the problem of determining if a trap is implemented under System 1?

For example, how to tell if _Gestalt is available... Due to the condensed trap table, _Gestalt has the same [N]GetTrapAddress() as _RmveResource, so it will return a valid trap address even if it's unimplemented.

I'm thinking of just checking for System version 6.0.4 in 'STR ' 0 at this point and using the Gestalt Manager if so.
 

cheesestraws

Well-known member
Did you ever solve the problem of determining if a trap is implemented under System 1?

I'm not sure you totally reliably can.

I'm thinking of just checking for System version 6.0.4 in 'STR ' 0 at this point and using the Gestalt Manager if so.

The "approved" way of checking for the Gestalt was, if I remember correctly, to check system version, but to use SysEnvirons. Compilers are supposed to provide glue for situations where the OS doesn't support SysEnvirons, although how well that works in newer compilers I don't know. Worth a try though.
 

David Cook

Well-known member
The "approved" way of checking for the Gestalt was, if I remember correctly, to check system version, but to use SysEnvirons. Compilers are supposed to provide glue for situations where the OS doesn't support SysEnvirons, although how well that works in newer compilers I don't know. Worth a try though.

CodeWarrror used the Apple glue code. (I assume Apple supplied their glue code to all the compiler companies?)

The first thing SysEnvirons (Apple source file "EnvironsGlue.a") does is check for the old ROM. If the ROM is 64K, it doesn't bother performing a trap check, it just immediately jumps to its own substitute code. It fills in all the fields except systemVersion, which is initialized to 0. This is safe to call on the oldest Macs all the way back to System 1.

For Gestalt, Apple's source code is GestaltGlue.a. Unfortunately, it does not check for old ROMs and instead performs a trap check which does not work on 64K ROMs. Apple's glue code is not safe to call on the oldest Macs. No big deal, as Gestalt is never implemented on 64K ROMs.

On startup, I set some global variables, which I then check in my utility wrapper routines so that my application can call Gestalt, HFS, etc, without worry. The utility wrapper routines perform the checks before calling the OS.
bool gIsNewerRom; // Low memory global check
bool gIsHFSAvailable; // Low memory global check
bool gIsGestaltAvailable; // Never available on 64K ROMs. Otherwise Trap check
bool gIsWaitNextEventAvailable; // Never available on 64K ROMs. Otherwise Trap check
bool gIsSystemSevenOrBetter; // SysEnvirons

Did you ever solve the problem of determining if a trap is implemented under System 1?

Probably, yes. You have three checks to perform before comparing the trap to Unimplimented.

1. Is it a Tool trap or OS trap. trapNumber & 0x0800 is not zero for Tool traps.
2. Is the Tool trap table limited to 0x200 i.e. _InitGraf trap address == 0xAA6E trap address
3. Is this the 64K ROM? LMGetROM85() < 0

Apple's published example code handles the first two cases. The third case is where you have to code against hard-coded knowns. That is, we know exactly where the 64K ROM traps overlap Tool/OS traps and we know the exact size of the 64K ROM trap table. Unless Apple suddently goes retro and releases a new OS for the original Mac, we are safe hard coding these values.

1719853685249.png

Mask out the high bits of the trap. OS traps are 0x04F and below, and also 0x054 and 0x057. Tool traps are 0x050 up to 0x19F, except for 0x054 and 0x057. If the requested trap does not fall in that range, it is not implemented. Otherwise, check the trap addresses against Unimplemented.

- David
 

cheesestraws

Well-known member
For Gestalt, Apple's source code is GestaltGlue.a. Unfortunately, it does not check for old ROMs and instead performs a trap check which does not work on 64K ROMs. Apple's glue code is not safe to call on the oldest Macs. No big deal, as Gestalt is never implemented on 64K ROMs.

I'm pretty sure you're meant to check SysEnvirons and infer from the OS version whether the Gestalt is available rather than just checking whether the trap exists or not, for precisely this reason.
 

David Cook

Well-known member
I'm pretty sure you're meant to check SysEnvirons and infer from the OS version whether the Gestalt is available rather than just checking whether the trap exists or not, for precisely this reason.

Agreed. For 99% of the programmers (excludes raw assembly guys), that approach works beautifully. I haven't checked to see if Apple or anyone else ever produced an init that implemented Gestalt in earlier versions of the system. Or, maybe Apple snuck in Gestalt prior to 6.0.4, just not officially?

1719855659858.png
Personally, I check for >64K ROMs and then just call Gestalt asking for its version number, relying on the glue code to check the trap address availability. If no error, then Gestalt is available.

- David
 

siddhartha

New member
Mask out the high bits of the trap. OS traps are 0x04F and below, and also 0x054 and 0x057. Tool traps are 0x050 up to 0x19F, except for 0x054 and 0x057. If the requested trap does not fall in that range, it is not implemented. Otherwise, check the trap addresses against Unimplemented.

Thank you, this was a big help. What I wound up doing:

C:
/*
    ===========
    checkTrap.h
    ===========
*/
#include <Gestalt.h>
#include <LowMem.h>
#include <Traps.h>

enum {
    kTrapTypeMask       = 0x0800,
    kTrapMask           = 0x07ff
};

enum {
    kInitGrafTestTrap   = 0xAA6E
};

enum {
    kTrapCountSmall     = 0x200,
    kTrapCountBig       = 0x400
};

enum {
    k64KROMOSTrapMin    = 0x000,
    k64KROMOSTrapMax    = 0x04f,
    k64KROMOSTrapEx1    = _UprString,
    k64KROMOStrapEx2    = _SetAppBase,
    
    k64KROMToolTrapMin  = 0x050,
    k64KROMToolTrapMax  = 0x19f
};

static short NumToolboxTraps(void);
Boolean IsTrapImplemented(long trap);

/*
    ===========
    checkTrap.c
    ===========
*/
#include "checkTrap.h"

static short NumToolboxTraps(void) {
    if ( NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(kInitGrafTestTrap, ToolTrap) ) {
        return kTrapCountSmall;
    } else {
        return kTrapCountBig;
    }
}

Boolean IsTrapImplemented(long trap) {
    short       romSize;
    Boolean     isToolTrap;
    
    isToolTrap = (trap & kTrapTypeMask) == 0;
    
    if (NumToolboxTraps() >= kTrapCountBig) {
        romSize = LMGetROM85();
        
        /* 64K ROM */
        if (romSize < 0) {
           trap &= kTrapMask;
          
            if (isToolTrap) {
                if ((trap >= k64KROMToolTrapMin && trap <= k64KROMToolTrapMax) ||
                        trap != k64KROMOSTrapEx1 || trap != k64KROMOStrapEx2) {
                    return true;
                } else {
                    return false;
                }
            } else {
                if ((trap >= k64KROMOSTrapMin && trap <= k64KROMOSTrapMax) ||
                        trap == k64KROMOSTrapEx1 || trap == k64KROMOStrapEx2) {
                    return true;
                } else {
                    return false;
                }
            }
        } else {
            if (NGetTrapAddress(trap, isToolTrap) != NGetTrapAddress(_Unimplemented, ToolTrap)) {
                return true;
            }
        }
    }
    
    return false;
}
 

David Cook

Well-known member
Another tip: Since every byte matters on a Macintosh 128K, if you are using the CodeWarrior's default 'Smart' code model (rather than small or large), CodeWarrior will only try to generate the smaller/faster jump instructions if the subroutine appears earlier in the same source file.

1727193718915.png

void foo(void)
{
SysBeep(1);
}

void main(void)
{
foo(); // will jsr 0x1234
bar(); // will jsr 0x12345678
}

void bar(void)
{
SysBeep(2);
}

Surprisingly, CodeWarrior doesn't seem to ever generate a BSR (only JSR) in this code model.

If you have more than 32K of code, and thus can't use the small code model in a single segment, simply reordering your subroutines to the start of each c source file will make a size difference.

- David
 
Top