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.
-
// 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
}
- Agora, vamos criar uma nova operação, o que significa que precisamos de uma instrução SML adicional.
- Primeiro, definiremos uma nova macro que representará o código de operação da linguagem SML para cálculos exponenciais.
- 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.
- Definiremos outra macro que representará o código de operação para exibir uma nova linha.
- 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.
- Primeiro, vamos definir uma nova macro para a operação de leitura de uma string.
- Primeiro, vamos definir uma macro para a operação de exibição de uma string.
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
#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;
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
}
#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;
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
#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.
#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.