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:
parent
2870bc03bc
commit
64afcb2040
@ -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);
|
||||
|
110
src/apple2.dd.c
110
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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user