272 lines
5.2 KiB
Plaintext
272 lines
5.2 KiB
Plaintext
; -*- fundamental -*- (asm-mode sucks)
|
|
; -----------------------------------------------------------------------
|
|
;
|
|
; Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
|
|
;
|
|
; This program is free software; you can redistribute it and/or modify
|
|
; it under the terms of the GNU General Public License as published by
|
|
; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
|
|
; Boston MA 02111-1307, USA; either version 2 of the License, or
|
|
; (at your option) any later version; incorporated herein by reference.
|
|
;
|
|
; -----------------------------------------------------------------------
|
|
|
|
;
|
|
; copybs.asm
|
|
;
|
|
; Small DOS program to copy the boot sector from a drive
|
|
; to a file
|
|
;
|
|
; Usage: copybs <drive>: <file>
|
|
;
|
|
|
|
absolute 0
|
|
pspInt20: resw 1
|
|
pspNextParagraph: resw 1
|
|
resb 1 ; reserved
|
|
pspDispatcher: resb 5
|
|
pspTerminateVector: resd 1
|
|
pspControlCVector: resd 1
|
|
pspCritErrorVector: resd 1
|
|
resw 11 ; reserved
|
|
pspEnvironment: resw 1
|
|
resw 23 ; reserved
|
|
pspFCB_1: resb 16
|
|
pspFCB_2: resb 16
|
|
resd 1 ; reserved
|
|
pspCommandLen: resb 1
|
|
pspCommandArg: resb 127
|
|
|
|
section .text
|
|
org 100h ; .COM format
|
|
_start:
|
|
mov ax,3000h ; Get DOS version
|
|
int 21h
|
|
xchg al,ah
|
|
mov [DOSVersion],ax
|
|
cmp ax,0200h ; DOS 2.00 minimum
|
|
jae dosver_ok
|
|
mov dx,msg_ancient_err
|
|
jmp die
|
|
|
|
section .bss
|
|
alignb 2
|
|
DOSVersion: resw 1
|
|
|
|
section .text
|
|
;
|
|
; Scan command line for a drive letter followed by a colon
|
|
;
|
|
dosver_ok:
|
|
xor cx,cx
|
|
mov si,pspCommandArg
|
|
mov cl,[pspCommandLen]
|
|
|
|
cmdscan1: jcxz bad_usage ; End of command line?
|
|
lodsb ; Load character
|
|
dec cx
|
|
cmp al,' ' ; White space
|
|
jbe cmdscan1
|
|
or al,020h ; -> lower case
|
|
cmp al,'a' ; Check for letter
|
|
jb bad_usage
|
|
cmp al,'z'
|
|
ja bad_usage
|
|
sub al,'a' ; Convert to zero-based index
|
|
mov [DriveNo],al ; Save away drive index
|
|
|
|
section .bss
|
|
DriveNo: resb 1
|
|
|
|
section .text
|
|
;
|
|
; Got the leading letter, now the next character must be a colon
|
|
;
|
|
got_letter: jcxz bad_usage
|
|
lodsb
|
|
dec cx
|
|
cmp al,':'
|
|
jne bad_usage
|
|
;
|
|
; Got the colon; now we should have at least one whitespace
|
|
; followed by a filename
|
|
;
|
|
got_colon: jcxz bad_usage
|
|
lodsb
|
|
dec cx
|
|
cmp al,' '
|
|
ja bad_usage
|
|
|
|
skipspace: jcxz bad_usage
|
|
lodsb
|
|
dec cx
|
|
cmp al,' '
|
|
jbe skipspace
|
|
|
|
mov di,FileName
|
|
copyfile: stosb
|
|
jcxz got_cmdline
|
|
lodsb
|
|
dec cx
|
|
cmp al,' '
|
|
ja copyfile
|
|
jmp short got_cmdline
|
|
|
|
;
|
|
; We end up here if the command line doesn't parse
|
|
;
|
|
bad_usage: mov dx,msg_unfair
|
|
jmp die
|
|
|
|
section .data
|
|
msg_unfair: db 'Usage: copybs <drive>: <filename>', 0Dh, 0Ah, '$'
|
|
|
|
section .bss
|
|
alignb 4
|
|
FileName resb 256
|
|
|
|
;
|
|
; Parsed the command line OK. Get device parameter block to get the
|
|
; sector size.
|
|
;
|
|
struc DPB
|
|
dpbDrive: resb 1
|
|
dpbUnit: resb 1
|
|
dpbSectorSize: resw 1
|
|
dpbClusterMask: resb 1
|
|
dpbClusterShift: resb 1
|
|
dpbFirstFAT: resw 1
|
|
dpbFATCount: resb 1
|
|
dpbRootEntries: resw 1
|
|
dpbFirstSector: resw 1
|
|
dpbMaxCluster: resw 1
|
|
dpbFATSize: resw 1
|
|
dpbDirSector: resw 1
|
|
dpbDriverAddr: resd 1
|
|
dpbMedia: resb 1
|
|
dpbFirstAccess: resb 1
|
|
dpbNextDPB: resd 1
|
|
dpbNextFree: resw 1
|
|
dpbFreeCnt: resw 1
|
|
endstruc
|
|
|
|
section .bss
|
|
alignb 2
|
|
SectorSize resw 1
|
|
|
|
section .text
|
|
got_cmdline:
|
|
xor al,al ; Zero-terminate filename
|
|
stosb
|
|
|
|
mov dl,[DriveNo]
|
|
inc dl ; 1-based
|
|
mov ah,32h
|
|
int 21h ; Get Drive Parameter Block
|
|
|
|
and al,al
|
|
jnz filesystem_error
|
|
|
|
mov dx,[bx+dpbSectorSize] ; Save sector size
|
|
;
|
|
; Read the boot sector.
|
|
;
|
|
section .data
|
|
align 4, db 0
|
|
DISKIO equ $
|
|
diStartSector: dd 0 ; Absolute sector 0
|
|
diSectors: dw 1 ; One sector
|
|
diBuffer: dw SectorBuffer ; Buffer offset
|
|
dw 0 ; Buffer segment
|
|
|
|
section .text
|
|
read_bootsect:
|
|
mov ax,cs ; Set DS <- CS
|
|
mov ds,ax
|
|
|
|
mov [SectorSize],dx ; Saved sector size from above
|
|
|
|
cmp word [DOSVersion],0400h ; DOS 4.00 has a new interface
|
|
jae .new
|
|
.old:
|
|
mov bx,SectorBuffer
|
|
mov cx,1 ; One sector
|
|
jmp short .common
|
|
.new:
|
|
mov [diBuffer+2],ax ; == DS
|
|
mov bx,DISKIO
|
|
mov cx,-1
|
|
.common:
|
|
xor dx,dx ; Absolute sector 0
|
|
mov al,[DriveNo]
|
|
int 25h ; DOS absolute disk read
|
|
pop ax ; Remove flags from stack
|
|
jc disk_read_error
|
|
|
|
;
|
|
; Open the file and write the boot sector to the file.
|
|
;
|
|
mov dx,FileName
|
|
mov cx,0020h ; Attribute = ARCHIVE
|
|
mov ah,3Ch ; Create file
|
|
int 21h
|
|
jc file_write_error
|
|
|
|
mov bx,ax
|
|
push ax ; Handle
|
|
|
|
mov cx,[SectorSize]
|
|
mov dx,SectorBuffer
|
|
mov ah,40h ; Write file
|
|
int 21h
|
|
jc file_write_error
|
|
cmp ax,[SectorSize]
|
|
jne file_write_error
|
|
|
|
pop bx ; Handle
|
|
mov ah,3Eh ; Close file
|
|
int 21h
|
|
jc file_write_error
|
|
;
|
|
; We're done!
|
|
;
|
|
mov ax,4C00h ; exit(0)
|
|
int 21h
|
|
|
|
;
|
|
; Error routine jump
|
|
;
|
|
filesystem_error:
|
|
mov dx,msg_filesystem_err
|
|
jmp short die
|
|
disk_read_error:
|
|
mov dx,msg_read_err
|
|
jmp short die
|
|
file_write_error:
|
|
mov dx,msg_write_err
|
|
die:
|
|
push cs
|
|
pop ds
|
|
push dx
|
|
mov dx,msg_error
|
|
mov ah,09h
|
|
int 21h
|
|
pop dx
|
|
|
|
mov ah,09h ; Write string
|
|
int 21h
|
|
|
|
mov ax,4C01h ; Exit error status
|
|
int 21h
|
|
|
|
section .data
|
|
msg_error: db 'ERROR: $'
|
|
msg_ancient_err: db 'DOS version 2.00 or later required', 0Dh, 0Ah, '$'
|
|
msg_filesystem_err: db 'Filesystem not found on disk', 0Dh, 0Ah, '$'
|
|
msg_read_err: db 'Boot sector read failed', 0Dh, 0Ah, '$'
|
|
msg_write_err: db 'File write failed', 0Dh, 0Ah, '$'
|
|
|
|
section .bss
|
|
alignb 4
|
|
SectorBuffer: resb 4096
|