/*-------------------------------------------------------------------------
| Damisoft Amiga Emulator
| https://amiga500.damisoft.ch
|
| Copyright (C) 1999-2019 Damisoft Groupe
|
| 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.
|
| Note: ported from WinUAE 3.2.x
-------------------------------------------------------------------------*/
/* global references */

var SAER_AutoConf_bank = null;

/*---------------------------------*/
/* global constants */

const SAEC_AutoConf_RTS = 0x4e75;
//const SAEC_AutoConf_RTE = 0x4e73;

/*---------------------------------*/
/* global variables */

var SAEV_AutoConf_base = 0xf00000; //RTAREA_DEFAULT;

var SAEV_AutoConf_boot_rom_type = 0;
var SAEV_AutoConf_boot_rom_size = 0;

/*---------------------------------*/

function SAEO_AutoConf() {
	const RTAREA_DEFAULT = 0xf00000;
	const RTAREA_BACKUP = 0xef0000;
	const RTAREA_BACKUP_2 = 0xdb0000;
	const RTAREA_SIZE = 0x10000;

	const RTAREA_TRAPS = 0x3000;
	const RTAREA_RTG = 0x3800;
	const RTAREA_TRAMPOLINE = 0x3b00;
	const RTAREA_DATAREGION = 0xF000;

	const RTAREA_FSBOARD = 0xFFEC;
	const RTAREA_HEARTBEAT = 0xFFF0;
	const RTAREA_TRAPTASK = 0xFFF4;
	const RTAREA_EXTERTASK = 0xFFF8;
	const RTAREA_INTREQ = 0xFFFC;

	const RTAREA_TRAP_DATA = 0x4000;
	const RTAREA_TRAP_DATA_SIZE = 0x8000;
	const RTAREA_TRAP_DATA_SLOT_SIZE = 0x2000; // 8192
	const RTAREA_TRAP_DATA_SECOND = 80;
	const RTAREA_TRAP_DATA_TASKWAIT = (RTAREA_TRAP_DATA_SECOND - 4);
	const RTAREA_TRAP_DATA_EXTRA = 144;
	const RTAREA_TRAP_DATA_EXTRA_SIZE = (RTAREA_TRAP_DATA_SLOT_SIZE - RTAREA_TRAP_DATA_EXTRA);

	const RTAREA_TRAP_SEND_DATA = 0xc0000;
	const RTAREA_TRAP_SEND_DATA_SIZE = 0x2000;

	const RTAREA_TRAP_STATUS = 0xF000;
	const RTAREA_TRAP_STATUS_SIZE = 8;
	const RTAREA_TRAP_STATUS_SECOND = 4;

	const RTAREA_TRAP_SEND_STATUS = 0xF100;

	const RTAREA_SYSBASE = 0x3FFC;

	const RTAREA_TRAP_DATA_NUM = (RTAREA_TRAP_DATA_SIZE / RTAREA_TRAP_DATA_SLOT_SIZE);



	/* Commonly used autoconfig strings */
	//var EXPANSION_explibname = 0, EXPANSION_doslibname = 0, EXPANSION_uaeversion = 0;
	//var EXPANSION_uaedevname, EXPANSION_explibbase = 0;
	//var EXPANSION_bootcode = 0, EXPANSION_nullfunc = 0;

	/* ROM tag area memory access */
	//var rtarea_base = RTAREA_DEFAULT; --> SAEV_AutoConf_base
	var hardware_trap_event = new Array(RTAREA_TRAP_DATA_SIZE / RTAREA_TRAP_DATA_SLOT_SIZE); //HANDLE

	var rt_trampoline_ptr = 0, trap_entry = 0;
	//var hwtrap_waiting = 0; //extern volatile uae_atomic, in traps
	var filesystem_state = 0; //extern int

	//var uae_boot_rom_type = 0; -> SAEV_AutoConf_boot_rom_type
	//var uae_boot_rom_size = 0; -> SAEV_AutoConf_boot_rom_size

	var uae_int_requested = 0; //volatile uae_atomic

	/*-----------------------------------------------------------------------*/

	function check_boot_rom(p) {
		var b = RTAREA_DEFAULT;

		/*if (currprefs.uaeboard > 1) {
			p.type = 2;
			return 0x00eb0000; // fixme!
		}
		p.type = 0;
		if (currprefs.boot_rom == 1)
			return 0;*/
		p.type = 1;
		/*if (currprefs.cs_cdtvcd || currprefs.cs_cdtvscsi || currprefs.uae_hide > 1)
			b = RTAREA_BACKUP;*/
		if (SAEV_config.chipset.mbdmac == 1)// || currprefs.cpuboard_type)
			b = RTAREA_BACKUP;
		// CSPPC enables MMU at boot and remaps 0xea0000->0xeffff.
		/*if (ISCPUBOARD(BOARD_BLIZZARD, BOARD_BLIZZARD_SUB_PPC))
			b = RTAREA_BACKUP_2;*/
		var ab = SAER_Memory_getBank(RTAREA_DEFAULT);
		if (ab !== null) {
			if (SAER_Memory_check(RTAREA_DEFAULT, 65536))
				b = RTAREA_BACKUP;
		}
		/*if (nr_directory_units(NULL))
			return b;
		if (nr_directory_units(&currprefs))
			return b;
		if (currprefs.socket_emu)
			return b;
		if (currprefs.uaeserial)
			return b;
		if (currprefs.scsi == 1) //uaescsi.device
			return b;
		if (currprefs.sana2)
			return b;
		if (currprefs.input_tablet > 0)
			return b;
		if (currprefs.rtgmem_size && currprefs.rtgmem_type < GFXBOARD_HARDWARE)
			return b;
		if (currprefs.win32_automount_removable)
			return b;*/
		if (SAEV_config.memory.chipSize > 2 * 1024 * 1024)
			return b;
		/*if (currprefs.z3chipmem_size)
			return b;
		if (currprefs.boot_rom >= 3)
			return b;
		if (currprefs.boot_rom == 2 && b == 0xf00000) {
			p.type = -1;
			return b;
		}*/
		p.type = 0;
		return 0;
	}

	this.need_uae_boot_rom = function() {
		var p = { type:0 };
		var v = check_boot_rom(p);
		SAEV_AutoConf_boot_rom_type = p.type;
		SAEF_log("autoconf.need_uae_boot_rom() type %d", SAEV_AutoConf_boot_rom_type);
		if (!SAEV_AutoConf_base) {
			v = 0;
			SAEV_AutoConf_boot_rom_type = 0;
		}
		return v;
	}

	/*-----------------------------------------------------------------------*/

	/*static bool istrapwait(void) {
		for (int i = 0; i < RTAREA_TRAP_DATA_NUM; i++) {
			uae_u8 *data = rtarea_bank.baseaddr + RTAREA_TRAP_DATA + i * RTAREA_TRAP_DATA_SLOT_SIZE;
			uae_u8 *status = rtarea_bank.baseaddr + RTAREA_TRAP_STATUS + i * RTAREA_TRAP_STATUS_SIZE;
			if (get_long_host(data + RTAREA_TRAP_DATA_TASKWAIT) && status[3] && status[2] >= 0x80) {
				return true;
			}
		}
		return false;
	}*/
	this.rethink_traps = function() {
		return false;
		/*if (currprefs.uaeboard < 2)
			return false;
		if (istrapwait()) {
			atomic_or(&uae_int_requested, 0x4000);
			set_special_exter(SPCFLAG_UAEINT);
			return true;
		}
		atomic_and(&uae_int_requested, ~0x4000);
		return false;*/
	}

	/*-----------------------------------------------------------------------*/

	const RTAREA_WRITEOFFSET = 0xfff0;

	function hwtrap_check_int() {
		if (hwtrap_waiting == 0) {
			atomic_and(uae_int_requested, ~0x2000);
		} else {
			atomic_or(uae_int_requested, 0x2000);
			set_special_exter(SPCFLAG_UAEINT);
		}
	}

	function rtarea_trap_data(addr) {
		if (addr >= RTAREA_TRAP_DATA && addr < RTAREA_TRAP_DATA + RTAREA_TRAP_DATA_SIZE)
			return true;
		return false;
	}

	function rtarea_trap_status(addr) {
		if (addr >= RTAREA_TRAP_STATUS && addr < RTAREA_TRAP_STATUS + RTAREA_TRAP_DATA_NUM * RTAREA_TRAP_STATUS_SIZE)
			return true;
		return false;
	}

	/*---------------------------------*/

	function rtarea_get32(addr) {
		addr &= 0xFFFF;
		return ((rtarea_bank.baseaddr[addr] << 24) | (rtarea_bank.baseaddr[addr + 1] << 16) | (rtarea_bank.baseaddr[addr + 2] << 8) | rtarea_bank.baseaddr[addr + 3]) >>> 0;
	}
	function rtarea_get16(addr) {
		addr &= 0xFFFF;
		return (rtarea_bank.baseaddr[addr] << 8) + rtarea_bank.baseaddr[addr + 1];
	}
	function rtarea_get8(addr) {
		addr &= 0xFFFF;

		if (rtarea_trap_status(addr)) {
			var addr2 = addr - RTAREA_TRAP_STATUS;
			var trap_offset = addr2 & (RTAREA_TRAP_STATUS_SIZE - 1);
			var trap_slot = Math.floor(addr2 / RTAREA_TRAP_STATUS_SIZE);
			if (trap_offset == 0) {
				// 0 = busy wait, 1 = Wait()
				rtarea_bank.baseaddr[addr] = filesystem_state ? 1 : 0;
			}
		} else if (addr == RTAREA_INTREQ + 0) {
			rtarea_bank.baseaddr[addr] = atomic_bit_test_and_reset(uae_int_requested, 0);
			//SAEF_log("autoconf.rtarea_get8() %s", rtarea_bank.baseaddr[addr] ? "+" : "-");
		} else if (addr == RTAREA_INTREQ + 1) {
			rtarea_bank.baseaddr[addr] = hwtrap_waiting != 0;
		} else if (addr == RTAREA_INTREQ + 2) {
			/*if (SAER.autoconf.rethink_traps()) //OWN empty
				rtarea_bank.baseaddr[addr] = 1;
			else*/
				rtarea_bank.baseaddr[addr] = 0;
		}
		hwtrap_check_int();
		return rtarea_bank.baseaddr[addr];
	}

	function rtarea_write(addr) {
		if (addr >= RTAREA_WRITEOFFSET)
			return true;
		if (addr >= RTAREA_SYSBASE && addr < RTAREA_SYSBASE + 4)
			return true;
		return rtarea_trap_data(addr) || rtarea_trap_status(addr);
	}
	function rtarea_put8(addr, value) {
		addr &= 0xffff;
		if (!rtarea_write(addr))
			return;
		rtarea_bank.baseaddr[addr] = value;
		if (!rtarea_trap_status(addr))
			return;
		addr -= RTAREA_TRAP_STATUS;
		var trap_offset = addr & (RTAREA_TRAP_STATUS_SIZE - 1);
		var trap_slot = Math.floor(addr / RTAREA_TRAP_STATUS_SIZE);
		if (trap_offset == RTAREA_TRAP_STATUS_SECOND + 3) {
			var v = value;
			if (v != 0xff && v != 0xfe && v != 0x01 && v != 02)
				SAEF_log("autoconf.rtarea_put8() TRAP %d (%02x)", trap_slot, v);
			if (v == 0xfe)
				atomic_dec(hwtrap_waiting);
			if (v == 0x01)
				atomic_dec(hwtrap_waiting);
			if (v == 0x01 || v == 0x02) {
				// signal call_hardware_trap_back()
				// FIXME: OS specific code!
				SetEvent(hardware_trap_event[trap_slot]);
			}
		}
	}
	function rtarea_put16(addr, value) {
		addr &= 0xffff;
		value &= 0xffff;
		if (!rtarea_write(addr))
			return;
		rtarea_put8(addr, value >> 8);
		rtarea_put8(addr + 1, value & 0xff);
		if (!rtarea_trap_status(addr))
			return;
		addr -= RTAREA_TRAP_STATUS;
		var trap_offset = addr & (RTAREA_TRAP_STATUS_SIZE - 1);
		var trap_slot = Math.floor(addr / RTAREA_TRAP_STATUS_SIZE);
		if (trap_offset == 0) {
			SAEF_log("autoconf.rtarea_put16() TRAP %d (%04x)", trap_slot, value);
			call_hardware_trap(rtarea_bank.baseaddr, SAEV_AutoConf_base, trap_slot);
		}
	}
	function rtarea_put32(addr, value) {
		addr &= 0xffff;
		if (!rtarea_write(addr))
			return;
		rtarea_bank.baseaddr[addr + 0] = value >>> 24;
		rtarea_bank.baseaddr[addr + 1] = (value >>> 16) & 0xff;
		rtarea_bank.baseaddr[addr + 2] = (value >>> 8) & 0xff;
		rtarea_bank.baseaddr[addr + 3] = value & 0xff;
	}

	function rtarea_xlate(addr) {
		addr &= 0xFFFF;
		//return rtarea_bank.baseaddr + addr;
		return addr;
	}
	function rtarea_check(addr, size) {
		addr &= 0xFFFF;
		return (addr + size) <= 0xFFFF;
	}

	var rtarea_bank = new SAEO_Memory_addrbank(
		rtarea_get32, rtarea_get16, rtarea_get8,
		rtarea_put32, rtarea_put16, rtarea_put8,
		rtarea_xlate, rtarea_check, null, "rtarea", "UAE Boot ROM",
		rtarea_get32, rtarea_get16,
		SAEC_Memory_addrbank_flag_ROMIN | SAEC_Memory_addrbank_flag_PPCIOSPACE//, S_READ, S_WRITE
	);
	SAER_AutoConf_bank = rtarea_bank;

	/*-----------------------------------------------------------------------*/

	this.reset = function() { //rtarea_reset()
		//memset(rtarea_bank.baseaddr + RTAREA_TRAP_DATA, 0, RTAREA_TRAP_DATA_SIZE);
		//memset(rtarea_bank.baseaddr + RTAREA_TRAP_STATUS, 0, RTAREA_TRAP_STATUS_SIZE * RTAREA_TRAP_DATA_NUM);
		SAEF_memset(rtarea_bank.baseaddr,RTAREA_TRAP_DATA, 0, RTAREA_TRAP_DATA_SIZE);
		SAEF_memset(rtarea_bank.baseaddr,RTAREA_TRAP_STATUS, 0, RTAREA_TRAP_STATUS_SIZE * RTAREA_TRAP_DATA_NUM);
	}

	/*-----------------------------------------------------------------------*/
	/* some quick & dirty code to fill in the rt area and save me a lot of scratch paper */

	var rt_addr = 0;
	var rt_straddr = 0;

	function addr(ptr) {
		//SAEF_log("autoconf.addr() %08x", ptr + SAEV_AutoConf_base);
		//return (uae_u32)ptr + SAEV_AutoConf_base;
		return ptr + SAEV_AutoConf_base;
	}
	this.db = function(data) {
		//SAEF_log("autoconf.db() %02x", data);
		rtarea_bank.baseaddr[rt_addr++] = data;
	}
	this.dw = function(data) {
		//SAEF_log("autoconf.dw() %04x", data);
		rtarea_bank.baseaddr[rt_addr++] = data >> 8;
		rtarea_bank.baseaddr[rt_addr++] = data & 0xff;
	}
	this.dl = function(data) {
		//SAEF_log("autoconf.dl() %08x", data);
		rtarea_bank.baseaddr[rt_addr++] = data >> 24;
		rtarea_bank.baseaddr[rt_addr++] = (data >> 16) & 0xff;
		rtarea_bank.baseaddr[rt_addr++] = (data >> 8) & 0xff;
		rtarea_bank.baseaddr[rt_addr++] = data & 0xff;
	}

	/*this.dbg = function(addr) {
		addr -= SAEV_AutoConf_base;
		return rtarea_bank.baseaddr[addr];
	}*/

	/* store strings starting at the end of the rt area and working backward.  store pointer at current address */
	/*uae_u32 ds_ansi (const uae_char *str) {
		int len;

		if (!str)
			return addr (rt_straddr);
		len = strlen (str) + 1;
		rt_straddr -= len;
		strcpy ((uae_char*)rtarea_bank.baseaddr + rt_straddr, str);
		return addr (rt_straddr);
	}
	uae_u32 ds (const TCHAR *str) {
		char *s = ua (str);
		uae_u32 v = ds_ansi (s);
		xfree (s);
		return v;
	}
	uae_u32 ds_bstr_ansi (const uae_char *str) {
		int len;

		len = strlen (str) + 2;
		rt_straddr -= len;
		while (rt_straddr & 3)
			rt_straddr--;
		rtarea_bank.baseaddr[rt_straddr] = len - 2;
		strcpy ((uae_char*)rtarea_bank.baseaddr + rt_straddr + 1, str);
		return addr (rt_straddr) >> 2;
	}*/

	this.calltrap = function(n) {
		/*if (currprefs.uaeboard > 2) {
			this.dw(0x4eb9); // JSR rt_trampoline_ptr
			this.dl(rt_trampoline_ptr);
			uaecptr a = this.here();
			this.org(rt_trampoline_ptr);
			this.dw(0x3f3c); // MOVE.W #n,-(SP)
			this.dw(n);
			this.dw(0x4ef9); // JMP rt_trampoline_entry
			this.dl(trap_entry);
			this.org(a);
			rt_trampoline_ptr += 3 * 2 + 1 * 4;
		} else*/
			this.dw(0xA000 + n);
	}

	this.org = function(a) {
		if (((a & 0xffff0000) >>> 0 != 0x00f00000) && ((a & 0xffff0000) >>> 0 != SAEV_AutoConf_base))
			SAEF_warn("autoconf.org() corrupt address %08X", a);
		rt_addr = a & 0xffff;
	}

	this.here = function() {
		return addr(rt_addr);
	}

	/*this.align = function(b) {
		rt_addr = (rt_addr + b - 1) & ~(b - 1);
	}*/

	/*-----------------------------------------------------------------------*/

	function mapped_malloc(ab) {
		ab.startmask = ab.start;
		try {
			//ab.baseaddr = xcalloc(uae_u8, ab.allocated + 4);
			ab.baseaddr = new Uint8Array(ab.allocated + 4);
			return true;
		} catch (e) {
			ab.baseaddr = null;
			return false;
		}
	}
	function mapped_free(ab) {
		//xfree(ab.baseaddr);
		ab.baseaddr = null;
	}

	/*static uae_u32 REGPARAM2 nullfunc (TrapContext *ctx) {
		write_log (_T("Null function called\n"));
		return 0;
	}
	static uae_u32 REGPARAM2 getchipmemsize (TrapContext *ctx) {
		trap_set_dreg(ctx, 1, z3chipmem_bank.allocated);
		trap_set_areg(ctx, 1, z3chipmem_bank.start);
		return chipmem_bank.allocated;
	}
	static uae_u32 REGPARAM2 uae_puts (TrapContext *ctx) {
		puts ((char*)get_real_address(trap_get_areg(ctx, 0)));
		return 0;
	}*/

	/* OPT inline ok
	function rtarea_init_mem() {
		if (SAER.autoconf.need_uae_boot_rom())
			rtarea_bank.flags &= ~SAEC_Memory_addrbank_flag_ALLOCINDIRECT;
		else
			rtarea_bank.flags |= SAEC_Memory_addrbank_flag_ALLOCINDIRECT;

		rtarea_bank.allocated = RTAREA_SIZE;
		if (!mapped_malloc(rtarea_bank)) {
			SAEF_fatal(SAEE_NoMemory, "autoconf.init_mem() memory exhausted");
			//abort();
		}
	}*/
	this.setup = function() { //rtarea_init()
		rt_straddr = 0xFF00 - 2;
		rt_addr = 0;

		rt_trampoline_ptr = SAEV_AutoConf_base + RTAREA_TRAMPOLINE;
		trap_entry = 0;

		this.init_traps();

		//rtarea_init_mem();
		{
			if (this.need_uae_boot_rom())
				rtarea_bank.flags &= ~SAEC_Memory_addrbank_flag_ALLOCINDIRECT;
			else
				rtarea_bank.flags |= SAEC_Memory_addrbank_flag_ALLOCINDIRECT;

			rtarea_bank.allocated = RTAREA_SIZE;
			if (!mapped_malloc(rtarea_bank)) {
				SAEF_fatal(SAEE_NoMemory, "autoconf.init_mem() memory exhausted");
				//abort();
			}
		}
		//memset(rtarea_bank.baseaddr, 0, RTAREA_SIZE);
		SAEF_memset(rtarea_bank.baseaddr,0, 0, RTAREA_SIZE);

		/*var uaever = sprintf("uae-%d.%d.%d", UAEMAJOR, UAEMINOR, UAESUBREV);
		var saever = sprintf("sae-%d.%d.%d", SAEC_Version, SAEC_Revision, SAEC_Patch);
		EXPANSION_uaeversion = ds(saever);
		EXPANSION_explibname = ds("expansion.library");
		EXPANSION_doslibname = ds("dos.library");
		EXPANSION_uaedevname = ds("uae.device");*/

		this.dw(0);
		this.dw(0);

		/*#ifdef FILESYS
		filesys_install_code();

		trap_entry = filesys_get_entry(10);
		write_log(_T("TRAP_ENTRY = %08x\n"), trap_entry);

		for (int i = 0; i < RTAREA_TRAP_DATA_SIZE / RTAREA_TRAP_DATA_SLOT_SIZE; i++) {
			hardware_trap_event[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
		}
		#endif*/

		this.define_trap(null, 0, "null"); /* Generic emulator trap */

		/*var a = this.here();
		// Dummy trap - removing this breaks the filesys emulation.
		this.org(SAEV_AutoConf_base + 0xFF00);
		this.calltrap(deftrap2(nullfunc, TRAPFLAG_NO_RETVAL, ""));

		this.org(SAEV_AutoConf_base + 0xFF80);
		this.calltrap(deftrapres(getchipmemsize, TRAPFLAG_DORET, "getchipmemsize"));
		this.dw(SAEC_AutoConf_RTS);

		this.org(SAEV_AutoConf_base + 0xFF10);
		this.calltrap(deftrapres(uae_puts, TRAPFLAG_NO_RETVAL, "uae_puts"));
		this.dw(SAEC_AutoConf_RTS);

		this.org(a);*/

		SAEV_AutoConf_boot_rom_size = this.here() - SAEV_AutoConf_base;
		SAEF_log("autoconf.setup() boot_rom_size %d/%d", SAEV_AutoConf_boot_rom_size, RTAREA_TRAPS);
		if (SAEV_AutoConf_boot_rom_size >= RTAREA_TRAPS) {
			SAEF_fatal(SAEE_NoMemory, "autoconf.setup() RTAREA_TRAPS needs to be increased!");
			//abort();
		}

		/*#ifdef PICASSO96
		uaegfx_install_code(SAEV_AutoConf_base + RTAREA_RTG);
		#endif*/

		this.org(RTAREA_TRAPS | SAEV_AutoConf_base);
		this.init_extended_traps();
	}

	this.cleanup = function() { //rtarea_free()
		mapped_free(rtarea_bank);
		this.free_traps();
	}

	this.init = function() { //rtarea_setup()
		var base = this.need_uae_boot_rom();
		if (base) {
			SAEF_log("autoconf.init() RTAREA located at %08X", base);
			SAEV_AutoConf_base = base;
		}
	}

	/*-----------------------------------------------------------------------*/

	this.makedatatable = function(resid, resname, type, priority, ver, rev) {
		var datatable = this.here();
		this.dw(0xE000); /* INITBYTE */
		this.dw(0x0008); /* LN_TYPE */
		this.dw(type << 8);
		this.dw(0xE000); /* INITBYTE */
		this.dw(0x0009); /* LN_PRI */
		this.dw(priority << 8);
		this.dw(0xC000); /* INITLONG */
		this.dw(0x000A); /* LN_NAME */
		this.dl(resname);
		this.dw(0xE000); /* INITBYTE */
		this.dw(0x000E); /* LIB_FLAGS */
		this.dw(0x0600); /* LIBF_SUMUSED | LIBF_CHANGED */
		this.dw(0xD000); /* INITWORD */
		this.dw(0x0014); /* LIB_VERSION */
		this.dw(ver);
		this.dw(0xD000); /* INITWORD */
		this.dw(0x0016); /* LIB_REVISION */
		this.dw(rev);
		this.dw(0xC000); /* INITLONG */
		this.dw(0x0018); /* LIB_IDSTRING */
		this.dl(resid);
		this.dw(0x0000); /* end of table */
		return datatable;
	}

	/*-----------------------------------------------------------------------*/
	/* SECT Traps */
	/*-----------------------------------------------------------------------*/

	const TRAPFLAG_NO_REGSAVE = 1;
	const TRAPFLAG_NO_RETVAL = 2;
	const TRAPFLAG_EXTRA_STACK = 4;
	const TRAPFLAG_DORET = 8;
	const TRAPFLAG_UAERES = 16;

	function Trap() {
		this.handler = null;	/* Handler function to be invoked for this trap */
		this.flags = 0;		/* Trap attributes */
		this.name = "";		/* For debugging purposes */
		this.addr = 0;
	};
	const MAX_TRAPS = 16; //4096;

	var trap_count = 1;
	var traps = new Array(MAX_TRAPS);
	for (var vi = 0; vi < MAX_TRAPS; vi++)
		traps[vi] = new Trap();

	var hwtrap_waiting = 0; //volatile uae_atomic

	const trace_traps = true;

	/*-----------------------------------------------------------------------*/

	this.find_trap = function(name) {
		for (var i = 0; i < trap_count; i++) {
			var trap = traps[i];
			if ((trap.flags & TRAPFLAG_UAERES) && trap.name.length && trap.name == name)
				return trap.addr;
		}
		return 0;
	}

	/*
	* Define an emulator trap
	*
	* handler_func = host function that will be invoked to handle this trap
	* flags        = trap attributes
	* name         = name for debugging purposes
	*
	* returns trap number of defined trap
	*/
	this.define_trap = function(handler_func, flags, name) {
		if (trap_count == MAX_TRAPS) {
			SAEF_fatal(SAEE_Internal, "define_trap() Ran out of emulator traps. (increase MAX_TRAPS)");
			//abort();
			//return -1;
		} else {
			var addr = this.here();

			for (var i = 0; i < trap_count; i++) {
				if (addr == traps[i].addr)
					return i;
			}

			var trap_num = trap_count++;
			var trap = traps[trap_num];

			trap.handler = handler_func;
			trap.flags   = flags;
			trap.name    = name;
			trap.addr    = addr;

			return trap_num;
		}
	}

	/*
	* This function is called by the 68k interpreter to handle an emulator trap.
	*
	* trap_num = number of trap to invoke
	* regs     = current 68k state
	*/
	this.m68k_handle_trap = function(trap_num) {
		var trap = traps[trap_num];
		var retval = 0;

		var has_retval = (trap.flags & TRAPFLAG_NO_RETVAL) == 0;
		var implicit_rts = (trap.flags & TRAPFLAG_DORET) != 0;

		if (trap.name.length && trace_traps)
			SAEF_log("m68k_handle_trap() TRAP '%s'", trap.name);

		if (trap_num < trap_count) {
			if (trap.flags & TRAPFLAG_EXTRA_STACK) {
				/* Handle an extended trap.
				* Note: the return value of this trap is passed back to 68k
				* space via a separate, dedicated simple trap which the trap
				* handler causes to be invoked when it is done.
				*/
				//trap_HandleExtendedTrap(trap.handler, has_retval); //FIX implement extended-traps
				SAEF_fatal(SAEE_Internal, "m68k_handle_trap() Extended-traps are not implemented.");
			} else {
				/* Handle simple trap */
				//retval = (trap.handler)(null);
				retval = trap.handler(null);

				if (has_retval) {
					SAER_CPU_regs.d[0] = retval;
					SAEF_log("m68k_handle_trap() D0 = %d", retval);
				}
				if (implicit_rts) {
					//m68k_do_rts(); {
						var newpc = SAER_Memory_get32(SAER_CPU_regs.a[7]);
						SAER_CPU_setPC(newpc);
						SAER_CPU_regs.a[7] += 4;
					//}
					SAER_CPU_fill_prefetch();
				}
			}
		} else
			SAEF_warn("m68k_handle_trap() illegal emulator trap");
	}

	/*-----------------------------------------------------------------------*/

	this.init_traps = function() {
		trap_count = 0;
		hwtrap_waiting = 0;
	}
	this.free_traps = function() {

	}
	this.init_extended_traps = function() {

	}
}
