chapman@sco.COM (Brian Chapman) (07/01/90)
Here is the ~300 line example of EGA/VGA graphics on SCO SystemV I promised. It is 350 lines, or just over 400 if you count the Makefiles. I include two Makefiles because Unix 3.2 has features that don't exist in Xenix, and I don't like how messy "portable" Makefiles get. This program does it all, Mode switches. Memory map the frame buffer Direct INs and OUTs to the EGA/VGA board. Screen Switch signals This program should fill the screen with a solid color one pixle at a time in a random order. It should cycle through 16 colors, black fades to blue, blue fades to green... etc. I will answer questions about the SCO video ioctls. I will not answer questions about how the EGA/VGA HW works. It is very complex, I am not the world's greatest expert and there are some very good books on the market. -- Chapman ---- Cut here ---- You know the drill. ----- # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # # This archive contains: # Makefile.unix Makefile.xenix dissolve.c ega.c portio.s scovid.c video.h echo x - Makefile.unix cat > "Makefile.unix" << '//E*O*F Makefile.unix//' # Remove UNIX internationlization (makes binary smaller) NOINTL= -nointl # Unix shared library link (makes binary smaller) LIBS= -lc_s PROF= #-p STRIP= -s CFLAGS= -O ${PROF} LDFLAGS= ${PROF} ${STRIP} ${NOINTL} AS= masm ASFLAGS= -Mx CVTOMF=cvtomf .s.o: ${AS} ${ASFLAGS} -o$@ $< ${CVTOMF} $@ dissolve: dissolve.o scovid.o ega.o portio.o cc ${LDFLAGS} -o dissolve dissolve.o scovid.o ega.o portio.o ${LIBS} lc -l dissolve dissolve.o: dissolve.c video.h scovid.o: scovid.c video.h ega.o: ega.c video.h //E*O*F Makefile.unix// echo x - Makefile.xenix cat > "Makefile.xenix" << '//E*O*F Makefile.xenix//' PROF= STRIP= -s CFLAGS= -O ${PROF} LDFLAGS= ${PROF} ${STRIP} AS= masm ASFLAGS= -Mx .s.o: ${AS} ${ASFLAGS} -o$@ $< dissolve: dissolve.o scovid.o ega.o portio.o cc ${LDFLAGS} -o dissolve dissolve.o scovid.o ega.o portio.o lc -l dissolve dissolve.o: dissolve.c video.h scovid.o: scovid.c video.h ega.o: ega.c video.h //E*O*F Makefile.xenix// echo x - dissolve.c cat > "dissolve.c" << '//E*O*F dissolve.c//' #include <sys/fcntl.h> #include <sys/errno.h> #include "video.h" randmask[33] = { -1, -1, 0x3, 0x6, 0xc, 0x14, 0x30, 0x60, 0xb8, 0x110, 0x240, 0x500, 0xca0, 0x1b00, 0x3500, 0x6000, 0xb400, 0x12000, 0x20400, 0x72000, 0x90000, 0x140000, 0x300000, 0x420000, 0xd80000, 0x1200000, 0x3880000, 0x7200000, 0x9000000, 0x14000000, 0x32800000, 0x48000000, 0xa3000000 }; main() { int i, rc, c; extern errno; graf(); for(i=1; i<=16; i++) dissolve(i, LPS, PPL); grafend(); } dissolve(color, height, width) int height, width; { int pixels, lastnum; /* last pixel's number */ int regwidth; /* width of sequence generator */ register int mask; /* mask to XOR w/ to create sequence */ register unsigned int element; /* one element of random sequence */ pixels = height * width; lastnum = pixels - 1; regwidth = bitwidth(lastnum); mask = randmask[regwidth]; element = 1; do { if(element <= lastnum) ega_bit(color, element); if(element & 1) element = (element >> 1) ^ mask; else element >>= 1 ; } while (element != 1); ega_bit(color, 0); } bitwidth(x) { int b, w, i; i = w = 0; b = 1; for(i=0; i<32; i++) { if(x & b) w = i; b <<= 1; } return(w+1); } //E*O*F dissolve.c// echo x - ega.c cat > "ega.c" << '//E*O*F ega.c//' /* * EGA 640x350 16 color, 4 plane frame buffer * accessing code. This also maintains the incore * image when the screen is not displayed. */ #include "video.h" #define PLANESZ (BPL * LPS) #define FRAMESZ (PLANESZ * 4) /* * IO addresses */ #define GC_INDEX 0x3CE /* Graphics controller index register */ #define GC_MAP 0x4 /* index for mapping the READ plane */ #define GC_MODE 0x5 /* index for setting the READ/WRITE mode */ #define GC_MASK 0x8 /* index for setting WRITE bit mask */ #define SQ_INDEX 0x3C4 /* Sequence controller index register */ #define SQ_MAP 0x2 /* index for mapping the WRITE plane */ char *scr_buf; ega_save() { register int i; for(i=0; i<4; i++) { out_index2(GC_INDEX, GC_MAP, i); memcpy(scr_buf+(i*PLANESZ), Screenmem, PLANESZ); } out_index2(SQ_INDEX, GC_MAP, 0); } ega_restore() { register int i; ioctl(0, GRAPHICS_MODE, (char *)0); for(i=0; i<4; i++) { out_index2(SQ_INDEX, SQ_MAP, 1<<i); memcpy(Screenmem, scr_buf+(i*PLANESZ), PLANESZ); } out_index2(SQ_INDEX, SQ_MAP, 0x0F); out_index2(GC_INDEX, GC_MODE, 2); /* write mode 2 */ } ega_grafmode() { if(-1 == ioctl(0, GRAPHICS_MODE, (char *)0)) { perror("graphics mode"); exit(1); } out_index2(GC_INDEX, GC_MODE, 2); /* write mode 2 */ /* * Allocate and init the save screen data structure. */ scr_buf = (char *)malloc(FRAMESZ); memset(scr_buf, '\0', FRAMESZ); } ega_bit(color, offset) register int offset; { register char *des; register int junk; while(!Isdisplayed) pause(); des = &Screenmem[offset >> 3]; /* * Using write mode 2 */ out_index2(GC_INDEX, GC_MASK, 0x80 >> (offset&0x07) ); junk = *des; /* latch the data */ *des = color; } //E*O*F ega.c// echo x - portio.s cat > "portio.s" << '//E*O*F portio.s//' ; Static Name Aliases ; TITLE video .386 _TEXT SEGMENT BYTE PUBLIC 'CODE' _TEXT ENDS _DATA SEGMENT WORD PUBLIC 'DATA' _DATA ENDS CONST SEGMENT WORD PUBLIC 'CONST' CONST ENDS _BSS SEGMENT WORD PUBLIC 'BSS' _BSS ENDS DGROUP GROUP CONST, _BSS, _DATA ASSUME CS: _TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP _TEXT SEGMENT public _out_index2 _out_index2 proc near push ebp mov ebp,esp mov edx,[ebp+8] mov al,[ebp+12] mov ah,[ebp+16] out dx, ax leave ret _out_index2 endp _TEXT ENDS END //E*O*F portio.s// echo x - scovid.c cat > "scovid.c" << '//E*O*F scovid.c//' #include <stdio.h> #include <sys/types.h> #include <sys/signal.h> #include <sys/vtkd.h> #include "video.h" #define SIG_REL SIGUSR1 #define SIG_ACQ SIGUSR2 /* * internal only symbols. */ void rel_screen(), acq_screen(), grafquit(); int Oldmode; /* save mode of user shell screen */ /* * Set up the graphics multiscreen stuff and call another * routine to set up card. */ graf() { struct vt_mode smode; Isdisplayed = 1; /* * Set up to catch the screen switch signals. */ signal(SIG_REL, rel_screen); signal(SIG_ACQ, acq_screen); signal(SIGINT, grafquit); /* * Set up the data structure that asks the driver * to send you signals when the screens are switched. * mode == VT_PROCESS means send screen switch signals. * mode == VT_AUTO means turn off screen switch signals (regular mode). * relsig == the signal you want when the user switches away. * acqsig == the signal you want when the user switches back to you. */ smode.mode = VT_PROCESS; smode.waitv = 0; /* not implemented, reserved */ smode.relsig = SIG_REL; smode.acqsig = SIG_ACQ; smode.frsig = SIGINT; /* not implemented, reserved */ if(-1 == ioctl(0, VT_SETMODE, &smode)) { perror("screen switch signal ioctl VT_SETMODE"); exit(1); } grafmode(); } /* * this is the signal handler for when the user screen flips * away from us. */ void rel_screen() { signal(SIG_REL, rel_screen); Isdisplayed = 0; ega_save(); /* * Tell the video driver that you have saved your state * and it can now have the card to switch to the new screen. * The video driver waits (forever) for this ioctl before * it will complete the screen switch requested by the user. * If you don't make this ioctl the screen switcher will * be wedged until it gets one. It is best to have a * small one line reldisp.c program to unwedge your screen * switcher when development programs screw up from time * to time. */ ioctl(0, VT_RELDISP, VT_TRUE); } /* * this is the signal handler for when the user screen flips * back to us. */ void acq_screen() { signal(SIG_ACQ, acq_screen); Isdisplayed = 1; ega_restore(); /* * Tell the video driver that you have restored your state * and screen switching can now continue. */ ioctl(0, VT_RELDISP, VT_ACKACQ); } void grafquit() { grafend(); exit(0); } /* * restore text mode. */ void grafend() { ioctl(0, MODESWITCH | Oldmode, (char *)0); } grafmode() { int adapter, privlcmd; /* * Confirm that we are on a supported video adapter. */ adapter = ioctl(0, CONS_CURRENT, (char *)0); if(EGA != adapter && VGA != adapter) { puts("Stdin must be an EGA or VGA multiscreen\n"); exit(0); } /* * Save the user's current text mode so you * can restore it on exit. */ Oldmode = ioctl(0, CONS_GET, (char *)0); /* * Get privledge to do direct INs and OUTs to the video card. */ if(EGA == adapter) privlcmd = EGA_IOPRIVL; else privlcmd = VGA_IOPRIVL; if(-1 == ioctl(0, privlcmd, 1)) { perror("I/O privilege denied"); exit(1); } /* * Have the video driver reprogram the card for EGA 640x350 16 color mode. */ ega_grafmode(); /* * Map the video card's frame buffer into your address space. * This must be done after the mode switch command or you get * frame buffer address for the wrong mode mapped in. */ Screenmem = (char *)ioctl(0, MAPCONS, (char *)0); } //E*O*F scovid.c// echo x - video.h cat > "video.h" << '//E*O*F video.h//' #ifndef SW_ENH_CG640 #include <sys/machdep.h> #endif #define VGA_DEMO #ifdef VGA_DEMO #define GRAPHICS_MODE SW_VGA12 /* VGA 640x480 16 colors */ #define LPS (480) /* lines per screen */ #endif #ifdef EGA_DEMO #define GRAPHICS_MODE SW_ENH_CG640 /* EGA 640x350 16 colors */ #define LPS (350) /* lines per screen */ #endif #define PPL (640) /* pixels per line */ #define BPL (PPL/8) /* bytes per line */ /* * externaly availible symbols. */ int Isdisplayed; /* flag: when are we flipped away */ char *Screenmem; /* physical map to the video RAM */ int graf(); /* Set everything up */ void grafend(); /* Restore user's text mode */ void grafquit(); /* Clean-up and exit */ //E*O*F video.h// exit 0 -- Brian Chapman uunet!sco!chapman Pay no attention to the man behind the curtain!