diff options
author | Ian C <ianc@noddybox.co.uk> | 2003-12-23 23:33:02 +0000 |
---|---|---|
committer | Ian C <ianc@noddybox.co.uk> | 2003-12-23 23:33:02 +0000 |
commit | 33dfea1c74749124379ef8052f0689577ebe68ff (patch) | |
tree | 6db163a5b8a17fe06da01ac13c707d25a3a342bc /src/spec.c | |
parent | 15261bfe7390fb0d0ac40c9d4e20c3a86b5e3e33 (diff) |
This commit was generated by cvs2svn to compensate for changes in r2,
which included commits to RCS files with non-trunk default branches.
Diffstat (limited to 'src/spec.c')
-rw-r--r-- | src/spec.c | 673 |
1 files changed, 673 insertions, 0 deletions
diff --git a/src/spec.c b/src/spec.c new file mode 100644 index 0000000..51006df --- /dev/null +++ b/src/spec.c @@ -0,0 +1,673 @@ +/* + + espec - Sinclair Spectrum emulator + + Copyright (C) 2003 Ian Cowburn (ianc@noddybox.demon.co.uk) + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + Provides the emulation for the SPEC + +*/ +static const char ident[]="$Id$"; + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "spec.h" +#include "gfx.h" +#include "gui.h" +#include "config.h" +#include "exit.h" + +static const char ident_h[]=ESPEC_SPECH; + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + + +/* ---------------------------------------- STATICS +*/ +static const int ROMLEN=0x2000; +static const int ROM_SAVE=0x2fc; +static const int ROM_LOAD=0x347; + +/* No of cycles in each 64us HSYNC (hopefully) +*/ +static const int HSYNC_PERIOD=321; + +/* The SPEC screen +*/ +static const int SCR_W=256; +static const int SCR_H=192; +static const int TXT_W=32; +static const int TXT_H=24; + +/* These assume a 320x200 screen +*/ +static const int OFF_X=(320-256)/2; +static const int OFF_Y=(200-192)/2; + +static Z80Byte mem[0x10000]; + +static Z80Word RAMBOT=0; +static Z80Word RAMTOP=0; +static Z80Word RAMLEN=0; + +/* Counter used when triggering the interrupts for the display +*/ +static int nmigen=FALSE; +static int hsync=FALSE; + +/* The ULA +*/ +static struct +{ + int x; + int y; + int c; + int release; +} ULA; + +/* GFX vars +*/ +static Uint32 white; +static Uint32 black; + + +/* The keyboard +*/ +static Z80Byte matrix[8]; + +typedef struct +{ + SDLKey key; + int m1,b1,m2,b2; +} MatrixMap; + +#define KY1(m,b) m,1<<b,-1,-1 +#define KY2(m1,b1,m2,b2) m1,1<<b1,m2,1<<b2 + +static const MatrixMap keymap[]= +{ + {SDLK_1, KY1(3,0)}, + {SDLK_2, KY1(3,1)}, + {SDLK_3, KY1(3,2)}, + {SDLK_4, KY1(3,3)}, + {SDLK_5, KY1(3,4)}, + {SDLK_6, KY1(4,4)}, + {SDLK_7, KY1(4,3)}, + {SDLK_8, KY1(4,2)}, + {SDLK_9, KY1(4,1)}, + {SDLK_0, KY1(4,0)}, + + {SDLK_a, KY1(1,0)}, + {SDLK_b, KY1(7,4)}, + {SDLK_c, KY1(0,3)}, + {SDLK_d, KY1(1,2)}, + {SDLK_e, KY1(2,2)}, + {SDLK_f, KY1(1,3)}, + {SDLK_g, KY1(1,4)}, + {SDLK_h, KY1(6,4)}, + {SDLK_i, KY1(5,2)}, + {SDLK_j, KY1(6,3)}, + {SDLK_k, KY1(6,2)}, + {SDLK_l, KY1(6,1)}, + {SDLK_m, KY1(7,2)}, + {SDLK_n, KY1(7,3)}, + {SDLK_o, KY1(5,1)}, + {SDLK_p, KY1(5,0)}, + {SDLK_q, KY1(2,0)}, + {SDLK_r, KY1(2,3)}, + {SDLK_s, KY1(1,1)}, + {SDLK_t, KY1(2,4)}, + {SDLK_u, KY1(5,3)}, + {SDLK_v, KY1(0,4)}, + {SDLK_w, KY1(2,1)}, + {SDLK_x, KY1(0,2)}, + {SDLK_y, KY1(5,4)}, + {SDLK_z, KY1(0,1)}, + + {SDLK_RETURN, KY1(6,0)}, + {SDLK_SPACE, KY1(7,0)}, + + {SDLK_COMMA, KY1(7,1)}, /* In the right place... */ + {SDLK_PERIOD, KY1(7,1)}, /* ...or the right key... */ + + {SDLK_BACKSPACE, KY2(0,0,4,0)}, + {SDLK_DELETE, KY2(0,0,4,0)}, + {SDLK_UP, KY2(0,0,4,3)}, + {SDLK_DOWN, KY2(0,0,4,4)}, + {SDLK_LEFT, KY2(0,0,3,4)}, + {SDLK_RIGHT, KY2(0,0,4,2)}, + + {SDLK_RSHIFT, KY1(0,0)}, + {SDLK_LSHIFT, KY1(0,0)}, + + {SDLK_UNKNOWN, 0,0,0,0}, +}; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static void RomPatch(void) +{ + static const Z80Byte save[]= + { + 0xed, 0xf0, /* ED F0 illegal op */ + 0xc3, 0x08, 0x02, /* JP $0207 */ + 0xff /* End of patch */ + }; + + static const Z80Byte load[]= + { + 0xed, 0xf1, /* ED F0 illegal op */ + 0xc3, 0x08, 0x02, /* JP $0207 */ + 0xff /* End of patch */ + }; + + int f; + + for(f=0;save[f]!=0xff;f++) + mem[ROM_SAVE+f]=save[f]; + + for(f=0;load[f]!=0xff;f++) + mem[ROM_LOAD+f]=load[f]; +} + + +static char ToASCII(Z80Byte b) +{ + if (b==0) /* SPACE */ + return ' '; + + if (b==22) /* Dash (-) */ + return '-'; + + if (b>=28 && b<=37) /* 0-9 */ + return '0'+b-28; + + if (b>=38 && b<=63) /* A-Z */ + return 'a'+b-38; + + return 0; +} + + +static const char *ConvertFilename(Z80Word addr) +{ + static char buff[FILENAME_MAX]; + char *p; + + p=buff; + *p=0; + + if (addr>0x8000) + return buff; + + do + { + char c=ToASCII(mem[addr]&0x7f); + + if (c) + *p++=c; + + } while(mem[addr++]<0x80); + + *p=0; + + return buff; +} + + +static void LoadTape(Z80State *state) +{ + const char *p=ConvertFilename(state->DE); + char path[FILENAME_MAX]; + FILE *fp; + Z80Word addr; + int c; + + if (strlen(p)==0) + { + GUIMessage("ERROR","Can't load empty filename"); + return; + } + + strcpy(path,SConfig(CONF_TAPEDIR)); + strcat(path,"/"); + strcat(path,p); + strcat(path,".p"); + + if (!(fp=fopen(path,"rb"))) + { + GUIMessage("ERROR","Can't load file:\n%s",path); + return; + } + + addr=0x4009; + + while((c=getc(fp))!=EOF) + { + if (addr>=0x4000) + mem[addr]=(Z80Byte)c; + + addr++; + } + + fclose(fp); +} + + +static void SaveTape(Z80State *state) +{ + const char *p=ConvertFilename(state->DE); + char path[FILENAME_MAX]; + FILE *fp; + Z80Word start; + Z80Word end; + + if (strlen(p)==0) + { + GUIMessage("ERROR","Can't save empty filename"); + return; + } + + strcpy(path,SConfig(CONF_TAPEDIR)); + strcat(path,"/"); + strcat(path,p); + strcat(path,".p"); + + if (!(fp=fopen(path,"wb"))) + { + GUIMessage("ERROR","Can't write file:\n%s",path); + return; + } + + start=0x4009; + end=(Z80Word)mem[0x4014]|(Z80Word)mem[0x4015]<<8; + + while(start<=end) + putc(mem[start++],fp); + + fclose(fp); +} + + +static int EDCallback(Z80 *z80, Z80Val data) +{ + Z80State state; + + Z80GetState(z80,&state); + + switch((Z80Byte)data) + { + case 0xf0: + SaveTape(&state); + break; + + case 0xf1: + LoadTape(&state); + break; + + default: + break; + } + + return TRUE; +} + + +static void ULA_Video_Shifter(Z80 *z80, Z80Byte val) +{ + Z80State state; + Z80Word base; + int x,y; + int inv; + int b; + + Z80GetState(z80,&state); + + /* Extra check due to out dodgy ULA emulation + */ + if (ULA.y>=0 && ULA.y<SCR_H) + { + Uint32 fg,bg; + + /* Position on screen corresponding to ULA + */ + x=OFF_X+ULA.x*8; + y=OFF_Y+ULA.y; + + /* Get ULA invert state and clear to ULA 6-but code + */ + inv=val&0x80; + val&=0x3f; + + base=((Z80Word)state.I<<8)|(val<<3)|ULA.c; + + if (inv) + { + fg=white; + bg=black; + } + else + { + fg=black; + bg=white; + } + + for(b=0;b<8;b++) + { + if (mem[base]&(1<<(7-b))) + GFXPlot(x+b,y,fg); + else + GFXPlot(x+b,y,bg); + } + } + + ULA.x=(ULA.x+1)&0x1f; + + if (ULA.x==0) + Z80Interrupt(z80,0xff); +} + + +static int CheckTimers(Z80 *z80, Z80Val val) +{ + if (val>HSYNC_PERIOD) + { + Z80ResetCycles(z80,0); + + if (nmigen) + { + Z80NMI(z80,0xff); + printf("NMIGEN\n"); + } + else if (hsync) + { + printf("HSYNC\n"); + if (ULA.release) + { + /* ULA.release=FALSE; */ + ULA.c=(ULA.c+1)&7; + ULA.y++; + ULA.x=0; + } + } + } + + return TRUE; +} + + +/* ---------------------------------------- EXPORTED INTERFACES +*/ +void SPECInit(Z80 *z80) +{ + FILE *fp; + Z80Word f; + + if (!(fp=fopen(SConfig(CONF_ROMFILE),"rb"))) + { + GUIMessage("ERROR","Failed to open SPEC ROM\n%s",SConfig(CONF_ROMFILE)); + Exit(""); + } + + if (fread(mem,1,ROMLEN,fp)!=ROMLEN) + { + fclose(fp); + GUIMessage("ERROR","ROM file must be %d bytes long\n",ROMLEN); + Exit(""); + } + + /* Patch the ROM + */ + RomPatch(); + Z80LodgeCallback(z80,Z80_EDHook,EDCallback); + Z80LodgeCallback(z80,Z80_Fetch,CheckTimers); + + /* Mirror the ROM + */ + memcpy(mem+ROMLEN,mem,ROMLEN); + + RAMBOT=0x4000; + + /* Memory size (1 or 16K) + */ + if (IConfig(CONF_MEMSIZE)==16) + RAMLEN=0x2000; + else + RAMLEN=0x400; + + RAMTOP=RAMBOT+RAMLEN; + + for(f=RAMBOT;f<=RAMTOP;f++) + mem[f]=0; + + for(f=0;f<8;f++) + matrix[f]=0x1f; + + white=GFXRGB(230,230,230); + black=GFXRGB(0,0,0); + + nmigen=FALSE; + hsync=FALSE; + + GFXStartFrame(); +} + + +void SPECKeyEvent(SDL_Event *e) +{ + const MatrixMap *m; + + m=keymap; + + while(m->key!=SDLK_UNKNOWN) + { + if (e->key.keysym.sym==m->key) + { + if (e->key.state==SDL_PRESSED) + { + matrix[m->m1]&=~m->b1; + + if (m->m2!=-1) + matrix[m->m2]&=~m->b2; + } + else + { + matrix[m->m1]|=m->b1; + + if (m->m2!=-1) + matrix[m->m2]|=m->b2; + } + } + + m++; + } +} + + +Z80Byte SPECReadMem(Z80 *z80, Z80Word addr) +{ + /* Memory reads above 32K invoke the ULA + */ + if (addr>0x7fff) + { + Z80Byte b; + + /* B6 of R is tied to the IRQ line (only when HSYNC active?) + if ((HSYNC)&&(!(z80->R&0x40))) + z80->IRQ=TRUE; + */ + + b=mem[addr&0x7fff]; + + /* If bit 6 of the opcode is set the opcode is sent as is to the + Z80. If it's not, the byte is interretted by the ULA. + */ + if (b&0x40) + { + ULA_Video_Shifter(z80,0); + } + else + { + ULA_Video_Shifter(z80,b); + b=0; + } + + return b; + } + else + return mem[addr&0x7fff]; + +} + + +void SPECWriteMem(Z80 *z80, Z80Word addr, Z80Byte val) +{ + addr=addr&0x7fff; + + if (addr>=RAMBOT && addr<=RAMTOP) + mem[addr]=val; +} + + +Z80Byte SPECReadPort(Z80 *z80, Z80Word port) +{ + Z80Byte b=0; + + printf("IN %4.4x\n",port); + + switch(port&0xff) + { + 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; + } + + /* Turn off HSYNC if NMI generator is OFF and redraw screen to + match output ULA has since sent out. + */ + if (!nmigen && hsync) + { + hsync=FALSE; + + GFXEndFrame(TRUE); + GFXClear(white); + + ULA.x=0; + ULA.y=-1; + ULA.c=7; + ULA.release=FALSE; + + GFXStartFrame(); + } + else + { + /* Reset and hold ULA counter + */ + ULA.c=0; + ULA.release=FALSE; + } + break; + + default: + break; + } + + return b; +} + + +void SPECWritePort(Z80 *z80, Z80Word port, Z80Byte val) +{ + printf("OUT %4.4x\n",port); + + /* Any port write releases the ULA line counter + */ + ULA.release=TRUE; + + switch(port&0xff) + { + case 0xfd: /* NMI generator OFF */ + nmigen=FALSE; + break; + + case 0xfe: /* NMI generator ON */ + nmigen=TRUE; + break; + + case 0xff: /* HSYNC generator ON */ + hsync=TRUE; + Z80ResetCycles(z80,0); + break; + } +} + + +Z80Byte SPECReadForDisassem(Z80 *z80, Z80Word addr) +{ + return mem[addr&0x7fff]; +} + + +const char *SPECInfo(Z80 *z80) +{ + static char buff[80]; + + sprintf(buff,"NMIGEN: %s HSYNC: %s", + nmigen ? "ON":"OFF", + hsync ? "ON":"OFF"); + + return buff; +} + + +/* END OF FILE */ |