;/*****************************************************************************
;*
;* SOURCE FILE NAME =
;*
;* DESCRIPTIVE NAME =
;*
;* COPYRIGHT    COPYRIGHT IBM CORPORATION, 1991, 1992
;*              Copyright Microsoft Corporation, 1990
;*              LICENSED MATERIAL - PROGRAM PROPERTY OF IBM
;*              REFER TO COPYRIGHT INSTRUCTION FORM#G120-2083
;*              RESTRICTED MATERIALS OF IBM
;*              IBM CONFIDENTIAL
;*
;* VERSION      V2.0
;*
;* DATE
;*
;* DESCRIPTION
;*
;* Virtual Machine Disk Generator (real-mode version)
;*
;* By Jeff Parsons  April, 1990
;*
;*
;* This program creates a file containing all the data on the
;* specified diskette.  The layout of the file is:
;*
;*   Item    Offset  Description
;*   ----    ------  -----------
;*   BYTE    0       Flags (bit 0 = write-protect)
;*   BYTE    1       # heads
;*   WORD    2       # cylinders
;*   WORD    4       # sectors per track
;*   WORD    6       # bytes per sector
;*
;*   DATA    8       Disk data
;*
;* If the virtual disk contains non-homogenous tracks (ie, varying
;* # sectors per track and/or bytes per sector), then the words at offset
;* 4 and 6 will be zero, and there will be a series of track tables
;* starting at offset 8, with data immediately following.  Note that it
;* is the product of heads and cylinders that defines how many track tables
;* there are.
;*
;*   Item    Offset  Description
;*   ----    ------  -----------
;*   BYTE    0       Flags (bit 0 = write-protect)
;*   BYTE    1       # heads
;*   WORD    2       # cylinders
;*   WORD    4       # sectors per track (0)
;*   WORD    6       # bytes per sector (0)
;*
;*   WORD    8       # sectors this track
;*   WORD    10      # bytes per sector this track
;*   DWORD   12      offset within file for this track
;*   ...
;*   DATA    ???     Disk data
;*
;* Don't be deceived by the apparent support for non-homogenous disks
;* however;  this program can really only handle certain very specific
;* types of variations (eg, 1024-byte sectors on 360Kb disks).  Additional
;* support must be added as needed.
;*
;* When a size (in kbytes) is specified instead of a diskette drive,
;* a virtual machine disk file is created with the appropriate header and
;* amount of zero-initialized disk space.
;*
;* FUNCTIONS
;*
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*****************************************************************************/


hilo    struc
        lo  db  ?
        hi  db  ?
hilo    ends

seloff  struc
        off dw  ?
        sel dw  ?
seloff  ends

dskbase struc                           ;* DISK BASE
        dkb_sb1             db  ?
        dkb_sb2             db  ?
        dkb_wtime           db  ?
        dkb_ssec            db  ?
        dkb_nsec            db  ?
        dkb_glrw            db  ?
        dkb_dlen            db  ?
        dkb_glfmt           db  ?
        dkb_bfmt            db  ?
        dkb_htime           db  ?
        dkb_mtime           db  ?
dskbase ends

bpb     struc                           ;* BIOS PARAMETER BLOCK
        bpb_nbSector        dw  ?       ;* sector size (in bytes)
        bpb_nsecCluster     db  ?       ;* cluster size (in sectors)
        bpb_nsecReserved    dw  ?       ;* # reserved sectors
        bpb_nFATs           db  ?       ;* # FATs
        bpb_ndirRoot        dw  ?       ;* root directory size (in dir. entries)
        bpb_nsecTotal       dw  ?       ;* media size (in sectors)
        bpb_bMedia          db  ?       ;* media descriptor byte (eg, 0xF8)
        bpb_nsecFAT         dw  ?       ;* FAT size (in sectors)
        bpb_nsecTrack       dw  ?       ;* track size (in sectors)
        bpb_nHeads          dw  ?       ;* # heads (ie, tracks per cylinder)
        bpb_nsecHidden      dd  ?       ;* # hidden sectors (prior to partition)
        bpb_nsecHugeTotal   dd  ?       ;* media size (if nsecTotal is zero)
bpb     ends


_TEXT   segment public 'CODE'
        assume  cs:_TEXT,ds:_TEXT,es:_TEXT,ss:nothing

        org     100h                    ;* .COM file

main    proc    near
        mov     si,80h                  ;* SI -> command line
        lodsb
        cbw
        mov     cx,ax                   ;* # chars in line
        jcxz    no_drive
get_drive:
        lodsb
        cmp     al,' '
        loope   get_drive
        jne     chk_drive
no_drive:
no_file:
        mov     dx,offset msguse        ;* no arguments specified
        jmp     exit_msg

chk_drive:
        cmp     al,'a'                  ;* convert to upper case
        jb      save_drive
        cmp     al,'z'
        ja      save_drive
        sub     al,'a'-'A'
save_drive:
        sub     al,'A'
        cmp     al,26
        jae     bad_drive               ;* bad drive
        mov     [drive],al
        jcxz    bad_drive               ;* bad drive (no colon)
        lodsb
        dec     cx
        jcxz    no_file
        cmp     al,':'
        je      get_file                ;* now get the filename
bad_drive:
        mov     dx,offset msgarg
        jmp     exit_msg

get_file:
        lodsb
        cmp     al,' '
        loope   get_file
        je      no_file
        mov     dx,si
        dec     dx                      ;* DS:DX -> filename
        add     si,cx                   ;* add whatever's left to pointer
        mov     [si].lo,0               ;* null-terminate the name

        sub     cx,cx                   ;* CX == attribute
        mov     ah,3Ch
        int     21h                     ;* create file
        jnc     file_created
file_error:
        mov     dx,offset msgfile
        jmp     exit_msg
file_created:
        mov     [hFile],ax

        mov     dx,offset szTmp
        mov     ah,3Ch
        int     21h                     ;* create temp file
        jc      file_error
        mov     [hTmp],ax

        mov     dh,0                    ;* head #
        mov     dl,[drive]              ;* drive #
        mov     cx,1                    ;* track #, sector #
        mov     bx,offset abData        ;* destination
        mov     si,3                    ;* retry count
boot_retry:
        mov     ax,0201h                ;* read the boot sector
        int     13h
        jnc     boot_read
        dec     si
        jnz     boot_retry
        mov     dx,offset msgdisk       ;* we have to be able to read this!
        jmp     exit_msg
boot_read:
        lea     si,[bx+0Bh]             ;* BPB in boot sector is at offset 0Bh

        mov     ax,[si].bpb_nbSector    ;* now compute default track size
        cmp     ax,512                  ;* non-DOS disk?
        je      boot_dos                ;* no
        mov     si,offset bpbDef
        mov     ax,[si].bpb_nbSector
boot_dos:
        mul     [si].bpb_nsecTrack
        mov     di,bx
        add     di,ax                   ;* DI -> header + track table
        mov     [pHdr],di               ;* save it

        sub     al,al
        stosb                           ;* zero header flags
        mov     ax,[si].bpb_nHeads
        mov     [nHeads],al
        stosb                           ;* header entry #1
        mov     ax,[si].bpb_nsecTotal
        sub     dx,dx                   ;* DX:AX == media size
        div     [si].bpb_nsecTrack
        push    ax
        mov     cl,3
        shl     ax,cl                   ;* multiply # tracks by 8
        add     ax,8                    ;* also account for primary header
        mov     posTmp.off,ax
        mov     posCur.off,ax
        pop     ax
        sub     dx,dx
        div     [si].bpb_nHeads         ;* AX == # cylinders
        mov     [nCyls],al
        stosw                           ;* header entry #2
        mov     ax,[si].bpb_nsecTrack
        stosw                           ;* header entry #3
        mov     [defnsec],al
        mov     ax,[si].bpb_nbSector
        stosw                           ;* header entry #4 (header complete)

read_cyl:
        mov     dh,0                    ;* head #
        mov     dl,[drive]              ;* drive #
        mov     cl,1                    ;* sector #
                                        ;* track # and destination are still set
read_head:
;*      push    es
;*      push    bx
;*      sub     bx,bx
;*      mov     es,bx
;*      les     bx,es:[1Eh*4]           ;* get ptr to disk parms
;*      mov     al,es:[bx].dkb_nsec     ;* AL == # sectors per track
;*      pop     bx
;*      pop     es

        mov     al,[defnsec]            ;* get # sectors to read this track

read_retry:
        mov     si,3                    ;* retry count

read_again:
        mov     ah,02h                  ;* INT 13h read function
        push    ax
        int     13h                     ;* read away
        pop     ax
        jnc     write_track
        dec     si
        jg      read_again

        cmp     al,18
        jne     read_try9
        mov     al,16
        jmp     read_again
read_try9:
        cmp     al,16
        jne     read_try8
        mov     al,9
        jmp     read_again
read_try8:
        cmp     al,9                    ;* trying to read 9 sectors?
        jne     read_trybase
        mov     al,8                    ;* yes, so switch to 8
        jmp     read_again

read_trybase:
        sub     ax,ax                   ;* default to 0 sectors, 0 size
        sub     si,si
        cmp     [dkbOld].off,ax         ;* have we tried to change diskbase?
        jne     skip_head               ;* yes, so skip this head
        push    es
        mov     es,ax
        mov     ax,es:[1Eh*4].off
        mov     [dkbOld].off,ax
        mov     ax,es:[1Eh*4].sel
        mov     [dkbOld].sel,ax
        mov     es:[1Eh*4].off,offset dkb1024
        mov     es:[1Eh*4].sel,cs
        mov     al,[dkb1024].dkb_nsec   ;* new # sectors per track
        pop     es
        jmp     read_retry

write_track:
        push    ax
        push    bx
        push    cx
        push    dx
        push    es
        sub     si,si
        mov     es,si
        les     si,es:[1Eh*4]
        mov     cl,es:[si].dkb_ssec     ;* CL == 0,1,2,3
        pop     es
        mov     si,128
        shl     si,cl                   ;* SI == sector size
        cbw                             ;* AX == # sectors read
        mul     si
        mov     cx,ax                   ;* CX == total data read
        mov     dx,bx                   ;* DS:DX -> data
        mov     bx,[hTmp]
        mov     ah,40h
        int     21h                     ;* write to file
        jc      write_fail
        cmp     ax,cx                   ;* did we write the requested amount?
        je      write_ok
write_fail:
        mov     dx,offset msgwrit
        jmp     exit_msg
write_ok:
        mov     ax,posTmp.off           ;* save position of block
        mov     posCur.off,ax
        mov     ax,posTmp.sel
        mov     posCur.sel,ax
        add     posTmp.off,cx           ;* update file position
        adc     posTmp.sel,0
        pop     dx
        pop     cx
        pop     bx
        pop     ax

skip_head:
        cbw
        stosw                           ;* save # sectors this track
        cmp     ax,[nsPrev]             ;* previous match?
        je      skip_head1              ;* yes
        inc     [nDiffs]
        mov     [nsPrev],ax
skip_head1:
        mov     ax,si
        stosw                           ;* save sector size this track
        cmp     ax,[nbPrev]             ;* previous match?
        je      skip_head2              ;* yes
        inc     [nDiffs]
        mov     [nbPrev],ax
skip_head2:
        mov     ax,posCur.off
        stosw
        mov     ax,posCur.sel
        stosw

        sub     ax,ax
        cmp     [dkbOld].off,ax         ;* did we change diskbase?
        je      next_head               ;* no
        push    es
        mov     es,ax
        xchg    ax,[dkbOld].off
        mov     es:[1Eh*4].off,ax
        mov     ax,[dkbOld].sel
        mov     es:[1Eh*4].sel,ax
        pop     es

next_head:
        inc     dh                      ;* advance head
        cmp     dh,[nHeads]             ;* too high?
        jae     next_track
        jmp     read_head               ;* no, read the track

next_track:
        inc     ch                      ;* advance cylinder
        cmp     ch,[nCyls]              ;* too high?
        jae     all_done
        jmp     read_cyl

all_done:
        mov     dx,[pHdr]
        mov     cx,di
        sub     cx,dx                   ;* CX == size of header + track tbl
        mov     di,dx
        cmp     [nDiffs].off,2          ;* many differences?
        ja      whole_table             ;* yes, need whole table
        mov     cx,8                    ;* only write out the primary header
        mov     ax,[di+8]
        mov     [di+4],ax               ;* and update the last two primary
        mov     ax,[di+10]              ;* values from the track tbl
        mov     [di+6],ax
        jmp     short write_table
whole_table:
        mov     [di+4].off,0
        mov     [di+6].off,0            ;* clear last two fields in header
write_table:
        mov     bx,[hFile]
        mov     ah,40h
        int     21h
        jc      write_fail2
        cmp     ax,cx                   ;* did we write the requested amount?
        je      write_ok2
write_fail2:
        mov     dx,offset msgwrit
        jmp     exit_msg
write_ok2:
        sub     cx,cx
        sub     dx,dx                   ;* now seek to start of temp file
        mov     bx,[hTmp]
        mov     ax,4200h
        int     21h
        mov     cx,32768
        mov     dx,offset abData
copy_loop:
        mov     bx,[hTmp]
        mov     ah,3Fh
        int     21h                     ;* read some data
        jc      copy_done               ;* get out
        mov     cx,ax
        mov     bx,[hFile]
        mov     ah,40h
        int     21h
        jc      write_fail2
        cmp     ax,cx
        jne     write_fail2
        cmp     cx,32768                ;* are we still on a roll?
        je      copy_loop
copy_done:
        mov     bx,[hTmp]
        mov     ah,3Eh
        int     21h                     ;* close file
        mov     bx,[hFile]
        mov     ah,3Eh
        int     21h                     ;* close temp file
        mov     dx,offset szTmp
        mov     ah,41h
        int     21h                     ;* delete temp file

        mov     dx,offset msgdone
exit_msg:
        mov     ah,09h
        int     21h

        mov     ax,4C00h                ;* terminate process
        int     21h
main    endp


msgarg  db      "Invalid argument",0Dh,0Ah,0Dh,0Ah
msguse  db      "Usage: VMDISK [drive|size] filename",0Dh,0Ah,"$"
msgfile db      "Cannot create file",0Dh,0Ah,"$"
msgdisk db      "Cannot read specified disk",0Dh,0Ah,"$"
msgwrit db      "Cannot write to file",0Dh,0Ah,"$"
msgdone db      "Processing complete",0Dh,0Ah,"$"

drive   db      0                       ;* drive to use
nHeads  db      0                       ;* # heads on disk
nCyls   db      0                       ;* # cylinders on disk
hFile   dw      0                       ;* handle to main file
hTmp    dw      0                       ;* handle to temp file
pHdr    dw      0                       ;* pointer to header + track tables
posTmp  dd      0                       ;* position within temp file
posCur  dd      0                       ;* position of last block written
nsPrev  dw      0                       ;* prev. track characteristics
nbPrev  dw      0
nDiffs  dw      0                       ;* number of track differences
defnsec db      9                       ;* default # sectors to read per track
szTmp   db      "VMDISK.$$$",0          ;* name of temp file
dkbOld  dd      0                       ;* previous diskbase ptr
bpbDef  bpb     <512, 1, 1, 2, 128, 320, 0F0h, 8, 8, 1, 0, 0>
dkb1024 dskbase <0CFh, 02h, 25h, 03h, 04h, 2Ah, 0FFh, 50h, 0F6h, 19h, 04h>

abData  label   byte

_TEXT   ends


        end     main
