/* * SPCONV - ZX-Spectrum 48K snapshot converter * * Public Domain software * * Author: Henk de Groot */ #if defined(__STDC__) || defined(__TURBOC__) #include #else #include #endif #ifdef __TURBOC__ #include #else #include #endif #include #include #include #include #include #ifdef __TURBOC__ #include #else #include #endif #ifndef O_BINARY #define O_BINARY 0 #endif #include "spconv.h" struct sna_s sna; struct vga_s vga; struct z80_s z80; struct prg_s prg; struct ach_s ach; struct kgb_s kgb; /* extra space just in case the Z80 decompress fails due to a corrupt image */ unsigned char image[IMSIZE+0x100]; unsigned int z80_size; int intype; int outtype; /* Path names are limited to 80 characters under MSDOS so this sould be */ /* enough... If you are using UNIX than you may have to make this array */ /* larger. */ char my_directory[120]; int main(int argc,char *argv[]) { char *p; struct stat status; const char * fromstring; const char * tostring; char * outfile; long file_size; #ifdef __TURBOC__ /* for Trubo-C: open all files in binary mode */ extern int _fmode; _fmode = O_BINARY; #endif if(argc != 3) USAG_usage(); if((strchr(argv[1],'*')!=NULL) || (strrchr(argv[1],'?')!=NULL) || (strchr(argv[2],'*')!=NULL) || (strrchr(argv[2],'?')!=NULL)) { fprintf(stderr,"This program can't handle wildcards, sorry!\n"); return EXIT_USAGE; } /* * Find the directory path where this program is started from. * This will be needed for finding the spectrum-rom. Some formats * need the spectrum-rom for proper conversion. */ strcpy(my_directory,argv[0]); if(strrchr(my_directory,'\\')!=NULL) { *strrchr(my_directory,'\\')='\0'; } else if(strrchr(my_directory,'/')!=NULL) { *strrchr(my_directory,'/')='\0'; } else if(strrchr(my_directory,':')!=NULL) { *strrchr(my_directory,':')='\0'; } else { my_directory[0]='\\'; my_directory[1]='\0'; } /* * Check if the input file exists and fetch the file lenght of * the file in the mean time. Two for the prise of one... */ if(stat(argv[1],&status)<0) { perror(argv[1]); return EXIT_FILE_ERROR; } file_size = status.st_size; /* * recognize input type on filename: * * .SNA -> SNA file (used in JPP) * .SP -> SP file (was VGASPEC) * .Z80 -> Z80 file * .PRG -> PRG file * .ACH -> ACH file (from archimedes emulator) * .ZX -> KGB file (from KGB emulator) * other -> if exact 48+header -> raw file * otherwise unknown */ fromstring = DTYP_determine_type(argv[1],&intype); if (intype == UNKNOWN) { if (file_size == (hdr_size+IMSIZE)) { fromstring="RAW"; intype=RAW; } else { fprintf(stderr,"Unknown input file format. Must be a valid .SNA, .SP, .Z80, .PRG, .ACH or\n"); fprintf(stderr,".ZX file, or a Raw file\n"); return EXIT_FILE_ERROR; } } /* * recognize output type on filename: * * .SNA -> SNA file * .SP -> SP file (was VGASPEC) * .Z80 -> Z80 file * .PRG -> PRG file * .ACH -> ACH file (from archimedes emulator) * .ZX -> KGB file (from KGB emulator) * otherwise unknown */ tostring = DTYP_determine_type(argv[2],&outtype); if(outtype==UNKNOWN) { fprintf(stderr,"Unknown output file format. Must be a .SNA, .SP, .Z80, .PRG, .ACH or .ZX file\n"); return EXIT_FILE_ERROR; } /* * if argv[2] only contains the suffix then use the prefix of * argv[1]; */ if(argv[2][0]=='.') { outfile=malloc(strlen(argv[1])+strlen(argv[2])+1); strcpy(outfile,argv[1]); /* copy prefix */ p=strrchr(outfile,'.'); if(p!=NULL) *p='\0'; /* put end of string at position of '.' */ strcat(outfile,argv[2]); /* append suffix */ } else { outfile=malloc(strlen(argv[2])+1); strcpy(outfile,argv[2]); } /* * Special converion between versions of the same type */ if(intype == outtype) return DIRC_direct_convert(intype, argv[1], outfile); /* * General conversion from one type to another */ printf("Converting %s from %s to %s\n",argv[1],fromstring,tostring); /* * convert input_file to SNA */ switch(intype) { case SNA: if (file_size == (sna_size+IMSIZE)) { RSNA_read_sna(argv[1]); } break; case SP: if ((file_size == (vga_size+IMSIZE))) { RVGA_read_vgaspec(argv[1]); VGSN_vgaspec_to_sna(); } else if ((file_size == (vga_size+IMSIZE-6))) { ROVG_read_old_vgaspec(argv[1]); VGSN_vgaspec_to_sna(); } break; case RAW: RRAW_read_raw(argv[1]); RASN_raw_to_sna(); break; case Z80: RZ80_read_z80(argv[1]); Z8SN_z80_to_sna(); break; case PRG: if(file_size != (prg_size+IMSIZE)) { printf("Warning: the image part of %s is not exactly 48k!\n",argv[1]); printf(" Converting anyway, the converted file may not work\n"); } RPRG_read_prg(argv[1]); PRSN_prg_to_sna(); break; case ACH: if(file_size == (ach_size+16384L+IMSIZE)) { RACH_read_ach(argv[1]); ACSN_ach_to_sna(); } break; case KGB: if(file_size == (132L+IMSIZE+kgb_size)) { RKGB_read_kgb(argv[1]); KGSN_kgb_to_sna(); } break; default: printf("Unrecognized input file type, can't convert\n"); return EXIT_FILE_ERROR; } /* * convert internal SNA format to output file */ switch(outtype) { case SNA: WSNA_write_sna(outfile); break; case SP: SNVG_sna_to_vgaspec(); WVGA_write_vgaspec(outfile); break; case Z80: SNZ8_sna_to_z80(); WZ80_write_z80(outfile); break; case PRG: SNPR_sna_to_prg(outfile); WPRG_write_prg(outfile); break; case ACH: SNAC_sna_to_ach(); WACH_write_ach(outfile); break; case KGB: SNKG_sna_to_kgb(); WKGB_write_kgb(outfile); break; default: printf("Unrecognized output file type, can't convert\n"); return EXIT_FILE_ERROR; } /* * That's it, simple isn't it? */ return EXIT_OK; } /* SPECIAL FUNCTIONS */ /* Determine file type and return both type and type-name */ const char * DTYP_determine_type(char * filename, int * type) { const char *p; char ext[5]; int i; /* * recognize type on filename: * * .SNA -> SNA file * .SP -> SP file (was VGASPEC) * .Z80 -> Z80 file * .PRG -> PRG file * .ACH -> ACH file (from archimedes emulator) * .ZX -> KGB file (from KGB emulator) * otherwise unknown */ *type=UNKNOWN; /* default if all ifs below fail */ p=strrchr(filename,'.'); if(p==NULL) p=filename; /* not found, set at begin of string */ /* Take extension and convert to uppercase */ for(i = 0; i < 4; i++) { if(*p != '\0') { ext[i] = toupper(*p); p++; } else { ext[i] = '\0'; } } ext[4] = '\0'; /* termination at least on the fifth char */ if(strcmp(ext,".SNA")==0) { *type=SNA; return ".SNA"; } if(strcmp(ext,".Z80")==0) { *type=Z80; return ".Z80"; } if(strcmp(ext,".SP")==0) { *type=SP; return ".SP"; } if(strcmp(ext,".PRG")==0) { *type=PRG; return ".PRG"; } if(strcmp(ext,".ACH")==0) { *type=ACH; return ".ACH"; } if(strcmp(ext,".ZX")==0) { *type=KGB; return ".ZX"; } /* unknown type, return question-mark */ return "?"; } /* Special converion between versions of the same type */ int DIRC_direct_convert(int type, char * infile, char * outfile) { struct stat status; long file_size; /* * Special conversion between versions of the same type. * These conversion don't use the intermediate 'sna' format * to preserve as much information as possible. * * This currently only applies to .SP and the .Z80 formats. * * The .SP formats will convert from the New to the Old format or * from the Old to the New format, controled by the current format * of the input. * * The .Z80 formats will convert to the version 1.45 .Z80 format * since version 1.45 .Z80 files can be read by both old and new * Z80-emulators. */ /* check if it is for the SP format */ if(type == SP) { if(stat(infile,&status)<0) { perror(infile); return EXIT_FILE_ERROR; } file_size = status.st_size; if((file_size == (vga_size+IMSIZE))) { printf("Converting %s from new .SP format to old .SP format.\n",infile); RVGA_read_vgaspec(infile); WOVG_write_old_vgaspec(outfile); return EXIT_OK; } if((file_size == (vga_size+IMSIZE-6))) { RVGH_read_vgaspec_header(infile); if((vga.S=='S')&&(vga.P=='P')) { fprintf(stderr,"Invalid input file format. This could be a new syle .SP file with\n"); fprintf(stderr,"an image of another length than 48Kb. This kind of .SP files cannot\n"); fprintf(stderr,"be converted. All other file formats (including the old .SP format)\n"); fprintf(stderr,"contain images of 48Kb length.\n"); return EXIT_FILE_ERROR; } printf("Converting %s from old .SP format to new .SP format.\n",infile); ROVG_read_old_vgaspec(infile); /* These values are fixed for a full 48K .SP file */ vga.S='S'; vga.P='P'; vga.len_l=0x00; vga.len_h=0xC0; vga.start_l=0x00; vga.start_h=0x40; WVGA_write_vgaspec(outfile); return EXIT_OK; } else { RVGH_read_vgaspec_header(infile); if((vga.S=='S')&&(vga.P=='P')) { fprintf(stderr,"Invalid input file format. This could be a new syle .SP file with\n"); fprintf(stderr,"an image of another length than 48Kb. This kind of .SP files cannot\n"); fprintf(stderr,"be converted. All other file formats (including the old .SP format)\n"); fprintf(stderr,"contain images of 48Kb length.\n"); } else { fprintf(stderr,"Unknown input file format. Must be a valid .SNA, .SP, .Z80, .PRG, .ACH, .ZX or RAW file\n"); } return EXIT_FILE_ERROR; } } if(type == Z80) { printf("Converting %s from .Z80 to .Z80 (output in version 1.45 format)\n",infile); RZ80_read_z80(infile); WZ80_write_z80(outfile); return EXIT_OK; } /* If we get here than we had no special handling for this type */ /* return an error in that case... */ fprintf(stderr,"Input and output file format are the same. "); fprintf(stderr,"What you try to do\n"); fprintf(stderr,"is handled much better by the MSDOS \"COPY\" "); fprintf(stderr,"command!\n"); return EXIT_USAGE; } /* ERROR HANDLING FUNCTIONS */ /* Print Usage and exit */ void USAG_usage() { fprintf(stderr,"SPCONV version 1.07 - %s\n\n",__DATE__); fprintf(stderr,"Usage: spconv \n\n"); fprintf(stderr,"Source must be a valid .SNA, .SP, .Z80, .PRG, .ACH, .ZX or RAW file.\n"); fprintf(stderr,"Target must be a .SNA, .SP, .Z80, .PRG, .ACH or .ZX file.\n\n"); fprintf(stderr,"If the second parameter contains only a suffix, the prefix\n"); fprintf(stderr,"of the input file will be used (i.e. 'spconv file.sna .z80')\n\n"); fprintf(stderr,"Output .SP files are in the new format, .Z80 files are compressed\n"); fprintf(stderr,"in the version 1.45 .Z80 format\n\n"); fprintf(stderr,"If and are .SP files, convertion from old\n"); fprintf(stderr,"to new format or from new to old format will be performed.\n"); fprintf(stderr,"If and are .Z80 files, convertion to\n"); fprintf(stderr,"the version 1.45 format will be performed.\n"); fprintf(stderr,"If and are of the same type an error message\n"); fprintf(stderr,"will be generated (unless they are both .SP or .Z80 files)\n"); fprintf(stderr,"\n\nPublic Domain, H. de Groot 1994\n\n"); exit(EXIT_USAGE); } void RERR_read_error(char * s, FILE * fd) { perror(s); if(fd != NULL) fclose(fd); exit(EXIT_READ_ERROR); } void WERR_write_error(char * s, FILE * fd) { perror(s); if(fd != NULL) fclose(fd); exit(EXIT_WRITE_ERROR); } /* I/O FUNCTIONS */ /* GENERIC I/O - READ/WRITE SNAPSHOT */ void RGEN_read_generic(char * s, void * header, size_t h_size) { FILE * fd; fd=fopen(s,"r"); if(fd == NULL) RERR_read_error(s, fd); if(fread(header, h_size, 1, fd)!=1) RERR_read_error(s, fd); if(fread(image, (size_t) IMSIZE, 1, fd)!=1) RERR_read_error(s, fd); fclose(fd); } void WGEN_write_generic(char * s, void * header, size_t h_size) { FILE * fd; unlink(s); fd=fopen(s,"w"); if(fd == NULL) WERR_write_error(s, fd); if(fwrite(header, h_size, 1, fd)!=1) WERR_write_error(s, fd); if(fwrite(image, (size_t) IMSIZE, 1, fd)!=1) WERR_write_error(s, fd); fclose(fd); } /* SPECIFIC I/O - READ/WRITE .SNA IMAGE */ void RSNA_read_sna(char * s) { RGEN_read_generic(s, (void *) &sna, sna_size); } void WSNA_write_sna(char * s) { WGEN_write_generic(s, (void *) &sna, sna_size); } /* SPECIFIC I/O - READ NEW .SP HEADER */ void RVGH_read_vgaspec_header(char * s) { FILE * fd; fd=fopen(s,"r"); if(fd == NULL) RERR_read_error(s, fd); if(fread(&vga,vga_size,1,fd)!=1) RERR_read_error(s, fd); fclose(fd); } /* SPECIFIC I/O - READ/WRITE NEW .SP IMAGE */ void RVGA_read_vgaspec(char * s) { RGEN_read_generic(s, (void *) &vga, vga_size); } void WVGA_write_vgaspec(char * s) { WGEN_write_generic(s, (void *) &vga, vga_size); } /* SPECIFIC I/O - READ/WRITE OLD .SP IMAGE */ void ROVG_read_old_vgaspec(char * s) { RGEN_read_generic(s, (void *)(((char *)&vga)+6), (unsigned int) (vga_size-6)); } void WOVG_write_old_vgaspec(char * s) { WGEN_write_generic(s, (void *)(((char *)&vga)+6), (unsigned int) (vga_size-6)); } /* SPECIFIC I/O - READ RAW IMAGE */ void RRAW_read_raw(char * s) { int i; FILE * fd; fd=fopen(s,"r"); if(fd == NULL) RERR_read_error(s, fd); if(fread(&h, hdr_size, 1, fd)!=1) RERR_read_error(s, fd); /* check if the image was saved the correct way */ for(i=0;i<9;i++) { if(h.in[i]!=expect[i]) { fprintf(stderr,"Header of spectum image not ok, "); fprintf(stderr,"Spectrum image should be saved with:\n"); fprintf(stderr,"SAVE *\"b\"CODE 16384,49152"); fclose(fd); exit(EXIT_FILE_ERROR); } } if(fread(image, (size_t) IMSIZE, 1, fd)!=1) RERR_read_error(s, fd); fclose(fd); } /* SPECIFIC I/O - READ/WRITE .Z80 IMAGE */ void RZ80_read_z80(char * s) { FILE * fd; fd=fopen(s,"r"); if(fd == NULL) RERR_read_error(s, fd); /* read old header part */ if(fread(&z80,z80_145_size,1,fd)!=1) RERR_read_error(s, fd); /* check for 2.01 format */ if((z80.pch == (unsigned char) 0) && (z80.pcl == (unsigned char) 0)) { /* 2.01 or better format, check if we can use this */ unsigned char *p; p = (unsigned char *) &z80; /* read an aditional 2 bytes */ if(fread(&p[z80_145_size],2,1,fd)!=1) RERR_read_error(s, fd); if((p[z80_145_size] != (unsigned char) 23) && (p[z80_145_size+1] != (unsigned char) 0)) { fprintf(stderr,"%s seems to be a new type Z80 file!\n",s); fprintf(stderr,"This program can only read files up to version 2.01 format\n"); fclose(fd); exit(EXIT_FILE_ERROR); } /* we passed this test, read the 23 byte long extra header */ if(fread(&p[z80_145_size+2],23,1,fd)!=1) RERR_read_error(s, fd); /* we got a complete header now. */ /* check the type of the file... */ if((unsigned int) z80.hardware >= 3) { fprintf(stderr,"%s is not a 48K Spectrum Z80 file, can't convert!\n",s); fclose(fd); exit(EXIT_FILE_ERROR); } /* check if the interface-1 rom was paged in */ if(z80.if1_paged != (unsigned char) 0) { fprintf(stderr,"%s has interface-1 rom paged in, can't convert!\n",s); fclose(fd); exit(EXIT_FILE_ERROR); } /* all fine up till now, put PC back to the old place and reset the */ /* compressed memory bit, the z80_read_page function decompresses. */ z80.pch = z80.n_pch; z80.pcl = z80.n_pcl; z80.data = z80.data & ~0x20; /* reset compressed mode bit to be sure */ while(RDPG_z80_read_page(s, fd)!=0); } else { /* The file is in version 1.45 format. */ /* Read and decompress if the image is compressed, */ /* just read if it is not compressed at all... */ if((z80.data & 0x20)!=0) { Z80D_z80_decompress(fd,0,(unsigned int) IMSIZE); z80.data = z80.data & ~0x20; /* reset compressed mode bit to be sure */ } else { if(fread(image, (size_t) IMSIZE, 1, fd)!=1) RERR_read_error(s, fd); } } fclose(fd); } void WZ80_write_z80(char * s) { FILE * fd; /* Try to compress the data */ z80.data=z80.data | Z80C_z80_compress(); unlink(s); fd=fopen(s,"w"); if(fd == NULL) WERR_write_error(s, fd); if(fwrite(&z80, z80_145_size, 1, fd)!=1) WERR_write_error(s, fd); if(fwrite(image, (size_t) z80_size, 1, fd)!=1) WERR_write_error(s, fd); fclose(fd); } /* SPECIFIC I/O - READ/WRITE .PRG IMAGE */ void RPRG_read_prg(char * s) { RGEN_read_generic(s, (void *) &prg, prg_size); } void WPRG_write_prg(char * s) { WGEN_write_generic(s, (void *) &prg, prg_size); } /* SPECIFIC I/O - READ/WRITE .ACH IMAGE */ void RACH_read_ach(char * s) { FILE * fd; fd=fopen(s,"r"); if(fd == NULL) RERR_read_error(s, fd); if(fread(&ach,ach_size,1,fd)!=1) RERR_read_error(s, fd); /* fseek over the 16K ram area */ if(fseek(fd,16384L,SEEK_CUR)!=0) RERR_read_error(s, fd); if(fread(image, (size_t) IMSIZE, 1, fd)!=1) RERR_read_error(s, fd); fclose(fd); } void WACH_write_ach(char * s) { char buffer[1024]; char *p; int i; struct stat status; const char * rom; FILE * fd; long file_size; /* clean buffer first */ p=(char *) buffer; for(i=0; i < 1024; i++) p[i]='\0'; unlink(s); fd=fopen(s,"w"); if(fd == NULL) WERR_write_error(s, fd); if(fwrite(&ach,ach_size,1,fd)!=1) WERR_write_error(s, fd); strcat(my_directory,"\\spectrum.rom"); rom=NULL; if(stat("spectrum.rom",&status)>=0) { rom="spectrum.rom"; } else if (stat(my_directory,&status)>=0) { rom=(const char *) my_directory; } file_size = status.st_size; if(rom==NULL) { printf("Warning: The file \"spectrum.rom\" needed for proper conversion to the .ach\n"); printf(" format could not be located, Converting anyway. The ROM space\n"); printf(" will be filled with 0 bytes, the converted file may not work\n"); /* write the 16K ram area as zero's */ for(i=0; i < 16; i++) { if(fwrite(buffer,1024,1,fd)!=1) WERR_write_error(s, fd); } } else { if (file_size != 16384) { printf("Warning: The file \"spectrum.rom\" needed for proper conversion to the .ach\n"); printf(" format has a wrong size (not 16K). Converting anyway. The ROM space\n"); printf(" will be filled with 0 bytes, the converted file may not work\n"); /* copy the 16K ROM area */ for(i=0; i < 16; i++) { if(fwrite(buffer,1024,1,fd)!=1) WERR_write_error(s, fd); } } else { FILE * fd_specrom; printf("Using Spectrum ROM: %s\n",rom); fd_specrom=fopen(rom,"r"); if(fd_specrom==NULL) { perror(rom); fclose(fd); exit(EXIT_WRITE_ERROR); } for(i=0; i < 16; i++) { if(fread(buffer,1024,1,fd_specrom)!=1) { perror(rom); fclose(fd_specrom); fclose(fd); exit(EXIT_WRITE_ERROR); } if(fwrite(buffer,1024,1,fd)!=1) { perror(s); fclose(fd_specrom); fclose(fd); exit(EXIT_WRITE_ERROR); } } fclose(fd_specrom); } } if(fwrite(image, (size_t) IMSIZE, 1, fd)!=1) WERR_write_error(s, fd); fclose(fd); } /* SPECIFIC I/O - READ/WRITE .ZX IMAGE */ void RKGB_read_kgb(char * s) { FILE * fd; fd=fopen(s,"r"); if(fd == NULL) RERR_read_error(s, fd); /* fseek over the first 132 bytes */ if(fseek(fd,132L,SEEK_CUR)!=0) RERR_read_error(s, fd); if(fread(image, (size_t) IMSIZE, 1, fd)!=1) RERR_read_error(s, fd); if(fread(&kgb,kgb_size,1,fd)!=1) RERR_read_error(s,fd); fclose(fd); } void WKGB_write_kgb(char * s) { char buffer[132]; char *p; int i; struct stat status; const char * rom; FILE * fd; long file_size; /* clean buffer first */ p=(char *) buffer; for(i=0; i < 132; i++) p[i]='\0'; unlink(s); fd=fopen(s,"w"); if(fd == NULL) WERR_write_error(s, fd); strcat(my_directory,"\\spectrum.rom"); rom=NULL; if(stat("spectrum.rom",&status)>=0) { rom="spectrum.rom"; } else if (stat(my_directory,&status)>=0) { rom=(const char *) my_directory; } file_size = status.st_size; if(rom==NULL) { printf("Warning: The file \"spectrum.rom\" needed for proper conversion to the .zx\n"); printf(" format could not be located, Converting anyway. The 132 bytes needed\n"); printf(" from the ROM will be filled with 0 bytes, the converted file may not\n"); printf(" work\n"); /* write the 132 byte area as zero's */ if(fwrite(buffer,132,1,fd)!=1) WERR_write_error(s, fd); } else { if (file_size != 16384) { printf("Warning: The file \"spectrum.rom\" needed for proper conversion to the .zx\n"); printf(" format has a wrong size (not 16K). Converting anyway. The 132 bytes\n"); printf(" needed from the ROM will be filled with 0 bytes, the converted file\n"); printf(" may not work\n"); /* write the 132 byte area as zero's */ if(fwrite(buffer,132,1,fd)!=1) WERR_write_error(s, fd); } else { FILE * fd_specrom; printf("Using Spectrum ROM: %s\n",rom); fd_specrom=fopen(rom,"r"); if(fd_specrom == NULL) { perror(rom); fclose(fd); exit(EXIT_WRITE_ERROR); } /* fseek over the first 16384-132 bytes */ if(fseek(fd_specrom,16252L,SEEK_CUR)!=0) { perror(rom); fclose(fd_specrom); fclose(fd); exit(EXIT_WRITE_ERROR); } if(fread(buffer,132,1,fd_specrom)!=1) { perror(rom); fclose(fd_specrom); fclose(fd); exit(EXIT_WRITE_ERROR); } if(fwrite(buffer,132,1,fd)!=1) { perror(s); fclose(fd_specrom); fclose(fd); exit(EXIT_WRITE_ERROR); } fclose(fd_specrom); } } if(fwrite(image, (size_t) IMSIZE, 1, fd)!=1) WERR_write_error(s, fd); if(fwrite(&kgb,kgb_size,1,fd)!=1) WERR_write_error(s, fd); fclose(fd); } /* CONVERSION FUNCTIONS - TO .SNA FORMAT */ void VGSN_vgaspec_to_sna() { unsigned int addr; unsigned int sp; sna.f=vga.f; sna.a=vga.a; sna.b=vga.b; sna.c=vga.c; sna.d=vga.d; sna.e=vga.e; sna.h=vga.h; sna.l=vga.l; sna.fax=vga.fax; sna.aax=vga.aax; sna.bax=vga.bax; sna.cax=vga.cax; sna.dax=vga.dax; sna.eax=vga.eax; sna.hax=vga.hax; sna.lax=vga.lax; sna.ixh=vga.ixh; sna.ixl=vga.ixl; sna.iyh=vga.iyh; sna.iyl=vga.iyl; sna.border=vga.border; sna.i=vga.i; sna.r=vga.r; /* If register I has changed, chances are good that it runs in IM2 mode */ if(sna.i==0x3f) sna.im=0x01; else sna.im=0x02; if((vga.im & 0x01) == 0) sna.iff2=0x00; else sna.iff2=0xff; sp=256*vga.sph+vga.spl; sp=sp-2; addr=sp-0x4000; image[addr]=vga.pcl; image[addr+1]=vga.pch; sna.sph=sp/256; sna.spl=sp%256; } void RASN_raw_to_sna() { unsigned int addr; unsigned int sp; unsigned int pc; pc=0x1bf4; /* entry of "next statement" */ sna.f=0x99; sna.a=0x5f; sna.b=0x1f; sna.c=0xf0; sna.d=0x5d; sna.e=0x0c; sna.h=0x5d; sna.l=0x0e; sna.fax=0x44; sna.aax=0x00; sna.bax=0x18; sna.cax=0x20; sna.dax=0x00; sna.eax=0x07; sna.hax=0x5c; sna.lax=0xf1; sna.ixh=0x03; sna.ixl=0xd4; sna.iyh=0x5c; sna.iyl=0x3a; sna.i=0x3f; sna.r=0x00; sna.im=0x01; sna.iff2=0xFF; /* set sp by means of RAMTOP in the image */ addr=0x5cb2-0x4000; sp=256*image[addr+1]+image[addr]-1; /* Reset ERR NR to no error */ image[0x5c3a-0x4000]=0xff; /* Set border by means of BORDCR */ sna.border=(image[0x5c48-0x4000] & 0x38)>>3; /* put return address to MAIN-4 (0x1303) on stack */ sp=sp-2; addr=sp-0x4000; image[addr]=0x03; image[addr+1]=0x13; sp=sp-2; addr=sp-0x4000; image[addr]=pc%256; image[addr+1]=pc/256; sna.sph=sp/256; sna.spl=sp%256; } void Z8SN_z80_to_sna() { unsigned int addr; unsigned int sp; sna.f=z80.f; sna.a=z80.a; sna.b=z80.b; sna.c=z80.c; sna.d=z80.d; sna.e=z80.e; sna.h=z80.h; sna.l=z80.l; sna.fax=z80.fax; sna.aax=z80.aax; sna.bax=z80.bax; sna.cax=z80.cax; sna.dax=z80.dax; sna.eax=z80.eax; sna.hax=z80.hax; sna.lax=z80.lax; sna.ixh=z80.ixh; sna.ixl=z80.ixl; sna.iyh=z80.iyh; sna.iyl=z80.iyl; sna.border=(z80.data/2) & 0x07; sna.i=z80.i; if(z80.data==0xff) z80.data=0; if((z80.data & 0x01)==1) sna.r=(z80.r & 0x7f)+0x80; else sna.r=z80.r & 0x7f; sna.im=z80.im & 0x03; if(z80.iff2 != 0) sna.iff2=0xff; else sna.iff2=0x00; sp=256*z80.sph+z80.spl; sp=sp-2; addr=sp-0x4000; sna.sph=sp/256; sna.spl=sp%256; image[addr]=z80.pcl; image[addr+1]=z80.pch; } void PRSN_prg_to_sna() { unsigned int addr; unsigned int sp; sna.b=prg.b; sna.c=prg.c; sna.d=prg.d; sna.e=prg.e; sna.h=prg.h; sna.l=prg.l; sna.fax=prg.fax; sna.aax=prg.aax; sna.bax=prg.bax; sna.cax=prg.cax; sna.dax=prg.dax; sna.eax=prg.eax; sna.hax=prg.hax; sna.lax=prg.lax; sna.ixh=prg.ixh; sna.ixl=prg.ixl; sna.iyh=prg.iyh; sna.iyl=prg.iyl; /* Set border by means of BORDCR */ sna.border=(image[0x5c48-0x4000] & 0x38)>>3; sna.i=prg.i; if(prg.i==0x3f) sna.im=0x01; else sna.im=0x02; sp=256*prg.sph+prg.spl; sp=sp+4; /* there are two more words on the stack besides PC */ addr=sp-0x4000; sna.r=image[addr-3]; /* the af combo is on the stack */ sna.f=image[addr-2]; sna.a=image[addr-1]; sna.sph=sp/256; sna.spl=sp%256; /* interrupts always on ??? */ sna.iff2=prg.iff2; } void ACSN_ach_to_sna() { unsigned int addr; unsigned int sp; sna.f=ach.f; sna.a=ach.a; sna.b=ach.b; sna.c=ach.c; sna.d=ach.d; sna.e=ach.e; sna.h=ach.h; sna.l=ach.l; sna.fax=ach.fax; sna.aax=ach.aax; sna.bax=ach.bax; sna.cax=ach.cax; sna.dax=ach.dax; sna.eax=ach.eax; sna.hax=ach.hax; sna.lax=ach.lax; sna.ixh=ach.ixh; sna.ixl=ach.ixl; sna.iyh=ach.iyh; sna.iyl=ach.iyl; sna.border=ach.border; sna.i=ach.i; sna.r=ach.r; sna.im=ach.im & 0x03; if(sna.im == 3) sna.im = 0; sna.iff2=ach.iff2; sp=256*ach.sph+ach.spl; sp=sp-2; addr=sp-0x4000; sna.sph=sp/256; sna.spl=sp%256; image[addr]=ach.pcl; image[addr+1]=ach.pch; } void KGSN_kgb_to_sna() { unsigned int addr; unsigned int sp; sna.f=kgb.f; sna.a=kgb.a; sna.b=kgb.b; sna.c=kgb.c; sna.d=kgb.d; sna.e=kgb.e; sna.h=kgb.h; sna.l=kgb.l; sna.fax=kgb.fax; sna.aax=kgb.aax; sna.bax=kgb.bax; sna.cax=kgb.cax; sna.dax=kgb.dax; sna.eax=kgb.eax; sna.hax=kgb.hax; sna.lax=kgb.lax; sna.ixh=kgb.ixh; sna.ixl=kgb.ixl; sna.iyh=kgb.iyh; sna.iyl=kgb.iyl; sna.i=kgb.i; sna.r=kgb.r; /* border-colour not found in KGB image */ /* Set border by means of BORDCR */ sna.border=(image[0x5c48-0x4000] & 0x38)>>3; /* determine interrupt mode using the value of register I */ if (kgb.i_mode_l==0xff) sna.im=0x00; else if(kgb.i_mode_l==1) sna.im=0x02; else sna.im=0x01; if((kgb.interruptstatus & 0x01) != 0) sna.iff2=0xff; else sna.iff2=0x0; sp=256*kgb.sph+kgb.spl; sp=sp-2; addr=sp-0x4000; sna.sph=sp/256; sna.spl=sp%256; image[addr]=kgb.pcl; image[addr+1]=kgb.pch; } /* CONVERSION FUNCTIONS - FROM .SNA FORMAT */ void SNVG_sna_to_vgaspec() { unsigned int addr; unsigned int sp; unsigned int pc; sp=256*sna.sph+sna.spl; addr=sp-0x4000; pc=image[addr]+256*image[addr+1]; sp=sp+2; vga.S='S'; vga.P='P'; vga.len_l=0x00; vga.len_h=0xC0; vga.start_l=0x00; vga.start_h=0x40; vga.f=sna.f; vga.a=sna.a; vga.b=sna.b; vga.c=sna.c; vga.d=sna.d; vga.e=sna.e; vga.h=sna.h; vga.l=sna.l; vga.fax=sna.fax; vga.aax=sna.aax; vga.bax=sna.bax; vga.cax=sna.cax; vga.dax=sna.dax; vga.eax=sna.eax; vga.hax=sna.hax; vga.lax=sna.lax; vga.ixh=sna.ixh; vga.ixl=sna.ixl; vga.iyh=sna.iyh; vga.iyl=sna.iyl; vga.i=sna.i; vga.r=sna.r; vga.im=sna.im & 0x02; /* 0 for IM1, 2 for IM2 */ /* works? how does it know it was IM1 ? */ if((sna.iff2 & 0x04) != 0) vga.im=vga.im | 0x01; vga.sph=sp/256; vga.spl=sp%256; vga.pch=pc/256; vga.pcl=pc%256; vga.border=sna.border; vga.res2=0; vga.res3=0; vga.res4=0; vga.res5=0; } void SNZ8_sna_to_z80() { unsigned int addr; unsigned int sp; unsigned int pc; sp=256*sna.sph+sna.spl; addr=sp-0x4000; pc=image[addr]+256*image[addr+1]; sp=sp+2; z80.f=sna.f; z80.a=sna.a; z80.b=sna.b; z80.c=sna.c; z80.d=sna.d; z80.e=sna.e; z80.h=sna.h; z80.l=sna.l; z80.fax=sna.fax; z80.aax=sna.aax; z80.bax=sna.bax; z80.cax=sna.cax; z80.dax=sna.dax; z80.eax=sna.eax; z80.hax=sna.hax; z80.lax=sna.lax; z80.ixh=sna.ixh; z80.ixl=sna.ixl; z80.iyh=sna.iyh; z80.iyl=sna.iyl; z80.i=sna.i; z80.r=sna.r | 0x080; /* bit 7 is stored somewhere else, always set */ z80.im=sna.im & 0x03; z80.im=z80.im + 0x60; /* fixed normal video/kempston joystick */ z80.sph=sp/256; z80.spl=sp%256; z80.pch=pc/256; z80.pcl=pc%256; /* all kinds of stuff put in "data" */ z80.data=(sna.border & 0x07)*2; if((sna.r & 0x80)!=0) z80.data=z80.data+1; /* here is bit 7 of r */ /* image is not compressed, compression will be done by the */ /* z80_write function. */ z80.data = z80.data & ~0x20; /* reset compressed mode bit to be sure */ if((sna.iff2 & 0x04) != 0) { z80.iff1=0xff; z80.iff2=0xff; } else { z80.iff1=0; z80.iff2=0; } } void SNPR_sna_to_prg(char * n) { unsigned int addr; unsigned int sp; int i; unsigned char * p; /* clean header structure first */ p=(unsigned char *) &prg; for(i=0; i < 256; i++) p[i]='\0'; prg.c_0x61=0x61; /* size of image in sectors */ prg.c_0x35=0x35; /* don't know yet */ prg.c_0x03=0x03; /* don't know yet */ sp=256*sna.sph+sna.spl; addr=sp-0x4000; /* these are on the stack */ image[addr-1]=sna.a; image[addr-2]=sna.f; image[addr-3]=sna.r; image[addr-4]=sna.iff2; sp=sp-4; prg.name[0]='\0'; strncpy(prg.name,n,10); prg.name[10]='\0'; prg.b=sna.b; prg.c=sna.c; prg.d=sna.d; prg.e=sna.e; prg.h=sna.h; prg.l=sna.l; prg.fax=sna.fax; prg.aax=sna.aax; prg.bax=sna.bax; prg.cax=sna.cax; prg.dax=sna.dax; prg.eax=sna.eax; prg.hax=sna.hax; prg.lax=sna.lax; prg.ixh=sna.ixh; prg.ixl=sna.ixl; prg.iyh=sna.iyh; prg.iyl=sna.iyl; prg.i=sna.i; prg.iff2=sna.iff2; prg.sph=sp/256; prg.spl=sp%256; /* prg.border=sna.border; */ } void SNAC_sna_to_ach() { unsigned int addr; unsigned int sp; unsigned int pc; int i; unsigned char * p; /* clean header structure first */ p=(unsigned char *) &ach; for(i=0; i < 256; i++) p[i]='\0'; sp=256*sna.sph+sna.spl; addr=sp-0x4000; pc=image[addr]+256*image[addr+1]; sp=sp+2; ach.f=sna.f; ach.a=sna.a; ach.b=sna.b; ach.c=sna.c; ach.d=sna.d; ach.e=sna.e; ach.h=sna.h; ach.l=sna.l; ach.fax=sna.fax; ach.aax=sna.aax; ach.bax=sna.bax; ach.cax=sna.cax; ach.dax=sna.dax; ach.eax=sna.eax; ach.hax=sna.hax; ach.lax=sna.lax; ach.ixh=sna.ixh; ach.ixl=sna.ixl; ach.iyh=sna.iyh; ach.iyl=sna.iyl; ach.i=sna.i; ach.r=sna.r; ach.border=sna.border; if((sna.iff2 & 0x04) != 0) ach.iff2=0xff; else ach.iff2=0x00; ach.im=sna.im; ach.sph=sp/256; ach.spl=sp%256; ach.pch=pc/256; ach.pcl=pc%256; } void SNKG_sna_to_kgb() { unsigned int addr; unsigned int sp; unsigned int pc; int i; unsigned char * p; /* clean info structure first */ p = (unsigned char *) &kgb; for(i=0; i < 202; i++) p[i]='\0'; /* make some assumptions here */ kgb.is3_1 = 3; /* always 3, don't ask me why */ kgb.colourmode = 1; /* assume colour */ kgb.soundmode = 1; /* assume simple sound */ kgb.haltmode = 1; /* assume not in halt mode */ sp=256*sna.sph+sna.spl; addr=sp-0x4000; pc=image[addr]+256*image[addr+1]; sp=sp+2; kgb.f=sna.f; kgb.a=sna.a; kgb.b=sna.b; kgb.c=sna.c; kgb.d=sna.d; kgb.e=sna.e; kgb.h=sna.h; kgb.l=sna.l; kgb.fax=sna.fax; kgb.aax=sna.aax; kgb.bax=sna.bax; kgb.cax=sna.cax; kgb.dax=sna.dax; kgb.eax=sna.eax; kgb.hax=sna.hax; kgb.lax=sna.lax; kgb.ixh=sna.ixh; kgb.ixl=sna.ixl; kgb.iyh=sna.iyh; kgb.iyl=sna.iyl; kgb.i=sna.i; kgb.r=sna.r; /* kgb.border=sna.border; NOT IN KGB IMAGE! */ /* Interupt mode is stored in a word in the KGB format. */ /* Use byte accesses to be CPU independent */ switch (sna.im & 0x03) { case 0: kgb.i_mode_h = 0xff; kgb.i_mode_l = 0xff; break; case 2: kgb.i_mode_h = 0; kgb.i_mode_l = 1; break; default:kgb.i_mode_h = 0; kgb.i_mode_l = 0; break; } if((sna.iff2 & 0x04) != 0) kgb.interruptstatus=0x01; else kgb.interruptstatus=0x00; kgb.sph=sp/256; kgb.spl=sp%256; kgb.pch=pc/256; kgb.pcl=pc%256; } /* 16K PAGE READ FUNCTION FOR .Z80 FORMAT */ /* * Function returns: * 0: No more pages * 1: Page read */ int RDPG_z80_read_page(char * s, FILE * fd) { struct z80_page_s page_info; unsigned int len; unsigned int pos; long f_len; if(fread(&page_info,z80_pg_size,1,fd)!=1) { return 0; } len = (256 * page_info.blklen_h) + page_info.blklen_l; switch(page_info.page_num) { case 0: fprintf(stderr,"%s - memory page 0 - 48K rom ignored\n",s); pos=0x0ffff; break; case 1: fprintf(stderr,"%s - memory page 1 - Interface 1 rom ignored\n",s); pos=0x0ffff; break; case 2: fprintf(stderr,"%s - memory page 2 - basic samram rom ignored\n",s); pos=0x0ffff; break; case 3: fprintf(stderr,"%s - memory page 3 - monitor samram rom ignored\n",s); pos=0x0ffff; break; case 4: pos=0x04000; /* second 16K of RAM area */ break; case 5: pos=0x08000; /* third 16K of RAM area */ break; case 6: fprintf(stderr,"%s - memory page 6 - shadow rom 8000-BFFF ignored\n",s); pos=0x0ffff; break; case 7: fprintf(stderr,"%s - memory page 7 - shadow rom C000-FFFF ignored\n",s); pos=0x0ffff; break; case 8: pos=0; /* first 16K of RAM area */ break; case 9: fprintf(stderr,"%s - memory page 8 - 128K page 6 ignored\n",s); pos=0x0ffff; break; case 10:fprintf(stderr,"%s - memory page 10 - 128K page7 ignored\n",s); pos=0x0ffff; break; default:fprintf(stderr,"%s - memory page %d - Invalid page number -fatal-\n",s,page_info.page_num); exit(EXIT_FILE_ERROR); break; } if(pos == (unsigned int) 0x0ffff) { /* wrong page, seek over this page */ if(fseek(fd, (long) len, SEEK_CUR)!=0) RERR_read_error(s, fd); } else { /* Valid 48K page, read and decompress */ f_len = 0 - ftell(fd); Z80D_z80_decompress(fd, pos, 16384); f_len = f_len + ftell(fd); if((long) len != f_len) { fprintf(stderr,"Z80 image corrupted, can't convert\n"); fclose(fd); exit(EXIT_FILE_ERROR); } } return 1; } /* COMPRESSION/DECOMPRESSION for .Z80 FORMAT */ void Z80D_z80_decompress(FILE * fd, unsigned int start, unsigned int imsize) { int c,j,k; unsigned char l; unsigned char im; j=start; while(j<(start+imsize)) { c=getc(fd); if(c == -1) return; im = (unsigned char) c; if(im!=0xed) { image[j++]=im; } else { c=getc(fd); if(c == -1) return; im = (unsigned char) c; if(im!=0xed) { image[j++]=0xed; ungetc(im,fd); } else { /* fetch count */ k=getc(fd); if(k == -1) return; /* fetch character */ c=getc(fd); if(c == -1) return; l = (unsigned char) c; while(k!=0) { image[j++]=l; k--; } } } } if(j!=(start+imsize)) { fprintf(stderr,"Z80 image corrupted, can't decompress\n"); exit(EXIT_FILE_ERROR); } } int Z80C_z80_compress() { #ifdef __TURBOC__ unsigned char far * comp; #else unsigned char * comp; #endif unsigned int i,j; unsigned int num; unsigned char c,n; unsigned int ed; z80_size=(unsigned int) IMSIZE; /* * We need an intermediate buffer here, if the compressed image * is bigger than the uncompressed image than the image will * not be compressed to save space (!) */ #ifdef __TURBOC__ comp=(unsigned char far *) farmalloc((unsigned long)(IMSIZE+0x0100)); #else comp=(unsigned char *) malloc((size_t)(IMSIZE+0x0100)); #endif if(comp==NULL) { printf("Warning: Not enough memory to compress the image, using uncopressed image\n"); return NOTCOMPRESSED; } i=0; j=0; /* ensure 'ed' is not set */ ed=NO; while(i= (IMSIZE-4)) { /* compressed image bigger or same than original */ #ifdef __TURBOC__ farfree((void far *) comp); #else free((void *) comp); #endif return NOTCOMPRESSED; } } /* append "end of compressed area" mark */ comp[j]=0; j++; comp[j]=0xed; j++; comp[j]=0xed; j++; comp[j]=0; j++; z80_size = j; /* copy back */ i=0; j=0; while(i