Advertisement
Guest User

Untitled

a guest
Feb 19th, 2020
474
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 41.42 KB | None | 0 0
  1. /*
  2. * tab:4
  3. *
  4. * modex.c - VGA mode X graphics routines
  5. *
  6. * "Copyright (c) 2004-2009 by Steven S. Lumetta."
  7. *
  8. * Permission to use, copy, modify, and distribute this software and its
  9. * documentation for any purpose, without fee, and without written agreement is
  10. * hereby granted, provided that the above copyright notice and the following
  11. * two paragraphs appear in all copies of this software.
  12. *
  13. * IN NO EVENT SHALL THE AUTHOR OR THE UNIVERSITY OF ILLINOIS BE LIABLE TO
  14. * ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
  15. * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
  16. * EVEN IF THE AUTHOR AND/OR THE UNIVERSITY OF ILLINOIS HAS BEEN ADVISED
  17. * OF THE POSSIBILITY OF SUCH DAMAGE.
  18. *
  19. * THE AUTHOR AND THE UNIVERSITY OF ILLINOIS SPECIFICALLY DISCLAIM ANY
  20. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  21. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
  22. * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND NEITHER THE AUTHOR NOR
  23. * THE UNIVERSITY OF ILLINOIS HAS ANY OBLIGATION TO PROVIDE MAINTENANCE,
  24. * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
  25. *
  26. * Author: Steve Lumetta
  27. * Version: 3
  28. * Creation Date: Fri Sep 10 09:59:17 2004
  29. * Filename: modex.c
  30. * History:
  31. * SL 1 Fri Sep 10 09:59:17 2004
  32. * First written.
  33. * SL 2 Sat Sep 12 16:41:45 2009
  34. * Integrated original release back into main code base.
  35. * SL 3 Sat Sep 12 17:58:20 2009
  36. * Added display re-enable to VGA blank routine and comments
  37. * on other VirtualPC->QEMU migration changes.
  38. */
  39.  
  40. #include <fcntl.h>
  41. #include <stdio.h>
  42. #include <string.h>
  43. #include <sys/io.h>
  44. #include <sys/mman.h>
  45. #include <unistd.h>
  46.  
  47. #include "blocks.h"
  48. #include "modex.h"
  49. #include "text.h"
  50.  
  51. /*
  52. * Calculate the image build buffer parameters. SCROLL_SIZE is the space
  53. * needed for one plane of an image. SCREEN_SIZE is the space needed for
  54. * all four planes. The extra +1 supports logical view x coordinates that
  55. * are not multiples of four. In these cases, some plane addresses are
  56. * shifted by 1 byte forward. The planes are stored in the build buffer
  57. * in reverse order to allow those planes that shift forward to do so
  58. * without running into planes that aren't shifted. For example, when
  59. * the leftmost x pixel in the logical view is 3 mod 4, planes 2, 1, and 0
  60. * are shifted forward, while plane 3 is not, so there is one unused byte
  61. * between the image of plane 3 and that of plane 2. BUILD_BUF_SIZE is
  62. * the size of the space allocated for building images. We add 20000 bytes
  63. * to reduce the number of memory copies required during scrolling.
  64. * Strictly speaking (try it), no extra space is necessary, but the minimum
  65. * means an extra 64kB memory copy with every scroll pixel. Finally,
  66. * BUILD_BASE_INIT places initial (or transferred) logical view in the
  67. * middle of the available buffer area.
  68. */
  69. #define SCROLL_SIZE (SCROLL_X_WIDTH * SCROLL_Y_DIM)
  70. #define SCREEN_SIZE (SCROLL_SIZE * 4 + 1)
  71. #define BUILD_BUF_SIZE (SCREEN_SIZE + 20000)
  72. #define BUILD_BASE_INIT ((BUILD_BUF_SIZE - SCREEN_SIZE) / 2)
  73.  
  74. /* Mode X and general VGA parameters */
  75. #define VID_MEM_SIZE 131072
  76. #define MODE_X_MEM_SIZE 65536
  77. #define NUM_SEQUENCER_REGS 5
  78. #define NUM_CRTC_REGS 25
  79. #define NUM_GRAPHICS_REGS 9
  80. #define NUM_ATTR_REGS 22
  81.  
  82. /* VGA register settings for mode X */
  83. static unsigned short mode_X_seq[NUM_SEQUENCER_REGS] = {
  84. 0x0100, 0x2101, 0x0F02, 0x0003, 0x0604
  85. };
  86.  
  87. // Here we modify 24th-indexed (6B), 7th-indexed (1F07 -> 1F07), and 9th-indexed (4109 -> 0109) addresses to contain the location for status bar
  88. static unsigned short mode_X_CRTC[NUM_CRTC_REGS] = {
  89. 0x5F00, 0x4F01, 0x5002, 0x8203, 0x5404, 0x8005, 0xBF06, 0x1F07,
  90. 0x0008, 0x0109, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
  91. 0x9C10, 0x8E11, 0x8F12, 0x2813, 0x0014, 0x9615, 0xB916, 0xE317,
  92. 0x6B18
  93. };
  94. // We modify the 16th-indexed (08 -> 28) address
  95. static unsigned char mode_X_attr[NUM_ATTR_REGS * 2] = {
  96. 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03,
  97. 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07,
  98. 0x28, 0x08, 0x09, 0x09, 0x0A, 0x0A, 0x0B, 0x0B,
  99. 0x0C, 0x0C, 0x0D, 0x0D, 0x0E, 0x0E, 0x0F, 0x0F,
  100. 0x10, 0x41, 0x11, 0x00, 0x12, 0x0F, 0x13, 0x00,
  101. 0x14, 0x00, 0x15, 0x00
  102. };
  103. static unsigned short mode_X_graphics[NUM_GRAPHICS_REGS] = {
  104. 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x4005, 0x0506, 0x0F07,
  105. 0xFF08
  106. };
  107.  
  108. /* VGA register settings for text mode 3 (color text) */
  109. static unsigned short text_seq[NUM_SEQUENCER_REGS] = {
  110. 0x0100, 0x2001, 0x0302, 0x0003, 0x0204
  111. };
  112. static unsigned short text_CRTC[NUM_CRTC_REGS] = {
  113. 0x5F00, 0x4F01, 0x5002, 0x8203, 0x5504, 0x8105, 0xBF06, 0x1F07,
  114. 0x0008, 0x4F09, 0x0D0A, 0x0E0B, 0x000C, 0x000D, 0x000E, 0x000F,
  115. 0x9C10, 0x8E11, 0x8F12, 0x2813, 0x1F14, 0x9615, 0xB916, 0xA317,
  116. 0xFF18
  117. };
  118. static unsigned char text_attr[NUM_ATTR_REGS * 2] = {
  119. 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03,
  120. 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07,
  121. 0x08, 0x08, 0x09, 0x09, 0x0A, 0x0A, 0x0B, 0x0B,
  122. 0x0C, 0x0C, 0x0D, 0x0D, 0x0E, 0x0E, 0x0F, 0x0F,
  123. 0x10, 0x0C, 0x11, 0x00, 0x12, 0x0F, 0x13, 0x08,
  124. 0x14, 0x00, 0x15, 0x00
  125. };
  126. static unsigned short text_graphics[NUM_GRAPHICS_REGS] = {
  127. 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x1005, 0x0E06, 0x0007,
  128. 0xFF08
  129. };
  130.  
  131. /* local functions--see function headers for details */
  132. static int open_memory_and_ports();
  133. static void VGA_blank(int blank_bit);
  134. static void set_seq_regs_and_reset(unsigned short table[NUM_SEQUENCER_REGS], unsigned char val);
  135. static void set_CRTC_registers(unsigned short table[NUM_CRTC_REGS]);
  136. static void set_attr_registers(unsigned char table[NUM_ATTR_REGS * 2]);
  137. static void set_graphics_registers(unsigned short table[NUM_GRAPHICS_REGS]);
  138. static void fill_palette();
  139. static void write_font_data();
  140. static void set_text_mode_3(int clear_scr);
  141. static void copy_image(unsigned char* img, unsigned short scr_addr);
  142.  
  143. /*
  144. * Images are built in this buffer, then copied to the video memory.
  145. * Copying to video memory with REP MOVSB is vastly faster than anything
  146. * else with emulation, probably because it is a single instruction
  147. * and translates to a native loop. It's also a pretty good technique
  148. * in normal machines (albeit not as elegant as some others for reducing
  149. * the number of video memory writes; unfortunately, these techniques
  150. * are slower in emulation...).
  151. *
  152. * The size allows the four plane images to move within an area of
  153. * about twice the size necessary (to reduce the need to deal with
  154. * the boundary conditions by moving the data within the buffer).
  155. *
  156. * Plane 3 is first, followed by 2, 1, and 0. The reverse ordering
  157. * is used because the logical address of 0 increases first; if plane
  158. * 0 were first, we would need a buffer byte to keep it from colliding
  159. * with plane 1 when plane 0 was offset by 1 from plane 1, i.e., when
  160. * displaying a one-pixel left shift.
  161. *
  162. * The memory fence (included when NDEBUG is not defined) allocates
  163. * the build buffer with extra space on each side. The extra space
  164. * is filled with magic numbers (something unlikely to be written in
  165. * error), and the fence areas are checked for those magic values at
  166. * the end of the program to detect array access bugs (writes past
  167. * the ends of the build buffer).
  168. */
  169. #ifndef NDEBUG
  170. #define MEM_FENCE_WIDTH 256
  171. #else
  172. #define MEM_FENCE_WIDTH 0
  173. #endif
  174. #define MEM_FENCE_MAGIC 0xF3
  175.  
  176. static unsigned char build[BUILD_BUF_SIZE + 2 * MEM_FENCE_WIDTH];
  177. static int img3_off; /* offset of upper left pixel */
  178. static unsigned char* img3; /* pointer to upper left pixel */
  179. static int show_x, show_y; /* logical view coordinates */
  180.  
  181. /* displayed video memory variables */
  182. static unsigned char* mem_image; /* pointer to start of video memory */
  183. static unsigned short target_img; /* offset of displayed screen image */
  184.  
  185. /*
  186. * functions provided by the caller to set_mode_X() and used to obtain
  187. * graphic images of lines (pixels) to be mapped into the build buffer
  188. * planes for display in mode X
  189. */
  190. static void (*horiz_line_fn)(int, int, unsigned char[SCROLL_X_DIM]);
  191. static void (*vert_line_fn)(int, int, unsigned char[SCROLL_Y_DIM]);
  192.  
  193. /*
  194. * macro used to target a specific video plane or planes when writing
  195. * to video memory in mode X; bits 8-11 in the mask_hi_bits enable writes
  196. * to planes 0-3, respectively
  197. */
  198. #define SET_WRITE_MASK(mask_hi_bits) \
  199. do { \
  200. asm volatile (" \n\
  201. movw $0x03C4, %%dx /* set write mask */ \n\
  202. movb $0x02, %b0 \n\
  203. outw %w0, (%%dx) \n\
  204. " \
  205. : /* no outputs */ \
  206. : "a"((mask_hi_bits)) \
  207. : "edx", "memory" \
  208. ); \
  209. } while (0)
  210.  
  211. /* macro used to write a byte to a port */
  212. #define OUTB(port, val) \
  213. do { \
  214. asm volatile ("outb %b1, (%w0)" \
  215. : /* no outputs */ \
  216. : "d"((port)), "a"((val)) \
  217. : "memory", "cc" \
  218. ); \
  219. } while (0)
  220.  
  221. /* macro used to write two bytes to two consecutive ports */
  222. #define OUTW(port, val) \
  223. do { \
  224. asm volatile ("outw %w1, (%w0)" \
  225. : /* no outputs */ \
  226. : "d"((port)), "a"((val)) \
  227. : "memory", "cc" \
  228. ); \
  229. } while (0)
  230.  
  231. /* macro used to write an array of two-byte values to two consecutive ports */
  232. #define REP_OUTSW(port, source, count) \
  233. do { \
  234. asm volatile (" \n\
  235. 1: movw 0(%1), %%ax \n\
  236. outw %%ax, (%w2) \n\
  237. addl $2, %1 \n\
  238. decl %0 \n\
  239. jne 1b \n\
  240. " \
  241. : /* no outputs */ \
  242. : "c"((count)), "S"((source)), "d"((port)) \
  243. : "eax", "memory", "cc" \
  244. ); \
  245. } while (0)
  246.  
  247. /* macro used to write an array of one-byte values to two consecutive ports */
  248. #define REP_OUTSB(port, source, count) \
  249. do { \
  250. asm volatile (" \n\
  251. 1: movb 0(%1), %%al \n\
  252. outb %%al, (%w2) \n\
  253. incl %1 \n\
  254. decl %0 \n\
  255. jne 1b \n\
  256. " \
  257. : /* no outputs */ \
  258. : "c"((count)), "S"((source)), "d"((port)) \
  259. : "eax", "memory", "cc" \
  260. ); \
  261. } while (0)
  262.  
  263. /*
  264. * set_mode_X
  265. * DESCRIPTION: Puts the VGA into mode X.
  266. * INPUTS: horiz_fill_fn -- this function is used as a callback (by
  267. * draw_horiz_line) to obtain a graphical
  268. * image of a particular logical line for
  269. * drawing to the build buffer
  270. * vert_fill_fn -- this function is used as a callback (by
  271. * draw_vert_line) to obtain a graphical
  272. * image of a particular logical line for
  273. * drawing to the build buffer
  274. * OUTPUTS: none
  275. * RETURN VALUE: 0 on success, -1 on failure
  276. * SIDE EFFECTS: initializes the logical view window; maps video memory
  277. * and obtains permission for VGA ports; clears video memory
  278. */
  279. int set_mode_X(void (*horiz_fill_fn)(int, int, unsigned char[SCROLL_X_DIM]),
  280. void (*vert_fill_fn)(int, int, unsigned char[SCROLL_Y_DIM])) {
  281.  
  282. /* loop index for filling memory fence with magic numbers */
  283. int i;
  284.  
  285. /*
  286. * Record callback functions for obtaining horizontal and vertical
  287. * line images.
  288. */
  289. if (horiz_fill_fn == NULL || vert_fill_fn == NULL)
  290. return -1;
  291. horiz_line_fn = horiz_fill_fn;
  292. vert_line_fn = vert_fill_fn;
  293.  
  294. /* Initialize the logical view window to position (0,0). */
  295. show_x = show_y = 0;
  296. img3_off = BUILD_BASE_INIT;
  297. img3 = build + img3_off + MEM_FENCE_WIDTH;
  298.  
  299. /* Set up the memory fence on the build buffer. */
  300. for (i = 0; i < MEM_FENCE_WIDTH; i++) {
  301. build[i] = MEM_FENCE_MAGIC;
  302. build[BUILD_BUF_SIZE + MEM_FENCE_WIDTH + i] = MEM_FENCE_MAGIC;
  303. }
  304.  
  305. /* One display page goes at the start of video memory. */
  306. target_img = 0x0000;
  307.  
  308. /* Map video memory and obtain permission for VGA port access. */
  309. if (open_memory_and_ports() == -1)
  310. return -1;
  311.  
  312. /*
  313. * The code below was produced by recording a call to set mode 0013h
  314. * with display memory clearing and a windowed frame buffer, then
  315. * modifying the code to set mode X instead. The code was then
  316. * generalized into functions...
  317. *
  318. * modifications from mode 13h to mode X include...
  319. * Sequencer Memory Mode Register: 0x0E to 0x06 (0x3C4/0x04)
  320. * Underline Location Register : 0x40 to 0x00 (0x3D4/0x14)
  321. * CRTC Mode Control Register : 0xA3 to 0xE3 (0x3D4/0x17)
  322. */
  323.  
  324. VGA_blank(1); /* blank the screen */
  325. set_seq_regs_and_reset(mode_X_seq, 0x63); /* sequencer registers */
  326. set_CRTC_registers(mode_X_CRTC); /* CRT control registers */
  327. set_attr_registers(mode_X_attr); /* attribute registers */
  328. set_graphics_registers(mode_X_graphics); /* graphics registers */
  329. fill_palette(); /* palette colors */
  330. clear_screens(); /* zero video memory */
  331. VGA_blank(0); /* unblank the screen */
  332.  
  333. /* Return success. */
  334. return 0;
  335. }
  336.  
  337. /*
  338. * clear_mode_X
  339. * DESCRIPTION: Puts the VGA into text mode 3 (color text).
  340. * INPUTS: none
  341. * OUTPUTS: none
  342. * RETURN VALUE: none
  343. * SIDE EFFECTS: restores font data to video memory; clears screens;
  344. * unmaps video memory; checks memory fence integrity
  345. */
  346. void clear_mode_X() {
  347. /* loop index for checking memory fence */
  348. int i;
  349.  
  350. /* Put VGA into text mode, restore font data, and clear screens. */
  351. set_text_mode_3(1);
  352.  
  353. /* Unmap video memory. */
  354. (void)munmap(mem_image, VID_MEM_SIZE);
  355.  
  356. /* Check validity of build buffer memory fence. Report breakage. */
  357. for (i = 0; i < MEM_FENCE_WIDTH; i++) {
  358. if (build[i] != MEM_FENCE_MAGIC) {
  359. puts("lower build fence was broken");
  360. break;
  361. }
  362. }
  363. for (i = 0; i < MEM_FENCE_WIDTH; i++) {
  364. if (build[BUILD_BUF_SIZE + MEM_FENCE_WIDTH + i] != MEM_FENCE_MAGIC) {
  365. puts("upper build fence was broken");
  366. break;
  367. }
  368. }
  369. }
  370.  
  371. /*
  372. * set_view_window
  373. * DESCRIPTION: Set the logical view window, moving its location within
  374. * the build buffer if necessary to keep all on-screen data
  375. * in the build buffer. If the location within the build
  376. * buffer moves, this function copies all data from the old
  377. * window that are within the new screen to the appropriate
  378. * new location, so only data not previously on the screen
  379. * must be drawn before calling show_screen.
  380. * INPUTS: (scr_x,scr_y) -- new upper left pixel of logical view window
  381. * OUTPUTS: none
  382. * RETURN VALUE: none
  383. * SIDE EFFECTS: may shift position of logical view window within build
  384. * buffer
  385. */
  386. void set_view_window(int scr_x, int scr_y) {
  387. int old_x, old_y; /* old position of logical view window */
  388. int start_x, start_y; /* starting position for copying from old to new */
  389. int end_x, end_y; /* ending position for copying from old to new */
  390. int start_off; /* offset of copy start relative to old build */
  391. /* buffer start position */
  392. int length; /* amount of data to be copied */
  393. int i; /* copy loop index */
  394. unsigned char* start_addr; /* starting memory address of copy */
  395. unsigned char* target_addr; /* destination memory address for copy */
  396.  
  397. /* Record the old position. */
  398. old_x = show_x;
  399. old_y = show_y;
  400.  
  401. /* Keep track of the new view window. */
  402. show_x = scr_x;
  403. show_y = scr_y;
  404.  
  405. /*
  406. * If the new view window fits within the boundaries of the build
  407. * buffer, we need move nothing around.
  408. */
  409. if (img3_off + (scr_x >> 2) + scr_y * SCROLL_X_WIDTH >= 0 &&
  410. img3_off + 3 * SCROLL_SIZE +
  411. ((scr_x + SCROLL_X_DIM - 1) >> 2) +
  412. (scr_y + SCROLL_Y_DIM - 1) * SCROLL_X_WIDTH < BUILD_BUF_SIZE)
  413. return;
  414.  
  415. /*
  416. * If the new screen does not overlap at all with the old screen, none
  417. * of the old data need to be saved, and we can simply reposition the
  418. * valid window of the build buffer in the middle of that buffer.
  419. */
  420. if (scr_x <= old_x - SCROLL_X_DIM || scr_x >= old_x + SCROLL_X_DIM ||
  421. scr_y <= old_y - SCROLL_Y_DIM || scr_y >= old_y + SCROLL_Y_DIM) {
  422. img3_off = BUILD_BASE_INIT - (scr_x >> 2) - scr_y * SCROLL_X_WIDTH;
  423. img3 = build + img3_off + MEM_FENCE_WIDTH;
  424. return;
  425. }
  426.  
  427. /*
  428. * Any still-visible portion of the old screen should be retained.
  429. * Rather than clipping exactly, we copy all contiguous data between
  430. * a clipped starting point to a clipped ending point (which may
  431. * include non-visible data).
  432. *
  433. * The starting point is the maximum (x,y) coordinates between the
  434. * new and old screens. The ending point is the minimum (x,y)
  435. * coordinates between the old and new screens (offset by the screen
  436. * size).
  437. */
  438. if (scr_x > old_x) {
  439. start_x = scr_x;
  440. end_x = old_x;
  441. }
  442. else {
  443. start_x = old_x;
  444. end_x = scr_x;
  445. }
  446. end_x += SCROLL_X_DIM - 1;
  447. if (scr_y > old_y) {
  448. start_y = scr_y;
  449. end_y = old_y;
  450. }
  451. else {
  452. start_y = old_y;
  453. end_y = scr_y;
  454. }
  455. end_y += SCROLL_Y_DIM - 1;
  456.  
  457. /*
  458. * We now calculate the starting and ending addresses for the copy
  459. * as well as the new offsets for use with the build buffer. The
  460. * length to be copied is basically the ending offset minus the starting
  461. * offset plus one (plus the three screens in between planes 3 and 0).
  462. */
  463. start_off = (start_x >> 2) + start_y * SCROLL_X_WIDTH;
  464. start_addr = img3 + start_off;
  465. length = (end_x >> 2) + end_y * SCROLL_X_WIDTH + 1 - start_off + 3 * SCROLL_SIZE;
  466. img3_off = BUILD_BASE_INIT - (show_x >> 2) - show_y * SCROLL_X_WIDTH;
  467. img3 = build + img3_off + MEM_FENCE_WIDTH;
  468. target_addr = img3 + start_off;
  469.  
  470. /*
  471. * Copy the relevant portion of the screen from the old location to the
  472. * new one. The areas may overlap, so copy direction is important.
  473. * (You should be able to explain why!)
  474. */
  475. if (start_addr < target_addr)
  476. for (i = length; i-- > 0; )
  477. target_addr[i] = start_addr[i];
  478. else
  479. for (i = 0; i < length; i++)
  480. target_addr[i] = start_addr[i];
  481. }
  482.  
  483. /*
  484. * show_screen
  485. * DESCRIPTION: Show the logical view window on the video display.
  486. * INPUTS: none
  487. * OUTPUTS: none
  488. * RETURN VALUE: none
  489. * SIDE EFFECTS: copies from the build buffer to video memory;
  490. * shifts the VGA display source to point to the new image
  491. */
  492. void show_screen() {
  493. unsigned char* addr; /* source address for copy */
  494. int p_off; /* plane offset of first display plane */
  495. int i; /* loop index over video planes */
  496.  
  497. /*
  498. * Calculate offset of build buffer plane to be mapped into plane 0
  499. * of display.
  500. */
  501. p_off = (3 - (show_x & 3));
  502.  
  503. /* Switch to the other target screen in video memory. */
  504. target_img ^= 0x4000;
  505.  
  506. /* Calculate the source address. */
  507. addr = img3 + (show_x >> 2) + show_y * SCROLL_X_WIDTH;
  508.  
  509. /* Draw to each plane in the video memory. */
  510. for (i = 0; i < 4; i++) {
  511. SET_WRITE_MASK(1 << (i + 8));
  512. copy_image(addr + ((p_off - i + 4) & 3) * SCROLL_SIZE + (p_off < i), target_img);
  513. }
  514.  
  515. /*
  516. * Change the VGA registers to point the top left of the screen
  517. * to the video memory that we just filled.
  518. */
  519. OUTW(0x03D4, (target_img & 0xFF00) | 0x0C);
  520. OUTW(0x03D4, ((target_img & 0x00FF) << 8) | 0x0D);
  521. }
  522.  
  523. /*
  524. * clear_screens
  525. * DESCRIPTION: Fills the video memory with zeroes.
  526. * INPUTS: none
  527. * OUTPUTS: none
  528. * RETURN VALUE: none
  529. * SIDE EFFECTS: fills all 256kB of VGA video memory with zeroes
  530. */
  531. void clear_screens() {
  532. /* Write to all four planes at once. */
  533. SET_WRITE_MASK(0x0F00);
  534.  
  535. /* Set 64kB to zero (times four planes = 256kB). */
  536. memset(mem_image, 0, MODE_X_MEM_SIZE);
  537. }
  538.  
  539. /*
  540. * draw_full_block
  541. * DESCRIPTION: Draw a BLOCK_X_DIM x BLOCK_Y_DIM block at absolute
  542. * coordinates. Mask any portion of the block not inside
  543. * the logical view window.
  544. * INPUTS: (pos_x,pos_y) -- coordinates of upper left corner of block
  545. * blk -- image data for block (one byte per pixel, as a C array
  546. * of dimensions [BLOCK_Y_DIM][BLOCK_X_DIM])
  547. * OUTPUTS: none
  548. * RETURN VALUE: none
  549. * SIDE EFFECTS: draws into the build buffer
  550. */
  551. void draw_full_block(int pos_x, int pos_y, unsigned char* blk) {
  552. int dx, dy; /* loop indices for x and y traversal of block */
  553. int x_left, x_right; /* clipping limits in horizontal dimension */
  554. int y_top, y_bottom; /* clipping limits in vertical dimension */
  555.  
  556. /* If block is completely off-screen, we do nothing. */
  557. if (pos_x + BLOCK_X_DIM <= show_x || pos_x >= show_x + SCROLL_X_DIM ||
  558. pos_y + BLOCK_Y_DIM <= show_y || pos_y >= show_y + SCROLL_Y_DIM)
  559. return;
  560.  
  561. /* Clip any pixels falling off the left side of screen. */
  562. if ((x_left = show_x - pos_x) < 0)
  563. x_left = 0;
  564. /* Clip any pixels falling off the right side of screen. */
  565. if ((x_right = show_x + SCROLL_X_DIM - pos_x) > BLOCK_X_DIM)
  566. x_right = BLOCK_X_DIM;
  567. /* Skip the first x_left pixels in both screen position and block data. */
  568. pos_x += x_left;
  569. blk += x_left;
  570.  
  571. /*
  572. * Adjust x_right to hold the number of pixels to be drawn, and x_left
  573. * to hold the amount to skip between rows in the block, which is the
  574. * sum of the original left clip and (BLOCK_X_DIM - the original right
  575. * clip).
  576. */
  577. x_right -= x_left;
  578. x_left = BLOCK_X_DIM - x_right;
  579.  
  580. /* Clip any pixels falling off the top of the screen. */
  581. if ((y_top = show_y - pos_y) < 0)
  582. y_top = 0;
  583. /* Clip any pixels falling off the bottom of the screen. */
  584. if ((y_bottom = show_y + SCROLL_Y_DIM - pos_y) > BLOCK_Y_DIM)
  585. y_bottom = BLOCK_Y_DIM;
  586. /*
  587. * Skip the first y_left pixel in screen position and the first
  588. * y_left rows of pixels in the block data.
  589. */
  590. pos_y += y_top;
  591. blk += y_top * BLOCK_X_DIM;
  592. /* Adjust y_bottom to hold the number of pixel rows to be drawn. */
  593. y_bottom -= y_top;
  594.  
  595. /* Draw the clipped image. */
  596. for (dy = 0; dy < y_bottom; dy++, pos_y++) {
  597. for (dx = 0; dx < x_right; dx++, pos_x++, blk++)
  598. *(img3 + (pos_x >> 2) + pos_y * SCROLL_X_WIDTH +
  599. (3 - (pos_x & 3)) * SCROLL_SIZE) = *blk;
  600. pos_x -= x_right;
  601. blk += x_left;
  602. }
  603. }
  604.  
  605. /*
  606. * The functions inside the preprocessor block below rely on functions
  607. * in maze.c to generate graphical images of the maze. These functions
  608. * are neither available nor necessary for the text restoration program
  609. * based on this file, and are omitted to simplify linking that program.
  610. */
  611. #ifndef TEXT_RESTORE_PROGRAM
  612.  
  613. /*
  614. * draw_vert_line
  615. * DESCRIPTION: Draw a vertical map line into the build buffer. The
  616. * line should be offset from the left side of the logical
  617. * view window screen by the given number of pixels.
  618. * INPUTS: x -- the 0-based pixel column number of the line to be drawn
  619. * within the logical view window (equivalent to the number
  620. * of pixels from the leftmost pixel to the line to be
  621. * drawn)
  622. * OUTPUTS: none
  623. * RETURN VALUE: Returns 0 on success. If x is outside of the valid
  624. * SCROLL range, the function returns -1.
  625. * SIDE EFFECTS: draws into the build buffer
  626. */
  627. int draw_vert_line(int x) {
  628. /* to be written... */
  629. unsigned char buf[SCROLL_Y_DIM]; /* buffer for graphical image of line */
  630. unsigned char* addr; /* address of first pixel in build */
  631. /* buffer (without plane offset) */
  632. int p_off; /* offset of plane of first pixel */
  633. int i; /* loop index over pixels */
  634.  
  635. /* Check whether requested line falls in the logical view window. */
  636. if (x < 0 || x >= SCROLL_X_DIM)
  637. return -1;
  638.  
  639. /* Adjust x to the logical row value. */
  640. x += show_x;
  641.  
  642. /* Get the image of the line. */
  643. (*vert_line_fn) (x, show_y, buf);
  644.  
  645. /* Calculate starting address in build buffer. */
  646. addr = img3 + (x >> 2) + show_y * SCROLL_X_WIDTH;
  647.  
  648. /* Calculate plane offset of first pixel. */
  649. p_off = (3 - (x & 3));
  650.  
  651. /* Copy image data into appropriate planes in build buffer. */
  652. for (i = 0; i < SCROLL_Y_DIM; i++) {
  653. addr[p_off * SCROLL_SIZE + i * SCROLL_X_WIDTH] = buf[i];
  654. }
  655.  
  656. /* Return success. */
  657. return 0;
  658. }
  659.  
  660. /*
  661. * draw_horiz_line
  662. * DESCRIPTION: Draw a horizontal map line into the build buffer. The
  663. * line should be offset from the top of the logical view
  664. * window screen by the given number of pixels.
  665. * INPUTS: y -- the 0-based pixel row number of the line to be drawn
  666. * within the logical view window (equivalent to the number
  667. * of pixels from the top pixel to the line to be drawn)
  668. * OUTPUTS: none
  669. * RETURN VALUE: Returns 0 on success. If y is outside of the valid
  670. * SCROLL range, the function returns -1.
  671. * SIDE EFFECTS: draws into the build buffer
  672. */
  673. int draw_horiz_line(int y) {
  674. unsigned char buf[SCROLL_X_DIM]; /* buffer for graphical image of line */
  675. unsigned char* addr; /* address of first pixel in build */
  676. /* buffer (without plane offset) */
  677. int p_off; /* offset of plane of first pixel */
  678. int i; /* loop index over pixels */
  679.  
  680. /* Check whether requested line falls in the logical view window. */
  681. if (y < 0 || y >= SCROLL_Y_DIM)
  682. return -1;
  683.  
  684. /* Adjust y to the logical row value. */
  685. y += show_y;
  686.  
  687. /* Get the image of the line. */
  688. (*horiz_line_fn) (show_x, y, buf);
  689.  
  690. /* Calculate starting address in build buffer. */
  691. addr = img3 + (show_x >> 2) + y * SCROLL_X_WIDTH;
  692.  
  693. /* Calculate plane offset of first pixel. */
  694. p_off = (3 - (show_x & 3));
  695.  
  696. /* Copy image data into appropriate planes in build buffer. */
  697. for (i = 0; i < SCROLL_X_DIM; i++) {
  698. addr[p_off * SCROLL_SIZE] = buf[i];
  699. if (--p_off < 0) {
  700. p_off = 3;
  701. addr++;
  702. }
  703. }
  704.  
  705. /* Return success. */
  706. return 0;
  707. }
  708.  
  709. #endif /* !defined(TEXT_RESTORE_PROGRAM) */
  710.  
  711. /*
  712. * open_memory_and_ports
  713. * DESCRIPTION: Map video memory into our address space; obtain permission
  714. * to access VGA ports.
  715. * INPUTS: none
  716. * OUTPUTS: none
  717. * RETURN VALUE: 0 on success, -1 on failure
  718. * SIDE EFFECTS: prints an error message to stdout on failure
  719. */
  720. static int open_memory_and_ports() {
  721. int mem_fd; /* file descriptor for physical memory image */
  722.  
  723. /* Obtain permission to access ports 0x03C0 through 0x03DA. */
  724. if (ioperm(0x03C0, 0x03DA - 0x03C0 + 1, 1) == -1) {
  725. perror("set port permissions");
  726. return -1;
  727. }
  728.  
  729. /* Open file to access physical memory. */
  730. if ((mem_fd = open("/dev/mem", O_RDWR)) == -1) {
  731. perror("open /dev/mem");
  732. return -1;
  733. }
  734.  
  735. /* Map video memory (0xA0000 - 0xBFFFF) into our address space. */
  736. if ((mem_image = mmap(0, VID_MEM_SIZE, PROT_READ | PROT_WRITE,
  737. MAP_SHARED, mem_fd, 0xA0000)) == MAP_FAILED) {
  738. perror("mmap video memory");
  739. return -1;
  740. }
  741.  
  742. /* Close /dev/mem file descriptor and return success. */
  743. (void)close(mem_fd);
  744. return 0;
  745. }
  746.  
  747. /*
  748. * VGA_blank
  749. * DESCRIPTION: Blank or unblank the VGA display.
  750. * INPUTS: blank_bit -- set to 1 to blank, 0 to unblank
  751. * OUTPUTS: none
  752. * RETURN VALUE: none
  753. * SIDE EFFECTS: none
  754. */
  755. static void VGA_blank(int blank_bit) {
  756. /*
  757. * Move blanking bit into position for VGA sequencer register
  758. * (index 1).
  759. */
  760. blank_bit = ((blank_bit & 1) << 5);
  761.  
  762. asm volatile (" \n\
  763. movb $0x01, %%al /* Set sequencer index to 1. */ \n\
  764. movw $0x03C4, %%dx \n\
  765. outb %%al, (%%dx) \n\
  766. incw %%dx \n\
  767. inb (%%dx), %%al /* Read old value. */ \n\
  768. andb $0xDF, %%al /* Calculate new value. */ \n\
  769. orl %0, %%eax \n\
  770. outb %%al, (%%dx) /* Write new value. */ \n\
  771. movw $0x03DA, %%dx /* Enable display (0x20->P[0x3C0]) */ \n\
  772. inb (%%dx), %%al /* Set attr reg state to index. */ \n\
  773. movw $0x03C0, %%dx /* Write index 0x20 to enable. */ \n\
  774. movb $0x20, %%al \n\
  775. outb %%al, (%%dx) \n\
  776. "
  777. :
  778. : "g"(blank_bit)
  779. : "eax", "edx", "memory"
  780. );
  781. }
  782.  
  783. /*
  784. * set_seq_regs_and_reset
  785. * DESCRIPTION: Set VGA sequencer registers and miscellaneous output
  786. * register; array of registers should force a reset of
  787. * the VGA sequencer, which is restored to normal operation
  788. * after a brief delay.
  789. * INPUTS: table -- table of sequencer register values to use
  790. * val -- value to which miscellaneous output register should be set
  791. * OUTPUTS: none
  792. * RETURN VALUE: none
  793. * SIDE EFFECTS: none
  794. */
  795. static void set_seq_regs_and_reset(unsigned short table[NUM_SEQUENCER_REGS], unsigned char val) {
  796. /*
  797. * Dump table of values to sequencer registers. Includes forced reset
  798. * as well as video blanking.
  799. */
  800. REP_OUTSW(0x03C4, table, NUM_SEQUENCER_REGS);
  801.  
  802. /* Delay a bit... */
  803. {volatile int ii; for (ii = 0; ii < 10000; ii++);}
  804.  
  805. /* Set VGA miscellaneous output register. */
  806. OUTB(0x03C2, val);
  807.  
  808. /* Turn sequencer on (array values above should always force reset). */
  809. OUTW(0x03C4, 0x0300);
  810. }
  811.  
  812. /*
  813. * set_CRTC_registers
  814. * DESCRIPTION: Set VGA cathode ray tube controller (CRTC) registers.
  815. * INPUTS: table -- table of CRTC register values to use
  816. * OUTPUTS: none
  817. * RETURN VALUE: none
  818. * SIDE EFFECTS: none
  819. */
  820. static void set_CRTC_registers(unsigned short table[NUM_CRTC_REGS]) {
  821. /* clear protection bit to enable write access to first few registers */
  822. OUTW(0x03D4, 0x0011);
  823. REP_OUTSW(0x03D4, table, NUM_CRTC_REGS);
  824. }
  825.  
  826. /*
  827. * set_attr_registers
  828. * DESCRIPTION: Set VGA attribute registers. Attribute registers use
  829. * a single port and are thus written as a sequence of bytes
  830. * rather than a sequence of words.
  831. * INPUTS: table -- table of attribute register values to use
  832. * OUTPUTS: none
  833. * RETURN VALUE: none
  834. * SIDE EFFECTS: none
  835. */
  836. static void set_attr_registers(unsigned char table[NUM_ATTR_REGS * 2]) {
  837. /* Reset attribute register to write index next rather than data. */
  838. asm volatile ("inb (%%dx),%%al"
  839. :
  840. : "d"(0x03DA)
  841. : "eax", "memory"
  842. );
  843. REP_OUTSB(0x03C0, table, NUM_ATTR_REGS * 2);
  844. }
  845.  
  846. /*
  847. * set_graphics_registers
  848. * DESCRIPTION: Set VGA graphics registers.
  849. * INPUTS: table -- table of graphics register values to use
  850. * OUTPUTS: none
  851. * RETURN VALUE: none
  852. * SIDE EFFECTS: none
  853. */
  854. static void set_graphics_registers(unsigned short table[NUM_GRAPHICS_REGS]) {
  855. REP_OUTSW(0x03CE, table, NUM_GRAPHICS_REGS);
  856. }
  857.  
  858. /*
  859. * fill_palette
  860. * DESCRIPTION: Fill VGA palette with necessary colors for the maze game.
  861. * Only the first 64 (of 256) colors are written.
  862. * INPUTS: none
  863. * OUTPUTS: none
  864. * RETURN VALUE: none
  865. * SIDE EFFECTS: changes the first 64 palette colors
  866. */
  867. static void fill_palette() {
  868. /* 6-bit RGB (red, green, blue) values for first 64 colors */
  869. static unsigned char palette_RGB[64][3] = {
  870. { 0x00, 0x00, 0x00 },{ 0x00, 0x00, 0x2A }, /* palette 0x00 - 0x0F */
  871. { 0x00, 0x2A, 0x00 },{ 0x00, 0x2A, 0x2A }, /* basic VGA colors */
  872. { 0x2A, 0x00, 0x00 },{ 0x2A, 0x00, 0x2A },
  873. { 0x2A, 0x15, 0x00 },{ 0x2A, 0x2A, 0x2A },
  874. { 0x15, 0x15, 0x15 },{ 0x15, 0x15, 0x3F },
  875. { 0x15, 0x3F, 0x15 },{ 0x15, 0x3F, 0x3F },
  876. { 0x3F, 0x15, 0x15 },{ 0x3F, 0x15, 0x3F },
  877. { 0x3F, 0x3F, 0x15 },{ 0x3F, 0x3F, 0x3F },
  878. { 0x00, 0x00, 0x00 },{ 0x05, 0x05, 0x05 }, /* palette 0x10 - 0x1F */
  879. { 0x08, 0x08, 0x08 },{ 0x0B, 0x0B, 0x0B }, /* VGA grey scale */
  880. { 0x0E, 0x0E, 0x0E },{ 0x11, 0x11, 0x11 },
  881. { 0x14, 0x14, 0x14 },{ 0x18, 0x18, 0x18 },
  882. { 0x1C, 0x1C, 0x1C },{ 0x20, 0x20, 0x20 },
  883. { 0x24, 0x24, 0x24 },{ 0x28, 0x28, 0x28 },
  884. { 0x2D, 0x2D, 0x2D },{ 0x32, 0x32, 0x32 },
  885. { 0x38, 0x38, 0x38 },{ 0x3F, 0x3F, 0x3F },
  886. { 0x3F, 0x3F, 0x3F },{ 0x3F, 0x3F, 0x3F }, /* palette 0x20 - 0x2F */
  887. { 0x00, 0x00, 0x3F },{ 0x00, 0x00, 0x00 }, /* wall and player colors */
  888. { 0x00, 0x00, 0x00 },{ 0x00, 0x00, 0x00 },
  889. { 0x00, 0x00, 0x00 },{ 0x00, 0x00, 0x00 },
  890. { 0x00, 0x00, 0x00 },{ 0x00, 0x00, 0x00 },
  891. { 0x00, 0x00, 0x00 },{ 0x00, 0x00, 0x00 },
  892. { 0x00, 0x00, 0x00 },{ 0x00, 0x00, 0x00 },
  893. { 0x00, 0x00, 0x00 },{ 0x00, 0x00, 0x00 },
  894. { 0x10, 0x08, 0x00 },{ 0x18, 0x0C, 0x00 }, /* palette 0x30 - 0x3F */
  895. { 0x20, 0x10, 0x00 },{ 0x28, 0x14, 0x00 }, /* browns for maze floor */
  896. { 0x30, 0x18, 0x00 },{ 0x38, 0x1C, 0x00 },
  897. { 0x3F, 0x20, 0x00 },{ 0x3F, 0x20, 0x10 },
  898. { 0x20, 0x18, 0x10 },{ 0x28, 0x1C, 0x10 },
  899. { 0x3F, 0x20, 0x10 },{ 0x38, 0x24, 0x10 },
  900. { 0x3F, 0x28, 0x10 },{ 0x3F, 0x2C, 0x10 },
  901. { 0x3F, 0x30, 0x10 },{ 0x3F, 0x20, 0x10 }
  902. };
  903.  
  904. /* Start writing at color 0. */
  905. OUTB(0x03C8, 0x00);
  906.  
  907. /* Write all 64 colors from array. */
  908. REP_OUTSB(0x03C9, palette_RGB, 64 * 3);
  909. }
  910.  
  911. /*
  912. * write_font_data
  913. * DESCRIPTION: Copy font data into VGA memory, changing and restoring
  914. * VGA register values in order to do so.
  915. * INPUTS: none
  916. * OUTPUTS: none
  917. * RETURN VALUE: none
  918. * SIDE EFFECTS: leaves VGA registers in final text mode state
  919. */
  920. static void write_font_data() {
  921. int i; /* loop index over characters */
  922. int j; /* loop index over font bytes within characters */
  923. unsigned char* fonts; /* pointer into video memory */
  924.  
  925. /* Prepare VGA to write font data into video memory. */
  926. OUTW(0x3C4, 0x0402);
  927. OUTW(0x3C4, 0x0704);
  928. OUTW(0x3CE, 0x0005);
  929. OUTW(0x3CE, 0x0406);
  930. OUTW(0x3CE, 0x0204);
  931.  
  932. /* Copy font data from array into video memory. */
  933. for (i = 0, fonts = mem_image; i < 256; i++) {
  934. for (j = 0; j < 16; j++)
  935. fonts[j] = font_data[i][j];
  936. fonts += 32; /* skip 16 bytes between characters */
  937. }
  938.  
  939. /* Prepare VGA for text mode. */
  940. OUTW(0x3C4, 0x0302);
  941. OUTW(0x3C4, 0x0304);
  942. OUTW(0x3CE, 0x1005);
  943. OUTW(0x3CE, 0x0E06);
  944. OUTW(0x3CE, 0x0004);
  945. }
  946.  
  947. /*
  948. * set_text_mode_3
  949. * DESCRIPTION: Put VGA into text mode 3 (color text).
  950. * INPUTS: clear_scr -- if non-zero, clear screens; otherwise, do not
  951. * OUTPUTS: none
  952. * RETURN VALUE: none
  953. * SIDE EFFECTS: may clear screens; writes font data to video memory
  954. */
  955. static void set_text_mode_3(int clear_scr) {
  956. unsigned long* txt_scr; /* pointer to text screens in video memory */
  957. int i; /* loop over text screen words */
  958.  
  959. VGA_blank(1); /* blank the screen */
  960. /*
  961. * The value here had been changed to 0x63, but seems to work
  962. * fine in QEMU (and VirtualPC, where I got it) with the 0x04
  963. * bit set (VGA_MIS_DCLK_28322_720).
  964. */
  965. set_seq_regs_and_reset(text_seq, 0x67); /* sequencer registers */
  966. set_CRTC_registers(text_CRTC); /* CRT control registers */
  967. set_attr_registers(text_attr); /* attribute registers */
  968. set_graphics_registers(text_graphics); /* graphics registers */
  969. fill_palette(); /* palette colors */
  970. if (clear_scr) { /* clear screens if needed */
  971. txt_scr = (unsigned long*)(mem_image + 0x18000);
  972. for (i = 0; i < 8192; i++)
  973. *txt_scr++ = 0x07200720;
  974. }
  975. write_font_data(); /* copy fonts to video mem */
  976. VGA_blank(0); /* unblank the screen */
  977. }
  978.  
  979. /*
  980. * copy_image
  981. * DESCRIPTION: Copy one plane of a screen from the build buffer to the
  982. * video memory.
  983. * INPUTS: img -- a pointer to a single screen plane in the build buffer
  984. * scr_addr -- the destination offset in video memory
  985. * OUTPUTS: none
  986. * RETURN VALUE: none
  987. * SIDE EFFECTS: copies a plane from the build buffer to video memory
  988. */
  989. static void copy_image(unsigned char* img, unsigned short scr_addr) {
  990. /*
  991. * memcpy is actually probably good enough here, and is usually
  992. * implemented using ISA-specific features like those below,
  993. * but the code here provides an example of x86 string moves
  994. */
  995. asm volatile (" \n\
  996. cld \n\
  997. movl $16000,%%ecx \n\
  998. rep movsb /* copy ECX bytes from M[ESI] to M[EDI] */ \n\
  999. "
  1000. : /* no outputs */
  1001. : "S"(img), "D"(mem_image + scr_addr)
  1002. : "eax", "ecx", "memory"
  1003. );
  1004. }
  1005.  
  1006. #ifdef TEXT_RESTORE_PROGRAM
  1007.  
  1008. /*
  1009. * main -- for the "tr" program
  1010. * DESCRIPTION: Put the VGA into text mode 3 without clearing the screens,
  1011. * which serves as a useful debugging tool when trying to
  1012. * debug programs that rely on having the VGA in mode X for
  1013. * normal operation. Writes font data to video memory.
  1014. * INPUTS: none (command line arguments are ignored)
  1015. * OUTPUTS: none
  1016. * RETURN VALUE: 0 on success, 3 in panic scenarios
  1017. */
  1018. int main() {
  1019. /* Map video memory and obtain permission for VGA port access. */
  1020. if (open_memory_and_ports() == -1)
  1021. return 3;
  1022.  
  1023. /* Put VGA into text mode without clearing the screen. */
  1024. set_text_mode_3(0);
  1025.  
  1026. /* Unmap video memory. */
  1027. (void)munmap(mem_image, VID_MEM_SIZE);
  1028.  
  1029. /* Return success. */
  1030. return 0;
  1031. }
  1032.  
  1033. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement