File:  [Plan 9 NeXT] / lucent / sys / src / boot / pc / bb.s
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 18:01:05 2018 UTC (8 years, 1 month ago) by root
Branches: lucent, MAIN
CVS tags: plan9, HEAD
Plan 9 NeXT

/*
 * Boot block. Loaded at 0x7C00
 *	8a bb.s; 8l -o bb -l -H3 -T0x7C00 bb.8
 */
/*
 * Can't write 16-bit code for 8a without getting into
 * lots of bother, so define some simple commands and
 * output the code directly.
 * This was a mistake in so many ways it's not true.
 * But I don't ever, ever have to do it again.
 */
#define rAX		0		/* rX  */
#define rCX		1
#define rDX		2
#define rBX		3
#define rSP		4		/* SP */
#define rBP		5		/* BP */
#define rSI		6		/* SI */
#define rDI		7		/* DI */

#define rAL		0		/* rL  */
#define rCL		1
#define rDL		2
#define rBL		3
#define rAH		4		/* rH */
#define rCH		5
#define rDH		6
#define rBH		7

#define rES		0		/* rS */
#define rCS		1
#define rSS		2
#define rDS		3
#define rFS		4
#define rGS		5

#define OP(o, m, r, rm)	BYTE $o;		/* op + modr/m byte */	\
			BYTE $((m<<6)|(r<<3)|rm)
#define OPrm(o, r, m)	OP(o, 0x00, r, 0x06);	/* general r <-> m */	\
			WORD $m;
#define OPrr(o, r0, r1)	OP(o, 0x03, r0, r1);	/* general r -> r */	\

#define LW(m, rX)	OPrm(0x8B, rX, m)	/* m -> rX */
#define LBPW(x, r)	OP(0x8B, 0x02, r, 0x06);/* x(rBP) -> r */	\
			WORD $x
#define LB(m, rB)	OPrm(0x8A, rB, m)	/* m -> r[HL] */
#define LBPB(x, r)	OP(0x8A, 0x01, r, 0x06);/* x(rBP) -> r */	\
			BYTE $x
#define SW(rX, m)	OPrm(0x89, rX, m)	/* rX -> m */
#define SBPW(r, x)	OP(0x89, 0x02, r, 0x06);/* r -> x(rBP) */	\
			WORD $x
#define STB(rB, m)	OPrm(0x88, rB, m)	/* rB -> m */
#define SBPB(r, x)	OP(0x88, 0x01, r, 0x06);/* r -> x(rBP) */	\
			BYTE $x
#define LWI(i, rX)	BYTE $(0xB8+rX);	/* i -> rX */		\
			WORD $i;
#define LBI(i, rB)	BYTE $(0xB0+rB);	/* i -> r[HL] */	\
			BYTE $i

#define MW(r0, r1)	OPrr(0x89, r0, r1)	/* r0 -> r1 */
#define MFSR(rS, rX)	OPrr(0x8C, rS, rX)	/* rS -> rX */
#define MTSR(rX, rS)	OPrr(0x8E, rS, rX)	/* rX -> rS */

#define ADC(r0, r1)	OPrr(0x11, r0, r1)	/* r0 + r1 -> r1 */
#define ADD(r0, r1)	OPrr(0x01, r0, r1)	/* r0 + r1 -> r1 */
#define AND(r0, r1)	OPrr(0x21, r0, r1)	/* r0 & r1 -> r1 */
#define ANDI(i, r)	OP(0x81, 0x03, 0x04, r);/* i & r -> r */	\
			WORD $i;
#define CLR(r)		OPrr(0x31, r, r)	/* r^r -> r */
#define CLRB(r)		OPrr(0x30, r, r)	/* r^r -> r */
#define CMP(r0, r1)	OPrr(0x39, r0, r1)	/* r1 - r0 -> flags */
#define CMPB(r0, r1)	OPrr(0x38, r0, r1)	/* r1 - r0 -> flags */
#define DEC(r)		BYTE $(0x48|r)		/* r-1 -> r */
#define DIV(r)		OPrr(0xF7, 0x06, r)	/* rAX/r -> rDX:rAX */
#define INC(r)		BYTE $(0x40|r)		/* r+1 -> r */
#define MUL(r)		OPrr(0xF7, 0x04, r)	/* r * rAX -> rDX:rAX */
#define OR(r0, r1)	OPrr(0x09, r0, r1)	/* r0|r1 -> r1 */
#define ORB(r0, r1)	OPrr(0x08, r0, r1)	/* r0|r1 -> r1 */
#define ROLI(i, r)	OPrr(0xC1, 0x00, r);	/* r<<>>i -> r */	\
			BYTE $i;
#define SHLI(i, r)	OPrr(0xC1, 0x04, r);	/* r<<i -> r */		\
			BYTE $i;
#define SHLBI(i, r)	OPrr(0xC0, 0x04, r);	/* r<<i -> r */		\
			BYTE $i;

#define CALL(f)		LWI(f, rDI);		/* &f -> rDI */		\
			BYTE $0xFF;		/* (*rDI) */		\
			BYTE $0xD7;
#define FARJUMP(s, o)	BYTE $0xEA;		/* far jump to s:o */	\
			WORD $o; WORD $s
#define	DELAY		BYTE $0xEB;		/* jmp .+2 */		\
			BYTE $0x00
#define SYSCALL(b)	INT $b			/* INT $b */

#define POKEW		BYTE $0x26;		/* MOVW	rAX, rES:[rBX] */	\
			BYTE $0x89; BYTE $0x07
#define OUTB(p, d)	LBI(d, rAL);		/* d -> I/O port p */	\
			BYTE $0xE6;					\
			BYTE $p; DELAY
#define PUSHA		BYTE $0x60
#define PUSHW(r)	BYTE $(0x50|r)		/* r  -> (--rSP) */
#define POPA		BYTE $0x61
#define POPW(r)		BYTE $(0x58|r)		/* (rSP++) -> r */
#define NOP		BYTE $0x90		/* nop */

/*
 * Now on to the real work.
 * If you're stilll reading, you have a
 * stomach of steel.
 */
#define RDIRBUF		0x00200		/* where to read the root directory (offset) */
#define CODEBUF		0x10000		/* where to load code (64Kb) */

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

/*
 * We keep data on the stack, indexed by rBP.
 */
#define Xdrive		0x00		/* boot drive, passed by BIOS in rDL */
#define Xrootlo		0x02		/* offset of root directory */
#define Xroothi		0x04
#define Xrootsz		0x06		/* file data area */
#define Xtotal		0x08		/* 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
	MFSR(rCS, rAX)			/* set the data and stack segments */
	MTSR(rAX, rDS)
	MTSR(rAX, rSS)
	MTSR(rAX, rES)
	LWI(_magic-Xtotal(SB), rSP)
	MW(rSP, rBP)			/* set the indexed-data pointer */

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

	STI

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

	CLR(rAX)			/* rAH == 0 == reset disc system */
	PUSHW(rBP)			/* may be trashed by SYSCALL */
	SYSCALL(0x13)			/* rDL == boot drive */
	POPW(rBP)
	ORB(rAH, rAH)			/* status (0 == successful completion) */
	JEQ _jmp00			/* can't rely on 8l doing other than short jumps */
	CALL(buggery(SB))

_jmp00:
	CLR(rAX)

	LB(_nfats(SB), rAL)		/* calculate root directory offset */
	LW(_fatsize(SB), rCX)
	MUL(rCX)
	LW(_nhiddenlo(SB), rCX)
	ADD(rCX, rAX)
	LW(_nhiddenhi(SB), rCX)
	ADC(rCX, rDX)
	LW(_nresrv(SB), rCX)
	ADD(rCX, rAX)
	LWI(0x0000, rCX)		/* don't set flags */
	ADC(rCX, rDX)

	SBPW(rAX, Xrootlo)		/* save */
	SBPW(rDX, Xroothi)

	LWI(_magic+RDIRBUF(SB), rBX)
	CALL(BIOSread(SB))		/* read the root directory */

	LWI(_magic+RDIRBUF(SB), rDI)	/* compare first directory entry */
	LWI(bootfile(SB), rSI)
	LWI(Dattr, rCX)
	REP
	CMPSB
	JEQ _jmp02
	CALL(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)
	DEC(rCX)
	ADD(rCX, rAX)
	ADC(rBX, rDX);
	LW(_sectsize(SB), rCX)
	DIV(rCX)
	SBPW(rAX, Xrootsz)

	LW(_magic+(RDIRBUF+Dstart)(SB), rAX)	/* calculate starting sector address */
	DEC(rAX)				/* that's just the way it is... */
	DEC(rAX)
	LB(_clustsize(SB), rCL)
	CLRB(rCH)
	MUL(rCX)
	LBPW(Xrootlo, rCX)
	ADD(rCX, rAX)
	LBPW(Xroothi, rCX)
	ADC(rCX, rDX)
	LBPW(Xrootsz, rCX)
	ADD(rCX, rAX)
	ADC(rBX, rDX);

	PUSHW(rAX)
	PUSHW(rDX)
	LW(_magic+(RDIRBUF+Dlengthlo)(SB), rAX)	/* calculate how many sectors to read */
	LW(_magic+(RDIRBUF+Dlengthhi)(SB), rDX)
	LW(_sectsize(SB), rCX)
	DEC(rCX)
	ADD(rCX, rAX)
	ADC(rBX, rDX);
	LW(_sectsize(SB), rCX)
	DIV(rCX)
	MW(rAX, rCX)
	POPW(rDX)
	POPW(rAX)

	LWI((CODEBUF/16), rBX)		/* address to load into (seg + offset) */
	MTSR(rBX, rES)
	LWI(0x0100, rBX)

_readboot:
	CALL(BIOSread(SB))		/* read the sector */

	LW(_sectsize(SB), rDI)		/* bump addresses/counts */
	ADD(rDI, rBX)
	INC(rAX)
	LWI(0x0000, rDI)		/* don't set flags */
	ADC(rDI, rDX)
	LOOP _readboot

	LWI((CODEBUF/16), rDI)		/* set rDS for loaded code */
	MTSR(rDI, rDS)
	FARJUMP((CODEBUF/16), 0x0100)	/* no deposit, no return */

TEXT buggery(SB), $0
	LWI(error(SB), rSI)
	CALL(BIOSputs(SB))

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

	FARJUMP(0xFFFF, 0x0000)		/* reset */


/*
 * Read a sector from a disc. On entry:
 *   rDX:rAX	sector number
 *   rES:rBX	buffer address
 * For SYSCALL(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
	PUSHA				/* may be trashed by SYSCALL */

	LW(_trksize(SB), rDI)		/* rDI is scratch */
	DIV(rDI)			/* track -> rAX, sector -> rDX */

	INC(rDX)			/* sector numbers are 1-based */
	MW(rDX, rCX)
	ANDI(0x003F, rCX)		/* form sector bits in rCL */

	CLR(rDX)
	LW(_nheads(SB), rDI)
	DIV(rDI)			/* cylinder -> rAX, head -> rDX */

	SHLBI(0x06, rAH)		/* form cylinder hi and lo bits */
	ROLI(0x08, rAX)			/* swap rA[HL] */
	OR(rAX, rCX)			/* rCH, rCL now set */

	SHLI(0x08, rDX)			/* form head */
	LBPB(Xdrive, rDL)		/* form drive */

	LWI(0x0201, rAX)		/* form command and sectors */
	SYSCALL(0x13)			/* CF set on failure */
	JCC _BIOSreadret
	CALL(buggery(SB))

_BIOSreadret:
	POPA
	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)
	SYSCALL(0x10)
	JMP _BIOSputs

_BIOSputsret:
	POPA
	RET

TEXT error(SB), $0
	BYTE $'N'; BYTE $'o'; BYTE $'t'; BYTE $' '; BYTE $'a'; BYTE $' '; BYTE $'b'; BYTE $'o';
	BYTE $'o'; BYTE $'t'; BYTE $'a'; BYTE $'b'; BYTE $'l'; BYTE $'e'; BYTE $' '; BYTE $'d';
	BYTE $'i'; BYTE $'s'; BYTE $'c'; BYTE $' '; BYTE $'o'; BYTE $'r'; BYTE $' '; BYTE $'d';
	BYTE $'i'; BYTE $'s'; BYTE $'c'; 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 $'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';

TEXT bootfile(SB), $0
	BYTE $'B'; BYTE $' '; BYTE $' '; BYTE $' '; BYTE $' '; BYTE $' '; BYTE $' '; BYTE $' ';
	BYTE $'C'; BYTE $'O'; BYTE $'M';
	BYTE $'\z';

TEXT confidence(SB), $0
	BYTE $'B'; BYTE $'o'; BYTE $'o'; BYTE $'t'; BYTE $'i'; BYTE $'n'; BYTE $'g'; BYTE $'.';
	BYTE $'.'; BYTE $'.'; BYTE $'\r';BYTE $'\n';
	BYTE $'\z';

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.