jackrubin
Veteran Member
Someone named Kirk brought one to VCF-West but I missed meeting him and didn't get his contact info. I'm trying to retrieve it now from some of the exhibitors. Hopefully I'll have info for you soon.
Jack
Jack
Folks,
I built Bela's RX02 emulator : http://www.vcfed.org/forum/album.php?albumid=341&attachmentid=32765 . I have it working somewhat on the VT278, but no luck yet on the RX8E or RXV21. I didn't try the RX11 since it wasn't convenient. On the Decmate, I can boot and read RX01 and RX02 disks with no problem, however writing is problematic. I can only write properly to RX01s in bytemode format. I can't even read on the 11 or 8. I think it's time to haul out the logic analyzer and see what the deal is. I've rechecked all the wiring/resistors/connector pinning/2N7000 operation (read they are static sensitive) but that is all good. It must be software!
Here are a few more photos of the build:
http://www.vcfed.org/forum/album.php?albumid=341&attachmentid=32768
http://www.vcfed.org/forum/album.php?albumid=341&attachmentid=32769
http://www.vcfed.org/forum/album.php?albumid=341&attachmentid=32764
I used an Arduino Mega protoshield as Bela did. It is perfect because it has a DIP area for the 74HC00 and surface mount area for the 74LVC14. There is just enough room otherwise for the six 2N7000s and the 180/390 ohm resistors and 40 pin header. The Due at 3.3V is somewhat convenient because the SD card is 3.3V. The 3.3V high level still satisfies the 5V TTL inputs of the LCD, so that is good. I put a DB25F on the back like a teacart RX02 and also a 40 pin female header just like the normal rackmount RX02.
Has anyone else built this yet and have any experiences to share?
Lou
Kirk,
Fantastic! So you and Bob have both also built one of these. What controllers have you had success with?
Yes, I should also add caps at the two ICs. I would normally always do that, but I didn't this time, mostly because Bela didn't. I do have plenty of power supply input filtering on the power supply board. It is oversized, capable of delivering 4A. I am powering everything off this one supply. It connects to the 5V rail on the Arduino, which then powers the 3.3V regulator on that. I have a removable jumper for the 5V connection to the signal line resistors. That way I can unplug the power supply, pull the jumper, and then power the Arduino through the programming cable. That all seems to be fine. My reads and writes to the SD card are all fine.
I will scope the signals to see how clean they look.
I believe that for one or two of something it is not worth my time to make a PCB, so I wire wrap things. The Mega protoshield works perfectly with the Due, and so I built everything up on that. I like clean looking front panels, and so remotely mounting the SD card slot from the Due and also mounting the LCD in the panel was much cleaner (and cheaper) than using a stack of shields.
After basic scoping of the signal cleanliness, my next step will be to watch the back-and-forth between a controller and the emulator with the logic analyzer. That was very helpful when I troubleshot Reinhard's RL02 emulator. Of course I will post my findings here.
This should be a fantastic tool, so let's get it running well!
Lou
I took a different path on a project like this. I used a Microsemi SmartFusion FPGA/ARM to make a controller/peripheral emulator. http://www.ricomputermuseum.org/Home/equipment/dec-pdp-8e/making-an-omnibus-peripheral-emulator
It is in the proof of concept stage now, but it will emulate an Omnibus PC05 paper tape reader. The time sensitive part of the bus interface is in the FPGA, and everything else is emulated with and application running under Linux in the ARM in the FPGA. Since the RX8E/RX01/RX02 does not use data-break I could add emulation with additional address decoding and skip logic in the FPGA and software in the ARM. My idea was to read the floppy image into RAM from flash, and have a flag that indicated if the floppy image in RAM was modified. If the processor PDP-8 RUN state was turned off the RAM image would be copied back to flash.
There is likely no practical limit to the number of controllers/peripherals that the FPGA and ARM could emulate at the same time.
The same concept could be used with Q-bus, but I haven't done that.
I had a short bit of quality time at the bench tonight : http://www.vcfed.org/forum/album.php?albumid=341&attachmentid=32802
Folks,
I had a short bit of quality time at the bench tonight : http://www.vcfed.org/forum/album.php?albumid=341&attachmentid=32802
The signal lines are all clean when examined on the scope, so next was to start watching the controller/emulated drive interaction with the logic analyzer. I am on the RXV21 for tonight's testing. After INIT L is asserted by the RXV21, the drive eventually asserts DONE L and then of all things, blasts out a 12 bit status word : http://www.vcfed.org/forum/album.php?albumid=341&attachmentid=32803 . No, 12BIT L is not asserted, we are on an 11 and I checked the signal level all the way back to the Due to confirm that it should see 12BIT L not asserted. But it responds as if it was!
To further surely confuse the matter, the status word, when decoded, reports a double density disk in an RX01 drive : http://www.vcfed.org/forum/album.php?albumid=341&attachmentid=32804. I am amazed that maybe the Decmate was able to swallow that, but for sure the RXV211 can't even digest this word and the RX8E wouldn't know what to do with a double density disk in an RX01.
So I think it's time for me to spend some quality time with the code listing of the program running in the Arduino.
Lou
.R ZRXD??
ZRXDC0.BIC
DRSSM-G2
CZRXDC0-0-0
RX02 SS PERF EXER
UNIT IS RX02
RSTRT ADR 145702
DR>STA
CHANGE HW (L) ? Y
# UNITS (D) ? 2
UNIT 0
RX BUS ADR (O) 177170 ?
VECTOR ADR (O) 264 ?
DRIVE # (O) 0 ?
EXP WRD-CR (O) 0 ?
UNIT 1
RX BUS ADR (O) 177170 ?
VECTOR ADR (O) 264 ?
DRIVE # (O) 0 ? 1
EXP WRD-CR (O) 0 ?
CHANGE SW (L) ? N
UNIT#0 UNIT#1
# SECTOR READS (8)= 00000020174 00000020174
# SECTOR WRITES (8)= 00000010076 00000010076
UNIT#0 UNIT#1
CHECK SUM: 0 0
FILL-EMP BUFF LOG: 0 0
NO ERR BIT: 0 0
INTER-NO DONE ERR: 0 0
INTERRUPT ERR: 0 0
SEEK: 0 0
CRC ERR: 0 0
CRC BAD: 0 0
READ ERR: 0 0
WRITE ERR: 0 0
DATA ERR: 0 0
DEL. DATA ERR: 0 0
HRD SEEK: 0 0
HRD CRC ERR: 0 0
HRD CRC BAD: 0 0
HRD READ: 0 0
HRD WRITE: 0 0
HRD DATA: 0 0
HRD DEL. DATA ERR: 0 0
ERR
CODE# UNIT#0 UNIT#1
010 0 0
020 0 0
030 0 0
040 0 0
050 0 0
060 0 0
070 0 0
100 0 0
110 0 0
120 0 0
130 0 0
140 0 0
150 0 0
160 0 0
170 0 0
200 0 0
210 0 0
220 0 0
230 0 0
240 0 0
250 0 0
260 0 0
TRACK# UNIT#0 UNIT#1
0 0 0
1 0 0
2 0 0
3 0 0
4 0 0
5 0 0
6 0 0
7 0 0
8 0 0
9 0 0
10 0 0
11 0 0
12 0 0
13 0 0
14 0 0
15 0 0
16 0 0
17 0 0
18 0 0
19 0 0
20 0 0
21 0 0
22 0 0
23 0 0
24 0 0
25 0 0
26 0 0
27 0 0
28 0 0
29 0 0
30 0 0
31 0 0
32 0 0
33 0 0
34 0 0
35 0 0
36 0 0
37 0 0
38 0 0
39 0 0
40 0 0
41 0 0
42 0 0
43 0 0
44 0 0
45 0 0
46 0 0
47 0 0
48 0 0
49 0 0
50 0 0
51 0 0
52 0 0
53 0 0
54 0 0
55 0 0
56 0 0
57 0 0
58 0 0
59 0 0
60 0 0
61 0 0
62 0 0
63 0 0
64 0 0
65 0 0
66 0 0
67 0 0
68 0 0
69 0 0
70 0 0
71 0 0
72 0 0
73 0 0
74 0 0
75 0 0
76 0 0
CZRXDC0 EOP 1
0 TOTAL ERRS
(Console)
^P
>>>H
Halted at 025154
>>>
Folks,
After INIT L is asserted by the RXV21, the drive eventually asserts DONE L and then of all things, blasts out a 12 bit status word : http://www.vcfed.org/forum/album.php?albumid=341&attachmentid=32803 . No, 12BIT L is not asserted, we are on an 11 and I checked the signal level all the way back to the Due to confirm that it should see 12BIT L not asserted. But it responds as if it was!
Actually this is the correct behavior. The status word returned at the completion of any command execution is 12 bits on the RX211/RXV21 interfaces (as well as the RX8E/RX28, of course). Only the RX11/RXV11 is 8b. Even the read command interface on the RX211/RXV21 is 12b (and not 8b) even tho 12b_mode is never asserted on an RX211/RX28.
//
// rx02_driver - Simple RX02 driver external interface
//
// (C) 2013 Don North <ak6dn_at_mindspring_dot_com>
//
// 21 Oct 2015 - donorth - Initial code
// 27 Mar 2016 - donorth- Ported to Arduino from CCS/Microchip
//
#ifndef rx02_driver_h
#define rx02_driver_h
//
// public prototypes
//
void rx_initialize (uint8_t flag);
void rx_debug (HardwareSerial *serialPort);
void rx_print_state (HardwareSerial *serialPort);
void rx_set_unit_file (uint8_t unit, char *name);
void rx_function (void);
#endif // rx02_driver_h
// the end
//
// rx02_driver - Simple RX02 driver
//
// (C) 2013 Don North <ak6dn_at_mindspring_dot_com>
//
// 21 Oct 2013 - donorth - Initial code
// 27 Mar 2016 - donorth- Ported to Arduino from CCS/Microchip
//
//
// includes
//
#include "my_project.h"
#include "led_driver.h"
#include "rx02_driver.h"
#include "sdcard_driver.h"
//
// definitions
//
// options
#define USE_BIT_BUILTINS 1 // use bit builtins BitRead() etc vs use logical bit shift/mask operations
// RXCS control/status bit definitions
//efine RXCS_GO (1<<0) // GO bit (reference only; not used in drive)
#define RXCS_FUNCTION (7<<1) // function bitfield
#define RXCS_UNITSEL (1<<4) // unit select
//efine RXCS_DONE (1<<5) // function complete (reference only; not used in drive)
//efine RXCS_INTENB (1<<6) // interrupt enable (reference only; not used in drive)
//efine RXCS_8BIT (1<<6) // 8b mode (RX8E/RX28 ONLY)
//efine RXCS_TR (1<<7) // transfer request (reference only; not used in drive)
//efine RXCS_MAINT (1<<7) // maintenance mode (RX8E/RX28 ONLY)
#define RXCS_DENSEL (1<<8) // density select
//efine RXCS_HEADSEL (1<<9) // head select
//efine RXCS_RX02 (1<<11) // RX02 interface (reference only; not used in drive)
//efine RXCS_EXTADDR (3<<12) // upper bits of phys address (reference only; not used in drive)
//efine RXCS_INIT (1<<14) // initialize RX (reference only; not used in drive)
//efine RXCS_ERROR (1<<15) // error flag (reference only; not used in drive)
#define RXFCN_FILL (0) // fill buffer
#define RXFCN_EMPTY (1) // empty buffer
#define RXFCN_WRSECT (2) // write sector
#define RXFCN_RDSECT (3) // read sector
#define RXFCN_SETMEDIA (4) // set media density
#define RXFCN_RDSTAT (5) // read status
#define RXFCN_WRDDSECT (6) // write deleted data sector
#define RXFCN_RDERROR (7) // read error code
// RXES status bit definitions
//efine RXES_CRC (1<<0) // CRC aka READ error
//efine RXES_SIDE (1<<1) // side (RX211/RX28 ONLY)
//efine RXES_PERR (1<<1) // parity error (RX11/RX8E w/RX01 ONLY)
#define RXES_ID (1<<2) // controller init done
#define RXES_RX02 (1<<3) // set for RX02 drive (RX28 w/ RX02 ONLY)
//efine RXES_ACLO (1<<3) // set for AC low (RX211 w/RX02 ONLY)
//efine RXES_WPERR (1<<3) // set for write to WP drive (RX11/RX8E w/RX01 ONLY)
#define RXES_DENERR (1<<4) // density error
#define RXES_DRVDEN (1<<5) // diskette double density
#define RXES_DELDATA (1<<6) // deleted data detected
#define RXES_DRVRDY (1<<7) // drive ready
#define RXES_UNITSEL (1<<8) // unit selected (RX211 w/RX02 ONLY)
#define RXES_WCOVF (1<<10) // word count overflow (RX211 w/RX02 ONLY)
//efine RXES_NXM (1<<11) // nonexistent memory (RX211 w/RX02 reference only; not used in drive)
// RX error codes
#define RXERR_SUCCESS 0000 // success, no error
//efine RXERR_DR0INIT 0010 // drive 0 failed to init
//efine RXERR_DR1INIT 0020 // drive 1 failed to init
//efine RXERR_STEPHOME 0030 // found home when stepping for init (RX01 ONLY)
#define RXERR_TRKERR 0040 // access to track > 76
#define RXERR_TRKFAIL 0050 // track not found
//efine RXERR_SELFDIAG 0060 // self diagnostic fail (RX01 ONLY)
#define RXERR_SECFAIL 0070 // sector not found
//efine RXERR_WRITEWP 0100 // write to a WP drive (RX01 ONLY)
//efine RXERR_SEPFAIL 0110 // no SEP clock in 40us
//efine RXERR_PREFAIL 0120 // preamble not found
#define RXERR_IDMFAIL 0130 // ID mark not found
//efine RXERR_CRCHEAD 0140 // CRC error on a header (RX01 ONLY)
//efine RXERR_TRKCMP 0150 // header track miscompare
//efine RXERR_IDMTRYS 0160 // too many tries for IDAM
//efine RXERR_DAMFAIL 0170 // data AM not found
//efine RXERR_CRCERR 0200 // CRC error on read
//efine RXERR_PERR 0210 // parity error on word from i/f to controller (RX01 ONLY)
//efine RXERR_RWFAIL 0220 // r/w failed maint test (RX02 ONLY)
#define RXERR_WCOVF 0230 // word count overflow (RX02 ONLY)
#define RXERR_DENERR 0240 // density error (RX02 ONLY)
#define RXERR_KEYERR 0250 // wrong key word for set density (RX02 ONLY)
// macros
// rx interface outputs set/clr
#if USE_BIT_BUILTINS
#define rx_set_done(xxx) bitSet(PORTA,6) /* digitalWrite(PIN_CTLR_DONE_H,HIGH) */
#define rx_clr_done(xxx) bitClear(PORTA,6) /* digitalWrite(PIN_CTLR_DONE_H,LOW) */
#define rx_set_error(xxx) bitSet(PORTA,7) /* digitalWrite(PIN_CTLR_ERROR_H,HIGH) */
#define rx_clr_error(xxx) bitClear(PORTA,7) /* digitalWrite(PIN_CTLR_ERROR_H,LOW) */
#define rx_set_aclo(xxx) bitSet(PORTC,1) /* digitalWrite(PIN_CTLR_ACLO_H,HIGH) */
#define rx_clr_aclo(xxx) bitClear(PORTC,1) /* digitalWrite(PIN_CTLR_ACLO_H,LOW) */
#define rx_set_shift(xxx) bitSet(PORTC,2) /* digitalWrite(PIN_CTLR_SHIFT_H,HIGH) */
#define rx_clr_shift(xxx) bitClear(PORTC,2) /* digitalWrite(PIN_CTLR_SHIFT_H,LOW) */
#define rx_set_out(xxx) bitSet(PORTC,3) /* digitalWrite(PIN_CTLR_OUT_H,HIGH) */
#define rx_clr_out(xxx) bitClear(PORTC,3) /* digitalWrite(PIN_CTLR_OUT_H,LOW) */
#define rx_set_datao(xxx) bitSet(PORTC,5) /* digitalWrite(PIN_CTLR_DATAO_H,HIGH) */
#define rx_clr_datao(xxx) bitClear(PORTC,5) /* digitalWrite(PIN_CTLR_DATAO_H,LOW) */
#define rx_set_request(xxx) bitSet(PORTC,6) /* digitalWrite(PIN_CTLR_TR_RQST_H,HIGH) */
#define rx_clr_request(xxx) bitClear(PORTC,6) /* digitalWrite(PIN_CTLR_TR_RQST_H,LOW) */
// rx interface inputs test
#define rx_tst_pio(xxx) bitRead(PINC,0) /* digitalRead(PIN_CTLR_DMA_MODE_L) */
#define rx_tst_dma(xxx) (rx_tst_pio()?0:1) /* !digitalRead(PIN_CTLR_DMA_MODE_L) */
#define rx_tst_datai(xxx) bitRead(PINC,4) /* digitalRead(PIN_CTLR_DATAI_H) */
#define rx_tst_12b(xxx) bitRead(PINC,7) /* digitalRead(PIN_CTLR_12BIT_H) */
#define rx_tst_8b(xxx) (rx_tst_12b()?0:1) /* !digitalRead(PIN_CTLR_12BIT_H) */
#define rx_tst_run(xxx) bitRead(PINE,4) /* digitalRead(PIN_CTLR_RUN_H)) */
#define rx_tst_init(xxx) bitRead(PINE,5) /* digitalRead(PIN_CTLR_INIT_H) */
#else
#define rx_set_done(xxx) PORTA |= (1<<6) /* digitalWrite(PIN_CTLR_DONE_H,HIGH) */
#define rx_clr_done(xxx) PORTA &= ~(1<<6) /* digitalWrite(PIN_CTLR_DONE_H,LOW) */
#define rx_set_error(xxx) PORTA |= (1<<7) /* digitalWrite(PIN_CTLR_ERROR_H,HIGH) */
#define rx_clr_error(xxx) PORTA &= ~(1<<7) /* digitalWrite(PIN_CTLR_ERROR_H,LOW) */
#define rx_set_aclo(xxx) PORTC |= (1<<1) /* digitalWrite(PIN_CTLR_ACLO_H,HIGH) */
#define rx_clr_aclo(xxx) PORTC &= ~(1<<1) /* digitalWrite(PIN_CTLR_ACLO_H,LOW) */
#define rx_set_shift(xxx) PORTC |= (1<<2) /* digitalWrite(PIN_CTLR_SHIFT_H,HIGH) */
#define rx_clr_shift(xxx) PORTC &= ~(1<<2) /* digitalWrite(PIN_CTLR_SHIFT_H,LOW) */
#define rx_set_out(xxx) PORTC |= (1<<3) /* digitalWrite(PIN_CTLR_OUT_H,HIGH) */
#define rx_clr_out(xxx) PORTC &= ~(1<<3) /* digitalWrite(PIN_CTLR_OUT_H,LOW) */
#define rx_set_datao(xxx) PORTC |= (1<<5) /* digitalWrite(PIN_CTLR_DATAO_H,HIGH) */
#define rx_clr_datao(xxx) PORTC &= ~(1<<5) /* digitalWrite(PIN_CTLR_DATAO_H,LOW) */
#define rx_set_request(xxx) PORTC |= (1<<6) /* digitalWrite(PIN_CTLR_TR_RQST_H,HIGH) */
#define rx_clr_request(xxx) PORTC &= ~(1<<6) /* digitalWrite(PIN_CTLR_TR_RQST_H,LOW) */
// rx interface inputs test
#define rx_tst_pio(xxx) ((PINC & (1<<0)) ? 1 : 0) /* (digitalRead(PIN_CTLR_DMA_MODE_L)==HIGH) */
#define rx_tst_dma(xxx) ((PINC & (1<<0)) ? 0 : 1) /* (digitalRead(PIN_CTLR_DMA_MODE_L)==LOW) */
#define rx_tst_datai(xxx) ((PINC & (1<<4)) ? 1 : 0) /* digitalRead(PIN_CTLR_DATAI_H) */
#define rx_tst_12b(xxx) ((PINC & (1<<7)) ? 1 : 0) /* (digitalRead(PIN_CTLR_12BIT_H)==HIGH) */
#define rx_tst_8b(xxx) ((PINC & (1<<7)) ? 0 : 1) /* (digitalRead(PIN_CTLR_12BIT_H)==LOW) */
#define rx_tst_run(xxx) ((PINE & (1<<4)) ? 1 : 0) /* (digitalRead(PIN_CTLR_RUN_H)==HIGH) */
#define rx_tst_init(xxx) ((PINE & (1<<5)) ? 1 : 0) /* (digitalRead(PIN_CTLR_INIT_H)==HIGH) */
#endif
// INIT/RUN status
#define RX_SAW_NONE 0 // saw neither RUN or INIT
#define RX_SAW_RUN 1 // saw RUN and not INIT
// emulation type
#define RX_TYPE_RX01 0 // RX02 in RX01 mode
#define RX_TYPE_RX02 1 // RX02 in native mode
// drive density
#define RX_DEN_SD 0 // single density mode
#define RX_DEN_DD 1 // double density mode
// data type
#define RX_NORMAL_DATA 0 // normal data
#define RX_DELETED_DATA 1 // deleted data
// disk definitions
#define RX_NTRKS 77L // number of tracks per disk
#define RX_NSECS 26L // number of sectors per track
#define RX_NBPS 128L // bytes per sector, single density
#define RX_NUNITS 2 // number if units
#define RX_BUFFER_SIZE (2*RX_NBPS) // maximum size sector buffer for double density
// convenience macros
#define rx_sec_size(den) (RX_NBPS<<(den)) // bytes per sector at density
#define rx_trk_size(den) (RX_NSECS*rx_sec_size(den)) // bytes per track at density
#define rx_dsk_size(den) (RX_NTRKS*rx_trk_size(den)) // bytes per disk at density
#define rx_get_bits(xxx) (rx_tst_12b() ? 12 : 8) // number of bits signaled by interface
//
// timing characteristics
//
#define RX02_ACTUAL_SPEED 0
// actual hardware timing
#if (RX02_ACTUAL_SPEED == 1)
#define RX_RDSTAT_TIME (250) // read status function
#define RX_INIT_TIME (500) // initialize floppy drive
#define RX_STEP_TIME (10) // track step time
#define RX_SETTLE_TIME (20) // seek settling time
#define RX_SEC_TIME (20) // sector access time
#define RX_RDERROR_TIME (1) // read error registers
#define RX_SETMEDIA_TIME (10000) // set media density
#endif // (RX02_ACTUAL_SPEED == 1)
// as fast as it can go
#if (RX02_ACTUAL_SPEED == 0)
#define RX_RDSTAT_TIME (0) // read status function
#define RX_INIT_TIME (0) // initialize floppy drive
#define RX_STEP_TIME (0) // track step time
#define RX_SETTLE_TIME (0) // seek settling time
#define RX_SEC_TIME (0) // sector access time
#define RX_RDERROR_TIME (0) // read error registers
#define RX_SETMEDIA_TIME (0) // set media density
#endif // (RX02_ACTUAL_SPEED == 0)
// compute seek time based on delta track amount
#define rx_seek_time(d) (RX_SEC_TIME + ((abs(d) == 0) ? 0 : RX_SETTLE_TIME + abs(d)*RX_STEP_TIME))
// PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE
volatile static uint8_t rx_init_seen; // set when RX INIT asserted (rising edge)
static jmp_buf rx_init_env; // environment to invoke when INIT is asserted (ie, reset/error condition)
static char fcn_name_list[][10] = { "FILL", "EMPTY", "WRSECT", "RDSECT", "SETMEDIA", "RDSTAT", "WRDDSECT", "RDERROR" };
static uint8_t rx_type = RX_TYPE_RX02; // default drive emulation, can be overwritten
struct drv_t {
uint32_t dd[RX_NTRKS]; // normal or deleted data per track per sector (1 bit per sector 0..31)
char name[32]; // associated file name
uint8_t rdy; // drive ready
uint8_t den; // density
uint8_t ta; // track address
uint8_t sa; // sector address
uint16_t len; // read/write length
uint32_t pos; // read/write position
};
static struct rx_t {
uint8_t type; // emulation type: RX01, RX02
uint16_t cs; // control/status image
uint16_t es; // error and status
uint16_t bc; // byte count
uint8_t wc; // word count
uint8_t ecode; // error code
uint8_t unit; // active unit number
uint8_t den; // density selected, DD or SD
uint8_t ta; // track address
uint8_t sa; // sector address
uint16_t len; // read/write length
uint32_t pos; // read/write position
struct {
uint8_t code; // function selected (numeric 0..7)
char * name; // function selected, ascii name id
} fcn;
struct drv_t drv[RX_NUNITS]; // drive specific parameters
uint8_t buffer[RX_BUFFER_SIZE]; // sector buffer
} rx;
static HardwareSerial *debug = NULL; // debug serial output
//
// RX02 INIT assertion interrupt
//
static void rx_intr_init (void)
{
rx_init_seen = 1;
return;
}
//
// RX time delay routine
//
static void rx_timing (uint16_t ms_delay)
{
const uint16_t ms_amount = 5;
// check for delay requested
if (ms_delay > 0) {
// delay in 5ms increments, checking for INIT
while (ms_delay > ms_amount) {
if (rx_init_seen) { longjmp(rx_init_env, 10); }
delay(ms_amount);
ms_delay -= ms_amount;
}
// final small delay less than 5ms
if (rx_init_seen) { longjmp(rx_init_env, 10); }
delay(ms_delay);
}
// all done
if (rx_init_seen) { longjmp(rx_init_env, 10); }
return;
}
//
// wait until the RX_RUN or RX_INIT signal is asserted, or timeout occurs
//
static uint8_t rx_wait_run_or_init (uint32_t expire)
{
uint32_t start = millis();
do {
if (rx_init_seen) { longjmp(rx_init_env, 1); }
if (rx_tst_run()) { return RX_SAW_RUN; }
} while (millis()-start <= expire);
// count timed out with neither RUN or INIT
return RX_SAW_NONE;
}
// wait forever until the RX_RUN or RX_INIT signal is asserted
static uint8_t rx_wait_run_or_init (void)
{
do {
if (rx_init_seen) { longjmp(rx_init_env, 2); }
if (rx_tst_run()) { return RX_SAW_RUN; }
} while (TRUE);
}
//
// receive a 12/8 bit word from the RX interface, msb first
//
#define _rx_recv_hs(xxx,yyy) { if (rx_tst_datai()) { delay_1c(); (xxx) |= (1<<(yyy)); } else { (xxx) &= ~(1<<(yyy)); delay_2c(); } delay_1c(); rx_set_shift(); delay_1c(); rx_clr_shift(); }
static uint16_t rx_recv12_hs (uint8_t handshake)
{
uint16_t data = 0;
rx_clr_out();
rx_clr_done();
rx_clr_datao();
rx_clr_error();
rx_clr_shift();
if (handshake) { rx_set_request(); rx_wait_run_or_init(); rx_clr_request(); }
noInterrupts();
_rx_recv_hs(data,11);
_rx_recv_hs(data,10);
_rx_recv_hs(data,9);
_rx_recv_hs(data,8);
_rx_recv_hs(data,7);
_rx_recv_hs(data,6);
_rx_recv_hs(data,5);
_rx_recv_hs(data,4);
_rx_recv_hs(data,3);
_rx_recv_hs(data,2);
_rx_recv_hs(data,1);
_rx_recv_hs(data,0);
interrupts();
if (rx_init_seen) { longjmp(rx_init_env, 3); }
return data;
}
static uint16_t rx_recv8_hs (uint8_t handshake)
{
uint8_t data = 0;
rx_clr_out();
rx_clr_done();
rx_clr_datao();
rx_clr_error();
rx_clr_shift();
if (handshake) { rx_set_request(); rx_wait_run_or_init(); rx_clr_request(); }
noInterrupts();
_rx_recv_hs(data,7);
_rx_recv_hs(data,6);
_rx_recv_hs(data,5);
_rx_recv_hs(data,4);
_rx_recv_hs(data,3);
_rx_recv_hs(data,2);
_rx_recv_hs(data,1);
_rx_recv_hs(data,0);
interrupts();
if (rx_init_seen) { longjmp(rx_init_env, 4); }
return uint16_t(data);
}
static uint16_t rx_recv (uint8_t n)
{
if (n == 12) return rx_recv12_hs(FALSE); else
if (n == 8) return rx_recv8_hs(FALSE); else
if (rx_tst_12b()) return rx_recv12_hs(FALSE); else
return rx_recv8_hs(FALSE);
}
static uint16_t rx_recv (void)
{
if (rx_tst_12b()) return rx_recv12_hs(FALSE); else
return rx_recv8_hs(FALSE);
}
static uint16_t rx_recv_hs (uint8_t n)
{
if (n == 12) return rx_recv12_hs(TRUE); else
if (n == 8) return rx_recv8_hs(TRUE); else
if (rx_tst_12b()) return rx_recv12_hs(TRUE); else
return rx_recv8_hs(TRUE);
}
static uint16_t rx_recv_hs (void)
{
if (rx_tst_12b()) return rx_recv12_hs(TRUE); else
return rx_recv8_hs(TRUE);
}
//
// transmit a 12/8 bit word to the RX interface, msb first
//
#define _rx_xmit_hs(xxx,yyy) { if ((xxx)&(1<<(yyy))) { delay_1c(); rx_set_datao(); } else { rx_clr_datao(); delay_2c(); } delay_1c(); rx_set_shift(); delay_1c(); rx_clr_shift(); }
static void rx_xmit12_hs (uint16_t value, uint8_t handshake)
{
uint16_t data = value;
noInterrupts();
if (handshake) { rx_clr_request(); }
rx_clr_shift();
rx_set_out();
delay_3c();
_rx_xmit_hs(data,11);
_rx_xmit_hs(data,10);
_rx_xmit_hs(data,9);
_rx_xmit_hs(data,8);
_rx_xmit_hs(data,7);
_rx_xmit_hs(data,6);
_rx_xmit_hs(data,5);
_rx_xmit_hs(data,4);
_rx_xmit_hs(data,3);
_rx_xmit_hs(data,2);
_rx_xmit_hs(data,1);
_rx_xmit_hs(data,0);
delay_3c();
rx_clr_datao();
interrupts();
if (handshake) { rx_set_request(); rx_wait_run_or_init(); rx_clr_request(); }
rx_clr_out();
if (rx_init_seen) { longjmp(rx_init_env, 5); }
return;
}
static void rx_xmit8_hs (uint16_t value, uint8_t handshake)
{
uint8_t data = value;
noInterrupts();
if (handshake) { rx_clr_request(); }
rx_clr_shift();
rx_set_out();
delay_3c();
_rx_xmit_hs(data,7);
_rx_xmit_hs(data,6);
_rx_xmit_hs(data,5);
_rx_xmit_hs(data,4);
_rx_xmit_hs(data,3);
_rx_xmit_hs(data,2);
_rx_xmit_hs(data,1);
_rx_xmit_hs(data,0);
delay_3c();
rx_clr_datao();
interrupts();
if (handshake) { rx_set_request(); rx_wait_run_or_init(); rx_clr_request(); }
rx_clr_out();
if (rx_init_seen) { longjmp(rx_init_env, 6); }
return;
}
static void rx_xmit (uint16_t data, uint8_t n)
{
if (n == 12) rx_xmit12_hs(data,FALSE); else
if (n == 8) rx_xmit8_hs(data,FALSE); else
if (rx_tst_12b()) rx_xmit12_hs(data,FALSE); else
rx_xmit8_hs(data,FALSE);
return;
}
static void rx_xmit (uint16_t data)
{
if (rx_tst_12b()) rx_xmit12_hs(data,FALSE); else
rx_xmit8_hs(data,FALSE);
return;
}
static void rx_xmit_hs (uint16_t data, uint8_t n)
{
if (n == 12) rx_xmit12_hs(data,TRUE); else
if (n == 8) rx_xmit8_hs(data,TRUE); else
if (rx_tst_12b()) rx_xmit12_hs(data,TRUE); else
rx_xmit8_hs(data,TRUE);
return;
}
static void rx_xmit_hs (uint16_t data)
{
if (rx_tst_12b()) rx_xmit12_hs(data,TRUE); else
rx_xmit8_hs(data,TRUE);
return;
}
//
// transmit an extended status word
//
static void rx_xmit_es (uint16_t data)
{
if (debug) debug->printf("RX: rx_xmit_es(%04o)\n", data);
if (rx_tst_dma() || rx_tst_12b() && rx.type == RX_TYPE_RX02) {
// RX211/RXV21 or RX28 attached
rx_set_done();
rx_clr_request();
rx_xmit(data, 12);
rx_set_request();
} else if (rx_tst_12b()) {
// RX8E attached
rx_xmit(data, 12);
} else {
// RX11/RXV11 attached
rx_xmit(data, 8);
}
return;
}
//
// setup error/status word
//
static uint16_t rx_init_es (void)
{
uint16_t es;
// clear all bits except init done
es = (rx.es & RXES_ID);
// insert drive type (RX28 only w/ RX02)
if (rx.type == RX_TYPE_RX02 && rx_tst_12b()) es |= RXES_RX02;
// insert unit selected
if (rx.unit == 1 && rx_tst_dma()) es |= RXES_UNITSEL;
// insert drive ready for that unit
if (rx.drv[rx.unit].rdy) es |= RXES_DRVRDY;
// insert density for that unit
if (rx.drv[rx.unit].den == RX_DEN_DD) es |= RXES_DRVDEN;
return es;
}
// setup error/status with flags: RXES_CRC, RXES_DENERR, RXES_DELDATA, RXES_WCOVF are data flag options
static uint16_t rx_init_es (uint16_t data)
{
return rx_init_es() | data;
}
// PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC
//
// initialize the RX subsystem
//
// flag: TRUE for powerup init; FALSE for INIT pulse seen
//
void rx_initialize (uint8_t flag)
{
uint8_t i;
// led status
led_state(red, on);
led_state(green, off);
led_state(yellow, off);
// setup at power up ... required first time only
if (flag) {
// setup pins
pinMode(PIN_CTLR_DMA_MODE_L, INPUT);
pinMode(PIN_CTLR_DATAI_H, INPUT);
pinMode(PIN_CTLR_12BIT_H, INPUT);
pinMode(PIN_CTLR_INIT_H, INPUT); // interrupt
pinMode(PIN_CTLR_RUN_H, INPUT);
//
pinMode(PIN_CTLR_TR_RQST_H, OUTPUT);
pinMode(PIN_CTLR_DATAO_H, OUTPUT);
pinMode(PIN_CTLR_SHIFT_H, OUTPUT);
pinMode(PIN_CTLR_ERROR_H, OUTPUT);
pinMode(PIN_CTLR_ACLO_H, OUTPUT);
pinMode(PIN_CTLR_DONE_H, OUTPUT);
pinMode(PIN_CTLR_OUT_H, OUTPUT);
// setup RX INIT interrupt
rx_init_seen = 0;
attachInterrupt(digitalPinToInterrupt(PIN_CTLR_INIT_H), rx_intr_init, RISING);
}
// setup control outputs
rx_clr_out();
rx_clr_aclo();
rx_clr_done();
rx_clr_datao();
rx_clr_error();
rx_clr_shift();
rx_clr_request();
// toggle ACLO at initial reset
if (flag) { rx_set_aclo(); delay(100); rx_clr_aclo(); }
// wait for INIT to go away
uint32_t start = millis(); // current time, in ms
uint32_t expire = 10000UL; // 10 sec delay
if (debug) debug->printf("RX: waiting for INIT to clear ... t=%lums\n", millis());
while ((rx_init_seen || rx_tst_init()) && millis()-start <= expire) { rx_init_seen = 0; }
if (debug) debug->printf("RX: INIT has %scleared t=%lums\n", rx_tst_init() ? "NOT " : "", millis());
// setup drive data structures
rx.type = rx_type;
rx.fcn.code = 8;
rx.fcn.name = "INIT";
rx.den = rx.type == RX_TYPE_RX02 ? RX_DEN_DD : RX_DEN_SD;
rx.cs = 0;
rx.es = 0;
rx.ta = 0;
rx.sa = 0;
rx.wc = 0;
rx.bc = 0;
rx.len = 0;
rx.pos = 0;
rx.unit = 0;
rx.ecode = 0;
// setup drive state
for (i = 0; i < RX_NUNITS; ++i) {
if (flag) sprintf(rx.drv[i].name, "RX%d.DSK", i);
rx_set_unit_file(i, NULL);
}
// clear sector buffer
memset(rx.buffer, 0x00, sizeof(rx.buffer));
// indicate SD card access in progress
led_state(yellow, on);
// copy drive 0 boot sector into the buffer
rx.drv[0].ta = rx.ta = 1;
rx.drv[0].sa = rx.sa = 1;
rx.drv[0].len = rx.len = rx_sec_size(rx.drv[0].den);
rx.drv[0].pos = rx.pos = (rx.ta*RX_NSECS + (rx.sa - 1)) * rx.len;
sd_read_bytes(rx.drv[0].name, rx.pos, rx.buffer, rx.len);
// indicate SD card access complete
led_state(yellow, off);
// init time delay
rx_timing(RX_INIT_TIME);
// setup error/status bits
rx.es = rx_init_es(RXES_ID);
// transmit status on INIT
rx_xmit_es(rx.es);
// led status
led_state(red, off);
return;
}
//
// setup the file for rx drive unit
//
void rx_set_unit_file (uint8_t unit, char *name)
{
uint32_t size;
// setup file name if provided
if (name) strcpy(rx.drv[unit].name, name);
// get size of (existing) file, check it for SD or DD or none
size = sd_get_file_size(rx.drv[unit].name);
if (!(rx.type == RX_TYPE_RX01 && size == rx_dsk_size(RX_DEN_SD) ||
rx.type == RX_TYPE_RX02 && (size == rx_dsk_size(RX_DEN_SD) || size == rx_dsk_size(RX_DEN_DD)))
) sd_set_file_size(rx.drv[unit].name, rx.type == RX_TYPE_RX01 ? rx_dsk_size(RX_DEN_SD) : rx_dsk_size(RX_DEN_DD));
// set initial state from common block
rx.drv[unit].ta = rx.ta;
rx.drv[unit].sa = rx.sa;
rx.drv[unit].len = rx.len;
rx.drv[unit].pos = rx.pos;
// set drive ready if file exists and is right size (SD or DD)
rx.drv[unit].rdy = sd_get_file_size(rx.drv[unit].name) > 0;
// set drive density based on file size
rx.drv[unit].den = sd_get_file_size(rx.drv[unit].name) == rx_dsk_size(RX_DEN_DD) ? RX_DEN_DD : RX_DEN_SD;
// zap the deleted data state to normal
memset(rx.drv[unit].dd, RX_NORMAL_DATA, sizeof(rx.drv[unit].dd));
return;
}
//
// setup the RX02 subsystem debug port
//
void rx_debug (HardwareSerial *serialPort)
{
// some status
if (debug != NULL && serialPort == NULL) debug->printf("RX: debug disabled\n");
if (debug == NULL && serialPort != NULL) serialPort->printf("RX: debug enabled\n");
// set debug serial port (or not)
debug = serialPort;
// and done
return;
}
//
// print RX emulator state
//
void rx_print_state (HardwareSerial *prt)
{
uint8_t i;
char den[] = { 'S', 'D', 'Q' };
// dump to user supplied port if not null
if (prt == NULL) prt = debug;
// else try debug port, else return
if (prt == NULL) return;
prt->printf("\n--- RX Emulator State Dump ---\n\n");
prt->printf(" i/f width = %d.\n", rx_get_bits());
prt->printf(" i/f mode = %s\n", rx_tst_dma() ? "DMA" : "PIO");
prt->printf(" i/f init = %o\n", rx_tst_init());
prt->printf(" i/f run = %o\n", rx_tst_run());
prt->printf("\n");
prt->printf(" rx.cs = %06o\n", rx.cs);
prt->printf(" rx.es = %04o\n", rx.es);
prt->printf(" rx.wc = %03o\n", rx.wc);
prt->printf(" rx.bc = %03o\n", rx.bc);
prt->printf(" rx.ta = %03o\n", rx.ta);
prt->printf(" rx.sa = %03o\n", rx.sa);
prt->printf(" rx.pos = %lu.\n", rx.pos);
prt->printf(" rx.len = %u.\n", rx.len);
prt->printf(" rx.den = %cD\n", den[rx.den]);
prt->printf(" rx.unit = %o\n", rx.unit);
prt->printf(" rx.type = RX0%d\n", rx.type+1);
prt->printf(" rx.ecode = %04o\n", rx.ecode);
prt->printf(" rx.fcn.code = %o\n", rx.fcn.code);
prt->printf(" rx.fcn.name = %s\n", rx.fcn.name);
for (i = 0; i < RX_NUNITS; ++i) {
prt->printf("\n");
prt->printf(" rx.drv[%d].name = '%s'\n", i, rx.drv[i].name);
prt->printf(" rx.drv[%d].rdy = %c\n", i, rx.drv[i].rdy ? 'Y' : 'N');
prt->printf(" rx.drv[%d].den = %cD\n", i, den[rx.drv[i].den]);
prt->printf(" rx.drv[%d].ta = %03o\n", i, rx.drv[i].ta);
prt->printf(" rx.drv[%d].sa = %03o\n", i, rx.drv[i].sa);
prt->printf(" rx.drv[%d].pos = %lu.\n", i, rx.drv[i].pos);
prt->printf(" rx.drv[%d].len = %u.\n", i, rx.drv[i].len);
}
return;
}
//
// execute an RX function
//
void rx_function (void)
{
uint16_t i, j;
uint16_t value;
// setup for a new request
rx_clr_request();
rx_set_done();
// setup INIT seen callback
if (setjmp(rx_init_env)) {
// return to here on longjmp(rx_init_env,N);
rx_init_seen = 0;
rx_initialize(false);
return;
}
// wait for RUN or INIT
uint8_t stat = rx_wait_run_or_init(250);
// return if timeout with no RUN or INIT
if (stat == RX_SAW_NONE) return;
// fall thru if RUN seen ...
// RUN seen, process command
// receive a command word: 12b on RX211 or RX8E/28 in 12b mode; 8b otherwise
rx.cs = rx_recv(rx_tst_dma() || rx_tst_12b() ? 12 : 8);
if (debug) debug->printf("RX: cmd=%04o\n", rx.cs);
// led status
led_state(red, off);
led_state(green, on);
led_state(yellow, off);
// separate out the function field
rx.fcn.code = (rx.cs & RXCS_FUNCTION)>>1;
rx.fcn.name = fcn_name_list[rx.fcn.code];
// separate out the unit number in the command
rx.unit = (rx.cs & RXCS_UNITSEL) ? 1 : 0;
// separate out the density flag in the command
rx.den = (rx.cs & RXCS_DENSEL) ? RX_DEN_DD : RX_DEN_SD;
// setup error/status bits
rx.es = rx_init_es();
// initial error code: success!
rx.ecode = RXERR_SUCCESS;
// print command
if (debug) debug->printf("RX: %s unit=%o den=%o\n", rx.fcn.name, rx.unit, rx.den);
// decode command function
switch (rx.fcn.code) {
// === buffer fill or empty ===
//
// DMA: command(12), wordcount(8), N*databytes(8)
// PIO: command(12), N*databytes(8 or 12)
//
case RXFCN_FILL:
case RXFCN_EMPTY:
// compute word count
if (rx_tst_dma()) {
// if DMA mode, receive word count from interface
rx.wc = rx_recv_hs(8);
// receive bus address
// rx.ba = rx_recv_hs(16); // done in RX211 hardware
} else {
// if PIO mode, compute word count from full sector size
rx.wc = rx_sec_size(rx.den) >> (rx_tst_8b() ? 1 : 2);
}
if (debug) debug->printf("RX: %s wc=%03o\n", rx.fcn.name, rx.wc);
// transform word count to byte count for transfer
rx.bc = 2*rx.wc;
// check for word count overflow
if (rx.bc > rx_sec_size(rx.drv[rx.unit].den)) {
if (debug) debug->printf("RX: %s wc=%03o WCOVF\n", rx.fcn.name, rx.wc);
rx.ecode = RXERR_WCOVF;
rx.es |= RXES_WCOVF;
goto error;
}
// transfer data words in/out
if (rx.fcn.code == RXFCN_EMPTY) {
// empty buffer (RX controller buffer to host interface)
for (i = 0; i < rx.bc; ++i) {
if (rx_tst_8b()) {
// extract 8b values from buffer to send as 8b
value = rx.buffer[i];
rx_xmit_hs(value, 8);
if (debug) debug->printf("RX: %s d[%03o]=%03o\n", rx.fcn.name, i, value);
} else {
// 12b values are packed into the first 2/3 of the sector; read 2 8b entries and build 12b value
j = 3*i/2;
if (i & 1) {
// odd byte
value = ((rx.buffer[j+0] & 017) << 8) | rx.buffer[j+1];
} else {
// even byte
value = (rx.buffer[j+0] << 4) | ((rx.buffer[j+1] >> 4) & 017);
}
rx_xmit_hs(value, 12);
if (debug) debug->printf("RX: %s d[%03o]=%04o\n", rx.fcn.name, i, value);
}
}
} else {
// fill buffer (host interface to RX controller buffer)
for (i = 0; i < rx.bc; ++i) {
if (rx_tst_8b()) {
// 8b values get stuffed into 8b buffer directly
value = rx_recv_hs(8);
rx.buffer[i] = value;
if (debug) debug->printf("RX: %s d[%03o]=%03o\n", rx.fcn.name, i, value);
} else {
// 12b values get split into 4b/8b and merged into two 8b entries
value = rx_recv_hs(12);
j = 3*i/2;
if (i & 1) {
// odd byte
rx.buffer[j+0] |= (value >> 8) & 017;
rx.buffer[j+1] = value;
} else {
// even byte
rx.buffer[j+0] = value >> 4;
rx.buffer[j+1] = (value & 017) << 4;
}
if (debug) debug->printf("RX: %s d[%03o]=%04o\n", rx.fcn.name, i, value);
}
}
// zero fill any unwritten bytes in the buffer
for (i = rx.bc, j = rx_sec_size(rx.den); i < j; ++i) rx.buffer[i] = 0;
}
// indicate transfer is complete
rx.wc = 0;
break;
// === read/write sector ===
//
// command(12), sector(8 or 12), track(8 or 12)
//
case RXFCN_RDSECT:
case RXFCN_WRSECT:
case RXFCN_WRDDSECT:
// first word is sector address
rx.sa = rx_recv_hs() & 037;
if (debug) debug->printf("RX: %s sa=%d.\n", rx.fcn.name, rx.sa);
// second word is track address
rx.ta = rx_recv_hs() & 0177;
if (debug) debug->printf("RX: %s ta=%d.\n", rx.fcn.name, rx.ta);
// check drive is ready
if (!rx.drv[rx.unit].rdy) {
// nope, clear drive ready bit and error
rx.es &= ~RXES_DRVRDY;
goto error;
}
// check track access is valid
if (rx.ta >= RX_NTRKS) {
rx.ecode = RXERR_TRKERR;
goto error;
}
// check density matches, error if does not
if (rx.type != RX_TYPE_RX01 && rx.den != rx.drv[rx.unit].den) {
rx.es |= RXES_DENERR;
rx.ecode = RXERR_DENERR;
goto error;
}
// check sector address is valid
if (rx.sa < 1 || rx.sa > RX_NSECS) {
rx.ecode = RXERR_SECFAIL;
goto error;
}
// compute length of transfer (sector size)
rx.len = rx_sec_size(rx.drv[rx.unit].den);
// compute byte offset into disk image
rx.pos = (rx.ta*RX_NSECS + (rx.sa-1)) * rx.len;
// print details if in debug mode
if (debug) debug->printf("RX: %s pos=%lu. len=%u.\n", rx.fcn.name, rx.pos, rx.len);
// bail on INIT seen
if (rx_init_seen) { longjmp(rx_init_env, 20); }
// indicate SD card access in progress
led_state(yellow, on);
// do the read or write
if (rx.fcn.code == RXFCN_RDSECT) {
// do a read; copy data into buffer from diskimage@offset
value = sd_read_bytes(rx.drv[rx.unit].name, rx.pos, rx.buffer, rx.len);
} else {
// do a write
if (rx.fcn.code == RXFCN_WRDDSECT) {
bitWrite(rx.drv[rx.unit].dd[rx.ta], rx.sa, RX_DELETED_DATA);
} else {
bitWrite(rx.drv[rx.unit].dd[rx.ta], rx.sa, RX_NORMAL_DATA);
}
// copy data from rx.buffer[] to diskimage@offset
value = sd_write_bytes(rx.drv[rx.unit].name, rx.pos, rx.buffer, rx.len);
}
if (bitRead(rx.drv[rx.unit].dd[rx.ta], rx.sa) == RX_DELETED_DATA) rx.es |= RXES_DELDATA;
// SD card access complete
led_state(yellow, off);
// simulate timing
rx_timing(rx_seek_time(abs(rx.ta - rx.drv[rx.unit].ta)));
// update state info for drive
rx.drv[rx.unit].ta = rx.ta;
rx.drv[rx.unit].sa = rx.sa;
rx.drv[rx.unit].pos = rx.pos;
rx.drv[rx.unit].len = rx.len;
// check for read/write error
if (value != rx.len) {
rx.ecode = RXERR_IDMFAIL;
goto error;
}
break;
// === set media density ===
//
// command(12), key(8 or 12)
//
case RXFCN_SETMEDIA:
// this function is a NOP for the RX01
if (rx.type == RX_TYPE_RX01) break;
// first word must be the magic key 'I'
value = rx_recv_hs();
// check key for expected value
if (value != 'I') {
rx.ecode = RXERR_KEYERR;
goto error;
}
// check drive is ready
if (!rx.drv[rx.unit].rdy) {
// nope, clear drive ready bit and error
rx.es &= ~RXES_DRVRDY;
goto error;
}
// simulate timing
rx_timing(RX_SETMEDIA_TIME);
// bail on INIT seen
if (rx_init_seen) { longjmp(rx_init_env, 21); }
// indicate SD card access in progress
led_state(yellow, on);
// change density to requested value
sd_set_file_size(rx.drv[rx.unit].name, rx_dsk_size(rx.den));
rx.drv[rx.unit].den = rx.den;
// indicate SD card access complete
led_state(yellow, off);
break;
// === read status ===
//
// command(12)
//
case RXFCN_RDSTAT:
// check drive is ready
if (!rx.drv[rx.unit].rdy) {
// nope, clear drive ready bit and error
rx.es &= ~RXES_DRVRDY;
goto error;
}
// simulate timing
rx_timing(RX_RDSTAT_TIME);
// check that density matches, error if does not
if (rx.type != RX_TYPE_RX01 && rx.den != rx.drv[rx.unit].den) {
rx.es |= RXES_DENERR;
rx.ecode = RXERR_DENERR;
goto error;
}
break;
// === read error code ===
//
// command(12)
//
case RXFCN_RDERROR:
// simulate timing
rx_timing(RX_RDERROR_TIME);
// only executes on RX211/RXV21; return four 16b status words
if (rx_tst_dma()) {
// receive bus address
// rx.ba = rx_recv_hs(16); // done in RX211 hardware
// generate status byte
value = (rx.unit == 1 ? (1<<7) : 0)
| (rx.drv[1].den == RX_DEN_DD ? (1<<6) : 0)
| (rx.drv[rx.unit].rdy ? (1<<5) : 0)
| (rx.drv[0].den == RX_DEN_DD ? (1<<4) : 0)
| (rx.den == RX_DEN_DD ? (1<<0) : 0);
// word 1
rx_xmit_hs(rx.ecode, 8);
rx_xmit_hs(rx.wc, 8);
// word 2
rx_xmit_hs(rx.drv[0].ta, 8);
rx_xmit_hs(rx.drv[1].ta, 8);
// word 3
rx_xmit_hs(rx.ta, 8);
rx_xmit_hs(rx.sa, 8);
// word 4
rx_xmit_hs(value, 8);
rx_xmit_hs(rx.drv[rx.unit].ta, 8);
}
break;
} // switch (rx.fcn.num)
done:
rx_xmit_es(rx.es);
led_state(green, off);
return;
error:
rx_set_error();
led_state(red, on);
goto done;
}
// the end
Just for reference here is my current Arduino driver code for the RX02 interface, originally based on the CHD RXM, but with lots of optimization tweaks for the Arduino.
At some point I'll probably upload my entire RX02 emulator project to my GITHUB area alongside TU58EM et al, but here is a 'first look' for anyone interested in comparing vs the Bela implementation.
rx02_driver.h prototype file:
Code:// // rx02_driver - Simple RX02 driver external interface // // (C) 2013 Don North <ak6dn_at_mindspring_dot_com> // // 21 Oct 2015 - donorth - Initial code // 27 Mar 2016 - donorth- Ported to Arduino from CCS/Microchip // #ifndef rx02_driver_h #define rx02_driver_h // // public prototypes // void rx_initialize (uint8_t flag); void rx_debug (HardwareSerial *serialPort); void rx_print_state (HardwareSerial *serialPort); void rx_set_unit_file (uint8_t unit, char *name); void rx_function (void); #endif // rx02_driver_h // the end
rx02_driver.cpp implementation:
Code:// // rx02_driver - Simple RX02 driver // // (C) 2013 Don North <ak6dn_at_mindspring_dot_com> // // 21 Oct 2013 - donorth - Initial code // 27 Mar 2016 - donorth- Ported to Arduino from CCS/Microchip // // // includes // #include "my_project.h" #include "led_driver.h" #include "rx02_driver.h" #include "sdcard_driver.h" // // definitions // // options #define USE_BIT_BUILTINS 1 // use bit builtins BitRead() etc vs use logical bit shift/mask operations // RXCS control/status bit definitions //efine RXCS_GO (1<<0) // GO bit (reference only; not used in drive) #define RXCS_FUNCTION (7<<1) // function bitfield #define RXCS_UNITSEL (1<<4) // unit select //efine RXCS_DONE (1<<5) // function complete (reference only; not used in drive) //efine RXCS_INTENB (1<<6) // interrupt enable (reference only; not used in drive) //efine RXCS_8BIT (1<<6) // 8b mode (RX8E/RX28 ONLY) //efine RXCS_TR (1<<7) // transfer request (reference only; not used in drive) //efine RXCS_MAINT (1<<7) // maintenance mode (RX8E/RX28 ONLY) #define RXCS_DENSEL (1<<8) // density select //efine RXCS_HEADSEL (1<<9) // head select //efine RXCS_RX02 (1<<11) // RX02 interface (reference only; not used in drive) //efine RXCS_EXTADDR (3<<12) // upper bits of phys address (reference only; not used in drive) //efine RXCS_INIT (1<<14) // initialize RX (reference only; not used in drive) //efine RXCS_ERROR (1<<15) // error flag (reference only; not used in drive) #define RXFCN_FILL (0) // fill buffer #define RXFCN_EMPTY (1) // empty buffer #define RXFCN_WRSECT (2) // write sector #define RXFCN_RDSECT (3) // read sector #define RXFCN_SETMEDIA (4) // set media density #define RXFCN_RDSTAT (5) // read status #define RXFCN_WRDDSECT (6) // write deleted data sector #define RXFCN_RDERROR (7) // read error code // RXES status bit definitions //efine RXES_CRC (1<<0) // CRC aka READ error //efine RXES_SIDE (1<<1) // side (RX211/RX28 ONLY) //efine RXES_PERR (1<<1) // parity error (RX11/RX8E w/RX01 ONLY) #define RXES_ID (1<<2) // controller init done #define RXES_RX02 (1<<3) // set for RX02 drive (RX28 w/ RX02 ONLY) //efine RXES_ACLO (1<<3) // set for AC low (RX211 w/RX02 ONLY) //efine RXES_WPERR (1<<3) // set for write to WP drive (RX11/RX8E w/RX01 ONLY) #define RXES_DENERR (1<<4) // density error #define RXES_DRVDEN (1<<5) // diskette double density #define RXES_DELDATA (1<<6) // deleted data detected #define RXES_DRVRDY (1<<7) // drive ready #define RXES_UNITSEL (1<<8) // unit selected (RX211 w/RX02 ONLY) #define RXES_WCOVF (1<<10) // word count overflow (RX211 w/RX02 ONLY) //efine RXES_NXM (1<<11) // nonexistent memory (RX211 w/RX02 reference only; not used in drive) // RX error codes #define RXERR_SUCCESS 0000 // success, no error //efine RXERR_DR0INIT 0010 // drive 0 failed to init //efine RXERR_DR1INIT 0020 // drive 1 failed to init //efine RXERR_STEPHOME 0030 // found home when stepping for init (RX01 ONLY) #define RXERR_TRKERR 0040 // access to track > 76 #define RXERR_TRKFAIL 0050 // track not found //efine RXERR_SELFDIAG 0060 // self diagnostic fail (RX01 ONLY) #define RXERR_SECFAIL 0070 // sector not found //efine RXERR_WRITEWP 0100 // write to a WP drive (RX01 ONLY) //efine RXERR_SEPFAIL 0110 // no SEP clock in 40us //efine RXERR_PREFAIL 0120 // preamble not found #define RXERR_IDMFAIL 0130 // ID mark not found //efine RXERR_CRCHEAD 0140 // CRC error on a header (RX01 ONLY) //efine RXERR_TRKCMP 0150 // header track miscompare //efine RXERR_IDMTRYS 0160 // too many tries for IDAM //efine RXERR_DAMFAIL 0170 // data AM not found //efine RXERR_CRCERR 0200 // CRC error on read //efine RXERR_PERR 0210 // parity error on word from i/f to controller (RX01 ONLY) //efine RXERR_RWFAIL 0220 // r/w failed maint test (RX02 ONLY) #define RXERR_WCOVF 0230 // word count overflow (RX02 ONLY) #define RXERR_DENERR 0240 // density error (RX02 ONLY) #define RXERR_KEYERR 0250 // wrong key word for set density (RX02 ONLY) // macros // rx interface outputs set/clr #if USE_BIT_BUILTINS #define rx_set_done(xxx) bitSet(PORTA,6) /* digitalWrite(PIN_CTLR_DONE_H,HIGH) */ #define rx_clr_done(xxx) bitClear(PORTA,6) /* digitalWrite(PIN_CTLR_DONE_H,LOW) */ #define rx_set_error(xxx) bitSet(PORTA,7) /* digitalWrite(PIN_CTLR_ERROR_H,HIGH) */ #define rx_clr_error(xxx) bitClear(PORTA,7) /* digitalWrite(PIN_CTLR_ERROR_H,LOW) */ #define rx_set_aclo(xxx) bitSet(PORTC,1) /* digitalWrite(PIN_CTLR_ACLO_H,HIGH) */ #define rx_clr_aclo(xxx) bitClear(PORTC,1) /* digitalWrite(PIN_CTLR_ACLO_H,LOW) */ #define rx_set_shift(xxx) bitSet(PORTC,2) /* digitalWrite(PIN_CTLR_SHIFT_H,HIGH) */ #define rx_clr_shift(xxx) bitClear(PORTC,2) /* digitalWrite(PIN_CTLR_SHIFT_H,LOW) */ #define rx_set_out(xxx) bitSet(PORTC,3) /* digitalWrite(PIN_CTLR_OUT_H,HIGH) */ #define rx_clr_out(xxx) bitClear(PORTC,3) /* digitalWrite(PIN_CTLR_OUT_H,LOW) */ #define rx_set_datao(xxx) bitSet(PORTC,5) /* digitalWrite(PIN_CTLR_DATAO_H,HIGH) */ #define rx_clr_datao(xxx) bitClear(PORTC,5) /* digitalWrite(PIN_CTLR_DATAO_H,LOW) */ #define rx_set_request(xxx) bitSet(PORTC,6) /* digitalWrite(PIN_CTLR_TR_RQST_H,HIGH) */ #define rx_clr_request(xxx) bitClear(PORTC,6) /* digitalWrite(PIN_CTLR_TR_RQST_H,LOW) */ // rx interface inputs test #define rx_tst_pio(xxx) bitRead(PINC,0) /* digitalRead(PIN_CTLR_DMA_MODE_L) */ #define rx_tst_dma(xxx) (rx_tst_pio()?0:1) /* !digitalRead(PIN_CTLR_DMA_MODE_L) */ #define rx_tst_datai(xxx) bitRead(PINC,4) /* digitalRead(PIN_CTLR_DATAI_H) */ #define rx_tst_12b(xxx) bitRead(PINC,7) /* digitalRead(PIN_CTLR_12BIT_H) */ #define rx_tst_8b(xxx) (rx_tst_12b()?0:1) /* !digitalRead(PIN_CTLR_12BIT_H) */ #define rx_tst_run(xxx) bitRead(PINE,4) /* digitalRead(PIN_CTLR_RUN_H)) */ #define rx_tst_init(xxx) bitRead(PINE,5) /* digitalRead(PIN_CTLR_INIT_H) */ #else #define rx_set_done(xxx) PORTA |= (1<<6) /* digitalWrite(PIN_CTLR_DONE_H,HIGH) */ #define rx_clr_done(xxx) PORTA &= ~(1<<6) /* digitalWrite(PIN_CTLR_DONE_H,LOW) */ #define rx_set_error(xxx) PORTA |= (1<<7) /* digitalWrite(PIN_CTLR_ERROR_H,HIGH) */ #define rx_clr_error(xxx) PORTA &= ~(1<<7) /* digitalWrite(PIN_CTLR_ERROR_H,LOW) */ #define rx_set_aclo(xxx) PORTC |= (1<<1) /* digitalWrite(PIN_CTLR_ACLO_H,HIGH) */ #define rx_clr_aclo(xxx) PORTC &= ~(1<<1) /* digitalWrite(PIN_CTLR_ACLO_H,LOW) */ #define rx_set_shift(xxx) PORTC |= (1<<2) /* digitalWrite(PIN_CTLR_SHIFT_H,HIGH) */ #define rx_clr_shift(xxx) PORTC &= ~(1<<2) /* digitalWrite(PIN_CTLR_SHIFT_H,LOW) */ #define rx_set_out(xxx) PORTC |= (1<<3) /* digitalWrite(PIN_CTLR_OUT_H,HIGH) */ #define rx_clr_out(xxx) PORTC &= ~(1<<3) /* digitalWrite(PIN_CTLR_OUT_H,LOW) */ #define rx_set_datao(xxx) PORTC |= (1<<5) /* digitalWrite(PIN_CTLR_DATAO_H,HIGH) */ #define rx_clr_datao(xxx) PORTC &= ~(1<<5) /* digitalWrite(PIN_CTLR_DATAO_H,LOW) */ #define rx_set_request(xxx) PORTC |= (1<<6) /* digitalWrite(PIN_CTLR_TR_RQST_H,HIGH) */ #define rx_clr_request(xxx) PORTC &= ~(1<<6) /* digitalWrite(PIN_CTLR_TR_RQST_H,LOW) */ // rx interface inputs test #define rx_tst_pio(xxx) ((PINC & (1<<0)) ? 1 : 0) /* (digitalRead(PIN_CTLR_DMA_MODE_L)==HIGH) */ #define rx_tst_dma(xxx) ((PINC & (1<<0)) ? 0 : 1) /* (digitalRead(PIN_CTLR_DMA_MODE_L)==LOW) */ #define rx_tst_datai(xxx) ((PINC & (1<<4)) ? 1 : 0) /* digitalRead(PIN_CTLR_DATAI_H) */ #define rx_tst_12b(xxx) ((PINC & (1<<7)) ? 1 : 0) /* (digitalRead(PIN_CTLR_12BIT_H)==HIGH) */ #define rx_tst_8b(xxx) ((PINC & (1<<7)) ? 0 : 1) /* (digitalRead(PIN_CTLR_12BIT_H)==LOW) */ #define rx_tst_run(xxx) ((PINE & (1<<4)) ? 1 : 0) /* (digitalRead(PIN_CTLR_RUN_H)==HIGH) */ #define rx_tst_init(xxx) ((PINE & (1<<5)) ? 1 : 0) /* (digitalRead(PIN_CTLR_INIT_H)==HIGH) */ #endif // INIT/RUN status #define RX_SAW_NONE 0 // saw neither RUN or INIT #define RX_SAW_RUN 1 // saw RUN and not INIT // emulation type #define RX_TYPE_RX01 0 // RX02 in RX01 mode #define RX_TYPE_RX02 1 // RX02 in native mode // drive density #define RX_DEN_SD 0 // single density mode #define RX_DEN_DD 1 // double density mode // data type #define RX_NORMAL_DATA 0 // normal data #define RX_DELETED_DATA 1 // deleted data // disk definitions #define RX_NTRKS 77L // number of tracks per disk #define RX_NSECS 26L // number of sectors per track #define RX_NBPS 128L // bytes per sector, single density #define RX_NUNITS 2 // number if units #define RX_BUFFER_SIZE (2*RX_NBPS) // maximum size sector buffer for double density // convenience macros #define rx_sec_size(den) (RX_NBPS<<(den)) // bytes per sector at density #define rx_trk_size(den) (RX_NSECS*rx_sec_size(den)) // bytes per track at density #define rx_dsk_size(den) (RX_NTRKS*rx_trk_size(den)) // bytes per disk at density #define rx_get_bits(xxx) (rx_tst_12b() ? 12 : 8) // number of bits signaled by interface // // timing characteristics // #define RX02_ACTUAL_SPEED 0 // actual hardware timing #if (RX02_ACTUAL_SPEED == 1) #define RX_RDSTAT_TIME (250) // read status function #define RX_INIT_TIME (500) // initialize floppy drive #define RX_STEP_TIME (10) // track step time #define RX_SETTLE_TIME (20) // seek settling time #define RX_SEC_TIME (20) // sector access time #define RX_RDERROR_TIME (1) // read error registers #define RX_SETMEDIA_TIME (10000) // set media density #endif // (RX02_ACTUAL_SPEED == 1) // as fast as it can go #if (RX02_ACTUAL_SPEED == 0) #define RX_RDSTAT_TIME (0) // read status function #define RX_INIT_TIME (0) // initialize floppy drive #define RX_STEP_TIME (0) // track step time #define RX_SETTLE_TIME (0) // seek settling time #define RX_SEC_TIME (0) // sector access time #define RX_RDERROR_TIME (0) // read error registers #define RX_SETMEDIA_TIME (0) // set media density #endif // (RX02_ACTUAL_SPEED == 0) // compute seek time based on delta track amount #define rx_seek_time(d) (RX_SEC_TIME + ((abs(d) == 0) ? 0 : RX_SETTLE_TIME + abs(d)*RX_STEP_TIME)) // PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE volatile static uint8_t rx_init_seen; // set when RX INIT asserted (rising edge) static jmp_buf rx_init_env; // environment to invoke when INIT is asserted (ie, reset/error condition) static char fcn_name_list[][10] = { "FILL", "EMPTY", "WRSECT", "RDSECT", "SETMEDIA", "RDSTAT", "WRDDSECT", "RDERROR" }; static uint8_t rx_type = RX_TYPE_RX02; // default drive emulation, can be overwritten struct drv_t { uint32_t dd[RX_NTRKS]; // normal or deleted data per track per sector (1 bit per sector 0..31) char name[32]; // associated file name uint8_t rdy; // drive ready uint8_t den; // density uint8_t ta; // track address uint8_t sa; // sector address uint16_t len; // read/write length uint32_t pos; // read/write position }; static struct rx_t { uint8_t type; // emulation type: RX01, RX02 uint16_t cs; // control/status image uint16_t es; // error and status uint16_t bc; // byte count uint8_t wc; // word count uint8_t ecode; // error code uint8_t unit; // active unit number uint8_t den; // density selected, DD or SD uint8_t ta; // track address uint8_t sa; // sector address uint16_t len; // read/write length uint32_t pos; // read/write position struct { uint8_t code; // function selected (numeric 0..7) char * name; // function selected, ascii name id } fcn; struct drv_t drv[RX_NUNITS]; // drive specific parameters uint8_t buffer[RX_BUFFER_SIZE]; // sector buffer } rx; static HardwareSerial *debug = NULL; // debug serial output // // RX02 INIT assertion interrupt // static void rx_intr_init (void) { rx_init_seen = 1; return; } // // RX time delay routine // static void rx_timing (uint16_t ms_delay) { const uint16_t ms_amount = 5; // check for delay requested if (ms_delay > 0) { // delay in 5ms increments, checking for INIT while (ms_delay > ms_amount) { if (rx_init_seen) { longjmp(rx_init_env, 10); } delay(ms_amount); ms_delay -= ms_amount; } // final small delay less than 5ms if (rx_init_seen) { longjmp(rx_init_env, 10); } delay(ms_delay); } // all done if (rx_init_seen) { longjmp(rx_init_env, 10); } return; } // // wait until the RX_RUN or RX_INIT signal is asserted, or timeout occurs // static uint8_t rx_wait_run_or_init (uint32_t expire) { uint32_t start = millis(); do { if (rx_init_seen) { longjmp(rx_init_env, 1); } if (rx_tst_run()) { return RX_SAW_RUN; } } while (millis()-start <= expire); // count timed out with neither RUN or INIT return RX_SAW_NONE; } // wait forever until the RX_RUN or RX_INIT signal is asserted static uint8_t rx_wait_run_or_init (void) { do { if (rx_init_seen) { longjmp(rx_init_env, 2); } if (rx_tst_run()) { return RX_SAW_RUN; } } while (TRUE); } // // receive a 12/8 bit word from the RX interface, msb first // #define _rx_recv_hs(xxx,yyy) { if (rx_tst_datai()) { delay_1c(); (xxx) |= (1<<(yyy)); } else { (xxx) &= ~(1<<(yyy)); delay_2c(); } delay_1c(); rx_set_shift(); delay_1c(); rx_clr_shift(); } static uint16_t rx_recv12_hs (uint8_t handshake) { uint16_t data = 0; rx_clr_out(); rx_clr_done(); rx_clr_datao(); rx_clr_error(); rx_clr_shift(); if (handshake) { rx_set_request(); rx_wait_run_or_init(); rx_clr_request(); } noInterrupts(); _rx_recv_hs(data,11); _rx_recv_hs(data,10); _rx_recv_hs(data,9); _rx_recv_hs(data,8); _rx_recv_hs(data,7); _rx_recv_hs(data,6); _rx_recv_hs(data,5); _rx_recv_hs(data,4); _rx_recv_hs(data,3); _rx_recv_hs(data,2); _rx_recv_hs(data,1); _rx_recv_hs(data,0); interrupts(); if (rx_init_seen) { longjmp(rx_init_env, 3); } return data; } static uint16_t rx_recv8_hs (uint8_t handshake) { uint8_t data = 0; rx_clr_out(); rx_clr_done(); rx_clr_datao(); rx_clr_error(); rx_clr_shift(); if (handshake) { rx_set_request(); rx_wait_run_or_init(); rx_clr_request(); } noInterrupts(); _rx_recv_hs(data,7); _rx_recv_hs(data,6); _rx_recv_hs(data,5); _rx_recv_hs(data,4); _rx_recv_hs(data,3); _rx_recv_hs(data,2); _rx_recv_hs(data,1); _rx_recv_hs(data,0); interrupts(); if (rx_init_seen) { longjmp(rx_init_env, 4); } return uint16_t(data); } static uint16_t rx_recv (uint8_t n) { if (n == 12) return rx_recv12_hs(FALSE); else if (n == 8) return rx_recv8_hs(FALSE); else if (rx_tst_12b()) return rx_recv12_hs(FALSE); else return rx_recv8_hs(FALSE); } static uint16_t rx_recv (void) { if (rx_tst_12b()) return rx_recv12_hs(FALSE); else return rx_recv8_hs(FALSE); } static uint16_t rx_recv_hs (uint8_t n) { if (n == 12) return rx_recv12_hs(TRUE); else if (n == 8) return rx_recv8_hs(TRUE); else if (rx_tst_12b()) return rx_recv12_hs(TRUE); else return rx_recv8_hs(TRUE); } static uint16_t rx_recv_hs (void) { if (rx_tst_12b()) return rx_recv12_hs(TRUE); else return rx_recv8_hs(TRUE); } // // transmit a 12/8 bit word to the RX interface, msb first // #define _rx_xmit_hs(xxx,yyy) { if ((xxx)&(1<<(yyy))) { delay_1c(); rx_set_datao(); } else { rx_clr_datao(); delay_2c(); } delay_1c(); rx_set_shift(); delay_1c(); rx_clr_shift(); } static void rx_xmit12_hs (uint16_t value, uint8_t handshake) { uint16_t data = value; noInterrupts(); if (handshake) { rx_clr_request(); } rx_clr_shift(); rx_set_out(); delay_3c(); _rx_xmit_hs(data,11); _rx_xmit_hs(data,10); _rx_xmit_hs(data,9); _rx_xmit_hs(data,8); _rx_xmit_hs(data,7); _rx_xmit_hs(data,6); _rx_xmit_hs(data,5); _rx_xmit_hs(data,4); _rx_xmit_hs(data,3); _rx_xmit_hs(data,2); _rx_xmit_hs(data,1); _rx_xmit_hs(data,0); delay_3c(); rx_clr_datao(); interrupts(); if (handshake) { rx_set_request(); rx_wait_run_or_init(); rx_clr_request(); } rx_clr_out(); if (rx_init_seen) { longjmp(rx_init_env, 5); } return; } static void rx_xmit8_hs (uint16_t value, uint8_t handshake) { uint8_t data = value; noInterrupts(); if (handshake) { rx_clr_request(); } rx_clr_shift(); rx_set_out(); delay_3c(); _rx_xmit_hs(data,7); _rx_xmit_hs(data,6); _rx_xmit_hs(data,5); _rx_xmit_hs(data,4); _rx_xmit_hs(data,3); _rx_xmit_hs(data,2); _rx_xmit_hs(data,1); _rx_xmit_hs(data,0); delay_3c(); rx_clr_datao(); interrupts(); if (handshake) { rx_set_request(); rx_wait_run_or_init(); rx_clr_request(); } rx_clr_out(); if (rx_init_seen) { longjmp(rx_init_env, 6); } return; } static void rx_xmit (uint16_t data, uint8_t n) { if (n == 12) rx_xmit12_hs(data,FALSE); else if (n == 8) rx_xmit8_hs(data,FALSE); else if (rx_tst_12b()) rx_xmit12_hs(data,FALSE); else rx_xmit8_hs(data,FALSE); return; } static void rx_xmit (uint16_t data) { if (rx_tst_12b()) rx_xmit12_hs(data,FALSE); else rx_xmit8_hs(data,FALSE); return; } static void rx_xmit_hs (uint16_t data, uint8_t n) { if (n == 12) rx_xmit12_hs(data,TRUE); else if (n == 8) rx_xmit8_hs(data,TRUE); else if (rx_tst_12b()) rx_xmit12_hs(data,TRUE); else rx_xmit8_hs(data,TRUE); return; } static void rx_xmit_hs (uint16_t data) { if (rx_tst_12b()) rx_xmit12_hs(data,TRUE); else rx_xmit8_hs(data,TRUE); return; } // // transmit an extended status word // static void rx_xmit_es (uint16_t data) { if (debug) debug->printf("RX: rx_xmit_es(%04o)\n", data); if (rx_tst_dma() || rx_tst_12b() && rx.type == RX_TYPE_RX02) { // RX211/RXV21 or RX28 attached rx_set_done(); rx_clr_request(); rx_xmit(data, 12); rx_set_request(); } else if (rx_tst_12b()) { // RX8E attached rx_xmit(data, 12); } else { // RX11/RXV11 attached rx_xmit(data, 8); } return; } // // setup error/status word // static uint16_t rx_init_es (void) { uint16_t es; // clear all bits except init done es = (rx.es & RXES_ID); // insert drive type (RX28 only w/ RX02) if (rx.type == RX_TYPE_RX02 && rx_tst_12b()) es |= RXES_RX02; // insert unit selected if (rx.unit == 1 && rx_tst_dma()) es |= RXES_UNITSEL; // insert drive ready for that unit if (rx.drv[rx.unit].rdy) es |= RXES_DRVRDY; // insert density for that unit if (rx.drv[rx.unit].den == RX_DEN_DD) es |= RXES_DRVDEN; return es; } // setup error/status with flags: RXES_CRC, RXES_DENERR, RXES_DELDATA, RXES_WCOVF are data flag options static uint16_t rx_init_es (uint16_t data) { return rx_init_es() | data; } // PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC // // initialize the RX subsystem // // flag: TRUE for powerup init; FALSE for INIT pulse seen // void rx_initialize (uint8_t flag) { uint8_t i; // led status led_state(red, on); led_state(green, off); led_state(yellow, off); // setup at power up ... required first time only if (flag) { // setup pins pinMode(PIN_CTLR_DMA_MODE_L, INPUT); pinMode(PIN_CTLR_DATAI_H, INPUT); pinMode(PIN_CTLR_12BIT_H, INPUT); pinMode(PIN_CTLR_INIT_H, INPUT); // interrupt pinMode(PIN_CTLR_RUN_H, INPUT); // pinMode(PIN_CTLR_TR_RQST_H, OUTPUT); pinMode(PIN_CTLR_DATAO_H, OUTPUT); pinMode(PIN_CTLR_SHIFT_H, OUTPUT); pinMode(PIN_CTLR_ERROR_H, OUTPUT); pinMode(PIN_CTLR_ACLO_H, OUTPUT); pinMode(PIN_CTLR_DONE_H, OUTPUT); pinMode(PIN_CTLR_OUT_H, OUTPUT); // setup RX INIT interrupt rx_init_seen = 0; attachInterrupt(digitalPinToInterrupt(PIN_CTLR_INIT_H), rx_intr_init, RISING); } // setup control outputs rx_clr_out(); rx_clr_aclo(); rx_clr_done(); rx_clr_datao(); rx_clr_error(); rx_clr_shift(); rx_clr_request(); // toggle ACLO at initial reset if (flag) { rx_set_aclo(); delay(100); rx_clr_aclo(); } // wait for INIT to go away uint32_t start = millis(); // current time, in ms uint32_t expire = 10000UL; // 10 sec delay if (debug) debug->printf("RX: waiting for INIT to clear ... t=%lums\n", millis()); while ((rx_init_seen || rx_tst_init()) && millis()-start <= expire) { rx_init_seen = 0; } if (debug) debug->printf("RX: INIT has %scleared t=%lums\n", rx_tst_init() ? "NOT " : "", millis()); // setup drive data structures rx.type = rx_type; rx.fcn.code = 8; rx.fcn.name = "INIT"; rx.den = rx.type == RX_TYPE_RX02 ? RX_DEN_DD : RX_DEN_SD; rx.cs = 0; rx.es = 0; rx.ta = 0; rx.sa = 0; rx.wc = 0; rx.bc = 0; rx.len = 0; rx.pos = 0; rx.unit = 0; rx.ecode = 0; // setup drive state for (i = 0; i < RX_NUNITS; ++i) { if (flag) sprintf(rx.drv[i].name, "RX%d.DSK", i); rx_set_unit_file(i, NULL); } // clear sector buffer memset(rx.buffer, 0x00, sizeof(rx.buffer)); // indicate SD card access in progress led_state(yellow, on); // copy drive 0 boot sector into the buffer rx.drv[0].ta = rx.ta = 1; rx.drv[0].sa = rx.sa = 1; rx.drv[0].len = rx.len = rx_sec_size(rx.drv[0].den); rx.drv[0].pos = rx.pos = (rx.ta*RX_NSECS + (rx.sa - 1)) * rx.len; sd_read_bytes(rx.drv[0].name, rx.pos, rx.buffer, rx.len); // indicate SD card access complete led_state(yellow, off); // init time delay rx_timing(RX_INIT_TIME); // setup error/status bits rx.es = rx_init_es(RXES_ID); // transmit status on INIT rx_xmit_es(rx.es); // led status led_state(red, off); return; } // // setup the file for rx drive unit // void rx_set_unit_file (uint8_t unit, char *name) { uint32_t size; // setup file name if provided if (name) strcpy(rx.drv[unit].name, name); // get size of (existing) file, check it for SD or DD or none size = sd_get_file_size(rx.drv[unit].name); if (!(rx.type == RX_TYPE_RX01 && size == rx_dsk_size(RX_DEN_SD) || rx.type == RX_TYPE_RX02 && (size == rx_dsk_size(RX_DEN_SD) || size == rx_dsk_size(RX_DEN_DD))) ) sd_set_file_size(rx.drv[unit].name, rx.type == RX_TYPE_RX01 ? rx_dsk_size(RX_DEN_SD) : rx_dsk_size(RX_DEN_DD)); // set initial state from common block rx.drv[unit].ta = rx.ta; rx.drv[unit].sa = rx.sa; rx.drv[unit].len = rx.len; rx.drv[unit].pos = rx.pos; // set drive ready if file exists and is right size (SD or DD) rx.drv[unit].rdy = sd_get_file_size(rx.drv[unit].name) > 0; // set drive density based on file size rx.drv[unit].den = sd_get_file_size(rx.drv[unit].name) == rx_dsk_size(RX_DEN_DD) ? RX_DEN_DD : RX_DEN_SD; // zap the deleted data state to normal memset(rx.drv[unit].dd, RX_NORMAL_DATA, sizeof(rx.drv[unit].dd)); return; } // // setup the RX02 subsystem debug port // void rx_debug (HardwareSerial *serialPort) { // some status if (debug != NULL && serialPort == NULL) debug->printf("RX: debug disabled\n"); if (debug == NULL && serialPort != NULL) serialPort->printf("RX: debug enabled\n"); // set debug serial port (or not) debug = serialPort; // and done return; } // // print RX emulator state // void rx_print_state (HardwareSerial *prt) { uint8_t i; char den[] = { 'S', 'D', 'Q' }; // dump to user supplied port if not null if (prt == NULL) prt = debug; // else try debug port, else return if (prt == NULL) return; prt->printf("\n--- RX Emulator State Dump ---\n\n"); prt->printf(" i/f width = %d.\n", rx_get_bits()); prt->printf(" i/f mode = %s\n", rx_tst_dma() ? "DMA" : "PIO"); prt->printf(" i/f init = %o\n", rx_tst_init()); prt->printf(" i/f run = %o\n", rx_tst_run()); prt->printf("\n"); prt->printf(" rx.cs = %06o\n", rx.cs); prt->printf(" rx.es = %04o\n", rx.es); prt->printf(" rx.wc = %03o\n", rx.wc); prt->printf(" rx.bc = %03o\n", rx.bc); prt->printf(" rx.ta = %03o\n", rx.ta); prt->printf(" rx.sa = %03o\n", rx.sa); prt->printf(" rx.pos = %lu.\n", rx.pos); prt->printf(" rx.len = %u.\n", rx.len); prt->printf(" rx.den = %cD\n", den[rx.den]); prt->printf(" rx.unit = %o\n", rx.unit); prt->printf(" rx.type = RX0%d\n", rx.type+1); prt->printf(" rx.ecode = %04o\n", rx.ecode); prt->printf(" rx.fcn.code = %o\n", rx.fcn.code); prt->printf(" rx.fcn.name = %s\n", rx.fcn.name); for (i = 0; i < RX_NUNITS; ++i) { prt->printf("\n"); prt->printf(" rx.drv[%d].name = '%s'\n", i, rx.drv[i].name); prt->printf(" rx.drv[%d].rdy = %c\n", i, rx.drv[i].rdy ? 'Y' : 'N'); prt->printf(" rx.drv[%d].den = %cD\n", i, den[rx.drv[i].den]); prt->printf(" rx.drv[%d].ta = %03o\n", i, rx.drv[i].ta); prt->printf(" rx.drv[%d].sa = %03o\n", i, rx.drv[i].sa); prt->printf(" rx.drv[%d].pos = %lu.\n", i, rx.drv[i].pos); prt->printf(" rx.drv[%d].len = %u.\n", i, rx.drv[i].len); } return; } // // execute an RX function // void rx_function (void) { uint16_t i, j; uint16_t value; // setup for a new request rx_clr_request(); rx_set_done(); // setup INIT seen callback if (setjmp(rx_init_env)) { // return to here on longjmp(rx_init_env,N); rx_init_seen = 0; rx_initialize(false); return; } // wait for RUN or INIT uint8_t stat = rx_wait_run_or_init(250); // return if timeout with no RUN or INIT if (stat == RX_SAW_NONE) return; // fall thru if RUN seen ... // RUN seen, process command // receive a command word: 12b on RX211 or RX8E/28 in 12b mode; 8b otherwise rx.cs = rx_recv(rx_tst_dma() || rx_tst_12b() ? 12 : 8); if (debug) debug->printf("RX: cmd=%04o\n", rx.cs); // led status led_state(red, off); led_state(green, on); led_state(yellow, off); // separate out the function field rx.fcn.code = (rx.cs & RXCS_FUNCTION)>>1; rx.fcn.name = fcn_name_list[rx.fcn.code]; // separate out the unit number in the command rx.unit = (rx.cs & RXCS_UNITSEL) ? 1 : 0; // separate out the density flag in the command rx.den = (rx.cs & RXCS_DENSEL) ? RX_DEN_DD : RX_DEN_SD; // setup error/status bits rx.es = rx_init_es(); // initial error code: success! rx.ecode = RXERR_SUCCESS; // print command if (debug) debug->printf("RX: %s unit=%o den=%o\n", rx.fcn.name, rx.unit, rx.den); // decode command function switch (rx.fcn.code) { // === buffer fill or empty === // // DMA: command(12), wordcount(8), N*databytes(8) // PIO: command(12), N*databytes(8 or 12) // case RXFCN_FILL: case RXFCN_EMPTY: // compute word count if (rx_tst_dma()) { // if DMA mode, receive word count from interface rx.wc = rx_recv_hs(8); // receive bus address // rx.ba = rx_recv_hs(16); // done in RX211 hardware } else { // if PIO mode, compute word count from full sector size rx.wc = rx_sec_size(rx.den) >> (rx_tst_8b() ? 1 : 2); } if (debug) debug->printf("RX: %s wc=%03o\n", rx.fcn.name, rx.wc); // transform word count to byte count for transfer rx.bc = 2*rx.wc; // check for word count overflow if (rx.bc > rx_sec_size(rx.drv[rx.unit].den)) { if (debug) debug->printf("RX: %s wc=%03o WCOVF\n", rx.fcn.name, rx.wc); rx.ecode = RXERR_WCOVF; rx.es |= RXES_WCOVF; goto error; } // transfer data words in/out if (rx.fcn.code == RXFCN_EMPTY) { // empty buffer (RX controller buffer to host interface) for (i = 0; i < rx.bc; ++i) { if (rx_tst_8b()) { // extract 8b values from buffer to send as 8b value = rx.buffer[i]; rx_xmit_hs(value, 8); if (debug) debug->printf("RX: %s d[%03o]=%03o\n", rx.fcn.name, i, value); } else { // 12b values are packed into the first 2/3 of the sector; read 2 8b entries and build 12b value j = 3*i/2; if (i & 1) { // odd byte value = ((rx.buffer[j+0] & 017) << 8) | rx.buffer[j+1]; } else { // even byte value = (rx.buffer[j+0] << 4) | ((rx.buffer[j+1] >> 4) & 017); } rx_xmit_hs(value, 12); if (debug) debug->printf("RX: %s d[%03o]=%04o\n", rx.fcn.name, i, value); } } } else { // fill buffer (host interface to RX controller buffer) for (i = 0; i < rx.bc; ++i) { if (rx_tst_8b()) { // 8b values get stuffed into 8b buffer directly value = rx_recv_hs(8); rx.buffer[i] = value; if (debug) debug->printf("RX: %s d[%03o]=%03o\n", rx.fcn.name, i, value); } else { // 12b values get split into 4b/8b and merged into two 8b entries value = rx_recv_hs(12); j = 3*i/2; if (i & 1) { // odd byte rx.buffer[j+0] |= (value >> 8) & 017; rx.buffer[j+1] = value; } else { // even byte rx.buffer[j+0] = value >> 4; rx.buffer[j+1] = (value & 017) << 4; } if (debug) debug->printf("RX: %s d[%03o]=%04o\n", rx.fcn.name, i, value); } } // zero fill any unwritten bytes in the buffer for (i = rx.bc, j = rx_sec_size(rx.den); i < j; ++i) rx.buffer[i] = 0; } // indicate transfer is complete rx.wc = 0; break; // === read/write sector === // // command(12), sector(8 or 12), track(8 or 12) // case RXFCN_RDSECT: case RXFCN_WRSECT: case RXFCN_WRDDSECT: // first word is sector address rx.sa = rx_recv_hs() & 037; if (debug) debug->printf("RX: %s sa=%d.\n", rx.fcn.name, rx.sa); // second word is track address rx.ta = rx_recv_hs() & 0177; if (debug) debug->printf("RX: %s ta=%d.\n", rx.fcn.name, rx.ta); // check drive is ready if (!rx.drv[rx.unit].rdy) { // nope, clear drive ready bit and error rx.es &= ~RXES_DRVRDY; goto error; } // check track access is valid if (rx.ta >= RX_NTRKS) { rx.ecode = RXERR_TRKERR; goto error; } // check density matches, error if does not if (rx.type != RX_TYPE_RX01 && rx.den != rx.drv[rx.unit].den) { rx.es |= RXES_DENERR; rx.ecode = RXERR_DENERR; goto error; } // check sector address is valid if (rx.sa < 1 || rx.sa > RX_NSECS) { rx.ecode = RXERR_SECFAIL; goto error; } // compute length of transfer (sector size) rx.len = rx_sec_size(rx.drv[rx.unit].den); // compute byte offset into disk image rx.pos = (rx.ta*RX_NSECS + (rx.sa-1)) * rx.len; // print details if in debug mode if (debug) debug->printf("RX: %s pos=%lu. len=%u.\n", rx.fcn.name, rx.pos, rx.len); // bail on INIT seen if (rx_init_seen) { longjmp(rx_init_env, 20); } // indicate SD card access in progress led_state(yellow, on); // do the read or write if (rx.fcn.code == RXFCN_RDSECT) { // do a read; copy data into buffer from diskimage@offset value = sd_read_bytes(rx.drv[rx.unit].name, rx.pos, rx.buffer, rx.len); } else { // do a write if (rx.fcn.code == RXFCN_WRDDSECT) { bitWrite(rx.drv[rx.unit].dd[rx.ta], rx.sa, RX_DELETED_DATA); } else { bitWrite(rx.drv[rx.unit].dd[rx.ta], rx.sa, RX_NORMAL_DATA); } // copy data from rx.buffer[] to diskimage@offset value = sd_write_bytes(rx.drv[rx.unit].name, rx.pos, rx.buffer, rx.len); } if (bitRead(rx.drv[rx.unit].dd[rx.ta], rx.sa) == RX_DELETED_DATA) rx.es |= RXES_DELDATA; // SD card access complete led_state(yellow, off); // simulate timing rx_timing(rx_seek_time(abs(rx.ta - rx.drv[rx.unit].ta))); // update state info for drive rx.drv[rx.unit].ta = rx.ta; rx.drv[rx.unit].sa = rx.sa; rx.drv[rx.unit].pos = rx.pos; rx.drv[rx.unit].len = rx.len; // check for read/write error if (value != rx.len) { rx.ecode = RXERR_IDMFAIL; goto error; } break; // === set media density === // // command(12), key(8 or 12) // case RXFCN_SETMEDIA: // this function is a NOP for the RX01 if (rx.type == RX_TYPE_RX01) break; // first word must be the magic key 'I' value = rx_recv_hs(); // check key for expected value if (value != 'I') { rx.ecode = RXERR_KEYERR; goto error; } // check drive is ready if (!rx.drv[rx.unit].rdy) { // nope, clear drive ready bit and error rx.es &= ~RXES_DRVRDY; goto error; } // simulate timing rx_timing(RX_SETMEDIA_TIME); // bail on INIT seen if (rx_init_seen) { longjmp(rx_init_env, 21); } // indicate SD card access in progress led_state(yellow, on); // change density to requested value sd_set_file_size(rx.drv[rx.unit].name, rx_dsk_size(rx.den)); rx.drv[rx.unit].den = rx.den; // indicate SD card access complete led_state(yellow, off); break; // === read status === // // command(12) // case RXFCN_RDSTAT: // check drive is ready if (!rx.drv[rx.unit].rdy) { // nope, clear drive ready bit and error rx.es &= ~RXES_DRVRDY; goto error; } // simulate timing rx_timing(RX_RDSTAT_TIME); // check that density matches, error if does not if (rx.type != RX_TYPE_RX01 && rx.den != rx.drv[rx.unit].den) { rx.es |= RXES_DENERR; rx.ecode = RXERR_DENERR; goto error; } break; // === read error code === // // command(12) // case RXFCN_RDERROR: // simulate timing rx_timing(RX_RDERROR_TIME); // only executes on RX211/RXV21; return four 16b status words if (rx_tst_dma()) { // receive bus address // rx.ba = rx_recv_hs(16); // done in RX211 hardware // generate status byte value = (rx.unit == 1 ? (1<<7) : 0) | (rx.drv[1].den == RX_DEN_DD ? (1<<6) : 0) | (rx.drv[rx.unit].rdy ? (1<<5) : 0) | (rx.drv[0].den == RX_DEN_DD ? (1<<4) : 0) | (rx.den == RX_DEN_DD ? (1<<0) : 0); // word 1 rx_xmit_hs(rx.ecode, 8); rx_xmit_hs(rx.wc, 8); // word 2 rx_xmit_hs(rx.drv[0].ta, 8); rx_xmit_hs(rx.drv[1].ta, 8); // word 3 rx_xmit_hs(rx.ta, 8); rx_xmit_hs(rx.sa, 8); // word 4 rx_xmit_hs(value, 8); rx_xmit_hs(rx.drv[rx.unit].ta, 8); } break; } // switch (rx.fcn.num) done: rx_xmit_es(rx.es); led_state(green, off); return; error: rx_set_error(); led_state(red, on); goto done; } // the end
.R ZRXF??
ZRXFB0.BIC
DRSSM-G2
CZRXFB0-0-0
RX02 FUNCTION-LOGIC TEST
UNIT IS RX02
RSTRT ADR 145702
DR>STA
CHANGE HW (L) ? Y
# UNITS (D) ? 2
UNIT 0
RX BUS ADR (O) 177170 ?
VECTOR ADR (O) 264 ?
DRIVE # (O) 0 ?
EXP WRD-CR (O) 0 ?
BR-LEVEL (O) 5 ?
UNIT 1
RX BUS ADR (O) 177170 ?
VECTOR ADR (O) 264 ?
DRIVE # (O) 0 ? 1
EXP WRD-CR (O) 0 ?
BR-LEVEL (O) 5 ?
CHANGE SW (L) ? N
IS FLOPPY SYSTEM CONTAINING UNIT #00
POWERED DOWN (L) N ? N
CZRXFB0 SYS FTL ERR 00050 ON UNIT 00 TST 017 SUB 000 PC: 003476
CONTROLLER-INTERFACE - LGC TST
NO PROG "INIT DONE" ERROR
POSSIBLE FAILING "FRU'S":
CONTROLLER - M7744
INTERFACE - M8256
UNIT#0 RXCSR=004044 RXESR=000244 CMD=000017 ->READ ERROR CODE
ERR CODE=000 ->
WORD CNT=000
CUR TRK DV0= 1. CUR TRK DV1= 0.
TARGET TRK = 1. TARGET SEC = 1. SOFT STAT=160 BAD TRK= 1.
CZRXFB0 SYS FTL ERR 00050 ON UNIT 00 TST 017 SUB 000 PC: 003476
CONTROLLER-INTERFACE - LGC TST
NO PROG "INIT DONE" ERROR
POSSIBLE FAILING "FRU'S":
CONTROLLER - M7744
INTERFACE - M8256
UNIT#0 RXCSR=004044 RXESR=000244 CMD=000001 ->FILL BUFFER
CZRXFB0 SYS FTL ERR 00050 ON UNIT 00 TST 017 SUB 000 PC: 003476
CONTROLLER-INTERFACE - LGC TST
NO PROG "INIT DONE" ERROR
POSSIBLE FAILING "FRU'S":
CONTROLLER - M7744
INTERFACE - M8256
UNIT#0 RXCSR=004044 RXESR=000244 CMD=000003 ->EMPTY BUFFER
CZRXFB0 SYS FTL ERR 00050 ON UNIT 00 TST 017 SUB 000 PC: 003476
CONTROLLER-INTERFACE - LGC TST
NO PROG "INIT DONE" ERROR
POSSIBLE FAILING "FRU'S":
INTERFACE - M8256
CONTROLLER - M7744
INTERFACE - M8256
UNIT#0 RXCSR=134044 RXESR=130264 CMD=000013 ->READ MAINT. STATUS
CZRXFB0 SYS FTL ERR 00050 ON UNIT 00 TST 018 SUB 000 PC: 003476
NPR - LGC TST
NO PROG "INIT DONE" ERROR
POSSIBLE FAILING "FRU'S":
INTERFACE - M8256
CONTROLLER - M7744
INTERFACE->CONTROLLER CABLE
UNIT#0 RXCSR=004444 RXESR=000244 CMD=000401 ->FILL BUFFER
CZRXFB0 SYS FTL ERR 00050 ON UNIT 00 TST 018 SUB 000 PC: 003476
NPR - LGC TST
NO PROG "INIT DONE" ERROR
POSSIBLE FAILING "FRU'S":
INTERFACE - M8256
CONTROLLER - M7744
INTERFACE->CONTROLLER CABLE
UNIT#0 RXCSR=004444 RXESR=000244 CMD=000403 ->EMPTY BUFFER
CZRXFB0 SYS FTL ERR 00050 ON UNIT 00 TST 025 SUB 000 PC: 003476
CONTROLLER-READ*WRITE ELECT - LGC TST
NO PROG "INIT DONE" ERROR
POSSIBLE FAILING "FRU'S":
INTERFACE - M8256
CONTROLLER - M7744
INTERFACE - M8256
UNIT#0 RXCSR=004044 RXESR=000244 CMD=000017 ->READ ERROR CODE
ERR CODE=000 ->
WORD CNT=000
CUR TRK DV0= 1. CUR TRK DV1= 0.
TARGET TRK = 1. TARGET SEC = 1. SOFT STAT=160 BAD TRK= 1.
Don,
I am thinking that when I get some free time, I will want to study your code enough to patch it to work on Bela's hardware. Otherwise I may just put the Due and his hardware interface to the side and move forward with a Mega and your hardware. Unfortunately my day job and chores have been killing my supply of free time! Please keep posting your progress though!
Lou
1 ******* USER DATA ON DY1 WILL BE DESTROYED !
1 ******* PROCEED?(Y/N/CR=N)Y
1 RXDB (R) MOV 2(R0),R1 (177172/000644) (123456/000644) (144426)
1 RXCS (R) BIT (SP),(R0) (060440/100040) (177170/004472) (144056) DD U1 RX02 DONE
1
1 RXCS (W) MOV (SP),(R0) (060444/000421) (177170/004620) (144132) FilBuf DD U1
1 RXCS (R) BIT (SP),(R0) (060444/100200) (177170/004620) (144056) DD U1 RX02 TR
1 RXDB (W) MOV 144236,2(R0) (144236/000200) (177172/000200) (144212) WC=0200
1 RXCS (R) BIT (SP),(R0) (060444/100200) (177170/004620) (144056) DD U1 RX02 TR
1 RXDB (W) MOV 143572,2(R0) (143572/154266) (177172/154266) (144226) BA=0154266
43 RXCS (R) BIT (SP),(R0) (060442/100040) (177170/004420) (144056) DD U1 RX02
2 RXCS (R) BIT (SP),(R0) (060442/100040) (177170/004460) (144056) DD U1 RX02 DONE
Real hardware hangs here; the above FILBUF command is received and executed correctly,
but after XXDP issues the WrSect command the RX02 emulator does not see it, and does not
respond. So the PDP-11 hangs in a loop.
1 RXCS (W) MOV (SP),(R0) (060446/000425) (177170/004624) (144132) WrSect DD U1
1 RXCS (R) BIT (SP),(R0) (060444/100200) (177170/004624) (144056) DD U1 RX02 TR
1 RXDB (W) MOV R3,2(R0) (123456/000005) (177172/000005) (144150) SA=05
1 RXCS (R) BIT (SP),(R0) (060444/100200) (177170/004624) (144056) DD U1 RX02 TR
1 RXDB (W) MOV R2,2(R0) (123456/000001) (177172/000001) (144160) TA=01
2 RXCS (R) BIT (SP),(R0) (060446/100040) (177170/004424) (144056) DD U1 RX02
1 RXCS (R) BIT (SP),(R0) (060446/100040) (177170/004464) (144056) DD U1 RX02 DONE
1 RXCS (R) BIT (SP),(R0) (060440/100040) (177170/004464) (144056) DD U1 RX02 DONE
144040: WDONE: MOV #100040,-(SP) ; ERROR or DONE
144044: BR WAIT
144046: WTR: MOV #100200,-(SP) ; ERROR or TR
144052: WAIT: TST R1
144054: BNE 15$
[B]144056: 10$: BIT (SP),(R0) ;************************ 11/44 processor is hanging in this tight loop
144060: BEQ 10$ ;************************ with (SP) = #100200 (ie, ERROR or TR expected)
[/B]144062: BPL 15$
144064: INC R1
144066: 15$: TST (SP)+
144070: TST R1
144072: RTS PC
// RX211/RXV21 or RX28 attached
rx_clr_request();
rx_set_out();
rx_set_done();
rx_xmit(data, 12);
rx_set_request();
[B]rx_clr_request();[/B] [B]<=== ADD THIS LINE[/B]
return;
//
// rx02_driver - Simple :-) RX02 driver
//
// (C) 2013 Don North <ak6dn_at_mindspring_dot_com>
//
// 21 Oct 2013 - donorth - Initial code
// 27 Mar 2016 - donorth - Ported to Arduino from CCS/Microchip
// 23 Sep 2016 - donorth - Updated rx_xmit_es() routine to fix XXDP hang.
//
//
// includes
//
#include "my_project.h"
#include "led_driver.h"
#include "rx02_driver.h"
#include "sdcard_driver.h"
//
// definitions
//
// options
#define USE_BIT_BUILTINS 0 // use bit builtins BitRead() etc vs use logical bit shift/mask operations
// RXCS control/status bit definitions
//efine RXCS_GO (1<<0) // GO bit (reference only; not used in drive)
#define RXCS_FUNCTION (7<<1) // function bitfield
#define RXCS_UNITSEL (1<<4) // unit select
//efine RXCS_DONE (1<<5) // function complete (reference only; not used in drive)
//efine RXCS_INTENB (1<<6) // interrupt enable (reference only; not used in drive)
//efine RXCS_8BIT (1<<6) // 8b mode (RX8E/RX28 ONLY)
//efine RXCS_TR (1<<7) // transfer request (reference only; not used in drive)
//efine RXCS_MAINT (1<<7) // maintenance mode (RX8E/RX28 ONLY)
#define RXCS_DENSEL (1<<8) // density select (RX211/RX28 w/RX02 ONLY)
//efine RXCS_HEADSEL (1<<9) // head select (RX211/RX28 w/RX03 ONLY)
//efine RXCS_NU10 (1<<10) // not used
//efine RXCS_RX02 (1<<11) // RX02 interface (reference only; not used in drive)
//efine RXCS_EXTADDR (3<<12) // upper bits of phys address (reference only; not used in drive)
//efine RXCS_INIT (1<<14) // initialize RX (reference only; not used in drive)
//efine RXCS_ERROR (1<<15) // error flag (reference only; not used in drive)
#define RXFCN_FILL (0) // fill buffer
#define RXFCN_EMPTY (1) // empty buffer
#define RXFCN_WRSECT (2) // write sector
#define RXFCN_RDSECT (3) // read sector
#define RXFCN_SETMEDIA (4) // set media density
#define RXFCN_RDSTAT (5) // read status
#define RXFCN_WRDDSECT (6) // write deleted data sector
#define RXFCN_RDERROR (7) // read error code
// RXES status bit definitions
//efine RXES_CRC (1<<0) // CRC aka READ error
//efine RXES_SIDRDY (1<<1) // side ready (RX211/RX28 ONLY)
//efine RXES_PERR (1<<1) // parity error (RX11/RX8E w/RX01 ONLY)
#define RXES_INIT (1<<2) // controller init done
#define RXES_RX02 (1<<3) // set for RX02 drive (RX28 w/ RX02 ONLY)
//efine RXES_ACLO (1<<3) // set for AC low (RX211 w/RX02 ONLY)
//efine RXES_WPERR (1<<3) // set for write to WP drive (RX11/RX8E w/RX01 ONLY)
#define RXES_DENERR (1<<4) // density error
#define RXES_DRVDEN (1<<5) // diskette double density
#define RXES_DELDATA (1<<6) // deleted data detected
#define RXES_DRVRDY (1<<7) // selected drive ready
#define RXES_UNITSEL (1<<8) // unit selected (RX211 w/RX02 ONLY)
//efine RXES_HEADSEL (1<<9) // head selected (RX211 w/RX03 ONLY
#define RXES_WCOVF (1<<10) // word count overflow (RX211 w/RX02 ONLY)
//efine RXES_NXM (1<<11) // nonexistent memory (RX211 w/RX02 reference only; not used in drive)
// RX error codes
#define RXERR_SUCCESS 0000 // success, no error
//efine RXERR_DR0INIT 0010 // drive 0 failed to init
//efine RXERR_DR1INIT 0020 // drive 1 failed to init
//efine RXERR_STEPHOME 0030 // found home when stepping for init (RX01 ONLY)
#define RXERR_TRKERR 0040 // access to track > 76
#define RXERR_TRKFAIL 0050 // track not found
//efine RXERR_SELFDIAG 0060 // self diagnostic fail (RX01 ONLY)
#define RXERR_SECFAIL 0070 // sector not found
//efine RXERR_WRITEWP 0100 // write to a WP drive (RX01 ONLY)
//efine RXERR_SEPFAIL 0110 // no SEP clock in 40us
//efine RXERR_PREFAIL 0120 // preamble not found
#define RXERR_IDMFAIL 0130 // ID mark not found
//efine RXERR_CRCHEAD 0140 // CRC error on a header (RX01 ONLY)
//efine RXERR_TRKCMP 0150 // header track miscompare
//efine RXERR_IDMTRYS 0160 // too many tries for IDAM
//efine RXERR_DAMFAIL 0170 // data AM not found
//efine RXERR_CRCERR 0200 // CRC error on read
//efine RXERR_PERR 0210 // parity error on word from i/f to controller (RX01 ONLY)
//efine RXERR_RWFAIL 0220 // r/w failed maint test (RX02 ONLY)
#define RXERR_WCOVF 0230 // word count overflow (RX02 ONLY)
#define RXERR_DENERR 0240 // density error (RX02 ONLY)
#define RXERR_KEYERR 0250 // wrong key word for set density (RX02 ONLY)
// macros
// rx interface outputs set/clr
#if USE_BIT_BUILTINS
#define rx_set_done(xxx) bitSet(PORTA,6) /* digitalWrite(PIN_CTLR_DONE_H,HIGH) */
#define rx_clr_done(xxx) bitClear(PORTA,6) /* digitalWrite(PIN_CTLR_DONE_H,LOW) */
#define rx_set_error(xxx) bitSet(PORTA,7) /* digitalWrite(PIN_CTLR_ERROR_H,HIGH) */
#define rx_clr_error(xxx) bitClear(PORTA,7) /* digitalWrite(PIN_CTLR_ERROR_H,LOW) */
#define rx_set_aclo(xxx) bitSet(PORTC,1) /* digitalWrite(PIN_CTLR_ACLO_H,HIGH) */
#define rx_clr_aclo(xxx) bitClear(PORTC,1) /* digitalWrite(PIN_CTLR_ACLO_H,LOW) */
#define rx_set_shift(xxx) bitSet(PORTC,2) /* digitalWrite(PIN_CTLR_SHIFT_H,HIGH) */
#define rx_clr_shift(xxx) bitClear(PORTC,2) /* digitalWrite(PIN_CTLR_SHIFT_H,LOW) */
#define rx_set_out(xxx) bitSet(PORTC,3) /* digitalWrite(PIN_CTLR_OUT_H,HIGH) */
#define rx_clr_out(xxx) bitClear(PORTC,3) /* digitalWrite(PIN_CTLR_OUT_H,LOW) */
#define rx_set_datao(xxx) bitSet(PORTC,5) /* digitalWrite(PIN_CTLR_DATAO_H,HIGH) */
#define rx_clr_datao(xxx) bitClear(PORTC,5) /* digitalWrite(PIN_CTLR_DATAO_H,LOW) */
#define rx_set_request(xxx) bitSet(PORTC,6) /* digitalWrite(PIN_CTLR_TR_RQST_H,HIGH) */
#define rx_clr_request(xxx) bitClear(PORTC,6) /* digitalWrite(PIN_CTLR_TR_RQST_H,LOW) */
// rx interface inputs test
#define rx_tst_pio(xxx) bitRead(PINC,0) /* digitalRead(PIN_CTLR_DMA_MODE_L) */
#define rx_tst_dma(xxx) (rx_tst_pio()?0:1) /* !digitalRead(PIN_CTLR_DMA_MODE_L) */
#define rx_tst_datai(xxx) bitRead(PINC,4) /* digitalRead(PIN_CTLR_DATAI_H) */
#define rx_tst_12b(xxx) bitRead(PINC,7) /* digitalRead(PIN_CTLR_12BIT_H) */
#define rx_tst_8b(xxx) (rx_tst_12b()?0:1) /* !digitalRead(PIN_CTLR_12BIT_H) */
#define rx_tst_run(xxx) bitRead(PINE,4) /* digitalRead(PIN_CTLR_RUN_H)) */
#define rx_tst_init(xxx) bitRead(PINE,5) /* digitalRead(PIN_CTLR_INIT_H) */
#else
#define rx_set_done(xxx) PORTA |= (1<<6) /* digitalWrite(PIN_CTLR_DONE_H,HIGH) */
#define rx_clr_done(xxx) PORTA &= ~(1<<6) /* digitalWrite(PIN_CTLR_DONE_H,LOW) */
#define rx_set_error(xxx) PORTA |= (1<<7) /* digitalWrite(PIN_CTLR_ERROR_H,HIGH) */
#define rx_clr_error(xxx) PORTA &= ~(1<<7) /* digitalWrite(PIN_CTLR_ERROR_H,LOW) */
#define rx_set_aclo(xxx) PORTC |= (1<<1) /* digitalWrite(PIN_CTLR_ACLO_H,HIGH) */
#define rx_clr_aclo(xxx) PORTC &= ~(1<<1) /* digitalWrite(PIN_CTLR_ACLO_H,LOW) */
#define rx_set_shift(xxx) PORTC |= (1<<2) /* digitalWrite(PIN_CTLR_SHIFT_H,HIGH) */
#define rx_clr_shift(xxx) PORTC &= ~(1<<2) /* digitalWrite(PIN_CTLR_SHIFT_H,LOW) */
#define rx_set_out(xxx) PORTC |= (1<<3) /* digitalWrite(PIN_CTLR_OUT_H,HIGH) */
#define rx_clr_out(xxx) PORTC &= ~(1<<3) /* digitalWrite(PIN_CTLR_OUT_H,LOW) */
#define rx_set_datao(xxx) PORTC |= (1<<5) /* digitalWrite(PIN_CTLR_DATAO_H,HIGH) */
#define rx_clr_datao(xxx) PORTC &= ~(1<<5) /* digitalWrite(PIN_CTLR_DATAO_H,LOW) */
#define rx_set_request(xxx) PORTC |= (1<<6) /* digitalWrite(PIN_CTLR_TR_RQST_H,HIGH) */
#define rx_clr_request(xxx) PORTC &= ~(1<<6) /* digitalWrite(PIN_CTLR_TR_RQST_H,LOW) */
// rx interface inputs test
#define rx_tst_pio(xxx) ((PINC & (1<<0)) ? 1 : 0) /* (digitalRead(PIN_CTLR_DMA_MODE_L)==HIGH) */
#define rx_tst_dma(xxx) ((PINC & (1<<0)) ? 0 : 1) /* (digitalRead(PIN_CTLR_DMA_MODE_L)==LOW) */
#define rx_tst_datai(xxx) ((PINC & (1<<4)) ? 1 : 0) /* digitalRead(PIN_CTLR_DATAI_H) */
#define rx_tst_12b(xxx) ((PINC & (1<<7)) ? 1 : 0) /* (digitalRead(PIN_CTLR_12BIT_H)==HIGH) */
#define rx_tst_8b(xxx) ((PINC & (1<<7)) ? 0 : 1) /* (digitalRead(PIN_CTLR_12BIT_H)==LOW) */
#define rx_tst_run(xxx) ((PINE & (1<<4)) ? 1 : 0) /* (digitalRead(PIN_CTLR_RUN_H)==HIGH) */
#define rx_tst_init(xxx) ((PINE & (1<<5)) ? 1 : 0) /* (digitalRead(PIN_CTLR_INIT_H)==HIGH) */
#endif
// INIT/RUN status
#define RX_SAW_NONE 0 // saw neither RUN or INIT
#define RX_SAW_RUN 1 // saw RUN and not INIT
// emulation type
#define RX_TYPE_NONE 0 // no type defined
#define RX_TYPE_RX01 1 // RX02 in RX01 mode
#define RX_TYPE_RX02 2 // RX02 in native mode
#define RX_TYPE_RX03 3 // RX02 in dual-sided RX03 mode (***UNSUPPORTED***)
// drive density
#define RX_DEN_SD 0 // single density mode (128B/sector)
#define RX_DEN_DD 1 // double density mode (256B/sector)
#define RX_DEN_QD 2 // quad density mode (512B/sector) (***UNSUPPORTED***)
// data type
#define RX_DATA_NORMAL 0 // normal data
#define RX_DATA_DELETED 1 // deleted data
// timing characteristics
#define RX_TIME_INIT 0 // initialize floppy drive
#define RX_TIME_SEEK 1 // read/write seek time, position dependent
#define RX_TIME_RDSTAT 2 // read status function
#define RX_TIME_RDERROR 3 // read error registers
#define RX_TIME_SETMEDIA 4 // set media density
// disk definitions
#define RX_NTRKS 77L // number of tracks per disk
#define RX_NSECS 26L // number of sectors per track
#define RX_NBPS 128L // bytes per sector, single density
#define RX_NUNITS 2 // number if units
// convenience macros
#define rx_sec_size(den) (RX_NBPS<<(den)) // bytes per sector at density
#define rx_trk_size(den) (RX_NSECS*rx_sec_size(den)) // bytes per track at density
#define rx_dsk_size(den) (RX_NTRKS*rx_trk_size(den)) // bytes per disk at density
#define rx_get_bits(xxx) (rx_tst_12b() ? 12 : 8) // number of bits signaled by interface
// buffer sizes
#define RX_BUFFER_SIZE (rx_sec_size(RX_DEN_DD)) // maximum size sector buffer for double density
// PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE
volatile static uint8_t rx_run_seen; // set when RX RUN asserted (rising edge)
volatile static uint8_t rx_init_seen; // set when RX INIT asserted (rising edge)
static jmp_buf rx_init_env; // environment to invoke when INIT is asserted (ie, reset/error condition)
static char fcn_name_list[][8] = { "FILBUF", "EMPBUF", "WRSECT", "RDSECT", "SETMED", "RDSTAT", "WRDDSE", "RDERRC" };
static uint8_t rx_type = RX_TYPE_RX02; // default drive emulation, can be overwritten
struct drv_t {
uint32_t dd[RX_NTRKS]; // normal or deleted data per track per sector (1 bit per sector 0..31)
char name[32]; // associated file name
uint8_t rdy; // drive ready
uint8_t den; // density
uint8_t ta; // track address
uint8_t sa; // sector address
uint16_t len; // read/write length
uint32_t pos; // read/write position
};
static struct rx_t {
uint32_t cmdcnt; // count of commands
uint32_t errcnt; // count of errors
uint16_t cs; // control/status image
uint16_t es; // error and status
uint16_t tc; // transfer count
uint8_t wc; // word count
uint8_t ec; // error code
uint8_t unit; // active unit number
uint8_t den; // density selected, DD or SD
uint8_t ta; // track address
uint8_t sa; // sector address
uint8_t type; // emulation type: RX01, RX02
uint8_t timing; // timing mode: 0=fastest, 2=normal/real
uint16_t len; // read/write length
uint32_t pos; // read/write position
struct {
uint8_t code; // function selected (numeric 0..7)
char * name; // function selected, ascii name id
} fcn;
struct drv_t drv[RX_NUNITS]; // drive specific parameters
uint8_t buffer[RX_BUFFER_SIZE]; // sector buffer
} rx;
static HardwareSerial *debugPort = NULL; // debug serial output
static uint8_t debugLevel = 0; // debug level, 0=NONE, higher is more
//
// RX02 INIT assertion interrupt
//
static void rx_intr_init (void)
{
rx_init_seen = 1;
return;
}
//
// RX02 RUN assertion interrupt
//
static void rx_intr_run (void)
{
rx_run_seen = 1;
return;
}
//
// RX time delay routine
//
static void rx_timing (uint8_t type)
{
uint16_t delta;
uint16_t ms_delay;
const uint16_t ms_amount = 5;
// check basic timing mode
if (rx.timing >= 1) {
// otherwise decode timing parameter
switch (type) {
// initialize floppy drive
case RX_TIME_INIT:
ms_delay = 500;
break;
// read status function
case RX_TIME_RDSTAT:
ms_delay = 250;
break;
// read error registers
case RX_TIME_RDERROR:
ms_delay = 10;
break;
// set media density
case RX_TIME_SETMEDIA:
ms_delay = 10000;
break;
// read/write seek time, position dependent
case RX_TIME_SEEK:
// compute track move delta
delta = abs(rx.ta - rx.drv[rx.unit].ta);
// compute seek time based on delta track amount
ms_delay = 20 + ((delta==0) ? 0 : (20 + delta*10));
break;
// none
default:
ms_delay = 0;
break;
}
// for medium timing mode, make delays 1/10 of normal
if (rx.timing == 1) ms_delay = ms_delay/10;
// check for delay requested
if (ms_delay > 0) {
// delay in 5ms increments, checking for INIT
while (ms_delay > ms_amount) {
if (rx_init_seen) { longjmp(rx_init_env, 10); }
delay(ms_amount);
ms_delay -= ms_amount;
}
// final small delay less than 5ms
if (rx_init_seen) { longjmp(rx_init_env, 10); }
delay(ms_delay);
}
}
// all done
if (rx_init_seen) { longjmp(rx_init_env, 10); }
return;
}
//
// wait until the RX_RUN or RX_INIT signal is asserted, or timeout occurs
//
static uint8_t rx_wait_run_or_init (uint32_t expire)
{
uint32_t start = millis();
do {
if (rx_init_seen) { rx_init_seen = 0; longjmp(rx_init_env, 1); }
if (rx_run_seen) { rx_run_seen = 0; return RX_SAW_RUN; }
} while (millis()-start <= expire);
// count timed out with neither RUN or INIT
return RX_SAW_NONE;
}
// wait forever until the RX_RUN or RX_INIT signal is asserted
static uint8_t rx_wait_run_or_init (void)
{
do {
if (rx_init_seen) { rx_init_seen = 0; longjmp(rx_init_env, 2); }
if (rx_run_seen) { rx_run_seen = 0; return RX_SAW_RUN; }
} while (TRUE);
}
//
// receive a 12/8 bit word from the RX interface, msb first
//
#define _rx_recv_hs(xxx,yyy) { if (rx_tst_datai()) { delay_1c(); (xxx) |= (1<<(yyy)); } else { (xxx) &= ~(1<<(yyy)); delay_2c(); } delay_1c(); rx_set_shift(); delay_1c(); rx_clr_shift(); }
static uint16_t rx_recv12_hs (uint8_t handshake)
{
uint16_t data = 0;
rx_clr_out();
rx_clr_done();
rx_clr_datao();
rx_clr_error();
rx_clr_shift();
if (handshake) { rx_set_request(); rx_wait_run_or_init(); rx_clr_request(); }
noInterrupts();
_rx_recv_hs(data,11);
_rx_recv_hs(data,10);
_rx_recv_hs(data,9);
_rx_recv_hs(data,8);
_rx_recv_hs(data,7);
_rx_recv_hs(data,6);
_rx_recv_hs(data,5);
_rx_recv_hs(data,4);
_rx_recv_hs(data,3);
_rx_recv_hs(data,2);
_rx_recv_hs(data,1);
_rx_recv_hs(data,0);
interrupts();
if (rx_init_seen) { longjmp(rx_init_env, 3); }
return data;
}
static uint16_t rx_recv8_hs (uint8_t handshake)
{
uint8_t data = 0;
rx_clr_out();
rx_clr_done();
rx_clr_datao();
rx_clr_error();
rx_clr_shift();
if (handshake) { rx_set_request(); rx_wait_run_or_init(); rx_clr_request(); }
noInterrupts();
_rx_recv_hs(data,7);
_rx_recv_hs(data,6);
_rx_recv_hs(data,5);
_rx_recv_hs(data,4);
_rx_recv_hs(data,3);
_rx_recv_hs(data,2);
_rx_recv_hs(data,1);
_rx_recv_hs(data,0);
interrupts();
if (rx_init_seen) { longjmp(rx_init_env, 4); }
return uint16_t(data);
}
static uint16_t rx_recv (uint8_t n)
{
if (n == 12) return rx_recv12_hs(FALSE); else
if (n == 8) return rx_recv8_hs(FALSE); else
if (rx_tst_12b()) return rx_recv12_hs(FALSE); else
return rx_recv8_hs(FALSE);
}
static uint16_t rx_recv (void)
{
if (rx_tst_12b()) return rx_recv12_hs(FALSE); else
return rx_recv8_hs(FALSE);
}
static uint16_t rx_recv_hs (uint8_t n)
{
if (n == 12) return rx_recv12_hs(TRUE); else
if (n == 8) return rx_recv8_hs(TRUE); else
if (rx_tst_12b()) return rx_recv12_hs(TRUE); else
return rx_recv8_hs(TRUE);
}
static uint16_t rx_recv_hs (void)
{
if (rx_tst_12b()) return rx_recv12_hs(TRUE); else
return rx_recv8_hs(TRUE);
}
//
// transmit a 12/8 bit word to the RX interface, msb first
//
#define _rx_xmit_hs(xxx,yyy) { if ((xxx)&(1<<(yyy))) { delay_1c(); rx_set_datao(); } else { rx_clr_datao(); delay_2c(); } delay_1c(); rx_set_shift(); delay_1c(); rx_clr_shift(); }
static void rx_xmit12_hs (uint16_t value, uint8_t handshake)
{
uint16_t data = value;
noInterrupts();
if (handshake) { rx_clr_request(); }
rx_clr_shift();
rx_set_out();
delay_3c();
_rx_xmit_hs(data,11);
_rx_xmit_hs(data,10);
_rx_xmit_hs(data,9);
_rx_xmit_hs(data,8);
_rx_xmit_hs(data,7);
_rx_xmit_hs(data,6);
_rx_xmit_hs(data,5);
_rx_xmit_hs(data,4);
_rx_xmit_hs(data,3);
_rx_xmit_hs(data,2);
_rx_xmit_hs(data,1);
_rx_xmit_hs(data,0);
delay_3c();
rx_clr_datao();
interrupts();
if (handshake) { rx_set_request(); rx_wait_run_or_init(); rx_clr_request(); }
rx_clr_out();
if (rx_init_seen) { longjmp(rx_init_env, 5); }
return;
}
static void rx_xmit8_hs (uint16_t value, uint8_t handshake)
{
uint8_t data = value;
noInterrupts();
if (handshake) { rx_clr_request(); }
rx_clr_shift();
rx_set_out();
delay_3c();
_rx_xmit_hs(data,7);
_rx_xmit_hs(data,6);
_rx_xmit_hs(data,5);
_rx_xmit_hs(data,4);
_rx_xmit_hs(data,3);
_rx_xmit_hs(data,2);
_rx_xmit_hs(data,1);
_rx_xmit_hs(data,0);
delay_3c();
rx_clr_datao();
interrupts();
if (handshake) { rx_set_request(); rx_wait_run_or_init(); rx_clr_request(); }
rx_clr_out();
if (rx_init_seen) { longjmp(rx_init_env, 6); }
return;
}
static void rx_xmit (uint16_t data, uint8_t n)
{
if (n == 12) rx_xmit12_hs(data,FALSE); else
if (n == 8) rx_xmit8_hs(data,FALSE); else
if (rx_tst_12b()) rx_xmit12_hs(data,FALSE); else
rx_xmit8_hs(data,FALSE);
return;
}
static void rx_xmit (uint16_t data)
{
if (rx_tst_12b()) rx_xmit12_hs(data,FALSE); else
rx_xmit8_hs(data,FALSE);
return;
}
static void rx_xmit_hs (uint16_t data, uint8_t n)
{
if (n == 12) rx_xmit12_hs(data,TRUE); else
if (n == 8) rx_xmit8_hs(data,TRUE); else
if (rx_tst_12b()) rx_xmit12_hs(data,TRUE); else
rx_xmit8_hs(data,TRUE);
return;
}
static void rx_xmit_hs (uint16_t data)
{
if (rx_tst_12b()) rx_xmit12_hs(data,TRUE); else
rx_xmit8_hs(data,TRUE);
return;
}
//
// transmit an extended status word
//
static void rx_xmit_es (uint16_t data)
{
if (debugLevel) debugPort->printf(F("RX: %s rx_xmit_es(%04o)\n\n"), rx.fcn.name, data);
if (rx_tst_dma() || rx_tst_12b() && rx.type == RX_TYPE_RX02) {
// RX211/RXV21 or RX28 attached
rx_clr_request();
rx_set_out();
rx_set_done();
rx_xmit(data, 12);
rx_set_request();
rx_clr_request();
} else if (rx_tst_12b()) {
// RX8E attached
rx_xmit(data, 12);
} else {
// RX11/RXV11 attached
rx_xmit(data, 8);
}
return;
}
static void rx_xmit_es (void)
{
rx_xmit_es(rx.es);
return;
}
//
// setup error/status word with flags: RXES_CRC, RXES_DENERR, RXES_DELDATA, RXES_WCOVF are data flag options
//
static void rx_init_es (uint16_t data)
{
// clear all bits
rx.es = 0;
// insert user data
rx.es |= data;
// insert drive ready for that unit
if (rx.drv[rx.unit].rdy) rx.es |= RXES_DRVRDY;
// certain bits for RX02/RX03
if (rx.type == RX_TYPE_RX02 || rx.type == RX_TYPE_RX03) {
// insert drive type (RX28 only)
if (rx_tst_12b()) rx.es |= RXES_RX02;
// insert unit selected (RX211 only)
if (rx.unit == 1 && rx_tst_dma()) rx.es |= RXES_UNITSEL;
// insert density for that unit
if (rx.drv[rx.unit].den == RX_DEN_DD) rx.es |= RXES_DRVDEN;
}
// done
return;
}
// setup error/status, no flags
static void rx_init_es (void)
{
rx_init_es(0);
return;
}
// PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC PUBLIC
//
// initialize the RX subsystem
//
// flag: TRUE for powerup init; FALSE for INIT pulse seen
//
void rx_initialize (uint8_t flag)
{
uint8_t i;
// led status
led_state(red, on);
led_state(green, off);
led_state(yellow, off);
// setup at power up ... required first time only
if (flag) {
// setup pins
pinMode(PIN_CTLR_DMA_MODE_L, INPUT);
pinMode(PIN_CTLR_DATAI_H, INPUT);
pinMode(PIN_CTLR_12BIT_H, INPUT);
pinMode(PIN_CTLR_INIT_H, INPUT); // interrupt capable
pinMode(PIN_CTLR_RUN_H, INPUT); // interrupt capable
//
pinMode(PIN_CTLR_TR_RQST_H, OUTPUT);
pinMode(PIN_CTLR_DATAO_H, OUTPUT);
pinMode(PIN_CTLR_SHIFT_H, OUTPUT);
pinMode(PIN_CTLR_ERROR_H, OUTPUT);
pinMode(PIN_CTLR_ACLO_H, OUTPUT);
pinMode(PIN_CTLR_DONE_H, OUTPUT);
pinMode(PIN_CTLR_OUT_H, OUTPUT);
// setup RX INIT interrupt
rx_init_seen = 0;
attachInterrupt(digitalPinToInterrupt(PIN_CTLR_INIT_H), rx_intr_init, RISING);
// setup RX RUN interrupt
rx_run_seen = 0;
attachInterrupt(digitalPinToInterrupt(PIN_CTLR_RUN_H), rx_intr_run, RISING);
// counters
rx.cmdcnt = 0;
rx.errcnt = 0;
}
// setup control outputs
rx_clr_out();
rx_clr_aclo();
rx_clr_done();
rx_clr_datao();
rx_clr_error();
rx_clr_shift();
rx_clr_request();
// toggle ACLO at initial reset
if (flag) { rx_set_aclo(); delay(100); rx_clr_aclo(); }
// wait for INIT to go away
uint32_t start = millis(); // current time, in ms
uint32_t expire = 10000UL; // 10 sec delay
if (debugLevel) debugPort->printf(F("RX: waiting for INIT to clear ... t=%lums\n"), millis());
while ((rx_init_seen || rx_tst_init()) && millis()-start <= expire) { rx_init_seen = 0; }
if (debugLevel) debugPort->printf(F("RX: INIT has %scleared t=%lums\n"), rx_tst_init() ? "NOT " : "", millis());
// setup drive data structures
rx.type = rx_type;
rx.fcn.code = 8;
rx.fcn.name = "INIT";
rx.den = (rx.type == RX_TYPE_RX02 ? RX_DEN_DD : RX_DEN_SD);
rx.cs = 0;
rx.es = 0;
rx.ec = 0;
rx.ta = 0;
rx.sa = 0;
rx.wc = 0;
rx.tc = 0;
rx.len = 0;
rx.pos = 0;
rx.unit = 0;
rx.timing = 0;
// setup drive state
for (i = 0; i < RX_NUNITS; ++i) {
if (flag) sprintf(rx.drv[i].name, "RX%d.DSK", i);
rx_unit_file(i);
}
// clear entire sector buffer
memset(rx.buffer, 0x00, sizeof(rx.buffer));
// indicate SD card access in progress
led_state(yellow, on);
// copy drive 0 boot sector into the buffer, if drive is ready
rx.drv[0].ta = rx.ta = 1;
rx.drv[0].sa = rx.sa = 1;
rx.drv[0].len = rx.len = rx_sec_size(rx.drv[0].den);
rx.drv[0].pos = rx.pos = (rx.ta*RX_NSECS + (rx.sa - 1)) * rx.len;
if (rx.drv[0].rdy) sd_read_bytes(rx.drv[0].name, rx.pos, rx.buffer, rx.len);
// indicate SD card access complete
led_state(yellow, off);
// init time delay
rx_timing(RX_TIME_INIT);
// setup error/status bits and transmit
rx_init_es(RXES_INIT);
rx_xmit_es();
// led status
led_state(red, off);
return;
}
//
// setup the file for rx drive unit
//
char *rx_unit_file (uint8_t unit, char *name)
{
uint32_t size;
// setup file name if provided
if (name) strcpy(rx.drv[unit].name, name);
// set initial state from common block
rx.drv[unit].ta = rx.ta;
rx.drv[unit].sa = rx.sa;
rx.drv[unit].len = rx.len;
rx.drv[unit].pos = rx.pos;
// zap the deleted data state to normal
memset(rx.drv[unit].dd, RX_DATA_NORMAL, sizeof(rx.drv[unit].dd));
// special file name of "NONE" (any capitalization) maps to an unmounted drive
if (strcasecmp(rx.drv[unit].name, "NONE") == 0) {
// no online disk
rx.drv[unit].rdy = FALSE;
rx.drv[unit].den = RX_DEN_SD;
// return the name
return rx.drv[unit].name;
}
// get size of (existing) file, check it for SD or DD or none; if not SD (RX01,RX02) or DD (RX02) then set it
size = sd_get_file_size(rx.drv[unit].name);
if (rx.type == RX_TYPE_RX01) {
// RX01 only supports SD disks
if (size != rx_dsk_size(RX_DEN_SD))
size = sd_set_file_size(rx.drv[unit].name, rx_dsk_size(RX_DEN_SD));
} else {
// RX02/RX03 supports SD or DD disks, default to DD
if (size != rx_dsk_size(RX_DEN_SD) && size != rx_dsk_size(RX_DEN_DD))
size = sd_set_file_size(rx.drv[unit].name, rx_dsk_size(RX_DEN_DD));
}
// set drive ready if file exists and is a right size (SD or DD)
rx.drv[unit].rdy = (size == rx_dsk_size(RX_DEN_SD)) || (size == rx_dsk_size(RX_DEN_DD));
// set drive density based on file size
rx.drv[unit].den = (size == rx_dsk_size(RX_DEN_DD)) ? RX_DEN_DD : RX_DEN_SD;
// return the name
return rx.drv[unit].name;
}
// setup drive for unit using existing file name
char * rx_unit_file (uint8_t unit)
{
return rx_unit_file(unit, NULL);
}
//
// setup the default emulation type (1=RX01, 2=RX02, etc)
//
uint8_t rx_emulation_type (uint8_t type)
{
rx.type = rx_type = type;
return rx.type;
}
uint8_t rx_emulation_type (void)
{
return rx.type;
}
//
// setup the default timing type (0=fastest, 1=medium, 2=slow/normal)
//
uint8_t rx_timing_type (uint8_t type)
{
rx.timing = type;
return rx.timing;
}
uint8_t rx_timing_type (void)
{
return rx.timing;
}
//
// setup the RX02 subsystem debug port
//
uint8_t rx_debug (HardwareSerial *serialPort, uint8_t level)
{
// set debug serial port (or not)
debugPort = serialPort;
// and the debug level
debugLevel = level;
// and done
return debugLevel;
}
uint8_t rx_debug (void)
{
return debugLevel;
}
//
// print RX emulator state
//
void rx_print_state (HardwareSerial *prt)
{
uint8_t i;
char den[] = { 'S', 'D', 'Q' };
// dump to user supplied port if not null
if (prt == NULL) prt = debugPort;
// else try debug port, else return
if (prt == NULL) return;
prt->printf(F("\n\n--- RX Emulator State Dump ---\n\n"));
prt->printf(F(" i/f width = %d.\n"), rx_get_bits());
prt->printf(F(" i/f mode = %s\n"), rx_tst_dma() ? "DMA" : "PIO");
prt->printf(F(" i/f init = %o\n"), rx_tst_init());
prt->printf(F(" i/f run = %o\n"), rx_tst_run());
prt->printf(F("\n"));
prt->printf(F(" rx.cs = %06o\n"), rx.cs);
prt->printf(F(" rx.es = %04o\n"), rx.es);
prt->printf(F(" rx.ec = %04o\n"), rx.ec);
prt->printf(F(" rx.wc = %03o\n"), rx.wc);
prt->printf(F(" rx.tc = %03o\n"), rx.tc);
prt->printf(F(" rx.ta = %03o\n"), rx.ta);
prt->printf(F(" rx.sa = %03o\n"), rx.sa);
prt->printf(F(" rx.pos = %lu.\n"), rx.pos);
prt->printf(F(" rx.len = %u.\n"), rx.len);
prt->printf(F(" rx.den = %cD\n"), den[rx.den]);
prt->printf(F(" rx.unit = %o\n"), rx.unit);
prt->printf(F(" rx.type = RX0%d\n"), rx.type);
prt->printf(F(" rx.cmdcnt = %u.\n"), rx.cmdcnt);
prt->printf(F(" rx.errcnt = %u.\n"), rx.errcnt);
prt->printf(F(" rx.timing = %o\n"), rx.timing);
prt->printf(F(" rx.fcn.code = %o\n"), rx.fcn.code);
prt->printf(F(" rx.fcn.name = %s\n"), rx.fcn.name);
for (i = 0; i < RX_NUNITS; ++i) {
prt->printf(F("\n"));
prt->printf(F(" rx.drv[%d].name = '%s'\n"), i, rx.drv[i].name);
prt->printf(F(" rx.drv[%d].rdy = %c\n"), i, rx.drv[i].rdy ? 'Y' : 'N');
prt->printf(F(" rx.drv[%d].den = %cD\n"), i, den[rx.drv[i].den]);
prt->printf(F(" rx.drv[%d].ta = %03o\n"), i, rx.drv[i].ta);
prt->printf(F(" rx.drv[%d].sa = %03o\n"), i, rx.drv[i].sa);
prt->printf(F(" rx.drv[%d].pos = %lu.\n"), i, rx.drv[i].pos);
prt->printf(F(" rx.drv[%d].len = %u.\n"), i, rx.drv[i].len);
}
prt->printf(F("\n"));
return;
}
//
// execute an RX function ... the mainline RX emulator routine, executes one command
//
void rx_function (void)
{
uint16_t i, j;
uint16_t value;
// setup for a new request
rx_clr_request();
rx_clr_out();
rx_set_done();
// setup INIT seen callback
if (setjmp(rx_init_env)) {
// return to here on longjmp(rx_init_env,N);
rx_init_seen = 0;
rx_initialize(false);
return;
}
// wait for RUN or INIT; 25ms timeout if no RUN or INIT
if (rx_wait_run_or_init(25) == RX_SAW_NONE) return;
// RUN seen, process command ...
// receive a command word: 12b on RX211 or RX8E/28 in 12b mode; 8b otherwise
rx.cs = rx_recv(rx_tst_dma() || rx_tst_12b() ? 12 : 8);
if (debugLevel) debugPort->printf(F("RX: cmd=%04o\n"), rx.cs);
// led status
led_state(red, off);
led_state(green, on);
led_state(yellow, off);
// separate out the function field
rx.fcn.code = (rx.cs & RXCS_FUNCTION)>>1;
rx.fcn.name = fcn_name_list[rx.fcn.code];
// separate out the unit number in the command
rx.unit = (rx.cs & RXCS_UNITSEL) ? 1 : 0;
// separate out the density flag in the command
rx.den = (rx.cs & RXCS_DENSEL) ? RX_DEN_DD : RX_DEN_SD;
// make selected unit ready, if exists
rx_unit_file(rx.unit);
// initial error code: success!
rx.ec = RXERR_SUCCESS;
// initial error status
rx_init_es();
// print command
if (debugLevel) debugPort->printf(F("RX: %s unit=%o den=%o\n"), rx.fcn.name, rx.unit, rx.den);
// decode command function
switch (rx.fcn.code) {
// === buffer fill or empty ===
//
// DMA: command(12), wordcount(8), N*databytes(8)
// PIO: command(12), N*databytes(8 or 12)
//
case RXFCN_FILL:
case RXFCN_EMPTY:
// compute word count
if (rx_tst_dma()) {
// if DMA mode, receive word count from interface
rx.wc = rx_recv_hs(8);
// receive bus address
// rx.ba = rx_recv_hs(16); // done in RX211 hardware
} else {
// if PIO mode, compute word count from full sector size
rx.wc = rx_sec_size(rx.den) >> (rx_tst_8b() ? 1 : 2);
}
if (debugLevel) debugPort->printf(F("RX: %s wc=%03o\n"), rx.fcn.name, rx.wc);
// transform word count to transfer count
rx.tc = 2*rx.wc;
// check for word count overflow (only can happen on RX211 dma interface with user word count)
if (rx.tc > rx_sec_size(rx.den)) {
if (debugLevel) debugPort->printf(F("RX: %s wc=%03o WCOVF\n"), rx.fcn.name, rx.wc);
rx.ec = RXERR_WCOVF;
rx.es |= RXES_WCOVF;
goto error;
}
// transfer data words in/out
if (rx.fcn.code == RXFCN_EMPTY) {
// empty buffer (RX controller buffer to host interface)
for (i = j = 0; i < rx.tc; ++i) {
if (rx_tst_8b()) {
// extract 8b values from buffer to send as 8b
value = rx.buffer[i];
rx_xmit_hs(value, 8);
} else {
// 12b values are packed into the first 2/3 of the sector; read 2 8b entries and build 12b value
j = 3*i/2;
if (i & 1) {
// odd byte
value = ((rx.buffer[j+0] & 017) << 8) | rx.buffer[j+1];
} else {
// even byte
value = (rx.buffer[j+0] << 4) | ((rx.buffer[j+1] >> 4) & 017);
}
rx_xmit_hs(value, 12);
}
if (debugLevel >= 2) {
if ((i & 7) == 0) debugPort->printf(F("RX: %s %03o:"), rx.fcn.name, i);
if (rx_tst_8b()) debugPort->printf(F(" %03o"), value); else debugPort->printf(F(" %04o"), value);
if ((i & 7) == 7) debugPort->printf(F("\n"));
}
} // for (i = 0; i < rx.tc; ++i)
if (debugLevel >= 2 && (i & 7) != 0) debugPort->printf(F("\n"));
} else {
// fill buffer (host interface to RX controller buffer)
for (i = j = 0; i < rx.tc; ++i) {
if (rx_tst_8b()) {
// 8b values get stuffed into 8b buffer directly
value = rx_recv_hs(8);
rx.buffer[i] = value;
j = i+1;
} else {
// 12b values get split into 4b/8b and merged into two 8b entries
value = rx_recv_hs(12);
j = 3*i/2;
if (i & 1) {
// odd byte
rx.buffer[j++] |= (value >> 8) & 017;
rx.buffer[j++] = value;
} else {
// even byte
rx.buffer[j++] = value >> 4;
rx.buffer[j++] = (value & 017) << 4;
}
}
if (debugLevel >= 2) {
if ((i & 7) == 0) debugPort->printf(F("RX: %s %03o:"), rx.fcn.name, i);
if (rx_tst_8b()) debugPort->printf(F(" %03o"), value); else debugPort->printf(F(" %04o"), value);
if ((i & 7) == 7) debugPort->printf(F("\n"));
}
} // for (i = 0; i < rx.tc; ++i)
if (debugLevel >= 2 && (i & 7) != 0) debugPort->printf(F("\n"));
if (debugLevel >= 2 && j < rx_sec_size(rx.den))
debugPort->printf(F("RX: %s zero fill buffer %03o..%03o\n"), rx.fcn.name, j, rx_sec_size(rx.den)-1);
// zero fill any unwritten bytes at the end of the buffer up to the sector size
while (j < rx_sec_size(rx.den)) rx.buffer[j++] = 0;
}
// indicate transfer is complete
rx.wc = 0;
break;
// === read/write sector ===
//
// command(12), sector(8 or 12), track(8 or 12)
//
case RXFCN_RDSECT:
case RXFCN_WRSECT:
case RXFCN_WRDDSECT:
// first word is sector address (001..032 are valid)
rx.sa = rx_recv_hs() & 0077;
if (debugLevel) debugPort->printf(F("RX: %s sa=%03o\n"), rx.fcn.name, rx.sa);
// second word is track address (000..114 are valid)
rx.ta = rx_recv_hs() & 0377;
if (debugLevel) debugPort->printf(F("RX: %s ta=%03o\n"), rx.fcn.name, rx.ta);
// check drive is ready
if (!rx.drv[rx.unit].rdy) {
// nope, error
goto error;
}
// check track access is valid
if (rx.ta >= RX_NTRKS) {
rx.ec = RXERR_TRKERR;
goto error;
}
// check density matches, error if does not
if (rx.den != rx.drv[rx.unit].den) {
rx.ec = RXERR_DENERR;
rx.es |= RXES_DENERR;
goto error;
}
// check sector address is valid
if (rx.sa < 1 || rx.sa > RX_NSECS) {
rx.ec = RXERR_SECFAIL;
goto error;
}
// compute length of transfer (sector size)
rx.len = rx_sec_size(rx.drv[rx.unit].den);
// compute byte offset into disk image
rx.pos = (rx.ta*RX_NSECS + (rx.sa-1)) * rx.len;
// print details if in debug mode
if (debugLevel) debugPort->printf(F("RX: %s pos=%lu. len=%u.\n"), rx.fcn.name, rx.pos, rx.len);
// bail on INIT seen
if (rx_init_seen) { longjmp(rx_init_env, 20); }
// indicate SD card access in progress
led_state(yellow, on);
// do the read or write
if (rx.fcn.code == RXFCN_RDSECT) {
// do a read; copy data into buffer from diskimage@offset
value = sd_read_bytes(rx.drv[rx.unit].name, rx.pos, rx.buffer, rx.len);
} else {
// do a write
if (rx.fcn.code == RXFCN_WRDDSECT) {
bitWrite(rx.drv[rx.unit].dd[rx.ta], rx.sa, RX_DATA_DELETED);
} else {
bitWrite(rx.drv[rx.unit].dd[rx.ta], rx.sa, RX_DATA_NORMAL);
}
// copy data from rx.buffer[] to diskimage@offset
value = sd_write_bytes(rx.drv[rx.unit].name, rx.pos, rx.buffer, rx.len);
}
// set deleted data status based on sector written or read deldata status
if (bitRead(rx.drv[rx.unit].dd[rx.ta], rx.sa) == RX_DATA_DELETED) rx.es |= RXES_DELDATA;
// SD card access complete
led_state(yellow, off);
// simulate timing
rx_timing(RX_TIME_SEEK);
// update state info for drive
rx.drv[rx.unit].ta = rx.ta;
rx.drv[rx.unit].sa = rx.sa;
rx.drv[rx.unit].pos = rx.pos;
rx.drv[rx.unit].len = rx.len;
// check for read/write error
if (value != rx.len) {
rx.ec = RXERR_IDMFAIL;
goto error;
}
// and done
break;
// === set media density ===
//
// command(12), key(8 or 12)
//
case RXFCN_SETMEDIA:
// this function is a NOP for the RX01
if (rx.type == RX_TYPE_RX01) break;
// first word must be the magic key 'I'
value = rx_recv_hs();
// check key for expected value
if (value != 'I') {
rx.ec = RXERR_KEYERR;
goto error;
}
// check drive is ready
if (!rx.drv[rx.unit].rdy) {
// nope, error
goto error;
}
// simulate timing
rx_timing(RX_TIME_SETMEDIA);
// bail on INIT seen
if (rx_init_seen) { longjmp(rx_init_env, 21); }
// indicate SD card access in progress
led_state(yellow, on);
// change density to requested value AND always zero the entire disk image
sd_set_file_size(rx.drv[rx.unit].name, rx_dsk_size(rx.den));
// update unit density flag
rx.drv[rx.unit].den = rx.den;
// update status since may have changed unit density
rx_init_es();
// indicate SD card access complete
led_state(yellow, off);
// and done
break;
// === read status ===
//
// command(12)
//
case RXFCN_RDSTAT:
// simulate timing
rx_timing(RX_TIME_RDSTAT);
// update error status (already done above)
// rx_init_es();
// and done
break;
// === read error code ===
//
// command(12)
//
case RXFCN_RDERROR:
if (rx_tst_dma()) {
// RX211/RXV21; return four 16b status words
// simulate timing
rx_timing(RX_TIME_RDERROR);
// receive bus address
// rx.ba = rx_recv_hs(16); // done in RX211 hardware
// generate status byte
value = (rx.unit == 1 ? (1<<7) : 0)
| (rx.drv[1].den == RX_DEN_DD ? (1<<6) : 0)
| (rx.drv[rx.unit].rdy ? (1<<5) : 0)
| (rx.drv[0].den == RX_DEN_DD ? (1<<4) : 0)
| (rx.den == RX_DEN_DD ? (1<<0) : 0);
// print error block
if (debugLevel >= 2) {
debugPort->printf(F("RX: %s Word%d: %03o %03o\n"), rx.fcn.name, 1, rx.wc, rx.ec);
debugPort->printf(F("RX: %s Word%d: %03o %03o\n"), rx.fcn.name, 2, rx.drv[1].ta, rx.drv[0].ta);
debugPort->printf(F("RX: %s Word%d: %03o %03o\n"), rx.fcn.name, 3, rx.sa, rx.ta);
debugPort->printf(F("RX: %s Word%d: %03o %03o\n"), rx.fcn.name, 4, rx.drv[rx.unit].ta, value);
}
// word 1
rx_xmit_hs(rx.ec, 8);
rx_xmit_hs(rx.wc, 8);
// word 2
rx_xmit_hs(rx.drv[0].ta, 8);
rx_xmit_hs(rx.drv[1].ta, 8);
// word 3
rx_xmit_hs(rx.ta, 8);
rx_xmit_hs(rx.sa, 8);
// word 4
rx_xmit_hs(value, 8);
rx_xmit_hs(rx.drv[rx.unit].ta, 8);
} else if (rx.type == RX_TYPE_RX01) {
// RX11/RXV11/RX8E; return EC register in DB
// return EC instead of ES
rx.es = rx.ec;
}
// and done
break;
} // switch (rx.fcn.num)
done:
rx.cmdcnt++;
rx_xmit_es();
led_state(green, off);
return;
error:
rx.errcnt++;
rx_set_error();
led_state(red, on);
goto done;
}
// the end
>>>b dy1
(Program)
RT-11SB V05.07
.R MSCPCK
.date
?KMON-W-No date
.da 21-jan-2000
.dir
21-Jan-2000
SWAP .SYS 28P 21-Jan-2000 RT11SB.SYS 97P 21-Jan-2000
RT11FB.SYS 106P 21-Jan-2000 DL .SYS 4P 21-Jan-2000
DU .SYS 10P 21-Jan-2000 DX .SYS 4P 21-Jan-2000
DY .SYS 4P 21-Jan-2000 DZ .SYS 4P 21-Jan-2000
RK .SYS 3P 21-Jan-2000 LD .SYS 11P 21-Jan-2000
LP .SYS 2P 21-Jan-2000 LS .SYS 5P 21-Jan-2000
NL .SYS 2P 21-Jan-2000 PI .SYS 60P 21-Jan-2000
SL .SYS 17P 21-Jan-2000 SP .SYS 7P 21-Jan-2000
VM .SYS 3P 21-Jan-2000 PIP .SAV 30P 21-Jan-2000
DIR .SAV 20P 21-Jan-2000 DUP .SAV 52P 21-Jan-2000
RESORC.SAV 35P 21-Jan-2000 HELP .SAV 161P 21-Jan-2000
FORMAT.SAV 28P 21-Jan-2000 DUMP .SAV 10P 21-Jan-2000
DATIME.SAV 4P 21-Jan-2000 EDIT .SAV 19P 21-Jan-2000
TECO .SAV 50P 21-Jan-2000 STRTSB.COM 1P 21-Jan-2000
MSCPCK.SAV 4P 21-Jan-2000
29 Files, 781 Blocks
193 Free blocks
.sho all
RT-11SB V05.07
Booted from DY1:RT11SB
USR is set SWAP
EXIT is set SWAP
KMON is set NOIND
MODE is set NOSJ
TT is set NOQUIET
ERROR is set ERROR
SL is set OFF
EDIT is set KED
FORTRAN is set FORTRA
KMON nesting depth is 3
CLI is set DCL, CCL, UCL, NO UCF
PDP 11/44 Processor
3840KB of memory
FP11 Hardware Floating Point Unit
Extended Instruction Set (EIS)
Commercial Instruction Set (CIS)
Memory Management Unit
ECC Memory
Cache Memory
60 Hertz System Clock
KW11-P User Programmable Clock
FPU support
Device Status CSR Vector(s)
------ ------ --- ---------
DL Not installed 174400 160
DU Installed 172150 154
DX Not installed 177170 264
DY Resident 177170 264
DZ Not installed 000000
RK Not installed 177400 220
LD Installed 000000 000
LP Not installed 177514 200
LS Installed 176500 470 474 300 304
NL Installed 000000 000
PI Not installed 000000 000
SL Installed 000000 000
SP Installed 000000 110
VM Installed 177572 250
TT (Resident)
DY (Resident)
DY1 = DK , SY
LD
SL
DU
VM
SP
LS
NL
13 free slots
Job Name Console Level State Low High Impure
--- ---- ------- ----- ----- --- ---- ------
0 RESORC 0 0 Run 000000 137010 140514
No multi-terminal support
Address Module Words
------- ------ -----
160000 IOPAGE 4096.
156542 DY 335.
137054 RMON 3995.
001000 ..BG.. 24086.
No LD units mounted
.
>>>b dd3
(Program)
BOOTING UP XXDP-XM EXTENDED MONITOR
XXDP-XM EXTENDED MONITOR - XXDP V2.5
REVISION: F0
BOOTED FROM DD3
124KW OF MEMORY
UNIBUS SYSTEM
RESTART ADDRESS: 152000
TYPE "H" FOR HELP !
.R ZRXF??
ZRXFB0.BIC
DRSSM-G2
CZRXFB0-0-0
RX02 FUNCTION-LOGIC TEST
UNIT IS RX02
RSTRT ADR 145702
DR>STA
CHANGE HW (L) ? Y
# UNITS (D) ? 2
UNIT 0
RX BUS ADR (O) 177170 ?
VECTOR ADR (O) 264 ?
DRIVE # (O) 0 ?
EXP WRD-CR (O) 0 ?
BR-LEVEL (O) 5 ?
UNIT 1
RX BUS ADR (O) 177170 ?
VECTOR ADR (O) 264 ?
DRIVE # (O) 0 ? 1
EXP WRD-CR (O) 0 ?
BR-LEVEL (O) 5 ?
CHANGE SW (L) ? Y
TEST HELP (L) N ? Y
DIAGNOSTIC MODES ARE:
LOGIC TEST, FUNCTION TEST, OR BOTH
-FUNCTION TESTS (1-10)
ACT AS QUICK VERIFY & REPORT FAILING FUNCTIONS
-LOGIC TESTS (11-36)
ANALYZE FAILURE & GIVE ERROR INFO
REPORT FIELD REPLACEABLE UNITS "FRU'S"
->DEVICE FATAL THRESHOLD LEVEL (DVTL) IS SET = 1
"DVTL" = NO. OF HARD ERRS THAT CAUSE DEVICE FATAL ERR
TYPE "CR" TO CONTINUE (L) N ?
LOGIC TEST MODE (L) Y ? Y
FUNCTION TEST MODE (L) N ? Y
HARD ERR -> DEVICE FATAL THRESHOLD LVL (O) 1 ?
NON-EXISTANT MEM ADR (NXM TST) (O) 160000 ?
EXTENDED ADR BITS: 13 & 12 (NPR-NXM TST) (O) 0 ?
TEST CONTROL FLAGS (L) N ?
EXPANSION WORD TYPE <CR> (L) N ?
IS FLOPPY SYSTEM CONTAINING UNIT #00
POWERED DOWN (L) N ? N
IS FLOPPY SYSTEM CONTAINING UNIT #01
POWERED DOWN (L) N ? N
CZRXFB0 EOP 1
0 TOTAL ERRS
IS FLOPPY SYSTEM CONTAINING UNIT #00
POWERED DOWN (L) N ? N
IS FLOPPY SYSTEM CONTAINING UNIT #01
POWERED DOWN (L) N ? N
CZRXFB0 EOP 2
0 TOTAL ERRS
(Console)
^P
>>>h
Halted at 135642
>>>b dd3
(Program)
BOOTING UP XXDP-XM EXTENDED MONITOR
XXDP-XM EXTENDED MONITOR - XXDP V2.5
REVISION: F0
BOOTED FROM DD3
124KW OF MEMORY
UNIBUS SYSTEM
RESTART ADDRESS: 152000
TYPE "H" FOR HELP !
.R ZRXE??
ZRXEA2.BIC
CZRXEA0 RX02 FMTR PROG
HELP? (Y OR N) N n
SET DISKETTE TO SINGLE DENSITY? (Y OR N) N n
VERIFY DISKETTE CRC (ALL TRACKS)? (Y OR N) N n
FLOPPY DISK SYSTEM: 0 ADDRESS CHANGE? (Y OR N) N n
IS ANOTHER FLOPPY DISK SYSTEM AVAILABLE? (Y OR N) N n
FORMAT DONE ON FOLLOWING
SYSTEM:0 DRIVE:0 DRIVE:1
FORMAT COMPLETED
DO YOU WANT TO FORMAT MORE DISKETTES? (Y OR N) N n
FORMATTER DONE-RESTART MONITOR OR UPDATE PROGRAM-->TYPE CTRL C TO
START THIS PROGRAM AGAIN
END OF PASS 0
(Console)
^P
>>>h
Halted at 001442
>>>b dd3
(Program)
BOOTING UP XXDP-XM EXTENDED MONITOR
XXDP-XM EXTENDED MONITOR - XXDP V2.5
REVISION: F0
BOOTED FROM DD3
124KW OF MEMORY
UNIBUS SYSTEM
RESTART ADDRESS: 152000
TYPE "H" FOR HELP !
.R ZRXD??
ZRXDC0.BIC
DRSSM-G2
CZRXDC0-0-0
RX02 SS PERF EXER
UNIT IS RX02
RSTRT ADR 145702
DR>STA
CHANGE HW (L) ? Y
# UNITS (D) ? 2
UNIT 0
RX BUS ADR (O) 177170 ?
VECTOR ADR (O) 264 ?
DRIVE # (O) 0 ?
EXP WRD-CR (O) 0 ?
UNIT 1
RX BUS ADR (O) 177170 ?
VECTOR ADR (O) 264 ?
DRIVE # (O) 0 ? 1
EXP WRD-CR (O) 0 ?
CHANGE SW (L) ? Y
HELP TEST SETUP (L) N ? Y
EXERCISE OPTIONS
0 = WRITE-READ-DATA CK & READ-DATA CK
1 = WRITE ONLY
2 = WRITE-READ
3 = WRITE-READ-DATA CHECK
4 = READ-DATA CHECK ONLY
5 = READ ONLY (CRC CHECK)
6 = WRITE-READ-DATA CHECK ON ALTERNATE DRIVES
DATA PATTERN OPTIONS
0 = RANDOM
1 = ZEROS
2 = ONES
3 = FLOATING ZERO
4 = FLOATING ONE
5 = 125
6 = 333
TRACK SEQUENCE OPTIONS
0 = RANDOM
1 = INCREMENT O.D.
2 = DECREMENT I.D.
3 = INCREMENT O.D.-DECREMENT I.D.
4 = BOUNCE BETWEEN I.D. & O.D.
5 = BOUNCE BETWEEN INCR. O.D. & DECR. I.D.
6 = BOUNCE BETWEEN O.D. & DECR. I.D.
(O.D. = OUTSIDE DIA. & I.D. = INSIDE DIA.)
->DEVICE FATAL THRESHOLD LVL=NO. OF HARD ERRS THAT CAUSE DEVICE FATAL ERR
IF DRS "EVL" FLAG IS SET, BUT HARD ERR WILL STILL LOG AS A HARD ERR.
THE "EVL" FLAG WILL CAUSE 10 RETRIED SOFT ERRS TO BECOME A HARD ERR
TYPE "CR" TO CONTINUE (L) N ?
EXERCISE # (0-6) (O) 0 ? 3
DATA PATTERN # (0-6) (O) 0 ? 0
TRACK SEQUENCE # (0-6) (O) 0 ? 0
DEVICE FATAL THRESHOLD LEVEL (D) 1 ?
RUN TEST IN DOUBLE DENSITY (L) Y ?
RUN TEST IN DELETED DATA MODE (L) N ? Y
ANY PROGRAM CONTROL FLAGS (L) N ?
MODIFY TRACK ADDRESS LIMITS (L) N ?
MODIFY SECTOR ADDRESS LIMITS (L) N ?
RXXX EXPANSION TYPE <CR> (L) N ?
UNIT#1- WRONG DENSITY -SINGLE DENSITY DISKETTE
->REFORMAT DISKETTE - ARE YOU SURE? (L) N ? Y
UNIT#1-REFORMATTING, DO NOT INTERRUPT
UNIT#0 UNIT#1
# SECTOR READS (8)= 00000004037 00000004037
# SECTOR WRITES (8)= 00000004037 00000004037
UNIT#0 UNIT#1
CHECK SUM: 0 0
FILL-EMP BUFF LOG: 0 0
NO ERR BIT: 0 0
INTER-NO DONE ERR: 0 0
INTERRUPT ERR: 0 0
SEEK: 0 0
CRC ERR: 0 0
CRC BAD: 0 0
READ ERR: 0 0
WRITE ERR: 0 0
DATA ERR: 0 0
DEL. DATA ERR: 0 0
HRD SEEK: 0 0
HRD CRC ERR: 0 0
HRD CRC BAD: 0 0
HRD READ: 0 0
HRD WRITE: 0 0
HRD DATA: 0 0
HRD DEL. DATA ERR: 0 0
ERR
CODE# UNIT#0 UNIT#1
010 0 0
020 0 0
030 0 0
040 0 0
050 0 0
060 0 0
070 0 0
100 0 0
110 0 0
120 0 0
130 0 0
140 0 0
150 0 0
160 0 0
170 0 0
200 0 0
210 0 0
220 0 0
230 0 0
240 0 0
250 0 0
260 0 0
TRACK# UNIT#0 UNIT#1
0 0 0
1 0 0
2 0 0
3 0 0
4 0 0
5 0 0
6 0 0
7 0 0
8 0 0
9 0 0
10 0 0
11 0 0
12 0 0
13 0 0
14 0 0
15 0 0
16 0 0
17 0 0
18 0 0
19 0 0
20 0 0
21 0 0
22 0 0
23 0 0
24 0 0
25 0 0
26 0 0
27 0 0
28 0 0
29 0 0
30 0 0
31 0 0
32 0 0
33 0 0
34 0 0
35 0 0
36 0 0
37 0 0
38 0 0
39 0 0
40 0 0
41 0 0
42 0 0
43 0 0
44 0 0
45 0 0
46 0 0
47 0 0
48 0 0
49 0 0
50 0 0
51 0 0
52 0 0
53 0 0
54 0 0
55 0 0
56 0 0
57 0 0
58 0 0
59 0 0
60 0 0
61 0 0
62 0 0
63 0 0
64 0 0
65 0 0
66 0 0
67 0 0
68 0 0
69 0 0
70 0 0
71 0 0
72 0 0
73 0 0
74 0 0
75 0 0
76 0 0
CZRXDC0 EOP 1
0 TOTAL ERRS
^C
DR>EXIT