/*
This is a java version of Eric Smith's CSIM simulator. 
http://www.brouhaha.com/~eric/hpcalc/
HP45 version by Copyright 2000 David G. Hicks  (java port)
This HP 35 version has been adapted in 2005, 2006 by Jacques Laporte

http://www.jacques-laporte.org/HP%2035%20Saga.htm

- firstly to make it compatible with hte HP35 ROM dumped optically by P. MONTA,
- secondly to add a capture enabling to edit and print sample calculation traces,
- thirdly (2006), to improve the "break point" logic.

The latest version of the HP35 ROM can be obtained from: 
http://svn.brouhaha.com/viewcvs/nonpareil/trunk/asm/35.asm?view=log
The hp35.lst file must be zipped.

*/

import java.applet.*;
import java.awt.*;
import java.io.*;
import java.util.*;
import java.util.zip.*;
import java.net.*;
import java.lang.Thread.*;




public class sim35 extends Applet {

//////////////////////////////////////////////////////////////////////////
//
// These methods display the graphics of the calculator and record button
// presses
//
//////////////////////////////////////////////////////////////////////////

    Font key_font, disp_font, trace_font;
    FontMetrics kfm, dfm, tfm;
    Graphics gr;
    String disp_buf = new String("");

    	Frame debugframe;
	TextArea debugtext;


    int pressed_key = -1;
    int pressed_debug_key = -1;

    static final int WINDOW_WIDTH = 255;
    static final int WINDOW_HEIGHT = 484;
    static final int DISPLAY_HEIGHT = 52;

    Rectangle display_rect = new Rectangle(0, 0, WINDOW_WIDTH, DISPLAY_HEIGHT);

    static final int  BLACK    = 0;
    static final int  WHITE    = 1;
    static final int  LT_GREY  = 2;
    static final int  MED_GREY = 3;
    static final int  DK_GREY  = 4;
    static final int  GOLD     = 5;
    static final int  BLUE     = 6;
    static final int  DK_RED   = 7;
    static final int  BR_RED   = 8;
    static final int  VLT_GREY = 9;

    Color color[] =
    {
	new Color(0x00, 0x00, 0x00),  
	new Color(0xff, 0xff, 0xff),  
	new Color(0xa0, 0xa0, 0xa0),  
	new Color(0x58, 0x58, 0x58),  
	new Color(0x38, 0x38, 0x38),  
	new Color(0xff, 0xd7, 0x00),  
	new Color(0x40, 0x40, 0xff),  
	new Color(0x50, 0x00, 0x00),  
	new Color(0xff, 0x40, 0x00),
	new Color(0xc0, 0xc8, 0xd8)  
    };

    class Keyinfo {
	Rectangle rect;
	String label;
	String flabel;
	int keycode;
	int fg, bg;

	public Keyinfo(Rectangle rect, String label, String flabel, int keycode, int fg, int bg) {
	    this.rect = rect; this.label = label; this.flabel = flabel;
	    this.keycode=keycode; this.fg = fg; this.bg = bg;
	}
    };

    Keyinfo keys [] =
    {
	new Keyinfo( new Rectangle( 14, 100, 30, 24 ), "x^y",   "",        006, WHITE, BLACK ),
	new Keyinfo( new Rectangle( 62, 100, 30, 24 ), "log",   "",        004, WHITE, BLACK ),
	new Keyinfo( new Rectangle(110, 100, 30, 24 ), "ln",    "",        003, WHITE, BLACK ),
	new Keyinfo( new Rectangle(158, 100, 30, 24 ), "e^x",   "",        002, WHITE, BLACK ),
	new Keyinfo( new Rectangle(206, 100, 30, 24 ), "CLR",   "",        000, WHITE, BLUE ),

	new Keyinfo( new Rectangle( 14, 148, 30, 24 ), "sqrt(x)", "",      056, WHITE, BLACK ),
	new Keyinfo( new Rectangle( 62, 148, 30, 24 ), "arc",   "",        054, WHITE, MED_GREY ),
	new Keyinfo( new Rectangle(110, 148, 30, 24 ), "sin",   "",        053, WHITE, MED_GREY ),
	new Keyinfo( new Rectangle(158, 148, 30, 24 ), "cos",   "",        052, WHITE, MED_GREY ),
	new Keyinfo( new Rectangle(206, 148, 30, 24 ), "tan",   "",        050, WHITE, MED_GREY ),

	new Keyinfo( new Rectangle( 14, 196, 30, 24 ), "1/x",  "",         016, WHITE, BLACK ),
	new Keyinfo( new Rectangle( 62, 196, 30, 24 ), "x<>y",   "",       014, WHITE, BLACK ),
	new Keyinfo( new Rectangle(110, 196, 30, 24 ), "Rdwn",   "",       013, WHITE, BLACK ),
	new Keyinfo( new Rectangle(158, 196, 30, 24 ), "STO",   "",        012, WHITE, BLACK ),
	new Keyinfo( new Rectangle(206, 196, 30, 24 ), "RCL",     "",      010, WHITE, BLACK ),
	
	new Keyinfo( new Rectangle( 14, 244, 78, 24 ), "ENTER^","",        076, WHITE, BLUE ),
	new Keyinfo( new Rectangle(110, 244, 30, 24 ), "CHS",   "",        073, WHITE, BLACK ),
	new Keyinfo( new Rectangle(158, 244, 30, 24 ), "EEX",   "",        072, WHITE, BLACK ),
	new Keyinfo( new Rectangle(206, 244, 30, 24 ), "CLX",   "",        070, WHITE, BLACK ),

	new Keyinfo( new Rectangle( 14, 292, 24, 24 ), "-",     "",        066, WHITE, BLUE ),
	new Keyinfo( new Rectangle( 63, 292, 37, 24 ), "7",     "",        064, BLACK, WHITE ),
	new Keyinfo( new Rectangle(131, 292, 37, 24 ), "8",     "",        063, BLACK, WHITE ),
	new Keyinfo( new Rectangle(199, 292, 37, 24 ), "9",     "",        062, BLACK, WHITE ),

	new Keyinfo( new Rectangle( 14, 340, 24, 24 ), "+",     "",        026, WHITE, BLUE ),
	new Keyinfo( new Rectangle( 63, 340, 37, 24 ), "4",     "",        024, BLACK, WHITE ),
	new Keyinfo( new Rectangle(131, 340, 37, 24 ), "5",     "",        023, BLACK, WHITE ),
	new Keyinfo( new Rectangle(199, 340, 37, 24 ), "6",     "",        022, BLACK, WHITE ),

	new Keyinfo( new Rectangle( 14, 388, 24, 24 ), "x",     "",        036, WHITE, BLUE ),
	new Keyinfo( new Rectangle( 63, 388, 37, 24 ), "1",     "",        034, BLACK, WHITE ),
	new Keyinfo( new Rectangle(131, 388, 37, 24 ), "2",     "",        033, BLACK, WHITE ),
	new Keyinfo( new Rectangle(199, 388, 37, 24 ), "3",     "",        032, BLACK, WHITE ),

	new Keyinfo( new Rectangle( 14, 436, 24, 24 ), "/",     "",        046, WHITE, BLUE ),
	new Keyinfo( new Rectangle( 63, 436, 37, 24 ), "0",     "",        044, BLACK, WHITE ),
	new Keyinfo( new Rectangle(131, 436, 37, 24 ), ".",     "",        043, BLACK, WHITE ),
	new Keyinfo( new Rectangle(199, 436, 37, 24 ), "Pi",  "",          042, BLACK, WHITE )
    };

    class DebugKeyinfo {
	Rectangle rect;
	String label;
	int fg, bg;
	boolean isKey;		// if false then this is just a lable
	boolean toggle;
	boolean value;

	public DebugKeyinfo(Rectangle rect, String label,
			    int fg, int bg, boolean isKey) {
	    this.rect = rect; this.label = label;
	    this.fg = fg; this.bg = bg;
	    this.isKey = isKey;
	    toggle = false; value = false;
	}
	public DebugKeyinfo(Rectangle rect, String label,
			    int fg, int bg, boolean isKey, boolean toggle, boolean value) {
	    this.rect = rect; this.label = label;
	    this.fg = fg; this.bg = bg;
	    this.isKey = isKey;
	    this.toggle = toggle; this.value = value;
	}
    };

    final static int LEDSIZE = 6;

    final static int TRACE = 0;   // constants must be in the same order
    final static int INCREGS = 1; // as the dkeys array below
    final static int RUN = 2;
    final static int SLOW = 3;  
    final static int STEP = 4;
    final static int STOP = 5;
    final static int SET1 = 6;
    final static int SET2 = 7;
    final static int SET3 = 8;
    final static int ENABLE1 = 9;
    final static int ENABLE2 = 10;
    final static int ENABLE3 = 11;
    final static int BP0 = 12;
    final static int BP1 = 13;
    final static int BP2 = 14;
    final static int BP3 = 15;
    final static int BP4 = 16;
    final static int BP5 = 17;
    final static int BP6 = 18;
    final static int BP7 = 19;
    final static int BPCLR = 20;
    final static int DEBUG = 21;  

    DebugKeyinfo dkeys [] =	
    {
	new DebugKeyinfo( new Rectangle( 270, 199, 70, 24 ), "    trace", WHITE, MED_GREY, true, true, false),
	new DebugKeyinfo( new Rectangle( 360, 199, 70, 24 ), "    regs",  WHITE, MED_GREY, true, true, false),
	new DebugKeyinfo( new Rectangle( 270, 62, 70, 24 ), "    run", WHITE, MED_GREY, true, true, false),
	new DebugKeyinfo( new Rectangle( 270, 92, 70, 24 ), "    slow", WHITE, MED_GREY, true, true, false),	  
	new DebugKeyinfo( new Rectangle( 270, 122, 70, 24 ), "    step", WHITE, MED_GREY, true),
	new DebugKeyinfo( new Rectangle( 270, 152, 70, 24 ), "    stop", WHITE, MED_GREY, true),

	// breakpint set and enable keys
	new DebugKeyinfo( new Rectangle( 540, 25, 33, 24 ), "set", WHITE, MED_GREY, true),
	new DebugKeyinfo( new Rectangle( 540, 55, 33, 24 ), "set", WHITE, MED_GREY, true),
	new DebugKeyinfo( new Rectangle( 540, 85, 33, 24 ), "set", WHITE, MED_GREY, true),
	new DebugKeyinfo( new Rectangle( 680, 25, 64, 24 ), "    enable", WHITE, MED_GREY, true, true, false),
	new DebugKeyinfo( new Rectangle( 680, 55, 64, 24 ), "    enable", WHITE, MED_GREY, true, true, false),
	new DebugKeyinfo( new Rectangle( 680, 85, 64, 24 ), "    enable", WHITE, MED_GREY, true, true, false),

	// breakpoint digit keys
	new DebugKeyinfo( new Rectangle( 540, 154, 28, 24 ), "0", WHITE, MED_GREY, true),
	new DebugKeyinfo( new Rectangle( 590, 154, 28, 24 ), "1", WHITE, MED_GREY, true),
	new DebugKeyinfo( new Rectangle( 630, 154, 28, 24 ), "2", WHITE, MED_GREY, true),
	new DebugKeyinfo( new Rectangle( 670, 154, 28, 24 ), "3", WHITE, MED_GREY, true),
	new DebugKeyinfo( new Rectangle( 540, 119, 28, 24 ), "4", WHITE, MED_GREY, true),
	new DebugKeyinfo( new Rectangle( 590, 119, 28, 24 ), "5", WHITE, MED_GREY, true),
	new DebugKeyinfo( new Rectangle( 630, 119, 28, 24 ), "6", WHITE, MED_GREY, true),
	new DebugKeyinfo( new Rectangle( 670, 119, 28, 24 ), "7", WHITE, MED_GREY, true),


	new DebugKeyinfo( new Rectangle( 710, 199, 28, 24 ), "clr", WHITE, MED_GREY, true),

	  
	new DebugKeyinfo( new Rectangle( 270, 12, 70, 24 ), "   debug", WHITE, MED_GREY, true, true, true),
			  
	new DebugKeyinfo( new Rectangle( 510, 1, 170, 24 ), "breakpoints", BLACK, VLT_GREY, true),
	new DebugKeyinfo( new Rectangle( 270, 375, 465, 24 ), "next and following instructions (PC, PC+1...)", BLACK, VLT_GREY, true),
	new DebugKeyinfo( new Rectangle( 375, 2, 100, 24 ), "internal registers", BLACK, VLT_GREY, true)
		
    };

    synchronized void draw_toggle_key(Graphics pgr, String s, Rectangle rect, int fg, int bg, boolean toggle) {

	draw_string (pgr, s, rect, fg, bg, kfm);

	if (toggle)
	    pgr.setColor(color[BR_RED]);
	else
	    pgr.setColor(color[DK_RED]);

	pgr.fillOval(rect.x+LEDSIZE/2+3,
		     rect.y+rect.height/2-LEDSIZE/2, LEDSIZE, LEDSIZE);
    }

    void update_run_key() {
	draw_toggle_key(gr, dkeys[RUN].label, dkeys[RUN].rect,
			dkeys[RUN].fg, dkeys[RUN].bg, dkeys[RUN].value);
    }


    void handle_debug_key(int key) {

	// enable only the DEBUG key when the debugger is off
	if (!dkeys[DEBUG].value && (key != DEBUG))
	    return; 
	
	switch (key) {
	    case TRACE:
	    case INCREGS:
	    case RUN:
	    case ENABLE1:
	    case ENABLE2:
	    case ENABLE3:
		dkeys[key].value = !dkeys[key].value;
		anyBPsEnabled = dkeys[ENABLE1].value ||
				dkeys[ENABLE2].value ||
				dkeys[ENABLE3].value;
		break;
	    case BP0:
	    case BP1:
	    case BP2:
	    case BP3:
	    case BP4:
	    case BP5:
	    case BP6:
	    case BP7:
		if (key>11 && key<20) {
		BpIn = BpIn * 10;

		BpIn += key-BP0 ;
		display_bpzone();
		}
		bpinput <<= 3;
		bpinput += key-BP0;

		
		break;
	    case BPCLR:
  		bpinput = 0;
		BpIn = 0;
		display_bpzone();
		break;
	    case DEBUG:
		dkeys[key].value = !dkeys[key].value;
		if (!dkeys[key].value) {
		    //debugger turned off
		    dkeys[SLOW].value = false;
		    dkeys[RUN].value = true;
		    step = false;
		    dkeys[ENABLE1].value = dkeys[ENABLE2].value =
			dkeys[ENABLE3].value = false;
		    anyBPsEnabled = false;
		    dkeys[INCREGS].value = false;
		    dkeys[TRACE].value = false;    
      		}
		draw_debugger(gr);

		break;
	    case SET1:
	    case SET2:
	    case SET3:
		dkeys[key].value = !dkeys[key].value;
		bpReg[key-SET1] = bpinput;
		bpinput = 0;
		display_user_bps();

		BpIn = 0;
		display_bpzone();

		break;
	    case SLOW:
		dkeys[key].value = !dkeys[key].value;
		if (dkeys[key].value) {
		    dkeys[RUN].value = true;
		    update_run_key();
		}
		break;
	    case STEP:
		dkeys[RUN].value = false;
		update_run_key();
		step = true;
		break;
	    case STOP:
		dkeys[RUN].value = false;
		update_run_key();
		break;
	}
	if (dkeys[key].toggle) {
	    draw_toggle_key(gr, dkeys[key].label, dkeys[key].rect,
			    dkeys[key].fg, dkeys[key].bg, dkeys[key].value);
	}

    }

    synchronized void draw_string (Graphics pgr, String s, Rectangle rect, int fg, int bg, FontMetrics fm)
    {
	int ascent, descent;
	int sheight, swidth;

	if (gr == null)
	    System.out.println("gr is null!!!");
	pgr.setColor(color[bg]);
	pgr.fillRect(rect.x, rect.y, rect.width, rect.height);

	if (s != null) {
	    if (!s.equals("")) {
		pgr.setColor(color[fg]);
		descent = fm.getMaxDescent();
		sheight = fm.getHeight();
		swidth = fm.stringWidth(s);
		pgr.drawString(s, rect.x + (rect.width - swidth)/2, rect.y + (rect.height*2 -sheight)/2+descent);
		
	    }
	}
    }

    synchronized void draw_string_left (Graphics pgr, String s, Rectangle rect, int fg, int bg, FontMetrics fm)
    {
	int ascent, descent;
	int sheight, swidth;

	if (pgr == null)
	    System.out.println("pgr is null!!!");

	pgr.setColor(color[bg]);
	pgr.fillRect(rect.x, rect.y, rect.width, rect.height);

	if (s != null) {
	    if (!s.equals("")) {
		pgr.setColor(color[fg]);
		descent = fm.getMaxDescent();
		sheight = fm.getHeight();
		pgr.drawString(s, rect.x + 2, rect.y + (rect.height*2 -sheight)/2+descent);
		
	    }
	}
    }

    synchronized void draw_display (Graphics pgr, String s)
    {
	pgr.setFont(disp_font);
	draw_string (pgr, s, display_rect, BR_RED, DK_RED, dfm); 
	pgr.setFont(key_font);
    }

    
    synchronized void draw_calc (Graphics pgr)
    {
	int i;
	Rectangle r;

	pgr.setColor(color[DK_GREY]);
	pgr.fillRect(0,DISPLAY_HEIGHT, WINDOW_WIDTH, WINDOW_HEIGHT-DISPLAY_HEIGHT);

	for (i = 0; i < keys.length ; i++)
	{
	    r = keys[i].rect;
	    draw_string (pgr, keys [i].label, r, keys [i].fg, keys [i].bg, kfm);
	    r.y -= r.height;
	    draw_string (pgr, keys [i].flabel, r, GOLD, DK_GREY, kfm);
	    r.y += r.height;
	}

    }

    synchronized void draw_basic_debugger (Graphics pgr)
    {
	int i;
	pgr.setColor(color[VLT_GREY]);
	pgr.fillRect(WINDOW_WIDTH,0, 760-WINDOW_WIDTH, WINDOW_HEIGHT);

	if (dkeys[DEBUG].value) {
	    for (i = 0; i < dkeys.length ; i++)
	    {
		if (dkeys[i].toggle) 
		    draw_toggle_key(pgr, dkeys[i].label,
				    dkeys[i].rect, dkeys[i].fg,
				    dkeys[i].bg, dkeys[i].value);
		else
		    draw_string (pgr, dkeys[i].label,
				 dkeys[i].rect, dkeys[i].fg,
				 dkeys[i].bg, kfm);
	    }
	} else {
	    draw_toggle_key(pgr, dkeys[DEBUG].label,
			    dkeys[DEBUG].rect, dkeys[DEBUG].fg,
			    dkeys[DEBUG].bg, dkeys[DEBUG].value);
	}
    }


    // used for font calculations
    int buttonWidth = 30, buttonHeight = 24;

    synchronized public void init() {
	int i;

	System.out.println("in init");
	gr = getGraphics();
	
	//loop until the font is small enough to fit the keys
	for (i = 24; i>6; i--) {
	    key_font = new Font("Helvetica", Font.PLAIN, i);
	    kfm = getFontMetrics(key_font);
	    
	    if ((buttonWidth >= (kfm.stringWidth("x<>y")+4)) &&
		(buttonHeight >= (kfm.getHeight()+2)) &&
		(DISPLAYREGWIDTH >= (kfm.stringWidth(" 01234567890123")+2))) break;
	}
	init_trace();
	init_next();
	init_reg_display();
	init_user_bp_display();

	disp_font = new Font("Helvetica", Font.PLAIN, i+5);
	dfm = getFontMetrics(disp_font);
	gr.setFont(key_font); // this line causes infinite repaints if in paint
	do_trace("Original C version by Eric L. Smith © 1995");
	do_trace("HP35 version with trace capture © J. Laporte 2005");
	do_trace("Distributed under GNU General Public License V2");

	if (!calcRunning) {
	    calcRunning = true;
	    System.out.println("Starting the calc thread");
	    calc_thread = new Thread(new run_calc());
	    calc_thread.start();
	}

    }

    public void stop_calc() {
	if (calcRunning) {
	    calc_thread.stop();
	    my_sleep(500);
	}  
    }

    public void destroy() {
	stop_calc();
	gr.dispose();
    }

    Thread calc_thread;

    synchronized public void paint(Graphics pgr) {
	draw_calc(pgr);
	draw_display (pgr, disp_buf);
	draw_debugger(pgr);
    }

    synchronized public void draw_debugger(Graphics pgr) {
	draw_basic_debugger(pgr);
	if (dkeys[DEBUG].value) {
	    display_trace();
	    display_next();
	    display_regs(true);
	    display_user_bps();
	}
    }

    
    boolean pt_in_rect (int x, int y, Rectangle rect)
    {
	return ((x >= rect.x) && (x < (rect.x + rect.width)) &&
		(y >= rect.y) && (y < (rect.y + rect.height)));
    }

    
    int find_key (int x, int y)
    {
	int i;

	for (i = 0; i < keys.length; i++)
	{
	    if (pt_in_rect (x, y, keys[i].rect))
		return (i);
	}
	return (-1);
    }

    int find_debug_key (int x, int y)
    {
	int i;

	for (i = 0; i < dkeys.length; i++)
	{
	    if (dkeys[i].isKey) {
		if (pt_in_rect (x, y, dkeys[i].rect))
		    return (i);
	    }
	}
	return (-1);
    }

    // this is never called in Applett viewer but works in Netscape
    public boolean keyDown(Event e, int key) {
	if (e.id == Event.KEY_ACTION) {
	    switch (key) {
		case Event.PGUP:
//		    do_button_back();
		    break;
		case Event.PGDN:
//		    do_button_fwd();
		    break;
	    }
	}
	return true;
    }

    public boolean mouseDown(Event e, int x, int y) {
	int key = find_key (x, y);
	if (key >= 0) {
	    // this is the Enter key hack
	    if (false) {
	    if (key == 75) {
		if (((e.modifiers & Event.CTRL_MASK) != 0) ||
		    ((e.modifiers & Event.SHIFT_MASK) != 0) ||
		    ((e.modifiers & Event.META_MASK) !=0))
		    key--;	// if user right clicks or ctrl clicks
	    }
	    }
	    pressed_key = key;
	} else {
	    key = find_debug_key(x, y);
	    if (key >= 0) {
		pressed_debug_key = key;
	    }
	}
	return true;
    }

    public boolean mouseUp(Event e, int x, int y) {
	return true;
    }

    
    void init_display ()
    {
	// left over from the C version
    }

    
    void update_display (String str)
    {
	if (str != null) {
	    if (!disp_buf.equals(str)) {
		disp_buf = new String(str);
		draw_display (gr, str);
	    }
	}
    }

    
    void check_debug_keyboard() {
	if (pressed_debug_key >= 0) {
//	    java.awt.Toolkit.getDefaultToolkit ().beep() ;
	    handle_debug_key(pressed_debug_key);
	    pressed_debug_key = -1;
            debugframe.show();	    
	}
    }
    
    /* returns -1 if no key pressed */
    int check_keyboard ()
    {
	check_debug_keyboard();

	if (pressed_key >= 0) {
//	    java.awt.Toolkit.getDefaultToolkit ().beep() ;
	    return (keys [pressed_key].keycode);
	} else
	    return (-1);
    }

    

    //////////////////////////////////////////////////////////////////////////
    //
    // These methods encompass the internals of the calculator
    //
    //////////////////////////////////////////////////////////////////////////

    Operation do_op_clear_reg = null;
    Operation do_op_clear_s = null;

    public interface Operation {
	public void execute(int opcode);
    }

    static final int WSIZE = 14;

    byte a[] = new byte[WSIZE];
    byte b[] = new byte[WSIZE];
    byte c[] = new byte[WSIZE];
    byte d[] = new byte[WSIZE];
    byte e[] = new byte[WSIZE];
    byte f[] = new byte[WSIZE];
    byte m[] = new byte[WSIZE];
    int p; 

    static final int MAX_RAM = 100;
    int max_ram = MAX_RAM;
    int ram_addr;
    byte ram[][] = new byte[MAX_RAM][WSIZE];

    static final int SSIZE = 12;
    byte[] s = new byte[SSIZE];

    byte carry, prev_carry;

    int pc;
    int rom;
    int group;

    int del_rom;
    int del_grp;

    int ret_pc;

    boolean display_enable;
    boolean key_flag;
    int key_buf;
    int io_count;

    int prev_pc;  /* used to store complete five-digit octal address of instruction */

    static final int MAX_GROUP = 2;
    static final int MAX_ROM = 8;
    static final int ROM_SIZE = 256;

    short ucode[][][] = new short [MAX_GROUP] [MAX_ROM] [ROM_SIZE];
    // break points for unintiallized ROM
    byte bpt[][][] = new byte [MAX_GROUP] [MAX_ROM] [ROM_SIZE];
    String source[][][] = new String [MAX_GROUP] [MAX_ROM] [ROM_SIZE];

    boolean step = false;
    boolean keep_src = true;

    void fatal (String s)
    {
	System.out.println("fatal error: " + s);
	stop_calc();
	this.destroy(); 
    }
    
    byte do_add (byte x, byte y)
    {
	int res;

	res = x + y + carry;
	if (res > 9)
	{
	    res -= 10;
	    carry = 1;
	}
	else
	    carry = 0;
	return (byte) res;
    }

    byte do_sub (byte x, byte y)
    {
	int res;

	res = (x - y) - carry;
	if (res < 0)
	{
	    res += 10;
	    carry = 1;
	}
	else
	    carry = 0;
	return (byte) res;
    }

    class bad_op implements Operation {
	public void execute(int opcode) {
	    System.out.println("illegal opcode " + opcode + " at " + prev_pc);
	}
    }

    class op_arith implements Operation {
	public void execute(int opcode) {
	    int op, field;
	    byte zero = 0;
	    int first = 0, last = 0;;
	    byte temp;
	    int i;
	    byte t[] = new byte[WSIZE];
	    
	    op = opcode >> 5;
	    field = (opcode >> 2) & 7;
	    
	    switch (field)
	    {
		case 0:  /* p  */
		    first =  p; last =  p;
		    if (p >= WSIZE)
		    {
			System.out.println("Warning! p > WSIZE at " + prev_pc);
			last = 0;  /* don't do anything */
		    }
		    break;
		case 1:  /* m  */  first =  3; last = 12; break;
		case 2:  /* x  */  first =  0; last =  2; break;
		case 3:  /* w  */  first =  0; last = 13; break;
		case 4:  /* wp */
		    first =  0; last =  p; /* break; bug in orig??? */
		    if (p > 13)
		    {
			System.out.println("Warning! p >= WSIZE at " + prev_pc);
			last = 13;
		    }
		    break;
		case 5:  /* ms */  first =   3; last = 13; break;
		case 6:  /* xs */  first =   2; last =  2; break;
		case 7:  /* s  */  first =  13; last = 13; break;
	    }
	    
	    switch (op)
	    {
		case 0x00:  /* if b[f] = 0 */  
		    carry = 0; // dgh added - needed?
		    for (i = first; i <= last; i++)
			carry |= (b [i] != 0)?1:0; 
		    break;
		case 0x01:  /* 0 -> b[f] */
		    for (i = first; i <= last; i++)
			b [i] = 0;
		    carry = 0;
		    break;
		case 0x02:  /* if a >= c[f] */
		    carry = 0;
		    for (i = first; i <= last; i++)
			t [i] = do_sub (a [i], c [i]);
		    break;
		case 0x03:  /* if c[f] >= 1 */
		    carry = 1;
		    for (i = first; i <= last; i++)
			carry &= (c [i] == 0)?1:0;
		    break;
		case 0x04:  /* b -> c[f] */
		    for (i = first; i <= last; i++)
			c [i] = b [i];
		    carry = 0;
		    break;
		case 0x05:  /* 0 - c -> c[f] */
		    carry = 0;
		    for (i = first; i <= last; i++)
			c [i] = do_sub (zero, c [i]);
		    break;
		case 0x06:  /* 0 -> c[f] */
		    for (i = first; i <= last; i++)
			c [i] = 0;
		    carry = 0;
		    break;
		case 0x07:  /* 0 - c - 1 -> c[f] */
		    carry = 1;
		    for (i = first; i <= last; i++)
			c [i] = do_sub (zero, c [i]);
		    break;
		case 0x08:  /* shift left a[f] */
		    for (i = last; i >= first; i--)
			a [i] = (i == first) ? 0 : a [i-1];
		    carry = 0;
		    break;
		case 0x09:  /* a -> b[f] */
		    for (i = first; i <= last; i++)
			b [i] = a [i];
		    carry = 0;
		    break;
		case 0x0a:  /* a - c -> c[f] */
		    carry = 0;
		    for (i = first; i <= last; i++)
			c [i] = do_sub (a [i], c [i]);
		    break;
		case 0x0b:  /* c - 1 -> c[f] */
		    carry = 1;
		    for (i = first; i <= last; i++)
			c [i] = do_sub (c [i], zero);
		    break;
		case 0x0c:  /* c -> a[f] */
		    for (i = first; i <= last; i++)
			a [i] = c [i];
		    carry = 0;
		    break;
		case 0x0d:  /* if c[f] = 0 */
		    for (i = first; i <= last; i++)
			carry |= (c [i] != 0)?1:0;
		    break;
		case 0x0e:  /* a + c -> c[f] */
		    carry = 0;
		    for (i = first; i <= last; i++)
			c [i] = do_add (a [i], c [i]);
		    break;
		case 0x0f:  /* c + 1 -> c[f] */
		    carry = 1;
		    for (i = first; i <= last; i++)
			c [i] = do_add (c [i], zero);
		    break;
		case 0x10:  /* if a >= b[f] */
		    carry = 0;
		    for (i = first; i <= last; i++)
			t [i] = do_sub (a [i], b [i]);
		    break;
		case 0x11:  /* b exchange c[f] */
		    for (i = first; i <= last; i++)
			{ temp = b[i]; b [i] = c [i]; c [i] = temp; }
		    carry = 0;
		    break;
		case 0x12:  /* shift right c[f] */
		    for (i = first; i <= last; i++)
			c [i] = (i == last) ? 0 : c [i+1];
		    carry = 0;
		    break;
		case 0x13:  /* if a[f] >= 1 */
		    carry = 1;
		    for (i = first; i <= last; i++)
			carry &= (a [i] == 0)?1:0;
		    break;
		case 0x14:  /* shift right b[f] */
		    for (i = first; i <= last; i++)
			b [i] = (i == last) ? 0 : b [i+1];
		    carry = 0;
		    break;
		case 0x15:  /* c + c -> c[f] */
		    carry = 0;
		    for (i = first; i <= last; i++)
			c [i] = do_add (c [i], c [i]);
		    break;
		case 0x16:  /* shift right a[f] */
		    for (i = first; i <= last; i++)
			a [i] = (i == last) ? 0 : a [i+1];
		    carry = 0;
		    break;
		case 0x17:  /* 0 -> a[f] */
		    for (i = first; i <= last; i++)
			a [i] = 0;
		    carry = 0;
		    break;
		case 0x18:  /* a - b -> a[f] */
		    carry = 0;
		    for (i = first; i <= last; i++)
			a [i] = do_sub (a [i], b [i]);
		    break;
		case 0x19:  /* a exchange b[f] */
		    for (i = first; i <= last; i++)
			{ temp = a[i]; a [i] = b [i]; b [i] = temp; }
		    carry = 0;
		    break;
		case 0x1a:  /* a - c -> a[f] */
		    carry = 0;
		    for (i = first; i <= last; i++)
			a [i] = do_sub (a [i], c [i]);
		    break;
		case 0x1b:  /* a - 1 -> a[f] */
		    carry = 1;
		    for (i = first; i <= last; i++)
			a [i] = do_sub (a [i], zero);
		    break;
		case 0x1c:  /* a + b -> a[f] */
		    carry = 0;
		    for (i = first; i <= last; i++)
			a [i] = do_add (a [i], b [i]);
		    break;
		case 0x1d:  /* a exchange c[f] */
		    for (i = first; i <= last; i++)
			{ temp = a[i]; a [i] = c [i]; c [i] = temp; }
		    carry = 0;
		    break;
		case 0x1e:  /* a + c -> a[f] */
		    carry = 0;
		    for (i = first; i <= last; i++)
			a [i] = do_add (a [i], c [i]);
		    break;
		case 0x1f:  /* a + 1 -> a[f] */
		    carry = 1;
		    for (i = first; i <= last; i++)
			a [i] = do_add (a [i], zero);
		    break;
	    }
	}
    }

    class op_goto implements Operation {
	public void execute(int opcode) {
	    if (prev_carry != 1)
	    {
		pc = opcode >> 2;
		rom = del_rom;
		group = del_grp;
	    }
	}
    }

    class op_jsb implements Operation {
	public void execute(int opcode) {
	    ret_pc = pc;
	    pc = opcode >> 2;
	    rom = del_rom;
	    group = del_grp;
	}
    }

    class op_return implements Operation {
	public void execute(int opcode) {
	    pc = ret_pc;
	}
    }

    class op_nop implements Operation {
	public void execute(int opcode) {
	}
    }

    class op_dec_p implements Operation {
	public void execute(int opcode) {
	    p = (p - 1) & 0xf;
	}
    }

    class op_inc_p implements Operation {
	public void execute(int opcode) {
	    p = (p + 1) & 0xf;
	}
    }

    class op_clear_s implements Operation {
	public void execute(int opcode) {
	    int i;
	    for (i = 0; i < SSIZE; i++)
		s [i] = 0;
	}
    }

    class op_c_exch_m implements Operation {
	public void execute(int opcode) {
	    int i;
	    byte t;
	    for (i = 0; i < WSIZE; i++)
	    {
		t = c [i]; c [i] = m[i]; m [i] = t;
	    }
	}
    }

    class op_m_to_c implements Operation {
	public void execute(int opcode) {
	    int i;
	    for (i = 0; i < WSIZE; i++)
		c [i] = m [i];
	}
    }

    class op_c_to_addr implements Operation {
	public void execute(int opcode) {
	    //#ifdef HP55
	    //	  ram_addr = c [12] * 10 + c[11];
	    //#else
	    ram_addr = (int) c [12];
	    //#endif
	    if (ram_addr >= max_ram)
		System.out.println("c -> ram addr: address " + ram_addr + " out of range\n");
	}
    }

    class op_c_to_data implements Operation {
	public void execute(int opcode) {
	    int i;
	    if (ram_addr >= max_ram)
	    {
		System.out.println("c -> data: address " + ram_addr + " out of range\n");
		return;
	    }
	    for (i = 0; i < WSIZE; i++)
		ram [ram_addr][i] = c [i];
	}
    }

    class op_data_to_c implements Operation {
	public void execute(int opcode) {
	    int i;
	    if (ram_addr >= max_ram)
	    {
		System.out.println("c -> data: address " + ram_addr + " out of range, loading 0\n");
		for (i = 0; i < WSIZE; i++)
		    c [i] = 0;
		return;
	    }
	    for (i = 0; i < WSIZE; i++)
		c [i] = ram [ram_addr][i];
	}
    }

    class op_c_to_stack implements Operation {
	public void execute(int opcode) {
	    int i;
	    for (i = 0; i < WSIZE; i++)
	    {
		f [i] = e [i];
		e [i] = d [i];
		d [i] = c [i];
	    }
	}
    }

    class op_stack_to_a implements Operation {
	public void execute(int opcode) {
	    int i;
	    for (i = 0; i < WSIZE; i++)
	    {
		a [i] = d [i];
		d [i] = e [i];
		e [i] = f [i];
	    }
	}
    }

    class op_down_rotate implements Operation {
	public void execute(int opcode) {
	    int i;
	    byte t;
	    for (i = 0; i < WSIZE; i++)
	    {
		t = c [i];
		c [i] = d [i];
		d [i] = e [i];
		e [i] = f [i];
		f [i] = t;
	    }
	}
    }

    class op_clear_reg implements Operation {
	public void execute(int opcode) {
	    int i;
	    for (i = 0; i < WSIZE; i++)
		a [i] = b [i] = c [i] = d [i] = e [i] = f [i] = m [i] = 0;
	}
    }

    class op_load_constant implements Operation {
	public void execute(int opcode) {
	    if (p >= WSIZE)
	    {
		// I didn't consider the hP-55 case because Eric's comments
		// indicated that it didn't work that well anyway
		//
		//#if 0 /* HP-45 depends on load constant with p > 13 not affecting C */
		//		  printf ("load constant w/ p >= WSIZE at %05o\n", prev_pc)
		//				  ;
		//#endif
	    }
	    else if ((opcode >> 6) > 9)
		System.out.println("load constant > 9");
	    else
		c [p] = (byte) (opcode >> 6);
	    p = (p - 1) & 0xf;
	}
    }

    class op_set_s implements Operation {
	public void execute(int opcode) {
	    if ((opcode >> 6) >= SSIZE)
		System.out.println("stat >= SSIZE at " + prev_pc);
	    else
		s [opcode >> 6] = 1;
	}
    }

    class op_clr_s implements Operation {
	public void execute(int opcode) {
	    if ((opcode >> 6) >= SSIZE)
		System.out.println("stat >= SSIZE at " + prev_pc);
	    else
		s [opcode >> 6] = 0;
	}
    }

    class op_test_s implements Operation {
	public void execute(int opcode) {
	    if ((opcode >> 6) >= SSIZE)
		System.out.println("stat >= SSIZE at " + prev_pc);
	    else
		carry = s [opcode >> 6];
	}
    }

    class op_set_p implements Operation {
	public void execute(int opcode) {
	    p = opcode >> 6;
	}
    }

    class op_test_p implements Operation {
	public void execute(int opcode) {
	    carry = (byte) (((int)p == (opcode >> 6))?1:0);
	}
    }

    class op_sel_rom implements Operation {
	public void execute(int opcode) {
	    rom = opcode >> 7;
	    group = del_grp;
	    
	    del_rom = rom;
	}
    }

    class op_del_sel_rom implements Operation {
	public void execute(int opcode) {
	    del_rom = opcode >> 7;
	}
    }

    class op_del_sel_grp implements Operation {
	public void execute(int opcode) {
	    del_grp = (opcode >> 7) & 1;
	}
    }

    class op_keys_to_rom_addr implements Operation {
	public void execute(int opcode) {
	    pc = key_buf;
	    Toolkit.getDefaultToolkit().beep();    
	}
    }

    class op_rom_addr_to_buf implements Operation {
	public void execute(int opcode) {
	    /* I don't know what the heck this instruction is supposed to do! */
	    //#ifdef DEBUG
	    System.out.println("rom addr to buf!!!!!!!!!!!!");
	    //#endif /* DEBUG */
	}
    }

    class op_display_off implements Operation {
	public void execute(int opcode) {
	    display_enable = false;
	    io_count = 2;
	    /*
	     * Don't immediately turn off display because the very next instruction
	     * might be a display_toggle to turn it on.  This happens in the HP-45
	     * stopwatch.
	     */
	}
    }

    class op_display_toggle implements Operation {
	public void execute(int opcode) {
	    display_enable = ! display_enable;
	    io_count = 0;  /* force immediate display update */
	}
    }

    
    Operation op_fcn[] = new Operation[1024];

    void init_ops ()
    {
	int i;
	for (i = 0; i < 1024; i += 4)
	{
	    op_fcn [i + 0] = new bad_op();
	    op_fcn [i + 1] = new op_jsb();
	    op_fcn [i + 2] = new op_arith();
	    op_fcn [i + 3] = new op_goto();
	}

	op_fcn [0x000] = new op_nop();
	op_fcn [0x01c] = new op_dec_p();
	op_fcn [0x028] = new op_display_toggle();
	op_fcn [0x030] = new op_return();
	op_fcn [0x034] = do_op_clear_s = new op_clear_s();
	op_fcn [0x03c] = new op_inc_p();
	op_fcn [0x0d0] = new op_keys_to_rom_addr();
	op_fcn [0x0a8] = new op_c_exch_m();
	op_fcn [0x128] = new op_c_to_stack();
	op_fcn [0x1a8] = new op_stack_to_a();
	op_fcn [0x200] = new op_rom_addr_to_buf();
	op_fcn [0x228] = new op_display_off();
	op_fcn [0x270] = new op_c_to_addr();
	op_fcn [0x2a8] = new op_m_to_c();
	op_fcn [0x2f0] = new op_c_to_data();
	op_fcn [0x2f8] = new op_data_to_c();
	op_fcn [0x328] = new op_down_rotate();
	op_fcn [0x3a8] = do_op_clear_reg = new op_clear_reg();

	op_fcn [0x234] = new op_del_sel_grp();
	op_fcn [0x2b4] = new op_del_sel_grp();

	for (i = 0; i < 1024; i += 128)
	{
	    op_fcn [i | 0x010] = new op_sel_rom();
	    op_fcn [i | 0x074] = new op_del_sel_rom();
	}
	for (i = 0; i < 1024; i += 64)
	{
	    op_fcn [i | 0x018] = new op_load_constant();
	    op_fcn [i | 0x004] = new op_set_s();
	    op_fcn [i | 0x024] = new op_clr_s();
	    op_fcn [i | 0x014] = new op_test_s();
	    op_fcn [i | 0x00c] = new op_set_p();
	    op_fcn [i | 0x02c] = new op_test_p();
	}
    }

    String pc_to_octal(int pc) {
	String t = Integer.toOctalString(p);
	if (t.length() == 1)
	    return "00" + t;
	else if (t.length() ==2)
	    return "0" + t;
	else return t;
    }
    
    void disassemble_instruction (int g, int r, int p, int opcode)
    {
	int i;
	System.out.print("L"+Integer.toOctalString(g)+
			 Integer.toOctalString(r)+
			 pc_to_octal(p));
	for (i = 0x200; i>0 ; i >>= 1)
	    System.out.print(((opcode & i)!=0) ? "1" : ".");
    }

    /*
     * set breakpoints at every location so we know if we hit
     * uninitialized ROM
     */
    void init_breakpoints ()
    {
	int g, r, p;

	for (g = 0; g < MAX_GROUP; g++)
	    for (r = 0; r < MAX_ROM; r++)
		for (p = 0; p < ROM_SIZE; p++)
		    bpt [g] [r] [p] = 1;
    }

    
    void init_source ()
    {
	int g, r, p;

	for (g = 0; g < MAX_GROUP; g++)
	    for (r = 0; r < MAX_ROM; r++)
		for (p = 0; p < ROM_SIZE; p++)
		    source [g] [r] [p] = null;
    }

    int gt, rt, pt, opcodet;

    boolean parse_address (String oct)
    {
	String s;

	try {
	    s = oct.substring(0,1);
	    gt = Integer.parseInt(s, 8); 
	    s = oct.substring(1,2);
	    rt = Integer.parseInt(s, 8); 
	    s = oct.substring(2,5);
	    pt = Integer.parseInt(s, 8);
	    return true;
	}
	catch (NumberFormatException e) {
	    return false;
	}
    }

    boolean parse_opcode (String bin)
    {
	int i;

	opcodet = 0;
	for (i = 0; i < 10; i++)
	{
	    opcodet <<= 1;
	    if (bin.charAt(i) == '1')
		opcodet += 1;
	    else if (bin.charAt(i) == '.')
		opcodet += 0;
	    else
		return false;
	}
	return true;  
    }

    //
    // trim source lines for display in the LED window
    //
    String trimSource(String str) {
	String t;

	if (str.length() < 48)
	    return new String(str);

	if (str.length() < 80)
	    t = str;
	else
	    t = str.substring(0,80);

	return t.substring(8,15) + t.substring(16,27) + t.substring(28,31)+
		t.substring(32,38) + t.substring(48);

	
    }

    //
    // This reads a zip file that is expected to contain one entry which
    // is the HP-45 firmware listing.  The instructions are parsed for later
    // execution and the source is stored for the debugger
    //
    void read_listing_file (String fn) throws IOException
    {
	int i;
	String buf;
	int count = 0;
	URL url;

	do_trace("Reading microcode file " + fn);
	try {
	    url = new URL(getDocumentBase(), fn);
	    
	    ZipInputStream zip = new ZipInputStream(url.openStream());
	    zip.getNextEntry(); // get the lst file in the zip
	    
	    InputStreamReader isr = new InputStreamReader(zip);
	    
	    BufferedReader in = new BufferedReader(isr);
	    
	    while ((buf = in.readLine()) != null) {
		if ((buf.length() >= 25) && (buf.charAt(7) == 'L') && (buf.charAt(13) == ':') &&
		    parse_address (buf.substring(8)) &&
		    parse_opcode (buf.substring(16)))
		{
		    if ((gt >= MAX_GROUP) || (rt >= MAX_ROM) || (pt >= ROM_SIZE))
			System.out.println("bad address");
		    else if ( bpt [gt][rt][pt] == 0) 
		    {
			System.out.println("duplicate listing line for address " +
					   Integer.toOctalString(gt) +
					   Integer.toOctalString(rt) +
					   pc_to_octal(pt));
			System.out.println("orig: " + source [gt][rt][pt]);
			System.out.println("dup:  " + buf);
		    }
		    else
		    {
			ucode  [gt][rt][pt] = (short) opcodet;
			bpt    [gt][rt][pt] = 0;
			if (keep_src)
			    source [gt][rt][pt] = trimSource(buf);
			count ++;
		    }
		}
	    }
	    System.out.println("Read " + count + " words from " + fn);
	    do_trace("Read " + count + " words of microcode from " + fn);
	} catch (MalformedURLException e) {
	    fatal("Can't access firmware file");
	}
    }

    String reg_to_string (byte r[])
    {
	int i;
	String st = new String("");

	for (i = WSIZE - 1; i >= 0; i--)
	    st += Integer.toHexString(r[i]);
	return st;
    }

    
    String stat_to_string ()
    {
	int i;
	String st = new String("");
	for (i = 0; i < SSIZE; i++)
	    st += (s[i] == 1) ? Integer.toHexString(i) : ".";
	return st;
    }

    
    void handle_io ()
    {
	String dstr = new String("");
	int i;

	if (display_enable)
	{
	    for (i = WSIZE - 1; i >= 0; i--)
	    {
		if (b [i] >= 8)		// using two spaces because 
		    dstr += "  ";	// it's a proportional font
		else if (i == 2)
		{
		    if (a [i] >= 8)
			dstr += "   -";
		    else
			dstr += "    ";
		}
		else if (i == 13)
		{
		    if (a [i] >= 8)
			dstr += "-";
		    else
			dstr += " ";
		}
		else
		    dstr += Integer.toString(a[i]);
		if (b [i] == 2)
		    dstr += ".";
	    }
	}

	update_display (dstr);
	i = check_keyboard ();
	pressed_key = -1;
	if (i >= 0)
	{
	    key_flag = true;
	    key_buf = i;
	}
	else
	    key_flag = false;
    }


    //
    // Debug console code follows
    //

    class CircAddressBuffer {
	int size;
	String buf[];
	int curPos;

	public CircAddressBuffer(int size) {
	    this.size = size;
	    buf = new String[size];
	    curPos = size -1;
	    for (int i=0; i<size; i++)
		buf[i] = "";
	}

	public void put(String s) {
	    buf[curPos--] = new String(s);
	    if (curPos < 0)
		curPos = size -1;
	}

	// 0 is the most recent element
	public String get(int position) {
	    int p;
	    
	    p = curPos+position;
	    if (p > size -1)
		p -= size;
	    return new String(buf[p]);
	}

    }

    //
    // Next displays instructions about to be executed
    // Trace displays previously executed instructions
    //
    final static int MAXTRACE = 8;
    final static int MAXTRACEPAGES = 1999;
    final static int TRACEHEIGHT = 19;
    final static int TRACEFONTSIZE = 18;
    final static int TRACEWIDTH = 889;
    final static String TRACETESTSTRING =
	new String("01234567890123456789012345678901234567890123456789012345678901");

    Rectangle tPageRect;
    Rectangle traceRect[] = new Rectangle[MAXTRACE];

    final static int MAXNEXT = 5;
    Rectangle nextRect[] = new Rectangle[MAXNEXT];
    String nextString[] = new String[MAXNEXT];

    CircAddressBuffer trace_buf;
    int tracePage = 0;
    int BpIn=0;

    void init_trace() {
	int i = TRACEFONTSIZE;

	// reduce font size if necessary to fit the debugger window
	for (i = TRACEFONTSIZE; i>6; i--) {
	    trace_font = new Font("Courier", Font.PLAIN, i);
	    tfm = getFontMetrics(trace_font);
	    
	    if ((TRACEWIDTH >= tfm.stringWidth(TRACETESTSTRING)+2) &&
		(TRACEHEIGHT >= (tfm.getHeight()+2))) break;
	}
	tPageRect = new Rectangle (540, 203, 60, TRACEHEIGHT);
	for (i=0; i<MAXTRACE; i++) {
	    traceRect[i] = new Rectangle(265, 228+i*TRACEHEIGHT, TRACEWIDTH, TRACEHEIGHT);
	};
	trace_buf = new CircAddressBuffer(MAXTRACE*MAXTRACEPAGES);
    }

    void init_next() {
	int i;

	for (i=0; i<MAXNEXT; i++) {
	    nextRect[i] = new Rectangle(265, 394+i*TRACEHEIGHT, TRACEWIDTH, TRACEHEIGHT);
	    nextString[i] = "";
	};
    }

    
    void add_trace(String s) {
	trace_buf.put(s);
    }

    synchronized void display_trace() {
	int i;

	gr.setFont(trace_font);
	for (i=1; i<=MAXTRACE; i++) {
	    draw_string_left (gr, trace_buf.get(i+tracePage*MAXTRACE),
			      traceRect[MAXTRACE-i], BLACK, WHITE, tfm);
	}
	draw_string_left (gr, Integer.toString(BpIn), tPageRect, BLACK, WHITE, tfm);
	gr.setFont(key_font);
    }

    void do_trace(String str){
	tracePage = 0;
	add_trace(str);
        debug(str);
	display_trace();
    }

    void add_next(String str) {
	int i;

	for (i = 1; i < MAXNEXT; i++) {
	    nextString[i-1] = nextString[i];
	}

	nextString[MAXNEXT-1] = new String(str);

    }

    synchronized void display_next() {
	int i;

	gr.setFont(trace_font);
	for (i=0; i<MAXNEXT; i++) {
	    draw_string_left (gr, nextString[i],
			      nextRect[i], BLACK, WHITE, tfm);
	}
	gr.setFont(key_font);
    }

    void do_next(String str){
	add_next(str);
	display_next();
    }

    
    final static int MAXBPS = 3;
    Rectangle bpRect[] = new Rectangle[MAXBPS];
    int bpReg[] = new int[MAXBPS];
    
    int bpinput = 0;	// keyboard input for user breakpoints

    boolean anyBPsEnabled = false;

    void init_user_bp_display() {
	int i;

	for (i = 0; i< MAXBPS; i++) {
	    bpRect[i] = new Rectangle(600, 28+i*30,
				      60, TRACEHEIGHT);
	    bpReg[i] = 0;
	}

    }

    void display_user_bps() {
	int i;

	for (i=0; i<MAXBPS; i++) {
	    draw_string_left (gr, Integer.toOctalString(bpReg[i]), bpRect[i], BR_RED, DK_RED, tfm);
	}
    }

	void display_bpzone() {
	    draw_string_left (gr, Integer.toString(BpIn), tPageRect, BLACK, WHITE, tfm);
    }


    final static int MAXREGS = 9;
    Rectangle regRect[] = new Rectangle[MAXREGS];
    Rectangle regLabelRect[] = new Rectangle[MAXREGS];
    String prevReg[] = new String[MAXREGS];
    final static int DISPLAYREGWIDTH = 116;

    void init_reg_display() {
	int i;

	for (i = 0; i< MAXREGS; i++) {
	    regRect[i] = new Rectangle(382, 25+i*TRACEHEIGHT,
				       DISPLAYREGWIDTH, TRACEHEIGHT);
	    regLabelRect[i] = new Rectangle(362, 25+i*TRACEHEIGHT,
					    20, TRACEHEIGHT);
	    prevReg[i] = new String("");
	}

    }

    void maybe_display(String s, boolean repaint, int reg) {
	if (repaint || !prevReg[reg].equals(s)) {
	    draw_string_left (gr, " " + s, regRect[reg], BLACK, WHITE, tfm);
	    prevReg[reg] = s;
	}
    }

    void display_regs(boolean repaint) {
	int i = 0;
	String temp;
	if (dkeys[DEBUG].value) {
	    if (repaint) {
		draw_string(gr, "A:",
			    regLabelRect[i++], BLACK, VLT_GREY, kfm);
		draw_string(gr, "B:",
			    regLabelRect[i++], BLACK, VLT_GREY, kfm);
		draw_string(gr, "C:",
			    regLabelRect[i++], BLACK, VLT_GREY, kfm);
		draw_string(gr, "D:",
			    regLabelRect[i++], BLACK, VLT_GREY, kfm);
		draw_string(gr, "E:",
			    regLabelRect[i++], BLACK, VLT_GREY, kfm);
		draw_string(gr, "F:",
			    regLabelRect[i++], BLACK, VLT_GREY, kfm);
		draw_string(gr, "M:",
			    regLabelRect[i++], BLACK, VLT_GREY, kfm);
		draw_string(gr, "P:",
			    regLabelRect[i++], BLACK, VLT_GREY, kfm);
		draw_string(gr, "St:",
			    regLabelRect[i++], BLACK, VLT_GREY, kfm);
	    }
	    
	    i = 0;
	    maybe_display(reg_to_string(a), repaint, i);  i++;
	    maybe_display(reg_to_string(b), repaint, i);  i++;
	    maybe_display(reg_to_string(c), repaint, i);  i++;
	    maybe_display(reg_to_string(d), repaint, i);  i++;
	    maybe_display(reg_to_string(e), repaint, i);  i++;
	    maybe_display(reg_to_string(f), repaint, i);  i++;
	    maybe_display(reg_to_string(m), repaint, i);  i++;
	    maybe_display(Integer.toHexString(p), repaint, i);  i++;
	    maybe_display(stat_to_string(), repaint, i);
	}
    }

    

    void my_sleep(long t) {
	try {
	    java.lang.Thread.sleep(t);  // half sec
	}
	catch (InterruptedException e) {
	    //ignore it
	}
    }

    void do_nextDisplay(int group, int rom, int tpc) {
	int j;
	for (j=0; j<MAXNEXT; j++) {
	    do_next(source [group][rom][tpc]);
	    tpc++;
	    if (tpc > 0377)
		tpc -= 0400; // wrap back to beginning of the ROM
			     // not in C version - I suppose C does that by default
	}
    }


public void makedebug()
{
	if (this.getParameter("diag") == null) return;
	debugframe = new Frame("ctrl A, ctrl C, ctrl V");
			
	debugframe.setVisible(true);
	debugframe.setSize(230, 290); // 230 400
	// Location(775, 195) L,H
	debugframe.setLocation(775, 445);
	debugtext = new TextArea();
	debugtext.setFont(new Font("Courier", Font.PLAIN, 14));
	debugframe.add(debugtext);
}

public void debug(String s)
{
	if (debugframe == null) return;
	debugtext.append(s + "\r\n");
	debugframe.setVisible(true);
}

    void debugger ()
    {
	int opcode = 0;
	int cycle = 0;
	int sleep_count = 0;
	makedebug();
	cycle = 0;
	pc = 0;
	rom = 0;
	group = 0;
	del_rom = 0;
	del_grp = 0;

	do_op_clear_reg.execute(0);
	do_op_clear_s.execute(0);
	p = 0;

	io_count = 0;
	display_enable = false;
	key_flag = false;


	for (;;)
	{

	    while (dkeys[RUN].value || step)
	    {
		if (dkeys[SLOW].value && !step){
		    my_sleep(200); 
		    check_debug_keyboard();  // need fast way to exit slow mode
		}
		
		
		// Make keyboard more responsive in stepping case
		if (step)
		    handle_io();
		
		if (dkeys[TRACE].value)
		{
		    if (source [group][rom][pc] != null) {
			do_trace(source [group][rom][pc]);
		    } else
			disassemble_instruction (group, rom, pc, opcode);
		}
		prev_pc = (group << 12) | (rom << 9) | pc;
		opcode = ucode [group][rom][pc];
		prev_carry = carry;
		carry = 0;
		if (key_flag)
		    s [0] = 1;
		
		//#if HP55
		//			  if (learn_mode)
		//				  s [3] = 1;
		//			  if (stopwatch_mode)
		//				  s [11] = 1;
		//#endif
		pc++;
		op_fcn[opcode].execute(opcode);
		if (pc > 0377)
		    pc -= 0400; // wrap back to beginning of the ROM
		
		if (dkeys[TRACE].value || dkeys[SLOW].value || step) {
		    display_regs(false);
		}
		
		cycle++;
		
		if (dkeys[TRACE].value || dkeys[SLOW].value || step) 
		    do_nextDisplay(group, rom, pc);
		
		
		if (dkeys[TRACE].value && dkeys[INCREGS].value) {
		    do_trace("  A=" + reg_to_string (a) +
			     "  B=" + reg_to_string (b) +
			     "  C=" + reg_to_string (c));
		    do_trace("  D=" + reg_to_string (d) +
			     "  M=" + reg_to_string (m) +
			     "  P=" + Integer.toHexString(p) + 
			     "  S=" + stat_to_string());
		}
		
		step = false;
		io_count--;
		if (io_count <= 0)
		{
		    handle_io ();
		    
		    // make keyboard more responsive in slow
		    // case - handle_io slow down full speed runs
		    if (dkeys[SLOW].value)
			io_count = 5;
		    else
			io_count = 35;
		    
		    if (sleep_count++ > 6) {
			sleep_count = 0;
			my_sleep(71);  // drop CPU utilization and make near real time
			display_regs(false); // keep display fresh
		    }
		}
		if (anyBPsEnabled) {
		    int address = (group << 12) | (rom << 9) | pc;
		    int i;
		    for (i=0; i<MAXBPS; i++) {
			if ((address == bpReg[i]) &&
			    dkeys[ENABLE1+i].value) {
			    dkeys[RUN].value = false;
			    update_run_key();
			    do_nextDisplay(group, rom, pc);
			}
		    }
		};
		
	    }
	    my_sleep(100); // don't spin if not running
	    // calling handle_io() here would loose keystroke in stepping case
	    check_debug_keyboard();
	}
    }

    boolean calcRunning = false;

    public void start() {
	if (calcRunning) {
	    //calc_thread.resume();  // warning about navigator deadlock
	}
    }

    public void stop() {
	if (calcRunning) {
	    //calc_thread.suspend();  // warning about navigator deadlock
	}
    }

    class run_calc implements Runnable {
	void get_source_and_run() throws IOException {
	    init_breakpoints ();
	    init_source ();
	    read_listing_file ("hp35.zip");
	    init_ops ();
	    do_trace("Ready.  Press run, slow or step.");
	    debugger ();	    
	}

	public void run () {
	    File f;
	    
	    init_display();
	    
	    try {
		get_source_and_run();
	    } catch (IOException e){
		// maybe it's just a temporary network problem
		// let's try to get it again
		do_trace("Error reading firmware listing file - retrying");
		System.out.println("Error reading firmware listing file - retrying");
		my_sleep(300);
		try {
		    get_source_and_run();
		} catch (IOException e2){
		    do_trace("Fatal error reading firmware listing file - 2nd try");
		    fatal("Error reading listing firmware file - 2nd try");
		}
	    }
	    
	}
    }    
}
