/*
* Hard disc boot block. Loaded at 0x7C00, relocates to 0x0600:
*      8a mbr.s; 8l -o mbr -l -H3 -T0x0600 mbr.8
*/
#include "x16.h"
#include "mem.h"

/*#define FLOPPY        1               /* test on a floppy */
#define TRACE(C)        PUSHA;\
                       CLR(rBX);\
                       MOVB $C, AL;\
                       LBI(0x0E, rAH);\
                       BIOSCALL(0x10);\
                       POPA

/*
* We keep data on the stack, indexed by BP.
*/
#define Xdap            0x00            /* disc address packet */
#define Xtable          0x10            /* partition table entry */
#define Xdrive          0x12            /* starting disc */
#define Xtotal          0x14            /* sum of allocated data above */

/*
* Start: loaded at 0000:7C00, relocate to 0000:0600.
* Boot drive is in rDL.
*/
TEXT _start(SB), $0
       CLI
       CLR(rAX)
       MTSR(rAX, rSS)                  /* 0000 -> rSS */
       LWI((0x7C00-Xtotal), rSP)       /* 7Bxx -> rSP */
       MW(rSP, rBP)                    /* set the indexed-data pointer */

       MTSR(rAX, rDS)                  /* 0000 -> rDS, source segment */
       LWI(0x7C00, rSI)                /* 7C00 -> rSI, source offset */
       MTSR(rAX, rES)                  /* 0000 -> rES, destination segment */
       LWI(0x600, rDI)                 /* 0600 -> rDI, destination offset */
       LWI(0x100, rCX)                 /* 0100 -> rCX, loop count (words) */

       CLD
       REP; MOVSL                      /* MOV DS:[(E)SI] -> ES:[(E)DI] */

       FARJUMP16(0x0000, _start0600(SB))

TEXT _start0600(SB), $0
#ifdef FLOPPY
       LBI(0x80, rDL)
#else
       CLRB(rAL)                       /* some systems pass 0 */
       CMPBR(rAL, rDL)
       JNE _save
       LBI(0x80, rDL)
#endif /* FLOPPY */
_save:
       SXB(rDL, Xdrive, xBP)           /* save disc */

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

       LWI(_start+0x01BE(SB), rSI)     /* address of partition table */
       LWI(0x04, rCX)                  /* 4 entries in table */
       LBI(0x80, rAH)                  /* active entry value */
       CLRB(rAL)                       /* inactive entry value */

_activeloop0:
       LXB(0x00, xSI, rBL)             /* get active entry from table */
       CMPBR(rBL, rAH)                 /* is this an active entry? */
       JEQ _active

       CMPBR(rBL, rAL)                 /* if not active it should be 0 */
       JNE _invalidMBR

       ADDI(0x10, rSI)                 /* next table entry */
       DEC(rCX)
       JNE _activeloop0

       LWI(noentry(SB), rSI)
       CALL16(buggery(SB))

_active:
       MW(rSI, rDI)                    /* save table address */

_activeloop1:
       ADDI(0x10, rSI)                 /* next table entry */
       DEC(rCX)
       JEQ _readsector

       LXB(0x00, xSI, rBL)             /* get active entry from table */
       CMPBR(rBL, rAH)                 /* is this an active entry? */
       JNE _activeloop1                /* should only be one active */

_invalidMBR:
       LWI(invalidMBR(SB), rSI)
       CALL16(buggery(SB))

_readsector:
       LBI(0x41, rAH)                  /* check extensions present */
       LWI(0x55AA, rBX)
       LXB(Xdrive, xBP, rDL)           /* drive */
       BIOSCALL(0x13)                  /* CF set on failure */
       JCS _readsector2
       CMPI(0xAA55, rBX)
       JNE _readsector2
       ANDI(0x0001, rCX)
       JEQ _readsector2

_readsector42:
       SBPBI(0x10, Xdap+0)             /* packet size */
       SBPBI(0x00, Xdap+1)             /* reserved */
       SBPBI(0x01, Xdap+2)             /* number of blocks to transfer */
       SBPBI(0x00, Xdap+3)             /* reserved */
       SBPWI(0x7C00, Xdap+4)           /* transfer buffer :offset */
       SBPWI(0x0000, Xdap+6)           /* transfer buffer seg: */
       LXW(0x08, xDI, rAX)             /* LBA (64-bits) */
       SBPW(rAX, Xdap+8)
       LXW(0x0A, xDI, rAX)
       SBPW(rAX, Xdap+10)
       SBPWI(0x0000, Xdap+12)
       SBPWI(0x0000, Xdap+14)

       MW(rBP, rSI)                    /* disk address packet */
       LBI(0x42, rAH)                  /* extended read */
       BIOSCALL(0x13)                  /* CF set on failure */
       JCC _readsectorok

       LWI(ioerror(SB), rSI)
       CALL16(buggery(SB))

/*
* Read a sector from a disc using the traditional BIOS call.
* For BIOSCALL(0x13/AH=0x02):
*   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
*/
_readsector2:
       LXB(0x01, xDI, rDH)             /* head */
       LXW(0x02, xDI, rCX)             /* save active cylinder/sector */

       LWI(0x0201, rAX)                /* read one sector */
       LXB(Xdrive, xBP, rDL)           /* drive */
       LWI(0x7C00, rBX)                /* buffer address (rES already OK) */
       BIOSCALL(0x13)                  /* CF set on failure */
       JCC _readsectorok

       LWI(ioerror(SB), rSI)
       CALL16(buggery(SB))

_readsectorok:
       LWI(0x7C00, rBX)                /* buffer address (rES already OK) */
       LXW(0x1FE, xBX, rAX)
       CMPI(0xAA55, rAX)
       JNE _bbnotok

       /*
        * Jump to the loaded PBS.
        * rDL and rSI should still contain the drive
        * and partition table pointer respectively.
        */
       MW(rDI, rSI)
       FARJUMP16(0x0000, 0x7C00)

_bbnotok:
       LWI(invalidPBS(SB), rSI)

TEXT buggery(SB), $0
       CALL16(BIOSputs(SB))
       LWI(reboot(SB), rSI)
       CALL16(BIOSputs(SB))

_wait:
       CLR(rAX)                        /* wait for 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 */

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

/* "No active entry in MBR" */
TEXT noentry(SB), $0
       BYTE $'N'; BYTE $'o'; BYTE $' '; BYTE $'a';
       BYTE $'c'; BYTE $'t'; BYTE $'i'; BYTE $'v';
       BYTE $'e'; BYTE $' '; BYTE $'e'; BYTE $'n';
       BYTE $'t'; BYTE $'r'; BYTE $'y'; BYTE $' ';
       BYTE $'i'; BYTE $'n'; BYTE $' '; BYTE $'M';
       BYTE $'B'; BYTE $'R';
       BYTE $'\z';

/* "Invalid MBR" */
TEXT invalidMBR(SB), $0
       BYTE $'I'; BYTE $'n'; BYTE $'v'; BYTE $'a';
       BYTE $'l'; BYTE $'i'; BYTE $'d'; BYTE $' ';
       BYTE $'M'; BYTE $'B'; BYTE $'R';
       BYTE $'\z';

/* "I/O error" */
TEXT ioerror(SB), $0
       BYTE $'I'; BYTE $'/'; BYTE $'O'; BYTE $' ';
       BYTE $'e'; BYTE $'r'; BYTE $'r'; BYTE $'o';
       BYTE $'r';
       BYTE $'\z';

/* "Invalid PBS" */
TEXT invalidPBS(SB), $0
       BYTE $'I'; BYTE $'n'; BYTE $'v'; BYTE $'a';
       BYTE $'l'; BYTE $'i'; BYTE $'d'; BYTE $' ';
       BYTE $'P'; BYTE $'B'; BYTE $'S';
       BYTE $'\z';

/* "\r\nPress almost any key to reboot..." */
TEXT reboot(SB), $0
       BYTE $'\r';BYTE $'\n';
       BYTE $'P'; BYTE $'r'; BYTE $'e'; BYTE $'s';
       BYTE $'s'; BYTE $' '; BYTE $'a'; BYTE $'l';
       BYTE $'m'; BYTE $'o'; BYTE $'s'; BYTE $'t';
       BYTE $' '; BYTE $'a'; BYTE $'n'; BYTE $'y';
       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';

/* "MBR..." */
TEXT confidence(SB), $0
       BYTE $'M'; BYTE $'B'; BYTE $'R'; BYTE $'.';
       BYTE $'.'; BYTE $'.';
       BYTE $'\z';