/*
 * prominit.S -- Initialize PROM Monitor
 * copyright (c) 1984  American Information Systems Corporation
 *
 *	Daniel Steinberg
 *	July, 1984
 *
 *	AIS/UNIX-System V.2  code format
 *
 *	This module contains the startup code to initialize the RAM Monitor
 *	and VIOS, regardless of whether it is running in RAM or ROM, as long
 *	as it is loaded above the first block of memory (above GRAIN (32kB)).
 *	It is entered from the promcrt0.S with the following variables set:
 *
 *		R0  - address of start of task image (start of checksum area)
 *
 *
 *	This routine performs the following functions (unrecoverable errors
 *	cause a branch into the RAMless-Monitor):
 *		1) Reset the processor state by disabling interrupts and
 *			special NMIs, setting the configuration bits,
 *			reading the Host CSR register, etc.
 *			If the high byte of the Host CSR is equal to
 *			SPECIAL_RESET (Host requested a soft reset without
 *			clearing memory), jump directly to the RAMless-Monitor.
 *		2) Initialize the UARTS, setting them to 9600 baud.
 *		3) Calculate the checksum, adding all locations from R0 to _end
 *			and comparing with the value at _end.  If it does not
 *			match, print a message and enter the RAMless-Monitor.
 *			If the checksum at _end is zero, skip this step.
 *		4) Set the first block of memory first to ones, then to zeroes,
 *			checking for errors.  If memory does not check out,
 *			print a message and enter the RAMless-Monitor.
 *		5) Allocate memory from the top of the first block of verified
 *			memory to hold a temporary stack.
 *		6) Initialize the AIS/RAM-Monitor trap/interrupt handlers
 *			by establishing two pseudo-Module Table Entries
 *			(for normal operation and interrupt dispatching).
 *			Copy the RAM-Monitor trap/interrupt dispatch table
 *			to the dispatch tables for the VIOS & OS-Kernel.
 *			Set all the dispatch addresses for the OS_User table
 *			to point to a "rett" instruction.  Point the NMI
 *			dispatch address to a local routine that detects
 *			Memory Timeout and Parity errors.  Then, set the
 *			OS-User dispatch table in use.
 *		7) Enable detection of Memory bus errors and test all of
 *			memory, marking good blocks in the Memory Bitmap.
 *			The low block of memory is read; the rest of memory
 *			is set to all ones, then all zeroes.
 *			If a Memory Timeout occurs, and if the address is
 *			not at the start of a memory board segment, then
 *			the current board is very flaky, so the RAMless-Monitor
 *			is entered.
 *			If the current program is executing in RAM, it is not
 *			tested or entered as valid in the Memory Bitmap.
 *		8) Calculate and print the size of valid memory on the console.
 *		9) Then branch to the entry point to initialize the RAM-Monitor
 *			and VIOS and get the ball rolling ('start_VIOS').
 */
	.file	"prominit.S"

/* Include configuration file */
#include "promhdr.h"
#include "3200config.h"

#include "RAMmonaddr.m"


#define PARENABLES (SBUS_TMOENABLE | SBUS_PARENABLE | HOST_TMOENABLE | HOST_PARENABLE)

	.set	BELL,0x07
	.set	CR,0x0D
	.set	LF,0x0A
	.set	ESC,0x1B
	.set	CTRL_Q,0x11
	.set	CTRL_S,0x13
	.set	DEL,0x7F

/* Define the address of the checksum of the PROM */
#define _checksum _edata

/* Define the CPU configuration flag */
#ifdef MMU /********************** MMU Available ***********************/
#define Conf i,m,f
#else
#define Conf i,f
#endif /************************** MMU Available ***********************/

	.text
/*  */
	.globl	prominit
prominit:
	bicpsrw	$(PSR_I | PSR_S | PSR_U)	/* disable interrupts */
	setcfg	[Conf]		/* set configuration */

#ifdef IMM_AS /*************** IMMEDIATE ADDRESS FLAG ******************/
	lprd	intbase,$ROMVECTORS	/* set AIS/ROM-Monitor vector table */
#else  /********************** IMMEDIATE ADDRESS FLAG ******************/
	addr	ROMVECTORS,r5	/* set AIS/ROM-Monitor dispatch table */
	lprd	intbase,r5
#endif /********************** IMMEDIATE ADDRESS FLAG ******************/

	lprd	sp,$256		/* set temporary stack */

	movqd	$0,r5		/* assume no SPECIAL_RESET */


    /* disable memory error NMIs and PICK byte-swapping */

#ifdef REV_2A /****************** CPU REVISION-2A **********************/
	movb	$(PARENABLES | PICK_ENABLE), @(UART_PARENABLE + U_CLROUT)

	tbitb	$(1 << CSR_I_FLG),@(INTCTLR+ICU_PDAT)	/* Host CSR written? */
	bfc	nocsr		/* if not, ignore its current contents */
#endif /************************* CPU REVISION-2A **********************/

/****	movw	@HOST_CSR,r5	/* read Host CSR and clear CSR interrupt */
nocsr:				/* (or ignore current CSR) */


#ifdef MMU /********************** MMU Available ***********************/
/* Disable address translation, breakpoints, tracing, etc. */
	lmr	msr,$0		/* clear all special bits */
#endif /************************** MMU Available ***********************/
/*  */
/*
 * Initialize UART-0 to 9600 baud,
 * check to see if the Host CSR indicated a non-destructive reset,
 * and, if not, initialize the rest of the UARTS.
 */

#ifdef REV_2A /****************** CPU REVISION-2A **********************/

initua:				/* Initialize UART-A */
	addr	@UARTA,r6	/* set base address of first DUART */
	addr	initub,r7	/* set return address */
	br	initduart

initub:				/* Initialize UART-B */
	addr	@UARTB,r6	/* set base address of second DUART */
	addr	inittys,r7	/* set return address */

initduart:			/* initialize duart and return thru R7 */
	movqb	$0,U_OUTCFG(r6)			/* program output pins */
	movb	$(U_BAUDMODE),U_AUXCTL(r6)	/* set baud rate bank */
	movqb	$0,U_IMR(r6)			/* clear interrupt mask reg */
	cmpqb	$0,U_STOP_CTR(r6)		/* stop internal counter */
	jump	0(r7)		/* return */

inittys:
	movb	$(DTR0),@(U_SETOUT+UART_MAPENABLE)	/* DTR on console */
	movb	$(DTR1|DTR2|DTR3),@(U_CLROUT+UART_MAPENABLE)	/* only */

	addr	@UART0,r6	/* initialize UART-0 */
	addr	chkcsr,r7	/* set return address */

inituart9600:			/* initialize uart and return thru R7 */
	movb	$(U_RXDISABLE | U_TXDISABLE | U_MRRESET),U_CMD(r6)
	movb	$(U_ERRESET),U_CMD(r6)		/* reset error flags */
	movb	$(U_BRKRESET),U_CMD(r6)		/* reset break state */
	movb	$(U_TXRESET),U_CMD(r6)		/* reset transmitter */
	movb	$(U_RXRESET),U_CMD(r6)		/* reset receiver */
	movb	$(U_8B | U_NOPARITY | U_CHRERR | U_RXRTSOFF),U_MODE(r6)
	movb	$(U_1STOP | U_CTSOFF | U_TXRTSOFF | U_NMODE),U_MODE(r6)
	movb	$(U_TX9600 | U_RX9600),U_BAUD(r6)	/* set 9600 baud */
	movb	$(U_RXENABLE | U_TXENABLE),U_CMD(r6)	/* enable uart */
	jump	0(r7)		/* return */

/*
 * Check to see if the Host CSR contained the SPECIAL_RESET code
 * to cause a non-destructive reset; if so, jump to RAMless-Monitor.
 */
chkcsr:	lshd	$-8,r5			/* shift high byte into low */
	cmpb	r5,$SPECIAL_RESET	/* Host issued non-destructive reset? */
	beq	spcreset		/* yes...go to RAMless-Monitor */


    /* Print a little to show that we are here */

	addr	_productmsg,r6	/* set address of special message */
	addr	initu1,r7	/* set return address */
	br	printmsg	/* and print message on uart-0 */

initu1:				/* Initialize UART-1 */
	addr	@UART1,r6	/* set base address */
	addr	initu2,r7	/* set return address */
	br	inituart9600	/* initialize uart and return */

initu2:				/* Initialize UART-2 */
	addr	@UART2,r6	/* set base address */
	addr	initu2b,r7	/* set return address */
	br	inituart9600	/* initialize uart */
initu2b:
	movb	$(U_TX1200 | U_RX1200),U_BAUD(r6)	/* set 1200 baud */

initu3:				/* Initialize UART-3 */
	addr	@UART3,r6	/* set base address */
	addr	initu3b,r7	/* set return address */
	br	inituart9600
initu3b:
	movb	$(U_TX1200 | U_RX1200),U_BAUD(r6)	/* set 1200 baud */

#endif /************************* CPU REVISION-2A **********************/

udone:				/* UARTs initialized */


    /* Initialize Interrupt Control Unit */

	addr	@INTCTLR,r6			/* point to ICU */
	movb	$(I_COUTD | I_FRZ),ICU_MCTL(r6)	/* freeze it */
	movqb	$-1,ICU_IMSK0(r6)		/* mask all ints */
	movqb	$-1,ICU_IMSK1(r6)
	movb	$(I_ICLRALL),ICU_IPND0(r6)	/* clear all pending ints */
	movb	$(I_ICLRALL),ICU_IPND1(r6)
	movqb	$0,ICU_CCTL(r6)			/* shut off counter */
	movqb	$0,ICU_CICTL(r6)		/* and ctr interrupts */
	movqb	$0,ICU_OCASN(r6)		/* no clock output */
	movqb	$-1,ICU_IPS(r6)			/* all pins are ints for now */
	movqb	$-1,ICU_PDIR(r6)		/* all pins are input */
	movqb	$0,ICU_ISRV0(r6)		/* clear in-service bits */
	movqb	$0,ICU_ISRV1(r6)
						/* leave ICU frozen */
/*  */
/*
 * Check the checksum of the code/initialized data segment
 * R0 contains the start address
 * _checksum is the end address / address of the checksum
 */
	addr	_checksum,r2	/* get address of end of data section */
	movqd	$0,r7		/* checksum accumulator */
	movd	r0,r6		/* code pointer */

cksum:	addd	0(r6),r7	/* calculate checksum */
	addqd	$4,r6		/* increment pointer */
	cmpd	r6,r2		/* reached the end yet? */
	blo	cksum		/* no....branch */
	cmpqd	$0,0(r6)	/* yes...is there a checksum there ? */
	beq	ckdisable	/*       no....just forget it */
	cmpd	r7,0(r6)	/*       yes...checksum match? */
ckdisable:
	bne	ckfail		/*             no....error */

    /* Make sure PROM code (R0 thru R2) does not intersect RAM data area */

#ifdef IMM_AS /*************** IMMEDIATE ADDRESS FLAG ******************/
	cmpd	r0,$A_ramend	/* executing beyond RAM? */
	bhs	ldok		/* yes...all's well */
	cmpd	r2,$A_ramstart	/* no....checksum before start of RAM? */
	bhs	ldfail		/*       no....task improperly loaded */
#else  /********************** IMMEDIATE ADDRESS FLAG ******************/
	addr	@A_ramstart,r6	/* start of memory to test */
	addr	@A_ramend,r5	/* end of memory to test */
	cmpd	r0,r5		/* executing beyond RAM? */
	bhs	ldok		/* yes...all's well */
	cmpd	r2,r6		/* no....checksum before start of RAM? */
	bhs	ldfail		/*       no....task improperly loaded */
#endif /********************** IMMEDIATE ADDRESS FLAG ******************/
ldok:

/*
 * Check that the absolute-addressed RAM area is good
 * [Checks all memory until end of Absolute-Addressed RAM]
 * Copy all ones, then clear it all
 */
	addr	@A_zeroramstart,r6	/* first address to test */
	addr	@A_ramend,r7		/* last address to test */
	subd	r6,r7			/* size to test */
	ashd	$-4,r7			/* divide by 16 (block-move size) */

	movb	$(ROM_MAP),@(UART_MAPENABLE + U_SETOUT)	/* enable RAM */

ck0mem:	addr	ONES,r5		/* set address of 16 bytes of ones */
	movmd	0(r5),0(r6),$4	/* copy all ones to low memory */
	cmpmd	0(r5),0(r6),$4	/* test bytes just copied */
	bne	memfail		/* if not correct, quit */
	addr	ZEROES,r5	/* set address of 16 bytes of zeroes */
	movmd	0(r5),0(r6),$4	/* clear low memory */
	cmpmd	0(r5),0(r6),$4	/* test bytes just copied */
	bne	memfail		/* if not correct, quit */
	addd	$16,r6		/* increment pointer */
	acbd	$-1,r7,ck0mem	/* and loop through first block */

/*
 * The absolute-addressed RAM memory is probably ok.
 * Initialize trap/interrupt handling.
 * Use the end of the tested RAM for a temporary Program Stack.
 */

	lprd	sp,r6			/* stack is here til memory is mapped */
	movd	r0,@A_reset_addr	/* save start address */
	jsr	RAMtrapinit		/* init trap handling (ramtrap.S) */

    /* copy the RAM-Monitor interrupt dispatch table to the VIOS table */

	addr	@A_mondispatch,r1	/* RAM-Monitor dispatch table addr */
	addr	@A_viosdispatch,r2	/* VIOS dispatch table addr */
	addr	@(TRAPS+VECTORS),r0	/* set number of vectors to copy */
	movsd				/* copy table */

	addr	@(A_oskdispatch-XPD_SIZE),r2	/* addr of OS-Kernel disp tbl */
	addr	@TRAPS,r0		/* fill in trap dispatches */
clrvecs:
	addr	no_trap,r2[r0:d]	/* copy address of 'rett' all over */
	acbd	$-1,r0,clrvecs		/* loop thru all traps */

	addqd	$XPD_SIZE,r2		/* set real dispatch table address */
	addr	parity_check,I_NMI(r2)	/* set memory check entry point */
	movd	r2,@A_state		/* set OS-Kernel table as current */

    /* Print "mapping memory..." */

	addr	_memtestmsg,tos	/* print start of memory test message */
	jsr	_printf		/* go print it out */
	adjspb	$-4		/* pop argument */

	jsr	chkrcvesc	/* did we want to abort initialization */

/*  */
/*
 * Check all addressible memory, filling in the Memory Bitmap.
 * Absolute-addressed RAM is Read-only and, if the code is executing from
 * RAM, the current code block is not checked or marked available.
 * If a memory address does not respond, 'parity_check' sets the
 * Flag Bit.  If there is a parity error, the Carry Bit is set.
 */
	movd	@A_reset_addr,r0	/* R0 has start address of PROM code */
	addr	_checksum,r2		/* R2 has end address of PROM code */
	addr	@A_membitmap,r7		/* R7 points to Memory Bitmap */
	movqd	$0,r6			/* R6 has memory test address */

    /* Enable parity error detection and memory timeout */

#ifdef REV_2A /****************** CPU REVISION-2A **********************/
	movb	$(SBUS_TMOENABLE | SBUS_PARENABLE),@(UART_PARENABLE + U_SETOUT)
#endif /************************* CPU REVISION-2A **********************/

nxtram:	cmpd	r6,$ENDMEM	/* Reached the end of addressible memory? */
	bhs	rammapped	/* yes...continue below */
	movd	r6,r5		/* Copy address of test start */
	addd	$GRAIN,r5	/* Calculate address of test end */

qualify_zeroram:
	cmpd	r6,$A_zeroramend	/* testing in low memory RAM? */
	blo	rmemtst		/* yes...test read-only */

qualify_ram:
#ifdef IMM_AS /*************** IMMEDIATE ADDRESS FLAG ******************/
	cmpd	r6,$A_ramend	/* Checking in absolute RAM area? */
	bhs	qualify_rom	/* no */
	cmpd	r5,$A_ramstart	/* End of test intersects RAM area? */
	bhi	rmemtst		/* yes...test memory read-only */
#else  /********************** IMMEDIATE ADDRESS FLAG ******************/
	addr	@A_ramend,r4
	cmpd	r6,r4
	bhs	qualify_rom
	addr	@A_ramstart,r4
	cmpd	r5,r4
	bhi	rmemtst
#endif /********************** IMMEDIATE ADDRESS FLAG ******************/

qualify_rom:
	cmpd	r6,r2		/* Checking in current code area? */
	bhi	wmemtst		/* no....go test this segment */
	cmpd	r5,r0		/* End of test intersects code segment? */
	bhi	rmemtst		/* yes...test memory read-only */

wmemtst:			/* Write memory test */
	jsr	chkrcvesc	/* do we want to abort? */
	addr	@(GRAIN/16),r5	/* set number of 16-byte segments to check */

wtloop:	bicpsrb	$(PSR_F | PSR_C)	/* clear Flag and Carry bits */
	addr	ONES,r4		/* set source address */
	movmd	0(r4),0(r6),$4	/* copy ones to memory */
	cmpmd	0(r4),0(r6),$4	/* did it work? */
	bne	badparity	/* nope (either data error or no board here) */
	addr	ZEROES,r4	/* set address of zeroes to clear memory */
	movmd	0(r4),0(r6),$4	/* copy zeroes to memory */
	cmpmd	0(r4),0(r6),$4	/* did it work? */
	bne	badparity	/* nope */
	bcs	badparity	/* skip if parity errors during all that */
	addd	$16,r6		/* point to next segment */
	acbd	$-1,r5,wtloop	/* and continue loop */

ramok:	movd	r6,r4		/* That block of RAM was ok */
	subd	$GRAIN,r4	/* point to start of it */
	divd	$GRAIN,r4	/* convert to an offset */
	sbitd	r4,0(r7)	/* and set the bit in the Memory Bitmap */
	br	nxtram

rmemtst:			/* Read-only memory test */
	jsr	chkrcvesc	/* do we want to abort? */
	addr	@(GRAIN/32),r5	/* set number of 32-byte segments to check */

rtloop:	bicpsrb	$(PSR_F | PSR_C)	/* Clear Flag and Carry Bits */
	cmpmd	0(r6),16(r6),$4	/* check two sets of 16-bytes */
	bcs	memfail		/* Parity error or Timeout in low memory */
	addd	$32,r6		/* increment ptr */
	acbd	$-1,r5,rtloop	/* decrement counter and loop */
	br	nxtram		/* that was ok, but don't count as free */

/*
 * Memory errors occurred...if it was a simple parity error, point
 * to the next block of RAM and continue the test.  If there was
 * a timeout on an address that was not the beginning of a board,
 * then the current memory board is flaky so just go to the RAMless-Monitor.
 * Otherwise, add the board grain size to the memory pointer and continue.
 */
badparity:
	bfs	noboard		/* timeout takes priority */
	
	/***			/* print message ? */

	andd	$~(GRAIN-1),r6	/* set address of start of this block */
	addd	$GRAIN,r6	/* Skip this block of memory */
	br	nxtram


noboard:
	movd	r6,r5		/* copy address of error */
	andd	$~(BOARDGRAIN-1),r5	/* set address of start of 1/2mB */
	cmpd	r5,r6		/* address right at possible start of board? */
	bne	badboard	/* no....board is flaky */
	addd	$BOARDGRAIN,r6	/* yes...try next possible board address */
	br	nxtram		/* and continue checking */


/*
 * Parity error NMI entry point
 * Sets Flag bit and Carry bit if memory timeout
 * Sets Carry bit if parity error
 */
parity_check:
	save	[r0,r1,r2]	/* save working registers */

#ifdef REV_2A /****************** CPU REVISION-2A **********************/
	tbitb	$B_SBUS_TIMEOUT,@(UART_TMODETECT + U_INPORT)
	bfs	no_timeout	/* memory timeout did not occur */
	orb	$(PSR_F | PSR_C),6(sp)	/* set flag & carry if timeout */
	br	reset_both

no_timeout:
	tbitb	$B_SBUS_PARITY,@(UART_TMODETECT + U_INPORT)
	bfc	par		/* parity error occurred */

#ifdef DEBUG3   /*************************************************************/
				/* NMI occurred with no apparent cause */
	addr	_badnmimsg,tos	/* set msg address */
	jsr	_printf
	adjspb	$-4		/* pop arg */
#endif /* DEBUG3 *************************************************************/
	br	reset_both

par:
	orb	$PSR_C,6(sp)	/* set carry bit to indicate parity error */

    /* print message and address of parity error */
	bsr	parity_message

reset_both:
    /* reset and then enable memory timeout */
/*	movb	$(SBUS_TMOENABLE),@(UART_PARENABLE + U_CLROUT)	*/
/*	movb	$(SBUS_TMOENABLE),@(UART_PARENABLE + U_SETOUT)	*/
reset_parity:
    /* reset and then enable parity */
/*	movb	$(SBUS_PARENABLE),@(UART_PARENABLE + U_CLROUT)	*/
/*	movb	$(SBUS_PARENABLE),@(UART_PARENABLE + U_SETOUT)	*/

	movb	$(SBUS_PARENABLE|SBUS_TMOENABLE),@(UART_PARENABLE + U_CLROUT)
	movb	$(SBUS_PARENABLE|SBUS_TMOENABLE),@(UART_PARENABLE + U_SETOUT)

#endif /************************* CPU REVISION-2A **********************/

	restore	[r0,r1,r2]	/* restore saved regs */
no_trap:			/* entry point for NOP traps */
	rett	$0		/* return from the trap */

/* parity_message() -- Print parity error message */
	.globl	parity_message
parity_message:
	movd	@(FAILURE),tos	/* push failure register contents */
	bicd	$0xfe000000,tos	/* mask all but address */
	addr	_parerrmsg,tos	/* push parity error msg address */
	jsr	_printf		/* hopefully, there's enough memory for this */
	adjspb	$-8		/* pop args */
	ret	$0
/*  */
    /* RAM is now mapped in the Memory Bitmap pointed to by R7 */
rammapped:

    /* Calculate the size of good memory and print it out */

	addr	@(MAPSIZ/4),r5	/* number of doubles in Memory Bitmap */
	addr	@A_ramgrains,r4	/* init bit ctr (add size of Absolute RAM) */
	addqd	$A_zeroramgrains,r4	/* add in size of Zero-RAM */
fndnxtdbl:
	movqd	$0,r6		/* init offset */
fndnxtbit:
	ffsd	0(r7),r6	/* look for first set bit in dblword at R7 */
	bfs	trynxtdbl	/* none in this double */
	addqd	$1,r4		/* got one here */
	addqd	$1,r6		/* increment offset past bit found last */
	cmpd	r6,$32		/* gone too far? */
	blo	fndnxtbit	/* no....try again */
trynxtdbl:
	addd	$4,r7		/* point to next double in bitmap */
	acbd	$-1,r5,fndnxtdbl  /* and try all the bits in it */
	muld	$GRAIN,r4	/* convert bit count to memory size */
	movd	r4,@A_memsize	/* and save it away */

    /* Now print memory size to terminal */

	movd	r4,tos		/* set memory size */
	addr	_memcntmsg,tos	/* set the address of the format string */
	jsr	_printf		/* and print it out */

#ifdef REV_2A /****************** CPU REVISION-2A **********************/
    /* Enable Host Bus mapping */
	movqb	$HOST_MEMENABLE, @(UART_MAPENABLE + U_SETOUT)
#endif /************************* CPU REVISION-2A **********************/

/*
 * Transfer control to 'start_VIOS' to dispatch to either the RAM-Monitor
 * or the VIOS.
 */
	jump	start_VIOS		/* Go boot something or other */
/*  */
/* Errors that cause a branch into the RAMless-Monitor */

badboard:
	addr	_badboardmsg,r6	/* Memory board is intermittent */
	br	fatalerr

ckfail:	addr	_ckfailmsg,r6	/* Checksum failure */
	br	fatalerr

ldfail:	addr	_ldfailmsg,r6	/* Program loaded at bad address */
	br	fatalerr

spcreset:
	addr	_spcresetmsg,r6	/* Non-destructive reset from Host */
	br	fatalerr

memfail:
	addr	_memfailmsg,r6	/* Low memory not there or no good */

fatalerr:		/* Print msg at R6 and jump to RAMless-Monitor */
	addr	_ramless_monitor,r7	/* set return address for 'printmsg' */

	cmpd	r0,$ROMSTART		/* running in PROM? */
	bne	printmsg		/* no...leave RAM turned on */
	movb	$(ROM_MAP),@(UART_MAPENABLE + U_CLROUT)	/* disable RAM */

/*
 * printmsg -- subroutine to print a message on UART-0
 *	in:	R6	ASCIZ message address
 *	in:	R7	return address
 *
 * Prints the string at R6 on UART-0.  Recognition of CTRL-S is disabled.
 */
	.globl	printmsg
printmsg:
	cmpqb	$0,0(r6)	/* end of string yet? */
	bne	nxtchr		/* no */
	jump	0(r7)		/* yes...return */

nxtchr:	tbitb	$U_B_TXRDY,@(UART0+U_STAT)	/* Transmitter empty? */
	bfc	nxtchr			/* no....hang until it is */
	movb	0(r6),@(UART0+U_ODATA)	/* yes...write next character */
	addqd	$1,r6			/* increment string ptr */
	br	printmsg		/* and loop thru message */


/*
 * chkrcvesc
 * check if we have recieved an esc character
 * if so branch to the ramless monitor
 */
chkrcvesc:
	enter	[r0],$0
	tbitb	$U_B_RXRDY,@(UART0+U_STAT)
	bfc	oktoret
	movb	@(UART0+U_IDATA),r0	/* read an input character */
	andb	$0x7f,r0
	cmpb	r0,$ESC			/* is it an escape? */
	beq	abostart		/* yes, abort everything NOW */
oktoret:
	exit	[r0]			/* otherwise return quietly */
	ret	$0
abostart:
	addr	_bootabomsg,tos		/* print ESC - boot aborted */
	jsr	_printf
	adjspb	$-4
	jump	_ramless_monitor



    /* Define blocks of zeroes and ones to copy */
	.globl	ZEROES
	.globl	ONES

ZEROES:	.double	0, 0, 0, 0
ONES:	.double	0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF

