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