****************************************** ** OFFICIAL FakeMode DOCUMENTATION FILE ** ****************************************** written by Yaka/Xography in April/May 1994 orthographic supervision by Sandman/Valhalla Hi, Dudes; I was asked about FakeMode and how I did it so often I thought I had to write a documentation about it. This documentation is written for all those who want to understand the principles behind FakeMode or even want to program the mode themselves. Feel free to use FakeMode in your own programs. There may be mistakes in this text, so you are self responsible for what may happen when you try to code FakeMode! There are a complete library with FakeMode routines and an example program for its use included with this doc. They were originally made for BC 3.1, but should be usable with other languages too. Just experiment with them! --------- CONTENTS: --------- Chapter 1. What is FakeMode? Chapter 2. The techniques behind FakeMode and how to program it Chapter 3. Information about the program included Chapter 4. Literature ----------------------------- CHAPTER 1: What is FakeMode? ----------------------------- FakeMode is a way to setup standard VGA cards so they are able to display more than 256 colors. I invented this mode in Xography's "Party93 Report" which was released in April 1994. So what is FakeMode good for? PRO FakeMode: -You have a screen resolution of 320x400 -You have 3840 (fixed) colors on standard VGA's CONTRA FakeMode: - It is slow - The screen flickers a little - You need the timer interrupt - It works only on register compatible VGAs - setting pixels is difficult - the palette is fixed; so you can't choose the colors freely from 262144 I think Fakemode is not useful for animations because: - you have to output 4 bytes to set 1 320x200 - Pixel ( -> slow) - screen memory access is rather complicated - slow movements will interfere with page flipping, this looks ugly (Hm, perhaps you'll find a way how to avoid this?) ----------------------------------------- CHAPTER 2: The techniques behind FakeMode ----------------------------------------- FakeMode is achieved by combination of several means: - Use Y-mode (320x400 at 256 colors and 2 pages) - Flip between the 2 pages at every vertical retrace - select the palette colors wisely - set pixel data in a special way. *** 2.1 Y-Mode Y-Mode (similar to X-mode) is a video mode for register compatible VGA cards, that pushes resolution up to 320x400 at still 256 colors and 2 pages! The disadvantage compared to standard mode 13h (320x200, 256col, 1 page) is that memory access is not so easy anymore (the pixels are split up in bitplanes). Here's the code I use to setup Y-Mode for FakedMode (in TASM 3.1) [1]: ******************************************** _F_initgraph PROC push di ;//save DI because of BC (I call from BC) mov ax,0f00h ;//Get old videomode... int 10h mov oldvideomode,al ;//...and save it (define oldvideomode!) mov ax,0013h ;//initialize normal Mode 13h int 10h mov dx,3ceh ;//select Graphics Controller... mov al,5 ;//...Graphics Mode Register out dx,al inc dx in al,dx and al,11101111b ;//switch off ODD/EVEN mode out dx,al dec dx mov al,6 ;//...Miscellaneous Register out dx,al inc dx in al,dx and al,11111101b ;//switch off ODD/EVEN mode here, too out dx,al mov dx,3c4h ;//select Sequencer Controller... mov al,4 ;//...Memory Mode Register out dx,al inc dx in al,dx and al,11110111b ;//use linear adressing or al,4 out dx,al mov ax,0a000h ;//access Video Memory mov es,ax xor di,di mov ax,di mov cx,8000h rep stosw ;//clear Screen mov dx,3d4h ;//select CRT Controller... mov al,9 ;//...Maximum Scan Line Register out dx,al inc dx in al,dx and al,01110000b ;//select 400 lines out dx,al dec dx mov al,14h ;//...Underline Location Register out dx,al inc dx in al,dx and al,10111111b ;//switch off Doubleword-Mode out dx,al dec dx mov al,17h ;//...Mode Control Register out dx,al inc dx in al,dx ;//select Word-Mode (normally: Bytemode) and al,10111111b ;//normally: or al,01000000b out dx,al call initpalette ;//call to palette setup routine (for FakeMode) call inittimer ;//call to timer setup routine (for FakeMode) pop di ;//restore value of di ret _F_initgraph ENDP ************************************ That's it. I modified the original routine a bit as I keep WordMode; it's because it is easier to write FakeMode pixels in WordMode. You can return to textmode or other graphics modes by normal BIOS function call (int 10h, Fkt 0). The calls to 'initpalette' and 'inittimer' are necessary to install FakeMode and are not part of Y-Mode installation. *** 2.2 Page Flipping This is best done (I think) by synchronizing the timer interrupt with the screen. Just before the vertical retrace appears, the interrupt is called. The interrupt handler routine should now set the screen offset address to its new value and wait for the vertical retrace. Then it should reprogram the timer and return to the main program. When the vertical retrace occurs, the new offset address is loaded in the internal registers of the VGA card and invokes the next screen update. See [1], ([2]), [3]. So we'll just have to: - hook the timer interrupt - write our own interrupt handler - synchronize the timer interrupt with the screen - still call system timer routine at 18.2 Hz from interrupt handler - program the timer chip to achieve MonoFlop mode. What could be simpler? :) *** 2.2.1 Hooking/Dehooking the timer interrupt, Synchronization with the screen Hooking an interrupt is quite easy; DOS interupt 21h has got functions to handle interrupt hooking (see below, routine inittimer). To synchronize the timer int with the screen, I first set the interrupt speed much faster than the screen (256 Hz) and use a handler that counts up a variable 'count'. Then I wait for a vertical retrace and let the timer run. When 'count' has changed at next vertical retrace, the timer still is too fast. I lower speed and try again, until 'count' doesn't change between start of timer and next vertical retrace. Then I know that with this speed, I'm just below the minimal speed. I increase it a little and now I know how long I have to wait aproximately between 2 timer int calls. Of course the value isn't exact, so I have to synchronize every interrupt call for new; that's done by the interrupt handler discussed below. The routine 'closetimer' should be called when you leave FakeMode; it stops the timer int and puts everything back to normal. ************************************ synchroint PROC ;// This interrupt handler is used for push ax ;// screen synchronization. mov ax,counter inc ax ;// just count up 'counter'... mov counter,ax mov al,20h ;// send EOI to interrupt controller... out 20h,al pop ax iret ;// return from interrupt handler synchroint ENDP inittimer PROC ;// This routine is called when FakeMode is push di ;// installed. it initializes & synchronizes mov ax,1234h ;// the timer mov currentfloptime,ax ;// start with 256 Hz mov ax,3508h ;//save old Interrupt 08 int 21h mov alterint08,bx mov alterint08+2,es xor ax,ax ;//redirect Int. 08 to Synchronisation Rout. mov es,ax mov di,08h*4 ;// this is the other method to access cli ;// interrupt vectors: via the interrupt table cld mov ax,offset synchroint stosw mov ax,cs stosw sti ;//------ synchronize timer with screen mov dx,3dah ;//Wait for End of Retrace s1endretjmp: in al,dx and al,00001000b jnz s1endretjmp s1retjmp: ;//Wait for Retrace in al,dx and al,00001000b jz s1retjmp synchroback: ;//now we can start measurement... mov al,36h ;//start Systemtimer in Rectangle Mode out 43h,al mov ax,currentfloptime out 40h,al mov al,ah out 40h,al mov ax,0 ;//reset counter. counter is increased mov counter, ax ;//by interrupt routine mov dx,3dah ;//Wait for End of Retrace s2endretjmp: in al,dx and al,00001000b jnz s2endretjmp s2retjmp: ;//Wait for Retrace in al,dx and al,00001000b jz s2retjmp mov ax,counter ;//did interrupt still occur? cmp ax,0 je fertig ;//no -> ready mov ax,currentfloptime add ax,250 ;//yes -> lower speed and try again mov currentfloptime,ax jmp synchroback fertig: mov al,34h ;//set Systemtimer right (Monoflop) out 43h,al mov ax, currentfloptime sub ax,800 ;//...we need time for the handler mov currentfloptime,ax out 40h,al mov al,ah out 40h,al xor ax,ax ;//redirect Int. 08 to Screenswitch Routine mov es,ax mov di,08h*4 cli cld mov ax,offset switchpageint ;//interrupt handler routine see below stosw mov ax,cs stosw sti pop di ret inittimer ENDP closetimer PROC ;// this routine de-installs the timer handler push ds push di push si cli mov al,36h ;//Systemtimer back to normal speed out 43h,al xor al,al out 40h,al out 40h,al push cs ;//restore Interrupt Vector back to normal pop ds mov si,offset alterint08 xor ax,ax mov es,ax mov di,08h*4 cld movsw movsw sti pop si pop di pop ds ret closetimer ENDP ************************************ *** 2.2.2 The interrupt Handler routine This is the main timer interrupt routine which is called after every screen update and performs the page flipping. There are three necessary things when you write a hardware interrupt handler: 1) be sure to preserve ALL registers you use (push them and pop them later)! 2) don't forget to acknowledge the hardware interrupt controller (mov al,20h out 20h,al)! 3) return from Interrupt with IRET, not with RET! Read the comments; they should explain everything. Literature used for this: [2], [3] ************************************ switchpageint PROC push ax ;//interrupt handlers must push all registers push bx ;//they use! push dx inc word ptr systimer ;//set system timer (this is my own timer; ;//i use it for timing in the main program) mov bx,currentpage add bx,32768 mov currentpage,bx mov dx,3d4h mov al,0ch ;//set Start Adresse High (0Ch) to flip pages mov ah,bh out dx,ax mov dx,3dah ;//Wait for Retrace swretjmp: ;//(this is done to keep synchronization) in al,dx and al,00001000b jz swretjmp mov al,34h ;//start Monoflop for new out 43h,al ;//(let the timer run for new) mov ax,currentfloptime out 40h,al mov al,ah out 40h,al mov bx,currentsystimer ;//do Systemtimer call at 18.2 Hz add ax,bx mov currentsystimer,ax cmp ax,bx ja short nosysroutine ;//No --> continue pop dx pop bx pop ax jmp dword ptr alterint08 ;//call Systemtimer Routine nosysroutine: mov al,20h ;//OK to Interrupt Controller out 20h,al pop dx pop bx pop ax iret ;//return from interrupt switchpageint ENDP ************************************ There may be timing problems when you use your own hardware interrupt handler. Especially the INT 13h calls are very time-sensitive, if a timer interrupt routine is called just when the processor is in INT13 handler, and the com- puting of the timer int takes too long, the computer may crash. In such cases it may help to check if the computer is just in the INT13 hand- ler when the timer interrupt is called (You have to hook int 13 and set a variable; then continue with int13 handler. After the handler has finished, reset the variable. So the timer int can check this variable to see if INT13 handler is active or not.) Well, I never had problems with INT13 and FakeMode, so I didn't implement this. :) *** 2.3 Palette Setup The palette is static; that means I don't change it when I flip pages. To achieve the 3840 color mode, I split up the colors to green and red/blue. The palette contains 16*15 values red/blue and 16 values green (16*15+16=256). (16 colors red * 16 colors green * 15 colors blue = 3840 different colors) To get harmonic greys, I set blue to the same values as red and green, but just leave out the darkest blue value (you can't see that one, anyway). So when you set pixels later, you have to decrement the blue value if it isn't zero to get the right color. (The H_setsmallpixel routine of the example file included does that, for example.) The palette values are stored directly to DAC, but are also buffered in 'palette' to make later changes possible (fadein/out, setluminance). Here comes the palette setup routine: ************************************ palette db 768 dup (?) ;//buffer for palette initpalette PROC push di push cs pop es mov di, offset palette cld mov dx,3c8h xor ax,ax xor bx,bx out dx,al inc dx ;//ah=red, bh=green, bl=blue mov cx,15 initpal_outer: ;//setup red/blue part of palette (0..239) push cx mov cx,16 initpal_inner: mov al,ah out dx,al stosb mov al,bh out dx,al stosb mov al,bl out dx,al stosb add ah,4 loop initpal_inner mov ah,0 add bl,4 cmp bl,4 jne goon add bl,4 goon: pop cx loop initpal_outer mov cx,16 xor ax,ax xor bx,bx initpal_second: ;//setup green part of palette (240..255) mov al,ah out dx,al stosb mov al,bh out dx,al stosb mov al,bl out dx,al stosb add bh,4 loop initpal_second pop di ret initpalette ENDP ************************************ *** How to set pixels in FakeMode The main purpose of the way I set pixels is to minimize the flicker. One pixel on the screen consists of 2 pixels, one on page 1 and one on page 2. On one of the pages the green value is displayed, on the other the red/blue value. Imagine I would set all pixels green values on page 1 and all red/blue values on page 2. I would get horrible flicker. To prevent this, I set the values like follows: if xpos+ypos=odd, then set red/blue on page 1 and green on page 2 else set green on page 1 and red/blue on page 2. So I get a 1/1 raster, and each of the 2 pages contain both red/blue and green values. Look at the following routine to see how it is done exactly: ************************************ _F_putsmallpixel PROC ;//values: x=0..319, y=0..399, red=0..15, green=0..15, blue=0..15 ARG x:word, y:word, red:byte, green:byte, blue:byte push bp mov bp,sp push di mov bx,x mov cx,bx and cl,00000011b ;//calculate bitplane... mov dx,3c4h mov ax,0102h shl ah,cl out dx,ax ;//...and set it mov ax,0a000h ;//set destination segment mov es,ax mov ax,160 ;//set destination offset mov dx,y mul dx shr bx,1 and bl,11111110b add bx,ax ;//bx contains basic offset mov di,bx mov al,blue ;//calculate red-blue value mov ah,16 mul ah cmp ax,0 je short smallpixgoon sub ax,16 ;// perform blue adjustment smallpixgoon: add al,red add cx,y and cl,00000001b ;// select if green value on page 1 or 2 jz short stypetwo mov ah,al mov al,green add al,240 mov es:[di],ax ;// set both pixels (on page 1 & 2) jmp short send stypetwo: mov ah,green add ah,240 mov es:[di],ax ;// set both pixels (on page 1 & 2) send: pop di pop bp ret _F_putsmallpixel ENDP ************************************ In FakeMode the video memory is built up like this: Bitplane 0| rb 0 | g 0 | g 4 | rb 4 | ... Bitplane 1| g 1 | rb 1 | rb 5 | g 5 | ... Bitplane 2| rb 2 | g 2 | g 6 | rb 6 | ... Bitplane 3| g 3 | rb 3 | rb 7 | g 7 | ... Offset | 0 | 1 | 2 | 3 | ... So one line on the screen uses 160 bytes of data in each Bitplane. The colors values for one pixel are stored besides each other (this is because I use wordmode). ------------------------------------------------- CHAPTER 3: Information about the program included ------------------------------------------------- With this doc comes an example program, written in BC 3.1 and TASM 3.1 . I also included the .PRJ file. (If the .PRJ file causes problems, create your own: Just be sure to include both FAKESHOW.CPP and FAKEMODE.ASM in your project and don't forget to set code generation to large.) There is also an example .TGA picture included (320x200, 24bit colors). This .TGA viewer will definitely only show UNPACKED 24-BIT .TGA pictures! (Perhaps you can extend this, but I considered it to be too much work for a little doc example). The viewer may display some of the .TGA pictures upside down; this is because the data is stored sometimes upside down. (I think there is a flag for this in the header of the .TGA). The file FAKEMODE.ASM is my complete FakeMode routine library and contains all useful basic routines concerning FakeMode. --------------------- CHAPTER 4: Literature --------------------- [1]: Michael Tischer: PC intern 3.0; Data Becker (a german book) Contains useful information about VGA programming (Although the Ferraro book might be better) [2]: DOS international, issue 3/89 p.170 ff; Everts&Hagedorn (german computer magazine). This is an article about how sample output with PC internal speaker is done. That's where I got timer / interrupt programming from [3]: A huge stack of copied sheets from several books I don't remember. :) ---------------------------------------------------------------------------- So, I think this is enough. I hope you will be able to program FakeMode with this doc. If you haven't understood yet how FakeMode works, take a look at source in the example program. If you want to see what effects are possible with FakeMode, have a look at Xography's "Party'93 Report"... (You may find it in the internet on WASP.ENG.UFL.EDU, in /pub/msdos/demos/alpha/x, xgy_pr93.zip). (Oh, by the way: You can greet me in your FakeMode productions if you want :) Cheers, Yaka/Xography P.S.: If you have additional questions or suggestions, you can EMail to: uke4@rzstud1.rz.uni-karlsruhe.de (Please only send important messages, I don't want to have my mailbox flooded with "how-can-I-do-this"'s and "how-can-I-do-that"'s... And remember, I haven't got the time to answer all letters I receive, but I will try to read them all.)