/* RL11 (RLV12) cartridge disk simulator

   Copyright (c) 1994, Robert M Supnik, Digital Equipment Corporation
   Commercial use prohibited

   The RL11 is a four drive cartridge disk subsystem.  An RL01 drive
   consists of 256 cylinders, each with 2 surfaces containing 40 sectors
   of 256 bytes.  An RL02 drive has 512 cylinders.  The RLV12 is a
   controller variant which supports 22b direct addressing.

   The most complicated part of the RL11 controller is the way it does
   seeks.  Seeking is relative to the current disk address; this requires
   keeping accurate track of the current cylinder.  The RL01 will not
   switch heads or cross cylinders during transfers.
*/

#include "pdp11_defs.h"

/* Constants */

#define RL_NUMWD	128				/* words/sector */
#define RL_NUMSC	40				/* sectors/surface */
#define RL_NUMSF	2				/* surfaces/cylinder */
#define RL_NUMCY	256				/* cylinders/drive */
#define RL_NUMDR	4				/* drives/controller */
#define RL01_SIZE (RL_NUMCY * RL_NUMSF * RL_NUMSC * RL_NUMWD)  /* words/drive */
#define RL02_SIZE	(RL01_SIZE * 2)			/* words/drive */
#define RL_MAXMEM	(MEMSIZE / sizeof (short))	/* words/memory */

/* Flags in the unit flags word */

#define UNIT_V_HWLK	(UNIT_V_UF)			/* hwre write lock */
#define UNIT_V_RL02	(UNIT_V_UF+1)			/* RL01 vs RL02 */
#define UNIT_V_AUTO	(UNIT_V_UF+2)			/* autosize enable */
#define UNIT_W_UF	3				/* user flags width */
#define UNIT_HWLK	(1 << UNIT_V_HWLK)
#define UNIT_RL02	(1 << UNIT_V_RL02)
#define UNIT_AUTO	(1 << UNIT_V_AUTO)

/* Parameters in the unit descriptor */

#define TRK		u3				/* current track */
#define STAT		u4				/* status */

/* RLDS */

#define RLDS_LOAD	0				/* no cartridge */
#define RLDS_LOCK	5				/* lock on */
#define RLDS_BHO	0000010				/* brushes home */
#define RLDS_HDO	0000020				/* heads out */
#define RLDS_CVO	0000040				/* cover open */
#define RLDS_HD		0000100				/* head select */
#define RLDS_DSE	0000400				/* drive select err */
#define RLDS_RL02	0000200				/* RL02 */
#define RLDS_VCK	0001000				/* volume check */
#define RLDS_WGE	0002000				/* write gate err */
#define RLDS_SPE	0004000				/* spin err */
#define RLDS_STO	0010000				/* seek time out */
#define RLDS_WLK	0020000				/* write locked */
#define RLDS_HCE	0040000				/* head current err */
#define RLDS_WDE	0100000				/* write data err */
#define RLDS_ATT	(RLDS_HDO+RLDS_BHO+RLDS_LOCK)	/* attached status */
#define RLDS_UNATT	(RLDS_CVO+RLDS_LOAD)		/* unattached status */
#define RLDS_ERR 	(RLDS_WDE+RLDS_HCE+RLDS_STO+RLDS_SPE+RLDS_WGE+ \
			RLDS_VCK+RLDS_DSE)		/* errors bits */

/* RLCS */

#define RLCS_DRDY	0000001				/* drive ready */
#define RLCS_M_FUNC	0000007				/* function */
#define  RLCS_NOP	0
#define  RLCS_WCHK	1
#define  RLCS_GSTA	2
#define  RLCS_SEEK	3
#define  RLCS_RHDR	4
#define  RLCS_WRITE	5
#define  RLCS_READ	6
#define  RLCS_RNOHDR	7
#define RLCS_V_FUNC	1
#define RLCS_M_MEX	03				/* memory extension */
#define RLCS_V_MEX	4
#define RLCS_MEX	(RLCS_M_MEX << RLCS_V_MEX)
#define RLCS_M_DRIVE	03
#define RLCS_V_DRIVE	8
#define RLCS_INCMP	0002000				/* incomplete */
#define RLCS_CRC	0004000				/* CRC error */
#define RLCS_HDE	0010000				/* header error */
#define RLCS_NXM	0020000				/* non-exist memory */
#define RLCS_DRE	0040000				/* drive error */
#define RLCS_ERR	0100000				/* error summary */
#define RLCS_ALLERR (RLCS_ERR+RLCS_DRE+RLCS_NXM+RLCS_HDE+RLCS_CRC+RLCS_INCMP)
#define RLCS_RW		0001776				/* read/write */
#define GET_FUNC(x)	(((x) >> RLCS_V_FUNC) & RLCS_M_FUNC)
#define GET_DRIVE(x)	(((x) >> RLCS_V_DRIVE) & RLCS_M_DRIVE)

/* RLDA */

#define RLDA_SK_DIR	0000004				/* direction */
#define RLDA_GS_CLR	0000010				/* clear errors */
#define RLDA_SK_HD	0000020				/* head select */

#define RLDA_V_SECT	0				/* sector */
#define RLDA_M_SECT	077
#define RLDA_V_TRACK	6				/* track */
#define RLDA_M_TRACK	01777
#define RLDA_HD0	(0 << RLDA_V_TRACK)
#define RLDA_HD1	(1 << RLDA_V_TRACK)
#define RLDA_V_CYL	7				/* cylinder */
#define RLDA_M_CYL	0777
#define RLDA_TRACK	(RLDA_M_TRACK << RLDA_V_TRACK)
#define RLDA_CYL	(RLDA_M_CYL << RLDA_V_CYL)
#define GET_SECT(x)	(((x) >> RLDA_V_SECT) & RLDA_M_SECT)
#define GET_CYL(x)	(((x) >> RLDA_V_CYL) & RLDA_M_CYL)
#define GET_TRACK(x)	(((x) >> RLDA_V_TRACK) & RLDA_M_TRACK)
#define GET_DA(x)	((GET_TRACK (x) * RL_NUMSC) + GET_SECT (x))

/* RLBA */

#define RLBA_IMP	0177776				/* implemented */

/* RLBAE */

#define RLBAE_IMP	0000077				/* implemented */

extern int int_req;
extern unsigned short M[];
int rlcs = 0, rlba = 0, rlbae = 0, rlda = 0, rlmp = 0, rlmp1 = 0, rlmp2 = 0;
int rl_swait = 10, rl_rwait = 10, rl_stopioe = 1;
int rl_svc (UNIT *uptr);
int rl_reset (DEVICE *dptr);
void rl_set_done (int error);
int rl_boot (int unitno);
int rl_attach (UNIT *uptr, char *cptr);
int rl_set_size (UNIT *uptr, int value);
extern int sim_activate (UNIT *uptr, int interval);
extern int sim_cancel (UNIT *uptr);
extern int sim_is_active (UNIT *uptr);
extern int attach_unit (UNIT *uptr, char *cptr);

/* RL11 data structures

   rl_dev	RL device descriptor
   rl_unit	RL unit list
   rl_reg	RL register list
   rl_mod	RL modifier list
*/

UNIT rl_unit[] = {
	{ UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_RL02, RL02_SIZE) },
	{ UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_RL02, RL02_SIZE) },
	{ UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE, RL01_SIZE) },
	{ UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE, RL01_SIZE) } };

REG rl_reg[] = {
	{ ORDATA (RLCS, rlcs, 16) },
	{ ORDATA (RLDA, rlda, 16) },
	{ ORDATA (RLBA, rlba, 16) },
	{ ORDATA (RLBAE, rlbae, 6) },
	{ ORDATA (RLMP, rlmp, 16) },
	{ ORDATA (RLMP1, rlmp1, 16) },
	{ ORDATA (RLMP2, rlmp2, 16) },
	{ FLDATA (INT, int_req, INT_V_RL) },
	{ FLDATA (ERR, rlcs, CSR_V_ERR) },
	{ FLDATA (DONE, rlcs, CSR_V_DONE) },
	{ FLDATA (IE, rlcs, CSR_V_IE) },
	{ DRDATA (STIME, rl_swait, 24), PV_LEFT },
	{ DRDATA (RTIME, rl_rwait, 24), PV_LEFT },
	{ GRDATA (FLG0, rl_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF), REG_RO },
	{ GRDATA (FLG1, rl_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF), REG_RO },
	{ GRDATA (FLG2, rl_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF), REG_RO },
	{ GRDATA (FLG3, rl_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF), REG_RO },
	{ FLDATA (STOP_IOE, rl_stopioe, 0) },
	{ NULL }  };

MTAB rl_mod[] = {
	{ UNIT_HWLK, 0, "write enabled", "ENABLED", NULL },
	{ UNIT_HWLK, UNIT_HWLK, "write locked", "LOCKED", NULL },
	{ (UNIT_RL02+UNIT_ATT), UNIT_ATT, "RL01", NULL, NULL },
	{ (UNIT_RL02+UNIT_ATT), (UNIT_RL02+UNIT_ATT), "RL02", NULL, NULL },
	{ (UNIT_AUTO+UNIT_RL02+UNIT_ATT), 0, "RL01", NULL, NULL },
	{ (UNIT_AUTO+UNIT_RL02+UNIT_ATT), UNIT_RL02, "RL02", NULL, NULL },
	{ (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL },
	{ UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", &rl_set_size },
	{ (UNIT_AUTO+UNIT_RL02), 0, NULL, "RL01", &rl_set_size },
	{ (UNIT_AUTO+UNIT_RL02), UNIT_RL02, NULL, "RL02", &rl_set_size },
	{ 0 }  };

DEVICE rl_dev = {
	"RL", rl_unit, rl_reg, rl_mod,
	RL_NUMDR, 8, 24, 1, 8, 16,
	NULL, NULL, &rl_reset,
	&rl_boot, &rl_attach, NULL };

/* I/O dispatch routine, I/O addresses 17774400 - 17774407

   17774400	RLCS	read/write
   17774402	RLBA	read/write
   17774404	RLDA	read/write
   17774406	RLMP	read/write
   17774410	RLBAE	read/write
*/

int rl_rd (int *data, int PA, int access)
{
UNIT *uptr;

switch ((PA >> 1) & 07) {				/* decode PA<2:1> */
case 0:							/* RLCS */
	rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX);
	if (rlcs & RLCS_ALLERR) rlcs = rlcs | RLCS_ERR;
	uptr = rl_dev.units + GET_DRIVE (rlcs);
	if (sim_is_active (uptr)) rlcs = rlcs & ~RLCS_DRDY;
	else rlcs = rlcs | RLCS_DRDY;			/* see if ready */
	*data = rlcs;
	return SCPE_OK;
case 1:							/* RLBA */
	*data = rlba & RLBA_IMP;
	return SCPE_OK;
case 2:							/* RLDA */
	*data = rlda;
	return SCPE_OK;
case 3:							/* RLMP */
	*data = rlmp;
	rlmp = rlmp1;					/* ripple data */
	rlmp1 = rlmp2;
	return SCPE_OK;
case 4:							/* RLBAE */
	*data = rlbae & RLBAE_IMP;
	return SCPE_OK;  }				/* end switch */
}							/* end rl_rd */

int rl_wr (int data, int PA, int access)
{
int curr, offs, newc, maxc;
UNIT *uptr;

switch ((PA >> 1) & 07) {				/* decode PA<2:1> */
case 0:							/* RLCS */
	rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX);
	if (rlcs & RLCS_ALLERR) rlcs = rlcs | RLCS_ERR;
	uptr = rl_dev.units + GET_DRIVE (data);		/* get new drive */
	if (sim_is_active (uptr)) rlcs = rlcs & ~RLCS_DRDY;
	else rlcs = rlcs | RLCS_DRDY;			/* see if ready */

	if (access == WRITEB) data = (PA & 1)?
		(rlcs & 0377) | (data << 8): (rlcs & ~0377) | data;
	rlcs = (rlcs & ~RLCS_RW) | (data & RLCS_RW);
	rlbae = (rlbae & ~RLCS_M_MEX) | ((rlcs >> RLCS_V_MEX) & RLCS_M_MEX);
	if (data & CSR_DONE) {				/* ready set? */
		if ((data & CSR_IE) == 0) int_req = int_req & ~INT_RL;
		else if ((rlcs & (CSR_DONE + CSR_IE)) == CSR_DONE)
			int_req = int_req | INT_RL;	
		return SCPE_OK;  }

	int_req = int_req & ~INT_RL;			/* clear interrupt */
	rlcs = rlcs & ~RLCS_ALLERR;			/* clear errors */
	switch (GET_FUNC (rlcs)) {			/* case on RLCS<3:1> */
	case RLCS_NOP:					/* nop */
		rl_set_done (0);
		return SCPE_OK;
	case RLCS_SEEK:					/* seek */
		curr = GET_CYL (uptr -> TRK);		/* current cylinder */
		offs = GET_CYL (rlda);			/* offset */
		if (rlda & RLDA_SK_DIR) {		/* in or out? */
			newc = curr + offs;		/* out */
			maxc = (uptr -> flags & UNIT_RL02)?
				RL_NUMCY * 2: RL_NUMCY;
			if (newc >= maxc) newc = maxc - 1;  }
		else {	newc = curr - offs;		/* in */
			if (newc < 0) newc = 0;  }
		uptr -> TRK = (newc << RLDA_V_CYL) |	/* put on track */
			((rlda & RLDA_SK_HD)? RLDA_HD1: RLDA_HD0);
		sim_activate (uptr, rl_swait * abs (newc - curr));
		return SCPE_OK;
	default:					/* data transfer */
		sim_activate (uptr, rl_swait);		/* activate unit */
		return SCPE_OK;  }			/* end switch func */
	return SCPE_OK;					/* end case RLCS */

case 1:							/* RLBA */
	if (access == WRITEB) data = (PA & 1)?
		(rlba & 0377) | (data << 8): (rlba & ~0377) | data;
	rlba = data & RLBA_IMP;
	return SCPE_OK;
case 2:							/* RLDA */
	if (access == WRITEB) data = (PA & 1)?
		(rlda & 0377) | (data << 8): (rlda & ~0377) | data;
	rlda = data;
	return SCPE_OK;
case 3:							/* RLMP */
	if (access == WRITEB) data = (PA & 1)?
		(rlmp & 0377) | (data << 8): (rlmp & ~0377) | data;
	rlmp = rlmp1 = rlmp2 = data;
	return SCPE_OK;
case 4:							/* RLBAE */
	if (PA & 1) return;
	rlbae = data & RLBAE_IMP;
	rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX);
	return SCPE_OK;  }				/* end switch */
}							/* end rl_wr */

/* Service unit timeout

   If seek in progress, complete seek command
   Else complete data transfer command

   The unit control block contains the function and cylinder for
   the current command.
*/

static unsigned short fill[RL_NUMWD] = { 0 };
int rl_svc (UNIT *uptr)
{
int comp, err, awc, wc, maxwc;
int func, pa, da, fillc;

func = GET_FUNC (rlcs);					/* get function */
if (func == RLCS_GSTA) {				/* get status */
	rlmp = uptr -> STAT | (uptr -> TRK & RLDS_HD) |
		((uptr -> flags & UNIT_ATT)? RLDS_ATT: RLDS_UNATT);
	if (rlda & RLDA_GS_CLR) rlmp = rlmp & ~RLDS_ERR;
	if (uptr -> flags & UNIT_RL02) rlmp = rlmp | RLDS_RL02;
	if (uptr -> flags & UNIT_HWLK) rlmp = rlmp | RLDS_WLK;
	uptr -> STAT = rlmp2 = rlmp1 = rlmp;
	rl_set_done (0);				/* done */
	return SCPE_OK;  }

if ((uptr -> flags & UNIT_ATT) == 0) {			/* attached? */
	rlcs = rlcs & ~RLCS_DRDY;			/* clear drive ready */
	rl_set_done (RLCS_ERR | RLCS_INCMP);		/* flag error */
	return IORETURN (rl_stopioe, SCPE_NOATT);  }

if ((func == RLCS_WRITE) && (uptr -> flags & UNIT_HWLK)) {
	uptr -> STAT = uptr -> STAT | RLDS_WGE;		/* write and locked */
	rl_set_done (RLCS_ERR | RLCS_DRE);
	return SCPE_OK;  }

if (func == RLCS_SEEK) {				/* seek? */
	rl_set_done (0);				/* done */
	return SCPE_OK;  }

if (func == RLCS_RHDR) {				/* read header? */
	rlmp = (uptr -> TRK & RLDA_TRACK) | GET_SECT (rlda);
	rlmp1 = 0;
	rl_set_done (0);				/* done */
	return SCPE_OK;  }

if (((func != RLCS_RNOHDR) && ((uptr -> TRK & RLDA_CYL) != (rlda & RLDA_CYL)))
   || (GET_SECT (rlda) >= RL_NUMSC)) {			/* bad cyl or sector? */
	rl_set_done (RLCS_ERR | RLCS_HDE | RLCS_INCMP);	/* wrong cylinder? */
	return SCPE_OK;  }
	
pa = ((rlbae << 16) | rlba) >> 1;			/* form phys addr */
da = GET_DA (rlda) * RL_NUMWD;				/* get disk addr */
wc = 0200000 - rlmp;					/* get true wc */

maxwc = (RL_NUMSC - GET_SECT (rlda)) * RL_NUMWD;	/* max transfer */
if (wc > maxwc) wc = maxwc;				/* track overrun? */
if ((pa + wc) > RL_MAXMEM) {				/* mem overrun? */
	rlcs = rlcs | RLCS_ERR | RLCS_NXM;
	wc = (RL_MAXMEM - pa);  }
if (wc < 0) {						/* abort transfer? */
	rl_set_done (RLCS_INCMP);
	return SCPE_OK;  }

err = fseek (uptr -> fileref, da * sizeof (short), SEEK_SET);
if ((func >= RLCS_READ) && (err == 0)) {		/* read (no hdr)? */
	awc = fread (&M[pa], sizeof (short), wc, uptr -> fileref);
	for ( ; awc < wc; awc++) M[pa + awc] = 0;
	err = ferror (uptr -> fileref);  }

if ((func == RLCS_WRITE) && (err == 0)) {		/* write? */
	fwrite (&M[pa], sizeof (short), wc, uptr -> fileref);
	err = ferror (uptr -> fileref);
	if ((err == 0) && (fillc = (wc & (RL_NUMWD - 1)))) {
		fwrite (fill, sizeof (short), fillc, uptr -> fileref);
		err = ferror (uptr -> fileref);  }  }

if ((func == RLCS_WCHK) && (err == 0)) {		/* write check? */
	fillc = wc;					/* xfer length */
	for (wc = 0; (err == 0) && (wc < fillc); wc++)  {
		awc = fread (&comp, sizeof (short), 1, uptr -> fileref);
		if (awc == 0) comp = 0;
		if (comp != M[pa + wc]) rlcs = rlcs | RLCS_ERR | RLCS_CRC;  }
	err = ferror (uptr -> fileref);  }

rlmp = (rlmp + wc) & 0177777;				/* final word count */
if (rlmp != 0) rlcs = rlcs | RLCS_ERR | RLCS_INCMP;	/* completed? */
pa = (pa + wc) << 1;					/* final byte addr */
rlbae = (pa >> 16) & RLBAE_IMP;				/* upper 6b */
rlba = pa & RLBA_IMP;					/* lower 16b */
rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX);
rlda = rlda + ((wc + (RL_NUMWD - 1)) / RL_NUMWD);
rl_set_done (0);

if (err != 0) {						/* error? */
	perror ("RL I/O error");
	clearerr (uptr -> fileref);
	return SCPE_IOERR;  }
return SCPE_OK;
}

/* Set done and possibly errors */

void rl_set_done (int status)
{
	rlcs = rlcs | status | CSR_DONE;		/* set done */
	if (rlcs & CSR_IE) int_req = int_req | INT_RL;
	else int_req = int_req & ~INT_RL;
	return;
}

/* Device reset

   Note that the RL11 does NOT recalibrate its drives on RESET
*/

int rl_reset (DEVICE *dptr)
{
int i;
UNIT *uptr;

rlcs = CSR_DONE;
rlda = rlba = rlbae = rlmp = rlmp1 = rlmp2 = 0;
int_req = int_req & ~INT_RL;
for (i = 0; i < RL_NUMDR; i++) {
	uptr = rl_dev.units + i;
	sim_cancel (uptr);
	uptr -> STAT = 0;  }
return SCPE_OK;
}

/* Attach routine */

int rl_attach (UNIT *uptr, char *cptr)
{
int reason;

reason = attach_unit (uptr, cptr);
if ((reason != SCPE_OK) || ((uptr -> flags & UNIT_AUTO) == 0))
	return reason;
uptr -> flags = uptr -> flags & ~UNIT_RL02;
uptr -> capac = RL01_SIZE;
if (fseek (uptr -> fileref, 0, SEEK_END)) return SCPE_OK;
if (ftell (uptr -> fileref) > (RL01_SIZE * sizeof (short))) {
	uptr -> flags = uptr -> flags | UNIT_RL02;
	uptr -> capac = RL02_SIZE;  }
return SCPE_OK;
}

/* Set size routine */

int rl_set_size (UNIT *uptr, int value)
{
if (uptr -> flags & UNIT_ATT) return SCPE_ALATT;
uptr -> capac = (value & UNIT_RL02)? RL02_SIZE: RL01_SIZE;
return SCPE_OK;
}

/* Device bootstrap */

#define BOOT_START 02000		/* start */
#define BOOT_UNIT 02006			/* where to store unit number */
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int))

static int boot_rom[] = {
	0012706, 0002000,		/* MOV #2000, SP */
	0012700, 0000000,		/* MOV #UNIT, R0 */
	0010003,			/* MOV R0, R3 */
	0000303,			/* SWAB R3 */
	0012701, 0174400,		/* MOV #RLCS, R1 	; csr */
	0012761, 0000013, 0000004,	/* MOV #13, 4(R1)	; clr err */
	0052703, 0000004,		/* BIS #4, R3		; unit+gstat */
	0010311,			/* MOV R3, (R1)		; issue cmd */
	0105711,			/* TSTB (R1)		; wait */
	0100376,			/* BPL .-2 */
	0105003,			/* CLRB R3 */
	0052703, 0000010,		/* BIS #10, R3		; unit+rdhdr */
	0010311,			/* MOV R3, (R1)		; issue cmd */
	0105711,			/* TSTB (R1)		; wait */
	0100376,			/* BPL .-2 */
	0016102, 0000006,		/* MOV 6(R1), R2	; get hdr */
	0042702, 0000077,		/* BIC #77, R2		; clr sector */
	0005202,			/* INC R2		; magic bit */
	0010261, 0000004,		/* MOV R2, 4(R1)	; seek to 0 */
	0105003,			/* CLRB R3 */
	0052703, 0000006,		/* BIS #6, R3		; unit+seek */
	0010311,			/* MOV R3, (R1)		; issue cmd */
	0105711,			/* TSTB (R1)		; wait */
	0100376,			/* BPL .-2 */
	0005061, 0000002,		/* CLR 2(R1)		; clr ba */
	0005061, 0000004,		/* CLR 4(R1)		; clr da */
	0012761, 0177000, 0000006,	/* MOV #-512., 6(R1)	; set wc */
	0105003,			/* CLRB R3 */
	0052703, 0000014,		/* BIS #14, R3		; unit+read */
	0010311,			/* MOV R3, (R1)		; issue cmd */
	0105711,			/* TSTB (R1)		; wait */
	0100376,			/* BPL .-2 */
	0042711, 0000377,		/* BIC #377, (R1) */
	0005002,			/* CLR R2 */
	0005003,			/* CLR R3 */
	0005004,			/* CLR R4 */
	0012705, 0062154,		/* MOV "DL, R5 */
	0005007				/* CLR PC */
};

int rl_boot (int unitno)
{
int i;
extern int saved_PC;

for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i];
M[BOOT_UNIT >> 1] = unitno & RLCS_M_DRIVE;
saved_PC = BOOT_START;
return SCPE_OK;
}
