/* * Copyright (C) 1996-1998 Szeredi Miklos * Email: mszeredi@inf.bme.hu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. See the file COPYING. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "zxmisc.h" #include "sptape.h" #include "tapefile.h" #include "spperif.h" #include "z80.h" #include "interf.h" #include "spconf.h" #include #include #include #include #define MAXLINELEN 256 int spt_auto_stop = 1; static int playing = 0; static int paused = 0; static char tapename[MAXFILENAME]; static int tapetype; #define EARBIT 0x40 #define IMPBUFLEN 1024 static int ingroup; static int segtype; static int lastdatak; static int currseg; static void stop_playing(void) { if(playing) close_tapefile(); playing = 0; } static void pause_playing(void) { if(playing) { paused = 1; playing = 0; } } static void unpause_playing(void) { if(paused) { paused = 0; playing = 1; segtype = SEG_END; } } #define MAXDESCLEN 256 static void put_seg_desc(void) { if(segtype != SEG_VIRTUAL) { if(segtype > SEG_END) { if(!ingroup) { long len; int i, ml; char *me; len = get_seglen(); me = msgbuf; rb->snprintf(me,MAXDESCLEN, "%4d: ", currseg); me = me+rb->strlen(me); if(segtype >= SEG_DATA && len) { rb->snprintf(me,MAXDESCLEN, "%5ld bytes, ", len); me = me+rb->strlen(me); } ml = 0; for(i = 0; seg_desc[i]; i++) { if(seg_desc[i] == '\n') { *me = '\0'; put_msg(msgbuf); me = msgbuf; rb->snprintf(me,MAXDESCLEN, " "); me = me+rb->strlen(me); ml = 0; } else { if(ml < MAXDESCLEN) *me++ = seg_desc[i]; ml++; } } *me = '\0'; put_msg(msgbuf); } else { rb->snprintf(msgbuf,MAXDESCLEN, "%4d:", currseg); put_tmp_msg(msgbuf); } } else put_msg(seg_desc); } #ifdef DEBUG_TAPE else fprintf(stderr, "virtual segment\n"); #endif } static void get_next_segment(void) { int propseg; do { propseg = 1; segtype = next_segment(); currseg = segment_pos(); put_seg_desc(); switch(segtype) { case SEG_ERROR: case SEG_END: stop_playing(); break; case SEG_STOP: pause_playing(); put_msg(" * Tape paused; Press Ctrl-o to restart * "); break; case SEG_SKIP: propseg = 0; break; case SEG_GRP_BEG: ingroup = 1; propseg = 0; break; case SEG_GRP_END: ingroup = 0; propseg = 0; break; } } while(!propseg); lastdatak = 0; } void play_tape(void) { static dbyte impbuf[IMPBUFLEN]; static int clevel; static dbyte *impbufp; static int impbufrem; static long imprem; static int cleared_buffers = 1; int tsn; dbyte *ibp; byte *tsp; int ibr; long ir; int cl; signed char *op; int ov; int ca; tsp = sp_tape_impinfo; op = sp_tape_sound; tsn = TMNUM; if(!playing) { if(cleared_buffers) return; sp_playing_tape = 0; if(!clevel) { ca = CHKTICK; clevel = ~clevel; } else { ca = 0; cleared_buffers = 1; } imprem = CHKTICK * TMNUM; } else if(!sp_playing_tape) { sp_playing_tape = 1; cleared_buffers = 0; impbufrem = 0; imprem = 0; clevel = get_level() ? ~(0) : 0; if(clevel) ca = 0; else ca = 1; } else ca = 0; #ifdef DEBUG_TAPE if(((clevel ? 1 : 0) ^ (DANM(ula_inport) & EARBIT ? 1 : 0) ^ (DANM(imp_change) ? 1 : 0) ^ (ca ? 1 : 0)) == 0) fprintf(stderr, "Levels don't match %i %i\n", imprem, impbufrem); #endif cl = clevel; ibr = impbufrem; ir = imprem; ibp = impbufp; if(cl) ov = CHKTICK/2; else ov = -(CHKTICK/2); do { if(ir > 0) { *tsp++ = ca; *op++ = ov; ir -= CHKTICK; tsn--; if(!tsn) goto done; if(cl) ov = CHKTICK/2; else ov = -(CHKTICK/2); while(ir > 0) { *tsp++ = 0; *op++ = ov; ir -= CHKTICK; tsn--; if(!tsn) goto done; } ca = 0; } if(ibr) { if(!ca) { if(cl) { ov += ir; ca = (CHKTICK/2) - ov + 1; } else { ov -= ir; ca = ov + (CHKTICK/2) + 1; } } else { ca = 0; if(cl) ov += ir; else ov -= ir; } ir += *ibp++; ibr--; cl = ~cl; } else { ibp = impbuf; do { ibr = next_imps(impbuf, IMPBUFLEN, CHKTICK * tsn); if(ibr) break; get_next_segment(); if(!playing) { if(!cl) { if(ca) ca = 0; else ca = CHKTICK; cl = ~cl; } ir = tsn*CHKTICK; ov = -(CHKTICK/2); break; } } while(1); } } while(1); done: clevel = cl; impbufrem = ibr; imprem = ir; impbufp = ibp; if(segtype >= SEG_DATA) { int datak; datak = (int) (get_segpos() / 1000); if(datak > lastdatak) { if(ingroup) rb->snprintf(msgbuf,MAXDESCLEN, "%4d: ", currseg); else rb->snprintf(msgbuf,MAXDESCLEN, " "); rb->snprintf(msgbuf+rb->strlen(msgbuf),MAXDESCLEN, "%3dk", datak); put_tmp_msg(msgbuf); lastdatak = datak; } } } /* 2168 2168 (9-10) 667 735 (2-4) 855 855 (3-5) 1710 1710 (7-9) 945 (4-5) hosszu: 7..9 rovid: 2..5 */ #define MICBIT 0x08 #define RC_NONE 0 #define RC_LEADER 1 #define RC_SYNC 2 #define RC_DATA 3 #define MAXLEN TMNUM #define LEADER_MIN 9 #define LEADER_MAX 10 #define SYNC_MIN 2 #define SYNC_MAX 4 #define BIT0_MIN 3 #define BIT0_MAX 5 #define BIT1_MIN 7 #define BIT1_MAX 9 #define LEADER_MIN_COUNT 512 #if 0 static int rec_segment; static int rec_state = RC_NONE; static byte *recbuf = NULL; static const char *waitchars = "-\\|/"; #endif static int recording = 0; void rec_tape(void) { #if 0 static byte lastmic = 0; static int lastlen = 0; static int whole; static int leadercount; static byte data; static byte parity; static int bitnum; static int bytecount; static int recbufsize; static int firsthalf; static int frameswait = 0; int tsl; byte *fep; int thishalf; if(!recording) return; for(fep = sp_fe_outport_time, tsl = TMNUM; tsl; fep++, tsl--) { lastlen++; if((*fep & MICBIT) == lastmic) { if(lastlen < MAXLEN) continue; } else lastmic = ~lastmic & MICBIT; switch(rec_state) { case RC_NONE: if(lastlen >= LEADER_MIN && lastlen <= LEADER_MAX) { rec_state = RC_LEADER; leadercount = 0; break; } if((frameswait++ & 15)) break; frameswait &= 0x3F; sprintf(msgbuf, " %s: WAITING %c", tapename, waitchars[(frameswait >> 4)]); put_tmp_msg(msgbuf); break; case RC_LEADER: if(lastlen >= LEADER_MIN && lastlen <= LEADER_MAX) { leadercount++; if(leadercount == LEADER_MIN_COUNT) { sprintf(msgbuf, " %s: LEADER", tapename); put_tmp_msg(msgbuf); } break; } if(leadercount >= LEADER_MIN_COUNT && lastlen >= SYNC_MIN && lastlen <= SYNC_MAX) rec_state = RC_SYNC; else rec_state = RC_NONE; break; case RC_SYNC: if(lastlen >= SYNC_MIN && lastlen <= SYNC_MAX) { rec_state = RC_DATA; whole = 0; data = 0; bitnum = 0; bytecount = 0; recbuf = NULL; recbufsize = 0; parity = 0; sprintf(msgbuf, " %s: DATA", tapename); put_tmp_msg(msgbuf); } else rec_state = RC_NONE; break; case RC_DATA: thishalf = -1; if(lastlen >= BIT0_MIN && lastlen <= BIT0_MAX) thishalf = 0; else if(lastlen >= BIT1_MIN && lastlen <= BIT1_MAX) thishalf = 1; if(thishalf < 0 || (whole && firsthalf != thishalf)) { char filename[11]; int filesize; int filetype; sprintf(msgbuf, "%s: %03d", tapename, rec_segment); if(bytecount >= 1) { sprintf(msgbuf+strlen(msgbuf), " %02X %5d %3s", recbuf[0], bytecount-2, parity == 0 ? "OK" : "ERR"); if(recbuf[0] == 0 && bytecount - 2 >= 17) { filetype = recbuf[1]; strncpy(filename, (char*) recbuf+2, 10); filename[10] = '\0'; filesize = recbuf[12] + (recbuf[13] << 8); sprintf(msgbuf+strlen(msgbuf), " %02X %10s %5i", filetype, filename, filesize); } } put_msg(msgbuf); putc(bytecount & 0xFF, tapefp); putc((bytecount >> 8) & 0xFF, tapefp); fwrite(recbuf, 1, (size_t) bytecount, tapefp); fflush(tapefp); rec_segment++; free(recbuf); recbuf = NULL; rec_state = RC_NONE; break; } if(!whole) { whole = 1; firsthalf = thishalf; } else { whole = 0; data |= thishalf; bitnum++; if(bitnum == 8) { bitnum = 0; if(recbufsize <= bytecount) { recbufsize += 1024; recbuf = realloc(recbuf, (size_t) recbufsize); if(recbuf == NULL) { //fprintf(stderr, "Out of memory\n"); exit(1); } } recbuf[bytecount] = data; parity = parity ^ data; data = 0; bytecount++; if(!(bytecount & 1023)) { sprintf(msgbuf, " %s: DATA %i kB", tapename, bytecount >> 10); put_tmp_msg(msgbuf); } } data <<= 1; } break; } lastlen = 0; } #endif } static void stop_recording(void) { #if 0 if(recording) { recording = 0; free(recbuf); recbuf = NULL; rb->close(tapefp); } #endif } static void restart_playing(void) { int res; struct tape_options tapeopt; if(tapetype < 0) { tapetype = TAP_TZX; res = open_tapefile(tapename, tapetype); if(!res) { tapetype = TAP_TAP; res = open_tapefile(tapename, tapetype); } } else res = open_tapefile(tapename, tapetype); if(!res) { put_msg(seg_desc); return; } INITTAPEOPT(tapeopt); #ifndef DEBUG_Z80 tapeopt.blanknoise = 1; #endif set_tapefile_options(&tapeopt); if(currseg) { res = goto_segment(currseg); if(!res) { put_msg(seg_desc); return; } } playing = 1; segtype = SEG_END; } void pause_play(void) { if(playing) { pause_playing(); put_msg(" * Tape paused * "); goto_segment(currseg); } else unpause_playing(); } void start_play_file_type(char *name, int seg, int type) { int filetype = FT_TAPEFILE; rb->strlcpy(tapename, name, MAXFILENAME-10 + 1); currseg = seg; tapetype = type; spcf_find_file_type(tapename, &filetype, &tapetype); if(currseg < 0) currseg = 0; ingroup = 0; restart_playing(); } void start_play(void) { char *name; int t; int seg; if(playing || paused || recording) { put_msg(" * Stop the tape first! * "); return; } put_msg("Enter tape file path:"); name = spif_get_tape_fileinfo(&seg, &t); if(name == NULL) return; start_play_file_type(name, seg, -1); } void stop_play(void) { if(playing || paused) { put_msg(" * Stopped playing * "); if(playing) stop_playing(); if(paused) paused = 0; } else if(recording) { #if 0 sprintf(msgbuf, " * Stopped recording tape `%s' * ", tapename); put_msg(msgbuf); #endif stop_recording(); } } void start_rec(void) { #if 0 char *name; if(playing || paused || recording) return; put_msg("Enter tape file to record (default: '.tap'):"); name = spif_get_filename(); if(name == NULL) return; strncpy(tapename, name, MAXFILENAME-10); tapename[MAXFILENAME-10] = '\0'; if(!check_ext(tapename, "tap")) add_extension(tapename, "tap"); tapefp = fopen(tapename, "ab"); if(tapefp == NULL) { sprintf(msgbuf, "Could not open tape file `%s', %s", tapename, strerror(errno)); put_msg(msgbuf); return; } recording = 1; rec_segment = 0; rec_state = RC_NONE; sprintf(msgbuf, "Recordind tape file `%s'. To stop press Ctrl-s", tapename); put_msg(msgbuf); #endif } #include "spkey_p.h" #define CF 0x01 #define ZF 0x40 void qload(void) { byte type, parity; dbyte length, dest, dtmp; int verify, success, firstbyte; int nextdata; if(recording) { put_msg("Can't quick load tape, because recording"); return; } do { if(!playing) { if(paused) unpause_playing(); else { start_play(); } } if(!playing) { put_msg("Not quick loading tape"); return; } while(playing && (segtype != SEG_DATA || get_segpos() > 0)) get_next_segment(); } while(!playing); dtmp = AFBK; AFBK = AF; AF = dtmp; type = RA; verify = !(RF & CF); length = DE; firstbyte = !(RF & ZF); dest = IX; parity = 0; success = 0; do { nextdata = next_byte(); if(nextdata < 0) break; parity ^= nextdata; if(!length) { if(!parity) success = 1; break; } if(firstbyte) { firstbyte = 0; if(nextdata != type) break; } else { if(!verify) { if(dest >= 0x4000) DANM(mem)[dest] = nextdata; } else { if(DANM(mem)[dest] != nextdata) break; } dest++; length--; } } while(1); if(success) RF |= (CF | ZF); else RF &= ~(CF | ZF); IX = dest; DE = length; PC = SA_LD_RET; DANM(iff1) = DANM(iff2) = 1; DANM(haltstate) = 1; sp_init_screen_mark(); if(spt_auto_stop) pause_playing(); }