/***************************************************************
 *  Alcuin: A HP48 emulator for Amiga Computers
 * 
 *  (C) 1995 Stephan Schupfer (schupfer@sbox.tu-graz.ac.at)
 *
 */

#include <stdio.h>
#include <exec/memory.h>
#include <dos/dos.h>

#include "hp48.h"
#include "device.h"
#include "main.h"
#include "romio.h"
#include "version.h"

#define X48_MAGIC 0x48503438
#define NR_CONFIG 8

extern struct Window *win;

long  ram_size;
long  port1_size;
long  port1_mask;
short port1_is_ram;
long  port2_size;
long  port2_mask;
short port2_is_ram;
long  buf_bank1,buf_bank2;

/******************************************************************/
void saturn_config_init(void)
{
saturn_version = VERSION;
/*memset(&device, 0, sizeof(device));*/
device.display_touched = 1;
device.contrast_touched = 1;
device.baud_touched = 1;
device.ann_touched = 1;
saturn_rcs = 0x0;
saturn_tcs = 0x0;
saturn_lbr = 0x0;
}

static int mem[NR_MCTL] = {1,1,1,1,1,0};
static long base[NR_MCTL] = {0x00100,0x80000,0x7f000,0x7e000,0x7e000,0x0000};
static long size[NR_MCTL] = {0x00040,0x40000,0x01000,0x01000,0x01000,0x10000};

/******************************************************************/
void init_saturn(void)
{
int i;

/*memset(&saturn, 0, sizeof(saturn) - 4 * sizeof(unsigned char *));*/
saturn_PC = 0x00000;
saturn_magic = X48_MAGIC;
saturn_t1_tick = 8192;
saturn_t2_tick = 16;
/*saturn_i_per_s = 0;*/
saturn_version = VERSION;
saturn_hexmode = HEX;
saturn_rstkp = -1;
saturn_intenable = 1;
saturn_int_pending = 0;
saturn_kbd_ien = 1;
saturn_timer1 = 0;
saturn_timer2 = 0x2000;
saturn_bank_switch = 0;
for (i = 0; i < NR_MCTL; i++) {
	saturn_mem[i] = mem[i];
	saturn_base[i] = base[i];
	saturn_size[i] = size[i];
	}
}

/******************************************************************/
int read_char(APTR *filepointer, char *var)
{
unsigned char tmp;

if (Read(filepointer,&tmp, 1) != 1) return 0;
*var = tmp;
return 1;
}

/******************************************************************/
int read_8(APTR *filepointer, word_8 *var)
{
unsigned char tmp;

if (Read(filepointer,&tmp, 1) != 1) return 0;
*var = tmp;
return 1;
}

/******************************************************************/
int read_16(APTR *filepointer, word_16 *var)
{
unsigned char tmp[2];

if (Read(filepointer,tmp, 2) != 2) return 0;
*var = tmp[0] << 8;
*var |= tmp[1];
return 1;
}

/******************************************************************/
int read_32(APTR *filepointer, word_32 *var)
{
unsigned char tmp[4];

if (Read(filepointer,tmp, 4) != 4) return 0;
*var = tmp[0] << 24;
*var |= tmp[1] << 16;
*var |= tmp[2] << 8;
*var |= tmp[3];
return 1;
}

/******************************************************************/
int read_state_file(APTR *filepointer)
{
int i;

/* win->LeftEdge win->TopEdge (WORD)*/
for (i=0;i<16;i++) if (!read_8(filepointer, &saturn_A[i])) return 0;
for (i=0;i<16;i++) if (!read_8(filepointer, &saturn_B[i])) return 0;
for (i=0;i<16;i++) if (!read_8(filepointer, &saturn_C[i])) return 0;
for (i=0;i<16;i++) if (!read_8(filepointer, &saturn_D[i])) return 0;
if (!read_32(filepointer, &saturn_D0)) return 0;
if (!read_32(filepointer, &saturn_D1)) return 0;
if (!read_8(filepointer, &saturn_P)) return 0;
if (!read_32(filepointer, &saturn_PC)) return 0;
for (i=0;i<16;i++) if (!read_8(filepointer, &saturn_R0[i])) return 0;
for (i=0;i<16;i++) if (!read_8(filepointer, &saturn_R1[i])) return 0;
for (i=0;i<16;i++) if (!read_8(filepointer, &saturn_R2[i])) return 0;
for (i=0;i<16;i++) if (!read_8(filepointer, &saturn_R3[i])) return 0;
for (i=0;i<16;i++) if (!read_8(filepointer, &saturn_R4[i])) return 0;
for (i=0;i<4;i++) if (!read_8(filepointer, &saturn_IN[i])) return 0;
for (i=0;i<3;i++) if (!read_8(filepointer, &saturn_OUT[i])) return 0;
if (!read_8(filepointer, &saturn_CARRY)) return 0;
if (!read_16(filepointer, &saturn_ST)) return 0;
if (!read_8(filepointer, &saturn_HST)) return 0;
if (!read_8(filepointer, &saturn_hexmode)) return 0;
for (i = 0; i < NR_RSTK; i++)
	if (!read_32(filepointer, &saturn_rstk[i])) return 0;
if (!read_16(filepointer, (word_16 *)&saturn_rstkp)) return 0;
for (i = 0; i < 9; i++)
	if (!read_16(filepointer, (word_16 *)&saturn_keybuf[i])) return 0;
if (!read_8(filepointer, &saturn_intenable)) return 0;
if (!read_8(filepointer, &saturn_int_pending)) return 0;
if (!read_8(filepointer, &saturn_kbd_ien)) return 0;
if (!read_8(filepointer, &saturn_disp_io)) return 0;
if (!read_8(filepointer, &saturn_contrast_ctrl)) return 0;
if (!read_8(filepointer, &saturn_disp_test)) return 0;
if (!read_16(filepointer, &saturn_crc)) return 0;
if (!read_8(filepointer, &saturn_power_status)) return 0;
if (!read_8(filepointer, &saturn_power_ctrl)) return 0;
if (!read_8(filepointer, &saturn_mode)) return 0;
if (!read_8(filepointer, &saturn_annunc)) return 0;
if (!read_8(filepointer, &saturn_baud)) return 0;
if (!read_8(filepointer, &saturn_card_ctrl)) return 0;
if (!read_8(filepointer, &saturn_card_status)) return 0;
if (!read_8(filepointer, &saturn_io_ctrl)) return 0;
if (!read_8(filepointer, &saturn_rcs)) return 0;
if (!read_8(filepointer, &saturn_tcs)) return 0;
if (!read_8(filepointer, &saturn_rbr)) return 0;
if (!read_8(filepointer, &saturn_tbr)) return 0;
if (!read_8(filepointer, &saturn_sreq)) return 0;
if (!read_8(filepointer, &saturn_ir_ctrl)) return 0;
if (!read_8(filepointer, &saturn_base_off)) return 0;
if (!read_8(filepointer, &saturn_lcr)) return 0;
if (!read_8(filepointer, &saturn_lbr)) return 0;
if (!read_8(filepointer, &saturn_scratch)) return 0;
if (!read_8(filepointer, &saturn_base_nibble)) return 0;
if (!read_32(filepointer, &saturn_disp_addr)) return 0;
if (!read_16(filepointer, &saturn_line_offset)) return 0;
if (!read_8(filepointer, &saturn_line_count)) return 0;
if (!read_16(filepointer, &saturn_unknown)) return 0;
if (!read_8(filepointer, &saturn_t1_ctrl)) return 0;
if (!read_8(filepointer, &saturn_t2_ctrl)) return 0;
if (!read_32(filepointer, &saturn_menu_addr)) return 0;
if (!read_8(filepointer, &saturn_unknown2)) return 0;
if (!read_char(filepointer, &saturn_timer1)) return 0;
if (!read_32(filepointer, &saturn_timer2)) return 0;
if (!read_32(filepointer, &saturn_t1_instr)) return 0;
if (!read_32(filepointer, &saturn_t2_instr)) return 0;
if (!read_16(filepointer, (word_16 *)&saturn_t1_tick)) return 0;
if (!read_16(filepointer, (word_16 *)&saturn_t2_tick)) return 0;
if (!read_32(filepointer, &saturn_i_per_s)) return 0;
if (!read_16(filepointer, &saturn_bank_switch)) return 0;
for (i = 0; i < NR_MCTL; i++) {
	if (!read_16(filepointer,(word_16 *)&(saturn_mem[i]))) return 0;
	if (!read_32(filepointer,&(saturn_base[i]))) return 0;
	if (!read_32(filepointer,&(saturn_size[i]))) return 0;
	}
if (!read_32(filepointer,&buf_bank1)) return 0;
if (!read_32(filepointer,&buf_bank2)) return 0;
return 1;
}

/******************************************************************/
int read_mem_file(APTR *filepointer, word_4 *mem, int size)
{
struct FileInfoBlock FIB;
word_8 *tmp_mem;
word_8 byte;
int i, j;

if (ExamineFH(filepointer, &FIB) == 0) {
	return 0;
	}

if (FIB.fib_Size != size / 2) {
    return 0;
	}

if (!(tmp_mem = (word_8 *)AllocMem(FIB.fib_Size,MEMF_CLEAR))) {
	for (i = 0, j = 0; i < size / 2; i++) {
		if (Read(filepointer,&byte, 1) != 1) {
			return 0;
			}
		mem[j++] = (word_4)((int)byte & 0xf);
		mem[j++] = (word_4)(((int)byte >> 4) & 0xf);
		}
	}
else
	if (Read(filepointer,tmp_mem, size / 2) != size / 2) {
		FreeMem(tmp_mem,FIB.fib_Size);
		return 0;
		}
for (i = 0, j = 0; i < size / 2; i++) {
	mem[j++] = (word_4)((int)tmp_mem[i] & 0xf);
	mem[j++] = (word_4)(((int)tmp_mem[i] >> 4) & 0xf);
	}
    
FreeMem(tmp_mem,FIB.fib_Size);
return 1;
}

/******************************************************************/
int read_rom()
{
char fnam[1024];

strcpy(fnam, homeDirectory);
strcat(fnam,romFileName);
if (!read_rom_file(fnam, &saturn_rom, &rom_size)) return 0;
dev_memory_init();
if (opt_gx) ram_size = RAM_SIZE_GX;
else ram_size = RAM_SIZE_SX;
if (!(saturn_ram = (word_4 *)AllocMem(ram_size,MEMF_CLEAR)))
	stirb("Can't alloc mem for %s.",102,"Alcuin RAM");
port1_size = 0;
port1_mask = 0;
port1_is_ram = 0;
saturn_port1 = (unsigned char *)0;
saturn_bank1 = (unsigned char *)0;
port2_size = 0;
port2_mask = 0;
port2_is_ram = 0;
saturn_port2 = (unsigned char *)0;
saturn_bank2 = (unsigned char *)0;
saturn_card_status = 0;
return 1;
}

/******************************************************************/
int read_files(void)
{
char           path[1024];
char           fnam[1024];
unsigned long  v1, v2;
int            i, read_version;
struct FileInfoBlock FIB;
APTR          *filepointer;


strcpy(path, homeDirectory);
saturn_rom = (word_4 *)NULL;
strcpy(fnam, path);
strcat(fnam, "Alcuin_ROM");
if (!read_rom_file(fnam, &saturn_rom, &rom_size)) return 0;

strcpy(fnam, path);
strcat(fnam, "Alcuin_State");
if (NULL == (filepointer = (APTR *)Open(fnam, MODE_OLDFILE))) {
	stirb ("Can't open %s.",100,fnam);
	}
read_32(filepointer,(word_32 *) &saturn_magic);
read_version = 1;
if (!read_8(filepointer, &saturn_version)) read_version = 0;
if (!read_state_file(filepointer)) init_saturn();
Close(filepointer);
dev_memory_init();
init_time();
saturn_config_init();
if (opt_gx)
	ram_size = RAM_SIZE_GX;
else
	ram_size = RAM_SIZE_SX;
saturn_ram = (word_4 *)NULL;
if (!(saturn_ram = (word_4 *)AllocMem(ram_size,MEMF_CLEAR)))
	stirb("Can't alloc mem for %s.",102,"Alcuin RAM");

strcpy(fnam, path);
strcat(fnam, "Alcuin_RAM");
if ((filepointer = (APTR *)Open(fnam, MODE_OLDFILE)) == NULL) 
	stirb("File %s is corrupt or missing.",107,fnam);
if (!read_mem_file(filepointer, saturn_ram, ram_size)) {
	Close(filepointer);
	stirb("File %s is corrupt or missing.",107,fnam);
	}
Close(filepointer);

saturn_card_status = 0;
port1_size = 0;
port1_mask = 0;
port1_is_ram = 0;
saturn_port1 = (unsigned char *)0;
saturn_bank1 = (unsigned char *)0;
if (get_port1) {
	strcpy(fnam, path);
	strcat(fnam, "Alcuin_Port1");
	if ((filepointer = (APTR *)Open(fnam, MODE_OLDFILE)) == NULL) {
		sage("File %s is corrupt or missing.",107,fnam);
		return 0;
		}
	if (ExamineFH(filepointer, &FIB)) {
		port1_size = 2 * FIB.fib_Size;
		if ((port1_size == 0x10000) || (port1_size == 0x40000)) {
			if (NULL == (saturn_port1 = (word_4 *)AllocMem(port1_size,MEMF_CLEAR)))
				sage("Can't alloc mem for %s.",102,"Alcuin PORT1");
			else {
				saturn_bank1 = saturn_port1;
				if (!read_mem_file(filepointer, saturn_port1, port1_size)) {
					sage("File %s is corrupt or missing.",107,fnam);
					port1_size = 0;
					port1_is_ram = 0;
					}
				else {
					port1_is_ram = (short)get_port1-1;
					port1_mask = port1_size - 1;
					}
				}
			}
		}
	Close(filepointer);
	if (opt_gx) {
		saturn_card_status |= (port1_size > 0) ? 2 : 0;
		saturn_card_status |= port1_is_ram ? 8 : 0;
		}
	else {
		saturn_card_status |= (port1_size > 0) ? 1 : 0;
		saturn_card_status |= port1_is_ram ? 4 : 0;
		}
	}
port2_size = 0;
port2_mask = 0;
port2_is_ram = 0;
saturn_port2 = (unsigned char *)0;
saturn_bank2 = (unsigned char *)0;
if (get_port2){
	strcpy(fnam, path);
	strcat(fnam, "Alcuin_Port2");
	if ((filepointer = (APTR *)Open(fnam, MODE_OLDFILE)) == NULL) {
		sage("File %s is corrupt or missing.",107,fnam);
		return 0;
		}
	if (ExamineFH(filepointer, &FIB)) {
		port2_size = 2 * FIB.fib_Size;
		if ((opt_gx && ((port2_size % 0x40000) == 0)) || (!opt_gx && ((port2_size == 0x10000) || (port2_size == 0x40000)))) {
			if (NULL == (saturn_port2 = (word_4 *)AllocMem(port2_size,MEMF_CLEAR)))
				sage("Can't alloc mem for %s.",102,"Alcuin PORT2");
			else {
				saturn_bank2 = saturn_port2;
				if (!read_mem_file(filepointer, saturn_port2, port2_size)) {
					sage("File %s is corrupt or missing.",107,fnam);
					port2_size = 0;
					port2_is_ram = 0;
					}
				else {
					port2_is_ram = (short)get_port2-1;
					port2_mask = port2_size - 1;
					}
				}
			}
		}
	Close(filepointer);
	if (opt_gx) {
		saturn_card_status |= (port1_size > 0) ? 1 : 0;
		saturn_card_status |= port1_is_ram ? 4 : 0;
		}
	else {
		saturn_card_status |= (port1_size > 0) ? 2 : 0;
		saturn_card_status |= port1_is_ram ? 8 : 0;
		}
	}
saturn_bank1=buf_bank1+saturn_port1;
saturn_bank2=buf_bank2+saturn_port2;
return 1;
}

/******************************************************************/
int write_char(APTR *filepointer, char *var)
{
unsigned char tmp;

tmp = *var;
if (Write(filepointer, &tmp, 1) != 1) return 0;
return 1;
}

/******************************************************************/
int write_8(APTR *filepointer, word_8 *var)
{
unsigned char tmp;

tmp = *var;
if (Write(filepointer, &tmp, 1) != 1) return 0;
return 1;
}

/******************************************************************/
int write_16(APTR *filepointer, word_16 *var)
{
unsigned char tmp[2];

tmp[0] = (*var >> 8) & 0xff;
tmp[1] = *var & 0xff;
if (Write(filepointer,tmp, 2) != 2) return 0;
return 1;
}

/******************************************************************/
int write_32(APTR *filepointer, word_32 *var)
{
unsigned char tmp[4];

tmp[0] = (*var >> 24) & 0xff;
tmp[1] = (*var >> 16) & 0xff;
tmp[2] = (*var >> 8) & 0xff;
tmp[3] = *var & 0xff;
if (Write(filepointer,tmp, 4) != 4) return 0;
return 1;
}

/******************************************************************/
int write_mem_file(char *name, word_4 *mem, int size)
{
APTR *filepointer;
word_8 *tmp_mem;
word_8 byte;
int i, j;

if (!(filepointer = (APTR *)Open(name, MODE_READWRITE))) {
	sage("Can't open %s.",100,name);
	return 0;
	}
if (!(tmp_mem = (word_8 *)AllocMem((size_t)size / 2,MEMF_CLEAR))) {
	for (i = 0, j = 0; i < size / 2; i++) {
		byte = (mem[j++] & 0x0f);
		byte |= (mem[j++] << 4) & 0xf0;
		if (Write(filepointer,&byte, 1) != 1) {
			Close(filepointer);
			sage("Error writing file %s.",108,name);
			return 0;
			}
		}
	}
else {
	for (i = 0, j = 0; i < size / 2; i++) {
		tmp_mem[i] = (mem[j++] & 0x0f);
		tmp_mem[i] |= (mem[j++] << 4) & 0xf0;
		}

	if (Write(filepointer,tmp_mem, (size_t)size / 2) != size / 2) {
		Close(filepointer);
		FreeMem(tmp_mem,(size_t)size / 2);
		sage("Error writing file %s.",108,name);
		}
	FreeMem(tmp_mem,(size_t)size / 2);
	}
Close(filepointer);
return 1;
}


/******************************************************************/
int write_files(void)
{
char fnam[1024];
int i, make_dir;
APTR *filepointer;

strcpy(fnam, homeDirectory);
strcat(fnam, "Alcuin_State");
if (!(filepointer = (APTR *)Open(fnam, MODE_READWRITE))) {
	sage ("Can't open %s.",100,fnam);
	return 0;
	}
write_32(filepointer, (word_32 *)&saturn_magic);
write_8(filepointer, &saturn_version);
for (i = 0; i < 16; i++) write_8(filepointer, &saturn_A[i]);
for (i = 0; i < 16; i++) write_8(filepointer, &saturn_B[i]);
for (i = 0; i < 16; i++) write_8(filepointer, &saturn_C[i]);
for (i = 0; i < 16; i++) write_8(filepointer, &saturn_D[i]);
write_32(filepointer, &saturn_D0);
write_32(filepointer, &saturn_D1);
write_8(filepointer, &saturn_P);
write_32(filepointer, &saturn_PC);
for (i = 0; i < 16; i++) write_8(filepointer, &saturn_R0[i]);
for (i = 0; i < 16; i++) write_8(filepointer, &saturn_R1[i]);
for (i = 0; i < 16; i++) write_8(filepointer, &saturn_R2[i]);
for (i = 0; i < 16; i++) write_8(filepointer, &saturn_R3[i]);
for (i = 0; i < 16; i++) write_8(filepointer, &saturn_R4[i]);
for (i = 0; i < 4; i++) write_8(filepointer, &saturn_IN[i]);
for (i = 0; i < 3; i++) write_8(filepointer, &saturn_OUT[i]);
write_8(filepointer, &saturn_CARRY);
write_16(filepointer, &saturn_ST);
write_8(filepointer, &saturn_HST);
write_8(filepointer, &saturn_hexmode);
for (i = 0; i < NR_RSTK; i++) write_32(filepointer, &saturn_rstk[i]);
write_16(filepointer, (word_16 *)&saturn_rstkp);
for (i = 0; i < 9; i++) write_16(filepointer, (word_16 *)&saturn_keybuf[i]);
write_8(filepointer, &saturn_intenable);
write_8(filepointer, &saturn_int_pending);
write_8(filepointer, &saturn_kbd_ien);
write_8(filepointer, &saturn_disp_io);
write_8(filepointer, &saturn_contrast_ctrl);
write_8(filepointer, &saturn_disp_test);
write_16(filepointer, &saturn_crc);
write_8(filepointer, &saturn_power_status);
write_8(filepointer, &saturn_power_ctrl);
write_8(filepointer, &saturn_mode);
write_8(filepointer, &saturn_annunc);
write_8(filepointer, &saturn_baud);
write_8(filepointer, &saturn_card_ctrl);
write_8(filepointer, &saturn_card_status);
write_8(filepointer, &saturn_io_ctrl);
write_8(filepointer, &saturn_rcs);
write_8(filepointer, &saturn_tcs);
write_8(filepointer, &saturn_rbr);
write_8(filepointer, &saturn_tbr);
write_8(filepointer, &saturn_sreq);
write_8(filepointer, &saturn_ir_ctrl);
write_8(filepointer, &saturn_base_off);
write_8(filepointer, &saturn_lcr);
write_8(filepointer, &saturn_lbr);
write_8(filepointer, &saturn_scratch);
write_8(filepointer, &saturn_base_nibble);
write_32(filepointer, &saturn_disp_addr);
write_16(filepointer, &saturn_line_offset);
write_8(filepointer, &saturn_line_count);
write_16(filepointer, &saturn_unknown);
write_8(filepointer, &saturn_t1_ctrl);
write_8(filepointer, &saturn_t2_ctrl);
write_32(filepointer, &saturn_menu_addr);
write_8(filepointer, &saturn_unknown2);
write_char(filepointer, &saturn_timer1);
write_32(filepointer, &saturn_timer2);
write_32(filepointer, &saturn_t1_instr);
write_32(filepointer, &saturn_t2_instr);
write_16(filepointer, (word_16 *)&saturn_t1_tick);
write_16(filepointer, (word_16 *)&saturn_t2_tick);
write_32(filepointer, &saturn_i_per_s);
write_16(filepointer, &saturn_bank_switch);
for (i = 0; i < NR_MCTL; i++) {
	write_16(filepointer,(word_16 *)&(saturn_mem[i]));
	write_32(filepointer,&(saturn_base[i]));
	write_32(filepointer,&(saturn_size[i]));
	}
buf_bank1=saturn_bank1-saturn_port1;
buf_bank2=saturn_bank2-saturn_port2;
write_32(filepointer,&buf_bank1);
write_32(filepointer,&buf_bank2);
Close(filepointer);

if (opt_gx)
  ram_size = RAM_SIZE_GX;
else
  ram_size = RAM_SIZE_SX;

strcpy(fnam, homeDirectory);
strcat(fnam, "Alcuin_RAM");
if (!write_mem_file(fnam, saturn_ram, ram_size)) return 0;

if ((port1_size > 0) && port1_is_ram) {
	strcpy(fnam, homeDirectory);
	strcat(fnam, "Alcuin_Port1");
	if (!write_mem_file(fnam, saturn_port1, port1_size)) return 0;
	}

if ((port2_size > 0) && port2_is_ram) {
	strcpy(fnam, homeDirectory);
	strcat(fnam, "Alcuin_Port2");
	if (!write_mem_file(fnam, saturn_port2, port2_size)) return 0;
	}
return 1;
}

/******************************************************************/
int init_emulator(void)
{
if (initialize && key) {
	if (read_files())
    	{
        if (resetOnStartup) saturn_PC = 0x00000;
		return 0;
		}
	}
else {
	if (!read_rom()) stirb("File %s is corrupt or missing.",107,"Alcuin_ROM");
	}
init_time();
init_saturn();
return 0;
}

/******************************************************************/
int exit_emulator(void)
{
if (key) 
	if (!write_files()) return 1;
}

/******************************************************************/
