sábado, 23 de fevereiro de 2008

Biblioteca SPI para porta de Joystick

A porta de Joystick pode ser utilizada como uma porta SPI, permitindo assim ligar um grande número de dispositivos, como memórias flash seriais, relógios RTC, conversores A/D e D/A, displays de celulares, cartões MMC/SD, etc.

Eis abaixo um código básico para comandar um dispositivo na porta B do joystick.

Uma desvantagem do protocolo SPI é necessitar de um sinal de Chip Select para cada dispositivo. Mas isso pode ser contornado, utilizando um duplo Flip-Flop, permitindo assim que uma cadeia de dispositivos seja conectada simultaneamente.

A velocidade de transmissão deve ser algo em torno de 1,7KBytes/segundo.

; Biblioteca SPI para a porta de Joystick do MSX
; Danjovic 2008 - danjovic@vespanet.com.br
; http://hotbit.blogspot.com
;
; Version: 0.1 : 18/02/2008
; Version: 0.2 : 22/02/2008 - Added 8 clock pulses after release of CS line.

; Caracteristicas:
; Modo 0 ->; CPOL=0, CPHA=0
; Sample in Rise, shift in Fall
;
; Pinout:
; MISO =>; 1 (UP)
; MOSI =>; 6 (TRGA)
; MSSL =>; 7 (TRGB)
; MSCK =>; 8 (PULSE)

;SPIUP - Init SPI port B
;SPIDW - Release SPI port
;SPITX - Transmit a byte
;SPIRX - Receive a byte
;SPIXF - Transfer a byte



; Bits do Registro 14 do PSG
;
BMISO EQU 1 ; UP
MSKSO EQU 0FEH

; Bits do Registro 15 do PSG
;
BMOSI EQU 2 ; TRG1
BMSSL EQU 3 ; TRG2
BMSCK EQU 5 ; PULSE
ABSEL EQU 6 ; A/B SELECT

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


SPIUP: ; Initialize SPI Port
; Inputs: None
; Outputs: A - Reg 15 state
; Changes: A,EI
DI
LD A,15
OUT [PSGAD],A
IN A,[PSGRD]
LD [PSGSAV],A
SET ABSEL,A
RES BMSSL,A ; Chip Select low
OUT [PSGWR],A
RET

SPIDW: ; Release SPI Port
; Inputs: A - Reg 15 State
; Outputs: None
; Changes: A,EI
LD B,A ; Save Reg 15 State
; Release /CS line off
LD A,15
OUT [PSGAD],A
LD A,B
SET BMSSL,A
OUT [PSGWR],A

; Dummy RX - 8 clock pulses
SCF
CALL SPIRX

; Restore PSG reg 15 State
LD A,[PSGSAV]
OUT [PSGWR],A
EI
RET

SPITX: ; Transmit a byte
; Inputs: A - Reg 15 State
; C - Byte to transmit
; PSG Reg 15 already selected
; Outputs: None
; Changes: AF,B,C
LD B,8
TXBIT: RL C ; MSB -> CY Flag
SET BMOSI,A
JR C,TPUTB
RES BMOSI,A
TPUTB: OUT [PSGWR],A
; CLOCK()
SET BMSCK,A
OUT [PSGWR],A
RES BMSCK,A
OUT [PSGWR],A
;
DJNZ TXBIT


SPIRX: ; Receive a byte
; Inputs: A - Reg 15 State
; CY - MOSI line state while receiving
; PSG Reg 15 already selected
; Outputs: C - Byte received
; Changes: AF,B,C,D

; Set MOSI line state
SET BMOSI,A
JR C,RXHIGH
RES BMOSI,A
RXHIGH: OUT [PSGWR],A

LD B,8
RXBIT: ; CLOCK HI
SET BMSCK,A
OUT [PSGWR],A
LD D,A ; Save Reg 15 State
; Read bit
LD A,14
OUT [PSGAD],A
IN A,[PSGRD]
AND MSKSO
NEG ; Bit MSKSO->CY
RL C ; LSB<-CY
; CLOCK LO
LD A,15
OUT [PSGAD],A
LD A,D ; Restore Reg15 state
RES BMSCK,A
OUT [PSGWR],A
;
DJNZ RXBIT


SPIXF: ; Transfer a byte
; Inputs: A - Reg 15 State
; C - Byte to transmit
; PSG Reg 15 already selected
; Outputs: E - Byte received
; Changes: AF,B,C,D,E

LD B,8

XFBIT: ; Write bit
RL C ; MSB -> CY Flag
SET BMOSI,A
JR C,XFPUTB
RES BMOSI,A
XFPUTB: OUT [PSGWR],A
; CLOCK HI
SET BMSCK,A
OUT [PSGWR],A
LD D,A ; Save Reg 15 State
; Read bit
LD A,14
OUT [PSGAD],A
IN A,[PSGRD]
AND MSKSO
NEG ; Bit MSKSO->CY
RL E ; LSB<-CY
; CLOCK LO
LD A,15
OUT [PSGAD],A
LD A,D ; Restore Reg15 state
RES BMSCK,A
OUT [PSGWR],A
;
DJNZ XFBIT

terça-feira, 19 de fevereiro de 2008

RX232 - Primeira versão compilável

Integrei hoje a rotina de recepção de um único byte na rotina de recepçao de buffer, e já tenho uma estimativa do tamanho do 'driver' de recepção após compilado: 122 bytes.
Agora tenho que passar um pente fino na lógica, no uso dos registradores e da pilha. Mas eventuais correções não vão alterar sensivelmente o tamanho do código gerado.

Tenho ainda que construir o adaptador RS232 (com um MAX232) para poder testar o hardware.

Segue abaixo o código:
;
; Recepcao serial via
; porta de Joystick do MSX
;
; danjovic@hotmail.com
; http://hotbit.blogspot.com
;
; Versao 0.1 18/02/2008
;
; Licenca de uso: GNU GPL
;
;
; Compilado no ASMSX
; http://www.robsy.net/asmsx.htm

ORG 0E000H

.BASIC
.START RX232

;
; Bits do Registro 14 do PSG
;
BRTS EQU 0 ; UP
BRXD EQU 1 ; DOWN
MRXD EQU 0FDH ; mask for bit BRXD
;
; Bits do Registro 15 do PSG
;
BTXD EQU 2 ; TRG1
BCTS EQU 5 ; TRG2
ABSEL EQU 6 ; A/B Port select

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

; Variaveis em RAM
PSGSAV EQU 0F974H ; RS232 putback
; Not used in MSX
;
; Constantes

; BaudRates
;RBAUD EQU 6 ; 19200 Bauds, (6)
;RBAUD EQU 10 ; 14400 Bauds, (10)
RBAUD EQU 18 ; 9600 Bauds, (17-19)
;RBAUD EQU 42 ; 4800 Bauds, (41-43)
;RBAUD EQU 92 ; 2400 Bauds, (90-93)
;RBAUD EQU 190 ; 1200 Bauds, (188-191)

ERFUL EQU 0FFH ; Buffer cheito
ERFRM EQU 0FEH ; Framing error
ERBRK EQU 0FDH ; RTS off antes startbit


RX232:
; Entradas: HL = Ender Buffer em RAM
; C = Tam maximo do buffer
; Saicas : D = Bytes recebidos
; CY = 0, OK
; CY = 1, Error; A=Codigo erro
;
LD A,15
OUT [PSGAD],A
IN A,[PSGRD]
LD [PSGSAV],A
SET ABSEL,A
LD E,A
LD D,0
NEWBYTE: LD A,14
OUT [PSGAD],A
IN A,[PSGRD]
AND A ; Clear carry
BIT BRTS,A
LD A,D
JR NZ,EXIT ; RTS off

LD A,D
CP C
LD A,ERFUL
JR C,EXIT ; Buffer full

PUSH BC
PUSH HL
CALL RECEIVE
POP HL
POP BC
JR C,EXIT ; Error

LD [HL],A ; Save byte
INC HL
INC D ; Incr counter
JR NEWBYTE ; Next byte

EXIT: PUSH AF
LD A,15
OUT [PSGAD],A
LD A,[PSGSAV]
OUT [PSGWR],A
POP AF
RET


RECEIVE:
; Recebe um único caractere
; E - State of PSG register 15
; Currently selected PSG rgister 14
; Return Byte received in Register A

; init local variables
LD B,10 ; bits to receive
LD H,RBAUD ; Baud rate delay
LD L,H
AND A ; Clear CY
RR L ; L= L\2, Half bit timer
SET BCTS,E ; CTS ON

; CTS=ON
; Set PSG register 15
LD A,15
OUT [PSGAD],A
LD A,E
RES BCTS,E ; Reg 15 with CTS OFF
OUT [PSGWR],A

; Set PSG register 14
LD A,14
OUT [PSGAD],A

WAITSTRT: ; Awaits Start Bit
IN A,[PSGRD]
BIT BRXD,A
JR Z,MEIOBIT ;
BIT BRTS,A
JR NZ,WAITSTRT

;Break: RTS went off before
; Start bit
LD A,ERBRK
SCF
RET

; Receive 10 bits
; First is discarded,
; last is the Stop Bit
RXBIT: LD L,H ; Reload delay time

; Delay for bit time
MEIOBIT: DEC L
JR NZ,MEIOBIT

RR C
IN A,[PSGRD]
AND MRXD ; Mask RXD bit
NEG ; RXD to Carry Flag
DJNZ RXBIT ;
; Here Carry contains Stop Bit

; CTS=OFF
; Set PSG register 15
LD A,15
OUT [PSGAD],A
LD A,E ; Reg E has bit CTS off
OUT [PSGWR],A

LD A,ERFRM
CCF ; invert CY to indicate
; Framing error
RET C

LD A,C ; Se sucesso, retorna
RET ; NC, A=byte recebido

segunda-feira, 18 de fevereiro de 2008

Código base RS232

Eis o código da rotina de recepção de "buffer". Agora é preciso adaptar o código da rotina anterior para criar a rotina RXBYTE. Também é preciso acrescentar o código que chaveia do registro 14 para o registro 15 do PSG. Essa operação é necessária devido à arquitetura do PSG

RX232:
; Entradas: HL = Ender Buffer em RAM
; C = Tam maximo do buffer
; Saicas : D = Bytes recebidos
; CY = 0, OK
; CY = 1, Error, A=Codigo erro
;
LD A,15
OUT [PSGAC],A
IN A,[PSGRD]
LD [PSGSAV],A
SET ABSEL,A
LD E,A
LD D,0
NEWBYTE: LD A,14
OUT [PSGAD],A
IN A,[PSGRD]
AND A ; Clear carry
BIT RTS,A
LD A,D
JR NZ,EXIT ; RTS off

LDA A,D
CP C
LD A,ERFUL
JR C,EXIT ; Buffer full

PUSH BC
PUSH HL
CALL RXBYTE
POP HL
POP BC
JR C,EXIT ; Error

LD [HL],A ; Save byte
INC HL
INC D ; Incr counter
JR NEWBYTE ; Next byte

EXIT: PUSH AF
LD A,15
OUT [PSGAD],A
LD A,[PSGSAV]
OUT [PSGWR],A
POP AF
RET

Delineando a rotina de recepção

Após alguns tropeços enquanto escrevia a rotina de recepção de um "buffer", resolvi definir melhor o funcionamento do código que chama a rotina de recepção de dados, do post anterior.

Eis abaixo o fluxograma da rotina, que deve ser chamada com 2 parâmetros:
- Endereço do buffer que vai receber os dados
- Tamanho do buffer de recepção

Uma simplificação importante que fiz, foi que considerar que o buffer está vazio na inícion da chamada da rotina. Isso evita a necessidade de uma chamada de inicialização do buffer e simplifica o tratamento de erros.

Outra vantagem é não depender diretamente de um buffer em RAM para receber temporariamente os caracteres da RS232, o que gerava a necessidade de um LDIR para o destino dos bytes. Com o esquema atual, basta apontar para a região de memória que deve receber os dados e dizer qual o tamanho máximo do buffer de dados.

Caso tudo a recepção tenha se processado corretamente, a quantidade de bytes recebidos é retornada. Caso contrário, um dos códigos de erro é retornado:
  • ERFUL - Buffer Cheiro
  • ERFRA - "Framing error", ou stop bit recebido incorretamente
  • ERBRK - Sinal RTS foi desativado pelo PC antes que o "Start" Bit fosse enviado.


sexta-feira, 15 de fevereiro de 2008

Código para a recepção RS232

Estive trabalhando no código para a recepção RS232 na porta de joystick. .

Eis um trecho da rotina de recepção de dados, que aguarda pelo Start Bit, tão logo ele seja detectado, recebe os 10 bits da palavra de dados, dentro de um só loop, para simplificar a temporização. Ao final da rotina, flag Carry contém o estado do Stop bit. Note que start bit é descartado, pois ele já havia sido detectado.
Outro detalhe importante é que o primeiro delay é de meio bit, e os subsequentes de 1 bit inteiro. Isso faz com que a amostragem seja feita no meio do tempo de bit, diminuindo assim a taxa de erro. Após o stop bit ser amostrado, ainda temos meio tempo de bit disponível, o que é importante, pois é necessário mudar registro do PSG antes de levantar novamente o sinal CTS, para fazer o PC esperar o MSX processar o dado antes de enviar outro.

;...
WAITSTART: IN A,(PSGRD)
BIT RXD,A ; Start bit?
JR Z,MEIOBIT ;yes, wait 1/2 bit before start
; to acquire samples
BIT RTS,A ; PC stopped TX ?
JR Z,WAITSTART ; No, sample again
JP BREAKERROR

MEIOBIT: LD L,BITTIME ;
SRL L ; divide by two
LD B,10 ; Ten bits.
PROXBIT: CALL DELAYBIT ; Wait Bit Time
RR C ; CY->7...0->CY
IN A,(PSGRD) ; Sample bit
AND RXD
NEG ; Bit RXD->CARRY
LD L,BITTIME ; Reload delay preset
DJNZ PROXBIT ; Wait full bit time before
; get another sample
LD A,(15)
OUT (PSGAD),A ; Select Register 15
LD A,E ; Previous state of reg 15
SET RTS,A
OUT (PSGWR),A ; deassert RTS
LD E,A ; Save state of reg15

JR NC,FRAMERR ; If stop bit=0 an framing
; error has occoured
;...
PROCESSABIT:

quinta-feira, 14 de fevereiro de 2008

Recepção serial na porta de Joystick

Fiz uns testes para determinar se o controle de fluxo por RTS/CTS poderia ser utilizado para fazer o PC esperar o MSX atender o pedido de solicitação de transmissão, e também para ver se a transmissão pode ser interrompida a qualquer momento. Os resultados do teste demonstraram que sim.

A correspondência entre os pinos da porta de joystick do MSX e a porta de PC pode ser vista na figura abaixo. Entre os dois deve ser utilizado um conversor de nível, tipo um MAX232.

MSX – Porta Joystick Dir PC – Porta Serial
Função Pino RS232 - Pino RS232
DOWN 2 RxD <- 3 TxD
UP 1 RTS <- 7 RTS
TRGA 6 TxD -> 2 RxD
TRGB 7 CTS -> 8 CTS
GND 9 GND - 5 GND

WiznetX (correção do circuito)

Algumas pessoas já me questionaram sobre como fazer a ligação de um módulo Wiznet WIZ810MJ no MSX.



Este módulo de rede é baseado no chip WM5100 e tem dois modos de interligação: SPI e memória. No modo memória, o chipa aparece para o 'host' como uma área endereçável de 32Kbytes, de acordo com o mapa abaixo (vide data sheet):




O WM5100 é alimentado por uma tensão de 3,3Volts, mas tem tolerância a sinais TTL, o que torna bem fácil a conexão ao MSX. Além disso, os sinais de controle têem a mesma polaridade (nível ativo) do Z80.

Então, todo o hardware necessário para conectar este módulo ao MSX se resume a um decodificador e um regulador de tensão LDO, que possa fornecer ao menos 183mA.

O circuito abaixo é de uma placa de protótipo. O decodificador é um LS138, que permite mapear a placa no intervalo 0000-7FFF. A temporização dos sinais /RD e /WR teve que ser adaptada para o chip W5100 pois difere em alguns pontos do Z80, em especial o sinal /WR, que fica ativo 1 ciclo de clock após o sinal MREQ do Z80. Segundo o "data sheet" do W5100 os sinais /RD e /WR têm que acontecer simultaneamente ao sinal /CS. Eu fiz umas perguntas sobre a temporização destes sinais no site do fabricante do CHIP, pois achei meio crótica a temporização "oficial" que diz que o sinal de /RD ou de /WR deve acontecer em, no máximo 1ns após o sinal de /CS, e deve retornar a nível alto 1ns antes do sinal de /CS. Mas caso seja assim mesmo, um latch controlado pelo clock do Z80 deve resolver a situação.

O regulador é o TC1108, que tem capacidade de fornecer até 300mA.




Abaixo está uma sugestão de placa para o protótipo. O módulo Wiznet deve ser montado do lado de baixo da placa.



Como eu não tenho um módulo desses, não devo montar uma placa dessas tão cedo, mas quem se aventurar a montar, seguem dois conselhos:

-O data sheet do WM5100 estipula um tempo máximo de 1ns entre o sinal de Chip Select e o READ ou Write, o que para mim significa que os sinais têem que ser simultâneos. O decodificador do meu circuito induz um certo atraso entre o sinal SLCTSLT e o CS, que eu estimo entre 30 a 40ns. Para a leitura isso não deve ser problema, pois já que o Data sheet não estipula tempo mínimo eu suponho que o sinal de RD Possa iniciar antes do sinal CS. Já para a escrita, o Z80 atrasa em 1 ciclo de clock (280ns) o sinal de WR em relaçao ao sinal MREQ. Pode ser que isso não traga problema algum, mas sempre é bom ficar e olho.

-Quando o circuito endereça na faixa de 4000-BFFF, o pino A14 está em nível alto entre - 4000-7FFF e em nível baixo entre 8000-BFFF. Isso significa que o mapa de memória vai aparecer trocado, ou seja, as áreas do TX buffer e RXbuffer vêm primeiro, entre 4000-7FFF, e os registros vêm depois, na faixa de 8000-BFFF.

sexta-feira, 18 de janeiro de 2008

Recepção RS-232 na porta de Joystick

Tenho uma estratégia para poder fazer a parte de recepção, utilizando os sinais RTS e CTS.

O Sinal RTS serve para indicar ao dispositivo de recepção, que o dispositivo de transmissão quer enviar dados. Já o sinal CTS diz é a resposta que o dispositivo de recepção dá ao dispositivo de transmissão de que já pode receber os dados.

Pois bem, utilizando estes sinais é possível sincronizar o MSX com o PC criando assim uma rotina de recepção mais inteligente, de forma a sinalizar ao PC quando este deve aguardar antes de enviar os dados. Desta maneira pode-se perder um tempo salvando o caractere, ou mesmo movendo um buffer.

É possível, por exemplo redirecionar a entrada (de teclado) para ler a partir da RS232 (como no apple) utilizando o hook da interrupção externa do Z80, gerada pelo VDP, que ocorre a 60Hz. O funcionamento é o seguinte:

Quando o VDP gera uma interrupção, a BIOS chama o Hook HTIMI no endereço 0FD9Fh. Neste momento pode-se checar a entrada de joystick para ver se o PC ativou o sinal RTS. Se o sinal RTS não estiver ativo, então não há caracteres a serem enviados e a rotina termina, devolvendo o controle à BIOS.

Mas se o sinal RTS estiver ativo, as interrupções são desabilitadas, e a rotina de recepção de um caractere é chamada. Esta rotina ativa o sinal CTS para indicar ao PC que o MSX está pronto para receber os dados.

O MSX então aguarda pelo start bit, e em seguida recebe os 8 bits do dado, e o stop bit. Depois ela desativa novamente o sinal CTS e vai processar o caractere recebido.

O Stop bit é checado para garantir que é válido. Se for inválido (“0”), então o caractere recebido é ignorado, caso contrário, ele é colocado no buffer do teclado, que é incrementado. Se o buffer estiver cheio, então a rotina termina. Se não estiver, o MSX checa se o sinal RTS ainda está ativo, e se estiver, ele recebe o próximo caractere, caso contrário, retorna

segunda-feira, 14 de janeiro de 2008

Conversor TTL-232 testado

Acabei de testar com sucesso o circuito de conversão TTL para RS-232 do post anterior. Agora o projeto está completo.

LPRINT na porta de Joystick - Código em BASIC

Acabei de testar a impressão via 'hook' na porta de joystick. O código tinha um pequeno erro mas já corrigi.

O programa ocupa apenas 83 na memória e é carregado a partir do endereço &HE000

A listagem abaixo foi capturada utilizando o lprint para o PC, após o programa ter sido executado:


5 '
10 ' Imprime na porta de Joystick B
20 ' via protocolo rs-232, 9600,N,8,1
30 '
40 ' danjovic@hotmail.com
50 '
60 FOR E=0 TO 82
70 READ B$:POKE &HE000+E,VAL("&H"+B$)
80 NEXT E
90 '
100 DEFUSR=&HE047: B=USR(0)
110 '
200 DATA F3,F5,C5,E5,4F,26,12,3E
210 DATA 0F,D3,A0,DB,A2,CB,F7,D3
220 DATA A1,CD,1C,E0,E1,C1,F1,A7
230 DATA FB,33,33,C9,6C,CD,34,E0
240 DATA 06,08,6C,CB,09,DC,3D,E0
250 DATA D4,34,E0,10,F5,2E,12,CD
260 DATA 3D,E0,47,C9,CB,97,D3,A1
270 DATA 2D,C2,38,E0,C9,CB,D7,D3
280 DATA A1,2D,C2,41,E0,C9,00,21
290 DATA 00,E0,22,B7,FF,3E,C3,32
300 DATA B6,FF,C9

domingo, 13 de janeiro de 2008

Conversor TTL-RS232

Fiz o layout de um conversor de nível RS232 para TTL a partir de um esquema que encontrei. Aproveitei apenas a parte de transmissão, que resultou no circuito abaixo.


Fiz o "layout" de uma placa de circuito impresso em face simples, de forma que ficasse o menor possível. Segue abaixo uma imagem em 300 dpi




E a disposiçao dos componentes na placa fica como a figura abaixo:




Os dois conectores são do tipo para cabo (solda) mas são soldados diretamente na placa, um virado para cima e outro virado para baixo. Assim só foi necessário um Jumper para ligar o pino 5 do conector de joystick do MSX (Vcc) ao circuito.

LPRINT na porta de Joystick

Estou trabalhando numa rotina para capturar o Hook de impressão e redirecionar o caractere à porta de joystick. O código, até o presente momento, consome 86 bytes.



;
; Impressao serial via
; porta de Joystick do MSX
;
; danjovic@hotmail.com
; http://hotbit.blogspot.com
;
; Versao 1.0 14/01/2008
;
; Licenca de uso: GNU GPL
;
;
; Compilado no ASMSX
; http://www.robsy.net/asmsx.htm


ORG 0E000H

.BASIC
.START INSTALA



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

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


; BaudRates
;BAUD EQU 6 ; 19200 Bauds, (6)
;BAUD EQU 10 ; 14400 Bauds, (10)
BAUD EQU 18 ; 9600 Bauds, (17-19)
;BAUD EQU 42 ; 4800 Bauds, (41-43)
;BAUD EQU 92 ; 2400 Bauds, (90-93)
;BAUD EQU 190 ; 1200 Bauds, (188-191)



;
; Envia o Caractere no reg A
; para a porta de B (Joystick)
; 9600,8,n,1

PRNTJ232:
;CALL 0a2h
DI
PUSH AF
PUSH BC
PUSH HL

LD C,A ; Caractere a ser impresso
LD H,BAUD ; define taxa Baud

LD A,15 ; Registro 15
OUT [PSGAD],A
IN A,[PSGRD]
;LD [PSGSAV],A
SET ABSEL,A ; Porta Joystick B
OUT [PSGWR],A
;LD B,A ; salva conteudo reg 15

CALL SND232
;LD A,[PSGSAV]
;OUT [PSGWR],A

POP HL
POP BC
POP AF
AND A
EI
INC SP ; descarta stack
INC SP
RET




SND232:
; Entradas:
; C: Byte a ser transmitido
; A: Estado atual do registrador 15 do PSG
; H: Constante para Baud Rate
;

;
; Envia Start bit (txd=0)
;
LD L,H ; 4+1
CALL SND0 ; 17+1 + (SEND0)

;
; 8 bits do dado, LSbit primeiro
;
LD B,08H ; 7+1
S20:
LD L,H ; 4+1
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


;
; Stopbit - retorna linha txd para IDLE
;
LD L,BAUD ; 8
CALL SND1 ; 17+1 + (SEND1)
LD B,A ; 4+1
RET ; 10+1


;
; Envia um bit 0
;
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

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


PSGSAV:
DS 1


;
; Instala subrotina no
; HOOK da impressora
;
; Do livro +50 dicas para o MSX
;
HLPT EQU 0FFB6H
;
INSTALA:
LD HL,PRNTJ232
LD [HLPT+1],HL
LD A,0C3H
LD [HLPT],A
RET

sábado, 12 de janeiro de 2008

Serial Bitbang a 19200 bauds na porta de Joystick

Fiz funcionar a rotina de transmissão serial na porta de joystick. Removidos alguns erros de lógica, consegui transmitir para o PC nas seguintes velocidades:

19200 Bauds, (6)
14400 Bauds, (10)
9600 Bauds, (17-19)
4800 Bauds, (41-43)
2400 Bauds, (90-93)
1200 Bauds, (188-191)

Toda a temporização é feita por software. Os números em parênteses são o parâmetro de temporização. As velocidades de 14400 e 19200 são meio críticas, mas funcionam bem.

A conexão com o PC é através do pino 6 da porta de Joystick B. Eu utilizei um MAX232, alimentado pelo pino 5 (Vcc), mas há outras alternativas[1], [2], embora eu ainda não as tenha testado.

O código fonte encontra-se abaixo.

;
; Rotinas de impressão na porta
; de Joystick do MSX
;
; danjovic@hotmail.com
; http://hotbit.blogspot.com
;
; Versao 0.5 12/01/2008
;
; Licenca de uso: GNU GPL
;
;


ORG 09000H

; Compilado no ASMSX
; http://www.robsy.net/asmsx.htm
.BASIC
.START PRNTJ232

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

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


;
; Constantes

; BaudRates
;BAUD EQU 6 ; 19200 Bauds, (6)
;BAUD EQU 10 ; 14400 Bauds, (10)
BAUD EQU 18 ; 9600 Bauds, (17-19)
;BAUD EQU 42 ; 4800 Bauds, (41-43)
;BAUD EQU 92 ; 2400 Bauds, (90-93)
;BAUD EQU 190 ; 1200 Bauds, (188-191)


;
; Rotina de teste de impressão
; na porta de Joystick
;



PRNTJ232:
DI
LD A,15
OUT [PSGAD],A
IN A,[PSGRD]
LD [PSGSAV],A

SET ABSEL,A ; JOY B
SET BTXD,A ; SDA=1

SYNC:
SET BPLS,A ; start sync
OUT [PSGWR],A
NOP
NOP
NOP
NOP
RES BPLS,A ; stop sync
OUT [PSGWR],A
LD B,A ; B=conteudo reg 15

MUDABAUD:
LD H,BAUD
LD DE,MENSAGEM
REPETE:
LD A,[DE]
INC DE
OR A
JR Z,FIM
LD C,A
CALL SND232
JR REPETE

FIM:
EI
RET


MENSAGEM:
DB "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG 0123456789 ",0

PSGSAV:
DS 1


SND232:
; Entradas:
; C: Byte a ser transmitido
; B: Estado atual do registrador 15 do PSG
; H: Constante para Baud Rate
;
; Modifica A,F,B,C,L

LD A,B ; B=Estado registro 15 PSG

;
; Start bit (txd=0)
;
LD L,H ; 4+1
CALL SND0 ; 17+1 + (SEND0)

;
; 8 bits do dado, LSbit primeiro
;
LD B,08H ; 7+1
S20:
LD L,H ; 4+1
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 - retorna linha txd para IDLE
;
LD L,BAUD ; 8
CALL SND1 ; 17+1 + (SEND1)
LD B,A ; 4+1
RET ; 10+1


;
; Envia um bit 0
;
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

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




domingo, 9 de dezembro de 2007

Adaptador CF Interno

Condensando alguns projetos de ZX Spectrum ([1], [2]) e fazendo algumas medições no adaptador CF-IDE que possuo (idêntico ao modelo à direita nesta foto) cheguei à seguinte pinagem:



A interface IDE-CF pode ser alimentada pelo pino 20 a partir dos 5Vcc do micro.

O sinal Y7 é o /CS (Chip Select) na faixa de endereços 0B8h~0BFh, disponível no pino 7 de IC5 (74LS138) no HotBit (mesmo pino de IC29 no Expert).

Uma observação importante é que esta faixa de endereços é reservada para a "light pen" da SANYO, e para controle do "VHD".

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

domingo, 28 de outubro de 2007

Paddle do NES no Arkanoid do MSX? (update)

Estava investigando o código desassemblado do Arkanoid1 para procurar uma maneira de fazer um "patch" para colocar um paddle, quando me deparei com um trecho de código bem interessante, que sugere que o MSX possa utilizar o mesmo controlador de Arkanoid do NES (novo link) , mudando apenas o conector.
O pino 6 é utilizado como sinal de clock, o pino 8 como sinal de select/LOAD e os dados vêm pelo sinal UP do joystick.

O trecho de código abaixo ilustra o protocolo.
...
42fc 3e0e      ld      a,0eh
42fe d3a0      out     (0a0h),a
4300 dba2      in      a,(0a2h)
4302 67        ld      h,a
4303 0608      ld      b,08h       ; 8 bits
4305 0e00      ld      c,00h
4307 1e00      ld      e,00h
4309 3e0f      ld      a,0fh
430b d3a0      out     (0a0h),a
430d 3e1e      ld      a,1eh
430f d3a1      out     (0a1h),a    ; clock low
4311 3e1f      ld      a,1fh
4313 d3a1      out     (0a1h),a    ; clock high
4315 3e0e      ld      a,0eh
4317 d3a0      out     (0a0h),a
4319 dba2      in      a,(0a2h)
431b 5f        ld      e,a
431c cb3f      srl     a           ; lê bit
431e cb11      rl      c           ; armazena em C
4320 10e7      djnz    4309h       ; próximo bit
4322 79        ld      a,c
4323 32c1e0    ld      (0e0c1h),a  ; armazena byte recebido 
4326 7c        ld      a,h
4327 e601      and     01h
4329 32c2e0    ld      (0e0c2h),a
432c 3e0f      ld      a,0fh
432e d3a0      out     (0a0h),a
4330 3e1f      ld      a,1fh
4332 d3a1      out     (0a1h),a    ; pino8 high
4334 3e0f      ld      a,0fh
4336 d3a1      out     (0a1h),a    ; pino8 low
4338 3e1f      ld      a,1fh
433a d3a1      out     (0a1h),a    ; pino8 high
433c 3e0e      ld      a,0eh
433e d3a0      out     (0a0h),a
4340 dba2      in      a,(0a2h)
4342 5f        ld      e,a
4343 21c4e0    ld      hl,0e0c4h
4346 7e        ld      a,(hl)
4347 73        ld      (hl),e
4348 e60f      and     0fh
434a a3        and     e
434b ab        xor     e
434c 32c5e0    ld      (0e0c5h),a
434f 47        ld      b,a
4350 3a0be0    ld      a,(0e00bh)
4353 b7        or      a
4354 c0        ret     nz

4355 cb48      bit     1,b
4357 c8        ret     z

4358 3a0ae0    ld      a,(0e00ah)
435b b7        or      a
435c ca7043    jp      z,4370h
435f af        xor     a
4360 320ae0    ld      (0e00ah),a
4363 213ce5    ld      hl,0e53ch
4366 113de5    ld      de,0e53dh
4369 3600      ld      (hl),00h
436b 010700    ld      bc,0007h
436e edb0      ldir 
4370 3e01      ld      a,01h
4372 320ce0    ld      (0e00ch),a
4375 c9        ret  
...

quarta-feira, 3 de outubro de 2007

Copy 3

Usando um compilador chamado "pasmo" consegui compilar o código de um copiador para fita cassete chamado copy 3, que "imitava" o estilo do PCTOOLS.

Este copiador foi o primeiro programa "sério" que fiz para o MSX em Assembly. Um detalhe interssante é que os caracteres em fundo inverso eram simulados com caracteres redefinidos.

Seguem abaixo 2 screenshots, rodando no BlueMSX:





domingo, 23 de setembro de 2007

Sistema de arquivos TIC-TAC

TIC-TAC: Sistema de arquivos para pequenos dispositivos.

O sistema de arquivos TIC-TAC foi concebido para utilização com pequenas unidades de armazenamentos de memória, com capacidade de até 128Kbytes. Apesar de ter sido projetado inicialmente para os "picodrives" conectados via interface I2C no MSX, outros dispositivos de memoria podem ser utilizados, bem como outros processadores ou microcontroladores.

O nome TIC-TAC vem de:

TIC Tabela identificação de caracteres
TAC Tabela de alocação de cadeias

Estas tabelas são utilizadas para organizar o armazenamento dos arquivos de forma semelhante a uma FAT. Devido à limitação de espaço, o número máximo de arquivos que podem ser armazenados é de 64. Dessa forma os arquivos podem ter nome com 8 caracteres de comprimento e um byte de atributos.

A TIC ocupa os primeiros 768 bytes da memória serial. Compõe-se de um cabeçalho com 64 bytes de cabeçalho, e de 64 entradas de arquivos (com 11 bytes). A estrututa da TIC encontra-se na tabela abaixo:

Cabeçalho:

Bytes Função/descrição
02 Assinatura - "ST"
10 Nome do dispositivo (volume)
01 Bit 7: Flag de Boot
Bits 5..0: Entrada da TIC (bits 5..0) do programa de boot.
01 Tamanho em setores) do dispositivo (Máximo 128K)
01 Tamanho da página de escrita da Flash
49 Vazio. Reserva para expansões
-----
64 Total de bytes do cabeçalho da TIC


Entradas:

08 Nome do arquivo
01 Atributos
Bit 7: Indica uma entrada apagada
Bit 6: Indica proteção contra escrita/apagamento
Bits 1..0: Tipo de arquivo
00 - Basic
01 - Binário
10 - Dados
11 - Tela
01 Tamanho do arquivo (quantidade de entradas na TAC)
01 Ponto de entrada na TAC
------
11 x 64 = 704 bytes

Total 64+704=768 bytes

Para calcular a quantidade de setores ocupados por um arquivo, a seguinte fórmula é utilizada:

QUANT = 1 + [ ( TAMARQ - 1) DIV 512 ] , onde TAMARQ é o tamanho em bytes do arquivo.

Já a TAC ocupa 256 bytes e corresponde a um mapa de setores (512bytes) da memória serial. A posição da entrada na TAC (1 byte) corresponde à posição de um setor (512 bytes) no dispositivo. Assim, a primeira entrada na TAC corresponde aos Bytes (0..511), a segunda aos bytes (512..1023), a terceira aos bytes (1024-1535) e assim sucessivamente.

O conteúdo de uma entrada da TAC armazena a posição da próxima entrada, formando assim uma cadeia de entradas. A última entrada é utilizada para armazenar quantos bytes (em múltiplos de 4) são armazenados no último setor. Como uma entrada de valor "0" (zero) significa um setor vazio (não utilizado), o valor do último setor é acrescido de um, ficando então entre 01 e 128, representando de 04 a 512bytes.

Para calcular o valor do último byte da seqüencia a seguinte fórmula é utilizada:

VALOR = 1 + [ ( TAMARQ MOD 512 ) DIV 2)

Preâmbulo dos arquivos:

Os 16 primeiros bytes do arquivo são utilizados para armazenar algumas informações que não cabem nas entradas de arquivo, como data e hora, e configuração da máquina.

Os bytes 0 a 6 armazenam informações de data e hora:

Byte Conteúdo
0 Bits 6..0 Segundos (00..59) em BCD
1 Bits 6..0 Minutos (00..59) em BCD
2 Bits 5..0 Horas (00..23) em BCD
3 Bits 5..0 Dia do mês (00..31) em BCD
4 Bits 2..0 Dia da semana (0..6), Domingo=0
5 Bit 7: Século, 0=2000, 1=1900;
Bits 4..0: Mês (1..12) em BCD
6 Bits 7..0 Ano (dezena, 00..99) em BCD

O formato dos bytes 7 a 15 depende do tipo de arquivo. Para arquivos Basic ou de Dados, estes bytes ficam sem uso. Para arquivos binários e de tela, o formato encontra-se abaixo:

Para aquivos binários:

Byte Conteúdo
7,8 Endereço de de entrada (carga) do programa na memória
9,10 Endereço de execução do programa
11 Configuração de Slots primários para carga do programa
12 Configuração de Slots secundários para carga do programa
13..15 Reservado para uso futuro

Para aquivos de tela:

Byte Conteúdo
7,8 Endereço de entrada na VRAM
9 Modo de tela (screen)
10..15 Reservado para uso futuro