TOY.java


Below is the syntax highlighted version of TOY.java from §6.4 TOY Programming.


/*************************************************************************
 *  Compilation:  javac-introcs TOY.java
 *  Execution:    java-introcs TOY [--verbose] filename.toy
 *  Dependencies: StdIn.java In.java
 *
 *  We use variables of type int to store the TOY registers, main
 *  memory, and program counter even though in TOY these quantities
 *  are 16 and 8 bit integers. Java does not have an 8-bit unsigned
 *  type. The type short in Java does represent 16-bit 2's complement
 *  integers, but using it requires alot of casting. Instead, we are
 *  careful to treat all of the variable as if they were the appropriate
 *  type so that the behavior truly models the TOY machine.
 *
 *  % more multiply.toy
 *  10: 8AFF   read R[A]
 *  11: 8BFF   read R[B]
 *  12: 7C00   R[C] <- 0000
 *  13: 7101   R[1] <- 0001
 *  14: CA18   if (R[A] == 0) goto 18
 *  15: 1CCB   R[C] <- R[C] + R[B]
 *  16: 2AA1   R[A] <- R[A] - R[1]
 *  17: C014   goto 14
 *  18: 9CFF   write R[C]
 *  19: 0000   halt
 *
 *  % java-introcs TOY multiply.toy
 *  0002
 *  0004
 *  0008
 *
 *  % java-introcs TOY --verbose multiply.toy
 *  [core dump]
 *  0002
 *  0004
 *  0008
 *  [core dump]
 *
 *************************************************************************/

import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class TOY {
    private int pc;                     // program counter
    private int[] reg = new int[16];    // 16 registers
    private int[] mem = new int[256];   // 256 main memory locations

    // create a new TOY VM and load with program from specified file
    public TOY(String filename) {
        this(filename, 0x10);
    }

    public TOY(String filename, int pc) {
        this.pc = pc;
        In in = new In(filename);

       /****************************************************************
        *  Read in memory location and instruction.
        *  A valid input line consists of 2 hex digits followed by a
        *  colon, followed by any number of spaces, followed by 4
        *  hex digits. The rest of the line is ignored.
        ****************************************************************/
        String regexp = "^([0-9A-Fa-f]{2}):[ \t]*([0-9A-Fa-f]{4})";
        Pattern pattern = Pattern.compile(regexp);
        while (in.hasNextLine()) {
            String line = in.readLine();
            Matcher matcher = pattern.matcher(line);
            if (matcher.find()) {
                int addr = fromHex(matcher.group(1));
                int inst = fromHex(matcher.group(2));
                mem[addr] = inst;
            }
        }
    }

    // return a 4-digit hex string corresponding to 16-bit integer n
    public static String toHex(int n) {
        return String.format("%04X", n & 0xFFFF);
    }

    // return a 16-bit integer corresponding to the 4-digit hex string s
    public static int fromHex(String s) {
        return Integer.parseInt(s, 16) & 0xFFFF;
    }

    // write to an array of hex integers, 8 per line to standard output
    public static void show(int[] a) {
        for (int i = 0; i < a.length; i++) {
            StdOut.print(toHex(a[i]) + " ");
            if (i % 8 == 7) StdOut.println();
        }
    }


    // print core dump of TOY to standard output
    public void dump() {
        StdOut.println("PC:");
        StdOut.printf("%02X\n", pc);
        StdOut.println();
        StdOut.println("Registers:");
        show(reg);
        StdOut.println();
        StdOut.println("Main memory:");
        show(mem);
        StdOut.println();
    }


    public void run() {

        while (true) {

            // Fetch and parse
            int inst = mem[pc++];            // fetch next instruction
            int op   = (inst >> 12) &  15;   // get opcode (bits 12-15)
            int d    = (inst >>  8) &  15;   // get dest   (bits  8-11)
            int s    = (inst >>  4) &  15;   // get s      (bits  4- 7)
            int t    = inst         &  15;   // get t      (bits  0- 3)
            int addr = inst         & 255;   // get addr   (bits  0- 7)

            // halt
            if (op == 0) break;

            // stdin
            if ((addr == 255 && op == 8) || (reg[t] == 255 && op == 10))
                mem[255] = fromHex(StdIn.readString());

            // Execute
            switch (op) {
                case  1: reg[d] = reg[s] +  reg[t];           break;    // add
                case  2: reg[d] = reg[s] -  reg[t];           break;    // subtract
                case  3: reg[d] = reg[s] &  reg[t];           break;    // bitwise and
                case  4: reg[d] = reg[s] ^  reg[t];           break;    // bitwise xor
                case  5: reg[d] = reg[s] << reg[t];           break;    // shift left
                case  6: reg[d] = (short) reg[s] >> reg[t];   break;    // shift right
                case  7: reg[d] = addr;                       break;    // load address
                case  8: reg[d] = mem[addr];                  break;    // load
                case  9: mem[addr] = reg[d];                  break;    // store
                case 10: reg[d] = mem[reg[t] & 255];          break;    // load indirect
                case 11: mem[reg[t] & 255] = reg[d];          break;    // store indirect
                case 12: if ((short) reg[d] == 0) pc = addr;  break;    // branch if zero
                case 13: if ((short) reg[d] >  0) pc = addr;  break;    // branch if positive
                case 14: pc = reg[d];                         break;    // jump indirect
                case 15: reg[d] = pc; pc = addr;              break;    // jump and link
            }

            // stdout
            if ((addr == 255 && op == 9) || (reg[t] == 255 && op == 11))
                StdOut.println(toHex(mem[255]));

            reg[0] = 0;                // ensure reg[0] is always 0
            reg[d] = reg[d] & 0xFFFF;  // don't let reg[d] overflow a 16-bit integer
            pc = pc & 0xFF;            // don't let pc overflow an 8-bit integer

        }
    }


    // run the TOY simulator with specified file
    public static void main(String[] args) {

        // -v or --verbose is an optional first command-line argument
        boolean isVerbose = false;
        if (args.length > 0 && (args[0].equals("-v") || args[0].equals("--verbose"))) {
            isVerbose = true;
        }

        // the filename is the next command-line argument
        String filename = null;
        if (!isVerbose && args.length > 0) filename = args[0];
        if ( isVerbose && args.length > 1) filename = args[1];

        // the initial value of the PC is an optional last command-line argument
        int pc = 0x10;
        if (!isVerbose && args.length > 1) pc = fromHex(args[1]);
        if ( isVerbose && args.length > 2) pc = fromHex(args[2]);

        // no command-line arguments
        if (args.length == 0) {
            System.err.println("TOY:   invalid command-line options");
            System.err.println("usage: java-introcs TOY [--verbose] filename.toy [pc]");
            return;
        }

        TOY toy = new TOY(filename, pc);

        if (isVerbose) {
            StdOut.println("Core Dump of TOY Before Executing");
            StdOut.println("---------------------------------------");
            toy.dump();

            StdOut.println("Terminal");
            StdOut.println("---------------------------------------");
        }

        toy.run();

        if (isVerbose) {
            StdOut.println();
            StdOut.println("Core Dump of TOY After Executing");
            StdOut.println("---------------------------------------");
            toy.dump();

        }

    }
}


Copyright © 2000–2022, Robert Sedgewick and Kevin Wayne.
Last updated: Thu Aug 11 10:32:25 EDT 2022.