; boot1.asm - boot1 written for turbo assembler, since gas only␍␊ |
; generates 32 bit code and this must run in real mode.␊ |
; To compile as hard disk boot1:␊ |
;␉tasm /m3 /dBOOTDEV=HDISK boot1␊ |
;␉tlink boot1␊ |
;␉exe2bin boot1␊ |
;␉ren boot1.bin boot1␊ |
; To compile as floppy boot1f:␊ |
;␉tasm /m3 /dBOOTDEV=FLOPPY boot1 ,boot1f␊ |
;␉tlink boot1f␊ |
;␉exe2bin boot1f␊ |
;␉ren boot1f.bin boot1f␊ |
␍␊ |
;***********************************************************************␍␊ |
;␉This is the code for the NeXT boot1 bootsector.␊ |
;***********************************************************************␍␊ |
␍␊ |
␉P486␉␉␉;enable i386 instructions␍␊ |
␉IDEAL␍␊ |
␉SEGMENT CSEG␍␊ |
␉ASSUME CS:CSEG,DS:CSEG␍␊ |
␍␊ |
␉SDEBUG = 0␍␊ |
␍␊ |
;BOOTSEG␉␉=␉100h␉; boot will be loaded at 4k␍␊ |
;BOOTOFF␉␉=␉0000h␊ |
BOOTSEG␉␉=␉00h␊ |
BOOTOFF␉␉=␉1000h␊ |
BUFSZ␉␉=␉2000h␉; 8K disk transfer buffer␍␊ |
␍␊ |
; FDISK partition table in sector 0␍␊ |
PARTSTART␉=␉1beh␉; starting address of partition table␍␊ |
NUMPART␉␉=␉4␉; number of partitions in partition table␍␊ |
PARTSZ␉␉=␉16␉; each partition table entry is 16 bytes␍␊ |
BOOT_IND␉=␉0␉; offset of boot indicator in partition table␍␊ |
BEG_HEAD␉=␉1␉; offset of beginning head␍␊ |
BEG_SEC␉␉= ␉2␉; offset of beginning sector␍␊ |
BEG_CYL␉␉=␉3␉; offset of beginning cylinder␍␊ |
NAME_OFFSET␉=␉4␉; offset of partition name␍␊ |
PARTSEC␉␉=␉8␉; offset of partition sector specifier␍␊ |
NEXTNAME␉=␉0A7h␉; value of boot_ind, means bootable partition␍␊ |
␍␊ |
LOADSZ␉␉=␉88␉; maxiumum possible size of unix boot␍-- 44k␊ |
␍␊ |
FLOPPY␉␉=␉0␊ |
HDISK␉␉=␉80h␊ |
␊ |
;BOOTDEV␉=␉?␉; set to 00h for floppy, 80h for hard disk␊ |
␉␉␉␉; (use a command line switch to set)␊ |
␊ |
; NeXT disk label␍␊ |
DISKLABEL␉=␉15␉; sector num of 2nd disk label, 1st is trashed by bootsec␍␊ |
DL_DISKTAB␉=␉44␍␊ |
␍␊ |
; We support disk label version 3 "3Vld" in our little endian world␍␊ |
DL_V3␉␉= ␉33566c64h␍␊ |
␍␊ |
; NeXT disktab␍␊ |
DT_SECSIZE␉=␉48␍␊ |
DT_BOOT0_BLKNO␉=␉80␍␊ |
␍-␍␊ |
; This code is a replacement for boot1. It is loaded at 0x0:0x7c00␍␊ |
␍␊ |
start:␍␊ |
␉mov␉ax,BOOTSEG␊ |
␉cli␉␉␉; interrupts off␍␊ |
␉mov␉ss,ax␉␉; set up stack seg␍␊ |
␉mov␉sp,0fff0h␍␊ |
␉sti␉␉␉; reenable interrupts␍␊ |
␊ |
␉xor␉ax,ax␊ |
␉mov␉es,ax␊ |
␉mov␉ds,ax␊ |
␉mov␉si,7C00h␊ |
␉cld␉␉␉; so pointers will get updated␍␊ |
␉mov␉di,0E000h␉; relocate boot program to 0xE000␍␊ |
␉mov␉cx,100h␉␉; copy 256x2 bytes␍␊ |
␉repnz␉movsw␉␉; move it␍␊ |
␉off1␉= 0E000h + (a1 - start)␍␊ |
␉jmp␉FAR 0000:off1␉; jump to a1 in relocated place␍␊ |
␊ |
a1:␍␊ |
␉mov␉ax,0E00h␍␊ |
␉mov␉ds,ax␍␊ |
␉mov␉ax,BOOTSEG␍␊ |
␉mov␉es,ax␍␊ |
␍␊ |
␉; load the boot loader (boot2) into BOOTSEG:BUFSZ␍␊ |
␉call␉loadboot␍␊ |
␍␊ |
␉; ljmp to the second stage boot loader (boot2).␍␊ |
␉; After ljmp, cs is BOOTSEG and boot1 (BUFSZ bytes) will be used␍␊ |
␉; as an internal buffer "intbuf".␍␊ |
␍␊ |
␉xor␉edx,edx␉␉; bootdev = 0 for hard disk␍␊ |
IF␉( BOOTDEV EQ FLOPPY )␊ |
␉inc␉edx␉␉; bootdev = 1 for floppy disk␊ |
ENDIF␊ |
␊ |
␉;boot2 immediately follows disk buffer; 4K + BUFSZ␍␊ |
␉jmp␉FAR BOOTSEG:(BOOTOFF + BUFSZ)␉␊ |
␉␉␉␉; jump to boot2 in loaded location␍␊ |
␍␊ |
␍␊ |
␍␊ |
loadboot:␍␊ |
␉mov␉si, OFFSET intro␍␊ |
␉call␉message␉␉; display intro message␍␊ |
␍␊ |
␉; load second stage boot from fixed disk␍␊ |
␉; get boot drive parameters␍␊ |
␉; Note: I believe that the bootsector read may not be necessary;␍␊ |
␉; at least some blk0 bootsectors leave a pointer to the active␍␊ |
␉; partition entry in si (assuming there was another blk0 bootsec)␍␊ |
␍␊ |
␉call␉getinfo␍␊ |
␍-␉␊ |
IF␉( BOOTDEV EQ HDISK )␊ |
␉␊ |
␉; read sector 0 into BOOTSEG:0 by using BIOS call (INT 13H 02H)␍␊ |
␉; this gets info on the disk's actual partition table␍␊ |
␉; However, in the case of multiple partitions, this may not␍␊ |
␉; be the same as the sector with the code here.␍␊ |
␍␊ |
␉mov␉di,5␉␉; try 5 times to read bootsector␍␊ |
␍␊ |
retry_disk:␍␊ |
␉␉xor␉bx, bx␉␉; buffer is BOOTSEG:0␍␊ |
␉␉mov␉ax,201h␍␊ |
␉␉mov␉cx,bx␍␊ |
␉␉mov␉dx,bx␍␊ |
␉␉mov␉bx,BOOTOFF␉; actually, it's 0:BOOTOFF␊ |
␉␉inc␉cx␉␉; cyl 0, sector 1␍␊ |
␉␉mov␉dl,BOOTDEV␉; target 0, head 0␍␊ |
␍␊ |
␉␉push␉di␍␊ |
␉␉int␉13h␉␉; read the bootsector␍␊ |
␉␉pop␉di␍␊ |
␍␊ |
␉␉jnb␉read_success1␍␊ |
␍␊ |
␉␉; woops, bios failed to read sector␍␊ |
␉␉xor␉ax,ax␍␊ |
␉␉int␉13h␉␉; reset disk␍␊ |
␉␉dec␉di␍␊ |
␉␉jne␉retry_disk␍␊ |
␍␊ |
␉␉jmp␉read_error␉; disk failed␍␊ |
␍␊ |
␍␊ |
read_success1:␉␉; find the NeXT partition␍␊ |
␉mov␉bx,PARTSTART␍␊ |
␉mov␉cx,NUMPART␍␊ |
␍␊ |
again:␍␊ |
␉mov␉al, [es:(bx+BOOTOFF)+NAME_OFFSET]␊ |
␉␉␉␉␉; get partition name␍␊ |
␉cmp␉al, NEXTNAME␉␉; is it NeXT partition?␍␊ |
␉jne␉cont␉␉␉; nope, keep looking␍␊ |
␍␊ |
foundNextPart:␉␉␉␉; found it, get label location␍␊ |
␍␊ |
␉mov␉eax, [es:(bx+BOOTOFF)+PARTSEC]␊ |
␉␉␉␉␉; offset to NeXT partition␍␊ |
␉add␉eax, DISKLABEL␉␉; add offset to the label␍␊ |
␉jmp␉getLabl␉␉␉; fetch that label␍␊ |
␍␊ |
cont:␍␊ |
␉add␉bx, PARTSZ␍␊ |
␉loop␉again␉␉␉; if more part table entries,␊ |
␉␉␉␉␉; keep looking␍␊ |
␍␊ |
␉; fall through, didn't find NeXT disk partition entry␍␊ |
␍␊ |
no_fdisk:␍␊ |
ENDIF␊ |
␉; Read NeXT disk label␍␊ |
␉mov␉eax, DISKLABEL␉␉; Get block number of label␍␊ |
getLabl:␍␊ |
␉mov␉bx,BOOTOFF␉␉; read into load area␊ |
␉mov␉cx,1␊ |
␉call␉readSectors␍␊ |
␍␊ |
␉; we used to think about testing the disk label version here...␍␊ |
␊ |
␉mov␉bx,BOOTOFF␉␉; point to beginning of label␊ |
␍␊ |
␉; Use values from label to read entire boot program␍␊ |
␉; Get block number of boot␍␊ |
␉; Get dl_secsize and dl_boot0_blkno[0]␍␊ |
␉mov␉edx, [es:(bx + DL_DISKTAB+DT_SECSIZE)]␍␊ |
␉bswap␉edx␉␉␉; edx -> sector size␍␊ |
␍␊ |
␉mov␉eax, [es:(bx + DL_DISKTAB+DT_BOOT0_BLKNO)]␍␊ |
␉bswap␉eax␉␉␉; eax -> block #␍␊ |
␊ |
␉; Compute dl_secsize * dt_boot0_blkno[0] / 512␍␊ |
␉shr␉edx, 9␉␉␉; divide dl_secsize by 512␍␊ |
␉mul␉edx␉␉␉; multiply boot block loc␊ |
␉␉␉␉␉; by dl_secsize/512␍␊ |
␉␉␉␉␉; eax has secno␍␊ |
␉mov␉bx, (BUFSZ + BOOTOFF)␉; read boot2 into BOOTSEG:BUFSZ␍␊ |
␉mov␉edi, LOADSZ␉␉; read this many sectors␍␊ |
nexsec:␍␊ |
␉␉push␉eax␉␉; push sector #␍␊ |
␉␉push␉bx␉␉; push buffer address␍␊ |
␉␉mov␉ecx, edi␉; max number of sectors to read␊ |
␉␉␊ |
␉␉call␉readSectors␍␊ |
␉␉pop␉bx␍␊ |
␉␉pop␉eax␍␊ |
␉␉add␉eax, ecx␉; add number of sectors read␊ |
␉␉sub␉di, cx␊ |
␉␉shl␉cx, 9␉␉; multiply by 512 bytes per sector␊ |
␉␉add␉bx, cx␊ |
␊ |
␉cmp di, 0␊ |
␉jne␉nexsec␍␊ |
␊ |
␉ret␍␊ |
␍␊ |
spt:␉DW␉0␉␉␉; sectors;track (one-based)␍␊ |
spc:␉DW␉0␉␉␉; tracks;cylinder (zero-based)␍␊ |
nsec:␉DW␉0␉␉␉; number of sectors to read␊ |
␍␊ |
readSectors:␉; eax has starting block #, bx has offset from BOOTSEG␍␊ |
␉␉; cx has maximum number of sectors to read␊ |
␉␉; Trashes ax, bx, cx, dx␍␊ |
␍␊ |
␉; BIOS call "INT 0x13 Function 0x2" to read sectors␍␊ |
␉;␉ah = 0x2␉al = number of sectors␍␊ |
␉;␉ch = cylinder␉cl = sector␍␊ |
␉;␉dh = head␉dl = drive (0x80=hard disk, 0=floppy disk)␍␊ |
␉;␉es:bx = segment:offset of buffer␍␊ |
␊ |
IF␉( BOOTDEV EQ FLOPPY )␊ |
␉␉push␉eax␍␊ |
␉␉mov␉al,'.'␍␊ |
␉␉call␉putchr␍␊ |
␉␉pop␉eax␍␊ |
ENDIF␍␊ |
␍␊ |
␉push␉bx␉␉␉; save offset␍␊ |
␉mov [WORD nsec], cx␊ |
␍␊ |
␉mov␉ebx, eax␉␉; bx -> block #␍␊ |
␉xor␉ecx, ecx␍␊ |
␉mov␉cx, [WORD spc]␉␉; cx -> sectors/cyl␍␊ |
␍␊ |
␉xor␉edx,edx␉␉␉; prepare for division␍␊ |
␉div␉ecx␉␉␉; eax = cyl, edx=remainder␍␊ |
␉push␉ax␉␉␉; save cylinder #, sec/spc␍␊ |
␍␊ |
␉mov␉eax, edx␍␊ |
␉mov␉cx, [WORD spt]␉␉; ecx -> sectors/track␍␊ |
␉xor␉edx,edx␉␉␉; prepare for division␍␊ |
␉div␉ecx␉␉␉; eax = head␍␊ |
␉push␉ax␉␉␉; save head, (secspc)/spt␍␊ |
␍␊ |
␉mov␉eax, ebx␉␉; reload block #␍␊ |
␉xor␉edx, edx␉␉; prepare for division␍␊ |
␉div␉ecx␉␉␉; edx has sector #␍␊ |
␍␊ |
␉sub␉cx, dx ␉␉␉;cx now has number of sectors to read␊ |
␉cmp␉cx, [WORD nsec]␊ |
␉jge␉last␉␉␉; use the minimum of bx and cx␊ |
␉mov␉[WORD nsec], cx␊ |
last:␊ |
␊ |
␉mov␉cx, dx␉␉␉; cl -> sector␍␊ |
␉inc␉cl␉␉␉; starts @ 1␍␊ |
␉pop␉ax␉␉␉; get head␍␊ |
␉mov␉dh, al␉␉␉; dh -> head␍␊ |
␍␊ |
␉pop␉ax␉␉␉; get cyl␍␊ |
␉mov␉ch, al␉␉␉; ch -> cyl␍␊ |
␉mov␉dl, BOOTDEV␉␉; floppy disk␍␊ |
␍␊ |
␉xor␉al,al␍␊ |
␉shr␉ax,2␍␊ |
␉or␉cl,al␉␉␉; retain pesky big cyl bits␍␊ |
␊ |
;␉mov␉al, 1␉␉␉; get # of sectors␍␊ |
;pop ax ; number of sectors to read␊ |
mov ax, [WORD nsec]␊ |
␉pop␉bx␉␉␉; get buffer␍␊ |
␉mov␉ah, 2␉␉␉; bios read function␍␊ |
␉␍␊ |
␉int␉13h␍␊ |
␍␊ |
␉jb␉read_error␍␊ |
mov cx, [WORD nsec] ; return number of sectors read␊ |
␉ret␍␊ |
␍␊ |
␍␊ |
getinfo:␉; get some drive parameters␍␊ |
␉mov␉dl, BOOTDEV␉␉; boot drive is drive C␍␊ |
␉mov␉ah, 8h␍␊ |
␉push␉es␊ |
␉int␉13h␍␊ |
␉pop␉es␊ |
␍␊ |
␉mov␉al, dh␉␉␉; max head #␍␊ |
␉inc␉al␉␉␉; al -> tracks/cyl␍␊ |
␉and␉cx, 3fh␉␉␉; cl -> secs/track␍␊ |
␉mul␉cl␉␉␉; ax -> secs/cyl␍␊ |
␉mov␉[WORD spc], ax␍␊ |
␉mov␉[WORD spt], cx␍␊ |
␍␊ |
␉ret␍␊ |
␍␊ |
␍␊ |
message:␉␉␉␉; write the error message in ds:esi␊ |
␉␉␉␉␉; to console␍␊ |
␉push␉es␍␊ |
␉mov␉ax,ds␍␊ |
␉mov␉es,ax␍␊ |
␍␊ |
␉mov␉bx, 1␉␉␉; bh=0, bl=1 (blue)␍␊ |
␉cld␍␊ |
␍␊ |
nextb:␍␊ |
␉lodsb␉␉␉␉; load a byte into al␍␊ |
␉cmp␉al, 0␍␊ |
␉je␉done␍␊ |
␉mov␉ah, 0eh␉␉␉; bios int 10, function 0xe␍␊ |
␉int␉10h␉␉␉; bios display a byte in tty mode␍␊ |
␉jmp␉nextb␍␊ |
done:␉pop␉es␍␊ |
␉ret␍␊ |
␍␊ |
putchr:␍␊ |
␉push␉bx␍␊ |
␉mov␉bx, 1␉␉␉; bh=0, bl=1 (blue)␍␊ |
␉mov␉ah, 0eh␉␉␉; bios int 10, function 0xe␍␊ |
␉int␉10h␉␉␉; bios display a byte in tty mode␍␊ |
␉pop␉bx␍␊ |
␉ret␍␊ |
␊ |
IF␉SDEBUG␍␊ |
hexout:␍-␉␉␉␉; print ebx as hex number␊ |
␉push␉cx␍␊ |
␉push␉ax␍␊ |
␉mov␉cx,8␉␉␉;output 8 nibbles␍␊ |
␍␊ |
htop:␍␊ |
␉␉rol␉ebx,4␍␊ |
␉␉mov␉al,bl␍␊ |
␉␉and␉al,0fh␍␊ |
␉␉cmp␉al,0ah␍␊ |
␉␉jb␉o_digit␍␊ |
␉␉add␉al,'A'-10␍␊ |
␉␉jmp␉o_print␍␊ |
o_digit:␉add␉al,'0'␍␊ |
o_print:␉call␉putchr␍␊ |
␉␉dec␉cx␍␊ |
␉␉jne␉htop␍␊ |
;␉mov␉al,10␍␊ |
;␉call␉putchr␍␊ |
;␉mov␉al,13␍␊ |
;␉call␉putchr␍␊ |
␉mov␉al,' '␊ |
␉call␉putchr␊ |
␉pop␉ax␍␊ |
␉pop␉cx␍␊ |
␉ret␍␊ |
ENDIF␍␊ |
␍␊ |
␍␊ |
read_error:␍␊ |
␉mov␉si, OFFSET eread␍␊ |
boot_exit:␉␉␉␉; boot_exit: write error message and halt␍␊ |
␉call␉message␉␉␉; display error message␍␊ |
halt:␉jmp␉halt␍␊ |
␍␊ |
␍␊ |
intro:␉db␉10,'NEXTSTEP boot1 vXX.XX.XX.XX.XX',10,13,0␍␊ |
eread:␉db␉'Read error',10,13,0␍␊ |
␊ |
; the last 2 bytes in the sector contain the signature␍␊ |
d1:␍␊ |
␉a2 = 510 - (d1 - start)␍␊ |
␉DB a2 dup (0)␍␊ |
␉DW 0AA55h␍␊ |
␉ENDS␍␊ |
␉END␍␊ |