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