/*
* UAE - The Un*x Amiga Emulator
*
* AutoConfig devices
*
* Copyright 1995, 1996 Bernd Schmidt
* Copyright 1996 Ed Hanway
*/

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

#define NEW_TRAP_DEBUG 0

#include "options.h"
#include "uae.h"
#include "memory.h"
#include "custom.h"
#include "events.h"
#include "newcpu.h"
#include "autoconf.h"
#include "traps.h"
#include "debug.h"
#include "threaddep/thread.h"
#include "native2amiga.h"
#include "inputdevice.h"
#include "uae/ppc.h"
#include "devices.h"

/* Commonly used autoconfig strings */

uaecptr EXPANSION_explibname, EXPANSION_doslibname, EXPANSION_uaeversion;
uaecptr EXPANSION_uaedevname, EXPANSION_explibbase = 0;
uaecptr EXPANSION_bootcode, EXPANSION_nullfunc;

/* ROM tag area memory access */

uaecptr rtarea_base = RTAREA_DEFAULT;
uae_sem_t hardware_trap_event[RTAREA_TRAP_DATA_SIZE / RTAREA_TRAP_DATA_SLOT_SIZE];
uae_sem_t hardware_trap_event2[RTAREA_TRAP_DATA_SIZE / RTAREA_TRAP_DATA_SLOT_SIZE];

static uaecptr rt_trampoline_ptr, trap_entry;
static bool rtarea_write_enabled;
extern volatile uae_atomic hwtrap_waiting;
extern volatile int trap_mode;

DECLARE_MEMORY_FUNCTIONS(rtarea);
addrbank rtarea_bank = {
	rtarea_lget, rtarea_wget, rtarea_bget,
	rtarea_lput, rtarea_wput, rtarea_bput,
	rtarea_xlate, rtarea_check, NULL, _T("rtarea"), _T("UAE Boot ROM"),
	rtarea_lget, rtarea_wget,
	ABFLAG_ROMIN | ABFLAG_PPCIOSPACE, S_READ, S_WRITE
};

#define MAX_ABSOLUTE_ROM_ADDRESS 1024

static int absolute_rom_address;
static uaecptr absolute_rom_addresses[MAX_ABSOLUTE_ROM_ADDRESS];
static uaecptr rombase_new;

static void hwtrap_check_int(void)
{
	if (currprefs.uaeboard < 2)
		return;
	if (hwtrap_waiting == 0) {
		atomic_and(&uae_int_requested, ~0x2000);
	} else {
		atomic_or(&uae_int_requested, 0x2000);
		set_special_exter(SPCFLAG_UAEINT);
	}
}

static bool istrapwait(void)
{
	for (int i = 0; i < RTAREA_TRAP_DATA_NUM + RTAREA_TRAP_DATA_SEND_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;
}

static bool rethink_traps2(void)
{
	if (currprefs.uaeboard < 2)
		return false;
	if (istrapwait()) {
		atomic_or(&uae_int_requested, 0x4000);
		set_special_exter(SPCFLAG_UAEINT);
		return true;
	} else {
		atomic_and(&uae_int_requested, ~0x4000);
		return false;
	}
}

static void rethink_traps(void)
{
	rethink_traps2();
}


#define RTAREA_WRITEOFFSET 0xfff0

static bool rtarea_trap_data(uaecptr addr)
{
	if (currprefs.uaeboard < 2)
		return false;
	if (addr >= RTAREA_TRAP_DATA && addr < RTAREA_TRAP_DATA + (RTAREA_TRAP_DATA_NUM + RTAREA_TRAP_DATA_SEND_NUM) * RTAREA_TRAP_DATA_SLOT_SIZE)
		return true;
	return false;
}
static bool rtarea_trap_status(uaecptr addr)
{
	if (currprefs.uaeboard < 2)
		return false;
	if (addr >= RTAREA_TRAP_STATUS && addr < RTAREA_TRAP_STATUS + (RTAREA_TRAP_DATA_NUM + RTAREA_TRAP_DATA_SEND_NUM) * RTAREA_TRAP_STATUS_SIZE)
		return true;
	return false;
}
static bool rtarea_trap_status_extra(uaecptr addr)
{
	if (currprefs.uaeboard < 2)
		return false;
	if (addr >= RTAREA_TRAP_STATUS + 0x100 && addr < RTAREA_TRAP_STATUS + 0x100 + (RTAREA_TRAP_DATA_NUM + RTAREA_TRAP_DATA_SEND_NUM) * RTAREA_TRAP_STATUS_SIZE)
		return true;
	return false;
}

static uae_u8 *REGPARAM2 rtarea_xlate (uaecptr addr)
{
	addr &= 0xFFFF;
	return rtarea_bank.baseaddr + addr;
}

static int REGPARAM2 rtarea_check (uaecptr addr, uae_u32 size)
{
	addr &= 0xFFFF;
	return (addr + size) <= 0xFFFF;
}

static uae_u32 REGPARAM2 rtarea_lget (uaecptr addr)
{
	addr &= 0xFFFF;
	if (addr & 1)
		return 0;
	if (addr >= 0xfffd)
		return 0;
	return (rtarea_bank.baseaddr[addr + 0] << 24) | (rtarea_bank.baseaddr[addr + 1] << 16) |
		(rtarea_bank.baseaddr[addr + 2] << 8) | (rtarea_bank.baseaddr[addr + 3] << 0);
}
static uae_u32 REGPARAM2 rtarea_wget (uaecptr addr)
{
	addr &= 0xFFFF;
	if (addr & 1)
		return 0;

	uaecptr addr2 = addr - RTAREA_TRAP_STATUS;

	if (rtarea_trap_status(addr)) {
		int trap_offset = addr2 & (RTAREA_TRAP_STATUS_SIZE - 1);
		int trap_slot = addr2 / RTAREA_TRAP_STATUS_SIZE;
		// lock attempt
		if (trap_offset == 2) {
			if (rtarea_bank.baseaddr[addr + 1] & 0x80) {
				return rtarea_bank.baseaddr[addr + 1];
			}
			rtarea_bank.baseaddr[addr + 1] |= 0x80;
			rtarea_bank.baseaddr[addr + 0] = 0;
			return 0;
		}
	}
	return (rtarea_bank.baseaddr[addr] << 8) + rtarea_bank.baseaddr[addr + 1];
}
static uae_u32 REGPARAM2 rtarea_bget (uaecptr addr)
{
	addr &= 0xFFFF;

	hwtrap_check_int();
	if (rtarea_trap_status(addr)) {
		uaecptr addr2 = addr - RTAREA_TRAP_STATUS;
		int trap_offset = addr2 & (RTAREA_TRAP_STATUS_SIZE - 1);
		int trap_slot = addr2 / RTAREA_TRAP_STATUS_SIZE;
		if (trap_offset == 0) {
			// 0 = busy wait, 1 = Wait()
			rtarea_bank.baseaddr[addr] = trap_mode == 1 ? 1 : 0;
		}
		uae_u8 v = rtarea_bank.baseaddr[addr];
#if NEW_TRAP_DEBUG
		write_log(_T("GET TRAP SLOT %d OFFSET %d: V=%02x\n"), trap_slot, trap_offset, v);
#endif
		return v;
	} else if (addr == RTAREA_INTREQ + 0) {
		rtarea_bank.baseaddr[addr] = atomic_bit_test_and_reset(&uae_int_requested, 0);
		//write_log(rtarea_bank.baseaddr[addr] ? _T("+") : _T("-"));
	} else if (addr == RTAREA_INTREQ + 1) {
		rtarea_bank.baseaddr[addr] = hwtrap_waiting != 0;
	} else if (addr == RTAREA_INTREQ + 2) {
		if (rethink_traps2()) {
			rtarea_bank.baseaddr[addr] = 1;
		} else {
			rtarea_bank.baseaddr[addr] = 0;
		}
	}
	return rtarea_bank.baseaddr[addr];
}

static bool rtarea_write(uaecptr addr)
{
	if (rtarea_write_enabled)
		return true;
	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) || rtarea_trap_status_extra(addr);
}

static void REGPARAM2 rtarea_bput (uaecptr addr, uae_u32 value)
{
	addr &= 0xffff;
	if (!rtarea_write(addr))
		return;
	rtarea_bank.baseaddr[addr] = value;
	if (addr == RTAREA_INTREQ + 3) {
		mousehack_wakeup();
	}
	if (!rtarea_trap_status(addr))
		return;
	addr -= RTAREA_TRAP_STATUS;
	int trap_offset = addr & (RTAREA_TRAP_STATUS_SIZE - 1);
	int trap_slot = addr / RTAREA_TRAP_STATUS_SIZE;
#if NEW_TRAP_DEBUG
	write_log(_T("PUT TRAP SLOT %d OFFSET %d: V=%02x\n"), trap_slot, trap_offset, (uae_u8)value);
#endif
	if (trap_offset == RTAREA_TRAP_STATUS_SECOND) {
		write_log(_T("TRAP SLOT %d PRI=%02x\n"), trap_slot, (uae_u8)value);
	}
	if (trap_offset == RTAREA_TRAP_STATUS_SECOND + 3) {
		uae_u8 v = (uae_u8)value;
		if (v != 0xff && v != 0xfd && v != 0x01 && v != 0x02 && v != 0x03 && v != 0x04)
			write_log(_T("TRAP SLOT %d STATUS = %02x\n"), trap_slot, v);
		if (v == 0xfd)
			atomic_dec(&hwtrap_waiting);
		if (v == 0x01)
			atomic_dec(&hwtrap_waiting);
		// 1 = interrupt ack
		// 2 = interrupt -> task ack
		// 3 = hwtrap_entry ack
		if (v == 0x01 || v == 0x02 || v == 0x03) {
			// signal call_hardware_trap_back_back()
			uae_sem_post(&hardware_trap_event[trap_slot]);
		}
	}
}

static void REGPARAM2 rtarea_wput (uaecptr addr, uae_u32 value)
{
	addr &= 0xffff;
	value &= 0xffff;

	if (addr & 1)
		return;

	if (!rtarea_write(addr))
		return;

	uaecptr addr2 = addr - RTAREA_TRAP_STATUS;

	if (rtarea_trap_status(addr)) {
		int trap_offset = addr2 & (RTAREA_TRAP_STATUS_SIZE - 1);
		int trap_slot = addr2 / RTAREA_TRAP_STATUS_SIZE;
		if (trap_offset == 2) {
			value = 0;
			if ((rtarea_bank.baseaddr[addr + 1] & 0x80) == 0) {
				write_log(_T("TRAP SLOT %d unlock without allocation!\n"), trap_slot);
			}
			rtarea_bank.baseaddr[addr + 0] = value >> 8;
			rtarea_bank.baseaddr[addr + 1] = (uae_u8)value;
			return;
		}
	}
	
	rtarea_bank.baseaddr[addr + 0] = value >> 8;
	rtarea_bank.baseaddr[addr + 1] = (uae_u8)value;

	if (rtarea_trap_status(addr)) {
		int trap_offset = addr2 & (RTAREA_TRAP_STATUS_SIZE - 1);
		int trap_slot = addr2 / RTAREA_TRAP_STATUS_SIZE;

#if NEW_TRAP_DEBUG
		write_log(_T("PUT TRAP SLOT %d OFFSET %d: V=%04x\n"), trap_slot, trap_offset, (uae_u16)value);
#endif
		if (trap_offset == 0) {
#if NEW_TRAP_DEBUG
			write_log(_T("TRAP SLOT %d NUM=%d\n"), trap_slot, value);
#endif
			call_hardware_trap(rtarea_bank.baseaddr, rtarea_base, trap_slot);
		}
	}
}

static void REGPARAM2 rtarea_lput (uaecptr addr, uae_u32 value)
{
	addr &= 0xffff;
	if (addr & 1)
		return;
	if (addr >= 0xfffd)
		return;
	if (!rtarea_write(addr))
		return;
	rtarea_bank.baseaddr[addr + 0] = value >> 24;
	rtarea_bank.baseaddr[addr + 1] = value >> 16;
	rtarea_bank.baseaddr[addr + 2] = value >> 8;
	rtarea_bank.baseaddr[addr + 3] = value >> 0;

	if (rtarea_trap_status(addr)) {
		addr -= RTAREA_TRAP_STATUS;
		int trap_offset = addr & (RTAREA_TRAP_STATUS_SIZE - 1);
		int trap_slot = addr / RTAREA_TRAP_STATUS_SIZE;
#if NEW_TRAP_DEBUG
		write_log(_T("PUT TRAP SLOT %d OFFSET %d: V=%08x\n"), trap_slot, trap_offset, value);
#endif
	}
}

static int rt_addr;
static int rt_straddr;
static int rt_addr_restart;
static bool rt_addr_reset;

void rtarea_reset(void)
{
	uae_u8 *p = rtarea_bank.baseaddr;
	if (p) {
		memset(p + RTAREA_TRAP_DATA, 0, RTAREA_TRAP_DATA_SLOT_SIZE * (RTAREA_TRAP_DATA_NUM + RTAREA_TRAP_DATA_SEND_NUM));
		memset(p + RTAREA_TRAP_STATUS, 0, RTAREA_TRAP_STATUS_SIZE * (RTAREA_TRAP_DATA_NUM + RTAREA_TRAP_DATA_SEND_NUM));
		memset(p + RTAREA_HEARTBEAT, 0, 0x10000 - RTAREA_HEARTBEAT);
		memset(p + RTAREA_VARIABLES, 0, RTAREA_VARIABLES_SIZE);
	}
	if (rt_addr_reset) {
		rt_addr_reset = false;
		rt_addr_restart = rt_addr;
	}
	rt_addr = rt_addr_restart;
	trap_reset();
	absolute_rom_address = 0;
}

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

uae_u32 addr (int ptr)
{
	return (uae_u32)ptr + rtarea_base;
}

void db (uae_u8 data)
{
	rtarea_bank.baseaddr[rt_addr++] = data;
}

void dw (uae_u16 data)
{
	rtarea_bank.baseaddr[rt_addr++] = (uae_u8)(data >> 8);
	rtarea_bank.baseaddr[rt_addr++] = (uae_u8)data;
}

void dl (uae_u32 data)
{
	rtarea_bank.baseaddr[rt_addr++] = data >> 24;
	rtarea_bank.baseaddr[rt_addr++] = data >> 16;
	rtarea_bank.baseaddr[rt_addr++] = data >> 8;
	rtarea_bank.baseaddr[rt_addr++] = data;
}

void df(uae_u8 b, int len)
{
	memset(&rtarea_bank.baseaddr[rt_addr], b, len);
	rt_addr += len;
}

uae_u8 dbg (uaecptr addr)
{
	addr &= 0xffff;
	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 = uaestrlen(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 dsf(uae_u8 b, int len)
{
	rt_straddr -= len;
	memset(rtarea_bank.baseaddr + rt_straddr, b, len);
	return addr(rt_straddr);
}

uae_u32 ds_bstr_ansi (const uae_char *str)
{
	int len;
 
	len = uaestrlen(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;
}

void save_rom_absolute(uaecptr addr)
{
	if (rombase_new)
		return;
	if (absolute_rom_address >= MAX_ABSOLUTE_ROM_ADDRESS) {
		write_log(_T("MAX_ABSOLUTE_ROM_ADDRESS is too low!"));
		abort();
	}
	for (int i = 0; i < absolute_rom_address; i++) {
		if (absolute_rom_addresses[i] == addr) {
			write_log(_T("Address %08x already added\n"), addr);
			return;
		}
	}
	absolute_rom_addresses[absolute_rom_address++] = addr;
}

void add_rom_absolute(uaecptr addr)
{
	uaecptr h = here();
	dl(addr);
	save_rom_absolute(h);
}

uae_u32 boot_rom_copy(TrapContext *ctx, uaecptr rombase, int mode)
{
	uaecptr reloc = 0;
	if (currprefs.uaeboard < 3)
		return 0;
	if (!mode) {
		rtarea_write_enabled = true;
		protect_roms(false);
		rombase_new = rombase;
		int size = 4 + 2 + 4;
		for (int i = 0; i < absolute_rom_address; i++) {
			uae_u32 a = absolute_rom_addresses[i];
			if (a >= rtarea_base && a < rtarea_base + 0x10000) {
				size += 2;
			} else {
				size += 4;
			}
		}
		reloc = uaeboard_alloc_ram(size);
		uae_u8 *p = uaeboard_map_ram(reloc);
		put_long_host(p, rtarea_base);
		p += 4;
		for (int i = 0; i < absolute_rom_address; i++) {
			uae_u32 a = absolute_rom_addresses[i];
			if (a >= rtarea_base && a < rtarea_base + 0x10000) {
				put_word_host(p, a & 0xffff);
				p += 2;
			}
		}
		put_word_host(p, 0);
		p += 2;
		for (int i = 0; i < absolute_rom_address; i++) {
			uae_u32 a = absolute_rom_addresses[i];
			if (a < rtarea_base || a >= rtarea_base + 0x10000) {
				put_long_host(p, a);
				p += 4;
			}
		}
		put_long_host(p, 0);
		write_log(_T("ROMBASE %08x RAMBASE %08x RELOC %08x (%d)\n"), rtarea_base, rombase, reloc, absolute_rom_address);
	} else {
		rtarea_write_enabled = false;
		protect_roms(true);
		write_log(_T("ROMBASE changed.\n"), absolute_rom_address);
		reloc = 1;
	}
	return reloc;
}

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

void org (uae_u32 a)
{
	if ( ((a & 0xffff0000) != 0x00f00000) && ((a & 0xffff0000) != rtarea_base) )
		write_log (_T("ORG: corrupt address! %08X"), a);
	rt_addr = a & 0xffff;
}

uae_u32 here (void)
{
	return addr (rt_addr);
}

void align (int b)
{
	rt_addr = (rt_addr + b - 1) & ~(b - 1);
}

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_size);
	trap_set_areg(ctx, 1, z3chipmem_bank.start);
	return chipmem_bank.allocated_size;
}

static uae_u32 REGPARAM2 uae_puts (TrapContext *ctx)
{
	uae_char buf[MAX_DPATH];
	trap_get_string(ctx, buf, trap_get_areg(ctx, 0), sizeof uae_char);
	TCHAR *s = au(buf);
	write_log(_T("%s"), s);
	xfree(s);
	return 0;
}

void rtarea_init_mem (void)
{
	if (need_uae_boot_rom(&currprefs)) {
		rtarea_bank.flags &= ~ABFLAG_ALLOCINDIRECT;
	} else {
		// not enabled and something else may use same address space
		rtarea_bank.flags |= ABFLAG_ALLOCINDIRECT;
	}
	rtarea_bank.reserved_size = RTAREA_SIZE;
	rtarea_bank.start = rtarea_base;
	if (!mapped_malloc (&rtarea_bank)) {
		write_log (_T("virtual memory exhausted (rtarea)!\n"));
		abort ();
	}
}

void rtarea_free(void)
{
	mapped_free(&rtarea_bank);
}

void rtarea_init(void)
{
	uae_u32 a;
	TCHAR uaever[100];

	rt_straddr = 0xFF00 - 2;
	rt_addr = 0;
	rt_addr_reset = true;

	rt_trampoline_ptr = rtarea_base + RTAREA_TRAMPOLINE;
	trap_entry = 0;
	absolute_rom_address = 0;
	rombase_new = 0;

	init_traps ();

	rtarea_init_mem ();
	memset (rtarea_bank.baseaddr, 0, RTAREA_SIZE);

	_stprintf (uaever, _T("uae-%d.%d.%d"), UAEMAJOR, UAEMINOR, UAESUBREV);

	EXPANSION_uaeversion = ds (uaever);
	EXPANSION_explibname = ds (_T("expansion.library"));
	EXPANSION_doslibname = ds (_T("dos.library"));
	EXPANSION_uaedevname = ds (_T("uae.device"));

	dw (0);
	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++) {
		uae_sem_init(&hardware_trap_event[i], 0, 0);
		uae_sem_init(&hardware_trap_event2[i], 0, 0);
	}

#endif

	deftrap(NULL); /* Generic emulator trap */

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

	org (rtarea_base + 0xFF80);
	calltrap (deftrapres (getchipmemsize, TRAPFLAG_DORET, _T("getchipmemsize")));
	dw(RTS);

	org (rtarea_base + 0xFF10);
	calltrap (deftrapres (uae_puts, TRAPFLAG_NO_RETVAL, _T("uae_puts")));
	dw (RTS);

	org (a);

	uae_boot_rom_size = here () - rtarea_base;
	if (uae_boot_rom_size >= RTAREA_TRAPS) {
		write_log (_T("RTAREA_TRAPS needs to be increased!"));
		abort ();
	}

#ifdef PICASSO96
	uaegfx_install_code (rtarea_base + RTAREA_RTG);
#endif

	org (RTAREA_TRAPS | rtarea_base);
	init_extended_traps ();

	if (currprefs.uaeboard >= 2) {
		device_add_rethink(rethink_traps);
	}
}

volatile uae_atomic uae_int_requested = 0;

void rtarea_setup (void)
{
	uaecptr base = need_uae_boot_rom (&currprefs);
	if (base) {
		write_log (_T("RTAREA located at %08X\n"), base);
		rtarea_base = base;
	}
}

uaecptr makedatatable (uaecptr resid, uaecptr resname, uae_u8 type, uae_s8 priority, uae_u16 ver, uae_u16 rev)
{
	uaecptr datatable = here ();

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

