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 |
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:
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 :)
Ah, esqueci de dizer que o link TicTacBlue não funcionou. Tem algum outro ponteiro pra ele?
Postar um comentário