/* 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 Spectrum */ #include #include #include #include "spec.h" #include "snap.h" #include "gfx.h" #include "gui.h" #include "config.h" #include "exit.h" #include "util.h" #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #define HIBYTE(w) ((w)>>8) #define LOBYTE(w) ((w)&0xff) /* ---------------------------------------- STATICS */ static const int ROMLEN=0x4000; static const int ROM_SAVE=0x4c6; static const int ROM_LOAD=0x562; static FILE *tape_in; static FILE *tape_out; #define LOAD_PATCH 0xf0 #define SAVE_PATCH 0xf1 /* The SPEC screen */ #define SCR_W 256 #define SCR_H 192 #define TXT_W 32 #define TXT_H 24 #define SCRDATA 0x4000 #define ATTR 0x5800 #define ATTR_AT(x,y) mem[ATTR+(x)+((y)/8)*32] static const int OFF_X=(GFX_WIDTH-SCR_W)/2; static const int OFF_Y=(GFX_HEIGHT-SCR_H)/2; static Z80Byte mem[0x10000]; /* Number of cycles per scan lines and scan line control */ static Z80Val SCAN_CYCLES=224; static int scanline=0; static int enable_screen=TRUE; /* GFX vars */ #define FLASH 16 /* Frames per flash */ static int flash=0; static int flashctr=0; static int border=0; #define TOPL 64 /* Scanlines before 'first' line */ #define SCRL SCR_H /* Scanlines making up screen data */ #define BOTL 56 /* Scanlines after 'last' line */ #define TOTL (TOPL+SCRL+BOTL) #define NVAL 235 /* Normal RGB intensity */ #define BVAL 255 /* Bright RGB intensity */ static Z80Byte *line[SCRL]; /* Accelerators to screen data */ static struct { Uint32 col; int r,g,b; } coltable[16]= { {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 */ }; /* The keyboard */ static Z80Byte matrix[8]; typedef struct { SDL_Keycode key; int m1,b1,m2,b2; } MatrixMap; #define KY1(m,b) m,1<>8,FlagString(s.AF&0xff)); printf("BC=%4.4x DE=%4.4x HL=%4.4x\n",s.BC,s.DE,s.HL); printf("IX=%4.4x IY=%4.4x SP=%4.4x\n",s.IX,s.IY,s.SP); printf("I=%2.2x IM=%2.2x R=%2.2x\n",s.I,s.IM,s.R); printf("IFF1=%2.2x IFF2=%2.2x\n",s.IFF1,s.IFF2); printf("%s\n",Z80Disassemble(z80,&s.PC)); } #endif /* ---------------------------------------- PRIVATE FUNCTIONS */ static void DrawScanlineAt(int y, int sline) { int aline; int f,r; int ink,paper,t; Z80Byte *scr; Z80Byte b; Z80Byte att; aline=sline-TOPL; GFXHLine(0,GFX_WIDTH-1,y,coltable[border].col); if (aline>=0 && aline>3; if (att&0x40) { ink+=8; paper+=8; } if ((att&0x80)&&(flash)) { t=ink; ink=paper; paper=t; } for(r=0,b=*scr++;r<8;r++) if (b&(1<<(7-r))) GFXPlot(f*8+r+OFF_X,y,coltable[ink].col); else GFXPlot(f*8+r+OFF_X,y,coltable[paper].col); } } } #define DrawScanline(y) DrawScanlineAt(y,scanline) static void RomPatch(void) { static const Z80Byte save[]= { 0xed, SAVE_PATCH, /* ED illegal op */ 0xc9, /* RET */ 0xff /* End of patch */ }; static const Z80Byte load[]= { 0x08, /* EX AF,AF' */ 0xed, LOAD_PATCH, /* ED illegal op */ 0xc9, /* RET */ 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]; } Z80Byte SPECPeek(Z80 *cpu, Z80Word addr) { return mem[addr]; } Z80Byte SnapPeek(Z80Word addr) { return mem[addr]; } void SPECPoke(Z80 *cpu, Z80Word addr, Z80Byte val) { if (addr>=ROMLEN) mem[addr]=val; } void SnapPoke(Z80Word addr, Z80Byte val) { if (addr>=ROMLEN) mem[addr]=val; } static int EDCallback(Z80 *z80, Z80Val data) { Z80State state; switch((Z80Byte)data) { case SAVE_PATCH: Z80GetState(z80,&state); if (!tape_out) { state.AF|=eZ80_Carry; } else { if (TAPSave(tape_out, HIBYTE(state.AF), &state.IX, &state.DE, SnapPeek)) { state.AF|=eZ80_Carry; } else { state.AF&=~eZ80_Carry; } } Z80SetState(z80,&state); break; case LOAD_PATCH: Z80GetState(z80,&state); if (!tape_in) { state.AF|=eZ80_Carry; } else { if (TAPLoad(tape_in, HIBYTE(state.AF), &state.IX, &state.DE, SnapPoke)) { state.AF|=eZ80_Carry; state.BC=0xb001; } else { state.AF&=~eZ80_Carry; state.BC=0xff01; } } Z80SetState(z80,&state); break; default: break; } return TRUE; } static int CheckTimers(Z80 *z80, Z80Val val) { if (val>SCAN_CYCLES) { int y; Z80ResetCycles(z80,val-SCAN_CYCLES); /* Increment scan line and check for frame flyback */ scanline++; if (scanline==TOTL) { scanline=0; flashctr++; if (flashctr==FLASH) { flash^=1; flashctr=0; } Z80Interrupt(z80,0xff); if (enable_screen) { GFXEndFrame(TRUE); GFXStartFrame(); } } /* Draw scanline */ y=scanline-TOPL+OFF_Y; if (y>=0 && ykey.repeat) { return; } 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 SPECReadPort(Z80 *z80, Z80Word port) { Z80Byte lo=port&0xff; Z80Byte hi=port>>8; Z80Byte b=0xff; int f; switch(lo) { case 0x1f: /* TODO: Kempston joystick */ break; case 0x7f: /* TODO: Fuller joystick */ break; case 0xfb: /* TODO: ZX Printer */ break; default: /* ULA */ if (!(lo&1)) { /* Key matrix */ b=0xff; for(f=0;f<8;f++) if (!(hi&(1<=0 && y