/*
* FAT Partition Boot Sector. Loaded at 0x7C00:
* 8a pbs.s; 8l -o pbs -l -H3 -T0x7C00 pbs.8
* Will load the target at LOADSEG*16+LOADOFF, so the target
* should be probably be loaded with LOADOFF added to the
* -Taddress.
* If LOADSEG is a multiple of 64KB and LOADOFF is 0 then
* targets larger than 64KB can be loaded.
*
* This code uses the traditional INT13 BIOS interface and can
* therefore only access the first 8.4GB of the disc.
*
* It relies on the _volid field in the FAT header containing
* the LBA of the root directory.
*/
#include "mem.h"
#define LOADSEG (0x10000/16) /* where to load code (64KB) */
#define LOADOFF 0
#define DIROFF 0x0200 /* where to read the root directory */
/*
* FAT directory entry.
*/
#define Dname 0x00
#define Dext 0x08
#define Dattr 0x0B
#define Dtime 0x16
#define Ddate 0x18
#define Dstart 0x1A
#define Dlengthlo 0x1C
#define Dlengthhi 0x1E
#define Dirsz 0x20
/*
* Data is kept on the stack, indexed by rBP.
*/
#define Xdap 0x00 /* disc address packet */
#define Xrootsz 0x10 /* file data area */
#define Xdrive 0x12 /* boot drive, passed by BIOS or MBR */
#define Xtotal 0x14 /* sum of allocated data above */
_magic:
JMP 0x7C3E
_version:
SKIP 8
_sectsize:
SKIP 2
_clustsize:
SKIP 1
_nresrv:
SKIP 2
_nfats:
SKIP 1
_rootsize:
SKIP 2
_volsize:
SKIP 2
_mediadesc:
SKIP 1
_fatsize:
SKIP 2
_trksize:
SKIP 2
_nheads:
SKIP 2
_nhiddenlo:
SKIP 2
_nhiddenhi:
SKIP 2
_bigvolsize:
SKIP 4
_driveno:
SKIP 1
_reserved0:
SKIP 1
_bootsig:
SKIP 1
_volid:
SKIP 4
_label:
SKIP 11
_type:
SKIP 8
_start0x3E:
CLI
/* zero SS, DS, ES (will zero CS later) */
XOR AX, AX
MOV AX, SS
MOV AX, DS
MOV AX, ES
/* set the indexed-data pointer, save boot drive */
MOV _magic-Xtotal, SP
MOV SP, BP
MOV DL, [Xdrive+BP]
/* booting from a CD starts us at 7C0:0. switch to 0:7C00 */
PUSH AX
PUSH csswitch
RETF
csswitch:
STI
/* for that warm, fuzzy feeling */
MOV confidence, SI
CALL puts
CALL dreset
/* read the root directory */
MOV [_volid], AX /* BUG: compute these? */
MOV [_volid+2], DX
MOV _magic+DIROFF, BX
CALL biosread
/* compare first directory entry */
MOV 512/Dirsz, BX
MOV _magic+DIROFF, DI
nextcmp:
PUSH DI
MOV bootfile, SI
MOV Dattr, CX
REP CMPS
POP DI
JEQ found
DEC BX
JEQ buggery
ADD Dirsz, DI
JMP nextcmp
found:
XOR BX, BX /* a handy value */
MOV [_rootsize], AX /* calculate and save Xrootsz */
MOV Dirsz, CX
MUL CX
MOV [_sectsize], CX
PUSH CX
DEC CX
ADD CX, AX
ADC BX, DX
POP CX /* [_sectsize] */
DIV CX
PUSH AX /* Xrootsz */
/*
* DI points to the matching directory entry
*/
LXW(Dstart, xDI, rAX) /* starting sector address */
DEC AX /* that's just the way it is */
DEC AX
LB(_clustsize(SB), rCL)
XOR CH,CH
MUL CX
MOV [_volid], CX /* Xrootlo */
LW(_volid(SB), rCX) /* Xrootlo */
ADD(rCX, rAX)
LW(_volid+2(SB), rCX) /* Xroothi */
ADC(rCX, rDX)
POPR(rCX) /* Xrootsz */
ADD(rCX, rAX)
ADC(rBX, rDX)
PUSHR(rAX) /* calculate how many sectors to read */
PUSHR(rDX)
LXW(Dlengthlo, xDI, rAX)
LXW(Dlengthhi, xDI, rDX)
LW(_sectsize(SB), rCX)
PUSHR(rCX)
DEC(rCX)
ADD(rCX, rAX)
ADC(rBX, rDX)
POPR(rCX) /* _sectsize(SB) */
DIV(rCX)
MW(rAX, rCX)
POPR(rDX)
POPR(rAX)
LWI(LOADSEG, rBX) /* address to load into (seg+offset) */
MTSR(rBX, rES) /* seg */
LWI(LOADOFF, rBX) /* offset */
_readboot:
CALL16(BIOSread(SB)) /* read the sector */
LW(_sectsize(SB), rDI) /* bump addresses/counts */
ADD(rDI, rBX)
JCC _incsecno
MFSR(rES, rDI) /* next 64KB segment */
ADDI(0x1000, rDI)
MTSR(rDI, rES)
_incsecno:
CLR(rDI)
INC(rAX)
ADC(rDI, rDX)
LOOP _readboot
LWI(LOADSEG, rDI) /* set rDS for loaded code */
MTSR(rDI, rDS)
FARJUMP16(LOADSEG, LOADOFF) /* no deposit, no return */
TEXT buggery(SB), $0
LWI(error(SB), rSI)
CALL16(BIOSputs(SB))
_wait:
CLR(rAX) /* wait for almost any key */
BIOSCALL(0x16)
_reset:
CLR(rBX) /* set ES segment for BIOS area */
MTSR(rBX, rES)
LWI(0x0472, rBX) /* warm-start code address */
LWI(0x1234, rAX) /* warm-start code */
POKEW /* MOVW AX, ES:[BX] */
FARJUMP16(0xFFFF, 0x0000) /* reset */
/*
* Read a sector from a disc. On entry:
* rDX:rAX sector number
* rES:rBX buffer address
* For BIOSCALL(0x13):
* rAH 0x02
* rAL number of sectors to read (1)
* rCH low 8 bits of cylinder
* rCL high 2 bits of cylinder (7-6), sector (5-0)
* rDH head
* rDL drive
* rES:rBX buffer address
*/
TEXT BIOSread(SB), $0
LWI(5, rDI) /* retry count (ATAPI ZIPs suck) */
_retry:
PUSHA /* may be trashed by BIOSCALL */
PUSHR(rBX)
LW(_trksize(SB), rBX)
LW(_nheads(SB), rDI)
IMUL(rDI, rBX)
OR(rBX, rBX)
JZ _ioerror
_okay:
DIV(rBX) /* cylinder -> rAX, track,sector -> rDX */
MW(rAX, rCX) /* save cylinder */
ROLI(0x08, rCX) /* swap rC[HL] */
SHLBI(0x06, rCL) /* move high bits up */
MW(rDX, rAX)
CLR(rDX)
LW(_trksize(SB), rBX)
DIV(rBX) /* head -> rAX, sector -> rDX */
INC(rDX) /* sector numbers are 1-based */
ANDI(0x003F, rDX) /* should not be necessary */
OR(rDX, rCX)
MW(rAX, rDX)
SHLI(0x08, rDX) /* form head */
LBPB(Xdrive, rDL) /* form drive */
POPR(rBX)
LWI(0x0201, rAX) /* form command and sectors */
BIOSCALL(0x13) /* CF set on failure */
JCC _BIOSreadret
POPA
DEC(rDI) /* too many retries? */
JEQ _ioerror
CALL16(dreset(SB))
JMP _retry
_ioerror:
LWI(ioerror(SB), rSI)
CALL16(BIOSputs(SB))
JMP _wait
_BIOSreadret:
POPA
RET
TEXT dreset(SB), $0
PUSHA
CLR(rAX) /* rAH == 0 == reset disc system */
LBPB(Xdrive, rDL)
BIOSCALL(0x13)
ORB(rAH, rAH) /* status (0 == success) */
POPA
JNE _ioerror
RET
/*
* Output a string to the display.
* String argument is in rSI.
*/
TEXT BIOSputs(SB), $0
PUSHA
CLR(rBX)
_BIOSputs:
LODSB
ORB(rAL, rAL)
JEQ _BIOSputsret
LBI(0x0E, rAH)
BIOSCALL(0x10)
JMP _BIOSputs
_BIOSputsret:
POPA
RET
/* "Bad format or I/O error\r\nPress almost any key to reboot..."*/
TEXT error(SB), $0
BYTE $'B'; BYTE $'a'; BYTE $'d'; BYTE $' ';
BYTE $'f'; BYTE $'o'; BYTE $'r'; BYTE $'m';
BYTE $'a'; BYTE $'t'; BYTE $' '; BYTE $'o';
BYTE $'r'; BYTE $' ';
/* "I/O error\r\nPress almost any key to reboot..." */
TEXT ioerror(SB), $0
BYTE $'I'; BYTE $'/'; BYTE $'O'; BYTE $' ';
BYTE $'e'; BYTE $'r'; BYTE $'r'; BYTE $'o';
BYTE $'r'; BYTE $'\r';BYTE $'\n';
BYTE $'P'; BYTE $'r'; BYTE $'e'; BYTE $'s';
BYTE $'s'; BYTE $' '; BYTE $'a'; BYTE $' ';
BYTE $'k'; BYTE $'e'; BYTE $'y';
BYTE $' '; BYTE $'t'; BYTE $'o'; BYTE $' ';
BYTE $'r'; BYTE $'e'; BYTE $'b'; BYTE $'o';
BYTE $'o'; BYTE $'t';
BYTE $'.'; BYTE $'.'; BYTE $'.';
BYTE $'\z';
#ifdef USEBCOM
/* "B COM" */
TEXT bootfile(SB), $0
BYTE $'B'; BYTE $' '; BYTE $' '; BYTE $' ';
BYTE $' '; BYTE $' '; BYTE $' '; BYTE $' ';
BYTE $'C'; BYTE $'O'; BYTE $'M';
BYTE $'\z';
#else
/* "9LOAD " */
TEXT bootfile(SB), $0
BYTE $'9'; BYTE $'L'; BYTE $'O'; BYTE $'A';
BYTE $'D'; BYTE $' '; BYTE $' '; BYTE $' ';
BYTE $' '; BYTE $' '; BYTE $' ';
BYTE $'\z';
#endif /* USEBCOM */
/* "PBS..." */
TEXT confidence(SB), $0
BYTE $'P'; BYTE $'B'; BYTE $'S'; BYTE $'.';
BYTE $'.'; BYTE $'.';
BYTE $'\z';
|