wok view BootProg/stuff/boot16.asm @ rev 24942

BootProg: clear cmdline
author Pascal Bellard <pascal.bellard@slitaz.org>
date Wed Apr 20 15:10:50 2022 +0000 (2022-04-20)
parents d8c511e24c20
children 584e67527789
line source
1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2 ;; ;;
3 ;; "BootProg" Loader v 1.5 by Alexey Frunze (c) 2000-2015 ;;
4 ;; 2-clause BSD license. ;;
5 ;; ;;
6 ;; ;;
7 ;; How to Compile: ;;
8 ;; ~~~~~~~~~~~~~~~ ;;
9 ;; nasm boot16.asm -f bin -o boot16.bin ;;
10 ;; ;;
11 ;; ;;
12 ;; Features: ;;
13 ;; ~~~~~~~~~ ;;
14 ;; - FAT12 and FAT16 supported using BIOS int 13h function 42h or 02h. ;;
15 ;; ;;
16 ;; - Loads a 16-bit executable file in the MS-DOS .COM or .EXE format ;;
17 ;; from the root directory of a disk and transfers control to it ;;
18 ;; (the "ProgramName" variable holds the name of the file to be loaded) ;;
19 ;; Its maximum size can be up to 635KB without Extended BIOS Data area. ;;
20 ;; ;;
21 ;; - Prints an error if the file isn't found or couldn't be read ;;
22 ;; ("File not found" or "Read error") ;;
23 ;; and waits for a key to be pressed, then executes the Int 19h ;;
24 ;; instruction and lets the BIOS continue bootstrap. ;;
25 ;; ;;
26 ;; - cpu 8086 is supported ;;
27 ;; ;;
28 ;; ;;
29 ;; Known Bugs: ;;
30 ;; ~~~~~~~~~~~ ;;
31 ;; - All bugs are fixed as far as I know. The boot sector has been tested ;;
32 ;; on the following types of diskettes (FAT12): ;;
33 ;; - 360KB 5"25 ;;
34 ;; - 1.2MB 5"25 ;;
35 ;; - 1.44MB 3"5 ;;
36 ;; on my HDD (FAT16). ;;
37 ;; ;;
38 ;; ;;
39 ;; Memory Layout: ;;
40 ;; ~~~~~~~~~~~~~~ ;;
41 ;; The diagram below shows the typical memory layout. The actual location ;;
42 ;; of the boot sector and its stack may be lower than A0000H if the BIOS ;;
43 ;; reserves memory for its Extended BIOS Data Area just below A0000H and ;;
44 ;; reports less than 640 KB of RAM via its Int 12H function. ;;
45 ;; ;;
46 ;; physical address ;;
47 ;; +------------------------+ 00000H ;;
48 ;; | Interrupt Vector Table | ;;
49 ;; +------------------------+ 00400H ;;
50 ;; | BIOS Data Area | ;;
51 ;; +------------------------+ 00500H ;;
52 ;; | PrtScr Status / Unused | ;;
53 ;; +------------------------+ 00600H ;;
54 ;; | Loaded Image | ;;
55 ;; +------------------------+ nnnnnH ;;
56 ;; | Available Memory | ;;
57 ;; +------------------------+ A0000H - 512 - 3KB ;;
58 ;; | Boot Sector | ;;
59 ;; +------------------------+ A0000H - 3KB ;;
60 ;; | 3KB Boot Stack | ;;
61 ;; +------------------------+ A0000H ;;
62 ;; | Video RAM | ;;
63 ;; ;;
64 ;; ;;
65 ;; Boot Image Startup (register values): ;;
66 ;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ;;
67 ;; dl = BIOS boot drive number (e.g. 0, 80H) ;;
68 ;; cs:ip = program entry point ;;
69 ;; ss:sp = program stack (don't confuse with boot sector's stack) ;;
70 ;; COM program defaults: cs = ds = es = ss = 50h, sp = 0, ip = 100h ;;
71 ;; EXE program defaults: ds = es = EXE data - 10h (fake MS-DOS psp), ;;
72 ;; ax = 0ffffh (both FCB in the PSP don't have a valid drive identifier), ;;
73 ;; cs:ip and ss:sp depends on EXE header ;;
74 ;; Magic numbers: ;;
75 ;; si = 16381 (prime number 2**14-3) ;;
76 ;; di = 32749 (prime number 2**15-19) ;;
77 ;; bp = 65521 (prime number 2**16-15) ;;
78 ;; The magic numbers let the program know whether it has been loaded by ;;
79 ;; this boot sector or by MS-DOS, which may be handy for universal, bare- ;;
80 ;; metal and MS-DOS programs. ;;
81 ;; ;;
82 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
84 %define bx(label) bx+label-boot
86 [BITS 16]
87 [CPU 8086]
89 ImageLoadSeg equ 60h
90 StackSize equ 3072 ; Stack + cluster list
92 [SECTION .text]
93 [ORG 0]
95 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
96 ;; Boot sector starts here ;;
97 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
99 boot:
100 jmp short start ; MS-DOS/Windows checks for this jump
101 nop
102 bsOemName DB "BootProg" ; 0x03
104 ;;;;;;;;;;;;;;;;;;;;;
105 ;; BPB starts here ;;
106 ;;;;;;;;;;;;;;;;;;;;;
108 bpbBytesPerSector DW 0 ; 0x0B
109 bpbSectorsPerCluster DB 0 ; 0x0D
110 bpbReservedSectors DW 0 ; 0x0E
111 bpbNumberOfFATs DB 0 ; 0x10
112 bpbRootEntries DW 0 ; 0x11
113 bpbTotalSectors DW 0 ; 0x13
114 bpbMedia DB 0 ; 0x15
115 bpbSectorsPerFAT DW 0 ; 0x16
116 bpbSectorsPerTrack DW 0 ; 0x18
117 bpbHeadsPerCylinder DW 0 ; 0x1A
118 bpbHiddenSectors DD 0 ; 0x1C
119 bpbTotalSectorsBig DD 0 ; 0x20
121 ;;;;;;;;;;;;;;;;;;;
122 ;; BPB ends here ;;
123 ;;;;;;;;;;;;;;;;;;;
125 bsDriveNumber DB 0 ; 0x24
126 bsUnused DB 0 ; 0x25
127 bsExtBootSignature DB 0 ; 0x26
128 bsSerialNumber DD 0 ; 0x27
129 bsVolumeLabel DB "NO NAME " ; 0x2B
130 bsFileSystem DB "FAT12 " ; 0x36
132 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
133 ;; Boot sector code starts here ;;
134 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
136 start:
137 cld
139 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
140 ;; How much RAM is there? ;;
141 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
143 int 12h ; get conventional memory size (in KBs)
144 mov cx, 106h
145 shl ax, cl ; and convert it to 16-byte paragraphs
147 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
148 ;; Reserve memory for the boot sector and its stack ;;
149 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
151 sub ax, (512+StackSize) /16 ; reserve bytes for the code and the stack
152 mov es, ax ; cs:0 = ds:0 = ss:0 -> top - 512 - StackSize
153 mov ss, ax
154 mov sp, 512+StackSize ; bytes 0-511 are reserved for the boot code
156 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
157 ;; Copy ourselves to top of memory ;;
158 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
160 mov si, 7C00h
161 xor di, di
162 mov ds, di
163 mov [si], dx ; store BIOS boot drive number
164 rep movsw ; move 512 bytes (+ 12)
166 ;;;;;;;;;;;;;;;;;;;;;;
167 ;; Jump to the copy ;;
168 ;;;;;;;;;;;;;;;;;;;;;;
170 push es
171 mov cl, byte main
172 push cx
173 retf
175 main:
176 %if ImageLoadSeg != main-boot
177 %if ImageLoadSeg >= 100h
178 mov cx, ImageLoadSeg
179 %else
180 mov cl, ImageLoadSeg
181 %endif
182 %endif
183 push cx
185 ;;;;;;;;;;;;;;;;;;;;;;;;;;
186 ;; Get drive parameters ;;
187 ;; Update heads count ;;
188 ;; for current BIOS ;;
189 ;;;;;;;;;;;;;;;;;;;;;;;;;;
191 mov ah, 8
192 int 13h ; may destroy SI,BP, and DS registers
193 ; update AX,BL,CX,DX,DI, and ES registers
194 push cs
195 pop ds
196 xor bx, bx
198 and cx, byte 3Fh
199 cmp [bx(bpbSectorsPerTrack)], cx
200 jne BadParams ; verify updated and validity
201 mov al, dh
202 inc ax
203 mov [bpbHeadsPerCylinder], ax
204 BadParams:
206 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
207 ;; Load FAT (FAT12: 6KB max, FAT16: 128KB max) ;;
208 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
210 pop es ; ImageLoadSeg
211 push es
213 mul bx ; dx:ax = 0 = LBA (LBA are relative to FAT)
214 mov cx, word [bx(bpbSectorsPerFAT)]
216 call ReadCXSectors ; read fat and clear ax & cx
218 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
219 ;; load the root directory in ;;
220 ;; its entirety (16KB max) ;;
221 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
223 mov al, 32
225 mul word [bx(bpbRootEntries)]
226 div word [bx(bpbBytesPerSector)]
227 xchg ax, cx ; cx = root directory size in sectors, clear ax
229 mov al, [bpbNumberOfFATs]
230 mul bp ; [bx(bpbSectorsPerFAT)], set by ReadCXSectors
232 push es
233 call ReadCXSectors ; read root directory; clear ax, cx and di
234 pop es
236 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
237 ;; Look for the COM/EXE file to load and run ;;
238 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
240 ; es:di -> root entries array
242 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
243 ;; Looks for the file/dir ProgramName ;;
244 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
245 ;; Input: ES:DI -> root directory array ;;
246 ;; Output: SI = cluster number ;;
247 ;; AX = file sector count ;;
248 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
250 FindNameCycle:
251 push di
252 mov cl, 11
253 mov si, ProgramName ; ds:si -> program name
254 repe cmpsb
255 pop di
256 je FindNameFound
257 scasb
258 je FindNameFailed ; end of root directory (NULL entry found)
259 add di, byte 31
260 dec word [bx(bpbRootEntries)]
261 jnz FindNameCycle ; next root entry
263 FindNameFailed:
264 call Error
265 db "File not found."
267 FindNameFound:
268 push si
269 mov si, [es:di+1Ah] ; si = cluster no.
270 les ax, [es:di+1Ch] ; file size
271 mov dx, es
272 div word [bx(bpbBytesPerSector)]
273 cmp bx, dx ; sector aligned ?
274 adc ax, bx ; file last sector
275 pop di ; di = ClusterList
277 pop es ; ImageLoadSeg
278 push ax
280 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
281 ;; build cluster list ;;
282 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
283 ;; Input: ES:0 -> FAT ;;
284 ;; SI = first cluster ;;
285 ;; DI = cluster list :;
286 ;; CH = 0 ;;
287 ;; Output: SI = cluster list ;;
288 ;; CH = 0 ;;
289 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
291 push di ; up to 2 * 635K / BytesPerCluster bytes
292 mov cl, 12
293 ClusterLoop:
294 mov [di], si
296 mov ax, es ; ax = FAT segment = ImageLoadSeg
297 add si, si ; si = cluster * 2
298 jnc First64
299 mov ah, (1000h+ImageLoadSeg)>>8 ; adjust segment for 2nd part of FAT16
300 First64:
301 mov dx, 0FFF8h
303 cmp [bx(bpbSectorsPerFAT)], cx ; 1..12 = FAT12, 16..256 = FAT16
304 mov ds, ax
305 ja ReadClusterFat16
307 mov dh, 0Fh
308 add si, [cs:di]
309 shr si, 1 ; si = cluster * 3 / 2
311 ReadClusterFat16:
312 lodsw ; ax = next cluster
313 jnc ReadClusterEven
315 rol ax, cl
317 ReadClusterEven:
318 and ah, dh ; mask cluster value
319 cmp ax, dx
321 push cs
322 pop ds
323 inc di
324 inc di
325 xchg ax, si
326 jc ClusterLoop
327 pop si
328 pop di ; file size in sectors
330 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
331 ;; Load the entire file ;;
332 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
333 ;; Input: ES:0 -> buffer ;;
334 ;; SI = cluster list ;;
335 ;; DI = file sectors ;;
336 ;; CH = 0 ;;
337 ;; Output: BP:0 -> buffer ;;
338 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
340 push es
342 ReadClusters:
343 lodsw
344 dec ax
345 dec ax
347 mov cl, [bx(bpbSectorsPerCluster)]
348 mul cx ; cx = sector count (ch = 0)
350 add ax, bp ; LBA for cluster data
351 adc dx, bx ; dx:ax = LBA
353 call ReadSector ; clear ax & cx, restore dx
355 jne ReadClusters
357 pop bp ; ImageLoadSeg
359 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
360 ;; Type detection, .COM or .EXE? ;;
361 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
363 mov ds, bp ; bp=ds=seg the file is loaded to
365 add bp, [bx+08h] ; bp = image base
366 mov di, [bx+18h] ; di = reloc table pointer
368 cmp word [bx], 5A4Dh ; "MZ" signature?
369 je RelocateEXE ; yes, it's an EXE program
371 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
372 ;; Setup and run a .COM program ;;
373 ;; Set CS=DS=ES=SS SP=0 IP=100h ;;
374 ;; AX=0ffffh BX=0 CX=0 DX=drive ;;
375 ;; and cmdline=void ;;
376 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
378 mov di, 100h ; ip
379 mov bp, ImageLoadSeg-10h ; "org 100h" stuff :)
380 mov ss, bp
381 xor sp, sp
382 push bp ; cs, ds and es
383 jmp short Run
385 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
386 ;; Relocate, setup and run a .EXE program ;;
387 ;; Set CS:IP, SS:SP, DS, ES and AX according ;;
388 ;; to wiki.osdev.org/MZ#Initial_Program_State ;;
389 ;; AX=0ffffh BX=0 CX=0 DX=drive cmdline=void ;;
390 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
392 ReloCycle:
393 add [di+2], bp ; item seg (abs)
394 les si, [di] ; si = item ofs, es = item seg
395 add [es:si], bp ; fixup
396 scasw ; di += 2
397 scasw ; point to next entry
399 RelocateEXE:
400 dec word [bx+06h] ; reloc items, 32768 max (128KB table)
401 jns ReloCycle
402 ; PSP don't have a valid drive identifier
403 les si, [bx+0Eh]
404 add si, bp
405 mov ss, si ; ss for EXE
406 mov sp, es ; sp for EXE
408 lea si, [bp-10h] ; ds and es both point to the segment
409 push si ; containing the PSP structure
411 add bp, [bx+16h] ; cs for EXE
412 mov di, [bx+14h] ; ip for EXE
413 Run:
414 pop ds
415 push bp
416 push di
417 push ds
418 pop es
419 mov [80h], ax ; clear cmdline
420 dec ax ; both FCB in the PSP don't have a valid drive identifier
422 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
423 ;; Set the magic numbers so the program knows that it ;;
424 ;; has been loaded by this bootsector and not by MS-DOS ;;
425 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
426 mov si, 16381 ; prime number 2**14-3
427 mov di, 32749 ; prime number 2**15-19
428 mov bp, 65521 ; prime number 2**16-15
430 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
431 ;; All done, transfer control to the program now ;;
432 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
433 retf
435 ReadCXSectors:
436 mov bp, cx
437 add bp, ax ; adjust LBA for cluster data
439 mov di, cx ; no file size limit
441 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
442 ;; Reads sectors using BIOS Int 13h ;;
443 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
444 ;; Input: DX:AX = LBA relative to FAT ;;
445 ;; BX = 0 ;;
446 ;; CX = sector count ;;
447 ;; DI = file sectors ;;
448 ;; ES:BX -> buffer address ;;
449 ;; Output: ES:BX -> next address ;;
450 ;; BX = 0 ;;
451 ;; CX or DI = 0 ;;
452 ;; DL = drive number ;;
453 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
455 ReadSector:
456 add ax, [bx(bpbHiddenSectors)]
457 adc dx, [bx(bpbHiddenSectors)+2]
458 add ax, [bx(bpbReservedSectors)]
460 push si
461 ReadSectorNext:
462 adc dx, bx
463 push di
464 push cx
466 push bx
467 push bx
468 push dx ; 32-bit LBA: up to 2TB
469 push ax
470 push es
471 push bx
472 inc bx ; sector count word = 1
473 push bx
474 dec bx
475 mov di, 16 ; packet size byte = 16, reserved byte = 0
476 push di
478 xchg ax, cx ; save low LBA
479 xchg ax, dx ; get high LBA
480 cwd ; clear dx (LBA offset <2TB)
481 div word [bx(bpbSectorsPerTrack)] ; up to 8GB disks
483 xchg ax, cx ; restore low LBA, save high LBA / SPT
484 div word [bx(bpbSectorsPerTrack)]
485 ; ax = LBA / SPT
486 ; dx = LBA % SPT = sector - 1
487 inc dx
489 xchg cx, dx ; restore high LBA / SPT, save sector no.
490 div word [bx(bpbHeadsPerCylinder)]
491 ; ax = (LBA / SPT) / HPC = cylinder
492 ; dx = (LBA / SPT) % HPC = head
493 mov ch, al
494 ; ch = LSB 0...7 of cylinder no.
495 mov al, 64
496 mul ah
497 or cl, al
498 ; cl = MSB 8...9 of cylinder no. + sector no.
499 mov dh, dl
500 ; dh = head no.
502 ReadSectorRetry:
503 mov dl, [bx]
504 ; dl = drive no.
505 mov ah, 42h ; ah = 42h = extended read function no.
506 mov si, sp
507 int 13h ; extended read sectors (DL, DS:SI)
508 jnc ReadSectorNextSegment
510 mov ax, 201h ; al = sector count = 1
511 ; ah = 2 = read function no.
512 int 13h ; read sectors (AL, CX, DX, ES:BX)
514 jnc ReadSectorNextSegment
515 cbw ; ah = 0 = reset function
516 int 13h ; reset drive (DL)
518 dec di
519 jnz ReadSectorRetry ; extra attempt
521 call Error
522 db "Read error."
524 ReadSectorNextSegment:
526 pop ax ; al = 16
527 mul byte [bx(bpbBytesPerSector)+1] ; = (bpbBytesPerSector/256)*16
528 pop cx ; sector count = 1
529 pop bx
530 add [si+6], ax ; adjust segment for next sector
531 pop es ; es:0 updated
532 pop ax
533 pop dx
534 pop di
535 pop di
537 add ax, cx ; adjust LBA for next sector
539 pop cx ; cluster sectors to read
540 pop di ; file sectors to read
541 dec di ; keep C
542 loopne ReadSectorNext ; until cluster sector count or file sector count is reached
543 pop si
544 mov ax, bx ; clear ax
545 mov dx, [bx] ; pass the BIOS boot drive to Run or Error
547 ret
549 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
550 ;; Fill free space with zeroes ;;
551 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
553 times (512-13-20-($-$$)) db 0
555 ;;;;;;;;;;;;;;;;;;;;;;;;;;
556 ;; Error Messaging Code ;;
557 ;;;;;;;;;;;;;;;;;;;;;;;;;;
559 Error:
560 pop si
562 PutStr:
563 mov ah, 0Eh
564 mov bl, 7
565 lodsb
566 int 10h
567 cmp al, "."
568 jne PutStr
570 cbw
571 int 16h ; wait for a key...
572 int 19h ; bootstrap
574 Stop:
575 hlt
576 jmp short Stop
578 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
579 ;; Name of the file to load and run ;;
580 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
582 ProgramName db "STARTUP BIN" ; name and extension each must be
583 ; padded with spaces (11 bytes total)
585 ;;;;;;;;;;;;;;;;;;;;;;;;;;
586 ;; End of the sector ID ;;
587 ;;;;;;;;;;;;;;;;;;;;;;;;;;
589 ClusterList dw 0AA55h ; BIOS checks for this ID