Baixe o app para aproveitar ainda mais
Prévia do material em texto
1 O conjunto de instruções da arquitetura RiSC-16 1. Conjunto de instruções RiSC-16 Este artigo descreve o conjunto de instruções do Computador Ridiculamente Simples (RiSC-16) de 16 bits, um ISA para aprendizagem que é baseado no Little Computer (LC- 896) desenvolvido por Peter Chen na Universidade de Michigan. O RiSC-16 é um computador de 16 bits e 6 registradores. Todos os endereços são endereços de palavras curtas (isto é, o endereço 0 corresponde aos primeiros dois bytes da memoria principal, endereço 1 corresponde aos segundos dois bytes da memoria principal, etc). Como o conjunto de instruções da arquitetura MIPS, pela convenção de hardware, o registrador 0 sempre conterá o valor 0. A maquina reforça isso: lê o registrador 0 sempre retorna 0, independente do que tenha escrito lá. O RiSC-16 é muito simples, mas é geral o bastante para resolver problemas complexos. Há três formatos de instruções de código de máquina e um total de 8 instruções. Elas estão listadas na figura abaixo: 2 A seguinte tabela descreve as diferentes operações de intrução. Mnemonico Nome e formato Opcode (binário) Formato assembly Ação add Add RRR-type 000 add rA, rB, rC Adiciona o conteúdo de regB com regC, armazena o resultado em regA addi Add Imediato RRI- type 001 addi rA, rB, imm Adiciona o conteúdo de regB com imm, armazena o resultado em regA nand Nand RRR-type 010 nand rA, rB, rC Operação nand do conteúdo de regB com regC, armazena o resultado em regA lui Load Upper Immediate RI- type 011 lui rA, imm Coloca os 10 primeiros bits do imm de 16 bits nos 10 primeiros bits de regA, colocando 0 nos 6 bits restantes de regA sw Store word RRI- type 101 sw rA, rB, imm Armazena o valor de regA na memória. O endereço de memória é formado pela adição de imm com o conteúdo de regB lw Load Word RRI- type 100 lw rA, rB, imm Carrega valor da memória em regA. O endereço de memória é formado pela adição de imm com o conteúdo de regB beq Branch if Equal RRI-type 110 beq rA, rB, imm Se o conteudo de regA e regB são os mesmos, vai para o endereço PC + 1 + imm, onde PC é o endereço da instrução beq jalr Jump And Link Register RRI-type 111 jalr rA, rB Vai para o endereço em regB. Armazena PC+1 em regA, onde PC é o endereço da instrução jalr. 2. Linguagem de montagem RiSC-16 e Assembler A distribuição inclui um simples montador para o RiSC-16 (este é o primeiro projeto atribuído aos meus estudantes na turma de Organização de Computadores). O montador é chamando “a” e vem como um executável SPARC. Também está incluído o código fonte do montador caso você queira recompilar para alguma outra arquitetura (por exemplo, x86). O formato para uma linha de código assembly é: Label:<espaço>opcode<espaço>field0, field1, filed2<espaço># comentário 3 O campos mais a esquerda na linha é o campo label. Labels RiSC válidos são qualquer combinação de letras e números seguidos por dois pontos. Os dois pontos no final do label não são opcionais – um label sem os dois pontos é interpretado como um opcode. Depois do label opcional está o espaço em branco (espaço(s) ou tab(s)). Então segue o campo opcode, onde o opcode pode ser qualquer instrução mnemônica da linguagem de montagem listada na tabela acima. Depois de mais um espaço vem uma seria de campos separados por virgulas e possivelmente espaços em branco (você precisa ter ou espaços em branco ou uma virgula ou ambos entre cada campo). Todos os campos para valores de registradores são dados como números decimais, opcionalmente precedidos pela letra “r” como em r0, r1,r2, etc. Campos para valores imediatos são dados ou na forma decimal, octal ou hexadecimal. Números octais são precedidos pelo caractere ‘0’ (zero). Por exemplo, 032 é interpretado como o número octal ‘zero-três-dois’ que corresponde ao número decimal 26. Não é interpretado como o número decimal 32. Números hexadecimais são precedidos por ‘0x’ (zero-x). Por exemplo, 0x12 (hex-um-dois) corresponde ao número decimal 18, não ao decimal 12. Para aqueles que conhecem a linguagem de programação C, você se sentirá perfeitamente em casa. O número de campos depende da instrução. A seguinte tabela descreve as instruções Codigo em formato assembly Significado add regA, regB, regC R[regA] <- R[regB] + R[regC] addi regA, regB, regC R[regA] <- R[regb] + immed nand regA, regB, regC R[regA] <- ~(R[regB] & R[regC]) lui regA, immed R[regA] <- immed & 0xFFC0 sw regA, regB, immed R[regA] -> Mem[R[regB] + immed] lw regA, regB, immed R[regA] <- Mem[R[regB] + immed] beq regA, regB, immed If (R[regA] == R[regB]){ PC <- PC + 1 + immed (if label, PC <- label)} jalr regA, regB PC <- R[regB], R[regA] <- PC + 1 Qualquer coisa depois de uma ‘#’ é considerado como um comentário e é ignorado. O campo de comentário termina no fim da linha. Comentários são vitais para a criação de programas em linguagem de montagem compreensíveis, por que as instruções em si são bastante críticas. Em adição as instruções do RiSC-16, um programa em linguagem de montagem pode conter diretivas para o montador. Essas são chamadas de pseudo-instruções. As seis diretivas assembler que usaremos são nop, halt, lli, movi, .fill e .space (note os pontos no inicio de .fill e .space, que simplesmente significa que estes representam valores de dados, não instruções executáveis). 4 Código em formato assembly Significado nop Faz nada halt Para a máquina e imprime o estado lli regA, immed R[regA] <- R[regB] + (immed & 0x3F) movi regA, immed R[regA] <- immed .fill immed Inicializa dado com o valor immed .space immed Array preenchido com 0 (zero) do tamanho de immed Os seguintes parágrafos descrevem essas pseudo-instruções em mais detalhes: A pseud-instrução nop significa “fazer nada neste ciclo” e é recolocada pela instrução add 0,0,0 (que claramente não faz nada). A pseudo-instrução halt significa “parar de executar instruções e imprimir o estado atual da máquina” e é substituída por jalr 0,0 com um campo imediato não-zero. Esta é descrita em mais detalhes nos documentos The pipelined RiSC- 16 e An out-of order Risc-16, no qual HALT é um subconjunto de instruções de chamada de sistema para o proposito de manipular interrupções e exceções: qualquer instrução JALR com um valor imediato não-zero usa aquele imediato como um opcode de chamada de sistema. Isto permite tais instruções como chamadas de sistema, halt, retorno de exceção, etc. A pseudo-instrução lli (load-lower-immediate) significa “OR dos seis últimos bits deste número para dentro do registrador indicado” e é substituído por addi X,X,imm6, onde X é o registrador especificado, e imm6 é e igual a imm & 0x3F. Esta instrução pode ser usada em conjunto com lui: o lui primeiro move os dez primeiros bits de um dado numero (ou endereço, se um label é especificado) para dentro do registrador, colocando os seis últimos bits em 0; o lli move os seis últimos bits para dentro. O numero de 6-bits é garantido de ser interpretado como um positivo e assim evita extensões de sinal; portanto, o resultado de addi é essencialmente a concatenação de dois campos de bits. A pseudo-instrução movi é só um atalho para a combinação lui+lli. Note, contudo, que a instrução movi parece que somente representa uma simples instrução, enquanto que de fato representa duas. Isso pode jogar fora sua contagem se você está esperando uma certar distância entre instruções. Assim, é sempre uma boa ideia usar labels sempre que possível. A diretiva .fill diz ao montador para colocar um numero no localonde a instrução a seria normalmente armazenada. A diretiva .fill usa um campo, que pode ser ou um valor numérico ou um endereço simbólico. Por exemplo, “.fill 32” coloca o valor 32 onde a instrução normalmente seria armazenada. Usando .fill com um endereço simbólico armazenará o endereço do label. No exemplo abaixo, a linha “.fill start” armazenará o valor 2, por que o label “start” refere ao endereço 2. 5 A diretiva .space toma um inteiro n como um argumento e é substituído por n copias de “.fill 0” no código; isto é, resulta na criação de n palavras de 16- bits todas inicializadas com zero. O seguinte código é um programa em linguagem de montagem que decrementa a partir de 5, parando quando chegar em 0. lw 1,0,count # carrega reg1 com 5 (usa endereço simbolico) lw 2,1,2 # carrega reg2 com -1 (usa enredeço simbólico) start: add 1,1,2 # decrementa reg1 – poderia ser addi 1,1,-1 beq 0,1,1 # Vai para o fim do programa quando reg1 == 0 beq 0,0,start # volta para o começo do loop done: halt # fim do programa count: .fill 5 neg1: .fill -1 startAddr: .fill Start # conterá o endereço do começo (2) Em geral, código risc-16 aceitável é uma instrução por linha. Está tudo bem se tem uma linha em branco, se é comentada (isto é, a linha começa com #) ou não (isto é, só uma linha em branco). Contudo, um label não pode aparecer em uma linha sozinho; ele deve ser seguido por uma instrução válida na mesma linha (uma diretiva .fill ou halt/nop/etc conta como uma instrução). Note que as 8 instruções básicas da arquitetura risc-16 forma uma ISA completa que pode executar computação arbitraria. Por exemplo: Mover valores constantes para dentro de registradores. O numero 0 pode ser movido para dentro de qualquer registrador em um ciclo (add rX r0 r0). Qualquer número entre -64 e 63 pode ser colocado dentro de um registrador em uma operação usando a instrução ADDI (addi rX r0 numero). E, como mencionado, qualquer numero de 16-bits pode ser movido para dentro de um registrador em duas operações (lui+lli). Subtrair números. Subtração é simplesmente adicionar o numero negativo. Qualquer numero pode ser negativado em duas instruções pela troca dos seus bits e adicionando 1. Troca de bits pode ser feita fazendo um NAND dos valores com eles mesmos; adicionando 1 é feito com uma instrução ADDI. Portanto, subtração é um processo de três instruções. Note que sem um registrador extra, é um processo destrutivo. Multiplicar números. Multiplicação é facilmente feita por adições repetidas, teste de bit, e deslocando a esquerda um bitmask por um bit (que é o mesmo que uma soma consigo mesmo).
Compartilhar