Lua 5.3 didn't need an x86 emulator. But now, it has one regardless.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

disks.lua 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. local drives = {}
  2. RAM[0x475] = 0
  3. local function disk_init_data(fptr, d)
  4. local fsize = 0
  5. if fptr ~= nil then
  6. fsize = fptr:seek("end")
  7. fptr:seek("set", 0)
  8. end
  9. emu_debug(2, string.format("fsize %02X == %d", d.id, fsize))
  10. d.inserted = true
  11. if fsize == 0 then
  12. d.inserted = false
  13. d.sector_size = 0
  14. d.sectors = 0
  15. d.heads = 0
  16. d.cylinders = 0
  17. elseif d.floppy then
  18. d.sector_size = 512
  19. if fsize == 2949120 then
  20. d.sectors = 36
  21. d.heads = 2
  22. d.cylinders = 80
  23. elseif fsize == 1761280 then
  24. d.sectors = 21
  25. d.heads = 2
  26. d.cylinders = 82
  27. elseif fsize == 1720320 then
  28. d.sectors = 21
  29. d.heads = 2
  30. d.cylinders = 80
  31. elseif fsize == 1474560 then
  32. d.sectors = 18
  33. d.heads = 2
  34. d.cylinders = 80
  35. elseif fsize == 1228800 then
  36. d.sectors = 15
  37. d.heads = 2
  38. d.cylinders = 80
  39. elseif fsize == 737280 then
  40. d.sectors = 9
  41. d.heads = 2
  42. d.cylinders = 80
  43. elseif fsize == (360*1024) then
  44. d.sectors = 9
  45. d.heads = 2
  46. d.cylinders = 40
  47. elseif fsize == (320*1024) then
  48. d.sectors = 8
  49. d.heads = 2
  50. d.cylinders = 40
  51. elseif fsize == (180*1024) then
  52. d.sectors = 9
  53. d.heads = 1
  54. d.cylinders = 40
  55. elseif fsize == (160*1024) then
  56. d.sectors = 8
  57. d.heads = 1
  58. d.cylinders = 40
  59. else
  60. error("unknown fsize: " .. fsize)
  61. end
  62. else
  63. d.sector_size = 512
  64. d.sectors = 63
  65. d.heads = 16
  66. d.cylinders = math.floor(fsize / (d.sector_size*d.sectors*d.heads))
  67. if d.cylinders <= 0 then
  68. error("unknown fsize: " .. fsize)
  69. end
  70. end
  71. emu_debug(2, string.format("disks: added %d x %d x %d x %d @ %02X", d.cylinders, d.heads, d.sectors, d.sector_size, d.id))
  72. -- configure table
  73. local tba = 0xF2000 + d.id*16
  74. if d.id == 0x80 then
  75. RAM:w16(0x0104, tba & 0xFFFF)
  76. RAM:w16(0x0106, 0xF000)
  77. elseif d.id == 0x81 then
  78. RAM:w16(0x0118, tba & 0xFFFF)
  79. RAM:w16(0x011A, 0xF000)
  80. elseif d.id == 0x00 then
  81. RAM:w16(0x0078, tba & 0xFFFF)
  82. RAM:w16(0x007A, 0xF000)
  83. end
  84. if d.floppy then
  85. RAM[tba] = 0xF0
  86. RAM[tba + 1] = 0x00
  87. RAM[tba + 2] = 0x00
  88. RAM[tba + 3] = math.ceil(d.sector_size / 128) - 1
  89. RAM[tba + 4] = d.sectors
  90. RAM[tba + 5] = 0
  91. RAM[tba + 6] = 0
  92. RAM[tba + 7] = 0
  93. RAM[tba + 8] = 0xF6
  94. RAM[tba + 9] = 0
  95. RAM[tba + 10] = 0
  96. else
  97. RAM:w16(tba, d.cylinders)
  98. RAM[tba + 2] = d.heads
  99. RAM:w16(tba + 3, 0)
  100. RAM:w16(tba + 5, 0)
  101. RAM[tba + 7] = 0
  102. RAM[tba + 8] = 0xC0
  103. if d.heads > 8 then
  104. RAM[tba + 8] = RAM[tba + 8] | 0x08
  105. end
  106. RAM[tba + 9] = 0
  107. RAM[tba + 10] = 0
  108. RAM[tba + 11] = 0
  109. RAM:w16(tba + 12, 0)
  110. RAM[tba + 14] = d.sectors
  111. end
  112. end
  113. function disk_init(fn, id)
  114. local d = {}
  115. d.id = id
  116. local is_floppy = id < 0x80
  117. d.floppy = is_floppy
  118. if type(fn) == "function" then
  119. d.ptr = fn
  120. else
  121. local ptrmode = nil
  122. local f = nil
  123. d.ptr = function(a, mode)
  124. if ptrmode ~= mode then
  125. if f ~= nil then f:close() end
  126. f = io.open(fn, mode)
  127. disk_init_data(f, a)
  128. f:seek("set", 0)
  129. ptrmode = mode
  130. end
  131. return f
  132. end
  133. end
  134. drives[id] = d
  135. if d.id >= 0x80 then
  136. RAM[0x475] = RAM[0x475] + 1
  137. end
  138. end
  139. function disk_has(id)
  140. return drives[id] ~= nil
  141. end
  142. local last_status = 0x00
  143. local function ret_status(v)
  144. if v ~= nil then
  145. emu_debug(2, "disks: setting status to " .. v)
  146. last_status = v & 0xFF
  147. end
  148. CPU["regs"][1] = (CPU["regs"][1] & 0xFF) | (last_status << 8)
  149. cpu_write_flag(0, last_status ~= 0)
  150. end
  151. cpu_register_interrupt_handler(0x13, function(ax,ah,al)
  152. if ah == 0x00 then
  153. emu_debug(1, "disks: disk system reset")
  154. ret_status(0)
  155. return true
  156. elseif ah == 0x01 then
  157. ret_status(nil)
  158. return true
  159. elseif ah == 0x02 then
  160. local cx = CPU["regs"][2]
  161. local dx = CPU["regs"][3]
  162. local bx = CPU["regs"][4]
  163. local sector = (cx & 0x3F)
  164. local cylinder = (cx >> 8)
  165. local head = (dx >> 8)
  166. local drive = (dx & 0xFF)
  167. if drive >= 0x80 then
  168. cylinder = cylinder | ((cx & 0xC0) << 2)
  169. end
  170. local d = drives[drive]
  171. if not d or d:ptr("rb") == nil then
  172. ret_status(1)
  173. return true
  174. else
  175. d:ptr("rb")
  176. if sector == 0 or sector > d.sectors or head >= d.heads or cylinder >= d.cylinders then
  177. ret_status(4)
  178. emu_debug(1, string.format("disks: out of bounds - %02X c=%d h=%d s=%d", drive, cylinder, head, sector))
  179. return true
  180. end
  181. end
  182. local pos = cylinder
  183. pos = pos * d.heads + head
  184. pos = pos * d.sectors + (sector - 1)
  185. pos = pos * d.sector_size
  186. CPU["regs"][1] = al -- AH = 0 (OK), AL = sectors transferred
  187. d:ptr("rb"):seek("set", pos)
  188. local count = al * d.sector_size
  189. local data = d:ptr("rb"):read(count)
  190. for i=0,count-1 do
  191. RAM[seg(SEG_ES, bx + i)] = data:byte(i+1,i+2)
  192. end
  193. emu_debug(1, string.format("disks: read %d bytes from %02X %d to %04X %04X", count, drive, pos, CPU["segments"][SEG_ES+1], bx))
  194. ret_status(0)
  195. return true
  196. elseif ah == 0x03 then
  197. local cx = CPU["regs"][2]
  198. local dx = CPU["regs"][3]
  199. local bx = CPU["regs"][4]
  200. local sector = (cx & 0x3F)
  201. local cylinder = (cx >> 8)
  202. local head = (dx >> 8)
  203. local drive = (dx & 0xFF)
  204. if drive >= 0x80 then
  205. cylinder = cylinder | ((cx & 0xC0) << 2)
  206. end
  207. local d = drives[drive]
  208. if not d or d:ptr("ab") == nil then
  209. ret_status(1)
  210. return true
  211. else
  212. d:ptr("ab")
  213. if sector == 0 or sector > d.sectors or head >= d.heads or cylinder >= d.cylinders then
  214. ret_status(4)
  215. emu_debug(1, string.format("disks: out of bounds - %02X c=%d h=%d s=%d", drive, cylinder, head, sector))
  216. return true
  217. end
  218. end
  219. local pos = cylinder
  220. pos = pos * d.heads + head
  221. pos = pos * d.sectors + (sector - 1)
  222. pos = pos * d.sector_size
  223. CPU["regs"][1] = al -- AH = 0 (OK), AL = sectors transferred
  224. d:ptr("ab"):seek("set", pos)
  225. local count = al * d.sector_size
  226. for i=0,count-1 do
  227. d:ptr("ab"):write(string.char(RAM[seg(SEG_ES, bx + i)]))
  228. end
  229. emu_debug(1, string.format("disks: wrote %d bytes from %02X %d to %04X %04X", count, drive, pos, CPU["segments"][SEG_ES+1], bx))
  230. ret_status(0)
  231. return true
  232. elseif ah == 0x04 then -- verify
  233. local drive = CPU["regs"][3] & 0xFF
  234. emu_debug(1, string.format("disks: verify drive %02X", drive))
  235. ret_status(0)
  236. return true
  237. elseif ah == 0x08 then -- get drive parameters
  238. local drive = CPU["regs"][3] & 0xFF
  239. local d = drives[drive]
  240. if not d or d:ptr("rb") == nil then
  241. ret_status(1)
  242. else
  243. d:ptr("rb") -- init disk data
  244. local maxc = d.cylinders - 1
  245. local drives = RAM[0x475]
  246. if d.floppy then drives = ((RAM[0x410] >> 6) & 3) + 1 end
  247. CPU["regs"][2] = ((maxc & 0xFF) << 8) | (d.sectors & 0x3F) | ((maxc & 0x300) >> 2) -- CX = cylinder number | sector number
  248. CPU["regs"][3] = ((d.heads - 1) << 8) | drives
  249. CPU["regs"][8] = 2000 + (drive*16)
  250. CPU["segments"][SEG_ES+1] = 0xF000 -- ES:DI - hdpt ptr
  251. if d.floppy then
  252. if d.sectors == 18 then
  253. CPU["regs"][4] = (CPU["regs"][4] & 0xFF00) | 4
  254. else
  255. CPU["regs"][4] = (CPU["regs"][4] & 0xFF00) | 3
  256. end
  257. else
  258. CPU["regs"][4] = (CPU["regs"][4] & 0xFF00)
  259. end
  260. ret_status(0)
  261. end
  262. emu_debug(1, string.format("disks: get drive parameters %02X %04X", drive, CPU["regs"][1]))
  263. return true
  264. elseif ah == 0x15 then -- get disk type
  265. local drive = CPU["regs"][3] & 0xFF
  266. local d = drives[drive]
  267. local code = 0
  268. if d and d:ptr("rb") ~= nil then
  269. if d.floppy then code = 1
  270. else code = 3 end
  271. end
  272. cpu_clear_flag(0) -- clear carry
  273. CPU["regs"][1] = (code << 8) | (CPU["regs"][1] & 0xFF) -- AH = drive code
  274. emu_debug(1, string.format("disks: get disk type %02X", drive))
  275. return true
  276. elseif ah == 0x18 then -- set media type for format
  277. -- TODO
  278. local drive = CPU["regs"][3] & 0xFF
  279. local code = 0x80
  280. if drives[drive] then code = 0 end
  281. cpu_clear_flag(0) -- clear carry
  282. CPU["regs"][1] = (code << 8) | (CPU["regs"][1] & 0xFF) -- AH = drive code
  283. emu_debug(1, string.format("disks: set media type %02X", drive))
  284. return true
  285. elseif ah == 0x41 then -- check extensions present
  286. ret_status(1)
  287. return true
  288. else
  289. cpu_set_flag(0)
  290. return false
  291. end
  292. end)
  293. function disk_boot(id)
  294. local f = drives[id]:ptr("rb")
  295. f:seek("set", 0)
  296. local bootsector = f:read(512)
  297. for i=0,511 do
  298. RAM[0x7c00 + i] = bootsector:byte(i+1,i+2)
  299. end
  300. cpu_set_ip(0x0000, 0x7C00)
  301. CPU["regs"][3] = 0x0000 | id
  302. CPU["regs"][5] = 0x8000
  303. end