Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 * 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 "x16.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 */

TEXT _magic(SB), $0
        BYTE $0xEB; BYTE $0x3C;         /* jmp .+ 0x3C  (_start0x3E) */
        BYTE $0x90                      /* nop */
TEXT _version(SB), $0
        BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
        BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
TEXT _sectsize(SB), $0
        BYTE $0x00; BYTE $0x00
TEXT _clustsize(SB), $0
        BYTE $0x00
TEXT _nresrv(SB), $0
        BYTE $0x00; BYTE $0x00
TEXT _nfats(SB), $0
        BYTE $0x00
TEXT _rootsize(SB), $0
        BYTE $0x00; BYTE $0x00
TEXT _volsize(SB), $0
        BYTE $0x00; BYTE $0x00
TEXT _mediadesc(SB), $0
        BYTE $0x00
TEXT _fatsize(SB), $0
        BYTE $0x00; BYTE $0x00
TEXT _trksize(SB), $0
        BYTE $0x00; BYTE $0x00
TEXT _nheads(SB), $0
        BYTE $0x00; BYTE $0x00
TEXT _nhiddenlo(SB), $0
        BYTE $0x00; BYTE $0x00
TEXT _nhiddenhi(SB), $0
        BYTE $0x00; BYTE $0x00;
TEXT _bigvolsize(SB), $0
        BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
TEXT _driveno(SB), $0
        BYTE $0x00
TEXT _reserved0(SB), $0
        BYTE $0x00
TEXT _bootsig(SB), $0
        BYTE $0x00
TEXT _volid(SB), $0
        BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
TEXT _label(SB), $0
        BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
        BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
        BYTE $0x00; BYTE $0x00; BYTE $0x00
TEXT _type(SB), $0
        BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
        BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;

_start0x3E:
        CLI
        CLR(rAX)
        MTSR(rAX, rSS)                  /* 0000 -> rSS */
        MTSR(rAX, rDS)                  /* 0000 -> rDS, source segment */
        MTSR(rAX, rES)
        LWI(_magic-Xtotal(SB), rSP)
        MW(rSP, rBP)                    /* set the indexed-data pointer */

        SBPB(rDL, Xdrive)               /* save the boot drive */

        /* booting from a CD starts us at 7C0:0.  Move to 0:7C00 */
        PUSHR(rAX)
        LWI(_nxt(SB), rAX)
        PUSHR(rAX)
        BYTE $0xCB      /* FAR RET */

TEXT _nxt(SB), $0
        STI

        LWI(confidence(SB), rSI)        /* for that warm, fuzzy feeling */
        CALL16(BIOSputs(SB))

        CALL16(dreset(SB))

_jmp00:
        LW(_volid(SB), rAX)             /* Xrootlo */
        LW(_volid+2(SB), rDX)           /* Xroothi */

        LWI(_magic+DIROFF(SB), rBX)
        CALL16(BIOSread(SB))            /* read the root directory */

        LWI((512/Dirsz), rBX)

        LWI(_magic+DIROFF(SB), rDI)     /* compare first directory entry */

_cmp00:
        PUSHR(rDI)                      /* save for later if it matches */
        LWI(bootfile(SB), rSI)
        LWI(Dattr, rCX)
        REP
        CMPSB
        POPR(rDI)
        JEQ _jmp02

        DEC(rBX)
        JEQ _jmp01

        ADDI(Dirsz, rDI)
        JMP _cmp00
_jmp01:
        CALL16(buggery(SB))

_jmp02:
        CLR(rBX)                        /* a handy value */
        LW(_rootsize(SB), rAX)          /* calculate and save Xrootsz */
        LWI(Dirsz, rCX)
        MUL(rCX)
        LW(_sectsize(SB), rCX)
        PUSHR(rCX)
        DEC(rCX)
        ADD(rCX, rAX)
        ADC(rBX, rDX)
        POPR(rCX)                       /* _sectsize(SB) */
        DIV(rCX)
        PUSHR(rAX)                      /* Xrootsz */

        /*
         * rDI points to the matching directory entry.
         */
        LXW(Dstart, xDI, rAX)           /* starting sector address */
        DEC(rAX)                        /* that's just the way it is */
        DEC(rAX)
        LB(_clustsize(SB), rCL)
        CLRB(rCH)
        MUL(rCX)
        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 $'1'; 
        BYTE $'.'; BYTE $'.'; BYTE $'.';
        BYTE $'\z';

Generated by GNU Enscript 1.6.6.