From 64afcb204094e98616fc648d4ee1b01a947e1318 Mon Sep 17 00:00:00 2001 From: Peter Evans Date: Fri, 23 Mar 2018 19:41:01 -0500 Subject: [PATCH] Rewrite phaser to use state transitions, whole phase states By whole phase states, I mean we no longer track if more than one phase is active. --- include/apple2.dd.h | 7 ++- src/apple2.dd.c | 110 +++++++++++++++++++++----------------------- tests/apple2.dd.c | 37 +++++++-------- 3 files changed, 72 insertions(+), 82 deletions(-) diff --git a/include/apple2.dd.h b/include/apple2.dd.h index 320067a..1462b9a 100644 --- a/include/apple2.dd.h +++ b/include/apple2.dd.h @@ -53,7 +53,7 @@ enum apple2_dd_mode { */ #define _240K_ 245760 -#define MAX_DRIVE_STEPS 140 +#define MAX_DRIVE_STEPS 70 /* * This is the last _accessible_ sector position within a track (you can @@ -98,8 +98,7 @@ struct apple2dd { * laid it out as a flat line, but still with those points defined. * Does that make sense? */ - vm_8bit phase_state; - vm_8bit last_phase; + vm_8bit phase; /* * Data is written via a "latch", and happens in two steps; one, you @@ -205,7 +204,7 @@ extern vm_8bit apple2_dd_switch_rw(apple2dd *); extern void apple2_dd_eject(apple2dd *); extern void apple2_dd_free(apple2dd *); extern void apple2_dd_map(vm_segment *); -extern void apple2_dd_phaser(apple2dd *); +extern void apple2_dd_phaser(apple2dd *, int); extern void apple2_dd_save(apple2dd *); extern void apple2_dd_set_mode(apple2dd *, int); extern void apple2_dd_shift(apple2dd *, int); diff --git a/src/apple2.dd.c b/src/apple2.dd.c index 702cfee..6ddb492 100644 --- a/src/apple2.dd.c +++ b/src/apple2.dd.c @@ -16,6 +16,7 @@ #include "apple2.enc.h" #include "apple2.h" #include "vm_di.h" +#include "vm_reflect.h" /* * Create a new disk drive. We do not create a memory segment for the @@ -45,8 +46,7 @@ apple2_dd_create() drive->online = false; drive->write_protect = true; drive->mode = DD_READ; - drive->phase_state = 0; - drive->last_phase = 0; + drive->phase = 0; drive->image_type = DD_NOTYPE; return drive; @@ -234,53 +234,55 @@ apple2_dd_decode(apple2dd *drive) * phase to be the current phase state if the step was successful. */ void -apple2_dd_phaser(apple2dd *drive) +apple2_dd_phaser(apple2dd *drive, int phase) { - int next = drive->phase_state; - int prev = drive->last_phase; - int step = 0; + /* + * There are four stepper motor phases, and you can transition from + * one phase to another. While it's possible--and necessary!--to + * have more than one phase energized in the drive, the thing that + * matters is the number you ultimately get to. For example: + * + * Phase 1 is on. You energize phase 2. Now both phases 1 and 2 are + * on; you then de-energize phase 1. Now only phase 2 is on. + * + * This sequence describes the steps necessary to step the head in + * by one half-track. As you can see, at some point, you have two + * phases on; but ultimately what is necessary is you get from phase + * 1 to phase 2. As such, we track only a single phase to keep + * things simple. + * + * It's definitely possible to have Shenanigans; that is to say, you + * can energize three or all four phases. We may not handle this + * accurately at this time; but this method is preferred at the + * moment for its clarity of intent. + * + * The table below both defines and documents what the phase + * transitions do; you can see, if phase 1 is energized, we will + * step in one half-track when phase 2 is energized, and step out + * one half-track when phase 4 is energized. If phase 1 alone + * remains energized, we do nothing; if phase 3 is energized--being + * opposite to phase 1, in a circular array--we do nothing. + */ + static int transitions[] = { +// 0 1 2 3 4 phase transition + 0, 0, 0, 0, 0, // no phases + 0, 0, 1, 0, -1, // phase 1 + 0, -1, 0, 1, 0, // phase 2 + 0, 0, -1, 0, 1, // phase 3 + 0, 1, 0, -1, 0, // phase 4 + }; - if (!drive->online) { + // You can transition only to phases 1-4, and alternatively to no + // phase. Also, if the motor is off, then don't bother. + if (phase < 0 || phase > 4 || !drive->online) { return; } - // If PHASE1 is on and PHASE4 was previously on, then we want to - // mimic an inward step - if ((next & DD_PHASE1) && (prev & DD_PHASE4)) { - next = DD_PHASE4 << 1; - } - - // If, however, PHASE4 is on and previously PHASE1 was on, then we - // want to mimic an outward step - if ((next & DD_PHASE4) && (prev & DD_PHASE1)) { - next = DD_PHASE1 >> 1; - } - - // If an adjacent phase is on, add an inward or outward step. If - // _both_ adjacent phases are on, the step will count as zero (or no - // step). - if (next & (prev << 1)) { - step++; - } - - if (next & (prev >> 1)) { - step--; - } - - // If the opposite cog is also on, then our accounting is for - // naught; nullify the step movement. - if (((next & DD_PHASE1) && (next & DD_PHASE3)) || - ((next & DD_PHASE2) && (next & DD_PHASE4)) - ) { - step = 0; - } - + int step = transitions[(drive->phase * 5) + phase]; apple2_dd_step(drive, step); - // Recall our trickery above with the phase variable? Because of it, - // we have to save the phase_state field into last_phase, and not - // the pseudo-value we assigned to phase. - drive->last_phase = drive->phase_state; + // Record this new phase for the next time we make a transition + drive->phase = phase; } /* @@ -296,7 +298,7 @@ apple2_dd_position(apple2dd *drive) return 0; } - int track_offset = (drive->track_pos / 4) * ENC_ETRACK; + int track_offset = (drive->track_pos / 2) * ENC_ETRACK; return track_offset + drive->sector_pos; } @@ -406,12 +408,6 @@ apple2_dd_step(apple2dd *drive, int steps) } else if (drive->track_pos < 0) { drive->track_pos = 0; } - - // The sector position is rehomed to zero whenever we step a - // non-zero length. - if (steps) { - drive->sector_pos = 0; - } } /* @@ -461,18 +457,16 @@ apple2_dd_write_protect(apple2dd *drive, bool protect) void apple2_dd_switch_phase(apple2dd *drive, size_t addr) { + int phase = -1; + switch (addr & 0xF) { - case 0x0: drive->phase_state &= ~0x1; break; - case 0x1: drive->phase_state |= 0x1; break; - case 0x2: drive->phase_state &= ~0x2; break; - case 0x3: drive->phase_state |= 0x2; break; - case 0x4: drive->phase_state &= ~0x4; break; - case 0x5: drive->phase_state |= 0x4; break; - case 0x6: drive->phase_state &= ~0x8; break; - case 0x7: drive->phase_state |= 0x8; break; + case 0x1: phase = 1; break; + case 0x3: phase = 2; break; + case 0x5: phase = 3; break; + case 0x7: phase = 4; break; } - apple2_dd_phaser(drive); + apple2_dd_phaser(drive, phase); } /* diff --git a/tests/apple2.dd.c b/tests/apple2.dd.c index ef44aa0..7852a26 100644 --- a/tests/apple2.dd.c +++ b/tests/apple2.dd.c @@ -208,48 +208,45 @@ Test(apple2_dd, phaser) // Test going backwards drive->track_pos = 3; - drive->phase_state = 1; - drive->last_phase = 2; - - apple2_dd_phaser(drive); + drive->phase = 2; + apple2_dd_phaser(drive, 1); cr_assert_eq(drive->track_pos, 2); - cr_assert_eq(drive->last_phase, 1); + cr_assert_eq(drive->phase, 1); // Forwards - drive->phase_state = 2; + drive->phase = 3; drive->track_pos = 5; - drive->last_phase = 1; - apple2_dd_phaser(drive); + apple2_dd_phaser(drive, 4); cr_assert_eq(drive->track_pos, 6); - cr_assert_eq(drive->last_phase, 2); - drive->phase_state = 4; - apple2_dd_phaser(drive); + cr_assert_eq(drive->phase, 4); + + apple2_dd_phaser(drive, 1); cr_assert_eq(drive->track_pos, 7); - cr_assert_eq(drive->last_phase, 4); + cr_assert_eq(drive->phase, 1); } Test(apple2_dd, switch_phase) { apple2_dd_switch_phase(drive, 0x1); - cr_assert_eq(drive->phase_state, 0x1); + cr_assert_eq(drive->phase, 1); apple2_dd_switch_phase(drive, 0x0); - cr_assert_eq(drive->phase_state, 0x0); + cr_assert_eq(drive->phase, 1); apple2_dd_switch_phase(drive, 0x3); - cr_assert_eq(drive->phase_state, 0x2); + cr_assert_eq(drive->phase, 2); apple2_dd_switch_phase(drive, 0x2); - cr_assert_eq(drive->phase_state, 0x0); + cr_assert_eq(drive->phase, 2); apple2_dd_switch_phase(drive, 0x5); - cr_assert_eq(drive->phase_state, 0x4); + cr_assert_eq(drive->phase, 3); apple2_dd_switch_phase(drive, 0x4); - cr_assert_eq(drive->phase_state, 0x0); + cr_assert_eq(drive->phase, 3); apple2_dd_switch_phase(drive, 0x7); - cr_assert_eq(drive->phase_state, 0x8); + cr_assert_eq(drive->phase, 4); apple2_dd_switch_phase(drive, 0x6); - cr_assert_eq(drive->phase_state, 0x0); + cr_assert_eq(drive->phase, 4); } Test(apple2_dd, switch_drive)