Dissecando a ULA do TK90X - Parte 8



O MULTIPLEXADOR DE ENDEREÇOS


Já vimos na parte 7 como a ULA gera o /RAS e /CAS. Agora vamos completar a explicação sobre as DRAMs, mostrando como ela faz o acesso aos endereços.

A idéia aqui é passar por todas as posições possíveis de memória, já que para a DRAM não perder o conteudo, ela precisa de um ciclo de refresh várias vezes por segundo, para recarregar os capacitores internos. No caso dos TKs/ZX Spectrum, as DRAMs usadas são as MCM4116 ou compatíveis/similares, que tem uma matriz de 128 linhas por 128 colunas, logo 16384 posições possíveis. Para acessar todas as posições precisaríamos de um contador de 13 bits.


No esquema temos um contador simples de 13 bits formado pela dupla em cascata de U33 e U34. O VidClock (inverso de vRAS, ver a parte 7) faz o papel de pulso de clock, enquanto o VCrst (reset do contador vertical, ver a parte 4) reseta o contador para 0 novamente.

No VHDL, nenhum mistério:
   

   
    process (VidClock, VCrst)
    begin
    
        if( VCrst = '1' ) then                      -- Se o contador vertical resetou
        
            cnt_refresh <= ( OTHERS => '0' );       -- resetamos o contador do refresh
            
        elsif falling_edge( VidClock ) then
        
            cnt_refresh <= cnt_refresh + 1;         -- senão, continuamos incrementando
            
        end if;
        
    end process;
	


Agora vamos criar os sinais intermediários, que formarão a matriz de endereços posteriormente.


U35 é um tranceiver bi-direcional, mas como a porta de direção esta travada em nivel alto, os dados sempre fluirão das portas A para a B, logo, os bits de 0 a 4 do contador e de 8 a 10, criarão os sinais VA0...7.

U36 e U37 são seletores 2 para 1, de quatro entradas cada. O pino 1, entrada A/B, de cada um deles está ligado ao sinal AL2 que fará a seleção entre os sinais. Exemplificando, em U36, temos na entrada 1A o bit 11 e na entrada 1B, o bit 5. Se AL2 for nivel baixo no pino A/B, a saída 1Y recebe a entrada 1A, no caso o bit 11 do contador. Se AL2 fosse nivel alto, 1Y receberia 1B. Isso acontece ao mesmo tempo com todas as entradas e saídas, ja que os dois CIs estão ligados em paralelo, recebendo os mesmos sinais de controle.

Uma particularidade é que todos os três CIs desse esquema são tri-state, ou seja, as saídas ficam "abertas", sem estado definido, se o sinal de controle, no caso aqui o VidBusEn, não for nivel baixo. Isso é uma garantia a mais que as saídas não vão estar habilitadas quando for a vez da CPU usar o barramento.

No VHDL, também não tem absolutamente nada diferente do que falamos.
	
	

    process ( vidbus_en, AL2, cnt_refresh )
    begin
    
        if vidbus_en = '0' then                        -- Fase da ULA
        
            VA( 0 ) <= cnt_refresh( 0 );
            VA( 1 ) <= cnt_refresh( 1 );
            VA( 2 ) <= cnt_refresh( 2 );
            VA( 3 ) <= cnt_refresh( 3 );
            VA( 4 ) <= cnt_refresh( 4 );
            VA( 5 ) <= cnt_refresh( 8 );
            VA( 6 ) <= cnt_refresh( 9 );
            VA( 7 ) <= cnt_refresh( 10 );

            if ( AL2 = '0' ) then
            
                VA( 8 )  <= cnt_refresh(11);
                VA( 9 )  <= cnt_refresh(12);
                VA( 10 ) <= '0';
                VA( 11 ) <= '1';
                VA( 12 ) <= '1';
                VA( 13 ) <= '0';
                
            else
            
                VA( 8 )  <= cnt_refresh( 5 );
                VA( 9 )  <= cnt_refresh( 6 );
                VA( 10 ) <= cnt_refresh( 7 );
                VA( 11 ) <= cnt_refresh( 11 );
                VA( 12 ) <= cnt_refresh( 12 );
                VA( 13 ) <= '0';
                
            end if;
        else
            VA( 13 downto 0 ) <= ( OTHERS => 'Z' );        -- Fase da CPU, entao libera o barramento de enderecos para o Z80
        end if;

    end process;



Aqui cabe uma breve explicação do porque das linhas do contador não irem diretamente na matriz. Acredito que todo usuário de Spectrum-compatível deve ter notado o modo estranhíssimo da montagem da tela durante a carga de algum jogo. Na verdade a aparente bagunça do endereçamento visa uma maneira muito otimizada de ler nas memórias os pixels da tela e a informação da área de atributos durante o refresh do vídeo. Na imagem abaixo é um resumo de como funciona na realidade a relação entre a posição de memoria x posição da tela x posição do atributo.


Para exemplificar, vamos considerar a primeira linha (topo) de pixels de um caracter na posição 10,15 da tela. Este teria como endereço de memória a posição 18511, porque:

0100 1000 0100 1111b = 18511

De acordo com a nossa referência:
Coluna = 01111b = 15
Linha = 01010b = 10
Linha do bloco = 000b = 0


Endereço Display
Fixo CA12 CA11 CA7 CA6 CA5 CA10 CA9 CA8 CA4 CA3 CA2 CA1 CA0
010 0 1 0 0 0 0 1 0 0 1 1 1 1


Enquanto que o atributo referente a essa posição, estaria em 22863, porque:

0101 1001 0100 1111b = 22863

Endereço do Atributo
Fixo CA12 CA11 CA10 CA9 CA8 CA4 CA3 CA2 CA1 CA0
010110 0 1 0 1 0 0 1 1 1 1


Notem que CA12...8 e CA4...0 são exatamente iguais pros dois endereços de memória, então na verdade, numa única posição do contador de refresh podemos fazer a leitura do byte para os pixels e do byte de atributos. Vamos olhar novamente a tabela de formas de onda do vRAS e vCAS.


Nas subidas do clock de AL1, é quando a ULA faz a leitura do byte pros pixels e nas subidas de AL2, acontece a leitura dos atributos. Estas partes estão marcadas em vermelho na nossa tabela. Percebam que um ciclo de clock antes o vRAS abaixa, seguido de dois vCAS, um pra subida de AL1, outro pra subida de AL2, repetindo o processo em seguida, porque a cada grupo de 8 ciclos do clock 3.5Mhz, temos a leitura de dois bytes de video e dois bytes de atributos. É posivel notar também que para ganhar ainda mais velocidade, o pixel e o atributo ficam na mesma linha da matriz, já que é usado apenas um vRAS, seguido de dois vCAS.

Bem, finalizando a parte de leitura das DRAMs pela ULA, temos a saída propriamente dita, onde irão as ligações externas.


Aqui novamente temos o mesmo tipo de seletor 2 para 1 em paralelo, formado por U38 e U39. Eles selecionam as linhas ou colunas da matriz, colocando-as nos pinos esternos da ULA. O chaveamento entre linhas/colunas acontece pelo sinal de RAS, ligado em duas portas em série de U40 para gerar um atraso para o sinal. Finalmente, VidBusEN controla a saída, então se ele fica em nivel alto, o barramento fica livre para a CPU.

Em VHDL:
	
    
    process ( OSC )
    begin
        if rising_edge( OSC ) then
        
            -- Gera o sinal de RAS com atraso  
            RAS_Delayed <= RAS;
            
        end if;
    end process;

    process ( RAS_Delayed, vidbus_en, VA )
    begin
    
        if vidbus_en = '1' then                -- É a vez da CPU usar as memorias dinamicas
                                               
            VRAM_A <= ( OTHERS => 'Z' );       -- então liberamos os pinos, colocando-os em tri-state
                                               
        else                                   -- É a vez da ULA usar
                                               
            if ( RAS_Delayed = '0' ) then      -- Verifica o RAS atrasado. A linha ja foi posicionada?
                                               
                VRAM_A <= VA( 13 downto 7 );   -- posiciona a coluna
                                               
            else                                      
                                               
                VRAM_A <= VA( 6 downto 0 );    -- posiciona a linha
                                               
            end if;
        end if;
        
    end process;



Voltar para o índice




Dúvidas, sugestões? Use o espaço abaixo.


Voltar - Home


Comente



COMENTÁRIOS DESABILITADOS NO MOMENTO! RETORNAM EM BREVE
É expressamente proibido a reprodução total ou parcial deste texto sem a minha devida autorização por escrito.