projects/2023–26
Objective-C on the Sega Dreamcast
Getting the Objective-C runtime and GNUstep Foundation running on a 1998 console.
- Objective-C
- C
- KallistiOS
- GNUstep
- SH-4
- Make

Objective-C is mostly thought of as Apple's language, but the runtime is just C. That makes it portable to anything with a C toolchain, including the Hitachi SH-4 inside a Sega Dreamcast. This project added Objective-C support to KallistiOS, the open-source Dreamcast SDK: the libobjc runtime, then GNUstep's libs-base (Foundation), with example programs that double as toolchain sanity checks.
What it took
Getting #import <Foundation/Foundation.h> to compile for a console with 16 MB of RAM meant working through the
whole stack:
- Runtime: building GCC’s
libobjcfor thesh-elftarget and linking it through KallistiOS’s toolchain wrappers. - libffi: NSInvocation and message forwarding need a foreign-function interface, so libffi had to build for the SH-4.
- GNUstep make + libs-base: porting the build system and Foundation itself to a target with no dynamic loader, a minimal libc and no real filesystem.
The runtime example
The first example tests the bare runtime: no Foundation, just Object, instance variables, properties and message
sends, compiled with kos-cc and running on real hardware. Falco Girgis and I wrote it together.
@interface Person: Object
{
const char *_name;
int _age;
float _height;
Person *_bestFriend;
BOOL _dead;
}
- (void)addName:(const char *)name age:(int)age height:(float)height;
- (void)setBestFriend:(Person *)bestFriend;
@property(nonatomic, assign)Person *bestFriend;
@property(nonatomic)BOOL dead;
@end
examples/dreamcast/objc/runtime/runtime.m
It then uses the runtime’s C API to create, inspect and copy objects:
// Create a new instance of person, using the runtime
person1 = class_createInstance(objc_getClass("Person"), 0);
// Call message handler for "addName," setting instance variables
[person1 addName: "Joe" age: 20 height: 6.1f];
// Use runtime to copy construct another Person instance
person2 = object_copy(person1, 0);
Building it is a normal KallistiOS Makefile that links -lobjc:
$(TARGET): $(OBJS)
kos-cc -o $(TARGET) $(OBJS) -lobjc
Foundation, with a homemade test runner
The second example brings in GNUstep Foundation. There’s no XCTest on a Dreamcast, so I wrote a small runner that
finds test methods the same way XCTest does, by reflecting over each class and calling every selector whose name
contains test:
Method *methodList = class_copyMethodList(testClass, &outCount);
id tests = class_createInstance(testClass, 0);
for(int ii = outCount-1; ii >= 0; ii--) {
SEL selector = method_getName(methodList[ii]);
const char *selectorName = sel_getName(selector);
if (strstr(selectorName, "test")) {
IMP imp = objc_msg_lookup(tests, selector);
id testCaseResult = imp(tests, selector);
...
}
}
examples/dreamcast/objc/foundation/foundation.m
The container tests exercise NSArray, NSDictionary and NSSet. My favourite counts the letters in the
console’s own name:
- (int) testNSDictionary {
NSMutableDictionary *container = [NSMutableDictionary dictionary];
NSString *consoleName = [@"sega dreamcast" stringByReplacingOccurrencesOfString: @" " withString: @""];
for (NSInteger i = 0; i < consoleName.length; i++) {
unichar character = [consoleName characterAtIndex:i];
NSString *letter = [NSString stringWithCharacters:&character length:1];
NSNumber *letterCount = [container objectForKey:letter];
if (!letterCount) {
[container setObject:[NSNumber numberWithInteger: 1] forKey:letter];
} else {
[container setObject:[NSNumber numberWithInt:[letterCount intValue] + 1] forKey:letter];
}
}
...
}
examples/dreamcast/objc/foundation/Containers/ContainerTests.m