emu48-mirror/MEM2.C
Gwenhael Le Moine c3ab4004ad
1996-02-01: Historic version 0.37
Signed-off-by: Gwenhael Le Moine <gwenhael.le.moine@gmail.com>
2024-03-19 22:11:59 +01:00

739 lines
No EOL
18 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (C) 1995 Sebastien Carlier
*/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <malloc.h>
#include <string.h>
#include "emu48.h"
/* Modules :
* 0 IO RAM
* 1 RAM
* 2 bank switcher
* 3 PORT 1
* 4 PORT 2
* 5 ROM
*/
/* ucfg value :
* 0 configured
* 1 unconfigured
* 2 got size only
*/
#define PORT1DEF "port1"
char PORT1[100] = PORT1DEF;
void setport1(const char *newstring)
{
if (newstring)
{
if (*newstring)
strcpy(PORT1, newstring);
else
strcpy(PORT1, PORT1DEF);
}
}
int readport1(const char *portname)
{
/* CARDSTATUS: P2W P1W P2C P1C */
long i, s;
unsigned char c;
FILE *in = fopen(PORT1, "rb");
if (in!=NULL)
{
fseek(in, 0, SEEK_END);
s = ftell(in) * 2;
fseek(in, 0, SEEK_SET);
#ifdef DOSX286
port1 = (char*)_halloc(262144L,1);
#else
port1 = (char*)malloc(262144L);
#endif
if (port1!=NULL) {
for (i=0; i<s;) {
c = (char)fgetc(in);
port1[i++] = (char)(c&0xf);
port1[i++] = (char)((c>>4)&0xf);
}
data[3] = port1;
CARDSTATUS |= 0xA;
if (s!=262144L) memset(port1, 0, 16);
}
else display_warning("Not enough memory for port1");
fclose(in);
return 0;
}
return -1;
}
int load()
{
FILE *in;
long i, s;
unsigned char c;
int swap;
if (load_state) {
in = fopen("saturn", "rb");
if (in != NULL) {
fread(A, 16, 1, in);
fread(B, 16, 1, in);
fread(C, 16, 1, in);
fread(D, 16, 1, in);
fread(R0, 16, 1, in);
fread(R1, 16, 1, in);
fread(R2, 16, 1, in);
fread(R3, 16, 1, in);
fread(R4, 16, 1, in);
if (!pc) fread(&pc, 4, 1, in); else fseek(in, 4, SEEK_CUR);
fread(&d0, 4, 1, in);
fread(&d1, 4, 1, in);
fread(&OUT, 4, 1, in);
fread(&IN, 4, 1, in);
fread(ST, 4, 1, in);
HST = (char)fgetc(in);
if (!P) P = (char)fgetc(in); else fseek(in, 1, SEEK_CUR);
CARRY = fgetc(in);
MODE = fgetc(in);
INTP = fgetc(in);
INTE = fgetc(in);
INTD = fgetc(in);
SHUTDN = fgetc(in);
fread(rstk, 32, 1, in);
rstkp = fgetc(in);
for (i=0; i<5; i++) {
ucfg[i] = fgetc(in);
fread(&base[i], 4, 1, in);
fread(&size[i], 4, 1, in);
}
fread(ioram, 64, 1, in);
t1 = fgetc(in);
fread(&t2, 4, 1, in);
fread(&crc, 4, 1, in);
fread(&display, sizeof(display_t), 1, in);
fclose(in);
}
}
#ifdef DOSX286
rom = (char*)_halloc(1048576L,1);
#else
rom = (char*)malloc(1048576L);
#endif
if (rom==NULL) display_error("Not enough memory for rom.\n");
in = fopen("rom", "rb");
if (in==NULL) {
free(rom);
display_error("The rom dump file 'rom' is missing.\n");
}
fseek(in,0,SEEK_END);
s = ftell(in)*2;
fseek(in,0,SEEK_SET);
c = (char)fgetc(in);
switch (c) {
case 0x32: swap = 0; break;
case 0x23: swap = 1; break;
default: swap = 0; break;
}
ungetc(c, in);
if (!swap)
for (i=0; i<s;) {
c = (char)fgetc(in);
rom[i++] = (char)(c&0xf);
rom[i++] = (char)((c>>4)&0xf);
}
else
for (i=0; i<s;) {
c = (char)fgetc(in);
rom[i++] = (char)((c>>4)&0xf);
rom[i++] = (char)(c&0xf);
}
fclose(in);
data[5] = rom;
#ifdef DOSX286
ram = (char*)_halloc(262144L,1);
#else
ram = (char*)malloc(262144L);
#endif
if (ram==NULL) display_error("Not enough memory for ram.\n");
in = fopen("ram", "rb");
if (in!=NULL) {
c = (char)fgetc(in);
switch (c) {
case 0x3F: swap = 0; break;
case 0xF3: swap = 1; break;
default: swap = 0; break;
}
ungetc(c, in);
if (!swap)
for (i=0; i<262144L;) {
c = (char)fgetc(in);
ram[i++] = (char)(c&0xf);
ram[i++] = (char)((c>>4)&0xf);
}
else
for (i=0; i<262144L;) {
c = (char)fgetc(in);
ram[i++] = (char)((c>>4)&0xf);
ram[i++] = (char)(c&0xf);
}
fclose(in);
}
data[1] = ram;
readport1(PORT1);
/* CARDSTATUS: P2W P1W P2C P1C */
in = fopen("port2", "rb");
if (in!=NULL) {
fseek(in, 0, SEEK_END);
s = ftell(in) * 2;
fseek(in, 0, SEEK_SET);
#ifdef DOSX286
port2 = (char*)_halloc(262144L,1);
#else
port2 = (char*)malloc(262144L);
#endif
if (port2!=NULL) {
for (i=0; i<s;) {
c = (char)fgetc(in);
port2[i++] = (char)(c&0xf);
port2[i++] = (char)((c>>4)&0xf);
}
data[4] = port2;
CARDSTATUS |= 0x5;
} else display_warning("Not enough memory for port2");
fclose(in);
}
return 0;
}
int save() {
FILE *out;
long i;
out = fopen("ram", "wb");
if (out==NULL) return 1;
for (i=0; i<262144L; i+=2) fputc(ram[i]|(ram[i+1]<<4), out);
fclose(out);
if (CARDSTATUS&2) bank_save(port1,PORT1,bank1);
if (CARDSTATUS&1) bank_save(port2,"port2",bank2);
out = fopen("saturn", "wb");
fwrite(A, 16, 1, out);
fwrite(B, 16, 1, out);
fwrite(C, 16, 1, out);
fwrite(D, 16, 1, out);
fwrite(R0, 16, 1, out);
fwrite(R1, 16, 1, out);
fwrite(R2, 16, 1, out);
fwrite(R3, 16, 1, out);
fwrite(R4, 16, 1, out);
fwrite(&pc, 4, 1, out);
fwrite(&d0, 4, 1, out);
fwrite(&d1, 4, 1, out);
fwrite(&OUT, 4, 1, out);
fwrite(&IN, 4, 1, out);
fwrite(ST, 4, 1, out);
fputc(HST, out);
fputc(P, out);
fputc(CARRY, out);
fputc(MODE, out);
fputc(INTP, out);
fputc(INTE, out);
fputc(INTD, out);
fputc(SHUTDN, out);
fwrite(rstk, 32, 1, out);
fputc(rstkp, out);
for (i=0; i<5; i++) {
fputc(ucfg[i], out);
fwrite(&base[i], 4, 1, out);
fwrite(&size[i], 4, 1, out);
}
fwrite(ioram, 64, 1, out);
fputc((int)t1, out);
fwrite(&t2, 4, 1, out);
fwrite(&crc, 4, 1, out);
fwrite(&display, sizeof(display_t), 1, out);
fclose(out);
return 0;
}
void bank_save(char *mem, char *bn, int bank) {
char name[12];
FILE *out;
long i;
if (bank)
sprintf(name,"%s.%i",bn,bank);
else
strcpy(name, bn);
out = fopen(name, "rb");
if (out==NULL) return;
fclose(out);
out = fopen(name, "wb");
if (out==NULL) {
printf("Failed to write %s\n", name);
return;
}
for (i=0; i<262144L; i+=2) fputc(mem[i]|(mem[i+1]<<4), out);
fclose(out);
return;
}
void bank_load(char *mem, char *bn, int bank) {
char name[12], c;
FILE *in;
long i, s;
if (bank)
sprintf(name,"%s.%i",bn,bank);
else
strcpy(name, bn);
in = fopen(name, "rb");
if (in==NULL) {
printf("Failed to read %s\n", name);
return;
}
fseek(in, 0, SEEK_END);
s = ftell(in) * 2;
fseek(in, 0, SEEK_SET);
for (i=0; i<s;) {
c = (char)fgetc(in);
mem[i++] = (char)(c&0xf);
mem[i++] = (char)((c>>4)&0xf);
}
fclose(in);
return;
}
void bank_switch(int bank) {
if (bank<32) /* port 1 */ {
if (bank1 == bank) return;
bank_save(port1,PORT1,bank1);
bank1 = bank;
bank_load(port1,PORT1,bank1);
} else {
bank -= 32;
if (bank2 == bank) return;
bank_save(port2,"port2",bank2);
bank2 = bank;
bank_load(port2,"port2",bank2);
}
return;
}
/******** IO RAM ********/
char ioram[64] = {
0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0, 0x0,0x0,0xC,0x0,
0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0xf, 0xf,0xf,0xf,0xf, 0xf,0xf,0xf,0xf
};
char read_io(long d) {
char c;
switch (d) {
case 0x04: c = (char)(crc); break;
case 0x05: c = (char)(crc>>4); break;
case 0x06: c = (char)(crc>>8); break;
case 0x07: c = (char)(crc>>12); break;
case 0x0F: c = CARDSTATUS; break;
case 0x14: return ioram[d];
case 0x15: ioram[0x11]&=0xe; return ioram[d];
case 0x16: /**/
case 0x17: /**/ return 3;
case 0x18: /**/
case 0x19: /**/ display_warning("Nibbles #118/#119 read.");
case 0x20: /**/
case 0x21: /**/
case 0x22: /**/
case 0x23: /**/
case 0x24: /**/
case 0x25: /**/
case 0x26: /**/
case 0x27: /**/ return 3;
case 0x28: c = (char)display.lcounter; break;
case 0x29: c = (char)(display.lcounter>>4); break;
case 0x30: /**/
case 0x31: /**/
case 0x32: /**/
case 0x33: /**/
case 0x34: /**/ c = 3; break;
case 0x37: c = (char)(t1); break;
case 0x38: c = (char)(t2); break;
case 0x39: c = (char)(t2>>4); break;
case 0x3A: c = (char)(t2>>8); break;
case 0x3B: c = (char)(t2>>12); break;
case 0x3C: c = (char)(t2>>16); break;
case 0x3D: c = (char)(t2>>20); break;
case 0x3E: c = (char)(t2>>24); break;
case 0x3F: c = (char)(t2>>28); break;
default:
return ioram[d];
}
return (char)(c&0xf);
}
void write_io(long d, char c) {
switch (d) {
/* 00100 = NS:DISPIO
* 00100 @ Display bit offset and DON [DON OFF2 OFF1 OFF0]
* 00100 @ 3 nibs for display offset (scrolling), DON=Display ON
*/
case 0x00:
if (display.boffset!=(c&7)) {
display.touched = 1;
display.boffset = c&7;
}
if (display.dispon!=(c>>3)) {
display.touched = 1;
display.dispon = c>>3;
}
break;
/* 00101 = NS:CONTRLSB
* 00101 @ Contrast Control [CON3 CON2 CON1 CON0]
* 00101 @ Higher value = darker screen
*/
case 0x01:
if ((display.contrast&0xf)!=c) {
display.contrast &= 0x10;
display.contrast |= c;
display.touched = 1;
}
break;
/* 00102 = NS:DISPTEST
* 00102 @ Display test [VDIG LID TRIM CON4] [LRT LRTD LRTC BIN]
* 00102 @ Normally zeros
*/
case 0x02:
if ((display.contrast>>4)!=(c&1)) {
display.contrast &= 0x0f;
display.contrast |= (c<<4)&0x10;
display.touched = 1;
}
break;
case 0x03:
/*display.noscan = (c>>3)&1;*/
/*display.touched = 1;*/
break;
/* 00104 = HP:CRC
* 00104 @ 16 bit hardware CRC (104-107) (X^16+X^12+X^5+1)
* 00104 @ crc = ( crc >> 4 ) ^ ( ( ( crc ^ nib ) & 0x000F ) * 0x1081 );
*/
case 0x04: crc=crc&0xfff0;crc|=(c); return;
case 0x05: crc=crc&0xff0f;crc|=(c<<4); return;
case 0x06: crc=crc&0xf0ff;crc|=(c<<8); return;
case 0x07: crc=crc&0x0fff;crc|=(c<<12); return;
/* 00108 = NS:POWERSTATUS
* 00108 @ Low power registers (108-109)
* 00108 @ [LB2 LB1 LB0 VLBI] (read only)
* 00108 @ LowBat(2) LowBat(1) LowBat(S) VeryLowBat
*/
case 0x08:
return;
/* 00109 = NS:POWERCTRL
* 00109 @ [ELBI EVLBI GRST RST] (read/write)
*/
case 0x09:
break;
/* 0010A = NS:MODE
* 0010A @ Mode Register (read-only)
*/
case 0x0A:
break;
/* 0010B = HP:ANNCTRL
* 0010B @ Annunciator control [LA4 LA3 LA2 LA1] = [ alarm alpha -> <- ]
*/
case 0x0B:
case 0x0C:
ioram[d] = c; display_ann(); return;
/* 0010D = NS:BAU
* 0010D @ Serial baud rate [UCK BD2 BD1 BD0] (bit 3 is read-only)
* 0010D @ 3 bits = {1200 1920 2400 3840 4800 7680 9600 15360}
*/
case 0x0D:
c &= 7;
break;
/* 0010E = NS:CARDCTRL
* 0010E @ [ECDT RCDT SMP SWINT] (read/write)
* 0010E @ Enable Card Det., Run Card Det., Set Module Pulled, Software interrupt
*/
case 0x0E:
if (c&1) INTERRUPT("SW");
if (c&2) { ioram[0x19] = 2; HST|=MP; INTERRUPT("MP"); }
if (c&8)
if (c&4) ioram[0x0F] = 0;
break;
/* 0010F = NS:CARDSTATUS
* 0010F @ [P2W P1W P2C P1C] (read-only) Port 2 writable .. Port 1 inserted
*/
case 0x0F:
return;
/* 00110 = HP:IOC
* 00110 @ Serial I/O Control [SON ETBE ERBF ERBZ]
* 00110 @ Serial On, Interrupt On Recv.Buf.Empty, Full, Buzy
*/
case 0x10:
break;
/* 00111 = HP:RCS
* 00111 @ Serial Receive Control/Status [RX RER RBZ RBF] (bit 3 is read-only)
*/
case 0x11:
c &= 7;
c |= ioram[0x11]&8;
break;
/* 00112 = HP:TCS
* 00112 @ Serial Transmit Control/Status [BRK LPB TBZ TBF]
*/
case 0x12:
break;
/* 00113 = HP:CRER
* 00113 @ Serial Clear RER (writing anything clears RER bit)
*/
case 0x13:
ioram[0x11]&=0xB;
return;
/* 00114 = HP:RBR
* 00114 @ Serial Receive Buffer Register (Reading clears RBF bit)
* 00114 @ [RX RER RBZ RBF]
*/
case 0x14:
case 0x15:
return;
/* 00116 = HP:TBR
* 00116 @ Serial Transmit Buffer Register (Writing sets TBF bit)
*/
case 0x016: break;
case 0x017:
ioram[0x12] |= 1;
break;
/* 00118 = NS:SRR
* 00118 @ Service Request Register (read-only)
* 00118 @ [ISQR TSQR USRQ VSRQ] [KDN NINT2 NINT LSRQ]
*/
case 0x18:
case 0x19:
return;
/* 0011A = HP:IRC
* 0011A @ IR Control Register [IRI EIRU EIRI IRE] (bit 3 is read-only)
* 0011A @ IR Input, Enable IR UART mode, Enable IR Interrupt, IR Event
*/
case 0x1A:
c &= 7;
c |= ioram[0x1A]&8;
break;
/* 0011B = NS:BASENIBOFF
* 0011B @ Used as addressto get BASENIB from 11F to the 5th nibble
*/
case 0x1B:
return;
/* 0011C = NS:LCR
* 0011C @ Led Control Register [LED ELBE LBZ LBF] (Setting LED is draining)
*/
case 0x1C:
break;
/* 0011D = NS:LBR
* 0011D @ Led Buffer Register [0 0 0 LBO] (bits 1-3 read zero)
*/
case 0x1D:
c &= 1;
break;
/* 0011E = NS:SCRATCHPAD
* 0011E @ Scratch pad (11F is BASEIB, 7 or F for base memory)
*/
case 0x1E:
break;
/* 0011F = NS:BASENIB
*/
case 0x1F:
break;
/* 00120 = NS:DISPADDR
* 00120 @ Display Start Address (write only)
* 00120 @ bit 0 is ignored (display must start on byte boundary)
*/
case 0x20:
if ((display.start1&0x0000F)!=c) {
display.start1 = (display.start1&0xFFFF0)|(c&0xE);
display.touched = 1;
}
break;
case 0x21:
if (((display.start1&0x000F0)>>4)!=c) {
display.start1=(display.start1&0xFFF0F)|(c<<4);
display.touched = 1;
}
break;
case 0x22:
if (((display.start1&0x00F00)>>8)!=c) {
display.start1=(display.start1&0xFF0FF)|(c<<8);
display.touched = 1;
}
break;
case 0x23:
if (((display.start1&0x0F000)>>12)!=c) {
display.start1=(display.start1&0xF0FFF)|(c<<12);
display.touched = 1;
}
break;
case 0x24:
if (((display.start1&0xF0000)>>16)!=c) {
display.start1=(display.start1&0x0FFFF)|(c<<16);
display.touched = 1;
}
break;
/* 00125 = NS:LINEOFFS
* 00125 @ Display Line offset (write only) (no of bytes skipped after each line)
* 00125 @ MSG sign extended
*/
case 0x25: c &= 0xe;
case 0x26:
case 0x27:
ioram[d] = c;
display.loffset = (int)Npack(ioram+0x25, 3);
if (display.loffset&0x800) display.loffset = display.loffset - 0x1000;
display.touched = 1;
return;
/* 00128 = NS:LINECOUNT
* 00128 @ Display Line Counter and miscellaneous (28-29)
* 00128 @ [LC3 LC2 LC1 LC0] [DA19 M32 LC5 LC4]
* 00128 @ Line counter 6 bits -> max = 2^6-1 = 63 = disp height
* 00128 @ Normally has 55 -> Menu starts at display row 56
*/
case 0x28:
if ((display.lcntsave&0xf)!=c) {
display.lcntsave=(display.lcntsave&0x30)|c;
display.touched = 1;
}
break;
case 0x29:
if ((display.lcntsave&0x30)!=((c&3)<<4)) {
display.lcntsave=(display.lcntsave&0x0f)|((c&3)<<4);
display.touched = 1;
}
break;
case 0x2A:
case 0x2B:
case 0x2C:
case 0x2D:
return;
/* 0012E = NS:TIMER1CTRL
* 0012E @ TIMER1 Control [SRQ WKE INT XTRA]
*/
case 0x2E:
c &= 0xE;
break;
/* 0012F = NS:TIMER2CTRL
* 0012F @ TIMER2 Control [SRQ WKE INT RUN]
*/
case 0x2F:
break;
/* 00130 = NS:MENUADDR
* 00130 @ Display Secondary Start Address (write only) (30-34)
* 00130 @ Menu Display Address, no line offsets
*/
case 0x30:
if ((display.start2&0x0000F)!=c) {
display.start2=(display.start2&0xFFFF0)|(c&0xE);
display.touched = 1;
}
break;
case 0x31:
if (((display.start2&0x000F0)>>4)!=c) {
display.start2=(display.start2&0xFFF0F)|(c<<4);
display.touched = 1;
}
break;
case 0x32:
if (((display.start2&0x00F00)>>8)!=c) {
display.start2=(display.start2&0xFF0FF)|(c<<8);
display.touched = 1;
}
break;
case 0x33:
if (((display.start2&0x0F000)>>12)!=c) {
display.start2=(display.start2&0xF0FFF)|(c<<12);
display.touched = 1;
}
break;
case 0x34:
if (((display.start2&0xF0000)>>16)!=c) {
display.start2=(display.start2&0x0FFFF)|(c<<16);
display.touched = 1;
}
break;
case 0x35:
case 0x36:
return;
/* 00137 = HP:TIMER1
* 00137 @ Decremented 16 times/s
*/
case 0x37:
t1 = c;
break;
/* 00138 = HP:TIMER2
* 00138 @ hardware timer (38-3F), decremented 8192 times/s
*/
case 0x38: t2=(t2&0xFFFFFFF0)|c; break;
case 0x39: t2=(t2&0xFFFFFF0F)|(c<<4); break;
case 0x3A: t2=(t2&0xFFFFF0FF)|(c<<8); break;
case 0x3B: t2=(t2&0xFFFF0FFF)|(c<<12); break;
case 0x3C: t2=(t2&0xFFF0FFFF)|(c<<16); break;
case 0x3D: t2=(t2&0xFF0FFFFF)|(c<<20); break;
case 0x3E: t2=(t2&0xF0FFFFFF)|(c<<24); break;
case 0x3F: t2=(t2&0x0FFFFFFF)|(c<<28); break;
default: return;
}
ioram[d] = c;
return;
}