Modificações no simulador do Simpletron. No Exercício 7.28, você escreveu uma simulação de software de um computador que executa programas escritos na Simpletron Machine Language (SML). Nesse exercício, propomos várias modificações e melhorias no Simulador do Simpletron. Nos exercícios 12.26 e 12.27, propomos criar um compilador que converte programas escritos em uma linguagem de

programação de alto nível (uma variação do BASIC) para

a linguagem de máquina do Simpletron. Algumas das modificações

e melhorias a seguir podem ser necessárias para

executar os programas produzidos pelo compilador.

a) Estenda a memória do Simulador do Simpletron

para que ele contenha 1000 locais de memória, permitindo

que ele trate de programas maiores.

b) Permita que o simulador realize cálculos de módulo.

Isso exige uma instrução adicional na linguagem

de máquina do Simpletron.

c) Permita que o simulador realize cálculos de exponenciação.

Isso exige uma instrução adicional na

linguagem de máquina do Simpletron.

d) Modifique o simulador para que seja possível usar

valores hexadecimais em vez de valores inteiros para

representar as instruções na linguagem de máquina

do Simpletron.

e) Modifique o simulador para permitir a saída de uma

nova linha. Isso exige uma instrução adicional na

linguagem de máquina do Simpletron.

f) Modifique o simulador para que ele possa processar

valores de ponto flutuante, além de valores inteiros.

g) Modifique o simulador fazendo com que seja possível

lidar com a input de string. [Dica: cada palavra do

Simpletron pode ser dividida em dois grupos, cada um

mantendo um inteiro de dois dígitos. Cada inteiro de

dois dígitos representa o equivalente decimal ASCII de

um caractere. Inclua uma instrução em linguagem de

máquina que entrará com uma string, e armazenará

essa string, a partir de um local específico da memória

do Simpletron. A primeira metade da palavra nesse

local será um contador do número de caracteres na

string (ou seja, o comprimento da string). Cada meia

palavra seguinte conterá um caractere ASCII expresso

como dois dígitos decimais. A instrução em linguagem

de máquina converterá cada caractere em seu equivalente

ASCII e o atribuirá a uma meia palavra.]

h) Modifique o simulador para que seja possível lidar

com a saída de strings armazenadas no formato da

parte (g). [Dica: inclua uma instrução em linguagem

de máquina que imprima uma string que começa

em um local específico da memória do Simpletron.

A primeira metade da palavra nesse local

é o comprimento da string em caracteres. Cada

meia palavra seguinte contém um caractere ASCII

expresso como dois dígitos decimais. A instrução

em linguagem de máquina verifica o comprimento

e imprime a string, traduzindo cada número de dois

dígitos em seu caractere equivalente.]

Passo 1

No exercício anterior, escrevemos um programa que simula o computador Simpletron. Ele usa Simpletron Machine Language, que consiste em palavras. Cada palavra é um inteiro de quatro dígitos que representa uma operação e um operando.

Agora modificaremos esse programa seguindo as instruções fornecidas neste exercício.

Primeiro, vamos tornar o array de memória maior. Agora será capaz de armazenar até 10001000 palavras.

Também devemos alterar algumas condições nas funções load(), execute() e dump(). Onde quer que tenhamos usado o número 100100, devemos substituí-lo por 10001000.

  1. // na função main()

    int memory[1000] = { 0 };

    Na função load(), mudaremos a condição do loop do-while externo, para que o loop seja iterado desde que a instrução não seja -99999−99999 e a contagem seja menor que 10001000 (costumava ser 100100).

    void load(int* memory)

    {

    int count = 0;

    int instruction;

    do {

    do {

    printf("%02d ? ", count);

    scanf("%d", &instruction);

    if (instruction != -99999 &&

    (instruction 9999))

    printf("*** Invalid instruction. "\

    "Please repeat. ***\n");

    } while (instruction != -99999 &&

    (instruction 9999));

    if (instruction != -99999)

    memory[count++] = instruction;

    } while (instruction != -99999 && count

    }

    Na função dump(), devemos aumentar o número de iterações do loop for usado para exibir o conteúdo da memória para 10001000.

    void dump(int* memory, int accumulator, int instructionCounter,

    int instructionRegister, int operationCode, int operand)

    {

    printf("\nREGISTERS:\n");

    printf("%-25s%+05d\n", "accumulator", accumulator);

    printf("%-28s%02d\n", "instructionCounter", instructionCounter);

    printf("%-25s%+05d\n", "instructionRegister", instructionRegister);

    printf("%-28s%02d\n", "operationCode", operationCode);

    printf("%-28s%02d\n", "operand", operand);

    printf("\nMEMORY:\n ");

    for (int i = 0; i

    printf(" %5d", i);

    }

    printf("\n");

    for (int i = 0; i

    if (i % 10 == 0)

    printf("%3d", i);

    printf(" %+05d", memory[i]);

    if (i % 10 == 9)

    printf("\n");

    }

    printf("\n");

    }

    Passo 2

    Como nas duas funções anteriores, também devemos editar as iterações de loops na função execute().

    Na última parte da função, devemos substituir o 100100 por 10001000 nas condições da instrução if e do loop principal do-while, ao verificar qual é o valor para o qual a instruçãoCounter aponta.

    void execute(int* memory, int* accumulator, int* instructionCounter,

    int* operationCode, int* operand, int* instructionRegister)

    {

    do {

    *instructionRegister = memory[*instructionCounter];

    *operationCode = *instructionRegister / 100;

    *operand = *instructionRegister % 100;

    switch (*operationCode) {

    case READ:

    do {

    printf("? ");

    scanf("%d", &memory[*operand]);

    if (memory[*operand]

    memory[*operand] > 9999) {

    printf("*** Invalid value ***\n");

    }

    } while (memory[*operand]

    memory[*operand] > 9999);

    break;

    case WRITE:

    printf("%d\n", memory[*operand]);

    break;

    case LOAD:

    *accumulator = memory[*operand];

    break;

    case STORE:

    memory[*operand] =* accumulator;

    break;

    case ADD:

    *accumulator += memory[*operand];

    if (*accumulator > 9999) {

    printf("\n*** Accumulator overflow ***\n");

    printf("*** Simpletron execution abnormally"\

    " terminated ***\n");

    dump(memory, *accumulator, *instructionCounter,

    *instructionRegister, *operationCode,

    *operand);

    exit(1);

    }

    break;

    case SUBTRACT:

    *accumulator -= memory[*operand];

    if (*accumulator

    printf("\n*** Accumulator overflow ***\n");

    printf("*** Simpletron execution abnormally"\

    " terminated ***\n");

    dump(memory, *accumulator, *instructionCounter,

    *instructionRegister, *operationCode,

    *operand);

    exit(1);

    }

    break;

    case DIVIDE:

    if (memory[*operand] == 0) {

    printf("\n*** Attempt to divide by zero ***\n");

    printf("*** Simpletron execution abnormally"\

    " terminated ***\n");

    dump(memory, *accumulator, *instructionCounter,

    *instructionRegister, *operationCode,

    *operand);

    exit(1);

    }

    else {

    *accumulator /= memory[*operand];

    }

    if (*accumulator > 9999 || *accumulator

    printf("\n*** Accumulator overflow ***\n");

    printf("*** Simpletron execution abnormally"\

    " terminated ***\n");

    dump(memory, *accumulator, *instructionCounter,

    *instructionRegister, *operationCode,

    *operand);

    exit(1);

    }

    break;

    case MULTIPLY:

    *accumulator *= memory[*operand];

    if (*accumulator > 9999 || *accumulator

    printf("\n*** Accumulator overflow ***\n");

    printf("*** Simpletron execution abnormally"\

    " terminated ***\n");

    dump(memory, *accumulator, *instructionCounter,

    *instructionRegister, *operationCode,

    *operand);

    exit(1);

    }

    break;

    case BRANCH:

    *instructionCounter = *operand;

    break;

    case BRANCHNEG:

    if (*accumulator

    *instructionCounter = *operand;

    }

    else

    (*instructionCounter)++;

    break;

    case BRANCHZERO:

    if (*accumulator == 0) {

    *instructionCounter = *operand;

    }

    else

    (*instructionCounter)++;

    break;

    case HALT:

    printf("\n*** Simpletron execution terminated ***\n");

    dump(memory, *accumulator, *instructionCounter,

    *instructionRegister, *operationCode, *operand);

    break;

    default:

    printf("\n*** Invalid opration code! ***\n");

    printf("*** Simpletron execution abnormally"\

    " terminated ***\n");

    dump(memory, *accumulator, *instructionCounter,

    *instructionRegister, *operationCode,

    *operand);

    exit(1);

    break;

    }

    if (*operationCode != BRANCH && *operationCode != BRANCHNEG

    && *operationCode != BRANCHZERO) {

    (*instructionCounter)++;

    }

    if (*instructionCounter >= 1000) {

    printf("\n*** Insufficient memory ***\n");

    printf("*** Simpletron execution abnormally"\

    " terminated ***\n");

    dump(memory, *accumulator, *instructionCounter,

    *instructionRegister, *operationCode,

    *operand);

    exit(1);

    }

    } while (*operationCode != HALT

    && *instructionCounter

    }

  2. Agora, vamos criar uma nova operação, o que significa que precisamos de uma instrução SML adicional.
  3. Vamos definir a macro primeiro.

    #define REMAINDER 34

    Agora, devemos escrever um caso na instrução switch localizada na função execute() que processará esta operação.

    Ele simplesmente usará o operador % para obter o restante.

    case REMAINDER:

    *accumulator %= memory[*operand];

    break;

    Passo 3

  4. Primeiro, definiremos uma nova macro que representará o código de operação da linguagem SML para cálculos exponenciais.
  5. #define EXPONENTIAL 35

    Agora, escreveremos um caso que processará esta operação. A função pow() será usada para isso.

    Garantiremos que o resultado seja um valor válido que possa ser armazenado na memória do Simpletron.

    case EXPONENTIAL:

    *accumulator = pow(*accumulator, memory[*operand]);

    if (*accumulator > 9999 || accumulator

    printf("\n*** Accumulator overflow ***\n");

    printf("*** Simpletron execution abnormally"\

    " terminated ***\n");

    dump(memory, *accumulator, *instructionCounter,

    *instructionRegister, *operationCode,

    *operand);

    exit(1);

    }

    break;

  6. Se quisermos que o programa represente as instruções como valores hexadecimais, simplesmente devemos substituir o especificador de conversão d pelo especificador de conversão x.
  7. Isso é feito na função load().

    void load(int* memory)

    {

    int count = 0;

    int instruction;

    do {

    do {

    printf("%02d ? ", count);

    scanf("%x", &instruction);

    if (instruction != -99999 &&

    (instruction 9999))

    printf("*** Invalid instruction."\

    " Please repeat. ***\n");

    } while (instruction != -99999 &&

    (instruction 9999));

    if (instruction != -99999)

    memory[count++] = instruction;

    } while (instruction != -99999 && count

    }

  8. Definiremos outra macro que representará o código de operação para exibir uma nova linha.
  9. #define NEWLINE 12

    O caso para esta nova operação usará uma instrução printf() para exibir um caractere de nova linha '\n'.

    case NEWLINE:

    printf("\n");

    break;

  10. Para que o programa possa trabalhar com valores de ponto flutuante, devemos alterar o array de memória e a variável e o ponteiro do acumulador para o tipo de dados double ou float.
  11. Novos protótipos de funções:

    void load(double* memory);

    void execute(double* memory, double* accumulator, int* instructionCounter,

    int* operationCode, int* operand, int* instructionRegister);

    void dump(double* memory, double accumulator, int instructionCounter,

    int instructionRegister, int operationCode, int operand);

    Na função main(), vamos alterar o tipo de dados para o array de memória e alguns registradores.

    int main()

    {

    printf("*** Welcome to Simpletron! ***\n"\

    "*** Please enter your program one instruction ***\n"\

    "*** (or data word) at a time. I will type the ***\n"\

    "*** location number and a question mark (?). ***\n"\

    "*** You then type the word for that location. ***\n"\

    "*** Type the sentinel -9999 to stop entering ***\n"\

    "*** your program. ***\n\n");

    double memory[1000] = { 0 };

    load(memory);

    printf("^^^ Program loading completed ^^^\n"\

    "*** Program execution begins ***\n\n");

    double accumulator = 0;

    int instructionCounter = 0;

    int operationCode = 0;

    int operand = 0;

    int instructionRegister = 0;

    execute(memory, &accumulator, &instructionCounter, &operationCode,

    &operand, &instructionRegister);

    }

    Na função execute(), devemos alterar o caso READ na instrução switch, para que o programa leia um valor duplo em vez de um inteiro. Substituiremos o especificador de conversão d pelo especificador de conversão lf.

    Isso também será feito para o caso WRITE.

    Aqui são mostradas as únicas alterações na função, pois é fácil localizá-las.

    // the execute() function

    switch (*operationCode) {

    case READ:

    do {

    printf("? ");

    scanf("%lf", &memory[*operand]);

    if (memory[*operand]

    || memory[*operand] > 9999) {

    printf("*** Invalid value ***\n");

    }

    } while (memory[*operand]

    || memory[*operand] > 9999);

    break;

    case WRITE:

    printf("%lf\n", memory[*operand]);

    break;

    Também devemos alterar a função dump() para exibir todos os valores duplos como valores de ponto flutuante. A formatação deve mudar um pouco e o especificador de conversão lf deve ser usado em vez do especificador de conversão d em alguns lugares.

    void dump(double* memory, double accumulator, int instructionCounter,

    int instructionRegister, int operationCode, int operand)

    {

    printf("\nREGISTERS:\n");

    printf("%-24s%+08.2lf\n", "accumulator", accumulator);

    printf("%-30s%02d\n", "instructionCounter", instructionCounter);

    printf("%-27s%+05d\n", "instructionRegister", instructionRegister);

    printf("%-30s%02d\n", "operationCode", operationCode);

    printf("%-30s%02d\n", "operand", operand);

    printf("\nMEMORY:\n ");

    for (int i = 0; i

    printf(" %8d", i);

    }

    printf("\n");

    for (int i = 0; i

    if (i % 10 == 0)

    printf("%3d", i);

    printf(" %+08.2lf", memory[i]);

    if (i % 10 == 9)

    printf("\n");

    }

    printf("\n");

    }

    Passo 4

  12. Primeiro, vamos definir uma nova macro para a operação de leitura de uma string.
  13. #define READSTR 13

    Agora, escreveremos o caso que será executado para ler uma string e armazená-la no array de memória.

    Primeiro, garantiremos que a string possa ser armazenada no local fornecido na instrução. Se puder, usaremos um loop for para armazenar a string, caractere por caractere. O comprimento da string será armazenado nos dois primeiros dígitos da palavra. O caractere nulo final '\0' também será armazenado.

    case READSTR:

    printf("? ");

    char str[30];

    scanf("%s", str);

    if (*operand + strlen(str) > 1000) {

    printf("\n*** Memory access violation ***\n");

    printf("*** Simpletron execution abnormally"\

    " terminated ***\n");

    dump(memory, *accumulator, *instructionCounter,

    *instructionRegister, *operationCode,

    *operand);

    exit(1);

    }

    else {

    int ch;

    for (int i = 0; i

    ch = strlen(str) * 100 + str[i];

    memory[*operand + i] = ch;

    }

    }

    break;

    Aviso: Esta função funcionará apenas para os caracteres maiúsculos, pois alguns caracteres minúsculos são representados com números de três dígitos. Podemos usar os operadores % e + para converter esses valores em valores de um ou dois dígitos para usá-los.

  14. Primeiro, vamos definir uma macro para a operação de exibição de uma string.

#define WRITESTR 14

A instrução case para este código de operação é mostrada abaixo.

Ele lê o comprimento da string de cada palavra e usa um loop for para percorrer a string e exibir os caracteres. Tudo, exceto o caractere nulo final, é exibido.

Se um caractere nulo de terminação for alcançado durante uma iteração, o loop será interrompido.

case WRITESTR:

for (int i = 0; i

&& memory[*operand + i] != '\0'; i++) {

printf("%c", (int)memory[*operand + i] % 100);

}

break;

Resposta

Ver desenvolvimento meu parça.