Skip to content

Commit

Permalink
Add drive letter (0,1,2,A,B) to gw.py read/write commands.
Browse files Browse the repository at this point in the history
Allows use of multiple drives with new F7 GW hardware.
  • Loading branch information
keirf committed Jan 17, 2020
1 parent d0429cf commit f58c0fb
Show file tree
Hide file tree
Showing 9 changed files with 402 additions and 126 deletions.
50 changes: 32 additions & 18 deletions inc/cdc_acm_protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,48 @@

/* CMD_GET_INFO, length=3, 0. Returns 32 bytes after ACK. */
#define CMD_GET_INFO 0
/* CMD_SEEK, length=3, cyl# */
#define CMD_SEEK 1
/* [BOOTLOADER] CMD_UPDATE, length=6, <update_len>.
* Host follows with <update_len> bytes.
* Bootloader finally returns a status byte, 0 on success. */
#define CMD_UPDATE 1
/* CMD_SEEK, length=3, cyl#. Seek to cyl# on selected drive. */
#define CMD_SEEK 2
/* CMD_SIDE, length=3, side# (0=bottom) */
#define CMD_SIDE 2
#define CMD_SIDE 3
/* CMD_SET_PARAMS, length=3+nr, idx, <nr bytes> */
#define CMD_SET_PARAMS 3
#define CMD_SET_PARAMS 4
/* CMD_GET_PARAMS, length=4, idx, nr_bytes. Returns nr_bytes after ACK. */
#define CMD_GET_PARAMS 4
/* CMD_MOTOR, length=3, motor_mask */
#define CMD_MOTOR 5
#define CMD_GET_PARAMS 5
/* CMD_MOTOR, length=4, drive#, on/off. Turn on/off a drive motor. */
#define CMD_MOTOR 6
/* CMD_READ_FLUX, length=2-3. Optionally include all or part of gw_read_flux.
* Returns flux readings until EOStream. */
#define CMD_READ_FLUX 6
#define CMD_READ_FLUX 7
/* CMD_WRITE_FLUX, length=2-7. Optionally include all or part of gw_write_flux.
* Host follows with flux readings until EOStream. */
#define CMD_WRITE_FLUX 7
#define CMD_WRITE_FLUX 8
/* CMD_GET_FLUX_STATUS, length=2. Last read/write status returned in ACK. */
#define CMD_GET_FLUX_STATUS 8
#define CMD_GET_FLUX_STATUS 9
/* CMD_GET_INDEX_TIMES, length=4, first, nr.
* Returns nr*4 bytes after ACK. */
#define CMD_GET_INDEX_TIMES 9
/* CMD_SELECT, length=3, select_mask */
#define CMD_SELECT 10
#define CMD_GET_INDEX_TIMES 10
/* CMD_SWITCH_FW_MODE, length=3, <mode> */
#define CMD_SWITCH_FW_MODE 11
#define CMD_MAX 11
/* CMD_SELECT, length=3, drive#. Select drive# as current unit. */
#define CMD_SELECT 12
/* CMD_DESELECT, length=2. Deselect current unit (if any). */
#define CMD_DESELECT 13
/* CMD_SET_BUS_TYPE, length=3, bus_type. Set the bus type. */
#define CMD_SET_BUS_TYPE 14
#define CMD_MAX 14

/* [BOOTLOADER] CMD_UPDATE, length=6, <update_len>.
* Host follows with <update_len> bytes.
* Bootloader finally returns a status byte, 0 on success. */
#define CMD_UPDATE 1

/*
* CMD_SET_BUS CODES
*/
#define BUS_NONE 0
#define BUS_IBMPC 1
#define BUS_SHUGART 2


/*
Expand All @@ -59,6 +70,9 @@
#define ACK_FLUX_OVERFLOW 4
#define ACK_FLUX_UNDERFLOW 5
#define ACK_WRPROT 6
#define ACK_NO_UNIT 7
#define ACK_NO_BUS 8
#define ACK_BAD_UNIT 9


/*
Expand Down
2 changes: 2 additions & 0 deletions scripts/greaseweazle/tools/read.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ def main(argv):

parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--drive", type=util.drive_letter, default='A',
help="drive to read (A,B,0,1,2)")
parser.add_argument("--revs", type=int, default=3,
help="number of revolutions to read per track")
parser.add_argument("--scyl", type=int, default=0,
Expand Down
24 changes: 19 additions & 5 deletions scripts/greaseweazle/tools/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,27 @@
# This is free and unencumbered software released into the public domain.
# See the file COPYING for more details, or visit <http://unlicense.org>.

import os, sys, serial, struct, time
import argparse, os, sys, serial, struct, time

from greaseweazle import version
from greaseweazle import usb as USB
from greaseweazle.image.scp import SCP
from greaseweazle.image.hfe import HFE


def drive_letter(letter):
types = {
'A': (USB.BusType.IBMPC, 0),
'B': (USB.BusType.IBMPC, 1),
'0': (USB.BusType.Shugart, 0),
'1': (USB.BusType.Shugart, 1),
'2': (USB.BusType.Shugart, 2)
}
if not letter.upper() in types:
raise argparse.ArgumentTypeError("invalid drive letter: '%s'" % letter)
return types[letter.upper()]


def get_image_class(name):
image_types = { '.scp': SCP, '.hfe': HFE }
_, ext = os.path.splitext(name)
Expand All @@ -25,18 +38,19 @@ def get_image_class(name):


def with_drive_selected(fn, usb, args):
usb.set_bus_type(args.drive[0])
try:
usb.drive_select(True)
usb.drive_motor(True)
usb.drive_select(args.drive[1])
usb.drive_motor(args.drive[1], True)
fn(usb, args)
except KeyboardInterrupt:
print()
usb.reset()
usb.ser.close()
usb.ser.open()
finally:
usb.drive_motor(False)
usb.drive_select(False)
usb.drive_motor(args.drive[1], False)
usb.drive_deselect()


def usb_reopen(usb, is_update):
Expand Down
2 changes: 2 additions & 0 deletions scripts/greaseweazle/tools/write.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ def main(argv):

parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--drive", type=util.drive_letter, default='A',
help="drive to write (A,B,0,1,2)")
parser.add_argument("--scyl", type=int, default=0,
help="first cylinder to write")
parser.add_argument("--ecyl", type=int, default=81,
Expand Down
99 changes: 74 additions & 25 deletions scripts/greaseweazle/usb.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,37 @@ class ControlCmd:
## Command set
class Cmd:
GetInfo = 0
Seek = 1
Side = 2
SetParams = 3
GetParams = 4
Motor = 5
ReadFlux = 6
WriteFlux = 7
GetFluxStatus = 8
GetIndexTimes = 9
Select = 10
SwitchFwMode = 11
# Bootloader specific:
Update = 1
Seek = 2
Side = 3
SetParams = 4
GetParams = 5
Motor = 6
ReadFlux = 7
WriteFlux = 8
GetFluxStatus = 9
GetIndexTimes = 10
SwitchFwMode = 11
Select = 12
Deselect = 13
SetBusType = 14
str = {
GetInfo: "GetInfo",
Update: "Update",
Seek: "Seek",
Side: "Side",
SetParams: "SetParams",
GetParams: "GetParams",
Motor: "Motor",
ReadFlux: "ReadFlux",
WriteFlux: "WriteFlux",
GetFluxStatus: "GetFluxStatus",
GetIndexTimes: "GetIndexTimes",
SwitchFwMode: "SwitchFwMode",
Select: "Select",
Deselect: "Deselect",
SetBusType: "SetBusType"
}


## Command responses/acknowledgements
Expand All @@ -42,28 +60,47 @@ class Ack:
FluxOverflow = 4
FluxUnderflow = 5
Wrprot = 6
Max = 6
NoUnit = 7
NoBus = 8
BadUnit = 9
str = {
Okay: "Okay",
BadCommand: "Bad Command",
NoIndex: "No Index",
NoTrk0: "Track 0 not found",
FluxOverflow: "Flux Overflow",
FluxUnderflow: "Flux Underflow",
Wrprot: "Disk is Write Protected",
NoUnit: "No drive unit selected",
NoBus: "No bus type (eg. Shugart, IBM/PC) specified",
BadUnit: "Bad unit number"
}



## Cmd.{Get,Set}Params indexes
class Params:
Delays = 0


## Cmd.SetBusType values
class BusType:
Invalid = 0
IBMPC = 1
Shugart = 2


## CmdError: Encapsulates a command acknowledgement.
class CmdError(Exception):

str = [ "Okay", "Bad Command", "No Index", "Track 0 not found",
"Flux Overflow", "Flux Underflow", "Disk is Write Protected" ]

def __init__(self, cmd, code):
self.cmd = cmd
self.code = code

def __str__(self):
if self.code <= Ack.Max:
return self.str[self.code]
return "Unknown Error (%u)" % self.code
return "%s: %s" % (Cmd.str.get(self.cmd, "UnknownCmd"),
Ack.str.get(self.code, "Unknown Error (%u)"
% self.code))


class Unit:
Expand Down Expand Up @@ -134,16 +171,28 @@ def seek(self, cyl, side):
self._send_cmd(struct.pack("3B", Cmd.Side, 3, side))


## set_bus_type:
## Set the floppy bus type.
def set_bus_type(self, type):
self._send_cmd(struct.pack("3B", Cmd.SetBusType, 3, type))


## drive_select:
## Select/deselect the drive.
def drive_select(self, state):
self._send_cmd(struct.pack("3B", Cmd.Select, 3, int(state)))
## Select the specified drive unit.
def drive_select(self, unit):
self._send_cmd(struct.pack("3B", Cmd.Select, 3, unit))


## drive_deselect:
## Deselect currently-selected drive unit (if any).
def drive_deselect(self):
self._send_cmd(struct.pack("2B", Cmd.Deselect, 2))


## drive_motor:
## Turn the selected drive's motor on/off.
def drive_motor(self, state):
self._send_cmd(struct.pack("3B", Cmd.Motor, 3, int(state)))
## Turn the specified drive's motor on/off.
def drive_motor(self, unit, state):
self._send_cmd(struct.pack("4B", Cmd.Motor, 4, unit, int(state)))


## _get_index_times:
Expand Down
43 changes: 43 additions & 0 deletions scripts/scp_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import struct, sys

trknr = int(sys.argv[2])

with open(sys.argv[1], "rb") as f:
dat = f.read()

header = struct.unpack("<3s9BI", dat[0:16])
(sig, _, _, nr_revs, s_trk, e_trk, flags, _, ss, _, _) = header
assert sig == b"SCP"
nr_sides = 1 if ss else 2

trk_offs = struct.unpack("<168I", dat[16:0x2b0])

print("Revolutions: %u" % nr_revs)
print("Track %u:" % trknr)

trk_off = trk_offs[trknr]
if trk_off == 0:
print("Empty")
sys.exit(0)

# Parse the SCP track header and extract the flux data.
thdr = dat[trk_off:trk_off+4+12*nr_revs]
sig, tnr, _, _, s_off = struct.unpack("<3sB3I", thdr[:16])
assert sig == b"TRK"
assert tnr == trknr
for i in range(nr_revs):
t,n,_ = struct.unpack("<3I", thdr[4+i*12:4+(i+1)*12])
print("Rev %u: time=%uus flux=%u" % (i, t//40, n))
_, e_nr, e_off = struct.unpack("<3I", thdr[-12:])
tdat = dat[trk_off+s_off:trk_off+e_off+e_nr*2]
fluxl = []
while tdat:
flux, = struct.unpack(">H", tdat[:2])
tdat = tdat[2:]
fluxl.append(flux / 40)
tot = 0.0
for x in fluxl:
print(x)
tot += x
print("Total: %uus (%uus per rev)" % (int(tot), tot//nr_revs))

Loading

0 comments on commit f58c0fb

Please sign in to comment.