diff options
author | Ian C <ianc@noddybox.co.uk> | 2021-07-05 19:29:54 +0000 |
---|---|---|
committer | Ian C <ianc@noddybox.co.uk> | 2021-07-05 19:29:54 +0000 |
commit | 0bc192bffbf28ba5733ae955916e5ebc72f3641f (patch) | |
tree | cb68ae6e3785b2f712e3c639f687580fe75f5806 /source/spec.c | |
parent | 25a57883291bc89210edbd3670e5548a0b838913 (diff) |
Initial working version. Still todo : Tapes, Sound
Diffstat (limited to 'source/spec.c')
-rw-r--r-- | source/spec.c | 631 |
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); } |