Programming in 80x86 assembly language

Part 1: the coredump

HTML Flowcharted sourcecode

Below is the source of a small program to produce a (hello Linux guys!) Core Dump. This can come in handy if you need to investigate what's left of a program after it's execution has been halted. Or if you need to know the state of certain memory variables or disk buffers. For these purposes Mem2File can send any region of memory to disk.
I soaked the files with HTML codes so you can click your way through the execution of a program. Just use your browser as if this were a website.
In good A86 style, this program starts just after the assembler directive CODE segment. This is an unconditional jump to the actual main routine of the program. Between the JMP  MAIN and the actual MAIN routine are (to make life easier for A86; it's a single pass assembler) the subroutines that are called in that order (approximately).

Give it a go and see if yoy can work your way to the end of the program. If you have questions or remarks about this source, or about programming in assembly language in general, don't hesitate to send me an E-mail.
You can get a copy of the A86 assembler  via the website of the maker.

The coredump program under DOS: Mem2File

     name     mem2file
     title    Send an area of memory to a diskfile.
     page     80, 120

     ; version 1.0  : Had to be compiled for each area/filename      OK: 01-01-1991
     ; version 1.1  : Same as above, for A86 format                  OK: 01-01-1999
     ; version 1.2  : Make it commandline driven                     OK: 01-02-1999
     ; version 1.3  : Make it reliable                               OK: 02-02-1999
     ; version 1.4  : Provide linear addresses as well               OK: something FOR YOU?
     ; ----------------------

     stdout   =  1
     tab      =  9
     lf       = 10
     cr       = 13
     
     store MACRO
         mov  al, #1
	 stosb
	 #EM
     
     clr MACRO
         mov  #1, 0
	 #EM

     Dum1   STRUC
     OffVal   dw    ?
     SegVal   dw    ?
            ENDS
     ; ----------------------
   

Here start the variables which need no initialisation.

      DATA segment
      
      ByteF = $
      dummy   db    ?        ; just to fool D86....

              even
      Start   dw    ?, ?     ; segment:address to start
      Stop    dw    ?, ?     ; segment:address to stop
      Block   dw    ?        ; number of 16K chucks to save
      Rest    dw    ?        ; remaining part to save
      ArgNum  dw    ?        ; nr of bytes in this argument
      OldClp  dw    ?        ; current pointer into command line
      Handle  dw    ?
      Length  dw    ?, ?

      FileName:
      Argument db   80 dup (?)       ; storage for next argument from command-line
      Output   db   16K dup (?)      ; buffered output

      Bytes = $ - ByteF
      ; ----------------------
   

Place for the variables that do need initialisation and messages

CODE segment

         jmp   main

HexTable db    '0123456789ABCDEF', 0

         db    'VeRsIoN=Mem2File 1.3', 0
	 db    'CoPyRiGhT=CopyLeft Jan Verhoeven, fruttenboel@verhoeven272.nl', 0

Mess001  db    'Mem2File collects a part of conventional memory and sends it to a file.', cr, lf, lf
         db    'The syntax is:', cr, lf, lf
	 db    tab, 'Mem2File  segm1:offs1 [-] segm2:offs2 <path>file.ext.', cr, lf, lf
	 db    'Mem2File is GNU GPL style FREE software. ', cr, lf
	 db    'Please read the GNU GPL if you are in doubt.', cr, lf, lf

Mess002  db    'Mem2File was made by Jan Verhoeven, NL-5012 GH 272, The Netherlands', cr, lf
         db    'E-mail address : fruttenboel@tomaatnet.nl', cr, lf, lf
Len001 = $ - Mess001
Len002 = $ - Mess002

Mess004  db    7, 'Error! All numbers are expected to be hexadecimal.', cr, lf
Len004 = $ - Mess004
;------------------------

InitMem: mov   di, ByteF                ; Init memory with zero's.
         mov   cx, Bytes
	 mov   al, 0
	 rep   stosb
	 ret
;------------------------

L0:      mov   b [di], 0        ; terminate argument string
         mov   [OldClp], si     ; done, => clean up.
	 clc                    ; indicate "No Error"

L3:      pop   di, si, ax       ; restore registers, ...
         ret                    ; ... and leave.

GetArg:  push  ax, si, di       ; get next argument from command-line in ASCIIZ format
         mov   si, [OldClp]     ; now, where did we leave last time?
	 cmp   si, 0            ; Have we ever used this routine?

         IF  E mov  si, 081             ; if not, prime SI, ...
	 mov   di, offset Argument      ; ... DI and ...
	 mov   [ArgNum], 0              ; ... nr of chars in argument.
L1:      lodsb                  ; get byte
         cmp   al, ' '          ; skip over spaces, ...
         je    L1
         cmp   al, tab          ; ... and tabs.
         je    L1
         cmp   al, 1            ; ONLY if AL is 0, we get a carry
         jc    L3               ; if CARRY, we're done
L2:      stosb                  ; else store char in Arguments array
         inc   [ArgNum]         ; adjust counter
         lodsb                  ; and get next char
         cmp   al, ' '          ; is it a delimiting space?
         je    L0
	 cmp   al, tab          ; or a tab?
         je    L0
	 cmp   al, ':'          ; or a colon?
         je    L0
         cmp   al, 0            ; or an end-of-line?
         jne   L2               ; if not, loop back,
         mov   si, 0FFFF        ; else make SI ridiculously high, ...
         stc                    ; ... set carry flag, ...
         jmp   L0               ; and get out.
 ;------------------------

L1:      stc                    ; byte not in table!
         pop   dx
         ret                    ; exit
	 
L2:      sub   bx, dx           ; calculate position in table
         pop   dx
         clc
         ret

TableFind:
         push  dx               ; find AL in ASCIIZ table [BX] and report position
         mov   dx, bx           ; keep value of SI
	 
L0:      cmp   b [bx], 0        ; is it end of table?
         je    L1               ; if so, jump out
         cmp   al, [bx]         ; compare byte with table
         je    L2               ; if same, jump out
         
	 inc   bx               ; else increment pointer
         jmp   L0               ; and loop back
 ;------------------------
 
MakeUpper:
         cmp   al, 'a'
         jb    ret
         cmp   al, 'z'          ; if in range, ...
         ja    ret
         and   al, not bit 5    ; ... make uppercase
         ret
 ;------------------------

BadNumber:                      ; hey typo, you made a dumbo!
         mov   dx, offset Mess004
         mov   cx, Len004
         mov   bx, StdOut
         mov   ah, 040
         int   021
         mov   ax, 04C02        ; and exit with errorcode 2
         int   021
 ;------------------------

SyntErr: mov  dx, offset Mess001
         mov   cx, Len001
         mov   bx, StdOut
         mov   ah, 040          ; print out "help" screen and ...
         int   021

         mov   ax, 04C01        ; ... exit with errorcode 1
         int   021
 ;------------------------

L8:      mov   ax, dx           ; ConvertHex has result in DX, that's why.
         pop   dx, bx
         ret

Convert: push  bx, dx
         mov   si, offset Argument
         clr   dx

L1:      lodsb
         cmp   al, 0            ; end of string?
         je    L8
         call  MakeUpper               ; if not, make uppercase
         mov   bx, offset HexTable
         call  TableFind               ; and lookup in table
         jc    BadNumber
         shl   dx, 4            ; multiply DX by 16
         or    dl, bl           ; bx = index into table
         jmp   L1               ; repeat until done
 ;------------------------


Credits: mov  dx, offset Mess002
         mov   cx, Len002
         mov   bx, stdout
         mov   ah, 040
         int   021
         ret
 ;------------------------

main:    call  InitMem          ; prime volatile data
         mov   al, [080]
         cbw
         mov   si, 081          ; point to start of tail
         add   si, ax           ; point to end of tail
         mov   [si], ah         ; make commandtail ASCIIZ
         call  GetArg
         jc    SyntErr
         call  Convert
         mov   [Start.SegVal], ax
         call  GetArg
         jc    SyntErr
         call  Convert
         mov   [Start.OffVal], ax

L0:      call  GetArg
         jc    SyntErr
         cmp   b [Argument], '-'
         je    L0
         call  Convert
         mov   [Stop.SegVal], ax
         call  GetArg
         jc    SyntErr
         call  Convert
         mov   [Stop.OffVal], ax

         call  GetArg
         IF  C jmp  SyntErr
         mov   si, offset Argument
         add   si, [ArgNum]
         mov   b [si], 0

         mov   dx, offset FileName     ; same as Argument buffer....
         mov   cx, 0
         mov   ah, 03C
         int   021                     ; create the file
         IF  C jmp  SyntErr
         mov   [Handle], ax
         mov   ax, [Stop.SegVal]
         mov   dx, 0                   ; prime DX
         mov   cx, 4

L0:      shl   ax, 1
         rcl   dx, 1
         loop  L0                      ; shift upper 4 bits of address into DX
         add   ax, [Stop.OffVal]
         adc   dx, 0                   ; now, dx:ax = linear address to stop at
         mov   [Stop.SegVal], dx
         mov   [Stop.OffVal], ax       ; store linear address STOP

         mov   ax, [Start.SegVal]
         mov   dx, 0                   ; prime DX
         mov   cx, 4

L0:      shl   ax, 1
         rcl   dx, 1
         loop  L0                      ; shift upper 4 bits of address into DX
         add   ax, [Start.OffVal]
         adc   dx, 0                   ; now, dx:ax = linear address to start from
         mov   [Start.SegVal], dx
         mov   [Start.OffVal], ax      ; store linear address START

         cmp   dx, [Stop.SegVal]       ; start > stop?
         ja    >L1                     ; fix it!
         jb    >L2
         cmp   ax, [Stop.OffVal]       ; start > stop?
         jbe   >L2                     ; if not, OK

L1:      push  [Stop.SegVal]
         push  [Stop.OffVal]
         mov   [Stop.SegVal], dx
         mov   [Stop.OffVal], ax
         pop   [Start.OffVal]
         pop   [Start.SegVal]

L2: mov dx, [Stop.SegVal] mov ax, [Stop.OffVal] add ax, 1 adc dx, 0 ; limits are INCLUSIVE sub ax, [Start.OffVal] sbb dx, [Start.SegVal] ; dx:ax = bytes to move shl ax, 1 rcl dx, 1 shl ax, 1 rcl dx, 1 ; dx = nr of 16 Kb blocks to move mov [Blocks], dx ; store it shr ax, 2 ; ax = remainder to move mov [Rest], ax ; save it mov ax, [Start.SegVal] ; end of linear addressing, we're going to DOS! mov cl, 12 shl ax, cl mov [Start.SegVal], ax mov es, ds ; use es to refer to data mov bx, [Handle] lds dx, d [Start] es cmp [Blocks], 0 ; if less than 16 Kb, skip this one. je >S0 mov cx, 16K L0: mov ah, 040 int 021 mov ax, ds ; store ds into ax add dx, 04000 ; next buffer to load data from IF C add ax, 01000 ; if carry, inc ds mov ds, ax ; ds:dx now ready for next bufferfull of data es dec [Blocks] jnz L0 S0: es cmp [Rest], 0 je >L1 mov ah, 040 es mov cx, [Rest] int 021 L1: mov ds, es mov bx, [Handle] mov ah, 03E int 021 ; close file call Credits ; show my ego mov ax, 04C00 int 021 ; exit to DOS

About the source.

If you tinker out the HTML tags, you can use A86 to assemble this source to create "MEM2FILE.COM" so you can try it out. See how far you can come. If tinkering is too much effort, you can also download the original source here.

Page created in 1998,

Page equipped with FroogleBuster technology