pbs.s 8.1 KB

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