/* * 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';