walters@osubem.UUCP (walters) (08/27/87)
-------- Following are routines for reading and writing to EGA graphics memory under Microport UNIX. First off, let me thank all of you who responded to my plea for help about EGA graphics and Microport UNIX. Almost all replys were helpfull. Following are two sets of routines, one for doing graphics on the EGA under Microport System V/AT and the other to save the text screen. There are several prerequisites to using these routines. Some obvious and some not so obvious. 1) An EGA card and a version of Microport that supports shared memory (for most people thats >= 2.2.0). 2) Shared memory segments are created are bootup with a file in /etc/rc.d like /etc/rc.d/shm.rc with the following lines. /etc/shmcreate 0xa0000 a0000 65535 # ega high res /etc/shmcreate 0xb8000 b8000 32768 # cga 3) A working version of /etc/shmcreate. The version that came with 2.2.0L didn't work but there was a patch posted in the microp newsletter. (I patched mine then lost the patch) 4) Read/write access to /dev/mem. A potential security hole. The routines are pretty obvious and are commented. They must be compiled with the large memory model. A small demo is given below. Compile with cc -Ml mtest.c mega.c mcgasr.c -o mtest For a more complex application it would be a good idea to catch signals and call ega_close() in your cleanup routine. Otherwise, if something happens you won't get your text screen back. If this happens, you still can type in commands you just can't SEE them. Now I understand why most people don't submit code they have written, it takes forever to make it presentable. The *best* way to do all of this non-sense would be to put it in a device driver to make it fast. Oh well, a project for the future. Please send bug reports to me and flames to /dev/null. Enjoy. -- Harold G. Walters Internet: walters@ce.okstate.edu School of Civil Engineering Uucp: {cbosgd, ihnp4, rutgers, seismo, Oklahoma State University uiucdcs}!okstate!osubem!walters Stillwater, OK 74078 "Ignorance is temporary, stupidity is forever." /* mtest.c */ main() { int x, y, i; if (ega_open() < 0) exit(1); ega_clear(4); ega_wdot_start(); for (y = 100; y <= 300; y++) for (x = 100; x <= 300; x++) ega_wdot_multi(x, y, 5); ega_wdot_stop(); i = ega_rdot(100, 100); sleep(5); ega_close(); printf(">>> %d <<<\n", i); exit(0); } /*eof*mtest.c*/ /*mcgasr.c***************************************************************/ /* */ /* mega */ /* */ /* Copyright (c) 1987 Harold G. Walters */ /* */ /* This software may be copied and/or redistributed without */ /* permission as long as this copyright notice is included */ /* prominently in any copy and/or redistribution. */ /* */ /* This software is provided as is. No claims are made for */ /* this software regarding its fitness and/or correctness for */ /* any purpose. Use of this software is at the user's own risk. */ /* */ /************************************************************************/ /* * This software assumes that shared memory has been * set up to allow access to the EGA card. Normally * done by /etc/shmcreate during bootup. * * /etc/shmcreate 0xa0000 a0000 65535 # ega high res * * Write access to /dev/mem is also required */ #include <stdio.h> #include <errno.h> #include <signal.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/io_op.h> /* Port Addresses */ #define seq_addr 0x3c4 #define seq_data 0x3c5 #define crtc_addr 0x3d4 #define crtc_addr_b 0x3b4 #define crtc_data 0x3d5 #define graph_1_pos 0x3cc #define graph_2_pos 0x3ca #define graph_addr 0x3ce #define graph_data 0x3cf #define misc_output 0x3c2 #define in_stat_o 0x3c2 #define input_status_b 0x3ba #define input_status 0x3da #define attr_read 0x3da #define attr_write 0x3c0 /* Address Register Values */ #define g_set_reset 0x0 #define g_enbl_set 0x1 #define g_clr_comp 0x2 #define g_data_rot 0x3 #define g_read_map 0x4 #define g_mode 0x5 #define g_misc 0x6 #define g_color 0x7 #define g_bit_mask 0x8 #define s_reset 0x0 #define s_clock 0x1 #define s_map 0x2 #define s_cgen 0x3 #define s_nem 0x4 #define write_mode_0 0x0 #define write_mode_1 0x1 #define write_mode_2 0x2 #define g_default 0xff #define s_default 0xf /* shm stuff */ #define EGAKEY 0xa0000 #define EGALEN 65535 #define EGAFLAGS 0 /* EGA extrema */ #define X_MIN 0 #define X_MAX 639 #define Y_MIN 0 #define Y_MAX 349 /* default colors */ #define BLACK 0x00 #define BLUE 0x01 #define GREEN 0x02 #define CYAN 0x03 #define RED 0x04 #define MAGENTA 0x05 #define YELLOW 0x06 #define WHITE 0x07 #define BRIGHT 0x08 static int dev_mem_fd; /* must have read/write access to /dev/mem */ static char *ega_ram; /* output byte data to addr */ #define outb(addr, data) \ { \ io_op_t poke; \ poke.io_port = addr; \ poke.io_byte = data; \ ioctl(dev_mem_fd, IOCIOP_WB, &poke); \ } /* set modes as defined in console.7 NOT as in IBM EGA manual */ void ega_set_mode(m) int m; { static char buf[10]; if (m < 0) /* text mode */ sprintf(buf, "%1c[=h", 0x1b); else sprintf(buf, "%1c[=%dh", 0x1b, m); write(1, buf, strlen(buf)); return; } /* does all of the initialization for EGA graphics */ int ega_open() { void ega_save_bp(); extern char *shmat(); int shmid; if (cga_save() < 0) /* save text screen (optional) */ { return(-1); } if ((dev_mem_fd = open("/dev/mem", 2)) == -1) { perror("ega_open: /dev/mem"); return(-1); } if ((shmid = shmget((key_t) EGAKEY, EGALEN, EGAFLAGS)) == -1) { perror("ega_open: shmget"); return(-1); } if ((ega_ram = shmat(shmid, (char *) 0, 0)) == (char *) -1) { perror("ega_open: shmat"); return(-1); } ega_set_mode(10); ega_save_bp(); /* see below */ return(0); } /* * its probably a good idea to call ega_close in your cleanup routine * if you catch signals (if you don't text mode won't get restored) */ /* cleans up and restores text screen */ int ega_close() { void ega_rest_bp(); int stat = 0; ega_rest_bp(); /* see below */ if (shmdt(ega_ram) == -1) { perror("ega_close: shmdt"); stat = -1; } if (close(dev_mem_fd) == -1) { perror("ega_close: /dev/mem"); stat = -1; } ega_set_mode(-1); if (cga_rest() < 0) /* restore cga text (optional) */ stat = -1; return(stat); } /* * the strategy behind the ega_wdot_{start, multi, stop} triplet is * to minimize writes to /dev/mem to speed things up by * 1) buffering writes inside a byte (good for rows) * 2) write out new bit mask only when it changes (for columns) */ static int last_mask, cur_mask, last_col, first; static unsigned int last_byte; /* void dodot(byte, mask, col) int byte, mask, col; { if (last_mask != mask) { outb(graph_addr, g_bit_mask); outb(graph_data, mask); last_mask = mask; } mask = ega_ram[byte]; ega_ram[byte] = col; return; } */ /* macro is barely faster than the subroutine */ #define dodot(byte, mask, col) \ { \ register unsigned int lb; \ register int lc, junk; \ if (last_mask != mask) \ { \ outb(graph_addr, g_bit_mask); \ outb(graph_data, mask); \ last_mask = mask; \ } \ lb = byte; \ lc = col; \ junk = ega_ram[lb]; \ ega_ram[lb] = lc; \ } /* call before calling ega_wdot_multi */ void ega_wdot_start() { outb(graph_addr, g_mode); outb(graph_data, write_mode_2); /* let the EGA do the work */ first = 1; /* must be restored to mode 0 later */ last_mask = -1; return; } /* write a pixel to the graphics screen at x, y in color col */ /* may be called only after calling ega_wdot_start */ void ega_wdot_multi(x, y, col) int x, y, col; { register unsigned int byte; register int mask; if (x < X_MIN || x > X_MAX || y < Y_MIN || y > Y_MAX) return; byte = (Y_MAX - y) * 80 + (x >> 3); mask = 1 << ((x & 7) ^ 7); if (first) { cur_mask = mask; last_col = col; last_byte = byte; first = 0; } if (last_col == col && last_byte == byte) { cur_mask |= mask; } else { dodot(last_byte, cur_mask, last_col); cur_mask = mask; last_byte = byte; last_col = col; } return; } /* call after ega_wdot_start and ega_wdot_multi to restore default modes */ void ega_wdot_stop() { dodot(last_byte, cur_mask, last_col); outb(graph_addr, g_mode); outb(graph_data, write_mode_0); outb(graph_addr, g_bit_mask); outb(graph_data, g_default); return; } /* all in one function (slllooooooowwwwwww) */ void ega_wdot(x, y, col) int x, y, col; { register unsigned int byte; register int mask; if (x < X_MIN || x > X_MAX || y < Y_MIN || y > Y_MAX) return; outb(graph_addr, g_mode); outb(graph_data, write_mode_2); byte = (Y_MAX - y) * 80 + (x >> 3); mask = 1 << ((x & 7) ^ 7); outb(graph_addr, g_bit_mask); outb(graph_data, mask); mask = ega_ram[byte]; ega_ram[byte] = col; outb(graph_addr, g_mode); outb(graph_data, write_mode_0); outb(graph_addr, g_bit_mask); outb(graph_data, g_default); return; } /* read a pixel at x, y and return color */ int ega_rdot(x, y) int x, y; { register unsigned int byte; register int mask, val, i; if (x < X_MIN || x > X_MAX || y < Y_MIN || y > Y_MAX) return(-1); val = 0; byte = (Y_MAX - y) * 80 + (x >> 3); mask = 1 << ((x & 7) ^ 7); outb(graph_addr, g_read_map); outb(graph_data, 0); if (((int) ega_ram[byte]) & mask) val |= (1 << 0); outb(graph_addr, g_read_map); outb(graph_data, 1); if (((int) ega_ram[byte]) & mask) val |= (1 << 1); outb(graph_addr, g_read_map); outb(graph_data, 2); if (((int) ega_ram[byte]) & mask) val |= (1 << 2); outb(graph_addr, g_read_map); outb(graph_data, 3); if (((int) ega_ram[byte]) & mask) val |= (1 << 3); return(val); } /* fast clear screen to color col */ void ega_clear(col) int col; { register unsigned int byte; register char on; register int i, tmp; outb(graph_addr, g_mode); outb(graph_data, write_mode_0); outb(graph_addr, g_bit_mask); outb(graph_data, g_default); for (i = 0; i < 4; i++) { outb(seq_addr, s_map); tmp = 1 << i; outb(seq_data, tmp); if (tmp & col) on = 0xff; else on = 0; for (byte = 0; byte < EGALEN; byte++) { ega_ram[byte] = on; } } outb(seq_addr, s_map); outb(seq_data, s_default); return; } /* * the following routines save the first 8k of all four bit planes * to enable a restore to text mode (chars, attributes, char generator map) * I'm not sure if everyone needs these */ #define MAXBP 8192 static char bp[4][MAXBP]; /* save 'em */ void ega_save_bp() { register int byte, i; for (i = 0; i < 4; i++) { outb(graph_addr, g_read_map); outb(graph_data, i); for (byte = 0; byte < MAXBP; byte++) { bp[i][byte] = ega_ram[byte]; } } return; } /* restore 'em */ void ega_rest_bp() { register int byte, i; outb(graph_addr, g_mode); outb(graph_data, write_mode_0); outb(graph_addr, g_bit_mask); outb(graph_data, g_default); for (i = 0; i < 4; i++) { outb(seq_addr, s_map); outb(seq_data, (1 << i)); for (byte = 0; byte < MAXBP; byte++) { ega_ram[byte] = bp[i][byte]; } } outb(seq_addr, s_map); outb(seq_data, s_default); return; } /*eof*mega.c*/ /*mcgasr.c***************************************************************/ /* */ /* mega */ /* */ /* Copyright (c) 1987 Harold G. Walters */ /* */ /* This software may be copied and/or redistributed without */ /* permission as long as this copyright notice is included */ /* prominently in any copy and/or redistribution. */ /* */ /* This software is provided as is. No claims are made for */ /* this software regarding its fitness and/or correctness for */ /* any purpose. Use of this software is at the user's own risk. */ /* */ /************************************************************************/ /* * This software assumes that shared memory has been * set up to allow access to the EGA card (CGA mapped memory). * Normally done by /etc/shmcreate during bootup. * * /etc/shmcreate 0xb8000 b8000 32768 # cga */ #include <stdio.h> #include <errno.h> #include <signal.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/io_op.h> /* shm stuff */ #define CGAKEY 0xb8000 #define CGALEN 4096 #define CGAFLAGS 0 /* default colors */ #define BLACK 0x00 #define BLUE 0x01 #define GREEN 0x02 #define CYAN 0x03 #define RED 0x04 #define MAGENTA 0x05 #define YELLOW 0x06 #define WHITE 0x07 #define BRIGHT 0x08 #define cga_attrib(f, b) (((b & 15) << 4) | (f & 15)) static char *cga_ram; static char cbuf[CGALEN]; /* save the console text screen */ int cga_save() { extern char *shmat(); int shmid; register unsigned int i; if ((shmid = shmget((key_t) CGAKEY, CGALEN, CGAFLAGS)) == -1) { perror("cga_open: shmget"); return(-1); } if ((cga_ram = shmat(shmid, (char *) 0, 0)) == (char *) -1) { perror("cga_open: shmat"); return(-1); } for (i = 0; i < CGALEN; i++) cbuf[i] = cga_ram[i]; return(0); } /* restore the console text screen */ int cga_rest() { register unsigned int i; for (i = 0; i < CGALEN; i++) cga_ram[i] = cbuf[i]; if (shmdt(cga_ram) == -1) { perror("cga_close: shmdt"); return(-1); } return(0); } /*eof*mcgasr.c*/ -------