PR04 - Sobre tipos de operandos e labels

PR04 - Sobre tipos de operandos e labels

por Nathalia Orlandi Borin -
Número de respostas: 9

Olá! Eu e meu grupo estamos com algumas dúvidas em relação ao projeto:

>> Tipos:
   Na hora de checar se os tipos dos operandos que estamos criando são válidos, estávamos fazendo o seguinte:
   1) Encontra o operador op correspondente na tabela optable
   2) Checa se operand[i]->type == op->opd_types[i]
   Porém, no caso em que o operando pode ter múltiplos tipos, como para um IS, isso parece não funcionar:


   { .name = "IS",   .opcode = IS,   .opd_types = { REGISTER | TETRABYTE | NEG_NUMBER, OP_NONE, OP_NONE }

   Na tabela temos que o primeiro operand para o operador IS pode ser um REGISTER, um TETRABYTE ou um NEG_NUMBER, e quando fazemos opd_types[0] temos 0x68 (que  a soma desses valores: REGISTER = 0x20, TETRABYTE = 0x08 e NEG_NUMBER = 0x40)

   Porém, os únicos tipos de operandos que podemos criar nessa fase são REGISTER, LABEL, STRING e NUMBER_TYPE (usando as funções em asmtypes.h), então nesse caso teríamos um operand de tipo REGISTER, isso é, operand->type = 0x32 != 0x104 que falharia a comparação acima, fazendo com que o operando seja considerado inválido, quando isso não deveria acontecer.

   O mesmo ocorre com um operador que exige um BYTE específico, sendo que só podemos mandar NUMBER_TYPE definido como:


   #define NUMBER_TYPE  (BYTE1 | BYTE2 | BYTE3 | TETRABYTE | NEG_NUMBER)

   E se checamos o tipo de um operando NUMBER_TYPE obtemos o valor 0x65, que é a soma dos valores de BYTE1, BYTE2, etc definidos acima.

   A soma dos valores deveria acontecer? Ou existe algum outro jeito de testar a validade dos tipos de cada operando?


>> Sobre as chamadas de sistema:
    { .name = "INT",  .opcode = INT,  .opd_types = { BYTE3,    OP_NONE,  OP_NONE   } },

     O primeiro operando das instruções definidas por INT #num seriam teoricamente do tipo NUMBER_TYPE (que engloba BYTE3), portanto criados pela função operand_create_number, que recebe um octa, que no caso é um long long.

     Como converteríamos um valor '#num' para um long long? Pegamos apenas a parte numérica e ignoramos o '#'? Mas nesse caso qual seria a diferença entre um "INT #80" e um "INT 80"? A segunda instrução não deveria causar um erro?

     Também tem o caso das instruções de depuração INT #DBYYZZ e INT #ADYYZZ, onde YY e ZZ são registradores. Como converter esses valores pra long long?

>> Sobre labels e a tabela de símbolos:
    1) No caso de labels que aparecem como operandos, deveríamos apenas checar se elas são válidas? (não começa com número, apenas caracteres alfanuméricos + '_', etc) ou também  checar se elas estão na tabela de símbolos e levantar algum erro do tipo "label não definida"?

    2) No caso de labels que aparecem como labels mesmo (antes de um operador), deveríamos guardar o valor da linha na tabela para usarmos no caso de um JMP? Se sim, como diferenciar o EntryData que é um operando (caso 1) de um EntryData que é um inteiro? (caso 2)

Obrigada!

Em resposta à Nathalia Orlandi Borin

Re: PR04 - Sobre tipos de operandos e labels

por Nathalia Orlandi Borin -

  #define NUMBER_TYPE  (BYTE1 | BYTE2 | BYTE3 | TETRABYTE | NEG_NUMBER)

Ah, agora que percebi que isso é um OR bitwise, então faz sentido estar somando os valores

Porém ainda não entendi como comparar esses tipos de um jeito legal sem fazer vários ifs :/

Em resposta à Nathalia Orlandi Borin

Re: PR04 - Sobre tipos de operandos e labels

por José Coelho de Pina -

Oi Nathalie,

Ah, agora que percebi que isso é um OR bitwise, então faz sentido estar somando os valores

Excelente!

Porém ainda não entendi como comparar esses tipos de um jeito legal sem fazer vários ifs :/

Operador de máscara & ajuda?

  
tipo_esperado = NUMBER_TYPE;
if (!(tipo & tipo_esperado)) { // bitwise and
    // tipo e tipo_esperado não têm bit em comum
    set_error_msg("tipo de operando errado");
}
else {
    printf("tipo do operando é ok");
}

E as suas outras perguntas?
Já descobriu as respostas?

Em resposta à Nathalia Orlandi Borin

Re: PR04 - Sobre tipos de operandos e labels

por José Coelho de Pina -

Oi Nathalia,

Desculpem pela demora...
Muito obrigado pela pergunta do seu grupo!
Outros devem estar com a mesma dúvida.

Vamos por partes.

Na tabela temos que o primeiro operand para o operador IS pode ser um REGISTER, um TETRABYTE ou um NEG_NUMBER, e quando fazemos opd_types[0] temos 0x68 (que a soma desses valores: REGISTER = 0x20, TETRABYTE = 0x08 e NEG_NUMBER = 0x40)

Nesses casos é hábito ver os número como binários e utilizar operações de máscara, bitwise operators & (and), | (or), ^ (xor) 2.8.2.3. The bitwise operators

[...]
// Masks of operand types. #define OP_NONE 0 // No operand. #define BYTE1 0x01 // One-byte number. 0000 0001 #define BYTE2 0x02 // Two-byte number. 0000 0010 #define BYTE3 0x04 // Three-byte number. 0000 0100 #define TETRABYTE 0x08 // A tetrabyte. 0000 1000 #define LABEL 0x10 // Label. 0001 0000 #define REGISTER 0x20 // Register. 0010 0000 #define NEG_NUMBER 0x40 // Number can be negative. 0100 0000 #define STRING 0x80 // A quote-enclosed string. 1000 0000
#define IMMEDIATE (REGISTER | BYTE1) // Immediate constant. 0010 0001 #define ADDR2 (LABEL | BYTE2 | NEG_NUMBER) // 0101 0010 #define ADDR3 (LABEL | BYTE3 | NEG_NUMBER)// 0101 0100 #define NUMBER_TYPE (BYTE1 | BYTE2 | BYTE3 | TETRABYTE | NEG_NUMBER) // 1100 0111 // Type of an operand. typedef unsigned int OperandType; // Opcodes for psudo-operators. #define IS -1 #define EXTERN -2 #define TETRA -3 #define STR -4 #define CALL -5 #define RET -6 #define PUSH -7 [...]

Temos que

    0x20  = 0010 0000
    0x08  = 0000 0100
    0x40  = 0100 0000

Portanto, REGISTER | TETRABYTE | NEG_NUMBER == 0110 0100 = 0x64.

   #define NUMBER_TYPE  (BYTE1 | BYTE2 | BYTE3 | TETRABYTE | NEG_NUMBER)

Temos que

    0x01  = 0000 0001
    0x02  = 0000 0010
    0x04  = 0000 0100
    0x80  = 1000 0000
    0x40  = 0100 0000

Portanto, BYTE1 | BYTE2 | BYTE3 | TETRABYTE | NEG_NUMBER == 1100 0111 == 0xc7 != 0x65 == 0110 0101. (bitwise or)

Porém, os únicos tipos de operandos que podemos criar nessa fase são REGISTER, LABEL, STRING e NUMBER_TYPE (usando as funções em asmtypes.h),

Para todos eles um byte é suficiente, certo?

/*
  Return new operand with the given value.
*/
Operand *operand_create_register(unsigned char reg);
Operand *operand_create_number(octa num);
Operand *operand_create_label(const char *label);
Operand *operand_create_string(const char *str);

Hmm.
Não sei se respondi algo...

Em resposta à Nathalia Orlandi Borin

Re: PR04 - Sobre tipos de operandos e labels

por José Coelho de Pina -

Oi Nathalia,

Mais uma.

>> Sobre labels e a tabela de símbolos:
    1) No caso de labels que aparecem como operandos, deveríamos apenas checar se elas são válidas? (não começa com número, apenas caracteres alfanuméricos + '_', etc) ou também  checar se elas estão na tabela de símbolos e levantar algum erro do tipo "label não definida"?

Veja apenas se o rótulo é válido (= sitaticamente correto, começa com ... seguido de uma sequência de ...). O paser ou analisador sintático é o responsável por esse tipo de verificação.

O montador é de dois passos.
No segundo passo, no momento de gerar o código, os rótulos deverão estar definidos ou uma mensagem de erro deverá ser exibida.

Em resposta à Nathalia Orlandi Borin

Re: PR04 - Sobre tipos de operandos e labels

por José Coelho de Pina -

2) No caso de labels que aparecem como labels mesmo (antes de um operador), deveríamos guardar o valor da linha na tabela para usarmos no caso de um JMP?

Não. A tabela alias_table é apenas de apelidos. Apelidos são definidos através de IS.

Nessa caso proceda apenas como descrito a seguir.

  • instr: aponta para o início da lista ligada de instruções (instâncias de Instruction). Cada nó da lista contém:

    • rótulo (=*label) associado à instrução;
    • operador (=*op) da instrução e
    • os seus operandos (=opds)

Se a linha de código sendo analisada contém uma instrução válida, essa deverá ser inserida como um novo nó da lista ligada.

 

Em resposta à José Coelho de Pina

Re: PR04 - Sobre tipos de operandos e labels

por Nathalia Orlandi Borin -

Veja apenas se o rótulo é válido (= sitaticamente correto, começa com ... seguido de uma sequência de ...). O paser ou analisador sintático é o responsável por esse tipo de verificação.

Não. A tabela alias_table é apenas de apelidos. Apelidos são definidos através de IS.

Oh, ok, lidaremos apenas com o IS por enquanto, então?

 

Operador de máscara & ajuda?

Aaah, era exatamente isso que faltava, não conhecia essa técnica de masking, muito obrigada pelas respostas!

 

Ficou apenas uma última dúvida: numa instrução de tipo INT #80 ou INT #DB0A14

Temos os operandos #80 e #DB0A14, eles seriam considerados de tipo NUMBER_TYPE (mais especificamente BYTE3, de acordo com a optable), mas os valores ficariam como? Consideramos a representação hexadecimal desses valores? (#80 = 0x80 = 128 e #DB0A14 = 0xDB0A14 = 14354964)?

Em resposta à Nathalia Orlandi Borin

Re: PR04 - Sobre tipos de operandos e labels

por José Coelho de Pina -

Oi Nathalia,

Novamente, desculpe pela demora...

Ficou apenas uma última dúvida: numa instrução de tipo INT #80 ou INT #DB0A14

Temos os operandos #80 e #DB0A14, eles seriam considerados de tipo NUMBER_TYPE (mais especificamente BYTE3, de acordo com a optable), mas os valores ficariam como? Consideramos a representação hexadecimal desses valores? (#80 = 0x80 = 128 e #DB0A14 = 0xDB0A14 = 14354964)?

Desta vez eu pensei em deixar o parser() do Fernando Mario dizer o que que pensa a respeito:

meu_prompt> ./parse_test "   INT #80 ; comentario"
Instrucao = '   INT #80 ; comentario'
parse(): intrucao valida
   pos    = 0
   lineno = 0
   label  = '(null)' 
   operador =
   {
       name   = 'INT'
       opcode = 0xfe
       opd_types =
       {
            opds[0] == NUM_TYPE (0x80)
            opds[1] == NULL
            opds[2] == NULL
       }
   }

meu_prompt> ./parse_test "   INT #DB0A14 * comentario"
Instrucao = '   INT #DB0A14 * comentario'
parse(): intrucao valida
   pos    = 0
   lineno = 0
   label  = '(null)' 
   operador =
   {
       name   = 'INT'
       opcode = 0xfe
       opd_types =
       {
            opds[0] == NUM_TYPE (0xdb0a14)
            opds[1] == NULL
            opds[2] == NULL
       }
   }

Ajudou?