MUI coding in assembler

MUI, or Magic User Interface, is a popular UI system on the Amiga that offers users an attractive and consistent interface to work with. For developers, it delivers an object-oriented way to code UIs, with perks like automatic layout management and simple Arexx integration.

MUI

MUI coding is an advanced topic, but one that’s worth learning properly. There’s no doubt MUI was designed with C programmers in mind, and little has been done to accommodate those of us who prefer assembler syntax. The MUI development package hides much of its functionality behind C macros and libraries, making it tricky to find documentation on how it really works. That’s the kind of insight assembler coders need.

I highly recommend checking out the developer guide and autodocs included with the MUI developer package. They offer a solid understanding of how the system works. Here, I’ll highlight the parts specific to the assembler and share examples you can use as a starting point for your own applications.

To get started, we need to open “muimaster.library”. Although MUI objects are technically Intuition BOOPSI classes, we should use MUI functions to create new objects and set their attributes. Another important thing to note is that the UI is built from the bottom up. That’s why, in the later example, we start by creating the buttons and text, then the groups they belong to. Only after that do we create the window, and finally, the application to host everything. The reason for this is that each object we create needs to have a pointer to its children. As a result, when we create our window, for example, the application isn’t ready yet. This means we can’t actually open the window at that moment. We need to build the entire chain of objects first, then open the window using _LVOMUISetAttrsA.

Creating new objects is done using the _LVOMUINewObjectA function. Like other functions ending with a capital “A,” its attributes are provided using a taglist. In the example, I use my own macros to build a taglist on the stack. They’re the same ones I’d use with, for instance, _LVOOpenScreenTaglist, which is actually an alias for _LVOOpenScreenA, by the way.

Every new object we create needs to be of a MUIC_<class>. In the C documentation for MUI, you’ll see references to MUIC_Window, MUIC_Text, and so on, but these don’t appear in the assembler includes. The fix is to add the necessary classes ourselves in the assembler source. These aren’t constants representing numbers like other labels, but pointers to strings containing the class names. Here is how it’s done in assembler:

MUIC_Application	dc.b	"Application.mui",0
MUIC_Window		dc.b	"Window.mui",0
MUIC_Group		dc.b	"Group.mui",0
MUIC_Text		dc.b	"Text.mui",0
			even

One big advantage of having the entire application and its UI as a single chain of objects is that when it’s time to exit and clean up, we just make one call to _LVOMUI_DisposeObject with our application as the parameter. MUI will then automatically close all its child objects and free up all used resources in one go.

With our application and its UI elements now created and visible on screen, it’s time to add functionality to bring everything to life. The ideal MUI application has a minimalistic event handler that only checks whether the program should quit or continue running. All other functionality should be added as hooks to the UI elements, with MUI handling the calls to these routines. This is exactly the approach we took in the example.

Important: When MUI calls your hook, don’t assume any data will be in the CPU’s registers. Retrieve all necessary parameters from variables or memory locations, and be sure to save and restore any registers you use on the stack.

In object-oriented programming, the functions an object can perform are called “methods”. In MUI, these methods are invoked using the DoMethod() command. However, DoMethod() isn’t actually an AmigaOS function; it resides in a linker library used by the C compiler, making it not particularly useful for assembler coders. To rub salt in the assembler coders’ wounds, the documentation on how to manually invoke these methods is flat-out wrong.

Here is what needs to be done:

  • Make a list of methods and their parameters stored in memory or on the stack.
  • Point a1 to this list
  • Point a2 to the relevant MUI object
  • Point a0 to the objects hook
  • execute h_entry from the hook

	movea.l	mt_MUI_Application(a5),a2
	movea.l	-4(a2),a0					; Offset to Hook Struct (Is this undocumented?)
	movea.l	h_Entry(a0),a6				; Find entry to execute method
	lea	mt_Method_Input,a1
	jsr	(a6)							; DoMethod();

All of this could be made easier for the coder by creating macros to generate specific types of objects and invoke methods, but for clarity and understanding, I’ve chosen not to include them in this source.

To sum it up, here’s a list of what needs to be done to build a MUI application:

  • Open “muimaster.library”
  • Build your application starting from the bottom objects and up while linking any child objects to it
  • Set attributes that only become valid after creating the application, such as opening a window.
  • Use DoMethod() to set notify and hooks to your objects
  • Run a minimalistic eventhandler loop that fetch Application_ReturnID_Quit by calling DoMethod() and waiting for signal
  • Clean up by a single call to MUI_DisposeObject()
  • Close “muimaster.library”

Finally, here’s a complete example of source code showing how to accomplish all of the above.

Basic_MUISetup.s