A apresentação está carregando. Por favor, espere

A apresentação está carregando. Por favor, espere

Instituto Superior de Engenharia de Lisboa

Apresentações semelhantes


Apresentação em tema: "Instituto Superior de Engenharia de Lisboa"— Transcrição da apresentação:

1 Instituto Superior de Engenharia de Lisboa
Departamento de Eng. Electrónica e Telecomunicações e de Computadores Secção de Sistemas Digitais Folhas de Apoio à Disciplina de Introdução aos Microprocessadores Versão  1.4 © Jorge Pais

2 Objectivo O principal objectivo destas folhas de apoio à disciplina de Introdução aos Microprocessadores é dar ao aluno um texto de apoio sintético mas que foque os aspectos considerados importantes e essenciais sobre cada um dos tópicos do programa da disciplina. De modo, a que o aluno se sinta apoiado nas aulas teóricas e na realização dos trabalhos práticos. Este texto não pretende substituir de forma alguma a bibliografia recomendada para a disciplina. E, deve ser entendido como um texto de apoio à matéria leccionada nas aulas teóricas da disciplina, como tal, o aluno deverá sempre ser assíduo e estudar continuamente a matéria leccionada para concretizar com êxito a realização da disciplina. Pré-requisitos: Sistemas Digitais e Programação. Nota ao aluno: Como este conjunto de folhas encontra-se ainda numa fase embrionária é natural que contenha alguns erros ortográficos e também técnicos. Portanto, agradece-se a colaboração activa de todos os alunos interessados que ao detectarem qualquer erro, ou ao lerem certo tópico do texto acharem-no menos esclarecedor que contactem o autor via com as vossas criticas e sugestões. Muito obrigado. J.Pais

3 Memória de Acesso Aleatório - RAM (random access memory)
Para se compreender o funcionamento de uma memória de acesso aleatório, nada melhor que projectar em primeiro lugar a estrutura interna de um dispositivo destes recorrendo aos dispositivos digitais registo bidireccional e demultiplexer. No entanto, algumas considerações devem ser feitas sobre a sua estrutura interna. A RAM é constituída por um conjunto de registos de n bits cada e sendo cada registo identificado internamente através de um endereço personalizado. Assim, o número de bits de cada registo define a dimensão do conjunto de sinais por onde flui a informação de e para a RAM, este conjunto de sinais é designado por barramento de dados (data bus). A quantidade de registos internos define o número de bits necessários para o endereço de modo a que cada registo tenha um endereço distinto dos outros. Este conjunto de bits é designado por barramento de endereços (address bus). Existem ainda sinais de controlo que definem o fluxo de informação de e para a RAM que se designam respectivamente por OE/ (output enable) ou WE/ (write enable), ambos activos a zero. Também deve ser disponibilizado um sinal de controlo CE/ (chip enable) que função do seu nível lógico deve permitir realizar ou não operações sobre a RAM consoante esteja ou não activo. Por exemplo, a seguinte figura ilustra a arquitectura interna de uma RAM 4x8, ou seja, um dispositivo com 4 registos de 8 bits cada. E/ RAM 4x8 Reg. 8 D0-7 0’ 0’’ 1’ 1’’ 2’ 2’’ 3’ 3’’ OE/ WE/ Y’ Y’’ E/ Reg. 8 D0-7 OE/ OE/ CE/ EN/ E/ Reg. 8 D0-7 OE/ S0 A0 S1 E/ Reg. 8 D0-7 A1 OE/ 8 D0-7

4 Sistema de acesso à memória
Depois de se compreender o modo de funcionamento e a estrutura interna de uma RAM pode-se então proceder ao projecto de um sistema simples que permita ler e escrever dados de uma RAM 16x8. No sistema sugerido existem dois registos com os seguintes objectivos: ·    Memory Address Register (MAR) define um endereço a 4 bits seleccionando a posição de memória a aceder. O seu conteúdo pode ser estabelecido muito simplesmente através de um thumbweel switch hexadecimal quando o sinal Address/Data/ está a 1 e houver a transição ascendente do sinal Strobe, ou então, por acção do sinal Next quando se pretenda aceder ao endereço seguinte; ·    Memory Buffer Register (MBR) contém os 8 bits de dados a escrever na RAM. O seu conteúdo pode ser definido através de dois thumbweel switch quando o sinal Address/Data/ está a 0 e houver a transição ascendente do sinal Strobe.   

5  As ligações sugeridas na figura seguinte ilustram o funcionamento descrito anteriormente.
Exercício: Projecte o circuito combinatório (C.C.) da figura de modo a que o circuito anterior opere correctamente sobre a RAM. 

6 Projecto da Estrutura Interna de um CPU e sua Interligação à Memória
Considere uma unidade de processamento central (CPU – Central Processing Unit) como uma arquitectura de Von Newman, na qual os dados e o código são acedidos através dos mesmos barramentos de endereço e dados. Os barramentos têm ambos 8 bits. O CPU suporta o seguinte conjunto de instruções: Exercício: Pretende-se desenhar a estrutura interna do CPU especificado bem como a sua interligação à memória. Para a realização do exercício vamos fazer uma breve revisão do formalismo gráfico Basic Schemata que nos vai servir como ferramenta para o projecto de hardware complexo. Também vamos rever o conceito de instrução e a sua decomposição/composição em subcampos como auxiliar importante para a codificação em binário.

7 Esquema de fluência de informação - EFI
Basic Schemata O basic schemata é um formalismo gráfico constituído por dois esquemas. Esquema de fluência de informação - EFI Este esquema disponibiliza dois mecanismos. Um mecanismo chamado registo que serve para armazenar informação e que é representado por um rectângulo, tal e qual o ilustrado na seguinte figura: Nome Nº bits - símbolo de registo. Um outro mecanismo é designado acção e representa operações sobre um ou mais registos, sejam elas de transferência de informação, aritméticas ou lógicas. Este mecanismo gráfico tem a sintaxe ilustrada na seguinte figura: Nome da acção Operação ou - símbolo de acção. Esquema de Sequência de Acções – ESA Este esquema é composto por quatro mecanismos. O mecanismo acção onde se evoca o nome de uma acção existente no EFI e que obedece à seguinte sintaxe gráfica: Nome da acção

8 - decisão simples. - repetição.
Um outro mecanismo é designado decisão simples e representa um teste sobre uma expressão lógica englobando uma ou mais entidades existentes no EFI e cujo resultado define o conjunto de acções a executar. O seu grafismo é: Expressão Lógica V F E.L. - decisão simples. acções2 acções1 No exemplo da figura anterior, quando a avaliação da expressão lógica for verdadeira (V) o conjunto de acções a executar é o referido por “acções1”, caso contrário são executadas as acções identificadas por “acções2”. Quando terminado um conjunto de acções o fluxo de acções continua pelo ramo central. O terceiro mecanismo é designado por repetição e consiste na avaliação de uma expressão lógica cujo resultado pode originar a execução de um conjunto de acções de um modo repetitivo. O grafismo inerente ao mecanismo de repetição é: Acções a repetir E.L. Expressão Lógica F - repetição. v V f

9 - decisão múltipla Instruções
O último mecanismo é designado por decisão múltipla e consiste na avaliação de uma expressão cujo resultado pode originar múltiplos valores e no qual cada valor conduz à execução de um conjunto exclusivo de acções. A sintaxe gráfica é representada na seguinte figura: Expressão a avaliar valor1 valor3 valor2 valorN Acções 1 Acções 2 Acções 3 Acções N - decisão múltipla Instruções As instruções são em geral agrupadas em classes ou famílias. As classes mais relevantes de instruções são as designadas por transferência de informação, aritméticas, lógicas e de controlo. As instruções de transferência de informação são responsáveis pelas as acções de leitura ou escrita em registos, contadores e flags da estrutura interna do CPU bem como pela a alteração ou leitura de valores em dispositivos externos ao CPU mas que partilhem todos os seus barramentos.

10 As instruções aritméticas são aquelas que desempenham operações aritméticas e em geral são realizadas por uma máquina designada por unidade aritmética e lógica (ALU – arithmetic logic unit) e que é parte integrante da estrutura interna do CPU. As instruções lógicas implementam as funções lógicas comuns e em geral são realizadas na ALU. As instruções de controlo são todas as que podem alterar, de alguma forma, a execução sequencial de instruções. Depois de uma breve revisão dos tipos de instruções vamos de seguida analisar o problema da codificação das instruções tendo como exemplo o exercício proposto. De uma forma pragmática podemos dizer que o CPU a projectar tem dez instruções logo necessito de pelo menos 4 bits para a sua codificação. O modo de codificar as instruções pode ser realizado utilizando 2 abordagens completamente distintas que conduzem a complexidades de hardware e de estruturação muito diversas. A primeira abordagem pode ser designada como força bruta e consiste em atribuir um código binário distinto a cada instrução sem qualquer tipo de critério. Por exemplo, se o CPU tem 10 instruções então uma possível codificação a 4 bits é atribuir valores entre 0000B e 1001B a cada uma das instruções, resolvendo-se este problema.

11 D C1 C0 T D – Dimensão 0  1 byte 1  2 bytes C1 C0 – Classe
A segunda abordagem é uma abordagem estruturada onde os critérios são perfeitamente bem definidos quanto à funcionalidade de cada um dos bits da codificação. A figura seguinte ilustra esta abordagem para o exercício proposto. Código da instrução D C1 C0 T D – Dimensão 0  1 byte 1  2 bytes C1 C0 – Classe  Transf. de Informação  Aritmética 1 X  Controlo T – Tipo Exemplo para C1= 0 e C0=1: 0  ADDC 1  SUBB Portanto, estamos a assumir que as instruções do nosso CPU têm duas dimensões possíveis, de 1 ou 2 bytes. Então, de seguida vamos esclarecer como se calcula a dimensão de cada uma das instruções. Basicamente a dimensão depende do número de bits do código de identificação e do número de bits dos operandos da instrução.

12 código endereço MOV A, end8   2 bytes 4 bits + 8 bits - - - - 1000
e7e6e5e4e3e2e1e0 código endereço MOV end8, A   2 bytes 4 bits 8 bits 1001 e7e6e5e4e3e2e1e0 + código valor relativo 1 byte SJMP rel   1 byte 4 bits + 4 bits r3 r2 r1 r0 0100 código endereço LJMP end8   2 bytes 4 bits 8 bits 1100 e7e6e5e4e3e2e1e0 + código endereço ADDC A, end8   2 bytes 4 bits 1010 e7e6e5e4e3e2e1e0 + 8 bits código endereço SUBB A, end8   2 bytes 4 bits 1011 e7e6e5e4e3e2e1e0 + 8 bits código endereço relativo CJNC A, end8, rel4   2 bytes CJZ 4 bits + 8 bits + 4 bits r3 r2 r1 r0 1101 e7e6e5e4e3e2e1e0 código SETB C   1 byte CLR C 4 bits 0000 r3 r2 r1 r0 1111 e7e6e5e4e3e2e1e0 0001

13 I I TMP 8 I A 8 I MBR 8 I I I I Flag TCy IR 4 MAR 8 I +1 I I I I
EFI RDA RDTMP I I TMP 8 WRM I WRA A 8 Memória 256x8 I MBR 8 I 4 4 WRTMP I WRIR AU I I RDM WRMAR Flag TCy 1 1 IR 4 WRREL MAR 8 I WRTCY Inst. +1 I I I I IMAR Flag Cy RDPC WRPC REL 4 RDTCY 1 PC 8 + 1 ADDREL CLRC SETC RESET(ext.)

14 ESA RDPC RDM RDM IMAR WRIR WRREL IMAR (1 byte) IR3 1 (2 bytes) Fase de
IR3 1 (2 bytes) Fase de Preparação WRPC WRPC WRMAR case instrução MOV A,end8 MOV end8, A SJMP rel4 LJMP end8 ADDC A, end8 SUBB A, end8 RDM RDA ADDREL WRPC RDM RDM Fase de execução WRA WRM AU(IR0=0+) AU(IR0=1-)

15 CLRC SETC RDM WRTMP WRTCY CLRC RDM WRTMP WRTCY AU(IR0=1-) CLRC 1 Cy
SETB C CJNC A, end8, rel4 CJZ A, end8, rel4 CLRC SETC RDM WRTMP WRTCY CLRC RDM WRTMP WRTCY AU(IR0=1-) CLRC 1 Cy AU(IR0=1-) ADDREL 1 A=0 ADDREL RDTCY RDTMP RDTCY RDTMP

16 Depois de termos projectado o CPU, utilizando o formalismo basic schemata, precisamos de verificar a correcção do desenho. Para isso, precisa-se de realizar um programa de prova de correcção numa qualquer linguagem de alto nível (C, C++, Java, etc). De seguida, apresenta-se o programa de prova da correcção nas linguagens C, C++ e JAVA. Um aspecto importante, é que o programa não tem interface com o utilizador, sendo esta realizada utilizando a opção watch dos ambientes de desenvolvimento utilizados. Programa em C /////////////////////////// Inicio do programa em C ////////////////////////////// // definir tipo byte e word #define byte unsigned char #define word unsigned int // definição da sintaxe das instruções para introdução do programa #define MOVA 0x08 #define MOVM 0x09 #define _SJMP 0x04 #define SJMP(R) (_SJMP | (R << 4)) #define LJMP 0x0C #define ADDC 0x0A

17 #define SUBB 0x0B #define _CJNC 0x0D #define CJNC(R) (_CJNC | (R << 4)) #define _CJZ 0x0F #define CJZ(R) (_CJZ | (R << 4)) #define SETBC 0x00 #define CLRB C 0x01 // definição dos operadores simples #define RDPC cpu.mar= cpu.pc #define RDM cpu.mbr= RAM[cpu.mar] #define IMAR ++cpu.mar #define WRIR cpu.ir= cpu.mbr & 0x0F #define WRREL cpu.rel= cpu.mbr >> 4 #define WRMAR cpu.mar= cpu.mbr #define WRPC cpu.pc= cpu.mar #define WRA cpu.a= cpu.mbr #define RDA cpu.mbr= cpu.a #define WRM RAM[cpu.mar]= cpu.mbr #define ADDREL cpu.pc += (cpu.rel & 0x08)? cpu.rel | 0xF0 : cpu.rel #define SETC cpu.cy= true #define CLRC cpu.cy=false #define WRTMP cpu.tmp= cpu.a #define RDTMP cpu.a= cpu.tmp #define WRTCY cpu.tcy= cpu.cy #define RDTCY cpu.cy= cpu.tcy #define AU au() #define IR3 (cpu.ir & 0x08)

18 // memória byte RAM[256]; // registos do CPU struct t_cpu { byte pc, mar, mbr, rel, ir, tmp, a; bool cy, tcy; } cpu; // definição do macro-operador au void au() { word aux; if (IR3) { aux= cpu.a - cpu.mbr - cpu.cy; cpu.cy= cpu.a < cpu.mbr+cpu.cy; } else { aux= cpu.a + cpu.mbr + cpu.cy; cpu.cy= aux > 0xFF; cpu.a= aux; void ResetCPU() { cpu.pc= 0;

19 void InitRam() { // Exemplo: Colocar na posição de memória com o endereço 12H // o maior valor das posições de memória 10H e 11H // código em 0 RAM[0x00]= MOVA; RAM[0x01]= 0x10; RAM[0x02]= CJNC(+2); RAM[0x03]= 0x11; RAM[0x04]= MOVA; RAM[0x05]= 0x11; RAM[0x06]= MOVM; RAM[0x07]= 0x12; RAM[0x08]= SJMP(-1); // dados inicializados em 0x10 RAM[0x10]= 0xFF; RAM[0x11]= 0x01; // variável de saída RAM[0x12]= 0; } void FasePreparacao() { RDPC; RDM; IMAR; WRIR; WRREL; if (IR3) { // 2 bytes IMAR; WRPC; WRMAR; else WRPC; // 1 byte

20 void FaseExecucao() { switch (cpu.ir) { case MOVA: RDM; WRA; break; case MOVM: RDA; WRM; case _SJMP: ADDREL; break; case LJMP: WRPC; break; case ADDC: case SUBB: RDM; AU; case _CJNC: RDM; WRTMP; WRTCY; CLRC; if (!cpu.cy) ADDREL; RDTMP; RDTCY; case _CJZ: RDM; WRTMP; WRTCY; if (cpu.a) ADDREL; case SETBC:SETC; break; case CLRBC: CLRC; break; default: break; }

21 Programa em C++ void CpuRun() { for(;;) { FasePreparacao();
FaseExecucao(); } void main() InitRam(); // faz load do programa em RAM ResetCPU(); // simula a activação do reset do CPU CpuRun(); // run ao CPU ///////////////////// FIM do programa em C ///////////////////////// Programa em C++ ///////////////////////////////////////////////////// ficheiro CPU.H //////////////////////////////////////////// // tipo byte e word #define byte unsigned char #define word unsigned int // definição da sintaxe das instruções para introdução do programa #define MOVA 0x08 #define MOVM 0x09 #define _SJMP 0x04 #define SJMP(R) (_SJMP | ((R & 0x0F) << 4)) #define LJMP 0x0C #define ADDC 0x0A #define SUBB 0x0B #define _CJNC 0x0D

22 #define CJNC(R) (_CJNC | ((R & 0x0F) << 4))
#define _CJZ 0x0F #define CJZ(R) (_CJZ | ((R & 0x0F) << 4)) #define SETBC 0x00 #define CLRBC 0x01 class Registo { protected: byte r; public: byte OE() { return r; } void PL(const byte d) { r= d; } }; class Contador : public Registo { void CLR() { r= 0; } void UP() { ++r; } void DWN() { --r; } class RAM { Registo ram[256]; void LoadPrograma(); RAM () { LoadPrograma(); } byte Read(const byte end) { return ram[end].OE(); } void Write(const byte end, const byte dados) { ram[end].PL(dados); } class ALU { word aux; void run(Registo&, Registo&, Registo&, bool&);

23 class CPU { Registo mbr, rel, ir, tmp, a; Contador mar, pc; bool cy, tcy; RAM& mem; ALU au; // definição dos operadores simples void RDPC() { mar.PL(pc.OE()); } void RDM() { mbr.PL(mem.Read(mar.OE())); } void IMAR() { mar.UP(); } void WRIR() { ir.PL(mbr.OE() & 0x0F); } void WRREL() { rel.PL(mbr.OE() >> 4); } void WRMAR() { mar.PL(mbr.OE()); } void WRPC() { pc.PL(mar.OE()); } void WRA() { a.PL(mbr.OE()); } void RDA() { mbr.PL(a.OE()); } void WRM() { mem.Write(mar.OE(), mbr.OE()); } void ADDREL() { pc.PL(pc.OE() + ((rel.OE() & 0x08)? rel.OE() | 0xF0 : rel.OE())); } void SETC() { cy= true; } void CLRC() { cy=false; } void WRTMP() { tmp.PL(a.OE()); } void RDTMP() { a.PL(tmp.OE()); } void WRTCY() { tcy= cy; } void RDTCY() { cy= tcy; } // as duas fases distintas de uma instrução do CPU void FasePreparacao(); void FaseExecucao(); public: CPU(RAM& m):mem(m) { pc.CLR(); } void Run(); }; /////////// fim do ficheiro CPU.H ///////////////

24 #include "CPU_cpp.h" void RAM::LoadPrograma() { // Exemplo: Colocar na posição de memória com o endereço 0CH // o maior valor das posições de memória 0AH e 0BH // código em 0 ram[0x00].PL(MOVA); ram[0x01].PL(0x10); ram[0x02].PL(CJNC(+2)); ram[0x03].PL(0x11); ram[0x04].PL(MOVA); ram[0x05].PL(0x11); ram[0x06].PL(MOVM); ram[0x07].PL(0x12); ram[0x08].PL(SJMP(-1)); // dados inicializados em 0x10 ram[0x10].PL(0xFE); ram[0x11].PL(0xFF); // variável de saída em 0x12 } void CPU::FasePreparacao() { RDPC(); RDM(); IMAR(); WRIR(); WRREL(); if (ir.OE() & 0x08) { // 2 bytes IMAR(); WRPC(); WRMAR(); else WRPC(); // 1 byte

25 void ALU::run(Registo& a, Registo& mbr, Registo& ir, bool& cy)
{ if (ir.OE() & 0x01) { aux= a.OE() - mbr.OE() - cy; cy= a.OE() < (mbr.OE() + cy); } else { aux= a.OE() + mbr.OE() + cy; cy= aux > 0xFF; a.PL(aux); void CPU::FaseExecucao() { switch (ir.OE()) { case MOVA: RDM(); WRA(); break; case MOVM: RDA(); WRM(); case _SJMP: ADDREL(); case LJMP: WRPC(); case ADDC: case SUBB: RDM(); au.run(a, mbr, ir, cy);

26 case _CJNC: RDM(); WRTMP(); WRTCY(); CLRC(); au.run(a, mbr, ir, cy); if (!cy) ADDREL(); RDTMP(); RDTCY(); break; case _CJZ: RDM(); if (a.OE()==0) ADDREL(); case SETBC: SETC(); case CLRBC: CLRC(); default: break; } void CPU::Run() { for(;;) { FasePreparacao(); FaseExecucao(); void main() { RAM m; // criação da memória CPU x(m); // criação do CPU associando-lhe uma memória x.Run(); // run ao CPU } //////////////// fim do ficheiro CPU.CPP ////////////////////

27 Programa em JAVA sem interface gráfica
/// início do ficheiro Instrucao.java package CPUX; public interface Instrucao { // definição da código das instruções para introdução do programa int MOVA= 0x08; int MOVM= 0x09; int SJMP= 0x04; int LJMP= 0x0C; int ADDC= 0x0A; int SUBB= 0x0B; int CJNC= 0x0D; int CJZ= 0x0F; int SETC= 0x00; int CLRC= 0x01; public abstract int CJZ(int op); public abstract int CJNC(int op); public abstract int SJMP(int op); } /// fim de ficheiro /// início do ficheiro Registo.java package CPUX; public class Registo { // variáveis protected int valor; // construtor public Registo() { valor= 0; } // métodos public int OE() { return valor; } public void PL(int v) { valor= v & 0xFF; } } /// fim de ficheiro /// início do ficheiro Flag.java package CPUX; public class Flag { // variáveis private boolean valor; // construtor public Flag() { valor= false; } // métodos public boolean OE() { return valor; } public void CLR() { valor= false; } public void SET() { valor= true; } public void LD(boolean v) { valor= v; } } /// fim de ficheiro ///início do ficheiro Contador.java package CPUX; // import Registo.*; public class Contador extends Registo { public Contador() { valor= 0; } public void CLR() { valor= 0; } public void INC() { ++valor; } public void DEC() { --valor; } } /// fim de ficheiro

28 /// início do ficheiro ALU.java
package CPUX; public class ALU { private int aux; public void run(Registo a, Registo mbr, Registo ir, Flag cy) { if ((ir.OE() & 1) == 1) { aux= a.OE() - mbr.OE() - (cy.OE()? 1 : 0); cy.LD(aux < 0); } else { aux= a.OE() + mbr.OE() + (cy.OE()? 1 : 0); cy.LD(aux > 0xFF); a.PL(aux); } /// fim de ficheiro /// início do ficheiro Memoria.java // classe memória de acesso aleatório package CPUX; public class Memoria { static final int DIM_MEM= 256; private Registo m[]; public Memoria() { m= new Registo[DIM_MEM]; for(int i= 0 ; i<DIM_MEM ; ++i) // reserva de espaço em memória m[i]= new Registo(); } public int Read(int end) { return m[end].OE(); } public void Write(int end, int dados) { m[end].PL(dados); } } /// fim de ficheiro

29 /// início do ficheiro CPU.java
package CPUX; import java.lang.*; // classe CPU public class CPU implements Instrucao { // implementação da interface instrucao public int CJZ(int op) { return CJZ | ((op & 0x0F) << 4); } public int CJNC(int op){ return CJNC | ((op & 0x0F) << 4); } public int SJMP(int op){ return SJMP | ((op & 0x0F) << 4); } // registos internos ao CPU private Registo mbr, rel, ir, tmp, a; private Contador mar, pc; private Flag cy, tcy; private Memoria mem; // Unidade Aritmética e Lógica do CPU private ALU au; // definição dos operadores simples private void RDPC() { mar.PL(pc.OE()); } private void RDM() { mbr.PL(mem.Read(mar.OE())); } private void IMAR() { mar.INC(); } private void WRIR() { ir.PL(mbr.OE() & 0x0F); } private void WRREL() { rel.PL(mbr.OE() >> 4); } private void WRMAR() { mar.PL(mbr.OE()); } private void WRPC() { pc.PL(mar.OE()); } private void WRA() { a.PL(mbr.OE()); } private void RDA() { mbr.PL(a.OE()); } private void WRM() { mem.Write(mar.OE(), mbr.OE()); } /// continua /// continuação private void ADDREL() { pc.PL(pc.OE() + (((rel.OE() & 0x08)==0x08)? rel.OE() | 0xFFFFFFF0 : rel.OE())); } private void SETCY() { cy.SET(); } private void CLRCY() { cy.CLR(); } private void WRTMP() { tmp.PL(a.OE()); } private void RDTMP() { a.PL(tmp.OE()); } private void WRTCY() { tcy.LD(cy.OE()); } private void RDTCY() { cy.LD(tcy.OE()); } public CPU(Memoria m) { mem= m; au= new ALU(); cy= new Flag(); tcy= new Flag(); mar= new Contador(); pc= new Contador(); mbr= new Registo(); rel= new Registo(); ir= new Registo(); tmp= new Registo(); a= new Registo(); } /// continua

30 /// continuação private void FasePreparacao() { RDPC(); RDM(); IMAR(); WRIR(); WRREL(); if ((ir.OE() & 0x08)==0x08) { // 2 bytes IMAR(); WRPC(); WRMAR(); } else WRPC(); // 1 byte private void FaseExecucao() { switch (ir.OE()) { case MOVA: WRA(); break; case MOVM: RDA(); WRM(); case SJMP: ADDREL(); case LJMP: /// continua /// continuação case ADDC: case SUBB: RDM(); au.run(a, mbr, ir, cy); break; case CJNC: WRTMP(); WRTCY(); CLRCY(); if (cy.OE()==false) ADDREL(); RDTMP(); RDTCY(); case CJZ: if (a.OE()==0) ADDREL(); case SETC: SETCY(); case CLRC: default: } } /// continua

31 /// continuação public void LoadPrograma() { // Exemplo: Colocar na posição de memória com o endereço 12H // o maior valor das posições de memória 10H e 11H // código em 0 mem.Write(0x00, MOVA); mem.Write(0x01, 0x10); mem.Write(0x02, CJNC(+2)); mem.Write(0x03, 0x11); mem.Write(0x04, MOVA); mem.Write(0x05, 0x11); mem.Write(0x06, MOVM); mem.Write(0x07, 0x12); mem.Write(0x08, SJMP(-1)); // dados inicializados em 0x10 mem.Write(0x10, 0xFE); mem.Write(0x11, 0xFF); // variável de saída em 0x12 } public void Run() for(;;) FasePreparacao(); FaseExecucao(); }/// fim de ficheiro /// início do ficheiro KitXXX.java package CPUX; public class KitXXX { public static void main(String args[]) { // criação da memória Memoria m= new Memoria(); // criação do CPU associando-lhe uma memória CPU x= new CPU(m); // carregar o programa de teste na memoria do Kit x.LoadPrograma(); // run ao CPU x.Run(); } } /// fim de ficheiro

32 Simulação do CPU, DMA e RAM com interface gráfica em JAVA
Algumas considerações devem ser realizadas antes da apresentação do código do programa em Java devido à simulação aqui descrita diferir nalguns conceitos e em complexidade relativamente ao anteriormente apresentado. Em primeiro lugar o que é um DMA? DMA é a sigla abreviada do termo anglo-saxónico Direct Memory Access que em português traduz um dispositivo de acesso directo a uma memória. Por outras palavras é uma máquina hardware (previamente descrita no ponto “sistema de acesso a uma memória”) que permite a leitura ou a escrita numa memória. A segunda questão que se coloca é: como dois dispositivos físicos podem aceder à mesma memória? O problema do acesso múltiplo de duas máquinas hardware a um mesmo dispositivo de memória consiste na partilha física dos barramentos de dados, endereço e controlo que em caso de simultaneidade temporal no acesso poria em risco a integridade física das respectivas máquinas. A solução simples deste problema passa por estabelecer um protocolo físico entre as duas máquinas designado por HOLD/HLDA (abreviatura de hold/hold acknowledge). A ideia deste protocolo é baseada no facto de existir uma máquina que por defeito acede à memória, designada por sistema Mestre (Master em inglês) e a outra máquina que pretende o acesso à memória é designada por sistema Escravo (Slave em inglês). Este protocolo estabelece uma hierarquia de acesso à memória por hardware designada por Mestre-Escravo. O sinal físico Hold é uma entrada do sistema Mestre e é uma saída do sistema Escravo, permitindo ao Escravo indicar ao Mestre que pretende aceder à memória. Existe o sinal HLDA (HoLD Acknowledge) que é uma saída do sistema Mestre e uma entrada no sistema Escravo que indica ao sistema Escravo que o Mestre libertou os barramentos e que este pode aceder aos barramentos. Quando o sistema Escravo deixa de aceder aos barramentos por ter executado todas as suas tarefas descativa o sinal Hold e o sistema Mestre ao detectar a desactivação deste sinal também desactiva o sinal HLDA passando a aceder novamente à memória.

33 Em termos gráficos pode-se descrever do seguinte modo o acesso à memória função do protocolo HOLD/HLDA: HOLD HLDA Acesso do Master Acesso do Master Acesso do Slave Memória AB DB CB Master Slave AB AB DB DB CPU DMA CB CB HOLD HOLD HLDA HLDA

34 CPU 1 ESA HOLD HOLDA RDPC 1 RDM 4 RDM 2 IMAR 5 Fase de Preparação IMAR WRIR WRREL 3 WRPC 6 (1 byte) 1 (2 bytes) IR3 WRMAR 7 WRPC 8 case instrução SETB C CLR C MOV end8, A SJMP rel4 LJMP end8 ADDC A, end8 SUBB A, end8 CJNC A, end8, rel4 CJZ A, end8, rel4 MOV A,end8 28 9 11 13 14 15 15 17 CLRC RDM RDA ADREL WRPC RDM RDM RDM WRTMP WRTCY 22 10 12 16 18 AU(IR0=0+) AU(IR0=1-) 16 RDM WRTMP WRTCY SETC 27 WRA WRM CLRC Fase de Execução CLRC 23 AU(IR0=1-) 19 AU(IR0=1-) 24 Nota: Na figura, o ESA do CPU foi redesenhado tendo em atenção que originará uma máquina de estados hardware o que faz corresponder a cada nível de acções um número que é o identificador de estado. Cy 1 20 ADREL 1 A=0 ADREL 25 26 21 RDTMP RDTCY RDTCY RDTMP

35 RDA EFI RDTMP I TMP 8 I MBR 8 WRM 4 4 WRA I I Memória 256x8 A 8 I
WRTMP I WRIR WRMAR I RDM I AU Flag TCy 1 1 IR 4 WRREL I IMAR Inst. +1 WRTCY I MAR 8 I RDTCY REL 4 1 RDPC I I WRPC Flag Cy (ext.) SETHOLD PC 8 + 1 1 CLRC (ext.) CLRHOLD ADDREL SETC Flag HldA Flag Hold RESET(ext.) 1 Nota: Ao EFI foi acrescentado as flags Hold e HoldA. CLRHOLDA SETHOLDA

36 Neste ponto, apresenta-se um sistema software desenvolvido em Java que implementa a arquitectura ilustrada. A interface é gráfica e é composta por 3 janelas, cada uma das quais ilustra ao “raio-x” o conteúdo de cada dispositivo permitindo o utilizador interagir com o DMA e com o modo de funcionamento do CPU. O sistema é composto por três componentes Memória, DMA e CPU que são implementados utilizando os seguintes ficheiros e classes: Sistema KitXXX.java – define a classe KitXXX que tem o método “main” e que aglotina todos os dispositivos físicos do sistema. Memória Memoria.java - define a classe Memória e as acções de leitura e escrita sobre o dispositivo. E também inclui o suporte da interface gráfica. DMA DMA.java – define um DMA e todos os registos e sinais que o utilizador pode manipular para concretizar a escrita e a leitura da memória. CPU CPUComInterface.java – define a classe com o mesmo nome que implementa a interface gráfica do CPU. Flag.java – define a classe Flag que implementa um flip-flop e os respectivos métodos de manipulação. Registo.java – define a classe Registo bem como os métodos de operação. Contador.java – a classe Contador aumenta a operacionalidade do Registo tal como em hardware. ALU.java – define uma classe ALU que permite realizar operações aritméticas ou lógicas. InstrucaoRegistos.java – define uma interface abstracta designada InstrucaoRegistos aonde se identificam os recursos físicos do CPU, tais como flags, registos, contadores... CPU.java – define a classe CPU que contém toda a arquitectura interna do CPU definida pelo projectista em Basic Schemata e que serve para provar a sua correcção. Nota: O aluno só deve alterar ou interferir na classe CPU e na interface InstrucaoRegistos.

37 // continuação do ficheiro Memoria.java
private String Int2Hex(int v){ return new String(Integer.toHexString(v)); } public Memoria() { // inicialização das posições de memória m= new Registo[DIM_MEM]; for(int i= 0 ; i<DIM_MEM ; ++i) // reserva de espaço em memória m[i]= new Registo(); //inicialização dos grafismos lista= new List(16, false); Font font= new Font("Monospaced", Font.PLAIN, 12); lista.setFont(font); lista.setBackground(Color.yellow); for (int adr= 0 ; adr<DIM_MEM ; ++adr) lista.add("M(" + Int2Hex(adr) + "):" + Int2Hex(m[adr].OE()), adr); janela= new Frame("Memória"); janela.setBackground(Color.yellow); janela.add(lista); janela.setBounds(0, 0, 140, 280); janela.show(); public void refresh(int end, int dados) { lista.replaceItem("M(" + Int2Hex(end) + "):" + Int2Hex(m[end].OE()), end); public int Read(int end) { return m[end].OE(); } public void Write(int end, int dados) { m[end].PL(dados); refresh(end, dados); // fim de ficheiro // Ficheiro KitXXX.java package CPUX; public class KitXXX { public static void main(String args[]) { // criação da memória Memoria m= new Memoria(); // criação do DMA associando-lhe uma memória DMA d= new DMA(m); // criação do CPU associando-lhe uma memória CPUComInterface x= new CPUComInterface(m); for (;;){ x.Hold(d.Hold()); // ligar o hold do DMA ao hold do CPU d.Holda(x.Holda()); // ligar o holda do CPU ao holda do DMA } // fim de ficheiro // Ficheiro Memoria.java package CPUX; import java.awt.*; public class Memoria { // classe memória de acesso aleatório static final int DIM_MEM= 256; protected Registo m[]; // grafismos private Frame janela; private List lista; // continua

38 // ficheiro DMA.java package CPUX; import java.awt.*; import java.awt.event.*; import javax.swing.*; public class DMA { private Memoria mem; private Flag holda; int estado; // interface gráfica Frame janela; Panel quadro; Button next, strobe; Label label_mbr, label_mar, label_holda; TextField mbr, mar; Checkbox hold; // variáveis para simular o clock do DMA final static int TCLOCK= 200; // 200 mse ActionListener Lclock; Timer clock; private void Run(){ switch (estado) { case 1: if (holda.OE()) estado=2; break; case 2: mbr.setText(Integer.toHexString( String2Int(mbr.getText()))); mar.setText(Integer.toHexString( String2Int(mar.getText()))); mem.Write(String2Int(mar.getText()), String2Int(mbr.getText())); estado=0; break; default: break; } label_holda.setText("HOLDA:" + ((holda.OE())? "1" : "0")); // continua // Continuação do ficheiro DMA.java public DMA(Memoria m) { // recursos mem= m; holda= new Flag(); // grafismos janela= new Frame(); quadro= new Panel(); janela.setBackground(Color.green); janela.add(quadro); janela.setTitle("DMA"); janela.setBounds(140, 0, 200, 120); label_mbr= new Label("MBR:"); quadro.add(label_mbr); mbr= new TextField("00"); mbr.setColumns(2); mbr.setBackground(new Color(150, 255, 0)); quadro.add(mbr); label_mar= new Label("MAR:"); quadro.add(label_mar); mar= new TextField("00"); mar.setColumns(2); mar.setBackground(new Color(200, 255, 0)); quadro.add(mar); hold= new Checkbox("HOLD"); quadro.add(hold); label_holda= new Label(""); quadro.add(label_holda); FlagsRefresh(); next= new Button("Next"); quadro.add(next); strobe= new Button("STROBE"); quadro.add(strobe); TratarEventos(); janela.show(); } public boolean Hold() { return hold.getState(); } public void Holda(boolean h) { holda.LD(h); } // continua // Continuação do ficheiro DMA.java private void FlagsRefresh(){ } private void IncMar(){ int i= String2Int(mar.getText()); mar.setText(Integer.toHexString(++i)); private int String2Int(String s){ int i; try { i= Integer.parseInt(s, 16) & 0xff; catch (NumberFormatException e) { return 0xff; } return i; private void TratarEventos() { // next next.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { IncMar(); }}); // strobe strobe.addActionListener(new ActionListener() { if (hold.getState()) estado=1; // clock DMA Lclock= new ActionListener() { Run(); } }; clock= new Timer(TCLOCK, Lclock); clock.start(); // Fim de Ficheiro

39 // continuação de CPUComInterface.java
public CPUComInterface(Memoria m) { nclocks= new Contador(); CriaRecursos(m); estado_execucao= MenorEstado(DecoderInstrucao); janela= new Frame("CPU"); janela.setBackground(new Color(100, 100, 255)); janela.setTitle("CPU: Unidade de Processamento Central"); janela.setBounds(340, 0, NREGISTOS*100, 150); Panel norte= new Panel(); display_fase= new Label(); norte.add(display_fase); display_estado= new Label(); norte.add(display_estado); display_clocks= new Label(); norte.add(display_clocks); janela.add("North", norte); next= new Button(" Next "); janela.add("East", next); reset= new Button(" RESET "); janela.add("West", reset); Panel centro= new Panel(); display_flags= new Label[NFLAGS]; for(int i= 0 ; i<NFLAGS ; ++i) { display_flags[i]= new Label(); display_flags[i].setForeground(f[i].Cor()); centro.add(display_flags[i]); } display_registos= new Label[NREGISTOS]; for(int i= 0 ; i<NREGISTOS ; ++i) { display_registos[i]= new Label(); display_registos[i].setForeground(r[i].Cor()); centro.add(display_registos[i]); display_contadores= new Label[NCONTADORES]; for(int i= 0 ; i<NCONTADORES ; ++i) { display_contadores[i]= new Label(); display_contadores[i].setForeground(c[i].Cor()); centro.add(display_contadores[i]); janela.add("Center", centro); Panel sul= new Panel(); power_down= new Button(" POWER DOWN "); sul.add(power_down); run= new Checkbox("Run|Step/"); sul.add(run); janela.add("South", sul); TratarEventos(); VisualizarRecursos(); // Ficheiro CPUComInterface.java package CPUX; import java.awt.*; import java.awt.event.*; import javax.swing.*; public class CPUComInterface extends CPU{ Frame janela; Label display_flags[], display_contadores[], display_registos[]; Label display_estado, display_clocks, display_fase; Button next, reset, power_down; Checkbox run; Contador nclocks; int estado_execucao; // variáveis para a simulação do clock do CPU final static int TCLOCK= 1000; // 1000 mseg= 1seg ActionListener Lclock; Timer clock; public void TratarEventos() { // next next.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (!run.getState()){ nclocks.INC(); Run(); VisualizarRecursos(); } }}); // power down power_down.addActionListener(new ActionListener() { System.exit(0); } }); // reset reset.addActionListener(new ActionListener() { nclocks.CLR(); Reset(); VisualizarRecursos(); // clock CPU Lclock= new ActionListener() { if (run.getState()){ if (!f[HOLDA].OE()) { nclocks.INC(); Run(); VisualizarRecursos(); } else Run(); } } }; clock= new Timer(TCLOCK, Lclock); clock.start(); // continuação do CPUComInterface.java public void VisualizarRecursos(){ String s= new String(""); for(int i= 0 ; i<NFLAGS ; ++i) { if (i!= HOLD && i!=HOLDA) display_flags[i].setForeground(f[i].Cor()); s= " " + f[i].Nome() + ":" + ((f[i].OE())? "1" : "0") + " "; display_flags[i].setText(s); f[i].Cor(Color.black); } for(int i= 0 ; i<NREGISTOS ; ++i) { display_registos[i].setForeground(r[i].Cor()); s= " " + r[i].Nome() + ":" + Integer.toHexString(r[i].OE()) + " "; display_registos[i].setText(s); r[i].Cor(Color.black); for(int i= 0 ; i<NCONTADORES ; ++i){ display_contadores[i].setForeground(c[i].Cor()); s= " " + c[i].Nome() + ":" + I nteger.toHexString(c[i].OE()) + " "; display_contadores[i].setText(s); c[i].Cor(Color.black); s= "estado: " + Integer.toString(Estado()); display_estado.setText(s); s= "clocks: " + Integer.toString(nclocks.OE()); display_clocks.setText(s); s= (estado >= estado_execucao)? "Fase de Execução " : "Fase de Preparação"; display_fase.setText(s); janela.show(); private int MenorEstado(int di[]){ int menor= Integer.MAX_VALUE; for(int i= 0 ; i< di.length ; ++i) if ((di[i]!= 0) && (di[i]< menor)) menor= di[i]; return menor; // continua

40 // Ficheiro Registo.java
package CPUX; import java.awt.*; public class Registo { // variáveis protected String nome; protected Color cor; protected int valor; // construtor public Registo() { valor= 0; cor=Color.black; } public Registo(String n) { nome= new String(n); cor= Color.black; // métodos protected String Nome() { return nome; } protected void Cor(Color c) { cor= c; } protected Color Cor() { return cor; } public int OE() { if (cor==Color.black) cor= Color.green; return valor; public void PL(int v) { cor=Color.red; valor= v & 0xFF; } // fim de ficheiro // Ficheiro Contador.java package CPUX; import java.awt.*; // import Registo.*; public class Contador extends Registo { public Contador() { } public Contador(String n) { nome= new String(n); } public void CLR() { cor= Color.yellow; valor= 0; public void INC() { cor= Color.yellow; valor= ++valor & 0xff; public void DEC() { valor= --valor & 0xff; } // Ficheiro Flag.java package CPUX; import java.awt.*; public class Flag { // variáveis private String nome; private Color cor; private boolean valor; // construtor public Flag() { valor= false; cor= Color.black; } public Flag(String n){ nome= new String(n); // métodos public String Nome() { return nome; } public void Cor(Color c) { cor= c; } public Color Cor() { return cor; } public boolean OE() { cor=Color.green; return valor; } public void CLR() { cor= Color.yellow; valor= false; } public void SET() { cor= Color.yellow; valor= true; } public void LD(boolean v) { cor= Color.red; valor= v; } // fim de ficheiro

41 // ficheiro InstrucaoRegistos.java
package CPUX; public interface InstrucaoRegistos { /* definição da código das instruções existentes no CPU int MOVA= 0x08; int MOVM= 0x09; int SJMP= 0x04; int LJMP= 0x0C; int ADDC= 0x0A; int SUBB= 0x0B; int CJNC= 0x0D; int CJZ= 0x0F; int SETC= 0x00; int CLRC= 0x01; */ // descodificador de instrução int [] DecoderInstrucao={27,28,0,0,13,0,0,0,9,11,15,15,14,17,0,22}; // identificação dos registos int MBR= 0; int REL= 1; int IR= 2; int TMP= 3; int A= 4; int NREGISTOS= 5; // identificação dos contadores int ESTADO= 0; int MAR= 1; int PC= 2; int NCONTADORES= 3; // identificação de flags int CY= 0; int TCY= 1; int HOLD= 2; int HOLDA= 3; int NFLAGS= 4; } // fim de ficheiro // Ficheiro ALU.java package CPUX; public class ALU { private int aux; public void run(Registo a, Registo mbr, Registo ir, Flag cy) { if ((ir.OE() & 1) == 1) { aux= a.OE() - mbr.OE() - (cy.OE()? 1 : 0); cy.LD(aux < 0); } else { aux= a.OE() + mbr.OE() + (cy.OE()? 1 : 0); cy.LD(aux > 0xFF); a.PL(aux); // fim de ficheiro

42 // ficheiro CPU.java package CPUX; import java.lang.*; public class CPU implements InstrucaoRegistos { // registos internos ao CPU protected Registo r[]; protected Contador c[]; protected Flag f[]; protected Memoria mem; // Unidade Aritmética e Lógica do CPU private ALU au; // definição dos operadores simples private void IESTADO() { c[ESTADO].INC(); } private void CLRESTADO() { c[ESTADO].CLR(); } protected int RDESTADO() { return c[ESTADO].OE(); } private void WRESTADO(int v) { c[ESTADO].PL(v); } private void CLRPC() { c[PC].CLR(); } private void RDPC() { c[MAR].PL(c[PC].OE()); } private void RDM() { r[MBR].PL(mem.Read(c[MAR].OE())); } private void IMAR() { c[MAR].INC(); } private void WRIR() { r[IR].PL(r[MBR].OE() & 0x0F); } private int RDIR() { return r[IR].OE(); } private boolean IR3() { return (r[IR].OE() & 0x08) == 0x08;} private void WRREL() { r[REL].PL(r[MBR].OE() >> 4); } private void WRMAR() { c[MAR].PL(r[MBR].OE()); } private void WRPC() { c[PC].PL(c[MAR].OE()); } private void WRA() { r[A].PL(r[MBR].OE()); } private void RDA() { r[MBR].PL(r[A].OE()); } private boolean AZERO() { return r[A].OE()==0; } private void WRM() { mem.Write(c[MAR].OE(), r[MBR].OE()); } private void ADDREL() { c[PC].PL(c[PC].OE() + (((r[REL].OE() & 0x08)==0x08)? r[REL].OE() | 0xFFFFFFF0 : r[REL].OE())); } private void SETCY() { f[CY].SET(); } private void CLRCY() { f[CY].CLR(); } private boolean RDCY() { return f[CY].OE(); } private void WRTMP() { r[TMP].PL(r[A].OE()); } private void RDTMP() { r[A].PL(r[TMP].OE()); } private void WRTCY() { f[TCY].LD(f[CY].OE()); } // continua // continuação private void RDTCY() { f[CY].LD(f[TCY].OE()); } private void CLRHOLDA() { f[HOLDA].CLR(); } private void SETHOLDA() { f[HOLDA].SET(); } public boolean RDHOLDA(){ return f[HOLDA].OE(); } private boolean RDHOLD(){ return f[HOLD].OE(); } public void WRHOLD(boolean v){ f[HOLD].LD(v); } public CPU(){ } // continua

43 // continuação do ficheiro CPU.java
protected void CriaRecursos(Memoria m) { mem= m; au= new ALU(); // criação de flags f= new Flag[NFLAGS]; f[CY]= new Flag("CY"); f[TCY]= new Flag("TCY"); f[HOLD]= new Flag("HOLD"); f[HOLDA]= new Flag("HOLDA"); // criação de contadores c= new Contador[NCONTADORES]; c[ESTADO]= new Contador("ESTADO"); c[MAR]= new Contador("MAR"); c[PC]= new Contador("PC"); // criação de registos r= new Registo[NREGISTOS]; r[MBR]= new Registo("MBR"); r[REL]= new Registo("REL"); r[IR]= new Registo("IR"); r[TMP]= new Registo("TMP"); r[A]= new Registo("A"); } public void Reset(){ CLRPC(); CLRESTADO(); // continua // continuação de CPU.java public void Run(){ switch (RDESTADO()) { case 0: // Fase de Preparação if (RDHOLD()) SETHOLDA(); else{ CLRHOLDA(); IESTADO(); }break; case 1: RDPC(); IESTADO(); break; case 2: RDM(); IESTADO(); break; case 3: IMAR(); WRIR(); WRREL(); if (IR3()) IESTADO(); else WRESTADO(6); break; // 2 bytes case 4: RDM(); IESTADO(); break; case 5: IMAR(); IESTADO(); break; case 6: WRPC(); IESTADO(); break; case 7: WRMAR(); WRESTADO(DecoderInstrucao[RDIR()]); break; case 8: WRPC(); WRESTADO(DecoderInstrucao[RDIR()]); break; case 9: // Fase de Execução RDM(); IESTADO(); break; // MOV A, M case 10: WRA(); CLRESTADO(); break; // MOV A, M case 11: RDA(); IESTADO(); break; // MOV M, A case 12: WRM(); CLRESTADO(); break; // MOV M, A case 13: ADDREL(); CLRESTADO(); break; // SJMP rel4 case 14: WRPC(); CLRESTADO(); break; // LJMP end8 case 15: RDM(); IESTADO(); break; // ADDC A, M : SUBB A, M case 16: au.run(r[A], r[MBR], r[IR], f[CY]); CLRESTADO(); break; // ADDC A, M : SUBB A, M case 17: RDM(); WRTMP(); WRTCY(); IESTADO(); break; // CJNC A, M, rel4 case 18: CLRCY(); IESTADO(); break; // CJNC A, M, rel4 case 19: au.run(r[A], r[MBR], r[IR], f[CY]); if (RDCY()) WRESTADO(21); else IESTADO(); break; // CJNC A, M, rel4 case 20: ADDREL(); IESTADO(); break; // CJNC A, M, rel4 case 21: RDTMP(); RDTCY(); CLRESTADO(); break; // CJNC A, M, rel4 case 22: RDM(); WRTMP(); WRTCY(); IESTADO(); break; // CJZ A, M, rel4 case 23: CLRCY(); IESTADO(); break; // CJZ A, M, rel4 case 24: au.run(r[A], r[MBR], r[IR], f[CY]); IESTADO(); if (AZERO()) IESTADO(); else WRESTADO(26); break; // CJZ A, M, rel4 case 25: ADDREL(); IESTADO(); break; // CJZ A, M, rel4 case 26: RDTMP(); RDTCY(); CLRESTADO(); break; // CJZ A, M, rel4 case 27: SETCY(); CLRESTADO(); break; // SETB C case 28: CLRCY(); CLRESTADO(); break; // CLR C }}} // fim de ficheiro

44 Exemplo de programa: Colocar como conteúdo da posição de memória de endereço 12H o maior valor existente entre os conteúdos das posições de memória com endereços 10H e 11H. endereço código hexadecimal instrução 0: 08, 10 MOV A, 10H 2: 2D, 11 CJNC A, +2, 11H 4: 08, 11 MOV A, 11H 6: 09, 12 MOV M, 12H 8: F4 SJMP -1 Exercício: Acrescente ao CPU projectado as seguintes instruções.  Proceda às alterações da estrutura interna do CPU projectado de modo a que este suporte todo o conjunto de instruções. No programa simulador apenas deve modificar os ficheiros CPU.java e InstrucaoRegistos.java de modo a que respeitem o projecto realizado em termos de ESA e EFI..

45 Organização de memória
CPU 80535 Nesta secção pretende-se estudar o CPU80535 numa perspectiva de programador. Assumindo-se que este já tem ao seu dispor uma arquitectura de desenvolvimento baseada no CPU Deste modo, o programador tem de conhecer os aspectos principais da estrutura interna do CPU, tais como, a organização de memória, os modos de endereçamento e as instruções para lhe permitir o começo do desenvolvimento de programas de aplicação. De seguida vamos abordar todos estes aspectos. Organização de memória Memória de Código FFFFH Algumas características a ter em conta: Address Bus externo – 16 bits Data Bus – 8 bits Instruções de 1, 2 ou 3 bytes de dimensão. Memória Externa 0H

46 Memória Externa de dados Memória Interna de dados
Modelo da Memória de Dados Ex: A DPTR 16 bits Memória Externa de dados FFFFH Ex: A Port bits Ri bits Memória Interna de dados Ex: MOV A, #80H FFH FFH 128 bytes de RAM Interna IDATA Ex: A Registos especiais Internos DATA End. directo End. indirecto XDATA 80H 80H 7FH Ex: MOV 1, A 128 bytes de RAM interna IDATA 128 bytes de RAM interna DATA End. directo Ex: MOV 0H End. indirecto 0H Memória Física Partilhada pelos segmentos Data e IData

47 Memória de Dados Interna (endereços baixos)
7FH Uso geral 30H 7F 7E 7D 7C 7B 7A 78 2FH 79 Registos com manipulação ao bit BIT 07 06 05 04 03 02 20H 01 00 7FH 1FH 128 bytes de RAM interna (parte baixa) RB3 18H 17H RB2 10H 00H 0FH RB1 08H R7 07H R6 R5 R4 R3 RB0 R2 R1 R0 00H

48 Registos especiais internos
CCH1 – Compare/Capture reg1, high C3H P0 – Port 0 – acesso ao bit ou byte 80H CCL2 – Compare/Capture reg2, low C4H SP – Stack Pointer 81H CCH2 – Compare/Capture reg2, high C5H DPL – Data Pointer Low 82H CCL3 – Compare/Capture reg3, low C6H DPH – Data Pointer High 83H CCH3 – Compare/Capture reg3, high C7H PCON – Power Control 87H 80H FFH Registos especiais internos T2CON – Timer 2 Control C8H TCON – Timer Control 88H CRCL – Compare/Reload/Capture, low CAH TMOD –Timer Mode 89H CRCH – Compare/Reload/Capture, high CBH TL0 – Timer 0, low byte 8AH TL2 – Timer 2 , low byte CCH TL1 – Timer 1, low byte 8BH TH2 – Timer 2, high byte CDH TH0 – Timer 0, high byte 8CH PSW – Program Status Word – bit/byte D0H TH1 – Timer 1, high byte 8DH ADCON – A/D Converter Control – b/b D8H P1 – Port 1 – acesso ao bit e byte 90H ADDAT – A/D Converter Data D9H SCON –Serial Control- bit ou byte 98H DAPR – D/A Converter Program Reg. DAH SBUF – Serial buffer 99H P6 – Port 6 – acesso ao bit ou byte DBH P2 – Port 2 – acesso ao bit ou byte A0H Acc - Acumulator – bit ou byte E0H IEN0 – Interrupt Enable 0 – bit ou byte A8H P4 – Port 4 – acesso ao bit ou byte E8H IP0 – Interrupt Priority 0 A9H B – registo B F0H P3 – Port 3 – acesso ao bit ou byte B0H P5 – Port 5 – acesso ao bit ou byte F8H IEN1 – Interrupt Enable 1 – bit ou byte B8H IP1 – Interrupt Priority 1 B9H IRCON – Interrupt Request Control C0H CCEN – Compare/Capture Enable C1H CCL1 – Compare/Capture reg. 1, low C2H

49 Instruções do 80535

50

51

52

53 Algumas notas: Rn – qualquer registo desde R0 até R7. Ri – registos R0 e R1. Directo, directod, directof – RAM interna desde 00H até 7FH, Portos 0 a 6, qualquer registo de status e control. bit – qualquer dos 128 bits da memória interna, qualquer bit dos Portos, qualquer bit de status e control. A – acumulador. MX – posição de memória externa. MC – posição de memória de código (externa ou interna). relativo8 – valor a 8 bits entendido a código de complementos. endereço16 – valor a 16 bits entendido como um endereço. endereço11 – valor a 11 bits entendido como um endereço constante8 – valor a 8 bits.

54 Conjunto de instruções do 80535

55 Programas de Aplicação
Exercício 1: Faça um programa que determine o maior de 2 números do tipo byte. i. Os 2 números encontram-se respectivamente nas posições de memória externa 10H e 11H. O resultado deve ser colocado na posição de memória externa 12H. ii. Os 2 números encontram-se respectivamente nas posições de memória interna 10H e 11H. O resultado deve ser colocado na posição de memória interna 12H. Exercício 2: Faça um programa que inicializa uma tabela de 200 bytes. Cada byte da tabela deve ser inicializado com um valor igual ao do seu índice na tabela. Exercício 3: Faça um programa que simule uma Unidade Aritmética e Lógica com a funcionalidade ilustrada na seguinte tabela:

56 Resolução dos exercícios
Antes de se realizar um programa numa qualquer linguagem de programação seja ela de alto nível (Pascal, C, C++, Java) ou de baixo nível (Assembly51, Assembly86, etc), em primeiro lugar deve-se sempre realizar o algoritmo utilizando uma linguagem gráfica tipo fluxograma ou program graph e depois então implementar o algoritmo na linguagem de programação pretendida. Vamos então seguir esta estratégia na resolução dos nossos exercícios utilizando em primeiro lugar a linguagem gráfica program graph para a descrição do algoritmo e em segundo lugar então proceder-se-á à codificação do algoritmo em Assembly51. Programa1_i SEGMENT CODE RSEG Programa1_i Maiorde2: MOV DPTR, #10H MOVX MOV R7, A ; R7 = (10H) INC DPTR MOVX ; A= (11H) CLR C SUBB A, R7 ; A= (11H)-(10H) JNC ELSE_IF THEN_IF: MOV A, R7 A SJMP FIM_IF ELSE_IF: ADDC A, R7 ; repor o valor de A FIM_IF: SJMP $ ; forever: fim do programa Exercício 1: (10H) > (11H) (12H) = (11H) F T (12H) = (10H)

57 O programa anterior introduz algumas directivas novas do programa tradutor ASM51.EXE que passaremos a descrever. SEGMENT – define um segmento ou um troço de código ou dados consoante seja do tipo CODE ou XDATA e IDATA. A sintaxe é: NOME SEGMENT TIPO_SEGMENT No qual, NOME – identifica um conjunto de caracteres ou dígitos começados por letra. TIPO_SEGMENT – pode ser uma das seguintes palavras chave: CODE : identifica o segmento como de código. XDATA : o segmento é de dados (DATA) existentes na memória externa ao CPU. IDATA: as variáveis aqui definidas existem na memória interna do CPU. RSEG – define o segmento como relocalizavel, ou seja, o endereço do código ou das variáveis definidas num segmento com este atributo não têm um endereço absoluto. RSEG NOME Esclarecidas as directivas e analisado o programa “Programa_i” é fácil verificar que este poderia ser optimizado pois as instruções finais do THEN_IF e do ELSE_IF são comuns logo poderiam ser retiradas e passadas para o ramo FIM_IF. Exercício: Optimize o código do Programa1_i.

58 A solução para o exercício 1.ii é dado pelo programa “Programa1_ii”.
Programa1_ii SEGMENT CODE RSEG Programa1_ii Maiorde2: MOV A, 11H CLR C SUBB A, 10H ; A= (11H)-(10H) JNC ELSE_IF THEN_IF: MOV 12H, 10H SJMP FIM_IF ELSE_IF: MOV 12H, 11H FIM_IF: SJMP $ ; forever: fim do programa A solução para o exercício 1.ii é dado pelo programa “Programa1_ii”. O código é consideravelmente mais simples e mais fácil de perceber. Assim, sempre que se puder utiliza-se variáveis definidas na memória interna. Tendo realizado os programas anteriores,como é que se poderá testá-los no kit SAB Em primeiro lugar tem de se saber aonde está disponível a memória de desenvolvimento RAM. Sem grandes preocupações dos porquês, admita que a RAM está disponível entre os endereços 0000H e 7FFFH. Pois, em breve iremos estudar a arquitectura interna do kit SAB80535. Em segundo lugar terá de se perceber o ambiente de desenvolvimento a utilizar, que é composto por um qualquer editor de texto e pelos programas ASM51, L51 e OH51. A seguinte figura pretende elucidar os passos requeridos até termos produzido um ficheiro com extensão .HEX que deve ser carregado na memória do kit.

59 Editor de Texto A51.EXE L51.EXE OHS51.EXE Módulo Principal Módulos
FICHEIRO.LST FICHEIRO.M51 Editor de Texto FICHEIRO.HEX Há erros Há erros FICHEIRO.A51 FICHEIRO.OBJ A51.EXE L51.EXE OHS51.EXE FICHEIRO FICHEIRO1.A51 FICHEIRO1.OBJ Módulo Principal Módulos FICHEIRON.A51 FICHEIRON.OBJ

60 Segue-se uma breve explicação da figura anterior
Segue-se uma breve explicação da figura anterior. Em primeiro lugar deve-se criar um ou mais ficheiros de texto com extensão .A51 utilizando um editor. De seguida, deve-se utilizar o programa tradutor e analisador sintáctico ASM51.EXE de Assembly51 para gerar os ficheiros com extensão .OBJ e .LST, a sintaxe é: A51 FICHEIRO.A51 DEBUG Depois de gerarmos todos os ficheiros .OBJ sem erros sintácticos vamos agrupar todos os segmentos existentes nos nossos ficheiros assim como localizá-los em determinados endereços. Um exemplo possível é o seguinte comando: L51 FICHEIRO.OBJ, FICHEIRO1.OBJ, ..., FICHEIRON.OBJ XDATA(2000H) IDATA(30H) CODE(4000H) DATA(60H) BIT(00H) DEBUGSYMBOLS aonde se está a agrupar todos os segmentos XDATA, IDATA e CODE existentes nos n+1 ficheiros do comando. Cada segmento tem como endereço inicial o valor hexadecimal dado como parâmetro. Assim, o segmento XDATA começa em 2000H, IDATA em 30H, CODE em 4000H, etc. De notar que este comando é apenas um exemplo possível, podendo-se variar os valores de inicio de cada segmento. Este comando gera um ficheiro sem extensão com nome igual ao do primeiro ficheiro dado como parâmetro. Por fim para gerarmos o ficheiro com extensão .HEX temos de executar o comando: OHS51 FICHEIRO terminando-se então todos os passos necessários para se obter um ficheiro que se pode carregar na memória RAM do kit

61 Exercício 2: Voltemos aos exercícios:
Resolução com a estrutura algorítmica DO WHILE – a tabela tem de ter pelo menos 1 elemento. i= 0 i= i+1 Tabela[i]= i iDIM T F ; tabela existente na memória externa Programa2 SEGMENT CODE RSEG Programa2 IniciarTabela: MOV DPTR, #Tabela CLR A LOOP: A INC DPTR INC A CJNE A, #DIM, LOOP SJMP $ Dados SEGMENT XDATA RSEG Dados DIM EQU 200 Tabela: DS DIM END ; tabela existente na memória interna Programa2 SEGMENT CODE RSEG Programa2 IniciarTabela: MOV R0, #Tabela CLR A LOOP: MOV @R0, A INC R0 INC A CJNE A, #DIM, LOOP SJMP $ Dados SEGMENT IDATA RSEG Dados DIM EQU 200 Tabela: DS DIM END

62 Exercício 2: Resolução com a estrutura algorítmica WHILE DO – a tabela pode ter 0 ou mais elementos. i= 0 i= i+1 Tabela[i]= i iDIM T F ; tabela existente na memória interna Programa2 SEGMENT CODE RSEG Programa2 IniciarTabela: MOV R0, #Tabela CLR A CONDICAO: CJNE A, #DIM, DO SJMP FIM_WHILE DO: MOV @R0, A INC R0 INC A SJMP CONDICAO FIM_WHILE: SJMP $ Dados SEGMENT IDATA RSEG Dados DIM EQU 200 Tabela: DS DIM END ; tabela existente na memória externa Programa2 SEGMENT CODE RSEG Programa2 IniciarTabela: MOV DPTR, #Tabela CLR A CONDICAO; CJNE A, #DIM, DO SJMP FIM_WHILE DO: A INC DPTR INC A SJMP CONDICAO FIM_WHILE: SJMP $ Dados SEGMENT XDATA RSEG Dados DIM EQU 200 Tabela: DS DIM END

63 Exercício 3: Case S2 S1 S0 000 001 010 011 100 101 110 R= Op1 + Op2
R= Op1 x Op2 R= Op1 / Op2 R= 0 R= Op1 . Op2 R= Op1 or Op2 ; tabela existente na memória interna Programa3 SEGMENT CODE RSEG Programa3 ALU: MOV A, Operacao ANL A, #07H ; multiplicar por 2= sizeof(SJMP) RL A MOV DPTR, #TAB_JMP FIM_CASE: MOV RES, A SJMP $ TAB_JMP: SJMP SOMAR SJMP SUBTRAIR SJMP MULTIPLICAR SJMP DIVIDIR SJMP ZERO SJMP INTERSECCAO SJMP UNIAO SJMP NEGACAO ; continua ; continuação SOMAR: MOV A, Op1 ADD A, Op2 SJMP FIM_CASE SUBTRAIR: CLR C SUBB A, Op2 MULTIPLICAR: MOV B, Op2 MUL AB DIVIDIR: DIV AB ZERO: MOV A, #0 ; continuação INTERSECCAO: MOV A, Op1 ANL A, Op2 SJMP FIM_CASE UNIAO: ORL A, Op2 NEGACAO: CPL A Dados SEGMENT DATA RSEG Dados Operacao: DS 1 Op1: DS 1 Op2: DS 1 RES: DS 1 END 111 R= Op1/

64 Distinção entre o módulo principal e os restantes módulos
No módulo principal deve-se colocar no início a directiva, $TITLE (‘ Texto do título do programa’) Nos módulos secundários deve-se usar a directiva NAME para sua identificação, NAME Nome_do_Módulo Em termos práticos o módulo principal deve conter sempre o entry point ou o endereço inicial do programa. Quando se programa utilizando módulos, tem de se indicar em cada módulo as funções ou variáveis que estão definidas noutros módulos e que são utilizadas no módulo corrente. Para isso, utiliza-se a directiva EXTRN que tem a seguinte sintaxe: EXTRN segmento(lista de símbolos) Exemplo: EXTRN CODE(write_string, read_string) Estamos a indicar que no módulo corrente vamos utilizar duas funções chamadas write_string e read_string que pertencem ao segmento CODE e que estão definidas noutro módulo. Para que estas directivas não indiquem erro no processo de ligação a executar pelo programa l51.exe então deve-se nos módulos aonde estas funções estão definidas utilizando a directiva PUBLIC que tem a seguinte sintaxe, PUBLIC símbolo [, símbolo, ...] No nosso exemplo teríamos que definir, PUBLIC write-string, read_string

65 . Definição de stack no 80535 O segmento de stack é localizado na RAM interna. Este é usado para guardar o Program Counter (PC) durante as chamada e retorno de funções e rotinas de interrupção. As leituras e escritas no stack são controladas por um registo chamado stack pointer (SP) que faz uma gestão First In Last Out (FILO) ou Last In First Out (LIFO) do segmento de stack. O SP é um registo interno de 8 bits que contém o endereço do último byte a ser guardado no stack. É responsabilidade do programador que o SP tenha um valor válido no espaço de endereçamento do stack (menor que 80H) e que este espaço não é utilizado para outras variáveis. Um exemplo da definição do stack e correspondente inicialização do registo SP poderá ser, DSEG AT 40H ; DATA SEGMENT identifica a memória interna de 00H-7FH DIM_PILHA EQU 40H pilha: DS DIM_PILHA . codigo SEGMENT CODE RSEG codigo MOV SP, #pilha-1 ; porque o primeiro incrementa o SP e depois é que guarda De notar que após uma acção de reset ao CPU o SP fica com o valor 7 que coincide com o R7 do banco 0.

66 Convenção para os Parâmetros de Saída de Funções
Convenção para Parâmetros de Entrada passados por Registo Tipo char, 1 byte pointer int, word pointer long, float Pointer 1º parâmetro R R6, R R4-7 R1, R2, R3 2º parâmetro R R4, R R1, R2, R3 3º parâmetro R R2, R3 Quando o nº de parâmetros excede os indicados na anterior tabela então os parâmetros são passados em posições de memória fixas. Convenção para os Parâmetros de Saída de Funções Valor devolvido Registo Significado Bit Carry Char R7 Int R6, R7 R6 (MSB) e R7 (LSB) Long R4-7 R4 (MSB) e R7 (LSB) Float R4-7 R4(MSB) e R7 (LSB) Pointer R1, R2, R3 R1 – Tipo de memória, R2 – High Offset, R3 – Low Offset Tipo de memória: 1 – IDATA ; 2 – XDATA ; 3 – PDATA ; 4 – DATA ; 5 – CODE Exercício i: Realize uma função que recebe como parâmetro de entrada o endereço de uma StringC residente em XDATA faça a visualização da stringC na consola. Assuma que dispõe de uma função CO com um parâmetro de entrada tipo char e que faz a visualização do carácter na consola. ii. Generalize o parâmetro de entrada de modo à stringC poder residir em qualquer memória.

67 Exercício iii : Faça uma função Read_String que tem como parâmetro de entrada o endereço da StringC a ser afectada com o conjunto de caracteres introduzidos pelo teclado. Assuma que tem uma função CI que devolve um valor tipo char correspondente ao último carácter introduzido pelo teclado. Registo de flags Este registo no chama-se Program Status Word (PSW) e pode ser acedido em termos de bits ou ao byte. A informação que contém é a seguinte: D7H D6H D5H D4H D3H D2H D1H D0H Endereços de acesso ao bit PSW CY AC F0 RS1 RS0 OV F1 P D0H Endereço do acesso ao byte Significado das flags CY – carry AC – auxiliary carry F0 e F1 – a usar pelo utilizador OV – overflow P - paridade par RS1 RS0 - Bits de selecção do banco de registos R0-7. banco 0, endereços entre 00H e 07H banco 1, endereços entre 08H e 0FH banco 2, endereços entre 10H e 17H banco 3, endereços entre 18H e 1FH

68 Portos paralelo de entrada e saída
O CPU disponibiliza 48 sinais digitais de entrada/saída agrupados em 6 portos bidireccionais de 8 bits cada. Os acessos de leitura ou escrita a estes 6 portos são realizados através dos registos de função especial P0 a P5. Genericamente, cada bit consiste num flip-flop tipo D, num driver de saída e num buffer tri-state de entrada, tal como se ilustra na seguinte figura: Q Q/ D Escrita driver de saída Pino do porto Ler Pino Ler flip.flop Bus Interno R N-FET Circuito genérico associado a cada bit dos portos de entrada/saída.

69 Portos 0 e 2 Os drivers de saída dos portos 0 e 2 são diferentes pois também são usados para acesso à memória externa tal como se exemplifica na seguinte figura: N-FET 1 S Y Q/ bit de endereço/dados controlo Pino do porto Driver de saída do porto 0. R N-FET 1 S Y Q bit de endereço controlo Driver de saída do porto 2. Pino do porto O porto 0 multiplexa no tempo a parte baixa do endereço A0-7 com o byte a ser lido ou escrito D0-7, enquanto que o porto 2 define a parte alta do endereço A8-15 sempre que o endereçamento é a 16 bits. Caso contrário, em ambos os portos aparece o conteúdo do registo correspondente.

70 Portos 1 e 3 Os portos 1 e 3 são multifuncionais, ou seja, permitem implementar além da funcionalidade de entrada/saída incondicional também os seguintes sinais de entrada/saída alternativos: Porto 1 Pino Função Alternativa P1.0 /INT3/CC0 Entrada da interrupção 3; Entrada de captura 0; Saída de comparação 0. P1.1 INT4/CC1 Entrada da interrupção 4; Entrada de captura 1; Saída de comparação 1. P1.2 INT5/CC2 Entrada da interrupção 5; Entrada de captura 2; Saída de comparação 2. P1.3 INT6/CC3 Entrada da interrupção 6; Entrada de captura 3; Saída de comparação 3. P1.4 /INT2 Entrada da interrupção 2. P1.5 T2EX Entrada de reload trigger do timer 2. P1.6 CLKOUT Clock de saída. P1.7 T2 Entrada externa de clock do contador realizado com o timer 2. Porto 3 P3.0 RXD Entrada série de dados em modo assíncrono; entrada/saída série de dados em modo síncrono. P3.1 TXD Saída série de dados em modo síncrono; saída de clock em modo síncrono. P3.2 /INT0 Entrada da interrupção 0; gate de controlo do timer 0. P3.3 /INT1 Entrada da interrupção 1; gate de controlo do timer 1. P3.4 T0 Entrada externa de clock do contador realizado com o timer 0. P3.5 T1 Entrada externa de clock do contador realizado com o timer 1. P3.6 /WR Sinal de escrita (write/) para a memória externa. P3.7 /RD Sinal de leitura (read/) para a memória externa.

71 Sinal de saída alternativo
Portos 1 e 3 (continuação) O circuito completo correspondente a um bit de entrada/saída dos portos 1 e 3 é mostrado na seguinte figura: Q Q/ D Escrita driver de saída Pino do porto Ler Pino Ler flip.flop Bus Interno R N-FET Sinal de entrada alternativo Sinal de saída alternativo Após Reset todos os flip-flops dos portos 1 a 5 ficam com o valor lógico 1. Porto 6 Na versão MYMOS do CPU80535 o porto 6 define uma interface analógica para o conversor analógico-digital. Na versão ACMOS além da interface analógica também se pode definir entradas digitais. Nesta versão, o porto pode ser tratado como um porto de entrada digital cujo endereço é P6 (0DBH).

72 Características Eléctricas dos Portos de Entrada/Saída
Os drivers de saída dos portos 1 a 5 podem ser ligados directamente a entradas TTL pois as suas características eléctricas garantem valores lógicos correctos. Para mais informações deve consultar o data sheet do SAB80535 e verificar na secção de características DC os valores correspondentes aos parâmetros VOL e VOH. Os bits do porto 0 quando programados como saída também partilham esta característica. No entanto quando programados como entrada é necessário ligar-lhes pullups para estas não ficarem em alta impedância. A excepção é feita quando os bits do porto 0 estão sendo usados como sinais de endereço ou de dados (address/data bus). Instruções Read-Modify-Write dos portos 0 a 5 Todas as instruções, que têm como destino um porto de entrada/saída ou um bit de um porto, lêem em primeiro lugar o flip-flop de informação correspondente ao bit e não a entrada física do porto. Por exemplo, a instrução ORL P4, #99H lê a informação presente nos 8 flip-flops (latch do porto), modifica o valor e coloca o resultado no latch. Às instruções que em primeiro lugar lêem um ou mais flip-flops depois modificam o seus valores consoante a operação e finalmente actualizam os respectivos flip-flops são chamadas instruções de read-modify-write. Estas instruções são as seguintes: ANL ex: ANL P4, #01H ORL ex: ORL P3, A XRL ex: XRL P5, #0FFH JBC ex: JBC P3.1, LOOP CPL ex: CPL P3 INC ex: INC P5 DEC ex: DEC P5 DJNZ ex: DJNZ P4, LOOP MOV ex: MOV P3.2, C CLR ex: CLR P3.1 SETB ex: SETB P4.0 De salientar que este conjunto de instruções não lê os valores presentes nos pinos físicos dos portos mas sim os respectivos flip-flops.

73 Flat-Cables SD535 Flat-Cable SD535 Flat-Cable RAM ROM CPU P1 P2
P0.0 AD0 P0.1 AD1 P0.2 AD2 P0.3 AD3 P0.4 AD4 P0.5 AD5 P0.6 AD6 P0.7 AD7 NC AN6 AN7 VCC GND 1 40 2 39 3 38 4 37 5 36 6 35 7 34 8 33 9 32 10 31 11 30 12 29 13 28 14 27 15 26 16 25 17 24 18 23 19 22 20 21 A0 A1 A2 A3 A4 A5 A6 A7 A8 P2.0 A9 P2.1 A10 P2.2 A11 P2.3 A12 P2.4 A13 P2.5 A14 P2.6 A15 P2.7 PSEN ALE P3.6 WR/ P3.7 RD/ P1 1 20 21 40 RAM ROM Flat-Cable SD535 P2 1 20 21 40 P4.4 P4.5 P4.6 P4.7 P5.0 P5.1 P5.2 P5.3 P5.4 TXM P5.5 RXM P5.6 SELM P5.7 AN0 AN1 AN2 AN3 AN4 AN5 VCC GND 1 40 2 39 3 38 4 37 5 36 6 35 7 34 8 33 9 32 10 31 11 30 12 29 13 28 14 27 15 26 16 25 17 24 18 23 19 22 20 21 P1.0 P1.1 P1.2 P1.3 P1.4 P1.5 P1.6 P1.7 P3.0 RxD P3.1 TxD P3.2 INT0/ P3.3 INT1/ P3.4 T0 P3.5 T1 P3.6 WR/ P3.7 RD/ P4.0 P4.1 P4.2 P4.3 Flat-Cable CPU

74 Técnicas para a Descodificação de Endereços num Microsistema
No projecto hardware de microsistemas tem particular importância a atribuição de endereços aos módulos de memória que partilham os barramentos de endereços e dados estabelecidos pelo CPU. Assim, devemos saber qual a dimensão (nº de bits) dos barramentos de endereço e de dados do CPU para depois podermos começar o projecto hardware do microsistema. Os módulos de memória são caracterizados pela dimensão (nº de palavras), pela dimensão das palavras (nº de bits) e por serem memória volátil (RAM) ou não volátil (ROM, PROM, EPROM, EEPROM). De notar que a memória não volátil de um microsistema deve incluir sempre o endereço de reset do CPU. As técnicas de descodificação de endereços num microsistema que iremos estudar são as seguintes: Paginação Atribuição contígua Selecção linear Paginação Esta técnica de endereçamento que é talvez a mais utilizada no desenvolvimento de microsistemas consiste em dividir o espaço de endereçamento total do CPU, definido pelo seu número de bits de endereçamento externo, em blocos de endereços de semelhante dimensão que são designadas por páginas. Nas páginas inserem-se os dispositivos de memória do microsistema e a dimensão de cada página deve ser concordante com a dimensão do dispositivo de memória nela inserido. Por exemplo, num CPU que tem um barramento de endereços de 16 bits, a organização de páginas é criada pensando no endereçamento total de 64K, dividido em 2 páginas de 32K pelo bit de endereçamento externo A15 e depois cada página de 32K pode ser dividida em 2 páginas de 16K pelo bit A14, e assim sucessivamente até obtermos páginas com a dimensão desejada. Esta técnica pode ser ilustrada na seguinte figura: 64K A15 1 32K Dimensão das páginas Bits de endereçamento externo A14 1 1 16K A13 1 1 1 1 8K Bits responsáveis pela selecção de um dispositivo de memória.

75 Exemplo: Projecte um circuito descodificador de endereços para um microsistema baseado num CPU de 8 bits com 16 bits de endereços que contém os dispositivos de memória apresentados no seguinte mapa de memória: EPROM 8K RAM 8K Não utilizado 0000H FFFFH 2000H 6000H 4000H 1FFFH 3FFFH 5FFFH End. inicial End. final Solução: E S1 S0 1 2 3 A15 A14 A13 CSEPROM/ CSRAM#1/ CSRAM#2/ Circuito descodificador de endereços Exercício: Acrescente ao microsistema anterior 2 RAMs de 4K cada.

76 Atribuição Contígua RAM 8K
Esta técnica utiliza-se quando se pretende introduzir um dispositivo de memória de pequena dimensão contiguamente a outros dispositivos de memória com dimensão muito superior. Por exemplo, suponha que pretende realizar o circuito de descodificação de endereços para o seguinte mapa de memória: EPROM 8K ROM 256 RAM 8K 0000H 1FFFH 20FFH 40FFH 2000H 2100H 4100H Não utilizado Solução: Os sinais de selecção da EPROM e ROM são obtidos descodificando os bits de endereçamento externo de cada dispositivo. A solução sem atribuição repetitiva de endereços (chamada tecnicamente de foldback), será: CSEPROM/= A15 + A14 + A13 CSROM/= A15 + A14 + A13/ + A12 + A11 + A10 + A9 + A8 O problema está na realização do CSRAM/ pois a RAM é um dispositivo com 8K que se encontra localizado no início de uma página de 256. Assim sendo, a única solução é relocalizar o dispositivo em termos de endereço. Como? Utilizando um subtractor que permita passar o valor do endereço inicial definido pelos 8 bits de maior peso do endereço cujo valor é 21H para o valor 00H, endereçando-se à RAM com os bits do resultado, como o apresentado na seguinte figura: X0-7 Y0-7 8 S0-7 BW A8-15 21H RAM 8K A8-A12 A0-A7 D0-D7 CS/ OE/ WE/ A’8-15 5 A’8-12 3 A’13-15 WR/ RD/ Nota: O BW do subtractor entra na geração do CS/ da RAM e de eventuais dispositivos de memória que se venham a acrescentar ao sistema para distinguir estes dispositivos dos dispositivos que não são afectados com o problema de estarem inseridos em páginas com dimensão inferior à sua (ex: EPROM e ROM). 8

77 Selecção Linear Este método de endereçamento consiste em activar o CS/ de cada dispositivo com um bit de endereçamento externo. Este método tem como grande vantagem a simplificação do hardware para a descodificação de endereços. Embora também apresente graves desvantagens tais como: 1. A não contiguidade de endereços atribuídos a cada dispositivo; 2. Limitação no número de dispositivos a inserir que são tantos quantos os bits de endereçamento externo; 3. As instruções do CPU para acesso à memória externa ficam passíveis de seleccionar vários dispositivos simultaneamente. De referir que este tipo de endereçamento utiliza-se em sistemas vocacionados numa fase posterior ao seu desenvolvimento com o objectivo de reduzir os custos de produção. Exemplo: Suponha um microsistema baseado num CPU com 16 bits de endereço e que dispõe de dispositivos RAM e EPROM de 4K. Realize o circuito de descodificação de endereços para o microsistema inserindo o número máximo de dispositivos de memória possível utilizando a técnica de selecção linear. Bits de endereçamento interno: A0-11 Bits de endereçamento externo: A12-15. Deste modo, o número máximo de dispositivos permitido são 4. Cada um deles é seleccionado com cada um dos bits de endereço entre A12 e A15. CSRAM#1/ = A12 CSRAM#2/ = A13 CSRAM#3/ = A14 CSEPROM#1/ = A15 Para esta solução ser correcta na prática o CPU deveria de ter um endereço de reset entre 7000H e 7FFFH. Mapa de memória resultante 7000H EPROM#1 4K 7FFFH B000H RAM#1 4K BFFFH D000H RAM#2 4K DFFFH E000H RAM#3 4K EFFFH

78 Temporizações e formas de onda do 80535
Clock de saída para fXTALL=12MHz tCLCX min=20ns tCLCH max=20ns 83,3ns tCLCL tCHCX CLKout tCHCL Acesso às memórias de código e dados [TCLCL-40, TCLCL+40] ALE 7.TCLCL-40ns 7.TCLCL-40ns CLKout 10.TCLCL-40ns 2.TCLCL-40ns PSEN/ RD/ , WR/ Acesso à memória de código Acesso à memória de dados

79 Ciclo de read sobre a memória de código
ALE PSEN/ Port 0 A0-A7 Instrução A0-A7 Port 2 A8-A15 A8-A15 Ciclo de read sobre a memória de dados ALE PSEN/ RD/ Port 0 Dados A0-A7 de PCL A0-A7 de Ri ou DPL Instrução Port 2 P2.0-P2.7 ou A8-A15 de DPH A8-A15 de PCH

80 Ciclo de write sobre a memória de dados
ALE PSEN/ WR/ Port 0 A0-A7 de PCL A0-A7 de Ri ou DPL Dados Instrução Port 2 P2.0-P2.7 ou A8-A15 de DPH A8-A15 de PCH Oscilador recomendado com cristal C XTAL1 C= 30pF ±10pF 12MHz XTAL2 C

81 Circuito de reset para o 80535 com botão
C= [4.7uF, 10uF] Treset= 24xTosc R reset/ C + O conteúdo dos registos mais utilizados após e durante a acção de reset são os seguintes: PC= 0000H SP= 07H P0-P5= 0FFH DPTR= 0000H ACC= 00H B= 00H PSW= 00H Exercício 1: Projecte um microsistema baseado no com 32Kbytes de RAM para dados e código, e com 32Kbytes de EPROM só para código. Utilize na resolução do exercício 1 RAM 62256(32Kx8) e 1 EPROM 27256(32Kx8). Exercício 2: Projecte um microsistema baseado no com 32Kbytes de RAM e 32Kbytes de EPROM mas tendo as seguintes particularidades: 1. após reset o CPU só consegue aceder à EPROM que está localizada em qualquer endereço externo para acesso a código; 2. após ter-se acedido a um qualquer endereço com o bit A15=1 o microsistema deve passar a localizar a RAM no endereço base 0000H e a EPROM no endereço base 8000H. A EPROM deve continuar a aceitar acesso só para código e a RAM deve permitir acesso tipo código e dados.

82 Resolução do exercício 1, especificando os barramentos de endereços e dados e os sinais de controlo necessários: CPU80535 RAM62256 A0-14 CS/ OE/ WE/ D0-7 EPROM27256 P0.0-7, AD0-7 ALE P2.0-7, A8-15 PSEN/ P3.7, RD/ P3.6, WR/ LATCH Q0-7 EN A0-15 16 A0-7 8 DB0-7 A8-15 15 A15

83 Resolução do exercício 2:
RAM62256 8 DB0-7 8 D0-7 CPU80535 LATCH WE/ P0.0-7, AD0-7 D0-7 OE/ ALE EN 15 A0-14 Q0-7 A0-7 8 A0-14 P2.0-7, A8-15 8 A8-15 A0-15 CS/ 16 EPROM27256 P3.6, WR/ 8 A15 D0-7 P3.7, RD/ PSEN/ OE/ 15 A0-14 A0-14 A15 CS/ RESET/ Reset R S/ C R/

84 Temporizadores e Contadores do 80535
O tem três temporizadores/contadores (em linguagem anglo-saxónica são designados por timer/counters) de 16 bits chamados timer 0, timer 1 e timer 2. Timer 0 e 1 Estes dois dispositivos podem funcionar ou como temporizadores(timers) ou como contadores de eventos (counters). Quando em modo temporizador (timer), o registo é incrementado em todos os ciclos máquina. Cada ciclo máquina inclui 12 ciclos de clock, o que para um clock de 12MHz implica incrementar o registo em todos os 1uS. Quando em modo contador, o registo é incrementado nas transições descendentes existentes nos pinos de entrada T0(P3.4) e T1(P3.5). Neste modo a entrada externa é amostrada durante o ciclo de clock número 11 de todos os ciclos máquina. Quando os testes exibem o valor 1 num ciclo e o valor 0 no ciclo seguinte, o contador é incrementado. O novo valor aparece no registo durante o terceiro ciclo de clock do próximo ciclo máquina. Como este demora 2 ciclos máquina a reconhecer uma transição descendente então o ritmo máximo de contagem é de 1/24 a frequência do oscilador ou seja de 2uS. Cada temporizador consiste de dois registos de 8 bits cada. TH0 (parte alta) e TL0 (parte baixa) para o timer 0. TH1 e TL1 para o timer 1. As funções de cada um dos timers são controladas através dos registos TCON e TMOD como se pode constatar nas seguintes figuras e descrições: Endereço ao nível do bit 8FH 8EH 8DH 8CH TF1 TR1 TF0 TR0 TCON (88H) TR0 – Se a 1 permite o timer 0 contar. Caso contrário o timer está parado. TF0 – Timer 0 overflow flag. Esta flag é colocada a 1 sempre que há overflow no processo de contagem do timer, ou seja, quando o registo do temporizador passa de tudo a 1’s para tudo a 0’s. Esta flag é colocada a 0 por hardware quando o processador é vectorizado para a rotina de atendimento desta interrupção. TR1 – idem TR0 mas agora para o timer 1. TF1 – idem TF0 mas agora para o timer 1.

85 Timer 0 e 1 (continuação) timer 1 timer 0 GATE C-T/ M1 M0 GATE C-T/ M1
TMOD (89H) timer 1 timer 0 GATE – Quando a 1 permite contagem sempre que o pino INTX/ e o bit TRX estão ambos a 1. C-T/ – Se a 1 fica em modo contador com a entrada no pino TX. Quando a 0 funciona em modo temporizador. M1 M0 0 0 - Modo 0: temporizador/contador de 13bits. O THX funciona como contador de 8 bits de maior peso e o TLX serve como contador de 5 bits de menor peso. 0 1 - Modo 1: temporizador/contador de 16 bits. O THX funciona como contador de 8 bits de maior peso e TLX como contador de 8 bits de menor peso. 1 0 - Modo 2: temporizador/contador de 8 bits recarregável. THX contém um valor que é atribuído a TLX sempre que neste ocorre o overflow de contagem. 1 1 - Modo 3: timer 0: TL0 funciona como um temporizador/contador de 8 bits controlado pelos bits de controlo destinados ao timer 0. TH0 funciona como um temporizador de 8 bits comandado pelos bits de controlo destinados ao timer 1. 1 1 - Modo 3: timer 1: Não funciona.

86 Timer 0 e 1 (continuação) Modo 0 OSC Modo 1 OSC ÷12 Y TLx (5 bits) THx
Contador de 13 bits Y TLx (5 bits) THx (8 bits) Interrupção timer x TFx Tx 1 S f ÷32 “Prescaler” C-T/ TRx GATE INTx/ Modo 1 OSC ÷12 Y TLx (8 bits) THx (8 bits) Interrupção timer x TFx Tx 1 S Contador de 16 bits C-T/ TRx GATE INTx/

87 Modo 2 OSC Modo 3 OSC ÷12 Tx 1 S Y C-T/ TRx GATE INTx/ TLx (8 bits)
1 S Y C-T/ TRx GATE INTx/ TLx (8 bits) THx TFx Interrupção timer x PL Modo 3 OSC ÷12 Y TL0 (8 bits) Interrupção timer 0 TF0 T0 1 S C-T/ TR0 GATE INT0/ TH0 (8 bits) Interrupção timer 1 TF1 TR1

88 Comunicação Série A comunicação série consiste no envio e recepção, de forma sincronizada, de 1 bit de informação em cada intervalo temporal designado por tempo de bit (bit time). O nível lógico 1 costuma designar-se por “mark” e o nível lógico 0 designa-se por “space”. A nível físico, os dois níveis lógicos correspondem a níveis de tensão diferentes ou à existência ou não de corrente na linha. Por exemplo, apresenta-se de seguida alguns protocolos para comunicação série e os respectivos níveis de tensão: Em termos históricos, o primeiro protocolo adoptado foi o “loop de corrente de 40mA” que se adaptava perfeitamente às características electromecânicas dos dispositivos da época, em que os relés eram directamente excitados pela corrente de 40mA pulsando ao ritmo de mark-space dos sinais enviados. A conversão de níveis de tensão TTL para RS232 são realizados através de circuitos SSI desenvolvidos para o efeito, caso dos MC1488 e MC1489 da Motorola e os e da Texas Instruments. Usualmente costuma dizer-se que existem dois tipos de comunicação série, a comunicação síncrona e a comunicação assíncrona. Dentro da classe da comunicação síncrona englobam-se os protocolos Bisync, SDLC, HDLC, etc. Na classe da comunicação dita assíncrona temos o protocolo start-stop bit. É sobre este protocolo que vamos dirigir o nosso estudo. A comunicação designa-se assíncrona quando existe unicamente sincronismo ao nível do bit. Enquanto se houver sincronismo ao nível do carácter e da trama(conjunto de caracteres) a comunicação designa-se por síncrona.

89 Protocolo start-stop bit
A composição deste protocolo é ilustrada na seguinte figura: Espera(idle) Mark Space D0 D1 D2 D3 D4 P Start bit dados Stop bit Bit de paridade O bit de paridade pode ou não existir. Caso exista, a paridade designa-se por “par” quando o número de bits de dados mais o bit de paridade têm no conjunto um número par de 1’s. O inverso designa-se por paridade “ímpar”. Na comunicação série assíncrona o débito da comunicação ou o número de bits por segundo designa-se por baud rate. Os valores usuais deste parâmetro são: 110, 300, 600, 1200, 2400, 4800, 9600, 14400(14K), 28800(28K), 57600(56K). Exercício 1: Realize uma rotina para transmissão de dados série através do bit P5.6. Esta rotina tem como parâmetro de entrada o carácter a enviar. Exercício 2: Realize uma rotina para recepção de dados série através do bit P5.7. Esta rotina tem como parâmetro de saída o carácter recebido.

90 Resolução do exercício 1:
; o carácter a enviar é passado no registo R7 ; comunicação série: 1 start bit, 8 data bits, 1 Stop bit ; baud rate= 1/ Delay_1_bit_time CO: CLR P5.6 ; start bit LCALL DELAY_1_BIT_TIME MOV R0, #8 TX_DATA_BITS: MOV A, R7 RRC A MOV R7, A MOV P5.6, C ; 8 data bits DJNZ R0, TX_DATA_BITS SETB P5.6 ; stop bit RET ; o carácter a receber é devolvido no registo R7 ; comunicação série: 1 start bit, 8 data bits, 1 Stop bit ; baud rate= 1/ Delay_1_bit_time CI: JB P5.7, $ ; espera start bit LCALL DELAY_MEIO_BIT_TIME JB P5.7, CI ; falso start bit MOV R0, #8 RX_DATA_BITS: LCALL DELAY_1_BIT_TIME MOV C, P5.7 ; 8 data bits MOV A, R7 RRC A MOV R7, A DJNZ R0, RX_DATA_BITS JNB P5.7, CI ; falso stop bit RET Só falta fazer as funções de delay referidas nas anteriores funções. Então, como se faz um delay com um mínimo de rigor temporal? Em primeiro lugar, temos de recordar que um ciclo máquina no para uma frequência de 12MHz demora 12 ciclos de clock ou seja 1uS. Em segundo lugar tem de se repetir tantos uS ou ciclos máquinas quantos os desejados. Por exemplo se quiséssemos receber e transmitir com um baud rate= 9600, isto significa que cada bit tem de permanecer durante 1/9600= 104uS, daí o bit time= 104us e meio bit time= 52uS. Assim, a estrutura das rotinas será idêntica à parte de uma valor constante que chamaremos respectivamente UM_BIT_TIME e MEIO_BIT_TIME. DELAY_1_BIT_TIME: MOV R0, #UM_BIT_TIME NOP DJNZ R0, $ RET DELAY_MEIO_BIT_TIME: MOV R0, #MEIO_BIT_TIME NOP DJNZ R0, $ RET Cálculo das constantes: T(LCALL) + T(MOV) + T(NOP) + UM_BIT_TIME*T(DJNZ) + T(RET) = 104  UM_BIT_TIME*2 + 2= 104  UM_BIT_TIME= 49  MEIO_BIT_TIME 24

91 Modo 0 – Canal Síncrono de 8 bits
Comunicação série no 80535 Este CPU disponibiliza internamente um canal série que suporta comunicação série bidireccional em simultâneo (dita tecnicamente comunicação em full duplex). Este canal de comunicação pode funcionar em 4 modos diferentes, um modo síncrono e três modos assíncronos como se irá descrever de seguida Modo 0 – Canal Síncrono de 8 bits Neste modo de funcionamento os bits de dados são transmitidos e recebidos através do sinal RxD. O sinal TxD opera como saída, aonde é definido o clock do ritmo da transferência de informação. O campo de dados é de 8 bits e o baud rate é 1/12 da frequência do oscilador do CPU. A transmissão é iniciada sempre que uma instrução utiliza o registo SBUF(99H) como registo destino. A recepção é iniciada quando os bits de controlo REN= 0 (Receiver Enable) e RI= 0 (Receiver Interrupt). Modo 1 – Canal Assíncrono de 8 bits Neste modo de funcionamento são transmitidos e recebidos 10 bits: 1 start bit, 8 bits de dados(bit de menor peso em primeiro lugar) e 1 stop bit. Na recepção os bits de dados ficam no registo interno SBUF(99H) e o stop bit é registado no bit RB8 do registo SCON(98H). O baud rate do canal neste modo é programável através do timer 1 ou através do gerador de baud rate interno. Utilizando o timer 1 como gerador de baud rate, temos: Timer 1 em modo 1: baud rate= 2SMOD /32 * Timer1_Overflow Timer 1 em modo 2: baud rate= 2SMOD /32 * (fXTALL / 12*(256-TH1)) Utilizando o gerador de baud rate interno, o baud rate depende do bit SMOD(bit 7 do registo PCON(87H)) e da frequência do oscilador, através da seguinte fórmula: baud rate= 2SMOD/2500 * fXTALL

92 Modo 2 – Canal Assíncrono de 9 bits
Neste modo, o canal série transmite através do TxD ou recebe através do RxD 11 bits, distribuidos da seguinte forma: 1 start bit, 8 bits de dados, 1 bit de paridade e 1 stop bit. Na transmissão o bit de paridade pode ser afectado com 1 ou 0 e é o bit TB8(end. de bit 9BH) do registo SCON(98H). Na recepção o nono bit é afectado ao bit RB8(9AH) do registo SCON(98H). O baud rate é programável para 1/32 ou 1/64 de fXTALL função do valor do bit SMOD do registo PCON estar respectivamente a 1 ou a 0. Modo 3 – Canal Assíncrono de 9 bits Modo funcionalmente idêntico ao modo 2 com a diferença do baud rate poder ser programável através do timer 1 ou do gerador interno de baud rate. As fórmulas para o cálculo do baud rate obtido à custa do timer 1 ou do gerador de baud rate são idênticas às apresentadas na descrição do funcionamento do canal série em modo 1. Registos para parametrização do canal série do 80535 9FH 9EH 9DH 9CH 9BH 9AH 99H 98H SCON - 98H SM0 SM1 SM2 REN TB8 RB8 TI RI Flag de interrupção da recepção. É colocada a 1 por hardware no 8º bit time quando em modo 0, ou no início do stop bit nos outros modos. Deve ser afectado a 0 por programação. Modo Modo Modo Modo Flag de interrupção da transmissão. É colocada a 1 por hardware no 8º bit time quando em modo 0, ou no início do stop bit nos outros modos. Deve ser afectado a 0 por programação. Permite o funcionamento em modo 2 e 3, quando a 1. Em modo 1 e SM2= 1 implica RI=1 se o stop bit recebido for válido. Em modo 0, SM2 deverá estar sempre a 0. Bit 8 da recepção. Bit 8 da transmissão. Se a 1 permite recepção.

93 Registos para parametrização do canal série do 80535 (continuação)
SBUF - 99H D7 D6 D5 D4 D3 D2 D1 D0 Registo que define a interface entre o canal de comunicação série do e o programa. Escrever no registo SBUF implica escrever no registo de transmissão correspondendo a iniciar-se o processo de transmissão. Ler o SBUF implica ler o registo de recepção do canal. SMOD PCON - 87H Bits não utilizados na comunicação série. Quando a 1, duplica o baud rate do canal série nos modos 1, 2 e 3. BD ADCON – 0D8H Bits não utilizados na comunicação série. Quando a 1, o baud rate do canal série nos modos 1 e 3 são obtidos a partir de um divisor de frequências dedicado. Valores de baud rate de 4800 e 9600 podem ser obtidos a partir de fXTALL= 12MHz.

94 ; inicialização do UART do 535 INI_SERIE:
Consultando o anteriormente descrito, estamos em condições para desenvolver um módulo para manipular o canal série do 535 composto por uma rotina de iniciação chamada INI_SERIE, uma rotina de recepção chamada CI e uma rotina de transmissão chamada CO. ; continuação PROG SEGMENT CODE RSEG PROG ; inicialização do UART do 535 INI_SERIE: CLR P5.5 ; seleccionar o UART do 535 ; seleccionar 8 bits modo MOV SCON, #INIT_WORD ; MODO1, 8BITS ORL PCON, #BD ; baud rate enable ORL ADCON, #SMOD ; 9600 bit/s RET ; devolve em R7 um caracter CI: JNB RI, $ MOV R7, SBUF CLR RI ; tem um caracter como parametro de entrada em R7 ; e transmite-lo via canal série CO: JNB TI, $ CLR TI MOV SBUF, R7 END NAME CI_CO ; ISEL ; ; por Jorge Pais ; Rotinas que usam a UART do para enviar ; e receber dados série por pooling ; PUBLIC CI, CO, INIT_SERIE ;PUBLIC CO ; TI EQU SCON.1 ; RI EQU SCON.0 ; PORTO 5 P5 EQU 0F8H ; SCON REGISTER INIT_WORD EQU B ; MODO 1, RECEIVER ENABLE, POOLING ; TI ACTIVE ; PCON REGISTER SMOD EQU 80H ; ADCON REGISTER ADCON EQU 0D8H BD EQU 80H ; continua

95 Interrupções no 80535 TCON - 88H
As fontes de pedido de interrupção no são as seguintes, INT0/, INT1/, INT2/, INT3/, INT4/, INT5/, INT6/ - Interrupções externas 0, 1, 2, 3, 4, 5 e 6 respectivamente. Timer0, Timer1, Timer2 – Interrupções internas geradas pelos temporizadores existentes no CPU. No caso do Timer2 existem modos de funcionamento para os quais a interrupção é gerada externamente e outros em que a interrupção é interna. Canal Série 0 – Interrupção interna gerada pelos eventos de recepção ou transmissão série de um carácter. Conversor A/D – Interrupção interna gerada pelo conversor analógico/digital aquando do fim de uma conversão. O mecanismo de controlo das interrupções no envolve variadas flags localizadas nos seguintes registos de função especial: TCON.0 TCON.1 TCON.2 TCON.3 TCON.4 TCON.5 TCON.6 TCON.7 IT0 IE0 IT1 IE1 TF0 TCON - 88H TF1 8FH 8DH 8BH 8AH 89H 88H Endereço ao nível do bit IT0 – Define a característica da entrada INT0/. Quando a 1 indica pedido de interrupção nas transições descendentes de INT0/. Se a 0 implica pedido de interrupção quando INT0/ estiver a 0. IE0 – Flag de detecção de transições na entrada INT0/. Quando a 1 indica que houve um pedido de interrupção por transição descendente na entrada INT0/. É colocada a 0 quando a interrupção é atendida. IT1 – Idem IT0 mas agora o significado é para a entrada INT1/. IE1 – Idem IE0 mas respeitante à entrada INT1/. TF0 – Flag de overflow do timer 0. Colocada a 1 por hardware quando o temporizador/contador 1 passa da configuração binária definida por tudo a 1’s para tudo a 0’s. Esta flag é colocada a 0 por hardware sempre que o processador atende o pedido. TF1 – Idem TF0 mas agora para o timer 1.

96 T2CON – 0C8H IRCON – 0C0H T2CON.6 T2CON.5 I3FR I2FR CEH CDH C8H
Endereço do bit I2FR – Define se a entrada INT2/ afecta na transição ascendente ou descendente a flag de pedido de interrupção IEX2. Se a 1, a entrada INT2/ afecta IEX2 quando houver uma transição ascendente. Se a 0, INT2/ afecta IEX2 quando houver uma transição descendente. I3FR – Idem I2FR mas agora respeitante à entrada INT3/. IRCON.7 IRCON.6 IRCON.5 IRCON.4 IRCON.3 IRCON.2 IRCON.1 IRCON.0 IRCON – 0C0H EXF2 TF2 IEX6 IEX5 IEX4 IEX3 IEX2 IADC C7H C6H C5H C4H C3H C2H C1H C0H Endereço do bit IADC – Flag de pedido de interrupção do conversor analógico/digital. É colocado a 1 no fim de uma conversão e deve ser colocado a 0 por software. IEX2-6 – Flag de transição para a entrada de interrupção INTx/. Colocada a 1 por hardware quando for detectado um pedido de interrupção. Fica a zero quando é atendido o pedido de interrupção. TF2 – Flag de overflow do timer 2. Fica a 1 quando o timer 2 passa a sua configuração binária de todos os bits a 1 para todos os bits a 0. Deve ser colocada a 0 por software. EXF2 – Flag de recarregamento (reload) do timer 2. Fica a 1 quando existe uma transição descendente no sinal T2EX enquanto EXEN2= 1. Quando é permitido a interrupção do timer 2, EXF2=1 implica uma vectorização para a rotina de atendimento da interrupção timer 2. Deve ser colocado a 0 por software.

97 Cada entrada de interrupção pode ser mascarada ou desmascarada através dos seus bits de permissão, existentes nos registos IEN0 e IEN1. Existe também um bit global de permissão para todas as fontes de interrupção que quando a 0 não permite qualquer pedido de interrupção independentemente dos bits de permissão individuais. IEN0.7 IEN0.5 IEN0.4 IEN0.3 IEN0.2 IEN0.1 IEN0.0 IEN0 – 0A8H EAL ET2 ES ET1 EX1 ET0 EX0 AFH ADH ACH ABH AAH A9H A8H Endereço do bit EX0 – Se a 1 permite INT0/. ET0 – Quando a 1 permite interrupção gerada pelo timer 0. EX1 – Permite INT1/ sempre que esteja este bit a 1. ET1 – Se a 1 permite interrupção gerada pelo timer 1. ES – Quando a 1 permite interrupção gerada pelo canal série, tanto na transmissão como na recepção. ET2 – Permite interrupção do timer 2 quando a 1. EAL – Permite ou não interrupções. Se a 0 este bit não permite atendimento aos pedidos de interrupção. IEN1.0 IEN1.1 IEN1.2 IEN1.3 IEN1.4 IEN1.5 IEN1.7 EADC EX2 EX3 EX4 EX6 IEN1 – 0B8H EXEN2 B8H B9H BAH BBH BDH BFH Endereço do bit BCH EX5 EADC – Se a 1 permite interrupção gerada pelo conversor analógico/digital. EX2-6 – Quando a 1 permite o respectivo pedido de interrupção correspondente à entrada INTx. EXEN2 – Não permite interrupção por recarregamento automático do timer 2 enquanto este bit estiver a 0..

98 Todas as fontes de interrupção mencionadas obedecem à priori a uma hierarquia quanto às suas prioridades que podem ir do nível 0 a 3 e que são definidas através de bits dos registos IP0 e IP1. Uma rotina de atendimento de interrupção correspondente a uma entrada de interrupção de baixa prioridade só pode ser interrompida por outra interrupção de maior prioridade. Uma interrupção de nível 3 não poder ser interrompida por qualquer outra fonte de interrupção. A definição dos níveis de prioridade das várias fontes de interrupção realizam-se aos pares segundo a seguinte lista: INT0/ e Conversor A/D Timer0 e INT2/ INT1/ e INT3/ Timer1 e INT4/ Canal Série 0 e INT5/ Timer2 e INT6/ Registos para definição dos níveis de prioridade IP0, IP1 IP0.7 IP0.6 IP0.5 IP0.4 IP0.3 IP0.2 IP0.1 IP0.0 TF2+EXF2 IEX6 RI+TI IEX5 TF1 IEX4 IE1 IEX3 TF0 IEX2 IE0 IADC IP0 - 0A9H IP1.7 IP1.6 IP1.5 IP1.4 IP1.3 IP1.2 IP1.1 IP1.0 TF2+EXF2 IEX6 RI+TI IEX5 TF1 IEX4 IE1 IEX3 TF0 IEX2 IE0 IADC IP1- 0B9H O nível de prioridade 0 é o menor e o 3 é o maior. Quando as fontes de interrupção estão todas com a mesma prioridade e existem simultâneos pedidos de atendimento, estes pedidos são atendidos segundo um critério de prioridades pré-definidos no e ilustrados na seguinte tabela:

99 Atendimento de Interrupções
As flags correspondentes às entradas de interrupção são testadas em todos os ciclos máquina no 10º ciclo de clock. Se alguma destas flags está activa existe um ciclo máquina seguinte durante o qual estas flags são obsessivamente testadas. Caso continuem activas o sistema de interrupções gerará uma instrução LCALL Endereço16, na qual o Endereço16 é um valor a 16 bits que especifica o endereço absoluto do início da respectiva rotina de atendimento de interrupção. Os valores dos endereços absolutos correspondentes às rotinas de atendimento das interrupções são ilustrados na seguinte tabela: Uma rotina de atendimento de interrupção começa no endereço especificado e termina quando é cumprida a instrução RETI. A instrução RETI indica que a rotina de atendimento terminou a execução afectando o PC com os dois bytes que se encontram no topo do stack e também informa a máquina de atendimento de interrupções do CPU que pode voltar a atender interrupções de igual ou menor prioridade. Portanto, nunca se deve terminar uma rotina de interrupção com uma instrução RET porque para o CPU a rotina de atendimento da interrupção ainda está sendo realizada nunca mais atendendo interrupções de igual ou menor prioridade. Uma característica importante das rotinas de atendimento de interrupções é que devem ser sempre invisíveis, do ponto de vista do programa que foi interrompido. Ou seja, não devem alterar o estado do CPU nem demorar “demasiado” tempo de execução. O estado do CPU é definido como sendo os valores dos seus registos internos e stack. O tempo de execução de uma rotina de atendimento de interrupções poderá ser definido como “demasiado” quando este tempo degradar substancialmente o tempo de execução dos programas interrompidos.

100 Desenho das Rotinas para Atendimento de Interrupções
Para garantir a invisibilidade da rotina para atendimento da interrupção de nível 1, a sua estrutura genérica deve ser: ROTINA_DE_INTERRUPCAO: PUSH DPH ; salvar os registos que vão ser utilizados na rotina PUSH DPL ; de interrupção. Uma das condições para se garantir PUSH B ; a invisibilidade desta rotina de interrupção. PUSH ACC PUSH PSW SETB RS0 ; nível 1 de interrupção implica no 535 trabalhar com o ; banco 1 de registos por convenção. Ao nível do programa ; deve-se garantir que se trabalha com o banco 0 de registos. ; Assim garante-se a não destruição dos registos R0-7. LCALL Fazer_Accoes ; destrói A, B e DPTR. Acções a efectuar na rotina ; de interrupção. POP PSW ; reposição dos valores nos registos POP ACC POP B POP DPL POP DPH RETI ; assinala ao processador o fim do atendimento da interrupção ; e afecta o PC com a word que se encontra no topo do stack. Fazer_Accoes: RET

101 Desenho das Rotinas para Atendimento de Interrupções (continuação)
O módulo assembly535 para o atendimento de interrupções no kit didáctico SD535 deve ter a seguinte estrutura: ; entry point da interrupção do Canal Série Rx/Tx CSEG AT 23H LJMP INT_RXTX_ROT ; entry point da interrupção do Overflow do Timer 0 CSEG AT 0BH LJMP INT_T0_ROT  ; rotina de tratamento da interrupção do Overflow do Timer 1 CSEG AT 1BH LJMP INT_T1_ROT ; entry point da interrupção do Overflow do Timer 2 ; ou recarregamento externo do Timer 2 CSEG AT 2BH LJMP INT_T2_ROT ; entry point da interrupção do Conversor Analogico-Digital CSEG AT 43H LJMP INT_ADC_ROT ; entry point da interrupção 0 externa CSEG AT 03H LJMP INT0_ROT ; entry point da interrupção 1 externa CSEG AT 13H LJMP INT1_ROT ; entry point da interrupção 2 externa CSEG AT 4BH LJMP INT2_ROT ; entry point da interrupção 3 externa CSEG AT 53H LJMP INT3_ROT ; entry point da interrupção 4 externa CSEG AT 5BH LJMP INT4_ROT ; entry point da interrupção 5 externa CSEG AT 63H LJMP INT5_ROT ; entry point da interrupção 6 externa CSEG AT 6BH LJMP INT6_ROT Nota importante: As chamadas às rotinas de interrupção devem ser realizadas através da instrução LJMP ROTINA_INTERRUPÇÃO porque não nos podemos esquecer que o processador chega ao endereço de entry point da rotina de atendimento da interrupção através de uma instrução LCALL ENDEREÇO16 produzida internamente pelo CPU, a qual garante a memorização do endereço de retorno em stack. Deste modo, o conjunto de instruções que compõem o atendimento da interrupção deve ser sempre terminado pela instrução RETI, como o exemplificado na página anterior.

102 Desenho das Rotinas para Atendimento de Interrupções (continuação)
Exercício: Faça um programa que simule um contador crescente de 16 bits com clear assíncrono. O contador deve contar 20 unidades por segundo. O valor do contador deve ser visualizado na consola. Opções tomadas: 1. O timer 0 vai gerar o sinal de relógio (20Hz). Como trabalha a 1MHz tem de dividir-se esta frequência por 2. O sinal de clear do contador vai ser a entrada INT3 do CPU. $TITLE('Contador crescente de 16 bits com clear') ; ****************************************** ; * feito por Jorge Pais * ; * * EXTRN CODE(INIT_INT, INIT_TIMER0) EXTRN CODE(GOTOX) EXTRN CODE(WRITE_WORD, WRITELN) BANCO0 EQU B IEN0 EQU 0A8H EAL EQU IEN0.7 ; permite ou não todas as fontes de interrupção DSEG AT 40H DIM_PILHA EQU 40H PILHA: DS DIM_PILHA ; FICA COM UM STACK DE 40H ATÉ 7FH MAINCODE SEGMENT CODE RSEG MAINCODE START: MOV SP, #PILHA-1 ANL PSW,#BANCO0 ; TRABALHA COM O BANCO 0 MOV CONTADOR, #0 MOV CONTADOR+1, #0 ; inicialização do contador SETB VER_CONTADOR ; ver o valor 0 LCALL WRITELN; afixar o contador na linha seguinte ; continua ; continuação LCALL INIT_TIMER0 LCALL INIT_INT FOREVER: JNB VER_CONTADOR, FOREVER MOV R7, #0 LCALL GOTOX ; posiciona o cursor MOV R7, CONTADOR MOV R6, CONTADOR+1 LCALL WRITE_WORD CLR VER_CONTADOR SJMP FOREVER MAINDATA SEGMENT DATA RSEG MAINDATA PUBLIC CONTADOR CONTADOR: DS 2 MAINBIT SEGMENT BIT RSEG MAINBIT PUBLIC VER_CONTADOR VER_CONTADOR: DBIT 1 END

103 NAME INTERRUPTS ; **************************** ; * ISEL * ; * por Jorge Pais * ; * * EXTRN DATA(CONTADOR) EXTRN BIT(VER_CONTADOR) ; **************************************************** ; * definição dos registos e bits para tratamento das interrupções * IEN0 EQU 0A8H IEN1 EQU 0B8H T2CON EQU 0C8H IRCON EQU 0C0H ; BITS DO IEN0 EAL EQU IEN0.7 ; BITS DO IEN1 EX3 EQU IEN1.2 ; BITS DO T2CON I3FR EQU T2CON.6 ; BITS DO IRCON IEX3 EQU IRCON.2 ; *********************************************** ; *** entry points das rotinas de interrupção *** ; entry point da interrupção do Overflow do Timer 0 CSEG AT 0BH LJMP INT_T0_ROT ; entry point da interrupção 3 externa CSEG AT 53H LJMP INT3_ROT ;continua ; continuação ; ************************************************ ; *** rotinas para tratamento das interrupções *** INTS SEGMENT CODE RSEG INTS ; rotina de tratamento da interrupção do Overflow do Timer 0 INT_T0_ROT: MOV TL0, #LOW( ) MOV TH0, #HIGH( ) ; reinicialização do Timer PUSH PSW PUSH ACC INC CONTADOR MOV A, CONTADOR CJNE A, #0, IT0_FIM INC CONTADOR+1 IT0_FIM: SETB VER_CONTADOR POP ACC POP PSW RETI ; rotina de atendimento da entrada de interrupção externa 3 - Clear do contador INT3_ROT: MOV CONTADOR, #0 MOV CONTADOR+1, #0 CLR IEX3 ; clear ao isr3 - para aceitar novos interrupts ; rotina para inicialização das interrupções PUBLIC INIT_INT INIT_INT: CLR EAL ; disable a todos os interrupts SETB ET0 ; enable Timer 0 interrupt SETB EX3 ; enable IEX3 SETB I3FR ; transição ascendente IEX3 CLR IEX3 ; CLEAR FLAG INT3 SETB EAL ; enable aos interrupts permitidos RET END

104 Valor variável entre [1us, 3us] Escala De Tempo LCALL 0BH 2us
NAME TIMERS ; **************************** ; * ISEL * ; * por Jorge Pais * ; * * ; ************************************************ ; * definição dos registos e bits para os timers * ; peso dos BITS DO TMOD M0_T0 EQU 01H M1_T0 EQU 02H CT_T0 EQU 04H GATE_T0 EQU 08H M0_T1 EQU 10H M1_T1 EQU 20H CT_T1 EQU 40H GATE_T1 EQU 80H ; ******************************************** ; *** rotinas para incialização do Timer *** TIMERS SEGMENT CODE RSEG TIMERS ; rotina de inicialização do timer 0 que não destrói a programação do timer 1 ; Timer 0 programado em modo 1, e ; existe Timer Overflow passados 50000uS PUBLIC INIT_TIMER0 INIT_TIMER0: MOV TL0, #LOW( ) MOV TH0, #HIGH( ) ANL TMOD, #0F0H ORL TMOD, #M0_T0 ; modo 1-16 bits, timer, TRx SETB TR0 ; Timer0 ON RET END Algumas considerações devem ser feitas acerca da solução apresentada anteriormente. A rotina de tratamento de interrupção de Timer Overflow, chamada INT_T0_ROT, faz a reinicialização do Timer0 nas suas duas primeiras instruções para minimizar o erro temporal relativamente ao que é pedido no enunciado. E que é, passo a citar, “O contador deve contar 20 unidades por segundo.”. No entanto, existe um erro de tempo que é derivado do tempo que decorre entre existir Timer Overflow e a completa execução das 2 instruções de reinicialização de TL0 e TH0. Na figura seguinte, tenta-se ilustrar o erro temporal que não é exactamente quantificável, porque um pedido de interrupção é assíncrono relativamente à normal execução de instruções. Timer Overflow Valor variável entre [1us, 3us] Escala De Tempo LCALL 0BH 2us LJMP INT_T0-ROT 2us MOV TL0, #LOW(-50000) 1us MOV TH0, #HIGH(-50000) 1us

105 A questão que se coloca perante este problema é:
Qual a solução para a correcta contabilização de tempo? A resposta é: não existe uma forma exacta de contabilizar este tempo. No entanto, o próprio temporizador, que estamos a utilizar, é um contador de tempo então porque não utilizá-lo para a contagem do tempo ilustrado na figura anterior. Como? Se pensarmos um pouco, o tempo de execução das várias instruções a cumprir vai fazer com que o par de registos (TH0, TL0) fique com um valor diferente de zero. Logo a operação a realizar não será: (TH0, TL0) = mas sim, (TH0, TL0)= (TH0, TL0) – 50000 De referir, que para o problema simples do contador o erro temporal é quase contabilizavel pela figura anterior, mas geralmente e pensando num problema com vários níveis de interrupção, o tempo pode-se tornar perfeitamente incontabilizavel. Considere a figura seguinte em que a interrupção do Timer Overflow 0 tem prioridade 0 e existem mais fontes de interrupção com prioridades superior, então o erro temporal é ilustrado pela figura seguinte: Timer Overflow Rotina_Interrupção_Nível_1 Rotina_Interrupção_Nível_2 Rotina_Interrupção_Nível_3 LCALL 0BH LJMP INT_TO_ROT MOV TL0, #LOW(-50000) MOV TH0, #HIGH(-50000) Escala de Tempo As setas bidireccionais da figura anterior representam a chamada e retorno das rotinas de tratamento de interrupção com prioridade crescente. A figura é confusa e esta confusão ilustra devidamente a inaptidão para se medir um tempo correcto e concreto.

106 Deste modo, o conjunto de instruções que devem ser realizadas no início da rotina de tratamento da interrupção Timer Overflow, (INT_T0_ROT), para uma nova inicialização do temporizador com vista a minimizar o erro temporal do à precisão do temporizador que é de 1us, será: Bibliografia VALOR EQU ; 20 vezes por segundo ; rotina de tratamento da interrupção gerada por ; Overflow do Timer 0 INT_T0_ROT: PUSH PSW PUSH ACC ; reinicialização do timer CLR TR0 ; pára o timer MOV A, TL0 ADD A, #LOW(-VALOR+7) ; 7 é o nº de instruções MOV TL0, A ; da reinicialização MOV A, TH0 ADDC A, #HIGH(-VALOR+7) MOV TH0 SETB TR0 ; fim da reinicialização INC CONTADOR MOV A, CONTADOR CJNE A, #0, IT0_FIM INC CONTADOR+1 IT0_FIM: SETB VER_CONTADOR POP ACC POP PSW RETI SAB80535 Quick Reference Secção de Sistemas Digitais DEETC – ISEL SAB Bit Single-Chip Microcontroller Family Users Manual 08.95 Microcomputer Components Siemens SAB Bit CMOS Single-Chip Microcontroller Data Sheet 02.96 Nota Final Espero sinceramente que esta versão, ainda em construção, das folhas de apoio lhe tenham sido particularmente úteis na concretização teórico-prática da disciplina de Introdução aos Microprocessadores. O docente Jorge Pais


Carregar ppt "Instituto Superior de Engenharia de Lisboa"

Apresentações semelhantes


Anúncios Google