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