| 118 | 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> |