diff --git a/boot/boot.asm b/boot/boot.asm index af2c527..d8b2f45 100644 --- a/boot/boot.asm +++ b/boot/boot.asm @@ -26,41 +26,48 @@ ; Cambridge, MA 02139, USA. ; ; -; +--------+ 1FE0:7E00 -; |BOOT SEC| -; |RELOCATE| -; |--------| 1FE0:7C00 -; | | -; |--------| 1FE0:3000 -; | CLUSTER| -; | LIST | -; |--------| 1FE0:2000 -; | | -; |--------| 0000:7E00 -; |BOOT SEC| overwritten by max 128k FAT buffer -; |ORIGIN | and later by max 134k loaded kernel -; |--------| 0000:7C00 -; | | -; |--------| -; |KERNEL | also used as max 128k FAT buffer -; |LOADED | before kernel loading starts -; |--------| 0060:0000 -; | | -; +--------+ +; +--------+ 1FE0:7E00 +; |BOOT SEC| +; |RELOCATE| +; |--------| 1FE0:7C00 +; |LBA PKT | +; |--------| 1FE0:7BC0 +; |--------| 1FE0:7BA0 +; |BS STACK| +; |--------| +; |4KBRDBUF| used to avoid crossing 64KB DMA boundary +; |--------| 1FE0:63A0 +; | | +; |--------| 1FE0:3000 +; | CLUSTER| +; | LIST | +; |--------| 1FE0:2000 +; | | +; |--------| 0000:7E00 +; |BOOT SEC| overwritten by max 128k FAT buffer +; |ORIGIN | and later by max 134k loaded kernel +; |--------| 0000:7C00 +; | | +; |--------| +; |KERNEL | also used as max 128k FAT buffer +; |LOADED | before kernel loading starts +; |--------| 0060:0000 +; | | +; +--------+ ;%define ISFAT12 1 ;%define ISFAT16 1 -segment .text +segment .text %define BASE 0x7c00 org BASE Entry: jmp short real_start - nop + nop ; bp is initialized to 7c00h %define bsOemName bp+0x03 ; OEM label @@ -91,15 +98,91 @@ Entry: jmp short real_start ;%define StoreSI bp+3h ;temp store -; To save space, functions that are just called once are -; implemented as macros instead. Four bytes are saved by -; avoiding the call / ret instructions. +;----------------------------------------------------------------------- + + times 0x3E-$+$$ db 0 + +; using bp-Entry+loadseg_xxx generates smaller code than using just +; loadseg_xxx, where bp is initialized to Entry, so bp-Entry equals 0 +%define loadsegoff_60 bp-Entry+loadseg_off +%define loadseg_60 bp-Entry+loadseg_seg + +%define LBA_PACKET bp-0x40 +%define LBA_SIZE word [LBA_PACKET] ; size of packet, should be 10h +%define LBA_SECNUM word [LBA_PACKET+2] ; number of sectors to read +%define LBA_OFF LBA_PACKET+4 ; buffer to read/write to +%define LBA_SEG LBA_PACKET+6 +%define LBA_SECTOR_0 word [LBA_PACKET+8 ] ; LBA starting sector # +%define LBA_SECTOR_16 word [LBA_PACKET+10] +%define LBA_SECTOR_32 word [LBA_PACKET+12] +%define LBA_SECTOR_48 word [LBA_PACKET+14] + +%define READBUF 0x63A0 ; max 4KB buffer (min 2KB stack), == stacktop-0x1800 +%define READADDR_OFF BP-0x60-0x1804 ; pointer within user buffer +%define READADDR_SEG BP-0x60-0x1802 + +%define PARAMS LBA_PACKET+0x10 +;%define RootDirSecs PARAMS+0x0 ; # of sectors root dir uses + +%define fat_start PARAMS+0x2 ; first FAT sector + +%define root_dir_start PARAMS+0x6 ; first root directory sector + +%define data_start PARAMS+0x0a ; first data sector + + +;----------------------------------------------------------------------- +; ENTRY +;----------------------------------------------------------------------- + +real_start: + cli + cld + xor ax, ax + mov ds, ax + mov bp, BASE + + + ; a reset should not be needed here +; int 0x13 ; reset drive + +; int 0x12 ; get memory available in AX +; mov ax, 0x01e0 +; mov cl, 6 ; move boot sector to higher memory +; shl ax, cl +; sub ax, 0x07e0 + + mov ax, 0x1FE0 + mov es, ax + mov si, bp + mov di, bp + mov cx, 0x0100 + rep movsw + jmp word 0x1FE0:cont + +loadseg_off dw 0 +loadseg_seg dw LOADSEG + +cont: + mov ds, ax + mov ss, ax + lea sp, [bp-0x60] + sti +; +; Note: some BIOS implementations may not correctly pass drive number +; in DL, however we work around this in SYS.COM by NOP'ing out the use of DL +; (formerly we checked for [drive]==0xff; update sys.c if code moves) +; + mov [drive], dl ; rely on BIOS drive number in DL + + mov LBA_SIZE, 10h + mov LBA_SECNUM,1 ; initialise LBA packet constants + mov word [LBA_SEG],ds + mov word [LBA_OFF],READBUF ; GETDRIVEPARMS: Calculate start of some disk areas. ; - -%macro GETDRIVEPARMS 0 mov si, word [nHidden] mov di, word [nHidden+2] add si, word [bsResSectors] @@ -126,100 +209,14 @@ Entry: jmp short real_start xor dx, dx div bx - mov word [RootDirSecs], ax ; AX = sectors per root directory +; mov word [RootDirSecs], ax ; AX = sectors per root directory + push ax add si, ax adc di, byte 0 ; DI:SI = first data sector mov [data_start], si mov [data_start+2], di -%endmacro - -;----------------------------------------------------------------------- - - times 0x3E-$+$$ db 0 - -%define loadsegoff_60 bp+loadseg_off-Entry -%define loadseg_60 bp+loadseg_seg-Entry - -;%define LBA_PACKET bp+0x42 -; db 10h ; size of packet -; db 0 ; const -; dw 1 ; number of sectors to read -%define LBA_PACKET bp-0x40 -%define LBA_SIZE word [LBA_PACKET] -%define LBA_SECNUM word [LBA_PACKET+2] -%define LBA_OFF LBA_PACKET+4 -%define LBA_SEG LBA_PACKET+6 -%define LBA_SECTOR_0 word [LBA_PACKET+8 ] -%define LBA_SECTOR_16 word [LBA_PACKET+10] -%define LBA_SECTOR_32 word [LBA_PACKET+12] -%define LBA_SECTOR_48 word [LBA_PACKET+14] - - - -%define PARAMS LBA_PACKET+0x10 -%define RootDirSecs PARAMS+0x0 ; # of sectors root dir uses - -%define fat_start PARAMS+0x2 ; first FAT sector - -%define root_dir_start PARAMS+0x6 ; first root directory sector - -%define data_start PARAMS+0x0a ; first data sector - - -;----------------------------------------------------------------------- -; ENTRY -;----------------------------------------------------------------------- - -real_start: - cli - cld - xor ax, ax - mov ds, ax - mov bp, 0x7c00 - - - ; a reset should not be needed here -; int 0x13 ; reset drive - -; int 0x12 ; get memory available in AX -; mov ax, 0x01e0 -; mov cl, 6 ; move boot sector to higher memory -; shl ax, cl -; sub ax, 0x07e0 - - mov ax, 0x1FE0 - mov es, ax - mov si, bp - mov di, bp - mov cx, 0x0100 - rep movsw - jmp word 0x1FE0:cont - -loadseg_off dw 0 -loadseg_seg dw LOADSEG - -cont: - mov ds, ax - mov ss, ax - lea sp, [bp-0x60] - sti -; -; Some BIOS don't pass drive number in DL, so don't use it if [drive] is known -; - cmp byte [drive], 0xff ; impossible number written by SYS - jne dont_use_dl ; was SYS drive: other than A or B? - mov [drive], dl ; yes, rely on BIOS drive number in DL -dont_use_dl: ; no, rely on [drive] written by SYS - - mov LBA_SIZE, 10h - mov LBA_SECNUM,1 ; initialise LBA packet constants - - call print - db "FreeDOS",0 - - GETDRIVEPARMS ; FINDFILE: Searches for the file in the root directory. @@ -232,15 +229,13 @@ dont_use_dl: ; no, rely on [drive] written by SYS mov ax, word [root_dir_start] mov dx, word [root_dir_start+2] - mov di, word [RootDirSecs] + pop di ; mov di, word [RootDirSecs] les bx, [loadsegoff_60] ; es:bx = 60:0 call readDisk - jc jmp_boot_error - - les di, [loadsegoff_60] ; es:di = 60:0 + les di, [loadsegoff_60] ; es:di = 60:0 - ; Search for KERNEL.SYS file name, and find start cluster. + ; Search for KERNEL.SYS file name, and find start cluster. next_entry: mov cx, 11 mov si, filename @@ -251,17 +246,13 @@ next_entry: mov cx, 11 je ffDone add di, byte 0x20 ; go to next directory entry - cmp byte [es:di], 0 ; if the first byte of the name is 0, - jnz next_entry ; there is no more files in the directory + cmp byte [es:di], 0 ; if the first byte of the name is 0, + jnz next_entry ; there is no more files in the directory - jc boot_error ; fail if not found + jc boot_error ; fail if not found ffDone: push ax ; store first cluster number -; call print -; db " FAT",0 - - ; GETFATCHAIN: ; @@ -271,25 +262,21 @@ ffDone: ; ; The file must fit in conventional memory, so it can't be larger than ; 640 kb. The sector size must be at least 512 bytes, so the FAT chain -; can't be larger than around 3 kb. +; can't be larger than 2.5 KB (655360 / 512 * 2 = 2560). ; ; Call with: AX = first cluster in chain - ; Load the complete FAT into memory. The FAT can't be larger - ; than 128 kb, so it should fit in the temporary buffer. - - les bx, [loadsegoff_60] ; es:bx=60:0 + les bx, [loadsegoff_60] ; es:bx=60:0 mov di, [sectPerFat] mov ax, word [fat_start] mov dx, word [fat_start+2] call readDisk pop ax ; restore first cluster number -jmp_boot_error: jc boot_error ; Set ES:DI to the temporary storage for the FAT chain. push ds pop es - mov ds, [loadseg_60] + mov ds, [loadseg_60] mov di, FATBUF next_clust: stosw ; store cluster number @@ -309,8 +296,8 @@ fat_12: add si, si ; multiply cluster number by 3... ; the number was odd, CF was set in the last shift instruction. jnc fat_even - mov cl, 4 - shr ax, cl + mov cl, 4 + shr ax, cl fat_even: and ah, 0x0f ; mask off the highest 4 bits cmp ax, 0x0ff8 ; check for EOF @@ -342,9 +329,6 @@ finished: ; Mark end of FAT chain with 0, so we have a single push cs pop ds - ;call print - ;db " Kernel",0 ; "KERNEL" - ; loadFile: Loads the file into memory, one cluster at a time. @@ -353,10 +337,12 @@ finished: ; Mark end of FAT chain with 0, so we have a single mov si, FATBUF ; set DS:SI to the FAT chain cluster_next: lodsw ; AX = next cluster to read - or ax, ax ; if EOF... - je boot_success ; ...boot was successful + or ax, ax ; EOF? + jne load_next ; no, continue + mov bl,dl ; drive (left from readDisk) + jmp far [loadsegoff_60] ; yes, pass control to kernel - dec ax ; cluster numbers start with 2 +load_next: dec ax ; cluster numbers start with 2 dec ax mov di, word [bsSecPerClust] @@ -365,35 +351,27 @@ cluster_next: lodsw ; AX = next cluster to read add ax, [data_start] adc dx, [data_start+2] ; DX:AX = first sector to read call readDisk - jnc cluster_next + jmp short cluster_next +; shows text after the call to this function. -boot_error: call print - db " err",0 +show: pop si + lodsb ; get character + push si ; stack up potential return address + mov ah,0x0E ; show character + int 0x10 ; via "TTY" mode + cmp al,'.' ; end of string? + jne show ; until done + ret - xor ah,ah - int 0x16 ; wait for a key - int 0x19 ; reboot the machine +boot_error: call show +; db "Error! Hit a key to reboot." + db "Error!." -boot_success: - ;call print - ;db " GO! ",0 - mov bl, [drive] - jmp far [loadsegoff_60] - - -; prints text after call to this function. - -print_1char: - xor bx, bx ; video page 0 - mov ah, 0x0E ; else print it - int 0x10 ; via TTY mode -print: pop si ; this is the first character -print1: lodsb ; get token - push si ; stack up potential return address - cmp al, 0 ; end of string? - jne print_1char ; until done - ret ; and jump to it + xor ah,ah + int 0x13 ; reset floppy + int 0x16 ; wait for a key + int 0x19 ; reboot the machine ; readDisk: Reads a number of sectors into memory. @@ -401,58 +379,59 @@ print1: lodsb ; get token ; Call with: DX:AX = 32-bit DOS sector number ; DI = number of sectors to read ; ES:BX = destination buffer -; ES must be 64k aligned (1000h, 2000h etc). ; ; Returns: CF set on error ; ES:BX points one byte after the last byte read. readDisk: push si - mov LBA_SECTOR_0,ax - mov LBA_SECTOR_16,dx - mov word [LBA_SEG],es - mov word [LBA_OFF],bx + mov LBA_SECTOR_0,ax + mov LBA_SECTOR_16,dx + mov word [READADDR_SEG], es + mov word [READADDR_OFF], bx + call show + db "." read_next: ;******************** LBA_READ ******************************* - ; check for LBA support - - mov ah,041h ; - mov bx,055aah ; + ; check for LBA support + + mov ah,041h ; + mov bx,055aah ; mov dl, [drive] - test dl,dl ; don't use LBA addressing on A: - jz read_normal_BIOS ; might be a (buggy) - ; CDROM-BOOT floppy emulation + test dl,dl ; don't use LBA addressing on A: + jz read_normal_BIOS ; might be a (buggy) + ; CDROM-BOOT floppy emulation int 0x13 - jc read_normal_BIOS + jc read_normal_BIOS - shr cx,1 ; CX must have 1 bit set + shr cx,1 ; CX must have 1 bit set - sbb bx,0aa55h - 1 ; tests for carry (from shr) too! - jne read_normal_BIOS + sbb bx,0aa55h - 1 ; tests for carry (from shr) too! + jne read_normal_BIOS - - ; OK, drive seems to support LBA addressing + + ; OK, drive seems to support LBA addressing - lea si,[LBA_PACKET] + lea si,[LBA_PACKET] - ; setup LBA disk block - mov LBA_SECTOR_32,bx - mov LBA_SECTOR_48,bx - - mov ah,042h + ; setup LBA disk block + mov LBA_SECTOR_32,bx ; bx is 0 if extended 13h mode supported + mov LBA_SECTOR_48,bx + + mov ah,042h jmp short do_int13_read - + read_normal_BIOS: ;******************** END OF LBA_READ ************************ - mov cx,LBA_SECTOR_0 - mov dx,LBA_SECTOR_16 + mov cx,LBA_SECTOR_0 + mov dx,LBA_SECTOR_16 ; @@ -487,35 +466,38 @@ read_normal_BIOS: xchg ch, cl ; set cyl no low 8 bits ror cl, 1 ; move track high bits into ror cl, 1 ; bits 7-6 (assumes top = 0) - mov al, byte [sectPerTrack] - sub al, ah ; al has # of sectors left - inc ah ; sector offset from 1 or cl, ah ; merge sector into cylinder + inc cx ; make sector 1-based (1-63) - les bx,[LBA_OFF] + les bx,[LBA_OFF] mov ax, 0x0201 do_int13_read: mov dl, [drive] int 0x13 + jc boot_error ; exit on error -read_finished: - jnc read_ok ; jump if no error - xor ah, ah ; else, reset floppy - int 0x13 -read_next_chained: - jmp short read_next ; read the same sector again + mov ax, word [bsBytesPerSec] -read_ok: - mov ax, word [bsBytesPerSec] - div byte[LBA_PACKET] ; luckily 16 !! - add word [LBA_SEG], ax + push di + mov si,READBUF ; copy read in sector data to + les di,[READADDR_OFF] ; user provided buffer + mov cx, ax +; shr cx, 1 ; convert bytes to word count +; rep movsw + rep movsb + pop di + +; div byte[LBA_PACKET] ; luckily 16 !! + mov cl, 4 + shr ax, cl ; adjust segment pointer by increasing + add word [READADDR_SEG], ax ; by paragraphs read in (per sector) add LBA_SECTOR_0, byte 1 adc LBA_SECTOR_16, byte 0 ; DX:AX = next sector to read dec di ; if there is anything left to read, - jnz read_next_chained ; continue + jnz read_next ; continue - mov es,word [LBA_SEG] + les bx, [READADDR_OFF] ; clear carry: unnecessary since adc clears it pop si ret @@ -525,3 +507,32 @@ read_ok: filename db "KERNEL SYS",0,0 sign dw 0xAA55 + +%ifdef DBGPRNNUM +; DEBUG print hex digit routines +PrintLowNibble: ; Prints low nibble of AL, AX is destroyed + and AL, 0Fh ; ignore upper nibble + cmp AL, 09h ; if greater than 9, then don't base on '0', base on 'A' + jbe .printme + add AL, 7 ; convert to character A-F + .printme: + add AL, '0' ; convert to character 0-9 + mov AH,0x0E ; show character + int 0x10 ; via "TTY" mode + retn +PrintAL: ; Prints AL, AX is preserved + push AX ; store value so we can process a nibble at a time + shr AL, 4 ; move upper nibble into lower nibble + call PrintLowNibble + pop AX ; restore for other nibble + push AX ; but save so we can restore original AX + call PrintLowNibble + pop AX ; restore for other nibble + retn +PrintNumber: ; Prints (in Hex) value in AX, AX is preserved + xchg AH, AL ; high byte 1st + call PrintAL + xchg AH, AL ; now low byte + call PrintAL + retn +%endif diff --git a/boot/makefile b/boot/makefile index 0299d17..ebd3ef6 100644 --- a/boot/makefile +++ b/boot/makefile @@ -1,13 +1,11 @@ # # makefile for DOS-C boot # -# $Id$ -# !include "../mkfiles/generic.mak" -production: fat12com.bin fat16com.bin fat32chs.bin fat32lba.bin +production: fat12com.bin fat16com.bin fat32chs.bin fat32lba.bin oemfat12.bin oemfat16.bin fat12com.bin: boot.asm $(NASM) -dISFAT12 boot.asm -ofat12com.bin @@ -21,6 +19,12 @@ fat32chs.bin: boot32.asm fat32lba.bin: boot32lb.asm $(NASM) boot32lb.asm -ofat32lba.bin +oemfat12.bin: oemboot.asm + $(NASM) -dISFAT12 oemboot.asm -ooemfat12.bin + +oemfat16.bin: oemboot.asm + $(NASM) -dISFAT16 oemboot.asm -ooemfat16.bin + clobber: clean -$(RM) *.bin status.me diff --git a/boot/oemboot.asm b/boot/oemboot.asm new file mode 100644 index 0000000..a57bdbc --- /dev/null +++ b/boot/oemboot.asm @@ -0,0 +1,648 @@ +; +; File: +; oemboot.asm +; 2004, Kenneth J. Davis +; Copyright (c) 200?, +; Description: +; OEM boot sector for FreeDOS compatible with IBM's (R) PC-DOS, +; and Microsoft's (R) MS-DOS. It may work with older OpenDOS/DR-DOS, +; although the standard FreeDOS boot sector is needed with ver 7+ +; releases. May work with other versions of DOS that use +; IBMBIO.COM/IBMDOS.COM pair. This boot sector loads only up +; to 58 sectors (29KB) of the kernel (IBMBIO.COM) to 0x70:0 then +; jumps to it. As best I can tell, PC-DOS (and MS-DOS up to version +; 6.xx behaves similar) expects on entry for: +; ch = media id byte in the boot sector +; dl = BIOS drive booted from (0x00=A:, 0x80=C:, ...) +; ax:bx = the starting (LBA) sector of cluster 2 (ie the 1st +; data sector, which is 0x0000:0021 for FAT12) +; ?note? IBMBIO.COM/IO.SYS may use ax:bx and cluster # stored +; elsewhere (perhaps dir entry still at 0x50:0) to determine +; starting sector for full loading of kernel file. +; it also expects the boot sector (in particular the BPB) +; to still be at 0x0:7C00, the directory entry for IBMBIO.COM +; (generally first entry of first sector of the root directory) +; at 0x50:0 (DOS Data Area). The original boot sector may update +; the floppy disk parameter table (int 1Eh), but we don't so +; may fail for any systems where the changes (???) are needed. +; If the above conditions are not met, then IBMBIO.COM will +; print the not a bootable disk error message. +; +; For MS-DOS >= 7 (ie Win9x DOS) the following conditions +; must be met: +; bp = 0x7C00, ie offset boot sector loaded at +; [bp-4] = the starting (LBA) sector of cluster 2 (ie the 1st +; data sector [this is the same as ax:bx for earlier versions +; and dx:ax in Win9x boot sector] +; The starting cluster of the kernel file is stored in +; di for FAT 12/16 (where si is a don't care) and si:di +; for FAT 32. +; The values for ax,bx,cx,dx,ds and the stack do not +; seem to be important (used by IO.SYS) and so may be any value +; (though dx:ax=[data_start], cx=0, bx=0x0f00 on FAT12 or +; 0x0700 on FAT32, ds=0, ss:sp=0:7b??) + +; the boot time stack may store the original int1E floppy +; parameter table, otherwise nothing else important seems +; stored there and I am unsure if even this value is used +; beyond boot sector code. + +; +; This boot sector only supports FAT12/FAT16 as PC-DOS +; does not support FAT32 and newer FAT32 capable DOSes +; probably have different boot requirements; also do NOT +; use it to boot the FreeDOS kernel as it expects to be +; fully loaded by boot sector (> 29KB & usually to 0x60:0). +; +; WARNING: PC-DOS has additional requirements, in particular, +; it may expect that IBMBIO.COM and IBMDOS.COM be the 1st +; two entries in the root directory (even before the label) +; and that they occupy the 1st consecutive data sectors. +; Newer releases may support other positions, but still +; generally should occupy consecutive sectors. These conditions +; can usually be met by running sys on a freshly formatted +; and un-label'd disk. +; +; +; Derived From: +; boot.asm +; DOS-C boot +; +; Copyright (c) 1997, 2000-2004 +; Svante Frey, Jim Hall, Jim Tabor, Bart Oldeman, +; Tom Ehlert, Eric Auer, Luchezar Georgiev, Jon Gentle +; and Michal H. Tyc (DR-DOS adaptation, boot26dr.asm) +; All Rights Reserved +; +; This file is part of FreeDOS. +; +; DOS-C 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; either version +; 2, or (at your option) any later version. +; +; DOS-C is distributed in the hope that it will be useful, but +; WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +; the GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public +; License along with DOS-C; see the file COPYING. If not, +; write to the Free Software Foundation, 675 Mass Ave, +; Cambridge, MA 02139, USA. +; +; +; +--------+ +; | CLUSTER| +; | LIST | +; |--------| 0000:7F00 +; |LBA PKT | +; |--------| 0000:7E00 (0:BP+200) +; |BOOT SEC| contains BPB +; |ORIGIN | +; |--------| 0000:7C00 (0:BP) +; |VARS | only known is 1st data sector (start of cluster 2) +; |--------| 0000:7BFC (DS:[BP-4]) +; |STACK | minimal 256 bytes (1/2 sector) +; |- - - - | +; |KERNEL | kernel loaded here (max 58 sectors, 29KB) +; |LOADED | also used as FAT buffer +; |--------| 0070:0000 (0:0700) +; |DOS DA/ | DOS Data Area, +; |ROOT DIR| during boot contains directory entries +; |--------| 0000:0500 +; |BDA | BIOS Data Area +; +--------+ 0000:0400 +; |IVT | Interrupt Vector Table +; +--------+ 0000:0000 + +CPU 8086 ; enable assembler warnings to limit instruction set + +;%define ISFAT12 1 ; only 1 of these should be set, +;%define ISFAT16 1 ; defines which FAT is supported + +%define TRYLBAREAD 1 ; undefine to use only CHS int 13h +%define SETROOTDIR 1 ; if defined dir entry copied to 0:500 +%define LOOPONERR 1 ; if defined on error simply loop forever +;%define RETRYALWAYS 1 ; if defined retries read forever +;%define WINBOOT 1 ; use win9x kernel calling conventions (name & jmp addr) +;%define MSCOMPAT 1 ; sets default filename to MSDOS IO.SYS + +%ifdef WINBOOT ; if set also change from PC-DOS to +%ifndef MSCOMPAT ; kernel name to MS-DOS kernel name +%define MSCOMPAT +%endif +%endif + +segment .text + +%define BASE 0x7c00 ; boot sector originally at 0x0:BASE +%define LOADSEG 0x0070 ; segment to load kernel at LOADSEG:0 +%define LOADEND 0x07b0 ; limit reads to below this segment + ; LOADSEG+29KB, else data overwritten + +%define FATBUF bp-0x7500 ; offset of temporary buffer for FAT + ; chain 0:FATBUF = 0:0700 = LOADSEG:0 +%define ROOTDIR bp-0x7700 ; offset to buffer for root directory + ; entry of kernel 0:ROOTDIR +%define CLUSTLIST bp+0x0300 ; zero terminated list of clusters + ; that the kernel occupies + +; Some extra variables +; using bp-Entry+variable_name generates smaller code than using just +; variable_name, where bp is initialized to Entry, so bp-Entry equals 0 + +%define LBA_PACKET bp+0x0200 ; immediately after boot sector +%define LBA_SIZE word [LBA_PACKET] ; size of packet, should be 10h +%define LBA_SECNUM word [LBA_PACKET+2] ; number of sectors to read +%define LBA_OFF LBA_PACKET+4 ; buffer to read/write to +%define LBA_SEG LBA_PACKET+6 +%define LBA_SECTOR_0 word [LBA_PACKET+8 ] ; LBA starting sector # +%define LBA_SECTOR_16 word [LBA_PACKET+10] +%define LBA_SECTOR_32 word [LBA_PACKET+12] +%define LBA_SECTOR_48 word [LBA_PACKET+14] + +%define PARAMS LBA_PACKET+0x10 +;%define RootDirSecs PARAMS+0x0 ; # of sectors root dir uses +%define fat_start PARAMS+0x2 ; first FAT sector +;%define root_dir_start PARAMS+0x6 ; first root directory sector +%define first_cluster PARAMS+0x0a ; starting cluster of kernel file +%define data_start bp-4 ; first data sector (win9x expects here) + +;----------------------------------------------------------------------- + + + org BASE + +Entry: jmp short real_start + nop + +; bp is initialized to 7c00h +%define bsOemName bp+0x03 ; OEM label +%define bsBytesPerSec bp+0x0b ; bytes/sector +%define bsSecPerClust bp+0x0d ; sectors/allocation unit +%define bsResSectors bp+0x0e ; # reserved sectors +%define bsFATs bp+0x10 ; # of fats +%define bsRootDirEnts bp+0x11 ; # of root dir entries +%define bsSectors bp+0x13 ; # sectors total in image +%define bsMedia bp+0x15 ; media descrip: fd=2side9sec, etc... +%define sectPerFat bp+0x16 ; # sectors in a fat +%define sectPerTrack bp+0x18 ; # sectors/track +%define nHeads bp+0x1a ; # heads +%define nHidden bp+0x1c ; # hidden sectors +%define nSectorHuge bp+0x20 ; # sectors if > 65536 +%define drive bp+0x24 ; drive number +%define extBoot bp+0x26 ; extended boot signature +%define volid bp+0x27 +%define vollabel bp+0x2b +%define filesys bp+0x36 + + +;----------------------------------------------------------------------- + +; times 0x3E-$+$$ db 0 +; +; Instead of zero-fill, +; initialize BPB with values suitable for a 1440 K floppy +; + db 'IBM 5.0' ; OEM label + 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 * 36 ; total sectors on disk + db 0xF0 ; media descriptor + dw 9 ; sectors per 1 FAT copy + dw 18 ; sectors per track + dw 2 ; number of heads + dd 0 ; hidden sectors + dd 0 ; big total sectors + db 0 ; boot unit + db 0 ; reserved + db 0x29 ; extended boot record id + dd 0x12345678 ; volume serial number + db 'NO NAME '; volume label + db 'FAT12 ' ; filesystem id + +;----------------------------------------------------------------------- +; ENTRY +;----------------------------------------------------------------------- + +real_start: + cli ; disable interrupts until stack ready + cld ; all string operations increment + xor ax, ax ; ensure our segment registers ready + mov ds, ax ; cs=ds=es=ss=0x0000 + mov es, ax + mov ss, ax + mov bp, BASE + lea sp, [bp-4] ; for DOS <7 this may be [bp] + +; For compatibility, diskette parameter vector updated. +; lea di [bp+0x3E] ; use 7c3e([bp+3e]) for PC-DOS, +; ;lea di [bp] ; but 7c00([bp]) for DR-DOS 7 bug +; mov bx, 4 * 1eh ; stored at int 1E's vector +; lds si, [bx] ; fetch current int 1eh pointer +; push ds ; store original 1eh pointer at stack top +; push si ; so can restore later if needed +; +; Copy table to new location +; mov cl, 11 ; the parameter table is 11 bytes +; rep movsb ; and copy the parameter block +; mov ds, ax ; restore DS +; +; Note: make desired changes to table here +; +; Update int1E to new location +; mov [bx+2], 0 ; set to 0:bp or 0:bp+3e as appropriate +; mov word [bx], 0x7c3e ; (use 0x7c00 for DR-DOS) + + sti ; enable interrupts + +; If updated floppy parameter table then must notify BIOS +; Otherwise a reset should not be needed here. +; int 0x13 ; reset drive (AX=0) + +; +; Note: some BIOS implementations may not correctly pass drive number +; in DL, however we work around this in SYS.COM by NOP'ing out the use of DL +; (formerly we checked for [drive]==0xff; update sys.c if code moves) +; + mov [drive], dl ; rely on BIOS drive number in DL + + +; GETDRIVEPARMS: Calculate start of some disk areas. +; + mov si, word [nHidden] + mov di, word [nHidden+2] + add si, word [bsResSectors] + adc di, byte 0 ; DI:SI = first FAT sector + + mov word [fat_start], si + mov word [fat_start+2], di + + mov al, [bsFATs] + cbw + mul word [sectPerFat] ; DX:AX = total number of FAT sectors + + add si, ax + adc di, dx ; DI:SI = first root directory sector + push di ; mov word [root_dir_start+2], di + push si ; mov word [root_dir_start], si + + ; Calculate how many sectors the root directory occupies. + mov bx, [bsBytesPerSec] + mov cl, 5 ; divide BX by 32 + shr bx, cl ; BX = directory entries per sector + + mov ax, [bsRootDirEnts] + xor dx, dx + div bx ; set AX = sectors per root directory + push ax ; mov word [RootDirSecs], ax + + add si, ax + adc di, byte 0 ; DI:SI = first data sector + + mov [data_start], si + mov [data_start+2], di + + +; FINDFILE: Searches for the file in the root directory. +; +; Returns: +; AX = first cluster of file + + ; First, read the root directory into buffer. + ; into the temporary buffer. (max 29KB or overruns stuff) + + pop di ; mov di, word [RootDirSecs] + pop ax ; mov ax, word [root_dir_start] + pop dx ; mov dx, word [root_dir_start+2] + lea bx, [ROOTDIR] ; es:bx = 0:0500 + push es ; save pointer to ROOTDIR + call readDisk + pop es ; restore pointer to ROOTDIR + lea si, [ROOTDIR] ; ds:si = 0:0500 + + + ; Search for kernel file name, and find start cluster. + +next_entry: mov cx, 11 + mov di, filename + push si + repe cmpsb + pop si + mov ax, [si+0x1A]; get cluster number from directory entry + je ffDone + + add si, byte 0x20 ; go to next directory entry + jc boot_error ; fail if not found and si wraps + cmp byte [si], 0 ; if the first byte of the name is 0, + jnz next_entry ; there are no more files in the directory + +ffDone: + mov [first_cluster], ax ; store first cluster number + +%ifdef SETROOTDIR + ; copy over this portion of root dir to 0x0:500 for PC-DOS + ; (this may allow IBMBIO.COM to start in any directory entry) + lea di, [ROOTDIR] ; es:di = 0:0500 + mov cx, 32 ; limit to this 1 entry (rest don't matter) + rep movsw +%endif + +; GETFATCHAIN: +; +; Reads the FAT chain and stores it in a temporary buffer in the first +; 64 kb. The FAT chain is stored an array of 16-bit cluster numbers, +; ending with 0. +; +; The file must fit in conventional memory, so it can't be larger than +; 640 kb. The sector size must be at least 512 bytes, so the FAT chain +; can't be larger than 2.5 KB (655360 / 512 * 2 = 2560). +; +; Call with: AX = first cluster in chain + + ; Load the complete FAT into memory. The FAT can't be larger + ; than 128 kb + lea bx, [FATBUF] ; es:bx = 0:0700 + mov di, [sectPerFat] + mov ax, word [fat_start] + mov dx, word [fat_start+2] + call readDisk + + ; Set ES:DI to the temporary storage for the FAT chain. + push ds + pop es + lea di, [CLUSTLIST] + ; Set DS:0 to FAT data we loaded + mov ax, LOADSEG + mov ds, ax ; ds:0 = 0x70:0 = 0:FATBUF + + mov ax, [first_cluster] ; restore first cluster number + push ds ; store LOADSEG + +next_clust: stosw ; store cluster number + mov si, ax ; SI = cluster number + +%ifdef ISFAT12 + ; This is a FAT-12 disk. + +fat_12: add si, si ; multiply cluster number by 3... + add si, ax + shr si, 1 ; ...and divide by 2 + lodsw + + ; If the cluster number was even, the cluster value is now in + ; bits 0-11 of AX. If the cluster number was odd, the cluster + ; value is in bits 4-15, and must be shifted right 4 bits. If + ; the number was odd, CF was set in the last shift instruction. + + jnc fat_even + mov cl, 4 + shr ax, cl + +fat_even: and ah, 0x0f ; mask off the highest 4 bits + cmp ax, 0x0ff8 ; check for EOF + jb next_clust ; continue if not EOF + +%endif +%ifdef ISFAT16 + ; This is a FAT-16 disk. The maximal size of a 16-bit FAT + ; is 128 kb, so it may not fit within a single 64 kb segment. + +fat_16: mov dx, LOADSEG + add si, si ; multiply cluster number by two + jnc first_half ; if overflow... + add dh, 0x10 ; ...add 64 kb to segment value + +first_half: mov ds, dx ; DS:SI = pointer to next cluster + lodsw ; AX = next cluster + + cmp ax, 0xfff8 ; >= FFF8 = 16-bit EOF + jb next_clust ; continue if not EOF +%endif + +finished: ; Mark end of FAT chain with 0, so we have a single + ; EOF marker for both FAT-12 and FAT-16 systems. + + xor ax, ax + stosw + + push cs + pop ds + + +; loadFile: Loads the file into memory, one cluster at a time. + + pop es ; set ES:BX to load address 70:0 + xor bx, bx + + lea si, [CLUSTLIST] ; set DS:SI to the FAT chain + +cluster_next: lodsw ; AX = next cluster to read + or ax, ax ; EOF? + jne load_next ; no, continue + + ; dl set to drive by readDisk + mov ch, [bsMedia] ; ch set to media id + mov ax, [data_start+2] ; ax:bx set to 1st data sector + mov bx, [data_start] ; + mov di, [first_cluster] ; set di (si:di on FAT32) to starting cluster # +%ifdef WINBOOT + jmp LOADSEG:0x0200 ; yes, pass control to kernel +%else + jmp LOADSEG:0000 ; yes, pass control to kernel +%endif + + +; failed to boot +boot_error: +call show +; db "Error! Hit a key to reboot." + db "):." +%ifdef LOOPONERR +jmp $ +%else + + ; Note: should restore floppy paramater table address at int 0x1E + xor ah,ah + int 0x13 ; reset floppy + int 0x16 ; wait for a key + int 0x19 ; reboot the machine +%endif + + +load_next: dec ax ; cluster numbers start with 2 + dec ax + + mov di, word [bsSecPerClust] + and di, 0xff ; DI = sectors per cluster + mul di + add ax, [data_start] + adc dx, [data_start+2] ; DX:AX = first sector to read + call readDisk + jmp short cluster_next + + +; shows text after the call to this function. + +show: pop si + lodsb ; get character + push si ; stack up potential return address + mov ah,0x0E ; show character + int 0x10 ; via "TTY" mode + cmp al,'.' ; end of string? + jne show ; until done + ret + + +; readDisk: Reads a number of sectors into memory. +; +; Call with: DX:AX = 32-bit DOS sector number +; DI = number of sectors to read +; ES:BX = destination buffer +; +; Returns: CF set on error +; ES:BX points one byte after the last byte read. +; Exits early if LBA_SEG == LOADEND. + +readDisk: push si ; preserve cluster # + + mov LBA_SECTOR_0,ax + mov LBA_SECTOR_16,dx + mov word [LBA_SEG], es + mov word [LBA_OFF], bx + + call show + db "." +read_next: + +; initialize constants + mov LBA_SIZE, 10h ; LBA packet is 16 bytes + mov LBA_SECNUM,1 ; reset LBA count if error + +; limit kernel loading to 29KB, preventing stack & boot sector being overwritten + cmp word [LBA_SEG], LOADEND ; skip reading if past the end + je read_skip ; of kernel file buffer + +;******************** LBA_READ ******************************* + + ; check for LBA support + +%ifdef TRYLBAREAD + mov ah,041h ; + mov bx,055aah ; + mov dl, [drive] ; BIOS drive, 0=A:, 80=C: + test dl,dl ; don't use LBA addressing on A: + jz read_normal_BIOS ; might be a (buggy) + ; CDROM-BOOT floppy emulation + int 0x13 + jc read_normal_BIOS + + shr cx,1 ; CX must have 1 bit set + + sbb bx,0aa55h - 1 ; tests for carry (from shr) too! + jne read_normal_BIOS + ; OK, drive seems to support LBA addressing + lea si,[LBA_PACKET] + ; setup LBA disk block + mov LBA_SECTOR_32,bx ; bx is 0 if extended 13h mode supported + mov LBA_SECTOR_48,bx + + + mov ah,042h + jmp short do_int13_read +%endif + + + +read_normal_BIOS: + +;******************** END OF LBA_READ ************************ + mov cx, LBA_SECTOR_0 + mov dx, LBA_SECTOR_16 + + ; + ; translate sector number to BIOS parameters + ; + ; + ; abs = sector offset in track + ; + head * sectPerTrack offset in cylinder + ; + track * sectPerTrack * nHeads offset in platter + ; + mov al, [sectPerTrack] + mul byte [nHeads] + xchg ax, cx + ; cx = nHeads * sectPerTrack <= 255*63 + ; dx:ax = abs + div cx + ; ax = track, dx = sector + head * sectPertrack + xchg ax, dx + ; dx = track, ax = sector + head * sectPertrack + div byte [sectPerTrack] + ; dx = track, al = head, ah = sector + mov cx, dx + ; cx = track, al = head, ah = sector + + ; the following manipulations are necessary in order to + ; properly place parameters into registers. + ; ch = cylinder number low 8 bits + ; cl = 7-6: cylinder high two bits + ; 5-0: sector + mov dh, al ; save head into dh for bios + xchg ch, cl ; set cyl no low 8 bits + ror cl, 1 ; move track high bits into + ror cl, 1 ; bits 7-6 (assumes top = 0) + or cl, ah ; merge sector into cylinder + inc cx ; make sector 1-based (1-63) + + les bx,[LBA_OFF] + mov ax, 0x0201 +do_int13_read: + mov dl, [drive] + int 0x13 + +read_finished: +%ifdef RETRYALWAYS + jnc read_ok ; jump if no error + xor ah, ah ; else, reset floppy + int 0x13 +read_next_chained: + jmp short read_next ; read the same sector again +%else + jc boot_error ; exit on error +%endif + +read_ok: + mov ax, word [bsBytesPerSec] + mov cl, 4 ; adjust segment pointer by increasing + shr ax, cl + add word [LBA_SEG], ax ; by paragraphs read in (per sector) + + add LBA_SECTOR_0, byte 1 + adc LBA_SECTOR_16, byte 0 ; DX:AX = next sector to read + dec di ; if there is anything left to read, +%ifdef RETRYALWAYS + jnz read_next_chained ; continue +%else + jnz read_next ; continue +%endif + +read_skip: + mov es, word [LBA_SEG] ; load adjusted segment value + ; clear carry: unnecessary since adc clears it + pop si + ret + + times 0x01f1-$+$$ db 0 +%ifdef MSCOMPAT +filename db "IO SYS" +%else +filename db "IBMBIO COM" +%endif + db 0,0 + +sign dw 0xAA55 + diff --git a/build.bat b/build.bat index c35eb1f..4c70db2 100644 --- a/build.bat +++ b/build.bat @@ -65,9 +65,6 @@ if "%1" == "/D" goto setDefine shift if not "%1" == "" goto loop_commandline -if "%COMPILER%" == "" echo you MUST define a COMPILER variable in CONFIG.BAT -if "%COMPILER%" == "" goto abort - call default.bat :-if "%LAST%" == "" goto noenv diff --git a/default.bat b/default.bat index 641c481..0e1dc54 100644 --- a/default.bat +++ b/default.bat @@ -8,6 +8,16 @@ if "%1" == "clearset" goto clearset :----------------------------------------------------------------------- +if not "%COMPILER%" == "" goto skip_cc + +set COMPILER=WATCOM + +echo No compiler specified, defaulting to Open Watcom + +:skip_cc + +:----------------------------------------------------------------------- + if not "%MAKE%" == "" goto skip_make if "%COMPILER%" == "TC2" set MAKE=%TC2_BASE%\make diff --git a/hdr/debug.h b/hdr/debug.h new file mode 100644 index 0000000..4d063fa --- /dev/null +++ b/hdr/debug.h @@ -0,0 +1,145 @@ +/****************************************************************/ +/* */ +/* debug.h */ +/* */ +/* Routines to assist in debugging the kernel */ +/* */ +/* January, 2005 */ +/* */ +/* Copyright (c) 2005 */ +/* FreeDOS kernel dev. */ +/* All Rights Reserved */ +/* */ +/* This file is part of DOS-C. */ +/* */ +/* DOS-C 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; either version */ +/* 2, or (at your option) any later version. */ +/* */ +/* DOS-C is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */ +/* the GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public */ +/* License along with DOS-C; see the file COPYING. If not, */ +/* write to the Free Software Foundation, 675 Mass Ave, */ +/* Cambridge, MA 02139, USA. */ +/****************************************************************/ + +#ifndef __DEBUG_H +#define __DEBUG_H + +/* #define DEBUG (usually via 'build debug') to + enable debug support. + NOTE: this file included by INIT time code and normal + resident code, so use care for all memory references + */ + +/* allow output even in non-debug builds */ +#if 0 +#ifndef DEBUG_NEED_PRINTF +#define DEBUG_NEED_PRINTF +#endif +#endif + +/* use to limit output to debug builds */ +#ifdef DEBUG +#ifdef DEBUG_PRINT_COMPORT +#define DebugPrintf(x) dbgc_printf x +#else +#define DebugPrintf(x) printf x +#endif +#else +#define DebugPrintf(x) +#endif + +/* use to disable a chunk of debug output, but + keep around for later use. */ +#define DDebugPrintf(x) + + +/* enable or disable various chunks of debug output */ + +/* show stored IRQ vectors */ +/* #define DEBUGIRQ */ + +/* show output related to moving kernel into HMA */ +#ifdef DEBUG +#define HMAInitPrintf(x) DebugPrintf(x) +#else +#define HMAInitPrintf(x) +#endif + +/* display output during kernel config processing phase */ +/* #define DEBUGCFG */ +#ifdef DEBUGCFG +#define CfgDbgPrintf(x) DebugPrintf(x) +#else +#define CfgDbgPrintf(x) +#endif + +/* display info on various DOS functions (dosfns.c) */ +/* #define DEBUGDOSFNS */ +#ifdef DEBUGDOSFNS +#define DFnsDbgPrintf(x) DebugPrintf(x) +#else +#define DFnsDbgPrintf(x) +#endif + +/* extra debug output related to chdir */ +/* #define CHDIR_DEBUG */ + +/* extra debug output related to findfirst */ +/* #define FIND_DEBUG */ + +/* display info on various DOS directory functions (fatdir.c) */ +/* #define DEBUGFATDIR */ +#ifdef DEBUGFATDIR +#define FDirDbgPrintf(x) DebugPrintf(x) +#else +#define FDirDbgPrintf(x) +#endif + +/* extra debug output when transferring I/O chunks of data */ +/* #define DISPLAY_GETBLOCK */ + +/* extra output during read/write operations */ +/* #define DSK_DEBUG */ + +/* display info on various FAT handling functions (fatfs.c) */ +/* #define DEBUGFATFS */ +#ifdef DEBUGFATFS +#define FatFSDbgPrintf(x) DebugPrintf(x) +#else +#define FatFSDbgPrintf(x) +#endif + +/* debug truename */ +/* #define DEBUG_TRUENAME */ +#ifdef DEBUG_TRUENAME +#define tn_printf(x) DebugPrintf(x) +#else +#define tn_printf(x) +#endif + + +/* ensure printf is prototyped */ +#if defined(DEBUG) || defined(DEBUGIRQ) || defined(DEBUGCFG) || \ + defined(DEBUGDOSFNS) || defined(CHDIR_DEBUG) || defined(FIND_DEBUG) || \ + defined(DEBUGFATDIR) || defined(DEBUGFATFS) || \ + defined(FORCEPRINTF) +#ifndef DEBUG_NEED_PRINTF +#define DEBUG_NEED_PRINTF +#endif +#endif + +#ifdef DEBUG_NEED_PRINTF +int VA_CDECL printf(CONST char FAR * fmt, ...); +#ifdef DEBUG_PRINT_COMPORT +int VA_CDECL dbgc_printf(CONST char FAR * fmt, ...); +#endif +#endif + +#endif /* __DEBUG_H */ diff --git a/kernel/dosfns.c b/kernel/dosfns.c index 578fdf4..adaa90b 100644 --- a/kernel/dosfns.c +++ b/kernel/dosfns.c @@ -837,17 +837,21 @@ COUNT DosGetExtFree(BYTE FAR * DriveString, struct xfreespace FAR * xfsp) struct cds FAR *cdsp; UCOUNT rg[4]; + /* ensure all fields known value - clear reserved bytes & set xfs_version.actual to 0 */ + fmemset(xfsp, 0, sizeof(struct xfreespace)); + xfsp->xfs_datasize = sizeof(struct xfreespace); + /* - DriveString should be in form of "C:", "C:\", "\", - where "\" is treated as a request for the current drive, + DriveString should be in form of "C:", "C:\", "\", "", ., or .\ + where missing drive is treated as a request for the current drive, or network name in form "\\SERVER\share" however, network names like \\SERVER\C aren't supported yet */ cdsp = NULL; - if (DriveString[1] == ':') + if ( !*DriveString || (*DriveString == '.') || (IS_SLASH(DriveString[0]) && !IS_SLASH(DriveString[1])) ) + cdsp = get_cds(default_drive); /* if "" or .[\] or \[path] then use current drive */ + else if (DriveString[1] == ':') cdsp = get_cds(DosUpFChar(*DriveString) - 'A'); /* assume drive specified */ - else if (IS_SLASH(DriveString[0]) && !IS_SLASH(DriveString[1])) - cdsp = get_cds(default_drive); /* use current drive */ if (cdsp == NULL) /* either error, really bad string, or network name */ return DE_INVLDDRV; @@ -879,8 +883,6 @@ COUNT DosGetExtFree(BYTE FAR * DriveString, struct xfreespace FAR * xfsp) xfsp->xfs_freesectors = xfsp->xfs_freeclusters * xfsp->xfs_clussize; xfsp->xfs_datasize = sizeof(struct xfreespace); - fmemset(xfsp->xfs_reserved, 0, 8); - return SUCCESS; } #endif diff --git a/kernel/init-mod.h b/kernel/init-mod.h index 46f74e9..a7a865d 100644 --- a/kernel/init-mod.h +++ b/kernel/init-mod.h @@ -228,8 +228,8 @@ BOOL init_device(struct dhdr FAR * dhp, char * cmdLine, VOID init_fatal(BYTE * err_msg); /* prf.c */ -int VA_CDECL init_printf(const char * fmt, ...); -int VA_CDECL init_sprintf(char * buff, const char * fmt, ...); +int VA_CDECL init_printf(CONST char FAR * fmt, ...); +int VA_CDECL init_sprintf(char * buff, CONST char FAR * fmt, ...); /* procsupt.asm */ VOID ASMCFUNC FAR got_cbreak(void); diff --git a/kernel/initdisk.c b/kernel/initdisk.c index 1dc00ea..673d927 100644 --- a/kernel/initdisk.c +++ b/kernel/initdisk.c @@ -25,12 +25,9 @@ /****************************************************************/ #include "portab.h" +#include "debug.h" #include "init-mod.h" #include "dyndata.h" -#ifdef VERSION_STRINGS -static BYTE *dskRcsId = - "$Id$"; -#endif UBYTE InitDiskTransferBuffer[SEC_SIZE] BSS_INIT({0}); COUNT nUnits BSS_INIT(0); @@ -149,22 +146,6 @@ COUNT nUnits BSS_INIT(0); * CHS or LBA, strengthening its role as a rescue environment. */ -/* #define DEBUG */ - -#define _BETA_ /* messages for initial phase only */ - -#if defined(DEBUG) -#define DebugPrintf(x) printf x -#else -#define DebugPrintf(x) -#endif - -#if defined(_BETA_) -#define BetaPrintf(x) printf x -#else -#define BetaPrintf(x) -#endif - #define LBA_to_CHS init_LBA_to_CHS /* @@ -586,10 +567,7 @@ void DosDefinePartition(struct DriveParamS *driveParam, pddt->ddt_descflags &= ~DF_LBA; pddt->ddt_ncyl = driveParam->chs.Cylinder; -#ifdef DEBUG - if (pddt->ddt_descflags & DF_LBA) - DebugPrintf(("LBA enabled for drive %c:\n", 'A' + nUnits)); -#endif + DebugPrintf(("LBA %senabled for drive %c:\n", (pddt->ddt_descflags & DF_LBA)?"":"not ", 'A' + nUnits)); pddt->ddt_offset = StartSector; diff --git a/kernel/inthndlr.c b/kernel/inthndlr.c index a5187d4..d3e44af 100644 --- a/kernel/inthndlr.c +++ b/kernel/inthndlr.c @@ -224,8 +224,6 @@ int int21_fat32(lregs *r) rc = DosGetExtFree(MK_FP(r->DS, r->DX), xfsp); if (rc != SUCCESS) return rc; - xfsp->xfs_datasize = sizeof(struct xfreespace); - xfsp->xfs_version.actual = 0; break; } /* Set DPB to use for formatting */ diff --git a/kernel/prf.c b/kernel/prf.c index 144b7e9..6adf072 100644 --- a/kernel/prf.c +++ b/kernel/prf.c @@ -43,17 +43,12 @@ #define charp init_charp #endif -#ifdef VERSION_STRINGS -static BYTE *prfRcsId = - "$Id$"; -#endif +#include "debug.h" /* must be below xx to init_xx */ /* special console output routine */ /*#define DOSEMU */ #ifdef DOSEMU -int VA_CDECL printf(const char * fmt, ...); - #define MAX_BUFSIZE 80 /* adjust if necessary */ static int buff_offset = 0; static char buff[MAX_BUFSIZE]; @@ -63,7 +58,7 @@ void put_console(int c) if (buff_offset >= MAX_BUFSIZE) { buff_offset = 0; - printf("Printf buffer overflow!\n"); + DebugPrintf(("Printf buffer overflow!\n")); } if (c == '\n') { @@ -121,22 +116,21 @@ void put_console(int c) } #endif /* DOSEMU */ -#if defined(DEBUG) || defined(FORSYS) || defined(_INIT) +#if defined(DEBUG_NEED_PRINTF) || defined(FORSYS) || defined(_INIT) || defined(TEST) #ifndef FORSYS /* copied from bcc (Bruce's C compiler) stdarg.h */ -typedef char *va_list; -#define va_start(arg, last) ((arg) = (char *) (&(last)+1)) -#define va_arg(arg, type) (((type *)(arg+=sizeof(type)))[-1]) +typedef char FAR *va_list; +#define va_start(arg, last) ((arg) = (va_list) (&(last)+1)) +#define va_arg(arg, type) (((type FAR *)(arg+=sizeof(type)))[-1]) #define va_end(arg) #endif -static BYTE *charp = 0; +static BYTE FAR *charp = 0; STATIC VOID handle_char(COUNT); STATIC void ltob(LONG, BYTE *, COUNT); -STATIC void do_printf(const char *, REG va_list); -int VA_CDECL printf(const char * fmt, ...); +STATIC void do_printf(const char FAR *, REG va_list); /* special handler to switch between sprintf and printf */ STATIC VOID handle_char(COUNT c) @@ -188,7 +182,7 @@ STATIC void ltob(LONG n, BYTE * s, COUNT base) #define LONGARG 4 /* printf -- short version of printf to conserve space */ -int VA_CDECL printf(const char *fmt, ...) +int VA_CDECL printf(CONST char FAR *fmt, ...) { va_list arg; va_start(arg, fmt); @@ -197,7 +191,7 @@ int VA_CDECL printf(const char *fmt, ...) return 0; } -int VA_CDECL sprintf(char * buff, const char * fmt, ...) +int VA_CDECL sprintf(char FAR * buff, CONST char FAR * fmt, ...) { va_list arg; @@ -208,7 +202,7 @@ int VA_CDECL sprintf(char * buff, const char * fmt, ...) return 0; } -STATIC void do_printf(CONST BYTE * fmt, va_list arg) +STATIC void do_printf(CONST BYTE FAR * fmt, va_list arg) { int base; BYTE s[11], FAR * p; @@ -267,7 +261,7 @@ STATIC void do_printf(CONST BYTE * fmt, va_list arg) case 'p': { UWORD w0 = va_arg(arg, unsigned); - char *tmp = charp; + char FAR*tmp = charp; sprintf(s, "%04x:%04x", va_arg(arg, unsigned), w0); p = s; charp = tmp; @@ -320,6 +314,7 @@ STATIC void do_printf(CONST BYTE * fmt, va_list arg) default: handle_char('?'); + case '%': handle_char(*fmt); continue; @@ -405,14 +400,14 @@ void put_string(const char *s) #ifdef TEST /* - this testprogram verifies that the strings are printed correctly - ( or the way, I expect them to print) - - compile like (note -DTEST !) + this testprogram verifies that the strings are printed correctly + ( or the way, I expect them to print) + + compile like (note -DTEST !) - c:\tc\tcc -DTEST -DI86 -I..\hdr prf.c - - and run. if strings are wrong, the program will wait for the ANYKEY + c:\tc\tcc -DTEST -DI86 -I..\hdr prf.c + + and run. if strings are wrong, the program will wait for the ANYKEY */ #include diff --git a/kernel/proto.h b/kernel/proto.h index 7430b77..a3f1be8 100644 --- a/kernel/proto.h +++ b/kernel/proto.h @@ -280,8 +280,8 @@ UWORD ASMCFUNC syscall_MUX14(DIRECT_IREGS); /* prf.c */ #ifdef DEBUG -int VA_CDECL printf(const char * fmt, ...); -int VA_CDECL sprintf(char * buff, const char * fmt, ...); +int VA_CDECL printf(CONST char FAR * fmt, ...); +int VA_CDECL sprintf(char FAR * buff, CONST char FAR * fmt, ...); #endif VOID hexd(char *title, VOID FAR * p, COUNT numBytes); void put_unsigned(unsigned n, int base, int width); diff --git a/sys/fdkrncfg.c b/sys/fdkrncfg.c index 941e196..50ccd94 100644 --- a/sys/fdkrncfg.c +++ b/sys/fdkrncfg.c @@ -24,8 +24,12 @@ char KERNEL[] = "KERNEL.SYS"; #include #include "portab.h" -extern int VA_CDECL printf(const char * fmt, ...); -extern int VA_CDECL sprintf(char * buff, const char * fmt, ...); +/* These definitions deliberately put here instead of + * #including to make executable MUCH smaller + * using [s]printf from prf.c! + */ +extern int VA_CDECL printf(CONST char FAR * fmt, ...); +extern int VA_CDECL sprintf(char FAR * buff, CONST char FAR * fmt, ...); #ifdef __WATCOMC__ unsigned _dos_close(int handle); @@ -44,13 +48,13 @@ unsigned long lseek(int fildes, unsigned long offset, int whence); #else #include -#include +/* #include */ #endif #define FAR far #include "kconfig.h" -KernelConfig cfg = { 0 }; +KernelConfig cfg; /* static memory zeroed automatically */ typedef unsigned char byte; typedef signed char sbyte; @@ -59,10 +63,6 @@ typedef signed short sword; typedef unsigned long dword; typedef signed long sdword; -/* These structures need to be byte packed, if your compiler - does not do this by default, add the appropriate command, - such as #pragma pack(1) here, protected with #ifdefs of course. -*/ /* Displays command line syntax */ void showUsage(void) diff --git a/sys/makefile b/sys/makefile index 498b411..f0374b5 100644 --- a/sys/makefile +++ b/sys/makefile @@ -38,6 +38,12 @@ fat32chs.h: ../boot/fat32chs.bin bin2c.com fat32lba.h: ../boot/fat32lba.bin bin2c.com .$(DIRSEP)bin2c.com ../boot/fat32lba.bin fat32lba.h fat32lba +oemfat12.h: ../boot/oemfat12.bin bin2c.com + .$(DIRSEP)bin2c.com ../boot/oemfat12.bin oemfat12.h oemfat12 + +oemfat16.h: ../boot/oemfat16.bin bin2c.com + .$(DIRSEP)bin2c.com ../boot/oemfat16.bin oemfat16.h oemfat16 + prf.obj: ../kernel/prf.c $(CC) $(CFLAGS) ..$(DIRSEP)kernel$(DIRSEP)prf.c @@ -49,12 +55,12 @@ sys.com: $(SYS_EXE_dependencies) $(CL) $(CFLAGST) $(TINY) $(SYS_EXE_dependencies) clobber: clean - -$(RM) bin2c.com sys.com fat*.h + -$(RM) bin2c.com sys.com fat*.h oemfat*.h clean: -$(RM) *.obj *.bak *.crf *.xrf *.map *.lst *.las *.cod *.err status.me # *Individual File Dependencies* -sys.obj: sys.c ../hdr/portab.h ../hdr/device.h fat12com.h fat16com.h fat32chs.h fat32lba.h +sys.obj: sys.c ../hdr/portab.h ../hdr/device.h fat12com.h fat16com.h fat32chs.h fat32lba.h oemfat12.h oemfat16.h $(CC) $(CFLAGS) $*.c diff --git a/sys/sys.c b/sys/sys.c index c8bf6df..e1653b8 100644 --- a/sys/sys.c +++ b/sys/sys.c @@ -26,10 +26,26 @@ ***************************************************************/ -#define DEBUG -/* #define DDEBUG */ +/* #define DEBUG */ /* to display extra information */ +/* #define DDEBUG */ /* to enable display of sector dumps */ +/* #define WITHOEMCOMPATBS */ /* include support for OEM MS/PC DOS 3.??-6.x */ +#define FDCONFIG /* include support to configure FD kernel */ +/* #define DRSYS */ /* SYS for Enhanced DR-DOS (OpenDOS enhancement Project) */ -#define SYS_VERSION "v3.3" +#define SYS_VERSION "v3.6c" +#define SYS_NAME "FreeDOS System Installer " + + +#ifdef DRSYS /* set displayed name & drop FD kernel config */ +#undef SYS_NAME +#define SYS_NAME "Enhanced DR-DOS System Installer " +#ifdef FDCONFIG +#undef FDCONFIG +#endif +#ifdef WITHOEMCOMPATBS +#undef WITHOEMCOMPATBS +#endif +#endif #include #include @@ -59,8 +75,8 @@ * #including to make executable MUCH smaller * using [s]printf from prf.c! */ -extern int VA_CDECL printf(const char * fmt, ...); -extern int VA_CDECL sprintf(char * buff, const char * fmt, ...); +extern int VA_CDECL printf(CONST char FAR * fmt, ...); +extern int VA_CDECL sprintf(char FAR * buff, CONST char FAR * fmt, ...); #include "fat12com.h" #include "fat16com.h" @@ -68,12 +84,38 @@ extern int VA_CDECL sprintf(char * buff, const char * fmt, ...); #include "fat32chs.h" #include "fat32lba.h" #endif +#ifdef WITHOEMCOMPATBS +#include "oemfat12.h" +#include "oemfat16.h" +#endif #ifndef __WATCOMC__ #include +/* returns current DOS drive, A=0, B=1,C=2, ... */ +#ifdef __TURBOC__ +#define getcurdrive (unsigned)getdisk #else +unsigned getcurdrive(void) +{ + union REGS regs; + regs.h.ah = 0x19; + int86(0x21, ®s, ®s); + return regs.h.al; +} +#endif + +#else + +/* returns current DOS drive, A=0, B=1,C=2, ... */ +unsigned getcurdrive(void); +#pragma aux getcurdrive = \ + "mov ah, 0x19" \ + "int 0x21" \ + "xor ah, ah" \ + value [ax]; + long filelength(int __handle); #pragma aux filelength = \ @@ -125,12 +167,14 @@ int write(int fd, const void *buf, unsigned count) #define close _dos_close -int stat(const char *file_name, struct stat *buf) +int stat(const char *file_name, struct stat *statbuf) { struct find_t find_tbuf; - UNREFERENCED_PARAMETER(buf); - return _dos_findfirst(file_name, _A_NORMAL | _A_HIDDEN | _A_SYSTEM, &find_tbuf); + int ret = _dos_findfirst(file_name, _A_NORMAL | _A_HIDDEN | _A_SYSTEM, &find_tbuf); + statbuf->st_size = (off_t)find_tbuf.size; + /* statbuf->st_attr = (ULONG)find_tbuf.attrib; */ + return ret; } /* WATCOM's getenv is case-insensitive which wastes a lot of space @@ -157,30 +201,11 @@ char *getenv(const char *name) } #endif -#ifdef __TURBOC__ -typedef struct ftime ftime; -#else -typedef struct -{ -#if defined(__WATCOMC__) && __WATCOMC__ < 1280 - unsigned short date, time; -#else - unsigned date, time; -#endif -} ftime; -#endif BYTE pgm[] = "SYS"; -void put_boot(int, char *, char *, int, int); -BOOL check_space(COUNT, ULONG); -BYTE far * readfile(COUNT drive, BYTE * srcPath, BYTE * rootPath, - BYTE * file, ULONG * filesize, ftime *filetime); -BOOL writefile(COUNT drive, BYTE * rootPath, - BYTE * file, BYTE far * buffer, ULONG filesize, ftime *filetime); - #define SEC_SIZE 512 -#define COPY_SIZE 0x7e00 +#define COPY_SIZE 0x4000 struct bootsectortype { UBYTE bsJump[3]; @@ -254,195 +279,551 @@ struct VerifyBootSectorSize { /* (Watcom has a nice warning for this, by the way) */ }; +#ifdef FDCONFIG int FDKrnConfigMain(int argc, char **argv); +#endif -int main(int argc, char **argv) +/* FreeDOS sys, we default to our kernel and load segment, but + if not found (or explicitly given) support OEM DOS variants + (such as DR-DOS or a FreeDOS kernel mimicing other DOSes). + Note: other (especially older) DOS versions expect the boot + loader to perform particular steps, which we may not do; + older PC/MS DOS variants may work with the OEM compatible + boot sector (optionally included). +*/ +typedef struct DOSBootFiles { + const char * kernel; /* filename boot sector loads and chains to */ + const char * dos; /* optional secondary file for OS */ + WORD loadaddr; /* segment kernel file expects to start at for stdbs */ + /* or offset to jump into kernel for oem compat bs */ + BOOL stdbs; /* use FD boot sector (T) or oem compat one (F) */ + LONG minsize; /* smallest dos file can be and be valid, 0=existance optional */ +} DOSBootFiles; +#define FREEDOS_FILES { "KERNEL.SYS", NULL, 0x60/*:0*/, 1, 0 }, +DOSBootFiles bootFiles[] = { + /* Note: This order is the order OEM:AUTO uses to determine DOS flavor. */ +#ifndef DRSYS + /* FreeDOS */ FREEDOS_FILES +#endif + /* DR-DOS */ { "DRBIO.SYS", "DRDOS.SYS", 0x70/*:0*/, 1, 1 }, + /* DR-DOS */ { "IBMBIO.COM", "IBMDOS.COM", 0x70/*:0*/, 1, 1 }, +#ifdef DRSYS + /* FreeDOS */ FREEDOS_FILES +#endif +#ifdef WITHOEMCOMPATBS + /* PC-DOS */ { "IBMBIO.COM", "IBMDOS.COM", /*0x70:*/0x0, 0, 6138 }, /* pre v7 DR ??? */ + /* MS-DOS */ { "IO.SYS", "MSDOS.SYS", /*0x70:*/0x0, 0, 10240 }, + /* W9x-DOS */ { "IO.SYS", "MSDOS.SYS", /*0x70:*/0x0200, 0, 0 }, + /* Rx-DOS */ { "RXDOSBIO.SYS", "RXDOS.SYS", /*0x70:*/0x0, 0, 1 }, +#endif +}; +#define DOSFLAVORS (sizeof(bootFiles) / sizeof(*bootFiles)) + +/* associate friendly name with index into bootFiles array */ +#define OEM_AUTO (-1) /* attempt to guess DOS on source drive */ +#ifndef DRSYS +#define OEM_FD 0 /* standard FreeDOS mode */ +#define OEM_EDR 1 /* use FreeDOS boot sector, but OEM names */ +#define OEM_DR 2 /* FD boot sector,(Enhanced) DR-DOS names */ +#else +#define OEM_FD 2 /* standard FreeDOS mode */ +#define OEM_EDR 0 /* use FreeDOS boot sector, but OEM names */ +#define OEM_DR 1 /* FD boot sector,(Enhanced) DR-DOS names */ +#endif +#ifdef WITHOEMCOMPATBS +#define OEM_PC 3 /* use PC-DOS compatible boot sector and names */ +#define OEM_MS 4 /* use PC-DOS compatible BS with MS names */ +#define OEM_W9x 5 /* use PC-DOS compatible BS with MS names */ +#define OEM_RX 6 /* use PC-DOS compatible BS with Rx names */ +#endif + +CONST char * msgDOS[DOSFLAVORS] = { /* order should match above items */ + "\n", /* In standard FreeDOS/EnhancedDR mode, don't print anything special */ +#ifndef DRSYS + "Enhanced DR DOS (OpenDOS Enhancement Project) mode\n", +#endif + "DR DOS (OpenDOS Enhancement Project) mode\n", +#ifdef DRSYS + "\n", /* FreeDOS mode so don't print anything special */ +#endif +#ifdef WITHOEMCOMPATBS + "PC-DOS compatibility mode\n", + "MS-DOS compatibility mode\n", + "Win9x DOS compatibility mode\n", + "RxDOS compatibility mode\n", +#endif +}; + +typedef struct SYSOptions { + BYTE srcDrive[SYS_MAXPATH]; /* source drive:[path], root assumed if no path */ + BYTE dstDrive; /* destination drive [STD SYS option] */ + int flavor; /* DOS variant we want to boot, default is AUTO/FD */ + DOSBootFiles kernel; /* file name(s) and relevant data for kernel */ + BYTE defBootDrive; /* value stored in boot sector for drive, eg 0x0=A, 0x80=C */ + BOOL ignoreBIOS; /* true to NOP out boot sector code to get drive# from BIOS */ + BOOL skipBakBSCopy; /* true to not copy boot sector to backup boot sector */ + BOOL copyKernel; /* true to copy kernel files */ + BOOL copyShell; /* true to copy command interpreter */ + BOOL writeBS; /* true to write boot sector to drive/partition LBA 0 */ + BYTE *bsFile; /* file name & path to save bs to when saving to file */ + BYTE *bsFileOrig; /* file name & path to save original bs when backing up */ + BYTE *fnKernel; /* optional override to source kernel filename (src only) */ + BYTE *fnCmd; /* optional override to cmd interpreter filename (src & dest) */ +} SYSOptions; + +void dumpBS(const char *, int); +void restoreBS(const char *, int); +void put_boot(SYSOptions *opts); +BOOL check_space(COUNT, ULONG); +BOOL copy(const BYTE *source, COUNT drive, const BYTE * filename); + +void showHelpAndExit(void) { - COUNT drive; /* destination drive */ - COUNT drivearg = 0; /* drive argument position */ - COUNT srcarg = 0; /* source argument position */ - BYTE *bsFile = NULL; /* user specified destination boot sector */ - ULONG kernelsize = 0; /* size of the kernel to be copied */ - ftime kerneltime; /* time of the kernel to be copied */ - BYTE far *kernelbuf = NULL; /* kernel to be copied */ - ULONG shellsize = 0; /* size of the shell to be copied */ - ftime shelltime; /* time of the shell to be copied */ - BYTE far *shellbuf = NULL; /* shell to be copied */ - unsigned srcDrive; /* source drive */ - BYTE srcPath[SYS_MAXPATH]; /* user specified source drive and/or path */ - BYTE rootPath[4]; /* alternate source path to try if not '\0' */ - WORD slen; - int argno = 0; - int bootonly = 0; - int both = 0; - char *kernel_name = "KERNEL.SYS"; - int load_segment = 0x60; + printf( + "Usage: \n" + "%s [source] drive: [bootsect] [{option}]\n" + " source = A:,B:,C:\\DOS\\,etc., or current directory if not given\n" + " drive = A,B,etc.\n" + " bootsect = name of 512-byte boot sector file image for drive:\n" + " to write to *instead* of real boot sector\n" + " {option} is one or more of the following:\n" + " /BOTH : write to *both* the real boot sector and the image file\n" + " /BOOTONLY: do *not* copy kernel / shell, only update boot sector or image\n" + " /UPDATE : copy kernel and update boot sector (do *not* copy shell)\n" + " /OEM : indicates boot sector, filenames, and load segment to use\n" + " /OEM:FD use FreeDOS compatible settings\n" + " /OEM:EDR use Enhanced DR DOS 7+ compatible settings\n" + " /OEM:DR use DR DOS 7+ compatible settings\n" +#ifdef WITHOEMCOMPATBS + " /OEM:PC use PC-DOS compatible settings\n" + " /OEM:MS use MS-DOS compatible settings\n" + " /OEM:W9x use MS Win9x DOS compatible settings\n" +#endif + " default is /OEM[:AUTO], select DOS based on existing files\n" + " /K name : name of kernel to use in boot sector instead of %s\n" + " /L segm : hex load segment to use in boot sector instead of %02x\n" + " /B btdrv : hex BIOS # of boot drive set in bs, 0=A:, 80=1st hd,...\n" + " /FORCEDRV: force use of drive # set in bs instead of BIOS boot value\n" + " /NOBAKBS : skips copying boot sector to backup bs, FAT32 only else ignored\n" +#ifdef FDCONFIG + "%s CONFIG /help\n" +#endif + /*SYS, KERNEL.SYS/DRBIO.SYS 0x60/0x70*/ + , pgm, bootFiles[0].kernel, bootFiles[0].loadaddr +#ifdef FDCONFIG + , pgm +#endif + ); + exit(1); +} - printf("FreeDOS System Installer " SYS_VERSION ", " __DATE__ "\n\n"); - if (argc > 1 && memicmp(argv[1], "CONFIG", 6) == 0) - { - exit(FDKrnConfigMain(argc, argv)); - } +/* get and validate arguments */ +void initOptions(int argc, char *argv[], SYSOptions *opts) +{ + int argno; + int drivearg = 0; /* drive argument, position of 1st or 2nd non option */ + int srcarg = 0; /* nonzero if optional source argument */ + BYTE srcFile[SYS_MAXPATH]; /* full path+name of [kernel] file [to copy] */ + struct stat fstatbuf; + /* initialize to defaults */ + memset(opts, 0, sizeof(SYSOptions)); + /* set srcDrive and dstDrive after processing args */ + opts->flavor = OEM_AUTO; /* attempt to detect DOS user wants to boot */ + opts->copyKernel = 1; /* actually copy the kernel and cmd interpreter to dstDrive */ + opts->copyShell = 1; + + /* cycle through processing cmd line arguments */ for(argno = 1; argno < argc; argno++) { char *argp = argv[argno]; - if (argp[1] == ':' && argp[2] == '\0' && drivearg <= srcarg) - drivearg = argno; + if (argp[0] == '/') /* optional switch */ + { + argp++; /* skip past the '/' character */ - if (srcarg == 0) - { - srcarg = argno; - } - else if (argp[0] == '/' && toupper(argp[1]) == 'K' && argno + 1 < argc) - { - argno++; - kernel_name = argv[argno]; - } - else if (argp[0] == '/' && toupper(argp[1]) == 'L' && argno + 1 < argc) - { - argno++; - load_segment = (int)strtol(argv[argno], NULL, 16); - } - else if (memicmp(argp, "BOOTONLY", 8) == 0 && !bootonly) - { - bootonly = 1; - } - else if (memicmp(argp, "BOTH", 4) == 0 && !both) - { - both = 1; - } - else if (drivearg != argno) - { - if (bsFile == NULL) + /* explicit request for base help/usage */ + if ((*argp == '?') || (memicmp(argp, "HELP", 4) == 0)) { - bsFile = argp; + showHelpAndExit(); + } + /* write to *both* the real boot sector and the image file */ + else if (memicmp(argp, "BOTH", 4) == 0) + { + opts->writeBS = 1; /* note: if bs file omitted, then same as omitting /BOTH */ + } + /* do *not* copy kernel / shell, only update boot sector or image */ + else if (memicmp(argp, "BOOTONLY", 8) == 0) + { + opts->copyKernel = 0; + opts->copyShell = 0; + } + /* copy kernel and update boot sector (do *not* copy shell) */ + else if (memicmp(argp, "UPDATE", 6) == 0) + { + opts->copyKernel = 1; + opts->copyShell = 0; + } + /* indicates compatibility mode, fs, filenames, and load segment to use */ + else if (memicmp(argp, "OEM", 3) == 0) + { + argp += 3; + if (!*argp) + opts->flavor = OEM_AUTO; + else if (*argp == ':') + { + argp++; /* point to DR/PC/MS that follows */ + if (memicmp(argp, "AUTO", 4) == 0) + opts->flavor = OEM_AUTO; + else if (memicmp(argp, "EDR", 3) == 0) + opts->flavor = OEM_EDR; + else if (memicmp(argp, "DR", 2) == 0) + opts->flavor = OEM_DR; +#ifdef WITHOEMCOMPATBS + else if (memicmp(argp, "PC", 2) == 0) + opts->flavor = OEM_PC; + else if (memicmp(argp, "MS", 2) == 0) + opts->flavor = OEM_MS; + else if (memicmp(argp, "W9", 2) == 0) + opts->flavor = OEM_W9x; + else if (memicmp(argp, "RX", 2) == 0) + opts->flavor = OEM_RX; +#endif + else if (memicmp(argp, "FD", 2) == 0) + opts->flavor = OEM_FD; + else + { + printf("%s: unknown OEM qualifier %s\n", pgm, argp); + showHelpAndExit(); + } + } + else + { + printf("%s: unknown OEM qualifier %s\n", pgm, argp); + showHelpAndExit(); + } + } + /* force use of drive # set in bs instead of BIOS boot value */ + else if (memicmp(argp, "FORCEDRV", 8) == 0) + { + opts->ignoreBIOS = 1; + } + /* skips copying boot sector to backup bs, FAT32 only else ignored */ + else if (memicmp(argp, "NOBAKBS", 7) == 0) + { + opts->skipBakBSCopy = 1; + } + else if (argno + 1 < argc) /* two part options, /SWITCH VALUE */ + { + argno++; + if (toupper(*argp) == 'K') /* set Kernel name */ + { + opts->kernel.kernel = argv[argno]; + } + else if (toupper(*argp) == 'L') /* set Load segment */ + { + opts->kernel.loadaddr = (WORD)strtol(argv[argno], NULL, 16); + } + else if (memicmp(argp, "B", 2) == 0) /* set boot drive # */ + { + opts->defBootDrive = (BYTE)strtol(argv[argno], NULL, 16); + } + /* options not documented by showHelpAndExit() */ + else if (memicmp(argp, "SKFN", 4) == 0) /* set KERNEL.SYS input file and /OEM:FD */ + { + opts->flavor = OEM_FD; + opts->fnKernel = argv[argno]; + } + else if (memicmp(argp, "SCFN", 4) == 0) /* sets COMMAND.COM input file */ + { + opts->fnCmd = argv[argno]; + } + else if (memicmp(argp, "BACKUPBS", 8) == 0) /* save current bs before overwriting */ + { + opts->bsFileOrig = argv[argno]; + } + else if (memicmp(argp, "DUMPBS", 6) == 0) /* save current bs and exit */ + { + if (drivearg) + dumpBS(argv[argno], (BYTE)(toupper(*(argv[drivearg])) - 'A')); + else + printf("%s: unspecified drive, unable to obtain boot sector\n", pgm); + exit(1); + } + else if (memicmp(argp, "RESTORBS", 8) == 0) /* overwrite bs and exit */ + { + if (drivearg) + restoreBS(argv[argno], (BYTE)(toupper(*(argv[drivearg])) - 'A')); + else + printf("%s: unspecified drive, unable to restore boot sector\n", pgm); + exit(1); + } + else + { + printf("%s: unknown option, %s\n", pgm, argv[argno]); + showHelpAndExit(); + } } else { - drivearg = 0; + printf("%s: unknown option or missing parameter, %s\n", pgm, argv[argno]); + showHelpAndExit(); + } + } + else if (!drivearg) + { + drivearg = argno; /* either source or destination drive */ + } + else if (!srcarg /* && drivearg */) + { + srcarg = drivearg; /* set source path */ + drivearg = argno; /* set destination drive */ + } + else if (!opts->bsFile /* && srcarg && drivearg */) + { + opts->bsFile = argv[argno]; + } + else /* if (opts->bsFile && srcarg && drivearg) */ + { + printf("%s: invalid argument %s\n", pgm, argv[argno]); + showHelpAndExit(); + } + } + + /* if neither BOTH nor a boot sector file specified, then write to boot record */ + if (!opts->bsFile) + opts->writeBS = 1; + + /* set dest path */ + if (!drivearg) + showHelpAndExit(); + opts->dstDrive = (BYTE)(toupper(*(argv[drivearg])) - 'A'); + if (/* (opts->dstDrive < 0) || */ (opts->dstDrive >= 26)) + { + printf("%s: drive %c must be A:..Z:\n", pgm, *(argv[drivearg])); + exit(1); + } + + /* set source path, default to current drive */ + sprintf(opts->srcDrive, "%c:", 'A' + getcurdrive()); + if (srcarg) + { + int slen; + /* set source path, reserving room to append filename */ + if ( (argv[srcarg][1] == ':') || ((argv[srcarg][0]=='\\') && (argv[srcarg][1] == '\\')) ) + strncpy(opts->srcDrive, argv[srcarg], SYS_MAXPATH-13); + else if (argv[srcarg][1] == '\0') /* assume 1 char is drive not path specifier */ + sprintf(opts->srcDrive, "%c:", toupper(*(argv[srcarg]))); + else /* only path provided, append to default drive */ + strncat(opts->srcDrive, argv[srcarg], SYS_MAXPATH-15); + slen = strlen(opts->srcDrive); + /* if path follows drive, ensure ends in a slash, ie X:-->X: or X:.\mypath-->X:.\mypath\ */ + if ((slen>2) && (opts->srcDrive[slen-1] != '\\') && (opts->srcDrive[slen-1] != '/')) + strcat(opts->srcDrive, "\\"); + } + /* source path is now in form of just a drive, "X:" + or form of drive + path + directory separator, "X:\path\" or "\\path\" + If just drive we try current path then root, else just indicated path. + */ + + + /* if source and dest are same drive, then source should not be root, + so if is same drive and not explicit path, force only current + Note: actual copy routine prevents overwriting self when src=dst + */ + if ( (opts->dstDrive == (toupper(*(opts->srcDrive))-'A')) && (!opts->srcDrive[2]) ) + strcat(opts->srcDrive, ".\\"); + + + /* attempt to detect compatibility settings user needs */ + if (opts->flavor == OEM_AUTO) + { + /* 1st loop checking current just source path provided */ + for (argno = 0; argno < DOSFLAVORS; argno++) + { + /* look for existing file matching kernel filename */ + sprintf(srcFile, "%s%s", opts->srcDrive, bootFiles[argno].kernel); + if (stat(srcFile, &fstatbuf)) continue; /* if !exists() try again */ + if (!fstatbuf.st_size) continue; /* file must not be empty */ + + /* now check if secondary file exists and of minimal size */ + if (bootFiles[argno].minsize) + { + sprintf(srcFile, "%s%s", opts->srcDrive, bootFiles[argno].dos); + if (stat(srcFile, &fstatbuf)) continue; + if (fstatbuf.st_size < bootFiles[argno].minsize) continue; + } + + /* above criteria succeeded, so default to corresponding DOS */ + opts->flavor = argno; + break; + } + + /* if no match, and source just drive, try root */ + if ( (opts->flavor == OEM_AUTO) && (!opts->srcDrive[2]) ) + { + for (argno = 0; argno < DOSFLAVORS; argno++) + { + /* look for existing file matching kernel filename */ + sprintf(srcFile, "%s\\%s", opts->srcDrive, bootFiles[argno].kernel); + if (stat(srcFile, &fstatbuf)) continue; /* if !exists() try again */ + if (!fstatbuf.st_size) continue; /* file must not be empty */ + + /* now check if secondary file exists and of minimal size */ + if (bootFiles[argno].minsize) + { + sprintf(srcFile, "%s\\%s", opts->srcDrive, bootFiles[argno].dos); + if (stat(srcFile, &fstatbuf)) continue; + if (fstatbuf.st_size < bootFiles[argno].minsize) continue; + } + + /* above criteria succeeded, so default to corresponding DOS */ + opts->flavor = argno; + strcat(opts->srcDrive, "\\"); /* indicate to use root from now on */ break; } } } - if (drivearg == 0) - { - printf( - "Usage: %s [source] drive: [bootsect [BOTH]] [BOOTONLY] [/K name] [/L segm]\n" - " source = A:,B:,C:\\KERNEL\\BIN\\,etc., or current directory if not given\n" - " drive = A,B,etc.\n" - " bootsect = name of 512-byte boot sector file image for drive:\n" - " to write to *instead* of real boot sector\n" - " BOTH : write to *both* the real boot sector and the image file\n" - " BOOTONLY : do *not* copy kernel / shell, only update boot sector or image\n" - " /K name : name of kernel to use instead of KERNEL.SYS\n" - " /L segm : hex load segment to use instead of 60\n" - "%s CONFIG /help\n", pgm, pgm); - exit(1); - } - drive = toupper(argv[drivearg][0]) - 'A'; + /* if unable to determine DOS, assume FreeDOS */ + if (opts->flavor == OEM_AUTO) opts->flavor = +#ifdef DRSYS + OEM_EDR; +#else + OEM_FD; +#endif - if (drive < 0 || drive >= 26) - { - printf("%s: drive %c must be A:..Z:\n", pgm, - *argv[(argc == 3 ? 2 : 1)]); - exit(1); - } + printf(msgDOS[opts->flavor]); - srcPath[0] = '\0'; - if (drivearg > srcarg && srcarg) + /* set compatibility settings not explicitly set */ + if (!opts->kernel.kernel) opts->kernel.kernel = bootFiles[opts->flavor].kernel; + if (!opts->kernel.dos) opts->kernel.dos = bootFiles[opts->flavor].dos; + if (!opts->kernel.loadaddr) opts->kernel.loadaddr = bootFiles[opts->flavor].loadaddr; + opts->kernel.stdbs = bootFiles[opts->flavor].stdbs; + opts->kernel.minsize = bootFiles[opts->flavor].minsize; + + + /* if destination is floppy (A: or B:) then use drive # stored in boot sector */ + if (opts->dstDrive < 2) + opts->ignoreBIOS = 1; + + /* if bios drive to store in boot sector not set and not floppy set to 1st hd */ + if (!opts->defBootDrive && (opts->dstDrive >= 2)) + opts->defBootDrive = 0x80; + + + /* unless we are only setting boot sector, verify kernel file exists */ + if (opts->copyKernel) { - strncpy(srcPath, argv[srcarg], SYS_MAXPATH - 12); - /* leave room for COMMAND.COM\0 */ - srcPath[SYS_MAXPATH - 13] = '\0'; - /* make sure srcPath + "file" is a valid path */ - slen = strlen(srcPath); - if ((srcPath[slen - 1] != ':') && - ((srcPath[slen - 1] != '\\') || (srcPath[slen - 1] != '/'))) + /* check kernel (primary file) 1st */ + sprintf(srcFile, "%s%s", opts->srcDrive, (opts->fnKernel)?opts->fnKernel:opts->kernel.kernel); + if (stat(srcFile, &fstatbuf)) /* if !exists() */ { - srcPath[slen] = '\\'; - slen++; - srcPath[slen] = '\0'; + /* check root path as well if src is drive only */ + sprintf(srcFile, "%s\\%s", opts->srcDrive, (opts->fnKernel)?opts->fnKernel:opts->kernel.kernel); + if (opts->srcDrive[2] || stat(srcFile, &fstatbuf)) + { + printf("%s: failed to find kernel file %s\n", pgm, (opts->fnKernel)?opts->fnKernel:opts->kernel.kernel); + exit(1); + } + /* else found, but in root, so force to always use root */ + strcat(opts->srcDrive, "\\"); + } + + /* now check for secondary file */ + if (opts->kernel.dos && opts->kernel.minsize) + { + sprintf(srcFile, "%s%s", opts->srcDrive, opts->kernel.dos); + if (stat(srcFile, &fstatbuf)) + { + printf("%s: failed to find source file %s\n", pgm, opts->kernel.dos); + exit(1); + } + if (fstatbuf.st_size < opts->kernel.minsize) + { + printf("%s: source file %s appears corrupt, invalid size\n", pgm, opts->kernel.dos); + exit(1); + } } } - /* Get source drive */ - if ((strlen(srcPath) > 1) && (srcPath[1] == ':')) /* src specifies drive */ - srcDrive = toupper(*srcPath) - 'A'; - else /* src doesn't specify drive, so assume current drive */ + /* if updating or only setting bootsector then skip this check */ + if (opts->copyShell) { -#ifdef __TURBOC__ - srcDrive = (unsigned) getdisk(); -#else - _dos_getdrive(&srcDrive); - srcDrive--; -#endif - } - - /* Don't try root if src==dst drive or source path given */ - if ((drive == srcDrive) - || (*srcPath - && ((srcPath[1] != ':') || ((srcPath[1] == ':') && srcPath[2])))) - *rootPath = '\0'; - else - sprintf(rootPath, "%c:\\", 'A' + srcDrive); - - if (!bootonly) - { - printf("Reading %s...\n", kernel_name); - kernelbuf = readfile(drive, srcPath, rootPath, kernel_name, &kernelsize, - &kerneltime); - if (kernelbuf == NULL) - { - printf("\n%s: cannot read \"%s\"\n", pgm, kernel_name); - exit(1); - } /* fetch kernel */ - printf("\nReading COMMAND.COM...\n"); - shellbuf = readfile(drive, srcPath, rootPath, "COMMAND.COM", &shellsize, - &shelltime); - if (shellbuf == NULL) + /* lastly check for command interpreter */ + sprintf(srcFile, "%s%s", opts->srcDrive, (opts->fnCmd)?opts->fnCmd:"COMMAND.COM"); + if (stat(srcFile, &fstatbuf)) /* if !exists() */ { char *comspec = getenv("COMSPEC"); - if (comspec != NULL) + if (opts->fnCmd || (comspec == NULL) || stat(comspec, &fstatbuf)) { - printf("%s: Trying \"%s\"\n", pgm, comspec); - shellbuf = readfile(drive, comspec, NULL, "COMMAND.COM", &shellsize, - &shelltime); - if (shellbuf == NULL) - comspec = NULL; - } - if (comspec == NULL) - { - printf("\n%s: cannot read \"COMMAND.COM\"\n", pgm); + printf("%s: failed to find command interpreter (shell) file %s\n", pgm, srcFile); exit(1); } - } /* fetch shell */ - } /* file READS */ + } + } +} - printf("\nProcessing boot sector...\n"); /* READ and WRITE */ - put_boot(drive, bsFile, kernel_name, load_segment, both); +int main(int argc, char **argv) +{ + SYSOptions opts; /* boot options and other flags */ + BYTE srcFile[SYS_MAXPATH]; /* full path+name of [kernel] file [to copy] */ - if (!bootonly) + printf(SYS_NAME SYS_VERSION ", " __DATE__ "\n"); + +#ifdef FDCONFIG + if (argc > 1 && memicmp(argv[1], "CONFIG", 6) == 0) { - printf("\nWriting %s...\n", kernel_name); - if (!writefile(drive, rootPath, kernel_name, kernelbuf, kernelsize, - &kerneltime)) - { - printf("\n%s: cannot write \"%s\"\n", pgm, kernel_name); - exit(1); - } /* write kernel */ + exit(FDKrnConfigMain(argc, argv)); + } +#endif - printf("\nWriting COMMAND.COM...\n"); - if (!writefile(drive, rootPath, "COMMAND.COM", shellbuf, shellsize, - &shelltime)) + initOptions(argc, argv, &opts); + + printf("Processing boot sector...\n"); + put_boot(&opts); + + if (opts.copyKernel) + { + printf("Now copying system files...\n"); + + sprintf(srcFile, "%s%s", opts.srcDrive, (opts.fnKernel)?opts.fnKernel:opts.kernel.kernel); + if (!copy(srcFile, opts.dstDrive, opts.kernel.kernel)) { - printf("\n%s: cannot write \"COMMAND.COM\"\n", pgm); + printf("%s: cannot copy \"%s\"\n", pgm, srcFile); exit(1); - } /* write shell */ - } /* file WRITES */ + } /* copy kernel */ + + if (opts.kernel.dos) + { + sprintf(srcFile, "%s%s", opts.srcDrive, opts.kernel.dos); + if (!copy(srcFile, opts.dstDrive, opts.kernel.dos) && opts.kernel.minsize) + { + printf("%s: cannot copy \"%s\"\n", pgm, srcFile); + exit(1); + } /* copy secondary file (DOS) */ + } + } + + if (opts.copyShell) + { + printf("Copying shell (command interpreter)...\n"); + + /* copy command.com, 1st try source path, then try %COMSPEC% */ + sprintf(srcFile, "%s%s", opts.srcDrive, (opts.fnCmd)?opts.fnCmd:"COMMAND.COM"); + if (!copy(srcFile, opts.dstDrive, "COMMAND.COM")) + { + char *comspec = getenv("COMSPEC"); + if (!opts.fnCmd && (comspec != NULL)) + printf("%s: Trying shell from %%COMSPEC%%=\"%s\"\n", pgm, comspec); + if (opts.fnCmd || (comspec == NULL) || !copy(comspec, opts.dstDrive, "COMMAND.COM")) + { + printf("\n%s: failed to find command interpreter (shell) file %s\n", pgm, (opts.fnCmd)?opts.fnCmd:"COMMAND.COM"); + exit(1); + } + } /* copy shell */ + } printf("\nSystem transferred.\n"); return 0; @@ -477,6 +858,7 @@ VOID dump_sector(unsigned char far * sec) #endif + #ifdef __WATCOMC__ int absread(int DosDrive, int nsects, int foo, void *diskReadPacket); @@ -525,17 +907,17 @@ void reset_drive(int DosDrive); void truename(char far *dest, const char *src); #pragma aux truename = \ - "mov ah,0x60" \ + "mov ah,0x60" \ "int 0x21" \ parm [es di] [si]; -int generic_block_ioctl(unsigned char drive, unsigned cx, unsigned char *par); +int generic_block_ioctl(unsigned drive, unsigned cx, unsigned char *par); #pragma aux generic_block_ioctl = \ "mov ax, 0x440d" \ "int 0x21" \ "sbb ax, ax" \ value [ax] \ - parm [bl] [cx] [dx]; + parm [bx] [cx] [dx]; /* BH must be 0 for lock! */ #else @@ -587,14 +969,14 @@ void reset_drive(int DosDrive) intdos(®s, ®s); } /* reset_drive */ -int generic_block_ioctl(unsigned char drive, unsigned cx, unsigned char *par) +int generic_block_ioctl(unsigned drive, unsigned cx, unsigned char *par) { union REGS regs; regs.x.ax = 0x440d; regs.x.cx = cx; regs.x.dx = (unsigned)par; - regs.h.bl = drive + 1; + regs.x.bx = drive; /* BH must be 0 for lock! */ intdos(®s, ®s); return regs.x.cflag; } /* generic_block_ioctl */ @@ -644,7 +1026,7 @@ int MyAbsReadWrite(int DosDrive, int count, ULONG sector, void *buffer, unsigned getextdrivespace(void far *drivename, void *buf, unsigned buf_size); #pragma aux getextdrivespace = \ "mov ax, 0x7303" \ - "stc" \ + "stc" \ "int 0x21" \ "sbb ax, ax" \ parm [es dx] [di] [cx] \ @@ -689,7 +1071,7 @@ BOOL haveLBA(void); /* return TRUE if we have LBA BIOS, FALSE otherwise */ "and cx, 1" \ "xchg cx, ax" \ "quit:" \ - modify [bx cx dx] \ + modify [bx cx dx] \ value [ax]; #else @@ -724,27 +1106,155 @@ void correct_bpb(struct bootsectortype *default_bpb, oldboot->bsHiddenSecs = default_bpb->bsHiddenSecs; } -void put_boot(int drive, char *bsFile, char *kernel_name, int load_seg, int both) + +/* reads in boot sector (1st SEC_SIZE bytes) from file */ +void readBS(const char *bsFile, UBYTE *bootsector) +{ + if (bsFile != NULL) + { + int fd; + +#ifdef DEBUG + printf("reading bootsector from file %s\n", bsFile); +#endif + + /* open boot sector file, it must exists, then overwrite + drive with 1st SEC_SIZE bytes from the [image] file + */ + if ((fd = open(bsFile, O_RDONLY | O_BINARY)) < 0) + { + printf("%s: can't open\"%s\"\nDOS errnum %d", pgm, bsFile, errno); + exit(1); + } + if (read(fd, bootsector, SEC_SIZE) != SEC_SIZE) + { + printf("%s: failed to read %u bytes from %s\n", pgm, SEC_SIZE, bsFile); + close(fd); + exit(1); + } + close(fd); + } +} + +/* write bs in bsFile to drive's boot record unmodified */ +void restoreBS(const char *bsFile, int drive) +{ + UBYTE bootsector[SEC_SIZE]; + + if (bsFile == NULL) + { + printf("%s: missing filename of boot sector to restore\n", pgm); + exit(1); + } + + readBS(bsFile, bootsector); + + /* lock drive */ + generic_block_ioctl(drive + 1, 0x84a, NULL); + + reset_drive(drive); + + /* write bootsector to drive */ + if (MyAbsReadWrite(drive, 1, 0, bootsector, 1) != 0) + { + printf("%s: failed to write boot sector to drive %c:\n", pgm, drive + 'A'); + exit(1); + } + + reset_drive(drive); + + /* unlock_drive */ + generic_block_ioctl(drive + 1, 0x86a, NULL); +} + +/* write bootsector to file bsFile */ +void saveBS(const char *bsFile, UBYTE *bootsector) +{ + if (bsFile != NULL) + { + int fd; + +#ifdef DEBUG + printf("writing bootsector to file %s\n", bsFile); +#endif + + /* open boot sector file, create it if not exists, + but don't truncate if exists so we can replace + 1st SEC_SIZE bytes of an image file + */ + if ((fd = open(bsFile, O_WRONLY | O_CREAT | O_BINARY, + S_IREAD | S_IWRITE)) < 0) + { + printf("%s: can't create\"%s\"\nDOS errnum %d", pgm, bsFile, errno); + exit(1); + } + if (write(fd, bootsector, SEC_SIZE) != SEC_SIZE) + { + printf("%s: failed to write %u bytes to %s\n", pgm, SEC_SIZE, bsFile); + close(fd); + /* unlink(bsFile); don't delete in case was image */ + exit(1); + } + close(fd); + } /* if write boot sector file */ +} + +/* write drive's boot record unmodified to bsFile */ +void dumpBS(const char *bsFile, int drive) +{ + UBYTE bootsector[SEC_SIZE]; + + if (bsFile == NULL) + { + printf("%s: missing filename to dump boot sector to\n", pgm); + exit(1); + } + + /* lock drive */ + generic_block_ioctl(drive + 1, 0x84a, NULL); + + reset_drive(drive); + + /* suggestion: allow reading from a boot sector or image file here */ + if (MyAbsReadWrite(drive, 1, 0, bootsector, 0) != 0) + { + printf("%s: failed to read boot sector for drive %c:\n", pgm, drive + 'A'); + exit(1); + } + + reset_drive(drive); + + /* unlock_drive */ + generic_block_ioctl(drive + 1, 0x86a, NULL); + + saveBS(bsFile, bootsector); +} + + + +void put_boot(SYSOptions *opts) { #ifdef WITHFAT32 struct bootsectortype32 *bs32; #endif struct bootsectortype *bs; - static unsigned char oldboot[SEC_SIZE], newboot[SEC_SIZE]; - static unsigned char default_bpb[0x5c]; + UBYTE oldboot[SEC_SIZE], newboot[SEC_SIZE]; + UBYTE default_bpb[0x5c]; + int bsBiosMovOff; /* offset in bs to mov [drive],dl that we NOP out */ #ifdef DEBUG - printf("Reading old bootsector from drive %c:\n", drive + 'A'); + printf("Reading old bootsector from drive %c:\n", opts->dstDrive + 'A'); #endif /* lock drive */ - generic_block_ioctl((unsigned char)drive + 1, 0x84a, NULL); + generic_block_ioctl(opts->dstDrive + 1, 0x84a, NULL); + + reset_drive(opts->dstDrive); - reset_drive(drive); /* suggestion: allow reading from a boot sector or image file here */ - if (MyAbsReadWrite(drive, 1, 0, oldboot, 0) != 0) + if (MyAbsReadWrite(opts->dstDrive, 1, 0, oldboot, 0) != 0) { - printf("can't read old boot sector for drive %c:\n", drive + 'A'); + printf("%s: can't read old boot sector for drive %c:\n", pgm, opts->dstDrive + 'A'); exit(1); } @@ -753,6 +1263,13 @@ void put_boot(int drive, char *bsFile, char *kernel_name, int load_seg, int both dump_sector(oldboot); #endif + /* backup original boot sector when requested */ + if (opts->bsFileOrig) + { + printf("Backing up original boot sector to %s\n", opts->bsFileOrig); + saveBS(opts->bsFileOrig, oldboot); + } + bs = (struct bootsectortype *)&oldboot; if (bs->bsBytesPerSec != SEC_SIZE) @@ -769,7 +1286,7 @@ void put_boot(int drive, char *bsFile, char *kernel_name, int load_seg, int both ULONG fatSize, totalSectors, dataSectors, clusters; UCOUNT rootDirSectors; - bs32 = (struct bootsectortype32 *)&oldboot; + bs32 = (struct bootsectortype32 *)bs; rootDirSectors = (bs->bsRootDirEnts * DIRENT_SIZE /* 32 */ + bs32->bsBytesPerSec - 1) / bs32->bsBytesPerSec; fatSize = bs32->bsFATsecs ? bs32->bsFATsecs : bs32->bsBigFatSize; @@ -797,11 +1314,18 @@ void put_boot(int drive, char *bsFile, char *kernel_name, int load_seg, int both { printf("FAT type: FAT32\n"); /* get default bpb (but not for floppies) */ - if (drive >= 2 && - generic_block_ioctl((unsigned char)drive + 1, 0x4860, default_bpb) == 0) + if (opts->dstDrive >= 2 && + generic_block_ioctl(opts->dstDrive + 1, 0x4860, default_bpb) == 0) correct_bpb((struct bootsectortype *)(default_bpb + 7 - 11), bs); #ifdef WITHFAT32 /* copy one of the FAT32 boot sectors */ + if (!opts->kernel.stdbs) /* MS/PC DOS compatible BS requested */ + { + printf("%s: FAT32 versions of PC/MS DOS compatible boot sectors\n" + "are not supported.\n", pgm); + exit(1); + } + memcpy(newboot, haveLBA() ? fat32lba : fat32chs, SEC_SIZE); #else printf("SYS hasn't been compiled with FAT32 support.\n" @@ -812,10 +1336,23 @@ void put_boot(int drive, char *bsFile, char *kernel_name, int load_seg, int both else { /* copy the FAT12/16 CHS+LBA boot sector */ printf("FAT type: FAT1%c\n", fs + '0' - 10); - if (drive >= 2 && - generic_block_ioctl((unsigned char)drive + 1, 0x860, default_bpb) == 0) + if (opts->dstDrive >= 2 && + generic_block_ioctl(opts->dstDrive + 1, 0x860, default_bpb) == 0) correct_bpb((struct bootsectortype *)(default_bpb + 7 - 11), bs); - memcpy(newboot, fs == FAT16 ? fat16com : fat12com, SEC_SIZE); + + if (opts->kernel.stdbs) + { + memcpy(newboot, (fs == FAT16) ? fat16com : fat12com, SEC_SIZE); + } + else + { +#ifdef WITHOEMCOMPATBS + printf("Using OEM (PC/MS-DOS) compatible boot sector.\n"); + memcpy(newboot, (fs == FAT16) ? oemfat16 : oemfat12, SEC_SIZE); +#else + printf("Internal Error: no OEM compatible boot sector!\n"); +#endif + } } /* Copy disk parameter from old sector to new sector */ @@ -828,6 +1365,7 @@ void put_boot(int drive, char *bsFile, char *kernel_name, int load_seg, int both bs = (struct bootsectortype *)&newboot; + /* originally OemName was "FreeDOS", changed for better compatibility */ memcpy(bs->OemName, "FRDOS5.1", 8); /* Win9x seems to require 5 uppercase letters, digit(4 or 5) dot digit */ @@ -836,14 +1374,36 @@ void put_boot(int drive, char *bsFile, char *kernel_name, int load_seg, int both if (fs == FAT32) { bs32 = (struct bootsectortype32 *)&newboot; - /* put 0 for A: or B: (force booting from A:), otherwise use DL */ - bs32->bsDriveNumber = drive < 2 ? 0 : 0xff; + /* ensure appears valid, if not then force valid */ + if ((bs32->bsBackupBoot < 1) || (bs32->bsBackupBoot > bs32->bsResSectors)) + { + #ifdef DEBUG + printf("BPB appears to have invalid backup boot sector #, forcing to default.\n"); + #endif + bs32->bsBackupBoot = 0x6; /* ensure set, 6 is MS defined bs size */ + } + bs32->bsDriveNumber = opts->defBootDrive; + /* the location of the "0060" segment portion of the far pointer in the boot sector is just before cont: in boot*.asm. - This happens to be offset 0x78 (=0x3c * 2) for FAT32 and - offset 0x5c (=0x2e * 2) for FAT16 */ - /* i.e. BE CAREFUL WHEN YOU CHANGE THE BOOT SECTORS !!! */ - ((int *)newboot)[0x3C] = load_seg; + This happens to be offset 0x78 for FAT32 and offset 0x5c for FAT16 + + force use of value stored in bs by NOPping out mov [drive], dl + 0x82: 88h,56h,40h for fat32 chs & lba boot sectors + + i.e. BE CAREFUL WHEN YOU CHANGE THE BOOT SECTORS !!! + */ + if (opts->kernel.stdbs) + { + ((int *)newboot)[0x78/sizeof(int)] = opts->kernel.loadaddr; + bsBiosMovOff = 0x82; + } + else /* compatible bs */ + { + printf("%s: INTERNAL ERROR: how did you get here?\n", pgm); + exit(1); + } + #ifdef DEBUG printf(" FAT starts at sector %lx + %x\n", bs32->bsHiddenSecs, bs32->bsResSectors); @@ -852,9 +1412,58 @@ void put_boot(int drive, char *bsFile, char *kernel_name, int load_seg, int both else #endif { - /* put 0 for A: or B: (force booting from A:), otherwise use DL */ - bs->bsDriveNumber = drive < 2 ? 0 : 0xff; - ((int *)newboot)[0x2E] = load_seg; + + /* establish default BIOS drive # set in boot sector */ + bs->bsDriveNumber = opts->defBootDrive; + + /* the location of the "0060" segment portion of the far pointer + in the boot sector is just before cont: in boot*.asm. + This happens to be offset 0x78 for FAT32 and offset 0x5c for FAT16 + The oem boot sectors do not have/need this value for patching. + + the location of the jmp address (patching from + EA00007000 [jmp 0x0070:0000] to EA00207000 [jmp 0x0070:0200]) + 0x11b: for fat12 oem boot sector + 0x118: for fat16 oem boot sector + The standard boot sectors do not have/need this value patched. + + force use of value stored in bs by NOPping out mov [drive], dl + 0x66: 88h,56h,24h for fat16 and fat12 boot sectors + 0x4F: 88h,56h,24h for oem compatible fat16 and fat12 boot sectors + + i.e. BE CAREFUL WHEN YOU CHANGE THE BOOT SECTORS !!! + */ + if (opts->kernel.stdbs) + { + /* this sets the segment we load the kernel to, default is 0x60:0 */ + ((int *)newboot)[0x5c/sizeof(int)] = opts->kernel.loadaddr; + bsBiosMovOff = 0x66; + } + else + { + /* load segment hard coded to 0x70 in oem compatible boot sector, */ + /* this however changes the offset jumped to default 0x70:0 */ + if (fs == FAT12) + ((int *)newboot)[0x11c/sizeof(int)] = opts->kernel.loadaddr; + else + ((int *)newboot)[0x119/sizeof(int)] = opts->kernel.loadaddr; + bsBiosMovOff = 0x4F; + } + } + + if (opts->ignoreBIOS) + { + if ( (newboot[bsBiosMovOff]==0x88) && (newboot[bsBiosMovOff+1]==0x56) ) + { + newboot[bsBiosMovOff] = 0x90; /* NOP */ ++bsBiosMovOff; + newboot[bsBiosMovOff] = 0x90; /* NOP */ ++bsBiosMovOff; + newboot[bsBiosMovOff] = 0x90; /* NOP */ ++bsBiosMovOff; + } + else + { + printf("%s : fat boot sector does not match expected layout\n", pgm); + exit(1); + } } #ifdef DEBUG /* add an option to display this on user request? */ @@ -868,20 +1477,20 @@ void put_boot(int drive, char *bsFile, char *kernel_name, int load_seg, int both { int i = 0; memset(&newboot[0x1f1], ' ', 11); - while (kernel_name[i] && kernel_name[i] != '.') + while (opts->kernel.kernel[i] && opts->kernel.kernel[i] != '.') { if (i < 8) - newboot[0x1f1+i] = toupper(kernel_name[i]); + newboot[0x1f1+i] = toupper(opts->kernel.kernel[i]); i++; } - if (kernel_name[i] == '.') + if (opts->kernel.kernel[i] == '.') { /* copy extension */ int j = 0; i++; - while (kernel_name[i+j] && j < 3) + while (opts->kernel.kernel[i+j] && j < 3) { - newboot[0x1f9+j] = toupper(kernel_name[i+j]); + newboot[0x1f9+j] = toupper(opts->kernel.kernel[i+j]); j++; } } @@ -890,7 +1499,10 @@ void put_boot(int drive, char *bsFile, char *kernel_name, int load_seg, int both #ifdef DEBUG /* there's a zero past the kernel name in all boot sectors */ printf("Boot sector kernel name set to %s\n", &newboot[0x1f1]); - printf("Boot sector load segment set to %Xh\n", load_seg); + if (opts->kernel.stdbs) + printf("Boot sector kernel load segment set to %X:0h\n", opts->kernel.loadaddr); + else + printf("Boot sector kernel jmp address set to 70:%Xh\n", opts->kernel.loadaddr); #endif #ifdef DDEBUG @@ -898,66 +1510,53 @@ void put_boot(int drive, char *bsFile, char *kernel_name, int load_seg, int both dump_sector(newboot); #endif - if ((bsFile == NULL) || both) + if (opts->writeBS) { - #ifdef DEBUG - printf("Writing new bootsector to drive %c:\n", drive + 'A'); + printf("Writing new bootsector to drive %c:\n", opts->dstDrive + 'A'); #endif /* write newboot to a drive */ - if (MyAbsReadWrite(drive, 1, 0, newboot, 1) != 0) + if (MyAbsReadWrite(opts->dstDrive, 1, 0, newboot, 1) != 0) { - printf("Can't write new boot sector to drive %c:\n", drive + 'A'); + printf("Can't write new boot sector to drive %c:\n", opts->dstDrive + 'A'); exit(1); } - - if (fs==FAT32) + + /* for FAT32, we need to update the backup copy as well */ + /* unless user has asked us not to, eg for better dual boot support */ + /* Note: assuming sectors 1-5 & 7-11 (FSINFO+additional boot code) + are properly setup by prior format and need no modification + [technically freespace, etc. should be updated] + */ + if ((fs == FAT32) && !opts->skipBakBSCopy) { bs32 = (struct bootsectortype32 *)&newboot; - if ((bs32->bsBackupBoot > 0) && (bs32->bsBackupBoot < bs32->bsResSectors)) +#ifdef DEBUG + printf("writing backup bootsector to sector %d\n", bs32->bsBackupBoot); +#endif + if (MyAbsReadWrite(opts->dstDrive, 1, bs32->bsBackupBoot, newboot, 1) != 0) { + printf("Can't write backup boot sector to drive %c:\n", opts->dstDrive + 'A'); + exit(1); + } + } -#ifdef DEBUG - printf("Writing backup of new bootsector to drive %c:\n", drive + 'A'); -#endif + } /* if write boot sector to boot record*/ - /* write backup of newboot to a drive (errors acceptable here) */ - if (MyAbsReadWrite(drive, 1, bs32->bsBackupBoot, newboot, 1) != 0) - printf("Can't write backup of new boot sector to drive %c:\n", drive + 'A'); - } /* backup*/ - } /* fat32 */ - } /* if write boot sector */ - - if (bsFile != NULL) + if (opts->bsFile != NULL) { - int fd; - #ifdef DEBUG - printf("Writing new bootsector to file %s\n", bsFile); + printf("writing new bootsector to file %s\n", opts->bsFile); #endif - /* write newboot to bsFile */ - if ((fd = /* suggestion: do not trunc - allows to write to images */ - open(bsFile, O_RDWR | O_TRUNC | O_CREAT | O_BINARY, - S_IREAD | S_IWRITE)) < 0) - { - printf(" %s: can't create\"%s\"\nDOS errnum %d", pgm, bsFile, errno); - exit(1); - } - if (write(fd, newboot, SEC_SIZE) != SEC_SIZE) - { - printf("Can't write %u bytes to %s\n", SEC_SIZE, bsFile); - close(fd); - unlink(bsFile); - exit(1); - } - close(fd); - } /* if write boot sector file */ - reset_drive(drive); + saveBS(opts->bsFile, newboot); + } /* if write boot sector to file*/ + + reset_drive(opts->dstDrive); /* unlock_drive */ - generic_block_ioctl((unsigned char)drive + 1, 0x86a, NULL); + generic_block_ioctl(opts->dstDrive + 1, 0x86a, NULL); } /* put_boot */ @@ -994,175 +1593,166 @@ BOOL check_space(COUNT drive, ULONG bytes) BYTE copybuffer[COPY_SIZE]; -/* read the file, set filesize, and return a buffer */ -BYTE far * readfile(COUNT drive, BYTE * srcPath, BYTE * rootPath, - BYTE * file, ULONG * filesize, ftime *filetime) +/* allocate memory from DOS, return 0 on success, nonzero otherwise */ +int alloc_dos_mem(ULONG memsize, UWORD *theseg) { - static BYTE dest[SYS_MAXPATH], source[SYS_MAXPATH]; - UWORD ret; - int fdin; - ULONG copied = 0; - struct stat fstatbuf; - BYTE far *bufptr; - BYTE far *buffer; -#if defined(__WATCOMC__) && __WATCOMC__ < 1280 - UWORD theseg; + unsigned dseg; +#ifdef __TURBOC__ + if (allocmem((unsigned)((memsize+15)>>4), &dseg)!=-1) #else - unsigned theseg; + if (_dos_allocmem((unsigned)((memsize+15)>>4), &dseg)!=0) +#endif + return -1; /* failed to allocate memory */ + + *theseg = (UWORD)dseg; + return 0; /* success */ +} +#ifdef __TURBOC__ +#define dos_freemem freemem +#else +#define dos_freemem _dos_freemem #endif - strcpy(source, srcPath); - if (rootPath != NULL) /* trick for comspec */ - strcat(source, file); +/* copies file (path+filename specified by srcFile) to drive:\filename */ +BOOL copy(const BYTE *source, COUNT drive, const BYTE * filename) +{ + static BYTE src[SYS_MAXPATH]; + static BYTE dest[SYS_MAXPATH]; + unsigned ret; + int fdin, fdout; + ULONG copied = 0; - if (stat(source, &fstatbuf)) /* read mode */ - { - printf("%s: \"%s\" not found\n", pgm, source); + printf("Copying %s...\n", source); - if ((rootPath != NULL) && (*rootPath) /* && (errno == ENOENT) */ ) - { - sprintf(source, "%s%s", rootPath, file); - printf("%s: Trying \"%s\"\n", pgm, source); - if (stat(source, &fstatbuf)) - { - printf("%s: \"%s\" not found\n", pgm, source); - return NULL; - } - } - else - return NULL; - } - - truename(dest, source); /* use dest as buffer for truename(source) */ - strcpy(source, dest); /* write updated source string */ - sprintf(dest, "%c:\\%s", 'A' + drive, file); - if (stricmp(source, dest) == 0) + truename(src, source); + sprintf(dest, "%c:\\%s", 'A' + drive, filename); + if (stricmp(src, dest) == 0) { printf("%s: source and destination are identical: skipping \"%s\"\n", pgm, source); - *filesize = 0; /* special size */ - return (BYTE far *) 1; /* return something non-null */ + return TRUE; } if ((fdin = open(source, O_RDONLY | O_BINARY)) < 0) { printf("%s: failed to open \"%s\"\n", pgm, source); - return NULL; + return FALSE; } - *filesize = filelength(fdin); /* return size */ - /* allocate dos memory */ -#ifdef __TURBOC__ - if (allocmem((unsigned)((*filesize+15)>>4), &theseg)!=-1) -#else - if (_dos_allocmem((unsigned)((*filesize+15)>>4), &theseg)!=0) -#endif + if (!check_space(drive, filelength(fdin))) { - printf("Not enough memory to buffer %lu bytes for %s\n", *filesize, source); - return NULL; - } - bufptr = buffer = MK_FP(theseg, 0); - while ((ret = read(fdin, copybuffer, COPY_SIZE)) > 0) - { - UWORD offs; - if ((copied+ret) > *filesize) - { - ULONG dropped = copied + ret - *filesize; - printf("More bytes received than expected, dropping %lu??", dropped); - ret = ret - (UWORD) dropped; - } - for (offs = 0; offs < ret; offs++) - { - *bufptr = copybuffer[offs]; - bufptr++; - if (FP_OFF(bufptr) > 0x7777) /* watcom needs this in tiny model */ - { - bufptr = MK_FP(FP_SEG(bufptr)+0x700, FP_OFF(bufptr)-0x7000); - } - } - copied += ret; + printf("%s: Not enough space to transfer %s\n", pgm, filename); + close(fdin); + return FALSE; } -#if defined __WATCOMC__ || defined _MSC_VER /* || defined __BORLANDC__ */ - _dos_getftime(fdin, &filetime->date, &filetime->time); -#elif defined __TURBOC__ - getftime(fdin, filetime); -#endif - - close(fdin); - - printf("%lu Bytes transferred", copied); - - return buffer; -} /* readfile */ - -/* write, according to filesize, from buffer... */ -BOOL writefile(COUNT drive, BYTE * rootPath, - BYTE * file, BYTE far * buffer, ULONG filesize, ftime *filetime) -{ - static BYTE dest[SYS_MAXPATH], source[SYS_MAXPATH]; - UWORD ret; - int fdout; - ULONG copied = 0; - BYTE far * bufptr = buffer; - - if (filesize==0) /* write mode but no writing needed */ - { - printf("%s: source and destination were identical: skipping \"%s\"\n", - pgm, source); - return TRUE; - } - - if (!check_space(drive, filesize)) - { - printf("%s: Not enough space to transfer %s\n", pgm, file); - /* exit(1); too pessimistic? we might overwrite a pre-existing file */ - return FALSE; /* still pessimistic, did not even try to overwrite... */ - } - - sprintf(dest, "%c:\\%s", 'A' + drive, file); if ((fdout = open(dest, O_RDWR | O_TRUNC | O_CREAT | O_BINARY, S_IREAD | S_IWRITE)) < 0) { - printf(" %s: can't create\"%s\"\nDOS errnum %d", pgm, dest, errno); + printf(" %s: can't create\"%s\"\nDOS errnum %d\n", pgm, dest, errno); + close(fdin); return FALSE; } - while (TRUE) +#if 0 /* simple copy loop, read chunk then write chunk, repeat until all data copied */ + while ((ret = read(fdin, copybuffer, COPY_SIZE)) > 0) { - UWORD offs; - ULONG tocopy = filesize - copied; - if (tocopy==0) - break; /* done */ - if (tocopy > (ULONG)COPY_SIZE) - tocopy = COPY_SIZE; /* cannot do all in one go */ - for (offs=0; offs < tocopy; offs++) - { - copybuffer[offs] = *bufptr; - bufptr++; - if (FP_OFF(bufptr) > 0x7777) /* watcom needs this in tiny model */ - { - bufptr = MK_FP(FP_SEG(bufptr)+0x700, FP_OFF(bufptr)-0x7000); - } - } - ret = (UWORD) tocopy; if (write(fdout, copybuffer, ret) != ret) { printf("Can't write %u bytes to %s\n", ret, dest); close(fdout); unlink(dest); - break; + return FALSE; } copied += ret; } + #else /* read in whole file, then write out whole file */ + { + ULONG filesize; + UWORD theseg; + BYTE far *buffer, far *bufptr; + UWORD offs; + unsigned chunk_size; + + /* get length of file to copy, then allocate enough memory for whole file */ + filesize = filelength(fdin); + if (alloc_dos_mem(filesize, &theseg)!=0) + { + printf("Not enough memory to buffer %lu bytes for %s\n", filesize, source); + return NULL; + } + bufptr = buffer = MK_FP(theseg, 0); + /* read in whole file, a chunk at a time; adjust size of last chunk to match remaining bytes */ + chunk_size = (COPY_SIZE < filesize)?COPY_SIZE:(unsigned)filesize; + while ((ret = read(fdin, copybuffer, chunk_size)) > 0) + { + for (offs = 0; offs < ret; offs++) + { + *bufptr = copybuffer[offs]; + bufptr++; + if (FP_OFF(bufptr) > 0x7777) /* watcom needs this in tiny model */ + { + bufptr = MK_FP(FP_SEG(bufptr)+0x700, FP_OFF(bufptr)-0x7000); + } + } + /* keep track of how much read in, and only read in filesize bytes */ + copied += ret; + chunk_size = (COPY_SIZE < (filesize-copied))?COPY_SIZE:(unsigned)(filesize-copied); + } + + /* write out file, a chunk at a time; adjust size of last chunk to match remaining bytes */ + bufptr = buffer; + copied = 0; + do + { + /* keep track of how much read in, and only read in filesize bytes */ + chunk_size = (COPY_SIZE < (filesize-copied))?COPY_SIZE:(unsigned)(filesize-copied); + copied += chunk_size; + + /* setup chunk of data to be written out */ + for (offs = 0; offs < chunk_size; offs++) + { + copybuffer[offs] = *bufptr; + bufptr++; + if (FP_OFF(bufptr) > 0x7777) /* watcom needs this in tiny model */ + { + bufptr = MK_FP(FP_SEG(bufptr)+0x700, FP_OFF(bufptr)-0x7000); + } + } + + /* write the data to disk, abort on any error */ + if (write(fdout, copybuffer, chunk_size) != chunk_size) + { + printf("Can't write %u bytes to %s\n", ret, dest); + close(fdout); + unlink(dest); + return FALSE; + } + } while (copied < filesize); + + dos_freemem(theseg); + } + #endif + + { #if defined __WATCOMC__ || defined _MSC_VER /* || defined __BORLANDC__ */ - _dos_setftime(fdout, filetime->date, filetime->time); -#elif defined __TURBOC__ - setftime(fdout, filetime); +#if defined(__WATCOMC__) && __WATCOMC__ < 1280 + unsigned short date, time; +#else + unsigned date, time; #endif + _dos_getftime(fdin, &date, &time); + _dos_setftime(fdout, date, time); +#elif defined __TURBOC__ + struct ftime ftime; + getftime(fdin, &ftime); + setftime(fdout, &ftime); +#endif + } + close(fdin); close(fdout); #ifdef __SOME_OTHER_COMPILER__ @@ -1176,8 +1766,8 @@ BOOL writefile(COUNT drive, BYTE * rootPath, }; #endif - printf("%lu Bytes transferred", copied); + printf("%lu Bytes transferred\n", copied); return TRUE; -} /* writefile */ +} /* copy */