]>
Commit | Line | Data |
---|---|---|
f083c6c3 A |
1 | ; Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. |
2 | ; | |
3 | ; @APPLE_LICENSE_HEADER_START@ | |
4 | ; | |
5 | ; Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. | |
6 | ; | |
7 | ; This file contains Original Code and/or Modifications of Original Code | |
8 | ; as defined in and that are subject to the Apple Public Source License | |
9 | ; Version 2.0 (the 'License'). You may not use this file except in | |
10 | ; compliance with the License. Please obtain a copy of the License at | |
11 | ; http://www.opensource.apple.com/apsl/ and read it before using this | |
12 | ; file. | |
13 | ; | |
14 | ; The Original Code and all software distributed under the License are | |
15 | ; distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | ; EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | ; INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | ; FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
19 | ; Please see the License for the specific language governing rights and | |
20 | ; limitations under the License. | |
21 | ; | |
22 | ; @APPLE_LICENSE_HEADER_END@ | |
23 | ; | |
24 | ; Boot Loader: boot0 | |
25 | ; | |
26 | ; A small boot sector program written in x86 assembly whose only | |
27 | ; responsibility is to locate the booter partition, load the | |
28 | ; booter into memory, and jump to the booter's entry point. | |
29 | ; The booter partition can be a primary or a logical partition. | |
30 | ; | |
31 | ; This boot loader can be placed at any of the following places: | |
32 | ; 1. Master Boot Record (MBR) | |
33 | ; 2. Boot sector of an extended partition | |
34 | ; 3. Boot sector of a primary partition | |
35 | ; 4. Boot sector of a logical partition | |
36 | ; | |
37 | ; In order to coexist with a fdisk partition table (64 bytes), and | |
38 | ; leave room for a two byte signature (0xAA55) in the end, boot0 is | |
39 | ; restricted to 446 bytes (512 - 64 - 2). If boot0 did not have to | |
40 | ; live in the MBR, then we would have 510 bytes to work with. | |
41 | ; | |
42 | ; boot0 is always loaded by the BIOS or another booter to 0:7C00h. | |
43 | ; | |
44 | ; This code is written for the NASM assembler. | |
45 | ; nasm boot0.s -o boot0 | |
46 | ||
47 | ||
48 | ; | |
49 | ; Set to 1 to enable obscure debug messages. | |
50 | ; | |
51 | DEBUG EQU 0 | |
52 | ||
53 | ; | |
54 | ; Set to 1 to support loading the booter (boot2) from a | |
55 | ; logical partition. | |
56 | ; | |
57 | EXT_PART_SUPPORT EQU 1 | |
58 | ||
59 | ; | |
60 | ; Various constants. | |
61 | ; | |
62 | kBoot0Segment EQU 0x0000 | |
63 | kBoot0Stack EQU 0xFFF0 ; boot0 stack pointer | |
64 | kBoot0LoadAddr EQU 0x7C00 ; boot0 load address | |
65 | kBoot0RelocAddr EQU 0xE000 ; boot0 relocated address | |
66 | ||
67 | kMBRBuffer EQU 0x1000 ; MBR buffer address | |
68 | kExtBuffer EQU 0x1200 ; EXT boot block buffer address | |
69 | ||
70 | kPartTableOffset EQU 0x1be | |
71 | kMBRPartTable EQU kMBRBuffer + kPartTableOffset | |
72 | kExtPartTable EQU kExtBuffer + kPartTableOffset | |
73 | ||
74 | kBoot1uSectors EQU 16 ; sectors to load for boot2 | |
75 | kBoot1uAddress EQU 0x0000 ; boot2 load address | |
76 | kBoot1uSegment EQU 0x1000 ; boot2 load segment | |
77 | ||
78 | kSectorBytes EQU 512 ; sector size in bytes | |
79 | kBootSignature EQU 0xAA55 ; boot sector signature | |
80 | ||
81 | kPartCount EQU 4 ; number of paritions per table | |
82 | kPartTypeBoot EQU 0xab ; boot2 partition type | |
83 | kPartTypeUFS EQU 0xa8 ; | |
84 | kPartTypeExtDOS EQU 0x05 ; DOS extended partition type | |
85 | kPartTypeExtWin EQU 0x0f ; Windows extended partition type | |
86 | kPartTypeExtLinux EQU 0x85 ; Linux extended partition type | |
87 | kPartActive EQU 0x80 | |
88 | ||
89 | %ifdef FLOPPY | |
90 | kDriveNumber EQU 0x00 | |
91 | %else | |
92 | kDriveNumber EQU 0x80 | |
93 | %endif | |
94 | ||
95 | ; | |
96 | ; In memory variables. | |
97 | ; | |
98 | ebios_lba dd 0 ; starting LBA of the intial extended partition. | |
99 | ebios_present db 0 ; 1 if EBIOS is supported, 0 otherwise. | |
100 | ||
101 | ; | |
102 | ; Format of fdisk partition entry. | |
103 | ; | |
104 | ; The symbol 'part_size' is automatically defined as an `EQU' | |
105 | ; giving the size of the structure. | |
106 | ; | |
107 | struc part | |
108 | .bootid: resb 1 ; bootable or not | |
109 | .head: resb 1 ; starting head, sector, cylinder | |
110 | .sect: resb 1 ; | |
111 | .cyl: resb 1 ; | |
112 | .type: resb 1 ; partition type | |
113 | .endhead resb 1 ; ending head, sector, cylinder | |
114 | .endsect: resb 1 ; | |
115 | .endcyl: resb 1 ; | |
116 | .lba: resd 1 ; starting lba | |
117 | .sectors resd 1 ; size in sectors | |
118 | endstruc | |
119 | ||
120 | ; | |
121 | ; Macros. | |
122 | ; | |
123 | %macro DebugCharMacro 1 | |
124 | mov al, %1 | |
125 | call print_char | |
126 | %endmacro | |
127 | ||
128 | %if DEBUG | |
129 | %define DebugChar(x) DebugCharMacro x | |
130 | %else | |
131 | %define DebugChar(x) | |
132 | %endif | |
133 | ||
134 | ;-------------------------------------------------------------------------- | |
135 | ; Start of text segment. | |
136 | ||
137 | SEGMENT .text | |
138 | ||
139 | ORG 0xE000 ; must match kBoot0RelocAddr | |
140 | ||
141 | ;-------------------------------------------------------------------------- | |
142 | ; Boot code is loaded at 0:7C00h. | |
143 | ; | |
144 | start | |
145 | ; | |
146 | ; Set up the stack to grow down from kBoot0Segment:kBoot0Stack. | |
147 | ; Interrupts should be off while the stack is being manipulated. | |
148 | ; | |
149 | cli ; interrupts off | |
150 | xor ax, ax ; zero ax | |
151 | mov ss, ax ; ss <- 0 | |
152 | mov sp, kBoot0Stack ; sp <- top of stack | |
153 | sti ; reenable interrupts | |
154 | ||
155 | mov es, ax ; es <- 0 | |
156 | mov ds, ax ; ds <- 0 | |
157 | ||
158 | ; | |
159 | ; Relocate boot0 code. | |
160 | ; | |
161 | mov si, kBoot0LoadAddr ; si <- source | |
162 | mov di, kBoot0RelocAddr ; di <- destination | |
163 | ; | |
164 | cld ; auto-increment SI and/or DI registers | |
165 | mov cx, kSectorBytes/2 ; copy 256 words | |
166 | repnz movsw ; repeat string move (word) operation | |
167 | ||
168 | ; Code relocated, jump to start_reloc in relocated location. | |
169 | ; | |
170 | jmp 0:start_reloc | |
171 | ||
172 | ;-------------------------------------------------------------------------- | |
173 | ; Start execution from the relocated location. | |
174 | ; | |
175 | start_reloc: | |
176 | ||
177 | DebugChar('*') | |
178 | ||
179 | mov dl, kDriveNumber ; starting BIOS drive number | |
180 | ||
181 | .loop: | |
182 | ||
183 | %if DEBUG | |
184 | mov al, dl | |
185 | call print_hex | |
186 | %endif | |
187 | ||
188 | ; | |
189 | ; Clear various flags in memory. | |
190 | ; | |
191 | xor eax, eax | |
192 | mov [ebios_lba], eax ; clear EBIOS LBA offset | |
193 | mov [ebios_present], al ; clear EBIOS support flag | |
194 | ||
195 | ; | |
196 | ; Since this code may not always reside in the MBR, always start by | |
197 | ; loading the MBR to kMBRBuffer. | |
198 | ; | |
199 | mov al, 1 ; load one sector | |
200 | xor bx, bx | |
201 | mov es, bx ; MBR load segment = 0 | |
202 | mov bx, kMBRBuffer ; MBR load address | |
203 | mov si, bx ; pointer to fake partition entry | |
204 | mov WORD [si], 0x0000 ; CHS DX: head = 0 | |
205 | mov WORD [si + 2], 0x0001 ; CHS CX: cylinder = 0, sector = 1 | |
206 | ||
207 | call load | |
208 | jc .next_drive ; MBR load error | |
209 | ||
210 | ; | |
211 | ; Check if EBIOS is supported for this hard drive. | |
212 | ; | |
213 | mov ah, 0x41 ; Function 0x41 | |
214 | mov bx, 0x55AA ; check signature | |
215 | ; mov dl, kDriveNumber ; Drive number | |
216 | int 0x13 | |
217 | ||
218 | ; | |
219 | ; If successful, the return values are as follows: | |
220 | ; | |
221 | ; carry = 0 | |
222 | ; ah = major version of EBIOS extensions (0x21 = version 1.1) | |
223 | ; al = altered | |
224 | ; bx = 0xAA55 | |
225 | ; cx = support bits. bit 0 must be set for function 0x42. | |
226 | ; | |
227 | jc .ebios_check_done | |
228 | cmp bx, 0xAA55 ; check BX = 0xAA55 | |
229 | jnz .ebios_check_done | |
230 | test cl, 0x01 ; check enhanced drive read support | |
231 | setnz [ebios_present] ; EBIOS supported, set flag | |
232 | DebugChar('E') ; EBIOS supported | |
233 | .ebios_check_done: | |
234 | ||
235 | ; | |
236 | ; Look for the booter partition in the MBR partition table, | |
237 | ; which is at offset kMBRPartTable. | |
238 | ; | |
239 | mov di, kMBRPartTable ; pointer to partition table | |
240 | mov ah, 0 ; initial nesting level is 0 | |
241 | call find_boot ; will not return on success | |
242 | ||
243 | .next_drive: | |
244 | ;; inc dl ; next drive number | |
245 | ;; test dl, 0x84 ; went through all 4 drives? | |
246 | ;; jz .loop ; not yet, loop again | |
247 | ||
248 | mov si, boot_error_str | |
249 | call print_string | |
250 | ||
251 | hang: | |
252 | hlt | |
253 | jmp SHORT hang | |
254 | ||
255 | ;-------------------------------------------------------------------------- | |
256 | ; Find the boot partition and load the booter from the partition. | |
257 | ; | |
258 | ; Arguments: | |
259 | ; AH = recursion nesting level | |
260 | ; DL = drive number (0x80 + unit number) | |
261 | ; DI = pointer to fdisk partition table. | |
262 | ; | |
263 | ; Clobber list: | |
264 | ; AX, BX, EBP | |
265 | ; | |
266 | find_boot: | |
267 | push cx ; preserve CX and SI | |
268 | push si | |
269 | ||
270 | ; | |
271 | ; Check for boot block signature 0xAA55 following the 4 partition | |
272 | ; entries. | |
273 | ; | |
274 | cmp WORD [di + part_size * kPartCount], kBootSignature | |
275 | jne .exit ; boot signature not found | |
276 | ||
277 | mov si, di ; make SI a pointer to partition table | |
278 | mov cx, kPartCount ; number of partition entries per table | |
279 | ||
280 | .loop: | |
281 | ; | |
282 | ; First scan through the partition table looking for the boot | |
283 | ; partition. Postpone walking the extended partition chain for | |
284 | ; the second pass. Do not merge the two without changing the | |
285 | ; buffering scheme used to store extended partition tables. | |
286 | ; | |
287 | %if DEBUG | |
288 | mov al, ah ; indent based on nesting level | |
289 | call print_spaces | |
290 | mov al, [si + part.type] ; print partition type | |
291 | call print_hex | |
292 | %endif | |
293 | ||
294 | cmp BYTE [si + part.type], kPartTypeUFS | |
295 | jne .continue | |
296 | cmp BYTE [si + part.bootid], kPartActive | |
297 | jne .continue | |
298 | ||
299 | ; | |
300 | ; Found boot partition, read boot1u image to memory. | |
301 | ; | |
302 | mov al, kBoot1uSectors | |
303 | mov bx, kBoot1uSegment | |
304 | mov es, bx | |
305 | mov bx, kBoot1uAddress | |
306 | call load ; | |
307 | jc .continue ; load error, keep looking? | |
308 | ||
309 | DebugChar('^') | |
310 | ; | |
311 | ; Jump to boot1u. The drive number is already in register DL. | |
312 | ; | |
313 | ; The first sector loaded from the disk is reserved for the boot | |
314 | ; block (boot1), adjust the jump location by adding a sector offset. | |
315 | ; | |
316 | jmp kBoot1uSegment:kBoot1uAddress + kSectorBytes | |
317 | ||
318 | .continue: | |
319 | add si, part_size ; advance SI to next partition entry | |
320 | loop .loop ; loop through all partition entries | |
321 | ||
322 | %if EXT_PART_SUPPORT | |
323 | ; | |
324 | ; No primary (or logical) boot partition found in the current | |
325 | ; partition table. Restart and look for extended partitions. | |
326 | ; | |
327 | mov si, di ; make SI a pointer to partition table | |
328 | mov cx, kPartCount ; number of partition entries per table | |
329 | ||
330 | .ext_loop: | |
331 | ||
332 | mov al, [si + part.type] ; AL <- partition type | |
333 | ||
334 | cmp al, kPartTypeExtDOS ; Extended DOS | |
335 | je .ext_load | |
336 | ||
337 | cmp al, kPartTypeExtWin ; Extended Windows(95) | |
338 | je .ext_load | |
339 | ||
340 | cmp al, kPartTypeExtLinux ; Extended Linux | |
341 | je .ext_load | |
342 | ||
343 | .ext_continue: | |
344 | ; | |
345 | ; Advance si to the next partition entry in the extended | |
346 | ; partition table. | |
347 | ; | |
348 | add si, part_size ; advance SI to next partition entry | |
349 | loop .ext_loop ; loop through all partition entries | |
350 | jmp .exit ; boot partition not found | |
351 | ||
352 | .ext_load: | |
353 | ; | |
354 | ; Setup the arguments for the load function call to bring the | |
355 | ; extended partition table into memory. | |
356 | ; Remember that SI points to the extended partition entry. | |
357 | ; | |
358 | mov al, 1 ; read 1 sector | |
359 | xor bx, bx | |
360 | mov es, bx ; es = 0 | |
361 | mov bx, kExtBuffer ; load extended boot sector | |
362 | call load | |
363 | jc .ext_continue ; load error | |
364 | ||
365 | ; | |
366 | ; The LBA address of all extended partitions is relative based | |
367 | ; on the LBA address of the extended partition in the MBR, or | |
368 | ; the extended partition at the head of the chain. Thus it is | |
369 | ; necessary to save the LBA address of the first extended partition. | |
370 | ; | |
371 | or ah, ah | |
372 | jnz .ext_find_boot | |
373 | mov ebp, [si + part.lba] | |
374 | mov [ebios_lba], ebp | |
375 | ||
376 | .ext_find_boot: | |
377 | ; | |
378 | ; Call find_boot recursively to scan through the extended partition | |
379 | ; table. Load DI with a pointer to the extended table in memory. | |
380 | ; | |
381 | inc ah ; increment recursion level | |
382 | mov di, kExtPartTable ; partition table pointer | |
383 | call find_boot ; recursion... | |
384 | ;dec ah | |
385 | ||
386 | ; | |
387 | ; Since there is an "unwritten" rule that limits each partition table | |
388 | ; to have 0 or 1 extended partitions, there is no point in looking for | |
389 | ; any additional extended partition entries at this point. There is no | |
390 | ; boot partition linked beyond the extended partition that was loaded | |
391 | ; above. | |
392 | ; | |
393 | ||
394 | %endif ; EXT_PART_SUPPORT | |
395 | ||
396 | .exit: | |
397 | ; | |
398 | ; Boot partition not found. Giving up. | |
399 | ; | |
400 | pop si | |
401 | pop cx | |
402 | ret | |
403 | ||
404 | ;-------------------------------------------------------------------------- | |
405 | ; load - Load one or more sectors from a partition. | |
406 | ; | |
407 | ; Arguments: | |
408 | ; AL = number of 512-byte sectors to read. | |
409 | ; ES:BX = pointer to where the sectors should be stored. | |
410 | ; DL = drive number (0x80 + unit number) | |
411 | ; SI = pointer to the partition entry. | |
412 | ; | |
413 | ; Returns: | |
414 | ; CF = 0 success | |
415 | ; 1 error | |
416 | ; | |
417 | load: | |
418 | push cx | |
419 | test BYTE [ebios_present], 1 | |
420 | jz .chs | |
421 | ||
422 | .ebios: | |
423 | mov cx, 5 ; load retry count | |
424 | .ebios_loop: | |
425 | call read_lba ; use INT13/F42 | |
426 | jnc .exit | |
427 | loop .ebios_loop | |
428 | ||
429 | .chs: | |
430 | mov cx, 5 ; load retry count | |
431 | .chs_loop: | |
432 | call read_chs ; use INT13/F2 | |
433 | jnc .exit | |
434 | loop .chs_loop | |
435 | ||
436 | .exit | |
437 | pop cx | |
438 | ret | |
439 | ||
440 | ;-------------------------------------------------------------------------- | |
441 | ; read_chs - Read sectors from a partition using CHS addressing. | |
442 | ; | |
443 | ; Arguments: | |
444 | ; AL = number of 512-byte sectors to read. | |
445 | ; ES:BX = pointer to where the sectors should be stored. | |
446 | ; DL = drive number (0x80 + unit number) | |
447 | ; SI = pointer to the partition entry. | |
448 | ; | |
449 | ; Returns: | |
450 | ; CF = 0 success | |
451 | ; 1 error | |
452 | ; | |
453 | read_chs: | |
454 | pusha ; save all registers | |
455 | ||
456 | ; | |
457 | ; Read the CHS start values from the partition entry. | |
458 | ; | |
459 | mov dh, [ si + part.head ] ; drive head | |
460 | mov cx, [ si + part.sect ] ; drive sector + cylinder | |
461 | ||
462 | ; | |
463 | ; INT13 Func 2 - Read Disk Sectors | |
464 | ; | |
465 | ; Arguments: | |
466 | ; AH = 2 | |
467 | ; AL = number of sectors to read | |
468 | ; CH = lowest 8 bits of the 10-bit cylinder number | |
469 | ; CL = bits 6 & 7: cylinder number bits 8 and 9 | |
470 | ; bits 0 - 5: starting sector number (1-63) | |
471 | ; DH = starting head number (0 to 255) | |
472 | ; DL = drive number (80h + drive unit) | |
473 | ; es:bx = pointer where to place sectors read from disk | |
474 | ; | |
475 | ; Returns: | |
476 | ; AH = return status (sucess is 0) | |
477 | ; AL = burst error length if ah=0x11 (ECC corrected) | |
478 | ; carry = 0 success | |
479 | ; 1 error | |
480 | ; | |
481 | ; mov dl, kDriveNumber | |
482 | mov ah, 0x02 ; Func 2 | |
483 | int 0x13 ; INT 13 | |
484 | jnc .exit | |
485 | ||
486 | DebugChar('r') ; indicate INT13/F2 error | |
487 | ||
488 | ; | |
489 | ; Issue a disk reset on error. | |
490 | ; Should this be changed to Func 0xD to skip the diskette controller | |
491 | ; reset? | |
492 | ; | |
493 | xor ax, ax ; Func 0 | |
494 | int 0x13 ; INT 13 | |
495 | stc ; set carry to indicate error | |
496 | ||
497 | .exit: | |
498 | popa | |
499 | ret | |
500 | ||
501 | ;-------------------------------------------------------------------------- | |
502 | ; read_lba - Read sectors from a partition using LBA addressing. | |
503 | ; | |
504 | ; Arguments: | |
505 | ; AL = number of 512-byte sectors to read (valid from 1-127). | |
506 | ; ES:BX = pointer to where the sectors should be stored. | |
507 | ; DL = drive number (0x80 + unit number) | |
508 | ; SI = pointer to the partition entry. | |
509 | ; | |
510 | ; Returns: | |
511 | ; CF = 0 success | |
512 | ; 1 error | |
513 | ; | |
514 | read_lba: | |
515 | pusha ; save all registers | |
516 | mov bp, sp ; save current SP | |
517 | ||
518 | ; | |
519 | ; Create the Disk Address Packet structure for the | |
520 | ; INT13/F42 (Extended Read Sectors) on the stack. | |
521 | ; | |
522 | ||
523 | ; push DWORD 0 ; offset 12, upper 32-bit LBA | |
524 | push ds ; For sake of saving memory, | |
525 | push ds ; push DS register, which is 0. | |
526 | ||
527 | mov ecx, [ebios_lba] ; offset 8, lower 32-bit LBA | |
528 | add ecx, [si + part.lba] | |
529 | push ecx | |
530 | ||
531 | push es ; offset 6, memory segment | |
532 | ||
533 | push bx ; offset 4, memory offset | |
534 | ||
535 | xor ah, ah ; offset 3, must be 0 | |
536 | push ax ; offset 2, number of sectors | |
537 | ||
538 | push WORD 16 ; offset 0-1, packet size | |
539 | ||
540 | ; | |
541 | ; INT13 Func 42 - Extended Read Sectors | |
542 | ; | |
543 | ; Arguments: | |
544 | ; AH = 0x42 | |
545 | ; DL = drive number (80h + drive unit) | |
546 | ; DS:SI = pointer to Disk Address Packet | |
547 | ; | |
548 | ; Returns: | |
549 | ; AH = return status (sucess is 0) | |
550 | ; carry = 0 success | |
551 | ; 1 error | |
552 | ; | |
553 | ; Packet offset 2 indicates the number of sectors read | |
554 | ; successfully. | |
555 | ; | |
556 | ; mov dl, kDriveNumber | |
557 | mov si, sp | |
558 | mov ah, 0x42 | |
559 | int 0x13 | |
560 | ||
561 | jnc .exit | |
562 | ||
563 | DebugChar('R') ; indicate INT13/F42 error | |
564 | ||
565 | ; | |
566 | ; Issue a disk reset on error. | |
567 | ; Should this be changed to Func 0xD to skip the diskette controller | |
568 | ; reset? | |
569 | ; | |
570 | xor ax, ax ; Func 0 | |
571 | int 0x13 ; INT 13 | |
572 | stc ; set carry to indicate error | |
573 | ||
574 | .exit: | |
575 | mov sp, bp ; restore SP | |
576 | popa | |
577 | ret | |
578 | ||
579 | ;-------------------------------------------------------------------------- | |
580 | ; Write a string to the console. | |
581 | ; | |
582 | ; Arguments: | |
583 | ; DS:SI pointer to a NULL terminated string. | |
584 | ; | |
585 | ; Clobber list: | |
586 | ; AX, BX, SI | |
587 | ; | |
588 | print_string | |
589 | mov bx, 1 ; BH=0, BL=1 (blue) | |
590 | cld ; increment SI after each lodsb call | |
591 | .loop | |
592 | lodsb ; load a byte from DS:SI into AL | |
593 | cmp al, 0 ; Is it a NULL? | |
594 | je .exit ; yes, all done | |
595 | mov ah, 0xE ; INT10 Func 0xE | |
596 | int 0x10 ; display byte in tty mode | |
597 | jmp short .loop | |
598 | .exit | |
599 | ret | |
600 | ||
601 | %if DEBUG | |
602 | ||
603 | ;-------------------------------------------------------------------------- | |
604 | ; Write a ASCII character to the console. | |
605 | ; | |
606 | ; Arguments: | |
607 | ; AL = ASCII character. | |
608 | ; | |
609 | print_char | |
610 | pusha | |
611 | mov bx, 1 ; BH=0, BL=1 (blue) | |
612 | mov ah, 0x0e ; bios INT 10, Function 0xE | |
613 | int 0x10 ; display byte in tty mode | |
614 | popa | |
615 | ret | |
616 | ||
617 | ;-------------------------------------------------------------------------- | |
618 | ; Write a variable number of spaces to the console. | |
619 | ; | |
620 | ; Arguments: | |
621 | ; AL = number to spaces. | |
622 | ; | |
623 | print_spaces: | |
624 | pusha | |
625 | xor cx, cx | |
626 | mov cl, al ; use CX as the loop counter | |
627 | mov al, ' ' ; character to print | |
628 | .loop: | |
629 | jcxz .exit | |
630 | call print_char | |
631 | loop .loop | |
632 | .exit: | |
633 | popa | |
634 | ret | |
635 | ||
636 | ;-------------------------------------------------------------------------- | |
637 | ; Write the byte value to the console in hex. | |
638 | ; | |
639 | ; Arguments: | |
640 | ; AL = Value to be displayed in hex. | |
641 | ; | |
642 | print_hex: | |
643 | push ax | |
644 | ror al, 4 | |
645 | call print_nibble ; display upper nibble | |
646 | pop ax | |
647 | call print_nibble ; display lower nibble | |
648 | ||
649 | mov al, 10 ; carriage return | |
650 | call print_char | |
651 | mov al, 13 | |
652 | call print_char | |
653 | ret | |
654 | ||
655 | print_nibble: | |
656 | and al, 0x0f | |
657 | add al, '0' | |
658 | cmp al, '9' | |
659 | jna .print_ascii | |
660 | add al, 'A' - '9' - 1 | |
661 | .print_ascii: | |
662 | call print_char | |
663 | ret | |
664 | ||
665 | %endif ; DEBUG | |
666 | ||
667 | ;-------------------------------------------------------------------------- | |
668 | ; NULL terminated strings. | |
669 | ; | |
670 | boot_error_str db 10, 13, 'Error', 0 | |
671 | ||
672 | ;-------------------------------------------------------------------------- | |
673 | ; Pad the rest of the 512 byte sized booter with zeroes. The last | |
674 | ; two bytes is the mandatory boot sector signature. | |
675 | ; | |
676 | ; If the booter code becomes too large, then nasm will complain | |
677 | ; that the 'times' argument is negative. | |
678 | ||
679 | ;;pad_boot | |
680 | ;; times 446-($-$$) db 0 | |
681 | ||
682 | %ifdef FLOPPY | |
683 | ;-------------------------------------------------------------------------- | |
684 | ; Put fake partition entries for the bootable floppy image | |
685 | ; | |
686 | part1bootid db 0x80 ; first partition active | |
687 | part1head db 0x00 ; head # | |
688 | part1sect db 0x02 ; sector # (low 6 bits) | |
689 | part1cyl db 0x00 ; cylinder # (+ high 2 bits of above) | |
690 | part1systid db 0xab ; Apple boot partition | |
691 | times 3 db 0x00 ; ignore head/cyl/sect #'s | |
692 | part1relsect dd 0x00000001 ; start at sector 1 | |
693 | part1numsect dd 0x00000080 ; 64K for booter | |
694 | part2bootid db 0x00 ; not active | |
695 | times 3 db 0x00 ; ignore head/cyl/sect #'s | |
696 | part2systid db 0xa8 ; Apple UFS partition | |
697 | times 3 db 0x00 ; ignore head/cyl/sect #'s | |
698 | part2relsect dd 0x00000082 ; start after booter | |
699 | ; part2numsect dd 0x00000abe ; 1.44MB - 65K | |
700 | part2numsect dd 0x000015fe ; 2.88MB - 65K | |
701 | %endif | |
702 | ||
703 | pad_table_and_sig | |
704 | times 510-($-$$) db 0 | |
705 | dw kBootSignature | |
706 | ||
707 | END |