comparison _posts/2025-10-19-oms-part-1.html @ 118:503e22dd6cf5

blog: add (unfinished) series on OMS I'll update this as I do more research into the inner workings of OMS. It's much more interesting (and more convoluted) than ASIO is unfortunately, but it means the blog posts will probably be more interesting
author Paper <paper@tflc.us>
date Sun, 19 Oct 2025 23:15:02 -0400
parents
children
comparison
equal deleted inserted replaced
117:6ca8af53d424 118:503e22dd6cf5
1 ---
2 layout: post
3 author: Paper
4 title: 'The Open Music System, part 1 - initialization'
5 nowplaying: 'The Radio Dept. - Brobygatan'
6 ---
7 <span>
8 Applications wanting to interact with OMS must first call into
9 <code>Gestalt</code> with the first parameter being the magic
10 bytes <code>" OMS"</code> (<code>0x204F3D53</code>) and the
11 second parameter being a pointer to a 32-bit variable that
12 receives the magic pointer.
13 </span>
14 {% comment %}
15 is there was a way to do this with newlines? this is ugly
16 {% endcomment %}
17 <figure><pre class="code-block"><code>UniversalProcPtr OMS_GetGestaltPtr(void)
18 {
19 OSErr err;
20 uint32_t x;
21
22 err = Gestalt(0x204F3D53 /* " OMS" */, &x);
23 if (err != noErr)
24 return NULL;
25
26 return (UniversalProcPtr)x;
27 }</code></pre></figure>
28 <span>
29 From here, applications must retrieve an array of functions
30 through calling that magic pointer. Note that users cannot
31 simply call this pointer directly, unless they are specifically
32 only targeting 68k. You must use the <code>CallUniversalProc</code>
33 function to safely call into 68k code.
34 </span>
35 <figure><pre class="code-block"><code>UniversalProcPtr oms_table[0x5e];
36
37 #define OMS_GESTALT_PROC_TYPE \
38 (kThinkCStackBased \
39 | RESULT_SIZE(kFourByteCode) \
40 | STACK_ROUTINE_PARAMETER(1, kTwoByteCode) \
41 | STACK_ROUTINE_PARAMETER(2, kFourByteCode))
42
43 int32_t OMS_Init(void)
44 {
45 uint32_t functable;
46 UniversalProcPtr omsptr;
47
48 omsptr = OMS_GetGestaltPtr();
49 if (!omsptr)
50 return -1;
51
52 functable = CallUniversalProc(omsptr, OMS_GESTALT_PROC_TYPE, 4, 0);
53 if (functable != (uint32_t)-1) {
54 memcpy(oms_table, (const void *)functable, sizeof(oms_table));
55 } else {
56 /* very old OMS */
57 uint32_t i;
58
59 functable = CallUniversalProc(omsptr, OMS_GESTALT_PROC_TYPE, 1, 0);
60 if (!functable)
61 return -1;
62
63 for (i = 0; i < ARRAY_SIZE(oms_table); i++, functable += 4)
64 oms_table[i] = (UniversalProcPtr)functable;
65 }
66
67 return 0;
68 }</code></pre></figure>
69 <span>
70 At this point, you can now call into any of the pointers in
71 <code>oms_table</code>, provided that you know what the parameters
72 are. The easiest way I've found to find the parameters is to open an
73 OMS-capable program in a reverse-engineering tool, search for uses of
74 <code>CallUniversalProc</code> that look roughly like the initialization
75 code above, and mark the offset they are copied to as the OMS table with
76 the correct size and type. You can then find other uses of
77 <code>CallUniversalProc</code> that use the pointers in the OMS table,
78 and mark down the procedure type (the second parameter to
79 <code>CallUniversalProc</code>). To decode the procedure type, I've
80 written a simple utility to do the heavy lifting
81 <a class="prettylink" href="https://hg.tflc.us/codedump/file/tip/decode-mixed-mode.c">here</a>.
82 Do take note that it does not support register-based calling yet.
83 Feel free to send me any patches if you add support for it ;)
84 </span>