mirror of
https://github.com/forth-ev/VolksForth.git
synced 2024-12-01 19:49:57 +00:00
527 lines
19 KiB
NASM
527 lines
19 KiB
NASM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; "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
|