/*****************************************************************************
*           Change Log
*  Date     | Change
*-----------+-----------------------------------------------------------------
* 16-Nov-85 | [1.51] Created
* 24-Nov-85 | [1.105] Added display-char mode
* 25-Nov-85 | [1.135] Modified to allow single-cycle execution of instructions
* 11-Dec-85 | [1.165] Use ~, not !, for 'not' (bitwise, not logical)
* 12-Dec-85 | [1.171] Implemented adr_stop mode; just like single step
*           | except for checking of address
* 12-Dec-85 | [1.175] Removed length display to length.c
* 12-Dec-85 | [1.179] Clear instruction length light error
* 12-Dec-85 | [1.179] Set and clear address light as appropriate
* 13-Dec-85 | [1.181] Added bad_address_r for MRCM check
* 14-Dec-85 | [1.191] in diagnostic output, print d-char using bcd_to_ascii
* 15-Dec-85 | [1.199] Added logic state L, call display of it in display_state
* 15-Dec-85 | [1.201] Added compare_state
* 17-Dec-85 | [1.212] Added overflow indicator
* 20-Dec-85 | [1.221] set_X => show_X for various X
* 24-Dec-85 | [1.231] Include select.h for selective trace option; invoke
*           | selective trace if required.
* 27-Dec-85 | [1.254] print out d_char for instrs of length 2
* 29-Dec-85 | [1.273] Always set old_diagnostics because we always reset it!
* 30-Dec-85 | [1.283] After char_display mode, retain display of character
* 30-Dec-85 | [1.283] Added detection for slow execution mode, partial panel
*           | display update
* 24-Jan-86 | [1.314] check_left_mouse_button => check_mouse_halt
* 25-Jan-86 | [1.347] Implemented alter-plus-one
* 25-Feb-86 | [1.378] Include compare indicators in slow display
* 25-Feb-86 | [1.379] include<> => include ""
* 25-Feb-86 | [1.381] Call x_show instead of x_push in partial display mode
* 25-Feb-86 | [1.382] "partial display" now displays everything...	
* 25-Feb-86 | [1.382] include logic.h and initialize compare indicator to
*           | B=A
* 25-Feb-86 | [1.384] Do not clear I_cycle until partial display is done
* 29-Jul-86 | [1.385] Added lamp_test mode
* 30-Jul-86 | [1.402] Do not set I_cycle to 0 unless setting fetch_complete
*           | false (in reverting from single cycle mode to I/EX or RUN
*           | modes).  Added additional diagnostics about mode reversion
* 30-Jul-86 | [1.403] If diagnostics on, poll at the end of each interpreter
*           | cycle.
* 30-Jul-86 | [1.404] Before reading card on machine load, zero out locations
*           | 1..80
* 31-Jul-86 | [1.405] Made char variables unsigned
* 31-Jul-86 | [1.406] Changed _push calls to internal calls so internal
*           | state changes don't appear in CE log
* 31-Jul-86 | [1.409] In addr stop mode, set run to execute result
*  6-Aug-86 | [1.410] Include bcd.h
*  6-Aug-86 | [1.410] bcd_to_ASCII -> bcd_to_ascii
*  6-Aug-86 | [1.410] 'opcode' in 'execute' is now unsigned char
* 18-Nov-91 | [1.428] <jmn> memory.h => mem1401.h, avoid ANSI name
* 23-Dec-91 | [1.519] <jmn> added 'zip' test to suppress all active
*           | displays at full execute speed
* 23-Dec-91 | [1.524] <jmn> turn off stack checking on bad_address
* 22-Dec-94 | [1.600J] JRJ  initialize io data state (maybe move this later)
*****************************************************************************/

/*****************************************************************************
				 1401 Emulator

			       Machine Interface

This module interprets control panel actions

*****************************************************************************/
#include "stdio.h"
#include "boolean.h"

#include "btypes.h"
#include "scdspmsg.h"

#include "modes.h"
#include "alerts.h"
#include "button.h"
#include "mem1401.h"
#include "mach.h"
#include "panel.h"
#include "dispatch.h"
#include "length.h"
#include "enter.h"
#include "ifetch.h"
#include "alert.h"
#include "diag.h"
#include "select.h"
#include "logic.h"
#include "bcd.h"
#include "ifetch.h"
#include "data.h"
#include "kb.h"
#include "scan.h"
#include "addr.h"
#include "poll.h"
#include "cdr.h"
#include "pr.h"
#include "print.h"
#include "printers.h"
#include "io.h"

extern button A_button;
extern button B_button;
extern button I_button;
extern void set_wm();
extern char * mode_decode();
extern char * single_cycle_decode();
extern boolean slow;
extern boolean zip;

/*****************************************************************************
			      1401 MACHINE STATE
*****************************************************************************/

static boolean run;
int A_addr;	/* A-address register */
int B_addr;	/* B-address register */
int I_addr;	/* I-address register */
int NI_addr;	/* Next-I-address register (hidden register) */
int I_cycle = 0; /* Instruction cycle counter */
unsigned char OP = 0;	/* opcode */
unsigned char B = 0;	/* B -data register */
unsigned char A = 0;	/* A -data register */
unsigned char d_char;	/* d-character modified */
unsigned char L = 0;	/* Logic output */

extern int current_mode;	/* MODE switch setting */
int cycle;	/* Current type of cycle: cycle_I, cycle_A, cycle_B
		   (controls which light will be displayed) */
unsigned char B_char_displayed;		/* Last char displayed by char_disp mode */
int single_cycle_state;
boolean reset_mandatory = false;	/* if true, nothing can happen */
boolean fetch_complete = false;
int compare_state = logic_BEQA;
boolean overflow=false;	/* overflow indicator */

int cycle_count = 0;		/* used to update display every cycle_mod
				   instructions
				*/
boolean lamp_test = false;	/* lamp test mode */

boolean io_index_inhibit = false;
int io_device = 0;
int io_unit = 0;

#define cycle_mod 255		/* power of 2 minus 1 */

/****************************************************************************
*                               partial_display
* Effect:
*       Displays a subset of the machine state; slows down execution but
*	adds corroborative detail to an otherwise bald and unconvincing
*	simulation
****************************************************************************/

void partial_display()
    {
     show_O(OP);
     show_A(A);
     show_B(B);
     show_compare();
     show_ovf(overflow);
     show_logic_output(L);
     show_length(I_cycle);
     switch(cycle)
        { /* cycle display */
	 case cycle_none:
	 	break;
	 case cycle_I:
	 	i_show();
		break;
	 case cycle_A:
	 	a_show();
		break;
	 case cycle_B:
	 	b_show();
		break;
	 default: 
	 	i_show();
		break;
	} /* cycle display */

    }

/****************************************************************************
*                                display_state
* Effect: 
*       Displays machine state
****************************************************************************/

void display_state()
    {
     show_A(A);
     show_O(OP);
     show_B(B_char_displayed);
     show_logic_output(L);
     show_ovf(overflow);
     show_compare();
     switch(cycle)
        { /* cycle display */
	 case cycle_none:
	 	break;
	 case cycle_I:
	 	i_show();
		break;
	 case cycle_A:
	 	a_show();
		break;
	 case cycle_B:
	 	b_show();
		break;
	} /* cycle display */
     show_length(I_cycle);
    }

/****************************************************************************
*                                    stop
* Effect: 
*       Handles push of STOP button
****************************************************************************/

void stop()
    {
     run = false;
    }

/****************************************************************************
*                                    start
* Effect: 
*       Handles all the cases of the 'start' button
****************************************************************************/

#pragma optimize("t",on)
void start()
    {
     int stop_addr;		/* stop address for adr stop mode */

     run = true;

     if(reset_mandatory)
        { /* do nothing */
	 wait_for_mouse_up();
	 stop_execute();
	 return;
	} /* do nothing */

     show_mouse_cursor();

     stop_addr = get_address_switches();	/* in case adr stop mode */

     while(run)
         { /* Run Loop */
	  boolean trun;

	  if(check_mouse_halt()) 
	     break;
	  if(reset_mandatory)
	     { /* hit error */
	      break;
	     } /* hit error */

	   switch(current_mode)
	      { /* mode decode */
/* run */      case mode_run:
			cancel_alter_plus_one();
			if(fetch_complete) 
			   { /* do it */
			    switch(single_cycle_state)
			       { /* state decode */
			        
				case single_cycle_start:
						single_cycle_state = single_cycle_run;
						/* no break */
				case single_cycle_run:
						trun = execute();
						fetch_complete = false;
						break;
				default:
				 /* We have switched state from sing cyc to
				    run in the middle of executing
				    an instruction.  We have to complete
				    this instruction in single-cycle mode
				    before we can let the state revert
				    to a run state
				  */

				  if(diagnostics_on)
				     { /* tell */
				      sprintf(diag_buffer,"**Switched to RUN from sing_cyc");
				      tell(diag_buffer);
				     } /* tell */

				  while(trun = execute())
				     { /* execution continues */
				      if(check_mouse_halt())
				         { /* quit */
					  wait_for_mouse_up();
					  break;
					 } /* quit */
				      if(single_cycle_state == single_cycle_complete)
				         { /* done */
					  single_cycle_state = single_cycle_run;
					  if(diagnostics_on)
					     { /* resume */
					      sprintf(diag_buffer,"**execution stablized, reverting to RUN");
					      tell(diag_buffer);
					     } /* resume */
					  break;
					 } /* done */

				     } /* execution continues */
			       } /* state decode */
			    run &= trun;
			    B_char_displayed = B;
			    if(!zip &&
			       	(slow || ((cycle_count++ & cycle_mod) == 0)) )
			       		partial_display();
			    /* must do partial_display before I_cycle
			       cleared 
			    */
			    I_cycle = 0;

			   } /* do it */
			else
			   { /* fetch it */
			    I_addr = NI_addr;
			    fetch_complete = ifetch();
			    single_cycle_state = single_cycle_run;
			    NI_addr = I_addr;
			    B_char_displayed = B;
			    if(slow) 
			       partial_display();
			   } /* fetch it */
			break;
/*--------------------------------------------------------------------------*/
/* I/EX */     case mode_i_ex:
			cancel_alter_plus_one();
	       		if(fetch_complete) 
			   { /* execute /EX */
			    
			    switch(single_cycle_state)
			       { /* state decode */
			        
				case single_cycle_start:
					single_cycle_state = single_cycle_run;
					/* no break */
				case single_cycle_run:
					fetch_complete = false;
					trun = execute();
					break;
				default:
				 /* We have switched state from sing cyc to
				    I/EX in the middle of executing
				    an instruction.  We have to complete
				    this instruction in single-cycle mode
				    before we can let the state revert
				    to a run state
				  */

				  if(diagnostics_on)
				     { /* tell */
				      sprintf(diag_buffer,"**Switched to I/EX from sing_cyc");
				      tell(diag_buffer);
				     } /* tell */

				  while(trun = execute())
				     { /* execution continues */
				      if(check_mouse_halt())
				         { /* quit */
					  wait_for_mouse_up();
					  break;
					 } /* quit */
				      if(single_cycle_state == single_cycle_complete)
				         { /* done */
					  single_cycle_state = single_cycle_run;
					  if(diagnostics_on)
					     { /* resume */
					      sprintf(diag_buffer,"**execution stablized, reverting to I/EX");
					      tell(diag_buffer);
					     } /* resume */
					  break;
					 } /* done */
				     } /* execution continues */
				  break;
			       } /* state decode */

			    run &= trun;
			    if(single_cycle_state == single_cycle_complete)
			       { /* done with instr */
				I_cycle = 0;
			    	fetch_complete = false;
			       } /* done with instr */
			    B_char_displayed = B;
			   } /* execute /EX */
			else
			   { /* execute I/ */
			    I_addr = NI_addr;
			    fetch_complete = ifetch();

			    /* 
			       We set this state here in case the user
			       switches modes to sing/cyc after the
			       I/EX fetch has completed; in this case
			       the state will be right for the first
			       execute cycle under single cycle mode.
			       If we continue in I/EX mode, the
			       state will be set to 'single_cycle_run'
			       just before we call execution
			    */
			    single_cycle_state = single_cycle_start;

			    NI_addr = I_addr;
			    B_char_displayed = B;
			   } /* execute I/ */
			run = false;
			break;
/*--------------------------------------------------------------------------*/
/* adr_stop */ case mode_adr_stop:
			cancel_alter_plus_one();
	       		if(fetch_complete) 
			   {
			    if(B_addr == stop_addr)
			       { /* B-stop */
			        if(diagnostics_on) 
				   tell("B-ADDRESS STOP!");
				run = false;
			       } /* B-stop */
			    if(A_addr == stop_addr)
			       { /* A-stop */
			        if(diagnostics_on) 
				   tell("A-ADDRESS STOP!");
			   	run = false;
			       } /* A-stop */
			    run = execute();
			    B_char_displayed = B;
			    if(single_cycle_state==single_cycle_complete)
			       { /* done */
				fetch_complete = false;
				I_cycle = 0;
			       } /* done */
			   }
			else
			   {
			    I_addr = NI_addr;
			    if(I_addr == stop_addr)
			       { /* stop */
				if(diagnostics_on) 
				   tell("I-ADDRESS STOP!");
				run = false;
			       } /* stop */
			    fetch_complete = ifetch();
			    NI_addr = I_addr;
			    B_char_displayed = B;
			    single_cycle_state = single_cycle_start;
			   }
			break;
/*--------------------------------------------------------------------------*/
/* sing cyc */ case mode_sing_cyc:
			cancel_alter_plus_one();
	       		if(fetch_complete) 
			   {
			    execute();
			    B_char_displayed = B;
			    if(single_cycle_state==single_cycle_complete)
			       { /* done */
				fetch_complete = false;
				I_cycle = 0;
			       } /* done */
			   }
			else
			   {
			    I_addr = NI_addr;
			    fetch_complete = ifetch();
			    NI_addr = I_addr;
			    B_char_displayed = B;
			    single_cycle_state = single_cycle_start;
			   }
			run = false;
			break;
/*--------------------------------------------------------------------------*/
/* stg prt */    case mode_stg_prt:
			  cancel_alter_plus_one();
			      if(print_storage(current_printer))
				 { /* print OK */
				  wait_for_mouse_up();
				  stop_execute();
				 } /* print OK */
			      else
				 { /* print failed */
				  alert(alert_printer);
				  wait_for_mouse_up();
				  stop_execute();
				 } /* print failed */
			      run = false;
			      break;
/*--------------------------------------------------------------------------*/
/* char disp */ case mode_char_dsp:
		     {
		      int addr;
		      cancel_alter_plus_one();
		      addr = get_address_switches();
		      B_char_displayed = memory[addr];
		      show_B(B_char_displayed);
		      display_address(addr);
		      if(diagnostics_on)
		         { /* log it */
			  sprintf(diag_buffer,"Location %d, contents '%c'",
			  		addr,bcd_to_ascii(B_char_displayed));
			  tell(diag_buffer);
			 } /* log it */
		      wait_for_mouse_up();
		      stop_execute();
		      run = false;
		      break;
		     }

/*--------------------------------------------------------------------------*/
/* alter */    case mode_alter:

		      {
		       int addr;

		       /* We have hit 'start' so cancel the alter-plus-one
			  mode (it may be reset as a consequence of this
		       */
		       cancel_alter_plus_one();

		      /* update a machine register.
			 The contents of the address switches is stored
			 in the register (A,B,I) whose light is on
		      */
		      addr = get_address_switches();
		      if(A_button.active) 
		         { /* store A */
			  A_addr = addr;
			  cycle = cycle_A;
			 } /* store A */
		      else
		      if(B_button.active) 
		         { /* store B */
			  B_addr = addr;
			  cycle = cycle_B;
			  /* See if we are about to go into alter-plus-one
			     mode
			  */
			  check_alter_plus_one();
			 } /* store B */
		      else
		      if(I_button.active) 
		         { /* store I */
			  NI_addr = I_addr = addr;
			  cycle = cycle_I;
			 } /* store I */
		      else
			 { /* no reg selected */
			  wait_for_mouse_up();
			  run = false;
			  break;
			 } /* no reg selected */

		      /* if we get here, one of the buttons was active */

		      display_address(addr);
		      wait_for_mouse_up();
		      stop_execute();
		      }
		   run = false;
		   break;
	      } /* mode decode */
	   if(diagnostics_on) 
	      { /* talk to user */
	       if(!slow)
		  partial_display();	/* test because may have 
					   already done it above;
					   save time
					*/
	       poll();
	      } /* talk to user */
	 } /* Run Loop */
      hide_mouse_cursor();
      stop_execute();
      display_state();
    }
#pragma optimize("t",off)

/****************************************************************************
*                                load_machine
* Effect: 
*       If an execution mode is set on the console, initiate program load
*	and start the machine
****************************************************************************/

void load_machine()
    {
     int i;

     switch(current_mode)
        {
	 case mode_alter:
	 case mode_stg_prt:
	 case mode_char_dsp:
	 		/* don't do anything in these modes */
	 		return;
	}

     for(i=1;i<=80;i++) memory[i] = '\0';

     if(read_card())
        { /* card came in */
	 NI_addr = I_addr = 1;
	 set_wm(1);
	 start_execute();
	} /* card came in */
     else
        { /* reader wedged */
	 alert(alert_reader);
	 return;
	} /* reader wedged */

    }

/****************************************************************************
*                                require_reset
* Effect: 
*       Forbids further progress until reset is hit
****************************************************************************/

void require_reset()
    {
     reset_mandatory = true;
    }

/****************************************************************************
*                                  do_reset
* Effect: 
*       Resets the machine (start-reset)
****************************************************************************/

void do_reset()
    {
     reset_alerts();
     reset_mandatory = false;
     I_cycle = 0;
     show_length(I_cycle);
     fetch_complete = false;
     light_O(false);		/* turn off bad opcode light if on */
     clear_inst_length();
     clear_address_light();
    }

/****************************************************************************
*                                   set_wm
* Inputs:
*       short addr: Address to set word mark
* Effect: 
*       Sets a word mark in the indicated position
****************************************************************************/

void set_wm(short addr)
    {
     memory[addr] |= word_mark;
    }

/****************************************************************************
*                                  clear_wm
* Inputs:
*       int addr: address of where to clear word mark
* Effect: 
*       clears the word mark at the indicated location
****************************************************************************/

void clear_wm(short addr)
    {
     memory[addr] &= (~word_mark);
    }

/****************************************************************************
*                                   execute
* Result: boolean
*       true if run should continue
*	false if run should halt
* Effect: 
*       executes the instruction
****************************************************************************/

#pragma check_stack(off)
boolean execute()
    {
     unsigned char opcode = BA8421(OP);
     boolean result;
     boolean old_diagnostics;

     if(instructions[opcode] == NULL)
        { /* no dispatch address */
	 inst_illegal();
	 return false;
	} /* no dispatch address */

     old_diagnostics = diagnostics_on;

     if(diagnostics_on)
        { /* trace? */
	 if(selective_on)
	 	diagnostics_on = select_trace[opcode];
	} /* trace? */

     if(diagnostics_on)
        { /* tell user */
	 sprintf(diag_buffer,"OP = %d ('%c'), instructions[op] = 0x%x, SSS= %s, mode = %s",
	 			opcode,
				bcd_to_ascii(opcode),
				instructions[opcode],
				single_cycle_decode(),
				mode_decode());
         tell(diag_buffer);
	} /* tell user */

     result = (*instructions[opcode])();
     
     diagnostics_on = old_diagnostics;

     return result;	
    }
#pragma check_stack(on)


/****************************************************************************
*                                inst_illegal
* Result: boolean
*       false, always
* Effect: 
*       Marks instruction as illegal
****************************************************************************/

boolean inst_illegal()
    {
     light_O(true);
     alert(alert_process);
     return false;
    }

/****************************************************************************
*                             single_cycle_decode
* Result: char *
*       print string of microstate
****************************************************************************/

char * single_cycle_decode()
    {
     static char ssd[80];
     int micro;
     int macro;
     char * instr;

     switch(single_cycle_state)
        { /* state decode */
	 case single_cycle_run: return "run";
	 case single_cycle_start: return "start";
	 case single_cycle_complete: return "complete";
	} /* state decode */

     micro = microstate(single_cycle_state);
     macro = macrostate(single_cycle_state);
     instr = opcodes[macro];

     switch(micro)
        { /* microdecode */
	 case A_complete:
	 	sprintf(ssd,"%d:%s:A_complete",macro,instr);
		break;
	 case A_s_complete:
	 	sprintf(ssd,"%d:%s:A_s_complete",macro,instr);
		break;
	 case A_f_complete:
	 	sprintf(ssd,"%d:%s:A_f_complete",macro,instr);
		break;
	 case B_complete:
	 	sprintf(ssd,"%d:%s:B_complete",macro,instr);
		break;
	 case B_f_complete:
	 	sprintf(ssd,"%d:%s:B_f_complete",macro,instr);
		break;
	 case B_s_complete:
	 	sprintf(ssd,"%d:%s:B_s_complete",macro,instr);
		break;
	
	 default:
	 	sprintf(ssd,"%d:%s:%d",macro,instr,micro);
		break;
	} /* microdecode */

     return ssd;
	 
    }

/****************************************************************************
*                               tell_new_state
* Effect: 
*       Displays help message about processor state
****************************************************************************/

void diag_state()
    {
     char * ustate;

     ustate = single_cycle_decode();
     sprintf(diag_buffer,"after %s: I => %d, NI => %d, A => %d, B => %d, SSS = %s, mode = %s",
     		opcodes[BA8421(OP)],
		I_addr,
		NI_addr,
		A_addr,
		B_addr,
		ustate,
		mode_decode());
     tell(diag_buffer);
    }

/****************************************************************************
*                                   diag_op
*				   (tell_op)
* Inputs:
*       int mask: Mask of fields to print
* Effect: 
*       Prints out state for instructions as shown below
*
*	length	op_A	op_B	op_d
*	 1	[A]	[B]	[d]
*	 2	-	-	d
*	 4	A	[B]	-
*	 5	A	[B]	d
*	 7	A	B	[d]
*	 8	A	B	d
****************************************************************************/

void diag_op(short mask)
    {
     char msg[80];
     char * p;

     p = msg;

     p += sprintf(p,"before %d:  %s ",
     			I_addr - I_cycle,
			opcodes[BA8421(OP)]);

     if(mask & op_A)
        switch(I_cycle)
	   { /* A-decode */
	    case 1:	
	    		p += sprintf(p," [%d]",A_addr);
			break;
	    case 4:
	    case 5:
	    case 7:
	    case 8:
	    		p += sprintf(p," %d",A_addr);
			break;
	   } /* A-decode */

     if(mask & op_B)
        switch(I_cycle)
	   { /* B-decode */
	    case 1:	
	    case 4:
	    		p += sprintf(p,", [%d]",B_addr);
			break;
	    case 7:
	    case 8:
	    		p += sprintf(p,", %d",B_addr);
			break;
	   } /* B-decode */

     if(mask & op_d)
        switch(I_cycle)
	   { /* d-decode */
	    case 1:
	    		p += sprintf(p,", [%c]",bcd_to_ascii(d_char));
			break;
	    case 2:	
	    		p += sprintf(p,"%c",bcd_to_ascii(d_char));
			break;
	    case 5:
	    case 8:
	    		p += sprintf(p,", %c",bcd_to_ascii(d_char));
			break;
	   } /* d-decode */

	p += sprintf(p,"; SSS= %s, mode = %s ",
			single_cycle_decode(),
			mode_decode());


	tell(msg);
	    
    }

/****************************************************************************
*                                 bad_address
* Inputs:
*       short addr: Address
* Result: boolean
*       true if address is illegal (0 or not physical memory)
* Effect: 
*       Sets alert condition for address bad
****************************************************************************/

#pragma check_stack(off)
boolean bad_address(short addr)
    {
     if(ValidNonZero(addr)) 
	return false;
     alert(alert_process);
     set_address_light();
     return true;
    }
#pragma check_stack(on)

/****************************************************************************
*                                 bad_address_r
* Inputs:
*       short addr: Address
* Result: boolean
*       true if address is illegal moving right (15999 or not physical memory)
* Effect: 
*       Sets alert condition for address bad
****************************************************************************/

boolean bad_address_r(short addr)
    {
     if(addr < 15999) 
	return false;
     alert(alert_process);
     set_address_light();
     return true;
    }

/****************************************************************************
*                                 mode_decode
* Result: char *
*       Current mode
****************************************************************************/

char * mode_decode()
    {
     switch(current_mode)
        { /* mode decode */
	 
         case mode_alter: return "alter";
	 case mode_stg_prt: return "str prnt";
	 case mode_char_dsp: return "char disp";
	 case mode_run: return "run";
	 case mode_adr_stop: return "adr stop";
	 case mode_i_ex: return "I/EX";
	 case mode_sing_cyc: return "sing cyc";
	} /* mode decode */
     return "??";
    }

/****************************************************************************
*                                    bad_d
* Inputs:
*       char * msg: Message to issue
*	unsigned char d: bcd d-character
* Effect: 
*       Issues diagnostic message
****************************************************************************/

void bad_d(char * msg, unsigned char d)
    {
     if(!diagnostics_on) 
	return;

     sprintf(diag_buffer,"Illegal d-character [%s]: '%c' (%d)",
     			msg,bcd_to_ascii(d),d);
     tell(diag_buffer);
    }
