Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1571 lines (1368 sloc)
39.8 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| ** Oricutron | |
| ** Copyright (C) 2009-2014 Peter Gordon | |
| ** | |
| ** 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, version 2 | |
| ** of the License. | |
| ** | |
| ** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
| ** | |
| */ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "system.h" | |
| #include "6502.h" | |
| #include "via.h" | |
| #include "8912.h" | |
| #include "gui.h" | |
| #include "disk.h" | |
| #include "monitor.h" | |
| #include "6551.h" | |
| #include "machine.h" | |
| #include "joystick.h" | |
| #include "filereq.h" | |
| #include "tape.h" | |
| #include "msgbox.h" | |
| extern char tapefile[], tapepath[]; | |
| extern SDL_bool refreshtape; | |
| char tmptapename[4096]; | |
| extern char filetmp[]; | |
| // Pop-up the name of the currently inserted tape | |
| // image file (or an eject symbol if no tape image). | |
| void tape_popup( struct machine *oric ) | |
| { | |
| char tmp[40]; | |
| if( !oric->tapename[0] ) | |
| { | |
| // Eject symbol | |
| do_popup( oric, "\x0f\x10\x13" ); | |
| return; | |
| } | |
| // Tape image name | |
| sprintf( tmp, "\x0f\x10""%c %-16s", oric->tapemotor ? 18 : 17, oric->tapename ); | |
| do_popup( oric, tmp ); | |
| } | |
| void toggletapecap( struct machine *oric, struct osdmenuitem *mitem, int dummy ) | |
| { | |
| char ortheader[] = { 'O', 'R', 'T', 0 }; // Oric Raw Tape, Version 0 | |
| /* If capturing, stop */ | |
| if( oric->tapecap ) | |
| { | |
| fclose( oric->tapecap ); | |
| oric->tapecap = NULL; | |
| mitem->name = "Save tape output..."; | |
| refreshtape = SDL_TRUE; | |
| return; | |
| } | |
| /* Otherwise, prompt for the file to capture */ | |
| if( !filerequester( oric, "Capture tape output", tapepath, tmptapename, FR_TAPESAVEORT ) ) | |
| { | |
| // Never mind | |
| return; | |
| } | |
| if( tmptapename[0] == 0 ) return; | |
| /* If it ends in ".tap", we need to change it to ".ort", because | |
| we're capturing real signals here folks! */ | |
| if( (strlen(tmptapename)>3) && (strcasecmp(&tmptapename[strlen(tmptapename)-4], ".tap")==0) ) | |
| tmptapename[strlen(tmptapename)-4] = 0; | |
| /* Add .ort extension, if necessary */ | |
| if( (strlen(tmptapename)<4) || (strcasecmp(&tmptapename[strlen(tmptapename)-4], ".ort")!=0) ) | |
| strncat(tmptapename, ".ort", 4095); | |
| tmptapename[4095] = 0; | |
| joinpath( tapepath, tmptapename ); | |
| /* Open the file */ | |
| oric->tapecap = fopen( filetmp, "wb" ); | |
| if( !oric->tapecap ) | |
| { | |
| /* Oh well */ | |
| msgbox( oric, MSGBOX_OK, "Unable to create file" ); | |
| return; | |
| } | |
| /* Write header */ | |
| fwrite( ortheader, 4, 1, oric->tapecap); | |
| /* Counter reset */ | |
| oric->tapecapcount = -1; | |
| oric->tapecaplastbit = (oric->via.orb&oric->via.ddrb)>>7; | |
| /* Update menu */ | |
| mitem->name = "Stop tape recording"; | |
| refreshtape = SDL_TRUE; | |
| } | |
| /* When we're loading a .tap file (or a non-raw section of a .ort file), | |
| ** we have to do 2 things... First, we have to elongate the leader so the ROM | |
| ** can see it, and secondly we have to add a delay after the header so that the | |
| ** tape isn't several bytes into the program when the rom is ready to start | |
| ** reading it. | |
| ** | |
| ** Only call it when you think you're at a tape leader since it doesn't do many | |
| ** sanity checks. | |
| */ | |
| void tape_setup_header( struct machine *oric ) | |
| { | |
| int i; | |
| oric->tapedupbytes = 0; | |
| oric->tapehdrend = 0; | |
| if( oric->tapebuf[oric->tapeoffs] != 0x16 ) | |
| return; | |
| /* Does it look like a header? */ | |
| for( i=0; oric->tapebuf[oric->tapeoffs+i]==0x16; i++ ) | |
| { | |
| if( (oric->tapeoffs+i) >= oric->tapelen ) | |
| return; | |
| } | |
| if( ( i < 3 ) || ( oric->tapebuf[oric->tapeoffs+i] != 0x24 ) ) | |
| return; | |
| i++; | |
| if( (oric->tapeoffs+i+9) >= oric->tapelen ) | |
| return; | |
| i+=9; | |
| while( oric->tapebuf[oric->tapeoffs+i] != 0 ) | |
| { | |
| if( (oric->tapeoffs+i) >= oric->tapelen ) | |
| return; | |
| i++; | |
| } | |
| i++; | |
| oric->tapedupbytes = 80; | |
| oric->tapehdrend = oric->tapeoffs+i; | |
| } | |
| void tape_orbchange( struct via *via ) | |
| { | |
| struct machine *oric = via->oric; | |
| unsigned char buffer[4], tapebit, bufwrite; | |
| unsigned int count; | |
| /* Capturing tape output? */ | |
| if( !oric->tapecap ) return; | |
| /* Saving a tap section? */ | |
| if( oric->tapecap == oric->tsavf ) return; | |
| tapebit = (via->orb & via->ddrb) >> 7; | |
| if( tapebit == oric->tapecaplastbit ) return; | |
| oric->tapecaplastbit = tapebit; | |
| /* Were we waiting for the first toggle? */ | |
| if( oric->tapecapcount < 0 ) | |
| { | |
| /* Well, we found it */ | |
| oric->tapecapcount = 0; | |
| fputc( tapebit, oric->tapecap ); | |
| return; | |
| } | |
| count = (oric->tapecapcount>>1); | |
| if( count < 0xfc ) | |
| { | |
| buffer[0] = count; | |
| bufwrite = 1; | |
| } | |
| else if( count < 0x100 ) | |
| { | |
| buffer[0] = 0xfc; | |
| buffer[1] = count; | |
| bufwrite = 2; | |
| } | |
| else if( count < 0x10000 ) | |
| { | |
| buffer[0] = 0xfd; | |
| buffer[1] = (count>>8)&0xff; | |
| buffer[2] = count&0xff; | |
| bufwrite = 3; | |
| } | |
| else | |
| { | |
| buffer[0] = 0xfd; | |
| buffer[1] = 0xff; | |
| buffer[2] = 0xff; | |
| bufwrite = 3; | |
| } | |
| fwrite( buffer, bufwrite, 1, oric->tapecap ); | |
| oric->tapecapcount = 0; | |
| } | |
| // Set the tape motor on or off | |
| void tape_setmotor( struct machine *oric, SDL_bool motoron ) | |
| { | |
| if( motoron == oric->tapemotor ) | |
| return; | |
| // Refresh the tape status icon in the status bar | |
| refreshtape = SDL_TRUE; | |
| // "Real" tape emulation? | |
| if( ( !oric->tapeturbo ) || ( !oric->pch_tt_available ) ) | |
| { | |
| if( ( !oric->rawtape ) || ( oric->tapeoffs < oric->nonrawend ) ) | |
| { | |
| // If we're stopping part way through a byte, just move | |
| // the current position on to the start of the next byte | |
| if( !motoron ) | |
| { | |
| if( oric->tapebit > 0 ) | |
| oric->tapeoffs++; | |
| oric->tapebit = 0; | |
| } | |
| // If we're starting the tape motor and the tape | |
| // is at a sync header, generate a bunch of dummy | |
| // sync bytes. This makes the Oric Atmos welcome tape | |
| // work without turbo tape enabled, for example. | |
| if( ( motoron ) && | |
| ( oric->tapebuf ) && | |
| ( oric->tapeoffs < oric->tapelen ) && | |
| ( oric->tapebuf[oric->tapeoffs] == 0x16 ) ) | |
| { | |
| tape_setup_header(oric); | |
| } | |
| } | |
| } | |
| // Set the new status and do a popup | |
| oric->tapemotor = motoron; | |
| if( oric->tapename[0] ) tape_popup( oric ); | |
| } | |
| // Free up the current tape image | |
| void tape_eject( struct machine *oric ) | |
| { | |
| if( oric->tapebuf ) free( oric->tapebuf ); | |
| oric->tapebuf = NULL; | |
| oric->tapelen = 0; | |
| oric->tapename[0] = 0; | |
| tape_popup( oric ); | |
| refreshtape = SDL_TRUE; | |
| } | |
| void tape_next_raw_count( struct machine *oric ) | |
| { | |
| unsigned int n; | |
| int nonrawend; | |
| if( oric->tapeoffs >= oric->tapelen ) | |
| { | |
| if( oric->nonrawend != oric->tapelen ) | |
| oric->tapehitend = 3; | |
| return; | |
| } | |
| n = oric->tapebuf[oric->tapeoffs++]; | |
| if (n < 0xfc) | |
| { | |
| oric->tapecount = n<<1; | |
| return; | |
| } | |
| switch (n) | |
| { | |
| case 0xff: // non-raw section | |
| if( oric->tapeoffs >= (oric->tapelen-1) ) | |
| { | |
| oric->tapeoffs = oric->tapelen; | |
| oric->tapehitend = 3; | |
| return; | |
| } | |
| nonrawend = ((oric->tapebuf[oric->tapeoffs]<<8)|oric->tapebuf[oric->tapeoffs+1]) + oric->tapeoffs+2; | |
| if( nonrawend > oric->tapelen ) | |
| { | |
| oric->tapeoffs = oric->tapelen; | |
| oric->tapehitend = 3; | |
| return; | |
| } | |
| oric->nonrawend = nonrawend; | |
| oric->tapeoffs += 2; | |
| oric->tapebit = 0; | |
| oric->tapecount = 2; | |
| oric->tapeout = 0; | |
| tape_setup_header( oric ); | |
| return; | |
| case 0xfc: | |
| if( oric->tapeoffs >= oric->tapelen ) | |
| { | |
| oric->tapehitend = 3; | |
| return; | |
| } | |
| oric->tapecount = oric->tapebuf[oric->tapeoffs++] << 1; | |
| return; | |
| case 0xfd: | |
| if( oric->tapeoffs >= (oric->tapelen-1) ) | |
| { | |
| oric->tapeoffs = oric->tapelen; | |
| oric->tapehitend = 3; | |
| return; | |
| } | |
| oric->tapecount = (oric->tapebuf[oric->tapeoffs]<<9)|(oric->tapebuf[oric->tapeoffs+1]<<1); | |
| oric->tapeoffs += 2; | |
| return; | |
| } | |
| // Invalid data | |
| oric->tapeoffs = oric->tapelen; | |
| oric->tapehitend = 3; | |
| } | |
| // Rewind to the start of the tape | |
| void tape_rewind( struct machine *oric ) | |
| { | |
| oric->nonrawend = 0; | |
| if( oric->rawtape ) | |
| { | |
| oric->tapeout = oric->tapebuf[4]; | |
| via_write_CB1( &oric->via, oric->tapeout ); | |
| oric->tapeoffs = 5; | |
| tape_next_raw_count( oric ); | |
| } | |
| else | |
| { | |
| oric->tapeoffs = 0; | |
| oric->tapebit = 0; | |
| oric->tapecount = 2; | |
| oric->tapeout = 0; | |
| oric->tapedupbytes = 0; | |
| if( oric->tapebuf ) | |
| tape_setup_header( oric ); | |
| } | |
| oric->tapehitend = 0; | |
| oric->tapedelay = 0; | |
| refreshtape = SDL_TRUE; | |
| } | |
| // This is used by the "tapsections" function. It returns the next time | |
| // value from an ort buffer, or -1 for invalid (or if an existing tapsection | |
| // is found) | |
| static int next_ort_value( unsigned char *ortdata, int *offset, int end, SDL_bool *anytapsec ) | |
| { | |
| int val=0, skip; | |
| while ((*offset) < end) | |
| { | |
| switch (ortdata[*offset]) | |
| { | |
| case 0xfc: | |
| if ((*offset) >= (end-1)) return 0; | |
| val = ortdata[(*offset)+1]; | |
| (*offset) += 2; | |
| return val; | |
| case 0xfd: | |
| if ((*offset) >= (end-2)) return 0; | |
| val = (ortdata[(*offset)+1]<<8)|ortdata[(*offset)+2]; | |
| (*offset) += 3; | |
| return val; | |
| case 0xfe: | |
| return 0; | |
| case 0xff: | |
| if ((*offset) >= (end-2)) return 0; | |
| skip = (ortdata[(*offset)+1]<<8)|ortdata[(*offset)+2]; | |
| (*offset)+=3; | |
| if ((*offset) >= (end-skip)) return 0; | |
| (*offset)+=skip; | |
| (*anytapsec) = SDL_TRUE; | |
| break; | |
| default: | |
| return ortdata[(*offset)++]; | |
| } | |
| } | |
| return 0; | |
| } | |
| enum | |
| { | |
| TAPSEC_STATE_SEARCH = 0, | |
| TAPSEC_STATE_LEADER, | |
| TAPSEC_STATE_FINDHEADER, | |
| TAPSEC_STATE_HEADER, | |
| TAPSEC_STATE_FILENAME, | |
| TAPSEC_STATE_FINDDATA, | |
| TAPSEC_STATE_DATA | |
| }; | |
| static int validbyte(int accum) | |
| { | |
| int i, parity = 0; | |
| if ((accum&3)!=1) return 0; | |
| for (i=4; i!=0x800; i<<=1) | |
| if (accum&i) parity ^= 1; | |
| return parity; | |
| } | |
| // This converts oric standard encoded waveforms to decoded TAP sections | |
| static int tapsections( unsigned char *ortbuf, int ortbuflen, unsigned char *scratch, SDL_bool slow ) | |
| { | |
| int ort_offs, tapebit, ort_val, cyccount; | |
| int accum, state, thisbit, leaderstart=0, leadercount=0; | |
| int last_offsets[20], i, bitcount, tapbytes=0; | |
| int data_bytes=0, slow0s=0, slow1s=0; | |
| SDL_bool anytapsec = SDL_FALSE; | |
| ort_offs = 4; | |
| tapebit = ortbuf[ort_offs++]; | |
| cyccount = 0; | |
| state = TAPSEC_STATE_SEARCH; | |
| accum = 0; | |
| bitcount = 0; | |
| for (i=0; i<20; i++) | |
| last_offsets[i] = 5; | |
| // Loop through the ORT data | |
| while (ort_offs < ortbuflen) | |
| { | |
| // Shuffle the offset array | |
| for (i=0; i<19; i++) | |
| last_offsets[i] = last_offsets[i+1]; | |
| last_offsets[19] = ort_offs; | |
| ort_val = next_ort_value(ortbuf, &ort_offs, ortbuflen, &anytapsec); | |
| // Invalid ORT data? | |
| if (ort_val < 1) return ortbuflen; | |
| // Count these cycles | |
| cyccount += ort_val; | |
| // Invert the tape input | |
| tapebit ^= 1; | |
| // Rising edge? | |
| if (tapebit) | |
| { | |
| // smaller than 100? filter it out | |
| if (cyccount < 100) | |
| continue; | |
| // Convert to 1/0 | |
| thisbit = TIME_TO_BIT(cyccount); | |
| // Seen a value that is outside the bounds of Oric standard encoding? | |
| if (thisbit == -1) | |
| { | |
| // if ((state != TAPSEC_STATE_SEARCH) || (slow0s>0) || (slow1s>0)) | |
| // { | |
| // if (cyccount < TAPE_DECODE_1_MIN) printf("%d is too fast\n", cyccount); | |
| // if (cyccount > TAPE_DECODE_0_MAX) printf("%d is too slow\n", cyccount); | |
| // fflush(stdout); | |
| // } | |
| state = TAPSEC_STATE_SEARCH; | |
| cyccount = 0; | |
| accum = 0; | |
| slow1s = 0; | |
| slow0s = 0; | |
| } | |
| else | |
| { | |
| // Slow mode? | |
| if (slow) | |
| { | |
| if (thisbit==1) | |
| { | |
| slow1s++; | |
| slow0s=0; | |
| if (slow1s != 2) | |
| { | |
| if (slow1s >= 8) | |
| slow1s = 0; | |
| cyccount=0; | |
| continue; | |
| } | |
| } | |
| else | |
| { | |
| slow0s++; | |
| slow1s=0; | |
| if (slow0s != 2) | |
| { | |
| if (slow0s >= 4) | |
| slow0s = 0; | |
| cyccount=0; | |
| continue; | |
| } | |
| } | |
| } | |
| // Accumulate this bit | |
| accum = (accum>>1) | (thisbit<<10); | |
| // What are we doing? | |
| switch (state) | |
| { | |
| case TAPSEC_STATE_SEARCH: | |
| // Look for 0x058 (0x16 encoded with start bit and parity) | |
| if ((accum&0x7fe) == 0x58) | |
| { | |
| // printf("Seen 0x16 @ %d\n", ort_offs); | |
| // fflush(stdout); | |
| leaderstart = last_offsets[8]; | |
| leadercount = 1; | |
| state = TAPSEC_STATE_LEADER; | |
| bitcount = 0; | |
| } | |
| else if (validbyte(accum)) | |
| { | |
| // printf("Saw %02X @ %d\n", (accum>>2)&0xff, ort_offs); | |
| // fflush(stdout); | |
| } | |
| break; | |
| case TAPSEC_STATE_LEADER: | |
| bitcount = (bitcount+1)%14; | |
| if ((bitcount==13)&&validbyte(accum)) bitcount = 0; | |
| if (!bitcount) | |
| { | |
| // printf("Byte @ %d is %03x\n", ort_offs, accum&0x7fe); | |
| // fflush(stdout); | |
| if ((accum&0x7fe) != 0x58) | |
| { | |
| // printf("Which is NOT 0x58 :-(\n"); | |
| state = TAPSEC_STATE_SEARCH; | |
| cyccount = 0; | |
| break; | |
| } | |
| leadercount++; | |
| if (leadercount==4) | |
| { | |
| state = TAPSEC_STATE_FINDHEADER; | |
| break; | |
| } | |
| } | |
| break; | |
| case TAPSEC_STATE_FINDHEADER: | |
| bitcount = (bitcount+1)%14; | |
| if ((bitcount==13)&&validbyte(accum)) bitcount = 0; | |
| if (!bitcount) | |
| { | |
| // printf("Byte @ %d is %03x\n", ort_offs, accum&0x7fe); | |
| // fflush(stdout); | |
| if ((accum&0x7fe) == 0x490) // 0x24 fully encoded | |
| { | |
| // printf("At the header...\n"); | |
| // fflush(stdout); | |
| scratch[0] = 0x16; | |
| scratch[1] = 0x16; | |
| scratch[2] = 0x16; | |
| scratch[3] = 0x24; | |
| tapbytes = 4; | |
| state = TAPSEC_STATE_HEADER; | |
| } | |
| } | |
| break; | |
| case TAPSEC_STATE_HEADER: | |
| bitcount = (bitcount+1)%14; | |
| if ((bitcount==13)&&validbyte(accum)) bitcount = 0; | |
| if (!bitcount) | |
| { | |
| // printf("Byte @ %d is %03x (%02X)\n", ort_offs, accum&0x7fe, (accum>>2)&0xff); | |
| // fflush(stdout); | |
| scratch[tapbytes++] = (accum>>2)&0xff; | |
| if (tapbytes == 13) | |
| { | |
| int load_start, load_end; | |
| load_start = (scratch[10]<<8)|scratch[11]; | |
| load_end = (scratch[ 8]<<8)|scratch[ 9]; | |
| // printf("Load start = %04X, Load end = %04X\n", load_start, load_end); | |
| // fflush(stdout); | |
| if (load_end <= load_start) | |
| { | |
| state = TAPSEC_STATE_SEARCH; | |
| cyccount = 0; | |
| break; | |
| } | |
| data_bytes = (load_end-load_start)+1; | |
| state = TAPSEC_STATE_FILENAME; | |
| // printf("Lets try and load %d bytes!\n", data_bytes); | |
| // fflush(stdout); | |
| } | |
| } | |
| break; | |
| case TAPSEC_STATE_FILENAME: | |
| bitcount = (bitcount+1)%14; | |
| if ((bitcount==13)&&validbyte(accum)) bitcount = 0; | |
| if (!bitcount) | |
| { | |
| // printf("Filename byte = %03x (%02x) %c\n", accum&0x7fe, (accum>>2)&0xff, (accum>>2)&0xff); | |
| // fflush(stdout); | |
| scratch[tapbytes++] = (accum>>2)&0xff; | |
| if (scratch[tapbytes-1] == 0) | |
| { | |
| state = TAPSEC_STATE_FINDDATA; | |
| } | |
| } | |
| break; | |
| case TAPSEC_STATE_FINDDATA: | |
| // Look for a valid byte (start bits, valid parity) | |
| if (validbyte(accum)) | |
| { | |
| // printf("Data starts @ %d\n", ort_offs); | |
| // fflush(stdout); | |
| scratch[tapbytes++] = (accum>>2)&0xff; | |
| data_bytes--; | |
| bitcount=0; | |
| state = TAPSEC_STATE_DATA; | |
| } | |
| break; | |
| case TAPSEC_STATE_DATA: | |
| bitcount = (bitcount+1)%14; | |
| if ((bitcount==13)&&validbyte(accum)) bitcount = 0; | |
| if (!bitcount) | |
| { | |
| scratch[tapbytes++] = (accum>>2)&0xff; | |
| data_bytes--; | |
| // printf("%d to go..\n", data_bytes); | |
| // fflush(stdout); | |
| // ?? | |
| if (data_bytes < 0) | |
| { | |
| state = TAPSEC_STATE_SEARCH; | |
| break; | |
| } | |
| if (data_bytes > 0) | |
| break; | |
| if ((!anytapsec) && (leaderstart < 512)) | |
| leaderstart = 5; | |
| // Got a whole standard oric tap section! | |
| ortbuf[leaderstart++] = 0xff; | |
| ortbuf[leaderstart++] = (tapbytes>>8)&0xff; | |
| ortbuf[leaderstart++] = (tapbytes&0xff); | |
| memcpy(&ortbuf[leaderstart], scratch, tapbytes); | |
| leaderstart += tapbytes; | |
| memmove(&ortbuf[leaderstart], &ortbuf[ort_offs], ortbuflen-ort_offs); | |
| ortbuflen -= (ort_offs-leaderstart); | |
| ort_offs = leaderstart; | |
| state = TAPSEC_STATE_SEARCH; | |
| accum = 0; | |
| anytapsec = SDL_TRUE; | |
| // printf("Back to scanning...\n"); | |
| // fflush(stdout); | |
| } | |
| break; | |
| } | |
| } | |
| cyccount = 0; | |
| } | |
| } | |
| return ortbuflen; | |
| } | |
| // This is used by the wav loader routine. It gets a sample from a wav | |
| // file (either 8 or 16bit), and returns it as a signed value. No scaling | |
| // is performed, so it returns -128 to 127 for 8bit wavs, and -32768 to | |
| // 32767 for 16bit wavs. | |
| static inline signed short getsmp(unsigned char *buf, SDL_bool is8bit) | |
| { | |
| return is8bit ? ((short)*buf)-128 : (short)((buf[1]<<8)|buf[0]); | |
| } | |
| // This converts a WAV file to Oricutron's ORT format. It should cope | |
| // with any PCM wav file (stereo, mono, 8bit, 16bit...). It also adjusts | |
| // any DC offset in the recording. It also calls "tapsections" to convert | |
| // any standard oric tape format waveforms it finds into non-raw "tap" | |
| // style sections to enable turbo loading of those parts. | |
| SDL_bool wav_convert( struct machine *oric ) | |
| { | |
| // Chunk pointers | |
| unsigned char *p=oric->tapebuf, *data=NULL, *ortbuf=NULL; | |
| unsigned int i, j, k, l, chunklen, bps=0, freq=0, smpdelta=0, datalen=0, ortlen; | |
| signed int smaxl, sminl, smaxr, sminr, dcoffs=0, dcoffsav; | |
| signed int *lastsmps = NULL; | |
| signed short smp=0; | |
| // Cycles per sample | |
| double cps, count, pcount; | |
| SDL_bool stereo = SDL_FALSE, fmtseen = SDL_FALSE, useright = SDL_FALSE; | |
| // Basic validation | |
| i = 12; | |
| while ((!fmtseen) || (!data)) | |
| { | |
| // Run out of data? | |
| if (i >= (oric->tapelen-8)) return SDL_FALSE; | |
| // Length of this chunk | |
| chunklen = (p[i+7]<<24)|(p[i+6]<<16)|(p[i+5]<<8)|p[i+4]; | |
| // Sane length? | |
| if ((i+chunklen+8) > oric->tapelen) | |
| return SDL_FALSE; | |
| // Format chunk? | |
| if (memcmp(&p[i], "fmt ", 4) == 0) | |
| { | |
| // PCM? | |
| if ((chunklen != 16) || (((p[i+9]<<8)|p[i+8])!=1)) | |
| return SDL_FALSE; | |
| // Channels | |
| switch ((p[i+11]<<8)|(p[i+10])) | |
| { | |
| case 1: stereo = SDL_FALSE; break; | |
| case 2: stereo = SDL_TRUE; break; | |
| default: return SDL_FALSE; break; | |
| } | |
| // Frequency | |
| freq = (p[i+15]<<24)|(p[i+14]<<16)|(p[i+13]<<8)|p[i+12]; | |
| if( !freq ) return SDL_FALSE; | |
| // Sample delta | |
| smpdelta = (p[i+21]<<16)|p[i+20]; | |
| // Bits per sample | |
| bps = (p[i+23]<<16)|p[i+22]; | |
| if ((bps!=8)&&(bps!=16)) return SDL_FALSE; | |
| // Bytes per sample | |
| bps /= 8; | |
| fmtseen = SDL_TRUE; | |
| } | |
| else if (memcmp(&p[i], "data", 4) == 0) | |
| { | |
| data = &p[i+8]; | |
| datalen = chunklen; | |
| } | |
| // Skip chunk | |
| i += chunklen+8; | |
| } | |
| // printf("wavsize = %d\n", datalen); | |
| smaxl = -70000; | |
| sminl = 70000; | |
| smaxr = -70000; | |
| sminr = 70000; | |
| for (i=0; i<datalen; i+=smpdelta) | |
| { | |
| smp = getsmp(&data[i], bps==1); | |
| if (smp < sminl) sminl = smp; | |
| if (smp > smaxl) smaxl = smp; | |
| if (stereo) | |
| { | |
| smp = getsmp(&data[i+bps], bps==1); | |
| if (smp < sminr) sminr = smp; | |
| if (smp > smaxr) smaxr = smp; | |
| } | |
| } | |
| // Use the loudest channel | |
| if ((stereo) && ((smaxr-sminr)>(smaxl-sminl))) | |
| { | |
| useright = SDL_TRUE; | |
| } | |
| else | |
| { | |
| useright = SDL_FALSE; | |
| } | |
| dcoffsav = freq / 700; | |
| if (dcoffsav) | |
| { | |
| lastsmps = malloc(dcoffsav * sizeof(int)); | |
| if (lastsmps) memset(lastsmps, 0, dcoffsav * sizeof(int)); | |
| } | |
| dcoffsav--; | |
| // Now convert to a 1/0 squarewave | |
| j = 0; | |
| for (i=0; i<datalen; i+=smpdelta) | |
| { | |
| if (useright) | |
| smp = getsmp(&data[i+bps], bps==1); | |
| else | |
| smp = getsmp(&data[i], bps==1); | |
| if (lastsmps) | |
| { | |
| smaxl = smp; | |
| sminl = smp; | |
| for (k=0; k<dcoffsav; k++) | |
| { | |
| lastsmps[k] = lastsmps[k+1]; | |
| if (lastsmps[k] < sminl) sminl = lastsmps[k]; | |
| if (lastsmps[k] > smaxl) smaxl = lastsmps[k]; | |
| } | |
| dcoffs = ((smaxl-sminl)/2)+sminl; | |
| } | |
| if (j >= oric->tapelen) | |
| { | |
| // printf("Oh dear\n"); | |
| // fflush(stdout); | |
| break; | |
| } | |
| oric->tapebuf[j++] = (smp>dcoffs) ? 1 : 0; | |
| } | |
| // printf("ortsize = %d\n", j); | |
| // fflush(stdout); | |
| // Calculate cycles per sample | |
| cps = 500000 / ((double)freq); // .ORT is 500khz | |
| // Calculate the length of the .ORT data | |
| ortlen = 5; // header + initial state | |
| i = oric->tapebuf[0]; | |
| count = 0.0f; | |
| pcount = 0.0f; | |
| for (k=1; k<j; k++) | |
| { | |
| if (oric->tapebuf[k] != i) | |
| { | |
| i = oric->tapebuf[k]; | |
| if (((int)count) < 1) | |
| { | |
| // Just a spike? | |
| if (ortlen>5) ortlen--; | |
| count += pcount; | |
| } | |
| else if (((int)count) < 0xfc) | |
| { | |
| ortlen++; | |
| pcount = count; | |
| count = 0.0f; | |
| } | |
| else if (((int)count) < 0x100) | |
| { | |
| ortlen+=2; | |
| pcount = count; | |
| count = 0.0f; | |
| } | |
| else | |
| { | |
| ortlen+=3; | |
| pcount = count; | |
| count = 0.0f; | |
| } | |
| } | |
| count+=cps; | |
| } | |
| free( lastsmps ); | |
| // Allocate a buffer for the converted data | |
| ortbuf = malloc(ortlen); | |
| if (!ortbuf) return SDL_FALSE; | |
| // Write the header | |
| memcpy(ortbuf, "ORT\0", 4); | |
| // Write the ORT data | |
| i = oric->tapebuf[0]; | |
| ortbuf[4] = i; | |
| count = 0.0f; | |
| pcount = 0.0f; | |
| l = 5; | |
| for (k=1; k<j; k++) | |
| { | |
| if (oric->tapebuf[k] != i) | |
| { | |
| i = oric->tapebuf[k]; | |
| if (((int)count) < 1) | |
| { | |
| // Just a spike. Cancel the last swap. | |
| if (l==5) | |
| ortbuf[4] = i; | |
| else | |
| l--; | |
| count += pcount; | |
| } | |
| else if (((int)count) < 0xfc) | |
| { | |
| ortbuf[l++] = count; | |
| pcount = count; | |
| count = 0.0f; | |
| } | |
| else if (((int)count) < 0x100) | |
| { | |
| ortbuf[l++] = 0xfc; | |
| ortbuf[l++] = count; | |
| pcount = count; | |
| count = 0.0f; | |
| } | |
| else | |
| { | |
| ortbuf[l++] = 0xfd; | |
| ortbuf[l++] = (((int)count)>>8)&0xff; | |
| ortbuf[l++] = ((int)count)&0xff; | |
| pcount = count; | |
| count = 0.0f; | |
| } | |
| } | |
| count+=cps; | |
| } | |
| // Look for any standard oric encoded parts to convert | |
| // to non-raw "tap" sections | |
| ortlen = tapsections(ortbuf, ortlen, oric->tapebuf, SDL_FALSE); | |
| ortlen = tapsections(ortbuf, ortlen, oric->tapebuf, SDL_TRUE); | |
| // Substitute the ORT data for the original WAV data | |
| free(oric->tapebuf); | |
| oric->tapebuf = ortbuf; | |
| oric->tapelen = ortlen; | |
| return SDL_TRUE; | |
| } | |
| // Insert a new tape image | |
| SDL_bool tape_load_tap( struct machine *oric, char *fname ) | |
| { | |
| FILE *f; | |
| // First make sure the image file exists | |
| f = fopen( fname, "rb" ); | |
| if( !f ) return SDL_FALSE; | |
| // Eject any old image | |
| tape_eject( oric ); | |
| // Get the image size | |
| fseek( f, 0, SEEK_END ); | |
| oric->tapelen = (int)ftell( f ); | |
| fseek( f, 0, SEEK_SET ); | |
| if( oric->tapelen <= 4 ) // Even worth loading it? | |
| { | |
| fclose( f ); | |
| return SDL_FALSE; | |
| } | |
| // Allocate memory for the tape image and read it in | |
| oric->tapebuf = malloc( oric->tapelen+1 ); | |
| if( !oric->tapebuf ) | |
| { | |
| fclose( f ); | |
| oric->tapelen = 0; | |
| return SDL_FALSE; | |
| } | |
| if( fread( &oric->tapebuf[0], oric->tapelen, 1, f ) != 1 ) | |
| { | |
| fclose( f ); | |
| tape_eject( oric ); | |
| return SDL_FALSE; | |
| } | |
| fclose( f ); | |
| // WAV | |
| if ((oric->tapelen >= 36) && | |
| (memcmp(oric->tapebuf, "RIFF", 4) == 0) && | |
| (memcmp(oric->tapebuf+8, "WAVE", 4) == 0)) | |
| { | |
| if (!wav_convert( oric )) | |
| { | |
| msgbox( oric, MSGBOX_OK, "Invalid wav file" ); | |
| tape_eject( oric ); | |
| return SDL_FALSE; | |
| } | |
| oric->rawtape = SDL_TRUE; | |
| } | |
| // ORT | |
| else if (memcmp(oric->tapebuf, "ORT\0", 4) == 0) | |
| { | |
| oric->rawtape = SDL_TRUE; | |
| } | |
| // TAP | |
| else if (memcmp(oric->tapebuf, "\x16\x16\x16", 3) == 0) | |
| { | |
| oric->rawtape = SDL_FALSE; | |
| // I give up trying to do anything clever. | |
| // Just allow an extra byte for broken tape images. | |
| oric->tapelen++; | |
| } | |
| // ??? | |
| else | |
| { | |
| tape_eject( oric ); | |
| msgbox( oric, MSGBOX_OK, "Unrecognised tape format" ); | |
| return SDL_FALSE; | |
| } | |
| // Rewind the tape | |
| tape_rewind( oric ); | |
| // Make a displayable version of the image filename | |
| if( strlen( fname ) > 31 ) | |
| { | |
| strncpy( oric->tapename, &fname[strlen(fname)-31], 32 ); | |
| oric->tapename[0] = 22; | |
| } else { | |
| strncpy( oric->tapename, fname, 32 ); | |
| } | |
| oric->tapename[31] = 0; | |
| // Show it in the popup | |
| tape_popup( oric ); | |
| return SDL_TRUE; | |
| } | |
| void tape_autoinsert( struct machine *oric ) | |
| { | |
| char *odir; | |
| int i; | |
| SDL_bool tape_found; | |
| if( strncmp( (char *)&oric->mem[oric->pch_fd_getname_addr], oric->lasttapefile, 16 ) == 0 ) | |
| oric->mem[oric->pch_fd_getname_addr] = 0; | |
| // Try and load the tape image | |
| strcpy( tapefile, oric->lasttapefile ); | |
| i = (int)strlen(tapefile); | |
| odir = getcwd( NULL, 0 ); | |
| chdir( tapepath ); | |
| tape_found = tape_load_tap( oric, tapefile ); | |
| if( !tape_found ) | |
| { | |
| // Try appending .tap | |
| strcpy( &tapefile[i], ".tap" ); | |
| tape_found = tape_load_tap( oric, tapefile ); | |
| } | |
| if( !tape_found ) | |
| { | |
| // Try appending .ort | |
| strcpy( &tapefile[i], ".ort" ); | |
| tape_load_tap( oric, tapefile ); | |
| } | |
| if( oric->tapebuf ) | |
| { | |
| // We already inserted this one. Don't re-insert it when we get to the end. */ | |
| oric->lasttapefile[0] = 0; | |
| } | |
| chdir( odir ); | |
| free( odir ); | |
| } | |
| void tape_stop_savepatch( struct machine *oric ) | |
| { | |
| if( !oric->tsavf ) return; | |
| if( oric->tsavf == oric->tapecap ) | |
| { | |
| unsigned char bufdata[2]; | |
| fseek( oric->tsavf, oric->tapecapsavoffs, SEEK_SET ); | |
| bufdata[0] = (oric->tapecapsavbytes>>8)&0xff; | |
| bufdata[1] = oric->tapecapsavbytes&0xff; | |
| fwrite( bufdata, 2, 1, oric->tsavf ); | |
| oric->tapecapsavoffs = 0; | |
| oric->tapecapsavbytes = 0; | |
| } | |
| else | |
| { | |
| fclose( oric->tsavf ); | |
| } | |
| oric->tsavf = NULL; | |
| } | |
| // Do the tape patches (must be done after every m6502 setcycles) | |
| void tape_patches( struct machine *oric ) | |
| { | |
| Sint32 i, j; | |
| setromon( oric) ; | |
| if( oric->romon ) | |
| { | |
| if( oric->pch_fd_available ) | |
| { | |
| // Patch CLOAD to insert a tape image specified. | |
| // Unfortunately the way the ROM works means we | |
| // only have up to 16 chars. | |
| if( ( oric->cpu.calcpc == oric->pch_fd_cload_getname_pc ) || | |
| ( oric->cpu.calcpc == oric->pch_fd_recall_getname_pc ) ) | |
| { | |
| // Read in the filename from RAM | |
| for( i=0; i<16; i++ ) | |
| { | |
| j = oric->cpu.read( &oric->cpu, oric->pch_fd_getname_addr+i ); | |
| if( !j ) break; | |
| oric->lasttapefile[i] = j; | |
| } | |
| oric->lasttapefile[i] = 0; | |
| if( ( oric->cpu.read( &oric->cpu, oric->pch_fd_getname_addr ) != 0 ) && | |
| ( oric->autoinsert ) ) | |
| { | |
| // Only do this if there is no tape inserted, or we're at the | |
| // end of the current tape, or the filename ends in .TAP, .ORT or .WAV | |
| if( ( !oric->tapebuf ) || | |
| ( oric->tapeoffs >= oric->tapelen -1 ) || | |
| ( ( i > 3 ) && ( strcasecmp( &oric->lasttapefile[i-4], ".tap" ) == 0 ) ) || | |
| ( ( i > 3 ) && ( strcasecmp( &oric->lasttapefile[i-4], ".ort" ) == 0 ) ) || | |
| ( ( i > 3 ) && ( strcasecmp( &oric->lasttapefile[i-4], ".wav" ) == 0 ) ) ) | |
| { | |
| tape_autoinsert( oric ); | |
| oric->cpu.write( &oric->cpu, oric->pch_fd_getname_addr, 0 ); | |
| } | |
| } | |
| } | |
| else if( ( ( oric->cpu.calcpc == oric->pch_fd_csave_getname_pc ) || | |
| ( oric->cpu.calcpc == oric->pch_fd_store_getname_pc ) ) && | |
| ( ( !oric->tapecap ) || ( oric->tapeturbo ) ) ) | |
| { | |
| // Did we miss the end of a previous one? | |
| if( oric->tsavf ) | |
| tape_stop_savepatch( oric ); | |
| // If we're doing tape capture, we can just use that | |
| if( oric->tapecap ) | |
| { | |
| oric->tsavf = oric->tapecap; | |
| if( oric->tapecapcount < 0 ) | |
| fputc( 0, oric->tapecap ); | |
| fputc( 0xff, oric->tapecap ); | |
| oric->tapecapsavoffs = (int)ftell(oric->tapecap); | |
| fputc( 0x00, oric->tapecap ); | |
| fputc( 0x00, oric->tapecap ); | |
| oric->tapecapsavbytes = 0; | |
| } | |
| else | |
| { | |
| char *odir = NULL; | |
| // Read in the filename from RAM | |
| for( i=0; i<16; i++ ) | |
| { | |
| j = oric->cpu.read( &oric->cpu, oric->pch_fd_getname_addr+i ); | |
| if( !j ) break; | |
| tmptapename[i] = j; | |
| } | |
| tmptapename[i] = 0; | |
| // If no name, prompt for one | |
| if( tmptapename[0] == 0 ) | |
| { | |
| if( !filerequester( oric, "Save to tape", tapepath, tmptapename, FR_TAPESAVETAP ) ) | |
| tmptapename[0] = 0; | |
| } | |
| // If there is one, append .TAP | |
| if( tmptapename[0] ) | |
| { | |
| if( (strlen(tmptapename) < 4) || (strcasecmp(&tmptapename[strlen(tmptapename)-4], ".tap") != 0) ) | |
| { | |
| if (strlen(tmptapename)+5<sizeof(tmptapename)) // if we have enough space to add the .tap | |
| strncat(tmptapename, ".tap", strlen(tmptapename)+5); | |
| tmptapename[sizeof(tmptapename)-1] = 0; | |
| } | |
| } | |
| odir = getcwd( NULL, 0 ); | |
| if( odir ) | |
| { | |
| chdir( tapepath ); | |
| oric->tsavf = fopen(tmptapename, "wb"); | |
| chdir( odir ); | |
| free( odir ); | |
| } | |
| } | |
| } | |
| else if( ( oric->cpu.calcpc == oric->pch_tt_csave_end_pc ) || | |
| ( oric->cpu.calcpc == oric->pch_tt_store_end_pc ) ) | |
| { | |
| SDL_bool justtap = (oric->tsavf != oric->tapecap); | |
| tape_stop_savepatch( oric ); | |
| if( justtap ) | |
| { | |
| // filetmp defined in gui.c as filetmp[4096+512+4] | |
| snprintf( filetmp, 4096+512+4-1, "\x0f\x10 Saved to %s", tmptapename ); | |
| filetmp[31] = 0; | |
| if (strlen(tmptapename) > 20) | |
| { | |
| filetmp[30] = '\x16'; | |
| } | |
| do_popup( oric, filetmp ); | |
| } | |
| tmptapename[0] = 0; | |
| } | |
| } | |
| if( ( oric->pch_tt_save_available ) && ( ( !oric->tapecap ) || ( oric->tapeturbo ) ) ) | |
| { | |
| if( oric->cpu.calcpc == oric->pch_tt_putbyte_pc ) | |
| { | |
| if( oric->tsavf ) | |
| { | |
| fputc( oric->cpu.a, oric->tsavf ); | |
| if( oric->tsavf == oric->tapecap ) | |
| oric->tapecapsavbytes++; | |
| } | |
| oric->cpu.calcpc = oric->pch_tt_putbyte_end_pc; | |
| oric->cpu.calcop = oric->cpu.read( &oric->cpu, oric->cpu.calcpc ); | |
| } | |
| else if( oric->cpu.calcpc == oric->pch_tt_writeleader_pc ) | |
| { | |
| if( oric->tsavf ) | |
| { | |
| unsigned char leader[] = { 0x16, 0x16, 0x16, 0x16 }; | |
| fwrite( leader, 4, 1, oric->tsavf ); | |
| if( oric->tsavf == oric->tapecap ) | |
| oric->tapecapsavbytes += 4; | |
| } | |
| oric->cpu.calcpc = oric->pch_tt_writeleader_end_pc; | |
| oric->cpu.calcop = oric->cpu.read( &oric->cpu, oric->cpu.calcpc ); | |
| } | |
| } | |
| } | |
| // Only do turbotape past this point! | |
| // No tape? Motor off? | |
| if( ( !oric->tapebuf ) || ( !oric->tapemotor ) ) | |
| { | |
| if( ( oric->pch_tt_available ) && ( oric->tapeturbo ) && ( oric->romon ) && ( oric->cpu.calcpc == oric->pch_tt_getsync_pc ) ) | |
| oric->tapeturbo_syncstack = oric->cpu.sp; | |
| return; | |
| } | |
| if( ( oric->tapeturbo_forceoff ) && ( oric->pch_tt_available ) && ( oric->romon ) && ( oric->cpu.calcpc == oric->pch_tt_getsync_end_pc ) ) | |
| oric->tapeturbo_forceoff = SDL_FALSE; | |
| // Don't do turbotape if we have a raw tape | |
| // Turbotape can't work with rawtape. TODO: Auto enable warpspeed when CLOADing/CSAVEing rawtape | |
| if(( oric->rawtape ) && ( oric->tapeoffs >= oric->nonrawend )) return; | |
| if(( !oric->tapebuf ) || ( oric->tapeoffs >= oric->tapelen )) return; | |
| // Maybe do turbotape | |
| if( ( oric->pch_tt_available ) && ( oric->tapeturbo ) && ( !oric->tapeturbo_forceoff ) && ( oric->romon ) ) | |
| { | |
| SDL_bool dosyncpatch; | |
| dosyncpatch = SDL_FALSE; | |
| if( oric->cpu.calcpc == oric->pch_tt_getsync_loop_pc ) | |
| { | |
| // Cassette sync was called when there was no valid | |
| // tape to sync, so the normal cassette sync hack failed | |
| // and now we're stuck looking for a signal that will never | |
| // arrive. We have to recover back to the end of the cassette | |
| // sync routine. | |
| // Did we spot the entry into the cassette sync routine? | |
| if( oric->tapeturbo_syncstack == -1 ) | |
| { | |
| // No. Give up. | |
| oric->tapeturbo_forceoff = SDL_TRUE; | |
| return; | |
| } | |
| // Restore the stack | |
| oric->cpu.sp = oric->tapeturbo_syncstack; | |
| oric->tapeturbo_syncstack = -1; | |
| dosyncpatch = SDL_TRUE; | |
| } | |
| if( ( oric->cpu.calcpc == oric->pch_tt_getsync_pc ) || ( dosyncpatch ) ) | |
| { | |
| // Currently at a sync byte? | |
| if( oric->tapebuf[oric->tapeoffs] != 0x16 ) | |
| { | |
| // Find the next sync byte | |
| do | |
| { | |
| oric->tapeoffs++; | |
| // Give up at end of image | |
| if( oric->tapeoffs >= oric->tapelen ) | |
| { | |
| refreshtape = SDL_TRUE; | |
| return; | |
| } | |
| } while( oric->tapebuf[oric->tapeoffs] != 0x16 ); | |
| } | |
| // "Jump" to the end of the cassette sync routine | |
| oric->cpu.calcpc = oric->pch_tt_getsync_end_pc; | |
| oric->cpu.calcop = oric->cpu.read(&oric->cpu, oric->cpu.calcpc); | |
| return; | |
| } | |
| if( oric->cpu.calcpc == oric->pch_tt_readbyte_pc ) | |
| { | |
| // Read the next byte directly into A | |
| oric->cpu.a = oric->tapebuf[oric->tapeoffs++]; | |
| // Set flags | |
| oric->cpu.f_z = oric->cpu.a == 0; | |
| oric->cpu.f_c = oric->pch_tt_readbyte_setcarry ? 1 : 0; | |
| // Simulate the effects of the read byte routine | |
| if( oric->pch_tt_readbyte_storebyte_addr != -1 ) oric->cpu.write( &oric->cpu, oric->pch_tt_readbyte_storebyte_addr, oric->cpu.a ); | |
| if( oric->pch_tt_readbyte_storezero_addr != -1 ) oric->cpu.write( &oric->cpu, oric->pch_tt_readbyte_storezero_addr, 0x00 ); | |
| // Jump to the end of the read byte routine | |
| oric->cpu.calcpc = oric->pch_tt_readbyte_end_pc; | |
| oric->cpu.calcop = oric->cpu.read( &oric->cpu, oric->cpu.calcpc ); | |
| if( oric->tapeoffs >= oric->tapelen ) refreshtape = SDL_TRUE; | |
| } | |
| } | |
| } | |
| // Emulate the specified cpu-cycles time for the tape | |
| void tape_ticktock( struct machine *oric, int cycles ) | |
| { | |
| // Update the counter since last PB7 toggle | |
| if( ( oric->tapecap ) && ( oric->tapecapcount != -1 ) ) | |
| oric->tapecapcount += cycles; | |
| // The VSync hack is triggered in the video emulation | |
| // but actually handled here, since the VSync signal | |
| // is read into the tape input. The video emulation | |
| // puts 260+12 microseconds into this counter, | |
| // which we count down here. | |
| // See more comments about VSync in ula.c | |
| // If the VSync hack is active, we pull CB1 low | |
| // while the above counter is active and it's value | |
| // is less 260. | |
| if( oric->vsynchack ) | |
| { | |
| unsigned char j = (0 == oric->vsync || 260 < oric->vsync); | |
| if( oric->via.cb1 != j ) | |
| via_write_CB1( &oric->via, j ); | |
| } | |
| if( oric->vsync > 0 ) | |
| { | |
| oric->vsync -= cycles; | |
| if( oric->vsync < 0 ) | |
| oric->vsync = 0; | |
| } | |
| // No tape? Motor off? | |
| if( ( !oric->tapebuf ) || ( !oric->tapemotor ) ) | |
| return; | |
| // Tape offset outside the tape image limits? | |
| if( ( oric->tapeoffs < 0 ) || ( oric->tapeoffs >= oric->tapelen ) ) | |
| { | |
| if( oric->tapehitend > 2 ) | |
| { | |
| // Try to autoinsert a tape image | |
| if( ( oric->lasttapefile[0] ) && ( oric->autoinsert ) ) | |
| { | |
| tape_autoinsert( oric ); | |
| oric->lasttapefile[0] = 0; | |
| } | |
| } | |
| } | |
| // Abort if turbotape is on | |
| if( ( oric->pch_tt_available ) && ( oric->tapeturbo ) && ( !oric->tapeturbo_forceoff ) && ( oric->romon ) && ( !oric->rawtape ) ) | |
| return; | |
| if( oric->tapehitend > 2 ) | |
| return; | |
| if( ( oric->tapehdrend != 0 ) && ( oric->tapeoffs == oric->tapehdrend ) ) | |
| { | |
| oric->tapedelay = 1281; | |
| oric->tapehdrend = 0; | |
| } | |
| // No turbotape. Do "real" tape emulation. | |
| // Count down the cycle counter | |
| if( oric->tapecount > cycles ) | |
| { | |
| oric->tapecount -= cycles; | |
| if( oric->tapedelay > 0 ) | |
| { | |
| oric->tapedelay -= cycles; | |
| if( oric->tapedelay < 0 ) | |
| oric->tapedelay = 1; | |
| } | |
| return; | |
| } | |
| // Toggle the tape input | |
| oric->tapeout ^= 1; | |
| if( !oric->vsynchack ) | |
| { | |
| // Update the audio if tape noise is enabled | |
| if( oric->tapenoise ) | |
| { | |
| ay_lockaudio( &oric->ay ); // Gets unlocked at the end of each frame | |
| if( oric->ay.do_logcycle_reset ) | |
| { | |
| oric->ay.logcycle = oric->ay.newlogcycle; | |
| oric->ay.do_logcycle_reset = SDL_FALSE; | |
| } | |
| if( oric->ay.tlogged < TAPELOG_SIZE ) | |
| { | |
| oric->ay.tapelog[oric->ay.tlogged ].cycle = oric->ay.logcycle; | |
| oric->ay.tapelog[oric->ay.tlogged++].val = oric->tapeout; | |
| } | |
| } | |
| // Put tape signal onto CB1 | |
| via_write_CB1( &oric->via, oric->tapeout ); | |
| } | |
| if( oric->tapedelay > 0 ) | |
| { | |
| oric->tapedelay -= cycles; | |
| if( oric->tapedelay <= 0) | |
| { | |
| if( oric->tapebit == 1 ) | |
| oric->tapedelay = 1; | |
| else | |
| oric->tapedelay = 0; | |
| } | |
| oric->tapecount = TAPE_1_PULSE; | |
| return; | |
| } | |
| if( oric->tapeoffs >= oric->tapelen ) | |
| { | |
| switch( oric->tapehitend ) | |
| { | |
| case 0: oric->tapecount = TAPE_1_PULSE; break; | |
| case 1: oric->tapecount = 0x36*2; break; | |
| } | |
| oric->tapehitend++; | |
| refreshtape = SDL_TRUE; | |
| return; | |
| } | |
| // Raw tape | |
| if( oric->rawtape ) | |
| { | |
| // In a non-raw section? | |
| if( oric->tapeoffs >= oric->nonrawend ) | |
| { | |
| tape_next_raw_count( oric ); | |
| if( oric->tapehitend > 2 ) | |
| refreshtape = SDL_TRUE; | |
| return; | |
| } | |
| } | |
| // .TAP real tape simulation | |
| // Tape signal rising edge | |
| if( oric->tapeout ) | |
| { | |
| switch( oric->tapebit ) | |
| { | |
| case 0: // Start of a new byte. Send a 1 pulse | |
| oric->tapetime = TAPE_1_PULSE; | |
| break; | |
| case 1: // Then a sync pulse (0) | |
| oric->tapetime = TAPE_0_PULSE; | |
| oric->tapeparity = 1; | |
| break; | |
| default: // For bit numbers 2 to 9, send actual byte bits 0 to 7 | |
| if( oric->tapebuf[oric->tapeoffs]&(1<<(oric->tapebit-2)) ) | |
| { | |
| oric->tapetime = TAPE_1_PULSE; | |
| oric->tapeparity ^= 1; | |
| } else { | |
| oric->tapetime = TAPE_0_PULSE; | |
| } | |
| break; | |
| case 10: // Then a parity bit | |
| if( oric->tapeparity ) | |
| oric->tapetime = TAPE_1_PULSE; | |
| else | |
| oric->tapetime = TAPE_0_PULSE; | |
| break; | |
| case 11: | |
| case 12: | |
| case 13: | |
| oric->tapetime = TAPE_1_PULSE; | |
| break; | |
| } | |
| // Move on to the next bit | |
| oric->tapebit = (oric->tapebit+1)%14; | |
| if( !oric->tapebit ) | |
| { | |
| if( oric->tapedupbytes > 0 ) | |
| oric->tapedupbytes--; | |
| else | |
| oric->tapeoffs++; | |
| } | |
| } | |
| oric->tapecount = oric->tapetime; | |
| } | |