Interfacing Assembly Language with Modula-2 There are times when it would be convenient to use Assembly Language within a Modula-2 program. Assembly Language can be used to speed up critical sections of a program or to implement operating system calls. Modula-2 provides a number of low-level facilities that make it easy to do just that. Every Modula-2 environment includes the pseudo module SYSTEM which exports machine specific procedures and data types. These facilities provide the necessary tools to interface Assembly Language routines with Modula-2. The important point to remember is that anything exported by SYSTEM may have a different meaning to another Modula-2 compiler. For example, the type BYTE is usually an eight bit memory storage unit, but on some systems it is allocated as sixteen bits. Differences such as this can apply to anything you import from SYSTEM so it is wise to limit such imports if you want to create portable programs. The following steps demonstrate how to insert Assembly Language code into a Modula-2 program using the facilities of module SYSTEM. This example implements the AMOS SLEEP monitor call. STEP ONE: Create the Assembly Language source file and assemble it. For this example we have created an Assembly Language source file called SNOOZE.M68 and assembled it with the AMOS M68 assembler. The text of the source file is: ; SNOOZE.M68 SEARCH SYS ; Search AMOS System definitions SAVE A4,A5,A6 ; Save Modula-2 registers MOV #1,D0 ; Set result register SLEEP D6 ; Call SLEEP with arg in D6 BNE WOKEUP ; Branch if awakened by another job CLR D0 ; Clear result if not awakened WOKEUP: REST A4,A5,A6 ; Restore Modula-2 registers END The most important thing to note is that you must save the A4, A5 and A6 processor registers if they might get changed by your code. The Modula-2 program will give unpredictable results otherwise. Almost all AMOS Monitor Calls change A6, and if you are in doubt about A4 and A5 save them also. Most monitor calls set or clear the processor Z Flag to indicate the result of the call. The SLEEP call does this to indicate whether the job was awakened by another job before the sleep period expired. This example uses register D0 to communicate this result back to the Modula-2 program. STEP TWO: Convert the assembled file to Modula-2 INLINE statements. The next step is to convert the assembled file to Modula-2 statements. For this example we used the program CVTHEX which is available on the AMUS Net in the Modula-2 SIG Directory. The output file from CVTHEX for our example looks like this: INLINE(048E7H,0000EH,07001H,0A046H,06602H); INLINE(04280H,04CDFH,07000H); The CVTHEX program converts the assembled (.LIT) file to Modula-2 INLINE statements containing hexadecimal constants representing the machine code. These INLINE statements can be inserted into our Modula-2 source file. STEP THREE: Insert the INLINE statements into the program. Now it is time to construct the final Snooze procedure using regular Modula-2 statements and the Assembly Language code. The source to the Snooze procedure is: PROCEDURE Snooze(tenths: CARDINAL; VAR awakened: BOOLEAN); CONST D0 = 0; D6 = 6; BEGIN SETREG(D6,VAL(LONGCARD,tenths*1000); (* put argument in reg D6 *) INLINE(048E7H,0000EH,07001H,0A046H,06602H); (* inline code for SLEEP *) INLINE(04280H,04CDFH,07000H); (* monitro call. *) awakened := REG(D0) = 1D; (* set boolean result *) END Snooze; That's all there is to it. You will note that we have used the SETREG and REG procedures from SYSTEM to set and read processor registers. Each of these procedures accepts a register number from 0 to 15 as the first argument. Registers D0 to D7 are numbered from 0 to 7 and registers A0 to A7 are numbered from 8 to 15 in decimal. The Snooze procedure allows the caller to specify the sleep duration in tenths of seconds. Since the SLEEP monitor call expects its argument to be in microseconds we multiply tenths by 1000 to keep everyone happy. The boolean variable awakened is set according to the value in D0 to signal whether the sleep was terminated by another job. A few more notes are in order. It is usually a good idea to isolate inline code from other parts of your program by placing it in a seperate procedure. This will assure that the only processor registers in use will be the ones discussed above. If you must use inline code in the middle of a section of other Modula-2 code it is probably a good idea to save and restore all registers except A7. The WITH statement for example, uses one of the address registers to accomplish its purpose, if you were to overwrite this register your program would get "lost". You may also use the ADR function to put the address of a variable into a register. The following example puts the address of a variable called ddb into the A1 register: SETREG(A1,ADR(ddb)); Since the SETREG and REG procedures require that their second argument always be of longword size you can use the VAL function to transfer to the required data type. For example: SETREG(D0,VAL(LONGCARD,AnyTypeOfVar): AnyTypeOfVar := VAL(DestType,REG(D0)); I hope you have found this brief guide to be useful. Please post any questions or comments to the bulletin board or send email. David Tingler (TSI/AM) .