diff --git a/AltairZ80/altairz80_sys.c b/AltairZ80/altairz80_sys.c index 34c5c987..ff523051 100644 --- a/AltairZ80/altairz80_sys.c +++ b/AltairZ80/altairz80_sys.c @@ -69,6 +69,7 @@ extern DEVICE hdc1001_dev; extern DEVICE jade_dev; extern DEVICE tarbell_dev; extern DEVICE icom_dev; +extern DEVICE dj2d_dev; extern DEVICE m2sio0_dev; extern DEVICE m2sio1_dev; extern DEVICE pmmi_dev; @@ -130,6 +131,8 @@ DEVICE *sim_devices[] = { &tarbell_dev, /* iCOM Devices */ &icom_dev, + /* Disk Jockey 2D Devices */ + &dj2d_dev, /* MITS 88-2SIO */ &m2sio0_dev, &m2sio1_dev, diff --git a/AltairZ80/s100_dj2d.c b/AltairZ80/s100_dj2d.c new file mode 100644 index 00000000..fbc04a38 --- /dev/null +++ b/AltairZ80/s100_dj2d.c @@ -0,0 +1,2070 @@ +/* s100_dj2d.c: Morrow DISK JOCKEY 2D/B Floppy Disk Interface + + Created by Patrick Linstruth (patrick@deltecent.com) + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + *************************************************************************** + ** This device simulates the DISK JOCKEY 2D Model B, not the original 2D ** + ** ** + ** If I can find PROMs and CP/M images for the original 2D, I will add ** + ** support. ** + *************************************************************************** + + DJ2D units: + + DJ2D0 - Drive A + DJ2D1 - Drive B + DJ2D3 - Drive C + DJ2D4 - Drive D + DJ2D5 - Serial Port + +*/ + +/* #define DBG_MSG */ + +#include "altairz80_defs.h" +#include "sim_imd.h" +#include "sim_tmxr.h" + +#ifdef DBG_MSG +#define DBG_PRINT(args) sim_printf args +#else +#define DBG_PRINT(args) +#endif + +extern uint32 PCX; +extern t_stat set_membase(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), const char* name, uint8 unmap); +extern DEVICE *find_dev (const char *cptr); +extern uint32 getClockFrequency(void); +extern void setClockFrequency(const uint32 Value); + +#define DJ2D_MAX_ADAPTERS 1 +#define DJ2D_MAX_DRIVES 4 +#define DJ2D_UNITS DJ2D_MAX_DRIVES+1 +#define DJ2D_SIO_UNIT DJ2D_UNITS-1 +#define DJ2D_TRACKS 77 +#define DJ2D_TIMER 1 /* 1ms timer */ +#define DJ2D_ROTATION_MS 166 /* 166 milliseconds per revolution */ +#define DJ2D_HEAD_TIMEOUT (DJ2D_ROTATION_MS / DJ2D_TIMER * 6) /* 6 revolutions */ +#define DJ2D_INDEX_TIMEOUT (DJ2D_ROTATION_MS / DJ2D_TIMER) /* 1 revolution */ +#define DJ2D_BUSY_TIMEOUT 2 /* 2 timer ticks */ + +#define DJ2D_BAUD 19200 /* Default baud rate */ + +enum { FMT_SD, FMT_256, FMT_512, FMT_1024, FMT_UNKNOWN }; + +static uint32 dj2d_image_size[] = {256256, 509184, 587008, 625920, 0}; +static uint16 dj2d_sector_len[] = {128, 256, 512, 1024, 0}; +static uint16 dj2d_spt[] = {26, 26, 15, 8, 0}; +static uint16 dj2d_track_len[] = {5000, 9800, 10300, 9700, 0}; + +#define DJ2D_MEM_READ FALSE +#define DJ2D_MEM_WRITE TRUE + +#define DJ2D_PROM_BASE 0xe000 +#define DJ2D_PROM_SIZE 1024 +#define DJ2D_PROM_MASK (DJ2D_PROM_SIZE-1) +#define DJ2D_MEM_BASE DJ2D_PROM_BASE + DJ2D_PROM_SIZE +#define DJ2D_MEM_SIZE 1024 /* Must be on a page boundary */ +#define DJ2D_MEM_MASK (DJ2D_MEM_SIZE-1) + +static uint8 dj2d_mem[DJ2D_MEM_SIZE]; + +/* DJ2D PROM is 1018 bytes following by 8 memory-mapped I/O bytes */ + +static uint8 dj2d_prom_e000[DJ2D_PROM_SIZE] = { + 0xc3, 0x69, 0xe0, 0xc3, 0xe9, 0xe0, 0xc3, 0xda, + 0xe0, 0xc3, 0x5a, 0xe1, 0xc3, 0x8b, 0xe1, 0xc3, + 0x81, 0xe1, 0xc3, 0x43, 0xe1, 0xc3, 0xdd, 0xe1, + 0xc3, 0xbc, 0xe1, 0xc3, 0x3c, 0xe1, 0xc3, 0xf8, + 0xe0, 0xc3, 0x03, 0xe1, 0xc3, 0x34, 0xe1, 0xc3, + 0x09, 0xe1, 0xc3, 0xc5, 0xe0, 0xc3, 0xb3, 0xe3, + 0xc3, 0xe5, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x31, 0xfa, 0xe7, 0xcd, 0xd2, 0xe3, 0x21, + 0x01, 0x00, 0xe5, 0x2e, 0x09, 0xe5, 0x26, 0xff, + 0xe5, 0xe5, 0xe5, 0xe5, 0x21, 0x08, 0x00, 0xe5, + 0x2e, 0x7e, 0xe5, 0x2e, 0x08, 0xe5, 0x26, 0x18, + 0xe5, 0x3e, 0x7f, 0x32, 0xf9, 0xe3, 0x3e, 0xd0, + 0x32, 0xfc, 0xe3, 0xaf, 0xcd, 0x1b, 0xe3, 0xd2, + 0xa5, 0xe0, 0x3e, 0x01, 0x32, 0xf6, 0xe7, 0xcd, + 0xd2, 0xe3, 0xc3, 0x93, 0xe0, 0x3e, 0x09, 0x32, + 0xf6, 0xe7, 0xcd, 0x96, 0xe3, 0xc1, 0x01, 0x00, + 0xe7, 0xc5, 0xd5, 0x21, 0x00, 0x00, 0xe5, 0x00, + 0xc5, 0x06, 0x0c, 0xc5, 0xcd, 0xdd, 0xe1, 0xc1, + 0xd0, 0x05, 0xc2, 0xbb, 0xe0, 0x0e, 0x09, 0x11, + 0xc3, 0xa2, 0x1b, 0x7a, 0xb3, 0xc2, 0xca, 0xe0, + 0x3e, 0x08, 0xa9, 0x4f, 0x32, 0xfa, 0xe3, 0xc3, + 0xc7, 0xe0, 0x3a, 0xf9, 0xe3, 0xe6, 0x08, 0xc2, + 0xda, 0xe0, 0x79, 0x2f, 0x32, 0xf8, 0xe3, 0x2f, + 0xc9, 0x3a, 0xf9, 0xe3, 0xe6, 0x04, 0xc2, 0xe9, + 0xe0, 0x3a, 0xf8, 0xe3, 0x2f, 0xe6, 0x7f, 0xc9, + 0x3a, 0xf9, 0xe3, 0xe6, 0x04, 0xc0, 0xcd, 0xe9, + 0xe0, 0xb9, 0xc9, 0x3a, 0xf9, 0xe3, 0xe6, 0x04, + 0xc9, 0x21, 0xfd, 0xe3, 0x4e, 0x23, 0x46, 0x3a, + 0xf6, 0xe7, 0x2f, 0xe6, 0x01, 0x0f, 0x57, 0x3a, + 0xf7, 0xe7, 0x07, 0x07, 0x07, 0xb2, 0x57, 0x3a, + 0xe8, 0xe7, 0xee, 0x08, 0x17, 0x17, 0x82, 0x57, + 0x3a, 0xfd, 0xe7, 0x17, 0x17, 0xb2, 0x57, 0x3a, + 0xec, 0xe7, 0x82, 0xc9, 0xe5, 0x2a, 0xe6, 0xe7, + 0x44, 0x4d, 0xe1, 0xc9, 0x79, 0xe6, 0x03, 0x32, + 0xeb, 0xe7, 0xc9, 0x21, 0x00, 0x1c, 0x09, 0xda, + 0x54, 0xe1, 0x21, 0x08, 0x20, 0x09, 0xd2, 0x54, + 0xe1, 0x3e, 0x10, 0xc9, 0x60, 0x69, 0x22, 0xe6, + 0xe7, 0xc9, 0xcd, 0xe3, 0xe2, 0xd8, 0xcd, 0x70, + 0xe1, 0xf5, 0x9f, 0x32, 0xf9, 0xe7, 0x32, 0xfd, + 0xe3, 0xaf, 0x32, 0xed, 0xe7, 0xc3, 0x23, 0xe2, + 0xaf, 0x32, 0xe9, 0xe7, 0x21, 0x00, 0x00, 0x3e, + 0x09, 0xcd, 0x62, 0xe3, 0xe6, 0x04, 0xc0, 0x37, + 0xc9, 0xaf, 0xb1, 0x37, 0xc8, 0xe6, 0x3f, 0x32, + 0xf8, 0xe7, 0xc9, 0x79, 0xfe, 0x4d, 0x3f, 0xd8, + 0x32, 0xf9, 0xe7, 0xc9, 0x32, 0xe3, 0xe7, 0xcd, + 0x96, 0xe3, 0x0e, 0x01, 0x79, 0x32, 0xfe, 0xe3, + 0x3a, 0xf8, 0xe7, 0xb9, 0xc8, 0x3e, 0x80, 0xcd, + 0x5d, 0xe3, 0xda, 0x20, 0xe2, 0x0c, 0xc3, 0x9c, + 0xe1, 0x32, 0xfc, 0xe3, 0x48, 0x11, 0xff, 0xe3, + 0x2a, 0xe6, 0xe7, 0xc9, 0xcd, 0x33, 0xe2, 0xda, + 0x22, 0xe2, 0x3e, 0xa0, 0xcd, 0xb1, 0xe1, 0x7e, + 0x23, 0x12, 0x7e, 0x23, 0x12, 0x7e, 0x23, 0x12, + 0x0d, 0x7e, 0x23, 0x12, 0xc2, 0xc7, 0xe1, 0x21, + 0xc2, 0xe1, 0xc3, 0xfb, 0xe1, 0xcd, 0x33, 0xe2, + 0xda, 0x22, 0xe2, 0x3e, 0x80, 0xcd, 0xb1, 0xe1, + 0x1a, 0x77, 0x23, 0x1a, 0x77, 0x23, 0x1a, 0x77, + 0x23, 0x0d, 0x1a, 0x77, 0x23, 0xc2, 0xe8, 0xe1, + 0x21, 0xe3, 0xe1, 0xe5, 0x21, 0xfc, 0xe3, 0xcd, + 0x6c, 0xe3, 0xe6, 0x5f, 0xca, 0x21, 0xe2, 0xfe, + 0x10, 0xc2, 0x20, 0xe2, 0x3a, 0xe2, 0xe7, 0x3d, + 0xfa, 0x17, 0xe2, 0x32, 0xe2, 0xe7, 0xc9, 0x3a, + 0xe3, 0xe7, 0x3d, 0xf2, 0x94, 0xe1, 0x3e, 0x10, + 0x37, 0xe1, 0xf5, 0x3a, 0xf6, 0xe7, 0xee, 0x04, + 0x32, 0xfa, 0xe3, 0x3a, 0xea, 0xe7, 0x32, 0xf9, + 0xe3, 0xf1, 0xc9, 0xcd, 0xe3, 0xe2, 0xd8, 0x3a, + 0xfd, 0xe3, 0x3c, 0xcc, 0x70, 0xe1, 0xd8, 0x21, + 0xfd, 0xe3, 0x3a, 0xf9, 0xe7, 0xbe, 0x23, 0x23, + 0x77, 0x79, 0x32, 0xf9, 0xe3, 0xca, 0x6a, 0xe2, + 0xaf, 0x32, 0xe9, 0xe7, 0x3a, 0xfa, 0xe3, 0xe6, + 0x08, 0x32, 0xe8, 0xe7, 0x1f, 0x1f, 0x1f, 0xc6, + 0x18, 0x21, 0x00, 0x00, 0xcd, 0x62, 0xe3, 0xda, + 0x8e, 0xe2, 0x3a, 0xe9, 0xe7, 0xb7, 0xc2, 0xb9, + 0xe2, 0x06, 0x02, 0x3e, 0x1d, 0xcd, 0x5d, 0xe3, + 0xe6, 0x99, 0x57, 0xca, 0x95, 0xe2, 0x3a, 0xf6, + 0xe7, 0xee, 0x01, 0x32, 0xf6, 0xe7, 0x32, 0xfa, + 0xe3, 0x05, 0xc2, 0x73, 0xe2, 0x7a, 0x37, 0xf5, + 0xcd, 0x70, 0xe1, 0xf1, 0xc9, 0x06, 0x0a, 0x11, + 0xff, 0xe3, 0x21, 0xfa, 0xe7, 0x3e, 0xc4, 0x32, + 0xfc, 0xe3, 0x1a, 0x77, 0x2c, 0xc2, 0xa2, 0xe2, + 0x21, 0xfc, 0xe3, 0xcd, 0x6c, 0xe3, 0xb7, 0xca, + 0xb9, 0xe2, 0x05, 0xc2, 0x97, 0xe2, 0xc3, 0x8e, + 0xe2, 0x3a, 0xfd, 0xe7, 0x4f, 0x06, 0x00, 0x21, + 0xdf, 0xe2, 0x09, 0x3a, 0xf8, 0xe7, 0x47, 0x86, + 0x3e, 0x10, 0xd8, 0x78, 0x32, 0xfe, 0xe3, 0x3e, + 0x20, 0x21, 0x05, 0x05, 0x22, 0xe2, 0xe7, 0x0d, + 0x47, 0xf8, 0x17, 0xb7, 0xc3, 0xd7, 0xe2, 0xd5, + 0xd5, 0xf0, 0xf7, 0x21, 0xeb, 0xe7, 0x4e, 0x23, + 0x5e, 0x71, 0x23, 0x7b, 0xb9, 0x7e, 0x36, 0x01, + 0xca, 0x1b, 0xe3, 0x23, 0xe5, 0x16, 0x00, 0x42, + 0x19, 0x19, 0x3a, 0xf6, 0xe7, 0x77, 0x23, 0x11, + 0xfd, 0xe3, 0x1a, 0x77, 0xe1, 0x09, 0x09, 0x7e, + 0x32, 0xf6, 0xe7, 0x23, 0x7e, 0x12, 0x3e, 0x7f, + 0x07, 0x0d, 0xf2, 0x10, 0xe3, 0xe6, 0x7f, 0x32, + 0xea, 0xe7, 0xaf, 0x21, 0xfa, 0xe3, 0xa6, 0x32, + 0xe9, 0xe7, 0xf5, 0x3a, 0xea, 0xe7, 0x4f, 0x3a, + 0xf7, 0xe7, 0x2f, 0xa1, 0x32, 0xf9, 0xe3, 0xee, + 0x40, 0x4f, 0x3a, 0xf6, 0xe7, 0x47, 0x3a, 0xf9, + 0xe7, 0xd6, 0x01, 0x9f, 0x3d, 0x2f, 0xb0, 0x77, + 0xf1, 0xc2, 0x4f, 0xe3, 0xe5, 0x2a, 0xe4, 0xe7, + 0x2b, 0x7c, 0xb5, 0xc2, 0x48, 0xe3, 0xe1, 0x7e, + 0xe6, 0x80, 0xc0, 0x3a, 0xf6, 0xe7, 0xf6, 0x06, + 0x77, 0x3e, 0x80, 0x37, 0xc9, 0x2a, 0xe4, 0xe7, + 0x29, 0x29, 0xeb, 0x21, 0xfc, 0xe3, 0x77, 0x7e, + 0x1f, 0xd2, 0x67, 0xe3, 0x7e, 0x1f, 0x7e, 0xd0, + 0xc3, 0x76, 0xe3, 0xc3, 0xe3, 0xe2, 0x1b, 0x7a, + 0xb3, 0xc2, 0x6c, 0xe3, 0x5e, 0xe5, 0x23, 0x56, + 0x3a, 0xea, 0xe7, 0xee, 0x80, 0x32, 0xf9, 0xe3, + 0xee, 0xc0, 0xe3, 0x32, 0xf9, 0xe3, 0x36, 0xd0, + 0xe3, 0x72, 0xe1, 0x7b, 0x37, 0xc9, 0x11, 0x00, + 0x00, 0x21, 0xfa, 0xe3, 0x0e, 0x10, 0x7e, 0xa1, + 0xca, 0x9e, 0xe3, 0x7e, 0xa1, 0xc2, 0xa3, 0xe3, + 0x13, 0xe3, 0xe3, 0xe3, 0xe3, 0x7e, 0xa1, 0xca, + 0xa8, 0xe3, 0xc9, 0x79, 0xe6, 0x01, 0x2f, 0x47, + 0x21, 0xeb, 0xe7, 0x5e, 0x16, 0x00, 0x23, 0x7e, + 0xab, 0xf5, 0x23, 0x23, 0x19, 0x19, 0x7e, 0xf6, + 0x01, 0xa0, 0x77, 0xf1, 0xc0, 0x7e, 0x32, 0xf6, + 0xe7, 0xc9, 0x21, 0x00, 0x00, 0x2b, 0x7c, 0xb5, + 0xe3, 0xe3, 0xc2, 0xd5, 0xe3, 0xc9, 0xe5, 0x21, + 0xe2, 0xe3, 0xe9, 0xe1, 0xc9, 0x79, 0xe6, 0x01, + 0x17, 0x17, 0x17, 0x17, 0x32, 0xf7, 0xe7, 0xc9, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x00, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + +static uint8 dj2d_prom_f800[DJ2D_PROM_SIZE] = { + 0xc3, 0x69, 0xf8, 0xc3, 0xe9, 0xf8, 0xc3, 0xda, + 0xf8, 0xc3, 0x5a, 0xf9, 0xc3, 0x8b, 0xf9, 0xc3, + 0x81, 0xf9, 0xc3, 0x43, 0xf9, 0xc3, 0xdd, 0xf9, + 0xc3, 0xbc, 0xf9, 0xc3, 0x3c, 0xf9, 0xc3, 0xf8, + 0xf8, 0xc3, 0x03, 0xf9, 0xc3, 0x34, 0xf9, 0xc3, + 0x09, 0xf9, 0xc3, 0xc5, 0xf8, 0xc3, 0xb3, 0xfb, + 0xc3, 0xe5, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x31, 0xfa, 0xff, 0xcd, 0xd2, 0xfb, 0x21, + 0x01, 0x00, 0xe5, 0x2e, 0x09, 0xe5, 0x26, 0xff, + 0xe5, 0xe5, 0xe5, 0xe5, 0x21, 0x08, 0x00, 0xe5, + 0x2e, 0x7e, 0xe5, 0x2e, 0x08, 0xe5, 0x26, 0x18, + 0xe5, 0x3e, 0x7f, 0x32, 0xf9, 0xfb, 0x3e, 0xd0, + 0x32, 0xfc, 0xfb, 0xaf, 0xcd, 0x1b, 0xfb, 0xd2, + 0xa5, 0xf8, 0x3e, 0x01, 0x32, 0xf6, 0xff, 0xcd, + 0xd2, 0xfb, 0xc3, 0x93, 0xf8, 0x3e, 0x09, 0x32, + 0xf6, 0xff, 0xcd, 0x96, 0xfb, 0xc1, 0x01, 0x00, + 0xff, 0xc5, 0xd5, 0x21, 0x00, 0x00, 0xe5, 0x00, + 0xc5, 0x06, 0x0c, 0xc5, 0xcd, 0xdd, 0xf9, 0xc1, + 0xd0, 0x05, 0xc2, 0xbb, 0xf8, 0x0e, 0x09, 0x11, + 0xc3, 0xa2, 0x1b, 0x7a, 0xb3, 0xc2, 0xca, 0xf8, + 0x3e, 0x08, 0xa9, 0x4f, 0x32, 0xfa, 0xfb, 0xc3, + 0xc7, 0xf8, 0x3a, 0xf9, 0xfb, 0xe6, 0x08, 0xc2, + 0xda, 0xf8, 0x79, 0x2f, 0x32, 0xf8, 0xfb, 0x2f, + 0xc9, 0x3a, 0xf9, 0xfb, 0xe6, 0x04, 0xc2, 0xe9, + 0xf8, 0x3a, 0xf8, 0xfb, 0x2f, 0xe6, 0x7f, 0xc9, + 0x3a, 0xf9, 0xfb, 0xe6, 0x04, 0xc0, 0xcd, 0xe9, + 0xf8, 0xb9, 0xc9, 0x3a, 0xf9, 0xfb, 0xe6, 0x04, + 0xc9, 0x21, 0xfd, 0xfb, 0x4e, 0x23, 0x46, 0x3a, + 0xf6, 0xff, 0x2f, 0xe6, 0x01, 0x0f, 0x57, 0x3a, + 0xf7, 0xff, 0x07, 0x07, 0x07, 0xb2, 0x57, 0x3a, + 0xe8, 0xff, 0xee, 0x08, 0x17, 0x17, 0x82, 0x57, + 0x3a, 0xfd, 0xff, 0x17, 0x17, 0xb2, 0x57, 0x3a, + 0xec, 0xff, 0x82, 0xc9, 0xe5, 0x2a, 0xe6, 0xff, + 0x44, 0x4d, 0xe1, 0xc9, 0x79, 0xe6, 0x03, 0x32, + 0xeb, 0xff, 0xc9, 0x21, 0x00, 0x04, 0x09, 0xda, + 0x54, 0xf9, 0x21, 0x08, 0x08, 0x09, 0xd2, 0x54, + 0xf9, 0x3e, 0x10, 0xc9, 0x60, 0x69, 0x22, 0xe6, + 0xff, 0xc9, 0xcd, 0xe3, 0xfa, 0xd8, 0xcd, 0x70, + 0xf9, 0xf5, 0x9f, 0x32, 0xf9, 0xff, 0x32, 0xfd, + 0xfb, 0xaf, 0x32, 0xed, 0xff, 0xc3, 0x23, 0xfa, + 0xaf, 0x32, 0xe9, 0xff, 0x21, 0x00, 0x00, 0x3e, + 0x09, 0xcd, 0x62, 0xfb, 0xe6, 0x04, 0xc0, 0x37, + 0xc9, 0xaf, 0xb1, 0x37, 0xc8, 0xe6, 0x3f, 0x32, + 0xf8, 0xff, 0xc9, 0x79, 0xfe, 0x4d, 0x3f, 0xd8, + 0x32, 0xf9, 0xff, 0xc9, 0x32, 0xe3, 0xff, 0xcd, + 0x96, 0xfb, 0x0e, 0x01, 0x79, 0x32, 0xfe, 0xfb, + 0x3a, 0xf8, 0xff, 0xb9, 0xc8, 0x3e, 0x80, 0xcd, + 0x5d, 0xfb, 0xda, 0x20, 0xfa, 0x0c, 0xc3, 0x9c, + 0xf9, 0x32, 0xfc, 0xfb, 0x48, 0x11, 0xff, 0xfb, + 0x2a, 0xe6, 0xff, 0xc9, 0xcd, 0x33, 0xfa, 0xda, + 0x22, 0xfa, 0x3e, 0xa0, 0xcd, 0xb1, 0xf9, 0x7e, + 0x23, 0x12, 0x7e, 0x23, 0x12, 0x7e, 0x23, 0x12, + 0x0d, 0x7e, 0x23, 0x12, 0xc2, 0xc7, 0xf9, 0x21, + 0xc2, 0xf9, 0xc3, 0xfb, 0xf9, 0xcd, 0x33, 0xfa, + 0xda, 0x22, 0xfa, 0x3e, 0x80, 0xcd, 0xb1, 0xf9, + 0x1a, 0x77, 0x23, 0x1a, 0x77, 0x23, 0x1a, 0x77, + 0x23, 0x0d, 0x1a, 0x77, 0x23, 0xc2, 0xe8, 0xf9, + 0x21, 0xe3, 0xf9, 0xe5, 0x21, 0xfc, 0xfb, 0xcd, + 0x6c, 0xfb, 0xe6, 0x5f, 0xca, 0x21, 0xfa, 0xfe, + 0x10, 0xc2, 0x20, 0xfa, 0x3a, 0xe2, 0xff, 0x3d, + 0xfa, 0x17, 0xfa, 0x32, 0xe2, 0xff, 0xc9, 0x3a, + 0xe3, 0xff, 0x3d, 0xf2, 0x94, 0xf9, 0x3e, 0x10, + 0x37, 0xe1, 0xf5, 0x3a, 0xf6, 0xff, 0xee, 0x04, + 0x32, 0xfa, 0xfb, 0x3a, 0xea, 0xff, 0x32, 0xf9, + 0xfb, 0xf1, 0xc9, 0xcd, 0xe3, 0xfa, 0xd8, 0x3a, + 0xfd, 0xfb, 0x3c, 0xcc, 0x70, 0xf9, 0xd8, 0x21, + 0xfd, 0xfb, 0x3a, 0xf9, 0xff, 0xbe, 0x23, 0x23, + 0x77, 0x79, 0x32, 0xf9, 0xfb, 0xca, 0x6a, 0xfa, + 0xaf, 0x32, 0xe9, 0xff, 0x3a, 0xfa, 0xfb, 0xe6, + 0x08, 0x32, 0xe8, 0xff, 0x1f, 0x1f, 0x1f, 0xc6, + 0x18, 0x21, 0x00, 0x00, 0xcd, 0x62, 0xfb, 0xda, + 0x8e, 0xfa, 0x3a, 0xe9, 0xff, 0xb7, 0xc2, 0xb9, + 0xfa, 0x06, 0x02, 0x3e, 0x1d, 0xcd, 0x5d, 0xfb, + 0xe6, 0x99, 0x57, 0xca, 0x95, 0xfa, 0x3a, 0xf6, + 0xff, 0xee, 0x01, 0x32, 0xf6, 0xff, 0x32, 0xfa, + 0xfb, 0x05, 0xc2, 0x73, 0xfa, 0x7a, 0x37, 0xf5, + 0xcd, 0x70, 0xf9, 0xf1, 0xc9, 0x06, 0x0a, 0x11, + 0xff, 0xfb, 0x21, 0xfa, 0xff, 0x3e, 0xc4, 0x32, + 0xfc, 0xfb, 0x1a, 0x77, 0x2c, 0xc2, 0xa2, 0xfa, + 0x21, 0xfc, 0xfb, 0xcd, 0x6c, 0xfb, 0xb7, 0xca, + 0xb9, 0xfa, 0x05, 0xc2, 0x97, 0xfa, 0xc3, 0x8e, + 0xfa, 0x3a, 0xfd, 0xff, 0x4f, 0x06, 0x00, 0x21, + 0xdf, 0xfa, 0x09, 0x3a, 0xf8, 0xff, 0x47, 0x86, + 0x3e, 0x10, 0xd8, 0x78, 0x32, 0xfe, 0xfb, 0x3e, + 0x20, 0x21, 0x05, 0x05, 0x22, 0xe2, 0xff, 0x0d, + 0x47, 0xf8, 0x17, 0xb7, 0xc3, 0xd7, 0xfa, 0xd5, + 0xd5, 0xf0, 0xf7, 0x21, 0xeb, 0xff, 0x4e, 0x23, + 0x5e, 0x71, 0x23, 0x7b, 0xb9, 0x7e, 0x36, 0x01, + 0xca, 0x1b, 0xfb, 0x23, 0xe5, 0x16, 0x00, 0x42, + 0x19, 0x19, 0x3a, 0xf6, 0xff, 0x77, 0x23, 0x11, + 0xfd, 0xfb, 0x1a, 0x77, 0xe1, 0x09, 0x09, 0x7e, + 0x32, 0xf6, 0xff, 0x23, 0x7e, 0x12, 0x3e, 0x7f, + 0x07, 0x0d, 0xf2, 0x10, 0xfb, 0xe6, 0x7f, 0x32, + 0xea, 0xff, 0xaf, 0x21, 0xfa, 0xfb, 0xa6, 0x32, + 0xe9, 0xff, 0xf5, 0x3a, 0xea, 0xff, 0x4f, 0x3a, + 0xf7, 0xff, 0x2f, 0xa1, 0x32, 0xf9, 0xfb, 0xee, + 0x40, 0x4f, 0x3a, 0xf6, 0xff, 0x47, 0x3a, 0xf9, + 0xff, 0xd6, 0x01, 0x9f, 0x3d, 0x2f, 0xb0, 0x77, + 0xf1, 0xc2, 0x4f, 0xfb, 0xe5, 0x2a, 0xe4, 0xff, + 0x2b, 0x7c, 0xb5, 0xc2, 0x48, 0xfb, 0xe1, 0x7e, + 0xe6, 0x80, 0xc0, 0x3a, 0xf6, 0xff, 0xf6, 0x06, + 0x77, 0x3e, 0x80, 0x37, 0xc9, 0x2a, 0xe4, 0xff, + 0x29, 0x29, 0xeb, 0x21, 0xfc, 0xfb, 0x77, 0x7e, + 0x1f, 0xd2, 0x67, 0xfb, 0x7e, 0x1f, 0x7e, 0xd0, + 0xc3, 0x76, 0xfb, 0xc3, 0xe3, 0xfa, 0x1b, 0x7a, + 0xb3, 0xc2, 0x6c, 0xfb, 0x5e, 0xe5, 0x23, 0x56, + 0x3a, 0xea, 0xff, 0xee, 0x80, 0x32, 0xf9, 0xfb, + 0xee, 0xc0, 0xe3, 0x32, 0xf9, 0xfb, 0x36, 0xd0, + 0xe3, 0x72, 0xe1, 0x7b, 0x37, 0xc9, 0x11, 0x00, + 0x00, 0x21, 0xfa, 0xfb, 0x0e, 0x10, 0x7e, 0xa1, + 0xca, 0x9e, 0xfb, 0x7e, 0xa1, 0xc2, 0xa3, 0xfb, + 0x13, 0xe3, 0xe3, 0xe3, 0xe3, 0x7e, 0xa1, 0xca, + 0xa8, 0xfb, 0xc9, 0x79, 0xe6, 0x01, 0x2f, 0x47, + 0x21, 0xeb, 0xff, 0x5e, 0x16, 0x00, 0x23, 0x7e, + 0xab, 0xf5, 0x23, 0x23, 0x19, 0x19, 0x7e, 0xf6, + 0x01, 0xa0, 0x77, 0xf1, 0xc0, 0x7e, 0x32, 0xf6, + 0xff, 0xc9, 0x21, 0x00, 0x00, 0x2b, 0x7c, 0xb5, + 0xe3, 0xe3, 0xc2, 0xd5, 0xfb, 0xc9, 0xe5, 0x21, + 0xe2, 0xfb, 0xe9, 0xe1, 0xc9, 0x79, 0xe6, 0x01, + 0x17, 0x17, 0x17, 0x17, 0x32, 0xf7, 0xff, 0xc9, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x00, 0xf8, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + +/* PROM selection (default E000) */ +uint8 *dj2d_prom = dj2d_prom_e000; + +/* +** Western Digital WD1791 Registers and Interface Controls +*/ +typedef struct { + uint8 track; /* Track Register */ + uint8 sector; /* Sector Register */ + uint8 command; /* Command Register */ + uint8 status; /* Status Register */ + uint8 data; /* Data Register */ + uint8 intrq; /* Interrupt Request */ + uint8 drq; /* Data Request */ + uint8 index; /* Index */ + int8 stepDir; /* Last Step Direction */ + uint32 dataCount; /* Number of data bytes transferred from controller for current sector/address */ + uint32 trkCount; /* Number of data bytes transferred from controller for current track */ + uint8 readActive; /* Read Active */ + uint8 readTrkActive; /* Read Track Active */ + uint8 writeActive; /* Write Active */ + uint8 writeTrkActive; /* Write Track Active */ + uint8 idAddrMrk; /* ID Addr Mark Flag */ + uint8 dataAddrMrk; /* Data Addr Mark Flag */ + uint8 addrActive; /* Address Active */ +} WD1791_REG; + +/* +** Disk Jockey 2D Registers +*/ +typedef struct { + uint8 uart_rxd; /* UART rx data register */ + uint8 uart_txd; /* UART tx data register */ + uint8 uart_txp; /* UART tx data pending */ + uint8 uart_status; /* UART status register */ + uint16 uart_baud; /* UART baud rate */ + uint8 status; /* Disk Jockey status register */ + uint8 control; /* Disk Jockey control register */ + uint8 function; /* Disk Jockey function register */ +} DJ2D_REG; + +#define WD1791_STAT_NOTREADY 0x80 +#define WD1791_STAT_WRITEPROT 0x40 +#define WD1791_STAT_RTYPEMSB 0x40 +#define WD1791_STAT_HEADLOAD 0x20 +#define WD1791_STAT_RTYPELSB 0x20 +#define WD1791_STAT_WRITEFAULT 0x20 +#define WD1791_STAT_SEEKERROR 0x10 +#define WD1791_STAT_NOTFOUND 0x10 +#define WD1791_STAT_CRCERROR 0x08 +#define WD1791_STAT_TRACK0 0x04 +#define WD1791_STAT_LOSTDATA 0x04 +#define WD1791_STAT_INDEX 0x02 +#define WD1791_STAT_DRQ 0x02 +#define WD1791_STAT_BUSY 0x01 + +static TMLN dj2d_tmln[] = { /* line descriptors */ + { 0 } +}; + +static TMXR dj2d_tmxr = { /* multiplexer descriptor */ + 1, /* number of terminal lines */ + 0, /* listening port (reserved) */ + 0, /* master socket (reserved) */ + dj2d_tmln, /* line descriptor array */ + NULL, /* line connection order */ + NULL /* multiplexer device (derived internally) */ +}; + +typedef struct { + uint32 io_base; /* NOT USED */ + uint32 io_size; /* NOT USED */ + uint32 mem_base; /* Memory Base Address */ + uint32 mem_size; /* Memory Address space requirement */ + uint32 prom_base; /* PROM Base Address */ + uint32 prom_size; /* PROM Address space requirement */ + int32 conn; /* Connected Status */ + TMLN *tmln; /* TMLN pointer */ + TMXR *tmxr; /* TMXR pointer */ + uint32 ticks; /* Timer ticks */ + uint32 sioticks; /* SIO Timer ticks */ + uint16 headTimeout; /* Head unload timer tick value */ + uint16 indexTimeout; /* Index timer tick value */ + uint16 busyTimeout; /* Busy timer tick value */ + uint8 promEnabled; /* PROM is enabled */ + uint8 writeProtect; /* Write Protect is enabled */ + uint8 currentDrive; /* currently selected drive */ + uint8 secsPerTrack; /* sectors per track */ + uint16 bytesPerTrack; /* bytes per track */ + uint8 headLoaded[DJ2D_MAX_DRIVES]; /* Head Loaded */ + uint8 format[DJ2D_MAX_DRIVES]; /* Attached disk format */ + uint16 sectorLen[DJ2D_MAX_DRIVES]; /* Attached disk sector length */ + uint8 side[DJ2D_MAX_DRIVES]; /* side 0 or side 1 */ + WD1791_REG WD1791; /* WD1791 Registers and Data */ + DJ2D_REG DJ2D; /* DJ2D Registers and Data */ + UNIT *uptr[DJ2D_UNITS]; +} DJ2D_INFO; + +static DJ2D_INFO dj2d_info_data = { + 0, 0, + DJ2D_MEM_BASE, DJ2D_MEM_SIZE, + DJ2D_PROM_BASE, DJ2D_PROM_SIZE, + 0, dj2d_tmln, &dj2d_tmxr, + 0, 0, 0 +}; + +static DJ2D_INFO *dj2d_info = &dj2d_info_data; + +static uint8 sdata[1024]; /* Sector data buffer */ + +/* DJ2D Registers */ +#define DJ2D_REG_BASE (DJ2D_PROM_BASE + 0x03f8) +#define DJ2D_REG_UART_DATA 0x00 /* Register 0 */ +#define DJ2D_REG_UART_STATUS 0x01 /* Register 1 */ +#define DJ2D_REG_2D_CONTROL 0x01 /* Register 1 */ +#define DJ2D_REG_2D_FUNCTION 0x02 /* Register 2 */ +#define DJ2D_REG_2D_STATUS 0x02 /* Register 2 */ +#define DJ2D_REG_1791_STATUS 0x04 /* Register 4 */ +#define DJ2D_REG_1791_COMMAND 0x04 /* Register 4 */ +#define DJ2D_REG_1791_TRACK 0x05 /* Register 5 */ +#define DJ2D_REG_1791_SECTOR 0x06 /* Register 6 */ +#define DJ2D_REG_1791_DATA 0x07 /* Register 7 */ + +#define DJ2D_STAT_HEAD 0x01 /* HEAD */ +#define DJ2D_STAT_DATARQ 0x02 /* DATARQ */ +#define DJ2D_STAT_INTRQ 0x04 /* INTRQ */ +#define DJ2D_STAT_N2SIDED 0x08 /* NOT 2 SIDED */ +#define DJ2D_STAT_INDEX 0x10 /* INDEX */ +#define DJ2D_STAT_READY 0x80 /* READY */ + +#define DJ2D_STAT_PE 0x01 /* Parity Error */ +#define DJ2D_STAT_OE 0x02 /* Overrun Error */ +#define DJ2D_STAT_DR 0x04 /* Data Ready */ +#define DJ2D_STAT_TBRE 0x08 /* TBRE */ +#define DJ2D_STAT_FE 0x10 /* Framing Error */ + +#define DJ2D_CTRL_DSEL 0x0f /* Drive Select Mask */ +#define DJ2D_CTRL_SIDE0 0x10 /* Side 0 Select */ +#define DJ2D_CTRL_INTDSBL 0x20 /* Interrupt Disable */ +#define DJ2D_CTRL_AENBL 0x40 /* AENBL */ +#define DJ2D_CTRL_RESET 0x80 /* Reset 1791 */ + +#define DJ2D_FUNC_SINGLE 0x01 /* Single Density */ +#define DJ2D_FUNC_HDMASK 0x06 /* Head Mask */ +#define DJ2D_FUNC_HDLOAD 0x00 /* Head Loaded */ +#define DJ2D_FUNC_HDUNLD 0x06 /* Head Unloaded */ +#define DJ2D_FUNC_LEDOFF 0x08 /* LED Off */ +#define DJ2D_FUNC_VCOFF 0x20 /* VCO Off */ + +/* DJ2D Commands */ +#define WD1791_CMD_RESTORE 0x00 +#define WD1791_CMD_SEEK 0x10 +#define WD1791_CMD_STEP 0x20 +#define WD1791_CMD_STEPU (WD1791_CMD_STEP | WD1791_FLAG_U) +#define WD1791_CMD_STEPIN 0x40 +#define WD1791_CMD_STEPINU (WD1791_CMD_STEPIN | WD1791_FLAG_U) +#define WD1791_CMD_STEPOUT 0x60 +#define WD1791_CMD_STEPOUTU (WD1791_CMD_STEPOUT | WD1791_FLAG_U) +#define WD1791_CMD_READ 0x80 +#define WD1791_CMD_READM (WD1791_CMD_READ | WD1791_FLAG_M) +#define WD1791_CMD_WRITE 0xA0 +#define WD1791_CMD_WRITEM (WD1791_CMD_WRITE | WD1791_FLAG_M) +#define WD1791_CMD_READ_ADDRESS 0xC0 +#define WD1791_CMD_READ_TRACK 0xE0 +#define WD1791_CMD_WRITE_TRACK 0xF0 +#define WD1791_CMD_FORCE_INTR 0xD0 + +#define WD1791_FLAG_V 0x04 +#define WD1791_FLAG_H 0x08 +#define WD1791_FLAG_U 0x10 +#define WD1791_FLAG_M 0x10 +#define WD1791_FLAG_B 0x08 +#define WD1791_FLAG_S 0x01 +#define WD1791_FLAG_E 0x04 + +#define WD1791_FLAG_A1A0_FB 0x00 +#define WD1791_FLAG_A1A0_FA 0x01 +#define WD1791_FLAG_A1A0_F9 0x02 +#define WD1791_FLAG_A1A0_F8 0x03 + +#define WD1791_FLAG_I0 0x01 +#define WD1791_FLAG_I1 0x02 +#define WD1791_FLAG_I2 0x04 +#define WD1791_FLAG_I3 0x08 + +#define WD1791_FLAG_R1R0_6MS 0x00 +#define WD1791_FLAG_R1R0_10ms 0x02 +#define WD1791_FLAG_R1R0_20ms 0x03 + +#define WD1791_ADDR_TRACK 0x00 +#define WD1791_ADDR_ZEROS 0x01 +#define WD1791_ADDR_SECTOR 0x02 +#define WD1791_ADDR_LENGTH 0x03 +#define WD1791_ADDR_CRC1 0x04 +#define WD1791_ADDR_CRC2 0x05 + +/* Local function prototypes */ +static t_stat dj2d_reset(DEVICE *dj2d_dev); +static t_stat dj2d_svc(UNIT *uptr); +static t_stat dj2d_sio_svc(UNIT *uptr); +static t_stat dj2d_attach(UNIT *uptr, CONST char *cptr); +static t_stat dj2d_detach(UNIT *uptr); +static t_stat dj2d_boot(int32 unitno, DEVICE *dptr); +static t_stat dj2d_set_prombase(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +static t_stat dj2d_show_prombase(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat dj2d_set_prom(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +static t_stat dj2d_show_prom(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat dj2d_set_baud(UNIT *uptr, int32 value, const char *cptr, void *desc); +static t_stat dj2d_show_baud(FILE *st, UNIT *uptr, int32 value, const void *desc); +static t_stat dj2d_config_line(void); +static uint16 sector_len(uint8 drive, uint8 track); +static uint32 secs_per_track(uint8 track); +static uint32 bytes_per_track(uint8 track); +static uint32 calculate_dj2d_sec_offset(uint8 track, uint8 sector); +static void DJ2D_HeadLoad(UNIT *uptr, WD1791_REG *pWD1791, uint8 load); +static uint8 DJ2D_Read(uint32 Addr); +static uint8 DJ2D_Write(uint32 Addr, int32 data); +static const char * DJ2D_CommandString(uint8 command); +static uint8 DJ2D_Command(UNIT *uptr, WD1791_REG *pWD1791, int32 data); +static uint32 DJ2D_ReadSector(UNIT *uptr, uint8 track, uint8 sector, uint8 *buffer); +static uint32 DJ2D_WriteSector(UNIT *uptr, uint8 track, uint8 sector, uint8 *buffer); +static const char* dj2d_description(DEVICE *dptr); +static void showdata(int32 isRead); + +static int32 dj2dprom(int32 Addr, int32 rw, int32 data); +static int32 dj2dmem(int32 Addr, int32 rw, int32 data); + +static UNIT dj2d_unit[DJ2D_UNITS] = { + { UDATA (dj2d_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, 0), 10000 }, + { UDATA (dj2d_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, 0), 10000 }, + { UDATA (dj2d_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, 0), 10000 }, + { UDATA (dj2d_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, 0), 10000 }, + { UDATA (dj2d_sio_svc, UNIT_ATTABLE + UNIT_DISABLE, 0), 10000 } +}; + +static REG dj2d_reg[] = { + { DRDATAD (DRIVE, dj2d_info_data.currentDrive, 8, "Current drive register"), }, + { HRDATAD (STATUS, dj2d_info_data.WD1791.status, 8, "Status register"), }, + { HRDATAD (COMMAND, dj2d_info_data.WD1791.command, 8, "Command register"), }, + { HRDATAD (DATA, dj2d_info_data.WD1791.data, 8, "Data register"), }, + { DRDATAD (TRACK, dj2d_info_data.WD1791.track, 8, "Track register"), }, + { DRDATAD (SECTOR, dj2d_info_data.WD1791.sector, 8, "Sector register"), }, + { DRDATAD (SPT, dj2d_info_data.secsPerTrack, 8, "Sectors per track register"), }, + { DRDATAD (BPT, dj2d_info_data.bytesPerTrack, 16, "Bytes per track register"), }, + { DRDATAD (STEPDIR, dj2d_info_data.WD1791.stepDir, 8, "Last step direction register"), }, + { DRDATAD (SECCNT, dj2d_info_data.WD1791.dataCount, 16, "Sector byte count register"), }, + { DRDATAD (TRKCNT, dj2d_info_data.WD1791.trkCount, 16, "Track byte count register"), }, + { FLDATAD (RDACT, dj2d_info_data.WD1791.readActive, 0, "Read sector active status bit"), }, + { FLDATAD (WRACT, dj2d_info_data.WD1791.writeActive, 0, "Write sector active status bit"), }, + { FLDATAD (RDTACT, dj2d_info_data.WD1791.readTrkActive, 0, "Read track active status bit"), }, + { FLDATAD (WRTACT, dj2d_info_data.WD1791.writeTrkActive, 0, "Write track active status bit"), }, + { FLDATAD (INTRQ, dj2d_info_data.WD1791.intrq, 0, "INTRQ status bit"), }, + { FLDATAD (DRQ, dj2d_info_data.WD1791.drq, 0, "DRQ status bit"), }, + { FLDATAD (PROM, dj2d_info_data.promEnabled, 0, "PROM enabled bit"), }, + { FLDATAD (WRTPROT, dj2d_info_data.writeProtect, 0, "Write protect enabled bit"), }, + { DRDATAD (TICKS, dj2d_info_data.ticks, 32, "Timer ticks"), }, + { DRDATAD (SIOTICKS, dj2d_info_data.sioticks, 32, "SIO timer ticks"), }, + { DRDATAD (HEAD, dj2d_info_data.headTimeout, 16, "Head unload timeout"), }, + { DRDATAD (INDEX, dj2d_info_data.indexTimeout, 16, "Index timeout"), }, + { DRDATAD (BUSY, dj2d_info_data.busyTimeout, 16, "Busy timeout"), }, + { HRDATAD (DJSTAT, dj2d_info_data.DJ2D.status, 8, "DJ2D status register"), }, + { HRDATAD (DJCTRL, dj2d_info_data.DJ2D.control, 8, "DJ2D control register"), }, + { HRDATAD (DJFUNC, dj2d_info_data.DJ2D.function, 8, "DJ2D function register"), }, + { HRDATAD (URXD, dj2d_info_data.DJ2D.uart_rxd, 8, "UART RX data register"), }, + { HRDATAD (UTXD, dj2d_info_data.DJ2D.uart_txd, 8, "UART TX data register"), }, + { HRDATAD (UTXP, dj2d_info_data.DJ2D.uart_txp, 8, "UART TX data pending"), }, + { HRDATAD (USTAT, dj2d_info_data.DJ2D.uart_status, 8, "UART status register"), }, + { DRDATAD (BAUD, dj2d_info_data.DJ2D.uart_baud, 16, "UART baud rate"), }, + { NULL } +}; + +#define DJ2D_NAME "DISK JOCKEY 2D/B Floppy Disk Controller" +#define DJ2D_SNAME "DJ2D" + +static const char* dj2d_description(DEVICE *dptr) { + return DJ2D_NAME; +} + +#define UNIT_V_DJ2D_WPROTECT (UNIT_V_UF + 0) /* WRTENB / WRTPROT */ +#define UNIT_DJ2D_WPROTECT (1 << UNIT_V_DJ2D_WPROTECT) + +/* Terminal multiplexer library descriptors */ + +static MTAB dj2d_mod[] = { + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "PROM", "PROM={ENABLE|DISABLE}", + &dj2d_set_prom, &dj2d_show_prom, NULL, "Set/Show PROM enabled/disabled status"}, + { MTAB_XTD|MTAB_VDV, 0, "PROMBASE", "PROMBASE", + &dj2d_set_prombase, &dj2d_show_prombase, NULL, "Sets PROM base address" }, + { UNIT_DJ2D_WPROTECT, 0, "WRTENB", "WRTENB", NULL, NULL, NULL, + "Enables " DJ2D_SNAME "n for writing" }, + { UNIT_DJ2D_WPROTECT, UNIT_DJ2D_WPROTECT, "WRTPROT", "WRTPROT", NULL, NULL, NULL, + "Protects " DJ2D_SNAME "n from writing" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "BAUD", "BAUD", &dj2d_set_baud, &dj2d_show_baud, + NULL, "Set baud rate (default=19200)" }, + { 0 } +}; + +/* Debug flags */ +#define ERROR_MSG (1 << 0) +#define SEEK_MSG (1 << 1) +#define CMD_MSG (1 << 2) +#define RD_DATA_MSG (1 << 3) +#define WR_DATA_MSG (1 << 4) +#define STATUS_MSG (1 << 5) +#define RD_DATA_DETAIL_MSG (1 << 6) +#define WR_DATA_DETAIL_MSG (1 << 7) +#define VERBOSE_MSG (1 << 8) +#define DEBUG_MSG (1 << 9) + +/* Debug Flags */ +static DEBTAB dj2d_dt[] = { + { "ERROR", ERROR_MSG, "Error messages" }, + { "SEEK", SEEK_MSG, "Seek messages" }, + { "CMD", CMD_MSG, "Command messages" }, + { "READ", RD_DATA_MSG, "Read messages" }, + { "WRITE", WR_DATA_MSG, "Write messages" }, + { "STATUS", STATUS_MSG, "Status messages" }, + { "RDDETAIL", RD_DATA_DETAIL_MSG, "Read detail messages" }, + { "WRDETAIL", WR_DATA_DETAIL_MSG, "Write detail messags" }, + { "VERBOSE", VERBOSE_MSG, "Verbose messages" }, + { "DEBUG", DEBUG_MSG, "Debug messages" }, + { NULL, 0 } +}; + +DEVICE dj2d_dev = { + DJ2D_SNAME, /* name */ + dj2d_unit, /* unit */ + dj2d_reg, /* registers */ + dj2d_mod, /* modifiers */ + DJ2D_UNITS, /* # units */ + 10, /* address radix */ + 31, /* address width */ + 1, /* addr increment */ + DJ2D_UNITS, /* data radix */ + DJ2D_UNITS, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &dj2d_reset, /* reset routine */ + &dj2d_boot, /* boot routine */ + &dj2d_attach, /* attach routine */ + &dj2d_detach, /* detach routine */ + &dj2d_info_data, /* context */ + (DEV_DISABLE | DEV_DIS | DEV_DEBUG), /* flags */ + ERROR_MSG, /* debug control */ + dj2d_dt, /* debug flags */ + NULL, /* mem size routine */ + NULL, /* logical name */ + NULL, /* help */ + NULL, /* attach help */ + NULL, /* context for help */ + &dj2d_description /* description */ +}; + +/* Reset routine */ +static t_stat dj2d_reset(DEVICE *dptr) +{ + uint8 i; + DJ2D_INFO *pInfo = (DJ2D_INFO *)dptr->ctxt; + + if (dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ + sim_map_resource(pInfo->prom_base, pInfo->prom_size, RESOURCE_TYPE_MEMORY, &dj2dprom, "dj2dprom", TRUE); + sim_map_resource(pInfo->mem_base, pInfo->mem_size, RESOURCE_TYPE_MEMORY, &dj2dmem, "dj2dmem", TRUE); + } else { + if (sim_map_resource(pInfo->prom_base, pInfo->prom_size, RESOURCE_TYPE_MEMORY, &dj2dprom, "dj2dprom", FALSE) != 0) { + sim_debug(ERROR_MSG, &dj2d_dev, DJ2D_SNAME ": Error mapping PROM resource at 0x%04x\n", pInfo->prom_base); + return SCPE_ARG; + } + if (sim_map_resource(pInfo->mem_base, pInfo->mem_size, RESOURCE_TYPE_MEMORY, &dj2dmem, "dj2dmem", FALSE) != 0) { + sim_debug(ERROR_MSG, &dj2d_dev, DJ2D_SNAME ": Error mapping MEM resource at 0x%04x\n", pInfo->mem_base); + return SCPE_ARG; + } + } + + for (i = 0; i < DJ2D_UNITS; i++) { + if (dj2d_info->uptr[i] == NULL) { + dj2d_info->uptr[i] = &dj2d_dev.units[i]; + } + } + + /* Reset Registers */ + pInfo->currentDrive = 0; + pInfo->promEnabled = TRUE; + pInfo->writeProtect = FALSE; + + pInfo->DJ2D.uart_status = DJ2D_STAT_TBRE; + pInfo->DJ2D.uart_txp = FALSE; + pInfo->DJ2D.uart_baud = DJ2D_BAUD; + + pInfo->WD1791.track = 0; + pInfo->WD1791.sector = 1; + pInfo->WD1791.command = 0; + pInfo->WD1791.status = 0; + pInfo->WD1791.data = 0; + pInfo->WD1791.drq = FALSE; + pInfo->WD1791.index = FALSE; + pInfo->WD1791.intrq = FALSE; + pInfo->WD1791.stepDir = 1; + pInfo->WD1791.dataCount = 0; + pInfo->WD1791.trkCount = 0; + pInfo->WD1791.addrActive = FALSE; + pInfo->WD1791.readActive = FALSE; + pInfo->WD1791.readTrkActive = FALSE; + pInfo->WD1791.writeActive = FALSE; + pInfo->WD1791.writeTrkActive = FALSE; + pInfo->WD1791.addrActive = FALSE; + + for (i = 0; i < DJ2D_MAX_DRIVES; i++) { + dj2d_info->headLoaded[i] = FALSE; + } + + /* Start timer for unit 0 (we only need 1 timer for all drive units) */ + dj2d_info->indexTimeout = DJ2D_ROTATION_MS; + sim_activate_after(dj2d_info->uptr[0], DJ2D_TIMER * 1000); + + /* Start timer for SIO unit */ + sim_activate_after(dj2d_info->uptr[DJ2D_SIO_UNIT], 500); /* activate 500 us timer */ + + /* Disable clockFrequency if it's set */ + if (getClockFrequency()) { + setClockFrequency(0); + sim_printf(DJ2D_SNAME ": CPU CLOCK register not supported. Use THROTTLE.\n"); + } + + /* Configure the serial interface */ + dj2d_config_line(); + + sim_debug(STATUS_MSG, &dj2d_dev, DJ2D_SNAME ": reset controller.\n"); + + return SCPE_OK; +} + +static t_stat dj2d_sio_svc(UNIT *uptr) +{ + int32 c; + t_stat r; + + dj2d_info->sioticks++; + + /* Check for new incoming connection */ + if (uptr->flags & UNIT_ATT) { + if (tmxr_poll_conn(dj2d_info->tmxr) >= 0) { /* poll connection */ + + dj2d_info->conn = 1; /* set connected */ + + sim_debug(STATUS_MSG, uptr->dptr, "new connection.\n"); + } + } + + /* TX byte pending? */ + if (dj2d_info->DJ2D.uart_txp) { + if (uptr->flags & UNIT_ATT) { + r = tmxr_putc_ln(dj2d_info->tmln, dj2d_info->DJ2D.uart_txd); + } else { + r = sim_putchar(dj2d_info->DJ2D.uart_txd); + } + + dj2d_info->DJ2D.uart_txp = FALSE; + + if (r == SCPE_LOST) { + dj2d_info->conn = 0; /* Connection was lost */ + sim_debug(STATUS_MSG, uptr->dptr, "lost connection.\n"); + } + } + + /* Update TBRE */ + if (!(dj2d_info->DJ2D.uart_status & DJ2D_STAT_TBRE)) { + if (uptr->flags & UNIT_ATT) { + tmxr_poll_tx(dj2d_info->tmxr); + dj2d_info->DJ2D.uart_status |= (tmxr_txdone_ln(dj2d_info->tmln) && dj2d_info->conn) ? DJ2D_STAT_TBRE : 0; + } else { + dj2d_info->DJ2D.uart_status |= DJ2D_STAT_TBRE; + } + } + + /* Check for Data if RX buffer empty */ + if (!(dj2d_info->DJ2D.uart_status & DJ2D_STAT_DR)) { + if (uptr->flags & UNIT_ATT) { + tmxr_poll_rx(dj2d_info->tmxr); + + c = tmxr_getc_ln(dj2d_info->tmln); + } else { + c = sim_poll_kbd(); + } + + if (c & (TMXR_VALID | SCPE_KFLAG)) { + dj2d_info->DJ2D.uart_rxd = c & 0xff; + dj2d_info->DJ2D.uart_status |= DJ2D_STAT_DR; + dj2d_info->DJ2D.uart_status &= ~(DJ2D_STAT_FE | DJ2D_STAT_OE | DJ2D_STAT_PE); + } + } + + /* Restart timer */ + sim_activate_after(uptr, 500); /* reactivate 500 us timer */ + + return SCPE_OK; +} + +static t_stat dj2d_svc(UNIT *uptr) +{ + WD1791_REG *pWD1791; + + pWD1791 = &dj2d_info->WD1791; + + dj2d_info->ticks++; + + if (dj2d_info->headTimeout) { + if (!(--dj2d_info->headTimeout)) { + DJ2D_HeadLoad(uptr, pWD1791, FALSE); + } + } + + if (dj2d_info->indexTimeout) { + if (!(--dj2d_info->indexTimeout)) { + pWD1791->index = FALSE; + dj2d_info->indexTimeout = DJ2D_INDEX_TIMEOUT; + } else { + pWD1791->index = TRUE; + } + } + + if (dj2d_info->busyTimeout) { + if (!(--dj2d_info->busyTimeout)) { + pWD1791->status &= ~WD1791_STAT_BUSY; + pWD1791->drq = FALSE; + pWD1791->intrq = TRUE; + } + } + + /* Restart timer */ + sim_activate_after(uptr, DJ2D_TIMER * 1000); /* activate timer */ + + return SCPE_OK; +} + +/* +** Verify that prombase is within valid range +** before calling set_membase +*/ +static t_stat dj2d_set_prombase(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + uint32 newba; + t_stat r; + + if (cptr == NULL) + return SCPE_ARG; + + newba = get_uint (cptr, 16, 0xFFFF, &r); + if (r != SCPE_OK) + return r; + + if ((newba != 0xe000) && (newba != 0xf800)) { + sim_printf(DJ2D_SNAME ": Valid options are E000,F800\n"); + return SCPE_ARG; + } + + /* + ** Release previous memory maps + */ + sim_map_resource(dj2d_info->prom_base, dj2d_info->prom_size, RESOURCE_TYPE_MEMORY, &dj2dprom, "dj2dprom", TRUE); + sim_map_resource(dj2d_info->mem_base, dj2d_info->mem_size, RESOURCE_TYPE_MEMORY, &dj2dmem, "dj2dmem", TRUE); + + dj2d_info->prom_base = newba; + dj2d_info->mem_base = newba+DJ2D_PROM_SIZE; + + if (sim_map_resource(dj2d_info->prom_base, dj2d_info->prom_size, RESOURCE_TYPE_MEMORY, &dj2dprom, "dj2dprom", FALSE) != 0) { + sim_debug(ERROR_MSG, &dj2d_dev, DJ2D_SNAME ": Error mapping PROM resource at 0x%04x\n", dj2d_info->prom_base); + return SCPE_ARG; + } + if (sim_map_resource(dj2d_info->mem_base, dj2d_info->mem_size, RESOURCE_TYPE_MEMORY, &dj2dmem, "dj2dmem", FALSE) != 0) { + sim_debug(ERROR_MSG, &dj2d_dev, DJ2D_SNAME ": Error mapping MEM resource at 0x%04x\n", dj2d_info->mem_base); + return SCPE_ARG; + } + + if (newba == 0xe000) { + dj2d_prom = dj2d_prom_e000; + } else { + dj2d_prom = dj2d_prom_f800; + } + + return dj2d_reset(&dj2d_dev); +} + +/* Show Base Address routine */ +t_stat dj2d_show_prombase(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + DEVICE *dptr; + DJ2D_INFO *pInfo; + + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit (uptr); + if (dptr == NULL) + return SCPE_IERR; + + pInfo = (DJ2D_INFO *) dptr->ctxt; + + if(pInfo->promEnabled) { + fprintf(st, "PROM=0x%04X-0x%04X", pInfo->prom_base, pInfo->prom_base+pInfo->prom_size-9); + fprintf(st, ", REG=0x%04X-0x%04X", pInfo->prom_base+pInfo->prom_size-8, pInfo->prom_base+pInfo->prom_size-1); + fprintf(st, ", RAM=0x%04X-0x%04X", pInfo->mem_base, pInfo->mem_base+pInfo->mem_size-1); + } + + return SCPE_OK; +} + +/* Attach routine */ +static t_stat dj2d_attach(UNIT *uptr, CONST char *cptr) +{ + char header[4]; + t_stat r; + int i,f; + + /* Attaching to serial interface? */ + if (uptr == &dj2d_dev.units[DJ2D_SIO_UNIT]) { + if ((r = tmxr_attach(dj2d_info->tmxr, uptr, cptr)) == SCPE_OK) { + dj2d_info->tmln->rcve = 1; + + sim_debug(VERBOSE_MSG, uptr->dptr, "attached '%s' to serial interface.\n", cptr); + } + + return r; + } + + r = attach_unit(uptr, cptr); /* attach unit */ + if (r != SCPE_OK) { /* error? */ + sim_debug(ERROR_MSG, &dj2d_dev, DJ2D_SNAME ": ATTACH error=%d\n", r); + return r; + } + + for (i = 0; i < DJ2D_UNITS; i++) { + if (dj2d_dev.units[i].fileref == uptr->fileref) { + break; + } + } + + if (i >= DJ2D_UNITS) { + return SCPE_ARG; + } + + uptr->capac = sim_fsize(uptr->fileref); + + /* Default is 1024 byte sectors */ + dj2d_info->format[i] = FMT_1024; + dj2d_info->sectorLen[i] = dj2d_sector_len[FMT_1024]; + + for (f = 0; f < FMT_UNKNOWN; f++) { + if (uptr->capac == dj2d_image_size[f]) { + dj2d_info->format[i] = f; + dj2d_info->sectorLen[i] = dj2d_sector_len[f]; + } + } + + sim_debug(DEBUG_MSG, &dj2d_dev, DJ2D_SNAME ": ATTACH drive=%d uptr->capac=%d format=%d sectorLen=%d\n", i, uptr->capac, dj2d_info->format[i], dj2d_info->sectorLen[i]); + + /* Default for new file is DSK */ + uptr->u3 = IMAGE_TYPE_DSK; + + if (uptr->capac > 0) { + char *rtn = fgets(header, 4, uptr->fileref); + if ((rtn != NULL) && (strncmp(header, "CPT", 3) == 0)) { + sim_printf("CPT images not yet supported\n"); + uptr->u3 = IMAGE_TYPE_CPT; + dj2d_detach(uptr); + return SCPE_OPENERR; + } + } + + + sim_debug(VERBOSE_MSG, uptr->dptr, DJ2D_SNAME "%d: attached to '%s', type=%s, len=%d\n", i, cptr, + uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK", + uptr->capac); + + return SCPE_OK; +} + + +/* Detach routine */ +static t_stat dj2d_detach(UNIT *uptr) +{ + t_stat r; + int8 i; + + for (i = 0; i < DJ2D_UNITS; i++) { + if (dj2d_dev.units[i].fileref == uptr->fileref) { + break; + } + } + + if (i >= DJ2D_UNITS) { + return SCPE_ARG; + } + + r = detach_unit(uptr); /* detach unit */ + + if (r != SCPE_OK) { + return r; + } + + dj2d_dev.units[i].fileref = NULL; + + dj2d_info->WD1791.index = TRUE; + dj2d_info->indexTimeout = 0; + + sim_debug(VERBOSE_MSG, uptr->dptr, DJ2D_SNAME "%d: detached\n", i); + + return SCPE_OK; +} + +static t_stat dj2d_set_baud(UNIT *uptr, int32 value, const char *cptr, void *desc) +{ + int32 baud; + t_stat r = SCPE_ARG; + + /* Force serial interface unit */ + uptr = &dj2d_dev.units[DJ2D_SIO_UNIT]; + + if (!(uptr->flags & UNIT_ATT)) { + return SCPE_UNATT; + } + + if (cptr != NULL) { + if (sscanf(cptr, "%d", &baud)) { + switch (baud) { + case 110: + case 1200: + case 9600: + case 19200: + dj2d_info->DJ2D.uart_baud = baud; + r = dj2d_config_line(); + break; + + default: + break; + } + } + } + + return r; +} + +static t_stat dj2d_show_baud(FILE *st, UNIT *uptr, int32 value, const void *desc) +{ + if (uptr->flags & UNIT_ATT) { + fprintf(st, "Baud rate: %d", dj2d_info->DJ2D.uart_baud); + } + + return SCPE_OK; +} + +static t_stat dj2d_config_line() +{ + char config[20]; + const char *fmt; + t_stat r = SCPE_IERR; + + fmt = "8N1"; + + sprintf(config, "%d-%s", dj2d_info->DJ2D.uart_baud, fmt); + + r = tmxr_set_config_line(dj2d_info->tmln, config); + + sim_debug(STATUS_MSG, &dj2d_dev, "port configuration set to '%s'.\n", config); + + return r; +} + +static t_stat dj2d_set_prom(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + if (!cptr) return SCPE_IERR; + if (!strlen(cptr)) return SCPE_ARG; + + /* this assumes that the parameter has already been upcased */ + if (!strncmp(cptr, "ENABLE", strlen(cptr))) { + sim_map_resource(dj2d_info->prom_base, dj2d_info->prom_size, + RESOURCE_TYPE_MEMORY, &dj2dprom, "dj2dprom", FALSE); + dj2d_info->promEnabled = TRUE; + } else if (!strncmp(cptr, "DISABLE", strlen(cptr))) { + dj2d_info->promEnabled = FALSE; + sim_map_resource(dj2d_info->prom_base, dj2d_info->prom_size, + RESOURCE_TYPE_MEMORY, &dj2dprom, "dj2dprom", TRUE); + } else { + return SCPE_ARG; + } + + return SCPE_OK; +} + +static t_stat dj2d_show_prom(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + if (dj2d_info->promEnabled) { + fprintf(st, "PROM"); + } else { + fprintf(st, "NOPROM"); + } + + return SCPE_OK; +} + +static t_stat dj2d_boot(int32 unitno, DEVICE *dptr) +{ + + DJ2D_INFO *pInfo = (DJ2D_INFO *)dptr->ctxt; + + sim_debug(STATUS_MSG, &dj2d_dev, DJ2D_SNAME ": Booting Controller at 0x%04x\n", pInfo->prom_base); + + *((int32 *) sim_PC->loc) = pInfo->prom_base; + + return SCPE_OK; +} + +static void showdata(int32 isRead) { + int32 i; + sim_debug(isRead ? RD_DATA_DETAIL_MSG : WR_DATA_DETAIL_MSG, &dj2d_dev, DJ2D_SNAME ": %s track/sector %02d/%03d:\n\t", isRead ? "Read" : "Write", dj2d_info->WD1791.track, dj2d_info->WD1791.sector); + for (i = 0; i < sector_len(dj2d_info->currentDrive, dj2d_info->WD1791.track); i++) { + sim_debug(isRead ? RD_DATA_DETAIL_MSG : WR_DATA_DETAIL_MSG, &dj2d_dev, "%02X ", sdata[i]); + if (((i+1) & 0xf) == 0) { + sim_debug(isRead ? RD_DATA_DETAIL_MSG : WR_DATA_DETAIL_MSG, &dj2d_dev, "\n\t"); + } + } + sim_debug(RD_DATA_DETAIL_MSG|WR_DATA_DETAIL_MSG, &dj2d_dev, "\n"); +} + +static uint16 sector_len(uint8 drive, uint8 track) +{ + if (track == 0) { /* Track 0 is always SD */ + return(dj2d_sector_len[FMT_SD]); + } + + return(dj2d_info->sectorLen[drive]); +} + +static uint32 secs_per_track(uint8 track) +{ + if (track == 0) { + dj2d_info->secsPerTrack = (uint8)dj2d_spt[FMT_SD]; + } else { + dj2d_info->secsPerTrack = (uint8)dj2d_spt[dj2d_info->format[dj2d_info->currentDrive]]; + } + + return dj2d_info->secsPerTrack; +} + +static uint32 bytes_per_track(uint8 track) +{ + int8 format; + + format = dj2d_info->format[dj2d_info->currentDrive]; + + if (track == 0) { + dj2d_info->bytesPerTrack = dj2d_track_len[FMT_SD]; + } else { + dj2d_info->bytesPerTrack = dj2d_track_len[format]; + } + + return dj2d_info->bytesPerTrack; +} + +static uint32 calculate_dj2d_sec_offset(uint8 track, uint8 sector) +{ + uint32 offset; + uint8 ds; + uint8 format; + + ds = dj2d_info->side[dj2d_info->currentDrive]; + format = dj2d_info->format[dj2d_info->currentDrive]; + + /* + ** Side 0: tracks 0-76 + ** Side 1: tracks 77-153 + */ + if (ds) { + track += 77; + } + + /* + ** Calculate track offset + */ + if (track == 0) { /* Track 0 is always SD */ + offset = 0; + format = FMT_SD; + } else { + offset = dj2d_spt[FMT_SD] * dj2d_sector_len[FMT_SD]; /* Track 0 / Side 0 always SD */ + offset += (track-1) * dj2d_spt[format] * dj2d_sector_len[format]; /* Track 1-153 */ + } + + /* + ** Add sector offset to track offset + */ + offset += (sector-1) * dj2d_sector_len[format]; + + sim_debug(DEBUG_MSG, &dj2d_dev, DJ2D_SNAME ": OFFSET=%d drive=%d side=%d format=%d track=%03d sector=%03d\r\n", offset, dj2d_info->currentDrive, ds, dj2d_info->format[dj2d_info->currentDrive], track, sector); + + return (offset); +} + +static void DJ2D_HeadLoad(UNIT *uptr, WD1791_REG *pWD1791, uint8 load) +{ + /* + ** If no disk has been attached, uptr will be NULL - return + */ + if (uptr == NULL) { + return; + } + + if (load) { + dj2d_info->headTimeout = DJ2D_HEAD_TIMEOUT; + + if (dj2d_info->headLoaded[dj2d_info->currentDrive] == FALSE) { + sim_debug(STATUS_MSG, &dj2d_dev, DJ2D_SNAME ": Drive %d head Loaded.\n", dj2d_info->currentDrive); + } + } else { + dj2d_info->headTimeout = 0; + + if (dj2d_info->headLoaded[dj2d_info->currentDrive] == TRUE) { + sim_debug(STATUS_MSG, &dj2d_dev, DJ2D_SNAME ": Drive %d head Unloaded.\n", dj2d_info->currentDrive); + } + } + + dj2d_info->headLoaded[dj2d_info->currentDrive] = load; +} + +static uint8 DJ2D_Read(uint32 Addr) +{ + uint8 cData; + uint8 driveNum; + WD1791_REG *pWD1791; + DJ2D_REG *pDJ2D; + UNIT *uptr; + + driveNum = dj2d_info->currentDrive; + uptr = dj2d_info->uptr[driveNum]; + pWD1791 = &dj2d_info->WD1791; + pDJ2D = &dj2d_info->DJ2D; + + switch(Addr & 0x07) { + case DJ2D_REG_UART_DATA: /* Read character from UART */ + if (pDJ2D->uart_status & DJ2D_STAT_DR) { + cData = ~pDJ2D->uart_rxd; /* Inverted */ + pDJ2D->uart_status &= ~DJ2D_STAT_DR; + } else { + cData = 0xff; + } + break; + + case DJ2D_REG_UART_STATUS: + cData = ~pDJ2D->uart_status; /* Inverted */ + break; + + case DJ2D_REG_2D_STATUS: + cData = (pWD1791->intrq) ? DJ2D_STAT_INTRQ : 0; + cData |= (pWD1791->drq) ? DJ2D_STAT_DATARQ : 0; + cData |= (pWD1791->index) ? DJ2D_STAT_INDEX : 0; + cData |= (dj2d_info->headLoaded[dj2d_info->currentDrive]) ? DJ2D_STAT_HEAD : 0; + cData |= (pWD1791->status & WD1791_STAT_NOTREADY) ? 0 : DJ2D_STAT_READY; + cData |= DJ2D_STAT_N2SIDED; + pDJ2D->status = cData; + break; + + case DJ2D_REG_1791_STATUS: + cData = pWD1791->status; + break; + + case DJ2D_REG_1791_TRACK: + cData = pWD1791->track; + break; + + case DJ2D_REG_1791_DATA: + /* + ** If a READ operation is currently active, get the next byte + */ + if (pWD1791->readActive) { + /* Store byte in DATA register */ + pWD1791->data = sdata[pWD1791->dataCount++]; + + /* If we reached the end of the sector, terminate command and set INTRQ */ + if (pWD1791->dataCount == sector_len(driveNum, pWD1791->track)) { + pWD1791->readActive = FALSE; + pWD1791->dataCount = 0; + pWD1791->status = 0x00; + pWD1791->drq = FALSE; + pWD1791->intrq = TRUE; + } + + DJ2D_HeadLoad(uptr, pWD1791, TRUE); + } else if (pWD1791->readTrkActive) { + /* If we reached the end of the track data, terminate command and set INTRQ */ + if (pWD1791->trkCount == bytes_per_track(pWD1791->track)) { + pWD1791->readTrkActive = FALSE; + pWD1791->status = 0x00; + pWD1791->drq = FALSE; + pWD1791->intrq = TRUE; + } else { + pWD1791->trkCount++; + } + + DJ2D_HeadLoad(uptr, pWD1791, TRUE); + } else if (pWD1791->addrActive) { + /* Store byte in DATA register */ + pWD1791->data = sdata[pWD1791->dataCount++]; + + /* If we reached the end of the address data, terminate command and set INTRQ */ + if (pWD1791->dataCount > WD1791_ADDR_CRC2) { + pWD1791->addrActive = FALSE; + pWD1791->status = 0x00; + pWD1791->drq = FALSE; + pWD1791->intrq = TRUE; + } + + DJ2D_HeadLoad(uptr, pWD1791, TRUE); + } + + cData = pWD1791->data; + break; + + case DJ2D_REG_1791_SECTOR: + cData = pWD1791->sector; + break; + + default: + sim_debug(ERROR_MSG, &dj2d_dev, DJ2D_SNAME ": READ REG Invalid I/O Address %02x (%02x)\n", Addr & 0xFF, Addr & 0x07); + cData = 0xff; + break; + } + + sim_debug(DEBUG_MSG, &dj2d_dev, DJ2D_SNAME ": READ REG currentDrive=%d format=%d track=%02d sector=%02d data=%02x status=%02x\n", dj2d_info->currentDrive, dj2d_info->format[dj2d_info->currentDrive], pWD1791->track, pWD1791->sector, pWD1791->data, pWD1791->status); + + return (cData); +} + +static uint8 DJ2D_Write(uint32 Addr, int32 Data) +{ + uint8 cData; + uint8 driveNum; + int32 rtn; + UNIT *uptr; + WD1791_REG *pWD1791; + DJ2D_REG *pDJ2D; + + Data &= 0xff; + + sim_debug(CMD_MSG, &dj2d_dev, DJ2D_SNAME ": OUT %04X Data %02X\n", Addr, Data); + + cData = 0; + driveNum = dj2d_info->currentDrive; + uptr = dj2d_info->uptr[driveNum]; + pWD1791 = &dj2d_info->WD1791; + pDJ2D = &dj2d_info->DJ2D; + + switch(Addr & 0x07) { + case DJ2D_REG_UART_DATA: + pDJ2D->uart_txd = ~Data; /* Character is inverted */ + pDJ2D->uart_txp = TRUE; + pDJ2D->uart_status &= ~DJ2D_STAT_TBRE; + break; + + case DJ2D_REG_1791_COMMAND: + cData = DJ2D_Command(uptr, pWD1791, Data); + break; + + case DJ2D_REG_2D_FUNCTION: + pDJ2D->function = Data; + + switch (Data & DJ2D_FUNC_HDMASK) { + case DJ2D_FUNC_HDLOAD: + DJ2D_HeadLoad(uptr, pWD1791, TRUE); + break; + + case DJ2D_FUNC_HDUNLD: + DJ2D_HeadLoad(uptr, pWD1791, FALSE); + break; + + default: + break; + } + + break; + + case DJ2D_REG_1791_DATA: + pWD1791->data = Data; /* Store byte in DATA register */ + + if (pWD1791->writeActive) { + + /* Store DATA register in Sector Buffer */ + sdata[pWD1791->dataCount++] = pWD1791->data; + + /* If we reached the end of the sector, write sector, terminate command and set INTRQ */ + if (pWD1791->dataCount == sector_len(driveNum, pWD1791->track)) { + pWD1791->status = 0x00; /* Clear Status Bits */ + + rtn = DJ2D_WriteSector(uptr, pWD1791->track, pWD1791->sector, sdata); + + showdata(FALSE); + + if (rtn != sector_len(driveNum, pWD1791->track)) { + sim_debug(ERROR_MSG, &dj2d_dev, DJ2D_SNAME ": sim_fwrite errno=%d\n", errno); + + pWD1791->status |= WD1791_STAT_WRITEFAULT; + } + pWD1791->writeActive = FALSE; + pWD1791->dataCount = 0; + pWD1791->drq = FALSE; + pWD1791->intrq = TRUE; + } + + DJ2D_HeadLoad(uptr, pWD1791, TRUE); + } else if (pWD1791->writeTrkActive) { + + if (pWD1791->idAddrMrk) { + if (++pWD1791->dataCount == 4) { /* Sector Len */ + dj2d_info->sectorLen[dj2d_info->currentDrive] = dj2d_sector_len[pWD1791->data]; + dj2d_info->format[dj2d_info->currentDrive] = pWD1791->data; + pWD1791->idAddrMrk = 0; + pWD1791->dataCount = 0; + } + } else if (pWD1791->dataAddrMrk) { + /* Store DATA register in Sector Buffer */ + sdata[pWD1791->dataCount++] = pWD1791->data; + + /* If we reached the end of the sector, write sector */ + if (pWD1791->dataCount == sector_len(driveNum, pWD1791->track)) { + pWD1791->status &= ~WD1791_STAT_WRITEFAULT; /* Clear Status Bit */ + + rtn = DJ2D_WriteSector(uptr, pWD1791->track, pWD1791->sector, sdata); + + if (rtn != sector_len(driveNum, pWD1791->track)) { + pWD1791->status |= WD1791_STAT_WRITEFAULT; + sim_debug(ERROR_MSG, &dj2d_dev, DJ2D_SNAME ": WRITE ERROR could not write track %03d sector %03d\n", pWD1791->track, pWD1791->sector); + } + + sim_debug(DEBUG_MSG, &dj2d_dev, DJ2D_SNAME ": WRITE TRACK drive=%d track=%03d sector=%03d trkcount=%d datacount=%d data=%02X status=%02X\n", driveNum, pWD1791->track, pWD1791->sector, pWD1791->trkCount, pWD1791->dataCount, pWD1791->data, pWD1791->status); + + pWD1791->dataCount = 0; + pWD1791->idAddrMrk = 0; + pWD1791->dataAddrMrk = 0; + + if (pWD1791->sector < secs_per_track(pWD1791->track)) { + pWD1791->sector++; + } + } + } else if (pWD1791->data == 0xFE) { + pWD1791->idAddrMrk = 1; + } else if (pWD1791->data == 0xFB) { + pWD1791->dataAddrMrk = 1; + } + + /* + ** Increment number for bytes written to track + */ + pWD1791->trkCount++; + + if (pWD1791->trkCount == bytes_per_track(pWD1791->track)) { + pWD1791->status = 0x00; /* Clear Status Bits */ + pWD1791->drq = FALSE; + pWD1791->intrq = TRUE; + pWD1791->writeTrkActive = FALSE; + + /* + ** Last track, truncate file size in case it shrank + */ + if (pWD1791->track == 76) { + sim_set_fsize(uptr->fileref, (t_addr)sim_ftell(uptr->fileref)); + } + sim_debug(WR_DATA_MSG, &dj2d_dev, DJ2D_SNAME ": WRITE TRACK COMPLETE track=%03d sector=%03d trkcount=%d datacount=%d data=%02X status=%02X\n", pWD1791->track, pWD1791->sector, pWD1791->trkCount, pWD1791->dataCount, pWD1791->data, pWD1791->status); + } + + DJ2D_HeadLoad(uptr, pWD1791, TRUE); + } + + break; + + case DJ2D_REG_1791_TRACK: + pWD1791->track = Data; + break; + + case DJ2D_REG_1791_SECTOR: + pWD1791->sector = Data; + break; + + case DJ2D_REG_2D_CONTROL: + pDJ2D->control = Data & 0xff; + + /* Drive Select */ + switch (~Data & DJ2D_CTRL_DSEL) { + case 0x01: + cData = 0; + break; + case 0x02: + cData = 1; + break; + case 0x04: + cData = 2; + break; + case 0x08: + cData = 3; + break; + } + + /* Side */ + dj2d_info->side[cData] = (Data & DJ2D_CTRL_SIDE0) == 0x00; + + if (dj2d_info->currentDrive != cData) { + sim_debug(STATUS_MSG, &dj2d_dev, DJ2D_SNAME ": Current drive now %d side %d\n", cData, dj2d_info->side[cData]); + } + + dj2d_info->currentDrive = cData; + + break; + + default: + sim_debug(ERROR_MSG, &dj2d_dev, DJ2D_SNAME ": WRITE Invalid I/O Address %02x (%02x)\n", Addr & 0xFF, Addr & 0x07); + cData = 0xff; + break; + } + + sim_debug(DEBUG_MSG, &dj2d_dev, DJ2D_SNAME ": WRITE REG currentDrive=%d format=%d track=%02d sector=%02d data=%02x status=%02x\n", dj2d_info->currentDrive, dj2d_info->format[dj2d_info->currentDrive], pWD1791->track, pWD1791->sector, pWD1791->data, pWD1791->status); + + return(cData); +} + +static uint32 DJ2D_ReadSector(UNIT *uptr, uint8 track, uint8 sector, uint8 *buffer) +{ + uint32 sec_offset; + uint32 rtn = 0; + uint32 len; + + if (uptr->fileref == NULL) { + sim_debug(ERROR_MSG, &dj2d_dev, DJ2D_SNAME ": READSEC uptr.fileref is NULL!\n"); + return 0; + } + + sec_offset = calculate_dj2d_sec_offset(track, sector); + + len = sector_len(dj2d_info->currentDrive, track); + + sim_debug(RD_DATA_MSG, &dj2d_dev, DJ2D_SNAME ": READSEC track %03d sector %03d at offset %04X len %d\n", track, sector, sec_offset, len); + + if (sim_fseek(uptr->fileref, sec_offset, SEEK_SET) != 0) { + sim_debug(ERROR_MSG, &dj2d_dev, DJ2D_SNAME ": READSEC sim_fseek error.\n"); + return 0; + } + + rtn = sim_fread(buffer, 1, len, uptr->fileref); + + return rtn; +} + +static uint32 DJ2D_WriteSector(UNIT *uptr, uint8 track, uint8 sector, uint8 *buffer) +{ + uint32 sec_offset; + uint32 len; + uint32 rtn = 0; + + if (uptr->fileref == NULL) { + sim_debug(ERROR_MSG, &dj2d_dev, DJ2D_SNAME ": READSEC uptr.fileref is NULL!\n"); + return 0; + } + + sec_offset = calculate_dj2d_sec_offset(track, sector); + + len = sector_len(dj2d_info->currentDrive, track); + + sim_debug(WR_DATA_MSG, &dj2d_dev, DJ2D_SNAME ": WRITESEC track %03d sector %03d at offset %04X len %d\n", track, sector, sec_offset, len); + + if (sim_fseek(uptr->fileref, sec_offset, SEEK_SET) != 0) { + sim_debug(ERROR_MSG, &dj2d_dev, DJ2D_SNAME ": WRITESEC sim_fseek error.\n"); + return 0; + } + + rtn = sim_fwrite(buffer, 1, len, uptr->fileref); + + return rtn; +} + +static const char * DJ2D_CommandString(uint8 command) +{ + switch (command & 0xf0) { + case WD1791_CMD_RESTORE: + return "RESTORE"; + + case WD1791_CMD_SEEK: + return "SEEK"; + + case WD1791_CMD_STEP: + return "STEP"; + + case WD1791_CMD_STEPU: + return "STEP U"; + + case WD1791_CMD_STEPIN: + return "STEP IN"; + + case WD1791_CMD_STEPINU: + return "STEP IN U"; + + case WD1791_CMD_STEPOUT: + return "STEP OUT"; + + case WD1791_CMD_STEPOUTU: + return "STEP OUT U"; + + case WD1791_CMD_READ: + return "READ"; + + case WD1791_CMD_WRITE: + return "WRITE"; + + case WD1791_CMD_WRITEM: + return "WRITE M"; + + case WD1791_CMD_READ_ADDRESS: + return "READ ADDRESS"; + + case WD1791_CMD_READ_TRACK: + return "READ TRACK"; + + case WD1791_CMD_WRITE_TRACK: + return "WRITE TRACK"; + + case WD1791_CMD_FORCE_INTR: + return "FORCE INTR"; + + default: + break; + } + + return "UNRECOGNIZED COMMAND"; +} + +static uint8 DJ2D_Command(UNIT *uptr, WD1791_REG *pWD1791, int32 Data) +{ + uint8 cData; + uint8 newTrack; + uint8 statusUpdate; + int32 rtn; + + cData = 0; + statusUpdate = TRUE; + + if (uptr == NULL) { + return cData; + } + + pWD1791->command = Data; + + /* + ** Type II-IV Command + */ + if (pWD1791->command & 0x80) { + pWD1791->readActive = FALSE; + pWD1791->writeActive = FALSE; + pWD1791->readTrkActive = FALSE; + pWD1791->writeTrkActive = FALSE; + pWD1791->addrActive = FALSE; + pWD1791->dataCount = 0; + + pWD1791->status &= ~WD1791_STAT_DRQ; /* Reset DRQ */ + pWD1791->drq = FALSE; + } + + /* + ** Set BUSY for all but Force Interrupt + */ + if ((pWD1791->command & WD1791_CMD_FORCE_INTR) != WD1791_CMD_FORCE_INTR) { + pWD1791->status |= WD1791_STAT_BUSY; + dj2d_info->busyTimeout = DJ2D_BUSY_TIMEOUT; + } + + pWD1791->intrq = FALSE; + + switch(pWD1791->command & 0xf0) { + case WD1791_CMD_RESTORE: + pWD1791->track = 0; + + sim_debug(SEEK_MSG, &dj2d_dev, DJ2D_SNAME ": RESTORE track=%03d\n", pWD1791->track); + + DJ2D_HeadLoad(uptr, pWD1791, (Data & WD1791_FLAG_H) ? TRUE : FALSE); + + pWD1791->status &= ~WD1791_STAT_SEEKERROR; + pWD1791->status &= ~WD1791_STAT_DRQ; + pWD1791->drq = FALSE; + break; + + case WD1791_CMD_SEEK: + newTrack = pWD1791->data; + + pWD1791->status &= ~WD1791_STAT_SEEKERROR; + + if (newTrack < DJ2D_TRACKS) { + pWD1791->track = newTrack; + + DJ2D_HeadLoad(uptr, pWD1791, (Data & WD1791_FLAG_H) ? TRUE : FALSE); + + sim_debug(SEEK_MSG, &dj2d_dev, DJ2D_SNAME ": SEEK track=%03d\n", pWD1791->track); + } else { + pWD1791->status |= WD1791_STAT_SEEKERROR; + sim_debug(SEEK_MSG, &dj2d_dev, DJ2D_SNAME ": SEEK ERR track=%03d\n", newTrack); + } + + pWD1791->status &= ~WD1791_STAT_DRQ; + pWD1791->drq = FALSE; + break; + + case WD1791_CMD_STEP: + case WD1791_CMD_STEPU: + pWD1791->status &= ~WD1791_STAT_SEEKERROR; + + newTrack = pWD1791->track + pWD1791->stepDir; + + if (newTrack < DJ2D_TRACKS) { + if (Data & WD1791_FLAG_U) { + pWD1791->track = newTrack; + } + sim_debug(SEEK_MSG, &dj2d_dev, DJ2D_SNAME ": STEP track=%03d\n", pWD1791->track); + } else { + pWD1791->status |= WD1791_STAT_SEEKERROR; + sim_debug(SEEK_MSG, &dj2d_dev, DJ2D_SNAME ": STEP ERR track=%03d\n", newTrack); + } + + DJ2D_HeadLoad(uptr, pWD1791, (Data & WD1791_FLAG_H) ? TRUE : FALSE); + + pWD1791->status &= ~WD1791_STAT_DRQ; + pWD1791->drq = FALSE; + break; + + case WD1791_CMD_STEPIN: + case WD1791_CMD_STEPINU: + pWD1791->status &= ~WD1791_STAT_SEEKERROR; + + if (pWD1791->track < DJ2D_TRACKS-1) { + if (Data & WD1791_FLAG_U) { + pWD1791->track++; + } + + DJ2D_HeadLoad(uptr, pWD1791, (Data & WD1791_FLAG_H) ? TRUE : FALSE); + + sim_debug(SEEK_MSG, &dj2d_dev, DJ2D_SNAME ": STEPIN track=%03d\n", pWD1791->track); + } else { + pWD1791->status |= WD1791_STAT_SEEKERROR; + sim_debug(SEEK_MSG, &dj2d_dev, DJ2D_SNAME ": STEPIN ERR track=%03d\n", pWD1791->track+1); + } + + pWD1791->stepDir = 1; + pWD1791->status &= ~WD1791_STAT_DRQ; + pWD1791->drq = FALSE; + break; + + case WD1791_CMD_STEPOUT: + case WD1791_CMD_STEPOUTU: + pWD1791->status &= ~WD1791_STAT_SEEKERROR; + + if (pWD1791->track > 0) { + if (Data & WD1791_FLAG_U) { + pWD1791->track--; + } + + DJ2D_HeadLoad(uptr, pWD1791, (Data & WD1791_FLAG_H) ? TRUE : FALSE); + + sim_debug(SEEK_MSG, &dj2d_dev, DJ2D_SNAME ": STEPOUT track=%03d\n", pWD1791->track); + } else { + pWD1791->status |= WD1791_STAT_SEEKERROR; + sim_debug(SEEK_MSG, &dj2d_dev, DJ2D_SNAME ": STEPOUT ERR track=%03d\n", pWD1791->track-1); + } + + pWD1791->stepDir = -1; + pWD1791->status &= ~WD1791_STAT_DRQ; + pWD1791->drq = FALSE; + break; + + case WD1791_CMD_READ: + + if ((uptr == NULL) || (uptr->fileref == NULL)) { + sim_debug(STATUS_MSG, &dj2d_dev, DJ2D_SNAME ": " ADDRESS_FORMAT + " Drive: %d not attached - read ignored.\n", + PCX, dj2d_info->currentDrive); + + return cData; + } + + rtn = DJ2D_ReadSector(uptr, pWD1791->track, pWD1791->sector, sdata); + + if (rtn == sector_len(dj2d_info->currentDrive, pWD1791->track)) { + pWD1791->readActive = TRUE; + pWD1791->drq = TRUE; + + dj2d_info->busyTimeout = 0; /* BUSY not cleared until all bytes read */ + + showdata(TRUE); + } else { + sim_debug(ERROR_MSG, &dj2d_dev, DJ2D_SNAME ": sim_fread errno=%d rtn=%d len=%d\n", errno, rtn, sector_len(dj2d_info->currentDrive, pWD1791->track)); + + pWD1791->status |= WD1791_STAT_NOTFOUND; + pWD1791->intrq = TRUE; + } + + break; + + case WD1791_CMD_WRITE: + /* + ** If no disk in drive, return + */ + if ((uptr == NULL) || (uptr->fileref == NULL)) { + sim_debug(STATUS_MSG, &dj2d_dev, DJ2D_SNAME ": " ADDRESS_FORMAT + " Drive: %d not attached - write ignored.\n", + PCX, dj2d_info->currentDrive); + } + + if ((uptr->flags & UNIT_DJ2D_WPROTECT) || dj2d_info->writeProtect) { + sim_debug(VERBOSE_MSG, &dj2d_dev, DJ2D_SNAME ": Disk write protected. uptr->flags=%04x writeProtect=%04x\n", uptr->flags & UNIT_DJ2D_WPROTECT, dj2d_info->writeProtect); + pWD1791->intrq = TRUE; + } else { + dj2d_info->busyTimeout = 0; /* BUSY not cleared until all bytes written */ + + pWD1791->writeActive = TRUE; + pWD1791->dataCount = 0; + pWD1791->drq = TRUE; + } + + break; + + case WD1791_CMD_READ_ADDRESS: + sdata[WD1791_ADDR_TRACK] = pWD1791->track; + sdata[WD1791_ADDR_ZEROS] = 0; + sdata[WD1791_ADDR_SECTOR] = pWD1791->sector; + sdata[WD1791_ADDR_LENGTH] = (pWD1791->track) ? dj2d_info->format[dj2d_info->currentDrive] : 0; + sdata[WD1791_ADDR_CRC1] = 0; + sdata[WD1791_ADDR_CRC2] = 0; + + pWD1791->addrActive = TRUE; + pWD1791->drq = TRUE; + + break; + + case WD1791_CMD_READ_TRACK: + dj2d_info->busyTimeout = 0; /* BUSY not cleared until all bytes read */ + pWD1791->readTrkActive = TRUE; + pWD1791->trkCount = 0; + pWD1791->dataCount = 0; + pWD1791->sector = 1; + pWD1791->drq = TRUE; + break; + + case WD1791_CMD_WRITE_TRACK: + if ((uptr->flags & UNIT_DJ2D_WPROTECT) || dj2d_info->writeProtect) { + sim_debug(DEBUG_MSG, &dj2d_dev, DJ2D_SNAME ": Disk write protected. uptr->flags=%04x writeProtect=%04x\n", uptr->flags & UNIT_DJ2D_WPROTECT, dj2d_info->writeProtect); + pWD1791->intrq = TRUE; + } else { + dj2d_info->busyTimeout = 0; /* BUSY not cleared until all bytes written */ + pWD1791->writeTrkActive = TRUE; + pWD1791->trkCount = 0; + pWD1791->dataCount = 0; + pWD1791->sector = 1; + pWD1791->idAddrMrk = 0; + pWD1791->dataAddrMrk = 0; + pWD1791->drq = TRUE; + } + break; + + case WD1791_CMD_FORCE_INTR: + if (pWD1791->status & WD1791_STAT_BUSY) { + pWD1791->status &= ~WD1791_STAT_BUSY; + dj2d_info->busyTimeout = 0; + statusUpdate = FALSE; + } + + /* Reset Status */ + pWD1791->dataCount = 0; + pWD1791->trkCount = 0; + pWD1791->readActive = FALSE; + pWD1791->readTrkActive = FALSE; + pWD1791->writeActive = FALSE; + pWD1791->writeTrkActive = FALSE; + pWD1791->addrActive = FALSE; + break; + + default: + cData = 0xFF; + sim_debug(ERROR_MSG, &dj2d_dev, DJ2D_SNAME ": UNRECOGNIZED CMD %02X\n", pWD1791->command); + pWD1791->intrq = TRUE; + break; + } + + /**************************/ + /* Update Status Register */ + /**************************/ + + /* drive not ready bit */ + pWD1791->status &= ~WD1791_STAT_NOTREADY; + pWD1791->status |= (uptr->fileref == NULL) ? WD1791_STAT_NOTREADY : 0x00; + + /* DRQ bit */ + pWD1791->status &= ~WD1791_STAT_DRQ; + pWD1791->status |= (pWD1791->drq) ? WD1791_STAT_DRQ : 0x00; + + switch(pWD1791->command & 0xf0) { + case WD1791_CMD_RESTORE: + case WD1791_CMD_SEEK: + case WD1791_CMD_STEP: + case WD1791_CMD_STEPU: + case WD1791_CMD_STEPIN: + case WD1791_CMD_STEPINU: + case WD1791_CMD_STEPOUT: + case WD1791_CMD_STEPOUTU: + case WD1791_CMD_FORCE_INTR: + if (statusUpdate) { + pWD1791->status &= ~WD1791_STAT_HEADLOAD; + pWD1791->status &= ~WD1791_STAT_WRITEPROT; + pWD1791->status &= ~WD1791_STAT_CRCERROR; + pWD1791->status &= ~WD1791_STAT_TRACK0; + pWD1791->status &= ~WD1791_STAT_INDEX; + pWD1791->status |= ((uptr->flags & UNIT_DJ2D_WPROTECT) || dj2d_info->writeProtect) ? WD1791_STAT_WRITEPROT : 0x00; + pWD1791->status |= (pWD1791->track) ? 0x00 : WD1791_STAT_TRACK0; + pWD1791->status |= (dj2d_info->headLoaded[dj2d_info->currentDrive]) ? WD1791_STAT_HEADLOAD : 0x00; + pWD1791->status |= (pWD1791->index) ? WD1791_STAT_INDEX : 0x00; + } + break; + + case WD1791_CMD_READ: + pWD1791->status &= ~WD1791_STAT_LOSTDATA; + pWD1791->status &= ~WD1791_STAT_NOTFOUND; + pWD1791->status &= ~WD1791_STAT_CRCERROR; + pWD1791->status &= ~WD1791_STAT_RTYPELSB; + break; + + case WD1791_CMD_WRITE: + pWD1791->status &= ~WD1791_STAT_WRITEPROT; + pWD1791->status &= ~WD1791_STAT_LOSTDATA; + pWD1791->status &= ~WD1791_STAT_NOTFOUND; + pWD1791->status &= ~WD1791_STAT_CRCERROR; + pWD1791->status &= ~WD1791_STAT_RTYPELSB; + pWD1791->status |= ((uptr->flags & UNIT_DJ2D_WPROTECT) || dj2d_info->writeProtect) ? WD1791_STAT_WRITEPROT : 0x00; + break; + + case WD1791_CMD_READ_ADDRESS: + pWD1791->status &= ~0x20; + pWD1791->status &= ~0x40; + pWD1791->status &= ~WD1791_STAT_LOSTDATA; + pWD1791->status &= ~WD1791_STAT_NOTFOUND; + pWD1791->status &= ~WD1791_STAT_CRCERROR; + break; + + case WD1791_CMD_READ_TRACK: + pWD1791->status &= ~0x08; + pWD1791->status &= ~0x10; + pWD1791->status &= ~0x20; + pWD1791->status &= ~0x40; + pWD1791->status &= ~WD1791_STAT_LOSTDATA; + break; + + case WD1791_CMD_WRITE_TRACK: + pWD1791->status &= ~0x08; + pWD1791->status &= ~0x10; + pWD1791->status &= ~WD1791_STAT_WRITEPROT; + pWD1791->status &= ~WD1791_STAT_LOSTDATA; + pWD1791->status |= ((uptr->flags & UNIT_DJ2D_WPROTECT) || dj2d_info->writeProtect) ? WD1791_STAT_WRITEPROT : 0x00; + break; + } + + sim_debug(CMD_MSG, &dj2d_dev, + DJ2D_SNAME ": CMD cmd=%02X (%s) drive=%d side=%d track=%03d sector=%03d status=%02X\n", + pWD1791->command, DJ2D_CommandString(pWD1791->command), dj2d_info->currentDrive, + dj2d_info->side[dj2d_info->currentDrive], + pWD1791->track, pWD1791->sector, pWD1791->status); + + return(cData); +} + +/* +** The DJ2D has 1016 bytes of PROM followed by 8 memory-mapped +** I/O registers. +*/ +static int32 dj2dprom(int32 Addr, int32 rw, int32 Data) +{ + /* + ** Check for memory-mapped I/O + */ + if ((Addr & DJ2D_REG_BASE) == DJ2D_REG_BASE) { + if (rw == DJ2D_MEM_READ) { /* Read */ + return(DJ2D_Read(Addr)); + } else { /* Write */ + return(DJ2D_Write(Addr, Data)); + } + } + + /* + ** Read from PROM + */ + if (rw == DJ2D_MEM_READ) { + return(dj2d_prom[Addr & DJ2D_PROM_MASK]); + } + + /* + ** Writes are ignored and return 0xff + */ + return 0xff; +} + +/* +** The DJ2D has 1K of RAM following the PROM +*/ +static int32 dj2dmem(int32 Addr, int32 rw, int32 Data) +{ + if (rw == DJ2D_MEM_WRITE) { + dj2d_mem[Addr & DJ2D_MEM_MASK] = Data; + } + else { + Data = dj2d_mem[Addr & DJ2D_MEM_MASK]; + } + + return Data; +} + diff --git a/Visual Studio Projects/AltairZ80.vcproj b/Visual Studio Projects/AltairZ80.vcproj index d1b279e2..5fb8f139 100644 --- a/Visual Studio Projects/AltairZ80.vcproj +++ b/Visual Studio Projects/AltairZ80.vcproj @@ -315,6 +315,10 @@ RelativePath="..\AltairZ80\s100_disk3.c" > + + diff --git a/doc/altairz80_doc.pdf b/doc/altairz80_doc.pdf index fca0573f..31a04982 100644 Binary files a/doc/altairz80_doc.pdf and b/doc/altairz80_doc.pdf differ diff --git a/makefile b/makefile index df0c0e3b..e590eb02 100644 --- a/makefile +++ b/makefile @@ -1739,6 +1739,7 @@ ALTAIR_OPT = -I ${ALTAIRD} ALTAIRZ80D = ${SIMHD}/AltairZ80 ALTAIRZ80 = ${ALTAIRZ80D}/altairz80_cpu.c ${ALTAIRZ80D}/altairz80_cpu_nommu.c \ + ${ALTAIRZ80D}/s100_dj2d.c \ ${ALTAIRZ80D}/altairz80_dsk.c ${ALTAIRZ80D}/disasm.c \ ${ALTAIRZ80D}/altairz80_sio.c ${ALTAIRZ80D}/altairz80_sys.c \ ${ALTAIRZ80D}/altairz80_hdsk.c ${ALTAIRZ80D}/altairz80_net.c \