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.

platform_oc.lua 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. local component = require("component")
  2. local event = require("event")
  3. local unicode = require("unicode")
  4. local keyboard = require("keyboard")
  5. local computer = require("computer")
  6. local gpu = component.gpu
  7. if gpu.getDepth() < 4 then
  8. error("Tier 2 or above GPU card required!")
  9. end
  10. local cp437_trans = require("table_cp437")
  11. local oc_palette = require("table_ocpalette")
  12. dofile("kbdmaps.lua")
  13. local beeper = nil
  14. local beeper_count = 0
  15. for a,b in component.list("beep") do beeper_count = beeper_count + 1 end
  16. if beeper_count > 0 then beeper = component.beep end
  17. local qdr = {}
  18. for i=0,255 do
  19. local dat = (i & 0x01) << 7
  20. dat = dat | (i & 0x02) >> 1 << 6
  21. dat = dat | (i & 0x04) >> 2 << 5
  22. dat = dat | (i & 0x08) >> 3 << 2
  23. dat = dat | (i & 0x10) >> 4 << 4
  24. dat = dat | (i & 0x20) >> 5 << 1
  25. dat = dat | (i & 0x40) >> 6 << 3
  26. dat = dat | (i & 0x80) >> 7
  27. qdr[i + 1] = unicode.char(0x2800 | dat)
  28. end
  29. function emu_debug(s)
  30. return false
  31. end
  32. function platform_beep(freq, time)
  33. freq = math.floor(freq)
  34. if freq < 20 then freq = 20
  35. elseif freq > 2000 then freq = 2000 end
  36. if beeper then beeper.beep({[freq]=(time or 0.05)}) end
  37. end
  38. function platform_sleep(t)
  39. os.sleep(t)
  40. end
  41. function platform_key_down(code)
  42. return keyboard.isKeyDown(code)
  43. end
  44. event.listen("redstone_changed", function(name, addr, side, oldV, newV)
  45. if oldV < newV and newV >= 8 then
  46. kbd_send_ibm(0x01, 0x1B)
  47. end
  48. end)
  49. local function oc_code_transform(code, char)
  50. if code == 0x1C then char = 13 end
  51. if code >= 0xC0 and code <= 0xDF then code = code & 0x7F end
  52. if code >= 0x80 then
  53. -- fallback
  54. code = map_char_to_key[char] or code
  55. end
  56. return code, char
  57. end
  58. local kd_matrix = {}
  59. event.listen("key_down", function(name, addr, char, code, player)
  60. code, char = oc_code_transform(code, char)
  61. if code >= 0 and code <= 0x7F then
  62. if kd_matrix[code] then
  63. -- release first, for autorepeat
  64. kbd_send_ibm(0x80 | code, char)
  65. end
  66. kd_matrix[code] = true
  67. kbd_send_ibm(code, char)
  68. end
  69. end)
  70. event.listen("key_up", function(name, addr, char, code, player)
  71. code, char = oc_code_transform(code, char)
  72. if code >= 0 and code <= 0x7F then
  73. kd_matrix[code] = nil
  74. kbd_send_ibm(0x80 | code, char)
  75. end
  76. end)
  77. function platform_error(msg)
  78. print(msg)
  79. error(msg)
  80. end
  81. local function dstrings_add(dstrings, atr, x, y, str)
  82. if dstrings[atr] then
  83. table.insert(dstrings[atr], {x, y, table.concat(str)})
  84. else
  85. dstrings[atr] = {{x, y, table.concat(str)}}
  86. end
  87. end
  88. local function dstrings_draw(dstrings, pal)
  89. local dstrkeys = {}
  90. for k in pairs(dstrings) do table.insert(dstrkeys, k) end
  91. table.sort(dstrkeys)
  92. local lastBG = -1
  93. local lastFG = -1
  94. for _,atr in pairs(dstrkeys) do
  95. local arr = dstrings[atr]
  96. if pal then
  97. local tbg = (atr & 0xFF00) >> 8
  98. local tfg = atr & 0x00FF
  99. if lastBG ~= tbg then
  100. gpu.setBackground(oc_palette[tbg])
  101. lastBG = tbg
  102. end
  103. if lastFG ~= tfg then
  104. gpu.setForeground(oc_palette[tfg])
  105. lastFG = tfg
  106. end
  107. else
  108. local tbg = (atr & 0xF0) >> 4
  109. local tfg = atr & 0x0F
  110. if lastBG ~= tbg then
  111. gpu.setBackground(pc_16_colors[1 + tbg])
  112. lastBG = tbg
  113. end
  114. if lastFG ~= tfg then
  115. gpu.setForeground(pc_16_colors[1 + tfg])
  116. lastFG = tfg
  117. end
  118. end
  119. for i,dat in ipairs(arr) do
  120. gpu.set(dat[1], dat[2], dat[3], false)
  121. end
  122. end
  123. end
  124. local function gpu_set_res_diff(width, height)
  125. local rw, rh = gpu.getResolution()
  126. if rw ~= width or rh ~= height then
  127. gpu.setResolution(width, height)
  128. end
  129. end
  130. -- Takes four pixels and turns them into two.
  131. --local plat_cga_mono_avg = {
  132. -- 0, 1, 0, 1,
  133. -- 2, 3, 2, 3,
  134. -- 0, 1, 0, 1,
  135. -- 2, 3, 2, 3
  136. --} Naive
  137. local plat_cga_mono_avg = {
  138. 0, 1, 1, 1,
  139. 2, 1, 2, 1,
  140. 2, 1, 2, 1,
  141. 2, 2, 2, 3
  142. } -- Less naive?
  143. -- 00..0F for each
  144. local function plat_cga_chr(v1, v2, v3, v4)
  145. return qdr[1 +
  146. ( (plat_cga_mono_avg[v4 + 1])
  147. | (plat_cga_mono_avg[v3 + 1] << 2)
  148. | (plat_cga_mono_avg[v2 + 1] << 4)
  149. | (plat_cga_mono_avg[v1 + 1] << 6) )
  150. ]
  151. end
  152. local function colorDistSq(rgb1, rgb2)
  153. if rgb1 == rgb2 then return 0 end
  154. local r1 = (rgb1 >> 16) & 0xFF
  155. local g1 = (rgb1 >> 8) & 0xFF
  156. local b1 = (rgb1) & 0xFF
  157. local r2 = (rgb2 >> 16) & 0xFF
  158. local g2 = (rgb2 >> 8) & 0xFF
  159. local b2 = (rgb2) & 0xFF
  160. local rs = (r1 - r2) * (r1 - r2)
  161. local gs = (g1 - g2) * (g1 - g2)
  162. local bs = (b1 - b2) * (b1 - b2)
  163. local rAvg = math.floor((r1 + r2) / 2)
  164. return (((512 + rAvg) * rs) >> 8) + (4 * gs) + (((767 - rAvg) * bs) >> 8)
  165. end
  166. local function round(v)
  167. return math.floor(v + 0.5)
  168. end
  169. local function getOCPalEntry(rgb)
  170. local r = (rgb >> 16) & 0xFF
  171. local g = (rgb >> 8) & 0xFF
  172. local b = (rgb) & 0xFF
  173. if r == g and g == b then
  174. local i = round(g * 16.0 / 255.0)
  175. if i <= 0 then return 16 elseif i >= 16 then return 255 else return i - 1 end
  176. else
  177. return 16 + (round(r * 5.0 / 255.0) * 40) + (round(g * 7.0 / 255.0) * 5) + round(b * 4.0 / 255.0)
  178. end
  179. end
  180. -- p8 = bottom right, p7 = bottom left, ...
  181. -- returns: bg, fg, chr
  182. local AVG_CHR_2x4 = 0
  183. local AVG_CHR_1x4 = 1
  184. local AVG_CHR_2x2 = 2
  185. local function plat_avg_chr(pixels, pal, avg_chr_mode, empty_color)
  186. -- identify most common colors
  187. local pixelCount = {}
  188. local pixelColors = 0
  189. for i=1,#pixels do
  190. if pixelCount[pixels[i]] == nil then pixelColors = pixelColors + 1 end
  191. pixelCount[pixels[i]] = (pixelCount[pixels[i]] or 0) + 1
  192. end
  193. local bg, fg
  194. if pixelColors == 1 then
  195. return pixels[1], empty_color or 0, 0
  196. elseif pixelColors == 2 then
  197. bg = pixels[1]
  198. fg = -1
  199. for k,v in pairs(pixelCount) do
  200. if k ~= bg then
  201. fg = k
  202. break
  203. end
  204. end
  205. else
  206. local bgc = -1
  207. local fgc = -1
  208. for k,v in pairs(pixelCount) do
  209. if v > bgc then
  210. bg = k
  211. bgc = v
  212. end
  213. end
  214. for k,v in pairs(pixelCount) do
  215. if k ~= bg then
  216. local contrast = colorDistSq(pal[bg], pal[k]) * v
  217. if contrast > fgc then
  218. fg = k
  219. fgc = contrast
  220. end
  221. end
  222. end
  223. end
  224. local chr = 0
  225. local pbg = pal[bg]
  226. local pfg = pal[fg]
  227. if avg_chr_mode == AVG_CHR_2x4 then
  228. for i=1,8 do
  229. local p = pal[pixels[i]]
  230. if colorDistSq(pfg, p) < colorDistSq(pbg, p) then
  231. chr = chr | (0x80 >> (i - 1))
  232. end
  233. end
  234. elseif avg_chr_mode == AVG_CHR_1x4 then
  235. for i=1,4 do
  236. local p = pal[pixels[i]]
  237. if colorDistSq(pfg, p) < colorDistSq(pbg, p) then
  238. chr = chr | (0xC0 >> ((i - 1) << 1))
  239. end
  240. end
  241. elseif avg_chr_mode == AVG_CHR_2x2 then
  242. local ash = {0, 1, 4, 5}
  243. for i=1,4 do
  244. local p = pal[pixels[i]]
  245. if colorDistSq(pfg, p) < colorDistSq(pbg, p) then
  246. chr = chr | (0xA0 >> ash[i])
  247. end
  248. end
  249. end
  250. return bg, fg, chr
  251. end
  252. local function iter_dstrings(dlines, dstrings, func)
  253. for y,dline in pairs(dlines) do
  254. local lastAtr = -1
  255. local lastX = dline[1]
  256. local str = {}
  257. for x=dline[1],dline[2] do
  258. local atr, chr = func(x, y)
  259. if lastAtr ~= atr then
  260. if #str > 0 then
  261. dstrings_add(dstrings, lastAtr, lastX + 1, y + 1, str)
  262. end
  263. lastAtr = atr
  264. lastX = x
  265. str = {chr}
  266. else
  267. str[#str + 1] = chr
  268. end
  269. end
  270. if #str > 0 then
  271. dstrings_add(dstrings, lastAtr, lastX + 1, y + 1, str)
  272. end
  273. end
  274. end
  275. local function dlines_scale(dlines, amt)
  276. for y,dline in pairs(dlines) do
  277. dline[1] = dline[1]*amt
  278. dline[2] = dline[2]*amt+(amt-1)
  279. end
  280. end
  281. function platform_render_mcga_13h(vram, addr)
  282. gpu_set_res_diff(160, 50)
  283. local dlines = video_pop_dirty_lines()
  284. local dstrings = {}
  285. iter_dstrings(dlines, dstrings, function(x, y)
  286. local base = addr + y*1280
  287. local bg, fg, chr = plat_avg_chr({
  288. getOCPalEntry(video_vga_get_palette(vram[base + x*2] or 0)),
  289. getOCPalEntry(video_vga_get_palette(vram[base + x*2 + 1] or 0)),
  290. getOCPalEntry(video_vga_get_palette(vram[base + x*2 + 320] or 0)),
  291. getOCPalEntry(video_vga_get_palette(vram[base + x*2 + 321] or 0)),
  292. getOCPalEntry(video_vga_get_palette(vram[base + x*2 + 640] or 0)),
  293. getOCPalEntry(video_vga_get_palette(vram[base + x*2 + 641] or 0)),
  294. getOCPalEntry(video_vga_get_palette(vram[base + x*2 + 960] or 0)),
  295. getOCPalEntry(video_vga_get_palette(vram[base + x*2 + 961] or 0))
  296. }, oc_palette, AVG_CHR_2x4)
  297. local atr = (bg << 8) | fg
  298. return atr, qdr[1 + chr]
  299. end)
  300. dstrings_draw(dstrings, true)
  301. end
  302. function platform_render_pcjr_160(vram, addr)
  303. gpu_set_res_diff(160, 50)
  304. local dlines = video_pop_dirty_lines()
  305. local dstrings = {}
  306. dlines_scale(dlines, 2)
  307. iter_dstrings(dlines, dstrings, function(x, y)
  308. local base = addr + x + y*160 -- 4/2 lines
  309. local shift = 4
  310. if (x & 1) == 1 then shift = 0 end
  311. local bg, fg, chr = plat_avg_chr({
  312. (((vram[base] or 0) >> shift) & 0x0F) + 1,
  313. (((vram[base + 0x2000] or 0) >> shift) & 0x0F) + 1,
  314. (((vram[base + 80] or 0) >> shift) & 0x0F) + 1,
  315. (((vram[base + 80 + 0x2000] or 0) >> shift) & 0x0F) + 1
  316. }, pc_16_colors, AVG_CHR_1x4, 1)
  317. local atr = ((bg - 1) << 4) | (fg - 1)
  318. return atr, qdr[1 + chr]
  319. end)
  320. dstrings_draw(dstrings, false)
  321. end
  322. function platform_render_pcjr_320(vram, addr)
  323. gpu_set_res_diff(160, 50)
  324. local dlines = video_pop_dirty_lines()
  325. local dstrings = {}
  326. iter_dstrings(dlines, dstrings, function(x, y)
  327. local base = addr + x + y*160 -- 4/4 lines
  328. local bg, fg, chr = plat_avg_chr({
  329. (((vram[base] or 0) >> 4) & 0x0F) + 1,
  330. (((vram[base] or 0) ) & 0x0F) + 1,
  331. (((vram[base+0x2000] or 0) >> 4) & 0x0F) + 1,
  332. (((vram[base+0x2000] or 0) ) & 0x0F) + 1,
  333. (((vram[base+0x4000] or 0) >> 4) & 0x0F) + 1,
  334. (((vram[base+0x4000] or 0) ) & 0x0F) + 1,
  335. (((vram[base+0x6000] or 0) >> 4) & 0x0F) + 1,
  336. (((vram[base+0x6000] or 0) ) & 0x0F) + 1
  337. }, pc_16_colors, AVG_CHR_2x4, 1)
  338. local atr = ((bg - 1) << 4) | (fg - 1)
  339. return atr, qdr[1 + chr]
  340. end)
  341. dstrings_draw(dstrings, false)
  342. end
  343. local cga_col_pals = {}
  344. cga_col_pals[0] = {0, 11, 13, 15}
  345. cga_col_pals[1] = {0, 10, 12, 14}
  346. cga_col_pals[2] = {0, 11, 12, 15}
  347. cga_col_pals[3] = {0, 3, 5, 7}
  348. cga_col_pals[4] = {0, 2, 4, 6}
  349. cga_col_pals[5] = {0, 3, 4, 7}
  350. local function plat_avg_chr_cga(c1, c2, c3, c4, sh, cgp)
  351. local cga_col_pal = cga_col_pals[cgp]
  352. local bg, fg, ch = plat_avg_chr({
  353. cga_col_pal[((c1 >> (sh+2)) & 3) + 1] + 1,
  354. cga_col_pal[((c1 >> sh) & 3) + 1] + 1,
  355. cga_col_pal[((c2 >> (sh+2)) & 3) + 1] + 1,
  356. cga_col_pal[((c2 >> sh) & 3) + 1] + 1,
  357. cga_col_pal[((c3 >> (sh+2)) & 3) + 1] + 1,
  358. cga_col_pal[((c3 >> sh) & 3) + 1] + 1,
  359. cga_col_pal[((c4 >> (sh+2)) & 3) + 1] + 1,
  360. cga_col_pal[((c4 >> sh) & 3) + 1] + 1
  361. }, pc_16_colors, AVG_CHR_2x4, 1)
  362. return bg - 1, fg - 1, ch
  363. end
  364. function platform_render_cga_color(vram, addr, mode)
  365. gpu_set_res_diff(160, 50)
  366. local dlines = video_pop_dirty_lines()
  367. local dstrings = {}
  368. local cgp = (((cga_get_palette() >> 4) & 1) * 3)
  369. if mode == 5 then
  370. cgp = cgp + 2
  371. else
  372. cgp = cgp + (cga_get_palette() >> 5) & 1
  373. end
  374. -- cga_col_pals[cgp][1] = (cga_get_palette() & 0x0F)
  375. dlines_scale(dlines, 2)
  376. iter_dstrings(dlines, dstrings, function(x, y)
  377. local base = addr + y*160
  378. local xs = x >> 1
  379. local bg, fg, chr = plat_avg_chr_cga(
  380. vram[base + xs] or 0,
  381. vram[base + xs + 0x2000] or 0,
  382. vram[base + xs + 80] or 0,
  383. vram[base + xs + 0x2000 + 80] or 0,
  384. (4 - ((x & 1) * 4)), cgp
  385. )
  386. local atr = (bg << 4) | fg
  387. return atr, qdr[1 + chr]
  388. end)
  389. dstrings_draw(dstrings, false)
  390. end
  391. function platform_render_cga_mono(vram, addr)
  392. gpu_set_res_diff(160, 50)
  393. gpu.setBackground(pc_16_colors[1])
  394. gpu.setForeground(pc_16_colors[16])
  395. -- gpu.setForeground(pc_16_colors[(cga_get_palette() & 0x0F) + 1])
  396. local dlines = video_pop_dirty_lines()
  397. for y,dline in pairs(dlines) do
  398. local str = {}
  399. -- y = 0..49 -> 0, 4, ... -> jump by two odd scanlines (160 bytes)
  400. local base = addr + y*160
  401. for x=dline[1],dline[2] do
  402. -- left
  403. local c1 = vram[base + x] or 0
  404. local c2 = vram[base + x + 0x2000] or 0
  405. local c3 = vram[base + x + 80] or 0
  406. local c4 = vram[base + x + 0x2000 + 80] or 0
  407. str[#str + 1] = plat_cga_chr(c1 >> 4, c2 >> 4, c3 >> 4, c4 >> 4)
  408. str[#str + 1] = plat_cga_chr(c1 & 0x0F, c2 & 0x0F, c3 & 0x0F, c4 & 0x0F)
  409. end
  410. gpu.set(dline[1] * 2 + 1, y + 1, table.concat(str))
  411. end
  412. end
  413. function platform_render_text(vram, addr, width, height, pitch)
  414. gpu_set_res_diff(width, height)
  415. local dlines = video_pop_dirty_lines()
  416. local dstrings = {}
  417. for y,dline in pairs(dlines) do
  418. local base = addr + (y * pitch)
  419. local lastAtr = -1
  420. local lastX = 0
  421. local str = {}
  422. if dline[2] >= width then dline[2] = width-1 end
  423. for x=dline[1],dline[2] do
  424. local chr = cp437_trans[vram[base + x*2] or 0]
  425. local atr = vram[base + x*2 + 1] or 0
  426. -- TO TASTE
  427. if chr == 0x2593 then
  428. chr = 0x2591
  429. atr = (atr >> 4) | ((atr << 4) & 0xF0)
  430. end
  431. if lastAtr ~= atr then
  432. if #str > 0 then
  433. dstrings_add(dstrings, lastAtr, lastX + 1, y + 1, str)
  434. end
  435. lastAtr = atr
  436. lastX = x
  437. str = {unicode.char(chr)}
  438. else
  439. str[#str + 1] = unicode.char(chr)
  440. end
  441. end
  442. if #str > 0 then
  443. dstrings_add(dstrings, lastAtr, lastX + 1, y + 1, str)
  444. end
  445. end
  446. dstrings_draw(dstrings)
  447. end
  448. function platform_finish()
  449. end
  450. dofile("emu_core.lua")