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

Up alpine (2.26)
author Pascal Bellard <pascal.bellard@slitaz.org>
date Fri Jun 17 09:35:48 2022 +0000 (23 months ago)
parents 584e67527789
children b56b38cfd475
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 ;; ax = 0ffffh (both FCB in the PSP don't have a valid drive identifier), ;;
68 ;; bx = cx = 0, dl = BIOS boot drive number (e.g. 0, 80H) ;;
69 ;; cs:ip = program entry point ;;
70 ;; ss:sp = program stack (don't confuse with boot sector's stack) ;;
71 ;; COM program defaults: cs = ds = es = ss = 50h, sp = 0, ip = 100h ;;
72 ;; EXE program defaults: ds = es = EXE data - 10h (fake MS-DOS psp), ;;
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 ;; The command line contains no arguments. ;;
82 ;; ;;
83 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
85 %define bx(label) bx+label-boot
87 [BITS 16]
88 [CPU 8086]
90 ImageLoadSeg equ 60h
91 StackSize equ 3072 ; Stack + cluster list
93 [SECTION .text]
94 [ORG 0]
96 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
97 ;; Boot sector starts here ;;
98 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
100 boot:
101 jmp short start ; MS-DOS/Windows checks for this jump
102 nop
103 bsOemName DB "BootProg" ; 0x03
105 ;;;;;;;;;;;;;;;;;;;;;
106 ;; BPB starts here ;;
107 ;;;;;;;;;;;;;;;;;;;;;
109 bpbBytesPerSector DW 0 ; 0x0B
110 bpbSectorsPerCluster DB 0 ; 0x0D
111 bpbReservedSectors DW 0 ; 0x0E
112 bpbNumberOfFATs DB 0 ; 0x10
113 bpbRootEntries DW 0 ; 0x11
114 bpbTotalSectors DW 0 ; 0x13
115 bpbMedia DB 0 ; 0x15
116 bpbSectorsPerFAT DW 0 ; 0x16
117 bpbSectorsPerTrack DW 0 ; 0x18
118 bpbHeadsPerCylinder DW 0 ; 0x1A
119 bpbHiddenSectors DD 0 ; 0x1C
120 bpbTotalSectorsBig DD 0 ; 0x20
122 ;;;;;;;;;;;;;;;;;;;
123 ;; BPB ends here ;;
124 ;;;;;;;;;;;;;;;;;;;
126 bsDriveNumber DB 0 ; 0x24
127 bsUnused DB 0 ; 0x25
128 bsExtBootSignature DB 0 ; 0x26
129 bsSerialNumber DD 0 ; 0x27
130 bsVolumeLabel DB "NO NAME " ; 0x2B
131 bsFileSystem DB "FAT12 " ; 0x36
133 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
134 ;; Boot sector code starts here ;;
135 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
137 start:
138 cld
140 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
141 ;; How much RAM is there? ;;
142 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
144 int 12h ; get conventional memory size (in KBs)
145 mov cx, 106h
146 shl ax, cl ; and convert it to 16-byte paragraphs
148 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
149 ;; Reserve memory for the boot sector and its stack ;;
150 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
152 sub ax, (512+StackSize) /16 ; reserve bytes for the code and the stack
153 mov es, ax ; cs:0 = ds:0 = ss:0 -> top - 512 - StackSize
154 mov ss, ax
155 mov sp, 512+StackSize ; bytes 0-511 are reserved for the boot code
157 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
158 ;; Copy ourselves to top of memory ;;
159 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
161 mov si, 7C00h
162 xor di, di
163 mov ds, di
164 mov [si], dx ; store BIOS boot drive number
165 rep movsw ; move 512 bytes (+ 12)
167 ;;;;;;;;;;;;;;;;;;;;;;
168 ;; Jump to the copy ;;
169 ;;;;;;;;;;;;;;;;;;;;;;
171 push es
172 mov cl, byte main
173 push cx
174 retf
176 main:
177 %if ImageLoadSeg != main-boot
178 %if ImageLoadSeg >= 100h
179 mov cx, ImageLoadSeg
180 %else
181 mov cl, ImageLoadSeg
182 %endif
183 %endif
184 push cx
186 ;;;;;;;;;;;;;;;;;;;;;;;;;;
187 ;; Get drive parameters ;;
188 ;; Update heads count ;;
189 ;; for current BIOS ;;
190 ;;;;;;;;;;;;;;;;;;;;;;;;;;
192 mov ah, 8
193 int 13h ; may destroy SI,BP, and DS registers
194 ; update AX,BL,CX,DX,DI, and ES registers
195 push cs
196 pop ds
197 xor bx, bx
199 and cx, byte 3Fh
200 cmp [bx(bpbSectorsPerTrack)], cx
201 jne BadParams ; verify updated and validity
202 mov al, dh
203 inc ax
204 mov [bpbHeadsPerCylinder], ax
205 BadParams:
207 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
208 ;; Load FAT (FAT12: 6KB max, FAT16: 128KB max) ;;
209 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
211 pop es ; ImageLoadSeg
212 push es
214 mul bx ; dx:ax = 0 = LBA (LBA are relative to FAT)
215 mov cx, word [bx(bpbSectorsPerFAT)]
217 call ReadCXSectors ; read fat and clear ax & cx; bp = SectorsPerFAT
219 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
220 ;; load the root directory in ;;
221 ;; its entirety (16KB max) ;;
222 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
224 mov al, 32
226 mul word [bx(bpbRootEntries)]
227 div word [bx(bpbBytesPerSector)]
228 xchg ax, cx ; cx = root directory size in sectors, clear ax
230 mov al, [bpbNumberOfFATs]
231 mul bp ; [bx(bpbSectorsPerFAT)], set by ReadCXSectors
233 push es
234 call ReadCXSectors ; read root directory; clear ax, cx & di; bp = first data sector
235 pop es
237 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
238 ;; Look for the COM/EXE file to load and run ;;
239 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
241 ; es:di -> root entries array
243 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
244 ;; Looks for the file/dir ProgramName ;;
245 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
246 ;; Input: ES:DI -> root directory array ;;
247 ;; Output: SI = cluster number ;;
248 ;; AX = file sector count ;;
249 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
251 FindNameCycle:
252 push di
253 mov cl, 11
254 mov si, ProgramName ; ds:si -> program name
255 repe cmpsb
256 pop di
257 je FindNameFound
258 scasb
259 je FindNameFailed ; end of root directory (NULL entry found)
260 add di, byte 31
261 dec word [bx(bpbRootEntries)]
262 jnz FindNameCycle ; next root entry
264 FindNameFailed:
265 call Error
266 db "File not found."
268 FindNameFound:
269 push si
270 mov si, [es:di+1Ah] ; si = cluster no.
271 les ax, [es:di+1Ch] ; file size
272 mov dx, es
273 div word [bx(bpbBytesPerSector)]
274 cmp bx, dx ; sector aligned ?
275 adc ax, bx ; file last sector
276 pop di ; di = ClusterList
278 pop es ; ImageLoadSeg
279 push ax
281 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
282 ;; build cluster list ;;
283 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
284 ;; Input: ES:0 -> FAT ;;
285 ;; SI = first cluster ;;
286 ;; DI = cluster list :;
287 ;; CH = 0 ;;
288 ;; Output: SI = cluster list ;;
289 ;; CH = 0 ;;
290 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
292 push di ; up to 2 * 635K / BytesPerCluster bytes
293 mov cl, 12
294 ClusterLoop:
295 mov [di], si
297 mov ax, es ; ax = FAT segment = ImageLoadSeg
298 add si, si ; si = cluster * 2
299 jnc First64
300 mov ah, (1000h+ImageLoadSeg)>>8 ; adjust segment for 2nd part of FAT16
301 First64:
302 mov dx, 0FFF8h
304 cmp [bx(bpbSectorsPerFAT)], cx ; 1..12 = FAT12, 16..256 = FAT16
305 ja ReadClusterFat16
307 mov dh, 0Fh
308 add si, [di]
309 shr si, 1 ; si = cluster * 3 / 2
311 ReadClusterFat16:
312 mov ds, ax
313 lodsw ; ax = next cluster
314 jnc ReadClusterEven
316 rol ax, cl
318 ReadClusterEven:
319 and ah, dh ; mask cluster value
320 cmp ax, dx
322 push cs
323 pop ds
324 inc di
325 inc di
326 xchg ax, si
327 jc ClusterLoop
328 pop si
329 pop di ; file size in sectors
331 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
332 ;; Load the entire file ;;
333 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
334 ;; Input: ES:0 -> buffer ;;
335 ;; SI = cluster list ;;
336 ;; DI = file sectors ;;
337 ;; CH = 0 ;;
338 ;; BP = 1st data sec ;;
339 ;; Output: BP:0 -> buffer ;;
340 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
342 push es
344 ReadClusters:
345 lodsw
346 dec ax
347 dec ax
349 mov cl, [bx(bpbSectorsPerCluster)]
350 mul cx ; cx = sector count (ch = 0)
352 add ax, bp ; LBA for cluster data
353 adc dx, bx ; dx:ax = LBA
355 call ReadSector ; clear ax & cx, restore dx
357 jne ReadClusters
359 pop bp ; ImageLoadSeg
361 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
362 ;; Type detection, .COM or .EXE? ;;
363 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
365 mov ds, bp ; bp=ds=seg the file is loaded to
367 add bp, [bx+08h] ; bp = image base
368 mov di, [bx+18h] ; di = reloc table pointer
370 cmp word [bx], 5A4Dh ; "MZ" signature?
371 je RelocateEXE ; yes, it's an EXE program
373 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
374 ;; Setup and run a .COM program ;;
375 ;; Set CS=DS=ES=SS SP=0 IP=100h ;;
376 ;; AX=0ffffh BX=0 CX=0 DX=drive ;;
377 ;; and cmdline=void ;;
378 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
380 mov di, 100h ; ip
381 mov bp, ImageLoadSeg-10h ; "org 100h" stuff :)
382 mov ss, bp
383 xor sp, sp
384 push bp ; cs, ds and es
385 jmp short Run
387 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
388 ;; Relocate, setup and run a .EXE program ;;
389 ;; Set CS:IP, SS:SP, DS, ES and AX according ;;
390 ;; to wiki.osdev.org/MZ#Initial_Program_State ;;
391 ;; AX=0ffffh BX=0 CX=0 DX=drive cmdline=void ;;
392 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
394 ReloCycle:
395 add [di+2], bp ; item seg (abs)
396 les si, [di] ; si = item ofs, es = item seg
397 add [es:si], bp ; fixup
398 scasw ; di += 2
399 scasw ; point to next entry
401 RelocateEXE:
402 dec word [bx+06h] ; reloc items, 32768 max (128KB table)
403 jns ReloCycle
404 ; PSP don't have a valid drive identifier
405 les si, [bx+0Eh]
406 add si, bp
407 mov ss, si ; ss for EXE
408 mov sp, es ; sp for EXE
410 lea si, [bp-10h] ; ds and es both point to the segment
411 push si ; containing the PSP structure
413 add bp, [bx+16h] ; cs for EXE
414 mov di, [bx+14h] ; ip for EXE
415 Run:
416 pop ds
417 push bp
418 push di
419 push ds
420 pop es
421 mov [80h], ax ; clear cmdline
422 dec ax ; both FCB in the PSP don't have a valid drive identifier
424 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
425 ;; Set the magic numbers so the program knows that it ;;
426 ;; has been loaded by this bootsector and not by MS-DOS ;;
427 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
428 mov si, 16381 ; prime number 2**14-3
429 mov di, 32749 ; prime number 2**15-19
430 mov bp, 65521 ; prime number 2**16-15
432 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
433 ;; All done, transfer control to the program now ;;
434 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
435 retf
437 ReadCXSectors:
438 mov bp, cx
439 add bp, ax ; adjust LBA for cluster data
441 mov di, cx ; no file size limit
443 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
444 ;; Reads sectors using BIOS Int 13h ;;
445 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
446 ;; Input: DX:AX = LBA relative to FAT ;;
447 ;; BX = 0 ;;
448 ;; CX = sector count ;;
449 ;; DI = file sectors ;;
450 ;; ES:BX -> buffer address ;;
451 ;; Output: ES:BX -> next address ;;
452 ;; BX = 0 ;;
453 ;; CX or DI = 0 ;;
454 ;; DL = drive number ;;
455 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
457 ReadSector:
458 add ax, [bx(bpbHiddenSectors)]
459 adc dx, [bx(bpbHiddenSectors)+2]
460 add ax, [bx(bpbReservedSectors)]
462 push si
463 ReadSectorNext:
464 adc dx, bx
465 push di
466 push cx
468 push bx
469 push bx
470 push dx ; 32-bit LBA: up to 2TB
471 push ax
472 push es
473 push bx
474 inc bx ; sector count word = 1
475 push bx
476 dec bx
477 mov di, 16 ; packet size byte = 16, reserved byte = 0
478 push di
480 xchg ax, cx ; save low LBA
481 xchg ax, dx ; get high LBA
482 cwd ; clear dx (LBA offset <2TB)
483 div word [bx(bpbSectorsPerTrack)] ; up to 8GB disks
485 xchg ax, cx ; restore low LBA, save high LBA / SPT
486 div word [bx(bpbSectorsPerTrack)]
487 ; ax = LBA / SPT
488 ; dx = LBA % SPT = sector - 1
489 inc dx
491 xchg cx, dx ; restore high LBA / SPT, save sector no.
492 div word [bx(bpbHeadsPerCylinder)]
493 ; ax = (LBA / SPT) / HPC = cylinder
494 ; dx = (LBA / SPT) % HPC = head
495 mov ch, al
496 ; ch = LSB 0...7 of cylinder no.
497 mov al, 64
498 mul ah
499 or cl, al
500 ; cl = MSB 8...9 of cylinder no. + sector no.
501 mov dh, dl
502 ; dh = head no.
504 ReadSectorRetry:
505 mov dl, [bx]
506 ; dl = drive no.
507 mov ah, 42h ; ah = 42h = extended read function no.
508 mov si, sp
509 int 13h ; extended read sectors (DL, DS:SI)
510 jnc ReadSectorNextSegment
512 mov ax, 201h ; al = sector count = 1
513 ; ah = 2 = read function no.
514 int 13h ; read sectors (AL, CX, DX, ES:BX)
516 jnc ReadSectorNextSegment
517 cbw ; ah = 0 = reset function
518 int 13h ; reset drive (DL)
520 dec di
521 jnz ReadSectorRetry ; extra attempt
523 call Error
524 db "Read error."
526 ReadSectorNextSegment:
528 pop ax ; al = 16
529 mul byte [bx(bpbBytesPerSector)+1] ; = (bpbBytesPerSector/256)*16
530 pop cx ; sector count = 1
531 pop bx
532 add [si+6], ax ; adjust segment for next sector
533 pop es ; es:0 updated
534 pop ax
535 pop dx
536 pop di
537 pop di
539 add ax, cx ; adjust LBA for next sector
541 pop cx ; cluster sectors to read
542 pop di ; file sectors to read
543 dec di ; keep C
544 loopne ReadSectorNext ; until cluster sector count or file sector count is reached
545 pop si
546 mov ax, bx ; clear ax
547 mov dx, [bx] ; pass the BIOS boot drive to Run or Error
549 ret
551 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
552 ;; Fill free space with zeroes ;;
553 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
555 times (512-13-20-($-$$)) db 0
557 ;;;;;;;;;;;;;;;;;;;;;;;;;;
558 ;; Error Messaging Code ;;
559 ;;;;;;;;;;;;;;;;;;;;;;;;;;
561 Error:
562 pop si
564 PutStr:
565 mov ah, 0Eh
566 mov bl, 7
567 lodsb
568 int 10h
569 cmp al, "."
570 jne PutStr
572 cbw
573 int 16h ; wait for a key...
574 int 19h ; bootstrap
576 Stop:
577 hlt
578 jmp short Stop
580 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
581 ;; Name of the file to load and run ;;
582 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
584 ProgramName db "STARTUP BIN" ; name and extension each must be
585 ; padded with spaces (11 bytes total)
587 ;;;;;;;;;;;;;;;;;;;;;;;;;;
588 ;; End of the sector ID ;;
589 ;;;;;;;;;;;;;;;;;;;;;;;;;;
591 ClusterList dw 0AA55h ; BIOS checks for this ID