xref: /plan9-contrib/sys/src/boot/pc/pbslba.s (revision b94bb474148e9d24a82a427863d9c9eb4c20f4ae)
1/*
2 * FAT Partition Boot Sector. Loaded at 0x7C00:
3 *	8a pbslba.s; 8l -o pbslba -l -H3 -T0x7C00 pbslba.8
4 * Will load the target at LOADSEG*16+LOADOFF, so the target
5 * should be probably be loaded with LOADOFF added to the
6 * -Taddress.
7 * If LOADSEG is a multiple of 64KB and LOADOFF is 0 then
8 * targets larger than 64KB can be loaded.
9 *
10 * This code is uses Enhanced BIOS Services for Disc Drives and
11 * can be used with discs up to 137GB in capacity.
12 *
13 * It relies on the _volid field in the FAT header containing
14 * the LBA of the root directory.
15 */
16#include "x16.h"
17
18#define LOADSEG		(0x10000/16)	/* where to load code (64KB) */
19#define LOADOFF		0
20#define DIROFF		0x0200		/* where to read the root directory */
21
22/*
23 * FAT directory entry.
24 */
25#define Dname		0x00
26#define Dnamesz	0x0B
27#define Dext		0x08
28#define Dattr		0x0B
29#define Dtime		0x16
30#define Ddate		0x18
31#define Dstart		0x1A
32#define Dlengthlo	0x1C
33#define Dlengthhi	0x1E
34
35#define Dirsz		0x20
36
37/*
38 * Data is kept on the stack, indexed by rBP.
39 */
40#define Xdap		0x00		/* disc address packet */
41#define Xrootsz		0x10		/* file data area */
42#define Xdrive		0x12		/* boot drive, passed by BIOS or MBR */
43#define Xtotal		0x14		/* sum of allocated data above */
44
45TEXT _magic(SB), $0
46	BYTE $0xEB; BYTE $0x3C;		/* jmp .+ 0x3C  (_start0x3E) */
47	BYTE $0x90			/* nop */
48TEXT _version(SB), $0
49	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
50	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
51TEXT _sectsize(SB), $0
52	BYTE $0x00; BYTE $0x00
53TEXT _clustsize(SB), $0
54	BYTE $0x00
55TEXT _nresrv(SB), $0
56	BYTE $0x00; BYTE $0x00
57TEXT _nfats(SB), $0
58	BYTE $0x00
59TEXT _rootsize(SB), $0
60	BYTE $0x00; BYTE $0x00
61TEXT _volsize(SB), $0
62	BYTE $0x00; BYTE $0x00
63TEXT _mediadesc(SB), $0
64	BYTE $0x00
65TEXT _fatsize(SB), $0
66	BYTE $0x00; BYTE $0x00
67TEXT _trksize(SB), $0
68	BYTE $0x00; BYTE $0x00
69TEXT _nheads(SB), $0
70	BYTE $0x00; BYTE $0x00
71TEXT _nhiddenlo(SB), $0
72	BYTE $0x00; BYTE $0x00
73TEXT _nhiddenhi(SB), $0
74	BYTE $0x00; BYTE $0x00;
75TEXT _bigvolsize(SB), $0
76	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
77TEXT _driveno(SB), $0
78	BYTE $0x00
79TEXT _reserved0(SB), $0
80	BYTE $0x00
81TEXT _bootsig(SB), $0
82	BYTE $0x00
83TEXT _volid(SB), $0
84	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
85TEXT _label(SB), $0
86	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
87	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
88	BYTE $0x00; BYTE $0x00; BYTE $0x00
89TEXT _type(SB), $0
90	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
91	BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
92
93_start0x3E:
94	CLI
95	CLR(rAX)
96	MTSR(rAX, rSS)			/* 0000 -> rSS */
97	MTSR(rAX, rDS)			/* 0000 -> rDS, source segment */
98	MTSR(rAX, rES)
99	LWI(_magic-Xtotal(SB), rSP)
100	MW(rSP, rBP)			/* set the indexed-data pointer */
101
102	SBPB(rDL, Xdrive)		/* save the boot drive */
103
104	/* booting from a CD starts us at 7C0:0.  Move to 0:7C00 */
105	PUSHR(rAX)
106	LWI(_nxt(SB), rAX)
107	PUSHR(rAX)
108	BYTE $0xCB	/* FAR RET */
109
110TEXT _nxt(SB), $0
111	STI
112
113	LWI(confidence(SB), rSI)	/* for that warm, fuzzy feeling */
114	CALL16(BIOSputs(SB))
115
116	LBI(0x41, rAH)			/* check extensions present */
117	LWI(0x55AA, rBX)
118	LXB(Xdrive, xBP, rDL)		/* drive */
119	BIOSCALL(0x13)			/* CF set on failure */
120	JCS _jmp01
121	CMPI(0xAA55, rBX)
122	JNE _jmp01
123	ANDI(0x0001, rCX)
124	JEQ _jmp01
125
126					/* rCX contains 0x0001 */
127	SBPWI(0x0010, Xdap+0)		/* reserved + packet size */
128	SBPW(rCX, Xdap+2)		/* reserved + # of blocks to transfer */
129
130	DEC(rCX)
131	SBPW(rCX, Xdap+12)
132	SBPW(rCX, Xdap+14)
133
134	CALL16(dreset(SB))
135
136_jmp00:
137	LW(_volid(SB), rAX)		/* Xrootlo */
138	LW(_volid+2(SB), rDX)		/* Xroothi */
139
140	LWI(_magic+DIROFF(SB), rBX)
141	CALL16(BIOSread(SB))		/* read the root directory */
142
143	LWI((512/Dirsz), rBX)
144
145	LWI(_magic+DIROFF(SB), rDI)	/* compare first directory entry */
146
147_cmp00:
148	PUSHR(rDI)			/* save for later if it matches */
149	LWI(bootfile(SB), rSI)
150	LWI(Dnamesz, rCX)
151	REP
152	CMPSB
153	POPR(rDI)
154	JEQ _jmp02
155
156	DEC(rBX)
157	JEQ _jmp01
158
159	ADDI(Dirsz, rDI)
160	JMP _cmp00
161_jmp01:
162	CALL16(buggery(SB))
163
164_jmp02:
165	CLR(rBX)			/* a handy value */
166	LW(_rootsize(SB), rAX)		/* calculate and save Xrootsz */
167	LWI(Dirsz, rCX)
168	MUL(rCX)
169	LW(_sectsize(SB), rCX)
170	PUSHR(rCX)
171	DEC(rCX)
172	ADD(rCX, rAX)
173	ADC(rBX, rDX)
174	POPR(rCX)			/* _sectsize(SB) */
175	DIV(rCX)
176	PUSHR(rAX)			/* Xrootsz */
177
178	/*
179	 * rDI points to the matching directory entry.
180	 */
181	LXW(Dstart, xDI, rAX)		/* starting sector address */
182	DEC(rAX)			/* that's just the way it is */
183	DEC(rAX)
184	LB(_clustsize(SB), rCL)
185	CLRB(rCH)
186	MUL(rCX)
187	LW(_volid(SB), rCX)		/* Xrootlo */
188	ADD(rCX, rAX)
189	LW(_volid+2(SB), rCX)		/* Xroothi */
190	ADC(rCX, rDX)
191	POPR(rCX)			/* Xrootsz */
192	ADD(rCX, rAX)
193	ADC(rBX, rDX)
194
195	PUSHR(rAX)			/* calculate how many sectors to read */
196	PUSHR(rDX)
197	LXW(Dlengthlo, xDI, rAX)
198	LXW(Dlengthhi, xDI, rDX)
199	LW(_sectsize(SB), rCX)
200	PUSHR(rCX)
201	DEC(rCX)
202	ADD(rCX, rAX)
203	ADC(rBX, rDX)
204	POPR(rCX)			/* _sectsize(SB) */
205	DIV(rCX)
206	MW(rAX, rCX)
207	POPR(rDX)
208	POPR(rAX)
209
210	LWI(LOADSEG, rBX)		/* address to load into (seg+offset) */
211	MTSR(rBX, rES)			/*  seg */
212	LWI(LOADOFF, rBX)		/*  offset */
213
214_readboot:
215	CALL16(BIOSread(SB))		/* read the sector */
216
217	LW(_sectsize(SB), rDI)		/* bump addresses/counts */
218	ADD(rDI, rBX)
219	JCC _incsecno
220
221	MFSR(rES, rDI)			/* next 64KB segment */
222	ADDI(0x1000, rDI)
223	MTSR(rDI, rES)
224
225_incsecno:
226	CLR(rDI)
227	INC(rAX)
228	ADC(rDI, rDX)
229	LOOP _readboot
230
231	LWI(LOADSEG, rDI)		/* set rDS for loaded code */
232	MTSR(rDI, rDS)
233	FARJUMP16(LOADSEG, LOADOFF)	/* no deposit, no return */
234
235TEXT buggery(SB), $0
236	LWI(error(SB), rSI)
237	CALL16(BIOSputs(SB))
238
239_wait:
240	CLR(rAX)			/* wait for almost any key */
241	BIOSCALL(0x16)
242
243_reset:
244	CLR(rBX)			/* set ES segment for BIOS area */
245	MTSR(rBX, rES)
246
247	LWI(0x0472, rBX)		/* warm-start code address */
248	LWI(0x1234, rAX)		/* warm-start code */
249	POKEW				/* MOVW	AX, ES:[BX] */
250
251	FARJUMP16(0xFFFF, 0x0000)	/* reset */
252
253
254/*
255 * Read a sector from a disc. On entry:
256 *   rDX:rAX	sector number
257 *   rES:rBX	buffer address
258 */
259TEXT BIOSread(SB), $0
260	LWI(5, rDI)			/* retry count (ATAPI ZIPs suck) */
261_retry:
262	PUSHA				/* may be trashed by BIOSCALL */
263
264	SBPW(rBX, Xdap+4)		/* transfer buffer :offset */
265	MFSR(rES, rDI)			/* transfer buffer seg: */
266	SBPW(rDI, Xdap+6)
267	SBPW(rAX, Xdap+8)		/* LBA (64-bits) */
268	SBPW(rDX, Xdap+10)
269
270	MW(rBP, rSI)			/* disk address packet */
271	LBI(0x42, rAH)			/* extended read */
272	LBPB(Xdrive, rDL)		/* form drive */
273	BIOSCALL(0x13)			/* CF set on failure */
274	JCC _BIOSreadret
275
276	POPA
277	DEC(rDI)			/* too many retries? */
278	JEQ _ioerror
279
280	CALL16(dreset(SB))
281	JMP _retry
282
283_ioerror:
284	LWI(ioerror(SB), rSI)
285	CALL16(BIOSputs(SB))
286	JMP _wait
287
288_BIOSreadret:
289	POPA
290	RET
291
292TEXT dreset(SB), $0
293	PUSHA
294	CLR(rAX)			/* rAH == 0 == reset disc system */
295	LBPB(Xdrive, rDL)
296	BIOSCALL(0x13)
297	ORB(rAH, rAH)			/* status (0 == success) */
298	POPA
299	JNE _ioerror
300	RET
301
302/*
303 * Output a string to the display.
304 * String argument is in rSI.
305 */
306TEXT BIOSputs(SB), $0
307	PUSHA
308	CLR(rBX)
309_BIOSputs:
310	LODSB
311	ORB(rAL, rAL)
312	JEQ _BIOSputsret
313
314	LBI(0x0E, rAH)
315	BIOSCALL(0x10)
316	JMP _BIOSputs
317
318_BIOSputsret:
319	POPA
320	RET
321
322/* "Bad format or I/O error\r\nPress almost any key to reboot..." */
323TEXT error(SB), $0
324	BYTE $'B'; BYTE $'a'; BYTE $'d'; BYTE $' ';
325	BYTE $'f'; BYTE $'o'; BYTE $'r'; BYTE $'m';
326	BYTE $'a'; BYTE $'t'; BYTE $' '; BYTE $'o';
327	BYTE $'r'; BYTE $' ';
328/* "I/O error\r\nPress almost any key to reboot..." */
329TEXT ioerror(SB), $0
330	BYTE $'I'; BYTE $'/'; BYTE $'O'; BYTE $' ';
331	BYTE $'e'; BYTE $'r'; BYTE $'r'; BYTE $'o';
332	BYTE $'r'; BYTE $'\r';BYTE $'\n';
333	BYTE $'P'; BYTE $'r'; BYTE $'e'; BYTE $'s';
334	BYTE $'s'; BYTE $' '; BYTE $'a'; BYTE $' ';
335	BYTE $'k'; BYTE $'e'; BYTE $'y';
336	BYTE $' '; BYTE $'t'; BYTE $'o'; BYTE $' ';
337	BYTE $'r'; BYTE $'e'; BYTE $'b'; BYTE $'o';
338	BYTE $'o'; BYTE $'t';
339	BYTE $'.'; BYTE $'.'; BYTE $'.';
340	BYTE $'\z';
341
342#ifdef USEBCOM
343/* "B       COM" */
344TEXT bootfile(SB), $0
345	BYTE $'B'; BYTE $' '; BYTE $' '; BYTE $' ';
346	BYTE $' '; BYTE $' '; BYTE $' '; BYTE $' ';
347	BYTE $'C'; BYTE $'O'; BYTE $'M';
348	BYTE $'\z';
349#else
350/* "9LOAD      " */
351TEXT bootfile(SB), $0
352	BYTE $'9'; BYTE $'L'; BYTE $'O'; BYTE $'A';
353	BYTE $'D'; BYTE $' '; BYTE $' '; BYTE $' ';
354	BYTE $' '; BYTE $' '; BYTE $' ';
355	BYTE $'\z';
356#endif /* USEBCOM */
357
358/* "PBS..." */
359TEXT confidence(SB), $0
360	BYTE $'P'; BYTE $'B'; BYTE $'S'; BYTE $'2';
361	BYTE $'.'; BYTE $'.'; BYTE $'.';
362	BYTE $'\z';
363