summaryrefslogtreecommitdiff
path: root/source/spec.c
diff options
context:
space:
mode:
authorIan C <ianc@noddybox.co.uk>2021-07-05 19:29:54 +0000
committerIan C <ianc@noddybox.co.uk>2021-07-05 19:29:54 +0000
commit0bc192bffbf28ba5733ae955916e5ebc72f3641f (patch)
treecb68ae6e3785b2f712e3c639f687580fe75f5806 /source/spec.c
parent25a57883291bc89210edbd3670e5548a0b838913 (diff)
Initial working version. Still todo : Tapes, Sound
Diffstat (limited to 'source/spec.c')
-rw-r--r--source/spec.c631
1 files changed, 225 insertions, 406 deletions
diff --git a/source/spec.c b/source/spec.c
index e5932da..5fa5060 100644
--- a/source/spec.c
+++ b/source/spec.c
@@ -30,6 +30,7 @@
#include "spec.h"
#include "gui.h"
#include "framebuffer.h"
+#include "snap.h"
#include "stream.h"
@@ -45,63 +46,92 @@
#define FALSE 0
#endif
+#define HIBYTE(w) ((w)>>8)
+#define LOBYTE(w) ((w)&0xff)
+
/* ---------------------------------------- STATICS
*/
#define ROMLEN 0x4000
-#define ROM_SAVE 0x2fc
-#define ROM_LOAD 0x347
+#define ROM_SAVE 0x4c6
+#define ROM_LOAD 0x562
#define ED_SAVE 0xf0
#define ED_LOAD 0xf1
-#define SLOW_TSTATES 16000
-#define FAST_TSTATES 64000
-
-#define E_LINE 16404
-#define LASTK1 16421
-#define LASTK2 16422
-#define MARGIN 16424
-#define FRAMES 16436
-#define CDFLAG 16443
-
-#define NMI_PERIOD 208
+#define SCAN_CYCLES 224
-/* The SPEC screen and memory
+/* The 3DS screen
*/
-static int fast_mode=FALSE;
-static int nmigen=FALSE;
-static int hsync=FALSE;
-static int drawing_screen=FALSE;
-static int ula_line_counter;
+#define GFX_W 400
+#define GFX_H 240
-static u16 black;
-static u16 white;
+/* The Spectrum screen and memory
+*/
+#define SCR_W 256
+#define SCR_H 192
+#define TXT_W 32
+#define TXT_H 24
+#define SCRDATA 0x4000
+#define ATTR 0x5800
-#define SCR_W 256
-#define SCR_H 192
-#define TXT_W 32
-#define TXT_H 24
+#define OFF_X ((GFX_W-SCR_W)/2)
+#define OFF_Y ((GFX_H-SCR_H)/2)
-static Z80Byte mem[0x10000];
+#define ATTR_AT(x,y) mem[ATTR+(x)+((y)/8)*32]
-static u16 screen[SCR_W*SCR_H];
-static int ula_ptr;
+Z80Byte mem[0x10000];
-static Z80Word RAMBOT=0;
-static Z80Word RAMTOP=0;
+static int border;
+static int scanline;
-#define DFILE 0x400c
+#define TOPL 64 /* Scanlines before 'first' line */
+#define SCRL SCR_H /* Scanlines making up screen data */
+#define BOTL 56 /* Scanlines after 'last' line */
-#define WORD(a) (mem[a] | (Z80Word)mem[a+1]<<8)
+#define TOTL (TOPL+SCRL+BOTL)
-/* Tape
+/* GFX vars
*/
-static int enable_filesystem;
-static int allow_save;
-static const Z80Byte *tape_image;
-static int tape_len;
+#define FLASH 16 /* Frames per flash */
+
+static Framebuffer *fb;
+
+static int flash=0;
+static int flashctr=0;
+
+#define NVAL 205 /* Normal RGB intensity */
+#define BVAL 255 /* Bright RGB intensity */
+
+static Z80Byte *line[SCR_H]; /* Accelerators to screen data */
+
+#define NO_COLS 16
+
+static struct
+{
+ u16 col;
+ int r,g,b;
+} coltable[NO_COLS]=
+{
+ {0, 0x00,0x00,0x00}, /* BLACK */
+ {0, 0x00,0x00,NVAL}, /* BLUE */
+ {0, NVAL,0x00,0x00}, /* RED */
+ {0, NVAL,0x00,NVAL}, /* MAGENTA */
+ {0, 0x00,NVAL,0x00}, /* GREEN */
+ {0, 0x00,NVAL,NVAL}, /* CYAN */
+ {0, NVAL,NVAL,0x00}, /* YELLOW */
+ {0, NVAL,NVAL,NVAL}, /* WHITE */
+
+ {0, 0x00,0x00,0x00}, /* BLACK */
+ {0, 0x00,0x00,BVAL}, /* BLUE */
+ {0, BVAL,0x00,0x00}, /* RED */
+ {0, BVAL,0x00,BVAL}, /* MAGENTA */
+ {0, 0x00,BVAL,0x00}, /* GREEN */
+ {0, 0x00,BVAL,BVAL}, /* CYAN */
+ {0, BVAL,BVAL,0x00}, /* YELLOW */
+ {0, BVAL,BVAL,BVAL}, /* WHITE */
+
+};
-static char last_dir[FILENAME_MAX] = "/";
/* The keyboard
*/
@@ -126,29 +156,20 @@ static struct
/* ---------------------------------------- PRIVATE FUNCTIONS
*/
-#define PEEKW(addr) (mem[addr] | (Z80Word)mem[addr+1]<<8)
-
-#define POKEW(addr,val) do \
- { \
- Z80Word wa=addr; \
- Z80Word wv=val; \
- mem[wa]=wv; \
- mem[wa+1]=wv>>8; \
- } while(0)
-
static void RomPatch(void)
{
static const Z80Byte save[]=
{
0xed, ED_SAVE, /* (SAVE) */
- 0xc3, 0x07, 0x02, /* JP $0207 */
+ 0xc9, /* RET */
0xff /* End of patch */
};
static const Z80Byte load[]=
{
+ 0x08, /* EX AF,AF' */
0xed, ED_LOAD, /* (LOAD) */
- 0xc3, 0x07, 0x02, /* JP $0207 */
+ 0xc9, /* RET */
0xff /* End of patch */
};
@@ -165,176 +186,108 @@ static void RomPatch(void)
}
}
-/* Open a tape file the passed address
-*/
-static FILE *OpenTapeFile(Z80Word addr, int *cancelled, const char *mode)
+
+static void DrawScanline(int y, int sline)
{
- static const char zx_chars[] = "\"#$:?()><=+-*/;,."
- "0123456789"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- FILE *fp;
- char full_fn[FILENAME_MAX] = DEFAULT_SNAPDIR;
- char fn[FILENAME_MAX];
- int f;
- int done;
+ int aline;
+ int f,r;
+ int ink,paper,t;
+ Z80Byte *scr;
+ Z80Byte b;
+ Z80Byte att;
- fp = NULL;
- f = 0;
- done = FALSE;
- *cancelled = FALSE;
+ aline=sline-TOPL;
- while(f<(FILENAME_MAX-3) && !done)
+ y = fb->height - y - 1;
+
+ for(f = 0; f < fb->width; f++)
{
- int ch;
+ FB_ADDR(fb, f, y) = coltable[border].col;
+ }
- ch = mem[addr++];
+ if (aline>=0 && aline<SCRL)
+ {
+ scr=line[aline];
- if (ch&0x80)
+ for(f=0;f<TXT_W;f++)
{
- done = TRUE;
- ch &= 0x7f;
- }
+ att=ATTR_AT(f,aline);
- if (ch>=11 && ch<=63)
- {
- fn[f++] = zx_chars[ch-11];
- }
- }
+ ink=(att&0x07);
+ paper=(att&0x38)>>3;
- if (fn[0] == '*')
- {
- if (GUI_FileSelect(last_dir,fn,".P"))
- {
- fp = fopen(fn, mode);
- }
- else
- {
- *cancelled = TRUE;
- }
+ if (att&0x40)
+ {
+ ink+=8;
+ paper+=8;
+ }
- SK_DisplayKeyboard();
- }
- else
- {
- fn[f++] = '.';
- fn[f++] = 'P';
- fn[f] = 0;
+ if ((att&0x80)&&(flash))
+ {
+ t=ink;
+ ink=paper;
+ paper=t;
+ }
- strcat(full_fn,fn);
+ for(r=0,b=*scr++;r<8;r++)
+ {
+ if (b & 0x80)
+ {
+ FB_ADDR(fb, f*8+r+OFF_X, y) = coltable[ink].col;
+ }
+ else
+ {
+ FB_ADDR(fb, f*8+r+OFF_X, y) = coltable[paper].col;
+ }
- if (!(fp = fopen(full_fn, mode)))
- {
- fp = fopen(fn, mode);
+ b = b << 1;
+ }
}
}
-
- return fp;
-}
-
-
-static void LoadInternalTape(Z80 *z80)
-{
- memcpy(mem+0x4009,tape_image,tape_len);
}
-static void LoadExternalTape(FILE *tape, Z80 *z80)
+static int CheckTimers(Z80 *z80, Z80Val val)
{
- int c;
- Z80Byte *a;
-
- a=mem+0x4009;
+ int ret = TRUE;
- while((c=getc(tape))!=EOF)
+ if (val > SCAN_CYCLES)
{
- *a++=c;
- }
-}
-
+ int y;
-static void SaveExternalTape(FILE *tape, Z80 *z80)
-{
- int f;
- int end;
+ Z80ResetCycles(z80, val-SCAN_CYCLES);
- f = 0x4009;
- end = WORD(E_LINE);
+ /* Increment scan line and check for frame flyback
+ */
+ scanline++;
- while(f <= end)
- {
- fputc(mem[f++], tape);
- }
-}
+ if (scanline==TOTL)
+ {
+ scanline=0;
+ flashctr++;
-static int CheckTimers(Z80 *z80, Z80Val val)
-{
- static Z80Word last_PC;
- static Z80Byte last_R;
- int ret = TRUE;
+ if (flashctr==FLASH)
+ {
+ flash^=1;
+ flashctr=0;
+ }
- /* See if we're started or stopping executing the display file
- */
- if (z80->PC > 0x7fff)
- {
- if (last_PC < 0x8000)
- {
- drawing_screen = TRUE;
- ula_ptr = 0;
- }
- }
+ Z80Interrupt(z80,0xff);
- if (z80->PC < 0x8000)
- {
- if (last_PC > 0x7fff && ula_ptr == SCR_W * SCR_H)
- {
- drawing_screen = FALSE;
- ula_ptr = 0;
- Z80ResetCycles(z80, val - SLOW_TSTATES);
ret = FALSE;
- }
- }
+ }
- last_PC = z80->PC;
+ /* Draw scanline
+ */
+ y = scanline - TOPL + OFF_Y;
- /* Check HSYNC generation
- */
- if (hsync && !nmigen)
- {
- if (last_R & 0x40 && !(z80->R & 0x40))
+ if (y >= 0 && y < fb->height)
{
- Z80Interrupt(z80, 0xff);
- ula_line_counter = (ula_line_counter + 1) % 8;
+ DrawScanline(y, scanline);
}
- }
-
- last_R = z80->R;
- /* Check NMI generator
- */
- if (nmigen && val > NMI_PERIOD)
- {
- Z80NMI(z80);
- Z80ResetCycles(z80, val - NMI_PERIOD);
- }
-
- /* We should only be able to execute FAST_TSTATES if we're not drawing the
- screen.
- */
- if (val >= FAST_TSTATES)
- {
- Z80ResetCycles(z80,val-FAST_TSTATES);
-
- if (mem[CDFLAG] & 0x80)
- {
- fast_mode = FALSE;
- }
- else
- {
- fast_mode = TRUE;
- }
-
- ret = FALSE;
+ /* TODO: Process sound emulation */
}
return ret;
@@ -346,58 +299,23 @@ static int EDCallback(Z80 *z80, Z80Val data)
switch((Z80Byte)data)
{
case ED_SAVE:
- if (allow_save && z80->DE.w<0x8000)
- {
- FILE *fp;
- int cancel;
-
- if ((fp=OpenTapeFile(z80->HL.w, &cancel, "wb")))
- {
- SaveExternalTape(fp,z80);
- fclose(fp);
- }
- }
+ z80->AF.b[Z80_LO_WORD] &= ~eZ80_Carry;
break;
case ED_LOAD:
- /* Try and load the external file if a name given. Otherwise, we
- try the internal one. Some of this is slightly dodgy -- it was
- never intended for the emulator to be doing any GUI related
- nonsense (like the alerts) but simply emulating.
- */
- if (enable_filesystem && z80->DE.w<0x8000)
+ if (TAPLoad(z80->AF.b[Z80_HI_WORD],
+ &z80->IX.w,
+ &z80->DE.w,
+ SPECWriteSnap))
{
- FILE *fp;
- int cancel;
-
- if ((fp=OpenTapeFile(z80->DE.w, &cancel, "rb")))
- {
- LoadExternalTape(fp,z80);
- fclose(fp);
- }
- else
- {
- if (!cancel)
- {
- GUI_Alert(FALSE,"Couldn't open tape");
- SK_DisplayKeyboard();
- }
- }
+ z80->AF.b[Z80_LO_WORD] |= eZ80_Carry;
+ z80->BC.w=0xb001;
}
else
{
- if (tape_image)
- {
- LoadInternalTape(z80);
- }
- else
- {
- GUI_Alert(FALSE,"No tape image selected");
- SK_DisplayKeyboard();
- }
+ z80->AF.b[Z80_LO_WORD] &= ~eZ80_Carry;
+ z80->BC.w = 0xff01;
}
-
- mem[CDFLAG]=0xc0;
break;
default:
@@ -412,10 +330,14 @@ static int EDCallback(Z80 *z80, Z80Val data)
*/
void SPECInit(Z80 *z80)
{
- Z80Word f;
+ int f;
- black = FB_GetColour(COL_BLACK);
- white = FB_GetColour(COL_WHITE);
+ for(f = 0; f < NO_COLS; f++)
+ {
+ coltable[f].col = RGB8_to_565(coltable[f].r,
+ coltable[f].g,
+ coltable[f].b);
+ }
/* Load the ROM
*/
@@ -427,58 +349,13 @@ void SPECInit(Z80 *z80)
Z80LodgeCallback(z80,eZ80_EDHook,EDCallback);
Z80LodgeCallback(z80,eZ80_Instruction,CheckTimers);
- /* Mirror the ROM
- */
- memcpy(mem+ROMLEN,mem,ROMLEN);
-
- /* Memory size (16K)
- */
- RAMBOT=0x4000;
- RAMTOP=RAMBOT+0x4000;
-
- for(f = RAMBOT; f <= RAMTOP; f++)
- {
- mem[f] = 0;
- }
-
- for(f = 0; f < 8; f++)
- {
- matrix[f] = 0x1f;
- }
+ SPECReset(z80);
}
-void SPECRenderDisplay(Framebuffer *fb, Z80 *z80)
+void SPECStartFrame(Framebuffer *framebuffer)
{
- if (fast_mode)
- {
- u16 x;
- u16 y;
-
- for(x = 0; x < fb->width; x++)
- {
- for(y = 0; y < fb->height; y++)
- {
- FB_ADDR(fb, x, y) = ((x+y) % 2) ? black : white;
- }
- }
- }
- else
- {
- int x;
- int y;
-
- FB_Clear(fb, COL_WHITE);
-
- for(y = 0; y < SCR_H; y++)
- {
- for(x = 0; x < SCR_W; x++)
- {
- FB_ADDR(fb, 72 + x, fb->height - 24 - y) =
- screen[x + y * SCR_W];
- }
- }
- }
+ fb = framebuffer;
}
@@ -497,75 +374,29 @@ void SPECHandleKey(SoftKey key, int is_pressed)
}
else
{
- /* TODO: Joysticks? Were there any common ones for the 81? */
+ /* TODO: Joysticks? */
}
}
Z80Byte SPECReadMem(Z80 *z80, Z80Word addr)
{
- if (addr & 0x8000)
- {
- Z80Byte b;
-
- b = mem[addr & 0x7fff];
-
- /* If bit 6 of the byte is set it is sent to the CPU, otherwise it is
- used to generate the video signal and zero sent to the CPU
- */
- if (b & 0x40)
- {
- screen[ula_ptr++] = white;
- screen[ula_ptr++] = white;
- screen[ula_ptr++] = white;
- screen[ula_ptr++] = white;
- screen[ula_ptr++] = white;
- screen[ula_ptr++] = white;
- screen[ula_ptr++] = white;
- screen[ula_ptr++] = white;
- }
- else
- {
- Z80Word base;
- int inv;
- int f;
-
- inv=b & 0x80;
- b &= 0x3f;
-
- base = ((Z80Word)z80->I<<8)|(b<<3)|ula_line_counter;
-
- b = mem[base];
-
- for(f = 0; f < 8; f++)
- {
- if (b & 0x80)
- {
- screen[ula_ptr++] = rand(); // inv ? white : black;
- }
- else
- {
- screen[ula_ptr++] = rand(); // inv ? black : white;
- }
-
- b = b << 1;
- }
+ return mem[addr];
+}
- b = 0;
- }
- return b;
- }
- else
+void SPECWriteMem(Z80 *z80, Z80Word addr, Z80Byte val)
+{
+ if (addr>=ROMLEN)
{
- return mem[addr];
+ mem[addr]=val;
}
}
-void SPECWriteMem(Z80 *z80, Z80Word addr, Z80Byte val)
+void SPECWriteSnap(Z80Word addr, Z80Byte val)
{
- if (addr>=RAMBOT && addr<=RAMTOP)
+ if (addr>=ROMLEN)
{
mem[addr]=val;
}
@@ -574,55 +405,39 @@ void SPECWriteMem(Z80 *z80, Z80Word addr, Z80Byte val)
Z80Byte SPECReadPort(Z80 *z80, Z80Word port)
{
- Z80Byte b=0;
+ Z80Byte lo=port&0xff;
+ Z80Byte hi=port>>8;
+ Z80Byte b=0xff;
+ int f;
- switch(port&0xff)
+ switch(lo)
{
- case 0xfe: /* ULA */
- /* Key matrix
- */
- switch(port&0xff00)
- {
- case 0xfe00:
- b=matrix[0];
- break;
- case 0xfd00:
- b=matrix[1];
- break;
- case 0xfb00:
- b=matrix[2];
- break;
- case 0xf700:
- b=matrix[3];
- break;
- case 0xef00:
- b=matrix[4];
- break;
- case 0xdf00:
- b=matrix[5];
- break;
- case 0xbf00:
- b=matrix[6];
- break;
- case 0x7f00:
- b=matrix[7];
- break;
- }
-
- /* Some code expects some of the top bits set... Of course, whether
- or not this may be worse as other code doesn't expect the bits,
- we shall find out!
- */
- b |= 0x60;
+ case 0x1f: /* Kempston joystick */
+ break;
- /* Reset ULA line counter
- */
- ula_line_counter = 0;
+ case 0x7f: /* Fuller joystick */
+ break;
+ case 0xfb: /* ZX Printer */
break;
- default:
- b = 0xff; /* Idle bus */
+ default: /* ULA */
+ if (!(lo&1))
+ {
+ /* Key matrix
+ */
+ b=0xff;
+
+ for(f=0;f<8;f++)
+ if (!(hi&(1<<f)))
+ b&=matrix[f];
+
+ b|=0xa0;
+
+ /* TODO: Emulation of contention? */
+ }
+ else
+ b=0xff;
break;
}
@@ -632,20 +447,19 @@ Z80Byte SPECReadPort(Z80 *z80, Z80Word port)
void SPECWritePort(Z80 *z80, Z80Word port, Z80Byte val)
{
- switch(port&0xff)
+ Z80Byte lo=port&0xff;
+
+ switch(lo)
{
- case 0xfd: /* NMI generator OFF */
- nmigen = FALSE;
+ case 0xfe: /* ULA */
+ border = val & 0x07;
break;
- case 0xfe: /* NMI generator ON */
- nmigen = TRUE;
- Z80ResetCycles(z80, 0);
+ case 0xfb: /* ZX Printer */
break;
- case 0xff: /* HSYNC generator ON */
- hsync = TRUE;
- break;
+ default:
+ break;
}
}
@@ -653,6 +467,8 @@ void SPECWritePort(Z80 *z80, Z80Word port, Z80Byte val)
void SPECReset(Z80 *z80)
{
int f;
+ int c;
+ int r;
for(f=0;f<8;f++)
matrix[f]=0x1f;
@@ -660,27 +476,32 @@ void SPECReset(Z80 *z80)
Z80Reset(z80);
Z80ResetCycles(z80,0);
- nmigen = FALSE;
- hsync = FALSE;
-}
-
+ border = 0;
+ scanline = 0;
-void SPECEnableFileSystem(int enable)
-{
- enable_filesystem=enable;
-}
+ /* Set up screen
+ */
+ c=0;
+ r=0;
+ for(f=0;f<SCRL;f++)
+ {
+ line[f]=mem+SCRDATA+(c*8*TXT_W)+(r*TXT_W);
+ c++;
-void SPECSetTape(const Z80Byte *image, int len)
-{
- tape_image=image;
- tape_len=len;
+ if ((c%8)==0)
+ {
+ if (++r==8)
+ r=0;
+ else
+ c-=8;
+ }
+ }
}
void SPECReconfigure(void)
{
- allow_save = enable_filesystem && DSSPEC_Config[DSSPEC_ALLOW_TAPE_SAVE];
}
@@ -698,8 +519,7 @@ void SPECSaveSnapshot(FILE *fp)
PUT_Byte(fp, matrix[f]);
}
- PUT_ULong(fp, RAMBOT);
- PUT_ULong(fp, RAMTOP);
+ PUT_Byte(fp, border);
}
@@ -717,8 +537,7 @@ void SPECLoadSnapshot(FILE *fp)
matrix[f] = GET_Byte(fp);
}
- RAMBOT = GET_ULong(fp);
- RAMTOP = GET_ULong(fp);
+ border = GET_Byte(fp);
}