Trabalho de Programação 1998/1999


Notas

  1. A resolver em grupo.
  2. Leia atentamente todo o enunciado.
  3. A resolução deste problema deve ser entregue até sexta-feira, 22 de Janeiro de 1999.
  4. A resolução deve ser entregue pessoalmente a um dos docentes da cadeira e consta de uma disquete, devidamente identificada, contendo apenas os ficheiros onde se encontra o código C++ (extensão .cpp) e o código assembly desenvolvido (extensão .asm).
  5. A entrega do problema fora do prazo implica a penalização de um valor por cada dia útil de atraso (i.e., 23, 24 ou 25 de Janeiro, -1 valor; 26 de Janeiro -2 valores; 27 de Janeiro -3 valores; 28 de Janeiro -4 valores) não se aceitando trabalhos após quinta-feira, 28 de Janeiro de 1999.

1  Objectivo

O objectivo deste trabalho é desenvolver em C++ um simulador de um computador com um pequeno conjunto de instruções e, usando um simples assemblador (assembler) que será fornecido brevemente, desenvolver um programa em assembly para esse mesmo computador.  O computador que será simulado não corresponde a nenhuma implementação real, muito embora fosse simples de concretizar em hardware.  A esse computador chamar-se-á XIM.

2  Descrição do computador XIM

O computador XIM é constituído por um processador, contendo um pequeno número de registos (16 registos, de nomes A a P, e ainda um registo especial PC), por uma memória (com 216 posições), e por dispositivos de entrada e de saída.  Os registos podem ser encarados como posições de memória especiais sobre as quais é possível efectuar operações aritméticas, não sendo possível efectuar quaisquer operações aritméticas directamente sobre as posições de memória.  As únicas operações que é possível efectuar sobre posições de memória são as de transferência de conteúdos entre um registo e uma posição de memória e vice-versa.  A memória pode ser encarada como um vector de palavras, sendo cada palavra constituida por 32 bits, representados aqui por b31 a b0.  Os registos têm exactamente a mesma constituição (com excepção do registo PC, que tem apenas 16 bits).  Esses padrões de bits tanto podem representar valores inteiros (em complemento para 2, se interpretado como um inteiro com sinal, sendo b31 o bit mais significativo) como instruções a serem executadas pelo processador.  Assim, a memória contém simultaneamente as instruções que constituem os programas a executar bem como, possivelmente, dados necessários à execução do programa (i.e., o XIM tem uma arquitectura de Von Neumann).  É muito importante perceber que, dada uma posição de memória arbitrária, o seu conteúdo é simplesmente um padrão de bits, que pode ser interpretado de formas diferentes, nomeadamente como um número inteiro ou como uma instrução para o processador.

2.1  A memória

A memória é constituída por 216 palavras de 32 bits.  Cada posição (palavra) tem um endereço.  Os endereços válidos são todos os inteiros entre 0 e 216 - 1.  Isto é:
Memória

 



 

Endereço
0
1
2
...
216 - 2
216 - 1
Palavras
p0
p1
p2
...
p216-2
p216-1

Como é óbvio, são necessários exactamente 16 bits para endereçar todas as posições válidas de memória.  Um endereço pode assim consistir num padrão de 16 bits interpretados como a representação binária (na base 2) de um número inteiro sem sinal (o endereço).

Cada posição de memória corresponde a uma palavra de 32 bits.  Isto é:
 

b31
b30
b29
 ...
b2
b1
b0

Em que cada bit pode valer 0 ou 1 (bit = binary digit).  Estes bits formam padrões que podem ser interpretados de formas diferentes.  Se forem tomados como instruções para o processador, são interpretados como se indica em 2.4, se forem tomados como inteiros sem sinal, são interpretados como uma representação em numeração binária, etc.

2.2  Os registos

Existem 16 registos básicos que consistem em palavras de 32 bits sobre as quais se podem efectuar várias operações descritas mais à frente.  Estes registos chamam-se A, B, C, D, E, F, G, H, I, J, K, L, M, N, O e P.  Também se podem referir os registos usando números inteiros, sendo o registo A correspondente ao número 0, o B ao número 1, e assim sucessivamente até ao registo P, com o número 15.  Os registos I (8) e O (14) são especiais no sentido em que estão associados a operações de leitura e escrita nos dispositivos externos.  O registo A (0) é também especial por ser apenas nele que se efectuam as operações de soma e subtração.

Finalmente, existe o registo PC (de program counter), com 16 bits apenas, que indica qual o endereço da posição de memória que contém a próxima instrução a ser executada.

2.3  Entradas e saídas

Assume-se que existe uma fonte inesgotável de padrões de 32 bits (a entrada) e um escoadouro, também com capacidade de escoamento infinita, dos mesmos padrões (a saída).  Pode-se imaginar quer a entrada quer a saída como duas fitas infinitas de papel: a da entrada coberta de padrões de bits e a da saída vazia, mas onde se podem escrever tantos quantos se desejar.  No simulador estas entradas e saídas serão associadas ao teclado e ao ecrã respectivamente, como se verá mais à frente.

2.4  O processador

O processador tem um papel muito simples: executar cegamente as instruções que encontrar em memória.  Que instruções existem e como são representadas em memória é o assunto desta secção.

2.4.1  As instruções

As instruções no XIM são compostas por uma operação e, possivelmente, pelos respectivos operandos.  As operações descrevem-se em seguida (em termos do seu efeito no estado do computador e das entradas e saídas).  No caso das operações com argumentos, estes apresentam-se em itálico, significando r um registo básico (de A a P) e e um endereço de memória (de 0 a 216 - 1). É de notar que todas as operações, com excepção das que envolvem saltos (jump, jz e jn), têm o efeito adicional de incrementarem o PC, de modo a que a próxima instrução a executar seja a subsequente na memória.

Os padrões de bits, no caso das operações aritméticas, são interpretados como inteiros com ou sem sinal em representação binária, é irrelevante (é uma característica da representação em complemento para dois que o padrão de bits resultante é exactamente o mesmo!).

São de notar aqui as operações que manipulam o PC, pois permitem fazer "saltos", i.e., podem ser usadas para implementar ciclos, e as instruções de acesso indexado à memória, pois permitem tratá-la (ou parte dela) como se de uma matriz (array) se tratasse.

Finalmente, note-se que, no caso dos acessos indexados à memória (loadi e storei) se usa uma palavra de 32 bits para endereçar a memória, quando apenas são necessários 16.  O que acontece nesse caso é que apenas são usados os 16 bits menos significativos do registo que contém endereço (o índice).

2.4.2  O código máquina

As instruções apresentadas são representadas como padrões de 32 bits.  Cada palavra de 32 bits é dividida (na sua interpretação como uma instrução), nas seguintes partes:

 
 
operação
r1
r2
(não usada)
e
b31
b30
b29
b28
b27
b26
b25
b24
b23
b22
b21
b20
b19
b18
b17
b16
b15
b14
b13
b12
b11
b10
b9
b8
b7
b6
b5
b4
b3
b2
b1
b0

Em que r1, r2 e e têm o mesmo significado que na descrição das operações feita anteriormente.  É de notar que algumas operações não fazem uso de algumas partes da palavra.  Por exemplo, a operação noop não faz uso de nenhum dos possíveis operandos, e portanto só mesmo os 4 bits mais significativos da palavra são relevantes (isto significa que existem 228 versões da instrução que não faz nada!).

A operação é, assim, representada pelos bits b31, b30, b29 e b28.  Se esses quatro bits forem interpretados como um valor inteiro temos a seguinte correspondência:
 

padrão de bits
(b31, b30, b29, b28)
inteiro correspondente 
(b31b30b29b28)2
operação representada
0000
0
noop
0001
1
load
0010
2
store
0011
3
loadi
0100
4
 storei
0101
5
 add
0110
6
 sub
0111
7
 zero
1000
8
 incr
1001
9
 decr
1010
10
 neg
1011
11
 jz
1100
12
 jn
1101
13
 jump
1110
14
 read
1111
15
 write

Quanto aos registos, exactamente o mesmo tipo de representação é usada, correspondendo o padrão 0000 (valor 0) ao registo A, o padrão 0001 (valor 1) ao registo B, ..., e o padrão 1111 (valor 15) ao registo P.

Os 16 bits do endereço e são interpretados como um valor inteiro, sem sinal, correspondente a uma posição de memória.

Exemplo

Qual a instrução correspondente ao número inteiro 268436567?  Convertendo para representação binária obtém-se (0001 0000 0000 0000 0000 0100 0101 0111)2, ou seja, a operação é a correspondente ao padrão 0001, que, consultando a tabela acima, corresponde à operação load.  Esta operação utiliza apenas o operando r1, que neste caso é o registo A (padrão 0000), e o operando e, com valor (0000 0100 0101 0111)2 = 1111.  Assim, a instrução representada é load A 1111.

A que número inteiro corresponde a instrução read?  A operação read corresponde ao padrão 1110.  Mas esta operação não utiliza quaisquer operandos, pelo que os restantes 28 bits são arbitrários.  Colocando-os a zero, obtém-se uma das 228 possíveis representações da instrução read: 1110 0000 0000 0000 0000 0000 0000 0000, que corresponde, na notação complemento para dois (e com 32 bits), ao inteiro -536870912.

2.5  A execução

Arranque

Quando o computador arranca assume-se que o registo PC contém o endereço 0, i.e., a primeira instrução a executar encontra-se na primeira posição de memória, e que a memória foi "automagicamente" carregada com um programa a executar (a parte da memória que não contenha o programa assume-se inicializada com 0).  Numa implementação prática a memória deverá ser carregada antes do arranque da simulação do computador virtual.

Execução

A execução processa-se instrução a instrução.  Em cada passo o padrão de bits guardado na posição de memória endereçada pelo conteúdo do registo PC (interpretado como um inteiro sem sinal) é executada, de acordo com o que se indicou acima.  Duma instrução normalmente resulta normalmente uma alteração no estado do computador (alteração da memória ou de registos ou das entradas ou saídas).  Essa alteração corresponde, salvo em casos patológicos (quais?) a uma alteração do conteúdo do registo PC, pelo que a instrução a ser executada no próximo passo é, normalmente, diferente (salvo no caso de saltos é a instrução subsequente na memória).

Paragem

Arbitra-se que o computador pára a sua execução logo que PC contiver o endereço 216 - 1 (ou seja, o endereço da última posição de memória).  Este método da paragem não tem paralelo em máquinas reais, mas para uma máquina virtual, tem a vantagem de ser simples e eficiente.

2.6  Um exemplo

Suponha que o seguinte programa (cada instrução é representada por um inteiro de 32 bits com sinal) foi carregado no ínicio da memória do XIM:
-536870912
671088657
268435473
-536870912
671088657
285212689
1912602624
-1073741813
1627389952
-2113929216
-805306361
1358954496
-1845493760
570425361
503316497
-268435456
-805240833
Este programa, traduzido para um formato mais compreensível, é:
read
store I 17
load A 17
read
store I 17
load B 17
zero C
jn A 11
sub B
incr C
jump 7
add B
decr C
store C 17
load O 17
write
jump 65535
Que acontece durante a execução do programa?  Nas primeiras linhas (até ao load B 17 inclusivé) lêem-se simplesmente dois valores que se guardam nos registos A e B.  Em seguida, usando o registo C, calcula-se a divisão do primeiro valor lido pelo segundo.  É capaz de perceber como?  Se parece confuso, veja mais abaixo o mesmo programa em assembly e comentado.

3  A linguagem assembly

Para utilização neste trabalho, foi desenvolvida uma linguagem assembly muito simples, mas que permite escrever programas para o computador XIM sem preocupações quanto à representação em padrão de bits de cada instrução.  A breve trecho será distribuida um assemblador que, dado um programa escrito na linguagem assembly definida abaixo, gera o conjunto de instruções na sua representação inteira em complemento para dois (com 32 bits).

3.1  Mnemónicas

As instruções podem ser escritas na forma
operação lista_de_operandos
tal como se sugeriu já atrás.  Por exemplo, incr G significa "incremente-se o valor contido no registo G".  É de notar que o assemblador distingue maiúsculas de minúsculas: as operações devem ser sempre escritas em minúsculas e os registos em maiúsculas.  Na linguagem assembly desenvolvida cada linha contém ou uma instrução (com o formato anterior), ou um valor inteiro com sinal (representação complemento para 2 em 32 bits), ou outras construções a ver abaixo.  O assemblador encarrega-se de converter instruções e valores inteiros para a sua representação em padrão de 32 bits e, posteriormente, de converter cada posição do programa no inteiro com sinal correspondente.

3.2  Etiquetas

Para que não se tenham de definir explicitamente os endereços, é possível usar etiquetas.  Assim, linhas começadas por :indentificador são interpretadas como significando "crie-se a etiqueta chamada identificador com o valor correspondente ao endereço da instrução subsequente".  Estas etiquetas podem depois ser usadas como qualquer endereço.  Por exemplo, poder-se-ia escrever
        read
        store I aux
        load A aux
        read
        store I aux
        load B aux
        zero C
:inicio_do_ciclo
        jn A fim_do_ciclo
        sub B
        incr C
        jump inicio_do_ciclo
:fim_do_ciclo
        add B
        decr C
        store C aux
        load O aux
        write
        jump end
:aux
em vez da versão original, mais dificil de interpretar, e onde o programador teve de se preocupar com escolher uma posição de memória fora da zona do programa para fazer a transferência dos valores (e teria de alterar manualmente esse valor sempre que o comprimento do programa se alterasse!).  Note-se que existe uma etiqueta especial end, pré-definida, e que tem como valor 216 - 1 = 65535, podendo por isso ser usada para parar a execução dum programa.

3.2.1  Reservando memória

É possível "reservar memória" usando uma construção da linguagem assembly que permite repetir instruções ou valores.  Por exemplo, as instruções assembly
:espaco
*100 0
 ...
atribuiriam à etiqueta espaco o endereço duma zona de memória com 100 palavras inicializadas com zero

3.2.2  Constantes

É possível ainda atribuir valores explícitos às etiquetas.  Por exemplo, as instruções assembly seguintes
=fim end
=bits 32
criam duas etiquetas fim e bits, a primeira com o mesmo valor que fim e a segunda com o valor 32.

3.3  Comentários

Consideram-se comentários todos os caracteres desde ; até ao final da linha respectiva.

3.4  Exemplo

O exemplo apresentado anteriormente pode ser expresso duma forma mais clara usando os mecanismos proprorcionados pela linguagem assembly apresentada:
        read                    ; cin >> I;
        store I aux             ; mem[aux] = I;
        load A aux              ; A = mem[aux];
        read                    ; cin >> I;
        store I aux             ; mem[aux] = I;
        load B aux              ; B = mem[aux];
                                ; // PC: A >= 0 && B > 0 && A = a
        zero C                  ; C = 0;
:inicio_do_ciclo                ; // CI: a = C * B + A && A >= -B
        jn A fim_do_ciclo       ; while(A >= 0) {
        sub B                   ;     A -= B;
        incr C                  ;     C++;
        jump inicio_do_ciclo    ; }
:fim_do_ciclo                   ; // CI && ~G: a = C * B + A && -B <= A < 0
                                ; // ou seja
        add B                   ; A += B;
        decr C                  ; C--;
                                ; // CO: a = C * B + A && 0 <= A < B
                                ; // ou seja, A contém o resto da divisão
                                ; // de a (valor inicial de A) por B, estando
                                ; // em C o quociente.
        store C aux             ; mem[aux] = C;
        load O aux              ; O = mem[aux];
        write                   ; cout << O;
        jump end                ; // o fim enfim!
:aux
Este programa em assembly, depois de traduzido para linguagem máquina e apresentado na forma de insteiros com sinal resulta em:
-536870912
671088657
268435473
-536870912
671088657
285212689
1912602624
-1073741813
1627389952
-2113929216
-805306361
1358954496
-1845493760
570425361
503316497
-268435456
-805240833
Note que pode (e deve) usar o programa acima para testar o simulador do XIM que desenvolver.

4  Implementação do simulador

Uma vez que será distribuído um assemblador, i.e., um tradutor de assembly para linguagem máquina representada por inteiros com sinal, o simulador ser capaz de carregar programas nesse formato e de os executar.  O simulador, ao correr, deverá permitir as seguintes opções:
  1. Carregar novo programa cujo nome seja dado pelo utilizador (ao arrancar o simulador recomenda-se que inicialize a memória do XIM com zero, o que corresponde a colocar na memória 216 noop).  Deve colocar PC a zero.
  2. Executar XIM (colocar o PC a zero e executar instruções até que o registo PC contenha 216 - 1).
  3. Continuar execução do XIM (sem colocar o PC a zero, executar instruções até que o registo PC contenha 216 - 1).
  4. Executar apenas a próxima instrução (avançar um passo).
  5. Colocar PC a zero (para recomeçar).
  6. Ver os valores nos registos A a P e no PC (como inteiros com sinal).
  7. Ver os valores de uma parte da memória (como inteiros com sinal e como instruções do XIM).
  8. Terminar execução do simulador.
As entradas e saídas do XIM devem ser associadas ao teclado e ecrã do computador (i.e., deve-se usar cin e cout).

4.1  Exemplo de interacção

Suponha que o programa da divisão de dois inteiros apresentado anteriormente se encontra num ficheiro chamado divide.xim (a versão código máquina, pois a versão assembly estaria no ficheiro divide.asm).  Apresenta-se um possível exemplo de interação com o simulador do XIM (a negrito os valores introduzidos  pelo utilizador do simulador, a negrito e itálico os valores introduzidos pelo utilizador como resposta a leituras do programa executado pelo simulador, e a itálico simplesmente os valores escritos pelo programa executado pelo simulador):
xim: ?
p nome - carregar programa do ficheiro 'nome'.
e      - executar programa.
c      - continuar execução.
a      - avançar um passo a execução.
z      - coloca PC a zero.
r      - mostrar registos.
m i n  - mostrar n posições de memória (começando em i).
s      - sair.
xim: p divide.xim
xim: m 0 17
memória:
0: valor = -536870912 operação = read
1: valor = 671088657 operação = store I 17
2: valor = 268435473 operação = load A 17
3: valor = -536870912 operação = read
4: valor = 671088657 operação = store I 17
5: valor = 285212689 operação = load B 17
6: valor = 1912602624 operação = zero C
7: valor = -1073741813 operação = jn A 11
8: valor = 1627389952 operação = sub B
9: valor = -2113929216 operação = incr C
10: valor = -805306361 operação = jump 7
11: valor = 1358954496 operação = add B
12: valor = -1845493760 operação = decr C
13: valor = 570425361 operação = store C 17
14: valor = 503316497 operação = load O 17
15: valor = -268435456 operação = write
16: valor = -805240833 operação = jump 65535
xim: e
1000
3
333
xim: z
xim: a
1000
xim: a
xim: a
xim: a
7
xim: r
registos:
A: valor = 1000 operação = noop
B: valor = 3 operação = noop
C: valor = 333 operação = noop
D: valor = 0 operação = noop
E: valor = 0 operação = noop
F: valor = 0 operação = noop
G: valor = 0 operação = noop
H: valor = 0 operação = noop
I: valor = 7 operação = noop
J: valor = 0 operação = noop
K: valor = 0 operação = noop
L: valor = 0 operação = noop
M: valor = 0 operação = noop
N: valor = 0 operação = noop
O: valor = 333 operação = noop
P: valor = 0 operação = noop
PC: 4
xim: c
142
xim: r
registos:
A: valor = 6 operação = noop
B: valor = 7 operação = noop
C: valor = 142 operação = noop
D: valor = 0 operação = noop
E: valor = 0 operação = noop
F: valor = 0 operação = noop
G: valor = 0 operação = noop
H: valor = 0 operação = noop
I: valor = 7 operação = noop
J: valor = 0 operação = noop
K: valor = 0 operação = noop
L: valor = 0 operação = noop
M: valor = 0 operação = noop
N: valor = 0 operação = noop
O: valor = 142 operação = noop
P: valor = 0 operação = noop
PC: 65535
xim: s

4.2  Notas sobre operações bit-a-bit

Pode assumir que na sua máquina (provavelmente um PC) e no seu ambiente de desenvolvimento (provavelmente o Visual-C++) os int e os unsigned [int] têm 32 bits, os short [int] e os unsigned short [int] têm 16 bits, e que os inteiros com sinal são representados em complemento para dois (isto é verdade na maior parte da máquinas).  Como, dado um int com um valor qualquer (por exemplo lido do ficheiro do programa), se pode saber qual a correspondente instrução XIM, de acordo com a interpretação apresentada na Secção 2.4.2?  Felizmente, o C++, embora sendo uma linguagem de nível razoavelmente alto, proporciona acesso a informação de baixo nível, como os bits individuais dos inteiros!  Para isso usam-se os chamados operadores bit-a-bit.  Estes operadores, por questões que têm a ver com a interpretação dos valores negativos, só devem ser usados com tipos sem sinal.  Assim, se a instrução estiver guardada num int chamado instrução_original,  deve-se começar por converter essa instrução para unsigned:
int instrução_original;
// Aqui, presume-se, ler-se-ia instrução de qualquer lado
// por exemplo da memória na posição dada por PC!
unsigned instrução = unsigned(instrução_original);
Depois desta conversão é já possível utilizar os operadores bit-a-bit.  Por exemplo:
instrução >> 28 // deslocamento para a direita.
desloca o padrão de bits do valor contido na variável 28 posições para a direita, deitando fora os bits que "caem" do lado direito (os unsigned só têm 32 bits!) e inserindo zeros à esquerda.  Assim, se instrução contiver o valor decimal (268436567)10 = (0001 0000 0000 0000 0000 0100 0101 0111)2 esse deslocamento resulta num inteiro com o valor (0000 0000 0000 0000 0000 0000 0000 0001)2 = (1)10, que é o código da operação load.  Esta operação usa dois operandos: r1 (registo onde carregar) e e (endereço de onde tirar o valor).  Que registo é especificado na instrução?  E que endereço?  Se deslocar o padrão de bits para a direita de apenas 24 bits, o padrão de bits correspondente ao registo fica concentrado nos quatro bits menos significativos, ou seja:
instrução >> 24
resulta no padrão 0000 0000 0000 0000 0000 0000 0001 0000.  Se se pudesse anular todos os bits com excepção dos últimos quatro o padrão obtido seria a representação binária do número do registo.  Para isso utiliza-se um mascaramento:
(instrução >> 24) & 0x0000000F // ou simplesmente 0xF (zeros à esquerda...)
O que significa 0x0000000F?  É um valor literal que, ao contrário de por exemplo 11, está especificado em hexadecimal, i.e., na base 16 (0x não passa de um prefixo usado pelo C++ para distinguir valores literais em hexadecimal de valores literais em decimal).  Por esta altura já aprendeu em Arquitectura de Computadores quais as vantagens desta base para este tipo de operações.  O valor (F)16 = (15)10 = (0000 0000 0000 000 0000 0000 0000 1111)2.  Como o operador & (e não &&, que é o e lógico!) faz o e bit-a-bit dos dois operandos, o resultado da expressão anterior é o padrão 0000 0000 0000 000 0000 0000 0000 0000, que, interpretado como um inteiro, tem o valor 0.  Conclui-se portanto que o registo da instrução é o registo A.  Relativamente ao endereço, não é necessário fazer qualquer deslocamento (porquê?) bastando mascarar a instrução com 0xFFFF, ou seja
instrução & 0xFFFF
que, neste caso, resulta no padrão de bits 0000 0000 0000 000 0000 0100 0101 0111, cujo valor em decimal é 1111.  Assim, a instrução correspondente ao valor inteiro 268436567 é load A 1111.

Finalmente, pode-lhe ser útil saber que existe um operador no C++ que nega todos os bits dum inteiro.  É a operação bit-a-bit ~ (não confundir com o símbolo ~ usado na notação matemática das folhas da cadeira para representar a negação lógica).  Assim, ~ 0xF0F3 resulta em 0x0F0C.

4.3  Mais notas sobre a implementação

  1. Recomenda-se a utilização de tipos enumerados para representar operações e registos (são possíveis conversões entre inteiros e tipos enumerados).
  2. Utilize matrizes sempre que desejável (fará sentido ter 16 variáveis para os registos básicos?).
  3. Tenha o cuidado de verificar erros (acessos fora da memória permitida, etc.).
  4. Antes de começar a escrever em C++, pense.
  5. Construa primeiro as estruturas de dados de que necessita (utilize classes sempre que justificável).
  6. Estruture o programa com cuidado em módulos (métodos, funções e procedimentos) que implementam as operações sobre as estruturas de dados.

5  Programação em assembly

Serão fornecidos à posteriori enunciados de pequenos problemas a serem resolvidos na linguagem assembly apresentada para o XIM.  Cada grupo deverá resolver um desse problemas (será publicada uma lista dos grupos com a indicação do problema a resolver por cada um), construindo o respectivo programa em assembly.  Esse programa deverá poder ser assemblado pelo assemblador fornecido e executado pelo simulador do XIM desenvolvido pelo grupo.

6  Avaliação

A avaliação do trabalho terá em conta, não só a correcta execução do programa, mas também, e principalmente, a correcta estruturação dos dados e métodos, funções e procedimentos que compõem o programa e ainda a sua legibilidade.

A nota deste trabalho será dada apenas após uma discussão, individual, com cada um dos elementos do grupo. Nesta discussão qualquer elemento do grupo terá de demonstrar um total conhecimento do programa e ser capaz de operar as alterações que forem pedidas.  Nessa oral poderão também ser feitas perguntas sobre a matéria em geral.  A nota final dependerá não só da qualidade do trabalho, mas também, e principalmente, do conhecimento do mesmo programa e da matéria em geral e da capacidade de resolver problemas em C++ demonstrados nessa discussão.

Quaisquer funcionalidades extra que não tenham sido pedidas no enunciado, tais como menus sofisticados, interfaces com janelas, etc., não serão avaliadas.

7  Curiosidades

Pense nisto.  Quando o simulador estiver a funcionar: há um processador (x86) a executar um programa, escrito em C++ e traduzido por um compilador (Visual C++), que simula um computador virtual (XIM) a executar outro programa, que por sua vez foi escrito numa linguagem assembly e traduzido para a linguagem máquina do computador virtual (XIM) por um assemblador escrito em C++ e traduzido......  Confuso?  Pense bem no assunto...  Lembre-se que o compilador (Visual C++) foi escrito em C++ e muito provavelmente compilado por si próprio (ou melhor, pela sua própria versão compilada por um outro compilador)!