 /*
  * UAE - The Un*x Amiga Emulator
  *
  * DOS VESA BIOS / VGA / Mode X graphics interface.
  *
  * (c) 1995 Bernd Schmidt
  * (c) 1996 Gustavo Goedert
  */

#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 "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 "dos-ui.h"

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

static FILE *logfile;

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 = 1, 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;

void my_kbd_handler(void);
void my_mouse_handler(_go32_dpmi_registers *mouse_regs);
void HandleDisk(int num);

/* 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

/* 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 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;

unsigned char PaletteData[768];

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

    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 AK_CTRL;
     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: break;
/*     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;
    }
    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) {
	specialflags |= 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 regs;
    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 */
    remove("uae.log");
    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);

    /* 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 */
    regs.x.ax=0;
    regs.x.ss=regs.x.sp=regs.x.flags=0;
    _go32_dpmi_simulate_int(0x33, &regs);
    if (regs.x.ax==0) {
	fprintf (stderr, "Mouse driver not present.\n");
	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");
	return 0;
    }
    regs.x.ax=0xc;
    regs.x.cx=0x7f;
    regs.x.es=mouse_handler.rm_segment;
    regs.x.dx=mouse_handler.rm_offset;
    regs.x.ss=regs.x.sp=regs.x.flags=0;
    _go32_dpmi_simulate_int(0x33, &regs);

    /* Save Old VGA Mode */
    regs.x.ax = 0x1C;
    regs.h.ah = 0x0F;
    regs.x.ss=regs.x.sp=regs.x.flags=0;
    _go32_dpmi_simulate_int(0x10, &regs);
    OldVGAMode = 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");
	return 0;
    }

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

    /* set up wanted video mode information */
    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");
	return 0;
    }
    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");
	return 0;
    }

    FoundMode = 0;

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

    if (HasVbe) {
	unsigned int dosmempos = (VBE_VbeInfoBlock.VideoModePtr >> 16) * 16 +
				 (VBE_VbeInfoBlock.VideoModePtr & 0xffff);
	dosmemget(dosmempos, 256, VBE_ModeList);
	VBE_ModeList[127] = -1;
	for (i=0; (VBE_ModeList[i] != -1) && (!FoundMode) ; i++) {
	    if ((VBE_ModeList[i] != (short)0x81ff) && VBE_GetModeInfoBlock(VBE_ModeList[i])) {
		if ((VBE_VbeInfoBlock.VbeVersion < 0x0200) && (VBE_ModeList[i] == 0x6a)) {
		    BasicModeInfo.ModeWidth = 800;
		    BasicModeInfo.ModeHeight = 600;
		    BasicModeInfo.ModeType = T16;
		    BasicModeInfo.BitsPerColor = 4;
		    BasicModeInfo.BitsPerPixel = 8;
		    VBE_ModeInfoBlock.BitsPerPixel = 8;
		} else if ((VBE_VbeInfoBlock.VbeVersion < 0x0200) && ((VBE_ModeList[i] >= 0x100) && (VBE_ModeList[i] <= 0x11b)))
		    BasicModeInfo = Old_VBE_Table[VBE_ModeList[i] - 0x100];
		else {
		    if (VBE_VbeInfoBlock.VbeVersion < 0x0102) {
			if (VBE_ModeInfoBlock.MemoryModel == PackedPixel) {
			    if (VBE_ModeInfoBlock.BitsPerPixel == 16) {
				VBE_ModeInfoBlock.MemoryModel = DirectColor;
				VBE_ModeInfoBlock.RedMaskSize = 5;
				VBE_ModeInfoBlock.RedFieldPosition = 10;
				VBE_ModeInfoBlock.GreenMaskSize = 5;
				VBE_ModeInfoBlock.GreenFieldPosition = 5;
				VBE_ModeInfoBlock.BlueMaskSize = 5;
				VBE_ModeInfoBlock.BlueFieldPosition = 0;
				VBE_ModeInfoBlock.RsvdMaskSize = 1;
				VBE_ModeInfoBlock.RsvdFieldPosition = 15;
			    } else if (VBE_ModeInfoBlock.BitsPerPixel == 24) {
				VBE_ModeInfoBlock.MemoryModel = DirectColor;
				VBE_ModeInfoBlock.RedMaskSize = 8;
				VBE_ModeInfoBlock.RedFieldPosition = 16;
				VBE_ModeInfoBlock.GreenMaskSize = 8;
				VBE_ModeInfoBlock.GreenFieldPosition = 8;
				VBE_ModeInfoBlock.BlueMaskSize = 8;
				VBE_ModeInfoBlock.BlueFieldPosition = 0;
				VBE_ModeInfoBlock.RsvdMaskSize = 0;
				VBE_ModeInfoBlock.RsvdFieldPosition = 0;
			    } else if (VBE_ModeInfoBlock.BitsPerPixel == 32) {
				VBE_ModeInfoBlock.MemoryModel = DirectColor;
				VBE_ModeInfoBlock.RedMaskSize = 8;
				VBE_ModeInfoBlock.RedFieldPosition = 16;
				VBE_ModeInfoBlock.GreenMaskSize = 8;
				VBE_ModeInfoBlock.GreenFieldPosition = 8;
				VBE_ModeInfoBlock.BlueMaskSize = 8;
				VBE_ModeInfoBlock.BlueFieldPosition = 0;
				VBE_ModeInfoBlock.RsvdMaskSize = 8;
				VBE_ModeInfoBlock.RsvdFieldPosition = 24;
			    }
			}
		    }
		    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)) {
		HasLinear = 0;
		if ((VBE_ModeInfoBlock.ModeAttributes & (1<<7)) && !use_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) {
			regs.x.ax = 0x4F02;
			regs.x.bx = VBE_ModeList[i] | (1<<14);
			regs.x.ss=regs.x.sp=regs.x.flags=0;
			_go32_dpmi_simulate_int(0x10, &regs);
			ControlInfo.ControlType = TVBE;
			ControlInfo.mode = VBE_ModeList[i] | (1<<14);
			DoFlushLine = DoFlushLineLinear;
		    }
		}
		if (!HasLinear) {
		    if (!(VBE_ModeInfoBlock.ModeAttributes & (1<<6))) {
			regs.x.ax = 0x4F02;
			regs.x.bx = VBE_ModeList[i];
			regs.x.ss=regs.x.sp=regs.x.flags=0;
			_go32_dpmi_simulate_int(0x10, &regs);
			ControlInfo.ControlType = TVBE;
			ControlInfo.mode = VBE_ModeList[i];
			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) {
			    WinStart = VBE_ModeInfoBlock.WinASegment * 16;
			    WriteWindow = 0;
			} else {
			    WinStart = VBE_ModeInfoBlock.WinBSegment * 16;
			    WriteWindow = 1;
			}
			for (i=1; i<32; i++) {
			    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
			FoundMode = 0;
		}
		LineLength = (VBE_ModeInfoBlock.BitsPerPixel >> 3) * BasicModeInfo.ModeWidth;
		LogicalLineLength = VBE_ModeInfoBlock.BytesPerScanLine;
	    }
	}
    }

    if (!FoundMode) {
	if (screen_res==0) {
	    if (WantedType == T256) {
		BasicModeInfo.ModeWidth=320;
		BasicModeInfo.ModeHeight=200;
		BasicModeInfo.ModeType=T256;
		BasicModeInfo.BitsPerPixel=8;
		BasicModeInfo.BitsPerColor=8;
		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;
		    else
			free(LinearBufferPtr);
		}
		if (HasLinear)
		    DoFlushLine = DoFlushLineLinear;
		else {
		    WinLength = 65536;
		    WinStart = 0xa0000;
		    FastWinCalc = 1;
		    WinMask1 = 16;
		    WinMask2 = WinLength-1;
		    DoFlushLine = DoFlushLineBuffered;
		    PutLine = PutLineBanked;
		}
		regs.h.ah = 0x00;
		regs.h.al = 0x13;
		regs.x.ss=regs.x.sp=regs.x.flags=0;
		_go32_dpmi_simulate_int(0x10, &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;
		regs.h.ah = 0x00;
		regs.h.al = 0xd;
		regs.x.ss=regs.x.sp=regs.x.flags=0;
		_go32_dpmi_simulate_int(0x10, &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;
	} 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;
	    regs.h.ah = 0x00;
	    regs.h.al = 0x12;
	    regs.x.ss=regs.x.sp=regs.x.flags=0;
	    _go32_dpmi_simulate_int(0x10, &regs);
	    ControlInfo.ControlType = TVGA;
	    ControlInfo.mode = 0x12;
	    LineLength = 80;
	    LogicalLineLength = 80;
	    FoundMode = 1;
	}
    }

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

    logfile = freopen("UAE.LOG", "w", stderr);

    if (HasVbe) {
	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");
    }

    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;

    init_colors();
    dosmemput(PaletteData, 768, PaletteMem.rm_segment*16);
    LoadPalette();

    buttonstate[0] = buttonstate[1] = buttonstate[2] = 0;
    for(i = 0; i < 256; i++)
	keystate[i] = 0;

    lastmx = lastmy = 0;
    newmousecounters = 0;

    /* Lock Data Touched by Interrup 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));

    return 1;
}

void graphics_leave(void)
{
    _go32_dpmi_registers regs;

    if (RestoreOldVGAMode) {
	FILE *fp;
	int inChar;

	regs.x.ax = 0x1C;
	regs.h.ah = 0x00;
	regs.h.al = OldVGAMode;
	regs.x.ss=regs.x.sp=regs.x.flags=0;
	_go32_dpmi_simulate_int(0x10, &regs);
	fclose(logfile);
	freopen("CON", "w", stderr);
	
	fp = fopen("uae.log","r");
	for (;;) {
	    inChar = getc(fp);
	    if (inChar == EOF)
		break;
	    putchar(inChar);
	}
	fclose(fp);
    }
    _go32_dpmi_set_protected_mode_interrupt_vector(9, &old_kbd_handler);
    _go32_dpmi_free_iret_wrapper(&new_kbd_handler);
    regs.x.ax=0xc;
    regs.x.cx=0x0;
    regs.x.ss=regs.x.sp=regs.x.flags=0;
    _go32_dpmi_simulate_int(0x33, &regs);
    _go32_dpmi_free_real_mode_callback(&mouse_handler);
    _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;
	}
    }
}

int debuggable(void)
{
    return 0;
}

int needmousehack(void)
{
    return 0;
}

void LED(int on)
{
}

void target_specific_usage(void)
{
    printf("  -S                       : Turn off sound support (if it is configured)\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");
    printf("  -8                       : Use 8 Bit Sound (default is 16 Bit Sound)\n");
    printf("  -4                       : Use 44100Hz Samplerate (default is 22050Hz)\n");
}

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

    _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 */
    regs.x.ax = 0x1C;
    regs.h.ah = 0x00;
    regs.h.al = 0x03;
    regs.x.ss=regs.x.sp=regs.x.flags=0;
    _go32_dpmi_simulate_int(0x10, &regs);

    strcpy(buf,dosfilereq("*.adf"));
    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 SetGraphicsMode(void) {
    _go32_dpmi_registers regs;

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

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

    regs.h.ah = 0x00;
    regs.h.al = 0x13;
    regs.x.ss=regs.x.sp=regs.x.flags=0;
    _go32_dpmi_simulate_int(0x10, &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 regs;

    regs.h.ah = 0x10;
    regs.h.al = 0x12;
    regs.x.bx = 0;
    if (BasicModeInfo.ModeType == T16)
	regs.x.cx = 16;
    else
	regs.x.cx = 256;
    regs.x.es = PaletteMem.rm_segment;
    regs.x.dx = 0x0000;
    regs.x.ss=regs.x.sp=regs.x.flags=0;
    _go32_dpmi_simulate_int(0x10, &regs);
}


int VBE_GetVbeInfoBlock(void) {
    _go32_dpmi_registers regs;

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

int VBE_GetModeInfoBlock(short mode) {
    _go32_dpmi_registers regs;

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

int VBE_GetProtectedModeInterface(void) {
    _go32_dpmi_registers regs;

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

void VBE_ChangeBankRealInterrupt(short BankNumber) {
    _go32_dpmi_registers regs;

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

void VBE_ChangeBankRealMode(short BankNumber) {
    _go32_dpmi_registers regs;

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

void VBE_ChangeBankProtectMode(short BankNumber) {
    asm ("
	 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"
    );
    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", "memory"
	);
    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", "memory"
	);
	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", "memory"
	);
    }
}

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, 0x102);
    for (i=0; i<BasicModeInfo.ModeWidth; i+=4, ptr++)
	_farnspokeb(ptr, addr[i]);
    ptr = start;
    outportw(0x3c4, 0x202);
    for (i=1; i<BasicModeInfo.ModeWidth; i+=4, ptr++)
	_farnspokeb(ptr, addr[i]);
    ptr = start;
    outportw(0x3c4, 0x402);
    for (i=2; i<BasicModeInfo.ModeWidth; i+=4, ptr++)
	_farnspokeb(ptr, addr[i]);
    ptr = start;
    outportw(0x3c4, 0x802);
    for (i=3; i<BasicModeInfo.ModeWidth; i+=4, ptr++)
	_farnspokeb(ptr, addr[i]);
}
