Metropoli BBS
VIEWER: fakemode.doc MODE: TEXT (ASCII)
	       ******************************************
	       ** 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.)
[ RETURN TO DIRECTORY ]