mirror of
https://github.com/forth-ev/VolksForth.git
synced 2025-04-11 07:37:02 +00:00
Tools for Bootdisk creation
This commit is contained in:
parent
dc865fdd5e
commit
2ca3cf3519
14
8086/pc-baremetal/bootdisk/Makefile
Normal file
14
8086/pc-baremetal/bootdisk/Makefile
Normal file
@ -0,0 +1,14 @@
|
||||
TARGET = mkimg144 flp144.bin
|
||||
|
||||
.PHONY: all
|
||||
all: $(TARGET)
|
||||
|
||||
flp144.bin: flp144.asm
|
||||
nasm $< -f bin -o $@
|
||||
|
||||
mkimg144: mkimg144.c
|
||||
$(CC) -o $@ $<
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f $(TARGET)
|
526
8086/pc-baremetal/bootdisk/flp144.asm
Normal file
526
8086/pc-baremetal/bootdisk/flp144.asm
Normal file
@ -0,0 +1,526 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; ;;
|
||||
;; "BootProg" Loader v 1.5 by Alexey Frunze (c) 2000-2015 ;;
|
||||
;; 2-clause BSD license. ;;
|
||||
;; ;;
|
||||
;; ;;
|
||||
;; This is a version of boot12.asm fully ready for a 1.44MB 3"5 floppy. ;;
|
||||
;; ;;
|
||||
;; ;;
|
||||
;; How to Compile: ;;
|
||||
;; ~~~~~~~~~~~~~~~ ;;
|
||||
;; nasm flp144.asm -f bin -o flp144.bin ;;
|
||||
;; ;;
|
||||
;; ;;
|
||||
;; Features: ;;
|
||||
;; ~~~~~~~~~ ;;
|
||||
;; - FAT12 supported ;;
|
||||
;; ;;
|
||||
;; - Loads a 16-bit executable file in the MS-DOS .COM or .EXE format ;;
|
||||
;; from the root directory of a disk and transfers control to it ;;
|
||||
;; (the "ProgramName" variable holds the name of the file to be loaded) ;;
|
||||
;; ;;
|
||||
;; - Prints an error if the file isn't found or couldn't be read ;;
|
||||
;; (the "RE" message stands for "Read Error", ;;
|
||||
;; the "NF" message stands for "file Not Found") ;;
|
||||
;; and waits for a key to be pressed, then executes the Int 19h ;;
|
||||
;; instruction and lets the BIOS continue bootstrap. ;;
|
||||
;; ;;
|
||||
;; ;;
|
||||
;; Known Limitations: ;;
|
||||
;; ~~~~~~~~~~~~~~~~~~ ;;
|
||||
;; - Works only on the 1st MBR partition which must be a PRI DOS partition ;;
|
||||
;; with FAT12 (File System ID: 1) ;;
|
||||
;; ;;
|
||||
;; ;;
|
||||
;; Known Bugs: ;;
|
||||
;; ~~~~~~~~~~~ ;;
|
||||
;; - All bugs are fixed as far as I know. The boot sector has been tested ;;
|
||||
;; on the following types of diskettes: ;;
|
||||
;; - 360KB 5"25 ;;
|
||||
;; - 1.2MB 5"25 ;;
|
||||
;; - 1.44MB 3"5 ;;
|
||||
;; ;;
|
||||
;; ;;
|
||||
;; Memory Layout: ;;
|
||||
;; ~~~~~~~~~~~~~~ ;;
|
||||
;; The diagram below shows the typical memory layout. The actual location ;;
|
||||
;; of the boot sector and its stack may be lower than A0000H if the BIOS ;;
|
||||
;; reserves memory for its Extended BIOS Data Area just below A0000H and ;;
|
||||
;; reports less than 640 KB of RAM via its Int 12H function. ;;
|
||||
;; ;;
|
||||
;; physical address ;;
|
||||
;; +------------------------+ 00000H ;;
|
||||
;; | Interrupt Vector Table | ;;
|
||||
;; +------------------------+ 00400H ;;
|
||||
;; | BIOS Data Area | ;;
|
||||
;; +------------------------+ 00500H ;;
|
||||
;; | PrtScr Status / Unused | ;;
|
||||
;; +------------------------+ 00600H ;;
|
||||
;; | Loaded Image | ;;
|
||||
;; +------------------------+ nnnnnH ;;
|
||||
;; | Available Memory | ;;
|
||||
;; +------------------------+ A0000H - 512 - 2KB ;;
|
||||
;; | 2KB Boot Stack | ;;
|
||||
;; +------------------------+ A0000H - 512 ;;
|
||||
;; | Boot Sector | ;;
|
||||
;; +------------------------+ A0000H ;;
|
||||
;; | Video RAM | ;;
|
||||
;; ;;
|
||||
;; ;;
|
||||
;; Boot Image Startup (register values): ;;
|
||||
;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ;;
|
||||
;; dl = BIOS boot drive number (e.g. 0, 80H) ;;
|
||||
;; cs:ip = program entry point ;;
|
||||
;; ss:sp = program stack (don't confuse with boot sector's stack) ;;
|
||||
;; COM program defaults: cs = ds = es = ss = 50h, sp = 0, ip = 100h ;;
|
||||
;; EXE program defaults: ds = es = 50h, other stuff depends on EXE header ;;
|
||||
;; Magic numbers: ;;
|
||||
;; si = 16381 (prime number 2**14-3) ;;
|
||||
;; di = 32749 (prime number 2**15-19) ;;
|
||||
;; bp = 65521 (prime number 2**16-15) ;;
|
||||
;; The magic numbers let the program know whether it has been loaded by ;;
|
||||
;; this boot sector or by MS-DOS, which may be handy for universal, bare- ;;
|
||||
;; metal and MS-DOS programs. ;;
|
||||
;; ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
[BITS 16]
|
||||
|
||||
;;? equ 0
|
||||
ImageLoadSeg equ 60h ; <=07Fh because of "push byte ImageLoadSeg" instructions
|
||||
|
||||
[SECTION .text]
|
||||
[ORG 0]
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Boot sector starts here ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
jmp short start ; MS-DOS/Windows checks for this jump
|
||||
nop
|
||||
bsOemName DB "BootProg" ; 0x03
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;
|
||||
;; BPB starts here ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
bpbBytesPerSector DW 512 ; 0x0B
|
||||
bpbSectorsPerCluster DB 1 ; 0x0D
|
||||
bpbReservedSectors DW 1 ; 0x0E
|
||||
bpbNumberOfFATs DB 2 ; 0x10
|
||||
bpbRootEntries DW 224 ; 0x11
|
||||
bpbTotalSectors DW 2880 ; 0x13
|
||||
bpbMedia DB 0F0h ; 0x15
|
||||
bpbSectorsPerFAT DW 9 ; 0x16
|
||||
bpbSectorsPerTrack DW 18 ; 0x18
|
||||
bpbHeadsPerCylinder DW 2 ; 0x1A
|
||||
bpbHiddenSectors DD 0 ; 0x1C
|
||||
bpbTotalSectorsBig DD 0 ; 0x20
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;
|
||||
;; BPB ends here ;;
|
||||
;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
bsDriveNumber DB 0 ; 0x24
|
||||
bsUnused DB 0 ; 0x25
|
||||
bsExtBootSignature DB 29H ; 0x26
|
||||
bsSerialNumber DD 11223344h ; 0x27
|
||||
bsVolumeLabel DB "NO NAME " ; 0x2B
|
||||
bsFileSystem DB "FAT12 " ; 0x36
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Boot sector code starts here ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
start:
|
||||
cld
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; How much RAM is there? ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
int 12h ; get conventional memory size (in KBs)
|
||||
shl ax, 6 ; and convert it to 16-byte paragraphs
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Reserve memory for the boot sector and its stack ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
sub ax, 512 / 16 ; reserve 512 bytes for the boot sector code
|
||||
mov es, ax ; es:0 -> top - 512
|
||||
|
||||
sub ax, 2048 / 16 ; reserve 2048 bytes for the stack
|
||||
mov ss, ax ; ss:0 -> top - 512 - 2048
|
||||
mov sp, 2048 ; 2048 bytes for the stack
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Copy ourselves to top of memory ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
mov cx, 256
|
||||
mov si, 7C00h
|
||||
xor di, di
|
||||
mov ds, di
|
||||
rep movsw
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Jump to the copy ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
push es
|
||||
push byte main
|
||||
retf
|
||||
|
||||
main:
|
||||
push cs
|
||||
pop ds
|
||||
|
||||
mov [bsDriveNumber], dl ; store BIOS boot drive number
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Reserve memory for the FAT12 image (6KB max) ;;
|
||||
;; and load it in its entirety ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
mov ax, [bpbBytesPerSector]
|
||||
shr ax, 4 ; ax = sector size in paragraphs
|
||||
mov cx, [bpbSectorsPerFAT] ; cx = FAT size in sectors
|
||||
mul cx ; ax = FAT size in paragraphs
|
||||
|
||||
mov di, ss
|
||||
sub di, ax
|
||||
mov es, di
|
||||
xor bx, bx ; es:bx -> buffer for the FAT
|
||||
|
||||
mov ax, [bpbHiddenSectors]
|
||||
mov dx, [bpbHiddenSectors+2]
|
||||
add ax, [bpbReservedSectors]
|
||||
adc dx, bx ; dx:ax = LBA
|
||||
|
||||
call ReadSector
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Reserve memory for the root directory ;;
|
||||
;; and load it in its entirety ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
mov bx, ax
|
||||
mov di, dx ; save LBA to di:bx
|
||||
|
||||
mov ax, 32
|
||||
|
||||
mov si, [bpbRootEntries]
|
||||
mul si
|
||||
div word [bpbBytesPerSector]
|
||||
mov cx, ax ; cx = root directory size in sectors
|
||||
|
||||
mov al, [bpbNumberOfFATs]
|
||||
cbw
|
||||
mul word [bpbSectorsPerFAT]
|
||||
add ax, bx
|
||||
adc dx, di ; dx:ax = LBA
|
||||
|
||||
push es ; push FAT segment (2nd parameter)
|
||||
|
||||
push byte ImageLoadSeg
|
||||
pop es
|
||||
xor bx, bx ; es:bx -> buffer for root directory
|
||||
|
||||
call ReadSector
|
||||
|
||||
add ax, cx
|
||||
adc dx, bx ; adjust LBA for cluster data
|
||||
|
||||
push dx
|
||||
push ax ; push LBA for data (1st parameter)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Look for the COM/EXE file to load and run ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
mov di, bx ; es:di -> root entries array
|
||||
mov dx, si ; dx = number of root entries
|
||||
mov si, ProgramName ; ds:si -> program name
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Looks for a file/dir by its name ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Input: DS:SI -> file name (11 chars) ;;
|
||||
;; ES:DI -> root directory array ;;
|
||||
;; DX = number of root entries ;;
|
||||
;; Output: SI = cluster number ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
FindName:
|
||||
mov cx, 11
|
||||
FindNameCycle:
|
||||
cmp byte [es:di], ch
|
||||
je FindNameFailed ; end of root directory
|
||||
pusha
|
||||
repe cmpsb
|
||||
popa
|
||||
je FindNameFound
|
||||
add di, 32
|
||||
dec dx
|
||||
jnz FindNameCycle ; next root entry
|
||||
FindNameFailed:
|
||||
jmp ErrFind
|
||||
FindNameFound:
|
||||
mov si, [es:di+1Ah] ; si = cluster no.
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Load the entire file ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
ReadNextCluster:
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Reads a FAT12 cluster ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Inout: ES:BX -> buffer ;;
|
||||
;; SI = cluster no ;;
|
||||
;; Output: SI = next cluster ;;
|
||||
;; ES:BX -> next addr ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
ReadCluster:
|
||||
mov bp, sp
|
||||
|
||||
lea ax, [si-2]
|
||||
xor ch, ch
|
||||
mov cl, [bpbSectorsPerCluster]
|
||||
; cx = sector count
|
||||
mul cx
|
||||
|
||||
add ax, [bp]
|
||||
adc dx, [bp+1*2]
|
||||
; dx:ax = LBA
|
||||
|
||||
call ReadSector
|
||||
|
||||
mov ax, [bpbBytesPerSector]
|
||||
shr ax, 4 ; ax = paragraphs per sector
|
||||
mul cx ; ax = paragraphs read
|
||||
|
||||
mov cx, es
|
||||
add cx, ax
|
||||
mov es, cx ; es:bx updated
|
||||
|
||||
mov ax, 3
|
||||
mul si
|
||||
shr ax, 1
|
||||
xchg ax, si ; si = cluster * 3 / 2
|
||||
|
||||
push ds
|
||||
mov ds, [bp+2*2] ; ds = FAT segment
|
||||
mov si, [si] ; si = next cluster
|
||||
pop ds
|
||||
|
||||
jnc ReadClusterEven
|
||||
|
||||
shr si, 4
|
||||
|
||||
ReadClusterEven:
|
||||
and si, 0FFFh ; mask cluster value
|
||||
ReadClusterDone:
|
||||
|
||||
cmp si, 0FF8h
|
||||
jc ReadNextCluster ; if not End Of File
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Type detection, .COM or .EXE? ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
push byte ImageLoadSeg
|
||||
pop ds
|
||||
mov ax, ds ; ax=ds=seg the file is loaded to
|
||||
|
||||
cmp word [0], 5A4Dh ; "MZ" signature?
|
||||
|
||||
je RelocateEXE ; yes, it's an EXE program
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Setup and run a .COM program ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
sub ax, 10h ; "org 100h" stuff :)
|
||||
mov es, ax
|
||||
mov ds, ax
|
||||
mov ss, ax
|
||||
xor sp, sp
|
||||
push es
|
||||
push word 100h
|
||||
jmp short Run
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Relocate, setup and run a .EXE program ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
RelocateEXE:
|
||||
|
||||
add ax, [08h] ; ax = image base
|
||||
mov cx, [06h] ; cx = reloc items
|
||||
mov bx, [18h] ; bx = reloc table pointer
|
||||
|
||||
jcxz RelocationDone
|
||||
|
||||
ReloCycle:
|
||||
mov di, [bx] ; di = item ofs
|
||||
mov dx, [bx+2] ; dx = item seg (rel)
|
||||
add dx, ax ; dx = item seg (abs)
|
||||
|
||||
push ds
|
||||
mov ds, dx ; ds = dx
|
||||
add [di], ax ; fixup
|
||||
pop ds
|
||||
|
||||
add bx, 4 ; point to next entry
|
||||
loop ReloCycle
|
||||
|
||||
RelocationDone:
|
||||
|
||||
mov bx, ax
|
||||
add bx, [0Eh]
|
||||
mov ss, bx ; ss for EXE
|
||||
mov sp, [10h] ; sp for EXE
|
||||
|
||||
add ax, [16h] ; cs
|
||||
push ax
|
||||
push word [14h] ; ip
|
||||
Run:
|
||||
mov dl, [cs:bsDriveNumber] ; pass the BIOS boot drive
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Set the magic numbers so the program knows that it ;;
|
||||
;; has been loaded by this bootsector and not by MS-DOS ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
mov si, 16381 ; prime number 2**14-3
|
||||
mov di, 32749 ; prime number 2**15-19
|
||||
mov bp, 65521 ; prime number 2**16-15
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; All done, transfer control to the program now ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
retf
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Reads a sector using BIOS Int 13h fn 2 ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Input: DX:AX = LBA ;;
|
||||
;; CX = sector count ;;
|
||||
;; ES:BX -> buffer address ;;
|
||||
;; Output: CF = 1 if error ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
ReadSector:
|
||||
pusha
|
||||
|
||||
ReadSectorNext:
|
||||
mov di, 5 ; attempts to read
|
||||
|
||||
ReadSectorRetry:
|
||||
pusha
|
||||
|
||||
div word [bpbSectorsPerTrack]
|
||||
; ax = LBA / SPT
|
||||
; dx = LBA % SPT = sector - 1
|
||||
|
||||
mov cx, dx
|
||||
inc cx
|
||||
; cx = sector no.
|
||||
|
||||
xor dx, dx
|
||||
div word [bpbHeadsPerCylinder]
|
||||
; ax = (LBA / SPT) / HPC = cylinder
|
||||
; dx = (LBA / SPT) % HPC = head
|
||||
|
||||
mov ch, al
|
||||
; ch = LSB 0...7 of cylinder no.
|
||||
shl ah, 6
|
||||
or cl, ah
|
||||
; cl = MSB 8...9 of cylinder no. + sector no.
|
||||
|
||||
mov dh, dl
|
||||
; dh = head no.
|
||||
|
||||
mov dl, [bsDriveNumber]
|
||||
; dl = drive no.
|
||||
|
||||
mov ax, 201h
|
||||
; al = sector count = 1
|
||||
; ah = 2 = read function no.
|
||||
|
||||
int 13h ; read sectors
|
||||
jnc ReadSectorDone ; CF = 0 if no error
|
||||
|
||||
xor ah, ah ; ah = 0 = reset function
|
||||
int 13h ; reset drive
|
||||
|
||||
popa
|
||||
dec di
|
||||
jnz ReadSectorRetry ; extra attempt
|
||||
jmp short ErrRead
|
||||
|
||||
ReadSectorDone:
|
||||
popa
|
||||
dec cx
|
||||
jz ReadSectorDone2 ; last sector
|
||||
|
||||
add bx, [bpbBytesPerSector] ; adjust offset for next sector
|
||||
add ax, 1
|
||||
adc dx, 0 ; adjust LBA for next sector
|
||||
jmp short ReadSectorNext
|
||||
|
||||
ReadSectorDone2:
|
||||
popa
|
||||
ret
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Error Messaging Code ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
ErrRead:
|
||||
mov si, MsgErrRead
|
||||
jmp short Error
|
||||
ErrFind:
|
||||
mov si, MsgErrFind
|
||||
Error:
|
||||
mov ah, 0Eh
|
||||
mov bx, 7
|
||||
|
||||
lodsb
|
||||
int 10h ; 1st char
|
||||
lodsb
|
||||
int 10h ; 2nd char
|
||||
|
||||
xor ah, ah
|
||||
int 16h ; wait for a key...
|
||||
mov dl, [bsDriveNumber] ; restore BIOS boot drive number
|
||||
int 19h ; bootstrap
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; String constants ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
MsgErrRead db "RE"
|
||||
MsgErrFind db "NF"
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Fill free space with zeroes ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
times (512-13-($-$$)) db 0
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Name of the file to load and run ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
ProgramName db "FORTH COM" ; name and extension each must be
|
||||
; padded with spaces (11 bytes total)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; End of the sector ID ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
dw 0AA55h ; BIOS checks for this ID
|
612
8086/pc-baremetal/bootdisk/mkimg144.c
Normal file
612
8086/pc-baremetal/bootdisk/mkimg144.c
Normal file
@ -0,0 +1,612 @@
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef unsigned char uchar, uint8;
|
||||
typedef unsigned short uint16;
|
||||
#ifndef __SMALLER_C__
|
||||
#if UINT_MAX >= 0xFFFFFFFF
|
||||
typedef unsigned uint32;
|
||||
#else
|
||||
typedef unsigned long uint32;
|
||||
#endif
|
||||
#else
|
||||
typedef unsigned long uint32;
|
||||
#endif
|
||||
typedef unsigned uint;
|
||||
typedef unsigned long ulong;
|
||||
|
||||
#ifndef __SMALLER_C__
|
||||
#define C_ASSERT(expr) extern char CAssertExtern[(expr)?1:-1]
|
||||
C_ASSERT(CHAR_BIT == 8);
|
||||
C_ASSERT(sizeof(uint16) == 2);
|
||||
C_ASSERT(sizeof(uint32) == 4);
|
||||
#endif
|
||||
|
||||
#pragma pack (push, 1)
|
||||
|
||||
typedef struct tFATBPB1
|
||||
{
|
||||
uint16 BytesPerSector;
|
||||
uint8 SectorsPerCluster;
|
||||
uint16 ReservedSectorsCount;
|
||||
uint8 NumberOfFATs;
|
||||
uint16 RootEntriesCount;
|
||||
uint16 TotalSectorsCount16;
|
||||
uint8 MediaType;
|
||||
uint16 SectorsPerFAT1x;
|
||||
uint16 SectorsPerTrack;
|
||||
uint16 HeadsPerCylinder;
|
||||
uint32 HiddenSectorsCount;
|
||||
uint32 TotalSectorsCount32;
|
||||
} tFATBPB1;
|
||||
|
||||
typedef union tFATBPB2
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint8 DriveNumber;
|
||||
uint8 reserved1;
|
||||
uint8 ExtendedBootSignature;
|
||||
uint32 VolumeSerialNumber;
|
||||
char VolumeLabel[11];
|
||||
char FileSystemName[8];
|
||||
uchar aBootCode1x[0x1C];
|
||||
} FAT1x;
|
||||
struct
|
||||
{
|
||||
uint32 SectorsPerFAT32;
|
||||
uint16 ExtendedFlags;
|
||||
uint16 FSVersion;
|
||||
uint32 RootDirectoryClusterNo;
|
||||
uint16 FSInfoSectorNo;
|
||||
uint16 BackupBootSectorNo;
|
||||
uint8 reserved[12];
|
||||
uint8 DriveNumber;
|
||||
uint8 reserved1;
|
||||
uint8 ExtendedBootSignature;
|
||||
uint32 VolumeSerialNumber;
|
||||
char VolumeLabel[11];
|
||||
char FileSystemName[8];
|
||||
} FAT32;
|
||||
} tFATBPB2;
|
||||
|
||||
typedef struct tFATBPB
|
||||
{
|
||||
tFATBPB1 BPB1;
|
||||
tFATBPB2 BPB2;
|
||||
} tFATBPB;
|
||||
|
||||
typedef struct tFATBootSector
|
||||
{
|
||||
uchar aJump[3];
|
||||
char OEMName[8];
|
||||
tFATBPB BPB;
|
||||
uchar aBootCode32[0x1A4];
|
||||
uint16 Signature0xAA55;
|
||||
} tFATBootSector;
|
||||
|
||||
typedef enum tFATDirEntryAttribute
|
||||
{
|
||||
dea_READ_ONLY = 0x01,
|
||||
dea_HIDDEN = 0x02,
|
||||
dea_SYSTEM = 0x04,
|
||||
dea_VOLUME_ID = 0x08,
|
||||
dea_DIRECTORY = 0x10,
|
||||
dea_ARCHIVE = 0x20,
|
||||
dea_LONG_NAME = dea_READ_ONLY|dea_HIDDEN|dea_SYSTEM|dea_VOLUME_ID
|
||||
} tFATDirEntryAttribute;
|
||||
|
||||
typedef struct tFATDirectoryEntry
|
||||
{
|
||||
char Name[8];
|
||||
char Extension[3];
|
||||
uint8 Attribute;
|
||||
uint8 WinNTreserved;
|
||||
uint8 CreationTimeSecTenths;
|
||||
uint16 CreationTime2Secs;
|
||||
uint16 CreationDate;
|
||||
uint16 LastAccessDate;
|
||||
uint16 FirstClusterHiWord;
|
||||
uint16 LastWriteTime;
|
||||
uint16 LastWriteDate;
|
||||
uint16 FirstClusterLoWord;
|
||||
uint32 Size;
|
||||
} tFATDirectoryEntry;
|
||||
|
||||
#define DELETED_DIR_ENTRY_MARKER 0xE5
|
||||
|
||||
#pragma pack (pop)
|
||||
|
||||
#ifndef __SMALLER_C_32__
|
||||
C_ASSERT(sizeof(tFATBootSector) == 512);
|
||||
C_ASSERT(sizeof(tFATDirectoryEntry) == 32);
|
||||
#endif
|
||||
|
||||
#define FBUF_SIZE 1024
|
||||
|
||||
char* BootSectName;
|
||||
|
||||
char* OutName = "floppy.img";
|
||||
|
||||
int UniqueSerial;
|
||||
|
||||
FILE* fout;
|
||||
|
||||
tFATBootSector BootSector;
|
||||
uint32 Fat1Lba;
|
||||
uint32 SectorsPerFat;
|
||||
uint32 Fats;
|
||||
uint32 RootDirLba;
|
||||
uint32 DirEntriesPerSector;
|
||||
uint32 RootDirEntries;
|
||||
uint32 RootDirSectors;
|
||||
uint32 Cluster2Lba;
|
||||
uint32 SectorsPerCluster;
|
||||
uint32 ClusterSize;
|
||||
uint32 DataSectors;
|
||||
uint32 Clusters;
|
||||
|
||||
uint8 FatSector[512];
|
||||
uint32 Cluster;
|
||||
|
||||
tFATDirectoryEntry RootDirSector[512 / sizeof(tFATDirectoryEntry)];
|
||||
uint32 RootDirEntryIdx;
|
||||
|
||||
void error(char* format, ...)
|
||||
{
|
||||
#ifndef __SMALLER_C__
|
||||
va_list vl;
|
||||
va_start(vl, format);
|
||||
#else
|
||||
void* vl = &format + 1;
|
||||
#endif
|
||||
|
||||
if (fout)
|
||||
fclose(fout);
|
||||
remove(OutName);
|
||||
|
||||
puts("");
|
||||
|
||||
vprintf(format, vl);
|
||||
|
||||
#ifndef __SMALLER_C__
|
||||
va_end(vl);
|
||||
#endif
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
FILE* Fopen(const char* filename, const char* mode)
|
||||
{
|
||||
FILE* stream = fopen(filename, mode);
|
||||
if (!stream)
|
||||
error("Can't open/create file \"%s\"\n", filename);
|
||||
return stream;
|
||||
}
|
||||
|
||||
void Fclose(FILE* stream)
|
||||
{
|
||||
if (fclose(stream))
|
||||
error("Can't close a file\n");
|
||||
}
|
||||
|
||||
void Fseek(FILE* stream, long offset, int whence)
|
||||
{
|
||||
int r = fseek(stream, offset, whence);
|
||||
if (r)
|
||||
error("Can't seek a file\n");
|
||||
}
|
||||
|
||||
void Fread(void* ptr, size_t size, FILE* stream)
|
||||
{
|
||||
size_t r = fread(ptr, 1, size, stream);
|
||||
if (r != size)
|
||||
error("Can't read a file\n");
|
||||
}
|
||||
|
||||
void Fwrite(const void* ptr, size_t size, FILE* stream)
|
||||
{
|
||||
size_t r = fwrite(ptr, 1, size, stream);
|
||||
if (r != size)
|
||||
error("Can't write a file\n");
|
||||
}
|
||||
|
||||
void FillWithByte(unsigned char byte, unsigned long size, FILE* stream)
|
||||
{
|
||||
static unsigned char buf[FBUF_SIZE];
|
||||
memset(buf, byte, FBUF_SIZE);
|
||||
while (size)
|
||||
{
|
||||
unsigned long csz = size;
|
||||
if (csz > FBUF_SIZE)
|
||||
csz = FBUF_SIZE;
|
||||
Fwrite(buf, csz, stream);
|
||||
size -= csz;
|
||||
}
|
||||
}
|
||||
|
||||
// Determines binary file size portably (when stat()/fstat() aren't available)
|
||||
long fsize(FILE* binaryStream)
|
||||
{
|
||||
long ofs, ofs2;
|
||||
int result;
|
||||
|
||||
if (fseek(binaryStream, 0, SEEK_SET) != 0 ||
|
||||
fgetc(binaryStream) == EOF)
|
||||
return 0;
|
||||
|
||||
ofs = 1;
|
||||
|
||||
while ((result = fseek(binaryStream, ofs, SEEK_SET)) == 0 &&
|
||||
(result = (fgetc(binaryStream) == EOF)) == 0 &&
|
||||
ofs <= LONG_MAX / 4 + 1)
|
||||
ofs *= 2;
|
||||
|
||||
// If the last seek failed, back up to the last successfully seekable offset
|
||||
if (result != 0)
|
||||
ofs /= 2;
|
||||
|
||||
for (ofs2 = ofs / 2; ofs2 != 0; ofs2 /= 2)
|
||||
if (fseek(binaryStream, ofs + ofs2, SEEK_SET) == 0 &&
|
||||
fgetc(binaryStream) != EOF)
|
||||
ofs += ofs2;
|
||||
|
||||
// Return -1 for files longer than LONG_MAX
|
||||
if (ofs == LONG_MAX)
|
||||
return -1;
|
||||
|
||||
return ofs + 1;
|
||||
}
|
||||
|
||||
void FlushFatSector(void)
|
||||
{
|
||||
uint32 ofs = (Cluster * 3 / 2) & 511;
|
||||
uint32 i;
|
||||
|
||||
if (ofs == 0 && (Cluster & 1) == 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < Fats; i++)
|
||||
{
|
||||
uint32 ofs = Fat1Lba + i * SectorsPerFat;
|
||||
ofs += (Cluster * 3 / 2) / 512;
|
||||
Fseek(fout, ofs * 512, SEEK_SET);
|
||||
Fwrite(FatSector, sizeof FatSector, fout);
|
||||
}
|
||||
|
||||
memset(FatSector, 0, sizeof FatSector);
|
||||
}
|
||||
|
||||
void ChainCluster(uint32 nextCluster)
|
||||
{
|
||||
uint32 ofs = (Cluster * 3 / 2) & 511;
|
||||
|
||||
if (Cluster & 1)
|
||||
FatSector[ofs] |= nextCluster << 4;
|
||||
else
|
||||
FatSector[ofs] = nextCluster;
|
||||
|
||||
if (ofs == 511)
|
||||
FlushFatSector();
|
||||
|
||||
ofs = (ofs + 1) & 511;
|
||||
|
||||
if (Cluster & 1)
|
||||
FatSector[ofs] = nextCluster >> 4;
|
||||
else
|
||||
FatSector[ofs] = (nextCluster >> 8) & 0xF;
|
||||
|
||||
if (ofs == 511 && (Cluster & 1))
|
||||
FlushFatSector();
|
||||
|
||||
Cluster++;
|
||||
}
|
||||
|
||||
void FlushRootDirSector(void)
|
||||
{
|
||||
uint32 ofs;
|
||||
|
||||
if (RootDirEntryIdx % DirEntriesPerSector == 0)
|
||||
return;
|
||||
|
||||
ofs = RootDirLba + RootDirEntryIdx / DirEntriesPerSector;
|
||||
|
||||
Fseek(fout, ofs * 512, SEEK_SET);
|
||||
Fwrite(RootDirSector, sizeof RootDirSector, fout);
|
||||
}
|
||||
|
||||
void AddRootDirEntry(tFATDirectoryEntry* de)
|
||||
{
|
||||
RootDirSector[RootDirEntryIdx % DirEntriesPerSector] = *de;
|
||||
|
||||
if ((RootDirEntryIdx + 1) % DirEntriesPerSector == 0)
|
||||
FlushRootDirSector();
|
||||
|
||||
RootDirEntryIdx++;
|
||||
}
|
||||
|
||||
void Init(void)
|
||||
{
|
||||
if (BootSectName)
|
||||
{
|
||||
FILE* fsect = Fopen(BootSectName, "rb");
|
||||
Fread(&BootSector, sizeof BootSector, fsect);
|
||||
Fclose(fsect);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(BootSector.OEMName, "BootProg", 8);
|
||||
memcpy(BootSector.BPB.BPB2.FAT1x.VolumeLabel, "NO NAME ", 11);
|
||||
memcpy(BootSector.BPB.BPB2.FAT1x.FileSystemName, "FAT12 ", 8);
|
||||
BootSector.aJump[0] = 0xEB; // jmp short $+0x3E
|
||||
BootSector.aJump[1] = 0x3C;
|
||||
BootSector.aJump[2] = 0x90; // nop
|
||||
// TBD??? replace the below with code to print an error message like "Not a system/bootable disk"?
|
||||
BootSector.BPB.BPB2.FAT1x.aBootCode1x[0] = 0xF4; // hlt
|
||||
BootSector.BPB.BPB2.FAT1x.aBootCode1x[1] = 0xEB; // jmp short $-1
|
||||
BootSector.BPB.BPB2.FAT1x.aBootCode1x[2] = 0xFD;
|
||||
}
|
||||
|
||||
fout = Fopen(OutName, "wb");
|
||||
|
||||
BootSector.BPB.BPB1.BytesPerSector = 512; // note, we're normally assuming 512 bytes per sector everywhere
|
||||
BootSector.BPB.BPB1.SectorsPerCluster = 1;
|
||||
BootSector.BPB.BPB1.ReservedSectorsCount = 1; // includes the boot sector
|
||||
BootSector.BPB.BPB1.NumberOfFATs = 2;
|
||||
BootSector.BPB.BPB1.RootEntriesCount = 224; // must be a multiple of 16 (16 32-byte entries in 512-byte sector)
|
||||
BootSector.BPB.BPB1.TotalSectorsCount16 = 2880;
|
||||
BootSector.BPB.BPB1.MediaType = 0xF0;
|
||||
BootSector.BPB.BPB1.SectorsPerFAT1x = 9;
|
||||
BootSector.BPB.BPB1.SectorsPerTrack = 18;
|
||||
BootSector.BPB.BPB1.HeadsPerCylinder = 2;
|
||||
BootSector.BPB.BPB1.HiddenSectorsCount = 0;
|
||||
BootSector.BPB.BPB1.TotalSectorsCount32 = 0;
|
||||
BootSector.BPB.BPB2.FAT1x.DriveNumber = 0;
|
||||
BootSector.BPB.BPB2.FAT1x.reserved1 = 0;
|
||||
BootSector.BPB.BPB2.FAT1x.ExtendedBootSignature = 0x29;
|
||||
BootSector.BPB.BPB2.FAT1x.VolumeSerialNumber = 0x11223344;
|
||||
if (UniqueSerial)
|
||||
BootSector.BPB.BPB2.FAT1x.VolumeSerialNumber = time(NULL);
|
||||
BootSector.Signature0xAA55 = 0xAA55;
|
||||
|
||||
// Write the boot sector
|
||||
Fwrite(&BootSector, sizeof BootSector, fout);
|
||||
|
||||
// Zero out the rest of the image
|
||||
FillWithByte(0, (BootSector.BPB.BPB1.TotalSectorsCount16 - 1) * 512UL, fout);
|
||||
|
||||
// FAT12's first two entries need special initialization
|
||||
ChainCluster(0xF00 | BootSector.BPB.BPB1.MediaType);
|
||||
ChainCluster(0xFFF);
|
||||
|
||||
// Helper variables
|
||||
|
||||
Fat1Lba = BootSector.BPB.BPB1.ReservedSectorsCount;
|
||||
SectorsPerFat = BootSector.BPB.BPB1.SectorsPerFAT1x;
|
||||
Fats = BootSector.BPB.BPB1.NumberOfFATs;
|
||||
|
||||
RootDirLba = Fat1Lba + SectorsPerFat * Fats;
|
||||
DirEntriesPerSector = 512 / sizeof(tFATDirectoryEntry);
|
||||
RootDirEntries = BootSector.BPB.BPB1.RootEntriesCount;
|
||||
RootDirSectors = (RootDirEntries * sizeof(tFATDirectoryEntry) + 511) / 512;
|
||||
|
||||
Cluster2Lba = RootDirLba + RootDirSectors;
|
||||
SectorsPerCluster = BootSector.BPB.BPB1.SectorsPerCluster;
|
||||
ClusterSize = SectorsPerCluster * 512;
|
||||
DataSectors = BootSector.BPB.BPB1.TotalSectorsCount16 -
|
||||
BootSector.BPB.BPB1.ReservedSectorsCount - SectorsPerFat * Fats - RootDirSectors;
|
||||
Clusters = DataSectors / SectorsPerCluster;
|
||||
}
|
||||
|
||||
void Done(void)
|
||||
{
|
||||
FlushFatSector();
|
||||
FlushRootDirSector();
|
||||
Fclose(fout);
|
||||
}
|
||||
|
||||
void NameTo8Dot3Name(const char* in, char out[8 + 3])
|
||||
{
|
||||
static const char aInvalid8Dot3NameChars[] = "\"*+,./:;<=>?[\\]|";
|
||||
int i, j;
|
||||
int namelen = 0, dots = 0, extlen = 0;
|
||||
|
||||
memset(out, ' ', 8 + 3);
|
||||
|
||||
if (*in == '\0' || *in == '.')
|
||||
goto lerr;
|
||||
|
||||
for (j = i = 0; in[i]; i++)
|
||||
{
|
||||
int c = (unsigned char)in[i];
|
||||
if (i >= 12) // at most 12 input chars can fit into an 8.3 name
|
||||
goto lerr;
|
||||
if (i == 0 && c == 0xE5)
|
||||
{
|
||||
// 0xE5 in the first character of the name is a marker for deleted files,
|
||||
// it needs to be translated to 0x05
|
||||
c = 0x05;
|
||||
}
|
||||
else if (c == '.')
|
||||
{
|
||||
if (dots++) // at most one dot allowed
|
||||
goto lerr;
|
||||
j = 8; // now writing extension
|
||||
continue;
|
||||
}
|
||||
if (c <= 0x20 || strchr(aInvalid8Dot3NameChars, c) != NULL)
|
||||
goto lerr;
|
||||
if (dots)
|
||||
{
|
||||
if (++extlen > 3) // at most 3 chars in extension
|
||||
goto lerr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (++namelen > 8) // at most 8 chars in name
|
||||
goto lerr;
|
||||
}
|
||||
if (c >= 'a' && c <= 'z')
|
||||
c -= 'a' - 'A';
|
||||
out[j++] = c;
|
||||
}
|
||||
|
||||
// TBD??? error out on the following reserved names: "COM1"-"COM9", "CON", "LPT1"-"LPT9", "NUL", "PRN"?
|
||||
|
||||
return;
|
||||
|
||||
lerr:
|
||||
error("Can't convert \"%s\" to an 8.3 DOS name\n", in);
|
||||
}
|
||||
|
||||
void AddFile(char* fname)
|
||||
{
|
||||
char* pslash = strrchr(fname, '/');
|
||||
char* pbackslash = strrchr(fname, '\\');
|
||||
char* pname;
|
||||
char name8_3[8 + 3];
|
||||
FILE* f;
|
||||
long size;
|
||||
tFATDirectoryEntry de;
|
||||
uint32 ofs;
|
||||
|
||||
// First, find where the path ends in the file name, if any
|
||||
|
||||
// In DOS/Windows paths can contain either '\\' or '/' as a separator between directories,
|
||||
// choose the right-most
|
||||
if (pslash && pbackslash)
|
||||
{
|
||||
if (pslash < pbackslash)
|
||||
pslash = pbackslash;
|
||||
}
|
||||
else if (!pslash)
|
||||
{
|
||||
pslash = pbackslash;
|
||||
}
|
||||
// If there's no slash, it could be "c:file"
|
||||
if (!pslash && ((*fname >= 'A' && *fname <= 'Z') || (*fname >= 'a' && *fname <= 'z')) && fname[1] == ':')
|
||||
pslash = fname + 1;
|
||||
|
||||
pname = pslash ? pslash + 1 : fname;
|
||||
|
||||
// Convert the name to 8.3
|
||||
NameTo8Dot3Name(pname, name8_3);
|
||||
|
||||
// TBD!!! error out on duplicate files/names
|
||||
|
||||
// Copy the file
|
||||
|
||||
f = Fopen(fname, "rb");
|
||||
|
||||
// Prepare the directory entry
|
||||
memset(&de, 0, sizeof de);
|
||||
memcpy(de.Name, name8_3, 8 + 3);
|
||||
de.Attribute = dea_ARCHIVE;
|
||||
de.Size = size = fsize(f);
|
||||
if (RootDirEntryIdx >= RootDirEntries ||
|
||||
size < 0 || (unsigned long)size > Clusters * ClusterSize)
|
||||
error("No space for file \"%s\"", fname);
|
||||
if (size)
|
||||
{
|
||||
de.FirstClusterLoWord = Cluster;
|
||||
de.FirstClusterHiWord = Cluster >> 16;
|
||||
}
|
||||
// TBD??? set file date/time to now?
|
||||
de.LastWriteDate = ((1990 - 1980) << 9) | (1 << 5) | 1; // 1990/01/01
|
||||
de.LastWriteTime = (12 << 11) | (0 << 5) | (0 >> 1); // 12(PM):00:00
|
||||
|
||||
// Seek both files
|
||||
Fseek(f, 0, SEEK_SET);
|
||||
ofs = Cluster2Lba + (Cluster - 2) * SectorsPerCluster;
|
||||
Fseek(fout, ofs * 512, SEEK_SET);
|
||||
|
||||
// Copy data sectors
|
||||
while (size)
|
||||
{
|
||||
uint8 sector[512];
|
||||
long sz = (size > 512) ? 512 : size;
|
||||
|
||||
memset(sector, 0, 512); // pad with zeroes the last partial sector
|
||||
Fread(sector, sz, f);
|
||||
|
||||
Fwrite(sector, 512, fout);
|
||||
|
||||
size -= sz;
|
||||
}
|
||||
|
||||
// Allocate and chain clusters in the FAT
|
||||
size = de.Size;
|
||||
while (size)
|
||||
{
|
||||
if (size > (long)ClusterSize)
|
||||
{
|
||||
// There's at least one more cluster in the chain
|
||||
ChainCluster(Cluster + 1);
|
||||
size -= ClusterSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No more clusters, this is the last one in the chain
|
||||
ChainCluster(0xFF8);
|
||||
size = 0;
|
||||
}
|
||||
Clusters--;
|
||||
}
|
||||
|
||||
// Write the directory entry
|
||||
AddRootDirEntry(&de);
|
||||
|
||||
Fclose(f);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
if (!strcmp(argv[i], "-o"))
|
||||
{
|
||||
if (i + 1 < argc)
|
||||
{
|
||||
argv[i++] = NULL;
|
||||
OutName = argv[i];
|
||||
argv[i] = NULL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(argv[i], "-bs"))
|
||||
{
|
||||
if (i + 1 < argc)
|
||||
{
|
||||
argv[i++] = NULL;
|
||||
BootSectName = argv[i];
|
||||
argv[i] = NULL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(argv[i], "-us"))
|
||||
{
|
||||
UniqueSerial = 1;
|
||||
argv[i++] = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argv[i][0] == '-')
|
||||
error("Invalid or unsupported command line option\n");
|
||||
}
|
||||
|
||||
Init();
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
if (argv[i])
|
||||
AddFile(argv[i]);
|
||||
|
||||
Done();
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user