Curso:Z80SoC:PT BR

From Retro-CPU.run
Jump to: navigation, search

Contents

Anatomia e Desenvolvimento de Um Microcomputador

Nota: Este é um projeto como parte the um hobby, faz uso dos princípios mais simples e práticos de arquitetura e projeto de computadores, e sua construção se dá por meio de implementação em FPGA.


Você já parou para imaginar o quanto um computar é complexo, e como os diversos componentes interagem entre si de modo a produzir uma máquina capaz de nos proporcionar benefícios como ferramentas de trabalho, ou diversão na forma de jogos por exemplo? Saiba que os princípios fundamentais da engenharia de computadores que foram utilizados nos anos 70 e 80 para criar as nossas tão queridas máquinas de 8 bits não é tão complicado assim, e é esse o desafio que me proponho a explorar numa série de artigos voltados para todos aqueles curiosos que quem entender como se faz um computador, desde as decisões de arquitetura como tamanho da RAM e resolução de vídeo até o adequado mapeamento de memória e portas de controle de periféricos, passando pelo desenho da BIOS e sua programação em linguagem assembly ou C. Interessado? Então vamos deixar bem claro os objetivos desta série de artigos e, ainda mais importante, o que não faz parte dos objetivos.

Objetivos

  • Um projeto didático que visa estudar a anatomia de um computador, entender seu funcionamento e como as diferentes partes se comunicam entre;
  • Como projetar um computador em todos os detalhes, desde a arquitectura, especificação de seus recursos, implementação usando VHDL e desenvolvimento da BIOS;
  • Utilização de kit de desenvolvimento em FPGA, utilizar a linguagem de descrição de hardware VHDL para implementar o computador, e as linguagens C e Assembly para desenvolver o BIOS;
  • Aprender a utilizar kits de desenvolvimento em FPGA mais utilizados nas comunidades de usuários de retrocomputardores: Terasic DE1, Terasic DE2, Xilinx Spartan 3E.


Não é objetivo desta série:

  • Este projeto não é um passo a passo de como fazer um MSX, Spectrum, TRS-80, ou (nome do seu computador preferido aqui). Para isso, procure na Web e vai encontrar projetos prontos para utilizar, e mais adequados se esta for sua intenção;
  • Não desenharemos placas de circuito impresso, nem soldaremos componentes físicos, uma vez que o hardware será implementado usando FPGA;
  • Não vamos criar um computador de uso genérico – o resultado deste projeto será um sistema de 8 bits ideal para experimentação e aprendizagem.


Não importa se você não sabe programar em Assembly ou C. Ao final, algum conhecimento você terá adquirido nesta área.

Não importa se você não sabe especificar hardware usando VHDL. Ao final você terá adquirido no mínimo conhecimento básico para conseguir ler projetos nessa linguagem.

Não importa se você não sabe como fazer programas que façam a ligação do hardware com software. O objetivo deste curso é principalmente mostrar como estes dois componentes se comunicam.

Pronto para começar?

Introdução ao Z80SoC

O Z80SoC surgiu de um súbito interesse por FPGA em 2007. Não me recordo exatamente como FPGA veio ao meu conhecimento, mas as imensas possibilidades desta tecnologia me conquistou de imediato, e parei literalmente tudo que estava fazendo (come exceção de trabalhar) para me dedicar a conhecer esta tecnologia, suas possibilidades e como a utilizar. E obviamente para explorar FPGAs, é necessário conhecer uma linguagem para descrever o hardware que queremos implementar e assim cheguei ao VHDL. Além do VHDL, é possível especificar hardware para FPGA em outras linguagens como o Verilog, mas após investigação e experimentação com essas duas linguagens, optei pela primeira, que utilizaremos nesta série de artigos. Em seguida à descoberta da tecnologia FPGA, foi necessário identificar como fazer uso dessa tecnologia e cheguei aos kits de desenvolvimento.

Z80soc rom1.png

Z80SoC rodando ROM de demonstração

Kits de Desenvolvimento em FPGA

Após muita pesquisa em fóruns, em principalmente aos diversos projetos existentes de retro computadores e consoles para FPGA, cheguei à conclusão que a imensa maioria dos projetos utilizavam principalmente dois Kits de desenvolvimentos facilmente encontrados no mercado:

  1. Terasic DE1, com FPGA da Altera;
  2. Xilinx Spartan 3E, com FPGA da Xilinx.

Com estas duas placas de desenvolvimento eu conseguiria rodar entre outros, estes microcomputadores, consoles e arcades já livremente disponíveis na web:

  1. Amiga
  2. Apple
  3. Atari console
  4. Colecovison
  5. TRS-80, TRS-80 Color Computer 1, 2 e 3
  6. Commodore 64
  7. MSX
  8. PC 8086
  9. ZX Spectrum
  10. Jogos de Arcade (Pac Man, Galaga, Defender, Space Invaders)
  11. … muitos outros!

A conclusão é então muito simples, e aqui vai minha recomendação de compra, por ordem de custo benefício:

  1. Altera / [DE1]
  2. Xilinx / [Spartan-3E]
  3. Altera / [DE2-70]
  4. Altera / [DE2-115]

Se puder comprar somente uma, compre (1). Se puder comprar duas, compre (1) e (2).

Se já tiver bom conhecimento de VHDL ao ponto de conseguir adaptar e modificar projetos existentes, então poderá adquirir os Kits (3) ou (4). Estes dois Kits não devem ser a primeira opção de um iniciante pelo motivo de serem placas de capacidade superiores à DE1, e portanto muito mais caras. E sendo mais caras, será muito menos comum de encontrar projetos prontos para utilizar nestes Kits, de modo que serão sub-utilizados se você não tiver capacidade de adaptar projetos desenvolvidos para outros Kits.

Características do Z80SoC

O Z80SoC é um sistema que possibilita modo texto 80 x 40 (80 colunas e 40 linhas de texto), e pseudo gráficos uma vez que a tabela de caracteres pode ser redefinida logo após o boot (os bits que compões cada caracter são copiados da ROM para a CHARRAM, que é uma RAM e portanto pode ser escrita). Pode-se escrever no vídeo usando endereços de memória e também através de portas. Na Spartan 3E e na Altera DE2 é possível escrever no display LCD de 2 linhas por 32 colunas da mesma forma que se escreve em RAM; Na Spartan 3E é ainda possível ler o estado do botão giratório, e nas Terasic DE1 e DE2 pode-se escrever no display de 7 segmentos. Todas essas operações são executadas via escrita em endereços de memória ou acesso a portas, tanto em Assembly quanto em C. Para utilização de C com o Z80SoC, foi desenvolvido os drivers básicos de Entrada e Saída, que podem ser utilizados pelos programas do usuário para acesso aos periféricos dos Kits (leds, lcd, memórias, teclado, etc.). Para criar este sistemas, foi utilizado a filosofia de um sistema aberto (como os PC XT da década de 80), ou seja, foi criado a especificação do hardware e depois foi-se ao “mercado” em busca de componentes para montar sistemas, sendo que neste caso o “mercado” é a web e os componentes são os códigos que implementam as diversas partes necessárias para se criar um computador. É possível encontrar códigos opensource para todos os componentes utilizados num microcomputador, e os códigos que foram utilizados no Z80SoC estão listados na tabela a seguir.


Códigos opensource utilizados
CPU Z80 T80 obtido em http://opencores.com/project,t80
Controlador de VGA Livro “Rapid Prototyping of Digital Systems“
Controlador de teclado Adaptado a partir de fonte desconhecida
Controlador de LCD Adaptado a partir do core de Rahul Vora, da Universidade de Novo Mexico
Controlador de 7Seg Desenvolvido especificamente para o Z80SoC
Controlador do botão giratório Adaptado a partir do modelo de referência da Xilinx Spartan-3E
Controlador dos Leds, switch e push button Desenvolvido especificamente para o Z80SoC
Controlador de acessos SRAM Desenvolvido especificamente para o Z80SoC
Controlador de acessos CHARRAM Desenvolvido especificamente para o Z80SoC
Controlador de acessos ROM Desenvolvido especificamente para o Z80SoC
BIOS e código da ROM Desenvolvido especificamente para o Z80SoC

Arquitetura do Z80SoC

Nota: Diagrama de arquitetura será inserido aqui brevemente.

Evite Problemas: A partir de agora, podemos referenciar as diferentes placas de desenvolvimento FPGA com “plataforma”.

Antes de começarmos a construção do computador, é necessário quais os recursos que queremos implementado, a disposição interna e interligação dos diversos componentes. Está uma definição ultra simplista de arquitetura de computadores. Nesta fase inicial, precisamos considerar aspectos como o que precisamos e o que gostaraimos de ter no computador, tais como:

  • qual o microprocessor
  • layout e tamanho da memória
  • resolução do modo texto (linhas x colunas)
  • resolução gráfica
  • numero de cores
  • som, numero de canais
  • periféricos externos (teclado, mouse, saída de vídeo, acesso a disco, K7)

Uma vez definido os recursos necessários ou desejados, é necessário verificar viabilidade do projeto. No caso do Z80SoC, ou de outro computador implementado em FPGA, é necessário considerar capacidade do FPGA, disponibilidades dos componentes em VHDL, desenvolvimento das partes não livremente disponíveis. Os passos para definir as especificações do computador deve consistir em identificar o kit disponível e suas características, fazendo os ajustes necessários na nossa especificação para viabilizar o projeto - decisões de arquitetura. Desde já vale à pena mencionar que os kits citados e utilizados neste projeto tem características e recursos diferenciados, o que irá impactar diretamente nas capacidades que serão implementados no computador. Mas a despeito de qualquer limitação no kit que você tiver disponível, está será uma jornada divertida e desafiadora.

Mapeamento de Memória

Vamos exemplificar uma decisão de arquitetura com relação à memória RAM do Z80SoC nos diferentes kits FPGA. A Terasic DE1 e DE2 tem uma SRAM interna, enquanto a Spartan-3E não tem esse recurso. Desta forma, a memória do Z80SoC foi implementado usando a SRAM nas DE1/DE2, o que possibilitou 40KB de RAM disponível. No caso da S3E, a RAM teve que ser implementada usando um recurso interno denominado BlockRAM, que é limitado, e consequentemente limitou a RAM nesta implementação em 12KB.O mapeamento da memória do computador, consequentemente, ficou diferente nestes. Este foi um exemplo inicial de decisões que são necessárias tomar antes de iniciar uma implementação, de forma a ultrapassar limitações e se chegar a um projeto satisfatório. Outros desafios surgirão, e serão discutidos em detalhes nas seções correspondentes.


Terasic DE1

+-------------------+ 0000h 
|                   | 
|       ROM         | 
|      16 KB        | 
|                   | 3FFFh 
+-------------------+ 4000h 
|    VIDEO RAM      | 
|   4800 Bytes      | 52BFh
+-------------------+ 52C0h 
|    Variáveis do   | 
|      Sistema      | 57FFh
+-------------------+ 5800h 
|      Tabela de    | 
|    caracteres     | 
|     (charram)     | 5FFFh
+-------------------+ 6000h 
|                   | 
|       RAM         | 
|                   | 
|      40 KB        | 
|                   | 
|                   | 
+-------------------+ FFFFh

Terasic DE2

+-------------------+ 0000h 
|                   | 
|       ROM         | 
|      16 KB        | 
|                   | 3FFFh 
+-------------------+ 4000h 
|    VIDEO RAM      | 
|   4800 Bytes      | 52BFh
+-------------------+ 52C0h 
|    Variáveis do   | 
|      Sistema      | 57DFh
+-------------------+ 57E0h 
|   LCD VIDEO RAM   | 
|      2 X 16       | 57FFh
+-------------------+ 5800h 
|      Tabela de    | 
|    caracteres     | 
|     (charram)     | 5FFFh
+-------------------+ 6000h 
|                   | 
|       RAM         | 
|                   | 
|      40 KB        | 
|                   | 
|                   | 
+-------------------+ FFFFh

Spartan-3E

+-------------------+ 0000h 
|                   | 
|       ROM         | 
|      16 KB        | 
|                   | 3FFFh 
+-------------------+ 4000h 
|    VIDEO RAM      | 
|   4800 Bytes      | 52BFh
+-------------------+ 52C0h 
|    Variáveis do   | 
|      Sistema      | 57DFh
+-------------------+ 57E0h 
|   LCD VIDEO RAM   | 
|      2 X 16       | 57FFh
+-------------------+ 5800h 
|      Tabela de    | 
|    caracteres     | 
|     (charram)     | 5FFFh
+-------------------+ 6000h 
|                   | 
|       RAM         | 
|                   | 
|      12 KB        | 
|                   | 
|                   | 
+-------------------+ 8FFFh

Portas de E/S

O controle de periféricos foi implementado através do acesso a portas do Z80. Seguindo o mesmo princípio aplicado ao mapeamento de memória, as portas foram definidas de forma padrão para as diferentes placas suportadas, sendo que algumas portas adicionais foram introduzidas para possibilitar o suporte a recursos exclusivos de cada placa.

Port In/Out Recurso Plataforma
Portas de E/S na CPU
01H Out Leds Verdes (7-0) DE1 / DE2 / S3E
02H Out Leds Vermelhos (7-0) DE1 / DE2
03H Out Leds Vermelhos (15-8) DE2
10H Out HEX0 a HEX1 DE1 / DE2
11H Out HEX2 a HEX3 DE1 / DE2
12H Out HEX4 a HEX5 DE2
13H Out HEX6 a HEX7 DE2
15H Out LCD On/Off DE2 / S3E
20H In SW(7-0) DE1 / DE2 / S3E*
21H In SW(15-8) DE2
30H In KEY(3-0) DE1 / DE2 / S3E
70H In Botão Giratório S3E
80H In PS/2 teclado DE1 / DE2 / S3E
90H Out Video DE1 / DE2 / S3E
91H In/Out Video cursor X DE1 / DE2 / S3E
92H In/Out Video cursor Y DE1 / DE2 / S3E

(*) Na Spartan-3E, somente 4 switches disponíveis.

Variáveis do Sistema

Variáveis do sistema e registradores são atributos inicializados com valores padrão quando o sistema é iniciado. Estes registradores tem diversas funções como identificar a versão do sistema, onde começa e termina a RAM, etc. Estas variáveis e registradores podem ser de leitura, escrita ou ambos.

Endereço R/W Descrição
Variáveis do Sistema
57DFH R Plataforma: 0=DE1 / 1=S3E / 2=DE2
57DEH R Última tecla pressionada
57DCH R LCD VRAM (Início da Memória)
57DAH R RAMTOP (Última posição de memória disponível)
57D8H R RAM Botton (Endereço inicial da RAM)
57D6H R CHARRAM (Endereço inicial)
57D4H R VRAM (Endereço inicial)
57D2H R Endereço para a pilha do Z80 (Valor inicial é o mesmo da RAMTOP)
57D0H R/W Endereço VRAM para escrever próximo caracter
57CFH R/W Coordenada X do cursor VRAM
57CEH R/W Coordenada Y do cursor VRAM
57CDH R/W Périférico para saída
57CCH R Resolução horizontal em modo texto (colunas)
57CBH R Resolução vertical em modo texto (linhas)

Preparando o Ambiente de Desenvolvimento FPGA

Neste projeto será utilizado o ISE da Xilinx, e o Quartus II Web Edition da Alterar, ambos software livres para uso.

Download do Software

Faça o download do software para a plataforma que for desenvolver - Altera ou Xilinx. Não há necessidade de descarregar ambos os softwares se não tiver placas de desenvolvimento da Altera e Xilinx.

Altera Quartus II 13.0 e Service Pack 1
Xilinx ISE WebPack 14.7 e Service Pack 3
Evite Problemas: Utilize as versões indicadas. Ao utilizar versões diferentes, poderá ser necessário adaptações ou conversões do projeto ou componentes que foram gerados usando o Gerador de Componentes. Se você é um usuário inexperiente e optar por uma versão diferente, está se submetendo a problemas desnecessários.

Criação do Projeto Z80SoC

Descarregue o arquivo compactado do projeto e expanda de forma a criar esta estrutura:

Z80SoCv0.7.3/
Z80SoCv0.7.3/memoryCores/
Z80SoCv0.7.3/vhdl/
Z80SoCv0.7.3/ROMdata/

Passo-a-Passo para compilar e Carregar o Projeto no FPGA

  • Execute o software de design (ISE / Quartus II) e abra o projeto Z80SoC contido em Z80SoCv0.7.3/
  • Faça a compilação do projeto. Serão gerados arquivos para programar o FPGA:
    • Altera: um arquivo com extensão .sof.
    • Xilinx: um arquivo com com extensão .bit.
  • Usando o Programador de FPGA, carregue binário (.sof or .bit) para o FPGA.

Atualizações de Componentes

Alguns componentes foram criados usando o Gerador de Componentes que existe tanto para Altera quanto para Xilinx. Deve-se utilizar o Gerador para efetuar todas as alterações que forem necessárias nestes componentes.

Quando a BIOS for alterada e compilada, o arquivo gerado (ihx ou mif) deverá ser copiado para o diretório ROMdata, o projeto deve ser compilado novamente no ISE ou Quartus, e carregado no FPGA.

Desenvolvendo a BIOS e Programas

Nesta seção será indicado as ferramentas de desenvolvimento necessárias para programar para o Z80SoC.

Desenvolver BIOS para um computador requer uma completa compreensão da especificação do hardware, mapa de memória e das portas de E/S para acessar periféricos.

O conceito da BIOS vem facilitar o desenvolvimento de software de uso genérico, que devem abstrair o acesso ao hardware e concentrar nas funções necessárias ao programa. A BIOS portanto deve conter todo o código de acesso ao hardware, e prover para os demais programas, um interface de fácil utilização escondendo sua complexidade interna.

Evite Problemas: Utilize os compiladores indicados na próxima seção, a menos que seja experiente e possa facilmente adaptar os códigos desse curso a outros compiladores.

Ferramentas de Programação: O Compilador Assembly

Para programar em assembly, vamos utilizar o Pasmo um compilador cruzado e multi plataforma de assembly Z80. Pasmo permite compilar código assembly Z80 em Linux ou Mac OS X. Entretanto pode ser que você precise compilar o Pasmo a partir de do código fonte.

Para fazer download do Pasmo, siga esse link: http://pasmo.speccy.org. A documentação encontra-se aqui.

Ferramentas de Programação: O Compilador C

SDCC significa Small Device C Compiler, e foi criado para desenvolver código em C compacto e eficiente para micro controladores e processadores de 8 bits como o Z80. SDCC dispõe de extensões para acessar portas de E/S facilmente, que estaremos explorando neste projeto.

SDCC pode ser encontrado aqui: http://sdcc.sourceforge.net

Programando em Assembly

A diversão começa agora.

BIOS do Z80SOC

Nesta seção estaremos descrevendo o código em assembly de várias rotinas de acesso ao hardware do computador. Começaremos pelos componentes mais simples e avançaremos para os mais complexos.

LEDs Verdes

Cada plataforma dispõe de pelo menos 8 LEDs verdes (DE2 tem nove LEDs, mas o nono é reservado e não ficará disponível para o programador).

Cada LED precisa de um bit para representar seu estado de ligado ou desligado. Bit em um significa que o LED será aceso, em zero o LED será apagado. Para acionar cada um dos LEDs, será utilizado ports de E/S conforme na tabela Portas_de_E/S. De acordo com esta tabela, os LEDs verdes (7-0) são controlados pela porta 0x01. Os LEDs são numerados da direita para a esquerda. Para acionar os LEDs verdes, é necessário enviar oito bits de dados para a porta 0x01. Cada bit acionará um LED verde.

Alguns exemplos:
Para ligar todos os LEDs verdes, devemos enviar b11111111 (em binário, 0xff in hexadecimal) para a porta 0x01.
Para ligar somente o LED 0 (primeiro à direita), devemos enviar o valor b00000001 para a porta 0x01.

Para controlar os LEDs, será necessário utilizar a instrução OUT do Z80. A seguir ilustramos o código para acionar LEDs verdes, que é parte da biblioteca de sub-rotinas na BIOS do Z80SoC.

LEDG:
    out    (0x01),a
    ret

Quando quisermos aceder um LED, carregamos o valor adequado no registrador A e executamos uma chamada à rotina LEDG. Claro que é pode-se optar por executar o OUT diretamente, sem chamar a rotina LEDG - isto de fato será mais eficiente em termos de processamento e uso de espaço de memória. Estamos definindo as rotinas por razões didáticas e de organização.

Note que o comando OUT vai alterar o estado de oito LEDs de uma vez, o que significa que o programador é responsável por manter o estado dos LEDs em memória, e alterar somente os bits correspondentes ao LED que pretende alterar o estado.

LEDs Vermelhos

Os LEDs Vermelhos estão disponíveis somente nas DE1 e DE2, e são controlados através da porta 0x02. Referindo-se a Portas_de_E/S, nota-se ainda que a DE2 tem um conjunto adicional de LEDs vermelhos (18 no total, mas somente 16 ficarão disponíveis para o programador). O segundo conjunto de LEDs da DE2 (15 a 8) são controlados pela porta 0x03.

As sub-rotinas da BIOS que controlam os LEDs vermelhos estão listados a seguir:

LEDR07:
    out     (0x02),a
    ret
LEDR815:
    out     (0x03),a
    ret

Display LED de 7-Segmentos

O Display LED de 7-Segmentos é controlado por pares de segmentos, ou seja, cada duas "letras" ou "números" no display é acionado por uma porta. Os segmentos são numerados da direita para a esquerda. A DE1 tem 4 segmentos, a DE2 tem 8 segmentos e a S3E não tem display LED, portanto esta seção não se aplica à Spartan-3E.

Exemplo de como controlado o 7-Seg Display. Para apresentar o valor "ABCD" no display, devemos enviar os dados para as portas 0x10 e 0x11:

ld    a,0xCD
out   (0x10),a
ld    a,0xAB
out   (0x11),a

A DE2 tem quatro displays adicionais, que serão acionados pelas portas 0x12 e 0x13.

As sub-rotinas na BIOS são estas:

Hex0:
    out     (0x10),a
    ret
Hex1:
    out     (0x11),a
    ret
Hex2:
    out     (0x12),a
    ret
Hex3:
    out     (0x13),a
    ret

Algumas vezes, desejamos mostrar um valor de 16 bits no display. Para facilicar o processo, podemos utilizar estas duas rotinas:

HEXDATA:
;; hl = contêm o valor a mostrar no display
    push    bc
    ld      c,0x10
    out     (c),l
    inc     c
    out     (c),h
    pop     bc
    ret
HEXADDR:
;; hl = contêm o valor a mostrar no display
    push    bc
    ld      c,0x12
    out     (c),l
    inc     c
    out     (c),h
    pop     bc
    ret
Evite Problemas: Note que as rotinas HEXDATA e HEXADDR salvam o par de registradores BC na pilha antes de alterar o registrador C (não é possível salvar somente C na pilha). Esta é uma boa prática de programação que possivelmente evitará diversos problemas como comportamentos imprevisíveis do programam devido a valores de registradores importantes sendo sobre-escritos pelas sub-rotinas.

Push Buttons

Todas as plataformas dispõem de quatro push buttons. A Spartan-3E tem um push button extra: o botão giratório. Neste projeto, o botão giratório da S3E será o quinto push button, e todos eles serão acessados pela porta 0x30.

Para detectar se um botão está pressionado, deve-se ler a porta 0x030. Os bits do valor lido representam o estado do botão (bit em 1, botão pressionado; bit em zero, botão não pressionado). Os bits devem ser lidos da direita para a esquerda, e o quinto bit representa o estado do push button do botão giratório.

PUSHBUTTON:
    in      a,(0x30)
    ret

LCD

O LCD da DE2 e da S3E consiste em um display com duas linhas de 16 caracteres. Este caracteres estão mapeados como endereços de memória que começa em 0x57E0 e termina em 0x57FF (consulte #Mapeamento_de_Memória). Para escrever no display de LCD, tudo que é preciso fazer é escrever os caracteres ASCII nestes endereços de memória, com instruções LD do Z80.

ld    a,0x41          ;carrega letra "A" no registrador a
ld    (0x57E0),a  ;mostra o caracter no LCD, na primeira coluna da primeira linha

Implementaremos na BIOS uma rotina para imprimir uma sequência de 32 caracteres no LCD, de modo que facilite o processo de escrever frases inteiras neste display:

LCD_print:
    ; hl = address of 32 characteres string to print
    push    de
    push    bc
    ; get the LCD start address
    ld      de,(0x57DC)
    ld      bc,32
    ldir
    pop     bc
    pop     de
    ret

Esta sub-rotina da BIOS deve ser utilizado conforme exemplificado a seguir:

    ld    hl,bootmsg
    call  LCD_print
bootmsg:
    ; mensagem a imprimir deve ter 32 caracteres de comprimento
    db    "Booting...      "
    db    "                "

Botão Giratório

O Botão Giratório está disponível nomente na Spartan-3E. Certamente é uma adição interessante a esta plataforma, que poderá vir a proporcionar mais diversão a alguns softwares, como jogos.

Este componente, entretanto, é mais complexo e necessita de um controlador. Foi utilizado um controlador de referência da Xilinx no projeto. A interface de acesso ao botão giratório é muito simples, e basicamente consiste em ler o valor na porta 0x70, que pode ter um de três valores:

  • b00 (ou zero em decimal): Botão não está girando.
  • b01 (ou 1 em decimal): Botão está girando para a direita.
  • b10 (ou2 em decimal): Botão está girando para a esquerda.

A seguir mostramos a rotina para ler o botão giratório, mas lembre-se que pode ler o valor diretamente sem chamar esta rotina, e economizar algum processamento. Para ler o botão, basta ler a porta 0x70 com a instrução IN do Z80.

READ_ROTARY:
    in    a,(0x70)
    ret

Programa de Teste do Z80SoC em Assembly Z80

Programando em C

Programar em C pode ser mais produtivo do que programar diretamente em assembly, ou pode ainda ser a única possibilidade quando não se está disposto a abraçar o "lado negro" do desenvolvimento de software. O problema em utilizar linguagens de mais alto nível para computadores de 8 bits é que normalmente o código gerado é muito extenso pois inclui diversas bibliotecas que muitas vezes não são necessárias em todas as aplicações. Outro fator negativo é que o código gerado normalmente é mais lento do que se fosse desenvolvido diretamente em assembly.

Felizmente o conceito de open source viabiliza projetos muito interessantes, e um desses é o compilador cruzado SDCC (Small Device C Compiler).

Com o SDCC é possível escrever programas para diversos processadores (como o Z80) e micro controladores sentado no conforto de um poderoso PC moderno rodando Linux, Mac OS X ou Windows. O código compilador pode ser então carregado no computador de destino.

O SDCC é um compilador C especializado em plataformas de 8 bits e micro controladores, o que significa que o código é otimizado para essas plataformas produzindo um código compacto e veloz, além de outras facilidades como extensões para acesso a portas e acesso direto a memória.

Z80SoC BIOS Nesta seção estaremos descrevendo o código em C de várias rotinas de acesso ao hardware do computador. Começaremos pelos componentes mais simples e avançaremos para os mais complexos. Mas antes, é necessário falar do módulo crt0.s necessário para que possamos utilizar algumas funções padrão da linguagem C.

Módulo crt0.s

Se quisermos utilizar a função printf padrão do C, temos que criar e disponibiliza-la nós mesmo. Isto ocorre porque o printf é uma função de alto nível que não sabe como interagir com o hardware (mais precisamente, o video) de modo a mostrar os caracteres na tela. O printf (incluído na biblioteca stdio.h) pressupõe que existe uma função "putchar" que deverá ser chamada sempre que for necessário apresentar um caracter no vídeo. O módulo crt0.s deve ter essa função definida. Inicialmente, será criado um módulo crt0.s muito simples, com funções básicas minimamente necessárias para ter um sistema funcional. Analise o código a seguir e as descrições para entender como o sistema é iniciado.

Mas agora não era pra falar de C?" 
Sim, mas o C precisa de algumas funções básicas que é mais simples de fazer diretamente em assembly. O módulo crt0.s faz a comunicação de mais baixo nível com o hardware durante a inicialização do sistema, sendo portanto muito razoável que seja implementado em assembly.

         .module crt0
         .globl  _main
         .area	  _HEADER (ABS)
         .org    #0
init:
;; Read the Z80 Stack address
         ld      sp,(#0x57D2)
;; Initialise global variables
         call    gsinit
         call	  _main
         jp	  _exit
         .area   _CODE
         .area   _CABS
;; Ordering of segments for the linker.
         .area	  _HOME
         .area	  _CODE
         .area   _GSINIT
         .area   _GSFINAL
         .area   _DATA
         .area   _BSEG
         .area   _BSS
         .area   _HEAP
         .area   _CODE
; -----------------------------
; putchar
; -----------------------------
_putchar_start::
_putchar::
_putchar_rr_s:: 
         ld      hl,#2
         add     hl,sp
         ld      a,(hl)
_putchar_print::
         ld      hl,(0x57D0)
         ld      (hl),a
         inc     hl
         ld      (0x57D0),hl
         ret
_putchar_rr_dbs::
         ld      a,e
         jr      _putchar_print
putchar_end::
_exit::
         jr      _exit
         .area   _GSINIT
gsinit::
;; outdevice = 0x00 = video
         xor     a
         ld      (#0x57CD),a
         ld      a,#60                  ; screen lines
         ld      (#0x57CB),a
         ld      a,#80                  ; columns
         ld      (#0x57CC),a
         ld      hl,(#0x57D4)           ; get VRAM address
         ld      (#0x57D0),hl           ;current video memory address to print
         xor     a
         ld      (#0x57CE),a		; video cursor y
         ld      (#0x57CF),a		; video cursos x
         .area   _GSFINAL
         ret

Análise do Módulo crt0.s

.org #0
A bios do Z80SoC é armazenada numa ROM acessada a partir do endereço de memória 0x000. org #0 diz ao compilador para alocar o código a partir deste endereço.
call gsinit
Deve conter código que inicia variáveis e registradores necessários ao correto funcionamento do computador quando ele inicia. Os endereços de memória utilizados nesta rotina estão descritos em Variáveis do Sistema.
call _main
Se procurar, não vai encontrar a rotina _main em crt0.s. A função _main (e este label) não deve se alterado em crt0.s pois é um label especial que será utilizado durante o processo de compilação e ligação com o programa principal em C. Programas em C tem uma função especial denominada main, que é o ponto de entrada ou execução (ou programa principal), e é esta função do C que é chamada pela instrução call  _main.
_putchar
Esta função é necessária para se utilizar o printf do C. Nossa função putchar obtêm o caracter a imprimir (que é armazenado na pilha), obtêm o endereço da VRRAM (0x57D0) onde o próximo caracter deverá ser mostrado, escreve naquele endereço (efetivamente, mostra o caracter no vídeo), incrementa o endereço da tela e salva de volta em 0x57D0. Nesta altura do desenvolvimento, o putchar é muito simples e não sabe nem mesmo como lidar com caracteres de controle como CRLF (nova linha), e fim de tela. Estes recursos podem ser acrescentados a qualquer momento ao módulo crt0.s.
jp _exit'
Se o programa principal (_main) terminar a execução, ele retornará e entrará num loop. Isto evita o processamento de partes do código que não desejamos, e eventuais travamentos. 


Evite Problemas: ld sp,(#0x57D2)
A primeira ação razoável a se fazer ao iniciar o sistema é assegurar que a pilha do Z80 é alocada adequadamente. O Z80SoC tem uma variável interna que contêm o endereço da pilha (definido inicialmente para a posição de memória mais alta disponível). 
Imagine que o registrador SP seja iniciado com o valor zero durante o boot do sistema, e em seguida seja efetuado uma instrução "call  gsinit". O endereço de memória para retorno após término da rotina gsinit é colocado na pilha, mas como a pilha está apontando para ROM, o valor não é realmente salvo. A instrução ret vai obter o endereço de retorno da pilha, e seja lá o que estiver armazenado na ROM apontada pela pilha seja utilizado, o que normalmente causa situações imprevisíveis e travamentos.

Programa de Teste do Z80SoC em C

Atualização da ROM com nova BIOS

Descrição do Hardware do Z80SoC em VHDL

LEDs

Chaves Micro Switches

Chaves Push Button

Display LEDs de 7 Segmentos

ROM

RAM

Vídeo RAM

RAM de Tabela de Caracteres de Vídeo (CharRAM)

Display LCD (Spartan 3E e Terasic DE2-115)

Botão Rotativo (Spartan 3E)

Teclado PS/2

VGA

Conclusão

Futuras Melhorias

Cores

Utilização da Memória Flash nos kits FPGA

Utilização da Memória SDRAM nos kits FPGA

Acesso ao cartão SD

Referências

fpgaforfun http://www.fpga4fun.com