segunda-feira, 11 de maio de 2015

Temporização do código na geração de réplicas dos sinais de vídeo do TK90, MSX1 e MSX2


Este projeto começou há alguns meses atrás, precisamente em 31/12/2014, como um passatempo em meu último dia de férias.

Eu estava relendo o artigo do Victor sobre o Tic Tac Blue e em determinado momento ele comenta que o circuito dele gerava uns artefatos porque entre fechar um loop e abrir outro ele perdia alguns ciclos de clock.


O circuito é baseado num PIC16F688 rodando a 20MHz
 Fiquei encucado com aquilo e na falta do que fazer comecei a brincar com o MPLAB. Nas primeiras experiências descartei a possibilidade de gerar vídeo por interrupções, mesmo a 20MHz. Para gerar vídeo, ainda mais em VGA tem que ser realmente contando ciclo a ciclo.

Então imaginei gerar cada parte do vídeo (sincronismo, bordas e vídeo ativo) com alguns loops. Mais ou menos algo assim:

video:

...
        movlw N_LINHAS
        movwf CONTA
_loop1:
        call GERA_LINHA
        decfsz CONTA,F
        goto _loop1
        movlw N_LINHAS
        movwf CONTA
_loop2:
        call GERA_LINHA
        decfsz CONTA,F
        goto _loop2
...
...
        goto video 



De cara o problema já se apresentou: Entre o começo de um loop e o final do outro perdem-se 2 ciclos. Não sei se foi exatamente assim o problema que o Victor enfrentou, mas como havia dito eu queria alguma coisa para distrair a cabeça isso já serviu:

A primeira coisa quer fiz foi analisar o exato número de ciclos gastos.


video:

...
        movlw N_LINHAS     ; 1
        movwf CONTA        ; 1
_loop1:
        call GERA_LINHA    ; N  2 do call, x da linha, 2 do return
        decfsz CONTA,F     ; 1 o goto vira um nop quando pulado
        goto _loop1        ; 2 na iteração, 1 quando é pulado 
        movlw N_LINHAS     ; 1
        movwf CONTA        ; 1
_loop2:
        call GERA_LINHA    ; N
        decfsz CONTA,F     ; 1
        goto _loop2        ; 2
...
...
        goto video

daí considerando as iterações:

video:

loop 1:
CONTA=4->N+1+2 ciclos
CONTA=3  N+1+2
CONTA=2  N+1+2
CONTA=1  N+1+2
CONTA=0  N+1+1+1
movlw    1 ciclo
movwf    1

loop 2:
CONTA=N_LINHAS    N+1+2
CONTA=N_LINHAS-1  N+1+2
...
...
CONTA=1  N+1+2
CONTA=0  N+1+1+1

...
goto video

Assim entre cada iteração temos N+3 ciclos e entre a última iteração do loop1 para a primeira iteração do loop2 temos N+5 ciclos.

Eu gastei algum tempo pensando em como poderia fazer para fazer para gastar o mesmo tempo entre a iteração e a passagem de um loop para o outro. Depois de algumas tentativas frustradas de colocar NOPs antes e depois dos loops, cheguei a uma solução que é muito simples:

Colocar o movlw dentro do loop!


Dessa forma:


video:

...
        movlw N_LINHAS     ; 1
        movwf CONTA        ; 1
_loop1:
        call GERA_LINHA    ; N  2 do call, x da linha, 2 do return
        movlw N_LINHAS     ; 1
        decfsz CONTA,F     ; 1 o goto vira um nop quando pulado
        goto _loop1        ; 2 na iteração, 1 quando é pulado 

        movwf CONTA        ; 1
_loop2:
        call GERA_LINHA    ; N
        movlw N_LINHAS     ; 1

        decfsz CONTA,F     ; 1
        goto _loop2        ; 2 / 1
...
...
        goto video         ;2
 
Assim o tempo gasto nas iterações fica:

video:

loop 1:
...
CONTA=4  N+1+1+2
CONTA=3  N
+1+1+2
CONTA=2  N
+1+1+2
CONTA=1  N
+1+1+2
CONTA=0  N
+1+1+1
movwf    1

loop 2:
CONTA=N_LINHAS    N
+1+1+2
CONTA=N_LINHAS-1  N
+1+1+2
...
...
CONTA=1 
N+1+1+2
CONTA=0 
N+1+1+1
...
goto video 2


Daí temos um tempo entre cada iteração de  N+1+1+2 ciclos e entre a última iteração do loop1 para a primeira iteração do loop2 temos N+1+1+1+1 ciclos, ambos resultando em N+4 ciclos!

Mas a coisa não pára por aí.... Ainda faltava resolver um último detalhe... o GOTO VIDEO no final consome 2 ciclos.O que fazer nesse caso?

A resposta para esse problema exigiu pensar um pouco mais. Quando estava quase desistindo veio a inspiração.

A última linha da tela teria que ser colocada fora do loop.

do_video:
        movlw N_LINHAS     ; 1 - valor inicial do primeiro loop
frame:
        movwf CONTA        ; 1
_loop1:
        call GERA_LINHA    ; N  2 do call, x da linha, 2 do return
        movlw N_LINHAS     ; 1
        decfsz CONTA,F     ; 1 o goto vira um nop quando pulado
        goto _loop1        ; 2 na iteração, 1 quando é pulado

        movwf CONTA        ; 1
_loop2:
        call GERA_LINHA    ; N
        movlw N_LINHAS     ; 1
        decfsz CONTA,F     ; 1
        goto _loop2        ; 2
...
...

        movwf CONTA        ; 1
_last_loop:
        call GERA_LINHA    ; N
        nop                ; 1 dummy, pois segue trecho sem iteração
        decfsz CONTA,F     ; 1
        goto _last_loop    ; 2

       
        nop                ; 1 

_last_line:
        call GERA_LINHA    ; N   última linha
        movlw  N_LINHAS    ; 1  quant. de linhas para o primeiro loop       
        goto frame         ; 2


Desta forma, entre a última linha gerada e o retorno ao primeiro loop temos N+1+2+1 ou seja novamente N+4 ciclos.



Assim para gerar vídeo basta definir quantas linhas de de cada tipo serão geradas e criar as rotinas para preencer cada linha, lembrando de descontar 8 ciclos por linha equivalentes aos 4 gastos nas iterações mais 2 do CALL e mais 2 do RETURN.

A título de exemplo o frame para gerar o vídeo do TK90X, conforme as medições que eu descrevi num artigo anterior fica:

; TK90X ULA Frame                     
; N_LINHAS TIPO                       
;   1      Heq1                       
;   3      Vser                       
;   1      Heq2                       
;  20      Brancas                    
;  15(33)  Borda topo NTSC(PAL)      
;  92      Video ativo primeira metade (barras de cor)
;   8      texto
;  92      Video ativo segunda metade (barras de cor)
;  22(54)  Borda de baixo NTSC(PAL)   
;   7      Brancas                      
;   1      Branca, ultima linha        
;-----                                
; 262 (312) linhas








2 comentários:

Leandro Heck disse...

Como não tem comentário, vou ser o primeiro. Achei muito legal tua postagem. Achei um link no Hackaday dizendo (full explanation - in portuguese - here) e achei mais legal ainda!! Valeu por ter compartilhado isso :)

Leandro Heck disse...

Ah, esqueci de dizer que o link TicTacBlue não funcionou. Tem algum outro ponteiro pra ele?