README.TXT for Modula-2  -> Assembler Conversion            25-Mar-92/Stu
TERRA Datentechnik, Tech Support
-------------------------------------------------------------------------


Reference: 'Programmer's Guide', chapter 9: 'Mixed Language Programming'


Introduction
------------

  We describe here how to convert a V3.40 module with CODE statements into
  an Assembler module that can be linked with V4.00 code.
  Our example consists of a main module called 'MAIN' and a module 'SAMPLE'
  with lots of CODE statments. We show here how to generate a 'SAMPLE.ASM'
  out of 'SAMPLE.OBJ' by means of V3.40-M2DECODE.
  In chapter 2 we generate 'SAMPLE3.ASM' from 'SAMPLE.DEC' with interme-
  diate steps 'SAMPLE1.DEC' and 'SAMPLE2.DEC'.
  Chapter 3 explains the new definition module for the assembler module.
  Last, in chapter 4 shows some additional enhancements to the assembler
  module 'SAMPLE3.ASM', generating 'SAMPLE4.ASM' which is a better version
  for easier maintenance.


MASM Version to be Used:
-----------------------

The Assembler used to produce the OBJ-files from the ASM-files was

        MASM 5.1

If any problems arise when passing the ASM-file through your Assembler,
it may be due to the fact that it is not 100% MASM 5.1 compatible.


files of this directory:
-----------------------

  README   TXT    description of how to convert a V3.40 module with
                  code statements to a V4.00 assembly module (this file)


                  our sample: main module 'main' that imports a module
                  ----------  'sample' which contains code statements.
                              'sample' is converted to asm module.

  MAIN     MOD    main module (code for V3.40 and V4.00)
  SAMPLE34 DEF    the original V3.40 definition module of sample
  SAMPLE   MOD    the original V3.40 implementation module of sample
  SAMPLE34 OBJ    the original V3.40 compiled object module of sample
  MAIN34   EXE    the original V3.40 compiled exe file of sample


                  steps towards an assembly module:
                  ---------------------------------

  SAMPLE   DEC    output file of m2decode for sampl34.obj
  SAMPLE1  DEC      steps toward an assembly module
  SAMPLE2  DEC      steps toward an assembly module
  SAMPLE3  ASM    assembly module of sample34.obj
  SAMPLE4  ASM    enhanced assembly module of sample34.obj


                  4.0 code:
                  --------

  SAMPLE40 DEF    new sample.def for assembly module
  MAIN     ARF    arf file for 4.00
  MAIN40   EXE    main.exe compiled with assembly module

  C_40     BAT    our compile batch for 4.00
  L_40     BAT    our link batch for 4.00


Contents:
--------

  1. Use V3.40-M2DECODE on V3.40-OBJ files    SAMPLE.MOD   -> SAMPLE.DEC
  2. Generate Assembler file
    2.1 Delete opcodes and header             SAMPLE.DEC   -> SAMPLE1.DEC
    2.2 Generate structure of ASM-file        SAMPLE1.DEC  -> SAMPLE2.DEC
    2.3 Define Procedures                          "       ->      "
    2.4 Adapt Procedure Calls                      "       ->      "
    2.5 Define Global Variables                    "       ->      "
    2.6 Adapt Variable Calls                       "       ->      "
    2.7 Initialization of module                   "       ->      "
    2.8 Set DATA Segment                           "       ->      "
    2.9 Replace absolute Jumps                     "       ->      "
    2.10 Case-statements                      SAMPLE2.DEC  -> SAMPLE3.ASM
    2.11 Correct all hexadecimal values            "       ->      "
         inside the code
    2.12 Parameter Length                          "       ->      "
    2.13 REP Command
  3. Definition module                        SAMPLE34.DEF -> SAMPLE40.DEF
  4. Enhancements to the Assembler Module
    4.1 Constants                             SAMPLE1.DEC  -> SAMPLE2.DEC
    4.2 Procedure-Header                           "       ->      "
    4.3 Introduce Names for Parameters             "       ->      "
    4.4 Define Local Variables
    4.5 USES



1. Use V3.40-M2DECODE on V3.40-OBJ files
----------------------------------------

   ->  M2DECODE generates 'sample.DEC' from 'sample.OBJ'


   Alternatively Tayloris' TOBJ2ASM can be used:

   Advantages:     - The generated output is almost MASM-compatible
                   - Finishing after chapters 3.1 and 3.2 not neccessary
                   - Labels are produced directly

   Disadvantages:  - No Modula-2 statements in ASM listing
                   - Some statements cannot be translated and are marked by
                     '???' (especially CODE statements)

   Usage of TOBJ2ASM will most probably produce a usable (i.e. linkable) result
   faster, but usage of M2DECODE will produce a well documented ASM-file, that
   is easier to maintain in the long run.

   The method described below is for output produced by M2DECODE, but it can
   of course be used for TOBJ2ASM output as well, with the neccessary
   adaptations.


2. Generate the Assembler file
------------------------------

   2.1 Delete opcodes and header
   -----------------------------

     'SAMPLE.DEC' as produced by M2DECODE:

     0005  ; *** line #39       ; waitForRetrace := WaitForRetrace;
     0005  8A 46 06             MOV   AL,[BP+0006]
     0008  B9 00 00             MOV   CX,SEG SAMPLE_DATA

     results in example 'SAMPLE1.DEC':

     ; waitForRetrace := WaitForRetrace;
     MOV   AL,[BP+0006]
     MOV   CX,SEG SAMPLE_DATA


   2.2 Generate structure of ASM-file
   ----------------------------------

     -> we add the structure of a ASM-file to our example 'SAMPLE1.DEC',
        resulting in example 'SAMPLE2.DEC'.

     .MODEL LARGE,Pascal                                ; conventions

     PUBLIC Sample                                      ; exported procedures
     PUBLIC Sample_InstallVideoBase                     ; exported procedures
     PUBLIC Sample_WriteVideo                           ; exported procedures
     PUBLIC Sample_WriteMono                            ; exported procedures
     PUBLIC Sample_WriteColor                           ; exported procedures


     SAMPLE_DATA SEGMENT WORD PUBLIC 'FAR_DATA'         ; Data section
     InitFlag DB 0                                      ; Data section
     EVEN                                               ; Data section
       ..... (definition of var's) ......               ; Data section
       ..... (definition of var's) ......               ; Data section
     @CurSeg  ENDS                                      ; Data section


     .CODE Sample_TEXT                                  ; Code section
                                                        ; Code section
     ..... (code of proc's) .....                       ; Code section
     ..... (code of proc's) .....                       ; Code section
     ..... (code of proc's) .....                       ; Code section

     Sample PROC                                        ; Initialization code
       ..... (initialisation proc) .....                ; Initialization code
       ..... (initialisation proc) .....                ; Initialization code
     Sample ENDP                                        ; Initialization code

     @CurSeg  ENDS                                      ; End of code section

     END                                                ; End of file


   2.3 Define Procedures
   ---------------------

     There are two ways to define the implemented procedures:

     - By defining a label with the procedure's name as shown in 'SAMPLE2.DEC':


         Define procedure labels (i.e. procedure identifiers):

                ; PROCEDURE L__InstallVideoBase__Sample
         Sample_InstallVideoBase:

     - By introducing the Assembler directives PROC and ENDP with the correct
       parameter definitions and SHORT/LONG attributes.
       This way of defining a procdure is described in chapter 4.2


   2.4 Adapt Procedure Calls
   -------------------------

     as translated:  CALL   L__WriteColor__Sample
     to be defined:  CALL   FAR PTR  Sample_WriteColor

     Explanations to our example 'SAMPLE2.DEC':
     The following two statements, namely a push cs and a near call,

         PUSH  CS
         CALLS L__WriteColor__Sample

     are equivalent to a far call and can be translated in two ways:

         PUSH  CS
         CALLS NEAR PTR Sample_WriteColor
     or
         ; PUSH  CS
         CALLS FAR PTR Sample_WriteColor


   2.5 Define Global Variables
   ---------------------------

     -> see example 'SAMPLE2.DEC':

     SAMPLE_DATA SEGMENT WORD PUBLIC 'FAR_DATA'
     InitFlag DB 0
     EVEN

     PUBLIC Sample_videoBase
     PUBLIC Sample_waitForRetrace

     Sample_videoBase      DW ColorBase
     Sample_waitForRetrace DB 0

     @CurSeg  ENDS


   2.6 Adapt Variable Calls
   ------------------------

     as translated:  MOV   AX,[videoBase__Sample]
     to be defined:  MOV   AX,[Sample_videoBase]


   2.7 Initialization of module
   ----------------------------

     - Insert the following code at the beginning of the initialization routine:

             ; PROCEDURE $INIT__Sample
       ;InitBegin
       Sample:
             push  DS
             SetDS AX
             cmp   InitFlag,0                 ; Module already initialized ?
             je    Initialize_Module
             pop   DS    <----                ; yes : just return
             retf
       Initialize_Module:
             mov   InitFlag,0FFH              ; no  : set M2 init flag
                                             ;       do the initialization

     - DS has to be saved (i.e. PUSHed) at the beginning of the initialization
       code. Thus it has to be restored (i.e. POPped) before each RET of the
       initialisation routine.

            .. code ..
            .. code ..
            pop   DS     <----
            RETF


   2.8 Set DATA Segment
   --------------------

     Whenever the module's global data are accessed, an ASSUME directive for
     the data to be accessed has to be active, and the DS register properly set.
     The best way to assure this is to use a macro that does these settings.
     see macro 'SetDS' in 'SAMPLE2.DEC':

            ; macro for Set Data Segment
            SetDS MACRO UseReg
                  mov UseReg, SAMPLE_DATA
                  mov DS, UseReg
                  ASSUME DS: SAMPLE_DATA
            ENDM


     as translated: MOV   CX,SEG SAMPLE_DATA
                    MOV   DS,CX

     to be defined: ; MOV   CX,SEG SAMPLE_DATA
                    ; MOV   DS,CX
                    SetDS CX

     Note also that it could be sufficient having the ASSUME directive once
     at the top of the module if you don't use other ASSUME's.


   2.9 Replace absolute Jumps
   --------------------------

     All absolute jumps have to be replaced by jumps to labels that have to
     be defined at the corresponding places in the Assembler source.

     as translated:  JE  0049
     to be defined:  JE  WriVid1


   2.10 CASE-statements
   --------------------

     The jump table of certain CASE statements has to be set up.

     example 'SAMPLE2.DEC', initialization procedure:

      00 ADD   [BX+SI],AL                      ; This represents the jump
      00 ADD   [BX+SI],AL                      ; table of the CASE stmnt.
      00 ADD   [BX+SI],AL                      ;
      00 ADD   [BX+SI],AL                      ; It is set to all zeros
      00 ADD   [BX+SI],AL                      ; which are filled by the
      00 ADD   [BX+SI],AL                      ; linker from the corres-
      00 ADD   [BX+SI],AL                      ; ponding fixups
      00 ADD   [BX+SI],AL                      ;
      00 ADD   [BX+SI],AL                      ;
         ; MOV   CX,SEG SAMPLE_DATA
         ; MOV   DS,CX
         SetDS CX
         MOV   BX,[Sample_videoBase]
         CMP   BX, 0007
         JBE   0160h
         MOV   BX,0008
         SHL   BX,1
         JMP   CS:[BX+013Ch]


     One way would be to do the linker's work by filling in the correct
     relative jump offsets. But usually it is easier to implement the
     complete CASE statement in Assembler, as done in 'SAMPLE3.ASM'


   2.11 Correct all hexadecimal values inside the code
   ---------------------------------------------------

     Due to an error in M2DECODE the 'H' character to indicate that a number
     must be interpreted as a hexadecimal value are missing. All numbers are
     correctly written with their hexadecimal value, and the 'H' has to be
     appended manually:


     as translated:  DEC  [BP+000E]       <- error when executing MASM
     to be defined:  DEC  [BP+000EH]

     as translated:  DEC  [BP+0012]       <- no error from MASM, but this number
     to be defined:  DEC  [BP+0012H]         will be interpreted as decimal 12



   2.12 Parameter Length
   ---------------------

     When procedures are defined by just introducing the corresponding label
     instead of by Assembler directives PROC and ENDP, the length of the
     parameters have to be inserted into all statements that do not just
     load or store registers (e.g. CMP)


     -> 'SAMPLE3.ASM'

                     ; IF WordsToMove = 0 THEN
     as translated:  CMP   [BP+0006], 00
     to be defined:  CMP   WORD PTR [BP+0006], 00


   2.13 REP Statement
   ------------------

     REP statements are decoded in a way that produces MASM errors, and thus
     have to be corrected:


                        ; CODE (0F3H,0A4H );    (* REP MOVSB *)
     as translated:     REPN
                        MOVSB

     to be defined:     REP MOVSB

     ( Our example does not include this kind of statement )


3. Definition module
--------------------

   V4.0 makes certain assumptions about which registers are altered or must
   be preserved by a procedure call (see Programmer's Guide, chapter 9.6).
   Additionally the correct re-loading of DS, as was guaranteed by V3.4, no
   longer is done automatically. Thus all procedure definitions that refer
   to another language must be done in a way that guarantees correct values
   after the procedure has been executed. This is done by specifying the
   ALTERS attribute for a procedure.

   A safe way to do this, is by specifying all registers as altered:

   SAMPLE34.DEF:
         PROCEDURE InstallVideoBase ( BaseAdr        : CARDINAL;
                                      WaitForRetrace : BOOLEAN );

   SAMPLE40.DEF:
         PROCEDURE InstallVideoBase ( BaseAdr        : CARDINAL;
                                      WaitForRetrace : BOOLEAN )
                                        [ALTERS(AX,BX,CX,DX,SI,DI,DS,ES)];


   To avoid pushing of all registers whenever the corresponding procedure
   is called, only those registers that effectively are altered can be
   specified. But this requires a close inspection of the procedure's
   code. Usually it is not sufficient to specify those registers that
   are already saved by the procedure itself (PUSH and POP done by CODE
   statements). DS usually must be specified as altered.


   Finally, the initialization routine of the module has to be exported:

         PROCEDURE InitSample ['Sample'] () [ALTERS(AX,BX,CX,DX,SI,DI,DS,ES)];



4. Enhancements to the Assembler Module
---------------------------------------

   Chapter 2 describes the fastest way to make a V3.4 module using CODE into
   a module usable from V4.0 when using the M2DECODE method.
   Some enhancements that first of all make the resulting Assembler module
   easier to understand and maintain are descibed below.

   All modifications described in this chapter require good knowledge about
   Assembler programming, and the way procedures are built and called.


   4.1 Constants
   -------------

   Assembler constants can be defined for the constants of the Modula program:

         RetracePort equ   03DAH
         NoVideo     =        0     ; these values correspond to the type
         Mono        =        1     ; VideoModes in the implementation module
         CGAColor    =        2
         CGAMono     =        3
         EGAColor    =        4
         EGAMono     =        5
         VGAColor    =        6
         VGAMono     =        7
         MGAColor    =        8
         MGAMono     =        9

         ColorBase   =    0B800H     ; base segments for alpha modes
         MonoBase    =    0B000H

   These constants can be introduced into the Assembler statements that use
   them, thus making it easy to just change a constant for all its uses in
   the module.


   4.2 Procedure-Header
   --------------------

   Instead of just introducing a label for the procedure name and leave the
   code, as it was originally generated by the V3.4 compiler, untouched the
   module's procedures can be declared using the Assembler directives PROC
   and ENDP. This automatically generates the correct procedure entry/exit
   code. Thus the corresponding code generated by V3.4 has to be removed.


       ; BEGIN
       PUSH  BP         <- This statement has to be removed
       MOV   BP,SP      <- This statement has to be removed

          ......

       ; END ProzedurName;
       MOV   SP,BP      <- This statement has to be removed
       POP   BP         <- This statement has to be removed
       RET   0002       <- This statement has to be removed



     sample3.asm (defines just a label):
     ---------------------------------

       ;PROCEDURE InstallVideoBase ( BaseAdr : CARDINAL;
       ;                             WaitForRetrace : BOOLEAN);
       Sample_InstallVideoBase:
                ; BEGIN
                PUSH  BP
                MOV   BP,SP
                    ......
                    ......
                ; END InstallVideoBase ;
                MOV   SP,BP
                POP   BP
                RETF  0004


     sample4.asm (uses PROC and ENDP):
     -------------------------------

       ;PROCEDURE InstallVideoBase ( BaseAdr : CARDINAL;
       ;                             WaitForRetrace : BOOLEAN);
       Sample_InstallVideoBase PROC BaseAdr:WORD, WaitForRetrace:WORD
                ; BEGIN
                ; PUSH  BP
                ; MOV   BP,SP
                    ......
                    ......
                ; END InstallVideoBase ;
                ; MOV   SP,BP
                ; POP   BP
                ; RET   0004
                RETF
       Sample_InstallVideoBase ENDP


   4.3 Introduce Names for Parameters
   ----------------------------------

   When using PROC/ENDP to define a procedure, the names given to the
   specified parameters can be introduced into the procedure's code:


     sample3.asm (uses hardcoded offsets):
     -----------------------------------

       Sample_WriteVideo:
                      .....
                      .....
           ; WriteColor (0,x,y,SourceAdr,WordsToMove)
           MOV   AX,0000
           PUSH  AX
           PUSH  [BP+000Ch]
           PUSH  [BP+000Ah]
           LES   BX,[BP+0006]
           PUSH  ES
           PUSH  BX
           PUSH  [BP+0004]


     sample4.asm (uses identifiers):
     -----------------------------

       Sample_WriteVideo PROC x:WORD, y:WORD, SourceAdr:DWORD, WordsToMove:WORD
                      .....
                      .....
           ; WriteColor (0,x,y,SourceAdr,WordsToMove)
           MOV   AX,0000
           PUSH  AX
           PUSH  x
           PUSH  y
           LES   BX,SourceAdr
           PUSH  ES
           PUSH  BX
           PUSH  WordsToMove


   4.4 Define Local Variables
   --------------------------

   To make the program easier to read and maintain, identifiers for local
   variables can be introduced.


        ProcName PROC
        ; BEGIN
        ; PUSH  BP
        ; MOV   BP,SP
        PUSH  AX        ; one local variable of wordsize

     The following declaration allows to access this variable by an identifier:

        LocVarName EQU [BP-0002]

     When replacing the references to [BP-0002] by 'LocVarName' make sure to
     add the directives that specify its length, otherwise an error message
     is produced:

     ; IF Search=0 THEN
     CMP WORD PTR SetVideoMode_Search, 0


   4.5 USES Directive
   ------------------

   As described above (3.), the saving of all modified registers may be
   done when calling a procedure by specifying ALTERS with the procedure
   declaration.
   An alternative to this is to use the USES directive of the Assembler,
   and do the saving of all modified registers inside the procedure.

   Example:

   Procedure 'AssembProc' modifies registers CX and DS

     Saving of CX, DS when calling the procedure is done by defining:

       PROCEDURE AssembProc( Formals ):ReturnType [ALTERS(CX,DS)]

     This definition is Modula-2 V4.0 specific and the ALTERS directive
     goes to the Module-2 4.0 compiler.


     Saving of CX, DS inside the procedure is done by defining:

       ProcBegin AssembProc <USES CX DS>,Mode:WORD

     This definition is Assembler specific and the USES directive goes
     to the Assembler (e.g. MASM)


     When using the USES method to save modified registers, the procedure's
     stack looks different, since the saved registers are now on the stack,
     too. Thus all local variables are on different locations, and have to
     be addressed differently (and preferably by identifiers!).
     The following table shows the stack contents of the procedure 'AssembProc'
     declared with  <USES CX,DS>. The local variable's address now is [BP-0006]
     instead of [BP-0002].


                   Ŀ
              23BC  78 00       Parameter1     
              23BA  64 00       Parameter2     
              23B8  00 00       Parameter3     
                   Ĵ
              23B4  3A 00 E7 4C return address 
                   Ĵ
              23B2  CE 23       BP                <- BP
                   Ĵ
              23B0  00 00       CX                <- BP-2
              23AE  C4 02       DS             
                   Ĵ
              23AC  00 00       loc VAR           <- BP-6
                   


      addressing via offset value in statement:

            ; loc := Parameter1;
            MOV   AX,Parameter1
            MOV   [BP-0002],AX        change into ->  MOV   [BP-0006],AX


      addressing via identifier:

            LocVarName EQU [BP-0002]  change into ->  LocVarName EQU [BP-0006]


      Additionally the local variables have to be removed from the stack
      manually (in the above case by a POP AX just before the ENDP directive),
      in order to allow the procedure exit code (generated automatically,
      according to the directives in the procedure declaration) to restore
      the modified registers properly.


