Table of Contents

AMOS Professional Extension Development

AMOS Professional extensions are written in Motorola 68000 Assembly Language.

Original Notes

+Music.s contains instructions for how to make an extension. I've added my own notes below and clarified some of the existing information.

Defining Tokens

Return value and parameter lists

Don't forget the terminating -1! If you built it and forgot it, trying to start AMOS Pro will do nothing. Replace with you known good copy and reboot, then try again.

Return value

Parameters

String parameters

Strings are stored in AMOS with a word for string length (so strings can be up to 65535 characters in length), and then the string itself. I'm not sure if they're null terminated.

Your own routines

You can Rbsr and other branch related jumps to your own stuff, but you can't Rjsr to your routines! Only to built-in AMOS routines. Use Rbsr and its equivalent! The Compiler will let you know of this mistake very quickly.

Built-in Routines

`L_Ram*`

These call exec/AllocMem for you through AMOS.

snippet.asm
  MOVE.L #400,D0
  Rjsr L_RamFast
  BNE RamAllocated
  ; couldn't get RAM, D0 is 0
RamAllocated:
  ; D0 contains address of RAM

Free it with L_RamFree:

snippet.asm
  MOVE.L D0,A1
  MOVE.L #length,D0
  Rjsr L_RamFree

L_Demande and HiChaine

When you want to demand string space, you apply for it with L_Demande.

The requested length goes into D3. It has to be even. You get the base address in both A0 and A1.

This is an AMOS string, so the first two bytes are the size of the string as a word. Then the string comes after.

Then, you have to eventually hand HiChaine(A5) (be sure to preserve A5) the ending address of the string.

Make sure you don't try to allocate a zero width string with this! It will fail hard.

Writing out a routine in +CompExt.s with comments to understand this:

snippet.asm
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;	=COMP ERR$
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	Lib_Par	CompErr
; - - - - - - - - - - - - -
	Dload	a2 ; get the data area
	moveq	#0,d3 ; clean out d3
	move.w	LErreur-CD(a2),d3 ; this is the length of the error string as a word
	beq.s	.Vide ; if it's zero there's nothing to do
	Rjsr	L_Demande ; get string space, put the address in a0 and a1
	move.w	d3,d0 ; preserve existing length in d0
	move.l	a0,d3 ; move the address into d3
	move.w	d0,(a1)+ ; put the length into the start of the string
	subq.w	#1,d0 ; reduce the length by 1
	lea	Erreur-CD(a2),a0 ; load the error string address into a0
.Loop1	move.b	(a0)+,(a1)+ ; copy a bit from a0 to a1
	dbra	d0,.Loop1 ; decrement d0. if d0 is greater than 0, go to loop
	move.w	a1,d1; put the current target address into a1
	and.w	#$0001,d1 ; keep the lowest bit
	add.w	d1,a1 ; add the current address to a1
	move.l	a1,HiChaine(a5) ; update the high end of string memory
	moveq	#2,d2
	rts
.Vide	move.l	ChVide(a5),d3 ; return empty string
	moveq	#2,d2
	rts

Here's a better version:

snippet.asm
    ; D4 contains your string length
    MOVE.L D4,D3
    AND.W #$FFFE,D3
    ADDQ #2,D3 ; round up to the nearest 2
 
    Rjsr L_Demande ; string is in A0/A1
    LEA 2(A0,D3.W),A1
    MOVE.L A1,HiChaine(A5)
    MOVE.L A0,A1 ; put A1 back
    MOVE.W D4,(A0)+ ; put length of string at start of memory
.stringstuff
    MOVE.B (A2)+,(A0)+
    BNE .stringstuff ; copy until null terminator

You know your string code is working if you can do this:

snippet.basic
A$=My String Function$ ' returns "wow"
Print "cat" + A$ + "dog" ' "catwowdog"

Returning an empty string

Send ChVide(A5) to D3. “vide” in French is “empty” so that makes sense.

Compiler

AMOS Professional will let you be a little sloppy with the differences between Lib_Def, Lib_Par, Rbcc, and Rjsr. The compiler will not.

Ensure you have your compiler set up properly:

Save those settings and you can use apcmp file.amos inclib in your build script to build something for testing.

If your code is crashing, check for the following:

If you do this, the compiler will be happy. I suggest having a test AMOS file you can compile and run that will exercise all the code that Rbccs within your own code and running it often, to keep you safe.

Tips and Tricks

snippet.asm
; functions
dc.w L_Nul,L_ThisWillReturnAValue
 
; instructions
dc.w L_ThisReturnsGarbage,L_Nul

How-Tos

Add a new command to be used in the Editor

These use a bunch of macros I built for the BSD Socket extension:

snippet.asm
C_Tk dc.w 1,0
;...
  ; if something that accepts parameters and returns a value
  AddTokenFunction MyNewFunction
  dc.b "my new functio","n"+$80,"00,0",-1
 
  ; if something that returns nothing and just has side effects
  AddTokenInstruction MyNewInstruction
  dc.b "my new instructio"."n"+$80,"I",-1
snippet.asm
  Lib_Par MyNewFunction
 
  PreserveStackFunction
  RestoreStackFunction
  Ret_Int
snippet.asm
  ; takes four parameters
  Lib_Par MyNewFunction
 
  PreserveStackFunction
 
  MOVE.L D3,D4 ; last parameter
  MOVE.L (A3)+,D3 ; second-to-last
  MOVE.L (A3)+,D2 ; second-to-first
  MOVE.L (A3)+,D1 ; first
 
  RestoreStackFunction
  Ret_Int
snippet.asm
  Lib_Par MyNewFunction
 
  PreserveStackFunction
  MOVE.L (A0),D3 ; return value
  RestoreStackFunction
  Ret_Int