예제 #1
0
void do_set(unsigned short address, const std::string& expr)
{
    int value_calc;
    if (lc3_calculate(state, expr, value_calc))
        return;
    do_set(address, (short)value_calc);
}
예제 #2
0
/** lc3_blackbox_test
  *
  * Tests if we have hit a blackbox.
  */
bool lc3_blackbox_test(lc3_state& state)
{
    bool blackboxing = false;
    // Test for blackboxes
    if (state.blackboxes.find(state.pc) != state.blackboxes.end())
    {
        lc3_blackbox_info& info = state.blackboxes[state.pc];
        if (info.enabled)
        {
            int boolean = 0;
            if (lc3_calculate(state, info.condition, boolean))
            {
                // Expression has an error...
                // TODO Restrict condition to 128 chars
                char buf[256];
                sprintf(buf, "Blackbox: %s Error in Expression: %s", info.label.c_str(), info.condition.c_str());
                lc3_warning(state, buf);
            }
            else if (boolean)
            {
                // Signal blackbox
                blackboxing = true;
                info.hit_count++;
            }
        }
    }

    return blackboxing;
}
예제 #3
0
void do_set(unsigned char reg, const std::string& expr)
{
    int value_calc;
    if (lc3_calculate(state, expr, value_calc))
        return;
    do_set(reg, (short)value_calc);
}
예제 #4
0
/** OnGoto
  *
  * Called when the user wants customize what memory addresses are shown
  */
void OnHideAddressesCustom(MemoryGrid* memory, MemoryView* memoryView)
{
    ///TODO come up with a better dialog for this.
    std::string rangesStr = wxGetTextFromUser(
        "Enter a list of ranges in the format start-end,start2-end2,start3-end3\n"
        "Ending address for range is inclusive\n"
        "Example x3000-x3010,xE000-xF000",
        "Custom Hide Addresses").ToStdString();
		if (rangesStr.empty()) return;
		std::vector<std::string> parsed_ranges;
		tokenize(rangesStr, parsed_ranges, ",");
		std::vector<ViewRange> ranges;
		for (const auto& parsed_range : parsed_ranges)
    {
        int start, end;
        std::vector<std::string> start_end;
        tokenize(parsed_range, start_end, "-");
        if (start_end.size() != 2)
        {
            wxMessageBox(wxString::Format("%s is not a valid range", parsed_range), "Error");
            return;
        }
        if (lc3_calculate(state, start_end[0], start) ||
            lc3_calculate(state, start_end[1], end))
        {
            wxMessageBox(wxString::Format("%s does not contain valid addresses or malformed", parsed_range), "Error");
            return;
        }
        ranges.emplace_back(start, end);
    }
    memoryView->ShowAllAddresses();
    memoryView->SetDefaultVisibility(ViewAction::HIDE);
    memoryView->ModifyAddresses(ranges);
    ///TODO learn the interface for updating a grid's dimensions via wxGridTableMessage
    // This is inefficient, but well...
    memory->SetView(memoryView);
    memory->SelectLocation((unsigned short) state.pc > 0xFFF0 ? 0xFFFF : 0);
    memory->SelectLocation((unsigned short) state.pc);
    memory->ForceRefresh();
}
예제 #5
0
/** OnGoto
  *
  * Called when the user wants to goto a certain address
  */
void OnGoto(MemoryGrid* memory)
{
    wxString wxaddress = wxGetTextFromUser(_("Input Address"), _("Goto Address"));
    if (wxaddress.IsEmpty()) return;
    int addr;
    int error = lc3_calculate(state, wxaddress.ToStdString(), addr);

    if (error)
    {
        PrintError(error);
        return;
    }

    MakeCellVisible(memory, addr);
}
예제 #6
0
/** OnGoto
  *
  * Called when the user wants to goto a certain address
  */
void OnGoto(MemoryGrid* memory)
{
    wxString wxaddress = wxGetTextFromUser(_("Input Address"), _("Goto Address"));
    if (wxaddress.IsEmpty()) return;
    int addr;
    int error = lc3_calculate(state, wxaddress.ToStdString(), addr);

    if (error)
    {
        PrintError(error);
        return;
    }

    memory->SelectLocation((unsigned short) addr > 0xFFF0 ? 0xFFFF : 0);
    memory->SelectLocation((unsigned short) addr);
    memory->Refresh();
}
예제 #7
0
/** OnGoto
  *
  * Jumps to a memory address
  */
void MemoryViewFrame::OnGoto(wxCommandEvent& event)
{
    wxString wxaddress = wxGetTextFromUser(_("Input Address"), _("Goto Address"), wxEmptyString, this);
    std::string address = (std::string) wxaddress.mb_str();
    int addr;
    int error = lc3_calculate(state, address, addr);

    if (error)
    {
        PrintError(error);
        return;
    }

    memory->SelectLocation(0);
    memory->SelectLocation((unsigned short) addr);
    memory->Refresh();
}
예제 #8
0
/** OnRegisterChanged
  *
  * Called when a registers value is changed.
  */
void ComplxFrame::OnRegisterChanged(wxCommandEvent& event)
{
    wxTextCtrl* text = static_cast<wxTextCtrl*>(event.GetEventObject());

    wxString name = text->GetName();
    std::string strdata = text->GetValue().ToStdString();
    int data;
    int error = lc3_calculate(state, strdata, data);

    if (name == _("CC") && strdata.size() == 1)
    {
        error = 0;
        if (strdata[0] == 'N' || strdata[0] == 'n')
            data = -1;
        else if (strdata[0] == 'Z' || strdata[0] == 'z')
            data = 0;
        else if (strdata[0] == 'P' || strdata[0] == 'p')
            data = 1;
        else
            error = 1;
    }

    if (error) lc3_warning(state, "Unable to parse expression " + strdata);
    if (name == _("PC"))
    {
        if (!error) state.pc = (unsigned short) data;
        UpdateRegister(text, error ? state.pc : data, 8);
        memory->SelectLocation(0);
        memory->SelectLocation(state.pc);
        memory->Refresh();
    }
    else if (name == _("CC"))
    {
        if (!error) lc3_setcc(state, data);
        int cc = state.n ? -1 : (state.z ? 0 : 1);
        UpdateRegister(text, error ? cc : data, 9);
    }
    else
    {
        int reg = name[1] - '0';
        if (!error) state.regs[reg] = (short) data;
        assert(reg <= 7);
        UpdateRegister(text, error ? state.regs[reg] : data, reg);
    }
}
예제 #9
0
/** ComplxFrame
  *
  * Constructor
  */
ComplxFrame::ComplxFrame(long decimal, long disassemble, long stack_size, long true_traps, long interrupts, long highlight,
                         wxString address_str, wxString state_file, wxArrayString files) : ComplxFrameDecl(NULL), console(NULL), memoryView(NULL)
{
    InitImages();

    menuStateTrueTraps->Check(true_traps == 1);
    menuStateInterrupts->Check(interrupts == 1);
    menuViewInstructionHighlighting->Check(highlight != 0);
    menuViewUnsignedDecimal->Check(decimal == 1);

    this->stack_size = stack_size;
    OnInit();

    if (!address_str.IsEmpty())
    {
        int addr;
        lc3_calculate(state, address_str.ToStdString(), addr);
        state.pc = addr;
    }

    if (!state_file.IsEmpty()) DoLoadMachine(wxFileName(state_file));
    if (files.size() > 0) DoLoadFile(wxFileName(files[0]));

    memoryView = new MemoryView();
    memory->SetView(memoryView);
    memory->SetDisassembleLevel(disassemble > 2 ? 2 : disassemble);
    memory->SetUnsignedMode(decimal > 1 ? 0 : decimal);
    memory->SetHighlight(highlight > 1 ? 1 : highlight);

    int widths[3] = {-7, -3, -3};
    int styles[3] = {wxSB_NORMAL, wxSB_NORMAL, wxSB_NORMAL};
    statusBar->SetStatusWidths(3, widths);
    statusBar->SetStatusStyles(3, styles);

    UpdateMemory();
    UpdateStatus();

    Connect(wxID_ANY, wxEVT_COMMAND_RUNTHREAD_COMPLETED, wxThreadEventHandler(ComplxFrame::OnRunComplete));
    Connect(wxID_ANY, wxEVT_COMMAND_RUNTHREAD_UPDATE, wxThreadEventHandler(ComplxFrame::OnRunUpdate));
    Connect(wxID_ANY, wxEVT_COMMAND_RUNTHREAD_IO, wxThreadEventHandler(ComplxFrame::OnIo));
    Connect(wxID_ANY, wxEVT_COMMAND_RUNTHREAD_NOIO, wxThreadEventHandler(ComplxFrame::OnNoIo));
    Connect(wxID_ANY, wxEVT_COMMAND_RUNTHREAD_OUTPUT, wxThreadEventHandler(ComplxFrame::OnOutput));
}
예제 #10
0
/** lc3_break_test
  *
  * Tests if we have hit a breakpoint or a watchpoint.
  */
bool lc3_break_test(lc3_state& state, const lc3_state_change* changes)
{
    // Test for breakpoints
    if (state.breakpoints.find(state.pc) != state.breakpoints.end())
    {
        lc3_breakpoint_info& info = state.breakpoints[state.pc];
        if (info.enabled)
        {
            int boolean = 0;
            if (lc3_calculate(state, info.condition, boolean))
            {
                // Expression has an error...
                // TODO Restrict condition to 128 chars
                char buf[256];
                sprintf(buf, "Breakpoint: %s Error in Expression: %s", info.label.c_str(), info.condition.c_str());
                lc3_warning(state, buf);
            }
            else if (boolean)
            {
                // Halt
                state.halted = 1;
                info.hit_count++;
                // If we are concerned about the number of times and we have surpassed the max.
                if (info.max_hits >= 0 && info.hit_count >= info.max_hits)
                    // Time to delete
                    lc3_remove_break(state, state.pc);
            }
        }
    }

    // Test for watchpoints
    bool r7changed = state.regs[7] != changes->r7;
    // Have we modified a register or a memory address?
    if (changes->changes == LC3_REGISTER_CHANGE)
    {
        int reg = changes->location;
        if (state.reg_watchpoints.find(reg) != state.reg_watchpoints.end())
        {
            lc3_watchpoint_info& info = state.reg_watchpoints[reg];
            if (info.enabled)
            {
                int boolean = 0;
                if (lc3_calculate(state, info.condition, boolean))
                {
                    // Expression has an error...
                    // TODO Restrict condition to 128 chars and label to 32 chars
                    char buf[256];
                    sprintf(buf, "Watchpoint: %s Error in Expression: %s", info.label.c_str(), info.condition.c_str());
                    lc3_warning(state, buf);
                }
                else if (boolean)
                {
                    // Halt
                    state.halted = 1;
                    info.hit_count++;
                    // If we are concerned about the number of times and we have surpassed the max.
                    if (info.max_hits >= 0 && info.hit_count >= info.max_hits)
                        // Time to delete
                        lc3_remove_watch(state, true, reg);
                }
            }
        }
    }
    else if (changes->changes == LC3_MEMORY_CHANGE)
    {
        int mem = changes->location;
        if (state.mem_watchpoints.find(mem) != state.mem_watchpoints.end())
        {
            lc3_watchpoint_info& info = state.mem_watchpoints[mem];
            if (info.enabled)
            {
                int boolean = 0;
                if (lc3_calculate(state, info.condition, boolean))
                {
                    // Expression has an error...
                    // TODO Restrict condition to 128 chars and label to 32 chars
                    char buf[256];
                    sprintf(buf, "Watchpoint: %s Error in Expression: %s", info.label.c_str(), info.condition.c_str());
                    lc3_warning(state, buf);
                }
                else if (boolean)
                {
                    // Halt
                    state.halted = 1;
                    info.hit_count++;
                    // If we are concerned about the number of times and we have surpassed the max.
                    if (info.max_hits >= 0 && info.hit_count >= info.max_hits)
                        // Time to delete
                        lc3_remove_watch(state, false, mem);
                }
            }
        }
    }

    // Also handle r7 changes.
    if (r7changed)
    {
        int reg = 7;
        if (state.reg_watchpoints.find(reg) != state.reg_watchpoints.end())
        {
            lc3_watchpoint_info& info = state.reg_watchpoints[reg];
            if (info.enabled)
            {
                int boolean = 0;
                if (lc3_calculate(state, info.condition, boolean))
                {
                    // Expression has an error...
                    // TODO Restrict condition to 128 chars and label to 32 chars
                    char buf[256];
                    sprintf(buf, "Watchpoint: %s Error in Expression: %s", info.label.c_str(), info.condition.c_str());
                    lc3_warning(state, buf);
                }
                else if (boolean)
                {
                    // Halt
                    state.halted = 1;
                    info.hit_count++;
                    // If we are concerned about the number of times and we have surpassed the max.
                    if (info.max_hits >= 0 && info.hit_count >= info.max_hits)
                        // Time to delete
                        lc3_remove_watch(state, true, reg);
                }
            }
        }
    }

    return state.halted;
}
예제 #11
0
void lc3_run_test_case(lc3_test& test, const std::string& filename, int seed)
{
    lc3_state state;

    // Preliminary stuff
    if (seed != -1) srand(seed);
    lc3_init(state, test.randomize, test.randomize);
    if (test.true_traps) lc3_set_true_traps(state, 1);
    if (test.interrupt_enabled) state.interrupt_enabled = 1;
    bool disable_plugins = test.disable_plugins;
    state.max_stack_size = 0;
    state.max_call_stack_size = -1;
    state.in_lc3test = true;

    try
    {
        LC3AssembleOptions options;
        options.multiple_errors = false;
        options.warnings_as_errors = false;
        options.process_debug_comments = false;
        options.enable_warnings = false;
        options.disable_plugins = disable_plugins;
        lc3_assemble(state, filename, options);
    }
    catch (LC3AssembleException e)
    {
        throw e.what();
    }


    std::stringstream* newinput = new std::stringstream();

    // Set up test environment
    for (unsigned int i = 0; i < test.input.size(); i++)
    {
        lc3_test_input& input = test.input[i];
        int value_calc;
        int address_calc = 0;
        unsigned short effective_address;

        ///TODO flip the condition here so that if new types are added you don't have to check for it here...
        if (input.type != TEST_IO && input.type != TEST_REGISTER && input.type != TEST_PC && input.type != TEST_SUBROUTINE &&
            lc3_calculate(state, input.address, address_calc) == -1)
            throw "An address expression " + input.address + " was not formed correctly.";
        else if (input.type == TEST_REGISTER)
        {
            if (input.address.size() != 2)
                throw "Register format is RX where x is between 0-7 found: " + input.address;

            address_calc = input.address[1] - '0';
            if (address_calc > 7 || address_calc < 0)
                throw "Invalid register " + input.address;
        }
        else
            lc3_calculate(state, input.address, address_calc);

        effective_address = (unsigned short) address_calc;

        switch (input.type)
        {
            case TEST_VALUE:
                if (lc3_calculate(state, input.value, value_calc))
                    throw "<in test-value> A value expression " + input.value + " was malformed.";
                state.mem[effective_address] = (short) value_calc;
                break;
            case TEST_REGISTER:
                if (lc3_calculate(state, input.registerval, value_calc))
                    throw "<in test-register> A value expression " + input.registerval + " was malformed.";
                state.regs[effective_address] = (short) value_calc;
                break;
            case TEST_PC:
                if (lc3_calculate(state, input.pcval, value_calc))
                    throw "<in test-pc> A value expression " + input.pcval + " was malformed.";
                state.pc = (unsigned short) value_calc;
                break;
            case TEST_POINTER:
                if (lc3_calculate(state, input.pointer, value_calc))
                    throw "<in test-pointer> An expression was " + input.pointer + " malformed.";
                state.mem[(unsigned short) state.mem[effective_address]] = (short) value_calc;
                break;
            case TEST_STRING:
                for (unsigned int j = 0; j < input.text.size(); j++)
                    state.mem[state.mem[effective_address] + j] = input.text[j];
                state.mem[(unsigned short) (state.mem[effective_address] + input.text.size())] = 0;
                break;
            case TEST_ARRAY:
                for (unsigned int j = 0; j < input.array.size(); j++)
                {
                    if (lc3_calculate(state, input.array[j], value_calc))
                        throw "<in test-array> An expression was " + input.array[j] + " malformed.";
                    state.mem[(unsigned short) (state.mem[effective_address] + j)] = (short) value_calc;
                }
                break;
            case TEST_IO:
                newinput->str(input.io);
                break;
            case TEST_SUBROUTINE:
                break;
            default:
                throw "Unknown test type";
        }

        if (input.type == TEST_SUBROUTINE)
        {
            lc3_subr_input& subr = input.subroutine;
            int pc;
            int r7;
            int r6;
            int r5;
            if (lc3_calculate(state, subr.name, pc) == -1)
                throw "<in test-subr> invalid subroutine name given " + subr.name;
            if (lc3_calculate(state, subr.stack, r6) == -1)
                throw "<in test-subr> stack expression was malformed " + subr.stack;
            if (lc3_calculate(state, subr.r7, r7) == -1)
                throw "<in test-subr> r7 expression was malformed " + subr.r7;
            if (lc3_calculate(state, subr.r5, r5) == -1)
                throw "<in test-subr> r5 expression was malformed " + subr.r5;

            state.pc = (unsigned short) pc;
            state.regs[6] = (unsigned short)(r6 - subr.params.size());
            state.regs[7] = (unsigned short) r7;
            state.regs[5] = (unsigned short) r5;
            state.mem[state.regs[7]] = 0xF025;

            for (unsigned int j = 0; j < subr.params.size(); j++)
            {
                if (lc3_calculate(state, subr.params[j], value_calc))
                    throw "<in test-subr> param expression " + subr.params[j] + " was malformed.";
                state.mem[(unsigned short)state.regs[6] + j] = (short) value_calc;
            }

            // This fixes the issue in which a subroutine is being tested, but it makes calls to other subroutines who takes a
            // different number of parameters than the subroutine under test.
            // If the file uses subroutine annotations then overwrite it.
            for (const auto& info : subr.subroutines)
            {
                int address = 0;
                if (lc3_calculate(state, info.name, address) == -1)
                    throw "<in test-subr> invalid subroutine name given " + info.name;

                lc3_subroutine_info test_info = info;
                test_info.address = (unsigned short) address;
                state.subroutines[(unsigned short)address] = test_info;
            }
        }
    }

    state.input = newinput;
    // Setup output capture device
    std::stringstream* newoutput = new std::stringstream();
    state.output = newoutput;
    std::stringstream* newwarning = new std::stringstream();
    state.warning = newwarning;

    if (test.has_max_executions)
    {
        unsigned long i = 0;
        // Do this num times or until halted.
        while (i < test.max_executions && !state.halted)
        {
            //printf("%04x: %s (%x)\n", state.pc, lc3_disassemble(state, state.mem[state.pc]).c_str(), (unsigned short)state.mem[state.pc]);
                /*printf("R0 %6d|x%04x\tR1 %6d|x%04x\tR2 %6d|x%04x\tR3 %6d|x%04x\nR4 %6d|x%04x\tR5 %6d|x%04x\tR6 %6d|x%04x\tR7 %6d|x%04x\nCC: %s\tPC: %04x\n\n",
                       state.regs[0], (unsigned short)state.regs[0], state.regs[1], (unsigned short)state.regs[1], state.regs[2], (unsigned short)state.regs[2],
                       state.regs[3], (unsigned short)state.regs[3], state.regs[4], (unsigned short)state.regs[4], state.regs[5], (unsigned short)state.regs[5],
                       state.regs[6], (unsigned short)state.regs[6], state.regs[7], (unsigned short)state.regs[7], (state.n ? "N" : (state.z ? "Z" : "P")),
                       (unsigned short) state.pc);*/
            // Step one instruction
            lc3_step(state);
            // Increment instruction count
            i++;
        }
        //lc3_run(state, test.max_executions);
    }
    else
        lc3_run(state);

    // Fill in the output values
    test.has_halted = state.halted;
    test.executions = state.executions;
    test.warnings = state.warnings;
    test.warning = newwarning->str();

    bool test_passed = true;
    unsigned int test_points = 0;
    unsigned int test_max_points = 0;
    // Check test environment
    for (unsigned int i = 0; i < test.output.size(); i++)
    {
        std::stringstream expected;
        std::stringstream actual;

        lc3_test_output& output = test.output[i];
        int value_calc;
        short short_cmp;
        int address_calc = 0;
        unsigned short effective_address;
        std::string str;
        std::vector<short> arrayexpected;
        std::vector<short> arrayactual;

        if (output.type != TEST_IO && output.type != TEST_REGISTER && output.type != TEST_PC && lc3_calculate(state, output.address, address_calc) == -1)
            throw "An address expression " + output.address + " was not formed correctly.";
        else if (output.type == TEST_REGISTER)
        {
            address_calc = output.address[1] - '0';
            if (address_calc > 7)
                throw "Invalid register " + output.address;
        }
        effective_address = (unsigned short) address_calc;

        output.passed = true;
        output.earned = 0;
        switch (output.type)
        {
            case TEST_VALUE:
                if (lc3_calculate(state, output.value, value_calc))
                    throw "<in test-value> An expression " + output.value + " was malformed.";

                short_cmp = (short) value_calc;
                output.passed = lc3_test_check(output, &state.mem[effective_address], &short_cmp);

                actual << state.mem[effective_address];
                expected << short_cmp;
                break;
            case TEST_REGISTER:
                if (lc3_calculate(state, output.registerval, value_calc))
                    throw "<in test-register> An expression " + output.registerval + " was malformed.";

                short_cmp = (short) value_calc;
                output.passed = lc3_test_check(output, &state.regs[effective_address], &short_cmp);

                actual << state.regs[effective_address];
                expected << short_cmp;
                break;
            case TEST_PC:
                if (lc3_calculate(state, output.pcval, value_calc))
                    throw "<in test-pc> An expression " + output.pcval + " was malformed.";

                short_cmp = (short) value_calc;
                output.passed = lc3_test_check(output, &state.pc, &short_cmp);

                actual << state.pc;
                expected << short_cmp;

                break;
            case TEST_POINTER:
                if (lc3_calculate(state, output.pointer, value_calc))
                    throw "<in test-pointer> An expression " + output.pointer + " was malformed.";

                short_cmp = (short) value_calc;
                output.passed = lc3_test_check(output, &state.mem[(unsigned short)state.mem[effective_address]], &short_cmp);

                actual << state.mem[(unsigned short)state.mem[effective_address]];
                expected << short_cmp;

                break;
            case TEST_STRING:
                value_calc = (unsigned short)state.mem[effective_address];
                short_cmp = state.mem[(unsigned short)value_calc];

                while(short_cmp > 0 && short_cmp <= 255)
                {
                    actual.put((char) short_cmp);
                    value_calc++;
                    short_cmp = state.mem[(unsigned short)value_calc];
                }

                str = actual.str();
                output.passed = lc3_test_check(output, &str, &output.text);
                expected << output.text;
                break;
            case TEST_ARRAY:
                for (unsigned int j = 0; j < output.array.size(); j++)
                {
                    if (lc3_calculate(state, output.array[j], value_calc))
                        throw "<in test-array> An expression " + output.array[j] + " was malformed.";

                    arrayexpected.push_back(state.mem[(unsigned short)(state.mem[effective_address] + j)]);
                    arrayactual.push_back((short)value_calc);

                    actual << state.mem[(unsigned short)(state.mem[effective_address] + j)] << " ";
                    expected << (short)value_calc << " ";
                }

                output.passed = lc3_test_check(output, &arrayactual, &arrayexpected);
                break;
            case TEST_IO:
                str = newoutput->str();
                output.passed = lc3_test_check(output, &str, &output.io);
                actual << str;
                expected << output.io;
                break;
            case TEST_SUBROUTINE:
                break;
            default:
                throw "Unknown test type";
        }

        if (output.type != TEST_SUBROUTINE)
        {
            if (output.passed)
                output.earned = output.points;
        }

        if (output.type == TEST_SUBROUTINE)
        {
            std::stringstream extra;
            lc3_subr_output& subr = output.subroutine;

            std::vector<short> expected_stack;
            std::vector<short> actual_stack;

            std::vector<short> locals;
            std::vector<short> params;
            for (int j = (int)subr.locals.size() - 1; j >= 0; j--)
            {
                if (lc3_calculate(state, subr.locals[j], value_calc))
                    throw "<in test-subr> A local variable expression " + subr.locals[i] + "was malformed.";

                expected_stack.push_back((short)value_calc);
                locals.push_back((short) value_calc);
            }

            int r7;
            int r6;
            int r5;
            int answer;
            if (lc3_calculate(state, subr.stack, r6) == -1)
                throw "<in test-subr> stack expression " + subr.stack + " was malformed";
            if (lc3_calculate(state, subr.r7, r7) == -1)
                throw "<in test-subr> r7 expression " + subr.r7 + " was malformed";
            if (lc3_calculate(state, subr.r5, r5) == -1)
                throw "<in test-subr> r5 expression " + subr.r5 + " was malformed";
            if (lc3_calculate(state, subr.answer, answer) == -1)
                throw "<in test-subr> answer expression " + subr.answer + " was malformed";

            expected_stack.push_back((short)r5);
            expected_stack.push_back((short)r7);
            expected_stack.push_back((short)answer);

            for (unsigned int j = 0; j < subr.params.size(); j++)
            {
                if (lc3_calculate(state, subr.params[j], value_calc))
                    throw "<in test-subr> A param expression " + subr.params[j] + " was malformed.";
                expected_stack.push_back((short)value_calc);
                params.push_back((short) value_calc);
            }

            // Code to get the students stack frame
            unsigned short actual_r6 = (unsigned short) r6;
            if (state.first_level_calls.size() >= 1)
            {
                const auto& call_info = state.first_level_calls[0];
                unsigned short subr_location = call_info.address;
                if (state.subroutines.find(subr_location) == state.subroutines.end())
                {
                    extra << "      [WARNING] Could not determine number of parameters for subroutine " <<
                        lc3_sym_rev_lookup(state, subr_location) << " at address " <<
                        std::hex << "0x" << subr_location << "\n";
                }
                unsigned short start = call_info.r6 + call_info.params.size();
                // Screams...
                if (start >= actual_r6)
                    extra << "      [WARNING] Could not get students stack frame.\n"
                             "      Is the student managing the stack correctly?\n";
                else
                    actual_stack.assign(state.mem + start, state.mem + actual_r6);
            }
            else if (state.first_level_calls.empty())
            {
                int num_params = subr.params.size();
                // Get at least the parameters student could probably not save anything...
                actual_stack.assign(state.mem + (actual_r6 - num_params), state.mem + actual_r6);
                // Get additional addresses modified
                unsigned short start = actual_r6 - num_params - 1;
                while (state.memory_ops.find(start) != state.memory_ops.end())
                {
                    if (!state.memory_ops[start].writes) break;
                    actual_stack.insert(actual_stack.begin(), state.mem[start]);
                    start--;
                }
            }

            for (unsigned int j = 0; j < expected_stack.size(); j++)
                expected << std::hex << "0x" << expected_stack[j] << " ";
            expected << " r5: " << std::hex << "0x" << (short)r5 <<
                        " r6: " << std::hex << "0x" << (actual_r6 - subr.params.size() - 1) <<
                        " r7: " << std::hex << "0x" << (short)(r7 + 1);


            for (unsigned int j = 0; j < actual_stack.size(); j++)
                actual << std::hex << "0x" << actual_stack[j] << " ";
            actual << " r5: " << std::hex << "0x" << state.regs[5] <<
                      " r6: " << std::hex << "0x" << state.regs[6] <<
                      " r7: " << std::hex << "0x" << state.regs[7];

            // now that I have all information available time for some checks.
            std::map<short, int> actual_stack_map;
            for (unsigned int j = 0; j < actual_stack.size(); j++)
                actual_stack_map[actual_stack[j]] += 1;

            int points = 0;
            // If they get something wrong then it will also be wrong in edit distance
            // so cut some slack if you get a value wrong.
            int ed_forgiveness = 0;

            if (actual_stack_map[(short)answer] > 0)
            {
                actual_stack_map[(short)answer] -= 1;
                points += subr.points_answer;
                extra << CHECK << ANSWER_FOUND << " +" << subr.points_answer << ".\n";
            }
            else
            {
                ed_forgiveness++;
                extra << MISS << ANSWER_FOUND << " -" << subr.points_answer << ".\n";
            }

            if (state.regs[6] == (short)(actual_r6 - subr.params.size() - 1))
            {
                points += subr.points_r6;
                extra << CHECK << R6_FOUND << " +" << subr.points_r6 << ".\n";
            }
            else
            {
                extra << MISS << R6_FOUND << " -" << subr.points_r6 << ".\n";
            }

            if (actual_stack_map[(short)r7] > 0 && state.regs[7] == (short)(r7+1))
            {
                actual_stack_map[(short)r7] -= 1;
                points += subr.points_r7;
                extra << CHECK << R7_FOUND << " +" << subr.points_r7 << ".\n";
            }
            else
            {
                // Don't count if just r7 was clobbered
                if (actual_stack_map[(short)r7] <= 0)
                    ed_forgiveness++;
                extra << MISS << R7_FOUND << " -" << subr.points_r7 << ".\n";
            }

            if (actual_stack_map[(short)r5] > 0 && state.regs[5] == (short)r5)
            {
                actual_stack_map[(short)r5] -= 1;
                points += subr.points_r5;
                extra << CHECK << R5_FOUND << " +" << subr.points_r5 << ".\n";
            }
            else
            {
                if (actual_stack_map[(short)r5] <= 0)
                    ed_forgiveness++;
                extra << MISS << R5_FOUND << " -" << subr.points_r5 << ".\n";
            }

            for (unsigned int j = 0; j < params.size(); j++)
            {
                if (actual_stack_map[params[j]] > 0)
                {
                    actual_stack_map[params[j]] -= 1;
                    points += subr.points_params;
                    extra << CHECK << params[j] << " " << PARAM_FOUND << " +" << subr.points_params << ".\n";
                }
                else
                {
                    ed_forgiveness++;
                    extra << MISS << params[j] << " " << PARAM_FOUND << " -" << subr.points_params << ".\n";
                }
            }

            bool all_locals_wrong = true;
            for (unsigned int j = 0; j < locals.size(); j++)
            {
                if (actual_stack_map[locals[j]] > 0)
                {
                    actual_stack_map[locals[j]] -= 1;
                    points += subr.points_locals;
                    all_locals_wrong = false;
                    extra << CHECK << locals[j] << " " << LOCAL_FOUND << " +" << subr.points_locals << ".\n";
                }
                else
                {
                    ed_forgiveness++;
                    extra << MISS << locals[j] << " " << LOCAL_FOUND << " -" << subr.points_locals << ".\n";
                }
            }

            // Subroutine calls check.
            std::set<lc3_subroutine_call_info, lc3_subroutine_call_info_cmp> actual_calls(state.first_level_calls.begin(), state.first_level_calls.end());
            std::set<lc3_subroutine_call_info, lc3_subroutine_call_info_cmp> expected_calls;
            for (const auto& expected_call : subr.calls)
            {
                lc3_subroutine_call_info info;
                int addr = lc3_sym_lookup(state, expected_call.name);
                if (addr == -1)
                    throw "Invalid subroutine name given in <call> " + expected_call.name;
                for (unsigned int i = 0; i < expected_call.params.size(); i++)
                {
                    int param;
                    if (lc3_calculate(state, expected_call.params[i], param) == -1)
                        throw "<in test-subr/call> param expression " + expected_call.params[i] + " was malformed";
                    info.params.push_back(param);
                }
                info.address = addr;
                info.r6 = 0;
                expected_calls.insert(info);
            }

            for (const auto& call : expected_calls)
            {
                std::stringstream call_name;
                call_name << lc3_sym_rev_lookup(state, call.address) << "(";
                if (state.subroutines.find(call.address) == state.subroutines.end())
                    call_name << "?";
                for (unsigned int i = 0; i < call.params.size(); i++)
                {

                    call_name << std::hex << "0x" << call.params[i];
                    if (i != call.params.size() - 1)
                        call_name << ",";
                }
                call_name << ")";

                if (actual_calls.find(call) != actual_calls.end())
                {
                    extra << CHECK << "Call " << call_name.str() << " made correctly.\n";
                    points += subr.points_calls;
                }
                else
                {
                    extra << MISS << "Call " << call_name.str() << " was not made.\n";
                }
            }
            for (const auto& call : actual_calls)
            {
                std::stringstream call_name;

                std::string name = lc3_sym_rev_lookup(state, call.address);
                if (name.empty())
                    call_name << "0x" << std::hex << call.address << "(";
                else
                    call_name << name << "(";
                if (state.subroutines.find(call.address) == state.subroutines.end())
                    call_name << "?";
                for (unsigned int i = 0; i < call.params.size(); i++)
                {

                    call_name << std::hex << "0x" << call.params[i];
                    if (i != call.params.size() - 1)
                        call_name << ",";
                }
                call_name << ")";

                if (expected_calls.find(call) == expected_calls.end())
                {
                    extra << MISS << "Unexpected Call " << call_name.str() << " made.\n";
                }
            }

            // Read answer check sigh...
            if (subr.points_read_answer > 0)
            {
                for (const auto& call : actual_calls)
                {
                    if (expected_calls.find(call) != expected_calls.end() && !call.params.empty())
                    {
                        if (state.memory_ops[call.r6 - 1].reads > 0)
                        {
                            extra << CHECK << "Read answer from stack.\n";
                            points += subr.points_read_answer;
                        }
                        else
                            extra << MISS << "Did not read answer from stack.\n";
                    }
                }
            }

            // If all local variables are wrong then it can be argued that they was saving registers
            // And forgot to save locals...
            if (all_locals_wrong)
            {
                // Bro do you even calling convention
                if (actual_stack.size() > subr.params.size() + 3)
                    // Truncate stack to last num_params + 3 elements the stuff we care about (don't do expected since ed forgiveness handles it).
                    actual_stack.erase(actual_stack.begin(), actual_stack.begin() + (actual_stack.size() - subr.params.size() - 3));
                if (!subr.locals.empty())
                    extra << "      All locals were not found, so locals aren't included in structure check.\n";
            }
            else
            {
                if (actual_stack.size() > expected_stack.size())
                    expected_stack.insert(expected_stack.begin(), actual_stack.begin(), actual_stack.begin() + (actual_stack.size() - expected_stack.size()));
            }

            int ed_grade = edit_distance(actual_stack, expected_stack);
            points -= (ed_grade - ed_forgiveness) * subr.deductions_edist;
            int mistakes = ed_grade - ed_forgiveness;


            if (mistakes == 0)
                extra << "      Found no structural mistakes in the stack.  No changes needed.\n";
            else
                extra << "      Found " << mistakes << " structural mistakes in stack -" << subr.deductions_edist * mistakes << ".\n";

            output.passed = (ed_grade == 0) && (ed_forgiveness == 0);
            output.extra_output += extra.str();
            output.earned = points;
        }

        test_passed = test_passed && output.passed;
        test_points += output.earned;
        test_max_points += output.points;

        output.expected = expected.str();
        output.actual = actual.str();
    }

    // Tear down
    delete state.input;
    delete state.output;
    delete state.warning;

    test.passed = test_passed;
    test.points = test_points;
    test.max_points = test_max_points;
}