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