; FORMAT.ASM  - Formats disks
; ------------- (c) 2004-2005 dataMAverick, Ltd.
;
; ---------------------------------------------------------------------------

code            segment byte public
                assume  cs:code, ds:code
		org	100h
format          proc    far

start:          jmp     _main
include         \nxdos\src\toolbox.asm
_main:

                mov  bh, '/'
                mov  bl, '?'
                call scancmdline
                mov  argc, cx
                or   cx, cx
                jnz  _chkparams
                _print missingparam, 1
                xor  ax, ax
                call dosexit
_chkparams:
                cmp  dl, 1
                jne  _nohelp
                ; jnc  _nohelp
                _print help, 1
                xor  ax, ax
                call dosexit
_nohelp:
                mov  bh, 'A'
                mov  bl, ':'
                call scancmdline
                jc   _firstfloppy
                mov  bh, 'B'
                mov  bl, ':'
                call scancmdline
                jc   _secondfloppy
                mov  bh, 'C'
                mov  bl, ':'
                call scancmdline
                jc   _masterharddisk
_firstfloppy:    mov  al, 0
                jmp  _setdrivenum
_secondfloppy:   mov  al, 1
                jmp  _setdrivenum
_masterharddisk:
                mov  al, 2
_setdrivenum:   mov  _diskdrive, al
                xor  ah, ah
                add  ax, 65
                mov  byte ptr Dosdrive, al
                call cs:main
exit:           call dosexit
format          endp

; ===========================================================================
main     proc far
; ===========================================================================

        mov  al, _diskdrive
        cmp  al, 2
        jge  nodiskprompt
        _print insertdiskmsg, 1
        mov  al, byte ptr Dosdrive
        call teletype
        _echo ':'
        _newline
        _print pressentermsg, 1
        call readkey
nodiskprompt:
        mov  al, _diskdrive
        cmp  al, 2
        jb   nowarningprompt
        _print warningprompt, 1
        call readkey
        cmp  al, 'Y'
        je   nowarningprompt
        cmp  al, 'y'
        je   nowarningprompt
        ret
nowarningprompt:

        ; get disk info
        mov byte ptr _diskdrive, al
        mov ah, 08h
        int 13h
        jc  _DISKNOTREADY
        mov  drivetype, bl

        call formatfloppydisk

        ; display report
        ; total and avail bytes on disk
        ; bytes per alloaction unit (sector)
        ; allocation units on disk

        ; print volume serial number

; prepare disk blocks
;for x 0 to tracks do
;    for y 1 to spt do
;        write sector_block
;        inc y
;    inc x
; write fat table
; write root directory
; write boot sector
; if /S then write system files

         jmp  short mainx
_DISKNOTREADY:
         _print NotReadyOrError, 1
mainx:   ret
main     endp

Formatfloppydisk proc
        ;
        _print formattingmsg, 1
        ; display size of format in K

        ; format disk
        mov AX, 19  ; Root Directory Address
        mov BX, offset FORMAT_FATBUFFER_S9
        mov CX, 14
write_root_dir_sectors:
        call write_floppysector
        jnc _DISKNOT1
         _print NotReadyOrError, 1
        ret
_DISKNOT1:
        inc AX
        cmp AX, 32
        jna short write_root_dir_sectors

        _print rootmsg_ok, 1

     ;   _print Writing_Data_Sectors, 1
     ;   mov AH, 3
     ;   mov BX, 7
     ;   int 10h ; Return Cursor Position
        ; DL = Column, DH= Line
     ;   mov word ptr  [Cursor_Pos], DX
        mov AX, 33  ; Root Directory Address
write_data_sectors:
        mov BX, offset FORMAT_SECTOR_BUFFER
        call write_floppysector
        jnc short write_data_sectors_cont
        and ah, 16h  ; Errors: 2h, 4h, 10h
        jnz DISKNOT2
         _print NotReadyOrError, 1
        ret
DISKNOT2:
        ;DX= LBA sector value
markbadcluster:    
        push dx
        call mark_bad_cluster
        pop ax
        jmp short write_data_sectors_cont
write_data_sectors_cont:
        mov BX, 7
        inc AX
        cmp AX, 2880
        jna short write_data_sectors

                mov AX, 1  ; FAT Beginning Address
                mov BX, offset FORMAT_FATBUFFER
write_fat1_sectors:
                call write_floppysector
                jnc short DISKNOT3
                _print NotReadyOrError, 1
                ret
DISKNOT3:
                inc AX
                add BX, 512
                cmp AX, 9
                jna short write_fat1_sectors
                mov BX, offset FORMAT_FATBUFFER
write_fat2_sectors:
                call write_floppysector
                jnc DISKNOT4
                _print NotReadyOrError, 1
                ret
DISKNOT4:
                inc AX
                add BX, 512
                cmp AX, 18
                jna short write_fat2_sectors

        _print successful, 1

        
        ; prompt for volume label
        _print vollabelprompt, 1
        mov  bx, 7c2ah
        mov  di, offset boot144
        add  di, bx
        mov  cx, 11
        call getdata

        ; write volume label/bootsector
        mov  byte ptr [RetryCount], 4
write_bootsector:
        xor ax, ax
        int 1Ah
        ; CX:DX contain clock ticks , calculate serial number from system time
        mov  bx, 7c26h
        mov  si, offset boot144
        add  si, bx
        mov  word ptr [si], cx
        mov  word ptr [si+2], dx
        ; mov word ptr bsVolumeID, dx
        ; mov word ptr bsVolumeID+2, cx           ; set unique volume ID
        ; Cheap...
        mov ax, 0301h                           ; write to disk
        mov bx, offset boot144                  ; location of boot code
        mov cx, 1                               ; cylinder = 0
                                                ; sector = 1
        mov dh, 0                               ; head = 0
        mov dl, byte ptr _diskdrive
        int 13h
        jnc doneformatting
        dec byte ptr [RetryCount]
        jz  DISKNOT5
        jmp write_bootsector
DISKNOT5:
        _print NotReadyOrError, 1
        ret
doneformatting:
        _print successful, 1
        _newline
        ret
Formatfloppydisk endp

write_floppysector    proc near
                ; Only for 1.44 MB FAT12 Floppy Disks
                ; INPUT -> AX = Logical Block Address
                ; ES:BX = Sector Buffer
                ; OUTPUT -> clc or stc
                ; CLC -> AX = Logical Block Address
                ; STC -> DX = Logical Block Address
                ; STC -> AH = Error Number
                mov     CX, 4  ; Retry Count
loc_write_fdisk_chs:
                push    AX                      ; Linear sector number
                push    CX                      
                mov     DX, 18                  ; Sectors per Track
                div     DL
                mov     CL, AH                  ; Sector (zero based)
                inc     CL                      ; To make it 1 based
                shr     AL, 1                   ; Convert Track to Cylinder
                adc     DH, 0                   ; Head (0 or 1)
                mov     DL, Byte Ptr [_diskdrive]
                mov     CH,AL                   
                mov     AX,0301h
                int     13h                     ; BIOS Service func ( ah ) = 3
                                                ; Write disk sectors
                mov     byte ptr [Error_Code], ah
                pop     CX
                pop     AX

                jnc     short pass_write_fdisk_chs_error
                loop    loc_write_fdisk_chs
                mov     DX, AX
                mov     ah, byte ptr [Error_Code]
                stc
pass_write_fdisk_chs_error:
                ret
write_floppysector    endp

mark_bad_cluster   proc near
                ; Only for FAT12 Floppy Disks (Full FAT Buffer)
                ; INPUT -> AX = Cluster/Sector Number
                ; INPUT -> 0FF7h = BAD Cluster Value
                mov     dx, 3
                mul     dx
                shr     ax, 1 ; Divide by 2
                mov     bx, ax  ; FAT Buffer Byte Offset
                mov     dx, 0FF7h ; "BAD CLUSTER" sign
loc_update_fat12_cell:
                mov     ax, word ptr [FORMAT_FATBUFFER][BX]
                jnc     short uc_FAT12_nc_even
                and     ax, 0Fh
                shl     dx, 1
                shl     dx, 1
                shl     dx, 1
                shl     dx, 1
                or      dx, ax
                mov     word ptr [FORMAT_FATBUFFER][BX], dx
                ret
uc_FAT12_nc_even:
                and     ax, 0F000h
                and     dh, 0Fh
                or      dx, ax
                mov     word ptr [FORMAT_FATBUFFER][BX], dx
                ret
mark_bad_cluster   endp

FORMAT_SECTOR_BUFFER db 512 dup(0F6h)
FORMAT_FATBUFFER     db 0F0h
                     db 0FFh
                     db 0FFh
                     db 4093 dup(0)
FORMAT_FATBUFFER_S9  db 512 dup(0)

RetryCount      db 0
Error_Code      db 0
argc            dw ?
handle          dw ?
_diskdrive      db 0
drivetype       db 0
missingparam    db 'Required parameter missing',0ffh,0
help            db 'Formats diskettes for usage by NX-DOS.',0ffh
                db 0ffh,'FORMAT [Drive:]',0ffh
                db 0ffh,'Drive:          Formats specified drive for use.',0ffh,0
bootpath        db 'A:\'
successful      db 'OK',0ffh,0
warningprompt   db 07h,0ffh
                db 'WARNING!', 0ffh
                db 'All data on the drive will be erased.',0ffh,0ffh
                db 'Do you want to format drive'
dosdrive:       db 'A: (Yes/No)? ', 0
insertdiskmsg   db 'Insert new diskette for drive ',0
pressentermsg   db 'and press ENTER when ready...',0
formattingmsg    db 'Formatting ',0
completedformatmsg  db 'Format complete.',0ffh,0
NotReadyOrError  db 'Disk error or drive not ready.',0
rootmsg_ok      db 'Root/FAT written ',0
vollabelprompt  db 'Volume label (11 charactersm ENTER for none)? ',0

include         \nxdos\src\boot\bootcore.inc

     Serialnum   dd      06221972
     Vollabel    db      '           '
     Filesys     db      'FAT12   '
     Dctable     dw      offset d360
                 dw      offset d120
                 dw      offset d720
                 dw      offset d144

     D360        dw      40                  ;Number of cylinders (tracks)
                 dw      512                 ;Bytes per sector
                 db      2                   ;Sectors per cluster
                 dw      1                   ;Reserved sectors
                 db      2                   ;Number of fats
                 dw      112                 ;Root directory entries
                 dw      40*9*2              ;Total sectors
                 db      0fdh                ;Media descriptor
                 dw      2                   ;Sectors per fat
                 dw      9                   ;Sectors per track
                 dw      2                   ;Number of heads
                 dd      00000000            ;Hidden sectors
                 dd      00000000            ;Large sector count

     D120        dw      80                  ;Number of cylinders (tracks)
                 dw      512                 ;Bytes per sector
                 db      1                   ;Sectors per cluster
                 dw      1                   ;Reserved sectors
                 db      2                   ;Number of fats
                 dw      224                 ;Root directory entries
                 dw      80*15*2             ;Total sectors
                 db      0f9h                ;Media descriptor
                 dw      7                   ;Sectors per fat
                 dw      15                  ;Sectors per track
                 dw      2                   ;Number of heads
                 dd      00000000            ;Hidden sectors
                 dd      00000000            ;Large sector count

     D720        dw      80                  ;Number of cylinders (tracks)
                 dw      512                 ;Bytes per sector
                 db      2                   ;Sectors per cluster
                 dw      1                   ;Reserved sectors
                 db      2                   ;Number of fats
                 dw      112                 ;Root directory entries
                 dw      80*9*2              ;Total sectors
                 db      0f9h                ;Media descriptor
                 dw      3                   ;Sectors per fat
                 dw      9                   ;Sectors per track
                 dw      2                   ;Number of heads
                 dd      00000000            ;Hidden sectors
                 dd      00000000            ;Large sector count

     D144        dw      80                  ;Number of cylinders (tracks)
                 dw      512                 ;Bytes per sector
                 db      1                   ;Sectors per cluster
                 dw      1                   ;Reserved sectors
                 db      2                   ;Number of fats
                 dw      224                 ;Root directory entries
                 dw      80*18*2             ;Total sectors
                 db      0f0h                ;Media descriptor
                 dw      9                   ;Sectors per fat
                 dw      18                  ;Sectors per track
                 dw      2                   ;Number of heads
                 dd      00000000            ;Hidden sectors
                 dd      00000000            ;Large sector count
     Drmsg2      db      ' disk in drive '
     Ascdrive    db      'A:',13,10,0

     Fmtmsg      db      'Formatting....',0

     Fttable     dw      offset Ft00
                 dw      offset Ft01
                 dw      offset Ft02
                 dw      offset Ft03

     Ft00        db      '360k 5.25"',0
     Ft01        db      '1.2m 5.25"',0
     Ft02        db      '720k 3.5"',0
     Ft03        db      '1.44m 3.5"',0

     Verbose     db      0
     Trackmsg1   db      13,'Formatting track ',0
     Trackmsg2   db      ' of ',0
     Finishmsg   db      13,'Format completed.........',13,10,0

     InsertDisk  db      'Please insert disk to format.',13,10
                 db      ' Press any key when ready...',13,10,0
     Emsg1       db      'Could not determine drive parameters.',13,10,0
     Emsg2       db      'Could not work with this device type.',13,10,0
     Emsg3       db      'Could not set proper parameters.',13,10,0
     Emsg4       db      13,10,'Error formatting track...',13,10,0

code            ends

ProgramEnd      EQU $-01h

		end	start


FrmtDisk   PROC far
; FDrive:word, FSize:word, Verb:word

            mov     bx,offset Verb
            mov     ax,[bx]
            mov     Verbose,al
            mov     bx,offset FDrive
            mov     ax,[bx]
            mov     drive,al            ;Store drive number
            add     al,64               ;Make it into an ASCII character
            mov     ascdrive,al         ;Store it
            mov     al,0                ;Special function desired
            call    getparms            ;Go find out about drive
            jnc     NoErr1
            jmp     error1              ;Didn't work, so exit
NoErr1:     call    xferparms           ;Transfer to real work area

            mov     al,devicetype
            cmp     al,3                ;Valid device types?
            jb      deviceok
            cmp     al,7                ;Still valid?
            je      NotErr
            jmp     error2              ;Nope, so exit
NotErr:     mov     al,3                ;Force 7=3 for format type

Deviceok:
            mov     si,offset FSize
            mov     ax,[si]
            mov     formattype,al       ;Assume 0 or 2


Ready:      mov     al,formattype
            shl     al,1                ;X's by 2
            mov     ah,0
            mov     bx,offset fttable
            add     bx,ax               ;Adjust for proper message
            mov     si,[bx]             ;Get address from table
            call    prtstring
            mov     si,offset drmsg2
            call    prtstring

            call    doparms             ;Set up parameters for format
            mov     al,5                ;Special code desired
            call    setparms            ;Send them to IOCTL
            jc      error3

            mov     si,offset InsertDisk
            call    prtstring
            xor     ah,ah
            int     16h

            mov     curhead,0
            mov     cx,cylinders        ;Number of tracks to format
            mov     curcylinder,-1      ;Track 0
Fmtloop:    inc     curcylinder         ;
            call    status              ;Display status
            call    formattrack         ;Format the track
            jc      error4
            loop    fmtloop

            call    writeboot           ;Write the boot sector
            call    setmid

            call    writefat            ;Write the fat
            call    writedir            ;Write the directory

            call    restparms           ;Set the ioctl back right
            mov     si,offset finishmsg
            call    prtstring
            jmp     alldone

Error1:     mov     si,offset emsg1
            jmp     errcommon
Error2:     mov     si,offset emsg2
            jmp     errcommon
Error3:     call    restparms           ;Restore parameters
            mov     si,offset emsg3
            jmp     errcommon
Error4:     call    restparms           ;Restore parameters
            mov     si,offset emsg4
Errcommon:  call    prtstring
Alldone:   ret
FrmtDisk   ENDP

; Get current parms for desired drive.  AL = to special function
Getparms    proc near
            mov     sforig,al           ;Want for current bpb
            mov     ah,44h              ;IOCTL
            mov     al,0dh              ;Generic block I/O
            mov     bl,drive
            mov     ch,08h              ;Disk drive
            mov     cl,60h              ;Get device parameters
            mov     dx,offset origparms
            int     21h
            ret
Getparms    endp


; Transfer from retrieved parameter area into one that will be used
; for the real work in the program
Xferparms   proc near
            cld
            push    ds
            pop     es
            mov     si,offset origparms
            mov     di,offset newparms
            mov     cx,parmlen
            rep     movsb
            clc                         ;Exit with no error
            ret
Xferparms   endp


; Set parameters.  AL = to special function desired
Setparms    proc near
            mov     specfuncnew,al
            mov     ah,44h              ;Ioctl
            mov     al,0dh              ;Generic block I/O
            mov     bl,drive
            mov     ch,08h              ;Disk drive
            mov     cl,40h              ;Set device parameters
            mov     dx,offset newparms
            int     21h
            ret
Setparms    endp

; Restore parms to their old condition
Restparms   proc near uses ax bx cx dx
            mov     sforig,00000100b
            mov     ah,44h              ;Ioctl
            mov     al,0dh              ;Generic block I/O
            mov     bl,drive
            mov     ch,08h              ;Disk drive
            mov     cl,40h              ;Set device parameters
            mov     dx,offset origparms
            int     21h
            ret
Restparms   endp


; Set up parameters (including BPB) for selected format type.  Also
; transfers information into the BPB in the boot sector.
Doparms     proc near uses ax bx cx si di es
            mov     ah,0
            mov     al,formattype
            shl     ax,1                ;X'2 by 2
            mov     bx,offset dctable
            add     bx,ax               ;Adjust for format type
            mov     si,[bx]             ;Get proper address
            mov     ax,[si]             ;Get number of cylinders
            inc     si                  ;Point to next parameter
            inc     si
            cmp     cylinders,ax        ;Default capacity for drive?
            je      dp1
            mov     mediatype,1         ;Set for low format
            mov     cylinders,ax
Dp1:        push    si                  ;Store for a moment
            mov     cx,25               ;Bytes to transfer
            mov     di,offset diskbpb
            push    ds
            pop     es
            cld
            rep     movsb               ;Transfer parms
            call    dolayout

            pop     si                  ;Get start of bpb back
            mov     ax,seg bootbpb
            mov     es,ax
            mov     di,offset es:bootbpb
            mov     cx,25
            rep     movsb               ;Transfer to boot sector
            ret
Doparms     endp


; set the track layout field
Dolayout    proc near uses ax bx cx si di es
            cld
            mov     di,offset tracklayout
            mov     ax,secttrack
            push    ds
            pop     es
            stosw
            mov     bx,1                ;First sector number
            mov     cx,ax               ;Number of sectors per track
            mov     dx,bytessect        ;Bytes per sector
Trkloop:    mov     ax,bx               ;Sector number
            stosw
            inc     bx                  ;Point to next sector
            mov     ax,dx               ;Bytes per sector
            stosw
            loop    trkloop             ;Do it all again
            ret
Dolayout    endp

; set the media id
Setmid      proc near uses ax bx cx dx
            mov     ah,44h              ;IOCTL
            mov     al,0dh              ;Generic block I/O
            mov     bl,drive
            mov     ch,08h              ;Disk drive
            mov     cl,46h              ;Set media ID
            mov     dx,offset midstruct
            int     21h
            ret
Setmid      endp

; Handle formatting a single track.  Return with carry set if error
Formattrack proc near uses cx
            mov     cx,numheads
            dec     cx                  ;Make zero based
Ftloop:     mov     curhead,cl
            call    formatside
            jc      ftdone
            loop    ftloop
            clc                         ;Return with no errors
Ftdone:     ret
Formattrack endp

; Format one head of a track.  Return with carry set if error
Formatside  proc near uses ax bx cx dx
            mov     fmtparmsf,0
            mov     ah,0
            mov     al,curhead
            mov     fmtparmhead,ax
            mov     al,curcylinder
            mov     fmtparmcyl,ax

            mov     ah,44h              ;IOCTL
            mov     al,0Dh              ;Generic block I/O
            mov     bh,0
            mov     bl,drive
            mov     ch,08h              ;Disk drive
            mov     cl,42h              ;Format/verify track
            mov     dx,offset fmtparms
            int     21h
            ret
Formatside  endp

; Display the status message during formatting
Status      proc near uses ax si
            mov     si,offset trackmsg1
            call    prtstring
            mov     ah,0
            mov     al,curcylinder
            inc     ax
            call    prtdec
            mov     si,offset trackmsg2
            call    prtstring
            mov     ax,cylinders
            call    prtdec
            ret
Status      endp


; Write the boot sector to the disk
Writeboot   proc near uses ax bx cx es ds
            mov     ax,seg bootsector   ;Point to boot sector
            mov     es,ax
            mov     bx,offset es:bootsector
            mov     buffstart[0],bx
            mov     buffstart[2],es
            mov     sectbegin,0
            mov     sectcount,1
            mov     al,drive
            dec     al                  ;Make it zero based
            mov     bx,offset diskio    ;Point to buffer
            mov     cx,-1
            int     26h
            popf                        ;Get rid of flags
            ret
Writeboot   endp

; Write the fat to the disk
Writefat    proc near uses ax bx cx dx di es ds
            mov     ax,sectfat
            mov     bl,32               ;Paragraphs per 512 bytes
            mul     bl                  ;Ax now contains paragraphs needed
            mov     bx,ax               ;   for one fat image
            mov     ah,48h              ;Allocate memory
            int     21h                 ;DOS services
            mov     es,ax               ;Put in proper place

            mov     ax,sectfat
            mov     cl,8                ;2^8 power (256)
            shl     ax,cl               ;Number of words in memory area
            mov     cx,ax               ;Iteration count
            mov     di,0                ;Point to start of memory block
            mov     ax,0                ;Want to clear memory area
            cld                         ;Go in proper direction
            rep     stosw               ;Store 0s in memory

            mov     ax,0FFFFh
            mov     bl,mediaid
            mov     es:[0],bl           ;Store FAT ID
            mov     es:[1],ax           ;Store filler

            mov     ax,1
            mov     ch,0
            mov     cl,numfats
Fatloop:    call    putfat
            add     ax,sectfat
            loop    fatloop

            mov     ah,49h              ;Release memory block
            int     21h                 ;DOS services

            ret
Writefat    endp

; Put one copy of the fat on disk.  AX=beginning sector number
Putfat      proc near uses ax bx cx es
            mov     buffstart[0],0
            mov     buffstart[2],es
            mov     sectbegin,ax
            mov     ax,sectfat
            mov     sectcount,ax
            mov     al,drive
            dec     al
            mov     bx,offset diskio
            mov     cx,-1
            int     26h
            popf                       ;Get rid of flags
            ret
Putfat      endp

; Write the directory to the disk
Writedir    proc near uses ax bx cx dx di es ds
            mov     ax,rootdir          ;Maximum root directory entries
            mov     cl,4                ;2^4 power (16)
            shr     ax,cl               ;Divide by 16, now ax = sectors needed
            push    ax                  ;Store for later

            mov     bl,32               ;Paragraphs per 512 bytes
            mul     bl                  ;AX now contains paragraphs needed
            mov     bx,ax
            mov     ah,48h              ;Allocate memory
            int     21h                 ;DOS services
            mov     es,ax               ;Put in proper place

            pop     ax                  ;Get back number of directory sectors
            push    ax                  ;And store for later
            mov     cl,8                ;2^8 power (256)
            shl     ax,cl               ;AX now has number of words in data area
            mov     cx,ax               ;Put into loop counter
            mov     di,0                ;Start at beginning of block
            mov     ax,0                ;Want to clear memory area
            cld                         ;Go in proper direction
            rep     stosw               ;Store 0s in memory

            mov     ax,sectfat          ;Number of sectors in one FAT
            mov     bl,numfats          ;Get number of fats
            mul     bl                  ;AX now has number of sectors
            inc     ax                  ;Point to sector past FAT

            mov     buffstart[0],0
            mov     buffstart[2],es
            mov     sectbegin,ax
            pop     ax                  ;Get back directory sectors
            mov     sectcount,ax
            mov     al,drive
            dec     al
            mov     bx,offset diskio
            mov     cx,-1
            int     26h
            popf                        ;Get rid of flags

            mov     ah,49h              ;Release memory block
            int     21h                 ;DOS services
            ret
Writedir    endp

