From 5dc8898e0c222e202089b2c44a55f3d6bb9aa4f2 Mon Sep 17 00:00:00 2001 From: Patrick Linstruth Date: Fri, 22 May 2020 22:08:03 -0700 Subject: [PATCH] ALTAIRZ80: Add JADEDD Jade Double D disk controller device --- AltairZ80/altairz80_sys.c | 3 + AltairZ80/s100_jadedd.c | 1239 +++++++++++++++++++++++ Visual Studio Projects/AltairZ80.vcproj | 12 +- descrip.mms | 3 +- makefile | 1 + 5 files changed, 1253 insertions(+), 5 deletions(-) create mode 100644 AltairZ80/s100_jadedd.c diff --git a/AltairZ80/altairz80_sys.c b/AltairZ80/altairz80_sys.c index 9e8e5332..12760681 100644 --- a/AltairZ80/altairz80_sys.c +++ b/AltairZ80/altairz80_sys.c @@ -66,6 +66,7 @@ extern DEVICE switchcpu_dev; extern DEVICE adcs6_dev; extern DEVICE hdc1001_dev; +extern DEVICE jade_dev; extern DEVICE tarbell_dev; extern DEVICE cromfdc_dev; @@ -118,6 +119,8 @@ DEVICE *sim_devices[] = { &mdsa_dev, &mdsad_dev, /* Seattle Computer Products Devices */ &scp300f_dev, + /* Jade DD Devices */ + &jade_dev, /* Tarbell Devices */ &tarbell_dev, /* Vector Graphic Devices */ diff --git a/AltairZ80/s100_jadedd.c b/AltairZ80/s100_jadedd.c new file mode 100644 index 00000000..b9bf4ad6 --- /dev/null +++ b/AltairZ80/s100_jadedd.c @@ -0,0 +1,1239 @@ +/* s100_jadedd.c: Jade Double D Disk Controller + + Created by Patrick Linstruth (patrick@deltecent.com) + Based on s100_mdsa.c written by Mike Douglas + + 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. +*/ + +/* + The Jade Double Density Disk Controller "Double D" is a difficult device + to emulate in SIMH due to the device having its own Z80 processor, I/O + and memory address space. + + While the Double D is capable to loading many different operating systems, + this emulator is centered around Digital Reasearch's CP/M 2 operating + system as it was released by Jade. + + The process of booting CP/M with the DD is a bit more complicated than + other controllers with a Western Digital FD FD179x. This is because + the host is completely insulated from the FD179x. All interaction is + done on the DD by the on-board Z80 processor. + + The process of loading CP/M starts with the DDBOOT PROM on the host + system, typically at F000. The DDBOOT PROM contains the DD Boot + Module that is injected onto the DD controller in memory bank 0. The + host then resets the DD causing the on-board Z80 to execute the + uploaded code. The DDBOOT PROM waits for the DD to complete its + initialization program. + + The DD boot module loads the Disk Control Module (DCM) from track 0 + sectors 13-20 (1K) into memory bank 1. The DD boot module then jumps to + the DCM's INIT vector at 0403H. + + The first job of the DCM INIT code is move itself from memory bank 1 to + memory bank 0. The DCM, now executing from memory bank 0, loads the DD + BIOS loader from track 0 sector 2 into the sector buffer. The BIOS loader + program is then executed which reads the CP/M BIOS into memory bank 1. + The Command Block in DCM is set to indicate the BIOS module size and + the system load address. The DD then halts. + + When the DDBOOT PROM sees that the DD has halted, it checks for errors + and then moves BIOS from memory bank 1 to the address stored in the + Command Block. DDBOOT PROM then jumps to the BIOS cold start address. + + ** NOTE ** + + This emulator does not actually execute Z80 code injected on the DD. The + functionality of the code is only emulated. Changing the DD modules on the + attached disk image, such as running DCMGEN, will not change the functionality + of the DD emulator. +*/ + + +/* + The Double D is an intelligent S-100 based disk controller. It is capable + of handling up to four full size (8") or mini (5") disk drives. Provisions + have been made for double sided drives. Single and double sided drives may + be mixed. The controller is capable of single density (FM) and double + density (MFM) operation. It can be used in software polled as well as + interrupt driven environments. + + The Double D contains an on-board Z80A microprocessor with 2K of static memory. + The on-board processor runs simultaneously with and transparent to the S-100 + bus. All critic81 timing is handled on board; data transfers are fully buffered + by sector in the on-board memory. The host system (8080, 8085, Z80, or ?) need + only transfer commands and data through a block of static memory, which can be + accessed from the bus. This architecture provides a high degree of timing + independence from the host system. Also, since the disk controller program is + contained on-board in ram, this board's operational characteristics are + redefinable at any time during system operation. + + The host system communicates with the on-board processor thru the memory window. + During a system boot, the control program must be loaded thru the memory window + before the on-board processor can operate properly. It is entirely possible for + the initial control program to be a small bootstrap which then loads a larger + control program from disk. For reading and writing disk sectors, the host system + must block move sector data through the memory window. + + The memory on the DD is allocated as follows: + + +--------------------------------------+ + | | + | BANK 0 0000H-03FFH | + | | + +--------------------------------------+ + | 0000H-036FH | DCM | + +--------------------+-----------------+ + | 0370H-037FH | I/O BLOCK BEGIN | + +--------------------+-----------------+ + | 0380H-03FFH | SECTOR BUFFER | + +--------------------+-----------------+ + + +--------------------------------------+ + | | + | BANK 1 0400H-07FFH | + | | + +--------------------------------------+ + | 0000H-02FFH | | + +--------------------+-----------------+ + | 0300H-03FFH | FORMAT BUFFER | + +--------------------+-----------------+ + | 0308H | FORMAT PROGRAM | + +--------------------+-----------------+ + + NOTE: Because the 5 upper address bits are not decoded, the 2K static memory + block appears 32 times in the Z80A 64K address range. This allows internal + programs to be assembled on any 2K boundary. Also note that the address + selected for the memory window has no effect on the on-board processor or + the on-board software. + + +------------------------------------+ + | | + | I/O COMMAND BLOCK | + | 0370H-037FH | + | | + +------------------------------------+ + | 0370H | CONTROL COMMAND | + +-------------+----------------------+ + | 0371H | DRIVE NUMBER | + +-------------+----------------------+ + | 0372H | LOGICAL TRACK NUMBER | + +-------------+----------------------+ + | 0373H | SECTOR NUMBER | + +-------------+----------------------+ + | 0374H | FORMAT FLAGS | + +-------------+----------------------+ + | 0375H | EIA CHARACTER | + +-------------+----------------------+ + | 0376H | MODE SELECTS | + +-------------+----------------------+ + | 0377H | CONTROLLER STATUS | + +-------------+----------------------+ + | 0378H-0379H | LOAD ADDRESS | + +-------------+----------------------+ + | 037AH-370BH | LOAD LENGTH | + +-------------+----------------------+ + + +--------------------+ + | | + | CONTROL COMMANDS | + | | + +--------------------+ + | 00H | LOG-ON DRIVE | + +-----+--------------+ + | 01H | READ SECTOR | + +-----+--------------+ + | 02H | WRITE SECTOR | + +-----+--------------+ + | 03H | FORMAT TRACK | + +-----+--------------+ + | 04H | READ ADDRESS | + +-----+--------------+ + | 05H | LIST OUTPUT | + +-----+--------------+ + | 06H | LIST STATUS | + +-----+--------------+ + | 07H | BACKGROUND | + +-----+--------------+ + +*/ + +/* #define DBG_MSG */ + +#include "altairz80_defs.h" +#include "sim_imd.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 t_stat set_iobase(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +extern t_stat show_iobase(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), uint8 unmap); +extern int32 find_unit_index(UNIT *uptr); + +#define JADE_MAX_ADAPTERS 1 +#define JADE_MAX_DRIVES 4 +#define JADE_SECTOR_SIZE 128 +#define JADE_SPT_SD 26 +#define JADE_SPT_DD 50 +#define JADE_TRACKS 77 +#define JADE_CAPACITY (JADE_TRACKS*JADE_SPT_SD+36)*JADE_SECTOR_SIZE /* Default Jade Disk Capacity */ + +#define JADE_PROM_BASE 0xf000 +#define JADE_PROM_SIZE 1024 +#define JADE_PROM_MASK (JADE_PROM_SIZE-1) +#define JADE_MEM_SIZE 2048 +#define JADE_MEM_MASK (JADE_MEM_SIZE-1) +#define JADE_BANK_BASE 0xe000 +#define JADE_BANK_SIZE 1024 +#define JADE_BANK_MASK (JADE_BANK_SIZE-1) +#define JADE_IO_SIZE 1 +#define JADE_IO_BASE 0x43 + +#define DCM_SEC 13 /* DCM SECTOR */ + +/****************************************************** +* DRIVE TABLE AREA DEFINED : +******************************************************/ + +/*******( FLAG BIT DEFINITIONS )**********************/ + +#define DF_T1D 0x02 /* TRACK 1 DENSITY (1 = DOUBLE). */ +#define DF_DTD 0x04 /* DATA TRACKS DENSITY (1 = DD). */ +#define DF_TSD 0x08 /* TWO SIDED ( 1 = TWO SIDES). */ + +/*******( DRIVE TABLE AREA )**************************/ + +typedef struct { + uint8 spt; /* SECTORS PER TRACK */ + uint8 flg; /* SIDE AND DENSITY FLAGS */ +} drvtbl_t; + +typedef struct { + uint32 mem_base; /* Memory Base Address */ + uint32 mem_size; /* Memory Address space requirement */ + uint32 io_base; /* I/O Base Address */ + uint32 io_size; /* I/O Address Space requirement */ + uint32 prom_base; /* Memory Base Address */ + uint32 prom_size; /* Memory Address space requirement */ + uint8 pe; /* PROM enable */ + uint8 mem_bank; /* 0 or 1 */ + uint8 mem_sys; /* FALSE=OUT or TRUE=IN */ + uint8 curdrv; /* Currently selected drive */ + drvtbl_t dt[JADE_MAX_DRIVES]; + UNIT *uptr[JADE_MAX_DRIVES]; +} JADE_INFO; + +static JADE_INFO jade_info_data = { JADE_BANK_BASE, JADE_BANK_SIZE, + JADE_IO_BASE, JADE_IO_SIZE, + JADE_PROM_BASE, JADE_PROM_SIZE, + TRUE, 0, FALSE, 0, + { JADE_SPT_SD, DF_T1D, + JADE_SPT_SD, DF_T1D, + JADE_SPT_SD, DF_T1D, + JADE_SPT_SD, DF_T1D } + }; + +static JADE_INFO *jade_info = &jade_info_data; + +/* Jade DD BOOT PROM is 590 bytes and executes at F000 */ +static uint8 jade_prom[JADE_PROM_SIZE] = { + 0xc3,0x12,0xf0,0xc3,0x3a,0xf0,0xc3,0xd7,0xf0,0xc3,0xf3,0xf0,0xc3,0x10,0xf1,0xc3, + 0x2f,0xf1,0x3e,0x03,0xd3,0x10,0x3e,0x15,0xd3,0x10,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x80,0x00,0xdb,0x43,0xe6, + 0x0e,0x07,0xf6,0xe0,0x67,0x2e,0x00,0x22,0x40,0x00,0x3e,0x01,0x32,0x42,0x00,0x32, + 0x43,0x00,0x3e,0x01,0xd3,0x43,0x01,0xc8,0x00,0xeb,0x21,0x86,0xf1,0xcd,0xa4,0xf0, + 0x3e,0x80,0xd3,0x43,0xe3,0xe3,0x3a,0x42,0x00,0x47,0xdb,0x43,0xa0,0xc2,0x6a,0xf0, + 0x3e,0x01,0xd3,0x43,0x2a,0x40,0x00,0x11,0x77,0x03,0x19,0x7e,0xe6,0x80,0xc2,0xb1, + 0xf0,0x7e,0xa7,0xc2,0xc5,0xf0,0x2a,0x40,0x00,0x11,0x78,0x03,0x19,0x5e,0x23,0x56, + 0x23,0x4e,0x23,0x46,0xd5,0x3e,0x03,0xd3,0x43,0x2a,0x40,0x00,0xcd,0xa4,0xf0,0x3e, + 0x01,0xd3,0x43,0xc9,0x7e,0x23,0xeb,0x77,0x23,0xeb,0x0b,0x78,0xb1,0xc2,0xa4,0xf0, + 0xc9,0x3a,0x43,0x00,0xa7,0xca,0x52,0xf0,0xaf,0x32,0x43,0x00,0x21,0x53,0xf1,0xcd, + 0x2f,0xf1,0xc3,0x52,0xf0,0x32,0x43,0x00,0x21,0x6e,0xf1,0xcd,0x2f,0xf1,0x3a,0x43, + 0x00,0xcd,0x3b,0xf1,0x76,0x00,0x00,0xdb,0x10,0xee,0x00,0xe6,0x01,0xc8,0x3e,0xff, + 0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0xcd,0xd7,0xf0,0xca,0xf3,0xf0,0xdb,0x11,0xe6,0x7f,0xc9,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xdb,0x10,0xee,0x00,0xe6,0x02,0xca,0x10,0xf1,0x79,0xd3,0x11,0xc9,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e, + 0xfe,0x24,0xc8,0x4f,0xcd,0x10,0xf1,0x23,0xc3,0x2f,0xf1,0xf5,0x0f,0x0f,0x0f,0x0f, + 0xcd,0x44,0xf1,0xf1,0xe6,0x0f,0xfe,0x0a,0xda,0x4d,0xf1,0xc6,0x07,0xc6,0x30,0x4f, + 0xc3,0x10,0xf1,0x0d,0x0a,0x0a,0x49,0x4e,0x53,0x45,0x52,0x54,0x20,0x53,0x59,0x53, + 0x54,0x45,0x4d,0x20,0x44,0x49,0x53,0x4b,0x45,0x54,0x54,0x45,0x20,0x24,0x0d,0x0a, + 0x0a,0x44,0x44,0x42,0x4f,0x4f,0x54,0x20,0x4c,0x4f,0x41,0x44,0x20,0x45,0x52,0x52, + 0x4f,0x52,0x20,0x2d,0x20,0x24,0x31,0x00,0x04,0xdb,0x40,0x0e,0x00,0xdb,0x00,0xe6, + 0x01,0xc2,0x10,0x00,0x0e,0xff,0xcd,0x50,0x00,0x3e,0x04,0xd3,0x00,0xcd,0x50,0x00, + 0x32,0x77,0x03,0xe6,0x80,0xca,0x26,0x00,0xaf,0xc3,0xb1,0x00,0x79,0xd3,0x05,0xd3, + 0x07,0xfd,0x21,0x37,0x00,0x3e,0x18,0xa9,0xd3,0x04,0xc3,0x34,0x00,0x2e,0x4c,0xcd, + 0x50,0x00,0xe6,0x04,0xc2,0x70,0x00,0x2d,0xca,0xaf,0x00,0xdb,0x08,0x11,0x0a,0x00, + 0xcd,0xba,0x00,0xc3,0x39,0x00,0x3e,0xd0,0xa9,0xd3,0x04,0xe3,0xe3,0xe3,0xe3,0xdb, + 0x04,0xa9,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x04,0xa9,0x32, + 0x77,0x03,0xfd,0xe3,0xed,0x45,0x11,0x28,0x00,0xcd,0xba,0x00,0x11,0x00,0x04,0x21, + 0x00,0x04,0xfd,0x21,0xa5,0x00,0x3e,0x0d,0xa9,0xd3,0x06,0x3e,0x98,0xa9,0xd3,0x04, + 0xdb,0x80,0xdb,0x07,0xa9,0x77,0x23,0x1b,0x7a,0xb3,0xc2,0x8a,0x00,0xdb,0x04,0xa9, + 0xe6,0x9c,0xc2,0xaa,0x00,0xcd,0x50,0x00,0xc3,0x03,0x04,0x3e,0x02,0xc3,0xb1,0x00, + 0x3e,0x04,0xc3,0xb1,0x00,0x3e,0x01,0x32,0x76,0x03,0xaf,0xd3,0x00,0xdb,0x10,0x76, + 0x3e,0xdc,0x3d,0x00,0xc2,0xbc,0x00,0x1b,0x7a,0xb3,0xc2,0xba,0x00,0xc9 +}; + +#define JADE_STAT_HLT_MSK 0x01 +#define JADE_STAT_HALT 0x00 +#define JADE_STAT_MEM_MSK 0x0e + +#define CMD_SIN 0x01 /* Switch DD bank 0 into system */ +#define CMD_MD0 0x01 /* Select DD bank 0 */ +#define CMD_MD1 0x03 /* Select DD bank 1 */ +#define CMD_SOT 0x00 /* Switch DD mem out of system */ +#define CMD_INT 0x02 /* Isssue DD Z80A interrupt */ +#define CMD_BGN 0x80 /* Reset Z80 and execute */ + +#define DC_LOG 0x00 /* Log on diskette */ +#define DC_RDS 0x01 /* Read sector */ +#define DC_WRS 0x02 /* Write sector */ +#define DC_FMT 0x03 /* Format track */ +#define DC_ADR 0x04 /* Address */ +#define DC_LST 0x05 /* List character */ +#define DC_LCK 0x06 /* List status check */ +#define DC_IDL 0x07 /* Idle */ + +#define DD_CBT 0x0370 /* Command block (bank 0) */ +#define DD_BUF 0x0380 /* Sector buffer (bank 0) */ +#define DD_FBF 0x0300 /* Format buffer (bank 1) */ +#define DD_FPS 0x0308 /* Format program (bank 1) */ +#define DD_DPB 0x03a0 /* ID Sec DPB */ +#define DD_DDF 0x03b1 /* ID Sec flags */ + +/*******( STATUS BIT DEFINITIONS )*****************/ + +#define CS_DNR 0x80 /* DRIVE NOT READY */ +#define CS_WRP 0x40 /* WRITE PROTECTED */ +#define CS_BT5 0x20 /* NOT ASSIGNED */ +#define CS_RNF 0x10 /* RECORD NOT FOUND */ +#define CS_CRC 0x08 /* CRC ERROR */ +#define CS_LDE 0x04 /* LOST DATA ERROR */ +#define CS_HME 0x02 /* DRIVE HOME ERROR */ +#define CS_TSD 0x01 /* TWO SIDES FLAG (FORMAT) */ +#define CS_NOE 0x00 /* NO ERROR */ + + +/* +** 2 banks of 1K RAM on Jade DD +*/ +static uint8 jade_mem[JADE_MEM_SIZE]; +static uint8 *bank0=jade_mem; +static uint8 *bank1=jade_mem + JADE_BANK_SIZE; +static uint8 *sbuf=jade_mem+DD_BUF; + +#define WORD(lsb,msb) (lsb + (msb << 8)) +#define MEM_BANK_OFFSET(a) ((int)((uint8 *) a - bank0)) + +/* Double D - DCM Command Block */ + +#define CB_CMD 0 /* DCM command */ +#define CB_DRV 1 /* Drive number */ +#define CB_TRK 2 /* Track number */ +#define CB_SEC 3 /* Sector number */ +#define CB_SP0 4 /* Spare byte 0 */ +#define CB_CHR 5 /* Character list */ +#define CB_MOD 6 /* Mode controls */ +#define CB_STS 7 /* Command status */ +#define CB_LAD 8 /* Load address (WORD) */ +#define CB_LNG 10 /* Load length (WORD) */ + +uint8 *cb = jade_mem + DD_CBT; + +#define ID_LBL 0 /* ID SECTOR LABEL */ +#define ID_BLK ID_LBL+0x20 /* ID BLOCK AREA */ +#define ID_SPT ID_BLK /* ID SECT PER TRK */ +#define ID_FLG ID_BLK+0x11 /* DISKETTE FLAGS */ +#define ID_FLD 0 /* 3740 FLAGS */ + +/* +** The FORMAT sector buffer is the following format +** +** DB 'FORMAT!' +** DB 'S' or 'D' +** LXI SECTOR LIST ADDRESS +** MVI E,SECTORS +** +*/ +#define FMT_HDR 0 /* 'FORMAT!' */ +#define FMT_DEN 7 /* 'S' or 'D' */ +#define FMT_LST 8 /* Sector List */ +#define FMT_SEC 12 /* Sectors */ + +uint8 *fmt = jade_mem + JADE_BANK_SIZE + DD_FBF; + +/* Local function prototypes */ +static t_stat jade_reset(DEVICE *jade_dev); +static t_stat jade_svc(UNIT *uptr); +static t_stat jade_attach(UNIT *uptr, CONST char *cptr); +static t_stat jade_detach(UNIT *uptr); +static t_stat jade_boot(int32 unitno, DEVICE *dptr); +static t_stat jade_set_iobase(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +static t_stat jade_set_membase(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +static t_stat jade_show_membase(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat jade_set_prom(UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static uint32 calculate_jade_sec_offset(uint8 track, uint8 sector, uint8 flg); +static void showsector(uint8 drive, uint8 isRead, uint8 *buf); +static void showcb(); +static uint8 JADE_In(uint32 Addr); +static uint8 JADE_Out(uint32 Addr, int32 data); +static uint8 PROM_Boot(JADE_INFO *info); +static void DCM_Init(); +static void DCM_DBSLdr(); +static uint8 DCM_Execute(); +static uint8 DCM_Logon(uint8 drive); +static uint8 DCM_ReadSector(uint8 drive, uint8 track, uint8 sector, uint8 *buffer); +static uint8 DCM_WriteSector(uint8 drive, uint8 track, uint8 sector, uint8 *buffer); +static uint8 DCM_Format(uint8 drive, uint8 track); +static const char* jade_description(DEVICE *dptr); + +static int32 jadedev(int32 Addr, int32 rw, int32 data); +static int32 jadeprom(int32 Addr, int32 rw, int32 data); +static int32 jademem(int32 Addr, int32 rw, int32 data); + +static UNIT jade_unit[JADE_MAX_DRIVES] = { + { UDATA (jade_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, JADE_CAPACITY), 10000 }, + { UDATA (jade_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, JADE_CAPACITY), 10000 }, + { UDATA (jade_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, JADE_CAPACITY), 10000 }, + { UDATA (jade_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, JADE_CAPACITY), 10000 } +}; + +static REG jade_reg[] = { + { NULL } +}; + +#define JADE_NAME "Jade Double D Controller" +#define JADE_SNAME "JADEDD" + +static const char* jade_description(DEVICE *dptr) { + return JADE_NAME; +} + +/* +** These definitions should probably be in s100_jade.h +** so they can be included in other modules. +*/ +#define UNIT_V_JADE_VERBOSE (UNIT_V_UF + 0) /* VERBOSE / QUIET */ +#define UNIT_JADE_VERBOSE (1 << UNIT_V_JADE_VERBOSE) +#define UNIT_V_JADE_WPROTECT (UNIT_V_UF + 1) /* WRTENB / WRTPROT */ +#define UNIT_JADE_WPROTECT (1 << UNIT_V_JADE_WPROTECT) + +/* +** These definitions should probably be in altairz80_sio.h +** so they can be included in other modules, like this one. +*/ +#define UNIT_V_SIO_SLEEP (UNIT_V_UF + 7) /* sleep after keyboard status check */ +#define UNIT_SIO_SLEEP (1 << UNIT_V_SIO_SLEEP) + +static MTAB jade_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", + &jade_set_iobase, &show_iobase, NULL, "Sets Jade Double D IO base address" }, + { MTAB_XTD|MTAB_VDV, 0, "MEMBASE", "MEMBASE", + &jade_set_membase, &jade_show_membase, NULL, "Sets Jade Double D memory block base address" }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "PROM", &jade_set_prom, + NULL, NULL, "Enable Jade Double D boot PROM"}, + { MTAB_XTD | MTAB_VDV, 0, NULL, "NOPROM", &jade_set_prom, + NULL, NULL, "Disable Jade Double D boot PROM" }, + { UNIT_JADE_VERBOSE, 0, "QUIET", "QUIET", + NULL, NULL, NULL, "No verbose messages for unit " JADE_NAME "n" }, + { UNIT_JADE_VERBOSE, UNIT_JADE_VERBOSE, "VERBOSE", "VERBOSE", + NULL, NULL, NULL, "Verbose messages for unit " JADE_NAME "n" }, + { UNIT_JADE_WPROTECT, 0, "WRTENB", "WRTENB", NULL, NULL, NULL, + "Enables " JADE_NAME "n for writing" }, + { UNIT_JADE_WPROTECT, UNIT_JADE_WPROTECT, "WRTPROT", "WRTPROT", NULL, NULL, NULL, + "Protects " JADE_NAME "n from writing" }, + { 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) + +/* Debug Flags */ +static DEBTAB jade_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" }, + { NULL, 0 } +}; + +DEVICE jade_dev = { + JADE_SNAME, /* name */ + jade_unit, /* unit */ + jade_reg, /* registers */ + jade_mod, /* modifiers */ + JADE_MAX_DRIVES, /* # units */ + 10, /* address radix */ + 31, /* address width */ + 1, /* addr increment */ + JADE_MAX_DRIVES, /* data radix */ + JADE_MAX_DRIVES, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &jade_reset, /* reset routine */ + &jade_boot, /* boot routine */ + &jade_attach, /* attach routine */ + &jade_detach, /* detach routine */ + &jade_info_data, /* context */ + (DEV_DISABLE | DEV_DIS | DEV_DEBUG), /* flags */ + ERROR_MSG, /* debug control */ + jade_dt, /* debug flags */ + NULL, /* mem size routine */ + NULL, /* logical name */ + NULL, /* help */ + NULL, /* attach help */ + NULL, /* context for help */ + &jade_description /* description */ +}; + +/* Reset routine */ +t_stat jade_reset(DEVICE *dptr) +{ + uint8 i; + JADE_INFO *pInfo = (JADE_INFO *)dptr->ctxt; + + if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ + sim_map_resource(pInfo->prom_base, pInfo->prom_size, RESOURCE_TYPE_MEMORY, &jadeprom, TRUE); + sim_map_resource(pInfo->mem_base, pInfo->mem_size, RESOURCE_TYPE_MEMORY, &jademem, TRUE); + sim_map_resource(pInfo->io_base, pInfo->io_size, RESOURCE_TYPE_IO, &jadedev, TRUE); + } else { + if(pInfo->pe) { + if(sim_map_resource(pInfo->prom_base, pInfo->prom_size, RESOURCE_TYPE_MEMORY, &jadeprom, FALSE) != 0) { + sim_debug(ERROR_MSG, &jade_dev, JADE_SNAME ": Error mapping MEM resource at 0x%04x" NLP, pInfo->prom_base); + return SCPE_ARG; + } + } + if(sim_map_resource(pInfo->mem_base, pInfo->mem_size, RESOURCE_TYPE_MEMORY, &jademem, FALSE) != 0) { + sim_debug(ERROR_MSG, &jade_dev, JADE_SNAME ": Error mapping MEM resource at 0x%04x" NLP, pInfo->mem_base); + return SCPE_ARG; + } + /* Connect I/O Ports at base address */ + if(sim_map_resource(pInfo->io_base, pInfo->io_size, RESOURCE_TYPE_IO, &jadedev, FALSE) != 0) { + sim_debug(ERROR_MSG, &jade_dev, JADE_SNAME ": Error mapping I/O resource at 0x%02x" NLP, pInfo->io_base); + return SCPE_ARG; + } + } + + pInfo->curdrv = 0; + + /* Reset Registers and Interface Controls */ + for (i=0; i < JADE_MAX_DRIVES; i++) { + if (jade_info->uptr[i] == NULL) { + jade_info->uptr[i] = &jade_dev.units[i]; + } + } + + sim_debug(STATUS_MSG, &jade_dev, JADE_SNAME ": reset controller." NLP); + + return SCPE_OK; +} + +/* Attach routine */ +t_stat jade_attach(UNIT *uptr, CONST char *cptr) +{ + char header[4]; + t_stat r; + unsigned int i = 0; + + r = attach_unit(uptr, cptr); /* attach unit */ + + if(r != SCPE_OK) { /* error? */ + sim_debug(ERROR_MSG, &jade_dev, JADE_SNAME ": ATTACH error=%d" NLP, r); + return r; + } + + /* Determine length of this disk */ + if(sim_fsize(uptr->fileref) != 0) { + uptr->capac = sim_fsize(uptr->fileref); + } else { + uptr->capac = JADE_CAPACITY; + } + + DBG_PRINT(("JADE: ATTACH uptr->capac=%d" NLP, uptr->capac)); + + for (i = 0; i < JADE_MAX_DRIVES; i++) { + if(jade_dev.units[i].fileref == uptr->fileref) { + break; + } + } + + if (i >= JADE_MAX_DRIVES) { + return SCPE_ARG; + } + + /* 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(JADE_SNAME ": CPT images not yet supported" NLP); + uptr->u3 = IMAGE_TYPE_CPT; + jade_detach(uptr); + return SCPE_OPENERR; + } else { + uptr->u3 = IMAGE_TYPE_DSK; + } + } + + if (uptr->flags & UNIT_JADE_VERBOSE) { + sim_printf(JADE_SNAME "%d: attached to '%s', type=%s, len=%d" NLP, i, cptr, + uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK", + uptr->capac); + } + + return SCPE_OK; +} + + +/* Detach routine */ +t_stat jade_detach(UNIT *uptr) +{ + t_stat r; + int8 i; + + for (i = 0; i < JADE_MAX_DRIVES; i++) { + if(jade_dev.units[i].fileref == uptr->fileref) { + break; + } + } + + if (i >= JADE_MAX_DRIVES) { + return SCPE_ARG; + } + + DBG_PRINT(("Detach JADE%d" NLP, i)); + + r = detach_unit(uptr); /* detach unit */ + + if (r != SCPE_OK) { + return r; + } + + jade_dev.units[i].fileref = NULL; + + if (uptr->flags & UNIT_JADE_VERBOSE) { + sim_printf(JADE_SNAME "%d: detached." NLP, i); + } + + return SCPE_OK; +} + +/* +** Verify that iobase is within valid range +** before calling set_iobase +*/ +static t_stat jade_set_iobase(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, 0xFF, &r); + if (r != SCPE_OK) + return r; + + if ((newba > 0x43) || (newba < 0x40)) { + sim_printf(JADE_SNAME ": Valid options are 40,41,42,43" NLP); + return SCPE_ARG; + } + + return set_iobase(uptr, val, cptr, desc); +} + +/* +** Verify that membase is within valid range +** before calling set_membase +*/ +static t_stat jade_set_membase(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 > 0xFC00) || (newba < 0xE000) || (newba % jade_info->mem_size)) { + sim_printf(JADE_SNAME ": Valid options are E000,E400,E800,EC00,F000,F400,F800,FC00" NLP); + return SCPE_ARG; + } + + return set_membase(uptr, val, cptr, desc); +} + +/* Show Base Address routine */ +t_stat jade_show_membase(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + DEVICE *dptr; + JADE_INFO *pInfo; + + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit (uptr); + if (dptr == NULL) + return SCPE_IERR; + + pInfo = (JADE_INFO *) dptr->ctxt; + + fprintf(st, "MEM=0x%04X-0x%04X", pInfo->mem_base, pInfo->mem_base+pInfo->mem_size-1); + + if(pInfo->pe) + fprintf(st, ", PROM=0x%04X-0x%04X", pInfo->prom_base, pInfo->prom_base+pInfo->prom_size-1); + + return SCPE_OK; +} + +static t_stat jade_set_prom(UNIT *uptr, int32 value, CONST char *cptr, void *desc) +{ + JADE_INFO *pInfo = (JADE_INFO *)uptr->dptr->ctxt; + + jade_info->pe = (uint8) value; + + /* + ** Map/Unmap PROM + */ + sim_map_resource(pInfo->prom_base, pInfo->prom_size, RESOURCE_TYPE_MEMORY, &jadeprom, !value); + + if (uptr->flags & UNIT_JADE_VERBOSE) { + sim_printf(JADE_SNAME ": PROM %s" NLP, (value) ? "enabled" : "disabled"); + } + + return SCPE_OK; +} + +static t_stat jade_boot(int32 unitno, DEVICE *dptr) +{ + + JADE_INFO *pInfo = (JADE_INFO *)dptr->ctxt; + + if (pInfo->uptr[0]->flags & UNIT_JADE_VERBOSE) { + sim_printf(JADE_SNAME ": Booting Controller at 0x%04x" NLP, pInfo->prom_base); + } + + *((int32 *) sim_PC->loc) = pInfo->prom_base; + + return SCPE_OK; +} + +static t_stat jade_svc(UNIT *uptr) +{ + return SCPE_OK; +} + +static void showsector(uint8 drive, uint8 isRead, uint8 *buf) { + int32 i; + + sim_debug(RD_DATA_DETAIL_MSG|WR_DATA_DETAIL_MSG, &jade_dev, JADE_SNAME "%d: %s sector:" NLP "\t", drive, (isRead) ? "Read" : "Write"); + for (i=0; i < JADE_SECTOR_SIZE; i++) { + sim_debug(RD_DATA_DETAIL_MSG|WR_DATA_DETAIL_MSG, &jade_dev, "%02X ", *(buf+i)); + if (((i+1) & 0xf) == 0) { + sim_debug(RD_DATA_DETAIL_MSG|WR_DATA_DETAIL_MSG, &jade_dev, NLP "\t"); + } + } + sim_debug(RD_DATA_DETAIL_MSG|WR_DATA_DETAIL_MSG, &jade_dev, NLP); +} + +static void showcb() +{ + DBG_PRINT((JADE_SNAME + " cmd=0x%02X drv=%d trk=%02d sec=%02d mod=0x%02X sts=0x%02X lad=%04X lng=%04X" NLP, + cb[CB_CMD], cb[CB_DRV], cb[CB_TRK], cb[CB_SEC], cb[CB_MOD], cb[CB_STS], cb[CB_LAD] + (cb[CB_LAD+1] << 8), cb[CB_LNG] + (cb[CB_LNG+1] << 8))); +} + +static int32 jadedev(int32 Addr, int32 rw, int32 data) +{ + if (rw == 0) { /* Read */ + return(JADE_In(Addr)); + } else { /* Write */ + return(JADE_Out(Addr, data)); + } +} + +/* +** The SYSTEM TRACKS have a different layout than the diskettes distributed +** by DIGITAL RESEARCH. Those modules residing on the SYSTEM TRACKS which +** often need to be modified for a specific system are on track 0, which is in +** single density. CCP and BOOS, which are not modified by the user, are +** on track 1 in double density. All data tracks are in single density +** such that the DOUBLE D distribution diskette can be read and modified +** on most 8" single density CP/M systems. +** +** Track 0, sector 1, is used by the Jade DD to store a disk identity label. +** +** The identity label consists of the following: +** +** 00H-20H "Jade DD ..." ; Diskette ID Label +** 20H-31H ; ID Block Area +** 32H ; Diskette Flags +** 33H ; 3740 flags +** +*/ +static uint32 calculate_jade_sec_offset(uint8 track, uint8 sector, uint8 flg) +{ + uint32 offset; + + /* + ** Calculate track offset + */ + if (track<2) { + offset=track*JADE_SPT_SD*JADE_SECTOR_SIZE; + } + else { + offset=JADE_SPT_SD*JADE_SECTOR_SIZE; /* Track 0 */ + offset+=((flg&DF_T1D) ? JADE_SPT_DD : JADE_SPT_SD) * JADE_SECTOR_SIZE; /* Track 1 */ + offset+=(track-2)*((flg&DF_DTD) ? JADE_SPT_DD : JADE_SPT_SD) * JADE_SECTOR_SIZE; /* Track 2-77 */ + } + + /* + ** Add sector offset to track offset + */ + offset += (sector-1)*JADE_SECTOR_SIZE; + + return (offset); +} + +/* +** JADE DD Status Port +** +** The Disk Processor Status Port is an S-100 input port which allows +** the host processor to examine the current state of the Disk Processor. +** The port responds to occurrence of a pDBIN, sINP, and matching port +** address. The following states can be determined by reading this port. +** +** 1. On-board processor state (Run/Halt) +** 2. Address of 1K memory window +** +** Bit 0 0=HALT, 1=RUN +** Bit 1-3 000:E000-E3FF +** 001:E400-E7FF +** 001:E800-EBFF +** 001:EC00-EEFF +** 001:F000-F3FF +** 001:F400-F7FF +** 001:F800-FBFF +** 001:FC00-FEFF +*/ +static uint8 JADE_In(uint32 Addr) +{ + uint8 cData; + + sim_debug(CMD_MSG, &jade_dev, JADE_SNAME ": IN %02x Data %02x" NLP, Addr & 0xFF, cData & 0xFF); + + cData = JADE_STAT_HALT; /* Assume processor is in HALT* state */ + cData |= (jade_info->mem_base >> 9) & JADE_STAT_MEM_MSK; + + return (cData); +} + +static uint8 JADE_Out(uint32 Addr, int32 Data) +{ + sim_debug(CMD_MSG, &jade_dev, JADE_SNAME ": OUT %02x Data %02x" NLP, Addr & 0xFF, Data & 0xFF); + + switch (Data) { + case CMD_SOT: /* Bank 0 out of system */ + sim_debug(CMD_MSG, &jade_dev, JADE_SNAME ": Z80 system memory out" NLP); + jade_info->mem_sys = FALSE; + break; + + case CMD_SIN|CMD_MD0: /* Request memory bank 0 */ + sim_debug(CMD_MSG, &jade_dev, JADE_SNAME ": Z80 system memory in" NLP); + jade_info->mem_sys = TRUE; + sim_debug(CMD_MSG, &jade_dev, JADE_SNAME ": selected memory bank 0" NLP); + jade_info->mem_bank = 0; + break; + + case CMD_MD1: /* Select memory bank 1 */ + sim_debug(CMD_MSG, &jade_dev, JADE_SNAME ": selected memory bank 1" NLP); + jade_info->mem_bank = 1; + break; + + case CMD_INT: /* Interrupt Z80 */ + sim_debug(CMD_MSG, &jade_dev, JADE_SNAME ": Z80 interrupt" NLP); + cb[CB_STS] = DCM_Execute(); /* Save status in command block */ + break; + + case CMD_BGN: /* Reset and Execute */ + /* + ** Card has been reset and the host boot PROM + ** has loaded the DCM injector module onto the DD. + ** This modules reads the DCM from track 0 starting + ** at sector 13 into memory bank 1. After the DCM + ** is loaded, it is executed by the DD's on-board + ** Z80 processor. + */ + cb[CB_STS] = PROM_Boot(jade_info); + + break; + + default: + break; + } + + return(Data); +} + +/* +** This doesn't really do anything for us other +** than have the DCM available to the host through +** the DD's bank0 memory window. +*/ +static uint8 PROM_Boot(JADE_INFO *info) +{ + uint8 sec = DCM_SEC; + uint16 d = JADE_BANK_SIZE; + uint8 *h = bank1; + uint8 sts; + + while(d) { + if ((sts = DCM_ReadSector(0, 0, sec, h)) != CS_NOE) { + return(sts); + } + + h += JADE_SECTOR_SIZE; + d -= JADE_SECTOR_SIZE; + sec++; + } + + DCM_Init(); + + return (CS_NOE); +} + +/* +** Again, regarding the DCM, this doesn't really do anything +** for us other than be able to view from the host the DCM in +** in memory bank 0 that was loaded from disk and load the +** Boot Loader Transient (BLT) module into the sector buffer. +** The BLT is emulated with DCM_DBSLdr() function. +*/ +static void DCM_Init() +{ + /* Move Bank 1 to Bank 0 */ + memcpy(bank0, bank1, JADE_BANK_SIZE); + + /* Read the BLT from T0/S2 */ + cb[CB_TRK] = 0; + cb[CB_SEC] = 2; + + if (DCM_ReadSector(0, cb[CB_TRK], cb[CB_SEC], sbuf) == CS_NOE) { + + /* + ** Simulate the BLT loaded into the sector buffer + */ + DCM_DBSLdr(); + } +} + +/***************************************************** +; THE BIOS LOADER IS READ INTO THE DCM SECTOR BUFFER * +; AFTER DCM HAS INITIALIZED. THE BIOS LOADER PROGRAM * +; IS THEN EXECUTED WHICH READS THE DDBIOS MODULE * +; INTO BANK 1. THE COMMAND BLOCK (IN DCM) IS SET TO * +; INDICATE DDBIOS MODULE SIZE AND THE SYSTEM LOAD * +; ADDRESS. THE BIOS LOADER PROGRAM IS GENERATED BY * +; MOVCPM.COM AS THE COLD START LOADER (900-97F HEX). * +; THIS MODULE IS PROVIDED FOR REFERENCE PURPOSES. * +;****************************************************/ + +#define LNG_1K 1024 +#define SEC_BG 4 /* First BIOS sector */ + +static void DCM_DBSLdr() +{ + uint8 sec = SEC_BG; + uint16 d = LNG_1K; + uint8 *h = bank1; + + while(d) { + if (DCM_ReadSector(0, 0, sec, h) != CS_NOE) { + return; + } + + h += JADE_SECTOR_SIZE; + d -= JADE_SECTOR_SIZE; + sec++; + } + + /* + ** The first DDBIOS instruction is "JMP INIT" + ** To accommodate various memory sizes, we will use + ** the MSB of INIT to determine the load location for DDBIOS. + */ + cb[CB_LAD] = 0; /* LSB */ + cb[CB_LAD+1] = *(bank1+2); /* MSB */ + cb[CB_LNG] = (LNG_1K & 0xff); /* LSB */ + cb[CB_LNG+1] = ((LNG_1K & 0xff00) >> 8); /* MSB */ + + showcb(); +} + +/******************************************************** +* THIS FUNCTION GAINS CONTROL AFTER THE DISK CONTROLLER * +* IS INTERRUPTED FROM THE HALT CONDITION BY THE HOST * +* ISSUING A CMD_INT OUTPUT COMMAND. * +* * +* THIS FUNCTION HANDLES TO THE INDIVIDUAL COMMAND * +* ROUTINES. * +********************************************************/ +static uint8 DCM_Execute() +{ + uint8 sts; + + showcb(); + + switch (cb[CB_CMD]) { + case DC_LOG: + sts = DCM_Logon(cb[CB_DRV]); + break; + + case DC_RDS: + sts = DCM_ReadSector(cb[CB_DRV], cb[CB_TRK], cb[CB_SEC], sbuf); + break; + + case DC_WRS: + sts = DCM_WriteSector(cb[CB_DRV], cb[CB_TRK], cb[CB_SEC], sbuf); + break; + + case DC_FMT: + sts = DCM_Format(cb[CB_DRV], cb[CB_TRK]); + break; + + case DC_ADR: + case DC_LCK: + sts = 0xff; + break; + + case DC_LST: + case DC_IDL: + sts = cb[CB_STS]; /* Do not change status */ + break; + + default: + sts = 0; + break; + } + + return(sts); +} + +/****************************************************** +; LOG.ON IS THE SUBROUTINE THAT READS THE IDENTITY * +; SECTOR FROM THE DISKETTE AND MAKES THE NEEDED * +; ENTRYS INTO THE DRIVE TABLE. THE SECTOR DATA IS * +; ALSO LEFT IN THE SECTOR BUFFER FOR BIOS TO FINISH * +; THE LOG-ON OPERATION. * +;*****************************************************/ +static uint8 DCM_Logon(uint8 drive) +{ + uint8 sts; + + sts = DCM_ReadSector(drive, 0, 1, sbuf); + + if (!strncmp((char *) sbuf, "Jade DD ", 8)) { + jade_info->dt[drive].spt = sbuf[ID_SPT]; + jade_info->dt[drive].flg = sbuf[ID_FLG]; + if (jade_info->uptr[drive]->flags & UNIT_JADE_VERBOSE) { + sim_printf(JADE_SNAME "%d: JADE ID Found: '%.32s' SPT=%0d FLG=0x%02X" NLP, drive, sbuf, jade_info->dt[drive].spt, jade_info->dt[drive].flg); + } + } + else { + jade_info->dt[drive].spt = JADE_SPT_SD; + jade_info->dt[drive].flg = ID_FLD; + if (jade_info->uptr[drive]->flags & UNIT_JADE_VERBOSE) { + sim_printf(JADE_SNAME "%d: JADE ID Not Found: SPT=%0d FLG=0x%02X" NLP, drive, jade_info->dt[drive].spt, jade_info->dt[drive].flg); + } + } + + return(sts); +} + +/****************************************************** +; RD.SEC IS THE SUBROUTINE THAT INTERACTS WITH THE * +; 179X-02 DURING READ SECTOR OPERATIONS. THIS SECTION * +; INITIATES THE DISK TRANSFER, SERVICES THE CONTROLLER* +; CHIP DURING DATA TRANSFER, AND TERMINATES OPERATION * +; WHEN FINISHED. ERROR DETECTION IS IMPLEMENTED AND * +; RETRIES ARE EXRCUTED IF DATA ERRORS ARE DETECTED. * +;*****************************************************/ +static uint8 DCM_ReadSector(uint8 drive, uint8 track, uint8 sector, uint8 *buffer) +{ + uint32 offset; + + jade_info->curdrv = drive; + + cb[CB_TRK] = track; + cb[CB_SEC] = sector; + + /* + ** Make sure drive is ready + */ + if (jade_info->uptr[drive]->fileref == NULL) { + if (jade_info->uptr[drive]->flags & UNIT_JADE_VERBOSE) { + sim_printf(JADE_SNAME "%d: Drive Not Ready" NLP, drive); + } + return CS_DNR; + } + + offset = calculate_jade_sec_offset(track, sector, jade_info->dt[drive].flg); + + if (sim_fseek(jade_info->uptr[drive]->fileref, offset, SEEK_SET) != 0) { + sim_debug(ERROR_MSG, &jade_dev, JADE_SNAME "%d: RDSEC sim_fseek error." NLP, drive); + return CS_RNF; + } + + if (sim_fread(buffer, 1, JADE_SECTOR_SIZE, jade_info->uptr[drive]->fileref) != JADE_SECTOR_SIZE) { + sim_debug(ERROR_MSG, &jade_dev, JADE_SNAME "%d: RDSEC sim_fread error." NLP, drive); + return CS_CRC; + } + + showsector(drive, TRUE, buffer); + + return CS_NOE; +} + +/****************************************************** +; WR.SEC SUBROUTINE INTERACTS WITH THE FD179X-02 * +; DURING WRITE SECTOR OPERATIONS. THIS SECTION * +; INITIATES THE DISK TRANSFER, SERVICES THE CONTROLLER* +; CHIP, AND TERMINATES THE OPERATION. ERROR DETECTION * +; IS IMPLEMENTED. * +;*****************************************************/ +static uint8 DCM_WriteSector(uint8 drive, uint8 track, uint8 sector, uint8 *buffer) +{ + uint32 offset; + + jade_info->curdrv = drive; + + cb[CB_TRK] = track; + cb[CB_SEC] = sector; + + /* + ** Make sure drive is ready + */ + if (jade_info->uptr[drive]->fileref == NULL) { + return CS_DNR; + } + + /* + ** Check if drive is write protected + */ + if (jade_info->uptr[drive]->flags & UNIT_JADE_WPROTECT) { + return CS_WRP; + } + + offset = calculate_jade_sec_offset(track, sector, jade_info->dt[drive].flg); + + if (sim_fseek(jade_info->uptr[drive]->fileref, offset, SEEK_SET) != 0) { + sim_debug(ERROR_MSG, &jade_dev, JADE_SNAME "%d: WRSEC sim_fseek error." NLP, drive); + return CS_RNF; + } + + if (sim_fwrite(buffer, 1, JADE_SECTOR_SIZE, jade_info->uptr[drive]->fileref) != JADE_SECTOR_SIZE) { + sim_debug(ERROR_MSG, &jade_dev, JADE_SNAME "%d: WRSEC sim_fwrite error." NLP, drive); + return CS_CRC; + } + + showsector(drive, FALSE, buffer); + + return CS_NOE; +} + +/****************************************************** +; WR.TRK IS THE SUBROUTINE WHICH INITIATES A FORMAT * +; TRACK COMMAND (WRITE-TRACK 179X-02 TYPE 3). THE * +; FORMATTING BYTE STREAM IS PROVIDED BY A PROGRAM * +; WHICH MUST BE PRESENT IN THE FORMAT BUFFER. * +;*****************************************************/ +static uint8 DCM_Format(uint8 drive, uint8 track) +{ + uint8 sbuf[JADE_SECTOR_SIZE]; + uint8 sector; + uint8 sts; + + memset(sbuf, 0xe5, sizeof(sbuf)); + + jade_info->dt[drive].flg = 0; + + /* + ** Are we formatting double density? + */ + jade_info->dt[drive].flg |= (fmt[FMT_DEN] == 'D') ? DF_DTD : 0; + + /* + ** If track 1 is being formatted 50 sectors, set DF_T1D flag + */ + jade_info->dt[drive].flg |= (fmt[FMT_SEC] == 50) ? DF_T1D : 0; + + for (sector=1; sector<=fmt[FMT_SEC]; sector++) { + sts = DCM_WriteSector(drive, track, sector, sbuf); + } + + return(sts); +} + +static int32 jadeprom(int32 Addr, int32 rw, int32 Data) +{ + return(jade_prom[Addr & JADE_PROM_MASK]); +} + +static int32 jademem(int32 Addr, int32 rw, int32 Data) +{ + int32 offset = (Addr & JADE_BANK_MASK) + (jade_info->mem_bank * JADE_BANK_SIZE); + + /* + ** Need to select bank + */ + if (rw) { + jade_mem[offset] = Data & 0xff; + } + + return(jade_mem[offset]); +} + diff --git a/Visual Studio Projects/AltairZ80.vcproj b/Visual Studio Projects/AltairZ80.vcproj index 4f857346..f964698d 100644 --- a/Visual Studio Projects/AltairZ80.vcproj +++ b/Visual Studio Projects/AltairZ80.vcproj @@ -251,6 +251,10 @@ RelativePath="..\AltairZ80\insnsd.c" > + + @@ -259,10 +263,6 @@ RelativePath="..\AltairZ80\m68kdasm.c" > - - @@ -323,6 +323,10 @@ RelativePath="..\AltairZ80\s100_if3.c" > + + diff --git a/descrip.mms b/descrip.mms index ca901335..d40a4ace 100644 --- a/descrip.mms +++ b/descrip.mms @@ -356,7 +356,8 @@ ALTAIRZ80_SOURCE1 = $(ALTAIRZ80_DIR)ALTAIRZ80_CPU.C,$(ALTAIRZ80_DIR)ALTAIRZ80_CP $(ALTAIRZ80_DIR)I86_OPS.C,$(ALTAIRZ80_DIR)I86_PRIM_OPS.C,\ $(ALTAIRZ80_DIR)I8272.C,$(ALTAIRZ80_DIR)INSNSD.C,\ $(ALTAIRZ80_DIR)MFDC.C,$(ALTAIRZ80_DIR)N8VEM.C,\ - $(ALTAIRZ80_DIR)S100_MDSA.C,$(ALTAIRZ80_DIR)VFDHD.C + $(ALTAIRZ80_DIR)S100_MDSA.C,$(ALTAIRZ80_DIR)VFDHD.C,\ + $(ALTAIRZ80_DIR)S100_JADEDD.C ALTAIRZ80_LIB2 = $(LIB_DIR)ALTAIRZ80L2-$(ARCH).OLB ALTAIRZ80_SOURCE2 = $(ALTAIRZ80_DIR)S100_DISK1A.C,$(ALTAIRZ80_DIR)S100_DISK2.C,\ $(ALTAIRZ80_DIR)S100_FIF.C,$(ALTAIRZ80_DIR)S100_MDRIVEH.C,\ diff --git a/makefile b/makefile index 391c94bb..0a272806 100644 --- a/makefile +++ b/makefile @@ -1643,6 +1643,7 @@ ALTAIRZ80 = ${ALTAIRZ80D}/altairz80_cpu.c ${ALTAIRZ80D}/altairz80_cpu_nommu.c \ ${ALTAIRZ80D}/mfdc.c ${ALTAIRZ80D}/n8vem.c ${ALTAIRZ80D}/vfdhd.c \ ${ALTAIRZ80D}/s100_disk1a.c ${ALTAIRZ80D}/s100_disk2.c ${ALTAIRZ80D}/s100_disk3.c \ ${ALTAIRZ80D}/s100_fif.c ${ALTAIRZ80D}/s100_mdriveh.c \ + ${ALTAIRZ80D}/s100_jadedd.c \ ${ALTAIRZ80D}/s100_mdsa.c \ ${ALTAIRZ80D}/s100_mdsad.c ${ALTAIRZ80D}/s100_selchan.c \ ${ALTAIRZ80D}/s100_ss1.c ${ALTAIRZ80D}/s100_64fdc.c \