vga.js 73 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383
  1. "use strict";
  2. var
  3. /**
  4. * Always 64k
  5. * @const
  6. */
  7. VGA_BANK_SIZE = 64 * 1024,
  8. /** @const */
  9. MAX_XRES = 2560,
  10. /** @const */
  11. MAX_YRES = 1600,
  12. /** @const */
  13. MAX_BPP = 32;
  14. /** @const */
  15. //var VGA_LFB_ADDRESS = 0xFE000000; // set by seabios
  16. var VGA_LFB_ADDRESS = 0xE0000000;
  17. /**
  18. * @const
  19. * Equals the maximum number of pixels for non svga.
  20. * 8 pixels per byte.
  21. */
  22. var VGA_PIXEL_BUFFER_SIZE = 8 * VGA_BANK_SIZE;
  23. /** @const */
  24. var VGA_MIN_MEMORY_SIZE = 4 * VGA_BANK_SIZE;
  25. /**
  26. * @const
  27. * @see {@link http://www.osdever.net/FreeVGA/vga/graphreg.htm#06}
  28. */
  29. var VGA_HOST_MEMORY_SPACE_START = Uint32Array.from([
  30. 0xA0000,
  31. 0xA0000,
  32. 0xB0000,
  33. 0xB8000,
  34. ]);
  35. /**
  36. * @const
  37. * @see {@link http://www.osdever.net/FreeVGA/vga/graphreg.htm#06}
  38. */
  39. var VGA_HOST_MEMORY_SPACE_SIZE = Uint32Array.from([
  40. 0x20000, // 128K
  41. 0x10000, // 64K
  42. 0x8000, // 32K
  43. 0x8000, // 32K
  44. ]);
  45. /**
  46. * @constructor
  47. * @param {CPU} cpu
  48. * @param {BusConnector} bus
  49. * @param {number} vga_memory_size
  50. */
  51. function VGAScreen(cpu, bus, vga_memory_size)
  52. {
  53. this.cpu = cpu;
  54. /** @const @type {BusConnector} */
  55. this.bus = bus;
  56. this.vga_memory_size = vga_memory_size;
  57. /** @type {number} */
  58. this.cursor_address = 0;
  59. /** @type {number} */
  60. this.cursor_scanline_start = 0xE;
  61. /** @type {number} */
  62. this.cursor_scanline_end = 0xF;
  63. /**
  64. * Number of columns in text mode
  65. * @type {number}
  66. */
  67. this.max_cols = 80;
  68. /**
  69. * Number of rows in text mode
  70. * @type {number}
  71. */
  72. this.max_rows = 25;
  73. /**
  74. * Width in pixels in graphical mode
  75. * @type {number}
  76. */
  77. this.screen_width = 0;
  78. /**
  79. * Height in pixels in graphical mode
  80. * @type {number}
  81. */
  82. this.screen_height = 0;
  83. /**
  84. * Logical width in pixels of virtual buffer available for panning
  85. * @type {number}
  86. */
  87. this.virtual_width = 0;
  88. /**
  89. * Logical height in pixels of virtual buffer available for panning
  90. * @type {number}
  91. */
  92. this.virtual_height = 0;
  93. /**
  94. * The rectangular fragments of the image buffer, and their destination
  95. * locations, to be drawn every screen_fill_buffer during VGA modes.
  96. * @type {Array<Object<string, number>>}
  97. */
  98. this.layers = [];
  99. /**
  100. * video memory start address
  101. * @type {number}
  102. */
  103. this.start_address = 0;
  104. /**
  105. * Start address - a copy of start_address that only gets updated
  106. * during VSync, used for panning and page flipping
  107. * @type {number}
  108. */
  109. this.start_address_latched = 0;
  110. /**
  111. * Unimplemented CRTC registers go here
  112. */
  113. this.crtc = new Uint8Array(0x19);
  114. // Implemented CRTC registers:
  115. /** @type {number} */
  116. this.crtc_mode = 0;
  117. /** @type {number} */
  118. this.horizontal_display_enable_end = 0;
  119. /** @type {number} */
  120. this.horizontal_blank_start = 0;
  121. /** @type {number} */
  122. this.vertical_display_enable_end = 0;
  123. /** @type {number} */
  124. this.vertical_blank_start = 0;
  125. /** @type {number} */
  126. this.underline_location_register = 0;
  127. /** @type {number} */
  128. this.preset_row_scan = 0;
  129. /** @type {number} */
  130. this.offset_register = 0;
  131. /** @type {number} */
  132. this.line_compare = 0;
  133. // End of CRTC registers
  134. /**
  135. * Used for svga, e.g. banked modes
  136. * @type{boolean}
  137. */
  138. this.graphical_mode_is_linear = true;
  139. /** @type {boolean} */
  140. this.graphical_mode = false;
  141. setTimeout(() => { bus.send("screen-set-mode", this.graphical_mode); }, 0);
  142. /*
  143. * VGA palette containing 256 colors for video mode 13, svga 8bpp, etc.
  144. * Needs to be initialised by the BIOS
  145. */
  146. this.vga256_palette = new Int32Array(256);
  147. /**
  148. * VGA read latches
  149. * @type{number}
  150. */
  151. this.latch_dword = 0;
  152. /** @type {number} */
  153. this.svga_version = 0xB0C5;
  154. /** @type {number} */
  155. this.svga_width = 0;
  156. /** @type {number} */
  157. this.svga_height = 0;
  158. this.svga_enabled = false;
  159. /** @type {number} */
  160. this.svga_bpp = 32;
  161. /** @type {number} */
  162. this.svga_bank_offset = 0;
  163. /**
  164. * The video buffer offset created by VBE_DISPI_INDEX_Y_OFFSET
  165. * In bytes
  166. * @type {number}
  167. */
  168. this.svga_offset = 0;
  169. this.svga_offset_y = 0;
  170. const pci_revision = 0; // set to 2 for qemu extended registers
  171. // Experimental, could probably need some changes
  172. // 01:00.0 VGA compatible controller: NVIDIA Corporation GT216 [GeForce GT 220] (rev a2)
  173. this.pci_space = [
  174. 0x34, 0x12, 0x11, 0x11, 0x03, 0x01, 0x00, 0x00, pci_revision, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
  175. 0x08, VGA_LFB_ADDRESS >>> 8, VGA_LFB_ADDRESS >>> 16, VGA_LFB_ADDRESS >>> 24,
  176. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xfe, 0x00, 0x00, 0x00, 0x00,
  177. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x1a, 0x00, 0x11,
  178. 0x00, 0x00, 0xbe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  179. ];
  180. this.pci_id = 0x12 << 3;
  181. this.pci_bars = [
  182. {
  183. size: vga_memory_size,
  184. },
  185. ];
  186. // TODO: Should be matched with vga bios size and mapping address
  187. // Seabios config for this device:
  188. // CONFIG_VGA_PCI=y
  189. // CONFIG_OVERRIDE_PCI_ID=y
  190. // CONFIG_VGA_VID=0x10de
  191. // CONFIG_VGA_DID=0x0a20
  192. this.pci_rom_size = 0x10000;
  193. this.pci_rom_address = 0xFEB00000;
  194. this.name = "vga";
  195. this.stats = {
  196. is_graphical: false,
  197. res_x: 0,
  198. res_y: 0,
  199. bpp: 0,
  200. };
  201. this.index_crtc = 0;
  202. // index for setting colors through port 3C9h
  203. this.dac_color_index_write = 0;
  204. this.dac_color_index_read = 0;
  205. this.dac_state = 0;
  206. this.dac_mask = 0xFF;
  207. this.dac_map = new Uint8Array(0x10);
  208. this.attribute_controller_index = -1;
  209. this.palette_source = 0x20;
  210. this.attribute_mode = 0;
  211. this.color_plane_enable = 0;
  212. this.horizontal_panning = 0;
  213. this.color_select = 0;
  214. this.sequencer_index = -1;
  215. // bitmap of planes 0-3
  216. this.plane_write_bm = 0xF;
  217. this.sequencer_memory_mode = 0;
  218. this.clocking_mode = 0;
  219. this.graphics_index = -1;
  220. this.plane_read = 0, // value 0-3, which plane to read
  221. this.planar_mode = 0;
  222. this.planar_rotate_reg = 0;
  223. this.planar_bitmap = 0xFF;
  224. this.planar_setreset = 0;
  225. this.planar_setreset_enable = 0;
  226. this.miscellaneous_graphics_register = 0;
  227. this.color_compare = 0;
  228. this.color_dont_care = 0;
  229. this.max_scan_line = 0;
  230. this.miscellaneous_output_register = 0xff;
  231. this.port_3DA_value = 0xFF;
  232. var io = cpu.io;
  233. io.register_write(0x3C0, this, this.port3C0_write);
  234. io.register_read(0x3C0, this, this.port3C0_read, this.port3C0_read16);
  235. io.register_read(0x3C1, this, this.port3C1_read);
  236. io.register_write(0x3C2, this, this.port3C2_write);
  237. io.register_write_consecutive(0x3C4, this, this.port3C4_write, this.port3C5_write);
  238. io.register_read(0x3C4, this, this.port3C4_read);
  239. io.register_read(0x3C5, this, this.port3C5_read);
  240. io.register_write_consecutive(0x3CE, this, this.port3CE_write, this.port3CF_write);
  241. io.register_read(0x3CE, this, this.port3CE_read);
  242. io.register_read(0x3CF, this, this.port3CF_read);
  243. io.register_read(0x3C6, this, this.port3C6_read);
  244. io.register_write(0x3C6, this, this.port3C6_write);
  245. io.register_write(0x3C7, this, this.port3C7_write);
  246. io.register_read(0x3C7, this, this.port3C7_read);
  247. io.register_write(0x3C8, this, this.port3C8_write);
  248. io.register_read(0x3C8, this, this.port3C8_read);
  249. io.register_write(0x3C9, this, this.port3C9_write);
  250. io.register_read(0x3C9, this, this.port3C9_read);
  251. io.register_read(0x3CC, this, this.port3CC_read);
  252. io.register_write_consecutive(0x3D4, this, this.port3D4_write, this.port3D5_write);
  253. io.register_read(0x3D4, this, this.port3D4_read);
  254. io.register_read(0x3D5, this, this.port3D5_read, () => {
  255. dbg_log("Warning: 16-bit read from 3D5", LOG_VGA);
  256. return this.port3D5_read();
  257. });
  258. io.register_read(0x3CA, this, function() { dbg_log("3CA read", LOG_VGA); return 0; });
  259. io.register_read(0x3DA, this, this.port3DA_read);
  260. io.register_read(0x3BA, this, this.port3DA_read);
  261. // Bochs VBE Extensions
  262. // http://wiki.osdev.org/Bochs_VBE_Extensions
  263. this.dispi_index = -1;
  264. this.dispi_enable_value = 0;
  265. io.register_write(0x1CE, this, undefined, this.port1CE_write);
  266. io.register_write(0x1CF, this, undefined, this.port1CF_write);
  267. io.register_read(0x1CF, this, undefined, this.port1CF_read);
  268. if(this.vga_memory_size === undefined || this.vga_memory_size < VGA_MIN_MEMORY_SIZE)
  269. {
  270. this.vga_memory_size = VGA_MIN_MEMORY_SIZE;
  271. dbg_log("vga memory size rounded up to " + this.vga_memory_size, LOG_VGA);
  272. }
  273. else if(this.vga_memory_size & (VGA_BANK_SIZE - 1))
  274. {
  275. // round up to next 64k
  276. this.vga_memory_size |= VGA_BANK_SIZE - 1;
  277. this.vga_memory_size++;
  278. }
  279. const vga_offset = cpu.svga_allocate_memory(this.vga_memory_size);
  280. this.svga_memory = v86util.view(Uint8Array, cpu.wasm_memory, vga_offset, this.vga_memory_size);
  281. this.diff_addr_min = this.vga_memory_size;
  282. this.diff_addr_max = 0;
  283. this.diff_plot_min = this.vga_memory_size;
  284. this.diff_plot_max = 0;
  285. this.image_data = null;
  286. bus.register("screen-fill-buffer", function()
  287. {
  288. this.screen_fill_buffer();
  289. }, this);
  290. this.vga_memory = new Uint8Array(4 * VGA_BANK_SIZE);
  291. this.plane0 = new Uint8Array(this.vga_memory.buffer, 0 * VGA_BANK_SIZE, VGA_BANK_SIZE);
  292. this.plane1 = new Uint8Array(this.vga_memory.buffer, 1 * VGA_BANK_SIZE, VGA_BANK_SIZE);
  293. this.plane2 = new Uint8Array(this.vga_memory.buffer, 2 * VGA_BANK_SIZE, VGA_BANK_SIZE);
  294. this.plane3 = new Uint8Array(this.vga_memory.buffer, 3 * VGA_BANK_SIZE, VGA_BANK_SIZE);
  295. this.pixel_buffer = new Uint8Array(VGA_PIXEL_BUFFER_SIZE);
  296. var me = this;
  297. io.mmap_register(0xA0000, 0x20000,
  298. function(addr) { return me.vga_memory_read(addr); },
  299. function(addr, value) { me.vga_memory_write(addr, value); }
  300. );
  301. cpu.devices.pci.register_device(this);
  302. }
  303. VGAScreen.prototype.get_state = function()
  304. {
  305. var state = [];
  306. state[0] = this.vga_memory_size;
  307. state[1] = this.cursor_address;
  308. state[2] = this.cursor_scanline_start;
  309. state[3] = this.cursor_scanline_end;
  310. state[4] = this.max_cols;
  311. state[5] = this.max_rows;
  312. state[6] = this.vga_memory;
  313. state[7] = this.dac_state;
  314. state[8] = this.start_address;
  315. state[9] = this.graphical_mode;
  316. state[10] = this.vga256_palette;
  317. state[11] = this.latch_dword;
  318. state[12] = this.color_compare;
  319. state[13] = this.color_dont_care;
  320. state[14] = this.miscellaneous_graphics_register;
  321. state[15] = this.svga_width;
  322. state[16] = this.svga_height;
  323. state[17] = this.crtc_mode;
  324. state[18] = this.svga_enabled;
  325. state[19] = this.svga_bpp;
  326. state[20] = this.svga_bank_offset;
  327. state[21] = this.svga_offset;
  328. state[22] = this.index_crtc;
  329. state[23] = this.dac_color_index_write;
  330. state[24] = this.dac_color_index_read;
  331. state[25] = this.dac_map;
  332. state[26] = this.sequencer_index;
  333. state[27] = this.plane_write_bm;
  334. state[28] = this.sequencer_memory_mode;
  335. state[29] = this.graphics_index;
  336. state[30] = this.plane_read;
  337. state[31] = this.planar_mode;
  338. state[32] = this.planar_rotate_reg;
  339. state[33] = this.planar_bitmap;
  340. state[34] = this.max_scan_line;
  341. state[35] = this.miscellaneous_output_register;
  342. state[36] = this.port_3DA_value;
  343. state[37] = this.dispi_index;
  344. state[38] = this.dispi_enable_value;
  345. state[39] = this.svga_memory;
  346. state[40] = this.graphical_mode_is_linear;
  347. state[41] = this.attribute_controller_index;
  348. state[42] = this.offset_register;
  349. state[43] = this.planar_setreset;
  350. state[44] = this.planar_setreset_enable;
  351. state[45] = this.start_address_latched;
  352. state[46] = this.crtc;
  353. state[47] = this.horizontal_display_enable_end;
  354. state[48] = this.horizontal_blank_start;
  355. state[49] = this.vertical_display_enable_end;
  356. state[50] = this.vertical_blank_start;
  357. state[51] = this.underline_location_register;
  358. state[52] = this.preset_row_scan;
  359. state[53] = this.offset_register;
  360. state[54] = this.palette_source;
  361. state[55] = this.attribute_mode;
  362. state[56] = this.color_plane_enable;
  363. state[57] = this.horizontal_panning;
  364. state[58] = this.color_select;
  365. state[59] = this.clocking_mode;
  366. state[60] = this.line_compare;
  367. state[61] = this.pixel_buffer;
  368. state[62] = this.dac_mask;
  369. return state;
  370. };
  371. VGAScreen.prototype.set_state = function(state)
  372. {
  373. this.vga_memory_size = state[0];
  374. this.cursor_address = state[1];
  375. this.cursor_scanline_start = state[2];
  376. this.cursor_scanline_end = state[3];
  377. this.max_cols = state[4];
  378. this.max_rows = state[5];
  379. state[6] && this.vga_memory.set(state[6]);
  380. this.dac_state = state[7];
  381. this.start_address = state[8];
  382. this.graphical_mode = state[9];
  383. this.vga256_palette = state[10];
  384. this.latch_dword = state[11];
  385. this.color_compare = state[12];
  386. this.color_dont_care = state[13];
  387. this.miscellaneous_graphics_register = state[14];
  388. this.svga_width = state[15];
  389. this.svga_height = state[16];
  390. this.crtc_mode = state[17];
  391. this.svga_enabled = state[18];
  392. this.svga_bpp = state[19];
  393. this.svga_bank_offset = state[20];
  394. this.svga_offset = state[21];
  395. this.index_crtc = state[22];
  396. this.dac_color_index_write = state[23];
  397. this.dac_color_index_read = state[24];
  398. this.dac_map = state[25];
  399. this.sequencer_index = state[26];
  400. this.plane_write_bm = state[27];
  401. this.sequencer_memory_mode = state[28];
  402. this.graphics_index = state[29];
  403. this.plane_read = state[30];
  404. this.planar_mode = state[31];
  405. this.planar_rotate_reg = state[32];
  406. this.planar_bitmap = state[33];
  407. this.max_scan_line = state[34];
  408. this.miscellaneous_output_register = state[35];
  409. this.port_3DA_value = state[36];
  410. this.dispi_index = state[37];
  411. this.dispi_enable_value = state[38];
  412. this.svga_memory.set(state[39]);
  413. this.graphical_mode_is_linear = state[40];
  414. this.attribute_controller_index = state[41];
  415. this.offset_register = state[42];
  416. this.planar_setreset = state[43];
  417. this.planar_setreset_enable = state[44];
  418. this.start_address_latched = state[45];
  419. this.crtc.set(state[46]);
  420. this.horizontal_display_enable_end = state[47];
  421. this.horizontal_blank_start = state[48];
  422. this.vertical_display_enable_end = state[49];
  423. this.vertical_blank_start = state[50];
  424. this.underline_location_register = state[51];
  425. this.preset_row_scan = state[52];
  426. this.offset_register = state[53];
  427. this.palette_source = state[54];
  428. this.attribute_mode = state[55];
  429. this.color_plane_enable = state[56];
  430. this.horizontal_panning = state[57];
  431. this.color_select = state[58];
  432. this.clocking_mode = state[59];
  433. this.line_compare = state[60];
  434. state[61] && this.pixel_buffer.set(state[61]);
  435. this.dac_mask = state[62] === undefined ? 0xFF : state[62];
  436. this.bus.send("screen-set-mode", this.graphical_mode);
  437. if(this.graphical_mode)
  438. {
  439. // Ensure set_size_graphical will update
  440. this.screen_width = 0;
  441. this.screen_height = 0;
  442. if(this.svga_enabled)
  443. {
  444. this.set_size_graphical(this.svga_width, this.svga_height,
  445. this.svga_bpp, this.svga_width, this.svga_height);
  446. this.update_layers();
  447. }
  448. else
  449. {
  450. this.update_vga_size();
  451. this.update_layers();
  452. this.complete_replot();
  453. }
  454. }
  455. else
  456. {
  457. this.set_size_text(this.max_cols, this.max_rows);
  458. this.update_cursor_scanline();
  459. this.update_cursor();
  460. }
  461. this.complete_redraw();
  462. };
  463. VGAScreen.prototype.vga_memory_read = function(addr)
  464. {
  465. if(this.svga_enabled && this.graphical_mode_is_linear)
  466. {
  467. return this.cpu.read8((addr - 0xA0000 | this.svga_bank_offset) + VGA_LFB_ADDRESS | 0);
  468. }
  469. var memory_space_select = this.miscellaneous_graphics_register >> 2 & 0x3;
  470. addr -= VGA_HOST_MEMORY_SPACE_START[memory_space_select];
  471. // VGA chip only decodes addresses within the selected memory space.
  472. if(addr < 0 || addr >= VGA_HOST_MEMORY_SPACE_SIZE[memory_space_select])
  473. {
  474. dbg_log("vga read outside memory space: addr:" + h(addr), LOG_VGA);
  475. return 0;
  476. }
  477. this.latch_dword = this.plane0[addr];
  478. this.latch_dword |= this.plane1[addr] << 8;
  479. this.latch_dword |= this.plane2[addr] << 16;
  480. this.latch_dword |= this.plane3[addr] << 24;
  481. if(this.planar_mode & 0x08)
  482. {
  483. // read mode 1
  484. var reading = 0xFF;
  485. if(this.color_dont_care & 0x1)
  486. {
  487. reading &= this.plane0[addr] ^ ~(this.color_compare & 0x1 ? 0xFF : 0x00);
  488. }
  489. if(this.color_dont_care & 0x2)
  490. {
  491. reading &= this.plane1[addr] ^ ~(this.color_compare & 0x2 ? 0xFF : 0x00);
  492. }
  493. if(this.color_dont_care & 0x4)
  494. {
  495. reading &= this.plane2[addr] ^ ~(this.color_compare & 0x4 ? 0xFF : 0x00);
  496. }
  497. if(this.color_dont_care & 0x8)
  498. {
  499. reading &= this.plane3[addr] ^ ~(this.color_compare & 0x8 ? 0xFF : 0x00);
  500. }
  501. return reading;
  502. }
  503. else
  504. {
  505. // read mode 0
  506. var plane = this.plane_read;
  507. if(!this.graphical_mode)
  508. {
  509. // We currently put all text data linearly
  510. plane = 0;
  511. }
  512. else if(this.sequencer_memory_mode & 0x8)
  513. {
  514. // Chain 4
  515. plane = addr & 0x3;
  516. addr &= ~0x3;
  517. }
  518. else if(this.planar_mode & 0x10)
  519. {
  520. // Odd/Even host read
  521. plane = addr & 0x1;
  522. addr &= ~0x1;
  523. }
  524. return this.vga_memory[plane << 16 | addr];
  525. }
  526. };
  527. VGAScreen.prototype.vga_memory_write = function(addr, value)
  528. {
  529. if(this.svga_enabled && this.graphical_mode && this.graphical_mode_is_linear)
  530. {
  531. // vbe banked mode
  532. this.cpu.write8((addr - 0xA0000 | this.svga_bank_offset) + VGA_LFB_ADDRESS | 0, value);
  533. return;
  534. }
  535. var memory_space_select = this.miscellaneous_graphics_register >> 2 & 0x3;
  536. addr -= VGA_HOST_MEMORY_SPACE_START[memory_space_select];
  537. if(addr < 0 || addr >= VGA_HOST_MEMORY_SPACE_SIZE[memory_space_select])
  538. {
  539. dbg_log("vga write outside memory space: addr:" + h(addr) + ", value:" + h(value), LOG_VGA);
  540. return;
  541. }
  542. if(this.graphical_mode)
  543. {
  544. this.vga_memory_write_graphical(addr, value);
  545. }
  546. else
  547. {
  548. if(!(this.plane_write_bm & 0x3))
  549. {
  550. // Ignore writes to font planes.
  551. return;
  552. }
  553. this.vga_memory_write_text_mode(addr, value);
  554. }
  555. };
  556. VGAScreen.prototype.vga_memory_write_graphical = function(addr, value)
  557. {
  558. var plane_dword;
  559. var write_mode = this.planar_mode & 3;
  560. var bitmask = this.apply_feed(this.planar_bitmap);
  561. var setreset_dword = this.apply_expand(this.planar_setreset);
  562. var setreset_enable_dword = this.apply_expand(this.planar_setreset_enable);
  563. // Write modes - see http://www.osdever.net/FreeVGA/vga/graphreg.htm#05
  564. switch(write_mode)
  565. {
  566. case 0:
  567. value = this.apply_rotate(value);
  568. plane_dword = this.apply_feed(value);
  569. plane_dword = this.apply_setreset(plane_dword, setreset_enable_dword);
  570. plane_dword = this.apply_logical(plane_dword, this.latch_dword);
  571. plane_dword = this.apply_bitmask(plane_dword, bitmask);
  572. break;
  573. case 1:
  574. plane_dword = this.latch_dword;
  575. break;
  576. case 2:
  577. plane_dword = this.apply_expand(value);
  578. plane_dword = this.apply_logical(plane_dword, this.latch_dword);
  579. plane_dword = this.apply_bitmask(plane_dword, bitmask);
  580. break;
  581. case 3:
  582. value = this.apply_rotate(value);
  583. bitmask &= this.apply_feed(value);
  584. plane_dword = setreset_dword;
  585. plane_dword = this.apply_bitmask(plane_dword, bitmask);
  586. break;
  587. }
  588. var plane_select = 0xF;
  589. switch(this.sequencer_memory_mode & 0xC)
  590. {
  591. // Odd/Even (aka chain 2)
  592. case 0x0:
  593. plane_select = 0x5 << (addr & 0x1);
  594. addr &= ~0x1;
  595. break;
  596. // Chain 4
  597. // Note: FreeVGA may have mistakenly stated that this bit field is
  598. // for system read only, yet the IBM Open Source Graphics Programmer's
  599. // Reference Manual explicitly states "both read and write".
  600. case 0x8:
  601. case 0xC:
  602. plane_select = 1 << (addr & 0x3);
  603. addr &= ~0x3;
  604. break;
  605. }
  606. // Plane masks take precedence
  607. // See: http://www.osdever.net/FreeVGA/vga/seqreg.htm#02
  608. plane_select &= this.plane_write_bm;
  609. if(plane_select & 0x1) this.plane0[addr] = (plane_dword >> 0) & 0xFF;
  610. if(plane_select & 0x2) this.plane1[addr] = (plane_dword >> 8) & 0xFF;
  611. if(plane_select & 0x4) this.plane2[addr] = (plane_dword >> 16) & 0xFF;
  612. if(plane_select & 0x8) this.plane3[addr] = (plane_dword >> 24) & 0xFF;
  613. var pixel_addr = this.vga_addr_to_pixel(addr);
  614. this.partial_replot(pixel_addr, pixel_addr + 7);
  615. };
  616. /**
  617. * Copies data_byte into the four planes, with each plane
  618. * represented by an 8-bit field inside the dword.
  619. * @param {number} data_byte
  620. * @return {number} 32-bit number representing the bytes for each plane.
  621. */
  622. VGAScreen.prototype.apply_feed = function(data_byte)
  623. {
  624. var dword = data_byte;
  625. dword |= data_byte << 8;
  626. dword |= data_byte << 16;
  627. dword |= data_byte << 24;
  628. return dword;
  629. };
  630. /**
  631. * Expands bits 0 to 3 to ocupy bits 0 to 31. Each
  632. * bit is expanded to 0xFF if set or 0x00 if clear.
  633. * @param {number} data_byte
  634. * @return {number} 32-bit number representing the bytes for each plane.
  635. */
  636. VGAScreen.prototype.apply_expand = function(data_byte)
  637. {
  638. var dword = data_byte & 0x1 ? 0xFF : 0x00;
  639. dword |= (data_byte & 0x2 ? 0xFF : 0x00) << 8;
  640. dword |= (data_byte & 0x4 ? 0xFF : 0x00) << 16;
  641. dword |= (data_byte & 0x8 ? 0xFF : 0x00) << 24;
  642. return dword;
  643. };
  644. /**
  645. * Planar Write - Barrel Shifter
  646. * @param {number} data_byte
  647. * @return {number}
  648. * @see {@link http://www.phatcode.net/res/224/files/html/ch25/25-01.html#Heading3}
  649. * @see {@link http://www.osdever.net/FreeVGA/vga/graphreg.htm#03}
  650. */
  651. VGAScreen.prototype.apply_rotate = function(data_byte)
  652. {
  653. var wrapped = data_byte | (data_byte << 8);
  654. var count = this.planar_rotate_reg & 0x7;
  655. var shifted = wrapped >>> count;
  656. return shifted & 0xFF;
  657. };
  658. /**
  659. * Planar Write - Set / Reset Circuitry
  660. * @param {number} data_dword
  661. * @param {number} enable_dword
  662. * @return {number}
  663. * @see {@link http://www.phatcode.net/res/224/files/html/ch25/25-03.html#Heading5}
  664. * @see {@link http://www.osdever.net/FreeVGA/vga/graphreg.htm#00}
  665. */
  666. VGAScreen.prototype.apply_setreset = function(data_dword, enable_dword)
  667. {
  668. var setreset_dword = this.apply_expand(this.planar_setreset);
  669. data_dword |= enable_dword & setreset_dword;
  670. data_dword &= ~enable_dword | setreset_dword;
  671. return data_dword;
  672. };
  673. /**
  674. * Planar Write - ALU Unit
  675. * @param {number} data_dword
  676. * @param {number} latch_dword
  677. * @return {number}
  678. * @see {@link http://www.phatcode.net/res/224/files/html/ch24/24-01.html#Heading3}
  679. * @see {@link http://www.osdever.net/FreeVGA/vga/graphreg.htm#03}
  680. */
  681. VGAScreen.prototype.apply_logical = function(data_dword, latch_dword)
  682. {
  683. switch(this.planar_rotate_reg & 0x18)
  684. {
  685. case 0x08:
  686. return data_dword & latch_dword;
  687. case 0x10:
  688. return data_dword | latch_dword;
  689. case 0x18:
  690. return data_dword ^ latch_dword;
  691. }
  692. return data_dword;
  693. };
  694. /**
  695. * Planar Write - Bitmask Unit
  696. * @param {number} data_dword
  697. * @param {number} bitmask_dword
  698. * @return {number}
  699. * @see {@link http://www.phatcode.net/res/224/files/html/ch25/25-01.html#Heading2}
  700. * @see {@link http://www.osdever.net/FreeVGA/vga/graphreg.htm#08}
  701. */
  702. VGAScreen.prototype.apply_bitmask = function(data_dword, bitmask_dword)
  703. {
  704. var plane_dword = bitmask_dword & data_dword;
  705. plane_dword |= ~bitmask_dword & this.latch_dword;
  706. return plane_dword;
  707. };
  708. VGAScreen.prototype.text_mode_redraw = function()
  709. {
  710. var addr = this.start_address << 1,
  711. chr,
  712. color;
  713. for(var row = 0; row < this.max_rows; row++)
  714. {
  715. for(var col = 0; col < this.max_cols; col++)
  716. {
  717. chr = this.vga_memory[addr];
  718. color = this.vga_memory[addr | 1];
  719. this.bus.send("screen-put-char", [row, col, chr,
  720. this.vga256_palette[this.dac_mask & this.dac_map[color >> 4 & 0xF]],
  721. this.vga256_palette[this.dac_mask & this.dac_map[color & 0xF]]]);
  722. addr += 2;
  723. }
  724. }
  725. };
  726. VGAScreen.prototype.vga_memory_write_text_mode = function(addr, value)
  727. {
  728. var memory_start = (addr >> 1) - this.start_address,
  729. row = memory_start / this.max_cols | 0,
  730. col = memory_start % this.max_cols,
  731. chr,
  732. color;
  733. // XXX: Should handle 16 bit write if possible
  734. if(addr & 1)
  735. {
  736. color = value;
  737. chr = this.vga_memory[addr & ~1];
  738. }
  739. else
  740. {
  741. chr = value;
  742. color = this.vga_memory[addr | 1];
  743. }
  744. this.bus.send("screen-put-char", [row, col, chr,
  745. this.vga256_palette[this.dac_mask & this.dac_map[color >> 4 & 0xF]],
  746. this.vga256_palette[this.dac_mask & this.dac_map[color & 0xF]]]);
  747. this.vga_memory[addr] = value;
  748. };
  749. VGAScreen.prototype.update_cursor = function()
  750. {
  751. var row = (this.cursor_address - this.start_address) / this.max_cols | 0,
  752. col = (this.cursor_address - this.start_address) % this.max_cols;
  753. row = Math.min(this.max_rows - 1, row);
  754. this.bus.send("screen-update-cursor", [row, col]);
  755. };
  756. VGAScreen.prototype.complete_redraw = function()
  757. {
  758. dbg_log("complete redraw", LOG_VGA);
  759. if(this.graphical_mode)
  760. {
  761. if(this.svga_enabled)
  762. {
  763. this.cpu.svga_mark_dirty();
  764. }
  765. else
  766. {
  767. this.diff_addr_min = 0;
  768. this.diff_addr_max = VGA_PIXEL_BUFFER_SIZE;
  769. }
  770. }
  771. else
  772. {
  773. this.text_mode_redraw();
  774. }
  775. };
  776. VGAScreen.prototype.complete_replot = function()
  777. {
  778. dbg_log("complete replot", LOG_VGA);
  779. if(!this.graphical_mode || this.svga_enabled)
  780. {
  781. return;
  782. }
  783. this.diff_plot_min = 0;
  784. this.diff_plot_max = VGA_PIXEL_BUFFER_SIZE;
  785. this.complete_redraw();
  786. };
  787. VGAScreen.prototype.partial_redraw = function(min, max)
  788. {
  789. if(min < this.diff_addr_min) this.diff_addr_min = min;
  790. if(max > this.diff_addr_max) this.diff_addr_max = max;
  791. };
  792. VGAScreen.prototype.partial_replot = function(min, max)
  793. {
  794. if(min < this.diff_plot_min) this.diff_plot_min = min;
  795. if(max > this.diff_plot_max) this.diff_plot_max = max;
  796. this.partial_redraw(min, max);
  797. };
  798. VGAScreen.prototype.reset_diffs = function()
  799. {
  800. this.diff_addr_min = this.vga_memory_size;
  801. this.diff_addr_max = 0;
  802. this.diff_plot_min = this.vga_memory_size;
  803. this.diff_plot_max = 0;
  804. };
  805. VGAScreen.prototype.destroy = function()
  806. {
  807. };
  808. VGAScreen.prototype.vga_bytes_per_line = function()
  809. {
  810. var bytes_per_line = this.offset_register << 2;
  811. if(this.underline_location_register & 0x40) bytes_per_line <<= 1;
  812. else if(this.crtc_mode & 0x40) bytes_per_line >>>= 1;
  813. return bytes_per_line;
  814. };
  815. VGAScreen.prototype.vga_addr_shift_count = function()
  816. {
  817. // Count in multiples of 0x40 for convenience
  818. // Left shift 2 for word mode - 2 bytes per dot clock
  819. var shift_count = 0x80;
  820. // Left shift 3 for byte mode - 1 byte per dot clock
  821. shift_count += ~this.underline_location_register & this.crtc_mode & 0x40;
  822. // Left shift 1 for doubleword mode - 4 bytes per dot clock
  823. shift_count -= this.underline_location_register & 0x40;
  824. // But shift one less if PEL width mode - 2 dot clocks per pixel
  825. shift_count -= this.attribute_mode & 0x40;
  826. return shift_count >>> 6;
  827. };
  828. VGAScreen.prototype.vga_addr_to_pixel = function(addr)
  829. {
  830. var shift_count = this.vga_addr_shift_count();
  831. // Undo effects of substituted bits 13 and 14
  832. // Assumptions:
  833. // - max_scan_line register is set to the values shown below
  834. // - Each scan line stays within the offset alignment
  835. // - No panning and no page flipping after drawing
  836. if(~this.crtc_mode & 0x3)
  837. {
  838. var pixel_addr = addr - this.start_address;
  839. // Remove substituted bits
  840. pixel_addr &= this.crtc_mode << 13 | ~0x6000;
  841. // Convert to 1 pixel per address
  842. pixel_addr <<= shift_count;
  843. // Decompose address
  844. var row = pixel_addr / this.virtual_width | 0;
  845. var col = pixel_addr % this.virtual_width;
  846. switch(this.crtc_mode & 0x3)
  847. {
  848. case 0x2:
  849. // Alternating rows using bit 13
  850. // Assumes max scan line = 1
  851. row = row << 1 | (addr >> 13 & 0x1);
  852. break;
  853. case 0x1:
  854. // Alternating rows using bit 14
  855. // Assumes max scan line = 3
  856. row = row << 1 | (addr >> 14 & 0x1);
  857. break;
  858. case 0x0:
  859. // Cycling through rows using bit 13 and 14
  860. // Assumes max scan line = 3
  861. row = row << 2 | (addr >> 13 & 0x3);
  862. break;
  863. }
  864. // Reassemble address
  865. return row * this.virtual_width + col + (this.start_address << shift_count);
  866. }
  867. else
  868. {
  869. // Convert to 1 pixel per address
  870. return addr << shift_count;
  871. }
  872. };
  873. VGAScreen.prototype.scan_line_to_screen_row = function(scan_line)
  874. {
  875. // Double scanning. The clock to the row scan counter is halved
  876. // so it is not affected by the memory address bit substitutions below
  877. if(this.max_scan_line & 0x80)
  878. {
  879. scan_line >>>= 1;
  880. }
  881. // Maximum scan line, aka scan lines per character row
  882. // This is the number of repeats - 1 for graphic modes
  883. var repeat_factor = 1 + (this.max_scan_line & 0x1F);
  884. scan_line = Math.ceil(scan_line / repeat_factor);
  885. // Odd and Even Row Scan Counter
  886. // Despite repeated address counter values, because bit 13 of the shifted
  887. // address is substituted with bit 0 of the row scan counter, a different
  888. // display buffer address is generated instead of repeated
  889. // Assumes maximum scan line register is set to 2 or 4.
  890. // Note: can't assert this as register values may not be fully programmed.
  891. if(!(this.crtc_mode & 0x1))
  892. {
  893. scan_line <<= 1;
  894. }
  895. // Undo effects of substituted bit 14
  896. // Assumes maximum scan line register is set to 2 or 4
  897. // Note: can't assert this as register values may not be fully programmed.
  898. // Other maximum scan line register values would result in weird addressing
  899. // anyway
  900. if(!(this.crtc_mode & 0x2))
  901. {
  902. scan_line <<= 1;
  903. }
  904. return scan_line;
  905. };
  906. /**
  907. * @param {number} cols_count
  908. * @param {number} rows_count
  909. */
  910. VGAScreen.prototype.set_size_text = function(cols_count, rows_count)
  911. {
  912. this.max_cols = cols_count;
  913. this.max_rows = rows_count;
  914. this.bus.send("screen-set-size-text", [cols_count, rows_count]);
  915. };
  916. VGAScreen.prototype.set_size_graphical = function(width, height, bpp, virtual_width, virtual_height)
  917. {
  918. var needs_update = !this.stats.is_graphical ||
  919. this.stats.bpp !== bpp ||
  920. this.screen_width !== width ||
  921. this.screen_height !== height ||
  922. this.virtual_width !== virtual_width ||
  923. this.virtual_height !== virtual_height;
  924. if(needs_update)
  925. {
  926. this.screen_width = width;
  927. this.screen_height = height;
  928. this.virtual_width = virtual_width;
  929. this.virtual_height = virtual_height;
  930. this.stats.bpp = bpp;
  931. this.stats.is_graphical = true;
  932. this.stats.res_x = width;
  933. this.stats.res_y = height;
  934. if (typeof ImageData !== "undefined")
  935. {
  936. const size = virtual_width * virtual_height;
  937. const offset = this.cpu.svga_allocate_dest_buffer(size) >>> 0;
  938. this.dest_buffet_offset = offset;
  939. this.image_data = new ImageData(new Uint8ClampedArray(this.cpu.wasm_memory.buffer, offset, 4 * size), virtual_width, virtual_height);
  940. this.cpu.svga_mark_dirty();
  941. }
  942. else
  943. {
  944. // TODO: nodejs
  945. }
  946. this.bus.send("screen-set-size-graphical", [width, height, virtual_width, virtual_height, bpp]);
  947. }
  948. };
  949. VGAScreen.prototype.update_vga_size = function()
  950. {
  951. if(this.svga_enabled)
  952. {
  953. return;
  954. }
  955. var horizontal_characters = Math.min(1 + this.horizontal_display_enable_end,
  956. this.horizontal_blank_start);
  957. var vertical_scans = Math.min(1 + this.vertical_display_enable_end,
  958. this.vertical_blank_start);
  959. if(!horizontal_characters || !vertical_scans)
  960. {
  961. // Don't update if width or height is zero.
  962. // These happen when registers are not fully configured yet.
  963. return;
  964. }
  965. if(this.graphical_mode)
  966. {
  967. var screen_width = horizontal_characters << 3;
  968. // Offset is half the number of bytes/words/dwords (depending on clocking mode)
  969. // of display memory that each logical line occupies.
  970. // However, the number of pixels latched, regardless of addressing mode,
  971. // should always 8 pixels per character clock (except for 8 bit PEL width, in which
  972. // case 4 pixels).
  973. var virtual_width = this.offset_register << 4;
  974. // Pixel Width / PEL Width / Clock Select
  975. if(this.attribute_mode & 0x40)
  976. {
  977. screen_width >>>= 1;
  978. virtual_width >>>= 1;
  979. }
  980. var screen_height = this.scan_line_to_screen_row(vertical_scans);
  981. // The virtual buffer height is however many rows of data that can fit.
  982. // Previously drawn graphics outside of current memory address space can
  983. // still be drawn by setting start_address. The address at
  984. // VGA_HOST_MEMORY_SPACE_START[memory_space_select] is mapped to the first
  985. // byte of the frame buffer. Verified on some hardware.
  986. // Depended on by: Windows 98 start screen
  987. var available_bytes = VGA_HOST_MEMORY_SPACE_SIZE[0];
  988. var virtual_height = Math.ceil(available_bytes / this.vga_bytes_per_line());
  989. this.set_size_graphical(screen_width, screen_height, 8,
  990. virtual_width, virtual_height);
  991. this.update_vertical_retrace();
  992. this.update_layers();
  993. }
  994. else
  995. {
  996. if(this.max_scan_line & 0x80)
  997. {
  998. // Double scanning means that half of those scan lines
  999. // are just repeats
  1000. vertical_scans >>>= 1;
  1001. }
  1002. var height = vertical_scans / (1 + (this.max_scan_line & 0x1F)) | 0;
  1003. if(horizontal_characters && height)
  1004. {
  1005. this.set_size_text(horizontal_characters, height);
  1006. }
  1007. }
  1008. };
  1009. VGAScreen.prototype.update_layers = function()
  1010. {
  1011. if(!this.graphical_mode)
  1012. {
  1013. this.text_mode_redraw();
  1014. }
  1015. if(this.svga_enabled)
  1016. {
  1017. this.layers = [];
  1018. return;
  1019. }
  1020. if(!this.virtual_width || !this.screen_width)
  1021. {
  1022. // Avoid division by zero
  1023. return;
  1024. }
  1025. if(!this.palette_source || (this.clocking_mode & 0x20))
  1026. {
  1027. // Palette source and screen disable bits = draw nothing
  1028. // See http://www.phatcode.net/res/224/files/html/ch29/29-05.html#Heading6
  1029. // and http://www.osdever.net/FreeVGA/vga/seqreg.htm#01
  1030. this.layers = [];
  1031. this.bus.send("screen-clear");
  1032. return;
  1033. }
  1034. var start_addr = this.start_address_latched;
  1035. var pixel_panning = this.horizontal_panning;
  1036. if(this.attribute_mode & 0x40)
  1037. {
  1038. pixel_panning >>>= 1;
  1039. }
  1040. var byte_panning = this.preset_row_scan >> 5 & 0x3;
  1041. var pixel_addr_start = this.vga_addr_to_pixel(start_addr + byte_panning);
  1042. var start_buffer_row = pixel_addr_start / this.virtual_width | 0;
  1043. var start_buffer_col = pixel_addr_start % this.virtual_width + pixel_panning;
  1044. var split_screen_row = this.scan_line_to_screen_row(1 + this.line_compare);
  1045. split_screen_row = Math.min(split_screen_row, this.screen_height);
  1046. var split_buffer_height = this.screen_height - split_screen_row;
  1047. this.layers = [];
  1048. for(var x = -start_buffer_col, y = 0; x < this.screen_width; x += this.virtual_width, y++)
  1049. {
  1050. this.layers.push({
  1051. image_data: this.image_data,
  1052. screen_x: x,
  1053. screen_y: 0,
  1054. buffer_x: 0,
  1055. buffer_y: start_buffer_row + y,
  1056. buffer_width: this.virtual_width,
  1057. buffer_height: split_screen_row,
  1058. });
  1059. }
  1060. var start_split_col = 0;
  1061. if(!(this.attribute_mode & 0x20))
  1062. {
  1063. // Pixel panning mode. Allow panning for the lower split screen
  1064. start_split_col = this.vga_addr_to_pixel(byte_panning) + pixel_panning;
  1065. }
  1066. for(var x = -start_split_col, y = 0; x < this.screen_width; x += this.virtual_width, y++)
  1067. {
  1068. this.layers.push({
  1069. image_data: this.image_data,
  1070. screen_x: x,
  1071. screen_y: split_screen_row,
  1072. buffer_x: 0,
  1073. buffer_y: y,
  1074. buffer_width: this.virtual_width,
  1075. buffer_height: split_buffer_height,
  1076. });
  1077. }
  1078. };
  1079. VGAScreen.prototype.update_vertical_retrace = function()
  1080. {
  1081. // Emulate behaviour during VSync/VRetrace
  1082. this.port_3DA_value |= 0x8;
  1083. if(this.start_address_latched !== this.start_address)
  1084. {
  1085. this.start_address_latched = this.start_address;
  1086. this.update_layers();
  1087. }
  1088. };
  1089. VGAScreen.prototype.update_cursor_scanline = function()
  1090. {
  1091. this.bus.send("screen-update-cursor-scanline", [this.cursor_scanline_start, this.cursor_scanline_end]);
  1092. };
  1093. /**
  1094. * Attribute controller register / index write
  1095. * @see {@link http://www.osdever.net/FreeVGA/vga/attrreg.htm}
  1096. * @see {@link http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf} page 89
  1097. * @see {@link https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-hsw-display_0.pdf} page 48
  1098. */
  1099. VGAScreen.prototype.port3C0_write = function(value)
  1100. {
  1101. if(this.attribute_controller_index === -1)
  1102. {
  1103. dbg_log("attribute controller index register: " + h(value), LOG_VGA);
  1104. this.attribute_controller_index = value & 0x1F;
  1105. dbg_log("attribute actual index: " + h(this.attribute_controller_index), LOG_VGA);
  1106. if(this.palette_source !== (value & 0x20))
  1107. {
  1108. // A method of blanking the screen.
  1109. // See http://www.phatcode.net/res/224/files/html/ch29/29-05.html#Heading6
  1110. this.palette_source = value & 0x20;
  1111. this.update_layers();
  1112. }
  1113. }
  1114. else
  1115. {
  1116. if(this.attribute_controller_index < 0x10)
  1117. {
  1118. dbg_log("internal palette: " + h(this.attribute_controller_index) + " -> " + h(value), LOG_VGA);
  1119. this.dac_map[this.attribute_controller_index] = value;
  1120. if(!(this.attribute_mode & 0x40))
  1121. {
  1122. this.complete_redraw();
  1123. }
  1124. }
  1125. else
  1126. switch(this.attribute_controller_index)
  1127. {
  1128. case 0x10:
  1129. dbg_log("3C0 / attribute mode control: " + h(value), LOG_VGA);
  1130. if(this.attribute_mode !== value)
  1131. {
  1132. var previous_mode = this.attribute_mode;
  1133. this.attribute_mode = value;
  1134. var is_graphical = (value & 0x1) > 0;
  1135. if(!this.svga_enabled && this.graphical_mode !== is_graphical)
  1136. {
  1137. this.graphical_mode = is_graphical;
  1138. this.bus.send("screen-set-mode", this.graphical_mode);
  1139. }
  1140. if((previous_mode ^ value) & 0x40)
  1141. {
  1142. // PEL width changed. Pixel Buffer now invalidated
  1143. this.complete_replot();
  1144. }
  1145. this.update_vga_size();
  1146. // Data stored in image buffer are invalidated
  1147. this.complete_redraw();
  1148. }
  1149. break;
  1150. case 0x12:
  1151. dbg_log("3C0 / color plane enable: " + h(value), LOG_VGA);
  1152. if(this.color_plane_enable !== value)
  1153. {
  1154. this.color_plane_enable = value;
  1155. // Data stored in image buffer are invalidated
  1156. this.complete_redraw();
  1157. }
  1158. break;
  1159. case 0x13:
  1160. dbg_log("3C0 / horizontal panning: " + h(value), LOG_VGA);
  1161. if(this.horizontal_panning !== value)
  1162. {
  1163. this.horizontal_panning = value & 0xF;
  1164. this.update_layers();
  1165. }
  1166. break;
  1167. case 0x14:
  1168. dbg_log("3C0 / color select: " + h(value), LOG_VGA);
  1169. if(this.color_select !== value)
  1170. {
  1171. this.color_select = value;
  1172. // Data stored in image buffer are invalidated
  1173. this.complete_redraw();
  1174. }
  1175. break;
  1176. default:
  1177. dbg_log("3C0 / attribute controller write " + h(this.attribute_controller_index) + ": " + h(value), LOG_VGA);
  1178. }
  1179. this.attribute_controller_index = -1;
  1180. }
  1181. };
  1182. VGAScreen.prototype.port3C0_read = function()
  1183. {
  1184. dbg_log("3C0 read", LOG_VGA);
  1185. return (this.attribute_controller_index | this.palette_source) & 0xFF;
  1186. };
  1187. VGAScreen.prototype.port3C0_read16 = function()
  1188. {
  1189. dbg_log("3C0 read16", LOG_VGA);
  1190. return this.port3C0_read() | this.port3C1_read() << 8 & 0xFF00;
  1191. };
  1192. VGAScreen.prototype.port3C1_read = function()
  1193. {
  1194. if(this.attribute_controller_index < 0x10)
  1195. {
  1196. dbg_log("3C1 / internal palette read: " + h(this.attribute_controller_index) +
  1197. " -> " + h(this.dac_map[this.attribute_controller_index]), LOG_VGA);
  1198. return this.dac_map[this.attribute_controller_index] & 0xFF;
  1199. }
  1200. switch(this.attribute_controller_index)
  1201. {
  1202. case 0x10:
  1203. dbg_log("3C1 / attribute mode read: " + h(this.attribute_mode), LOG_VGA);
  1204. return this.attribute_mode;
  1205. case 0x12:
  1206. dbg_log("3C1 / color plane enable read: " + h(this.color_plane_enable), LOG_VGA);
  1207. return this.color_plane_enable;
  1208. case 0x13:
  1209. dbg_log("3C1 / horizontal panning read: " + h(this.horizontal_panning), LOG_VGA);
  1210. return this.horizontal_panning;
  1211. case 0x14:
  1212. dbg_log("3C1 / color select read: " + h(this.color_select), LOG_VGA);
  1213. return this.color_select;
  1214. default:
  1215. dbg_log("3C1 / attribute controller read " + h(this.attribute_controller_index), LOG_VGA);
  1216. }
  1217. return 0xFF;
  1218. };
  1219. VGAScreen.prototype.port3C2_write = function(value)
  1220. {
  1221. dbg_log("3C2 / miscellaneous output register = " + h(value), LOG_VGA);
  1222. this.miscellaneous_output_register = value;
  1223. };
  1224. VGAScreen.prototype.port3C4_write = function(value)
  1225. {
  1226. this.sequencer_index = value;
  1227. };
  1228. VGAScreen.prototype.port3C4_read = function()
  1229. {
  1230. return this.sequencer_index;
  1231. };
  1232. /**
  1233. * Sequencer register writes
  1234. * @see {@link http://www.osdever.net/FreeVGA/vga/seqreg.htm}
  1235. * @see {@link http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf} page 47
  1236. * @see {@link https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-hsw-display_0.pdf} page 19
  1237. */
  1238. VGAScreen.prototype.port3C5_write = function(value)
  1239. {
  1240. switch(this.sequencer_index)
  1241. {
  1242. case 0x01:
  1243. dbg_log("clocking mode: " + h(value), LOG_VGA);
  1244. var previous_clocking_mode = this.clocking_mode;
  1245. this.clocking_mode = value;
  1246. if((previous_clocking_mode ^ value) & 0x20)
  1247. {
  1248. // Screen disable bit modified
  1249. this.update_layers();
  1250. }
  1251. break;
  1252. case 0x02:
  1253. dbg_log("plane write mask: " + h(value), LOG_VGA);
  1254. this.plane_write_bm = value;
  1255. break;
  1256. case 0x04:
  1257. dbg_log("sequencer memory mode: " + h(value), LOG_VGA);
  1258. this.sequencer_memory_mode = value;
  1259. break;
  1260. default:
  1261. dbg_log("3C5 / sequencer write " + h(this.sequencer_index) + ": " + h(value), LOG_VGA);
  1262. }
  1263. };
  1264. VGAScreen.prototype.port3C5_read = function()
  1265. {
  1266. dbg_log("3C5 / sequencer read " + h(this.sequencer_index), LOG_VGA);
  1267. switch(this.sequencer_index)
  1268. {
  1269. case 0x01:
  1270. return this.clocking_mode;
  1271. case 0x02:
  1272. return this.plane_write_bm;
  1273. case 0x04:
  1274. return this.sequencer_memory_mode;
  1275. case 0x06:
  1276. return 0x12;
  1277. default:
  1278. }
  1279. return 0;
  1280. };
  1281. VGAScreen.prototype.port3C6_write = function(data)
  1282. {
  1283. this.dac_mask = data;
  1284. };
  1285. VGAScreen.prototype.port3C6_read = function()
  1286. {
  1287. return this.dac_mask;
  1288. };
  1289. VGAScreen.prototype.port3C7_write = function(index)
  1290. {
  1291. // index for reading the DAC
  1292. dbg_log("3C7 write: " + h(index), LOG_VGA);
  1293. this.dac_color_index_read = index * 3;
  1294. this.dac_state &= 0x0;
  1295. };
  1296. VGAScreen.prototype.port3C7_read = function()
  1297. {
  1298. // prepared to accept reads or writes
  1299. return this.dac_state;
  1300. };
  1301. VGAScreen.prototype.port3C8_write = function(index)
  1302. {
  1303. this.dac_color_index_write = index * 3;
  1304. this.dac_state |= 0x3;
  1305. };
  1306. VGAScreen.prototype.port3C8_read = function()
  1307. {
  1308. return this.dac_color_index_write / 3 & 0xFF;
  1309. };
  1310. /**
  1311. * DAC color palette register writes
  1312. * @see {@link http://www.osdever.net/FreeVGA/vga/colorreg.htm}
  1313. * @see {@link http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf} page 104
  1314. * @see {@link https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-hsw-display_0.pdf} page 57
  1315. */
  1316. VGAScreen.prototype.port3C9_write = function(color_byte)
  1317. {
  1318. var index = this.dac_color_index_write / 3 | 0,
  1319. offset = this.dac_color_index_write % 3,
  1320. color = this.vga256_palette[index];
  1321. if((this.dispi_enable_value & 0x20) === 0)
  1322. {
  1323. color_byte &= 0x3F;
  1324. const b = color_byte & 1;
  1325. color_byte = color_byte << 2 | b << 1 | b;
  1326. }
  1327. if(offset === 0)
  1328. {
  1329. color = color & ~0xFF0000 | color_byte << 16;
  1330. }
  1331. else if(offset === 1)
  1332. {
  1333. color = color & ~0xFF00 | color_byte << 8;
  1334. }
  1335. else
  1336. {
  1337. color = color & ~0xFF | color_byte;
  1338. dbg_log("dac set color, index=" + h(index) + " value=" + h(color), LOG_VGA);
  1339. }
  1340. if(this.vga256_palette[index] !== color)
  1341. {
  1342. this.vga256_palette[index] = color;
  1343. this.complete_redraw();
  1344. }
  1345. this.dac_color_index_write++;
  1346. };
  1347. VGAScreen.prototype.port3C9_read = function()
  1348. {
  1349. dbg_log("3C9 read", LOG_VGA);
  1350. var index = this.dac_color_index_read / 3 | 0;
  1351. var offset = this.dac_color_index_read % 3;
  1352. var color = this.vga256_palette[index];
  1353. var color8 = color >> (2 - offset) * 8 & 0xFF;
  1354. this.dac_color_index_read++;
  1355. if(this.dispi_enable_value & 0x20)
  1356. {
  1357. return color8;
  1358. }
  1359. else
  1360. {
  1361. return color8 >> 2;
  1362. }
  1363. };
  1364. VGAScreen.prototype.port3CC_read = function()
  1365. {
  1366. dbg_log("3CC read", LOG_VGA);
  1367. return this.miscellaneous_output_register;
  1368. };
  1369. VGAScreen.prototype.port3CE_write = function(value)
  1370. {
  1371. this.graphics_index = value;
  1372. };
  1373. VGAScreen.prototype.port3CE_read = function()
  1374. {
  1375. return this.graphics_index;
  1376. };
  1377. /**
  1378. * Graphics controller register writes
  1379. * @see {@link http://www.osdever.net/FreeVGA/vga/graphreg.htm}
  1380. * @see {@link http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf} page 78
  1381. * @see {@link https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-hsw-display_0.pdf} page 29
  1382. */
  1383. VGAScreen.prototype.port3CF_write = function(value)
  1384. {
  1385. switch(this.graphics_index)
  1386. {
  1387. case 0:
  1388. this.planar_setreset = value;
  1389. dbg_log("plane set/reset: " + h(value), LOG_VGA);
  1390. break;
  1391. case 1:
  1392. this.planar_setreset_enable = value;
  1393. dbg_log("plane set/reset enable: " + h(value), LOG_VGA);
  1394. break;
  1395. case 2:
  1396. this.color_compare = value;
  1397. dbg_log("color compare: " + h(value), LOG_VGA);
  1398. break;
  1399. case 3:
  1400. this.planar_rotate_reg = value;
  1401. dbg_log("plane rotate: " + h(value), LOG_VGA);
  1402. break;
  1403. case 4:
  1404. this.plane_read = value;
  1405. dbg_log("plane read: " + h(value), LOG_VGA);
  1406. break;
  1407. case 5:
  1408. var previous_planar_mode = this.planar_mode;
  1409. this.planar_mode = value;
  1410. dbg_log("planar mode: " + h(value), LOG_VGA);
  1411. if((previous_planar_mode ^ value) & 0x60)
  1412. {
  1413. // Shift mode modified. Pixel buffer invalidated
  1414. this.complete_replot();
  1415. }
  1416. break;
  1417. case 6:
  1418. dbg_log("miscellaneous graphics register: " + h(value), LOG_VGA);
  1419. if(this.miscellaneous_graphics_register !== value)
  1420. {
  1421. this.miscellaneous_graphics_register = value;
  1422. this.update_vga_size();
  1423. }
  1424. break;
  1425. case 7:
  1426. this.color_dont_care = value;
  1427. dbg_log("color don't care: " + h(value), LOG_VGA);
  1428. break;
  1429. case 8:
  1430. this.planar_bitmap = value;
  1431. dbg_log("planar bitmap: " + h(value), LOG_VGA);
  1432. break;
  1433. default:
  1434. dbg_log("3CF / graphics write " + h(this.graphics_index) + ": " + h(value), LOG_VGA);
  1435. }
  1436. };
  1437. VGAScreen.prototype.port3CF_read = function()
  1438. {
  1439. dbg_log("3CF / graphics read " + h(this.graphics_index), LOG_VGA);
  1440. switch(this.graphics_index)
  1441. {
  1442. case 0:
  1443. return this.planar_setreset;
  1444. case 1:
  1445. return this.planar_setreset_enable;
  1446. case 2:
  1447. return this.color_compare;
  1448. case 3:
  1449. return this.planar_rotate_reg;
  1450. case 4:
  1451. return this.plane_read;
  1452. case 5:
  1453. return this.planar_mode;
  1454. case 6:
  1455. return this.miscellaneous_graphics_register;
  1456. case 7:
  1457. return this.color_dont_care;
  1458. case 8:
  1459. return this.planar_bitmap;
  1460. default:
  1461. }
  1462. return 0;
  1463. };
  1464. VGAScreen.prototype.port3D4_write = function(register)
  1465. {
  1466. dbg_log("3D4 / crtc index: " + register, LOG_VGA);
  1467. this.index_crtc = register;
  1468. };
  1469. VGAScreen.prototype.port3D4_read = function()
  1470. {
  1471. dbg_log("3D4 read / crtc index: " + this.index_crtc, LOG_VGA);
  1472. return this.index_crtc;
  1473. };
  1474. /**
  1475. * CRT controller register writes
  1476. * @see {@link http://www.osdever.net/FreeVGA/vga/crtcreg.htm}
  1477. * @see {@link http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf} page 55
  1478. * @see {@link https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-hsw-display_0.pdf} page 63
  1479. */
  1480. VGAScreen.prototype.port3D5_write = function(value)
  1481. {
  1482. switch(this.index_crtc)
  1483. {
  1484. case 0x1:
  1485. dbg_log("3D5 / hdisp enable end write: " + h(value), LOG_VGA);
  1486. if(this.horizontal_display_enable_end !== value)
  1487. {
  1488. this.horizontal_display_enable_end = value;
  1489. this.update_vga_size();
  1490. }
  1491. break;
  1492. case 0x2:
  1493. if(this.horizontal_blank_start !== value)
  1494. {
  1495. this.horizontal_blank_start = value;
  1496. this.update_vga_size();
  1497. }
  1498. break;
  1499. case 0x7:
  1500. dbg_log("3D5 / overflow register write: " + h(value), LOG_VGA);
  1501. var previous_vertical_display_enable_end = this.vertical_display_enable_end;
  1502. this.vertical_display_enable_end &= 0xFF;
  1503. this.vertical_display_enable_end |= (value << 3 & 0x200) | (value << 7 & 0x100);
  1504. if(previous_vertical_display_enable_end != this.vertical_display_enable_end)
  1505. {
  1506. this.update_vga_size();
  1507. }
  1508. this.line_compare = (this.line_compare & 0x2FF) | (value << 4 & 0x100);
  1509. var previous_vertical_blank_start = this.vertical_blank_start;
  1510. this.vertical_blank_start = (this.vertical_blank_start & 0x2FF) | (value << 5 & 0x100);
  1511. if(previous_vertical_blank_start !== this.vertical_blank_start)
  1512. {
  1513. this.update_vga_size();
  1514. }
  1515. this.update_layers();
  1516. break;
  1517. case 0x8:
  1518. dbg_log("3D5 / preset row scan write: " + h(value), LOG_VGA);
  1519. this.preset_row_scan = value;
  1520. this.update_layers();
  1521. break;
  1522. case 0x9:
  1523. dbg_log("3D5 / max scan line write: " + h(value), LOG_VGA);
  1524. this.max_scan_line = value;
  1525. this.line_compare = (this.line_compare & 0x1FF) | (value << 3 & 0x200);
  1526. var previous_vertical_blank_start = this.vertical_blank_start;
  1527. this.vertical_blank_start = (this.vertical_blank_start & 0x1FF) | (value << 4 & 0x200);
  1528. if(previous_vertical_blank_start !== this.vertical_blank_start)
  1529. {
  1530. this.update_vga_size();
  1531. }
  1532. this.update_layers();
  1533. break;
  1534. case 0xA:
  1535. dbg_log("3D5 / cursor scanline start write: " + h(value), LOG_VGA);
  1536. this.cursor_scanline_start = value;
  1537. this.update_cursor_scanline();
  1538. break;
  1539. case 0xB:
  1540. dbg_log("3D5 / cursor scanline end write: " + h(value), LOG_VGA);
  1541. this.cursor_scanline_end = value;
  1542. this.update_cursor_scanline();
  1543. break;
  1544. case 0xC:
  1545. if((this.start_address >> 8 & 0xFF) !== value)
  1546. {
  1547. this.start_address = this.start_address & 0xff | value << 8;
  1548. this.update_layers();
  1549. if(~this.crtc_mode & 0x3)
  1550. {
  1551. // Address substitution implementation depends on the
  1552. // starting row and column, so the pixel buffer is invalidated.
  1553. this.complete_replot();
  1554. }
  1555. }
  1556. dbg_log("3D5 / start addr hi write: " + h(value) + " -> " + h(this.start_address, 4), LOG_VGA);
  1557. break;
  1558. case 0xD:
  1559. if((this.start_address & 0xFF) !== value)
  1560. {
  1561. this.start_address = this.start_address & 0xff00 | value;
  1562. this.update_layers();
  1563. if(~this.crtc_mode & 0x3)
  1564. {
  1565. // Address substitution implementation depends on the
  1566. // starting row and column, so the pixel buffer is invalidated.
  1567. this.complete_replot();
  1568. }
  1569. }
  1570. dbg_log("3D5 / start addr lo write: " + h(value) + " -> " + h(this.start_address, 4), LOG_VGA);
  1571. break;
  1572. case 0xE:
  1573. dbg_log("3D5 / cursor address hi write: " + h(value), LOG_VGA);
  1574. this.cursor_address = this.cursor_address & 0xFF | value << 8;
  1575. this.update_cursor();
  1576. break;
  1577. case 0xF:
  1578. dbg_log("3D5 / cursor address lo write: " + h(value), LOG_VGA);
  1579. this.cursor_address = this.cursor_address & 0xFF00 | value;
  1580. this.update_cursor();
  1581. break;
  1582. case 0x12:
  1583. dbg_log("3D5 / vdisp enable end write: " + h(value), LOG_VGA);
  1584. if((this.vertical_display_enable_end & 0xFF) !== value)
  1585. {
  1586. this.vertical_display_enable_end = (this.vertical_display_enable_end & 0x300) | value;
  1587. this.update_vga_size();
  1588. }
  1589. break;
  1590. case 0x13:
  1591. dbg_log("3D5 / offset register write: " + h(value), LOG_VGA);
  1592. if(this.offset_register !== value)
  1593. {
  1594. this.offset_register = value;
  1595. this.update_vga_size();
  1596. if(~this.crtc_mode & 0x3)
  1597. {
  1598. // Address substitution implementation depends on the
  1599. // virtual width, so the pixel buffer is invalidated.
  1600. this.complete_replot();
  1601. }
  1602. }
  1603. break;
  1604. case 0x14:
  1605. dbg_log("3D5 / underline location write: " + h(value), LOG_VGA);
  1606. if(this.underline_location_register !== value)
  1607. {
  1608. var previous_underline = this.underline_location_register;
  1609. this.underline_location_register = value;
  1610. this.update_vga_size();
  1611. if((previous_underline ^ value) & 0x40)
  1612. {
  1613. // Doubleword addressing changed. Pixel buffer invalidated.
  1614. this.complete_replot();
  1615. }
  1616. }
  1617. break;
  1618. case 0x15:
  1619. dbg_log("3D5 / vertical blank start write: " + h(value), LOG_VGA);
  1620. if((this.vertical_blank_start & 0xFF) !== value)
  1621. {
  1622. this.vertical_blank_start = (this.vertical_blank_start & 0x300) | value;
  1623. this.update_vga_size();
  1624. }
  1625. break;
  1626. case 0x17:
  1627. dbg_log("3D5 / crtc mode write: " + h(value), LOG_VGA);
  1628. if(this.crtc_mode !== value)
  1629. {
  1630. var previous_mode = this.crtc_mode;
  1631. this.crtc_mode = value;
  1632. this.update_vga_size();
  1633. if((previous_mode ^ value) & 0x43)
  1634. {
  1635. // Word/byte addressing changed or address substitution changed.
  1636. // Pixel buffer invalidated.
  1637. this.complete_replot();
  1638. }
  1639. }
  1640. break;
  1641. case 0x18:
  1642. dbg_log("3D5 / line compare write: " + h(value), LOG_VGA);
  1643. this.line_compare = (this.line_compare & 0x300) | value;
  1644. this.update_layers();
  1645. break;
  1646. default:
  1647. if(this.index_crtc < this.crtc.length)
  1648. {
  1649. this.crtc[this.index_crtc] = value;
  1650. }
  1651. dbg_log("3D5 / CRTC write " + h(this.index_crtc) + ": " + h(value), LOG_VGA);
  1652. }
  1653. };
  1654. VGAScreen.prototype.port3D5_read = function()
  1655. {
  1656. dbg_log("3D5 read " + h(this.index_crtc), LOG_VGA);
  1657. switch(this.index_crtc)
  1658. {
  1659. case 0x1:
  1660. return this.horizontal_display_enable_end;
  1661. case 0x2:
  1662. return this.horizontal_blank_start;
  1663. case 0x7:
  1664. return (this.vertical_display_enable_end >> 7 & 0x2) |
  1665. (this.vertical_blank_start >> 5 & 0x8) |
  1666. (this.line_compare >> 4 & 0x10) |
  1667. (this.vertical_display_enable_end >> 3 & 0x40);
  1668. case 0x8:
  1669. return this.preset_row_scan;
  1670. case 0x9:
  1671. return this.max_scan_line;
  1672. case 0xA:
  1673. return this.cursor_scanline_start;
  1674. case 0xB:
  1675. return this.cursor_scanline_end;
  1676. case 0xC:
  1677. return this.start_address & 0xFF;
  1678. case 0xD:
  1679. return this.start_address >> 8;
  1680. case 0xE:
  1681. return this.cursor_address >> 8;
  1682. case 0xF:
  1683. return this.cursor_address & 0xFF;
  1684. case 0x12:
  1685. return this.vertical_display_enable_end & 0xFF;
  1686. case 0x13:
  1687. return this.offset_register;
  1688. case 0x14:
  1689. return this.underline_location_register;
  1690. case 0x15:
  1691. return this.vertical_blank_start & 0xFF;
  1692. case 0x17:
  1693. return this.crtc_mode;
  1694. case 0x18:
  1695. return this.line_compare & 0xFF;
  1696. }
  1697. if(this.index_crtc < this.crtc.length)
  1698. {
  1699. return this.crtc[this.index_crtc];
  1700. }
  1701. else
  1702. {
  1703. return 0;
  1704. }
  1705. };
  1706. VGAScreen.prototype.port3DA_read = function()
  1707. {
  1708. dbg_log("3DA read - status 1 and clear attr index", LOG_VGA);
  1709. var value = this.port_3DA_value;
  1710. // Status register, bit 3 set by update_vertical_retrace
  1711. // during screen-fill-buffer
  1712. if(!this.graphical_mode)
  1713. {
  1714. // But screen-fill-buffer may not get triggered in text mode
  1715. // so toggle it manually here
  1716. if(this.port_3DA_value & 1)
  1717. {
  1718. this.port_3DA_value ^= 8;
  1719. }
  1720. this.port_3DA_value ^= 1;
  1721. }
  1722. else
  1723. {
  1724. this.port_3DA_value ^= 1;
  1725. this.port_3DA_value &= 1;
  1726. }
  1727. this.attribute_controller_index = -1;
  1728. return value;
  1729. };
  1730. VGAScreen.prototype.port1CE_write = function(value)
  1731. {
  1732. this.dispi_index = value;
  1733. };
  1734. VGAScreen.prototype.port1CF_write = function(value)
  1735. {
  1736. dbg_log("1CF / dispi write " + h(this.dispi_index) + ": " + h(value), LOG_VGA);
  1737. switch(this.dispi_index)
  1738. {
  1739. case 0:
  1740. if(value >= 0xB0C0 && value <= 0xB0C5)
  1741. {
  1742. this.svga_version = value;
  1743. }
  1744. else
  1745. {
  1746. dbg_log("Invalid version value: " + h(value), LOG_VGA);
  1747. }
  1748. break;
  1749. case 1:
  1750. this.svga_width = value;
  1751. if(this.svga_width > MAX_XRES)
  1752. {
  1753. dbg_log("svga_width reduced from " + this.svga_width + " to " + MAX_XRES, LOG_VGA);
  1754. this.svga_width = MAX_XRES;
  1755. }
  1756. break;
  1757. case 2:
  1758. this.svga_height = value;
  1759. if(this.svga_height > MAX_YRES)
  1760. {
  1761. dbg_log("svga_height reduced from " + this.svga_height + " to " + MAX_YRES, LOG_VGA);
  1762. this.svga_height = MAX_YRES;
  1763. }
  1764. break;
  1765. case 3:
  1766. this.svga_bpp = value;
  1767. break;
  1768. case 4:
  1769. // enable, options
  1770. this.svga_enabled = (value & 1) === 1;
  1771. this.dispi_enable_value = value;
  1772. break;
  1773. case 5:
  1774. dbg_log("SVGA bank offset: " + h(value << 16), LOG_VGA);
  1775. this.svga_bank_offset = value << 16;
  1776. break;
  1777. case 9:
  1778. // y offset
  1779. const offset = value * this.svga_width;
  1780. dbg_log("SVGA offset: " + h(offset) + " y=" + h(value), LOG_VGA);
  1781. if(this.svga_offset_y !== value)
  1782. {
  1783. this.svga_offset_y = value;
  1784. this.svga_offset = offset;
  1785. this.complete_redraw();
  1786. }
  1787. break;
  1788. default:
  1789. dbg_log("Unimplemented dispi write index: " + h(this.dispi_index), LOG_VGA);
  1790. }
  1791. if(this.svga_enabled && (!this.svga_width || !this.svga_height))
  1792. {
  1793. dbg_log("SVGA: disabled because of invalid width/height: " + this.svga_width + "x" + this.svga_height, LOG_VGA);
  1794. this.svga_enabled = false;
  1795. }
  1796. dbg_assert(this.svga_bpp !== 4, "unimplemented svga bpp: 4");
  1797. dbg_assert(this.svga_bpp === 4 || this.svga_bpp === 8 ||
  1798. this.svga_bpp === 15 || this.svga_bpp === 16 ||
  1799. this.svga_bpp === 24 || this.svga_bpp === 32,
  1800. "unexpected svga bpp: " + this.svga_bpp);
  1801. dbg_log("SVGA: enabled=" + this.svga_enabled + ", " + this.svga_width + "x" + this.svga_height + "x" + this.svga_bpp, LOG_VGA);
  1802. if(this.svga_enabled && this.dispi_index === 4)
  1803. {
  1804. this.set_size_graphical(this.svga_width, this.svga_height, this.svga_bpp, this.svga_width, this.svga_height);
  1805. this.bus.send("screen-set-mode", true);
  1806. this.graphical_mode = true;
  1807. this.graphical_mode_is_linear = true;
  1808. }
  1809. if(!this.svga_enabled)
  1810. {
  1811. this.svga_bank_offset = 0;
  1812. }
  1813. this.update_layers();
  1814. };
  1815. VGAScreen.prototype.port1CF_read = function()
  1816. {
  1817. dbg_log("1CF / dispi read " + h(this.dispi_index), LOG_VGA);
  1818. return this.svga_register_read(this.dispi_index);
  1819. };
  1820. VGAScreen.prototype.svga_register_read = function(n)
  1821. {
  1822. switch(n)
  1823. {
  1824. case 0:
  1825. return this.svga_version;
  1826. case 1:
  1827. return this.dispi_enable_value & 2 ? MAX_XRES : this.svga_width;
  1828. case 2:
  1829. return this.dispi_enable_value & 2 ? MAX_YRES : this.svga_height;
  1830. case 3:
  1831. return this.dispi_enable_value & 2 ? MAX_BPP : this.svga_bpp;
  1832. case 4:
  1833. return this.dispi_enable_value;
  1834. case 5:
  1835. return this.svga_bank_offset >>> 16;
  1836. case 6:
  1837. // virtual width
  1838. if(this.screen_width)
  1839. {
  1840. return this.screen_width;
  1841. }
  1842. else
  1843. {
  1844. return 1; // seabios/windows98 divide exception
  1845. }
  1846. break;
  1847. case 8:
  1848. // x offset
  1849. return 0;
  1850. case 9:
  1851. return this.svga_offset_y;
  1852. case 0x0A:
  1853. // memory size in 64 kilobyte banks
  1854. return this.vga_memory_size / VGA_BANK_SIZE | 0;
  1855. default:
  1856. dbg_log("Unimplemented dispi read index: " + h(this.dispi_index), LOG_VGA);
  1857. }
  1858. return 0xFF;
  1859. };
  1860. /**
  1861. * Transfers graphics from VGA Planes to the Pixel Buffer
  1862. * VGA Planes represent data stored on actual hardware.
  1863. * Pixel Buffer caches the 4-bit or 8-bit color indices for each pixel.
  1864. */
  1865. VGAScreen.prototype.vga_replot = function()
  1866. {
  1867. // Round to multiple of 8 towards extreme
  1868. var start = this.diff_plot_min & ~0xF;
  1869. var end = Math.min((this.diff_plot_max | 0xF), VGA_PIXEL_BUFFER_SIZE - 1);
  1870. var addr_shift = this.vga_addr_shift_count();
  1871. var addr_substitution = ~this.crtc_mode & 0x3;
  1872. var shift_mode = this.planar_mode & 0x60;
  1873. var pel_width = this.attribute_mode & 0x40;
  1874. for(var pixel_addr = start; pixel_addr <= end;)
  1875. {
  1876. var addr = pixel_addr >>> addr_shift;
  1877. if(addr_substitution)
  1878. {
  1879. var row = pixel_addr / this.virtual_width | 0;
  1880. var col = pixel_addr - this.virtual_width * row;
  1881. switch(addr_substitution)
  1882. {
  1883. case 0x1:
  1884. // Alternating rows using bit 13
  1885. // Assumes max scan line = 1
  1886. addr = (row & 0x1) << 13;
  1887. row >>>= 1;
  1888. break;
  1889. case 0x2:
  1890. // Alternating rows using bit 14
  1891. // Assumes max scan line = 3
  1892. addr = (row & 0x1) << 14;
  1893. row >>>= 1;
  1894. break;
  1895. case 0x3:
  1896. // Cycling through rows using bit 13 and 14
  1897. // Assumes max scan line = 3
  1898. addr = (row & 0x3) << 13;
  1899. row >>>= 2;
  1900. break;
  1901. }
  1902. addr |= (row * this.virtual_width + col >>> addr_shift) + this.start_address;
  1903. }
  1904. var byte0 = this.plane0[addr];
  1905. var byte1 = this.plane1[addr];
  1906. var byte2 = this.plane2[addr];
  1907. var byte3 = this.plane3[addr];
  1908. var shift_loads = new Uint8Array(8);
  1909. switch(shift_mode)
  1910. {
  1911. // Planar Shift Mode
  1912. // See http://www.osdever.net/FreeVGA/vga/vgaseq.htm
  1913. case 0x00:
  1914. // Shift these, so that the bits for the color are in
  1915. // the correct position in the for loop
  1916. byte0 <<= 0;
  1917. byte1 <<= 1;
  1918. byte2 <<= 2;
  1919. byte3 <<= 3;
  1920. for(var i = 7; i >= 0; i--)
  1921. {
  1922. shift_loads[7 - i] =
  1923. byte0 >> i & 1 |
  1924. byte1 >> i & 2 |
  1925. byte2 >> i & 4 |
  1926. byte3 >> i & 8;
  1927. }
  1928. break;
  1929. // Packed Shift Mode, aka Interleaved Shift Mode
  1930. // Video Modes 4h and 5h
  1931. case 0x20:
  1932. shift_loads[0] = (byte0 >> 6 & 0x3) | (byte2 >> 4 & 0xC);
  1933. shift_loads[1] = (byte0 >> 4 & 0x3) | (byte2 >> 2 & 0xC);
  1934. shift_loads[2] = (byte0 >> 2 & 0x3) | (byte2 >> 0 & 0xC);
  1935. shift_loads[3] = (byte0 >> 0 & 0x3) | (byte2 << 2 & 0xC);
  1936. shift_loads[4] = (byte1 >> 6 & 0x3) | (byte3 >> 4 & 0xC);
  1937. shift_loads[5] = (byte1 >> 4 & 0x3) | (byte3 >> 2 & 0xC);
  1938. shift_loads[6] = (byte1 >> 2 & 0x3) | (byte3 >> 0 & 0xC);
  1939. shift_loads[7] = (byte1 >> 0 & 0x3) | (byte3 << 2 & 0xC);
  1940. break;
  1941. // 256-Color Shift Mode
  1942. // Video Modes 13h and unchained 256 color
  1943. case 0x40:
  1944. case 0x60:
  1945. shift_loads[0] = byte0 >> 4 & 0xF;
  1946. shift_loads[1] = byte0 >> 0 & 0xF;
  1947. shift_loads[2] = byte1 >> 4 & 0xF;
  1948. shift_loads[3] = byte1 >> 0 & 0xF;
  1949. shift_loads[4] = byte2 >> 4 & 0xF;
  1950. shift_loads[5] = byte2 >> 0 & 0xF;
  1951. shift_loads[6] = byte3 >> 4 & 0xF;
  1952. shift_loads[7] = byte3 >> 0 & 0xF;
  1953. break;
  1954. }
  1955. if(pel_width)
  1956. {
  1957. // Assemble from two sets of 4 bits.
  1958. for(var i = 0, j = 0; i < 4; i++, pixel_addr++, j += 2)
  1959. {
  1960. this.pixel_buffer[pixel_addr] = (shift_loads[j] << 4) | shift_loads[j + 1];
  1961. }
  1962. }
  1963. else
  1964. {
  1965. for(var i = 0; i < 8; i++, pixel_addr++)
  1966. {
  1967. this.pixel_buffer[pixel_addr] = shift_loads[i];
  1968. }
  1969. }
  1970. }
  1971. };
  1972. /**
  1973. * Transfers graphics from Pixel Buffer to Destination Image Buffer.
  1974. * The 4-bit/8-bit color indices in the Pixel Buffer are passed through
  1975. * the internal palette (dac_map) and the DAC palette (vga256_palette) to
  1976. * obtain the final 32 bit color that the Canvas API uses.
  1977. */
  1978. VGAScreen.prototype.vga_redraw = function()
  1979. {
  1980. var start = this.diff_addr_min;
  1981. var end = Math.min(this.diff_addr_max, VGA_PIXEL_BUFFER_SIZE - 1);
  1982. const buffer = new Int32Array(this.cpu.wasm_memory.buffer, this.dest_buffet_offset, this.virtual_width * this.virtual_height);
  1983. var mask = 0xFF;
  1984. var colorset = 0x00;
  1985. if(this.attribute_mode & 0x80)
  1986. {
  1987. // Palette bits 5/4 select
  1988. mask &= 0xCF;
  1989. colorset |= this.color_select << 4 & 0x30;
  1990. }
  1991. if(this.attribute_mode & 0x40)
  1992. {
  1993. // 8 bit mode
  1994. for(var pixel_addr = start; pixel_addr <= end; pixel_addr++)
  1995. {
  1996. var color256 = (this.pixel_buffer[pixel_addr] & mask) | colorset;
  1997. var color = this.vga256_palette[color256];
  1998. buffer[pixel_addr] = color & 0xFF00 | color << 16 | color >> 16 | 0xFF000000;
  1999. }
  2000. }
  2001. else
  2002. {
  2003. // 4 bit mode
  2004. // Palette bits 7/6 select
  2005. mask &= 0x3F;
  2006. colorset |= this.color_select << 4 & 0xC0;
  2007. for(var pixel_addr = start; pixel_addr <= end; pixel_addr++)
  2008. {
  2009. var color16 = this.pixel_buffer[pixel_addr] & this.color_plane_enable;
  2010. var color256 = (this.dac_map[color16] & mask) | colorset;
  2011. var color = this.vga256_palette[color256];
  2012. buffer[pixel_addr] = color & 0xFF00 | color << 16 | color >> 16 | 0xFF000000;
  2013. }
  2014. }
  2015. };
  2016. VGAScreen.prototype.screen_fill_buffer = function()
  2017. {
  2018. if(!this.graphical_mode)
  2019. {
  2020. // text mode
  2021. // Update retrace behaviour anyway - programs waiting for signal before
  2022. // changing to graphical mode
  2023. this.update_vertical_retrace();
  2024. return;
  2025. }
  2026. if(this.image_data.data.byteLength === 0)
  2027. {
  2028. // wasm memory resized
  2029. const buffer = new Uint8ClampedArray(this.cpu.wasm_memory.buffer, this.dest_buffet_offset, 4 * this.virtual_width * this.virtual_height);
  2030. this.image_data = new ImageData(buffer, this.virtual_width, this.virtual_height);
  2031. this.update_layers();
  2032. }
  2033. if(this.svga_enabled)
  2034. {
  2035. let min_y = 0;
  2036. let max_y = this.svga_height;
  2037. if(this.svga_bpp === 8)
  2038. {
  2039. // XXX: Slow, should be ported to rust, but it doesn't have access to vga256_palette
  2040. // XXX: Doesn't take svga_offset into account
  2041. const buffer = new Int32Array(this.cpu.wasm_memory.buffer, this.dest_buffet_offset, this.screen_width * this.screen_height);
  2042. const svga_memory = new Uint8Array(this.cpu.wasm_memory.buffer, this.svga_memory.byteOffset, this.vga_memory_size);
  2043. for(var i = 0; i < buffer.length; i++)
  2044. {
  2045. var color = this.vga256_palette[svga_memory[i]];
  2046. buffer[i] = color & 0xFF00 | color << 16 | color >> 16 | 0xFF000000;
  2047. }
  2048. }
  2049. else
  2050. {
  2051. this.cpu.svga_fill_pixel_buffer(this.svga_bpp, this.svga_offset);
  2052. const bytes_per_pixel = this.svga_bpp === 15 ? 2 : this.svga_bpp / 8;
  2053. min_y = (((this.cpu.svga_dirty_bitmap_min_offset[0] / bytes_per_pixel | 0) - this.svga_offset) / this.svga_width | 0);
  2054. max_y = (((this.cpu.svga_dirty_bitmap_max_offset[0] / bytes_per_pixel | 0) - this.svga_offset) / this.svga_width | 0) + 1;
  2055. }
  2056. if(min_y < max_y)
  2057. {
  2058. min_y = Math.max(min_y, 0);
  2059. max_y = Math.min(max_y, this.svga_height);
  2060. this.bus.send("screen-fill-buffer-end", [{
  2061. image_data: this.image_data,
  2062. screen_x: 0, screen_y: min_y,
  2063. buffer_x: 0, buffer_y: min_y,
  2064. buffer_width: this.svga_width,
  2065. buffer_height: max_y - min_y,
  2066. }]);
  2067. }
  2068. }
  2069. else
  2070. {
  2071. this.vga_replot();
  2072. this.vga_redraw();
  2073. this.bus.send("screen-fill-buffer-end", this.layers);
  2074. }
  2075. this.reset_diffs();
  2076. this.update_vertical_retrace();
  2077. };