/* dsspec - Nintendo DS Sinclair Spectrum 48K emulator. Copyright (C) 2006 Ian Cowburn 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. $Id$ */ #include #include #include #include #include "spec.h" #include "snap.h" #include "spec48_bin.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 uint16 *vram; static const int ROMLEN=0x4000; static const int ROM_SAVE=0x4c6; static const int ROM_LOAD=0x562; #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 Z80Byte mem[0x10000]; /* Number of cycles per frame */ static Z80Val FRAME_CYCLES=69888; /* GFX vars */ #define FLASH 16 /* Frames per flash */ static int flash=0; static int flashctr=0; #define NVAL 25 /* Normal RGB intensity */ #define BVAL 31 /* Bright RGB intensity */ static Z80Byte *line[SCR_H]; /* Accelerators to screen data */ static struct { uint16 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]; /* ---------------------------------------- PRIVATE FUNCTIONS */ static void DrawScreen(void) { int f,r; int ink,paper,t; int y; Z80Byte *scr; Z80Byte b; Z80Byte att; uint16 *vr; vr=vram; swiWaitForVBlank(); for(y=0;y>3; if (att&0x40) { ink+=8; paper+=8; } if ((att&0x80)&&(flash)) { t=ink; ink=paper; paper=t; } b=*scr++; for(r=0;r<8;r++) { if (b&(1<<(7-r))) { *vr++=coltable[ink].col; } else { *vr++=coltable[paper].col; } } } } } 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: state.AF&=~eZ80_Carry; Z80SetState(z80,&state); break; case LOAD_PATCH: Z80GetState(z80,&state); if (TAPLoad(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>FRAME_CYCLES) { Z80ResetCycles(z80,val-FRAME_CYCLES); flashctr++; if (flashctr==FLASH) { flash^=1; flashctr=0; } Z80Interrupt(z80,0xff); DrawScreen(); /* TODO: Process sound emulation */ return FALSE; } else { return TRUE; } } /* ---------------------------------------- EXPORTED INTERFACES */ void SPECInit(uint16 *v, Z80 *z80) { vram=v; memcpy(mem,spec48_bin,ROMLEN); /* Patch the ROM */ RomPatch(); Z80LodgeCallback(z80,eZ80_EDHook,EDCallback); Z80LodgeCallback(z80,eZ80_Instruction,CheckTimers); SPECReset(z80); } void SPECHandleKey(SoftKey key, int is_pressed) { if (key<=SK_SPACE) { int row; int bit; row=key/5; if (row&1) { bit=5-(key%5); } else { bit=(key%5); } if (is_pressed) { matrix[row]&=~bit; } else { matrix[row]|=bit; } } else { /* TODO: Joysticks! */ } } Z80Byte SPECReadPort(Z80 *z80, Z80Word port) { Z80Byte lo=port&0xff; Z80Byte hi=port>>8; Z80Byte b=0xff; int f; switch(lo) { case 0x1f: /* Kempston joystick */ break; case 0x7f: /* Fuller joystick */ break; case 0xfb: /* ZX Printer */ break; default: /* ULA */ if (!(lo&1)) { /* Key matrix */ b=0xff; for(f=0;f<8;f++) if (!(hi&(1<