/* ds48 - Nintendo DS ZX Spectrum emulator. Copyright (C) 2007 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 "spec48_bin.h" #include "gui.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 *pal; 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 Z80Byte Z80_MEMORY[0x10000]; /* Number of cycles per frame */ #define 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 */ /* Accelerators to screen data - start of screen mem that matches an attribute. */ static Z80Byte *scr_accel[TXT_W * TXT_H]; static uint16 *vram_accel[TXT_W * TXT_H]; static struct { int r,g,b; } coltable[16]= { {0x00,0x00,0x00}, /* BLACK */ {0x00,0x00,NVAL}, /* BLUE */ {NVAL,0x00,0x00}, /* RED */ {NVAL,0x00,NVAL}, /* MAGENTA */ {0x00,NVAL,0x00}, /* GREEN */ {0x00,NVAL,NVAL}, /* CYAN */ {NVAL,NVAL,0x00}, /* YELLOW */ {NVAL,NVAL,NVAL}, /* WHITE */ {0x00,0x00,0x00}, /* BLACK */ {0x00,0x00,BVAL}, /* BLUE */ {BVAL,0x00,0x00}, /* RED */ {BVAL,0x00,BVAL}, /* MAGENTA */ {0x00,BVAL,0x00}, /* GREEN */ {0x00,BVAL,BVAL}, /* CYAN */ {BVAL,BVAL,0x00}, /* YELLOW */ {BVAL,BVAL,BVAL}, /* WHITE */ }; /* Screen content checkers */ typedef struct { int ink; int paper; } ColourCell; static ColourCell last_attr[TXT_W * TXT_H]; static Z80Byte last_screen[SCR_W * SCR_H / 8]; /* Lookups for bitmask to screen data */ static uint16 bitmap_to_screen[256][4]; /* The keyboard */ static Z80Byte matrix[8]; static struct { int row; int bit; } key_matrix[]= { {3,0x01}, {3,0x02}, {3,0x04}, {3,0x08}, {3,0x10}, /* 1 - 5 */ {4,0x10}, {4,0x08}, {4,0x04}, {4,0x02}, {4,0x01}, /* 6 - 0 */ {2,0x01}, {2,0x02}, {2,0x04}, {2,0x08}, {2,0x10}, /* Q - T */ {5,0x10}, {5,0x08}, {5,0x04}, {5,0x02}, {5,0x01}, /* Y - P */ {1,0x01}, {1,0x02}, {1,0x04}, {1,0x08}, {1,0x10}, /* A - G */ {6,0x10}, {6,0x08}, {6,0x04}, {6,0x02}, {6,0x01}, /* H - NL */ {0,0x01}, {0,0x02}, {0,0x04}, {0,0x08}, {0,0x10}, /* CAPS - V */ {7,0x10}, {7,0x08}, {7,0x04}, {7,0x02}, {7,0x01} /* B - SPACE */ }; /* ---------------------------------------- PRIVATE FUNCTIONS */ static void DrawScreen(void) { int f; int r; int ink; int paper; Z80Byte *scr; Z80Byte *old_scr; int scr_data; int inv_scr_data; int att; uint16 *vr; old_scr = last_screen; swiWaitForVBlank(); for(f=0;f>3; } else { ink = (att&0x07); paper = (att&0x38)>>3; } if (att&0x40) { ink += 8; paper += 8; } /* Get screen address for this attribute and VRAM base */ scr = scr_accel[f]; vr = vram_accel[f]; /* If attribute has changed, redraw 8x8 block */ if (last_attr[f].ink != ink || last_attr[f].paper != paper) { for(r=0;r<8;r++) { scr_data = *scr; inv_scr_data = scr_data^0xff; *vr = (bitmap_to_screen[scr_data][0] & ink) | (bitmap_to_screen[inv_scr_data][0] & paper); *(vr+1) = (bitmap_to_screen[scr_data][1] & ink) | (bitmap_to_screen[inv_scr_data][1] & paper); *(vr+2) = (bitmap_to_screen[scr_data][2] & ink) | (bitmap_to_screen[inv_scr_data][2] & paper); *(vr+3) = (bitmap_to_screen[scr_data][3] & ink) | (bitmap_to_screen[inv_scr_data][3] & paper); vr += 128; *old_scr++ = scr_data; scr += 32; } last_attr[f].ink = ink; last_attr[f].paper = paper; } else { /* Attribute not changed, so see if screen data has changed */ for(r=0;r<8;r++) { scr_data = *scr; if (scr_data != *old_scr) { inv_scr_data = scr_data^0xff; *vr = (bitmap_to_screen[scr_data][0] & ink) | (bitmap_to_screen[inv_scr_data][0] & paper); *(vr+1) = (bitmap_to_screen[scr_data][1] & ink) | (bitmap_to_screen[inv_scr_data][1] & paper); *(vr+2) = (bitmap_to_screen[scr_data][2] & ink) | (bitmap_to_screen[inv_scr_data][2] & paper); *(vr+3) = (bitmap_to_screen[scr_data][3] & ink) | (bitmap_to_screen[inv_scr_data][3] & paper); *old_scr++ = scr_data; } else { old_scr++; } vr += 128; scr += 32; } } } } 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++) Z80_MEMORY[ROM_SAVE+f]=save[f]; for(f=0;load[f]!=0xff;f++) Z80_MEMORY[ROM_LOAD+f]=load[f]; } Z80Byte SPECPeek(Z80 *cpu, Z80Word addr) { return Z80_MEMORY[addr]; } void SPECPoke(Z80 *cpu, Z80Word addr, Z80Byte val) { if (addr>=ROMLEN) Z80_MEMORY[addr]=val; } static int EDCallback(Z80 *z80, Z80Val data) { switch((Z80Byte)data) { case SAVE_PATCH: z80->AF.w&=~eZ80_Carry; break; case LOAD_PATCH: /* if (TAPLoad(HIBYTE(state.AF), z80->IX, z80->DE, SnapPoke)) { state.AF|=eZ80_Carry; state.BC=0xb001; } else { state.AF&=~eZ80_Carry; state.BC=0xff01; } */ 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(); return FALSE; } else { return TRUE; } } /* ---------------------------------------- EXPORTED INTERFACES */ void SPECInit(uint16 *p, uint16 *v, Z80 *z80) { int c; int r; int f; pal=p; vram=v; memcpy(Z80_MEMORY,spec48_bin,ROMLEN); /* Patch the ROM */ RomPatch(); Z80LodgeCallback(z80,eZ80_EDHook,EDCallback); Z80LodgeCallback(z80,eZ80_Instruction,CheckTimers); /* Define the palette */ for(f=0;f<16;f++) { pal[f] = RGB15(coltable[f].r,coltable[f].g,coltable[f].b); } for(f=0;f>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<