/* * SPCONV - ZX-Spectrum 48K snapshot converter * * Public Domain software * * Author: Henk de Groot * SNX conversion routines added by Damien Burke (v1.09) */ #if defined(__STDC__) || defined(__TURBOC__) #include <stdlib.h> #else #include <sys/types.h> #endif #ifdef __TURBOC__ #include <io.h> #else #include <unistd.h> #endif #include <stdio.h> #include <fcntl.h> #include <sys/stat.h> #include <string.h> #include <ctype.h> #ifdef __TURBOC__ #include <alloc.h> #endif #ifndef O_BINARY #define O_BINARY 0 #endif #include "spconv.h" struct snx_s snx; 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; signed int intype; signed int outtype; /* Prototype for main */ extern int main(int argc, char *argv[]); /* 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: * * .SNX -> SNX file (used by Atari emulator) * .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 .SNX, .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: * * .SNX -> SNX file * .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 .SNX, .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 SNX: RSNX_read_snx(argv[1]); SXSN_snx_to_sna(); break; 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 SNX: SNSX_sna_to_snx(); WSNX_write_snx(outfile); break; 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, signed int * type) { const char *p; char ext[5]; signed int i; /* * recognize type on filename: * * .SNX -> SNX file * .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,".SNX")==0) { *type=SNX; return ".SNX"; } 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 */ signed int DIRC_direct_convert(signed int type, char * infile, char * outfile) { struct stat status; signed 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.09 - %s\n\n",__DATE__); fprintf(stderr,"Usage: spconv <source> <target>\n\n"); fprintf(stderr,"Source must be a valid .SNA, .SP, .Z80, .PRG, .ACH, .ZX, .SNX or RAW file.\n"); fprintf(stderr,"Target must be a .SNA, .SP, .Z80, .PRG, .ACH, .ZX or .SNX 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 <source> and <target> 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 <source> and <target> are .Z80 files, convertion to\n"); fprintf(stderr,"the version 1.45 format will be performed.\n"); fprintf(stderr,"If <source> and <target> 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 & D. Burke 1995\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) { signed 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 Spectrum 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; signed int ext_size; 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); ext_size = ((signed int) p[z80_145_size]) + 256 * ((signed int) p[z80_145_size+1]); if(ext_size < z80_201_ext_size) { fprintf(stderr,"%s seems to be a new type Z80 file!\n",s); fprintf(stderr,"This program is only tested for files up to version 3.0 format.\n"); fprintf(stderr,"The .Z80 file doesn't seem to be upwards compatible, since the\n"); fprintf(stderr,"extended header seems to be smaller than the version 2.01 extended\n"); fprintf(stderr,"header. The program can NOT convert this file!\n"); fclose(fd); exit(EXIT_FILE_ERROR); } if( (ext_size != z80_201_ext_size) && (ext_size != z80_300_ext_size ) ) { printf("Warning: %s seems to be a new type Z80 file!\n",s); printf(" This program is only tested for files up to version 3.0 format.\n"); printf(" Assuming upwards compatibiliy, the program will attempt to convert\n"); printf(" the file anyway. The resulting file may not work!.\n"); } /* we passed this test, read the first part of the extended header */ if(fread(&p[z80_145_size+2], z80_201_ext_size, 1, fd) != 1) RERR_read_error(s, fd); /* seek over the remaining extended header part if needed */ if(ext_size > z80_201_ext_size) { if (fseek(fd, (long) (ext_size - z80_201_ext_size), SEEK_CUR) != 0) RERR_read_error(s, fd); } /* we got a complete header now. */ /* check the type of the file... */ switch (ext_size) { case z80_201_ext_size: 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); } break; case z80_300_ext_size: if((unsigned int) z80.hardware >= 4) { fprintf(stderr,"%s is not a 48K Spectrum Z80 file, can't convert!\n",s); fclose(fd); exit(EXIT_FILE_ERROR); } break; default: if((unsigned int) z80.hardware >= 4) { printf("Warning: %s might not be a 48K Spectrum Z80 file!\n",s); printf(" (hardware flag in .Z80 file >= 4)\n"); printf(" Assuming 48K Spectrum anyway, continuing...\n"); } break; } /* 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; signed int i; struct stat status; const char * rom; FILE * fd; signed 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; signed int i; struct stat status; const char * rom; FILE * fd; signed 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); } /* SPECIFIC I/O - READ/WRITE .SNX IMAGE */ void RSNX_read_snx(char * s) { FILE * fd; unsigned char counthi, countlo, marker, fill; unsigned int count, i, addr; fd=fopen(s,"r"); if(fd == NULL) RERR_read_error(s, fd); /* read header part */ if(fread(&snx,snx_size,1,fd)!=1) RERR_read_error(s, fd); if((snx.signature[0] != 'X') || (snx.signature[1] != 'S') || (snx.signature[2] != 'N') || (snx.signature[3] != 'A')) { printf("This is not a .SNX file, it should have the 'XSNA' text at the start!\n"); RERR_read_error(s,fd); } /* If the header is an unexpected size, seek past it, ignoring any extra information held in it */ if (snx.headerlenhi * 256 + snx.headerlenlo != snx_size-6) { printf("Warning: the header part of %s is not the expected %u bytes!\n",s,snx_size); printf(" Converting anyway, the converted file may not work\n"); fseek(fd,snx.headerlenhi*256+snx.headerlenlo+6,SEEK_SET); } /* Read and decompress the image. */ addr = 0; while (addr < IMSIZE) { if(fread(&counthi,1,1,fd)!=1) RERR_read_error(s,fd); if (counthi >= 0xE0) { count = (counthi & 0x0F) + 1; if ((counthi & 0xF0) == 0xF0) marker = SNX_COMPRESSED; else marker = SNX_UNCOMPRESSED; } else { if(fread(&countlo,1,1,fd)!=1) RERR_read_error(s,fd); count = (counthi << 8) + countlo; if(fread(&marker,1,1,fd)!=1) RERR_read_error(s,fd); } if (addr + count > IMSIZE) RERR_read_error(s,fd); if (marker == SNX_COMPRESSED) { if(fread(&fill,1,1,fd)!=1) RERR_read_error(s,fd); for(i = 0; i < count; i++) image[addr + i] = fill; addr += count; } else { i = 0; while (i < count) { if(fread(&image[addr + i++],1,1,fd)!=1) RERR_read_error(s,fd); } addr += count; } } fclose(fd); } void WSNX_write_snx(char * s) { FILE * fd; unsigned char marker, countlo, counthi; unsigned int addr, i, j, count, packable; unlink(s); fd=fopen(s,"w"); if(fd == NULL) WERR_write_error(s, fd); /* Write header first */ if(fwrite(&snx,snx_size,1,fd)!=1) WERR_write_error(s, fd); /* Now compress and write RAM image */ addr = 0; while (addr < IMSIZE) { i = addr; j = i; /* First find a run we can compress; must be at least 6 identical bytes in a row. */ while ( (j+5 < IMSIZE) && (image[j] == image[j+1]) && (image[j] == image[j+2]) && (image[j] == image[j+3]) && (image[j] == image[j+4]) && (image[j] == image[j+5]) ) { j++; } if (j > i) { count = (j - i) + 5; if (count <= 16) { counthi = 0xF0 + (count - 1); if(fwrite(&counthi,1,1,fd)!=1) WERR_write_error(s,fd); } else { counthi = count >> 8; countlo = count & 0xFF; if(fwrite(&counthi,1,1,fd)!=1) WERR_write_error(s,fd); if(fwrite(&countlo,1,1,fd)!=1) WERR_write_error(s,fd); marker = SNX_COMPRESSED; if(fwrite(&marker,1,1,fd)!=1) WERR_write_error(s,fd); } if(fwrite(&image[i],1,1,fd)!=1) WERR_write_error(s,fd); addr += count; i = j + 5; } j = i + 1; packable = count = 0; /* Now find a run of non-identical bytes */ do { if (image[j] != image[j-1]) { packable = 0; count++; j++; } else /* Found two consecutive identical bytes */ { if ( (j+3 < IMSIZE) && (image[j] == image[j+1]) && (image[j] == image[j+2]) && (image[j] == image[j+3]) && (image[j] == image[j+4]) ) { /* Found six consecutive identical bytes */ packable = 6; j--; } else { packable = 0; count++; j++; } } } while ( (j < IMSIZE) && (packable < 6) ); if (count > 0) { if (IMSIZE - (i + count) < 6) count = (unsigned int)(IMSIZE - i); if (count <= 16) { counthi = 0xE0 + (count - 1); if(fwrite(&counthi,1,1,fd)!=1) WERR_write_error(s,fd); } else { counthi = count >> 8; countlo = count & 0xFF; if(fwrite(&counthi,1,1,fd)!=1) WERR_write_error(s,fd); if(fwrite(&countlo,1,1,fd)!=1) WERR_write_error(s,fd); marker = SNX_UNCOMPRESSED; if(fwrite(&marker,1,1,fd)!=1) WERR_write_error(s,fd); } while (i < j) { if(fwrite(&image[i++],1,1,fd)!=1) WERR_write_error(s,fd); } addr += count; } } 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; signed 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; signed 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; signed 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 */ signed int RDPG_z80_read_page(char * s, FILE * fd) { struct z80_page_s page_info; unsigned int len; unsigned int pos; signed 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 page 7 ignored\n",s); pos=0x0ffff; break; case 11:fprintf(stderr,"%s - memory page 11 - Multiface rom ignored\n",s); pos=0x0ffff; break; default:fprintf(stderr,"%s - memory page %d - Unknown page number -ignored-\n",s,page_info.page_num); pos=0x0ffff; 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((signed 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) { signed 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 uncompressed image\n"); return NOTCOMPRESSED; } i=0; j=0; /* ensure 'ed' is not set */ ed=NO; while(i<IMSIZE) { c=image[i]; i++; if(i<IMSIZE) { n=image[i]; } else { /* force 'n' to be unequal to 'c' */ n=c; n++; } if(c!=n) { comp[j]=c; j++; if(c==0xed) ed=YES; else ed=NO; } else { if(c==0xed) { /* two times 0xed - special care */ comp[j]=0xed; j++; comp[j]=0xed; j++; comp[j]=0x02; j++; comp[j]=0xed; j++; i++; /* skip second ED */ /* because 0xed is valid compressed we don't have to watch it! */ ed=NO; } else if(ed==YES) { /* can't compress now, skip this double pair */ comp[j]=c; j++; ed=NO; /* 'c' can't be 0xed */ } else { num=1; while(i<IMSIZE) { if(c!=image[i]) break; num++; i++; if(num==255) break; } if(num <= 4) { /* no use to compress */ while(num!=0) { comp[j]=c; j++; num--; } } else { comp[j]=0xed; j++; comp[j]=0xed; j++; comp[j]=(unsigned char) num; j++; comp[j]=c; j++; } } } if(j >= (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<IMSIZE) image[i++]=comp[j++]; #ifdef __TURBOC__ farfree((void far *) comp); #else free((void *) comp); #endif return COMPRESSED; } /* SNX to SNA is easy, because the SNX header contains all the stuff in a */ /* SNA header, it's even in the same order. */ void SXSN_snx_to_sna(void) { sna.i = snx.i; sna.lax = snx.lax; sna.hax = snx.hax; sna.eax = snx.eax; sna.dax = snx.dax; sna.cax = snx.cax; sna.bax = snx.bax; sna.fax = snx.fax; sna.aax = snx.aax; sna.l = snx.l; sna.h = snx.h; sna.e = snx.e; sna.d = snx.d; sna.c = snx.c; sna.b = snx.b; sna.iyl = snx.iyl; sna.iyh = snx.iyh; sna.ixl = snx.ixl; sna.ixh = snx.ixh; sna.iff2 = snx.iff2; sna.r = snx.r; sna.f = snx.f; sna.a = snx.a; sna.spl = snx.spl; sna.sph = snx.sph; sna.im = snx.im; sna.border = snx.border; } /* SNA to SNX is easy, because they have identical headers apart from the */ /* extra information present in the SNX header. */ void SNSX_sna_to_snx(void) { snx.signature[0] = 'X'; /* XSNA = eXtended SNA! */ snx.signature[1] = 'S'; snx.signature[2] = 'N'; snx.signature[3] = 'A'; snx.headerlenhi = (snx_size - 6) << 8; snx.headerlenlo = (snx_size - 6) & 0xFF; snx.i = sna.i; snx.lax = sna.lax; snx.hax = sna.hax; snx.eax = sna.eax; snx.dax = sna.dax; snx.cax = sna.cax; snx.bax = sna.bax; snx.fax = sna.fax; snx.aax = sna.aax; snx.l = sna.l; snx.h = sna.h; snx.e = sna.e; snx.d = sna.d; snx.c = sna.c; snx.b = sna.b; snx.iyl = sna.iyl; snx.iyh = sna.iyh; snx.ixl = sna.ixl; snx.ixh = sna.ixh; snx.iff2 = sna.iff2; snx.r = sna.r; snx.f = sna.f; snx.a = sna.a; snx.spl = sna.spl; snx.sph = sna.sph; snx.im = sna.im; snx.border = sna.border; /* Set the emulator's switches to some default values. Not sure just what these values mean, but they were like this in most of the SNX files I had to test the program with, so seem safe enough to use */ snx.emulator_switches[0] = 0; snx.emulator_switches[1] = 1; snx.emulator_switches[2] = 1; snx.emulator_switches[3] = 0x83; snx.emulator_switches[4] = 1; snx.emulator_switches[5] = 0; snx.emulator_switches[6] = 1; snx.emulator_switches[7] = 0x41; snx.emulator_switches[8] = 0x22; snx.emulator_switches[9] = 0xFF; }
Write, Run & Share C Language code online using OneCompiler's C online compiler for free. It's one of the robust, feature-rich online compilers for C language, running the latest C version which is C18. Getting started with the OneCompiler's C editor is really simple and pretty fast. The editor shows sample boilerplate code when you choose language as 'C' and start coding!
OneCompiler's C online editor supports stdin and users can give inputs to programs using the STDIN textbox under the I/O tab. Following is a sample C program which takes name as input and print your name with hello.
#include <stdio.h>
int main()
{
char name[50];
printf("Enter name:");
scanf("%s", name);
printf("Hello %s \n" , name );
return 0;
}
C language is one of the most popular general-purpose programming language developed by Dennis Ritchie at Bell laboratories for UNIX operating system. The initial release of C Language was in the year 1972. Most of the desktop operating systems are written in C Language.
When ever you want to perform a set of operations based on a condition if-else
is used.
if(conditional-expression) {
// code
} else {
// code
}
You can also use if-else for nested Ifs and if-else-if ladder when multiple conditions are to be performed on a single variable.
Switch is an alternative to if-else-if ladder.
switch(conditional-expression) {
case value1:
// code
break; // optional
case value2:
// code
break; // optional
...
default:
// code to be executed when all the above cases are not matched;
}
For loop is used to iterate a set of statements based on a condition.
for(Initialization; Condition; Increment/decrement){
// code
}
While is also used to iterate a set of statements based on a condition. Usually while is preferred when number of iterations are not known in advance.
while(condition) {
// code
}
Do-while is also used to iterate a set of statements based on a condition. It is mostly used when you need to execute the statements atleast once.
do {
// code
} while (condition);
Array is a collection of similar data which is stored in continuous memory addresses. Array values can be fetched using index. Index starts from 0 to size-1.
data-type array-name[size];
data-type array-name[size][size];
Function is a sub-routine which contains set of statements. Usually functions are written when multiple calls are required to same set of statements which increases re-usuability and modularity.
Two types of functions are present in C
Library functions are the in-built functions which are declared in header files like printf(),scanf(),puts(),gets() etc.,
User defined functions are the ones which are written by the programmer based on the requirement.
return_type function_name(parameters);
function_name (parameters)
return_type function_name(parameters) {
//code
}