ThinkC [Study Group 0] Getting your Development Environment Setup & Hello, World!

Relating to ThinkC Development

Mu0n

Active Tinkerer
Oct 29, 2021
609
560
93
Quebec
www.youtube.com
If I had a Macintosh Plus 1MB would that be enough to follow along ?

as the target machine on which your compiled + built-into-app code will run, no problem.

as the development machine, no. you'd have to go way back with an old C compiler, like MegaMax C, with less pre-programmed goodies and keywords (some globals we take for granted in THINK C, you'd have to hunt down the equivalent hex address, for example) and I'm not even sure even then that it would allow only with 1 Mb (I'm not familiar enough with it).
 
  • Like
Reactions: CPlusPlus

CPlusPlus

New Tinkerer
Aug 22, 2022
4
2
3
as the target machine on which your compiled + built-into-app code will run, no problem.

as the development machine, no. you'd have to go way back with an old C compiler, like MegaMax C, with less pre-programmed goodies and keywords (some globals we take for granted in THINK C, you'd have to hunt down the equivalent hex address, for example) and I'm not even sure even then that it would allow only with 1 Mb (I'm not familiar enough with it).
Okay what compact Mac would be good for this ? Is anyone following along with a vintage Mac ?
 

Patrick

Tinkerer
Oct 26, 2021
434
1
224
43
>Okay what compact Mac would be good for this
any that has at least 2MB of ram.


1MB is the minimum with think C 5.0. so it will work with your Macplus. But it will be a challange.


What you could do is run an emulator on your modern computer. like mini vmac or one of the emulators listed by OP and then you can have lots of ram and run system 7
 
  • Like
Reactions: CPlusPlus

CPlusPlus

New Tinkerer
Aug 22, 2022
4
2
3
>Okay what compact Mac would be good for this
any that has at least 2MB of ram.


1MB is the minimum with think C 5.0. so it will work with your Macplus. But it will be a challange.


What you could do is run an emulator on your modern computer. like mini vmac or one of the emulators listed by OP and then you can have lots of ram and run system 7
Okay I may just do that then. Thank you.
 

Crutch

Tinkerer
Jul 10, 2022
293
228
43
Chicago
From memory THINK C 4 (a perfectly good system, as a THINK C 5 … don’t worry too much about the version number really) will run beautifully on a Mac Plus. Give it a try!
 

atariorbit

Tinkerer
Nov 1, 2021
27
25
13
If I had a Macintosh Plus 1MB would that be enough to follow along ?

It should be good if you use v5 and not 6. I'm using 5 since if it's good enough for jcs it's good enough for me.

For ThinkC 5v:

Minimum Requirements: Macintosh Plus 1 MB RAM (2 MB Recommended) System 6.0
  • Debugger requires 2 MB RAM and MultiFinder.
 
  • Like
Reactions: Cashed and Patrick

BFEXTU

Tinkerer
Jul 15, 2022
177
147
43
If you really want a retro MacPlus experience and would like to learn Pascal instead of C, Turbo or LightSpeed Pascal might work (but different from C, of course). Alternately, MacSEs are relatively cheap ($50-150) and go to 4Mb. Otherwise, don't worry about the Mac and just install SheepShaver and run it virtually on a new PC or Mac -- definitely faster compile times.

Also, for drawing stuff in windows, it is usually best to save and restore the drawing port. Below is some very minimal example code that may involve some foreign concepts (like pointers, defines, data types, prototypes, digital vampirism, rodent dietary habits, etc.). The main thing to note is how to save and restore the port. Otherwise, maybe some of the other C stuff will help. Here is some example code below that may be slightly ahead of where you guys are right now, but maybe it will help. I am not at all trying to bust the lesson plan. So, if you find anything below confusing, just ignore it. You will get to it eventually. :)

Code:
GrafPtr     oldPort;

GetPort(&oldPort);     // save the current port -- (passes with '&" -- a pointer to the local storage location for saved GrafPtr)
SetPort(myPort);        // set the target port (where GrafPtr = WindowPtr)

[call a bunch of QuickDraw functions to draw text and pixelated anime woodland creatures trying not to get bitten by vampires]

SetPort(oldPort);     // restore the port

OR...in terms of scope, if you are delegating to dedicated graphic subroutines that expect the caller to manage the port and are only concerned about pixel manipulation, then you wouldn't need to call GetPort or SetPort in those subroutines. But, be careful and make sure you are not drawing somewhere you don't expect. Problems can and do occur -- like calling a subroutine that expects the port to be managed, then calling that same code from somewhere else when you forget you are supposed to manage the port and aren't doing it. In C++, this kind of problem might be less likely, depending on how you define your objects in terms of public/private routines. In C, it is easier to be naughty.

Here is a short example of delegating to a dedicated drawing routine that does not save/restore the port, plus some other tidbits:

Code:
// prototypes -- a prototype is a definition or specification of a procedure or function and terminates with a ";"
void BuffyTheBoxSlayer(Rect *r1, Rect *r2);
void BoxMagic(GrafPtr myPort);

// defines
#define kFatChipmunkWidth 512  // someone has been eating a few too many acorns!

// routines
// dedicated graphics subroutine that assumes it owns the port, so no port saving
void BuffyTheBoxSlayer(Rect *r1, Rect *r2)    // note that routines have no ";" at the end, "void" just means it returns nothing
{
   [some cool rectangle drawing stuff that can go wild without any port saving or restoring]
// here is how to dereference a pointer to a rectangle passed from another function to get to its members (top, left, bottom, right)
   r1->top = 0; // or (*r1).top
   r1->left = 0; // or (*r1).left..etc.
   r1->bottom = (*r2).bottom;   // like this
   r1->right = kFatChipmunkWidth; // or set a value with a constant
   FrameRect(r1);    // draw the new rectangle -- in this case, passing 'r1' passes the address of  (a pointer to) your Rect storage in BoxMagic,
                               //  or just passing along what was passed to you in this routine, like forwarding a phone call.
                               //  So, r1 passed in above is the pointer to the Rect r1 in BoxMagic, and when you want to see a field it points to,
                               //  you have to use either an * and  . [(*r1).top] or a -> [r1->top].
}

// the graphics calling routine that does all the port management
void BoxMagic(GrafPtr myPort)
{
     GrafPtr   oldPort;
     Rect       r1,r2;                               // define local storage for the rectangles

    GetPort(&oldPort);                       // save the current port (passes with '&" -- a pointer to the local storage location for saved GrafPtr)
    SetPort(myPort);                          // where WindowPtr = GrafPtr
// assume you initialize the Rects to something useful like myPort->portRect, etc.
    BuffyTheBoxSlayer(&r1,&r2);      // call Buffy to slay the boxes -- delegating to a dedicated drawing routine that assumes it owns the port
    SetPort(oldPort);                          // restore the port

n.b. The Rect is a very important variable type to learn. It consists of 4 integer fields -- top, left, bottom, right -- the coordinates of the corners of the rectangle. So, in your routines (and the above example), you use the type "Rect" which allocates the local storage for the Rect(s). Then, when you want to pass a Rect to a subroutine, you use an '&' which means that you are taking the effective address or location of that Rect in memory and telling the subroutine (...or pointing to the Rect...aka a "pointer") where it is. C has certain limits on parameter passing -- you will learn about them later...but that's why it's necesssary to use & and pass it as an address. More to learn for later.

Also, because you defined a "prototype" for BuffyTheBoxSlayer, the compiler has no trouble understanding the exact specification for the interface to that routine and will do the right thing when it compiles your code. Defining prototypes also helps ensure that your compiler will (almost always) be able to spit out type-checking errors when programmers screw up, which happens frequently. :D I say "almost always" because there are sometimes compiler bugs or exotic typing issues that do not throw errors -- or there may be no errors because some careless programmer has explicity told the compiler something is the expected type, even though doing that may cause another error later on. Using prototypes and header files are good habits to develop and necessary to define the calling interface to your code -- they will also help you create portable/reusable code and prevent a multitude of sins.

Again -- sorry -- not trying to skip ahead or cause confusion. Hope the above info is more helpful than not! ;)
 
Last edited:
  • Like
Reactions: Cashed

CPlusPlus

New Tinkerer
Aug 22, 2022
4
2
3
If you really want a retro MacPlus experience and would like to learn Pascal instead of C, Turbo or LightSpeed Pascal might work (but different from C, of course). Alternately, MacSEs are relatively cheap ($50-150) and go to 4Mb. Otherwise, don't worry about the Mac and just install SheepShaver and run it virtually on a new PC or Mac -- definitely faster compile times.

Also, for drawing stuff in windows, it is usually best to save and restore the drawing port. Below is some very minimal example code that may involve some foreign concepts (like pointers, defines, data types, prototypes, digital vampirism, rodent dietary habits, etc.). The main thing to note is how to save and restore the port. Otherwise, maybe some of the other C stuff will help. Here is some example code below that may be slightly ahead of where you guys are right now, but maybe it will help. I am not at all trying to bust the lesson plan. So, if you find anything below confusing, just ignore it. You will get to it eventually. :)

Code:
GrafPtr     oldPort;

GetPort(&oldPort);     // save the current port
SetPort(myPort);        // set the target port (where GrafPtr = WindowPtr)

[call a bunch of QuickDraw functions to draw text and pixelated anime woodland creatures trying not to get bitten by vampires]

SetPort(oldPort);     // restore the port

OR...in terms of scope, if you are delegating to dedicated graphic subroutines that expect the caller to manage the port and are only concerned about pixel manipulation, then you wouldn't need to call GetPort or SetPort in those subroutines. But, be careful and make sure you are not drawing somewhere you don't expect. Problems can and do occur -- like calling a subroutine that expects the port to be managed, then calling that same code from somewhere else when you forget you are supposed to manage the port and aren't doing it. In C++, this kind of problem might be less likely, depending on how you define your objects in terms of public/private routines. In C, it is easier to be naughty.

Here is a short example of delegating to a dedicated drawing routine that does not save/restore the port, plus some other tidbits:

Code:
// prototypes -- a prototype is a definition or specification of a procedure or function and terminates with a ";"
void BuffyTheBoxSlayer(Rect *r1, Rect *r2);
void BoxMagic(GrafPtr myPort);

// defines
#define kFatChipmunkWidth 512  // someone has been eating a few too many acorns!

// routines
// dedicated graphics subroutine that assumes it owns the port, so no port saving
void BuffyTheBoxSlayer(Rect *r1, Rect *r2)    // note that routines have no ";" at the end, "void" just means it returns nothing
{
   [some cool rectangle drawing stuff that can go wild without any port saving or restoring]
// here is how to dereference a pointer to a rectangle passed from another function to get to its members (top, left, bottom, right)
   r1->top = 0; // or (*r1).top
   r1->left = 0; // or (*r1).left..etc.
   r1->bottom = (*r2).bottom;   // like this
   r1->right = kFatChipmunkWidth; // or set a value with a constant
   FrameRect(r1);    // draw the new rectangle -- in this case, passing 'r1' passes the address of  (a pointer to) your Rect storage in BoxMagic,
                               //  or just passing along what was passed to you in this routine, like forwarding a phone call.
                               //  So, r1 passed in above is the pointer to the Rect r1 in BoxMagic, and when you want to see a field it points to,
                               //  you have to use either an * and  . [(*r1).top] or a -> [r1->top].
}

// the graphics calling routine that does all the port management
void BoxMagic(GrafPtr myPort)
{
     GrafPtr   oldPort;
     Rect       r1,r2;                               // define local storage for the rectangles

    GetPort(&oldPort);
    SetPort(myPort);                          // where WindowPtr = GrafPtr
// assume you initialize the Rects to something useful like myPort->portRect, etc.
    BuffyTheBoxSlayer(&r1,&r2);      // call Buffy to slay the boxes -- delegating to a dedicated drawing routine that assumes it owns the port
    SetPort(oldPort);                          // restore the port

n.b. The Rect is a very important variable type to learn. It consists of 4 integer fields -- top, left, bottom, right -- the coordinates of the corners of the rectangle. So, in your routines (and the above example), you use the type "Rect" which allocates the local storage for the Rect(s). Then, when you want to pass a Rect to a subroutine, you use an '&' which means that you are taking the effective address or location of that Rect in memory and telling the subroutine (...or pointing to the Rect...aka a "pointer") where it is. C has certain limits on parameter passing -- you will learn about them later...but that's why it's necesssary to use & and pass it as an address. More to learn for later.

Also, because you defined a "prototype" for BuffyTheBoxSlayer, the compiler has no trouble understanding the exact specification for the interface to that routine and will do the right thing when it compiles your code. Defining prototypes also helps ensure that your compiler will (almost always) be able to spit out type-checking errors when programmers screw up, which happens frequently. :D I say "almost always" because there are sometimes compiler bugs or exotic typing issues that do not throw errors -- or there may be no errors because some careless programmer has explicity told the compiler something is the expected type, even though doing that may cause another error later on. Using prototypes and header files are good habits to develop and necessary to define the calling interface to your code -- they will also help you create portable/reusable code and prevent a multitude of sins.

Again -- sorry -- not trying to skip ahead or cause confusion. Hope the above info is more helpful than not! ;)
I actually have a MacBook Pro 14 M1 Max should I just use it as an emulator ?
 
  • Like
Reactions: Patrick and BFEXTU

BFEXTU

Tinkerer
Jul 15, 2022
177
147
43
Yes - just install SheepShaver with an OldWorldROM -- should work fine. I run it under Windows 11. It's not the most intuitive setup, but there are online guides, etc., to get you past any uncertainty -- also other Mac users here. I installed it under Windows 11, so can't help too much.
 

atariorbit

Tinkerer
Nov 1, 2021
27
25
13
I actually have a MacBook Pro 14 M1 Max should I just use it as an emulator ?
I have a nice vMac zip file with everything you need to run - with think C 5 installed, MAC II ROM - ready to go. Works on my Apple Silicon Mac and my Intel macpro. PM me if you want the file (170MB).
 
  • Like
Reactions: Cashed

BFEXTU

Tinkerer
Jul 15, 2022
177
147
43
nitpic : Mac pluses can also go up to 4MB of ram.
Yes - sorry for omitting - I was in a hurry and my statement was not exclusionary. Thanks for adding the extra note. I actually own a 4mb MacPlus and should add it as a revival project thread. However, SE's are far superior, provide a much better baseline development experience in terms of the expanded toolbox, ADB, SCSI, etc., and they are dirt cheap. They may be as cheap as $50, albeit with possible PS and floppy issues -- but still relatively cheap to resolve. However, all that being said -- emulation is probably the best answer in this case.
 
  • Like
Reactions: Patrick

jupo

New Tinkerer
Oct 31, 2021
3
10
3
Tucson, AZ, USA
Using my SE/30, currently with "only" 16MB RAM (I took a second 16MB out because I'm planning to get a 64MB set soon), and System 7.5.5.

SG0-hello.jpg
 

Daniel Hansen

Tinkerer
Oct 29, 2021
177
133
43
I'm set up on a IIci, but @pfuentes69 has me re-evaluating... 😄

Anyway, getting set up and going through the first two chapters was pretty painless - I was up and running with @Mu0n 's resource kit in just a few minutes, and Chapter 2 has very gentle slope. While I have some coding background (but not with a workflow like this), I'm pretty confident in saying that this book (so far) is very beginner-friendly. Excited for Chapter 3!
 

Daniel Hansen

Tinkerer
Oct 29, 2021
177
133
43
Agree, however it does assume one knows C. So if anybody knows zero about C. maybe pick up the K&R book or something to just focus on the language itself. Which i think the book mentions also.
I didn't really pick up on that as it seems to be holding you by the hand just to get this first app up and running. I expect the book will flesh things out a bit more as we go. But yeah good point - it's super helpful (necessary even?) to have a language reference like K&R alongside a primer like this - seems like K&R is the de facto standard?
 
  • Like
Reactions: BFEXTU and Patrick

BFEXTU

Tinkerer
Jul 15, 2022
177
147
43
Powerbooks are solid machines for programming in THINK C, I've done it a lot when I was working in another city during the week. Brought my trusty Powerbook 180.
You need to totally CSS the crap out of that MacOS menubar on your site. Drop-Down Glory!!