;*********************************************************************
;*   MLMAP.ASM                                                       *
;*                                                                   *
;*   By:            Michael Devore                                   *
;*   Date:          12/21/92                                         *
;*   Model:         Small                                            *
;*   Version:       2.5                                              *
;*   Assembler:     MASM 5.0                                         *
;*   Environment:   MS-DOS 2.x+                                      *
;*                                                                   *
;*   map file code                                                   *
;*                                                                   *
;*********************************************************************

;TITLE   WARPLINK mlmap
;PAGE    50,80

;.MODEL  SMALL

;*****************************
;* Include files             *
;*****************************

[list -]
%include "lmacros2.mac"
[list -]
%include "mlequate.mac"
%include "mldata.mac"
%include "mlerrmes.mac"
[list +]
     
;*****************************
;* Public declarations       *
;*****************************
     
PUBLIC  init_map,finish_map,map_segments,map_groups
PUBLIC  map_detail_seg

;*****************************
;* Data begins               *
;*****************************

.DATA

;*****************************
;* External declarations     *
;*****************************

; variables
EXTRN   segment_start:DWORD,segment_stop:DWORD,true_seg_len:DWORD
EXTRN   filename:BYTE

; initialized local variables

; byte values
EVEN
detail_flag DB  0           ; nonzero if not calling map_detail_seg for first time
EVEN
comm_loop_flag  DB  0       ; nonzero if publics written to map are in communal (vs public) declarations block

array_weekday:	db "SunMonTueWedThuFriSat"

.DATA?

; uninitialized local variables

EVEN
; word values
map_seg_ovl DW  ?           ; nonzero if mapping overlaid segment, holds overlay id

; byte buffer
EVEN
map_text_buffer DB  700 DUP (?) ; space for buffered line in map file

;*****************************
;* Constant data             *
;*****************************

.CONST

EVEN
mhtext_len      DB  mhtext_stop-map_header_text
map_header_text DB  CR,LF,'Start  Stop   Length Name                   Class',CR,LF
mhtext_stop equ   $

prog_text       DB  CR,LF,'PROGRAM: '

date_text       DB  CR,LF,'DATE:    '

time_text       DB  CR,LF,'TIME:    '

metext_len      DB  metext_stop-map_entry_text
map_entry_text  DB  CR,LF,CR,LF,'Program entry point at '
map_entry_value DB  '0000:0000',CR,LF
metext_stop equ   $

mestext_len:	db mestext_stop - map_exe_size_text
map_exe_size_text:
		db "Executable header size "
map_exe_header_size_value:
		db "00000h",13,10
		db "Executable initialised image size "
map_exe_init_image_size_value:
		db "00000h",13,10
		db "Executable total image size "
map_exe_total_image_size_value:
		db "00000h",13,10
mestext_stop:

mgtext_len      DB  mgtext_stop-map_group_text
map_group_text  DB  CR,LF,'Origin   Group'
mgtext_stop equ   $

msegtext_len      DB  msegtext_stop-map_detseg_text
map_detseg_text   DB  CR,LF,CR,LF,'Detailed Segment Map',CR,LF
                  DB  'Name             Ovl# Address   Length Align Combine Class    Group     Module       File',CR,LF
msegtext_stop   equ   $

maddrtext_len   DB  maddrtext_stop-maddr_text
maddr_text      DB  CR,LF,' Address   Status   Symbol Name'
maddrtext_stop  equ   $

mpub_ovl    DB  '  Ovl      '   ; overlaid public
mpub_abs    DB  '  Abs      '   ; absolute public
mpub_unres  DB  '  Unres    '   ; unresolved public
mpub_comm   DB  '  Comm     '   ; communal variable
mpub_res    DB  '  Res      '   ; resolved, resident public

	align 2, db 0
align_table:
dw align_inval	; A0
dw align_byte	; A1
dw align_word	; A2
dw align_para	; A3
dw align_page	; A4
dw align_dword	; A5
dw align_4kib	; A6 (PharLap extension)
dw align_inval	; A7

align_byte  DB  'BYTE  '
align_word  DB  'WORD  '
align_dword db  'DWORD '
align_para  DB  'PARA  '
align_page  DB  'PAGE  '
align_4kib  db  '4KIB  '
align_inval db  'INVAL '

comb_priv   DB  'PRIVATE '
comb_pub    DB  'PUBLIC  '
comb_stack  DB  'STACK   '
comb_com    DB  'COMMON  '

;*****************************
;* Code begins               *
;*****************************

.CODE

;*****************************
;* External declarations     *
;*****************************

EXTRN   link_error:NEAR,dos_error:NEAR
EXTRN   restore_ems_map:NEAR

;*****************************
;* INIT_MAP                  *
;*****************************

; open map file and write header
; destroys ax,bx,cx,dx,es,si,di

init_map    PROC
    cmp byte [is_mapfile],0        ; see if map file to be created
    jne im_1                ; yes
    ret

im_1:
    mov dx,OFFSET map_name wrt DGROUP
    xor cx,cx               ; normal file
    mov ah,3ch              ; create or truncate file
    int 21h
    call    restore_ems_map
    jc  im_error

    mov [map_handle],ax       ; save handle of map file

; get date
    push    ds
    pop es
    mov di,OFFSET map_text_buffer wrt DGROUP    ; es:di -> buffer
    mov ah,2Ah              ;DOS function to get date
    int 21h			; cx = year, dh = month, dl = day

	xor ah, ah
	push ax			; ax = day of week (0 = Sunday)
	push dx
	xchg ax, cx
	mov bx, 10
	push bx
.loopdiv:
	xor dx, dx
	div bx
	push dx
	test ax, ax
	jnz .loopdiv
.loopsto:
	pop ax
	add al, '0'
	stosb
	cmp al, '9'
	jbe .loopsto
	dec di
	mov al, '-'
	stosb
	pop bx
    mov al,bh               ;AL = month
    aam                     ;AX = month in BCD format
    add ax,"00"             ;AX = month in decimal digits
    xchg    al,ah
    stosw                   ;write month to string
    mov al, '-'
    stosb                   ;write '-' to string
    xchg    ax,bx           ;AL = day
    aam                     ;AX = day in BCD format
    add ax,"00"             ;AX = day in decimal digits
    xchg    al,ah
    stosw                   ;write day to string
	pop si			; = day of week, 0 = Sunday
	mov ax, si
	add si, si		; times 2
	add si, ax		; times 3
	add si, array_weekday
	mov al, 32
	stosb			; store a blank
	movsw
	movsb			; copy over a 3-byte day name
	push di
; write program name
    mov bx,[map_handle]
    mov dx,OFFSET prog_text wrt DGROUP
    mov cx,11
    call    NEAR PTR im_sharerout

    mov di,OFFSET exe_name wrt DGROUP
im_nameloop:
    cmp BYTE PTR [di],0
    je  im_writedate
    mov dx,di
    mov cl,1
    call    NEAR PTR im_sharerout
    inc di
    jmp SHORT im_nameloop

; write date
im_writedate:
    mov dx,OFFSET date_text wrt DGROUP
    mov cl,11
    call    NEAR PTR im_sharerout

    mov dx,OFFSET map_text_buffer wrt DGROUP
	pop cx
	sub cx, dx
    call    NEAR PTR im_sharerout

; get time
    mov di,OFFSET map_text_buffer wrt DGROUP    ; es:di -> buffer
    mov ah,2Ch              ;dos function to get time
    int 21h                 ;returns time in CX:DX
    xchg    ax,dx           ;move time to DX:AX...
    mov dx,cx
    mov al,dh               ;Al = hours (24 hr clock)
    aam                     ;AX = hours in BCD format
    add ax,"00"             ;AX = hours in decimal digits
    xchg    al,ah
    stosw                   ;write hours to string
    mov al,":"
    stosb                   ;write ":" to string
    mov al,dl               ;AL = minutes
    aam                     ;AX = minutes in BCD format
    add ax,"00"             ;AX = minutes in decimal digits
    xchg    al,ah
    stosw                   ;write minutes to string

; write time
    mov dx,OFFSET time_text wrt DGROUP
    mov cx,11
    call    NEAR PTR im_sharerout

    mov dx,OFFSET map_text_buffer wrt DGROUP
    mov cx, di
    sub cx, dx
    call    NEAR PTR im_sharerout

; write segment header
    mov dx,OFFSET map_header_text wrt DGROUP
    mov cl,2                ; write CR/LF, fall through and write full text
    call    NEAR PTR im_sharerout

    mov cl,[mhtext_len]

im_sharerout:
    mov ah,40h              ; write to file
    int 21h
    call    restore_ems_map
    jc  im_error
	cmp ax, cx
	mov ax, 27h			; insufficient disk space error
	jne im_error
    retn

im_error:
    mov dx,OFFSET map_name wrt DGROUP
    jmp NEAR PTR dos_error  ; error writing to file

init_map    ENDP

;*****************************
;* MAP_SEGMENTS              *
;*****************************

; show map of segments
; called for each segment
; prints segment start, stop, length, name, and class in map file
; uses memory variables segment_start, segment_stop, true_seg_len
; destroys ax,bx,cx,dx,di

map_segments    PROC
    push    es              ; save critical register
	push	ds
	pop	es					; es -> warplink data

; convert all needed amount of map_text_buffer bytes to spaces
    mov cx,140              ; 140 words in field
    mov ax,2020h            ; spaces in high and low bytes
    mov di,OFFSET map_text_buffer wrt DGROUP
    rep stosw			; spaces in two bytes

    xor dx,dx               ; zero count of chars in string
    mov bx,OFFSET map_text_buffer wrt DGROUP    ; bx -> place to put ascii chars

    mov cl,BYTE PTR [segment_start+2] ; get value of LSB of MSW of segment start
    mov ah,1                ; 4 bit value
    call    hexxit

    mov cl,BYTE PTR [segment_start+1] ; get value of MSB of LSW of segment start
    xor ah,ah               ; 8 bit value
    call    hexxit

    mov cl,BYTE PTR [segment_start]   ; get value of LSB of LSW of segment start
    call    hexxit

    mov BYTE PTR [bx],'H'   ; put a hexadecimal number indicator
    inc bx
    inc bx                ; move to segment length portion of field
    inc dx
    inc dx
    mov cl,BYTE PTR [segment_stop+2]  ; get value of LSB of MSW of segment stop
    mov ah,1                ; 4 bit value
    call    hexxit

    mov cl,BYTE PTR [segment_stop+1]  ; get value of LSB of LSW of segment stop
    xor ah,ah               ; 8 bit value
    call    hexxit

    mov cl,BYTE PTR [segment_stop]    ; get value of LSB of LSW of segment stop
    call    hexxit

    mov BYTE PTR [bx],'H'   ; put a hexadecimal number indicator
    inc bx
    inc bx                ; move to segment length portion of field
    inc dx
    inc dx
    mov cl,BYTE PTR [true_seg_len+2]  ; get value of LSB of MSW of segment length
    mov ah,1                ; 4 bit value
    call    hexxit

    mov cl,BYTE PTR [true_seg_len+1]  ; get value of LSB of LSW of segment length
    xor ah,ah               ; 8 bit value
    call    hexxit

    mov cl,BYTE PTR [true_seg_len]    ; get value of LSB of LSW of segment length
    call    hexxit
    mov BYTE PTR [bx],'H'   ; put a hexadecimal number indicator
    inc bx
    inc bx                ; move to segment name part of field
    inc dx
    inc dx

    pop es                  ; es -> segdef entry
    push    es              ; restore to stack
    mov di,[es:8]
    add di,8                ; bump past 2 doubleword pointers
    mov es,[es:10]          ; es:di -> segment name
    xor cx,cx               ; zero count of chars in name

ms_segloop:
    mov al,[es:di]          ; get name char
    or  al,al               ; check for zero terminator
    je  ms_2                ; done
    mov [bx],al             ; transfer char
    inc di
    inc bx
    inc cx                  ; bump count of chars in name
    inc dx                  ; bump count of chars in map string
    jmp SHORT ms_segloop    ; loop back for next char

ms_2:
    cmp cx,22               ; see if at least 22 chars were in name
    jae ms_3                ; yes
    mov ax,22
    sub ax,cx               ; get difference between length of char string and 22
    add bx,ax
    add dx,ax               ; move to class name field in map file

ms_3:
    inc bx                  ; move one space to class name field
    inc dx

    pop es                  ; es -> segdef entry
    push    es              ; restore to stack
    mov di,[es:12]
    add di,8                ; bump past 2 doubleword pointers
    mov es,[es:14]          ; es:di -> class name
    
ms_clloop:
    mov al,[es:di]          ; get name char
    or  al,al               ; check for zero terminator
    je  ms_4                ; done
    mov [bx],al             ; transfer char
    inc di
    inc bx
    inc dx                  ; bump count of chars in map string
    jmp SHORT ms_clloop    ; loop back for next char

ms_4:
    mov word [bx], CR + (LF << 8)    ; cr/lf terminate
    inc dx
    inc dx                ; bump count of chars in string

    mov cx,dx               ; get count of chars to write
    mov bx,[map_handle]
    mov dx,OFFSET map_text_buffer wrt DGROUP   ; ds:dx -> text to write
    mov ah,40h              ; write to file
    int 21h
    call    restore_ems_map
    jnc .checkspace              ; no errors
.error:
    mov dx,OFFSET map_name wrt DGROUP
    jmp NEAR PTR dos_error  ; error writing to file

.checkspace:
	cmp ax, cx
	mov ax, 27h			; insufficient disk space error
	jne .error
ms_ret:
    pop es                  ; restore critical register
    ret
map_segments    ENDP

;*****************************
;* MAP_GROUPS                *
;*****************************

; show map of groups
; only called once
; destroys ax,bx,cx,dx,di,si,es

map_groups  PROC
    mov ax,[first_grpblk_ptr] ; get pointer to first group block
    mov di,ax               ; save pointer
    or  ax,ax               ; check if null
    jne mg_grp_exist        ; no, one or groups exist
    ret                     ; zero, no groups, return

; write group header
mg_grp_exist:
    mov bx,[map_handle]
    mov cl,[mgtext_len]       ; get length of text to write
    xor ch,ch               ; zap high byte
    mov dx,OFFSET map_group_text wrt DGROUP ; ds:dx -> text to write
    mov ah,40h              ; write to file
    int 21h
    call    restore_ems_map
    jnc .checkspace                ; no errors
.error:
    mov dx,OFFSET map_name wrt DGROUP
    jmp NEAR PTR dos_error  ; error writing to file

.checkspace:
	cmp ax, cx
	mov ax, 27h			; insufficient disk space error
	jne .error
mg_2:
    mov es,di               ; es -> block
    mov cx,[es:0]           ; get count
    mov ax,di
    inc ax
    mov es,ax               ; es -> first entry in block

mg_3:
    or  cx,cx               ; check entry count
    jne mg_3a
    jmp NEAR PTR mg_next_block  ; count is zero, get next group block, if any

mg_3a:
    dec cx                  ; decrement count
    push    cx              ; save critical register

; print group offset and  group name
    xor dx,dx               ; zero count of chars in string to print
    mov bx,OFFSET map_text_buffer wrt DGROUP    ; bx -> place to put ascii chars

    mov word [bx], CR + (LF << 8)    ; cr/lf prepend
    inc bx
    inc bx
    inc dx
    inc dx                ; bump count of chars in string

    mov cx,[es:0]           ; get group offset low word
    mov ax,[es:2]           ; get group offset high word

; convert offset value in ax:cx to paragraphs
    shr ax,1
    rcr cx,1                ; /2
    shr ax,1
    rcr cx,1                ; /4
    shr ax,1
    rcr cx,1                ; /8
    shr ax,1                ; ax should be zero by the final shift
    rcr cx,1                ; /16

    xchg    ch,cl           ; get high byte in cl, save low byte in ch
    xor ah,ah               ; 8 bit value
    call    hexxit

    mov cl,ch               ; get low byte
    call    hexxit

    mov word [bx], ":0"    ; put in ':0   ' after group origin segment value
    inc bx
    inc bx
    mov ax, "  "
    mov word [bx], ax
    inc bx
    inc bx
    mov BYTE PTR [bx],al
    inc bx
    add dx,5                ; bump count of chars in string

    mov si,[es:4]
    add si,8                ; bump past 2 doubleword pointers
    mov cx,es               ; save -> group entry
    mov es,[es:6]           ; es:si -> segment name

mg_grploop:
    mov al,[es:si]          ; get name char
    or  al,al               ; check for zero terminator
    je  mg_4                ; done
    mov [bx],al             ; transfer char
    inc si
    inc bx
    inc dx                  ; bump count of chars in map string
    jmp SHORT mg_grploop    ; loop back for next char

mg_4:
    mov es,cx               ; restore es -> group entry
    mov cx,dx               ; get count of chars to write
    mov bx,[map_handle]
    mov dx,OFFSET map_text_buffer wrt DGROUP    ; ds:dx -> text to write
    mov ah,40h              ; write to file
    int 21h
    call    restore_ems_map
    jnc .mg_5                ; no errors
.error:
    mov dx,OFFSET map_name wrt DGROUP
    jmp NEAR PTR dos_error  ; error writing to file

.mg_5:
	cmp ax, cx
	mov ax, 27h			; insufficient disk space error
	jne .error
    pop cx                  ; restore critical register
    mov ax,es
    inc ax
    mov es,ax               ; es -> next group entry
    jmp NEAR PTR mg_3       ; loop for next entry

mg_next_block:
    mov es,di               ; es -> group block
    mov di,[es:2]           ; get pointer to next block
    or  di,di               ; make sure that it isn't null
    je  mg_ret              ; null, return
    jmp NEAR PTR mg_2       ; non-null, another block exists, loop and print entries

mg_ret:
    ret
map_groups  ENDP

;*****************************
;* MAP_DETAIL_SEG            *
;*****************************

; detail map of segments
; upon entry es -> segment partition entry
; destroys ax,di

map_detail_seg  PROC
    push    bx              ; save critical register
    push    cx
    push    dx
    push    si

; convert all of map_text_buffer bytes to spaces
    mov bx,es               ; save es critical register
	push	ds
	pop	es					; es -> warplink data
    mov cx,350              ; 350 words in field
    mov ax,2020h            ; spaces in high and low bytes
    mov di,OFFSET map_text_buffer wrt DGROUP
    rep stosw                   ; store spaces

    mov es,bx               ; restore es critical register
    mov al,[detail_flag]
    or  al,al               ; see if detail segment header text was printed
    jne mds_2               ; yes

; print detail segment header text
    inc ax
    mov [detail_flag],al      ; see flag to show printed this pass
    mov bx,[map_handle]
    mov cl,[msegtext_len]     ; get length of text to write in cx
    xor ch,ch
    mov dx,OFFSET map_detseg_text wrt DGROUP
    mov ah,40h              ; write to file
    int 21h
    call    restore_ems_map
    jnc .checkspace               ; no errors
.error:
    mov dx,OFFSET map_name wrt DGROUP
    jmp NEAR PTR dos_error  ; error writing to file

.checkspace:
	cmp ax, cx
	mov ax, 27h			; insufficient disk space error
	jne .error
mds_2:
    xor dx,dx               ; zero count of chars in string to print
    mov di,OFFSET map_text_buffer wrt DGROUP    ; di -> place to put ascii chars

; write segment name
    mov bx,es               ; save -> segment partition entry
    mov word [map_seg_ovl],0       ; zero overlaid segment flag

    mov ax,[es:4]           ; get master segdef entry/overlay identifier
    test    BYTE PTR [es:15],80h    ; see if overlaid segment
    je  mds_2a              ; no, ax holds master segdef entry

; overlaid segment, ax holds overlay identifier
    mov [map_seg_ovl],ax      ; set overlaid segment flag with identifier
    mov si,ax
    dec si                  ; make relative zero
    shl si,1                ; convert to word offset
    mov es,[master_segblk]    ; es:si -> proper master segdef entry
    mov ax,[es:si]          ; get master segdef entry in ax

mds_2a:
    push    ax              ; save -> segdef entry on stack
    mov si,ds
    mov ds,ax               ; ds -> master segdef entry
    mov es,si               ; es:di -> write buffer
    lds si,[8]           ; ds:si -> segment name
    add si,8                ; adjust past doubleword pointers

mds_loop1:
    mov al,[si]             ; see if null terminator reached
    or  al,al
    je  mds_3               ; yes
    movsb                   ; transfer byte of name
    inc dx                  ; no, bump count of chars in string
    jmp SHORT mds_loop1     ; loop for next char transfer

mds_3:
    mov ax,DGROUP
    mov ds,ax               ; restore ds -> warplink data
    xchg    di,bx           ; bx -> write buffer, di -> segment partition entry
    inc bx                  ; add space after name
    inc dx
    cmp word [map_seg_ovl],0       ; see if overlaid segment
    je  mds_3b              ; no

; add overlay number
    mov ax,17
    cmp dx,ax               ; see if at least 17 chars of name+space written
    jae mds_3a              ; yes
    sub ax,dx
    add bx,ax               ; position to overlay number field
    add dx,ax               ; adjust char count

mds_3a:
    mov cx,[map_seg_ovl]      ; get overlay identifier
    xchg    ch,cl           ; high byte in cl, low byte in ch
    xor ah,ah               ; flag 8 bit values
    call    hexxit          ; print high byte
    mov cl,ch               ; get low byte
    call    hexxit          ; print low byte
    inc bx                  ; add space after overlay identifier
    inc dx

mds_3b:
    mov ax,22
    cmp dx,ax               ; see if at least 22 chars of name+overlay #+space written
    jae mds_4               ; yes
    sub ax,dx
    add bx,ax               ; position to address field
    add dx,ax               ; adjust char count

; write address
mds_4:
    cmp word [map_seg_ovl],0       ; see if overlaid segment
    je  mds_4a
    mov es,di               ; es -> segment partition entry
    xor cx,cx               ; zero offset bytes
    jmp SHORT mds_4b

mds_4a:
    pop es                  ; es -> segdef entry
    push    es              ; replace -> segdef entry on stack
    call    seg_offset_to_para  ; convert segment offset to paragraphs in cx

mds_4b:
    xchg    ch,cl           ; get high byte in cl, save low byte in ch
    xor ah,ah               ; 8 bit value
    call    hexxit
    mov cl,ch               ; get low byte
    call    hexxit
    mov BYTE PTR [bx],':'   ; put in ':'
    inc bx
    inc dx

    cmp word [map_seg_ovl],0       ; see if overlaid segment
    je  mds_4c
    xor cx,cx               ; zero offset bytes
    jmp SHORT mds_4d

mds_4c:
    mov al,[es:2]           ; get low byte of offset
    and ax,0fh              ; only save lowest 4 bits of offset
    mov es,di               ; es -> segment partition entry
    add ax,[es:0]           ; add in segment partition offset
    xchg cx, ax			; clobbers ax

mds_4d:
    xchg    ch,cl           ; get high byte in cl, save low byte in ch
    xor ah,ah               ; 8 bit value
    call    hexxit
    mov cl,ch               ; get low byte
    call    hexxit
    inc bx                  ; add space after address
    inc dx

; write length
    mov al,' '              ; assume length is less than 64K
    test BYTE PTR [es:11],2 ; test Big bit of acbp byte of segment partition
    je  mds_5

    mov al,'1'              ; Big bit set

mds_5:
    mov [bx],al             ; put in space or '1'
    inc bx
    inc dx
    mov cx,[es:12]          ; get segment partition length low word
    xchg    ch,cl           ; get high byte in cl, save low byte in ch
    xor ah,ah               ; 8 bit value
    call    hexxit
    mov cl,ch               ; get low byte
    call    hexxit
    mov BYTE PTR [bx],'h'   ; put in 'h '
    inc bx
    inc bx
    inc dx
    inc dx

; write alignment
	xor ax, ax
    mov al,[es:11]          ; get segment partition entry's acbp byte
	mov cl, 5
	shr ax, cl		; get alignment field
	xchg si, ax
	add si, si		; = word index
	mov si, word [align_table + si]
				; si -> alignment message
mds_6:
    mov cx,6                ; transfer 6 chars
    add dx, cx			; adjust total char count

mds_loop2:
    lodsb                   ; get align message char
    mov [bx],al             ; transfer char
    inc bx
    loop    mds_loop2       ; loop until all message chars transferred

; write combine
    mov al,[es:11]          ; get segment partition entry's acbp byte
    and al,1ch              ; get combine field
    je  mds_priv            ; private combine
    cmp al,8                ; check if public combine
    je  mds_public
    cmp al,10h              ; check if public combine
    je  mds_public
    cmp al,1ch              ; check if public combine
    je  mds_public
    cmp al,14h              ; check if stack combine
    je  mds_stack

; assume common combine (combine field 18h)
    mov si,OFFSET comb_com wrt DGROUP
    jmp SHORT mds_7

mds_stack:
    mov si,OFFSET comb_stack wrt DGROUP
    jmp SHORT mds_7

mds_public:
    mov si,OFFSET comb_pub wrt DGROUP
    jmp SHORT mds_7

mds_priv:
    mov si,OFFSET comb_priv wrt DGROUP

mds_7:
    mov cx,8                ; transfer 8 chars
    add dx, cx			; adjust total char count

mds_loop3:
    lodsb                   ; get combine message char
    mov [bx],al             ; transfer char
    inc bx
    loop    mds_loop3       ; loop until all message chars transferred

    mov si,ds

mds_7a:
    pop es                  ; es -> master segdef entry

; write class
mds_7b:
    xchg    di,bx           ; di -> write buffer, bx -> segment partition entry
    mov ax,es               ; get master segdef entry
    push    ax              ; save -> segdef entry on stack
    mov ds,ax               ; ds -> master segdef entry
    mov es,si               ; es:di -> write buffer
    lds si,[12]          ; ds:si -> class name
    add si,8                ; adjust past doubleword pointers

mds_loop4:
    mov al,[si]             ; see if null terminator reached
    or  al,al
    je  mds_8               ; yes
    movsb                   ; transfer byte of name
    inc dx                  ; no, bump count of chars in string
    jmp SHORT mds_loop4     ; loop for next char transfer

mds_8:
    inc di                  ; add space after name, di still -> write buffer
    inc dx
    mov ax,62
    cmp dx,ax               ; see if at least 62 chars written
    jae mds_9               ; yes
    sub ax,dx
    add di,ax               ; position to class field
    add dx,ax               ; adjust char count

mds_9:
    mov ax,DGROUP
    mov ds,ax
    pop es                  ; es -> segdef entry

    cmp word [map_seg_ovl],0       ; see if overlaid segment
    jne mds_10              ; yes, no group association

; write group
    mov si,DGROUP
    mov ax,es               ; get master segdef entry
    mov ds,ax               ; ds -> master segdef entry
    mov es,si               ; es:di -> write buffer
    mov ax,[16]          ; get pointer to group entry
    or  ax,ax               ; check if zero (no group)
    je  mds_10              ; zero, no group name
    mov ds,ax               ; ds -> group entry
    lds si,[4]           ; ds:si -> group name
    add si,8                ; adjust past doubleword pointers

mds_loop5:
    mov al,[si]             ; see if null terminator reached
    or  al,al
    je  mds_10              ; yes
    movsb                   ; transfer byte of name
    inc dx                  ; no, bump count of chars in string
    jmp SHORT mds_loop5     ; loop for next char transfer

mds_10:
    inc di                  ; add space after name
    inc dx
    mov ax,72
    cmp dx,ax               ; see if at least 72 chars written
    jae mds_11              ; yes
    sub ax,dx
    add di,ax               ; position to class field
    add dx,ax               ; adjust char count

mds_11:
    mov ax,DGROUP
    mov ds,ax               ; restore ds -> warplink data
    mov es,ax               ; es -> warplink data

; write module
    mov si,OFFSET tmod_name wrt DGROUP

mds_loop6:
    mov al,[si]             ; see if null terminator reached
    or  al,al
    je  mds_12              ; yes
    movsb                   ; transfer byte of name
    inc dx                  ; no, bump count of chars in string
    jmp SHORT mds_loop6     ; loop for next char transfer

mds_12:
    inc di                  ; add space after name
    inc dx
    mov ax,85
    cmp dx,ax               ; see if at least 85 chars written
    jae mds_13              ; yes
    sub ax,dx
    add di,ax               ; position to class field
    add dx,ax               ; adjust char count

; write file
mds_13:
    mov si,OFFSET filename wrt DGROUP

mds_loop7:
    mov al,[si]             ; see if null terminator reached
    or  al,al
    je  mds_14              ; yes
    movsb                   ; transfer byte of name
    inc dx                  ; no, bump count of chars in string
    jmp SHORT mds_loop7     ; loop for next char transfer

mds_14:
    mov word [di], CR + (LF << 8)    ; cr/lf terminate
    inc dx
    inc dx                ; bump count of chars in string

    mov es,bx               ; restore es -> segment partition entry

    mov cx,dx               ; get count of chars to write
    mov bx,[map_handle]
    mov dx,OFFSET map_text_buffer wrt DGROUP   ; ds:dx -> text to write
    mov ah,40h              ; write to file
    int 21h
    call    restore_ems_map
    jnc .checkspace             ; no errors
.error:
    mov dx,OFFSET map_name wrt DGROUP
    jmp NEAR PTR dos_error  ; error writing to file

.checkspace:
	cmp ax, cx
	mov ax, 27h			; insufficient disk space error
	jne .error

mds_ret:
    pop si
    pop dx
    pop cx
    pop bx
    ret
map_detail_seg  ENDP

;*****************************
;* SEG_OFFSET_TO_PARA        *
;*****************************

; convert segment offset to paragraphs
; destroys ax
; returns value in cx

 global seg_offset_to_para
seg_offset_to_para  PROC
    mov cx,[es:2]           ; get low word of offset
    mov ax,[es:4]           ; get high word of offset
    shr ax,1                ; convert offset in ax:cx to paragraphs
    rcr cx,1                ; /2
    shr ax,1
    rcr cx,1                ; /4
    shr ax,1
    rcr cx,1                ; /8
    shr ax,1
    rcr cx,1                ; /16
    ret
seg_offset_to_para  ENDP

;*****************************
;* HEXXIT                    *
;*****************************

; convert 4 or 8 bit hex number to its ASCII representation
; upon entry cl contains number
; ah == 1 if 4 bit, == 0 if 8 bit
; bx -> place to put digits
; destroys al,cl
; updates bx,dx (number of chars in string)

hexxit  PROC
    or  ah,ah               ; check if 4 bit or 8 bit value
    jne hx_4bit             ; 4 bit
    mov al,cl
    and al,0f0h             ; get high nybble
    shr al,1                ; make value relative zero
    shr al,1
    shr al,1
    shr al,1                ; al has relative zero value
    cmp al,0ah              ; see if need to use hex numbers a-f
    jb  hx_2                ; no
    add al,7                ; adjust for ASCII jump to alpha chars

hx_2:
    add al,30h              ; make number an ASCII representation
    mov [bx],al             ; save it to number string
    inc bx                  ; point to next char slot
    inc dx                  ; bump count of chars in number string

hx_4bit:
    and cl,0fh              ; get low nybble
    cmp cl,0ah              ; see if hex number a-f
    jb  hx_5                ; no
    add cl,7                ; adjust for ASCII jump to alpha chars
hx_5:
    add cl,30h              ; make number an ASCII representation
    mov [bx],cl             ; save it to number string
    inc bx                  ; point to next char slot
    inc dx                  ; bump count of chars in number string
    ret
hexxit  ENDP

;*****************************
;* FINISH_MAP                *
;*****************************

; write remaining map info
; destroys ax,bx,cx,dx

finish_map  PROC
    cmp byte [is_mapfile],0        ; see if map file
    je  fm_ret              ; no

    cmp byte [is_mapexpand],0      ; check if expanded map file
    je  fm_2                ; no
    call    map_publics     ; write the public addresses and names to map file

fm_2:
    call    map_entry       ; write program entry point
	call map_exe_size
    mov bx,[map_handle]       ; get handle of map file
    mov ah,3eh              ; close file
    int 21h
    call    restore_ems_map

fm_ret:
    ret
finish_map  ENDP

;*****************************
;* MAP_PUBLICS               *
;*****************************

; map public symbol addresses and names
; destroys ax,bx,cx,dx,di,si,es

map_publics PROC
    mov cl,[maddrtext_len]    ; get length of text to write
    xor ch,ch               ; zap high byte
    mov bx,[map_handle]
    mov dx,OFFSET maddr_text wrt DGROUP ; ds:dx -> text to write
    mov ah,40h              ; write to file
    int 21h
    call    restore_ems_map
    jnc .mp_2                ; no errors
.error:
    mov dx,OFFSET map_name wrt DGROUP
    jmp NEAR PTR dos_error  ; error writing to file

.mp_2:
	cmp ax, cx
	mov ax, 27h			; insufficient disk space error
	jne .error
    mov ax,[first_pdeclblk_ptr]   ; get pointer to first public declarations block
    or  ax,ax               ; check if non-null
    jne mp_blkloop          ; non-null, public declarations exist
    jmp NEAR PTR mp_communal_chk    ; null, try communal declarations block

mp_blkloop:
    push    ax              ; save -> block on stack
    mov es,ax               ; es -> declarations block
    mov si,[es:0]           ; get count of entries in block

mp_entloop:
    inc ax                  ; ax -> next entry in block
    mov es,ax               ; es -> declaration entry
    push    es              ; save -> declaration entry

; 12/21/92
;***	test    BYTE PTR [es:15],20h    ; see if local communal (don't list locals)
    test    BYTE PTR [es:15],24h    ; see if local

    je  mp_not_local        ; no

mp_noprint:
    pop es                  ; restore stack
    jmp NEAR PTR mp_next_entry  ; try next entry in block

mp_not_local:
    test    BYTE PTR [es:14],3  ; see if weak extdef
    je  mp_noprint          ; yes, don't print it

    mov ah,[es:15]          ; get general flags
    test    ah,1            ; get in overlay flag
    jne mp_overlaid         ; set, in overlay, zero segment offset
    mov al,[es:14]          ; get definition flag
    and al,3                ; get definition bits
    cmp al,3                ; see if absolute
    jne mp_not_abs          ; no

; absolute symbol
    mov cx,[es:2]           ; get frame number in cx

mp_zero_offset:
    xor di,di               ; di==low nybble of segment offset (always zero)
    jmp SHORT mp_3          ; bypass segment frame code

mp_not_abs:
    cmp al,2                ; see if resolved
    je  mp_2a               ; yes
    xor cx,cx               ; zero frame
    jmp SHORT mp_zero_offset    ; go to zero offset code

mp_overlaid:
    xor cx,cx               ; zero frame
    mov di,cx               ; zero public offset
    jmp SHORT mp_3          ; bypass segment frame code

mp_2a:
    mov es,[es:0]           ; es -> segment partition entry
    mov cx,[es:0]           ; get segment partition offset
    mov es,[es:4]           ; es -> segdef entry

    and ah,80h              ; see if public is in group
    je  mp_2b               ; no

    mov di,cx               ; di holds segment partition entry offset

    mov cx,[es:2]           ; get low word of segment offset
    mov ax,[es:4]           ; get high word of segment offset
    pop es                  ; es -> public declaration entry
    push    es              ; restore -> public declaration entry to stack
    mov es,[es:2]           ; es -> group entry
    sub cx,[es:0]           ; compute low word difference in group/segment offset
    sbb ax,[es:2]           ; compute high word difference in group/segment offset
    add di,cx               ; add difference into di, public offset

    mov ax,[es:0]           ; get low word of group offset
    mov cx,ax               ; save in cx
    and ax,0fh              ; get paragraph remainder
    add di,ax               ; add to public offset

    mov ax,[es:2]           ; get high word of group offset
    shr ax,1                ; convert offset in ax:cx to paragraphs
    rcr cx,1                ; /2
    shr ax,1
    rcr cx,1                ; /4
    shr ax,1
    rcr cx,1                ; /8
    shr ax,1
    rcr cx,1                ; /16
    jmp SHORT mp_3          ; bypass segment specific code

; non-group public
mp_2b:
    mov di,[es:2]           ; get low word of segment offset
    and di,0fh              ; di==low nybble of segment offset
    add di,cx               ; add in segment partition entry offset

    call    seg_offset_to_para  ; get segment offset in paragraphs (frame) in cx

mp_3:
    mov bx,OFFSET map_text_buffer wrt DGROUP
    mov word [bx], CR + (LF << 8)    ; prepend cr/lf pair
    inc bx
    inc bx
    xchg    ch,cl           ; get high byte in cl, save low byte in ch
    xor ah,ah               ; 8 bit value
    call    hexxit
    mov cl,ch               ; get low byte
    call    hexxit
    mov BYTE PTR [bx],':'   ; put in ':'
    inc bx

    pop es                  ; es -> declaration entry
    add di,[es:8]           ; add in public offset
    mov cx,di
    xchg    ch,cl           ; get high byte in cl, save low byte in ch
    xor ah,ah               ; 8 bit value
    call    hexxit
    mov cl,ch               ; get low byte
    call    hexxit

    mov cx,11                ; nine bytes to write, CR,LF,nnnn:nnnn
    mov bx,[map_handle]
    mov dx,OFFSET map_text_buffer wrt DGROUP    ; ds:dx -> text to write
    mov ah,40h              ; write to file
    int 21h
    call    restore_ems_map
    jnc mp_4                ; no errors

mp_toerr:
    mov dx,OFFSET map_name wrt DGROUP
    jmp NEAR PTR dos_error  ; error writing to file

mp_4:
	cmp ax, cx
	mov ax, 27h			; insufficient disk space error
	jne mp_toerr
    mov al,[es:15]          ; get general flag
    and al,1                ; see if overlaid
    jne mp_ovl              ; yes
    mov al,[es:14]          ; get definitions flag
    and al,3                ; get definition bits
    cmp al,3                ; see if absolute
    je  mp_abs              ; no
    cmp al,2                ; see if resolved public/communal
    jne mp_unres            ; no
    test    BYTE PTR [es:15],40h    ; see if communal
    je  mp_res              ; no, resolved public

;communal variable
    mov dx,OFFSET mpub_comm wrt DGROUP
    jmp SHORT mp_print_pubtype

; overlaid variable
mp_ovl:
    push    es              ; save critical register
    mov es,[es:0]           ; es -> segment partition entry
    mov cx,[es:4]           ; get overlay identifier
    pop es                  ; restore critical register
    mov bx,OFFSET mpub_ovl+5 wrt DGROUP ; bx -> place to put overlay number
    xchg    ch,cl           ; get high byte in cl, save low byte in ch
    xor ah,ah               ; 8 bit value
    call    hexxit
    mov cl,ch               ; get low byte
    call    hexxit

    mov dx,OFFSET mpub_ovl wrt DGROUP
    jmp SHORT mp_print_pubtype

; absolute variable
mp_abs:
    mov dx,OFFSET mpub_abs wrt DGROUP
    jmp SHORT mp_print_pubtype

; resolved public
mp_res:
    mov dx,OFFSET mpub_res wrt DGROUP
    jmp SHORT mp_print_pubtype

; unresolved public
mp_unres:
    mov dx,OFFSET mpub_unres wrt DGROUP

mp_print_pubtype:
    mov cx,11
    mov bx,[map_handle]
    mov ah,40h              ; write to file
    int 21h
    call    restore_ems_map
    jc  mp_toerr            ; error occurred
	cmp ax, cx
	mov ax, 27h			; insufficient disk space error
	jne mp_toerr

    mov bx,[map_handle]
    push    ds              ; save critical register
    lds di,[es:4]           ; get -> pubdef name in es:bx
    mov dx,di               ; save -> name
    xor cx,cx               ; cx will hold of chars in name

mp_loop:
    cmp BYTE PTR [di],0     ; see if zero terminator in symbol name found
    je  mp_5                ; yes
    inc cx                  ; bump count of chars in string
    inc di                  ; move to next char slot to test
    jmp SHORT mp_loop       ; loop back to test next char

mp_5:
    mov ah,40h              ; write to file
    int 21h
    call    restore_ems_map
    pop ds                  ; restore ds -> machlink data
    jc  mp_toerr2           ; error occurred
	cmp ax, cx
	mov ax, 27h			; insufficient disk space error
	jne mp_toerr2

mp_next_entry:
    mov ax,es               ; ax -> current entry
    dec si                  ; drop count of entries in block
    je  mp_next_block       ; no more entries try next block
    jmp NEAR PTR mp_entloop ; print next entry

mp_toerr2:
    mov dx,OFFSET map_name wrt DGROUP
    jmp NEAR PTR dos_error  ; error writing to file

mp_next_block:
    pop es                  ; restore es -> block
    mov ax,[es:2]           ; get pointer to next block, if any
    or  ax,ax               ; check if non-null
    je  mp_communal_chk     ; null, check communals
    jmp NEAR PTR mp_blkloop ; loop back for more publics

mp_communal_chk:
    inc byte [comm_loop_flag]      ; bump counter
    cmp byte [comm_loop_flag],1    ; see if communal blocks printed yet
    ja  mp_ret              ; yes
    mov ax,[first_cdeclblk_ptr]   ; get pointer to first communal declarations block
    or  ax,ax               ; check if non-null
    je  mp_ret              ; null, done
    jmp NEAR PTR mp_blkloop ; non-null, print declarations in communal block

mp_ret:
    ret
map_publics ENDP

;*****************************
;* MAP_ENTRY                 *
;*****************************

; map entry point of program
; destroys ax,bx,cx,dx

map_entry   PROC
    mov bx,OFFSET map_entry_value wrt DGROUP    ; point to place to stuff entry value
    mov cl,BYTE PTR [entry_segval+1]  ; get MSB of entry segment
    xor ah,ah               ; 8 bit value
    call    hexxit
    mov cl,BYTE PTR [entry_segval]    ; get LSB of entry segment
    call    hexxit

    inc bx                  ; bump past colon
    mov cl,BYTE PTR [entry_offval+1]  ; get MSB of entry offset
    call    hexxit
    mov cl,BYTE PTR [entry_offval]    ; get LSB of entry offset
    call    hexxit

    mov cl,[metext_len]       ; get length of text to write
    xor ch,ch               ; zap high byte
    mov bx,[map_handle]
    mov dx,OFFSET map_entry_text wrt DGROUP    ; ds:dx -> text to write
    mov ah,40h              ; write to file
    int 21h
    call    restore_ems_map
    jnc .me_ret              ; no errors
.error:
    mov dx,OFFSET map_name wrt DGROUP
    jmp NEAR PTR dos_error  ; error writing to file

.me_ret:
	cmp ax, cx
	mov ax, 27h			; insufficient disk space error
	jne .error
    ret
map_entry   ENDP



; map exe header/image progbits/image nobits sizes of program
; destroys ax,bx,cx,dx

map_exe_size:
	cmp byte [is_comfile], 0	; see if is a com file
	jne .ret

	lframe near
	lenter

 extern exe_header.eh_hsize
	mov cx, 4
	mov ax, [exe_header.eh_hsize]
	xor dx, dx
@@:
	add ax, ax
	adc dx, dx
	loop @B

	lvar dword, headersize
	 push dx
	 push ax

	mov bx, map_exe_header_size_value
	mov ah, 1
	mov cl, [bp + ?headersize + 2]
	call hexxit
	xor ah, ah
	mov cl, [bp + ?headersize + 1]
	call hexxit
	mov cl, [bp + ?headersize + 0]
	call hexxit

	mov ax, [highest_exe_write]
	mov dx, [highest_exe_write + 2]
	add ax, 1
	adc dx, 0

	lvar dword, initsize
	 push dx
	 push ax

	mov bx, map_exe_init_image_size_value
	mov ah, 1
	mov cl, [bp + ?initsize + 2]
	call hexxit
	xor ah, ah
	mov cl, [bp + ?initsize + 1]
	call hexxit
	mov cl, [bp + ?initsize + 0]
	call hexxit

	lleave

	mov bx, map_exe_total_image_size_value
	mov ah, 1
	mov cl, [image_size + 2]
	call hexxit
	xor ah, ah
	mov cl, [image_size + 1]
	call hexxit
	mov cl, [image_size + 0]
	call hexxit

    mov cl,[mestext_len]       ; get length of text to write
    xor ch,ch               ; zap high byte
    mov bx,[map_handle]
	mov dx, map_exe_size_text	; ds:dx -> text to write
    mov ah,40h              ; write to file
    int 21h
    call    restore_ems_map
    jnc .checkspace              ; no errors
.error:
    mov dx,OFFSET map_name wrt DGROUP
    jmp dos_error  ; error writing to file
.checkspace:
	cmp ax, cx
	mov ax, 27h			; insufficient disk space error
	jne .error
.ret:
    retn

END
