Skip to content

Commit

Permalink
v0.7 wip+
Browse files Browse the repository at this point in the history
try to restore autoplay/stop heuristics
animated tape bar
  • Loading branch information
r-lyeh committed Jun 3, 2024
1 parent 410ce8c commit 1d4d280
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 61 deletions.
50 changes: 27 additions & 23 deletions src/zx.c
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ void input() {
if( window_trigger(app, TK_F5) ) cmdkey = 'F5';
if( window_trigger(app, TK_F6) ) cmdkey = 'RUN';
if( window_trigger(app, TK_F7) ) cmdkey = 'ISSU';
if( window_trigger(app, TK_F8) ) cmdkey = 'F8';
if( window_trigger(app, TK_F8) ) cmdkey = 'ffwd';
if( window_trigger(app, TK_F9) ) cmdkey = window_pressed(app, TK_SHIFT) ? 'AY' : 'F9';
if( window_trigger(app, TK_F11) ) cmdkey = 'F11';
if( window_trigger(app, TK_F12) ) cmdkey = 'F12';
Expand Down Expand Up @@ -950,7 +950,7 @@ void draw_ui() {
}

// ui animation
int hovering_border = !active && (m.x > _320 * 5/6 || m.x < _320 * 1/6);
int hovering_border = !active && (m.x > _320 * 5/6); // || m.x < _320 * 1/6);
static float smooth; do_once smooth = hovering_border;
smooth = smooth * 0.75 + hovering_border * 0.25;
// left panel: game options
Expand Down Expand Up @@ -1033,7 +1033,7 @@ void draw_ui() {
// right panel: emulator options
if( 1 )
{
int chr_x = REMAP(smooth,0,1,33,28) * 11 + 0, chr_y = REMAP(smooth,0,1,-4,2) * 11;
int chr_x = REMAP(smooth,0,1,33,28) * 11 + 0, chr_y = REMAP(smooth,0,1,-4,2.5) * 11;

{
// draw black panel
Expand Down Expand Up @@ -1062,7 +1062,7 @@ void draw_ui() {

if( ui_click("-Toggle RunAHead-", !ZX_RUNAHEAD ? "🯆\f0\n" : "🯇\f1\n") ) cmdkey = 'RUN';
if( ui_click("-Toggle TurboROM-", !ZX_TURBOROM ? "\f0\n" : "\f1\n")) cmdkey = 'TROM';
if( ui_click("-Toggle AutoTape-", "%c\f%d\n", PLAY_CHR,ZX_AUTOPLAY) ) cmdkey = 'AUTO';
if( ui_click("-Toggle FastTape-", "%c\f%d%c\n", PLAY_CHR,ZX_FASTTAPE,ZX_AUTOPLAY > ZX_AUTOSTOP ? '>' : ZX_AUTOPLAY < ZX_AUTOSTOP ? '<' : ' ') ) cmdkey = 'ffwd';

if( ui_click("-Translate game menus-", "T\f%d\n", ZX_AUTOLOCALE)) cmdkey = 'ALOC';

Expand Down Expand Up @@ -1097,7 +1097,7 @@ void draw_ui() {
if( ZX_AUTOPLAY ) {
if( ui_click(NULL, "%c", !active ? PLAY_CHR : PAUSE_CHR) ) active ^= 1;
} else {
if( ui_click(NULL, "%c", !tape_playing() ? PLAY_CHR : PAUSE_CHR) ) tape_play(!tape_playing());
if( ui_click(NULL, "%c", !mic_on/*!tape_playing()*/ ? PLAY_CHR : PAUSE_CHR) ) tape_play(!tape_playing());
if( ui_click(NULL, "\xf\b\b\b\xf") ) cmdkey = 'prev';
if( ui_click(NULL, "%c\b\b\b%c", PLAY_CHR, PLAY_CHR) ) cmdkey = 'next';
if( ui_click(NULL, "■") ) cmdkey = 'stop';
Expand All @@ -1108,24 +1108,28 @@ void draw_ui() {

// tape progress
float pct = tape_tellf();
#if DEV
if( mic_on )
#else
if( ZX_AUTOPLAY ? tape_inserted() : 1 )
#endif
if( pct <= 1.00f )
int visible = !active && tape_inserted() ? ( pct <= 1. && tape_playing() ) || (m.y > -10 && m.y < _240/10) : 0;
static float smoothY; do_once smoothY = visible;
smoothY = smoothY * 0.75 + visible * 0.25;
if( smoothY > 0.01 )
{
TPixel white = {255,255,255,255}, black = {0,0,0,255}, *bar = &ui->pix[0 + UI_LINE1 * _320];
int y = REMAP(smoothY,0,1,-10,UI_LINE1);

TPixel white = {255,255,255,255}, black = {0,0,0,255}, *bar = &ui->pix[0 + y * _320];

// bars & progress
unsigned mark = pct * _320;
for( int x = 0; x < _320; ++x ) bar[x] = bar[x+2*_320] = white;
for( int x = 0; x<=mark; ++x ) bar[x+_320] = white; bar[_320-1+_320] = white;
for( int x = 0; x < _320; ++x ) if(tape_preview[x]) bar[x+1*_320] = white;
if(y>= 0) for( int x = 0; x < _320; ++x ) bar[x] = white;
if(y>=-2) for( int x = 0; x < _320; ++x ) bar[x+2*_320] = white;
if(y>= 1) for( int x = 0; x<=mark; ++x ) bar[x+_320] = white;
if(y>=-2) for( int x = 0; x<=mark; ++x ) bar[-1+2*_320] = black;
if(y>=-1) for( int x = 0; x < _320; ++x ) {
if(tape_preview[x]) bar[x+1*_320] = white;
}
// triangle marker (top)
bar[mark+4*_320] = white;
for(int i = -1; i <= +1; ++i) if((mark+i)>=0 && (mark+i)<_320) bar[mark+i+5*_320] = white;
for(int i = -2; i <= +2; ++i) if((mark+i)>=0 && (mark+i)<_320) bar[mark+i+6*_320] = white;
if(y>=-4) bar[mark+4*_320] = white;
if(y>=-5) for(int i = -1; i <= +1; ++i) if((mark+i)>=0 && (mark+i)<_320) bar[mark+i+5*_320] = white;
if(y>=-6) for(int i = -2; i <= +2; ++i) if((mark+i)>=0 && (mark+i)<_320) bar[mark+i+6*_320] = white;
// mouse seeking
if( m.y > 0 && m.y < 11 ) {
mouse_cursor(2);
Expand Down Expand Up @@ -1226,9 +1230,10 @@ int main() {
ui_frame();
input();

int tape_accelerated = tape_inserted() && tape_peek() == 'o' ? 0
: tape_playing() && (ZX_FASTTAPE || ZX_FASTCPU);
if( tape_accelerated && active ) tape_accelerated = 0;
int tape_accelerated = ZX_FASTCPU ? 1
: tape_inserted() && tape_peek() == 'o' ? 0
: tape_playing() && (tape_hz > 300) && ZX_FASTTAPE;
if( active ) tape_accelerated = 0;

// z80, ula, audio, etc
// static int frame = 0; ++frame;
Expand Down Expand Up @@ -1422,7 +1427,7 @@ if( do_runahead == 0 ) {
break; case 'prev': tape_prev();
break; case 'next': tape_next();
break; case 'stop': tape_stop();
break; case 'F8': ZX_FASTTAPE ^= 1;
break; case 'ffwd': ZX_FASTTAPE ^= 1;
// cycle tv modes
break; case 'F9': { static int mode = 0; do_once mode = ZX_CRT << 1 | ZX_RF; mode = (mode + 1) & 3; ZX_RF = mode & 1; crt( ZX_CRT = !!(mode & 2) ); }
break; case 'F11': quicksave(0);
Expand Down Expand Up @@ -1452,7 +1457,6 @@ if( do_runahead == 0 ) {
break; case 'GUNS': ZX_GUNSTICK ^= 1; if(ZX_GUNSTICK) ZX_MOUSE = 0, ZX_JOYSTICK = 0; // cycle guns
break; case 'MICE': ZX_MOUSE ^= 1; if(ZX_MOUSE) ZX_GUNSTICK = 0; // cycle kempston mouse(s)
break; case 'PLUS': ZX_ULAPLUS ^= 1; // cycle ulaplus
break; case 'AUTO': ZX_AUTOPLAY ^= 1; // cycle autoplay command
break; case 'RUN': ZX_RUNAHEAD ^= 1; // cycle runahead mode
break; case 'DEV': ZX_DEBUG ^= 1;
break; case 'KL': ZX_KLMODE ^= 1, ZX_KLMODE_PATCH_NEEDED = 1;
Expand Down
61 changes: 35 additions & 26 deletions src/zx.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
// - P47Thunderbolt
// - OddiTheViking128

// 128/+2
// - parapshock should break; it doesnt

// auto-stop is broken
// [ ] basil
// [ ] abadia
Expand Down Expand Up @@ -153,7 +156,8 @@ int ZX; // 16, 48, 128, 200 (+2), 210 (+2A), 300 (+3)
int ZX_AY = 1; // 0: no, 1: ayumi, 2: flooh's, 3: both (ZX_AY is a mask)
int ZX_TURBOROM = 0; // 0: no, 1: patch rom so loading standard tape blocks is faster (see: .tap files)
int ZX_JOYSTICK = 3; // 0: no, 1: sinclair1, 2: sinclair2, 3: cursor/fuller/kempston/protek, 4..: other mappings
int ZX_AUTOPLAY = 1; // yes/no: auto plays/stops tapes based on #FE port reads
int ZX_AUTOPLAY = 0; // yes/no: auto-plays tapes based on #FE port reads
int ZX_AUTOSTOP = 0; // yes/no: auto-stops tapes based on #FE port reads
int ZX_RUNAHEAD = 0; // yes/no: improves input latency
int ZX_MOUSE = 1; // yes/no: kempston mouse
int ZX_ULAPLUS = 1; // yes/no: ulaplus 64color mode
Expand All @@ -172,6 +176,7 @@ int ZX_KLMODE_PATCH_NEEDED = 0; // helper internal variable, must be init to mat
const
int ZX_ALTROMS = 0; // 0:(no, original), 1:(yes, custom)


void logport(word port, byte value, int is_out);
void outport(word port, byte value);
byte inport(word port);
Expand Down Expand Up @@ -327,6 +332,9 @@ int loadbin_(byte *ptr, int size, int preloader) {
else reset(ZX);
}

ZX_AUTOPLAY = 1;
ZX_AUTOSTOP = 0;

#define preload_snap(blob,len) ( sna_load(blob,len) || z80_load(blob,len) )

// pre-loaders
Expand All @@ -351,18 +359,21 @@ int loadbin_(byte *ptr, int size, int preloader) {
int is_bin = tape_type == 3, choose = slots[ZX/16] + 6 * is_bin;
if(preloader) preload_snap(bins[choose], lens[choose]);
if(tape_has_turbo) rom_restore(); // rom_restore(), rom_patch(tape_has_turbo ? 0 : do_rompatch);
ZX_AUTOSTOP = tape_has_stops <= 1;
return 2;
}
if(tap_load(ptr,(int)size)) {
int slots[] = { [1]=0,[3]=1,[8]=2,[12]=3,[13]=4,[18]=5 };
int is_bin = tape_type == 3, choose = slots[ZX/16] + 6 * is_bin;
if(preloader) preload_snap(bins[choose], lens[choose]);
ZX_AUTOSTOP = tape_has_stops <= 1;
return 2;
}
if(csw_load(ptr,(int)size)) {
int slots[] = { [1]=0,[3]=1,[8]=2,[12]=3,[13]=4,[18]=5 };
int is_bin = tape_type == 3, choose = slots[ZX/16] + 6 * is_bin;
if(preloader) preload_snap(bins[choose], lens[choose]);
ZX_AUTOSTOP = tape_has_stops <= 1;
return 2;
}

Expand Down Expand Up @@ -1050,51 +1061,49 @@ uint64_t tick1(int num_ticks, uint64_t pins, void* user_data) {

void sim_frame() {
// logic that ticks every new frame
// if( !((ticks) % ZX_TS) ) {
// this section has x50 faster rate than next section.
if( 1 ) {
// vsync (once per frame)
zx_int = 1;

// auto-play tape. this one has x50 faster rate than stopping tape.
}
if( tape_inserted() ) {
// auto-plays tape
if( autoplay && !tape_playing() ) {
ZX_FASTTAPE = 1;

if( ZX_AUTOPLAY ) {
printf("auto-play tape (%u Hz, %u reads)\n", tape_hz, autoplay_numreads);
printf("auto-play tape (%u Hz, %u reads)\n", tape_hz, autoplay_numreads),
tape_play(1);
}
else
if( PC(cpu) < 0x4000 && voc_pos == 0 ) { // if( (PC(cpu) & 0xff00) == 0x0500 && GET_MAPPED_ROMBANK() == GET_BASIC_ROMBANK() ) {
// kick off the initial tape playing automatically.
// user will need to pause/resume tape manually (via F2 key) from this point, as AUTOPLAY=off
// if( 1 ) { // tape_inserted() && !tape_playing() ) {
if( !tape_playing() ) {
puts("auto-play tape (rom)"),
tape_play(1);
}
}
}

autoplay = 0;
autostop = 0;
}
// logic that ticks once per second
// if( !((ticks) % (ZX_TS*50)) ) {
static int frame = 0; ++frame;
if( !(frame % 50) ) {
// auto-stop tape. stopping a tape is a risky action and load will fail if not done at the right moment.
// so, the thinking for this block has a x50 slower rate than playing a tape. we have to be sure.
if( autostop && mic_on ) {
// auto-stops tape. stopping a tape is a risky action and load will fail if not done at the right moment.
// so, the thinking for this block has a x50 slower rate than playing a tape. we have to be sure we dont
// want tape to be playing instead.
if( autostop && tape_playing() ) {
ZX_FASTTAPE = 0;

if( ZX_AUTOPLAY ) {
printf("auto-stop tape (%u Hz, %u reads)\n", tape_hz, autoplay_numreads);
if( ZX_AUTOSTOP ) {
printf("auto-stop tape (%u Hz, %u reads)\n", tape_hz, autoplay_numreads),
tape_stop();
}
}

if( /*autoplay &&*/ !ZX_AUTOPLAY ) {
// kick off the initial tape playing automatically. since AUTOPLAY is OFF,
// user will need to pause/resume tape manually (via F2 key) from this point.
if( tape_inserted() && !tape_playing() ) {
if( (PC(cpu) & 0xff00) == 0x0500 && GET_MAPPED_ROMBANK() == GET_BASIC_ROMBANK() ) {
puts("auto-play tape (rom)");
tape_play(1);
}
}
}

// autoplay = 0;
autoplay = 0;
autostop = 0;
}
}
Expand Down Expand Up @@ -2181,7 +2190,7 @@ void reset(unsigned FLAGS) {
autoplay = 0;
autostop = 0;

if( ZX_AUTOPLAY ) tape_rewind();
tape_rewind();

if( !(FLAGS & KEEP_MEDIA) ) {
eject();
Expand Down
39 changes: 27 additions & 12 deletions src/zx_tap.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ int mic,mic_on; /* these two belong to zx.h */ \
byte tape_type; \
uint64_t tape_tstate; \
int tape_level; \
int tape_has_turbo, voc_len, voc_pos, voc_count, voc_units; \
int tape_has_turbo, tape_has_stops, voc_len, voc_pos, voc_count, voc_units; \
struct tape_block q;
VOC_DEFINES

Expand All @@ -33,6 +33,7 @@ void tape_reset(void) {
voc = realloc(voc, sizeof(struct tape_block) * 0x1800000);
voc_len = 0;
tape_has_turbo = 0;
tape_has_stops = 0;
voc_pos = voc_count = voc_units = 0;

mic = 0;
Expand Down Expand Up @@ -71,6 +72,7 @@ void tape_render_pause(unsigned pause_ms) { // Used to be x1.03 for longer pause
void tape_render_stop(void) {
// OddiTheViking, Untouchables(Hitsquad), BatmanTheMovie, ExpressRaider
tape_push("stop", LEVEL_LOW, COUNT_PER_MS, 1); // st(o)p
++tape_has_stops;
}
void tape_render_data(byte *data, unsigned bytes, unsigned bits, unsigned zero, unsigned one, int bitrepeat) {
// keep 2nd byte in safe place
Expand Down Expand Up @@ -103,14 +105,10 @@ void tape_finish() {
// write a terminator
tape_render_stop();

// trim extremely large gaps now. they're between code blocks and without multiload most of the times
// (see nipper2(kixx): 13s, 15s, 23s, etc).
// I suspect these blocks were manually authored and never tested on real hw (probably just tested
// on emulators with flashload enabled). they're clearly wrong by a x10 factor at least.
// in any case, the automatic tape loader will pause/resume on these blocks for us, so there is no
// sense to keep such large gaps within the blocks. for the end-users that use manual tape buttons
// on multiload games, i think 5s pause should suffice them.
if(0) // if( automatic_tape )
// we could trim extremely large gaps now (>13s). however, we are missing a good heuristic to discern
// between real multiload pauses (gauntlet.tzx) and bad tapes (jacknipper2(kixx).tzx). disabled for now.
// idea: do not trim gaps on tape if any stop-tape blocks are found. wont work for tap/csw files, though.
if(0)
for( int i = 0; i < voc_len; ++i )
if( voc[i].debug == 'u' && voc[i].units > 5000 )
voc[i].units = 5000;
Expand Down Expand Up @@ -184,11 +182,13 @@ byte mic_read(uint64_t tstates) {
if( diff < 0 ) diff = 0;
if( diff > 69888 ) {
diff = 4;
#if 1 // 1942.tzx
// bypass datas from a missed pilot. would take ages to read otherwise
if( PC(cpu) < 0x4000 ) // && GET_MAPPED_ROMBANK() == GET_BASIC_ROMBANK() )
while( voc[voc_pos].debug == 't' ) {
voc_pos++, voc_pos -= (voc_pos >= voc_len), voc_count = 0, voc_units = 0, mic;
}
#endif
}
if( mic_on ) { // if tape not stopped
#endif
Expand All @@ -213,15 +213,15 @@ byte mic_read(uint64_t tstates) {
else if( q.level == LEVEL_HIGH ) mic = LEVEL;
else if( q.level == LEVEL_LOW ) mic = LEVEL ^ 64;

if( q.debug != 'o' ) // return mic_on = 0, mic; // st(o)p

if( ++voc_count >= q.count ) {
voc_count = 0;
voc_pos ++;
voc_pos -= voc_pos >= voc_len;

// transfer new block
q = mic_read_tapeblock(voc_pos);

if( voc[voc_pos].debug == 'o' ) return mic; // st(o)p
}
}
}
Expand Down Expand Up @@ -264,11 +264,23 @@ int tap_load(void *fp, int siz) {
#define tape_seekf(at) ( voc_pos = voc && voc_len && at >= 0 && at <= 1 ? at * (voc_len - 1) : voc_pos )
#define tape_peek() ( voc_pos < voc_len ? voc[voc_pos].debug : ' ' )
#define tape_tellf() ( voc_pos / (float)(voc_len+!voc_len) )
#define tape_play(on) ( mic_on = !!(on) )
//#define tape_play(on) ( mic_on = !!(on) )
#define tape_playing() (mic_on && voc_len)
#define tape_stop() tape_play(0)

void tape_play(int on) {
if( tape_inserted() ) {
/**/ if( !on ) mic_on = 0;
else if( voc_pos == 0 ) mic_on = 1;
else if( q.debug == 'o' && voc_pos < (voc_len-1) ) {
mic_on = 1;
q = mic_read_tapeblock(voc_pos += 1);
}
}
}

void tape_next() {
tape_play(0);
if( voc_len ) {
while(voc_pos < voc_len && 'l' == voc[voc_pos].debug) voc_pos++; // skip pi(l)ot; current, if any
while(voc_pos < voc_len && 'l' != voc[voc_pos].debug) voc_pos++; // find pi(l)ot
Expand All @@ -277,6 +289,7 @@ void tape_next() {
}
}
void tape_prev() {
tape_play(0);
if( voc_len ) {
while((unsigned)voc_pos < voc_len && 'l' == voc[voc_pos].debug) voc_pos--; // skip pi(l)ot; current, if any
while((unsigned)voc_pos < voc_len && 'l' != voc[voc_pos].debug) voc_pos--; // find pi(l)ot
Expand Down Expand Up @@ -529,6 +542,7 @@ void tape_rewind() {
IMPORT(tape_level); \
IMPORT(voc_len); \
IMPORT(tape_has_turbo); \
IMPORT(tape_has_stops); \
IMPORT(voc_pos); \
IMPORT(voc_count); \
IMPORT(voc_units); \
Expand All @@ -542,6 +556,7 @@ void tape_rewind() {
EXPORT(tape_level); \
EXPORT(voc_len); \
EXPORT(tape_has_turbo); \
EXPORT(tape_has_stops); \
EXPORT(voc_pos); \
EXPORT(voc_count); \
EXPORT(voc_units); \
Expand Down

0 comments on commit 1d4d280

Please sign in to comment.