Programming Objective C

Well I’ve been busy with work, and whatnot, however I’ve managed to get Frontier to render in colour now! And fix an annoyance in UAE on NeXTSTEP.

Someone pointed me to this:


Simson Garfinkel seems to have released this awesome boook… At least I don’t feel like I’m flailing about in the dark now.

Also I’ve googled about and found that MAME has an OpenSTEP interface, which I’ll be taking apart to see if it “fits” more then the UAE one which sputters about too much for my liking.

Failing that I’ll have to break down and really learn to program in NeXTSTEP “properly”.

Frontier Elite II for NeXTSTEP

Well I couldn’t resist. Currently there is no keyboard input, but after hacking the ObjectiveC from UAE, I was able to get GLFrontier to compile and run it’s intoduction under NeXTSTEP 3.3 on VirtualBOX…

I guess I’ll have to break down and order some new hard disks, and a PCI video card to rebuild me a whitebox.

In the meantime, here is a two bit picture:

Image of Frontier Elite II on NeXTSTEP 3.3

Amiga Days

Do you remember the Commodore Amiga?

I certainly do!

While I was busy fiddling with my Commodore 64, a friend of mine got an Amiga in high school. The 64 was cool, but I was simply blown away by the Amiga. To say there was a gulf between the 8 bit machine, and the quasi 32/16 bit machine would be a massive understatement. His Amiga 2000 could do all kinds of neat things, from talk, run IBM XT software at 100% speed via a “bridge board”, not to mention play super snazzy games. It took me a few years to save up to buy my very own Amiga 500, and once I was ready I didn’t have enough money for a bus ride home, so I walked the FIVE miles home toting my Amiga! I was so happy to say the least!! (At least it was summer, it was flat, and there weren’t any tropical waves/hurricanes around…)

While there are a few emulation alternatives for running the old m68000 software on all kinds of machines, I’m going to talk about AROS today.

AROS is to AmigaDOS what Wine is to Windows. Once it became clear that Commodore was going to die, and that AmigaDOS and the Amiga were lost a few brave people decided that they had to take matters into their own hands. Sadly there was lots of in fighting, a tradition of the comp.sys.amiga.advocacy news group where some people get too tied into little details and let little things (like the rise of Microsoft Windows) pass them by. At any rate, Aaron Digulla knuckled down, and start to write a bug for bug clone of AmigaDOS 3.1 in C to run on the IBM PC. The result of which is AROS. It currently will either run hosted on Linux/NetBSD or natively on the i386. There is a port to the Amd64 undergoing right now. AROS even has SDL support.

So this got me thinking..

What if I were to remove all the OpenGL calls from GLFrontier, and ran it as a strictly 2d app on AROS?

So I took the first step, and I trimmed out the OpenGL support from Tom’s fix of Frontier, so it’s completely SDL 2d friendly, and cross compiled it to AROS. I built the first pass on MinGW, and got frame rates of upwards of 1000 on Vista 64!

Now for the fun part of building an AROS version. While there is now a native GCC for AROS it cannot compile the assembly listing from GLFrontier.. I suspect it’s a heap overflow. This means you have to cross compile. I have cygwin installed in a Windows 2000 VM I use with Virtual PC 2007. I’m not sure if cygwin installs on Vista, let alone Vista 64, however I do suspect it MAY have issues… I keep my dev stuff in a VM so I can move it around without losing my settings.

I downloaded and unpacked the following files into my cygwin installation:


And the SDL include directory from cygwin.. I think its SDL version 1.2

I did have to find all the ‘exe’ files and make sure they were chmoded +x as the compiler would not run (chmod +x /usr/local/bin/exe /usr/local/i386-aros/bin/ /usr/local/lib/gcc-lib/i386-aros/3.3.1/*exe ). Once you are ready you should be able to build files with


I’m not sure how to build stripped files, but I suspect it’s out there somewhere. At any rate, I did this to compile my SDL 2d version of FrontierGL:

$ cd as68k

$ make
gcc -O2 -g -Wall -c -o output.o output.c
gcc -O2 -g -Wall -c -o output_c.o output_c.c
gcc -O2 -g -Wall -c -o output_i386.o output_i386.c
output_i386.c:981:1: warning: “_S” redefined
In file included from output_i386.c:6:
/usr/include/ctype.h:35:1: warning: this is the location of the previous definition
output_i386.c: In function

output_i386.c:1373: warning: 'out_reg' might be used uninitialized in this function
output_i386.c: In function
output_i386.c:1474: warning: ‘reg3’ might be used uninitialized in this function
output_i386.c:1474: warning: ‘out_reg’ might be used uninitialized in this function
gcc -O2 -g -Wall -c -o as68k.o as68k.c
as68k.c: In function `asm_pass1′:
as68k.c:663: warning: ‘size’ might be used uninitialized in this function
as68k.c:671: warning: ‘coutput_label’ might be used uninitialized in this function
gcc -O2 -g -Wall -c -o dict.o dict.c
gcc -O2 -g -Wall output.o output_c.o output_i386.o as68k.o dict.o -o as68k

$ cd ..

$ as68k/as68k.exe –output-i386 fe2.s
Pass 1
Pass 2
Done! 539337 bytes and 759 relocations.

$ i386-aros-gcc -c fe2.s.S

$ i386-aros-gcc -O2 -I /usr/local/i386-aros/sys-include/sdl -c audio.c
audio.c: In function

audio.c:334: warning: passing arg 5 of
SDL_LoadWAV_RW’ from incompatible pointer type

$ cd src

$ i386-aros-gcc -O2 -I /usr/local/i386-aros/sys-include/sdl -c hostcall.c

$ i386-aros-gcc -O2 -I /usr/local/i386-aros/sys-include/sdl -c input.c

$ i386-aros-gcc -O2 -I /usr/local/i386-aros/sys-include/sdl -c keymap.c

$ i386-aros-gcc -O2 -I /usr/local/i386-aros/sys-include/sdl -c main.c

$ i386-aros-gcc -O2 -I /usr/local/i386-aros/sys-include/sdl -c screen.c
screen.c: In function `draw_control_panel’:
screen.c:274: warning: assignment makes integer from pointer without a cast

$ i386-aros-gcc -O2 -I /usr/local/i386-aros/sys-include/sdl -c shortcut.c

$ i386-aros-gcc *.o ../fe2.s.o -lsdl -o ../frontier

$ cd ..

$ file frontier
frontier: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped

$ i386-aros-size frontier
text data bss dec hex filename
2580672 2036 1422076 4004784 3d1bb0 frontier

*REMEMBER as68k has to actually run on the host machine, so it needs to be native, you cannot use a cross compiler for that. However you do cross compile it’s output.

Naturally you’ll now want to run your exe!

For testing the binaries you’ll need some kind of Aros system, I recommend WinAROS, it’s a pre-built Qemu package, so the disk will work on all kinds of platforms. You can download it here: WinAros Developer

This is a pretty snazzy setup, I recommend booting into a 32bit depth display option, however you will want to modify the default script to look like this at the last line:

qemu.exe -L . -m 256 -localtime -boot c -hda WinArosHD.img -no-kqemu -net nic -net user

The next thing you’ll want to do is enable the networking. I don’t know why it’s disabled by default, but with a few moments in the CLI, and edit you can have it running:

To get the network running you have to go into Extras:Networking/Stacks/AROSTCP/db and edit static-routes, netbd-myhost, and interfaces.

First edit interfaces and uncomment the line that has device prm-rtl8029 and change the IP to

Second edit netbd-myhost and make it look like this:
HOST arosbox.arosnet arosbox
DOMAIN arosnet 10.0.2.

Third edit static-routes and change the address to

Last add the following line to your user-startup
Execute Extras:Networking/Stacks/AROSTCP/S/startnet.

Now reboot. Open a shell and verify that you have a connection with “ifconfig -a”
You can also try to ping a website, but you might not get any return packets because of QEMU’s built in firewall. You should at least see the ip address for the site you ping. Or ping to make sure you network is communicating with Qemu.

Now for the final moment, put your exe on a web server, and simply use wget to get it! I’d recommend zipping up your work directory, so it includes the ‘bin’ file, and the structure that GLFrontier expects. I like the zip file format as WinAROS includes unzip.

I do the following from AROS


It’s that easy! Now you can either ‘cd’ or click on your frontier, and away it should go!

The video is a bit wonky in that it seems to only work in 8 bit or 24bit depths. So far I’ve only tested this in WinArosDeveloper.

It actually works, the keyboard is buggy as hell, but you can play with the mouse. The first key code seems to work, then it gets stuck, so I recommend the ‘enter’ key so you can thrust…

I’ve published my zip up to The AROS Archives with any luck when it’s there I’ll update it with a link to it. In the mean time, I’ll provide a dump of the screen.c that I fixed to remove the OpenGL support. Naturally blogspot will screw up the formatting, however it should work…

#include “main.h”
#include “../m68000.h”
#include “screen.h”

/* new stuff */
enum RENDERERS use_renderer = R_OLD;
int len_main_palette;
unsigned short MainPalette[256];
unsigned short CtrlPalette[16];
int fe2_bgcol;
int mouse_shown = 0;
unsigned long logscreen, logscreen2, physcreen, physcreen2;
unsigned long VideoBase;
unsigned char *VideoRaster;
unsigned int MainRGBPalette[256];
unsigned int CtrlRGBPalette[16];
int screen_w=320;
int screen_h=200;
BOOL bGrabMouse = FALSE;
BOOL bInFullScreen = FALSE;
SDL_Surface *sdlscrn;

static const unsigned char font_bmp[] = {

static inline void read_m68k_vertex (int st_vptr, int output[3])
output[0] = STMemory_ReadLong (st_vptr);
output[1] = STMemory_ReadLong (st_vptr+4);
output[2] = -STMemory_ReadLong (st_vptr+8);

struct ZNode {
unsigned int z;
struct ZNode *less, *more;
void *data;

#define MAX_OBJ_DATA (2<<17) static unsigned char obj_data_area[MAX_OBJ_DATA]; static int obj_data_pos; #define MAX_ZNODES 1000 static struct ZNode znode_buf[MAX_ZNODES]; static int znode_buf_pos; static struct ZNode *znode_start; static struct ZNode *znode_cur; enum NuPrimitive { NU_END, NU_TRIANGLE, NU_QUAD, NU_LINE, NU_BEZIER_LINE, NU_TEARDROP, NU_COMPLEX_SNEXT, NU_COMPLEX_START, NU_COMPLEX_END, NU_COMPLEX_INNER, NU_COMPLEX_BEZIER, NU_TWINKLYCIRCLE, NU_PLANET, NU_CIRCLE, NU_CYLINDER, NU_BLOB, NU_OVALTHINGY, NU_POINT, NU_2DLINE, NU_MAX }; #define MAX_QUEUED_STRINGS 200 struct QueuedString { int x, y, col; unsigned char str[64]; } queued_strings[MAX_QUEUED_STRINGS]; int queued_string_pos; static bool no_znodes_kthx; /*********PROTOTYPES****************/ static void _BuildRGBPalette (unsigned int *rgb, unsigned short *st, int len); static void draw_3dview (struct ZNode *node); static int DrawChar (int col, int xoffset, char *scrline, int chr); static void Nu_DrawPrimitive (void *data); static inline int znode_rdlong (void **data); static void add_node (struct ZNode **node, unsigned int zval); static void znode_insert (struct ZNode *node, unsigned int zval); static inline void end_node (); static inline void znode_databegin (); static inline void znode_wrlong (int val); int DrawStr (int xpos, int ypos, int col, unsigned char *str, bool shadowed) { int x, y, chr; char *screen; x = xpos; y = ypos; if ((y > 192) || (y<0)) return x; set_line: screen = LOGSCREEN2; screen += SCREENBYTES_LINE * y; while (*str) { chr = *(str++); if (chr < 0x1e) { if (chr == '\r') { y += 10; x = xpos; goto set_line; } else if (chr == 1) col = *(str++); continue; } else if (chr == 0x1e) { /* read new xpos */ x = *(str++); x *= 2; continue; } else if (chr < 0x20) { /* Read new position */ x = *(str++); x *= 2; y = *(str++); goto set_line; } //if (x > 316) continue;

if (shadowed) {
DrawChar (0, x+1, screen+SCREENBYTES_LINE, chr-0x20);
x = DrawChar (col, x, screen, chr-0x20);

return x;
printf (“DrawStr [%s]\n”,str);
return 0;

void Nu_PutColoredPoint()

void Nu_ComplexSNext()
void Nu_DrawComplexSNext()

void Nu_DrawComplexStart()
void Nu_DrawComplexEnd()

void Nu_ComplexStartInner()
void Nu_DrawComplexStartInner()
void Nu_ComplexBezier()
void Nu_DrawComplexBezier()

void draw_control_panel()
unsigned int *pal;
// Lock surface if needed
if (SDL_MUSTLOCK(sdlscrn))
if (SDL_LockSurface(sdlscrn) < 0) return; // Declare a couple of variables int y, x, yofs, ofs; // Draw to screen yofs = 0; pal=MainRGBPalette; for (y = 0; y < screen_h; y++) { if(y==168)pal=CtrlRGBPalette; //bottom has differnet palette for (x = 0, ofs = yofs; x < screen_w; x++, ofs++) { //((unsigned int*)sdlscrn->pixels)[ofs] = pal[VideoRaster[ofs]];

switch (sdlscrn->format->BytesPerPixel)
case 1: // 8-bpp
Uint8 *bufp;
bufp = (Uint8 *)sdlscrn->pixels + y*sdlscrn->pitch + x;
*bufp =(Uint8*) pal[VideoRaster[ofs]];
case 2: // 15-bpp or 16-bpp
Uint16 *bufp;
bufp = (Uint16 *)sdlscrn->pixels + y*sdlscrn->pitch/2 + x;
*bufp = pal[VideoRaster[ofs]];
case 3: // 24-bpp mode, usually not used
Uint8 *bufp;
bufp = (Uint8 *)sdlscrn->pixels + y*sdlscrn->pitch + x * 3;
bufp[0] = pal[VideoRaster[ofs]];
bufp[1] = pal[VideoRaster[ofs]] >> 8;
bufp[2] = pal[VideoRaster[ofs]] >> 16;
} else {
bufp[2] = pal[VideoRaster[ofs]];
bufp[1] = pal[VideoRaster[ofs]] >> 8;
bufp[0] = pal[VideoRaster[ofs]] >> 16;
case 4: // 32-bpp
Uint32 *bufp;
bufp = (Uint32 *)sdlscrn->pixels + y*sdlscrn->pitch/4 + x;
*bufp = pal[VideoRaster[ofs]];

//yofs += sdlscrn->pitch / 4;
yofs += sdlscrn->pitch / sdlscrn->format->BytesPerPixel;

// Unlock if needed
if (SDL_MUSTLOCK(sdlscrn))

// Tell SDL to update the whole screen
SDL_UpdateRect(sdlscrn, 0, 0, screen_w, screen_h);


void Nu_DrawScreen()
_BuildRGBPalette (MainRGBPalette, MainPalette, len_main_palette);
_BuildRGBPalette (CtrlRGBPalette, CtrlPalette, 16);
//printf (“Frame: %d znodex.\n”,znode_buf_pos);
//draw_3dview (znode_start);
if (mouse_shown) {
SDL_ShowCursor (SDL_ENABLE);
mouse_shown = 0;
} else {



void Nu_PutBezierLine()
void Nu_DrawBezierLine()

void Nu_3DViewInit()
queued_string_pos = 0;
//printf (“3dviewinit()\n”);
znode_buf_pos = 0;
//printf (“%d bytes object data\n”, obj_data_pos);
obj_data_pos = 0;

znode_start = NULL;
znode_cur = NULL;
no_znodes_kthx = FALSE;
void Nu_InsertZNode()
unsigned int zval = GetReg (4);
if (use_renderer == R_OLD) return;
if (no_znodes_kthx) return;
if (znode_start == NULL) {
add_node (&znode_start, zval);
} else {
znode_insert (znode_start, zval);

void Nu_PutCircle()
void Nu_DrawCircle()

void Nu_PutOval()
void Nu_DrawOval()

void Nu_PutCylinder()
void Nu_DrawCylinder()

void Nu_QueueDrawStr()
void Nu_IsGLRenderer()

void Nu_PutPlanet()
void Nu_DrawPlanet()

void Nu_Put2Line()
void Nu_Draw2Line()

void Nu_PutQuad()
void Nu_DrawQuad()

void Nu_GLClearArea()
void Nu_PutBlob()
void Nu_DrawBlob()

void Nu_Put2DLine()
void Nu_Draw2DLine()

void Nu_PutLine()
void Nu_DrawLine()

void Nu_UnInit()

void Nu_PutTeardrop()
void Nu_DrawTeardrop()

void Nu_PutPoint()
void Nu_DrawPoint()

void Nu_PutTwinklyCircle()
void Nu_DrawTwinklyCircle()

void Nu_ComplexStart()

void Nu_PutTriangle()
void Nu_DrawTriangle()

void Nu_ComplexSBegin()

void Nu_ComplexEnd()

void Nu_32ViewInit()

void Screen_ToggleRenderer ()
use_renderer = R_OLD;

void change_vidmode(void);

void Screen_Init(void)
SDL_WM_SetCaption(PROG_NAME, “Frontier”);

void change_vidmode (void)
const SDL_VideoInfo *info = NULL;
int modes;

info = SDL_GetVideoInfo ();

assert (info != NULL);


if ((sdlscrn = SDL_SetVideoMode (screen_w, screen_h,
info->vfmt->BitsPerPixel, modes)) == 0) {
fprintf (stderr, “Video mode set failed: %s\n”, SDL_GetError ());
SDL_Quit ();
exit (-1);
printf(“change_vidmode %d x %d %d deep\n”,screen_w,screen_h,info->vfmt->BitsPerPixel);

void Screen_UnInit(void)
void Screen_ToggleFullScreen()

static void _BuildRGBPalette (unsigned int *rgb, unsigned short *st, int len)
int i;
int st_col, r, g, b;

for (i=0; i>4;
rgb[i] = 0xff000000 | (b<<16) | (g<<8) | (r); } } static int DrawChar (int col, int xoffset, char *scrline, int chr) { const char *font_pos; char *pix; int i; font_pos = font_bmp; font_pos += (chr&0xff)*10; scrline += xoffset; if (xoffset < 0) { font_pos += 9; return xoffset + *font_pos; } for (i=0; i<8; i++, font_pos++, scrline += SCREENBYTES_LINE) { pix = scrline; if (xoffset > 319) continue;
if (*font_pos & 0x80) *pix = col;
if (xoffset+1 > 319) continue;
if (*font_pos & 0x40) *pix = col;
if (xoffset+2 > 319) continue;
if (*font_pos & 0x20) *pix = col;
if (xoffset+3 > 319) continue;
if (*font_pos & 0x10) *pix = col;
if (xoffset+4 > 319) continue;
if (*font_pos & 0x8) *pix = col;
if (xoffset+5 > 319) continue;
if (*font_pos & 0x4) *pix = col;
if (xoffset+6 > 319) continue;
if (*font_pos & 0x2) *pix = col;
if (xoffset+7 > 319) continue;
if (*font_pos & 0x1) *pix = col;
/* width of character */
i = *font_pos;
return xoffset + i;
* znode_start is the head of a btree of znodes, each with a linked list
* of GL display lists to draw (in list order).
* Draw this crap starting from biggest value znodes.
static void draw_3dview (struct ZNode *node)
if (node == NULL) return;
if (node->more) draw_3dview (node->more);

if (use_renderer) {
printf (“Z=%d “, node->z);
Nu_DrawPrimitive (node->data);

if (node->less) draw_3dview (node->less);

typedef void (*NU_DRAWFUNC) (void **);
NU_DRAWFUNC nu_drawfuncs[NU_MAX] = {
&Nu_DrawComplexSNext, // 6
&Nu_DrawComplexStartInner, // 9

static void Nu_DrawPrimitive (void *data)
int fnum;

for (;;) {
fnum = znode_rdlong (&data);
//fprintf (stderr, “%d “, fnum);
if (!fnum) return;
nu_drawfuncs[fnum] (&data);

static inline int znode_rdlong (void **data)
int val = *((int*)(*data));
(*data) += 4;
return val;

static void add_node (struct ZNode **node, unsigned int zval)
assert (znode_buf_pos < MAX_ZNODES); /* end previous znode display list!!!!!!! */ if (znode_cur) end_node (); *node = znode_cur = &znode_buf[znode_buf_pos++]; znode_cur->z = zval;
znode_cur->less = NULL;
znode_cur->more = NULL;
znode_databegin ();

static inline void end_node ()
znode_wrlong (0);

static void znode_insert (struct ZNode *node, unsigned int zval)
if (zval > node->z) {
if (node->more) {
znode_insert (node->more, zval);
} else {
add_node (&node->more, zval);
} else {
if (node->less) {
znode_insert (node->less, zval);
} else {
add_node (&node->less, zval);

static inline void znode_databegin ()
znode_cur->data = &obj_data_area[obj_data_pos];

static inline void znode_wrlong (int val)
*((int*)(obj_data_area+obj_data_pos)) = val;


SheepShaver on Windows

SheepShaver on Windows

I was hoping for this to be a full featured review but alas there are a few major issues to prevent wide spread use of this emulator…

First you really need access to a Power Macintosh of some kind to use this. You’ll need a ROM and a version of the OS that isn’t for free on Apple’s site. Right now I’m using my 7.6.1 CD to run stuff, and it works great. Honestly its way better than the real deal.. Unless you are running something CPU intensive… Like SIMH on MiNT (all 68000 code) on Sheepshaver (PowerPC emulator)… it can be quirky at times.

However let me say if you do have MacOS 7.6 – 9 you’ll love Sheepshaver. If you feel left out from the PowerPC to Intel transition and feel you need this old clunker around to run some 68000 apps or even PowerPC system 7-9 applications that are not carbon compatible, sheepshaver is freaking AWESOME.

I’m still running Vista x86_64 on my laptop where I do most of my work, so for me I needed to download Sheepshaver. Now it does mention it can run MacOS 7.5.2 however the copy on apple’s site is a massive disk image… So you’ll need a Mac to extract it… Yeah I know.  I did make an ISO image to help those who need/want it.

I haven’t tried it, however I do know that 7.5.2 did *NOT* work on my 5400. The good news is that the disk images work between MiniVmac & Sheepshaver. This has saved my sanity (perhaps yours as well!) This does mean that you can also use HFVExplorer to move files in/out of Sheepshaver. A word of caution, the SLiRP networking causes my setup to crash… I really don’t know if it’s Vista 64, and I’m not in the mood to test within Virtual PC… emulating within an emulator is a recipe for disaster.

Another thing you’ll need is the GTK+ runtime for the gui configuration… I found mine by googling the term “gtk-2.12.9-win32-2.exe” … For some reason sourceforge seems to have issues at the moment with its projects unless you know exactly what you are looking for.

That being said I love sheepshaver. I run it full screen at my laptops native resolution (1280×800) and with the sound on, and it FEELS like a Macintosh Laptop. Even my 1.8Ghz laptop feels snappier than the 120Mhz PowerPC.. But from the Sheepshaver site, they do say to expect to run at 1/8th the speed.. So my laptop “feels” like it’s 225Mhz. A SCREAMER! Of course this would be horrible for people expecting to run photoshop & friends.

There is no doubt about it, Sheepshaver is great for running old games and applications.

A few tips though. Turn off Virtual memory in Sheepshaver. There is no need for it, as it’s far better to allow Sheepshaver to just have more memory.. In this day & age odds are you can easily allow it to have 64 – 256 megabytes. While you are unable to turn off the disk cache, you should tune it down to 64kb. Although the CPU isn’t much faster than many of the Power Mac’s, the disk IO of a modern computer far exceeds it. Most computers are actually IO bound, so running older systems with faster IO gives the appearance of them running substantially faster. It’s only nested emulation that brings it out how slow it is.

I’ve also found that older version of the OS support more software, and run better.  That is 7.5.3, 7.6, 8.0 and 8.1.  8.5, 8.6 and 9.0 have more PowerPC hooks in them, and SheepShaver hooks the 68000 code to do it’s paravirtualization (it doesn’t emulate any hardware), and the more older tied to the 68000 code tend to be more stable.

That being said, running Mathematica, SoftPC, and Photoshop would not be a “good idea” if you are expecting faster performance. But at the same time, it’s great being able to run old software that you’ve purchased throughout the years.

vMac/Mini vMac

Simply put vMac is a quick & fast Mac Plus emulator. That being said, it will emulate a 68000 cpu with 4 (yes, FOUR) megabytes of ram.

Now you will need a ROM from a plus Macintosh in order to run this. I actually did own a plus (it made a good foot rest), but before I moved I ripped the ROM, and trashed it.

vMac started in the late 1990’s but has been largely abandoned. But not to worry, this is where mini vMac stepped in, and took over the torch as it were.

Mini vMac can be downloaded here:

The good news is that it’s small, and portable so you can pick this up for several platforms. And yes, there is a version for PowerPC Macs so you can run some super ancient software with system 6 and prior on System 8/9 PowerMacs.

Assuming you have your rom in a file called vmac.rom you are almost ready to go! Now you need a copy of the operating system. Luckily Apple has made systems prior to 7.6 free, and you can download them from here:

And the System 7.5.3 updater:

Although for the first timer, I ‘d recommend something like this:

to get up & running quickly.

Now I’d highly recommend a utility called HFVExplorer. This will let you create a ‘hardfile’ to simulate a hard disk, and allow you to move files you’ve downloaded into your hardfile. You can find a copy here:

A good utility to have on the mac side is DiskCopy. You can download a copy from Apple here:

If you still have legacy 400k, 800k floppies you will require a real Mac to read them. They are formatted in a different method that a PC cannot read. However this changed with the high density drives (super drivers they were called) which a PC can at least read/write raw disk images of them.

Putting it all together:

Ok with mini vMac, your rom, a hard disk file & the system 7 boot disk you should have enough to have a booting instance. It should be about that simple.

I happen to love this game Captain Blood, and I found a copy for the Macintosh and using HFVExplorer I just move it into my disk file, unstuff & run.

The whole point is that this ancient game bypasses the toolbox for video calls and will crash on the Powermac I just bought, however it’ll run fine in emulation.

Next up we’ll cover the PowerMac emulation…!