sexta-feira, 16 de novembro de 2007

Pseudo DMA para o VDP

A porta de dados do VDP do MSX1 faz um incremento automático do endereço de acesso a cada instrução de transferência. Isso pode ser usado para simular um DMA no VDP. Um exemplo semelhante pode ser visto neste link.

O princípio de funcionamento consiste na manipulação dos sinais de escrita e de leitura, tanto do periférico quanto do VDP de tal forma que quando o Z80 lê um determinado endereço, o sinal de decodificação aciona tanto o sinal de leitura do periférico quando o sinal de escrita do VDP, e vice-versa para uma transferência VDP->periférico. Em ambos os casos, é utilizada a instrução de leitura do Z80, de forma que não há a necessidade de resistores de isolação de barramento, como no exemplo com o AVR. Contudo,m é importante que tal como o VDP, o periférico também possa realizar transferência em modo "burst", como é o caso de um cartão SD/MMC.






A velocidade de transferência é igual à velocidade de acesso à memória ou de acesso de I/O do Z80, ou seja, praticamente a mesma velocidade que se obteria com um acesso direto à VRAM.

Uma estimativa desta velocidade, para uma transferência de 16K, com um clock de 3,579MHz é:

LD B,0
TRANSF:
IN A,(P_DMA) (64x) 12 x 64
DJNZ TRANSF 14 / 9

Ao todo são 256 * ( (12*64) +14 ) - 5 = 200187 ciclos.

Num Z80 a 3,579MHz, isso significa 55,933ms

Então temos uma taxa de pico de aproximadamente 16K/55,933ms = 286,05Kbytes/segundo.

quarta-feira, 14 de novembro de 2007

SPI na porta de Joystick

O barramento SPI é muito utilizado como interface diversos tipos de CIS: ADCs, RTCs, DIGIPOTS, SERIAL FLASHes, e mesmo cartões SD/MMC.

Este barramento constitui-se dos seguintes sinais
MOSI - Master Out, Slave In, ou seja (Saída, do ponto de vista do MASTER)
MISO - Master IN, Slave Out (Entrada)
SCLK - Serial Clock (Saída)
SS - Slave Select

Um inconveniente do SPI é a necessidade de se utilizar um sinal SS para cada periférico conectado. Principalmente porque na porta de Joystick do MSX temos apenas 3 pinos de saída, ou seja, apenas um pino disponível para Chip Select.

A solução foi utilizar esta saída livre para realizar ao mesmo tempo o 'reset' e o 'clock' de um contador. Para distinguir um evento do outro, foi utilizada uma rede RC, de forma que um pulso mais longo provoque o 'reset' do contador, e um pulso mais curto provoque uma contagem.

Na implementação para o MSX, o pino escolhido para RESET/COUNT foi o pino 8 (pulse), que fica normalmente em 0 (zero). Para prevenir que uma contagem aconteça durante um pulso de leitura de 'paddle', uma segunda constante RC foi adicionada.

O diagrama de blocos encontra-se na figura abaixo:



E o código de seleção de dispositivo pode ser visto abaixo.

;
; Seleciona dispositivo SPI
; Entrada:
; D: número do device SPI
; Saida:
; B: estado registro 15 do psg
; Modifica:
; A, D, DI,
SELDEV:

; seleciona e salva registro e 15 do PSG
CALL SAVEPSG

; saída PULS (p 8) nível 1 por 1ms
SET SHFT,A ; PULSE 1
RES ABSEL,A ; JOY 1
OUT [PSGWR],A
CALL WAIT1MS
RES SHFT,A
OUT [PSGWR],A


; gera D pulsos
; cada pulso tem 100us de largura
; para o circuito não se confundir
; com pulso de leitura de paddle
SLDV0:
CALL WAIT100US
SET SHFT,A
OUT [PSGWR],A
CALL WAIT100US
RES SHFT,A
OUT [PSGWR],A
DEC D
JR NZ,SLDV0
; salva valor reg 15 psg
LD B,A
EI
RET

sexta-feira, 9 de novembro de 2007

Enviando dados via RS232 na porta de Joystick

Em muitos microcontroladores sem UART real é comum emular este periférico gerando os bits, um a um, via Software. Mas para fazer isso é necessário saber exatamente o tempo de execução de cada instrução, de forma a se produzir as temporizações corretas.

No MSX deve-se levar em consideração não somente os ciclos de máquina gastos com cada instrução, mas também o WAIT STATE inserido a cada ciclo M1.

O Ciclo M1 é gerado durante a busca de instruções do Z80 ("opcode fetch"), mas há algumas instruções onde esse ciclo ocorre mais de uma vez, ou seja, é necessário somar um ciclo a mais para cada ciclo M1 gerado.

Um exemplo é a instrução NEG:

INSTR BYTES M1
NEG 2 OCF(4)/OCF(4)
Esta instrução gasta ao todo 10 ciclos de máquina para ser executada no MSX.

Uma relação completa das instruções do Z80, bem como da quantidade de ciclos por instrução, e a quantidade de ciclos M1 pode ser encontrada neste link: http://www.z80.info/z80ins.txt

A rotina abaixo envia um byte recebido no registrador A pelo pino 6 da porta B de joystick.


;
; Bits do Registro 15 do PSG
;
BTXD EQU 2
ABSEL EQU 6

;
; Registros do PSG
;
PSGAD EQU 0A0H
PSGWR EQU 0A1H
PSGRD EQU 0A2H


;
; Constantes

; BaudRates
;BAUD EQU 8 ; 19200 Bauds, -0.32% erro
;BAUD EQU 14 ; 14400 Bauds, -1.8% erro
BAUD EQU 25 ; 9600 Bauds, -0.32% erro
;BAUD EQU 59 ; 4800 Bauds, -0.32% erro
;BAUD EQU 127; 2400 Bauds, -0.32% erro
;BAUD EQU 255 ;1200 Bauds, 2.6% erro



;
; A: Byte a ser transmitido
; B: Estado atual do registrador 15 do PSG
;
SND232:
LD C,A
LD A,B

; Start bit
LD L,BAUD
CALL SND0 ; 17+1 + (SEND0)

; 8 bits
LD B,08H ; 7+1
LD L,BAUD ; 8

S20:
RRC C ; 8+2
CALL C,SND1 ; 17+1 + (SEND0)/ 10+1 F
CALL NC,SND0 ; 17+1 + (SEND0)/ 10+1 F
DJNZ S20 ; 13+1 b<>0/ 8+1 b=0

; Stopbit - return line to IDLE state
LD L,BAUD ; 8
CALL SND1 ; 17+1 + (SEND0)
LD B,A ; 4+1
RET ; 10+1


SND0:
RES BTXD,A ; txd=0 8+2
OUT [PSGWR],A ; 11+1
S01: DEC L ; 4+1
JP NZ,S01 ; 10+1 ;
RET ; 10+1


SND1:
SET BTXD,A ; txd=1 8+2
OUT [PSGWR],A ; 11+1
S01: DEC L ; 4+1
JP NZ,S01 ; 10+1 ;
RET ; 10+1


E Antes de imprimir, é necessário desabilitar as interrupções e inicializar o registro 15:
PRNTJ232:
DI
LD A,15
OUT [PSGAD],A
IN A,[PSGRD]
LD [PSGSAV],A

SET ABSEL,A ; JOY B
SET BTXD,A ; SDA=1
OUT [PSGWR],A
LD B,A