diff options
Diffstat (limited to 'arm9/source/zx81.c')
-rw-r--r-- | arm9/source/zx81.c | 828 |
1 files changed, 828 insertions, 0 deletions
diff --git a/arm9/source/zx81.c b/arm9/source/zx81.c new file mode 100644 index 0000000..df30dd7 --- /dev/null +++ b/arm9/source/zx81.c @@ -0,0 +1,828 @@ +/* + ezx81 - X11 ZX81 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 ZX81 + +*/ +static const char ident[]="$Id$"; + +#if 0 +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "zx81.h" +#include "gfx.h" +#include "gui.h" +#include "config.h" +#include "util.h" +#include "exit.h" + +static const char ident_h[]=EZX81_ZX81H; + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + + +/* ---------------------------------------- STATICS +*/ +#define ROMLEN 0x2000 +#define ROM_SAVE 0x2fc +#define ROM_LOAD 0x347 + +#define ED_SAVE 0xf0 +#define ED_LOAD 0xf1 +#define ED_WAITKEY 0xf2 +#define ED_ENDWAITKEY 0xf3 +#define ED_PAUSE 0xf4 + +#define SLOW_TSTATES 16000 +#define FAST_TSTATES 64000 + +#define LASTK1 16421 +#define LASTK2 16422 +#define MARGIN 16424 +#define FRAMES 16436 +#define CDFLAG 16443 + +static Z80Val FRAME_TSTATES=FAST_TSTATES; + +/* The ZX81 screen and memory +*/ +static int waitkey=FALSE; +static int started=FALSE; + +#define SCR_W 256 +#define SCR_H 192 +#define TXT_W 32 +#define TXT_H 24 + +#define OFF_X (GFX_WIDTH-SCR_W)/2 +#define OFF_Y (GFX_HEIGHT-SCR_H)/2 + +static Z80Byte mem[0x10000]; + +static Z80Word RAMBOT=0; +static Z80Word RAMTOP=0; +static Z80Word RAMLEN=0; + +#define DFILE 0x400c + +#define WORD(a) (mem[a] | (Z80Word)mem[a+1]<<8) + +/* 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 +*/ +#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 */ + 0xff /* End of patch */ + }; + + static const Z80Byte load[]= + { + 0xed, ED_LOAD, /* (LOAD) */ + 0xc3, 0x07, 0x02, /* JP $0207 */ + 0xff /* End of patch */ + }; + + static const Z80Byte fast_hack[]= + { + 0xed, ED_WAITKEY, /* (START KEY WAIT) */ + 0xcb,0x46, /* L: bit 0,(hl) */ + 0x28,0xfc, /* jr z,L */ + 0xed, ED_ENDWAITKEY, /* (END KEY WAIT) */ + 0x00, /* nop */ + 0xff /* End of patch */ + }; + + static const Z80Byte kbd_hack[]= + { + 0x2a,0x25,0x40, /* ld hl,(LASTK) */ + 0xc9, /* ret */ + 0xff /* End of patch */ + }; + + static const Z80Byte pause_hack[]= + { + 0xed, ED_PAUSE, /* (PAUSE) */ + 0x00, /* nop */ + 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]; + } + + for(f=0;fast_hack[f]!=0xff;f++) + { + mem[0x4ca+f]=fast_hack[f]; + } + + for(f=0;kbd_hack[f]!=0xff;f++) + { + mem[0x2bb+f]=kbd_hack[f]; + } + + for(f=0;pause_hack[f]!=0xff;f++) + { + mem[0xf3a+f]=pause_hack[f]; + } + + /* Trust me, we have a ZX81... Honestly. + */ + mem[0x21c]=0x00; + mem[0x21d]=0x00; + + /* Remove HALTs as we don't do interrupts + */ + mem[0x0079]=0; + mem[0x02ec]=0; +} + +static char ToASCII(Z80Byte b) +{ + if (b==0) /* SPACE */ + return ' '; + + if (b==22) /* Dash (-) */ + return '-'; + + if (b==27) /* Period (.) */ + 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(Z80 *z80) +{ + const char *p=ConvertFilename(z80->DE.w); + char path[FILENAME_MAX]; + FILE *fp; + + if (strlen(p)==0) + { + GUIMessage(eMessageBox,"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(eMessageBox,"ERROR","Can't load file:\n%s",path); + return; + } + + fread(mem+0x4009,1,0x4000,fp); + fclose(fp); +} + + +static void SaveTape(Z80 *z80) +{ + const char *p=ConvertFilename(z80->DE.w); + char path[FILENAME_MAX]; + FILE *fp; + Z80Word start; + Z80Word end; + + if (strlen(p)==0) + { + GUIMessage(eMessageBox,"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(eMessageBox,"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 void PrintZX81Char(int x,int y,int code,int base) +{ + int cx,cy; + Uint32 fg,bg; + + x*=8; + y*=8; + + if (code&0x80) + { + fg=white; + bg=black; + code-=0x80; + } + else + { + fg=black; + bg=white; + } + + base=base<<8; + base+=code*8; + + for(cy=0;cy<8;cy++) + { + for(cx=0;cx<8;cx++) + { + if (mem[base]&(1<<(7-cx))) + GFXFastPlot(OFF_X+x+cx,OFF_Y+y+cy,fg); + else + GFXFastPlot(OFF_X+x+cx,OFF_Y+y+cy,bg); + } + + base++; + } +} + + +static void DrawScreen(Z80 *z80) +{ + Z80Byte *scr=mem+WORD(DFILE); + int x,y; + + x=0; + y=0; + + while(y<TXT_H) + { + scr++; + x=0; + + while((*scr!=118)&&(x<TXT_W)) + { + PrintZX81Char(x,y,*scr++,z80->I); + x++; + } + + y++; + } +} + + +static void DrawSnow(Z80 *z80) +{ + int x,y; + + x=0; + y=0; + + for(x=0;x<SCR_W;x++) + { + for(y=0;y<SCR_H;y++) + { + GFXFastPlot(OFF_X+x,OFF_Y+y,rand()&1 ? white:black); + } + } +} + + +/* Perform ZX81 housekeeping functions like updating FRAMES and updating LASTK +*/ +static void ZX81HouseKeeping(Z80 *z80) +{ + static unsigned prev_lk1,prev_lk2; + unsigned row; + unsigned lastk1; + unsigned lastk2; + + /* British ZX81 + */ + mem[MARGIN]=55; + + /* Update FRAMES + */ + if (FRAME_TSTATES==SLOW_TSTATES) + { + Z80Word frame=PEEKW(FRAMES)&0x7fff; + + if (frame) + { + frame--; + } + + POKEW(FRAMES,frame|0x8000); + } + + if (!started) + { + prev_lk1=0; + prev_lk2=0; + return; + } + + /* Update LASTK + */ + lastk1=0; + lastk2=0; + + for(row=0;row<8;row++) + { + unsigned b; + + b=(~matrix[row]&0x1f)<<1; + + if (row==0) + { + unsigned shift; + + shift=b&2; + b&=~2; + b|=(shift>>1); + } + + if (b) + { + if (b>1) + { + lastk1|=(1<<row); + } + + lastk2|=b; + } + } + + /* if ((lastk1 || lastk2) && (lastk1!=prev_lk1 || lastk2!=prev_lk2)) */ + if (lastk1 && (lastk1!=prev_lk1 || lastk2!=prev_lk2)) + { + mem[CDFLAG]|=1; + } + + prev_lk1=lastk1; + prev_lk2=lastk2; + + mem[LASTK1]=lastk1^0xff; + mem[LASTK2]=lastk2^0xff; +} + + +static int CheckTimers(Z80 *z80, Z80Val val) +{ + if (val>=FRAME_TSTATES) + { + Z80ResetCycles(z80,val-FRAME_TSTATES); + + GFXLock(); + + if (started && ((mem[CDFLAG] & 0x80) || waitkey)) + { + DrawScreen(z80); + FRAME_TSTATES=SLOW_TSTATES; + } + else + { + DrawSnow(z80); + FRAME_TSTATES=FAST_TSTATES; + } + + /* Update FRAMES (if in SLOW) and scan the keyboard. This only happens + once we've got to a decent point in the boot cycle (detected with + a valid stack pointer). + */ + if (z80->SP<0x8000) + { + ZX81HouseKeeping(z80); + } + + GFXUnlock(); + GFXEndFrame(TRUE); + GFXStartFrame(); + } + + return TRUE; +} + + +static int EDCallback(Z80 *z80, Z80Val data) +{ + Z80Word pause; + + switch((Z80Byte)data) + { + case ED_SAVE: + SaveTape(z80); + break; + + case ED_LOAD: + LoadTape(z80); + mem[CDFLAG]=0xc0; + break; + + case ED_WAITKEY: + waitkey=TRUE; + started=TRUE; + break; + + case ED_ENDWAITKEY: + waitkey=FALSE; + break; + + case ED_PAUSE: + waitkey=TRUE; + + pause=z80->BC.w; + + while(pause-- && !(mem[CDFLAG]&1)) + { + SDL_Event *e; + + e=GFXGetKey(); + + if (e) + { + ZX81KeyEvent(e); + } + + CheckTimers(z80,FRAME_TSTATES); + } + + waitkey=FALSE; + break; + + default: + break; + } + + return TRUE; +} + + +/* ---------------------------------------- EXPORTED INTERFACES +*/ +void ZX81Init(Z80 *z80) +{ + FILE *fp; + Z80Word f; + + if (!(fp=fopen(SConfig(CONF_ROMFILE),"rb"))) + { + GUIMessage(eMessageBox, + "ERROR","Failed to open ZX81 ROM\n%s",SConfig(CONF_ROMFILE)); + Exit(""); + } + + if (fread(mem,1,ROMLEN,fp)!=ROMLEN) + { + fclose(fp); + GUIMessage(eMessageBox, + "ERROR","ROM file must be %d bytes long\n",ROMLEN); + Exit(""); + } + + /* Patch the ROM + */ + RomPatch(); + Z80LodgeCallback(z80,eZ80_EDHook,EDCallback); + Z80LodgeCallback(z80,eZ80_Instruction,CheckTimers); + + /* Mirror the ROM + */ + memcpy(mem+ROMLEN,mem,ROMLEN); + + RAMBOT=0x4000; + + /* Memory size (1 or 16K) + */ + if (IConfig(CONF_MEMSIZE)==16) + RAMLEN=0x4000; + 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); + + GFXStartFrame(); +} + + +void ZX81KeyEvent(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 ZX81ReadMem(Z80 *z80, Z80Word addr) +{ + /* Hopefully by simply returning RET for ULA reads we save a lot of + ROM patching shenanigans. + */ + if (addr>0x7fff) + { + return 0xc9; + } + else + { + return mem[addr]; + } +} + + +void ZX81WriteMem(Z80 *z80, Z80Word addr, Z80Byte val) +{ + if (addr>=RAMBOT && addr<=RAMTOP) + { + mem[addr]=val; + } +} + + +Z80Byte ZX81ReadPort(Z80 *z80, Z80Word port) +{ + Z80Byte b=0; + + 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; + } + break; + + default: + break; + } + + return b; +} + + +void ZX81WritePort(Z80 *z80, Z80Word port, Z80Byte val) +{ + switch(port&0xff) + { + case 0xfd: + break; + + case 0xfe: + break; + } +} + + +Z80Byte ZX81ReadForDisassem(Z80 *z80, Z80Word addr) +{ + return mem[addr&0x7fff]; +} + + +const char *ZX81Info(Z80 *z80) +{ + static char buff[80]; + + sprintf(buff,"T-State/frame: %lu",FRAME_TSTATES); + return buff; +} + + +void ZX81Reset(Z80 *z80) +{ + int f; + + for(f=0;f<8;f++) + matrix[f]=0x1f; + + Z80ResetCycles(z80,0); + + started=FALSE; + + GFXStartFrame(); +} + +#endif + + +/* END OF FILE */ |