//
// Simple CPU simulator, based loosely on the architecture of the 6502

import java.awt.*;
import java.io.*;
import java.applet.*;
import java.util.*;

public class cpu_sim extends Applet
{ Button assemble, runProg, singleStep;  // Buttons for the user to assemble or run the program
  Button chooseFont;        // Button to convert from small font to big font and vice-versa.
  Button numberMode;
  TextArea progText;                                    // Text area for the user to enter the program

  static final Font textFont=new Font("Helvetica", Font.PLAIN, 14);
  static final int DEFAULT_INITIAL_PC = 500;    // Execution starts at this address by default
  static final Font bigFont = new Font("Helvetica",Font.PLAIN, 20);
  static final Font smallFont = new Font("Helvetica",Font.PLAIN, 14);

  // These constants are the 6502 instruction set opcodes
  static final int ADC_imm = 105;     // ADC immediate mode
  static final int ADC_abs = 109;      // ADC absolute mode
  static final int ADC_zp = 101;        // ADC zero page
  static final int ADC_absx = 125;     // ADC absolute,X
  static final int ADC_absy = 121;     // ADC absolute,Y
  static final int BCC = 144;
  static final int BCS = 176;
  static final int BEQ = 240;
  static final int BMI = 48;
  static final int BNE = 208;
  static final int BPL = 16;
  static final int BRK = 0;
  static final int BVC = 80;
  static final int BVS = 112;
  static final int CLC = 24;
  static final int CLD = 216;
  static final int CLI = 88;
  static final int CLV = 184;
  static final int CMP_abs = 205;
  static final int CMP_imm = 201;
  static final int CMP_zp = 197;
  static final int CPX_imm = 224;
  static final int CPX_abs = 236;
  static final int CPX_zp = 228;
  static final int CPY_imm = 192;
  static final int CPY_abs = 204;
  static final int CPY_zp = 196;
  static final int DEX = 202;
  static final int DEY = 136;
  static final int INX = 232;
  static final int INY = 200;
  static final int JMP_abs = 76;
  static final int JMP_ind = 108;
  static final int JSR = 32;
  static final int LDA_imm = 169;  // LDA immediate mode
  static final int LDA_zp = 165;    // LDA zero page mode
  static final int LDA_abs = 173;   // LDA absolute mode
  static final int LDA_absx = 189;  // LDA absolute,X mode
  static final int LDA_absy = 185;  // LDA absolute,Y mode
  static final int LDX_imm = 162;  // Ditto LDX
  static final int LDX_zp = 166;
  static final int LDX_abs = 174;
  static final int LDX_absy = 190;
  static final int LDY_imm = 160;  // Ditto LDY
  static final int LDY_zp = 164;
  static final int LDY_abs = 172;
  static final int LDY_absx = 188;
  static final int NOP = 234;
  static final int PHA = 72;
  static final int PHP = 8;
  static final int PLA = 104;
  static final int PLP = 40;
  static final int RTS = 96;
  static final int SBC_imm = 233;
  static final int SBC_zp = 229;
  static final int SBC_abs = 237;
  static final int SBC_absx = 253;
  static final int SBC_absy = 249;
  static final int SEC = 54;
  static final int SED = 251;
  static final int SEI = 120;
  static final int STA_zp = 133;
  static final int STA_abs = 141;
  static final int STA_absx = 149;
  static final int STA_absy = 153;
  static final int STX_zp = 134;
  static final int STX_abs = 142;
  static final int STX_absy = 150;
  static final int STY_zp = 132;
  static final int STY_abs = 140;
  static final int STY_absx = 148;
  static final int TAX = 170;
  static final int TAY = 177;
  static final int TXA = 138;
  static final int TYA = 152;
  
  static final int CARRY = 0;               // Status flags
  static final int DECIMAL = 1;
  static final int OVERFLOW = 2;
  static final int INTERRUPT = 3;
  static final int NEGATIVE = 4;
  static final int ZERO = 5;

  static final int UNKNOWN = -1;
  static final int IMMEDIATE = 0;        // Addressing modes
  static final int ZEROPAGE = 1;
  static final int ABSOLUTE = 2;
  static final int INDEX_X = 3;            // Equivalent to (value,X)
  static final int INDEX_Y = 4;            // Equivalent to (value),Y
  static final int ACCUM = 5;              // Accumulator mode (specified by 'A')
  static final int ABS_X = 6;               // Equivalent to value,X
  static final int ABS_Y = 7;               // Equivalent to value,Y
  static final int ZP_X = 8;                  // Same as ABS_X and ABS_Y except for zero page
  static final int ZP_Y = 9;
  static final int INDIRECT = 10;         // Same as (value)

  static final int MAXMEM = 65536;
  static final int MAXLABELS = 100;
  static final int BADNUMBER = 1;    // Error messages
  static final int OUTOFRANGE = 2;
  static final int TOOMANYLABELS = 3;
  static final int DUPLICATELABEL = 4;
  static final int WRONGADDRESSINGMODE = 5;

  int A = 0, X = 0, Y = 0, progCounter = DEFAULT_INITIAL_PC;    // Here are the register values
  int stack = 255;                                                                         // Stack register counts downwards
  int stack_base = 254;                         // High byte of stack pointer (&FE)
  int maxAssembledAddress = 0;        // Set to high water mark of assembled code
  int memory[] = new int[MAXMEM];
  int flags[] = new int[8];
  boolean hexMode = false;         // If set to true, then display all numbers in hex
  int memoryDisplayOffset = DEFAULT_INITIAL_PC;  // The first memory slot displayed on screen
  int zpDisplayOffset = 0;            // Ditto for the zero page displayed
  boolean singleStepMode = false;         // When true, indicates program being single stepped
  int assemblyError = 0;                        // Set to a non-zero value to indicate an error
  int assemblyPass = 0;                        // Two-pass assembler implemented
  int addressingMode = 0;
  int initialPCvalue = DEFAULT_INITIAL_PC;  // Default place where code is assembled to
  int runFromHere = initialPCvalue;        // First instruction to run from
  int startSpecified = -1;                        // The last word in where to run the program
  boolean first_ever_instruction;
  boolean font_is_big = false;                // Set to true when big font selected
  String label[] = new String[MAXLABELS];  // Used to store named locations
  String assemblyErrorString = "";               // Used to report error messages during assembly
  int labelAddress[] = new int[MAXLABELS];  // Holds the addresses corresponding to the locations
  int numLabels = 0;           // How many labels are stored in the array
  
  public void init ()
  { int i;
    setBackground(Color.cyan);
    setFont(textFont);
    assemble = new Button("Assemble");
    add(assemble);
    runProg = new Button("Run");
    add(runProg);
    singleStep = new Button("Single Step");
    add(singleStep);
    numberMode = new Button("Hex mode");
    add(numberMode);
    chooseFont = new Button(" Big font ");
    add(chooseFont);
    progText = new TextArea(10,50);
    add(progText);
    
    // Initialise memory
    for (i = 0; i < MAXMEM; i++)
      memory[i] = 0;
    // Initialise flags to zero
    for (i = 0; i < 8; i++)
      flags[i] = 0;
    // Initialise all labels to 'blank'
    for (i = 0; i < MAXLABELS; i++)
      { label[i] = "";
        labelAddress[i] = 0;
      }
  }
  
  public void paint (Graphics g)
  { // Draw the processor with its three registers, and its program counter
    g.drawRect(8,220,71,150);
    g.drawString("A",15,238);
    box(g, 30, 225, A);
    g.drawString("X",15,262);
    box(g, 30, 248, X);
    g.drawString("Y",15,286);
    box(g, 30, 271, Y);
    g.drawString("SP",10,308);
    box(g, 30, 294, stack);
    g.drawString("PC",10,331);
    box(g, 30, 317, progCounter);
    drawMemory(g);
    drawZeroPage(g);
    drawFlags(g);
    g.drawString(assemblyErrorString, 300, 300);
  }
  
  // Draw a number with a box round it
  public void box (Graphics g, int x, int y, int value)
  { g.drawRect(x, y, 45, 17);
    g.drawString(num(value), x+2, y+13);
  }

  // Draw a section of memory in the lower half of the screen
  public void drawMemory (Graphics g)
  { for (int i = 0; i < 10; i++)
     { int loc = i + memoryDisplayOffset;     // The memory location being displayed
       if (loc == progCounter)                     // The memory location indicated by the pc is shown in red
         g.setColor(Color.red);
       else
        g.setColor(Color.black);
       g.drawString(hex4(loc),100,234 + 17 * i);
       box(g, 137, 220 + 17 * i, memory[loc]);
     }
    g.setColor(Color.black);                      // Just in case
  }

  // Draw the first 20 locations of zero page memory
  public void drawZeroPage (Graphics g)
  { for (int i = 0; i < 10; i++)
     { int loc = i + zpDisplayOffset;        // The memory location being displayed
       if (loc == progCounter)                // The memory location indicated by the pc is shown in red
         g.setColor(Color.red);
       else
        g.setColor(Color.black);
       g.drawString(hex4(loc),200,234 + 17 * i);
       box(g, 237, 220 + 17 * i, memory[loc]);
     }
    g.setColor(Color.black);                      // Just in case
    g.drawRect(284,220, 10,10);              // Draw icon indicating "move window up"
    g.drawLine(284, 230, 289, 220);
    g.drawLine(289, 220, 294, 230);
    g.drawRect(284, 380, 10, 10);
    g.drawLine(284, 380, 289, 390);
    g.drawLine(289, 390, 294, 380);
  }

  // Draw the flags
  public void drawFlags (Graphics g)
  { String s = "CDVINZ";
    for (int i = 0; i < 6; i++)
     { if (i == 3)
         g.drawString("I",48, 350);
       else
         g.drawString("" + s.charAt(i),15 + 10 * i, 350);
       g.drawString("" + flags[i],15 + 10 * i, 365);
     }
  }

  // Return a number as a string in either decimal or hexadecimal mode
  public String num (int x)
  { if (hexMode == false)
      { return Integer.toString(x);
      }
    else
      { if (x > 255)
          return "&" + hex4(x);
        else
          return ("&" + hexDigit(x / 16) + hexDigit(x % 16));
      }
  }

  // Turn a simple number into a four-digit hex number
  public String hex4 (int x)
  { String s = hexDigit(x / (16*16*16));
    x %= 16 * 16 * 16;
    s = s + hexDigit(x / (16 * 16));
    x %= 16 * 16;
    return s + hexDigit(x / 16) + hexDigit(x % 16);
  } 

  // Turn a single number in the range 0..15 into a hexadecimal digit
  public String hexDigit (int digit)
  { String s = "";
    switch (digit)                     // Yes, I know it's inefficient but I can't find any other way!
     { case 0 : s = "0"; break;
       case 1 : s = "1"; break;
       case 2 : s = "2"; break;
       case 3 : s = "3"; break;
       case 4 : s = "4"; break;
       case 5 : s = "5"; break;
       case 6 : s = "6"; break;
       case 7 : s = "7"; break;
       case 8 : s = "8"; break;
       case 9 : s = "9"; break;
       case 10 : s = "A"; break;
       case 11 : s = "B"; break;
       case 12 : s = "C"; break;
       case 13 : s = "D"; break;
       case 14 : s = "E"; break;
       case 15 : s = "F";
     }
     return s;
  }

  // Process the button clicks
  public boolean action (Event e, Object arg)
  { if (e.target == numberMode)
      { hexMode = !hexMode;
        if (hexMode == true)
          numberMode.setLabel("Dec mode");
        else
          numberMode.setLabel("Hex mode");
        repaint();
      }
    if (e.target == assemble)
     { assemblyError = 0;            // 0 indicates no error
       assemblyErrorString = "";
       numLabels = 0;
       startSpecified = -1;
       for (assemblyPass = 1; assemblyPass <= 2; assemblyPass++)
         if (assemblyError == 0)
           assemble(assemblyPass);
       repaint();
     }
    if (e.target == singleStep && singleStepMode == true)
      singleStep();
    if (e.target == runProg)
      runProgram();
    if (e.target == chooseFont)
     { if (font_is_big == true)
        { font_is_big = false;
          progText.setFont(smallFont);
          chooseFont.setLabel("Big font");
        }
       else
        { font_is_big = true;
          progText.setFont(bigFont);
          chooseFont.setLabel("Small font");
        }
       repaint();
     }
    return true;
  }

  // Handle general mouse clicks somewhere on the screen
  public boolean mouseDown (Event event, int x, int y)
  { if (x > 284 && x < 294 && y > 220 && y < 230 && zpDisplayOffset > 0)
      { zpDisplayOffset--;
        repaint();
      }
    if (x > 284 && x < 294 && y > 380 && y < 390 && zpDisplayOffset < 65525)
      { zpDisplayOffset++;
        repaint();
      }
    return true;
  }

  // Assemble the code. The assembly pass is necessary to determine if labels are missing
  public void assemble (int assemblyPass)
  { String s = progText.getText();   // Copy the text into 's'
    StringTokenizer t = new StringTokenizer(s," \n");  // Splits text into tokens separated by spaces
    progCounter = initialPCvalue;
    first_ever_instruction = true;
    memoryDisplayOffset = progCounter;
    while (t.hasMoreTokens() && assemblyError == 0)
      { String s2 = t.nextToken();
        s2 = s2.toUpperCase();
        if (s2.charAt(0) == '.' && assemblyPass == 1)  // Store a label
          storeLabel(s2.substring(1,s2.length()));
        if (s2.compareTo("ADC") == 0)                       // Assemble ADC
          assembleAdd(t.nextToken());
        if (s2.compareTo("BCC") == 0)                       // Assemble BCC
          assembleBranch(t.nextToken(), BCC);
        if (s2.compareTo("BCS") == 0)                       // Assemble BCS
          assembleBranch(t.nextToken(), BCS);
        if (s2.compareTo("BEQ") == 0)                       // Assemble BEQ
          assembleBranch(t.nextToken(), BEQ);
        if (s2.compareTo("BNE") == 0)                       // Assemble BNE
          assembleBranch(t.nextToken(), BNE);
        if (s2.compareTo("BRK") == 0)                       // Assemble BRK
          store(BRK);
        if (s2.compareTo("BVC") == 0)                       // Assemble BVC
          assembleBranch(t.nextToken(), BVC);
        if (s2.compareTo("BVS") == 0)                       // Assemble BVS
          assembleBranch(t.nextToken(), BVS);
        if (s2.compareTo("CLC") == 0)                       // Assemble CLC
          store(CLC);
        if (s2.compareTo("CLD") == 0)                       // Assemble CLD
          store(CLD);
        if (s2.compareTo("CLI") == 0)                        // Assemble CLI
          store(CLI);
        if (s2.compareTo("CLV") == 0)                       // Assemble CLV
          store(CLV);
        if (s2.compareTo("CMP") == 0)                      // Assemble CMP
          assembleCompare(t.nextToken());
        if (s2.compareTo("CPX") == 0)                      // Assemble CPX
          assembleCompareXorY('X',t.nextToken());
        if (s2.compareTo("CPY") == 0)                      // Assemble CPY
          assembleCompareXorY('Y',t.nextToken());
        if (s2.compareTo("DEX") == 0)                       // Assemble DEX
          store(DEX);
        if (s2.compareTo("DEY") == 0)                      // Assemble DEY
          store(DEY);
        if (s2.compareTo("EQUB") == 0)                    // Assemble EQUB (byte value)
          store(getNumber(t.nextToken()));
        if (s2.compareTo("EQUS") == 0)                    // Assemble EQUS (string)
          storeString(t.nextToken());
        if (s2.compareTo("EQUW") == 0)                   // Assemble EQUW (word value)
          { int val = getNumber(t.nextToken());
            store(val % 256);                                       // Low byte
            store(val / 256);                                         // High byte
          }
        if (s2.compareTo("INX") == 0)                        // Assemble INX
          store(INX);
        if (s2.compareTo("INY") == 0)                         // Assemble INY
          store(INY);
        if (s2.compareTo("JMP") == 0)                      // Assemble JMP
          assembleJump(t.nextToken());
        if (s2.compareTo("JSR") == 0)                      // Assemble JSR
          assembleJSR(t.nextToken());
        if (s2.compareTo("LDA") == 0)
          assembleLoad(t.nextToken(),'A');
        if (s2.compareTo("LDX") == 0)
          assembleLoad(t.nextToken(),'X');
        if (s2.compareTo("LDY") == 0)
          assembleLoad(t.nextToken(),'Y');
        if (s2.compareTo("NOP") == 0)                        // Assemble NOP
          store(NOP);
        if (s2.compareTo("PHA") == 0)                        // Assemble PHA
          store(PHA);
        if (s2.compareTo("PHP") == 0)                        // Assemble PHP
          store(PHP);
        if (s2.compareTo("PLA") == 0)                        // Assemble PLA
          store(PLA);
        if (s2.compareTo("PLP") == 0)                        // Assemble PLP
          store(PLP);
        if (s2.compareTo("RTS") == 0)                        // Assemble RTS
          store(RTS);
        if (s2.compareTo("PC") == 0)
          { int newInitialAddress = getNumber(t.nextToken());
            if (assemblyError == 0)
              { if (first_ever_instruction)                    // Affects starts position only if this is the first instruc
                  { initialPCvalue = newInitialAddress;
                    if (startSpecified == -1)
                      runFromHere = newInitialAddress;
                  }
                memoryDisplayOffset = newInitialAddress;
                progCounter = newInitialAddress;
              }
          }
        if (s2.compareTo("SBC") == 0)                       // Assemble SBC
          assembleSubtract(t.nextToken());
        if (s2.compareTo("SEC") == 0)                       // Assemble SEC
          store(SEC);
        if (s2.compareTo("SED") == 0)                       // Assemble SED
          store(SED);
        if (s2.compareTo("SEI") == 0)                       // Assemble SEI
          store(SEI);
        if (s2.compareTo("STA") == 0)
          assembleStore(t.nextToken(),'A');
        if (s2.compareTo("STARTAT") == 0)               // Directive telling processor where to start
          { int newInitialAddress = getNumber(t.nextToken());  // executing code
            if (assemblyError == 0)
              { runFromHere = newInitialAddress;
                startSpecified = newInitialAddress;
              }
          }
        if (s2.compareTo("STX") == 0)
          assembleStore(t.nextToken(),'X');
        if (s2.compareTo("STY") == 0)
          assembleStore(t.nextToken(),'Y');
        if (s2.compareTo("TAX") == 0)                        // Assemble TAX
          store(TAX);
        if (s2.compareTo("TAY") == 0)                        // Assemble TAY
          store(TAY);
        if (s2.compareTo("TXA") == 0)                       // Assemble TXA
          store(TXA);
        if (s2.compareTo("TYA") == 0)                       // Assemble TYA
          store(TYA);
          
        if (progCounter != initialPCvalue)         // Detects whether an instruction has been assembled
          first_ever_instruction = false;
      }
    if (assemblyError != 0)
      reportError();
    maxAssembledAddress = progCounter;     // Store high water mark of assembled addresses
    if (startSpecified != -1)                            // Ready for program to run
      progCounter = startSpecified;
    else
      progCounter = runFromHere;
    singleStepMode = true;              // Indicates program can be stepped through
    repaint();
  }

  // Store a label in the defined list
  public void storeLabel (String lab)
  { lab = lab.toUpperCase();    // Just in case
    if (findLabel(lab) == -1)      // Label not present
     { if (numLabels == MAXLABELS)
         assemblyError = TOOMANYLABELS;
       else
         { label[numLabels] = lab;
           labelAddress[numLabels] = progCounter;
           numLabels++;
         }
     }
    else
     assemblyError = DUPLICATELABEL;
  }

  // find whether a label is present in the stored label list and to return the corresponding address
  // or -1 if the label is not present
  public int findLabel (String lab)
  { int address = -1;      // default value
    lab = lab.toUpperCase();      // Just in case
    if (numLabels > 0)
     for (int i = 0; i < numLabels; i++)
       if (lab.compareTo(label[i]) == 0)
         address = labelAddress[i];
    // If found the label, assume that the mode is absolute
    if (address != -1)
      addressingMode = ABSOLUTE;
    return address;
  }

  // Assemble the instruction ADC
  public void assembleAdd (String operand)
  { int operandNumber = decodeOperand(operand);
    if (assemblyError == 0)
     switch(addressingMode)
      { case IMMEDIATE : store(ADC_imm);
                                     store(operandNumber); break;
        case ZEROPAGE : store(ADC_zp);
                                     store(operandNumber); break;
        case ABSOLUTE : store(ADC_abs);
                                    store(operandNumber % 256);  // Store lower byte of operand
                                    store(operandNumber / 256);    // Store upper byte
                                    break;
        case ABS_X : store(ADC_absx);
                              store(operandNumber % 256);  // Store lower byte of operand
                              store(operandNumber / 256);    // Store upper byte
                              break;
        case ABS_Y : store(ADC_absy);
                              store(operandNumber % 256);  // Store lower byte of operand
                              store(operandNumber / 256);    // Store upper byte
                              break;
        default : assemblyError = WRONGADDRESSINGMODE;
      }
  }

  // Assemble the JMP instruction. Must be followed either by absolute address or indirect
  public void assembleJump (String operand)
  { int operandAddress = decodeOperand(operand);
    if (addressingMode == ABSOLUTE || addressingMode == ZEROPAGE)
      { store(JMP_abs);
        store(operandAddress % 256);   // Low byte
        store(operandAddress / 256);     // high byte
      }
    else if (addressingMode == INDIRECT)
             { store(JMP_ind);
               store(operandAddress % 256);   // Low byte
               store(operandAddress / 256);     // high byte
             }
           else
             { if (assemblyPass == 2)
                 assemblyError = WRONGADDRESSINGMODE;
               else
                 {  // If assembly mode = 1, then store three empty bytes to mark the position
                    store(0);       // These will be replaced by proper values on pass 2
                    store(0);
                    store(0);
                 }
             }
  }

  // Assemble the JSR instruction. Must be followed by absolute address
  public void assembleJSR (String operand)
  { int operandAddress = decodeOperand(operand);
    if (addressingMode == ABSOLUTE || addressingMode == ZEROPAGE)
      { store(JSR);
        store(operandAddress % 256);   // Low byte
        store(operandAddress / 256);     // high byte
      }
    else
      { if (assemblyPass == 2)
         assemblyError = WRONGADDRESSINGMODE;
        else
         {  // If assembly mode = 1, then store three empty bytes to mark the position
            store(0);       // These will be replaced by proper values on pass 2
            store(0);
            store(0);
         }
      }
  }

  // Assemble the Bxx (i.e. Branch on condition) instruction. The exact op-code is passed
  public void assembleBranch (String operand, int opCode)
  { int operandAddress = decodeOperand(operand);
    int offset = 0;
    store(opCode);                    // Store the op code for this particular instruction
    // If the operand was a label which happens to be in zero page, calculate the offset as per normal
    if (findLabel(operand) != -1 && addressingMode == ZEROPAGE)
      addressingMode = ABSOLUTE;      // Treat it as if it were not zero page
    // On the first pass, the labels may not have been defined, so store offset 0
    if (assemblyPass == 1)
      store(0);
    else
      switch (addressingMode)
       { case ABSOLUTE : offset = operandAddress - (progCounter + 1);
                                     if (offset >= 0 && offset <= 127)
                                       store(offset);
                                     else
                                       if (offset < 0 && offset >= -128)
                                         store(256 + offset);
                                       else
                                         assemblyError = OUTOFRANGE;
                                     break;
         case ZEROPAGE : store(operandAddress);  // A simple number after the Bxx command appears
                                      break;                           // the same as a zero page number
         default : assemblyError = WRONGADDRESSINGMODE;
       }
  }

  public void assembleLoad (String operand, char register)
  { int operandNumber = decodeOperand(operand);
    if (assemblyError == 0)
     switch(addressingMode)
      { case IMMEDIATE : switch (register)
                                       { case 'A' : store(LDA_imm); break;
                                         case 'X' : store(LDX_imm); break;
                                         case 'Y' : store(LDY_imm);
                                       }
                                     store(operandNumber); break;
        case ZEROPAGE : switch(register)
                                      { case 'A' : store(LDA_zp); break;
                                        case 'X' : store(LDX_zp); break;
                                        case 'Y' : store(LDY_zp);
                                      }
                                     store(operandNumber); break;
        case ABSOLUTE : switch(register)
                                      { case 'A' : store(LDA_abs); break;
                                        case 'X' : store(LDX_abs); break;
                                        case 'Y' : store(LDY_abs);
                                      }
                                    store(operandNumber % 256);  // Store lower byte of operand
                                    store(operandNumber / 256);    // Store upper byte
                                    break;
        case ABS_X : switch(register)
                                { case 'A' : store(LDA_absx); break;
                                  case 'X' : assemblyError = WRONGADDRESSINGMODE; break;
                                  case 'Y' : store(LDY_absx);
                                 }
                               store(operandNumber % 256);  // Store lower byte of operand
                               store(operandNumber / 256);    // Store upper byte
                               break;
        case ABS_Y : switch(register)
                                { case 'A' : store(LDA_absy); break;
                                  case 'X' : store(LDX_absy); break;
                                  case 'Y' : assemblyError = WRONGADDRESSINGMODE;
                                 }
                               store(operandNumber % 256);  // Store lower byte of operand
                               store(operandNumber / 256);    // Store upper byte
                               break;
      }
  }

  // Assemble the instruction SBC
  public void assembleSubtract (String operand)
  { int operandNumber = decodeOperand(operand);
    if (assemblyError == 0)
     switch(addressingMode)
      { case IMMEDIATE : store(SBC_imm);
                                     store(operandNumber); break;
        case ZEROPAGE : store(SBC_zp);
                                     store(operandNumber); break;
        case ABSOLUTE : store(SBC_abs);
                                    store(operandNumber % 256);  // Store lower byte of operand
                                    store(operandNumber / 256);    // Store upper byte
                                    break;
        case ABS_X : store(SBC_absx);
                              store(operandNumber % 256);  // Store lower byte of operand
                              store(operandNumber / 256);    // Store upper byte
                               break;
        case ABS_Y : store(SBC_absy);
                              store(operandNumber % 256);  // Store lower byte of operand
                              store(operandNumber / 256);    // Store upper byte
                               break;
        default : assemblyError = WRONGADDRESSINGMODE;
      }
  }

  // Assemble the instruction CMP
  public void assembleCompare (String operand)
  { int operandNumber = decodeOperand(operand);
    if (assemblyError == 0)
     switch(addressingMode)
      { case IMMEDIATE : store(CMP_imm);
                                     store(operandNumber); break;
        case ZEROPAGE : store(CMP_zp);
                                     store(operandNumber); break;
        case ABSOLUTE : store(CMP_abs);
                                    store(operandNumber % 256);  // Store lower byte of operand
                                    store(operandNumber / 256);    // Store upper byte
                                    break;
      }
  }

  // Assemble the instruction CPX and CPY
  public void assembleCompareXorY (char register, String operand)
  { int operandNumber = decodeOperand(operand);
    if (assemblyError == 0)
     switch(addressingMode)
      { case IMMEDIATE : if (register == 'X')
                                       store(CPX_imm);
                                     else
                                       store(CPY_imm);
                                     store(operandNumber);
                                     break;
        case ZEROPAGE : if (register == 'X')
                                       store(CPX_zp);
                                     else
                                       store(CPY_zp);
                                     store(operandNumber);
                                     break;
        case ABSOLUTE : if (register == 'X')
                                      store(CPX_abs);
                                    else
                                      store(CPY_abs);
                                    store(operandNumber % 256);  // Store lower byte of operand
                                    store(operandNumber / 256);    // Store upper byte
                                    break;
        default : assemblyError = WRONGADDRESSINGMODE;
      }
  }

  // Assemble the instruction STA, STX or STY (register specified as 'A', 'X' or 'Y')
  public void assembleStore (String operand, char register)
  { int operandNumber = decodeOperand(operand);
    if (assemblyError == 0)
     switch(addressingMode)
      { case ZEROPAGE : switch(register)
                                      { case 'A' : store(STA_zp); break;
                                        case 'X' : store(STX_zp); break;
                                        case 'Y' : store(STY_zp);
                                      }
                                     store(operandNumber); break;
        case ABSOLUTE : switch(register)
                                      { case 'A' : store(STA_abs); break;
                                        case 'X' : store(STX_abs); break;
                                        case 'Y' : store(STY_abs);
                                      }
                                    store(operandNumber % 256);  // Store lower byte of operand
                                    store(operandNumber / 256);    // Store upper byte
                                    break;
        case ABS_X : switch (register)
                                { case 'A' : store(STA_absx); break;
                                  case 'X' : assemblyError = WRONGADDRESSINGMODE; break;
                                  case 'Y' : store(STY_absx);
                                }
                              store(operandNumber % 256);  // Store lower byte of operand
                              store(operandNumber / 256);    // Store upper byte
                              break;
        case ABS_Y : switch(register)
                                { case 'A' : store(STA_absy); break;
                                  case 'X' : store(STX_absy); break;
                                  case 'Y' : assemblyError = WRONGADDRESSINGMODE;
                                }
                              store(operandNumber % 256);  // Store lower byte of operand
                              store(operandNumber / 256);    // Store upper byte
                              break;
        default : assemblyError = WRONGADDRESSINGMODE;
      }
  }

  // Store a single byte value in memory at the address specified by the program counter
  public void store (int x)
  { memory[progCounter] = x;
    progCounter++;
  }

  // Store a string as a sequence of bytes in ascending memory locations
  // At the moment the string cannot contain spaces as it is extracted using nextToken()
  // but needn't be contained within quotation marks
  public void storeString (String s)
  { for (int count = 0; count < s.length(); count++)
     { char c = s.charAt(count);
       memory[progCounter] = c;
       progCounter++;
     }
  }

  // Get a number parameter (e.g. the #0 part of LDA #0)
  // If an unrecognised parameter is found, then assemblyError is set to 'unrecognised parameter'
  public int decodeOperand (String token)
  { token = token.toUpperCase();
    int value = 0;
    addressingMode = UNKNOWN;
    
    if (token.charAt(0) == '#')
     { addressingMode = IMMEDIATE;
       value = getNumber(token.substring(1,token.length()));
       if (assemblyError == 0 && (value < 0 || value > 255))
         assemblyError = OUTOFRANGE;
     }

    // Detect special addressing modes here!

    if (token == "A")
      { addressingMode = ACCUM;  // "A" indicates accumulator mode
        value = 0;
      }

    // Detect indexed X addressing mode, i.e. (value,X)
    if (token.charAt(0) == '(' && token.endsWith(",X)"))
      { int endPos = token.indexOf(",X)");
        value = getNumber(token.substring(1,endPos));
        if (assemblyError == 0 && assemblyPass == 2 && (value < 0 || value > 65535))
          assemblyError = OUTOFRANGE;
        if (assemblyError == 0)
          addressingMode = INDEX_X;
      }

    // Detect indexed Y addressing mode, i.e. (value),Y
    if (token.charAt(0) == '(' && token.endsWith("),Y"))
      { int endPos = token.indexOf("),Y");
        value = getNumber(token.substring(1,endPos));
        if (assemblyError == 0 && assemblyPass == 2 && (value < 0 || value > 65535))
          assemblyError = OUTOFRANGE;
        if (assemblyError == 0)
          addressingMode = INDEX_Y;
      }

    // Now detect indirect mode, i.e. (value)
    if (assemblyError == 0 && addressingMode == UNKNOWN &&
                                                         token.charAt(0) == '(' && token.endsWith(")"))
      { value = getNumber(token.substring(1,token.length() - 1));
        if (assemblyError == 0 && assemblyPass == 2 && (value < 0 || value > 65535))
          assemblyError = OUTOFRANGE;
        if (assemblyError == 0)
          addressingMode = INDIRECT;
      }

    // Detect absolute X addressing mode, i.e. value,X
    if (addressingMode == UNKNOWN && token.endsWith(",X"))
      { int endPos = token.indexOf(",X");
        value = getNumber(token.substring(0,endPos));
        if (assemblyError == 0 && assemblyPass == 2 && (value < 0 || value > 65535))
          assemblyError = OUTOFRANGE;
        if (assemblyError == 0)
          addressingMode = ABS_X;
      }

    // Detect indexed Y addressing mode, i.e. value,Y
    // Not confused with index Y mode as tests for an unknown addressing mode
    if (addressingMode == UNKNOWN && token.endsWith(",Y"))
      { int endPos = token.indexOf(",Y");
        value = getNumber(token.substring(0,endPos));
        if (assemblyError == 0 && assemblyPass == 2 && (value < 0 || value > 65535))
          assemblyError = OUTOFRANGE;
        if (assemblyError == 0)
          addressingMode = ABS_Y;
      }

    // If addressing mode is still unknown, then it must be a simple number, i.e. zero page or absolute
    // Only flag an error in an 'improper' number if on the second pass (could be an unencountered label)
    if (addressingMode == UNKNOWN && assemblyError == 0)
      { value = getNumber(token);
        if (assemblyError == 0 && assemblyPass == 2 && (value < 0 || value > 65535))
          assemblyError = OUTOFRANGE;
        if (assemblyError == 0 && addressingMode == UNKNOWN)  // Addressing mode may have been
          { if (value < 256)                                                               // set by findLabel()
              addressingMode = ZEROPAGE;
            else
              addressingMode = ABSOLUTE;
          }
        // If the token starts with a letter or underscore then it is definitely a label.
        // Assume absolute addressing if this it's a label we haven't come across.
        if (token_is_label(token) == true && findLabel(token) == -1)
          addressingMode = ABSOLUTE;
      }

    return value;
  }

  // Determine whether a string starts with a letter (of either case)
  public boolean token_is_label (String s)
  { boolean temp = false;
    s = s.toUpperCase();      // No permanent damage done
    // I can't think of a better way of doing the following!
    if (s.startsWith("A") || s.startsWith("B") || s.startsWith("C") || s.startsWith("D") || 
        s.startsWith("E") || s.startsWith("F") || s.startsWith("G") || s.startsWith("H") || 
        s.startsWith("I") || s.startsWith("J") || s.startsWith("K") || s.startsWith("L") || 
        s.startsWith("M") || s.startsWith("N") || s.startsWith("O") || s.startsWith("P") || 
        s.startsWith("Q") || s.startsWith("R") || s.startsWith("S") || s.startsWith("T") || 
        s.startsWith("U") || s.startsWith("V") || s.startsWith("W") || s.startsWith("X") || 
        s.startsWith("Y") || s.startsWith("Z") == true) 
          temp  = true;
    return temp;
  }

  // Turn a token into a number - it should detect for & indicating a hex number
  public int getNumber (String s)
  { int value = 0, i;
    int temp = findLabel(s);    // Firstly, detect if the operand is a stored named location
    if (temp > -1)
      return temp;

    // If we get here, then it is not a stored label
    int startingPosition = 0, multiplier = 10;
    if (s.charAt(0) == '&')
      { startingPosition = 1;     // Ignore & from now on
        multiplier = 16;
      }
    for (i = startingPosition; i < s.length(); i++)
     { int x = getDig(s.charAt(i));
       if (x < 0 && assemblyPass == 2)       // Only register errors in numbers on second pass
         assemblyError = BADNUMBER;      // as it might be an unrecognised label.
       else
         value = multiplier * value + x;
     }
    return value;
  }

  // Turn a character into a value (I can't seem to find a 'code' function!)
  public int getDig (char c)
  { int value = 0;
    switch (c)
     { case '0' : value = 0; break;
       case '1' : value = 1; break;
       case '2' : value = 2; break;
       case '3' : value = 3; break;
       case '4' : value = 4; break;
       case '5' : value = 5; break;
       case '6' : value = 6; break;
       case '7' : value = 7; break;
       case '8' : value = 8; break;
       case '9' : value = 9; break;
       case 'A' : value = 10; break;
       case 'B' : value = 11; break;
       case 'C' : value = 12; break;
       case 'D' : value = 13; break;
       case 'E' : value = 14; break;
       case 'F' : value = 15; break;
       default : value = -1;               // Indicates error!
     }
    return value;
  }

  // Report an assembly error
  public void reportError ()
  { String s = "";
    switch (assemblyError)
      { case BADNUMBER : s = "A number in an invalid format."; break;
        case OUTOFRANGE : s = "A number which is out of range."; break;
        case TOOMANYLABELS : s = "You have defined too many labels."; break;
        case DUPLICATELABEL : s = "You have defined a label twice."; break;
        case WRONGADDRESSINGMODE : s = "Illegal addressing mode."; break;
      }
    assemblyErrorString = s;
  }

  // Single step through the program
  public void singleStep ()
  { singleStepMode = true;
    int opCode = memory[progCounter];   // Extract the opcode from memory
    switch (opCode)
     { case ADC_imm : add(IMMEDIATE); break;
       case ADC_zp : add(ZEROPAGE); break;
       case ADC_abs : add(ABSOLUTE); break;
       case ADC_absx : add(ABS_X); break;
       case ADC_absy : add(ABS_Y); break;
       case BCC : branch(CARRY, 0); break;
       case BCS : branch(CARRY, 1); break;
       case BEQ : branch(ZERO, 1); break;
       case BMI : branch(NEGATIVE, 1); break;
       case BNE : branch(ZERO, 0); break;
       case BPL : branch(NEGATIVE, 0); break;
       case BRK : singleStepMode = false;   // Indicates overflowed off end of program, so stop running
       case BVC : branch(OVERFLOW, 0); break;
       case BVS : branch(OVERFLOW, 1); break;
       case CLC : flags[CARRY] = 0; advance(); break;
       case CLD : flags[DECIMAL] = 0; advance(); break;
       case CLI : flags[INTERRUPT] = 0; advance(); break;
       case CLV : flags[OVERFLOW] = 0; advance(); break;
       case CMP_abs : compare('A',ABSOLUTE); break;
       case CMP_imm : compare('A',IMMEDIATE); break;
       case CMP_zp : compare('A',ZEROPAGE); break;
       case CPX_imm : compare('X',IMMEDIATE); break;
       case CPX_zp : compare('X',ZEROPAGE); break;
       case CPX_abs : compare('X',ABSOLUTE); break;
       case CPY_imm : compare('Y',IMMEDIATE); break;
       case CPY_zp : compare('Y',ZEROPAGE); break;
       case CPY_abs : compare('Y',ABSOLUTE); break;
       case DEX : inx_or_dex(255); break;
       case DEY : iny_or_dey(255); break;
       case INX : inx_or_dex(1); break;
       case INY : iny_or_dey(1); break;
       case JMP_abs : advance();
                                progCounter = getOperand(ABSOLUTE);
                                memoryDisplayOffset = progCounter;       // Display at correct location
                                break;
       case JMP_ind : advance();
                               progCounter = getOperand(INDIRECT);
                               memoryDisplayOffset = progCounter;       // Display at correct location
                               break;
       case JSR : jump_to_subroutine(); break;
       case LDA_imm : loadRegister('A',IMMEDIATE); break;
       case LDA_zp : loadRegister('A',ZEROPAGE); break;
       case LDA_abs : loadRegister('A',ABSOLUTE); break;
       case LDA_absx : loadRegister('A',ABS_X); break;
       case LDA_absy : loadRegister('A',ABS_Y); break;
       case LDX_imm : loadRegister('X',IMMEDIATE); break;
       case LDX_zp : loadRegister('X',ZEROPAGE); break;
       case LDX_abs : loadRegister('X',ABSOLUTE); break;
       case LDX_absy : loadRegister('X',ABS_Y); break;
       case LDY_imm : loadRegister('Y',IMMEDIATE); break;
       case LDY_zp : loadRegister('Y',ZEROPAGE); break;
       case LDY_abs : loadRegister('Y',ABSOLUTE); break;
       case LDY_absx : loadRegister('Y',ABS_X); break;
       case NOP : advance(); break;
       case PHA : push(A);
                         advance();
                         break;
       case PHP : push_flags_register(); break;
       case PLA : A = pop();
                        advance();
                        break;
       case PLP : pop_flags_register(); break;
       case RTS : return_from_subroutine(); break;
       case SBC_imm : subtract(IMMEDIATE); break;
       case SBC_zp : subtract(ZEROPAGE); break;
       case SBC_abs : subtract(ABSOLUTE); break;
       case SBC_absx : subtract(ABS_X); break;
       case SBC_absy : subtract(ABS_Y); break;
       case SEC : flags[CARRY] = 1; advance(); break;
       case SED : flags[DECIMAL] = 1; advance(); break;
       case SEI : flags[INTERRUPT] = 1; advance(); break;
       case STA_zp : storeRegister('A',ZEROPAGE); break;
       case STA_abs : storeRegister('A',ABSOLUTE); break;
       case STA_absx : storeRegister('A',ABS_X); break;
       case STA_absy : storeRegister('A',ABS_Y); break;
       case STX_zp : storeRegister('X',ZEROPAGE); break;
       case STX_abs : storeRegister('X',ABSOLUTE); break;
       case STX_absy : storeRegister('X',ABS_Y); break;
       case STY_zp : storeRegister('Y',ZEROPAGE); break;
       case STY_abs : storeRegister('Y',ABSOLUTE); break;
       case STY_absx : storeRegister('Y',ABS_Y); break;
       case TAX : X = A; advance(); break;
       case TAY : Y = A; advance(); break;
       case TXA : A = X; advance(); break;
       case TYA : A = Y; advance(); break;
     }
    repaint();
  }

  public void runProgram ()
  { progCounter = initialPCvalue;
    do
     { singleStep();
     }
    while (singleStepMode == true);
  }

  // Perform a relative branch if a given flag is a given value (1 or 0)
  public void branch (int flag_to_test, int one_or_zero)
  { advance();                                    // Get the next byte of memory
    int offset = getOperand(IMMEDIATE);  // getOperand also moves to byte after operand!
    if (flags[flag_to_test] == one_or_zero)   // Do the branch if the flag has specified value
     { if (offset < 128)        // Represents a leap forward
         progCounter += offset;
       else
         progCounter -= 256 - offset;
       if (progCounter < memoryDisplayOffset || progCounter > memoryDisplayOffset + 9)
         memoryDisplayOffset = progCounter;     // Display at correct new position
     }
  }

  // Execute ADC.
  public void add (int mode)
  { advance();
    int value = getOperand(mode);
    value += flags[CARRY];      // Add value of carry flag
    A += value;                        // Add adjusted value to accumulator
    if (A > 255)                        // Cope with overflow
      { A -= 256;
        flags[OVERFLOW] = 1;
        flags[CARRY] = 1;
      }
    else
      { flags[OVERFLOW] = 0;
        flags[CARRY] = 0;
      }
    zero_and_neg(A);
  }

  // Execute SBC. Can cope with immediate, zero page and absolute modes
  public void subtract (int mode)
  { advance();
    int value = getOperand(mode);
    // If absolute addressing mode, then the value is the address where value is to be found
    // Otherwise processor thinks the address is to be placed in the register!
    if (mode == ABSOLUTE)
      value = memory[value];
    value += 1 - flags[CARRY]; // Add 1 if carry flag is clear
    A -= value;                         // Subtract adjusted value to accumulator
    if (A < 0)                            // Cope with underflow
      { A += 256;
        flags[NEGATIVE] = 1;
        flags[CARRY] = 0;          // Carry = 0 indicates borrow occurred (in multi-byte subtraction)
      }
    else
      { flags[NEGATIVE] = 0;
        flags[CARRY] = 1;         // Indicates that no borrow has occurred
      }
    if (A == 0)
      flags[ZERO] = 1;
    else
      flags[ZERO] = 0;
  }
  
  // Execute CMP, CPX and CPY.
  public void compare (char register, int mode)
  { advance();
    int regValue = 0;                        // This represents the register being compared
    switch (register)
     { case 'A' : regValue = A; break;
       case 'X' : regValue = X; break;
       case 'Y' : regValue = 'Y';
     }
    int value = getOperand(mode);
    // If absolute addressing mode, then the value is the address where value is to be found
    // Otherwise processor thinks the address is to be placed in the register!
    if (mode == ABSOLUTE)
      value = memory[value];
    if (regValue >= value)
      flags[CARRY] = 1;
    else
      flags[CARRY] = 0;
    if (regValue == value)
      flags[ZERO] = 1;
    else
      flags[ZERO] = 0;
    int result = regValue - value;     // Calculate whether bit 7 of the "subtraction" is set
    if (result < 0)
      result += 256;
    if (result > 127)
      flags[NEGATIVE] = 1;
    else
      flags[NEGATIVE] = 0;
  }

  // Execute LDA, LDX, LDY. Can cope with immediate, zero page and absolute modes
  public void loadRegister (char register, int mode)
  { advance();
    int value = getOperand(mode);
    // If absolute addressing mode, then the value is the address where value is to be found
    // Otherwise processor thinks the address is to be placed in the register!
    if (mode == ABSOLUTE)
      value = memory[value];
    zero_and_neg(value);
    switch (register)
     { case 'A' : A = value; break;
       case 'X' : X = value; break;
       case 'Y' : Y = value; break;
     }
  }
  
  // Get an operand for instructions such as ADC, SBC and LDA/X/Y
  public int getOperand (int addressingMode)
  { int value = 0;
    int address = 0;               // Used for building complex addresses
    switch (addressingMode)
     { case IMMEDIATE : value = memory[progCounter]; break;
       case ZEROPAGE : value = memory[memory[progCounter]]; break;
       case ABSOLUTE : value = memory[progCounter];
                                   advance();
                                   value += 256 * memory[progCounter];
                                   break;
       case INDIRECT : address = memory[progCounter];
                                 advance();
                                 address += 256 * memory[progCounter];
                                 value = memory[address] + 256 * memory[(address+1) % MAXMEM];
                                 break;                                       // % MAXMEM prevents memory overflow
       case ABS_X : address = memory[progCounter];
                            advance();
                            address += 256 * memory[progCounter];
                            value = memory[(address + X) % MAXMEM];
                            break;
       case ABS_Y : address = memory[progCounter];
                            advance();
                            address += 256 * memory[progCounter];
                            value = memory[(address + Y) % MAXMEM];
                            break;
     }
    advance();        // Moves program counter to start of next instruction
    return value;
  }

  // Execute STA, STX, STY. Can cope with immediate, zero page and absolute modes
  public void storeRegister (char register, int mode)
  { int address = 0;
    advance();
    int value = 0;
    switch (register)
     { case 'A' : value = A; break;
       case 'X' : value = X; break;
       case 'Y' : value = Y; break;
     }
    switch (mode)
     { case ZEROPAGE : memory[memory[progCounter]] = value; break;
       case ABSOLUTE : address = memory[progCounter];
                                   advance();
                                   address += 256 * memory[progCounter];
                                   memory[address] = value;
                                   break;
       case ABS_X : address = memory[progCounter];
                             advance();
                             address += 256 * memory[progCounter];
                             memory[(address + X) % MAXMEM] = value;
                             break;
       case ABS_Y : address = memory[progCounter];
                             advance();
                             address += 256 * memory[progCounter];
                             memory[(address + Y) % MAXMEM] = value;
                             break;
     }
    advance();
  }

  public void jump_to_subroutine ()
  { advance();
    int destination = memory[progCounter];   // Low byte first
    advance();
    destination += 256 * memory[progCounter];
    advance();      // Move to the next address so that it can be stacked
    push(progCounter % 256);                     // Stack low byte first
    push((int)(progCounter / 256));               // Then stack high byte;
    progCounter = destination;
    if (progCounter < memoryDisplayOffset || progCounter > memoryDisplayOffset + 9)
      memoryDisplayOffset = progCounter;     // Display at correct new position
  }

  public void return_from_subroutine ()
  { int return_address = 256 * pop();
    return_address += pop();
    progCounter = return_address;
  }

  // Push a value onto the stack. There is no check for stack pointer underflow
  public void push (int value)
  { memory[256 * stack_base + stack] = value;
    stack--;  // Decrement stack pointer
    if (stack < 0)
      stack = 255;
  }

  // Pop a value from the stack. There is no check for stack pointer overflow
  public int pop ()
  { stack++;            // Firstly increment stack pointer
    if (stack > 255)
      stack = 0;
    return memory[256 * stack_base + stack];
  }

  // Construct a byte value from the flags and push it. I don't know if this
  // is the correct order for the flags.
  public void push_flags_register ()
  { push(32 * flags[CARRY] + 16 * flags[DECIMAL] + 8 * flags[OVERFLOW] +
             4 * flags[INTERRUPT] + 2 * flags[NEGATIVE] + flags[ZERO]);
    advance();
  }
  
  // Pop the flags register from the stack
  public void pop_flags_register ()
  { int temp = pop();
    flags[ZERO] = temp % 2;
    temp = (temp - flags[ZERO]) / 2;
    flags[NEGATIVE] = temp % 2;
    temp = (temp - flags[NEGATIVE]) / 2;
    flags[INTERRUPT] = temp % 2;
    temp = (temp - flags[INTERRUPT]) / 2;
    flags[OVERFLOW] = temp % 2;
    temp = (temp - flags[OVERFLOW]) / 2;
    flags[DECIMAL] = temp % 2;
    temp = (temp - flags[DECIMAL]) / 2;
    flags[CARRY] = temp % 2;                 // Just in case an unsuitable number was popped
    advance();
  }

  // Execute INX (value = 1) or DEX (value = 255)
  public void inx_or_dex (int value)
  { X = (X + value) % 256;
    zero_and_neg(X);
    advance();
  }

  // Execute INY (value = 1) or DEY (value = 255)
  public void iny_or_dey (int value)
  { Y = (Y + value) % 256;
    zero_and_neg(Y);
    advance();
  }

  // Determine the status of the Zero and Negative flags
  public void zero_and_neg (int value)
  { if (value == 0)
      flags[ZERO] = 1;
    else
      flags[ZERO] = 0;
    if (value > 127)             // MSB set
      flags[NEGATIVE] = 1;
    else
      flags[NEGATIVE] = 0;
  }

  // Advance the program counter one space. If it moves "off the screen" then advance the screen
  // offset one space unless we are already at the end of memory.
  public void advance ()
  { progCounter++;
    if (progCounter > MAXMEM)       // Exceeded maximum memory capacity!
      progCounter = 0;
    if (progCounter < memoryDisplayOffset)          // Program counter is prior to displayed memory
      memoryDisplayOffset = progCounter;
    if (progCounter > memoryDisplayOffset + 10)  // Program counter is beyond displayed memory
      memoryDisplayOffset = progCounter - 5;       // so display executed instruction in the middle
  }  // No need for repaint() as calling routine does that!

  // Report an error
  public void reportError (Graphics g, int errorNumber, String duffToken)
  { switch(errorNumber)
     { case BADNUMBER : g.drawString(duffToken + " is a bad number!",350,250); break;
     }
  }

}

//