1
0
mirror of https://github.com/pevans/erc-c.git synced 2024-12-21 08:30:55 +00:00

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.
This commit is contained in:
Peter Evans 2018-03-23 19:41:01 -05:00
parent 2870bc03bc
commit 64afcb2040
3 changed files with 72 additions and 82 deletions

View File

@ -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);

View File

@ -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);
}
/*

View File

@ -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)