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