; ****************************************************************************
; *                                                                          *
; *                       IBM/Microsoft Confidential                         *
; *                                                                          *
; *                 Copyright (c) IBM Corporation  1987, 1996                *
; *                 Copyright (c) Microsoft Corp.  1987, 1990                *
; *                           All Rights Reserved                            *
; *                                                                          *
; ****************************************************************************
;*****************************************************************************
;
;
; Change History:
; ---------------
;  4/29/96   1.01  cgw  Added "Error:" to error messages for scanerr
;                       Remove newsize parm - round to 512 byte bdy instead
;
;*****************************************************************************

        page    ,132
        title   bios2com - convert OS2LDR.EXE to OS2LDR (com file)
        subttl  ****  IBM  CONFIDENTIAL  ****
        name    bios2com
        .286c
banner  equ     'bios2com   Version 1.01   4/29/96'

cseg    segment word public 'code'
cseg    ends
dseg    segment word public 'data'
dseg    ends
sseg    segment word stack 'data'
sseg    ends

cgroup  group   cseg
dgroup  group   dseg,sseg

sseg    segment
        dw      512 dup (?)
tos     equ     $
sseg    ends

        .xlist
        include struc.inc
        .list
;=====> struc.inc has been included


;       Old EXE Header format

oh_s    STRUC
oh_signature    dw      ?               ; Must contain 4D5A
oh_len_mod_512  dw      ?               ; Low 9 bits of length
oh_pages        dw      ?               ; Number of 512 byte pages
oh_rle_count    dw      ?               ; Count of reloc entries
oh_par_dir      dw      ?               ; # of paras before image
oh_min_BSS      dw      ?               ; Min # of paras of BSS
oh_max_BSS      dw      ?               ; Max # of paras of BSS
oh_SS           dw      ?               ; SS of image
oh_SP           dw      ?               ; SP of image
oh_chksum       dw      ?               ; Ignored checksum of file
oh_IP           dw      ?               ; IP of image
oh_CS           dw      ?               ; CS of image
oh_rle_table    dw      ?               ; Byte offset to reloc table
oh_iov          dw      ?               ; Overlay number( 0 for root )
oh_sym_tab      dd      ?               ; offset of symbol table in file
oh_40_BB        dw      ?               ; DOS 4.0 behavior bits
oh_win_BB       dw      ?               ; Windows behavior bits
oh_oem_ID       dw      ?               ; Reserved for OEM's
oh_oem_info     dw      ?               ; Reserved for OEM's
oh_res          dw      10 DUP (?)      ; Reserved for future use
oh_nhdr_offset  dd      ?               ; offset to new EXE header
oh_s    ENDS

;       Module Table Entry format

mte_s   STRUC

;       New EXE Header format

mte_signature           dw      ?       ; must contain "NE"
mte_version             db      ?       ;
mte_revision            db      ?       ;
mte_entrytbl_off        dw      ?       ; file entry table offset
mte_entrytbl_len        dw      ?       ; length of export entry table (bytes)
mte_crc                 dd      ?       ; crc of entire file contents
mte_flags               dw      ?       ;
mte_autods              dw      ?       ; auto data segment table index
mte_heapsiz             dw      ?       ; dynamic heap bytes added to data seg
mte_stacksiz            dw      ?       ; stack bytes added to data segment
mte_startproc           dd      ?       ;
mte_stack               dd      ?       ;
mte_segtbl_cnt          dw      ?       ; # of entries in segment table
mte_modref_cnt          dw      ?       ; # of entries in module reference table
mte_nonresnam_len       dw      ?       ; length nonresident name table (bytes)
mte_segtbl_off          dw      ?       ; file segment table offset
mte_resrctbl_off        dw      ?       ; file resource table offset
mte_resnamtbl_off       dw      ?       ; file resident name table offset
mte_modref_off          dw      ?       ; file module reference table offset
mte_importnam_off       dw      ?       ; file import name table offset
mte_nonresnam_off       dw      2 DUP (?);file nonresident name table offset
mte_movent_cnt          dw      ?       ; count of movable entries
mte_alignshft           dw      ?       ; segment alignment shift count
mte_resrctbl_cnt        dw      ?       ; count of resource table entries
                        dw      ?       ; reserved
                        dw      ?       ; reserved
mte_dyntrchndl          dw      ?       ; dynamic trace segment handle
mte_usecount            dw      ?       ; usage count for program module
mte_pathname            dw      ?       ; offset to module pathname
mte_s   ENDS

;       Overloaded MTE field definitions

mte_link        EQU     (word ptr mte_version)  ; link to next mte, 0 is end
mte_modptrs     EQU     mte_entrytbl_len
mte_sfn         EQU     (word ptr mte_crc)      ; system file number for module
mte_size        EQU     (word ptr mte_crc+2)    ; size of the mte

;       Module Segment Table Entry format

ste_s   STRUC
ste_offset      dw      ?               ; file offset to segment data
ste_size        dw      ?               ; file data size (paragraphs)
ste_flags       dw      ?               ; type and attribute flags
ste_minsiz      dw      ?               ; minimum allocation size
ste_seghdl      dw      ?               ; segment handle
ste_selector    dw      ?               ; selector allocated for segment
ste_s   ENDS

; STE flags

ITERATED        EQU     0008h           ; segment data is iterated
RELOCINFO       EQU     0100h           ; segment has relocation records

        $defdist = $near


cr      equ     0dh
lf      equ     0ah

        .xlist
.begin  macro   $name,$scope
cseg    segment
        assume  cs:cgroup,ds:dgroup,ss:dgroup,es:nothing
$name   proc    $scope
        endm

.end    macro   $name
        ret
$name   endp
cseg    ends
        assume  cs:nothing,ds:nothing,es:nothing,ss:nothing
        endm

.byte   macro   $name,$data
dseg    segment
$name   db      $data
dseg    ends
        endm

.word   macro   $name,$data
dseg    segment
$name   dw      $data
dseg    ends
        endm

.dbyte  struc
lsb     db      ?
msb     db      ?
.dbyte  ends

.dword  macro   $name,$data
dseg    segment
$name   dd      $data
dseg    ends
        endm

.dblword struc
lsw     dw      ?
msw     dw      ?
.dblword ends

.string macro   $name,$data
dseg    segment
$name           db      $data,0
$name&Len       equ     $-$name-1
dseg    ends
        endm
        .list
;======> my pseudo ops have been included

        .xlist
        extrn   DosOpen:far
        extrn   DosRead:far
        extrn   DosWrite:far
        extrn   DosClose:far
        extrn   DosDelete:far
        extrn   DosExit:far

open_ro macro   $handle,$name
        local   label,label2,FileName,Handle,Action
        .string FileName,<$name>                ;@FileName
        mov     ax,seg FileName
        push    ax
        mov     ax,offset FileName
        push    ax
        .word   Handle,?                        ;@FileHandle
        mov     ax,seg Handle
        push    ax
        mov     ax,offset Handle
        push    ax
        .word   Action,?                        ;@ActionTaken
        mov     ax,seg Action
        push    ax
        mov     ax,offset Action
        push    ax
        xor     ax,ax                           ;FileSize
        push    ax
        push    ax
        mov     ax,0000000000000000b            ;FileAttribute
        push    ax
        mov     ax,00000001b                    ;OpenFlag
        push    ax
        mov     ax,0000000001000000b            ;OpenMode  @@ (read access)
        push    ax
        xor     ax,ax                           ;reserved
        push    ax
        push    ax
        call    DosOpen
        or      ax,ax
        jz      label2
        stc
        jmp     label
label2:
        mov     ax,Handle                       ;Handle
        mov     $handle,ax
label:
        endm

create  macro   $handle,$name
        local   label,label2,FileName,Handle,Action
        .string FileName,<$name>                ;@FileName
        mov     ax,seg FileName
        push    ax
        mov     ax,offset FileName
        push    ax
        .word   Handle,?                        ;@FileHandle
        mov     ax,seg Handle
        push    ax
        mov     ax,offset Handle
        push    ax
        .word   Action,?                        ;@ActionTaken
        mov     ax,seg Action
        push    ax
        mov     ax,offset Action
        push    ax
        xor     ax,ax                           ;FileSize
        push    ax
        push    ax
        mov     ax,0000000000000000b            ;FileAttribute
        push    ax
        mov     ax,00010010b                    ;OpenFlag
        push    ax
        mov     ax,0000000001000001b            ;OpenMode @@ (write access)
        push    ax
        xor     ax,ax                           ;reserved
        push    ax
        push    ax
        call    DosOpen
        or      ax,ax
        jz      label2
        stc
        jmp     label
label2:
        mov     ax,Handle                       ;FileHandle
        mov     $handle,ax
label:
        endm

buffer  macro   $name,$length
        .word   $name&Len,<$length>
        .byte   $name,<$length dup (?)>
        endm

read    macro   $handle,$buffer
        local   label,label2,BytesRead
        mov     ax,$handle                      ;FileHandle
        push    ax
        mov     ax,seg $buffer                  ;@BufferArea
        push    ax
        mov     ax,offset $buffer
        push    ax
        mov     ax,$buffer&Len                  ;BufferLength
        push    ax
        .word   BytesRead,?                     ;@BytesRead
        mov     ax,seg BytesRead
        push    ax
        mov     ax,offset BytesRead
        push    ax
        call    DosRead
        or      ax,ax
        jz      label2
        stc
        jmp     label
label2:
        mov     ax,BytesRead                    ;BytesRead
        mov     $buffer&Len,ax
label:
        endm

write   macro   $handle,$buffer
        local   label,label2,BytesWritten
        mov     ax,$handle                      ;FileHandle
        push    ax
        mov     ax,seg $buffer                  ;@BufferArea
        push    ax
        mov     ax,offset $buffer
        push    ax
        mov     ax,$buffer&Len                  ;BufferLength
        push    ax
        .word   BytesWritten,?                  ;@BytesWritten
        mov     ax,seg BytesWritten
        push    ax
        mov     ax,offset BytesWritten
        push    ax
        call    DosWrite
        or      ax,ax
        jz      label2
        stc
        jmp     label
label2:
        mov     ax,BytesWritten                 ;BytesWritten
        sub     ax,$buffer&Len          ;;set carry (borrow) if not all written
label:
        endm

close   macro   $handle
        local   label
        mov     ax,$handle                      ;FileHandle
        push    ax
        call    DosClose
        or      ax,ax
        jz      label
        stc
label:
        endm

erase   macro   $name
        local   label,FileName
        .string FileName,<$name>                ;@FileName
        mov     ax,seg FileName
        push    ax
        mov     ax,offset FileName
        push    ax
        xor     ax,ax                           ;reserved
        push    ax
        push    ax
        call    DosDelete
        or      ax,ax
        jz      label
        stc
label:
        endm

print   macro   $message
        local   label,label2,MessageText,BytesWritten
        mov     ax,1                            ;FileHandle
        push    ax
        .string MessageText,<$message>          ;@BufferArea
        mov     ax,seg MessageText
        push    ax
        mov     ax,offset MessageText
        push    ax
        mov     ax,MessageText&Len              ;BufferLength
        push    ax
        .word   BytesWritten,?                  ;@BytesWritten
        mov     ax,seg BytesWritten
        push    ax
        mov     ax,offset BytesWritten
        push    ax
        call    DosWrite
        or      ax,ax
        jz      label2
        stc
        jmp     label
label2:
        mov     ax,dgroup:BytesWritten          ;BytesWritten
        sub     ax,MessageText&Len      ;;set carry (borrow) if not all written
label:
        endm

stop    macro   $returncode
        mov     ax,1                            ;ActionCode
        push    ax
        mov     ax,$returncode                  ;ReturnCode
        push    ax
        call    DosExit
        endm
        .list
;=====> my dos macros have been included

        page

CBBUFFER                equ     16*1024 ; for file copy

os2memd         equ     -4      ;process os2ldrmd
os2mem          equ     -3      ;process os2ldrm
CallDebug       equ     -2      ;process os2ldrc
Debug           equ     -1      ;process os2ldrd
NoDebug         equ     0       ;process os2ldr

        .byte   Flag,NoDebug
        .byte   Usage,0
        .word   ExeFile,?
        .word   ComFile,?
        .word   SegLength,?
        .word   ComFileLength,0

.begin  bios2com,far
        push    ax
        push    bx
        print   <banner,cr,lf>
        pop     bx
        pop     ax
        call    ParseCommandLine
        call    OpenExeFile
        call    CheckHeader
        call    OpenComFile
        call    CopySegment
        call    PadComFile
        call    CloseFiles
        stop    0
.end    bios2com

.begin  ParseCommandLine,near
        cld
        mov     es,ax                   ;es:di -> command line
        mov     di,bx
        mov     al,0                    ;skip over the command
        repne   scasb
        push    di                      ;find the length of the argument string
        mov     cx,-1
        repne   scasb
        add     cx,1
        neg     cx
        pop     di
        dec     cx                      ;don't count the trailing <nul>
        .repeat
          mov     al,' '                ; find the start of the next argument
          repe    scasb
          .if <cx ne 0>
            mov   si,di
            dec   si
            inc   cx
            mov   dx,word ptr es:[si]
            .if   <dl eq '-'> and       ; check for -d
            .if   <dh eq 'd'> or
            .if   <dh eq 'D'>
              mov     Flag,Debug
              sub     cx,2
              add     si,2
            .elseif  <dl eq '-'> and    ; check for -c
            .if   <dh eq 'c'> or
            .if   <dh eq 'C'>
              mov     Flag,CallDebug
              sub     cx,2
              add     si,2
            .elseif  <dl eq '-'> and    ; check for -m
            .if   <dh eq 'm'> or
            .if   <dh eq 'M'>
              mov     Flag,os2mem
              sub     cx,2
              add     si,2
            .elseif  <dl eq '-'> and    ; check for -n
            .if   <dh eq 'n'> or
            .if   <dh eq 'N'>
              mov     Flag,os2memd
              sub     cx,2
              add     si,2
            .else
              inc   [Usage]
              inc   si
              dec   cx
            .endif
            mov   di,si
          .endif
        .until <cx eq 0>
        .if <[Usage] ne 0>
          print   <'bios2com: Error: invalid parameter',cr,lf>
          print   <'usage: bios2com [ -d | -c | -m | -n ]',cr,lf>
          stop    -1
        .endif
.end    ParseCommandLine

.begin  OpenExeFile,near
        .if     <Flag eq NoDebug>
          open_ro ExeFile,'OS2LDR.EXE'
        .elseif <Flag eq Debug>
          open_ro ExeFile,'OS2LDRD.EXE'
        .elseif <Flag eq os2mem>
          open_ro ExeFile,'OS2LDRM.EXE'
        .elseif <Flag eq os2memd>
          open_ro ExeFile,'OS2LDRMD.EXE'
        .else
          open_ro ExeFile,'OS2LDRC.EXE'
        .endif
        .if     <c>
          print   <'bios2com: Error opening OS2LDR(M)(D).EXE',cr,lf>
          stop    -1
        .endif
.end    OpenExeFile

.begin  CheckHeader,near
        buffer  Header,512
        read    ExeFile,Header
        .if     <c>
          print   <'bios2com: Error reading OS2LDR(M)(D).EXE',cr,lf>
          stop    -1
        .endif
; the following check is to verify an assumption
        .if     <HeaderLen ne 512>
          print   <'bios2com: Error: Less than 512 bytes in file',cr,lf>
          stop    -1
        .endif
        lea     bx,Header                       ;ds:bx -> old header
        .if     <[bx].oh_signature ne "ZM">
          print   <'bios2com: Error: Not an exe-type file',cr,lf>
          stop    -1
        .endif
        .if     <[bx].oh_rle_table ne 0040h>
          print   <'bios2com: Error: Not a new exe-type file',cr,lf>
          stop    -1
        .endif
        add     bx,[bx].oh_nhdr_offset.lsw      ;ds:bx -> new header
        .if     <[bx].mte_signature ne "EN">
          print   <'bios2com: Error: Not NE format file',cr,lf>
          stop    -1
        .endif
        .if     <[bx].mte_segtbl_cnt ne 1>
          print   <'bios2com: Error: Greater than 1 segment',cr,lf>
          stop    -1
        .endif
        .if     <[bx].mte_startproc.lsw ne 0>
          print   <'bios2com: Error: Entry point not 0',cr,lf>
          stop    -1
        .endif
        .if     <[bx].mte_modref_cnt ne 0>
          print   <'bios2com: Error: Dynamic link references',cr,lf>
          stop    -1
        .endif
; the following check is to verify an assumption
        .if     <[bx].mte_alignshft ne 9>
          print   <'bios2com: Error: Alignment is not 512',cr,lf>
          stop    -1
        .endif
        add     bx,[bx].mte_segtbl_off          ;ds:bx -> segment table
        test    [bx].ste_flags,RELOCINFO+ITERATED;test the segment flags
        .if     <nz>
          print   <'bios2com: Error: OS2LDR(M)(D).EXE: relocs or iterated data',cr,lf>
          stop    -1
        .endif
        mov     ax,[bx].ste_size                ;set up the segment size
        mov     SegLength,ax
.end    CheckHeader

.begin  OpenComFile,near
        .if     <Flag eq NoDebug>
          create  ComFile,'OS2LDR'
        .elseif <Flag eq Debug>
          create  ComFile,'OS2LDRD'
        .elseif <Flag eq os2mem>
          create  ComFile,'OS2LDRM'
        .elseif <Flag eq os2memd>
          create  ComFile,'OS2LDRMD'
        .else
          create  ComFile,'OS2LDRC'
        .endif
        .if     <c>
          print   <'bios2com: Error creating OS2LDR(M)(D)',cr,lf>
          stop    -1
        .endif
.end    OpenComFile

.begin  CopySegment,near
        buffer  CopyBuffer,CBBUFFER
        .repeat
          .if     <SegLength ae CBBUFFER
            mov     CopyBufferLen,CBBUFFER
          .else
            mov     ax,SegLength
            mov     CopyBufferLen,ax
          .endif
          read    ExeFile,CopyBuffer
          .if     <c>
            print   <'bios2com: Error reading OS2LDR(M)(D).EXE',cr,lf>
            erase   'OS2LDR'
            .if     <c>
              print   <'bios2com: Error deleting OS2LDR',cr,lf>
            .endif
            erase   'OS2LDRD'
            .if     <c>
              print   <'bios2com: Error deleting OS2LDR(D)',cr,lf>
            .endif
            erase   'OS2LDRM'
            .if     <c>
              print   <'bios2com: Error deleting OS2LDR(M)',cr,lf>
            .endif
            erase   'OS2LDRMD'
            .if     <c>
              print   <'bios2com: Error deleting OS2LDR(MD)',cr,lf>
            .endif
            stop    -1
          .endif
          write   ComFile,CopyBuffer
          .if     <c>
            print   <'bios2com: Error writing OS2LDR(M)(D)',cr,lf>
            .if <Flag eq NoDebug>
              erase   'OS2LDR'
            .elseif <Flag eq Debug>
              erase  'OS2LDRD'
            .elseif <Flag eq os2mem>
              erase  'OS2LDRM'
            .elseif <Flag eq os2memd>
              erase  'OS2LDRMD'
            .else
              erase  'OS2LDRC'
            .endif
            .if     <c>
              print   <'bios2com: Error deleting OS2LDR(M)(D)',cr,lf>
            .endif
            stop    -1
          .endif
          mov     ax,SegLength
          sub     ax,CopyBufferLen
          mov     SegLength,ax
          mov     ax,ComFileLength
          add     ax,CopyBufferLen
          mov     ComFileLength,ax
        .until  <SegLength eq 0>
.end    CopySegment

.begin  PadComFile,near
        print   'bios2com: Actual code length '
        mov     ax,ComFileLength
        call    DPrint
        print   <' bytes',cr,lf>
        mov     ax,ComFileLength
        add     ax,01FFh
        and     ax,0FE00h
        .word   FileLength,?
        mov     FileLength,ax
        sub     ax,ComFileLength
        .word   PadLength,?
        mov     PadLength,ax
        print   'bios2com: File length rounded to '
        mov     ax,FileLength
        call    DPrint
        print   <' bytes',cr,lf>
        mov     ax,PadLength
        .if     <ax eq 0>
          print   <'bios2com: No padding needed',cr,lf>
        .else
          print   'bios2com: Adding '
          mov     ax,PadLength
          call    DPrint
          print   <' bytes of padding',cr,lf>
          mov     cx,CBBUFFER           ;zero out the pad bytes
          xor     al,al
          lea     di,CopyBuffer
          push    ds
          pop     es
          rep stosb
          .repeat
            .if     <PadLength ae CBBUFFER>
              mov     CopyBufferLen,CBBUFFER
            .else
              mov     ax,PadLength
              mov     CopyBufferLen,ax
            .endif
            write   ComFile,CopyBuffer
            .if     <c>
              call    CloseFiles
              erase   'OS2LDR'
              erase   'OS2LDRD'
              erase   'OS2LDRM'
              erase   'OS2LDRMD'
              print   <'bios2com: Error writing OS2LDR(M)(D)',cr,lf>
              .if     <c>
                print   <'bios2com: Error deleting OS2LDR(M)(D)',cr,lf>
              .endif
              stop    -1
            .endif
            mov     ax,PadLength
            sub     ax,CopyBufferLen
            mov     PadLength,ax
          .until    <PadLength eq 0>
        .endif
.end    PadComFile

.begin  CloseFiles,near
        close   ComFile
        close   ExeFile
.end    CloseFiles

.begin  DPrint,near
        buffer  Number,5
        mov     di,seg Number
        mov     es,di
        mov     di,offset Number
        xor     dx,dx                   ;10,000
        div     d10000
        .word   d10000,10000
        add     al,'0'
        stosb
        mov     ax,dx                   ;1,000
        xor     dx,dx
        div     d1000
        .word   d1000,1000
        add     al,'0'
        stosb
        mov     ax,dx                   ;100
        div     d100
        .byte   d100,100
        add     al,'0'
        stosb
        mov     al,ah                   ;10
        xor     ah,ah
        div     d10
        .byte   d10,10
        add     al,'0'
        stosb
        mov     al,ah                   ;1
        add     al,'0'
        stosb
        write   1,Number
.end    DPrint
        end     bios2com
