/* * Copyright (C) 1996-1998 Szeredi Miklos * Email: mszeredi@inf.bme.hu * * 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. See the file COPYING. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include /* size_t */ #include "zxmisc.h" #include "spperif.h" #include "z80.h" #include "snapshot.h" #include "compr.h" #include "interf.h" #include "spconf.h" /* include this at last to avoid clashes with stdio functions */ #include "helpers.h" #define COMPRESS_SAVE 1 static char quick_snap_file[]= ROCKBOX_DIR "/zxboxq.z80"; typedef struct { int isfile; int fd; unsigned len; byte *at; } SNFILE; #define sngetc(snfp) ((snfp)->isfile ? getc((snfp)->fd) : snmgetc(snfp)) static int snmgetc(SNFILE *snfp) { if(!snfp->len) return EOF; snfp->len--; return *snfp->at++; } static int snread(void *ptr, int size, SNFILE *snfp) { int i; byte *dest; if(snfp->isfile) return (int) rb->read( snfp->fd,ptr, (size_t) size); dest = (byte *) ptr; for(i = 0; snfp->len && size; i++, snfp->len--, size--) *dest++ = *snfp->at++; return i; } /* These structures are taken from 'spconv' by Henk de Groot */ struct sna_s { byte i; byte lbk; byte hbk; byte ebk; byte dbk; byte cbk; byte bbk; byte fbk; byte abk; byte l; byte h; byte e; byte d; byte c; byte b; byte iyl; byte iyh; byte ixl; byte ixh; byte iff2; byte r; byte f; byte a; byte spl; byte sph; byte im; byte border; }; #define sna_size 27 /* sizeof(struct sna_s)=27 */ struct z80_1_s { byte a; /*00*/ byte f; /*01*/ byte c; /*02*/ byte b; /*03*/ byte l; /*04*/ byte h; /*05*/ byte pcl; /*06*/ byte pch; /*07*/ byte spl; /*08*/ byte sph; /*09*/ byte i; /*0A*/ byte r; /*0B*/ byte data; /*0C*/ byte e; /*0D*/ byte d; /*0E*/ byte cbk; /*0F*/ byte bbk; /*10*/ byte ebk; /*11*/ byte dbk; /*12*/ byte lbk; /*13*/ byte hbk; /*14*/ byte abk; /*15*/ byte fbk; /*16*/ byte iyl; /*17*/ byte iyh; /*18*/ byte ixl; /*19*/ byte ixh; /*1A*/ byte iff1; /*1B*/ byte iff2; /*1C*/ byte im; /*1D*/ }; #define z80_145_size 0x1e /* length of z80 V1.45 header */ struct z80_2_s { /* Extended 2.01 and 3.0 header, flagged with PC=0 */ byte h2_len_l; /*1E*/ byte h2_len_h; /*1F*/ byte n_pcl; /*20*/ byte n_pch; /*21*/ byte hardware; /*22*/ byte samram; /*23*/ byte if1_paged; /*24*/ byte r_ldir_emu; /*25*/ byte last_out; /*26*/ byte sound_reg[16]; /*27*/ /* Continues with extended 3.0 header, but this part is not used anyway */ }; #define z80_201_ext_size 23 /* length of extended z80 V2.01 header */ #define z80_300_ext_size 54 /* length of extended z80 V3.0 header */ struct z80_page_s { byte blklen_l; /*00*/ byte blklen_h; /*01*/ byte page_num; /*02*/ }; #define z80_pg_size 3 /* sizeof(struct z80_page_s)=3 */ static int savfd; static int memptr; int compr_read_byte(void) { if(memptr < 0x10000) return z80_proc.mem[memptr++]; else return -1; } void compr_put_byte(int i) { putc(i, savfd); } #define STORE_NORMAL_REGS(head) \ head.f = RF; /* F reg */ \ head.a = RA; /* A reg */ \ head.b = RB; /* B reg */ \ head.c = RC; /* C reg */ \ head.d = RD; /* D reg */ \ head.e = RE; /* E reg */ \ head.h = RH; /* H reg */ \ head.l = RL; /* L reg */ \ head.fbk = FBK; /* F' reg */ \ head.abk = ABK; /* A' reg */ \ head.bbk = BBK; /* B' reg */ \ head.cbk = CBK; /* C' reg */ \ head.dbk = DBK; /* D' reg */ \ head.ebk = EBK; /* E' reg */ \ head.hbk = HBK; /* H' reg */ \ head.lbk = LBK; /* L' reg */ \ head.iyh = YH; /* IY reg */ \ head.iyl = YL; \ head.ixh = XH; /* IX reg */ \ head.ixl = XL #define LOAD_NORMAL_REGS(head) \ RF = head.f; /* F reg */ \ RA = head.a; /* A reg */ \ RB = head.b; /* B reg */ \ RC = head.c; /* C reg */ \ RD = head.d; /* D reg */ \ RE = head.e; /* E reg */ \ RH = head.h; /* H reg */ \ RL = head.l; /* L reg */ \ FBK = head.fbk; /* F' reg */ \ ABK = head.abk; /* A' reg */ \ BBK = head.bbk; /* B' reg */ \ CBK = head.cbk; /* C' reg */ \ DBK = head.dbk; /* D' reg */ \ EBK = head.ebk; /* E' reg */ \ HBK = head.hbk; /* H' reg */ \ LBK = head.lbk; /* L' reg */ \ YH = head.iyh; /* IY reg */ \ YL = head.iyl; \ XH = head.ixh; /* IX reg */ \ XL = head.ixl static void snsh_z80_save(int fd) { struct z80_1_s z80; int to_comp = COMPRESS_SAVE; STORE_NORMAL_REGS(z80); z80.i = RI; /* I reg */ z80.r = RR; /* R reg */ z80.sph = SPH; /* SP reg */ z80.spl = SPL; z80.pch = PCH; /* PC reg */ z80.pcl = PCL; z80.iff1 = z80_proc.iff1; /* iff1 */ z80.iff2 = z80_proc.iff2; /* iff2 */ z80.im = (z80_proc.it_mode & 0x03) | 0x60; /* Bit 0-1: Interrupt mode (0, 1 or 2) Bit 2 : 1=Issue 2 emulation Bit 3 : 1=Double interrupt frequency Bit 4-5: 1=High video synchronisation 3=Low video synchronisation 0,2=Normal Bit 6-7: 0=Cursor/Protek/AGF joystick 1=Kempston joystick 2=Sinclair 1 joystick 3=Sinclair 2 joystick */ z80.data = ((RR >> 7) & 0x01) | ((z80_proc.ula_outport & 0x07) << 1) | (to_comp ? 0x20 : 0); /* Bit 0 : Bit 7 of the R-register Bit 1-3: Border colour Bit 4 : 1=Basic SamRom switched in Bit 5 : 1=Block of data is compressed Bit 6-7: No meaning */ rb->write(fd,&z80,z80_145_size); if(!to_comp) rb->write(fd,z80_proc.mem + 0x4000,0xC000); else { memptr = 0x4000; savfd = fd; compr(); } } static void snsh_sna_save(int fd) { struct sna_s sna; byte saves1, saves2; STORE_NORMAL_REGS(sna); sna.i = RI; /* I reg */ sna.r = RR; /* R reg */ sna.border = z80_proc.ula_outport & 0x07; SP -= 2; sna.sph = SPH; /* SP reg */ sna.spl = SPL; saves1 = z80_proc.mem[SP]; saves2 = z80_proc.mem[(dbyte)(SP+1)]; if(SP >= 0x4000) { z80_proc.mem[SP] = PCL; if(SP < 0xFFFF) z80_proc.mem[SP+1] = PCH; } sna.iff2 = z80_proc.iff2 ? 0xff : 0x00; /* iff2 */ sna.im = z80_proc.it_mode & 0x03; rb->write(fd,&sna, sna_size); rb->write(fd,z80_proc.mem + 0x4000, 0xC000); if(SP > 0x4000) { z80_proc.mem[SP] = saves1; if(SP < 0xFFFF) z80_proc.mem[SP+1] = saves2; } SP += 2; } #define GET_DATA(c) { \ if(!datalen) break; \ c = sngetc(fp); \ if(c == EOF) break; \ if(datalen > 0) datalen--; \ } static void read_compressed_data(SNFILE *fp, byte *start, unsigned size, long datalen) { int j; int times, last_ed, ch; byte *p, *end; p = start; end = start+size; last_ed = 0; while(p < end) { GET_DATA(ch); if(ch != 0xED) { last_ed = 0; *p++ = ch; } else { if(last_ed) { last_ed = 0; p--; GET_DATA(times); if(times == 0) break; GET_DATA(ch); if(p + times > end) { put_msg("Warning: Repeat parameter too large in snapshot"); times = (int) ((intptr_t) end - (intptr_t) p); } for(j = 0; j < times; j++) *p++ = ch; } else { last_ed = 1; *p++ = 0xED; } } } if(datalen < 0) { if(sngetc(fp) != 0 || sngetc(fp) != 0xED || sngetc(fp) != 0xED || sngetc(fp) != 0) put_msg("Warning: Illegal ending of snapshot"); } if(datalen > 0) { while(datalen) { if(sngetc(fp) == EOF) break; datalen--; } put_msg("Warning: Page too long in snapshot"); } if(p < end) put_msg("Warning: Page too short in snapshot"); } static int read_header(void *p, int size, SNFILE *fp) { int res; res = snread(p, size, fp); if(res != size) { put_msg("Error, End Of File in snapshot header"); return 0; } return 1; } static int read_z80_page(SNFILE *fp) { struct z80_page_s page; unsigned datalen; unsigned pos = 0; int validpage; int res; res = snread(&page, z80_pg_size, fp); if(res == 0) return 0; if(res != z80_pg_size) { put_msg("Error, End Of File in page header"); return 0; } datalen = (page.blklen_h << 8) | page.blklen_l; validpage = 1; switch(page.page_num) { case 4: pos = 0x8000; break; case 5: pos = 0xC000; break; case 8: pos = 0x4000; break; default: validpage = 0; while(datalen) { if(sngetc(fp) == EOF) { put_msg("Warning: Page too short in snapshot"); break; } datalen--; } } if(validpage) read_compressed_data(fp, z80_proc.mem+pos, 0x4000, (long) datalen); return 1; } static void snsh_z80_load(SNFILE *fp) { struct z80_1_s z80; if(!read_header(&z80, z80_145_size, fp)) return; if(z80.pch == 0 && z80.pcl == 0) { struct z80_2_s z80_2; int ext_size, rem; if(!read_header(&z80_2, 2, fp)) return; ext_size = z80_2.h2_len_l | (z80_2.h2_len_h << 8); if(ext_size < z80_201_ext_size) { put_msg("Error in Z80 header"); return; } if(!read_header(&z80_2.n_pcl, z80_201_ext_size, fp)) return; rem = ext_size - z80_201_ext_size; for(; rem; rem--) sngetc(fp); if(z80_2.hardware >= 3 && (ext_size == z80_201_ext_size || z80_2.hardware >= 4)) { put_msg("Can't load non 48k snapshot"); return; } if(z80_2.if1_paged) { put_msg("Can't load snapshot: IF1 roma paged in"); return; } PCH = z80_2.n_pch; PCL = z80_2.n_pcl; while(read_z80_page(fp)); } else { if(z80.data == 0xFF) z80.data = 1; if(z80.data & 0x20) read_compressed_data(fp, z80_proc.mem + 0x4000, 0xC000, -1); else { if(snread(z80_proc.mem + 0x4000, 0xC000, fp) != 0xC000) put_msg("Warning: Snapshot file too short (z80)"); else if(sngetc(fp) != EOF) put_msg("Warning: Snapshot file too long"); } PCH = z80.pch; PCL = z80.pcl; } LOAD_NORMAL_REGS(z80); RI = z80.i; /* I reg */ RR = (z80.r & 0x7F) | ((z80.data & 0x01) << 7); /* R reg */ SPH = z80.sph; /* SP reg */ SPL = z80.spl; z80_proc.ula_outport = (z80_proc.ula_outport & ~(0x07)) | ((z80.data >> 1) & 0x07); /* Bit 0 : Bit 7 of the R-register Bit 1-3: Border colour Bit 4 : 1=Basic SamRom switched in Bit 5 : 1=Block of data is compressed Bit 6-7: No meaning */ z80_proc.iff1 = z80.iff1 ? 1 : 0; z80_proc.iff2 = z80.iff2 ? 1 : 0; z80_proc.it_mode = z80.im & 0x03; /* Bit 0-1: Interrupt mode (0, 1 or 2) Bit 2 : 1=Issue 2 emulation Bit 3 : 1=Double interrupt frequency Bit 4-5: 1=High video synchronisation 3=Low video synchronisation 0,2=Normal Bit 6-7: 0=Cursor/Protek/AGF joystick 1=Kempston joystick 2=Sinclair 1 joystick 3=Sinclair 2 joystick */ z80_proc.haltstate = 0; sp_init_screen_mark(); } static void snsh_sna_load(SNFILE *fp) { struct sna_s sna; if(!read_header(&sna, sna_size, fp)) return; if(snread(z80_proc.mem+0x4000, 0xC000, fp) != 0xC000) put_msg("Warning: Snapshot file too short (sna)"); else if(sngetc(fp) != EOF) put_msg("Warning: Snapshot file too long"); LOAD_NORMAL_REGS(sna); RI = sna.i; /* I reg */ RR = sna.r; /* R reg */ z80_proc.ula_outport = (z80_proc.ula_outport & ~(0x07)) | (sna.border & 0x07); SPH = sna.sph; /* SP reg */ SPL = sna.spl; PCL = z80_proc.mem[SP]; if(SP >= 0x4000) z80_proc.mem[SP] = 0; SP++; PCH = z80_proc.mem[SP]; if(SP >= 0x4000) z80_proc.mem[SP] = 0; SP++; z80_proc.iff1 = z80_proc.iff2 = sna.iff2 ? 1 : 0; z80_proc.it_mode = sna.im & 0x03; z80_proc.haltstate = 0; sp_init_screen_mark(); } static void save_snapshot_file_type(char *name, int type) { int snsh; snsh = rb->open(name, O_WRONLY); if(snsh < 0) { snsh = rb->creat(name, 0666); if(snsh < 0) { put_msg("Could not create snapshot file"); return; } } if(type == SN_SNA) snsh_sna_save(snsh); else if(type == SN_Z80) snsh_z80_save(snsh); rb->close(snsh); } void save_snapshot_file(char *name) { int type; rb->strlcpy(filenamebuf, name, MAXFILENAME-10 + 1); type = SN_Z80; if(check_ext(filenamebuf, "z80")) type = SN_Z80; else if(check_ext(filenamebuf, "sna")) type = SN_SNA; else { add_extension(filenamebuf, "z80"); type = SN_Z80; } save_snapshot_file_type(filenamebuf, type); char msgbuf [MAXFILENAME]; rb->snprintf(msgbuf,MAXFILENAME, "Saved snapshot to file %s", filenamebuf); put_msg(msgbuf); } void save_quick_snapshot(void) { save_snapshot_file_type(quick_snap_file, SN_Z80); } void save_snapshot(void) { char name[MAXFILENAME]; name[0]='/'; name[1]='\0'; put_msg("Enter name of snapshot file to save:"); if (!rb->kbd_input((char*)&name, sizeof name)) save_snapshot_file(&name[0]); } void load_snapshot_file_type(char *name, int type) { int filetype = FT_SNAPSHOT; int snsh; SNFILE snfil; rb->strlcpy(filenamebuf, name, MAXFILENAME-10 + 1); spcf_find_file_type(filenamebuf, &filetype, &type); if(type < 0) type = SN_Z80; snsh = rb->open(filenamebuf, O_RDONLY); if(snsh < 0) { #ifndef USE_GRAY rb->splashf(HZ, "Could not open snapshot file `%s'",filenamebuf); #endif return; } snfil.isfile = 1; snfil.fd = snsh; if(type == SN_SNA) snsh_sna_load(&snfil); else if(type == SN_Z80) snsh_z80_load(&snfil); rb->close(snsh); } void snsh_z80_load_intern(byte *p, unsigned len) { SNFILE snfil; snfil.isfile = 0; snfil.at = p; snfil.len = len; snsh_z80_load(&snfil); } void load_quick_snapshot(void) { int qsnap; qsnap = rb->open(quick_snap_file,O_RDONLY); if(qsnap < 0) { put_msg("No quick snapshot saved yet"); return; } else rb->close ( qsnap ); load_snapshot_file_type(quick_snap_file, SN_Z80); } void load_snapshot(void) { char *name; put_msg("Enter name of snapshot file to load:"); name = spif_get_filename(); if(name == NULL) return; load_snapshot_file_type(name, -1); }