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