| ; -*- 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 |