Mercurial > web
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> |
