3b2: Make floppy controller a DEV_DISK device

- Formerly, the floppy controller buffered an entire diskette image
  in memory using the unit buffer, and flushed it out on detach or
  shutdown. This worked well enough, but it's better to have the
  device use the SIMH disk sector read and write routines and
  behave like a true disk.

- Read Only (Write Protect) logic has also been enabled in the
  floppy controller, allowing users to mount images that do not
  have write permissions.

- The IF (Integrated Floppy) and ID (Integrated Disk)
  device names were poorly chosen. They conflict with built-in
  scp commands and mess up the help system. This commit
  changes them to IFLOPPY and IDISK, respectively.

  NOTE!! THIS IS A BREAKING CONFIG CHANGE!!
This commit is contained in:
Seth Morabito 2019-01-13 22:25:26 -08:00
parent 9b631bffc5
commit ae0cad26e4
4 changed files with 157 additions and 61 deletions

View file

@ -164,11 +164,11 @@ MTAB id_mod[] = {
}; };
DEVICE id_dev = { DEVICE id_dev = {
"ID", id_unit, id_reg, id_mod, "IDISK", id_unit, id_reg, id_mod,
ID_NUM_UNITS, 16, 32, 1, 16, 8, ID_NUM_UNITS, 16, 32, 1, 16, 8,
NULL, NULL, &id_reset, NULL, NULL, &id_reset,
NULL, &id_attach, &id_detach, NULL, NULL, &id_attach, &id_detach, NULL,
DEV_DEBUG|DEV_SECTORS, 0, sys_deb_tab, DEV_DEBUG|DEV_DISK|DEV_SECTORS, 0, sys_deb_tab,
NULL, NULL, &id_help, NULL, NULL, NULL, NULL, &id_help, NULL, NULL,
&id_description &id_description
}; };
@ -937,21 +937,21 @@ void id_after_dma()
CONST char *id_description(DEVICE *dptr) CONST char *id_description(DEVICE *dptr)
{ {
return "MFM Hard Disk Controller"; return "Integrated Hard Disk";
} }
t_stat id_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) t_stat id_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{ {
fprintf(st, "Integrated Hard Disk (ID)\n\n"); fprintf(st, "Integrated Hard Disk (IDISK)\n\n");
fprintf(st, "The ID device implements the integrated MFM hard disk controller\n"); fprintf(st, "The IDISK device implements the integrated MFM hard disk of the\n");
fprintf(st, "of the 3B2/400. Up to two drives are supported on a single controller.\n\n"); fprintf(st, "3B2/400. Up to two drives are supported on a single controller.\n\n");
fprintf(st, "Supported device types are:\n\n"); fprintf(st, "Supported device types are:\n\n");
fprintf(st, " Name Size ID Cyl Head Sec Byte/Sec Description\n"); fprintf(st, " Name Size ID Cyl Head Sec Byte/Sec Description\n");
fprintf(st, " ---- -------- -- ---- ---- --- -------- ----------------------\n"); fprintf(st, " ---- -------- -- ---- ---- --- -------- ----------------------\n");
fprintf(st, " HD30 30.6 MB 3 697 5 18 512 CDC Wren 94155-36\n"); fprintf(st, " HD30 30.6 MB 3 697 5 18 512 CDC Wren 94155-36\n");
fprintf(st, " HD72 73.2 MB 5 925 9 18 512 CDC Wren II 94156-86\n"); fprintf(st, " HD72 73.2 MB 5 925 9 18 512 CDC Wren II 94156-86\n");
fprintf(st, " HD72C 72.9 MB 8 754 11 18 512 Fujitsu M2243AS\n"); fprintf(st, " HD72C 72.9 MB 8 754 11 18 512 Fujitsu M2243AS\n");
fprintf(st, " HD135 135.0 MB 11 1024 15 18 512 Maxtor XT1190 (SVR2)\n\n"); fprintf(st, " HD135 135.0 MB 11 1024 15 18 512 Maxtor XT1190 (SVR2)\n");
fprintf(st, " HD161 161.4 MB 11 1224 15 18 512 Maxtor XT1190 (SVR3+)\n\n"); fprintf(st, " HD161 161.4 MB 11 1224 15 18 512 Maxtor XT1190 (SVR3+)\n\n");
fprintf(st, "The drive ID and geometry values are used when low-level formatting a\n"); fprintf(st, "The drive ID and geometry values are used when low-level formatting a\n");
fprintf(st, "drive using the AT&T 'idtools' utility.\n"); fprintf(st, "drive using the AT&T 'idtools' utility.\n");

View file

@ -34,7 +34,7 @@
static SIM_INLINE void if_set_irq(); static SIM_INLINE void if_set_irq();
static SIM_INLINE void if_clear_irq(); static SIM_INLINE void if_clear_irq();
static SIM_INLINE void if_cancel_pending_irq(); static SIM_INLINE void if_cancel_pending_irq();
static SIM_INLINE uint32 if_buf_offset(); static SIM_INLINE uint32 if_lba();
/* /*
* Disk Format: * Disk Format:
@ -57,8 +57,8 @@ static SIM_INLINE uint32 if_buf_offset();
#define IF_HSW_DELAY 40000 /* us */ #define IF_HSW_DELAY 40000 /* us */
UNIT if_unit = { UNIT if_unit = {
UDATA (&if_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ UDATA (&if_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BINK+UNIT_ROABLE,
UNIT_MUSTBUF+UNIT_BINK, IF_DSK_SIZE) IF_DSK_SIZE_SECS)
}; };
REG if_reg[] = { REG if_reg[] = {
@ -66,14 +66,17 @@ REG if_reg[] = {
}; };
DEVICE if_dev = { DEVICE if_dev = {
"IF", &if_unit, if_reg, NULL, "IFLOPPY", &if_unit, if_reg, NULL,
1, 16, 8, 1, 16, 8, 1, 16, 8, 1, 16, 8,
NULL, NULL, &if_reset, NULL, NULL, &if_reset,
NULL, NULL, NULL, NULL, NULL, &if_attach, &if_detach, NULL,
DEV_DEBUG, 0, sys_deb_tab DEV_DEBUG|DEV_DISK|DEV_SECTORS, 0, sys_deb_tab,
NULL, NULL, &if_help, NULL, NULL,
&if_description
}; };
IF_STATE if_state; IF_STATE if_state;
uint8 if_buf[IF_SEC_SIZE];
uint32 if_sec_ptr = 0; uint32 if_sec_ptr = 0;
t_bool if_irq = FALSE; t_bool if_irq = FALSE;
@ -103,6 +106,9 @@ static SIM_INLINE void if_cancel_pending_irq()
t_stat if_svc(UNIT *uptr) t_stat if_svc(UNIT *uptr)
{ {
uint32 lba; /* Logical block address for write */
t_seccnt sectswritten;
if_state.status &= ~(IF_BUSY); if_state.status &= ~(IF_BUSY);
switch(if_state.cmd & 0xf0) { switch(if_state.cmd & 0xf0) {
@ -114,6 +120,23 @@ t_stat if_svc(UNIT *uptr)
if (if_state.track == 0) { if (if_state.track == 0) {
if_state.status |= IF_TK_0; if_state.status |= IF_TK_0;
} }
break;
case IF_WRITE_SEC:
lba = if_lba();
/* If we're read-only, don't actually do anything. */
if (if_unit.flags & UNIT_RO) {
break;
}
if (sim_disk_wrsect(&if_unit, lba, if_buf, &sectswritten, 1) == SCPE_OK) {
if (sectswritten != 1) {
sim_debug(EXECUTE_MSG, &if_dev,
"ERROR: ASKED TO wRITE ONE SECTOR, WROTE %d\n",
sectswritten);
}
}
break; break;
} }
@ -132,19 +155,28 @@ t_stat if_reset(DEVICE *dptr)
if_state.track = 0; if_state.track = 0;
if_state.sector = 1; if_state.sector = 1;
if_sec_ptr = 0; if_sec_ptr = 0;
return SCPE_OK; return SCPE_OK;
} }
t_stat if_attach(UNIT *uptr, CONST char *cptr)
{
return sim_disk_attach(uptr, cptr, 512, 1, TRUE, 0, "IF", 0, 0);
}
t_stat if_detach(UNIT *uptr)
{
return sim_disk_detach(uptr);
}
uint32 if_read(uint32 pa, size_t size) { uint32 if_read(uint32 pa, size_t size) {
uint8 reg, data; uint8 reg, data;
uint32 pos, pc; uint32 pc;
UNIT *uptr; UNIT *uptr;
uint8 *fbuf;
uptr = &(if_dev.units[0]); uptr = &(if_dev.units[0]);
reg = (uint8)(pa - IFBASE); reg = (uint8)(pa - IFBASE);
pc = R[NUM_PC]; pc = R[NUM_PC];
fbuf = (uint8 *)uptr->filebuf;
switch (reg) { switch (reg) {
case IF_STATUS_REG: case IF_STATUS_REG:
@ -205,11 +237,10 @@ uint32 if_read(uint32 pa, size_t size) {
return if_state.data; return if_state.data;
} }
pos = if_buf_offset(); data = if_buf[if_sec_ptr++];
data = fbuf[pos + if_sec_ptr++];
sim_debug(READ_MSG, &if_dev, "\tDATA\t%02x\n", data); sim_debug(READ_MSG, &if_dev, "\tDATA\t%02x\n", data);
if (if_sec_ptr >= IF_SECTOR_SIZE) { if (if_sec_ptr >= IF_SEC_SIZE) {
if_sec_ptr = 0; if_sec_ptr = 0;
} }
@ -228,6 +259,8 @@ void if_handle_command()
uint32 delay_ms = 0; uint32 delay_ms = 0;
uint32 head_switch_delay = 0; uint32 head_switch_delay = 0;
uint32 head_load_delay = 0; uint32 head_load_delay = 0;
uint32 lba; /* Logical block address */
t_seccnt sectsread;
if_sec_ptr = 0; if_sec_ptr = 0;
@ -285,6 +318,10 @@ void if_handle_command()
/* Reset HLT */ /* Reset HLT */
if_state.status &= ~IF_HEAD_LOADED; if_state.status &= ~IF_HEAD_LOADED;
if (if_unit.flags & UNIT_RO) {
if_state.status |= IF_WP;
}
/* If head should be loaded immediately, do so now */ /* If head should be loaded immediately, do so now */
if (if_state.cmd & IF_H_FLAG) { if (if_state.cmd & IF_H_FLAG) {
if_state.status |= IF_HEAD_LOADED; if_state.status |= IF_HEAD_LOADED;
@ -310,12 +347,18 @@ void if_handle_command()
case IF_STEP: case IF_STEP:
case IF_STEP_T: case IF_STEP_T:
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tStep\n", if_state.cmd); sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tStep\n", if_state.cmd);
if (if_unit.flags & UNIT_RO) {
if_state.status |= IF_WP;
}
if_activate(IF_STEP_DELAY); if_activate(IF_STEP_DELAY);
if_state.track = (uint8) MIN(MAX((int) if_state.track + if_state.step_dir, 0), 0x4f); if_state.track = (uint8) MIN(MAX((int) if_state.track + if_state.step_dir, 0), 0x4f);
break; break;
case IF_STEP_IN: case IF_STEP_IN:
case IF_STEP_IN_T: case IF_STEP_IN_T:
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tStep In\n", if_state.cmd); sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tStep In\n", if_state.cmd);
if (if_unit.flags & UNIT_RO) {
if_state.status |= IF_WP;
}
if_state.step_dir = IF_STEP_IN_DIR; if_state.step_dir = IF_STEP_IN_DIR;
if_state.track = (uint8) MAX((int) if_state.track + if_state.step_dir, 0); if_state.track = (uint8) MAX((int) if_state.track + if_state.step_dir, 0);
if_activate(IF_STEP_DELAY); if_activate(IF_STEP_DELAY);
@ -323,6 +366,9 @@ void if_handle_command()
case IF_STEP_OUT: case IF_STEP_OUT:
case IF_STEP_OUT_T: case IF_STEP_OUT_T:
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tStep Out\n", if_state.cmd); sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tStep Out\n", if_state.cmd);
if (if_unit.flags & UNIT_RO) {
if_state.status |= IF_WP;
}
if_state.step_dir = IF_STEP_OUT_DIR; if_state.step_dir = IF_STEP_OUT_DIR;
if_state.track = (uint8) MIN((int) if_state.track + if_state.step_dir, 0x4f); if_state.track = (uint8) MIN((int) if_state.track + if_state.step_dir, 0x4f);
if_activate(IF_STEP_DELAY); if_activate(IF_STEP_DELAY);
@ -333,6 +379,10 @@ void if_handle_command()
/* Reset HLT */ /* Reset HLT */
if_state.status &= ~IF_HEAD_LOADED; if_state.status &= ~IF_HEAD_LOADED;
if (if_unit.flags & UNIT_RO) {
if_state.status |= IF_WP;
}
/* If head should be loaded immediately, do so now */ /* If head should be loaded immediately, do so now */
if (if_state.cmd & IF_H_FLAG) { if (if_state.cmd & IF_H_FLAG) {
if_state.status |= IF_HEAD_LOADED; if_state.status |= IF_HEAD_LOADED;
@ -373,16 +423,27 @@ void if_handle_command()
break; break;
case IF_READ_SEC: case IF_READ_SEC:
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tRead Sector %d/%d/%d\n", lba = if_lba();
if_state.cmd, if_state.track, if_state.side, if_state.sector);
/* We set DRQ right away to request the transfer. */ sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tRead Sector %d/%d/%d (lba=%d)\n",
if_state.drq = TRUE; if_state.cmd, if_state.track, if_state.side, if_state.sector, lba);
if_state.status |= IF_DRQ;
if (if_state.cmd & IF_E_FLAG) { if (sim_disk_rdsect(&if_unit, lba, if_buf, &sectsread, 1) == SCPE_OK) {
if_activate(IF_R_DELAY + IF_VERIFY_DELAY + head_switch_delay); if (sectsread != 1) {
} else { sim_debug(EXECUTE_MSG, &if_dev,
if_activate(IF_R_DELAY + head_switch_delay); "ERROR: ASKED TO READ ONE SECTOR, READ %d\n",
sectsread);
}
/* We set DRQ right away to request the transfer. */
if_state.drq = TRUE;
if_state.status |= IF_DRQ;
if (if_state.cmd & IF_E_FLAG) {
if_activate(IF_R_DELAY + IF_VERIFY_DELAY + head_switch_delay);
} else {
if_activate(IF_R_DELAY + head_switch_delay);
}
} }
break; break;
case IF_READ_SEC_M: case IF_READ_SEC_M:
/* Not yet implemented. Halt the emulator. */ /* Not yet implemented. Halt the emulator. */
@ -392,9 +453,24 @@ void if_handle_command()
stop_reason = STOP_ERR; stop_reason = STOP_ERR;
break; break;
case IF_WRITE_SEC: case IF_WRITE_SEC:
sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tWrite Sector %d/%d/%d\n", lba = if_lba();
if_state.cmd, if_state.track, if_state.side, if_state.sector);
/* We set DRQ right away to request the transfer. */ sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tWrite Sector %d/%d/%d (lba=%d)\n",
if_state.cmd, if_state.track, if_state.side, if_state.sector, lba);
if (if_unit.flags & UNIT_RO) {
if_state.status |= IF_WP;
sim_debug(EXECUTE_MSG, &if_dev, "\tWON'T WRITE: WRITE PROTECTED.\n");
/* Still cause an interrupt... */
if_activate(IF_W_DELAY + head_switch_delay);
/* But don't set DRQ and ask for a transfer. */
break;
}
/* We set DRQ right away to request the transfer. Data will
* be written by the host into our buffer by 512 writes to the
* data register. When the IF device later activates, the data
* will actually be written. */
if_state.drq = TRUE; if_state.drq = TRUE;
if_state.status |= IF_DRQ; if_state.status |= IF_DRQ;
if (if_state.cmd & IF_E_FLAG) { if (if_state.cmd & IF_E_FLAG) {
@ -457,14 +533,11 @@ void if_write(uint32 pa, uint32 val, size_t size)
{ {
UNIT *uptr; UNIT *uptr;
uint8 reg; uint8 reg;
uint32 pos;
uint8 *fbuf;
val = val & 0xff; val = val & 0xff;
uptr = &(if_dev.units[0]); uptr = &(if_dev.units[0]);
reg = (uint8) (pa - IFBASE); reg = (uint8) (pa - IFBASE);
fbuf = (uint8 *)uptr->filebuf;
switch (reg) { switch (reg) {
case IF_CMD_REG: case IF_CMD_REG:
@ -497,11 +570,10 @@ void if_write(uint32 pa, uint32 val, size_t size)
* which we do not emulate. */ * which we do not emulate. */
} else if ((if_state.cmd & 0xf0) == IF_WRITE_SEC || } else if ((if_state.cmd & 0xf0) == IF_WRITE_SEC ||
(if_state.cmd & 0xf0) == IF_WRITE_SEC_M) { (if_state.cmd & 0xf0) == IF_WRITE_SEC_M) {
/* Find the right offset, and update the value. */
pos = if_buf_offset();
fbuf[pos + if_sec_ptr++] = (uint8) val;
if (if_sec_ptr >= IF_SECTOR_SIZE) { if_buf[if_sec_ptr++] = (uint8) val;
if (if_sec_ptr >= IF_SEC_SIZE) {
if_sec_ptr = 0; if_sec_ptr = 0;
} }
} }
@ -512,22 +584,34 @@ void if_write(uint32 pa, uint32 val, size_t size)
} }
} }
/* CONST char *if_description(DEVICE *dptr)
* Compute the offset of the currently selected C/H/S
*/
static SIM_INLINE uint32 if_buf_offset()
{ {
uint32 pos; return "Integrated Floppy Disk";
}
pos = IF_TRACK_SIZE * if_state.track * 2; t_stat if_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf(st, "Integrated Floppy Disk (IFLOPPY)\n\n");
fprintf(st, "The IFLOPPY device implements the integrated 720 KB floppy disk\n");
fprintf(st, "of the 3B2/400. A single floppy disk is supported on the controller.\n\n");
fprintf(st, "The format of the diskette media is as follows:\n\n");
fprintf(st, " Size Sides Tracks/Side Sectors/Track Bytes/Track\n");
fprintf(st, " ------ ----- ----------- ------------- -----------\n");
fprintf(st, " 720 KB 2 80 9 512\n\n");
fprintf(st, "Physical media is Double Sided/Quad Density, 96 tpi, 250kbps MFM encoding.\n");
return SCPE_OK;
}
if (if_state.side == 1) { /*
pos += IF_TRACK_SIZE; * Compute the offset of the currently selected C/H/S (in # of sectors)
} */
static SIM_INLINE uint32 if_lba()
pos += IF_SECTOR_SIZE * (if_state.sector - 1); {
/* Reminder that sectors are numbered 1-9 instead
return pos; * of being numbered 0-8 */
return((if_state.track * IF_SEC_COUNT * 2) +
(if_state.side * IF_SEC_COUNT) +
(if_state.sector - 1));
} }
void if_after_dma() void if_after_dma()

View file

@ -113,22 +113,27 @@ extern t_bool if_irq;
/* Constants */ /* Constants */
#define IF_SIDES 2 #define IF_SIDES 2
#define IF_SEC_COUNT 9
#define IF_SEC_SIZE 512
#define IF_TRACK_SIZE 4608 #define IF_TRACK_SIZE 4608
#define IF_SECTOR_SIZE 512
#define IF_TRACK_COUNT 80 #define IF_TRACK_COUNT 80
#define IF_STEP_IN_DIR 1 #define IF_STEP_IN_DIR 1
#define IF_STEP_OUT_DIR -1 #define IF_STEP_OUT_DIR -1
#define IF_DSK_SIZE (IF_SIDES * IF_TRACK_SIZE * IF_TRACK_COUNT) #define IF_DSK_SIZE_SECS (IF_SIDES * IF_TRACK_COUNT * IF_SEC_COUNT)
/* Function prototypes */ /* Function prototypes */
t_stat if_svc(UNIT *uptr); t_stat if_svc(UNIT *uptr);
t_stat if_reset(DEVICE *dptr); t_stat if_reset(DEVICE *dptr);
t_stat if_attach(UNIT *uptr, CONST char *cptr);
t_stat if_detach(UNIT *uptr);
uint32 if_read(uint32 pa, size_t size); uint32 if_read(uint32 pa, size_t size);
void if_write(uint32 pa, uint32 val, size_t size); void if_write(uint32 pa, uint32 val, size_t size);
void if_handle_command(); void if_handle_command();
void if_after_dma(); void if_after_dma();
CONST char *if_description(DEVICE *dptr);
t_stat if_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
#endif #endif

View file

@ -3,6 +3,10 @@ AT&T 3B2 Simulator
This module contains a simulator for the AT&T 3B2 Model 400 microcomputer. This module contains a simulator for the AT&T 3B2 Model 400 microcomputer.
Full documentation for the 3B2 simulator is available here:
- https://loomcom.com/3b2/emulator.html
Devices Devices
------- -------
@ -15,16 +19,19 @@ devices are given in parentheses:
- PD8253 Interval Timer (TIMER) - PD8253 Interval Timer (TIMER)
- AM9517 DMA controller (DMAC) - AM9517 DMA controller (DMAC)
- SCN2681A Integrated DUART (IU) - SCN2681A Integrated DUART (IU)
- TMS2793 Integrated Floppy Controller (IF) - TMS2793 Integrated Floppy Controller (IFLOPPY)
- uPD7261A Integrated MFM Fixed Disk Controller (ID) - uPD7261A Integrated MFM Fixed Disk Controller (IDISK)
- Non-Volatile Memory (NVRAM)
- MM58174A Time Of Day Clock (TOD) - MM58174A Time Of Day Clock (TOD)
- CM195B 4-port Serial MUX (PORTS)
- CM195H Cartridge Tape Controller (CTC)
Usage Usage
----- -----
To boot the 3B2 simulator into firmware mode, simply type: To boot the 3B2 simulator into firmware mode, simply type:
sim> BOOT CPU sim> BOOT
You will be greeted with the message: You will be greeted with the message:
@ -73,8 +80,8 @@ UNIX SVR3 is the only operating system available for the 3B2. To boot
UNIX, attach the first disk image from the 3B2 "Essential Utilities" UNIX, attach the first disk image from the 3B2 "Essential Utilities"
distribution. distribution.
sim> ATTACH IF <floppy-image> sim> ATTACH IFLOPPY <floppy-image>
sim> BOOT CPU sim> BOOT
Once you reach the `SYSTEM FAILURE` message, type `mcp` to enter Once you reach the `SYSTEM FAILURE` message, type `mcp` to enter
firmware mode. When prompted for the name of a program to boot, enter firmware mode. When prompted for the name of a program to boot, enter
@ -94,15 +101,15 @@ Installing SVR3
--------------- ---------------
To install SVR3 to the first hard disk, first, attach a new image To install SVR3 to the first hard disk, first, attach a new image
to the ID0 device: to the IDISK0 device:
sim> ATTACH ID0 <hd-image> sim> ATTACH IDISK0 <hd-image>
Then, boot the file `idtools` from the "3B2 Maintenance Utilities - Then, boot the file `idtools` from the "3B2 Maintenance Utilities -
Issue 4.0" floppy diskette. Issue 4.0" floppy diskette.
From `idtools`, select the `formhard` option and low-level format From `idtools`, select the `formhard` option and low-level format
integrated disk 0. Parameters are: integrated disk 0. Parameters for the default 72MB hard disk are:
Drive Id: 5 Drive Id: 5
Number cylinders: 925 Number cylinders: 925