These are just some notes from trying to get some of my existing System6 & 7 software working on System 3.3 for a 512KE system. These earlier systems lack a bunch of functionality that became standard with the SE and Mac II.
When trying to get newer software working on older systems, having MacsBug installed to catch unimplemented traps is invaluable. I'm using MacsBug 6.1 and it works great. If there's an unimplemented trap, 1) macsbug will be entered and tell you, and 2) the trap number will be in the register list on the left side of the screen. Look for 0xAXXX.
When building code, whether a code resource, device driver, or an application, the compiler will insert glue code for you. CodeWarrior's glue code for all of the above targets will call the trap _StripAddress, which was introduced with the SE/MacII for 24/32bit address handling. It doesn't exist on earlier releases. If you want to stick with CodeWarrior to build code targeting these older systems, you can replace calls to _StripAddress with nops, since the older system is al 24bit all the time. This is a bad idea on newer systems though. The proper way to do this would be to test for the existence of the trap and call it conditionally. But that's an involved patch for glue code. The easiest way to nop them out is to open your project in Resorcerer, and check out your code resources (DRVR, INIT, CODE, etc.). _StripAddress is 0xA055.
THINK C 5's glue code doesn't do this. However, THINK C's device driver glue code is... involved. And broken. When developing device drivers in THINK C, it will always create a DATA resource, which contains the constant data from the DRVR. Instead of using PC relative constant data (strings, etc), it puts it all into a separate resource, and then has glue code that will load the DATA resource for you automatically when your DRVR is entered. Unfortunately, it makes a bunch of assumptions when doing so. On entry into your DRVR, it will inspect your device control entry parameter (passed to all uses of the DRVR), and find your reference number, from which your Unit Table entry can be found, and then assumes your DRVR resource will 1) be in the currently opened resource file, and 2) will have a resource ID matching your unit table entry number, 3) your DATA resource ID will match all of the above. This all bad and makes actually loading and using DRVRs developed in THINK C very difficult.
This driver loading code is easy to use and extensively patches the THINK C driver at load time to make sure things actually work. YMMV
However, for applications, THINK C is pretty easy. Although I'm still trying to figure out how to effectively use THINK C with Resedit created resources. It wants to compile resources from the Rez specification language rather than merge in pre-compiled resources generated with something like Resedit. Additionally, when compiling an application, THINK will overwrite the target rather than merge in the compiled resources. So after every build, you'll need to copy & paste in your precompiled resources by hand.