File:  [UNIX Amiga Emulator] / uae / src / md-i386-dos / video / vbe.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 16:39:33 2018 UTC (8 years, 3 months ago) by root
Branches: MAIN, BSchmidt
CVS tags: uae068, HEAD
uae-0.6.8

 /*
  * UAE - The Un*x Amiga Emulator
  *
  * DOS VESA BIOS / VGA / Mode X graphics interface.
  *
  * (c) 1996 Gustavo Goedert
  *
  * Originaly based on svga.c -- (c) 1995 Bernd Schmidt
  * Led routines by Michael Sontheimer
  *
  */

#include "sysconfig.h"
#include "sysdeps.h"

#include <dos.h>
#include <go32.h>
#include <dpmi.h>
#include <sys/movedata.h>
#include <sys/exceptn.h>
#include <sys/farptr.h>
#include <conio.h>
#include <fcntl.h>
#include <crt0.h>
#include <signal.h>

#include "config.h"
#include "options.h"
#include "memory.h"
#include "custom.h"
#include "newcpu.h"
#include "keyboard.h"
#include "xwin.h"
#include "keybuf.h"
#include "disk.h"
#include "gui.h"
#include "tui.h"
#include "uae.h"

#define PACKED __attribute__ ((packed))
#define ALIGN(x) ((((int)(x)>>12)<<12)+4096)

enum VBE_MemoryModels { TextMode, CGA_Graphics, HerculesGraphics,
			Planar, PackedPixel, ModeX, DirectColor, YUV } ;

typedef struct {
	char VbeSignature[4] PACKED; // = VBE2
	short VbeVersion PACKED;
	char *OemStringPtr PACKED;
	char Capabilities[4] PACKED;
	unsigned int VideoModePtr PACKED;
	short TotalMemory PACKED;
	short OemSoftwareRev PACKED;
	char *OemVendorNamePtr PACKED;
	char *OemProductNamePtr PACKED;
	char *OemProductRevPtr PACKED;
	char Reserved[222] PACKED;
	char OemData[256] PACKED;
} T_VBE_VbeInfoBlock;

typedef struct {
	// Mandatory information for all VBE revisions
	short ModeAttributes PACKED;
	char WinAAttributes PACKED;
	char WinBAttributes PACKED;
	short WinGranularity PACKED;
	unsigned short WinSize PACKED;
	unsigned short WinASegment PACKED;
	unsigned short WinBSegment PACKED;
	unsigned int WinFuncPtr PACKED;
	short BytesPerScanLine PACKED;
	// Mandatory information for VBE 1.2 and above
	short XResolution PACKED;
	short YResolution PACKED;
	char XCharSize PACKED;
	char YCharSize PACKED;
	char NumberOfPlanes PACKED;
	char BitsPerPixel PACKED;
	char NumberOfBanks PACKED;
	char MemoryModel PACKED;
	char BankSize PACKED;
	char NumberOfImagePages PACKED;
	char ReservedA PACKED; // = 1
	// Direct Color fields (required for direct/6 and YUV/7 memory models)
	char RedMaskSize PACKED;
	char RedFieldPosition PACKED;
	char GreenMaskSize PACKED;
	char GreenFieldPosition PACKED;
	char BlueMaskSize PACKED;
	char BlueFieldPosition PACKED;
	char RsvdMaskSize PACKED;
	char RsvdFieldPosition PACKED;
	char DirectColorModeInfo PACKED;
	// Mandatory information for VBE 2.0 and above
	unsigned int PhysBasePtr PACKED;
	unsigned int OffScreenMemOffset PACKED;
	short OffScreenMemSize PACKED;
	char ReservedB[206] PACKED;
} T_VBE_ModeInfoBlock;

typedef struct {
	short SetWindowOffset PACKED;
	short SetDisplayStartOffset PACKED;
	short SetPrimaryPaletteDataOffset PACKED;
	short PortsMemoryOffset PACKED;
} T_VBE_ProtectedModeInterface;

typedef enum { TText, T16, T256, T256X, T32K, T64K, T16M, TNone } T_ModeType;

typedef struct {
	int ModeWidth, ModeHeight;
	T_ModeType ModeType;
	int BitsPerPixel;
	int BitsPerColor;
} T_BasicModeInfo;

typedef enum { TVBE, TVGA, TModeX } T_ControlType;

typedef struct {
	T_ControlType ControlType;
	int mode;
} T_ControlInfo;

void SetGraphicsMode(void);
void SetModeX(int num);
void LoadPalette(void);
int VBE_GetVbeInfoBlock(void);
int VBE_GetModeInfoBlock(short mode);
int VBE_GetProtectedModeInterface(void);
void VBE_ChangeBankRealInterrupt(short BankNumber);
void VBE_ChangeBankRealMode(short BankNumber);
void VBE_ChangeBankProtectMode(short BankNumber);
void DoFlushLineLinear(int y);
void DoFlushLineBuffered(int y);
void PutLineBanked(char *addr, int target_y);
void PutLineVGA16(char *addr, int target_y);
void PutLineVGAX(char *addr, int target_y);

void (*VBE_ChangeBank)(short BankNumber);
void (*DoFlushLine)(int y);
void (*PutLine)(char *addr, int y);

T_ControlInfo ControlInfo;

int RestoreOldVGAMode = 0, OldVGAMode;

short CurrentBankNumber = 0;
short WriteWindow;
unsigned short WinFuncPtrSegment, WinFuncPtrOffset;

unsigned int SetWindow;
short *PortsMemory;

T_VBE_VbeInfoBlock VBE_VbeInfoBlock;
T_VBE_ModeInfoBlock VBE_ModeInfoBlock;
T_VBE_ProtectedModeInterface *VBE_ProtectedModeInterface;
short VBE_ModeList[128];

unsigned int WinLength, WinStart;
int FastWinCalc = 0;
unsigned int WinMask1, WinMask2;
int HasVbe = 0, HasLinear = 0;
int LineLength, LogicalLineLength;

char *LinearBufferPtr;
char *AlignedLinearBufferPtr;

T_BasicModeInfo BasicModeInfo;
unsigned char PaletteData[768];
_go32_dpmi_seginfo PaletteMem;

T_BasicModeInfo Old_VBE_Table[28] = { { 640, 400, T256, 8, 8 },
				      { 640, 480, T256, 8, 8 },
				      { 800, 600, T16, 8, 4 },
				      { 800, 600, T256, 8, 8 },
				      { 1024, 768, T16, 8, 4 },
				      { 1024, 768, T256, 8, 8 },
				      { 1280, 1024, T16, 8, 4 },
				      { 1280, 1024, T256, 8, 8 },
				      { 80, 60, TText, 0, 0 },
				      { 132, 25, TText, 0, 0 },
				      { 132, 43, TText, 0, 0 },
				      { 132, 50, TText, 0, 0 },
				      { 132, 60, TText, 0, 0 },
				      { 320, 200, T32K, 16, 15 },
				      { 320, 200, T64K, 16, 16 },
				      { 320, 200, T16M, 24, 24 },
				      { 640, 480, T32K, 16, 15 },
				      { 640, 480, T64K, 16, 16 },
				      { 640, 480, T16M, 24, 24 },
				      { 800, 600, T32K, 16, 15 },
				      { 800, 600, T64K, 16, 16 },
				      { 800, 600, T16M, 24, 24 },
				      { 1024, 768, T32K, 16, 15 },
				      { 1024, 768, T64K, 16, 16 },
				      { 1024, 768, T16M, 24, 24 },
				      { 1280, 1024, T32K, 16, 15 },
				      { 1280, 1024, T64K, 16, 16 },
				      { 1280, 1024, T16M, 24, 24 }  };

_go32_dpmi_seginfo old_kbd_handler, new_kbd_handler, mouse_handler;
_go32_dpmi_registers mouse_callback_regs;

int CorrectLines = 0;
int Original_produce_sound;

void my_kbd_handler(void);
void my_mouse_handler(_go32_dpmi_registers *mouse_regs);
int SetupVBEMode(int mode);
void disalocate_memory(int level);
void HandleDisk(int num);
void SaveScreen(void);
void RestoreLEDStatus(void);

/* dummie Amiga Keys */
#define AK_EjectDisk 0xfe
#define AK_ChangeDisk 0xff

/* Command Flags */
#define ResetCPU    0x001
#define EjectDisk0  0x002
#define EjectDisk1  0x004
#define EjectDisk2  0x008
#define EjectDisk3  0x010
#define ChangeDisk0 0x020
#define ChangeDisk1 0x040
#define ChangeDisk2 0x080
#define ChangeDisk3 0x100
#define ChangeOptions 0x200
#define SaveImage     0x400

/* PC Keys */
#define SCODE_CURSORBLOCKUP	103	/* Cursor key block. */
#define SCODE_CURSORBLOCKLEFT 105
#define SCODE_CURSORBLOCKRIGHT 106
#define SCODE_CURSORBLOCKDOWN 108

#define SCODE_INSERT 110
#define SCODE_HOME 102
#define SCODE_PGUP 104
#define SCODE_DELETE 111
#define SCODE_END 107
#define SCODE_PGDN 109

#define SCODE_KEYPAD0	82
#define SCODE_KEYPAD1	79
#define SCODE_KEYPAD2	80
#define SCODE_KEYPAD3	81
#define SCODE_KEYPAD4	75
#define SCODE_KEYPAD5	76
#define SCODE_KEYPAD6	77
#define SCODE_KEYPAD7	71
#define SCODE_KEYPAD8	72
#define SCODE_KEYPAD9	73
#define SCODE_KEYPADENTER	96
#define SCODE_KEYPADPLUS	78
#define SCODE_KEYPADMINUS	74

#define SCODE_Q		16
#define SCODE_W		17
#define SCODE_E		18
#define SCODE_R		19
#define SCODE_T		20
#define SCODE_Y		21
#define SCODE_U		22
#define SCODE_I		23
#define SCODE_O		24
#define SCODE_P		25

#define SCODE_A		30
#define SCODE_S		31
#define SCODE_D		32
#define SCODE_F		33
#define SCODE_G		34
#define SCODE_H		35
#define SCODE_J		36
#define SCODE_K		37
#define SCODE_L		38

#define SCODE_Z		44
#define SCODE_X		45
#define SCODE_C		46
#define SCODE_V		47
#define SCODE_B		48
#define SCODE_N		49
#define SCODE_M		50

#define SCODE_ESCAPE		1
#define SCODE_ENTER		28
#define SCODE_RIGHTCONTROL	97
#define SCODE_CONTROL	97
#define SCODE_RIGHTALT	100
#define SCODE_LEFTCONTROL	29
#define SCODE_LEFTALT	56
#define SCODE_SPACE		57

#define SCODE_F1		59
#define SCODE_F2		60
#define SCODE_F3		61
#define SCODE_F4		62
#define SCODE_F5		63
#define SCODE_F6		64
#define SCODE_F7		65
#define SCODE_F8		66
#define SCODE_F9		67
#define SCODE_F10		68

#define KEY_EVENTRELEASE 0
#define KEY_EVENTPRESS 1

#define SCODE_0 11
#define SCODE_1 2
#define SCODE_2 3
#define SCODE_3 4
#define SCODE_4 5
#define SCODE_5 6
#define SCODE_6 7
#define SCODE_7 8
#define SCODE_8 9
#define SCODE_9 10

#define SCODE_LEFTSHIFT 42
#define SCODE_RIGHTSHIFT 54
#define SCODE_TAB 15

#define SCODE_F11 87
#define SCODE_F12 88
#define SCODE_NEXT 81
#define SCODE_PRIOR 73
#define SCODE_BS 14
/*
#define SCODE_asciicircum 1
*/
#define SCODE_bracketleft 26
#define SCODE_bracketright 27
#define SCODE_comma 51
#define SCODE_period 52
#define SCODE_slash 53
#define SCODE_semicolon 39
#define SCODE_grave 40
#define SCODE_minus 12
#define SCODE_equal 13
#define SCODE_numbersign 41
#define SCODE_ltgt 43
#define SCODE_scrolllock 70

#define SCODE_LWIN95 125
#define SCODE_RWIN95 126
#define SCODE_MWIN95 127

#define SCODE_Caps_Lock 58

static unsigned char escape_keys[128] = {
  0,			0,			0,		0,
  0, 			0,			0,		0,
  0, 			0,			0,		0,
  0, 			0,			0,		0,
  0, 			0,			0,		0,
  0, 			0,			0,		0,
  0,			0,			0,		0,
  SCODE_KEYPADENTER,	SCODE_RIGHTCONTROL,	0,		0,
  0, 			0,			0,		0,
  0, 			0,			0,		0,
  0, 			0,			0,		0,
  0, 			0,			0,		0,
  0, 			0,			0,		0,
  0, 			0,			0,		0,
  0, 			0,			0,		0,
  0, 			0,			0,		0,
  0, 			0,			0,		0,
  0,			0, 			0,		SCODE_HOME,
  SCODE_CURSORBLOCKUP,	SCODE_PGUP,		0,		SCODE_CURSORBLOCKLEFT,
  0,			SCODE_CURSORBLOCKRIGHT,	0,		SCODE_END,
  SCODE_CURSORBLOCKDOWN,SCODE_PGDN,		SCODE_INSERT,	SCODE_DELETE,
  0,			0,			0,		0,
  0,			0,			0,		SCODE_LWIN95,
  SCODE_RWIN95,		SCODE_MWIN95,		0,		0,
  0,			0,			0,		0,
  0,			0,			0,		0,
  0,			0,			0,		0,
  0,			0,			0,		0,
  0,			0,			0,		0,
  0,			0,			0,		0,
  0,			0,			0,		0,
  0,			0,			0,		0
};

static int vsize;
static int CommandFlags = 0;
static int need_dither;

static UBYTE dither_buf[1000];

xcolnr xcolors[4096];

struct vidbuf_description gfxvidinfo;

void setup_brkhandler(void)
{
}

void flush_line(int y)
{
    DoFlushLine(y);
}

void flush_block(int a, int b)
{
    abort();
}

void flush_screen(int a, int b)
{
}

void calc_adjustment(void)
{
    switch (screen_res) {
     case 0: case 1: case 2: /* LoRes, 320xfoo */
	gfxvidinfo.x_adjust = prev_max_diwstop - 320;
	break;

     case 3: /* 640xbar */
	gfxvidinfo.x_adjust = prev_max_diwstop - 640;
	break;
     default:
	gfxvidinfo.x_adjust = 0;
	break;
    }
}

static int colors_allocated;

static int get_color(int r, int g, int b, xcolnr *cnp)
{
    _go32_dpmi_registers int_regs;

    if ((BasicModeInfo.ModeType == T16) && (colors_allocated == 16))
	return -1;
    else if (colors_allocated == 256)
	return -1;
    *cnp = colors_allocated;
    PaletteData[colors_allocated*3+0] = doMask(r, 6, 0);
    PaletteData[colors_allocated*3+1] = doMask(g, 6, 0);
    PaletteData[colors_allocated*3+2] = doMask(b, 6, 0);
    colors_allocated++;
    return 1;
}

static void init_colors(void)
{
    if (need_dither) {
	setup_dither(BasicModeInfo.BitsPerColor, get_color);
    } else {
	colors_allocated = 0;
	if (gfxvidinfo.pixbytes == 1)
	    alloc_colors256(get_color);
	else
	    alloc_colors64k(VBE_ModeInfoBlock.RedMaskSize,
			    VBE_ModeInfoBlock.GreenMaskSize,
			    VBE_ModeInfoBlock.BlueMaskSize,
			    VBE_ModeInfoBlock.RedFieldPosition,
			    VBE_ModeInfoBlock.GreenFieldPosition,
			    VBE_ModeInfoBlock.BlueFieldPosition);
   }
}

int buttonstate[3] = { 0, 0, 0 };
int lastmx, lastmy;
int newmousecounters = 0;

static int keystate[256];

static int scancode2amiga(int scancode)
{
    switch(scancode) {
     case SCODE_A: return AK_A;
     case SCODE_B: return AK_B;
     case SCODE_C: return AK_C;
     case SCODE_D: return AK_D;
     case SCODE_E: return AK_E;
     case SCODE_F: return AK_F;
     case SCODE_G: return AK_G;
     case SCODE_H: return AK_H;
     case SCODE_I: return AK_I;
     case SCODE_J: return AK_J;
     case SCODE_K: return AK_K;
     case SCODE_L: return AK_L;
     case SCODE_M: return AK_M;
     case SCODE_N: return AK_N;
     case SCODE_O: return AK_O;
     case SCODE_P: return AK_P;
     case SCODE_Q: return AK_Q;
     case SCODE_R: return AK_R;
     case SCODE_S: return AK_S;
     case SCODE_T: return AK_T;
     case SCODE_U: return AK_U;
     case SCODE_V: return AK_V;
     case SCODE_W: return AK_W;
     case SCODE_X: return AK_X;
     case SCODE_Y: return AK_Y;
     case SCODE_Z: return AK_Z;

     case SCODE_0: return AK_0;
     case SCODE_1: return AK_1;
     case SCODE_2: return AK_2;
     case SCODE_3: return AK_3;
     case SCODE_4: return AK_4;
     case SCODE_5: return AK_5;
     case SCODE_6: return AK_6;
     case SCODE_7: return AK_7;
     case SCODE_8: return AK_8;
     case SCODE_9: return AK_9;

     case SCODE_KEYPAD0: return AK_NP0;
     case SCODE_KEYPAD1: return AK_NP1;
     case SCODE_KEYPAD2: return AK_NP2;
     case SCODE_KEYPAD3: return AK_NP3;
     case SCODE_KEYPAD4: return AK_NP4;
     case SCODE_KEYPAD5: return AK_NP5;
     case SCODE_KEYPAD6: return AK_NP6;
     case SCODE_KEYPAD7: return AK_NP7;
     case SCODE_KEYPAD8: return AK_NP8;
     case SCODE_KEYPAD9: return AK_NP9;

     case SCODE_F1: return AK_F1;
     case SCODE_F2: return AK_F2;
     case SCODE_F3: return AK_F3;
     case SCODE_F4: return AK_F4;
     case SCODE_F5: return AK_F5;
     case SCODE_F6: return AK_F6;
     case SCODE_F7: return AK_F7;
     case SCODE_F8: return AK_F8;
     case SCODE_F9: return AK_F9;
     case SCODE_F10: return AK_F10;

     case SCODE_BS: return AK_BS;
     case SCODE_LEFTCONTROL: return AK_CTRL;
     case SCODE_RIGHTCONTROL: return !fake_joystick ? AK_CTRL : AK_NP5;
     case SCODE_TAB: return AK_TAB;
     case SCODE_LEFTALT: return AK_LALT;
     case SCODE_RIGHTALT: return AK_RALT;
     case SCODE_ENTER: return AK_RET;
     case SCODE_SPACE: return AK_SPC;
     case SCODE_LEFTSHIFT: return AK_LSH;
     case SCODE_RIGHTSHIFT: return AK_RSH;
     case SCODE_ESCAPE: return AK_ESC;

     case SCODE_INSERT: return AK_HELP;
/*     case SCODE_END:
     case SCODE_HOME: break;*/

     case SCODE_DELETE: return AK_DEL;
     case SCODE_CURSORBLOCKUP: return AK_UP;
     case SCODE_CURSORBLOCKDOWN: return AK_DN;
     case SCODE_CURSORBLOCKLEFT: return AK_LF;
     case SCODE_CURSORBLOCKRIGHT: return AK_RT;

     case SCODE_F11: return AK_BACKSLASH;
/*
     case SCODE_asciicircum: return AK_00;
 */
     case SCODE_bracketleft: return AK_LBRACKET;
     case SCODE_bracketright: return AK_RBRACKET;
     case SCODE_comma: return AK_COMMA;
     case SCODE_period: return AK_PERIOD;
     case SCODE_slash: return AK_SLASH;
     case SCODE_semicolon: return AK_SEMICOLON;
     case SCODE_grave: return AK_QUOTE;
     case SCODE_minus: return AK_MINUS;
     case SCODE_equal: return AK_EQUAL;

	/* This one turns off screen updates. */
     case SCODE_scrolllock: return AK_inhibit;

     case SCODE_PGUP: case SCODE_RWIN95: return AK_RAMI;
     case SCODE_PGDN: case SCODE_LWIN95: return AK_LAMI;

/*#ifdef KBD_LANG_DE*/
     case SCODE_numbersign: return AK_NUMBERSIGN;
     case SCODE_ltgt: return AK_LTGT;
/*#endif*/
     case SCODE_END: return AK_EjectDisk;
     case SCODE_HOME: return AK_ChangeDisk;
    }
    return -1;
}

void my_kbd_handler(void)
{
    static int is_escape = 0;
    int scancode, newstate, akey;
    scancode = inportb(0x60);
    if (scancode == 0xe0) {
	is_escape = 1;
	outportb(0x20, 0x20);
	return;
    }

    /* PAUSE Key */
    if (is_escape > 1) {
	is_escape++;
	if (is_escape == 7) {
	    /* turn on/off sound */
	    if (produce_sound == 0)
		produce_sound = Original_produce_sound;
	    else
		produce_sound = 0;
	    is_escape = 0;
	}
	outportb(0x20, 0x20);
	return;
    }
    if (scancode == 0xe1) {
	is_escape = 2;
	outportb(0x20, 0x20);
	return;
    }

    /* PrintScreen Key */
    if (is_escape && (scancode == 0x37)) {
	CommandFlags |= SaveImage;
	outportb(0x20, 0x20);
	return;
    }

    newstate = !(scancode & 0x80);
    scancode = scancode & 0x7f;
    if (is_escape) {
	scancode = escape_keys[scancode];
	is_escape = 0;
    }
    outportb(0x20, 0x20);

    akey = scancode2amiga(scancode);
    if (scancode == SCODE_Caps_Lock) {
	if (newstate == KEY_EVENTPRESS) {
	    akey = AK_CAPSLOCK;
	    newstate = !keystate[akey];
	} else
	    return;
    }
    if (scancode == SCODE_F12) {
	regs.spcflags |= SPCFLAG_BRK;
	quit_program = 1;
    }

    if (akey == -1)
	return;
    if (akey <= 0xFF) {
	if (keystate[akey] == newstate)
	    return;
	keystate[akey] = newstate;
    }

    if ((akey != AK_EjectDisk) && (akey != AK_ChangeDisk)) {
    if (newstate == KEY_EVENTPRESS) {
	if (akey == AK_inhibit)
	    inhibit_frame ^= 1;
	else
	    record_key (akey << 1);
    } else
	record_key ((akey << 1) | 1);
    }

    /* "Affengriff" */
    if (keystate[AK_CTRL] && keystate[AK_LAMI] && keystate[AK_RAMI])
	CommandFlags |= ResetCPU;
    if (keystate[AK_EjectDisk]) {
	if (keystate[AK_F1])
	    CommandFlags |= EjectDisk0;
	if (keystate[AK_F2])
	    CommandFlags |= EjectDisk1;
	if (keystate[AK_F3])
	    CommandFlags |= EjectDisk2;
	if (keystate[AK_F4])
	    CommandFlags |= EjectDisk3;
    }
    if (keystate[AK_ChangeDisk]) {
	if (keystate[AK_F1])
	    CommandFlags |= ChangeDisk0;
	if (keystate[AK_F2])
	    CommandFlags |= ChangeDisk1;
	if (keystate[AK_F3])
	    CommandFlags |= ChangeDisk2;
	if (keystate[AK_F4])
	    CommandFlags |= ChangeDisk3;
    }
}

void my_mouse_handler(_go32_dpmi_registers *mouse_regs)
{
    lastmx = (short)mouse_regs->x.si;
    lastmy = (short)mouse_regs->x.di;
    buttonstate[0] = mouse_regs->x.bx & 1;
    buttonstate[1] = mouse_regs->x.bx & 4;
    buttonstate[2] = mouse_regs->x.bx & 2;
}

int graphics_init(void)
{
    int i;
    _go32_dpmi_registers int_regs;
    unsigned int dosmempos;
    extern int kpb_first, kpb_last;
    extern int keybuf[256];
    int WantedWidth, WantedHeight;
    T_ModeType WantedType;
    int FoundMode;
    int VideoMemorySize, ExtraVideoMemorySize;

    /* small check and setup */
    freopen("uae.log", "w", stderr);
    if(ersatzkickfile && disk_empty(0)) {
	fprintf (stderr, "No KickStart and no bootdisk, giving up.\n");
	return 0;
    }
    _go32_want_ctrl_break(1);
    __djgpp_set_ctrl_c(0);
    signal(SIGFPE, SIG_IGN);

    /* save produce_sound state */
    Original_produce_sound = produce_sound;

    /* enable case on LFN systems (eg. win95) */
    if (_USE_LFN)
	_crt0_startup_flags = _CRT0_FLAG_PRESERVE_FILENAME_CASE;

    /* set up keyboard handler */
    _go32_dpmi_get_protected_mode_interrupt_vector(9, &old_kbd_handler);
    new_kbd_handler.pm_offset = (int)my_kbd_handler;
    if (_go32_dpmi_allocate_iret_wrapper(&new_kbd_handler) != 0) {
	fprintf (stderr, "Can't allocate keyboard iret_wrapper.\n");
	return 0;
    }
    if (_go32_dpmi_set_protected_mode_interrupt_vector(9, &new_kbd_handler) != 0) {
	fprintf (stderr, "Can't set protected mode interrupt vector.\n");
	return 0;
    }

    /* set up mouse handler */
    int_regs.x.ax=0;
    int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
    _go32_dpmi_simulate_int(0x33, &int_regs);
    if (int_regs.x.ax==0) {
	fprintf (stderr, "Mouse driver not present.\n");
	disalocate_memory(1);
	return 0;
    }
    mouse_handler.pm_offset = (int)my_mouse_handler;
    if (_go32_dpmi_allocate_real_mode_callback_retf(&mouse_handler, &mouse_callback_regs) != 0) {
	fprintf (stderr, "Can't allocate mouse callback_retf.\n");
	disalocate_memory(1);
	return 0;
    }
    int_regs.x.ax=0xc;
    int_regs.x.cx=0x7f;
    int_regs.x.es=mouse_handler.rm_segment;
    int_regs.x.dx=mouse_handler.rm_offset;
    int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
    _go32_dpmi_simulate_int(0x33, &int_regs);

    /* Save Old VGA Mode */
    int_regs.x.ax = 0x1C;
    int_regs.h.ah = 0x0F;
    int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
    _go32_dpmi_simulate_int(0x10, &int_regs);
    OldVGAMode = int_regs.h.al;

    /* Allocate Palette Memory */
    PaletteMem.size = 48;
    if (_go32_dpmi_allocate_dos_memory(&PaletteMem)) {
	fprintf (stderr, "Can't allocate real mode memory for palette data.\n");
	disalocate_memory(2);
	return 0;
    }

    if (correct_aspect)
	CorrectLines = 1;
    if (screen_res < 2)
	correct_aspect = 0;

    /* set up wanted video mode information */
    switch (screen_res) {
     case 0: WantedWidth=320; WantedHeight=200; break;
     case 1: WantedWidth=320; WantedHeight=240; break;
     case 2: WantedWidth=320; WantedHeight=400; break;
     case 3: WantedWidth=640; WantedHeight=480; break;
     case 4: WantedWidth=800; WantedHeight=600; break;
     default:
	fprintf(stderr, "Invalid screen mode.\n");
	disalocate_memory(3);
	return 0;
    }
    switch (color_mode) {
     case 0: WantedType=T256; need_dither=0; break;
     case 1: WantedType=T32K; need_dither=0; break;
     case 2: WantedType=T64K; need_dither=0; break;
     case 3: WantedType=T256; need_dither=1; break;
     case 4: WantedType=T16;  need_dither=1; break;
     case 5: WantedType=T16M; need_dither=0; break;
     default:
	fprintf(stderr, "Invalid color mode.\n");
	disalocate_memory(3);
	return 0;
    }

    FoundMode = 0;

    /* check for vbe */
    HasVbe = VBE_GetVbeInfoBlock();

    if (HasVbe) {
	dosmempos = (VBE_VbeInfoBlock.VideoModePtr >> 16) * 16 +
				 (VBE_VbeInfoBlock.VideoModePtr & 0xffff);
	dosmemget(dosmempos, 256, VBE_ModeList);
	VBE_ModeList[127] = -1;
	for (i=0; i<28; i++) {
	    if ((WantedWidth == Old_VBE_Table[i].ModeWidth) &&
		(WantedHeight == Old_VBE_Table[i].ModeHeight) &&
		(WantedType == Old_VBE_Table[i].ModeType)) {
		if (VBE_GetModeInfoBlock(0x100+i)) {
		    BasicModeInfo = Old_VBE_Table[i];
		    FoundMode = SetupVBEMode(0x100+i);
			    }
		break;
			}
		    }
	for (i=0; (VBE_VbeInfoBlock.VbeVersion >= 0x0200) && (!FoundMode) && (VBE_ModeList[i] != -1) ; i++) {
	    if ((VBE_ModeList[i] != (short)0x81ff) && VBE_GetModeInfoBlock(VBE_ModeList[i])) {

		    BasicModeInfo.ModeWidth = VBE_ModeInfoBlock.XResolution;
		    BasicModeInfo.ModeHeight = VBE_ModeInfoBlock.YResolution;
		    if ((VBE_ModeInfoBlock.MemoryModel == PackedPixel) && (VBE_ModeInfoBlock.BitsPerPixel == 8)) {
			BasicModeInfo.ModeType = T256;
			BasicModeInfo.BitsPerColor = 8;
		    VBE_ModeInfoBlock.BitsPerPixel = 8;
		    } else if (VBE_ModeInfoBlock.MemoryModel == DirectColor) {
		    int BitsperColor = VBE_ModeInfoBlock.RedMaskSize+VBE_ModeInfoBlock.GreenMaskSize+VBE_ModeInfoBlock.BlueMaskSize;
		    if (BitsperColor == 15) {
			    BasicModeInfo.ModeType = T32K;
			VBE_ModeInfoBlock.BitsPerPixel = 16;
		    } else if (BitsperColor == 16) {
			    BasicModeInfo.ModeType = T64K;
			VBE_ModeInfoBlock.BitsPerPixel = 16;
		    } else if (BitsperColor == 24) {
			    BasicModeInfo.ModeType = T16M;
			if (VBE_ModeInfoBlock.RsvdMaskSize == 8)
			    VBE_ModeInfoBlock.BitsPerPixel = 32;
			    else
			    VBE_ModeInfoBlock.BitsPerPixel = 24;
			} else
			BasicModeInfo.ModeType = TNone;
		    BasicModeInfo.BitsPerColor = BitsperColor;
		    } else if (VBE_ModeInfoBlock.MemoryModel == Planar) {
			BasicModeInfo.ModeType = T16;
			BasicModeInfo.BitsPerColor = 4;
		    VBE_ModeInfoBlock.BitsPerPixel = 8;
		    } else
		    BasicModeInfo.ModeType = TNone;

		if ((BasicModeInfo.ModeType == T256) || (BasicModeInfo.ModeType == T16))
		    VBE_ModeInfoBlock.BitsPerPixel = 8;
		BasicModeInfo.BitsPerPixel = VBE_ModeInfoBlock.BitsPerPixel;
		if (WantedWidth == BasicModeInfo.ModeWidth &&
		    WantedHeight == BasicModeInfo.ModeHeight &&
		    WantedType == BasicModeInfo.ModeType) {
		    FoundMode = 1;
		}
	    }
	    if (FoundMode && (VBE_ModeInfoBlock.ModeAttributes & 1))
		FoundMode = SetupVBEMode(VBE_ModeList[i]);
	}
    }

    /* if we don't have VBE, see if Normal VGA can do the job... */
    if (!FoundMode) {
	if (screen_res==0) {
	    if (WantedType == T256) {
		BasicModeInfo.ModeWidth=320;
		BasicModeInfo.ModeHeight=200;
		BasicModeInfo.ModeType=T256;
		BasicModeInfo.BitsPerPixel=8;
		BasicModeInfo.BitsPerColor=8;
		if (!no_xhair) {
		ExtraVideoMemorySize = 2560;
		VideoMemorySize = ALIGN(64000);
		LinearBufferPtr = malloc(VideoMemorySize + ExtraVideoMemorySize);
		if (LinearBufferPtr != NULL) {
		    AlignedLinearBufferPtr = (char *) ALIGN(LinearBufferPtr + ExtraVideoMemorySize);
			if (!__djgpp_map_physical_memory(AlignedLinearBufferPtr, VideoMemorySize, 0xa0000)) {
			HasLinear = 1;
			    DoFlushLine = DoFlushLineLinear;
			} else
			free(LinearBufferPtr);
		}
		}
		if (!HasLinear) {
		    WinLength = 65536;
		    WinStart = 0xa0000;
		    FastWinCalc = 1;
		    WinMask1 = 16;
		    WinMask2 = WinLength-1;
		    DoFlushLine = DoFlushLineBuffered;
		    PutLine = PutLineBanked;
		}
		int_regs.h.ah = 0x00;
		int_regs.h.al = 0x13;
		int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
		_go32_dpmi_simulate_int(0x10, &int_regs);
		ControlInfo.ControlType = TVGA;
		ControlInfo.mode = 0x13;
		LineLength = 320;
		LogicalLineLength = 320;
		FoundMode = 1;
	    } else if (WantedType == T16) {
		BasicModeInfo.ModeWidth=320;
		BasicModeInfo.ModeHeight=200;
		BasicModeInfo.ModeType=T16;
		BasicModeInfo.BitsPerPixel=8;
		BasicModeInfo.BitsPerColor=4;
		WinLength = 65536;
		WinStart = 0xa0000;
		DoFlushLine = DoFlushLineBuffered;
		PutLine = PutLineVGA16;
		int_regs.h.ah = 0x00;
		int_regs.h.al = 0xd;
		int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
		_go32_dpmi_simulate_int(0x10, &int_regs);
		ControlInfo.ControlType = TVGA;
		ControlInfo.mode = 0xd;
		LineLength = 40;
		LogicalLineLength = 40;
		FoundMode = 1;
	    }
	} else if ((screen_res==1) && (WantedType==T256)) {
	    BasicModeInfo.ModeWidth=320;
	    BasicModeInfo.ModeHeight=240;
	    BasicModeInfo.ModeType=T256X;
	    BasicModeInfo.BitsPerPixel=8;
	    BasicModeInfo.BitsPerColor=8;
	    WinLength = 65536;
	    WinStart = 0xa0000;
	    DoFlushLine = DoFlushLineBuffered;
	    PutLine = PutLineVGAX;
	    ControlInfo.ControlType = TModeX;
	    ControlInfo.mode = 1;
	    LineLength = 80;
	    LogicalLineLength = 80;
	    SetModeX(1);
	    FoundMode = 1;
	} else if ((screen_res==2) && (WantedType==T256)) {
	    BasicModeInfo.ModeWidth=320;
	    BasicModeInfo.ModeHeight=400;
	    BasicModeInfo.ModeType=T256X;
	    BasicModeInfo.BitsPerPixel=8;
	    BasicModeInfo.BitsPerColor=8;
	    WinLength = 65536;
	    WinStart = 0xa0000;
	    DoFlushLine = DoFlushLineBuffered;
	    PutLine = PutLineVGAX;
	    ControlInfo.ControlType = TModeX;
	    ControlInfo.mode = 2;
	    LineLength = 80;
	    LogicalLineLength = 80;
	    SetModeX(2);
	    FoundMode = 1;
	} else if ((screen_res==3) && (WantedType==T16)) {
	    BasicModeInfo.ModeWidth=640;
	    BasicModeInfo.ModeHeight=480;
	    BasicModeInfo.ModeType=T16;
	    BasicModeInfo.BitsPerPixel=8;
	    BasicModeInfo.BitsPerColor=4;
	    WinLength = 65536;
	    WinStart = 0xa0000;
	    DoFlushLine = DoFlushLineBuffered;
	    PutLine = PutLineVGA16;
	    int_regs.h.ah = 0x00;
	    int_regs.h.al = 0x12;
	    int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
	    _go32_dpmi_simulate_int(0x10, &int_regs);
	    ControlInfo.ControlType = TVGA;
	    ControlInfo.mode = 0x12;
	    LineLength = 80;
	    LogicalLineLength = 80;
	    FoundMode = 1;
	}
    }

    if (!FoundMode) {
	fprintf(stderr, "Sorry, this combination of color and video mode is not available.\n");
	disalocate_memory(3);
	return 0;
    }

    if (ControlInfo.ControlType == TVBE) {
	fprintf (stderr, "VESA BIOS Extension version %d.%d found.\n", VBE_VbeInfoBlock.VbeVersion >> 8, VBE_VbeInfoBlock.VbeVersion & 0xFF);
	if (HasLinear)
	    fprintf (stderr, "Using Linear FrameBuffer.\n");
	if (VBE_ChangeBank == VBE_ChangeBankProtectMode)
	    fprintf (stderr, "Using Protected Mode Bank Switch Function.\n");
	if (VBE_ChangeBank == VBE_ChangeBankRealMode)
	    fprintf (stderr, "Using Real Mode Bank Switch Function.\n");
	if (VBE_ChangeBank == VBE_ChangeBankRealInterrupt)
	    fprintf (stderr, "Using Real Mode Bank Switch Interrupt Function.\n");
    } else {
	fprintf (stderr, "Using normal VGA hardware.\n");
	if (HasLinear)
	    fprintf (stderr, "Using Linear FrameBuffer. (vga memory fits on a single segment)\n");
	if (screen_res>0)
	    fprintf (stderr, "Using VGA Mode X.\n");
    }
    RestoreOldVGAMode = 1;

    if (!need_dither)
	gfxvidinfo.pixbytes = (BasicModeInfo.BitsPerPixel >> 3);
    else
	gfxvidinfo.pixbytes = 2;

    vsize = correct_aspect ? 2*numscrlines : numscrlines;
    gfxvidinfo.maxblocklines = 0;

    if (HasLinear)
	HasLinear = !need_dither;
    if (HasLinear) {
	gfxvidinfo.rowbytes = LogicalLineLength;
	if (correct_aspect) {
	    if (screen_res == 4) {
		gfxvidinfo.bufmem = AlignedLinearBufferPtr + (15 * LogicalLineLength);
		gfxvidinfo.maxline = 585;
	    } else {
		gfxvidinfo.bufmem = AlignedLinearBufferPtr - (8 * LogicalLineLength);
		gfxvidinfo.maxline = BasicModeInfo.ModeHeight + 8;
	    }
	} else {
	    if (screen_res == 4) {
		gfxvidinfo.bufmem = AlignedLinearBufferPtr + (157 * LogicalLineLength);
		gfxvidinfo.maxline = 443;
	    } else if (screen_res == 3) {
		gfxvidinfo.bufmem = AlignedLinearBufferPtr + (97 * LogicalLineLength);
		gfxvidinfo.maxline = 383;
	    } else if (screen_res == 2) {
		gfxvidinfo.bufmem = AlignedLinearBufferPtr + (57 * LogicalLineLength);
		gfxvidinfo.maxline = 343;
	    } else {
		gfxvidinfo.bufmem = AlignedLinearBufferPtr - (8 * LogicalLineLength);
		gfxvidinfo.maxline = BasicModeInfo.ModeHeight + 8;
	    }
	}
    } else {
	gfxvidinfo.rowbytes = BasicModeInfo.ModeWidth * gfxvidinfo.pixbytes;
	gfxvidinfo.bufmem = malloc(gfxvidinfo.rowbytes * vsize);
	memset(gfxvidinfo.bufmem, 0, gfxvidinfo.rowbytes * vsize);
	gfxvidinfo.maxline = vsize;
    }

    gfxvidinfo.maxlinetoscr = BasicModeInfo.ModeWidth < 800 ? BasicModeInfo.ModeWidth : 0;
    gfxvidinfo.x_adjust = 0;

    /* initialize colors/palette */
    init_colors();
    dosmemput(PaletteData, 768, PaletteMem.rm_segment*16);
    LoadPalette();

    /* initialize mouse data */
    buttonstate[0] = buttonstate[1] = buttonstate[2] = 0;
    lastmx = lastmy = 0;
    newmousecounters = 0;

    /* initialize keyboard data */
    for(i = 0; i < 256; i++)
	keystate[i] = 0;

    /* Lock Data Touched by Interrupt Handlers */
    _go32_dpmi_lock_code(scancode2amiga, (unsigned int)graphics_init-(unsigned int)scancode2amiga);
    _go32_dpmi_lock_data(escape_keys, sizeof(escape_keys));
    _go32_dpmi_lock_data(&regs, sizeof(regs));
    _go32_dpmi_lock_data(keystate, sizeof(keystate));
    _go32_dpmi_lock_data(&CommandFlags, sizeof(CommandFlags));
    _go32_dpmi_lock_data(&lastmx, sizeof(lastmx));
    _go32_dpmi_lock_data(&lastmy, sizeof(lastmy));
    _go32_dpmi_lock_data(buttonstate, sizeof(buttonstate));
    _go32_dpmi_lock_data(&old_kbd_handler, sizeof(old_kbd_handler));
    _go32_dpmi_lock_data(&new_kbd_handler, sizeof(new_kbd_handler));
    _go32_dpmi_lock_data(&mouse_handler, sizeof(mouse_handler));
    _go32_dpmi_lock_data(&mouse_callback_regs, sizeof(mouse_callback_regs));
    _go32_dpmi_lock_code(record_key, (unsigned int)keybuf_init-(unsigned int)record_key);
    _go32_dpmi_lock_data(&kpb_first, sizeof(kpb_first));
    _go32_dpmi_lock_data(&kpb_last, sizeof(kpb_last));
    _go32_dpmi_lock_data(keybuf, sizeof(keybuf));
    _go32_dpmi_lock_data(&Original_produce_sound, sizeof(Original_produce_sound));
    _go32_dpmi_lock_data(&produce_sound, sizeof(produce_sound));

    return 1;
}

int SetupVBEMode(int mode) {
    int i;
    _go32_dpmi_registers int_regs;
    int VideoMemorySize, ExtraVideoMemorySize;

    HasLinear = 0;
    if ((VBE_ModeInfoBlock.ModeAttributes & (1<<7)) && !no_xhair && (VBE_ModeInfoBlock.MemoryModel != Planar)) {
	if (screen_res<4)
	    ExtraVideoMemorySize = VBE_ModeInfoBlock.BytesPerScanLine * 8;
	else
	    ExtraVideoMemorySize = 0;
	VideoMemorySize = ALIGN(VBE_ModeInfoBlock.BytesPerScanLine * VBE_ModeInfoBlock.YResolution);
	LinearBufferPtr = malloc(VideoMemorySize + ExtraVideoMemorySize);
	if (LinearBufferPtr != NULL) {
	    AlignedLinearBufferPtr = (char *) ALIGN(LinearBufferPtr + ExtraVideoMemorySize);
	    if (!__djgpp_map_physical_memory(AlignedLinearBufferPtr, VideoMemorySize, VBE_ModeInfoBlock.PhysBasePtr))
		HasLinear = 1;
	    else
		free(LinearBufferPtr);
	}
	if (HasLinear) {
	    int_regs.x.ax = 0x4F02;
	    int_regs.x.bx = mode | (1<<14);
	    int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
	    _go32_dpmi_simulate_int(0x10, &int_regs);
	    ControlInfo.ControlType = TVBE;
	    ControlInfo.mode = mode | (1<<14);
	    DoFlushLine = DoFlushLineLinear;
	}
    }
    if (!HasLinear) {
	if (!(VBE_ModeInfoBlock.ModeAttributes & (1<<6))) {
	    int_regs.x.ax = 0x4F02;
	    int_regs.x.bx = mode;
	    int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
	    _go32_dpmi_simulate_int(0x10, &int_regs);
	    ControlInfo.ControlType = TVBE;
	    ControlInfo.mode = mode;
	    if ((VBE_VbeInfoBlock.VbeVersion >= 0x0200) && VBE_GetProtectedModeInterface()) {
		/* Test memory need for linear memory selector */
		VBE_ChangeBank = VBE_ChangeBankProtectMode;
	    } else if (VBE_ModeInfoBlock.WinFuncPtr != 0) {
		WinFuncPtrSegment = VBE_ModeInfoBlock.WinFuncPtr >> 16;
		WinFuncPtrOffset = VBE_ModeInfoBlock.WinFuncPtr & 0xffff;
		VBE_ChangeBank = VBE_ChangeBankRealMode;
	    } else
		VBE_ChangeBank = VBE_ChangeBankRealInterrupt;
	    WinLength = VBE_ModeInfoBlock.WinSize * 1024;
	    if ((VBE_ModeInfoBlock.WinAAttributes & 4) || (!(VBE_ModeInfoBlock.WinBAttributes & 4))) {
		WriteWindow = 0;
		WinStart = VBE_ModeInfoBlock.WinASegment * 16;
	    } else {
		WriteWindow = 1;
		WinStart = VBE_ModeInfoBlock.WinBSegment * 16;
	    }
	    /* You never know... */
	    if (WinStart == 0)
		WinStart = 0xa0000;
	    if (WinLength == 0)
		WinLength = 65536;
	    for (i=1; i<32; i++) {                             // This was a bug!
		if (((unsigned int) 1<<i) == WinLength) {
		    FastWinCalc = 1;
		    WinMask1 = i;
		    WinMask2 = WinLength-1;
		    break;
		}
	    }
	    DoFlushLine = DoFlushLineBuffered;
	    if (VBE_ModeInfoBlock.MemoryModel != Planar)
		PutLine = PutLineBanked;
	    else
		PutLine = PutLineVGA16;
	} else
	    return 0;
    }
    LineLength = (VBE_ModeInfoBlock.BitsPerPixel >> 3) * BasicModeInfo.ModeWidth;
    LogicalLineLength = VBE_ModeInfoBlock.BytesPerScanLine;
    return 1;
}

void graphics_leave(void)
{
    _go32_dpmi_registers int_regs;
    FILE *fp;
    char inChar;

    if (RestoreOldVGAMode) {
	dumpcustom();
	int_regs.x.ax = 0x1C;
	int_regs.h.ah = 0x00;
	int_regs.h.al = OldVGAMode;
	int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
	_go32_dpmi_simulate_int(0x10, &int_regs);
	disalocate_memory(3);
    }

    freopen("CON", "w", stderr);
    fp = fopen("uae.log","r");
    if(fp)
    {
	inChar = getc(fp);
	while(inChar != EOF)
	{
	    putchar(inChar);
	    inChar=getc(fp);
	}
	fclose(fp);
    }
}

void disalocate_memory(int level) {
    _go32_dpmi_registers int_regs;

    if (level>=1) {
    _go32_dpmi_set_protected_mode_interrupt_vector(9, &old_kbd_handler);
    _go32_dpmi_free_iret_wrapper(&new_kbd_handler);
    }
    if (level>=2) {
	int_regs.x.ax=0xc;
	int_regs.x.cx=0x0;
	int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
	_go32_dpmi_simulate_int(0x33, &int_regs);
	_go32_dpmi_free_real_mode_callback(&mouse_handler);
    }
    if (level>=3)
	_go32_dpmi_free_dos_memory(&PaletteMem);
}

void handle_events(void)
{
    if (CommandFlags) {
	if (CommandFlags & ResetCPU) {
	    m68k_reset();
	    CommandFlags &= ~ResetCPU;
	}
	if (CommandFlags & EjectDisk0) {
	    disk_eject(0);
	    CommandFlags &= ~EjectDisk0;
	}
	if (CommandFlags & EjectDisk1) {
	    disk_eject(1);
	    CommandFlags &= ~EjectDisk1;
	}
	if (CommandFlags & EjectDisk2) {
	    disk_eject(2);
	    CommandFlags &= ~EjectDisk2;
	}
	if (CommandFlags & EjectDisk3) {
	    disk_eject(3);
	    CommandFlags &= ~EjectDisk3;
	}
	if (CommandFlags & ChangeDisk0) {
	    HandleDisk(0);
	    keystate[AK_ChangeDisk] = 0;
	    keystate[AK_F1] = 0;
	    record_key ((AK_F1 << 1) | 1);
	    CommandFlags &= ~ChangeDisk0;
	}
	if (CommandFlags & ChangeDisk1) {
	    HandleDisk(1);
	    keystate[AK_ChangeDisk] = 0;
	    keystate[AK_F2] = 0;
	    record_key ((AK_F2 << 1) | 1);
	    CommandFlags &= ~ChangeDisk1;
	}
	if (CommandFlags & ChangeDisk2) {
	    HandleDisk(2);
	    keystate[AK_ChangeDisk] = 0;
	    keystate[AK_F3] = 0;
	    record_key ((AK_F3 << 1) | 1);
	    CommandFlags &= ~ChangeDisk2;
	}
	if (CommandFlags & ChangeDisk3) {
	    HandleDisk(3);
	    keystate[AK_ChangeDisk] = 0;
	    keystate[AK_F4] = 0;
	    record_key ((AK_F4 << 1) | 1);
	    CommandFlags &= ~ChangeDisk3;
	}
	if (CommandFlags & SaveImage) {
	    SaveScreen();
	    CommandFlags &= ~SaveImage;
	}
    }
}

int debuggable(void)
{
    return 0;
}

int needmousehack(void)
{
    return 0;
}

// ***MS Now the scroll-lock-led does the same as the amiga power-led

UBYTE ubSavedLED;

void RestoreLEDStatus(void)
{

    _go32_dpmi_registers regs;

    _farsetsel( _dos_ds );

    _farnspokeb( (0x40<<4)+0x17, ubSavedLED );

    regs.x.ax = 0x0100;
    regs.x.ss=regs.x.sp=regs.x.flags=0;
    _go32_dpmi_simulate_int(0x16, &regs);

}

void LED(int on)
{

    static int OldStat=-1;


    _go32_dpmi_registers regs;

    if( OldStat != on )     // Avoid more int's than necessary
    {
        _farsetsel( _dos_ds );

        if( OldStat == -1 )         // Am I here the first time ?
        {
            ubSavedLED = _farnspeekb( (0x40<<4)+0x17 );    // Save LED-status
            atexit( RestoreLEDStatus );

        }

	if( !on )
            _farnspokeb( (0x40<<4)+0x17, _farnspeekb( (0x40<<4)+0x17 ) | 16 );
        else
            _farnspokeb( (0x40<<4)+0x17, _farnspeekb( (0x40<<4)+0x17 ) & ~16 );

        regs.x.ax = 0x0100;
        regs.x.ss=regs.x.sp=regs.x.flags=0;
        _go32_dpmi_simulate_int(0x16, &regs);

        OldStat = on;
    }


}

void target_specific_usage(void)
{
    printf("  -S n                     : Sound emulation accuracy (n = 0, 1, 2 or 3)\n"
	   "                             For sound emulation, n = 2 is recommended\n");
    printf("  -b n                     : Use n bits for sound output (8 or 16)\n");
    printf("  -R n                     : Use n Hz to output sound. Common values are\n"
	   "                             22050 Hz or 44100 Hz\n");
    printf("  -l lang                  : Set keyboard language to lang, where lang is\n"
	   "                             DE or US\n");
    printf("  -x                       : Don't use linear framebuffer, even if available.\n");
    printf("  -p filename              : Use filename to save printer output (i.e. PRN)\n");
    printf("  -I device                : Name of the used serial device (i.e. AUX)\n");
}

void HandleDisk(int num)
{
    int i, memsize = 0;
    char *ptr=NULL, buf[256], *save_mem = NULL;
    _go32_dpmi_registers int_regs;
    extern char *adfdefaultdir;

    _go32_dpmi_set_protected_mode_interrupt_vector(9, &old_kbd_handler);

    if (HasLinear) {
	memsize = LogicalLineLength * BasicModeInfo.ModeHeight;
	save_mem = malloc(memsize);
	if (save_mem != NULL)
	    memcpy(save_mem, AlignedLinearBufferPtr, memsize);
    }
    /* Set text mode */
    int_regs.x.ax = 0x1C;
    int_regs.h.ah = 0x00;
    int_regs.h.al = 0x03;
    int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
    _go32_dpmi_simulate_int(0x10, &int_regs);

    tui_setup();
    switch(num) {
     case 0:
	tui_gotoxy(5, 4);
	tui_puts("DF0:");
	ptr = tui_filereq("*.adf", df0);
	break;
     case 1:
	tui_gotoxy(5, 4);
	tui_puts("DF1:");
	ptr = tui_filereq("*.adf", df1);
	break;
     case 2:
	tui_gotoxy(5, 4);
	tui_puts("DF2:");
	ptr = tui_filereq("*.adf", df2);
	break;
     case 3:
	tui_gotoxy(5, 4);
	tui_puts("DF3:");
	ptr = tui_filereq("*.adf", df3);
	break;
    }
    tui_shutdown();
    if (ptr != NULL) {
	strcpy(buf, ptr);
	disk_insert(num, buf);
    }

    _go32_dpmi_set_protected_mode_interrupt_vector(9, &new_kbd_handler);

    /* Set graphics mode */
    SetGraphicsMode();
    /* Restore screen */
    if ((gfxvidinfo.pixbytes == 1) || need_dither)
	LoadPalette();
    if (HasLinear) {
	if (save_mem != NULL) {
	    memcpy(AlignedLinearBufferPtr, save_mem, memsize);
	    free(save_mem);
	}
    } else {
	for(i=0; i<vsize; i++)
	    flush_line(i);
    }
}

void SaveScreen(void) {
	static int err=0, lastsave=0;
	char filename[13];
	FILE *imagefile;
	char targaheader[18];
	int i;

	if (err)
	    return;

	while(1) {
	    sprintf(filename, "uae%05d.tga", lastsave);
	    imagefile = fopen(filename, "rb");
	    if (imagefile == NULL) {
		imagefile = fopen(filename, "wb");
		if (imagefile == NULL) {
		    err = 1;
		    return;
		}
		break;
	    }
	    fclose(imagefile);
	    lastsave++;
	    if (lastsave == 100000) {
		err = 1;
		return;
	    }
	}
	memset(targaheader, 0, 18);
	if (BasicModeInfo.BitsPerPixel == 8) {
	    targaheader[1] = 1;
	    targaheader[2] = 1;
	    *((unsigned short *)&(targaheader[5])) = 256;
	    targaheader[7] = 24;
	} else
	    targaheader[2] = 2;
	*((unsigned short *)&(targaheader[12])) = BasicModeInfo.ModeWidth;
	*((unsigned short *)&(targaheader[14])) = gfxvidinfo.maxline;
	targaheader[16] = BasicModeInfo.BitsPerPixel;
	targaheader[17] = 0x20;
	fwrite(targaheader, 18, 1, imagefile);
	if (BasicModeInfo.BitsPerPixel == 8) {
	    for(i=0; i<256; i++) {
	       fputc(PaletteData[i*3+2]<<2, imagefile);
	       fputc(PaletteData[i*3+1]<<2, imagefile);
	       fputc(PaletteData[i*3+0]<<2, imagefile);
	    }
	}
	if (need_dither) {
	    for(i=0; i<gfxvidinfo.maxline; i++) {
		DitherLine(dither_buf, (UWORD *)(gfxvidinfo.bufmem + i*gfxvidinfo.rowbytes), 0, i, BasicModeInfo.ModeWidth, BasicModeInfo.BitsPerPixel);
		fwrite(dither_buf, BasicModeInfo.ModeWidth, 1, imagefile);
	    }
	} else {
	    if (BasicModeInfo.BitsPerColor!=16)
		fwrite(gfxvidinfo.bufmem, gfxvidinfo.rowbytes * gfxvidinfo.maxline, 1, imagefile);
	    else {
		int r, g, b, c;

		for(i=0; i<(gfxvidinfo.rowbytes * gfxvidinfo.maxline); i=i+2) {
		    r = (*((int *)&(gfxvidinfo.bufmem[i])) >> VBE_ModeInfoBlock.RedFieldPosition) & ((1<<VBE_ModeInfoBlock.RedMaskSize)-1);
		    g = (*((int *)&(gfxvidinfo.bufmem[i])) >> VBE_ModeInfoBlock.GreenFieldPosition) & ((1<<VBE_ModeInfoBlock.GreenMaskSize)-1);
		    b = (*((int *)&(gfxvidinfo.bufmem[i])) >> VBE_ModeInfoBlock.BlueFieldPosition) & ((1<<VBE_ModeInfoBlock.BlueMaskSize)-1);
		    if (VBE_ModeInfoBlock.RedMaskSize == 6)
			r = r>>1;
		    if (VBE_ModeInfoBlock.GreenMaskSize == 6)
			g = g>>1;
		    if (VBE_ModeInfoBlock.BlueMaskSize == 6)
			b = b>>1;
		    c = b | (g<<5) | (r<<10);
		    fwrite(&c, 2, 1, imagefile);
		}
	    }
	}
	fclose(imagefile);
}

void SetGraphicsMode(void) {
    _go32_dpmi_registers int_regs;

    switch(ControlInfo.ControlType) {
	case TVBE:
	    VBE_GetModeInfoBlock(ControlInfo.mode);
	    int_regs.x.ax = 0x4F02;
	    int_regs.x.bx = ControlInfo.mode;
	    int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
	    _go32_dpmi_simulate_int(0x10, &int_regs);
	    break;
	case TVGA:
	    int_regs.h.ah = 0x00;
	    int_regs.h.al = ControlInfo.mode;
	    int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
	    _go32_dpmi_simulate_int(0x10, &int_regs);
	    break;
	case TModeX:
	    SetModeX(ControlInfo.mode);
	    break;
    }
}

void SetModeX(int num) {
    _go32_dpmi_registers int_regs;
    int i;
    char cleared[320];

    int_regs.h.ah = 0x00;
    int_regs.h.al = 0x13;
    int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
    _go32_dpmi_simulate_int(0x10, &int_regs);
    for (i=0; i<320; i++)
	cleared[i] = 0;
    outportw(0x3c4, 0x604);
    if (num==1) {
	outportw(0x3c4, 0x0100);
	outportb(0x3c2, 0x00e3);
	outportw(0x3c4, 0x0300);
	outportw(0x3d4, 0x2c11);
	outportw(0x3d4, 0x5f00);
	outportw(0x3d4, 0x4f01);
	outportw(0x3d4, 0x5002);
	outportw(0x3d4, 0x8203);
	outportw(0x3d4, 0x5404);
	outportw(0x3d4, 0x8005);
	outportw(0x3d4, 0x0d06);
	outportw(0x3d4, 0x3e07);
	outportw(0x3d4, 0x0008);
	outportw(0x3d4, 0x4109);
	outportw(0x3d4, 0x000a);
	outportw(0x3d4, 0x000b);
	outportw(0x3d4, 0x000c);
	outportw(0x3d4, 0x000d);
	outportw(0x3d4, 0x000e);
	outportw(0x3d4, 0x000f);
	outportw(0x3d4, 0xea10);
	outportw(0x3d4, 0xac11);
	outportw(0x3d4, 0xdf12);
	outportw(0x3d4, 0x2813);
	outportw(0x3d4, 0x0014);
	outportw(0x3d4, 0xe715);
	outportw(0x3d4, 0x0616);
	outportw(0x3d4, 0xe317);
	for (i=0; i<240; i++)
	    PutLineVGAX(cleared, i);
    } else {
	outportw(0x3d4, 0x0e11);
	outportw(0x3d4, 0x5f00);
	outportw(0x3d4, 0x4f01);
	outportw(0x3d4, 0x5002);
	outportw(0x3d4, 0x8203);
	outportw(0x3d4, 0x5404);
	outportw(0x3d4, 0x8005);
	outportw(0x3d4, 0xbf06);
	outportw(0x3d4, 0x1f07);
	outportw(0x3d4, 0x0008);
	outportw(0x3d4, 0x4009);
	outportw(0x3d4, 0x000a);
	outportw(0x3d4, 0x000b);
	outportw(0x3d4, 0x000c);
	outportw(0x3d4, 0x000d);
	outportw(0x3d4, 0x000e);
	outportw(0x3d4, 0x000f);
	outportw(0x3d4, 0x9c10);
	outportw(0x3d4, 0x8e11);
	outportw(0x3d4, 0x8f12);
	outportw(0x3d4, 0x2813);
	outportw(0x3d4, 0x0014);
	outportw(0x3d4, 0x9615);
	outportw(0x3d4, 0xb916);
	outportw(0x3d4, 0xe317);
	for (i=0; i<400; i++)
	    PutLineVGAX(cleared, i);
    }
}

void LoadPalette(void) {
    _go32_dpmi_registers int_regs;
    int i;

    if (BasicModeInfo.ModeType == T16) {
	for(i=0; i<16; i++) {
	    int_regs.h.ah = 0x10;
	    int_regs.h.al = 0x00;
	    int_regs.h.bh = i;
	    int_regs.h.bl = i;
	    int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
	    _go32_dpmi_simulate_int(0x10, &int_regs);
	}
    }
    int_regs.h.ah = 0x10;
    int_regs.h.al = 0x12;
    int_regs.x.bx = 0;
    if (BasicModeInfo.ModeType == T16)
	int_regs.x.cx = 16;
    else
	int_regs.x.cx = 256;
    int_regs.x.es = PaletteMem.rm_segment;
    int_regs.x.dx = 0x0000;
    int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
    _go32_dpmi_simulate_int(0x10, &int_regs);
}


int VBE_GetVbeInfoBlock(void) {
    _go32_dpmi_registers int_regs;

    strncpy(VBE_VbeInfoBlock.VbeSignature, "VBE2", 4);
    dosmemput(&VBE_VbeInfoBlock, sizeof(VBE_VbeInfoBlock), __tb);
    int_regs.x.ax = 0x4F00;
    int_regs.x.di = __tb & 0x0F;
    int_regs.x.es = (__tb >> 4) & 0xFFFF;
    int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
    _go32_dpmi_simulate_int(0x10, &int_regs);
    dosmemget(__tb, sizeof(VBE_VbeInfoBlock), &VBE_VbeInfoBlock);
    return((int_regs.x.ax == 0x004F) && (VBE_VbeInfoBlock.VbeVersion >= 0x0100));
}

int VBE_GetModeInfoBlock(short mode) {
    _go32_dpmi_registers int_regs;

    VBE_ModeInfoBlock.ReservedA = 1;
    int_regs.x.ax = 0x4F01;
    int_regs.x.cx = mode;
    int_regs.x.di = __tb & 0x0F;
    int_regs.x.es = (__tb >> 4) & 0xFFFF;
    int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
    _go32_dpmi_simulate_int(0x10, &int_regs);
    dosmemget(__tb, sizeof(VBE_ModeInfoBlock), &VBE_ModeInfoBlock);
    return(int_regs.x.ax == 0x004F);
}

int VBE_GetProtectedModeInterface(void) {
    _go32_dpmi_registers int_regs;

    int_regs.x.ax = 0x4F0A;
    int_regs.x.bx = 0x0000;
    int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
    _go32_dpmi_simulate_int(0x10, &int_regs);
    VBE_ProtectedModeInterface = (T_VBE_ProtectedModeInterface *) malloc(int_regs.x.cx);
    if (VBE_ProtectedModeInterface == NULL)
	return(0);
    dosmemget(int_regs.x.es * 16 + int_regs.x.di, int_regs.x.cx, VBE_ProtectedModeInterface);
    SetWindow = (unsigned int) VBE_ProtectedModeInterface + VBE_ProtectedModeInterface->SetWindowOffset;
    PortsMemory = (short *) VBE_ProtectedModeInterface + VBE_ProtectedModeInterface->PortsMemoryOffset;
    return(int_regs.x.ax == 0x004F);
}

void VBE_ChangeBankRealInterrupt(short BankNumber) {
    _go32_dpmi_registers int_regs;

    int_regs.x.ax = 0x4F05;
    int_regs.x.bx = WriteWindow;
    int_regs.x.dx = BankNumber;
    int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
    _go32_dpmi_simulate_int(0x10, &int_regs);
    CurrentBankNumber = BankNumber;
}

void VBE_ChangeBankRealMode(short BankNumber) {
    _go32_dpmi_registers int_regs;

    int_regs.x.ax = 0x4F05;
    int_regs.x.bx = WriteWindow;
    int_regs.x.dx = BankNumber;
    int_regs.x.cs = WinFuncPtrSegment;
    int_regs.x.ip = WinFuncPtrOffset;
    int_regs.x.ss=int_regs.x.sp=int_regs.x.flags=0;
    _go32_dpmi_simulate_fcall(&int_regs);
    CurrentBankNumber = BankNumber;
}

void VBE_ChangeBankProtectMode(short BankNumber) {
    asm __volatile__ ("
	 push %%es
	 movw %0, %%es
	 mov 0x4F05, %%ax
	 mov %1, %%bx
	 mov %2, %%dx
	 call %3
	  pop %%es
	 "
	 :
	 : "g" (_dos_ds), "g" (WriteWindow), "g" (BankNumber), "r" (SetWindow)
	 : "%ax", "%bx", "%dx", "cc"
    );
    CurrentBankNumber = BankNumber;
}

void DoFlushLineLinear(int y) {
    int target_y;
    char *addr;

    if (need_dither) {
	target_y = y;
	switch (screen_res) {
	 case 0:
	    if (CorrectLines) {
		if (target_y%3==0)
		    return;
		target_y=(target_y<<1)/3+5;
	    } else
		target_y -= 8;
	    break;
	 case 1:
	    if (CorrectLines) {
		if (target_y%5==0)
		    return;
		target_y=(target_y<<2)/5+6;
	    } else
		target_y -= 8;
	    break;
	 case 2:
	    if (CorrectLines) {
		if (target_y%3==0)
		    return;
		target_y=(target_y<<1)/3+10;
	    } else
		target_y += 57;
	    break;
	 case 3:
	    if (CorrectLines) {
		if (target_y%5==0)
		    return;
		target_y=(target_y<<2)/5+12;
	    } else
		target_y += 97;
	    break;
	 case 4:
	    if (CorrectLines)
		target_y += 15;
	    else
		target_y += 157;
	    break;
	}
	if (target_y < BasicModeInfo.ModeHeight && target_y >= 0) {
	    addr = gfxvidinfo.bufmem + y*gfxvidinfo.rowbytes;
	    DitherLine(AlignedLinearBufferPtr + LogicalLineLength * target_y, (UWORD *)addr, 0, y, BasicModeInfo.ModeWidth, BasicModeInfo.BitsPerPixel);
	}
    }
}

void DoFlushLineBuffered(int y) {
    int target_y = y;
    char *addr;

    switch (screen_res) {
     case 0:
	if (CorrectLines) {
	    if (target_y%3==0)
		return;
	    target_y=(target_y<<1)/3+5;
	} else
	    target_y -= 8;
	break;
     case 1:
	if (CorrectLines) {
	    if (target_y%5==0)
		return;
	    target_y=(target_y<<2)/5+6;
	} else
	    target_y -= 8;
	break;
     case 2:
	if (CorrectLines) {
	    if (target_y%3==0)
		return;
	    target_y=(target_y<<1)/3+10;
	} else
	    target_y += 57;
	break;
     case 3:
	if (CorrectLines) {
	    if (target_y%5==0)
		return;
	    target_y=(target_y<<2)/5+12;
	} else
	    target_y += 97;
	break;
     case 4:
	if (CorrectLines)
	    target_y += 15;
	else
	    target_y += 157;
	break;
    }
    if (target_y < BasicModeInfo.ModeHeight && target_y >= 0) {
	addr = gfxvidinfo.bufmem + y*gfxvidinfo.rowbytes;
	if (need_dither) {
	    DitherLine(dither_buf, (UWORD *)addr, 0, y, BasicModeInfo.ModeWidth, BasicModeInfo.BitsPerPixel);
	    addr = dither_buf;
	}
	PutLine(addr, target_y);
    }
}

void PutLineBanked(char *addr, int target_y) {
    unsigned int offs;
    int page, pos, left;
    char *ptr;

    offs = target_y * LogicalLineLength;
    if (FastWinCalc) {
	page = offs >> WinMask1;
	pos = offs & WinMask2;
    } else {
	page = offs / WinLength;
	pos = offs % WinLength;
    }
    left = WinLength - pos;
    ptr = (char *) WinStart + pos;
    if (page != CurrentBankNumber)
	VBE_ChangeBank(page);
    if (left >= LineLength)
	asm __volatile__ ("
	     push %%es
	     cld
	     movw %0, %%es
	     shrl $2, %%ecx
	     rep
	     movsl
	     pop %%es
	     "
	     :
	     : "g" (_dos_ds), "S" (addr), "D" (ptr), "c" (LineLength)
	     : "%esi", "%edi", "%ecx", "cc"
	);
    else {
	asm __volatile__ ("
	     push %%es
	     cld
	     movw %0, %%es
	     shrl $2, %%ecx
	     rep
	     movsl
	     pop %%es
	     "
	     :
	     : "g" (_dos_ds), "S" (addr), "D" (ptr), "c" (left)
	     : "%esi", "%edi", "%ecx", "cc"
	);
	VBE_ChangeBank(page+1);
	asm __volatile__ ("
	     push %%es
	     cld
	     movw %0, %%es
	     addl %1, %%esi
	     subl %1, %%ecx
	     shrl $2, %%ecx
	     rep
	     movsl
	     pop %%es
	     "
	     :
	     : "g" (_dos_ds), "g" (left), "S" (addr), "D" (WinStart), "c" (LineLength)
	     : "%esi", "%edi", "%ecx", "cc"
	);
    }
}

void PutLineVGA16(char *addr, int target_y) {
    unsigned int start, ptr;
    int i;
    UBYTE c;

    _farsetsel(_dos_ds);
#if 0
    ptr = WinStart + target_y * LogicalLineLength;
    outportw(0x3ce, 0x205);
    outportw(0x3ce, 3);
    for (i=0; i<BasicModeInfo.ModeWidth; i++) {
	outportw(0x3ce, 1<<(((i&7)^7)+8)|8);
	_farnspeekb(ptr+(i>>3));
	_farnspokeb(ptr+(i>>3), addr[i]);
    }
#else
    start = WinStart + target_y * LogicalLineLength;
    ptr = start;
    outportw(0x3c4, 0x802);
    for (i=0; i<BasicModeInfo.ModeWidth; i+=8, ptr++) {
	c = ((addr[i  ]<<4)&128)|((addr[i+1]<<3)&64)|((addr[i+2]<<2)&32)|((addr[i+3]<<1)&16)|
	    ((addr[i+4]   )&8  )|((addr[i+5]>>1)&4 )|((addr[i+6]>>2)&2 )|((addr[i+7]>>3)&1);
	_farnspokeb(ptr, c);
    }
    ptr = start;
    outportw(0x3c4, 0x402);
    for (i=0; i<BasicModeInfo.ModeWidth; i+=8, ptr++) {
	c = ((addr[i  ]<<5)&128)|((addr[i+1]<<4)&64)|((addr[i+2]<<3)&32)|((addr[i+3]<<2)&16)|
	    ((addr[i+4]<<1)&8  )|((addr[i+5]   )&4 )|((addr[i+6]>>1)&2 )|((addr[i+7]>>2)&1 );
	_farnspokeb(ptr, c);
    }
    ptr = start;
    outportw(0x3c4, 0x202);
    for (i=0; i<BasicModeInfo.ModeWidth; i+=8, ptr++) {
	c = ((addr[i  ]<<6)&128)|((addr[i+1]<<5)&64)|((addr[i+2]<<4)&32)|((addr[i+3]<<3)&16)|
	    ((addr[i+4]<<2)&8  )|((addr[i+5]<<1)&4 )|((addr[i+6]   )&2 )|((addr[i+7]>>1)&1 );
	_farnspokeb(ptr, c);
    }
    ptr = start;
    outportw(0x3c4, 0x102);
    for (i=0; i<BasicModeInfo.ModeWidth; i+=8, ptr++) {
	c = ((addr[i  ]<<7)&128)|((addr[i+1]<<6)&64)|((addr[i+2]<<5)&32)|((addr[i+3]<<4)&16)|
	    ((addr[i+4]<<3)&8  )|((addr[i+5]<<2)&4 )|((addr[i+6]<<1)&2 )|((addr[i+7]   )&1 );
	_farnspokeb(ptr, c);
    }
#endif
}

void PutLineVGAX(char *addr, int target_y) {
    unsigned int start, ptr;
    int i;
    UBYTE c;

    _farsetsel(_dos_ds);
    start = WinStart + target_y * LogicalLineLength;
    ptr = start;
    outportw(0x3c4, 0xf02);
    for (i=0; i<BasicModeInfo.ModeWidth; i+=4, ptr++)
	_farnspokeb(ptr, addr[i]);
    ptr = start;
    outportw(0x3c4, 0xe02);
    for (i=1; i<BasicModeInfo.ModeWidth; i+=4, ptr++) {
	if (addr[i] != addr[i-1])
	_farnspokeb(ptr, addr[i]);
    }
    ptr = start;
    outportw(0x3c4, 0xc02);
    for (i=2; i<BasicModeInfo.ModeWidth; i+=4, ptr++) {
	if (addr[i] != addr[i-1])
	_farnspokeb(ptr, addr[i]);
    }
    ptr = start;
    outportw(0x3c4, 0x802);
    for (i=3; i<BasicModeInfo.ModeWidth; i+=4, ptr++) {
	if (addr[i] != addr[i-1])
	_farnspokeb(ptr, addr[i]);
    }
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.