/*
 *  Copyright (C) 2002-2021  The DOSBox Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <assert.h>
#include <string.h>
#include <cstdint>

#include "callback.h"
#include "logging.h"
#include "bios.h"
#include "cpu.h"

#if C_EMSCRIPTEN
# include <emscripten.h>
#endif

uint16_t CB_SEG=0,CB_SOFFSET=0;
extern Bitu vm86_fake_io_seg;
extern Bitu vm86_fake_io_off;

unsigned int last_callback = 0;

/* CallBack are located at 0xF000:0x1000  (see CB_SEG and CB_SOFFSET in callback.h)
   And they are 16 bytes each and you can define them to behave in certain ways like a
   far return or and IRET
*/

CallBack_Handler CallBack_Handlers[CB_MAX] = {NULL};
char* CallBack_Description[CB_MAX] = {NULL};

Bitu call_stop,call_default;
uint8_t call_idle;
Bitu call_priv_io;

static Bitu illegal_handler(void) {
	E_Exit("Illegal CallBack #%u Called",last_callback);
	return 1;
}

void DBG_CALLBACK_Dump(void) {
	LOG_MSG("Callbacks");
    for (Bitu i=0;i < CB_MAX;i++) {
        if (CallBack_Handlers[i] == &illegal_handler)
            continue;

        LOG_MSG("  [%u] func=%p desc='%s'",
            (unsigned int)i,
            (void*)((uintptr_t)CallBack_Handlers[i]), /* shut the compiler up by func -> uintptr_t -> void* conversion */
            CallBack_Description[i] != NULL ? CallBack_Description[i] : "");
    }
	LOG_MSG("--------------");
}

void CALLBACK_Dump(void) {
	LOG(LOG_CPU,LOG_DEBUG)("Callback dump");
    for (Bitu i=0;i < CB_MAX;i++) {
        if (CallBack_Handlers[i] == &illegal_handler)
            continue;

        LOG(LOG_CPU,LOG_DEBUG)("  [%u] func=%p desc='%s'",
            (unsigned int)i,
            (void*)((uintptr_t)CallBack_Handlers[i]), /* shut the compiler up by func -> uintptr_t -> void* conversion */
            CallBack_Description[i] != NULL ? CallBack_Description[i] : "");
    }
	LOG(LOG_CPU,LOG_DEBUG)("--------------");
}

void CALLBACK_Shutdown(void) {
	for (Bitu i=0;(i<CB_MAX);i++) {
		CallBack_Handlers[i] = &illegal_handler;
		CALLBACK_SetDescription(i,NULL);
	}
}

uint8_t CALLBACK_Allocate(void) {
	for (uint8_t i=1;(i<CB_MAX);i++) {
		if (CallBack_Handlers[i]==&illegal_handler) {
			if (CallBack_Description[i] != NULL) LOG_MSG("CALLBACK_Allocate() warning: empty slot still has description string!\n");
			CallBack_Handlers[i]=nullptr;
			return i;
		}
	}
	E_Exit("CALLBACK:Can't allocate handler.");
	return 0;
}

void CALLBACK_DeAllocate(Bitu in) {
    assert(in != 0);
    assert(in < CB_MAX);

	CallBack_Handlers[in]=&illegal_handler;
	CALLBACK_SetDescription(in,NULL);
}


void CALLBACK_Idle(void) {
#if C_EMSCRIPTEN
	void GFX_Events();
	GFX_Events();
#endif

/* this makes the cpu execute instructions to handle irq's and then come back */
	Bitu oldIF=GETFLAG(IF);
	SETFLAGBIT(IF,true);
	uint16_t oldcs=SegValue(cs);
	uint32_t oldeip=reg_eip;
	SegSet16(cs,CB_SEG);
	reg_eip=CB_SOFFSET+call_idle*CB_SIZE;
	CPU_CycleLeft += CPU_Cycles - 1; /* Ahem: This makes the CPU execute instructions to handle IRQs and then come back. Nothing more. */
	CPU_Cycles = 1;
	DOSBOX_RunMachine();
	reg_eip=oldeip;
	SegSet16(cs,oldcs);
	SETFLAGBIT(IF,oldIF);
	if (!CPU_CycleAutoAdjust && CPU_Cycles>0) {
		CPU_CycleLeft += CPU_Cycles;
		CPU_Cycles = 0;
	}
}

void CALLBACK_IdleNoInts(void) {
#if C_EMSCRIPTEN
	void GFX_Events();
	GFX_Events();
#endif

	/* this makes the cpu execute instructions to handle irq's and then come back */
//	Bitu oldIF=GETFLAG(IF);
//	SETFLAGBIT(IF,true);
	uint16_t oldcs=SegValue(cs);
	uint32_t oldeip=reg_eip;
	SegSet16(cs,CB_SEG);
	reg_eip=CB_SOFFSET+call_idle*CB_SIZE;
	CPU_CycleLeft += CPU_Cycles - 1; /* Ahem: This makes the CPU execute instructions to handle IRQs and then come back. Nothing more. */
	CPU_Cycles = 1;
	DOSBOX_RunMachine();
	reg_eip=oldeip;
	SegSet16(cs,oldcs);
//	SETFLAGBIT(IF,oldIF);
	if (!CPU_CycleAutoAdjust && CPU_Cycles>0) {
		CPU_CycleLeft += CPU_Cycles;
		CPU_Cycles = 0;
	}
}

static Bitu default_handler(void) {
	LOG(LOG_CPU,LOG_ERROR)("Illegal Unhandled Interrupt Called %X",lastint);
	return CBRET_NONE;
}

static Bitu stop_handler(void) {
	return CBRET_STOP;
}

Bitu FillFlags(void);

void CALLBACK_RunRealFarInt(uint16_t seg,uint16_t off) {
	FillFlags();

	reg_sp-=6;
	if (cpu.pmode && !(reg_flags & FLAG_VM)) {
		mem_writew(SegPhys(ss)+reg_sp+0,RealOff(CALLBACK_RealPointer(call_stop)));
		mem_writew(SegPhys(ss)+reg_sp+2,RealSeg(CALLBACK_RealPointer(call_stop)));
		mem_writew(SegPhys(ss)+reg_sp+4,(uint16_t)reg_flags);
	}
	else {
		// See commit [https://github.com/joncampbell123/dosbox-x/commit/00378e8cc2bc5a71c3691be5f3bfd20246a26874]
		// which mentions UNLZEXE and something about the expectation of stack pointer wraparound in real mode for this case.
		real_writew(SegValue(ss),reg_sp+0,RealOff(CALLBACK_RealPointer(call_stop)));
		real_writew(SegValue(ss),reg_sp+2,RealSeg(CALLBACK_RealPointer(call_stop)));
		real_writew(SegValue(ss),reg_sp+4,(uint16_t)reg_flags);
	}
	uint32_t oldeip=reg_eip;
	uint16_t oldcs=SegValue(cs);
	reg_eip=off;
	SegSet16(cs,seg);
	DOSBOX_RunMachine();
	reg_eip=oldeip;
	SegSet16(cs,oldcs);
}

void CALLBACK_RunRealFar(uint16_t seg,uint16_t off) {
	reg_sp-=4;
	if (cpu.pmode && !(reg_flags & FLAG_VM)) {
		mem_writew(SegPhys(ss)+reg_sp+0,RealOff(CALLBACK_RealPointer(call_stop)));
		mem_writew(SegPhys(ss)+reg_sp+2,RealSeg(CALLBACK_RealPointer(call_stop)));
	}
	else {
		// See commit [https://github.com/joncampbell123/dosbox-x/commit/00378e8cc2bc5a71c3691be5f3bfd20246a26874]
		// which mentions UNLZEXE and something about the expectation of stack pointer wraparound in real mode for this case.
		real_writew(SegValue(ss),reg_sp+0,RealOff(CALLBACK_RealPointer(call_stop)));
		real_writew(SegValue(ss),reg_sp+2,RealSeg(CALLBACK_RealPointer(call_stop)));
	}
	uint32_t oldeip=reg_eip;
	uint16_t oldcs=SegValue(cs);
	reg_eip=off;
	SegSet16(cs,seg);
	DOSBOX_RunMachine();
	reg_eip=oldeip;
	SegSet16(cs,oldcs);
}

void CALLBACK_RunRealInt_retcsip(uint8_t intnum,Bitu &rcs,Bitu &rip) {
	uint32_t oldeip=reg_eip;
	uint16_t oldcs=SegValue(cs);
	reg_eip=CB_SOFFSET+(CB_MAX*CB_SIZE)+(intnum*6U);
	SegSet16(cs,CB_SEG);
	DOSBOX_RunMachine();
	rcs = SegValue(cs);
	rip = reg_ip;
	reg_eip=oldeip;
	SegSet16(cs,oldcs);
}

void CALLBACK_RunRealInt(uint8_t intnum) {
	uint32_t oldeip=reg_eip;
	uint16_t oldcs=SegValue(cs);
	reg_eip=CB_SOFFSET+(CB_MAX*CB_SIZE)+(intnum*6U);
	SegSet16(cs,CB_SEG);
	DOSBOX_RunMachine();
	reg_eip=oldeip;
	SegSet16(cs,oldcs);
}

namespace {

template <std::uint32_t FLAG>
inline void CALLBACK_SET_FLAG(bool const val) {
	uint32_t tempf;

	if (cpu.pmode && !(reg_flags & FLAG_VM)) {
		if (cpu.stack.big)
			tempf = mem_readd(SegPhys(ss)+reg_esp+8); // first word past FAR 32:32
		else
			tempf = mem_readw(SegPhys(ss)+reg_sp+4); // first word past FAR 16:16
	}
	else {
		if (cpu.stack.big)
			tempf = real_readd(SegValue(ss),reg_esp+8); // first word past FAR 32:32
		else
			tempf = real_readw(SegValue(ss),reg_sp+4); // first word past FAR 16:16
	}

	if (val) tempf |= FLAG;
	else tempf &= ~FLAG;

	if (cpu.pmode && !(reg_flags & FLAG_VM)) {
		if (cpu.stack.big)
			mem_writed(SegPhys(ss)+reg_esp+8,tempf);
		else
			mem_writew(SegPhys(ss)+reg_sp+4,(uint16_t)tempf);
	}
	else {
		if (cpu.stack.big)
			real_writed(SegValue(ss),reg_esp+8,tempf);
		else
			real_writew(SegValue(ss),reg_sp+4,(uint16_t)tempf);
	}
}

} // anonymous namespace

void CALLBACK_SZF(bool const val) { CALLBACK_SET_FLAG<FLAG_ZF>(val); }
void CALLBACK_SCF(bool const val) { CALLBACK_SET_FLAG<FLAG_CF>(val); }
void CALLBACK_SIF(bool const val) { CALLBACK_SET_FLAG<FLAG_IF>(val); }

void CALLBACK_SetDescription(Bitu nr, const char* descr) {
	if (CallBack_Description[nr]) delete[] CallBack_Description[nr];
	CallBack_Description[nr] = nullptr;

	if (descr != NULL) {
		CallBack_Description[nr] = new char[strlen(descr)+1];
		strcpy(CallBack_Description[nr],descr);
	}
}

const char* CALLBACK_GetDescription(Bitu nr) {
	if (nr>=CB_MAX) return nullptr;
	return CallBack_Description[nr];
}

Bitu CALLBACK_SetupExtra(Bitu callback, Bitu type, PhysPt physAddress, bool use_cb=true) {
	if (callback>=CB_MAX)
		return 0;
	switch (type) {
	case CB_RETN:
		if (use_cb) {
			phys_writeb(physAddress+0x00,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x01,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x02,(uint16_t)callback);	//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x00,(uint8_t)0xC3);		//A RETN Instruction
		return (use_cb?5:1);
	case CB_RETF:
		if (use_cb) {
			phys_writeb(physAddress+0x00,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x01,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x02,(uint16_t)callback);	//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x00,(uint8_t)0xCB);		//A RETF Instruction
		return (use_cb?5:1);
	case CB_RETF8:
		if (use_cb) {
			phys_writeb(physAddress+0x00,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x01,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x02,(uint16_t)callback);	//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x00,(uint8_t)0xCA);		//A RETF 8 Instruction
		phys_writew(physAddress+0x01,(uint16_t)0x0008);
		return (use_cb?7:3);
	case CB_RETF_STI:
		phys_writeb(physAddress+0x00,(uint8_t)0xFB);		//STI
		if (use_cb) {
			phys_writeb(physAddress+0x01,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x02,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x03,(uint16_t)callback);	//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x01,(uint8_t)0xCB);		//A RETF Instruction
		return (use_cb?6:2);
	case CB_RETF_CLI:
		phys_writeb(physAddress+0x00,(uint8_t)0xFA);		//CLI
		if (use_cb) {
			phys_writeb(physAddress+0x01,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x02,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x03,(uint16_t)callback);	//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x01,(uint8_t)0xCB);		//A RETF Instruction
		return (use_cb?6:2);
	case CB_IRET:
		if (use_cb) {
			phys_writeb(physAddress+0x00,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x01,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x02,(uint16_t)callback);		//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x00,(uint8_t)0xCF);		//An IRET Instruction
		return (use_cb?5:1);
	case CB_IRETD:
		if (use_cb) {
			phys_writeb(physAddress+0x00,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x01,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x02,(uint16_t)callback);		//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x00,(uint8_t)0x66);		//An IRETD Instruction
		phys_writeb(physAddress+0x01,(uint8_t)0xCF);
		return (use_cb?6:2);
	case CB_IRET_STI:
		phys_writeb(physAddress+0x00,(uint8_t)0xFB);		//STI
		if (use_cb) {
			phys_writeb(physAddress+0x01,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x02,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x03,(uint16_t)callback);	//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x01,(uint8_t)0xCF);		//An IRET Instruction
		return (use_cb?6:2);
	case CB_IRET_EOI_PIC1:
		if (use_cb) {
			phys_writeb(physAddress+0x00,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x01,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x02,(uint16_t)callback);		//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x00,(uint8_t)0x50);		// push ax
		phys_writeb(physAddress+0x01,(uint8_t)0xb0);		// mov al, 0x20
		phys_writeb(physAddress+0x02,(uint8_t)0x20);
		phys_writeb(physAddress+0x03,(uint8_t)0xe6);		// out 0x20, al (IBM) / out 0x00, al (PC-98)
		phys_writeb(physAddress+0x04,(uint8_t)(IS_PC98_ARCH ? 0x00 : 0x20));
		phys_writeb(physAddress+0x05,(uint8_t)0x58);		// pop ax
		phys_writeb(physAddress+0x06,(uint8_t)0xcf);		//An IRET Instruction
		return (use_cb?0x0b:0x07);
	case CB_IRQ0:	// timer int8
		phys_writeb(physAddress+0x00,(uint8_t)0xFB);		//STI
		if (use_cb) {
			phys_writeb(physAddress+0x01,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x02,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x03,(uint16_t)callback);		//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x01,(uint8_t)0x1e);		// push ds
		phys_writeb(physAddress+0x02,(uint8_t)0x50);		// push ax
		phys_writeb(physAddress+0x03,(uint8_t)0x52);		// push dx
		phys_writew(physAddress+0x04,(uint16_t)0x1ccd);	// int 1c
		phys_writeb(physAddress+0x06,(uint8_t)0xfa);		// cli
		phys_writew(physAddress+0x07,(uint16_t)0x20b0);	// mov al, 0x20
		phys_writew(physAddress+0x09,(uint16_t)(IS_PC98_ARCH ? 0x00e6 : 0x20e6));	// out 0x20, al / out 0x00, al (PC-98) (FIXME: PC-98 does not have INT 1Ch)
		phys_writeb(physAddress+0x0b,(uint8_t)0x5a);		// pop dx
		phys_writeb(physAddress+0x0c,(uint8_t)0x58);		// pop ax
		phys_writeb(physAddress+0x0d,(uint8_t)0x1f);		// pop ds
		phys_writeb(physAddress+0x0e,(uint8_t)0xcf);		//An IRET Instruction
		return (use_cb?0x13:0x0f);
	case CB_IRQ1:	// keyboard int9
		if (machine == MCH_PCJR) {
			/*      IBM PCjr reads the infrared input on NMI interrupt, which then calls INT 48h to
			 *      translate to IBM PC/XT scan codes before passing AL directly to IRQ1 (INT 9).
			 *      PCjr keyboard handlers, including games made for the PCjr, assume the scan code
			 *      is in AL and do not read the I/O port. */
			if (use_cb) {
				phys_writeb(physAddress+0x00,(uint8_t)0xFE);		//GRP 4
				phys_writeb(physAddress+0x01,(uint8_t)0x38);		//Extra Callback instruction
				phys_writew(physAddress+0x02,(uint16_t)callback);	//The immediate word
				physAddress+=4;
			}

			/* It wasn't an interrupt, so there's no need to ack */
			phys_writeb(physAddress+0x00,(uint8_t)0xcf);			//An IRET Instruction
			return (use_cb?0x05:0x01);
		}
		else {
			phys_writeb(physAddress+0x00,(uint8_t)0x50);			// push ax
			if (IS_PC98_ARCH) {
				/* NTS: NEC PC-98 does not have keyboard input on port 60h, it's a 8251 UART elsewhere. */
				phys_writew(physAddress+0x01,(uint16_t)0x9090);		// nop, nop
			}
			else {
				phys_writew(physAddress+0x01,(uint16_t)0x60e4);		// in al, 0x60
			}
			if (IS_PC98_ARCH || IS_TANDY_ARCH) {
				phys_writew(physAddress+0x03,(uint16_t)0x9090);		// nop, nop
				phys_writeb(physAddress+0x05,(uint8_t)0x90);		// nop
				phys_writew(physAddress+0x06,(uint16_t)0x9090);		// nop, nop (PC-98 does not have INT 15h keyboard hook)
			}
			else {
				phys_writew(physAddress+0x03,(uint16_t)0x4fb4);		// mov ah, 0x4f
				phys_writeb(physAddress+0x05,(uint8_t)0xf9);			// stc
				phys_writew(physAddress+0x06,(uint16_t)0x15cd);		// int 15
			}

			if (use_cb) {
				if (IS_PC98_ARCH || IS_TANDY_ARCH)
					phys_writew(physAddress+0x08,(uint16_t)0x9090);	// nop nop
				else
					phys_writew(physAddress+0x08,(uint16_t)0x0473);	// jc skip

				phys_writeb(physAddress+0x0a,(uint8_t)0xFE);		//GRP 4
				phys_writeb(physAddress+0x0b,(uint8_t)0x38);		//Extra Callback instruction
				phys_writew(physAddress+0x0c,(uint16_t)callback);	//The immediate word
				// jump here to (skip):
				physAddress+=6;
			}
			phys_writeb(physAddress+0x08,(uint8_t)0xfa);			// cli
			phys_writew(physAddress+0x09,(uint16_t)0x20b0);			// mov al, 0x20
			phys_writew(physAddress+0x0b,(uint16_t)(IS_PC98_ARCH ? 0x00e6 : 0x20e6)); // out 0x20, al
			phys_writeb(physAddress+0x0d,(uint8_t)0x58);			// pop ax
			phys_writeb(physAddress+0x0e,(uint8_t)0xcf);			//An IRET Instruction
			phys_writeb(physAddress+0x0f,(uint8_t)0xfa);			// cli
			phys_writew(physAddress+0x10,(uint16_t)0x20b0);			// mov al, 0x20
			phys_writew(physAddress+0x12,(uint16_t)0x20e6);			// out 0x20, al
			phys_writeb(physAddress+0x14,(uint8_t)0x55);			// push bp
			phys_writew(physAddress+0x15,(uint16_t)0x05cd);			// int 5
			phys_writeb(physAddress+0x17,(uint8_t)0x5d);			// pop bp
			phys_writeb(physAddress+0x18,(uint8_t)0x58);			// pop ax
			phys_writeb(physAddress+0x19,(uint8_t)0xcf);			//An IRET Instruction
			return (use_cb?0x20:0x1a);
		}
	case CB_IRQ1_BREAK:	// return from int9, when Ctrl-Break is detected; invokes int 1b
		phys_writew(physAddress+0x00,(uint16_t)0x1bcd);		// int 1b
		phys_writeb(physAddress+0x02,(uint8_t)0xfa);		// cli
		if (use_cb) {
			phys_writeb(physAddress+0x03,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x04,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x05,(uint16_t)callback);		//The immediate word
			physAddress+=4;
		}
		phys_writew(physAddress+0x03,(uint16_t)0x20b0);		// mov al, 0x20
		phys_writew(physAddress+0x05,(uint16_t)(IS_PC98_ARCH ? 0x00e6 : 0x20e6));		// out 0x20, al
		phys_writeb(physAddress+0x07,(uint8_t)0x58);			// pop ax
		phys_writeb(physAddress+0x08,(uint8_t)0xcf);			//An IRET Instruction
		return (use_cb?0x0d:0x09);
	case CB_IRQ9:	// pic cascade interrupt
		if (use_cb) {
			phys_writeb(physAddress+0x00,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x01,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x02,(uint16_t)callback);		//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x00,(uint8_t)0x50);		// push ax
		phys_writew(physAddress+0x01,(uint16_t)0x61b0);	// mov al, 0x61
		phys_writew(physAddress+0x03,(uint16_t)0xa0e6);	// out 0xa0, al
		phys_writew(physAddress+0x05,(uint16_t)0x0acd);	// int a
		phys_writeb(physAddress+0x07,(uint8_t)0xfa);		// cli
		phys_writeb(physAddress+0x08,(uint8_t)0x58);		// pop ax
		phys_writeb(physAddress+0x09,(uint8_t)0xcf);		//An IRET Instruction
		return (use_cb?0x0e:0x0a);
	case CB_IRQ12:	// ps2 mouse int74
		if (!use_cb) E_Exit("int74 callback must implement a callback handler!");
		phys_writeb(physAddress++,(uint8_t)0xfb);		// sti
		phys_writeb(physAddress++,(uint8_t)0x1e);		// push ds
		phys_writeb(physAddress++,(uint8_t)0x06);		// push es
		if (CPU_ArchitectureType>=CPU_ARCHTYPE_386) {
			phys_writew(physAddress,(uint16_t)0x6066);// pushad
			physAddress += 2;
		}
		else if (CPU_ArchitectureType>=CPU_ARCHTYPE_80186) {
			phys_writeb(physAddress++,(uint8_t)0x60);	// pusha
		}
		else {
			// 8086-level tedium, PUSHA not available
			phys_writeb(physAddress++,(uint8_t)0x50);	// push ax
			phys_writeb(physAddress++,(uint8_t)0x51);	// push cx
			phys_writeb(physAddress++,(uint8_t)0x52);	// push dx
			phys_writeb(physAddress++,(uint8_t)0x53);	// push bx
			phys_writeb(physAddress++,(uint8_t)0x55);	// push bp
			phys_writeb(physAddress++,(uint8_t)0x56);	// push si
			phys_writeb(physAddress++,(uint8_t)0x57);	// push di
		}
		phys_writeb(physAddress++,(uint8_t)0xFE);		//GRP 4
		phys_writeb(physAddress++,(uint8_t)0x38);		//Extra Callback instruction
		phys_writew(physAddress,(uint16_t)callback);			//The immediate word
		physAddress += 2;
		phys_writeb(physAddress++,(uint8_t)0x50);		// push ax
		phys_writew(physAddress,(uint16_t)0x20b0);	// mov al, 0x20
		physAddress += 2;
		phys_writew(physAddress,(uint16_t)0xa0e6);	// out 0xa0, al
		physAddress += 2;
		phys_writew(physAddress,(uint16_t)0x20e6);	// out 0x20, al
		physAddress += 2;
		phys_writeb(physAddress++,(uint8_t)0x58);		// pop ax
		phys_writeb(physAddress++,(uint8_t)0xfc);		// cld
		phys_writeb(physAddress++,(uint8_t)0xCB);		//A RETF Instruction
		return 0x13;
	case CB_IRQ12_RET:	// ps2 mouse int74 return
		phys_writeb(physAddress++,(uint8_t)0xfa);		// cli
		phys_writew(physAddress,(uint16_t)0x20b0);	// mov al, 0x20
		physAddress += 2;
		phys_writew(physAddress,(uint16_t)0xa0e6);	// out 0xa0, al
		physAddress += 2;
		phys_writew(physAddress,(uint16_t)0x20e6);	// out 0x20, al
		physAddress += 2;
		if (use_cb) {
			phys_writeb(physAddress++,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress++,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress,(uint16_t)callback);		//The immediate word
			physAddress+=2;
		}
		if (CPU_ArchitectureType>=CPU_ARCHTYPE_386) {
			phys_writew(physAddress,(uint16_t)0x6166);// popad
			physAddress += 2;
		}
		else if (CPU_ArchitectureType>=CPU_ARCHTYPE_80186) {
			phys_writeb(physAddress++,(uint8_t)0x61);	// popa
		}
		else {
			// 8086-level tedium, POPA not available
			phys_writeb(physAddress++,(uint8_t)0x5F);	// pop di
			phys_writeb(physAddress++,(uint8_t)0x5E);	// pop si
			phys_writeb(physAddress++,(uint8_t)0x5D);	// pop bp
			phys_writeb(physAddress++,(uint8_t)0x5B);	// pop bx
			phys_writeb(physAddress++,(uint8_t)0x5A);	// pop dx
			phys_writeb(physAddress++,(uint8_t)0x59);	// pop cx
			phys_writeb(physAddress++,(uint8_t)0x58);	// pop ax
		}
		phys_writeb(physAddress++,(uint8_t)0x07);		// pop es
		phys_writeb(physAddress++,(uint8_t)0x1f);		// pop ds
		phys_writeb(physAddress++,(uint8_t)0xcf);		//An IRET Instruction
		return (use_cb?0x10:0x0c);
	case CB_MOUSE:
		phys_writew(physAddress+0x00,(uint16_t)0x07eb);		// jmp i33hd
		physAddress+=9;
		// jump here to (i33hd):
		if (use_cb) {
			phys_writeb(physAddress+0x00,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x01,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x02,(uint16_t)callback);		//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x00,(uint8_t)0xCF);		//An IRET Instruction
		return (use_cb?0x0e:0x0a);
	case CB_INT16:
		phys_writeb(physAddress+0x00,(uint8_t)0xFB);		//STI
		if (use_cb) {
			phys_writeb(physAddress+0x01,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x02,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x03,(uint16_t)callback);	//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x01,(uint8_t)0xCF);		//An IRET Instruction
		for (uint8_t i=0;i<=0x0b;i++) phys_writeb(physAddress+0x02+i,0x90);
		phys_writew(physAddress+0x0e,(uint16_t)0xedeb);	//jmp callback
		return (use_cb?0x10:0x0c);
	case CB_INT28:	// DOS idle
		if (use_cb) {
			phys_writeb(physAddress+0x00,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x01,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x02,(uint16_t)callback);		//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x00,(uint8_t)0xFB);		// STI
		phys_writeb(physAddress+0x01,(uint8_t)0xF4);		// HLT
		phys_writeb(physAddress+0x02,(uint8_t)0xcf);		// An IRET Instruction
		return (0x04);
	case CB_INT29:	// fast console output
		if (IS_PC98_ARCH) LOG_MSG("WARNING: CB_INT29 callback setup not appropriate for PC-98 mode (INT 10h no longer BIOS call)");
		if (use_cb) {
			phys_writeb(physAddress+0x00,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x01,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x02,(uint16_t)callback);		//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x00,(uint8_t)0x50);	// push ax
		phys_writeb(physAddress+0x01,(uint8_t)0x53);	// push bx
		phys_writew(physAddress+0x02,(uint16_t)0x0eb4);	// mov ah, 0x0e
		phys_writeb(physAddress+0x04,(uint8_t)0xbb);	// mov bx,
		phys_writew(physAddress+0x05,(uint16_t)0x0007);	// 0x0007
		phys_writew(physAddress+0x07,(uint16_t)0x10cd);	// int 10
		phys_writeb(physAddress+0x09,(uint8_t)0x5b);	// pop bx
		phys_writeb(physAddress+0x0a,(uint8_t)0x58);	// pop ax
		phys_writeb(physAddress+0x0b,(uint8_t)0xcf);	//An IRET Instruction
		return (use_cb?0x10:0x0c);
	case CB_HOOKABLE:
		phys_writeb(physAddress+0x00,(uint8_t)0xEB);		//jump near
		phys_writeb(physAddress+0x01,(uint8_t)0x03);		//offset
		phys_writeb(physAddress+0x02,(uint8_t)0x90);		//NOP
		phys_writeb(physAddress+0x03,(uint8_t)0x90);		//NOP
		phys_writeb(physAddress+0x04,(uint8_t)0x90);		//NOP
		if (use_cb) {
			phys_writeb(physAddress+0x05,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x06,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x07,(uint16_t)callback);		//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x05,(uint8_t)0xCB);		//A RETF Instruction
		return (use_cb?0x0a:0x06);
	case CB_TDE_IRET:	// TandyDAC end transfer
		if (use_cb) {
			phys_writeb(physAddress+0x00,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x01,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x02,(uint16_t)callback);		//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x00,(uint8_t)0x50);		// push ax
		phys_writeb(physAddress+0x01,(uint8_t)0xb8);		// mov ax, 0x91fb
		phys_writew(physAddress+0x02,(uint16_t)0x91fb);
		phys_writew(physAddress+0x04,(uint16_t)0x15cd);	// int 15
		phys_writeb(physAddress+0x06,(uint8_t)0xfa);		// cli
		phys_writew(physAddress+0x07,(uint16_t)0x20b0);	// mov al, 0x20
		phys_writew(physAddress+0x09,(uint16_t)0x20e6);	// out 0x20, al
		phys_writeb(physAddress+0x0b,(uint8_t)0x58);		// pop ax
		phys_writeb(physAddress+0x0c,(uint8_t)0xcf);		//An IRET Instruction
		return (use_cb?0x11:0x0d);
/*	case CB_IPXESR:		// IPX ESR
		if (!use_cb) E_Exit("ipx esr must implement a callback handler!");
		phys_writeb(physAddress+0x00,(uint8_t)0x1e);		// push ds
		phys_writeb(physAddress+0x01,(uint8_t)0x06);		// push es
		phys_writew(physAddress+0x02,(uint16_t)0xa00f);	// push fs
		phys_writew(physAddress+0x04,(uint16_t)0xa80f);	// push gs
		phys_writeb(physAddress+0x06,(uint8_t)0x60);		// pusha
		phys_writeb(physAddress+0x07,(uint8_t)0xFE);		//GRP 4
		phys_writeb(physAddress+0x08,(uint8_t)0x38);		//Extra Callback instruction
		phys_writew(physAddress+0x09,(uint16_t)callback);	//The immediate word
		phys_writeb(physAddress+0x0b,(uint8_t)0xCB);		//A RETF Instruction
		return 0x0c;
	case CB_IPXESR_RET:		// IPX ESR return
		if (use_cb) E_Exit("ipx esr return must not implement a callback handler!");
		phys_writeb(physAddress+0x00,(uint8_t)0xfa);		// cli
		phys_writew(physAddress+0x01,(uint16_t)0x20b0);	// mov al, 0x20
		phys_writew(physAddress+0x03,(uint16_t)0xa0e6);	// out 0xa0, al
		phys_writew(physAddress+0x05,(uint16_t)0x20e6);	// out 0x20, al
		phys_writeb(physAddress+0x07,(uint8_t)0x61);		// popa
		phys_writew(physAddress+0x08,(uint16_t)0xA90F);	// pop gs
		phys_writew(physAddress+0x0a,(uint16_t)0xA10F);	// pop fs
		phys_writeb(physAddress+0x0c,(uint8_t)0x07);		// pop es
		phys_writeb(physAddress+0x0d,(uint8_t)0x1f);		// pop ds
		phys_writeb(physAddress+0x0e,(uint8_t)0xcf);		//An IRET Instruction
		return 0x0f; */
	case CB_INT21:
		phys_writeb(physAddress+0x00,(uint8_t)0xFB);		//STI
		if (use_cb) {
			phys_writeb(physAddress+0x01,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x02,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x03,(uint16_t)callback);	//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x01,(uint8_t)0xCF);		//An IRET Instruction
		phys_writeb(physAddress+0x02,(uint8_t)0xCB);		//A RETF Instruction
		phys_writeb(physAddress+0x03,(uint8_t)0x51);		// push cx
		phys_writeb(physAddress+0x04,(uint8_t)0xB9);		// mov cx,
		phys_writew(physAddress+0x05,(uint16_t)0x0140);		// 0x140
		phys_writew(physAddress+0x07,(uint16_t)0xFEE2);		// loop $-2
		phys_writeb(physAddress+0x09,(uint8_t)0x59);		// pop cx
		phys_writeb(physAddress+0x0A,(uint8_t)0xCF);		//An IRET Instruction
		return (use_cb?15:11);
	case CB_INT13:
		phys_writeb(physAddress+0x00,(uint8_t)0xFB);		//STI
		if (use_cb) {
			phys_writeb(physAddress+0x01,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x02,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x03,(uint16_t)callback);	//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x01,(uint8_t)0xCF);		//An IRET Instruction
		phys_writew(physAddress+0x02,(uint16_t)0x0ECD);		// int 0e
		phys_writeb(physAddress+0x04,(uint8_t)0xCF);		//An IRET Instruction
		// for the image disk support to call
		phys_writew(physAddress+0x05,(uint16_t)0x13CD);		// int 13
		phys_writeb(physAddress+0x07,0xFE);
		phys_writeb(physAddress+0x08,0x38);
		phys_writew(physAddress+0x09,(uint16_t)call_idle);
		return (use_cb?9:5);
	case CB_VESA_WAIT:
		if (use_cb) E_Exit("VESA wait must not implement a callback handler!");
		phys_writeb(physAddress+0x00,(uint8_t)0xFB);		// sti
		phys_writeb(physAddress+0x01,(uint8_t)0x50);		// push ax
		phys_writeb(physAddress+0x02,(uint8_t)0x52);		// push dx
		phys_writeb(physAddress+0x03,(uint8_t)0xBA);		// mov dx,
		phys_writew(physAddress+0x04,(uint16_t)0x03DA);	// 0x3da
		phys_writeb(physAddress+0x06,(uint8_t)0xEC);		// in al,dx
		phys_writew(physAddress+0x07,(uint16_t)0x08A8);	// test al,8
		phys_writew(physAddress+0x09,(uint16_t)0xFB75);	// jne $-5
		phys_writeb(physAddress+0x0B,(uint8_t)0xEC);		// in al,dx
		phys_writew(physAddress+0x0C,(uint16_t)0x08A8);	// test al,8
		phys_writew(physAddress+0x0E,(uint16_t)0xFB74);	// je $-5
		phys_writeb(physAddress+0x10,(uint8_t)0x5A);		// pop dx
		phys_writeb(physAddress+0x11,(uint8_t)0x58);		// pop ax
		phys_writeb(physAddress+0x12,(uint8_t)0xCB);		//A RETF Instruction
		return 19;
	case CB_VESA_PM:
		if (use_cb) {
			phys_writeb(physAddress+0x00,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x01,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x02,(uint16_t)callback);	//The immediate word
			physAddress+=4;
		}
		phys_writew(physAddress+0x00,(uint16_t)0xC3F6);	// test bl,
		phys_writeb(physAddress+0x02,(uint8_t)0x80);		// 0x80
		phys_writew(physAddress+0x03,(uint16_t)0x1674);	// je $+22
		phys_writew(physAddress+0x05,(uint16_t)0x5066);	// push ax
		phys_writew(physAddress+0x07,(uint16_t)0x5266);	// push dx
		phys_writew(physAddress+0x09,(uint16_t)0xBA66);	// mov dx,
		phys_writew(physAddress+0x0B,(uint16_t)0x03DA);	// 0x3da
		phys_writeb(physAddress+0x0D,(uint8_t)0xEC);		// in al,dx
		phys_writew(physAddress+0x0E,(uint16_t)0x08A8);	// test al,8
		phys_writew(physAddress+0x10,(uint16_t)0xFB75);	// jne $-5
		phys_writeb(physAddress+0x12,(uint8_t)0xEC);		// in al,dx
		phys_writew(physAddress+0x13,(uint16_t)0x08A8);	// test al,8
		phys_writew(physAddress+0x15,(uint16_t)0xFB74);	// je $-5
		phys_writew(physAddress+0x17,(uint16_t)0x5A66);	// pop dx
		phys_writew(physAddress+0x19,(uint16_t)0x5866);	// pop ax
		if (use_cb)
			phys_writeb(physAddress+0x1B,(uint8_t)0xC3);	//A RETN Instruction
		return (use_cb?32:27);
	case CB_IRET_EOI_PIC2:
		if (use_cb) {
			phys_writeb(physAddress+0x00,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x01,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x02,(uint16_t)callback);		//The immediate word
			physAddress+=4;
		}
		phys_writeb(physAddress+0x00,(uint8_t)0x50);		// push ax
		phys_writeb(physAddress+0x01,(uint8_t)0xb0);		// mov al, 0x20
		phys_writeb(physAddress+0x02,(uint8_t)0x20);
		phys_writeb(physAddress+0x03,(uint8_t)0xe6);		// out 0xA0, al (IBM) / out 0x08, al (PC-98)
		phys_writeb(physAddress+0x04,(uint8_t)(IS_PC98_ARCH ? 0x08 : 0xA0));
		phys_writeb(physAddress+0x05,(uint8_t)0xe6);		// out 0x20, al (IBM) / out 0x00, al (PC-98)
		phys_writeb(physAddress+0x06,(uint8_t)(IS_PC98_ARCH ? 0x00 : 0x20));
		phys_writeb(physAddress+0x07,(uint8_t)0x58);		// pop ax
		phys_writeb(physAddress+0x08,(uint8_t)0xcf);		//An IRET Instruction
		return (use_cb?0x0d:0x09);
	case CB_CPM:
		phys_writeb(physAddress+0x00,(uint8_t)0x9C);		//PUSHF
		return CALLBACK_SetupExtra(callback,CB_INT21,physAddress+1,use_cb)+1;
	case CB_INT6F_ATOK:
		phys_writeb(physAddress+0x00,(uint8_t)0xEB);		// jmp
		phys_writeb(physAddress+0x01,(uint8_t)0x06);		// 108
		phys_writeb(physAddress+0x03,(uint8_t)'A');
		phys_writeb(physAddress+0x04,(uint8_t)'T');
		phys_writeb(physAddress+0x05,(uint8_t)'O');
		phys_writeb(physAddress+0x06,(uint8_t)'K');
		if (use_cb) {
			phys_writeb(physAddress+0x08,(uint8_t)0xFE);	//GRP 4
			phys_writeb(physAddress+0x09,(uint8_t)0x38);	//Extra Callback instruction
			phys_writew(physAddress+0x0a,(uint16_t)callback);		//The immediate word
		}
		phys_writeb(physAddress+0x0c,(uint8_t)0xCF);		//An IRET Instruction
		return 13;
	default:
		E_Exit("CALLBACK:Setup:Illegal type %u",(unsigned int)type);
	}
	return 0;
}

bool CALLBACK_Setup(Bitu callback,CallBack_Handler handler,Bitu type,const char* descr) {
	if (callback>=CB_MAX) return false;
	CALLBACK_SetupExtra(callback,type,CALLBACK_PhysPointer(callback)+0,(handler!=NULL));
	CallBack_Handlers[callback]=handler;
	CALLBACK_SetDescription(callback,descr);
	return true;
}

Bitu CALLBACK_Setup(Bitu callback,CallBack_Handler handler,Bitu type,PhysPt addr,const char* descr) {
	if (callback>=CB_MAX) return 0;
	Bitu csize=CALLBACK_SetupExtra(callback,type,addr,(handler!=NULL));
	if (csize>0) {
		CallBack_Handlers[callback]=handler;
		CALLBACK_SetDescription(callback,descr);
	}
	return csize;
}

void CALLBACK_RemoveSetup(Bitu callback) {
	if (MemBase == NULL) {
		/* avoid crash */
		return;
	}

	for (uint8_t i = 0;i < CB_SIZE;i++) {
		phys_writeb(CALLBACK_PhysPointer(callback)+i ,(uint8_t) 0x00);
	}
}

void CALLBACK_HandlerObject::Uninstall(){
	if(!installed) return;
	if(m_type == CALLBACK_HandlerObject::SETUP) {
		if(vectorhandler.installed && MemBase != NULL && !cpu.pmode){
			//See if we are the current handler. if so restore the old one
			if(RealGetVec(vectorhandler.interrupt) == Get_RealPointer()) {
				RealSetVec(vectorhandler.interrupt,vectorhandler.old_vector);
			} else
				LOG(LOG_MISC,LOG_WARN)("Interrupt vector changed on %X %s",vectorhandler.interrupt,CALLBACK_GetDescription(m_callback));
		}
		CALLBACK_RemoveSetup(m_callback);
	} else if(m_type == CALLBACK_HandlerObject::SETUPAT){
		E_Exit("Callback:SETUP at not handled yet.");
	} else if(m_type == CALLBACK_HandlerObject::NONE){
		//Do nothing. Merely DeAllocate the callback
	} else E_Exit("what kind of callback is this!");
	if(CallBack_Description[m_callback]) delete [] CallBack_Description[m_callback];
	CallBack_Description[m_callback] = nullptr;
	CALLBACK_DeAllocate(m_callback);
	installed=false;
}

CALLBACK_HandlerObject::~CALLBACK_HandlerObject(){
	Uninstall();
}

void CALLBACK_HandlerObject::Install(CallBack_Handler handler,Bitu type,const char* description){
	if(!installed) {
		installed=true;
		m_type=SETUP;
		m_callback=CALLBACK_Allocate();
		CALLBACK_Setup(m_callback,handler,type,description);
	} else E_Exit("Callback handler object already installed");
}
void CALLBACK_HandlerObject::Install(CallBack_Handler handler,Bitu type,PhysPt addr,const char* description){
	if(!installed) {
		installed=true;
		m_type=SETUP;
		m_callback=CALLBACK_Allocate();
		CALLBACK_Setup(m_callback,handler,type,addr,description);
	} else E_Exit("Callback handler object already installed");
}

void CALLBACK_HandlerObject::Allocate(CallBack_Handler handler,const char* description) {
	if(!installed) {
		installed=true;
		m_type=NONE;
		m_callback=CALLBACK_Allocate();
		CALLBACK_SetDescription(m_callback,description);
		CallBack_Handlers[m_callback]=handler;
	} else E_Exit("Callback handler object already installed");
}

void CALLBACK_HandlerObject::Set_RealVec(uint8_t vec,bool reinstall){
	if(!vectorhandler.installed || reinstall) {
		vectorhandler.installed=true;
		vectorhandler.interrupt=vec;
		RealSetVec(vec,Get_RealPointer(),vectorhandler.old_vector);
	} else E_Exit ("double usage of vector handler");
}

extern bool custom_bios;

void CALLBACK_Init() {
	{
		/* NTS: Layout of the callback area:
		 *
		 * CB_MAX entries CB_SIZE each, where executable x86 code is written per callback,
		 * followed by 256 entries 6 bytes each corresponding to an interrupt call */
		Bitu o;

		LOG(LOG_MISC,LOG_DEBUG)("Initializing DOSBox callback instruction system");

		o = ROMBIOS_GetMemory((CB_MAX*CB_SIZE)+(256*6),"DOSBox callback area",/*align*/4);
		if (o == 0) E_Exit("Cannot allocate callback area");
		CB_SOFFSET = o&0xFFFF;
		CB_SEG = (o>>4)&0xF000;
		if (((Bitu)CB_SOFFSET + (CB_MAX*CB_SIZE) + (256*6)) > 0x10000) E_Exit("Callback area spans 64KB segment");

		o = ROMBIOS_GetMemory(14/*2+2+3+2+2+3*/,"DOSBox vm86 hack",/*align*/4);
		if (o == 0) E_Exit("Cannot allocate vm86 hack");
		vm86_fake_io_off = o&0xFFFF;
		vm86_fake_io_seg = (o>>4)&0xF000;
		if ((vm86_fake_io_off+14) > 0x1000000) E_Exit("vm86 area spans 64KB segment");
	}

	LOG(LOG_CPU,LOG_DEBUG)("Callback area starts at %04x:%04x",CB_SEG,CB_SOFFSET);

	uint16_t i;
	for (i=0;i<CB_MAX;i++) {
		CallBack_Handlers[i]=&illegal_handler;
		CallBack_Description[i]=NULL;
	}

	/* Setup the Stop Handler */
	call_stop=CALLBACK_Allocate();
	CallBack_Handlers[call_stop]=stop_handler;
	CALLBACK_SetDescription(call_stop,"stop");
	phys_writeb(CALLBACK_PhysPointer(call_stop)+0,0xFE);
	phys_writeb(CALLBACK_PhysPointer(call_stop)+1,0x38);
	phys_writew(CALLBACK_PhysPointer(call_stop)+2,(uint16_t)call_stop);

	/* Setup the idle handler */
	call_idle=CALLBACK_Allocate();
	CallBack_Handlers[call_idle]=stop_handler;
	CALLBACK_SetDescription(call_idle,"idle");
	for (i=0;i<=11;i++) phys_writeb(CALLBACK_PhysPointer(call_idle)+i,0x90);
	phys_writeb(CALLBACK_PhysPointer(call_idle)+12,0xFE);
	phys_writeb(CALLBACK_PhysPointer(call_idle)+13,0x38);
	phys_writew(CALLBACK_PhysPointer(call_idle)+14,(uint16_t)call_idle);

	/* Default handlers for unhandled interrupts that have to be non-null */
	call_default=CALLBACK_Allocate();
	CALLBACK_Setup(call_default,&default_handler,CB_IRET,"default");

	/* Setup block of 0xCD 0xxx instructions */
	PhysPt rint_base=CALLBACK_GetBase()+CB_MAX*CB_SIZE;
	for (i=0;i<=0xff;i++) {
		phys_writeb(rint_base,0xCD);
		phys_writeb(rint_base+1,(uint8_t)i);
		phys_writeb(rint_base+2,0xFE);
		phys_writeb(rint_base+3,0x38);
		phys_writew(rint_base+4,(uint16_t)call_stop);
		rint_base+=6;

	}

	call_priv_io=CALLBACK_Allocate();

	// virtualizable in-out opcodes
	phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x00,(uint8_t)0xec);	// in al, dx
	phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x01,(uint8_t)0xcb);	// retf
	phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x02,(uint8_t)0xed);	// in ax, dx
	phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x03,(uint8_t)0xcb);	// retf
	phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x04,(uint8_t)0x66);	// in eax, dx
	phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x05,(uint8_t)0xed);
	phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x06,(uint8_t)0xcb);	// retf

	phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x08,(uint8_t)0xee);	// out dx, al
	phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x09,(uint8_t)0xcb);	// retf
	phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x0a,(uint8_t)0xef);	// out dx, ax
	phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x0b,(uint8_t)0xcb);	// retf
	phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x0c,(uint8_t)0x66);	// out dx, eax
	phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x0d,(uint8_t)0xef);
	phys_writeb(CALLBACK_PhysPointer(call_priv_io)+0x0e,(uint8_t)0xcb);	// retf
}
