sábado, 27 de agosto de 2022

"MC-1000", "MC 1000" ou "MC1000"?

No gabinete do computador, nos acessórios e nos materiais impressos (caixa, capas dos manuais, rótulos e capas das fitas, propagandas etc.) se lê apenas "MC1000" — ou "MC 1000", com um quase imperceptível espaço entre as letras e o número.

Apesar disso, lemos "MC-1000", com hífen, em alguns lugares: na tela de inicialização, em uma "ficha técnica" impressa na base do gabinete, e no texto interno dos manuais.

Entre os periféricos, a fonte e a expansão de memória estão identificadas sem hífen ("PS 1000" e "EM 1000"). Já no o gravador "DR-1000A" há hífen.

Proteção contra acesso ao código-fonte BASIC

Os programas em BASIC das fitas numeradas da CCESOFT (F-18 a F-30) são autoexecutáveis (salvos sem nome) e trazem uma proteção para impedir que o usuário possa visualizar o código-fonte. Os programas começam com as instruções:

1  REM ~3E~C3~32~20~01~3E~E9~32~21~01~3E~03~32~22~01~3A~1B~01~FE~03~C0~3E~F4~32~21~01~3E~01~32~44~03~C9
2  CALL 986
...

(Nesta listagem, cada sequência hexadecimal ~XX corresponde a um byte/caracter.)

A rotina em código de máquina do Z80 codificada na linha REM é:

        ; Variáveis do sistema:
KEY0:   equ $011b ; Última tecla pressionada.
JOB:    equ $0120 ; Hook chamado durante a leitura do teclado.
OUTFLG: equ $0344 ; Desabilita impressão se diferente de zero.
 
        ; Programa começa no primeiro byte
        ; após o token da instrução REM da linha 1.
        org $03da ; =986.
init:
        ; Inicialmente JOB contém uma instrução RET.
        ; Coloca em JOB um JP para a rotina "checa".
        ld  a,$c3 ; Opcode de JP.
        ld  (JOB),a
        ld  a,$e9 ; LSB do endereço da rotina "checa".
        ld  (JOB+1),a
        ld  a,$03 ; MSB do endereço da rotina "checa".
        ld  (JOB+2),a
checa:
        ; Retorna se a última tecla pressionada
        ; não for <CTRL>+<C>.
        ld  a,(KEY0)
        cp  $03 ; <CTRL>+<C>
        ret nz
desvia:
        ; O usuário pressionou <CTRL>+<C>.
        ; Desvia JOB para a rotina "oculta".
        ld  a,$f4 ; LSB do endereço da rotina "oculta".
        ld  (JOB+1),a
oculta:
        ; Desabilita a impressão de caracteres.
        ld  a,$01
        ld  (OUTFLG),a
        ret

A parte inicial da rotina (init), chamada logo no início do programa em BASIC pelo CALL na linha 2, configura o hook JOB ($0120 / 288) (vide variáveis do sistema). A partir daí, continuamente, a cada leitura de teclado, é chamada a segunda parte da rotina (checa), que checa se o usuário pressionou <CTRL>+<C>. Se isso acontecer, a terceira parte (desvia) reconfigura JOB para chamar a parte final da rotina (oculta), de modo que, continuamente, a cada leitura de teclado, seja colocado o valor 1 na variável OUTFLG ($0344 / 836), o que desabilita a impressão de caracteres.

O efeito disso é que, depois que se interrompe o programa em BASIC com <CTRL>+<C>, não se veem na tela nem os caracteres sendo digitados, nem os resultados das instruções PRINT e LIST (o que é o objetivo da rotina). Veem-se apenas o "OK" que aparece ao final da execução das instruções e as mensagens de erro. No modo DEBUG, porém, tudo continua visível.

Como contornar

Simplesmente mudar o valor de OUTFLG para zero (POKE 836,0) não adianta, pois a rotina oculta que muda OUTFLAG para 1 está atrelada ao hook JOB e é chamada a cada leitura de teclado, frustrando essa tentativa.

A solução é primeiro desabilitar o hook JOB (colocando aí uma instrução RET do Z80) e, então, zerar OUTFLAG.

POKE 288,201: POKE 836,0

Ou, alternativamente, via DEBUG (onde se pode ver o que se está digitando):

DEBUG
>S120
0120 C3 C9
0121 F4 
>S344
0344 01 0
0345 FF
>Q

Depois disso podem-se apagar as linhas 1 e 2 do programa em BASIC para rodá-lo novamente sem reativar a proteção.

Software

Software original do MC1000 está disponível em https://github.com/ensjo/mc1000-software/tree/master/cce:

  • ROM
  • Fitas CCESOFT

Programas de outras origens:

sexta-feira, 26 de agosto de 2022

Teclado

No hardware

<SHIFT>+<RESET>
Conecta-se ao pino ¬RESET ao Z80, reiniciando o MC1000. Isto é implementado diretamente em hardware. A tecla <RESET>, sozinha ou em combinação, é visível na malha do teclado que o MC1000 lê pelo PSG AY-3-8910.

Na rotina SKEY? da ROM

Esta é a rotina padrão de leitura do teclado. Ela tem um comportamento durante o modo BASIC (variável do sistema HEAD = $FF/255) e outro no modo de jogo (HEAD ≠ $FF/255).

Modo BASIC

No teclado do MC1000 nota-se que as teclas <0>, <@>, <↑>, <RETURN>, <SPACE> e <RUBOUT> não têm uso previsto com a tecla <SHIFT>. E <CTRL> provavelmente só deveria ser combinada com letras, <@> e <↑>. Combinações com <SHIFT>+<CTRL> são completamente imprevistas. Os valores retornados nos casos imprevistos são meros efeitos colaterais do cálculo para os casos previstos.

Tecla Código +SHIFT +CTRL +SHIFT +CTRL Tecla Código +SHIFT +CTRL +SHIFT +CTRL Tecla Código +SHIFT +CTRL +SHIFT +CTRL
@ $40/64:@ $60/96:` $00/0:nul $20/32: P $50/80:P $70/112:p $10/16:dle $30/48:0 0 $30/48:0 $20/32: $60/96:` $50/80:P
A $41/65:A $61/97:a $01/1:soh $21/33:! Q $51/81:Q $71/113:q $11/17:dc1 $31/49:1 1 $31/49:1 $21/33:! $61/97:a $51/81:Q
B $42/66:B $62/98:b $02/2:stx $22/34:" R $52/82:R $72/114:r $12/18:dc2 $32/50:2 2 $32/50:2 $22/34:" $62/98:b $52/82:R
C $43/67:C $63/99:c $03/3:etx $23/35:# S $53/83:S $73/115:s $13/19:dc3 $33/51:3 3 $33/51:3 $23/35:# $63/99:c $53/83:S
D $44/68:D $64/100:d $04/4:eot $24/36:$ T $54/84:T $74/116:t $14/20:dc4 $34/52:4 4 $34/52:4 $24/36:$ $64/100:d $54/84:T
E $45/69:E $65/101:e $05/5:enq $25/37:% U $55/85:U $75/117:u $15/21:nak $35/53:5 5 $35/53:5 $25/37:% $65/101:e $55/85:U
F $46/70:F $66/102:f $06/6:ack $26/38:& V $56/86:V $76/118:v $16/22:syn $36/54:6 6 $36/54:6 $26/38:& $66/102:f $56/86:V
G $47/71:G $67/103:g $07/7:bel $27/39:' W $57/87:W $77/119:w $17/23:etb $37/55:7 7 $37/55:7 $27/39:' $67/103:g $57/87:W
H $48/72:H $68/104:h $08/8:bs $28/40:( X $58/88:X $78/120:x $18/24:can $38/56:8 8 $38/56:8 $28/40:( $68/104:h $58/88:X
I $49/73:I $69/105:i $09/9:tab $29/41:) Y $59/89:Y $79/121:y $19/25:em $39/57:9 9 $39/57:9 $29/41:) $69/105:i $59/89:Y
J $4A/74:J $6A/106:j $0A/10:lf $2A/42:* Z $5A/90:Z $7A/122:z $1A/26:sub $3A/58:: : $3A/58:: $2A/42:* $6A/106:j $5A/90:Z
K $4B/75:K $6B/107:k $0B/11:vt $2B/43:+ RETURN $0D/13:cr $7B/123:{ $1B/27:esc $3B/59:; ; $3B/59:; $2B/43:+ $6B/107:k $0D/13:cr
L $4C/76:L $6C/108:l $0C/12:ff $3C/60:< SPACE $20/32: $7C/124:| $1C/28:fs $2C/44:, , $2C/44:, $3C/60:< $6C/108:l $20/32:
M $4D/77:M $6D/109:m $0D/13:cr $3D/61:= RUBOUT $7F/127:del $7D/125:} $1D/29:gs $2D/45:- - $2D/45:- $3D/61:= $6D/109:m $7F/127:del
N $4E/78:N $6E/110:n $0E/14:so $3E/62:> $5E/94: $7E/126:~ $1E/30:rs $2E/46:. . $2E/46:. $3E/62:> $6E/110:n $5E/94:
O $4F/79:O $6F/111:o $0F/15:si $3F/63:? / $2F/47:/ $3F/63:? $6F/111:o $5F/95:

Modo de jogo

As seguintes teclas e combinações retornam valores diferentes:

Tecla Código +SHIFT +CTRL +SHIFT +CTRL Tecla Código +SHIFT +CTRL +SHIFT +CTRL Tecla Código +SHIFT +CTRL +SHIFT +CTRL
RETURN $5B/91:[ ; $5B/91:[
L $2C/44:, SPACE $5C/92:\ $3C/60:< , $3C/60:< $2C/44:, $5C/92:\
M $2D/45:- RUBOUT $5D/93:] $3D/61:= - $3D/61:= $2D/45:- $5D/93:]
N $2E/46:. $3E/62:> . $3E/62:> $2E/46:.
O $2F/47:/ / $3F/63:? $2F/47:/

(Onde não houver valor indicado, o valor é o mesmo da tabela anterior.)

Note-se que a combinação <CTRL>+<H> é automaticamente capturada por SKEY?, disparando a rotina de alteração do "tópico do jogo".

Na rotina GETL da ROM

Esta rotina é usada pelo interpretador BASIC para ler uma linha de comando e para obter resposta a comando INPUT.

Internamente os caracteres vão sendo armazenados no buffer de linha ($0100–$01FF).

A teclas e combinações de teclas abaixo são "consumidas" pela rotina e acabam não fazendo parte da linha digitada.

<RETURN> ou <CTRL>+<M>
Insere um caracter CHR$(0) no buffer de linha na posição do cursor, emite uma quebra de linha e termina a rotina GETL. O caracter CHR$(0) marca o fim da linha digitada, portanto, se o cursor foi movido com as teclas de movimento abaixo, tudo o que foi digitado e que ficou da posição do cursor até o fim da linha será descartado.
<CTRL>+<H>, <CTRL>+<J>, <CTRL>+<K> e <CTRL>+<L>
Durante a digitação de uma linha no modo direto ou resposta a um comando INPUT, movimentam o cursor dentro do espaço dos caracteres já digitados, respectivamente para a esquerda, para baixo, para cima e para a direita. Devido ao bug na impressão do caracter de controle CHR$(12), o cursor não anda para a direita, consequentemente este recurso para a digitação da linha não funciona a contento e nem é mencionado no manual do BASIC.
<RUBOUT>
Embora a tecla gere um código diferente, seu comportamento aqui é igual à combinação <CTRL>+<H>: movimenta o cursor para a esquerda sem apagar os caracteres na tela.
<CTRL>+<C>
Emite uma quebra de linha, desativa a geração automática de número de linhas (instrução AUTO do BASIC), ativa o flag de carry do Z80 (para sinalizar o cancelamento da digitação) e termina a rotina GETL e emite uma quebra de linha. Diferentemente de <RETURN>, devolve o flag de carry do Z80 ligado para indicar que foi pressionado <CTRL>+<C>. Também desliga a geração automática de número de linhas do comando AUTO.
<@> ou <CTRL>+<U>

Durante a digitação de uma linha em modo direto ou resposta a um comando INPUT, descarta o que já foi digitado, emite uma quebra de linha e aguarda nova digitação.

Esse uso da tecla <@> remonta ao BASIC da Microsoft para Altair 8800. Vide http://www.altair32.com/pdf/Altair_8800_BASIC_Reference_Manual_1975.PDF, página 41.

<CTRL>+<↑>
Limpa a tela e move o cursor para o canto superior esquerdo da tela.
<CTRL>+<@>
Esta combinação de teclas gera CHR$(0). Como GETL usa CHR$(0) para marcar o fim da linha digitada, a rotina substitui o <CTRL>+<@> digitado pelo usuário pelo caracter "8" (aparentemente, uma escolha arbitrária).

No interpretador BASIC

Note-se que ao ler uma linha do teclado (linha entrada em modo direto, resposta a comando INPUT) o interpretador BASIC usa a rotina GETL, então as combinações de tecla acima valem nessas situações. Além disso:

<CTRL>+<C>
  • Durante a execução de um programa, interrompe a execução com a mensagem "PAUSA EM <número de linha>" e volta ao modo direto. A execução do programa pode ser retomada de onde parou com o comando CONT.
  • Durante a execução de uma linha de comandos em modo direto, interrompe a execução com a mensagem "PAUSA".
  • Durante a execução de um comando LIST, interrompe a listagem com a mensagem "PAUSA".
<CTRL>+<S>
Durante a execução de um programa, execução de uma linha de comandos em modo direto ou execução de um comando LIST, inicia uma pausa até que outra tecla seja pressionada.

terça-feira, 23 de agosto de 2022

Posicionamento do cursor

Para grande frustração dos proprietários do MC1000, seu BASIC não fornecia um comando para posicionamento de cursor, como os que existem em outras linhas de microcomputadores. Exemplos:

MSX: LOCATE linha,coluna
TK-90X (ZX-Spectrum): PRINT AT linha,coluna;
CP-400 (TRS-80 Color): PRINT @ posição,
Apple II: VTAB linha : HTAB coluna

Mas há uma forma de fazê-lo, usando uma sequência de quatro caracteres iniciada pelo caracter de controle ESC, documentada (de maneira não muito compreensível) no [[Manual de Referência]], ao tratar da rotina CO da ROM, que serve para imprimir caracteres (e é usada pelo comando PRINT do BASIC):

  1. Caracter ESC ($1B / 27);
  2. Caracter "=" ($3D / 61);
  3. Caracter com código ASCII correspondente à linha;
  4. Caracter com código ASCII correspondente à coluna*.

* A rotina CO contém uma distinção para os modos de 32 e 80 colunas. Em modo de 80 colunas, acrescenta-se 32 ao valor da coluna.

Assim, em BASIC temos, para o modo de 32 colunas:

PRINT CHR$ (27);"="; CHR$ (linha); CHR$ (coluna);

Onde 0 ≤ linha ≤ 15 e 0 ≤ coluna ≤ 31.

E para o modo de 80 colunas:

PRINT CHR$ (27);"="; CHR$ (linha); CHR$ (coluna + 32);

Onde 0 ≤ linha ≤ 23 e 0 ≤ coluna ≤ 79.

Infelizmente há um bug no posicionamento para 32 colunas que faz com que o cursor seja posicionado uma linha abaixo do lugar correto.

Por sorte, a sintaxe para 80 colunas (somando 32 ao número da coluna) também funciona no modo de 32 colunas, e aí sim o cursor cai no lugar certo.

Esse recurso foi utilizado numa dica de Edmundo Tojal Donato Júnior, de Alagoas, publicada na Revista Micro Sistemas nº 51 de dezembro de 1985:

10  HOME
20  INPUT "LINHA,COLUNA";L,C: HOME
30  GOSUB 100
40  PRINT "MC-1000";
50  POKE 283,0
60  IF  PEEK (283) <  >  ASC ("C") THEN 60
70  GOTO 10
100  PRINT  CHR$ (27); CHR$ (61); CHR$ (L); CHR$ (C + 32);: RETURN

Para reduzir o tamanho de bytes utilizados no comando PRINT e a poluição visual causada por tantos CHR$, uma sugestão é guardar os dois primeiros bytes da sequência de escape em uma variável. Uma dica é usar uma variável chamada AT$ de uma forma que lembra a sintaxe usada no ZX-Spectrum:

10 AT$ =  CHR$ (27) + "="
20  TEXT : HOME
30  PRINT AT$; CHR$ (10); CHR$ (12 + 32);"MC-1000";
40  PRINT AT$; CHR$ (12); CHR$ (6 + 32);"CCE COLOR COMPUTER";
50  PRINT  CHR$ (30);

Essa solução ainda é um pouco visualmente poluída. Pode-se fazer como na dica do Edmundo Tojal e esconder toda a sequência de escape em uma sub-rotina, passando-se as coordenadas em variáveis:

10  TEXT : HOME
20 L = 10:C = 12: GOSUB 100: PRINT "MC-1000";
30 L = 12:C = 6: GOSUB 100: PRINT "CCE COLOR COMPUTER";
40  PRINT CHR$ (30);
50  END
100  PRINT  CHR$ (27);"="; CHR$ (L); CHR$ (C + 32);: RETURN

Uma outra dica é usar o comando SET x,y para passar as coordenadas para a subrotina, dispensando as variáveis adicionais. A subrotina obtém com PEEK os valores das variáveis do sistema onde o comando SET armazena os valores das coordenadas. Mas há um porém: o comando SET emite "TI ERRO" se verificar que está em modo TEXT. Para ludibriar essa verificação, basta um POKE na variável do sistema verificada pelo comando SET, para ele achar que estamos em modo GR ou HGR:

10  TEXT : POKE 856,2: HOME
20  SET 12,10: GOSUB 100: PRINT "MC-1000";
30  SET 6,12: GOSUB 100: PRINT "CCE COLOR COMPUTER";
40  PRINT  CHR$ (30);
50  END
100  PRINT  CHR$ (27);"="; CHR$ ( PEEK (857)); CHR$ ( PEEK (858) + 32);: RETURN

Onde se usam os endereços:

  • 856 = Anotação sobre o modo de vídeo atual: 0 = TEXT, 1 = GR, 2 = HGR.
  • 857 = Coordenada vertical definida por SET.
  • 858 = Coordenada horizontal definida por SET.

Outras dicas rápidas referentes ao cursor:

Estas dicas só se aplicam ao modo de 32 colunas.

Cursor sem piscar (1):

POKE 304,201

A cada leitura do teclado (rotina SKEY?) são chamados dois hooks na área de variáveis do sistema na RAM. O segundo deles é JOBM ($0103 / 304), que originalmente contém uma instrução JP para a rotina da ROM que conta ciclos para o piscamento do cursor. O POKE coloca uma instrução RET em JOBM, desativando o piscamento.

Cursor sem piscar (2):

GR : HOME

Os comandos de seleção de modo gráfico (GR ou HGR) realizam automaticamente o procedimento de colocar um RET em JOBM, desativando o piscamento do cursor. O comando HOME até faz o MC6847 entrar no modo alfanumérico, mas, diferentemente do comando TEXT, não restabelece todas as variáveis aos valores usuais do modo de texto. Por exemplo, após um GR : HOME o MC1000 continua aceitando comandos de desenho (SET, PLOT etc.) sem emitir "TI ERRO".

Esta solução é visualmente poluída, pois o usuário vai enxergar a tela piscando em modo GR.

Para voltar a piscar (1):

POKE 304,195

Restabelece a instrução JP em JOBM.

Para voltar a piscar (2):

TEXT

Cursor invisível:

POKE 247,202: POKE 248,192: POKE 249,202: POKE 250,192: CALL 52013

Coloca o endereço $C0CA (onde há uma instrução RET) nas variáveis de 2 bytes UPDBM ($00F7 / 247) e UPDBCM ($00F9 / 249), que originalmente contêm endereços de rotinas da ROM responsáveis pela inversão do caracter na posição do cursor.

O CALL do final apaga o cursor se ele ainda estiver aceso.

Atenção: O comando HOME e a impressão de CHR$(30) desfazem esta configuração.

Para voltar a aparecer (1):

POKE 247,29: POKE 248,203: POKE 249,45: POKE 250,203

Restaura os valores originais de UPDBM e UPDBCM.

Para voltar a aparecer (2):

HOME
ou
PRINT CHR$ (30);

segunda-feira, 22 de agosto de 2022

Portas

$00 / 0

Manual de Referência (p.50):

Quando você instala a interface da unidade de disco, o cartão da interface lhe oferecerá essa porta. Sua função com relação aos dados que passaram pelo "latch" é:

  • bit 0=0, ativa a ROM do drive de inicialização
  • bit 0=1, desativa a ROM do drive de inicialização
  • bit 1=0, desativa o MONITOR e o interpretador BASIC (residente)
  • bit 1=1, ativa o MONITOR e o interpretador.
$02 / 2

Manual de Referência (p.50):

Porta de acionamento liga/desliga dos motores da unidade de disco.

Um outro manual sobre CP/M dará uma descrição mais detalhada sobre a unidade de disco.

$04 / 4 (SPORT/STROB)

Status da impressora (bit 0 = 1: ocupada).

Manual de Referência (p.48):

Porta de entrada do estado da impressora. Na tabela de jump, apresentamos LPSTS para verificar o estado de prontidão; você também pode fazer a entrada a partir dessa porta para verificar o bit 0. Se este for 1, a impressora está ocupada.

Manual de Referência (p.49):

Porta de saída de "strob" da impressora. A porta 04H é uma E/S de duas vias; quando usada como entrada, ela ecoa o estado da impressora; se for usada como saída, ela faz o "strob" da impressora chamar a prontidão novamente.

Toda vez que você faz sair um dado para a impressora, ocorre um "strob" da mesma.

$05 / 5 (DPORT)

Envio de caracter à impressora.

Manual de Referência (p.49):

Porta de saída dos dados para a impressora. Quando a impressora estiver pronta, faça a saída do código ASCII para essa porta, e a impressora pegará esse dado automaticamente.

$0D / 13

Manual de Referência (p.50):

Porta de comando do controlador de disquete (FDC).

$10 / 16 (RPORT1)

Seleciona registrador do monitor de 80 colunas ($0E = linha do cursor; $0F = coluna do cursor).

Manual de Referência (p.12):

Porta de seleção de registro do VDG MC6845: o 6845 tem 16 registros, cada um com seu próprio significado; você tem que usar esta porta para selecionar o registro em que você deseja colocar um dado.

Manual de Referência (p.49):

"Latch" de seleção dos registradores do 6845, semelhante ao 20H do 8910.

$11 / 17 (DPORT1)

Escreve no registrador do monitor de 80 colunas selecionado pela porta $10 (RPORT1).

Manual de Referência (p.12):

Após selecionar um dos 16 registros do 6845, use este protocolo para estabelecer o dado apropriado no registro.

Manual de Referência (p.49):

"Latch" de E/S de dados do 6845 após haver selecionado o registrador.

$12 / 18 (COL80)

Define status da VRAM (texto de 80 colunas). Se o bit 0 for 1, os endereços $2000–$27FF são mapeados para a VRAM, e ela fica disponível para leitura/escrita. Senão, lê-se a RAM normal.

Manual de Referência (p.13):

Bit 0=1: Banco da VRAM 6845 ligado, memória principal se superpõe ao cartão de 80 colunas; banco da VRAM desligado, endereço da VRAM: 2000H–27FFH

Manual de Referência (p.49):

Porta do banco da VRAM do cartão de 80 colunas. A saída de um dado com bit 0=1 fará a habilitação VRAM 6845 (2000H–27FFH).

$20 / 32 (REG)

Nota: O circuito que faz a distinção entre as portas $20, $40, $60 e $80 considera apenas os três primeiros bits do endereço da porta. Por isso, na verdade, todos os endereços na faixa de $20 a $3F (%001XXXXX) apontam para a mesma porta REG; todos os endereços de $40 a $5F (%010XXXXX) apontam para a mesma porta RD; todos os endereços de $60 a $7F (%011XXXXX) apontam para a mesma porta WR; e todos os endereços de $80 a $9F (%100XXXXX) apontam para a mesma porta COL32.

Seleciona um registrador do PSG AY-3-8910. Os registradores $00 a $0D são dedicados à geração de som. Os "registradores" $0E (IOA) e $0F (IOB) são portas de E/S do PSG que, no MC1000, estão ligadas ao circuito (matriz) do teclado (conforme tabela abaixo). Por meio do registrador $0E (IOA), configurado para saída, se seleciona uma das 8 "linhas" da matriz do teclado, devendo-se para isso zerar o bit correspondente à linha desejada. Uma vez selecionada a linha, por meio do registrador $0F (IOB), configurado para entrada, se lê um byte cujos bits correspondem ao estado das 8 teclas daquela linha. Teclas pressionadas são representadas por bits 0, teclas soltas por bits 1. Os estados das teclas <CTRL> e <SHIFT> são visíveis em todas as linhas. O bit 3 da 7ª linha não está associado a nenhuma tecla, seu valor é sempre 1.

Linha da matriz do teclado Valor para o registrador IOA ($0E / 14) Bit do registrador IOB ($0F / 15)
7 6 5 4 3 2 1 0
0 %11111110 ($FE) C
T
R
L
S
H
I
F
T
8 0 X P H @
1 %11111101 ($FD) 9 1 Y Q I A
2 %11111011 ($FB) : 2 Z R J B
3 %11110111 ($F7) ; 3 ENTER S K C
4 %11101111 ($EF) , 4 SPACE T L D
5 %11011111 ($DF) 5 RUBOUT U M E
6 %10111111 ($BF) . 6 V N F
7 %01111111 ($7F) / 7 N/A W O G

Manual de Referência(p.48):

Porta de seleção de registradores do 8910. Há 16 registradores no 8910; antes de fazer a entrada/saída em/de qualquer registrador, você deve primeiramente selecionar esse registrador.

$40 / 64 (RD)

(Vide nota no texto referente à porta REG ($20).)

Lê o registrador do PSG selecionado pela porta REG ($20).

Manual de Referência (p.48):

"Latch" de entrada do 8910.

$60 / 96 (WR)

(Vide nota no texto referente à porta REG ($20).)

Escreve no registrador do PSG selecionado pela porta REG ($20).

Manual de Referência (p.48):

"Latch" de saída do 8910. Tudo referente a som, E/S de fita, varredura de teclado, tem que utilizar as três E/S acima.

$80 / 128 (COL32)

(Vide nota no texto referente à porta REG ($20).)

Configura o status da VRAM (texto de 32 colunas e modos gráficos). Se o bit 0 for 0, os endereços $8000–$9FFF são mapeados para a VRAM, e ela fica disponível para leitura/escrita. Senão, lê-se a RAM normal.

Manual de Referência (p.12) (aqui a porta está erroneamente identificada como "00H"):

Porta de seleção de formato do VDG MC6847P: use esta porta para escolher a resolução do 6847.

Manual de Referência (p.13):

Bit 0=0: Banco da VRAM 6847 ligado. Caso contrário, banco da VRAM 6847 desligado superposto com banco da memória principal.
Endereço da VRAM: 8000H–97FFH

Manual de Referência (p.49):

Porta do banco da VRAM 6847. A saída de um dado com bit 0=0 habilitará a VRAM 6847 (8000H–97FFH).

Se você quiser fazer um POKE dos dados na VRAM, certifique-se de habilitar a VRAM.

Leitura de teclado

O BASIC do MC1000 não tinha a função INKEY$, existente em outros microcomputadores, que verifica se há alguma tecla pressionada no momento e retorna o caracter correspondente, ou uma cadeia vazia ("") se não houver nenhuma tecla pressionada. Essa função é imprescindível para jogos de ação.

Mas mesmo que tivesse, o MC1000 tem uma característica que é um grande obstáculo à programação de jogos de ação em BASIC: o interpretador BASIC fica paralisado enquanto alguma tecla estiver pressionada! :-O

De todo modo, existe uma variável do sistema que armazena o código ASCII da última tecla pressionada: o endereço 283 (KEY0).

Não é a mesma coisa que um INKEY$, já que, por exemplo, não se pode saber se a pessoa apertou a mesma tecla mais de uma vez... mas pelo menos é melhor que a instrução INPUT! ¦-)

Programinha-brinde: Move um caracter "#" pela tela usando as teclas <W> (cima), <S> (baixo), <A> (esquerda) e <D> (direita).

10  HOME
20 C = 15:L = 7
30 T$ = CHR$ (PEEK (283))
40 CC = (C + ABS (T$ = "D") - ABS (T$ = "A")) AND 31
50 LL = (L + ABS (T$ = "S") - ABS (T$ = "W")) AND 15
60  PRINT  CHR$ (27);"="; CHR$ (L); CHR$ (C + 32);" ";
70  PRINT  CHR$ (27);"="; CHR$ (LL); CHR$ (CC + 32);"#"; CHR$ (8);
80  POKE 910,0
90 C = CC : L = LL
100  GOTO 30

Solução para permitir que a execução do BASIC continue mesmo com uma tecla pressionada

(Desenvolvida por Ensjo em 19 de outubro de 2011.)

O MC1000 tem três rotinas na ROM relacionadas com a leitura do teclado: SKEY?, KEY? e KEY.

  • SKEY? verifica se alguma tecla está sendo pressionada no momento.
  • KEY? chama SKEY? para checar se uma mesma tecla está sendo pressionada durante 7ms.
  • KEY chama KEY? e, se uma tecla for pressionada, espera até que ela seja liberada.

A causa do MC1000 travar quando uma tecla é pressionada é um CALL KEY no endereço $DDDC, em meio à interpretação do programa BASIC:

...
DD5A call KEY?        ; Alguma tecla pressionada?
DD5D or a
DD5E call nz, $dddc   ; Sim: capturar e tratar.
DD61 ...
...
DDDC call KEY         ; Captura a tecla e espera ser liberada.
DDDF cp $13           ; É CTRL+S?
DDE1 jp nz, $de0b     ; (etc.)
...

A ideia é dar um jeito de impedir que essa chamada seja feita.

A rotina KEY? chama a rotina SKEY?, que por sua vez chama dois hooks em RAM:

  • O primeiro hook (JOB, no endereço $0120) está livre. Ele tem apenas uma instrução RET ($C9) do Z80.
  • O segundo hook (JOBM, no endereço $0130) por padrão está associado à rotina de piscamento do cursor.

Vamos então usar o hook JOB. Quando ele é atingido a partir da instrução CALL KEY? no endereço $DD5A, a pilha do Z80 contém:

SP $85 $C385, endereço para retorno a SKEY? ao final da execução do hook.
SP+1 $C3
SP+2 ?? Valor de BC salvo com PUSH em SKEY?.
SP+3 ??
SP+4 ?? Valor de DE salvo com PUSH em SKEY?.
SP+5 ??
SP+6 ?? Valor de HL salvo com PUSH em SKEY?.
SP+7 ??
SP+8 $53 $C353, endereço para retorno a KEY? ao final da execução de SKEY?.
SP+9 $C3
SP+10 ?? Valor de BC salvo com PUSH em KEY?
SP+11 ??
SP+12 ?? Valor de DE salvo com PUSH em KEY?
SP+13 ??
SP+14 $5D $DD5D, endereço para retorno ao interpretador BASIC ao final da execução de KEY?.
SP+15 $DD

O que vamos fazer é examinar o conteúdo de SP+14 e SP+15 e ver se temos lá o endereço $DD5D. Se for este o caso, sabemos que estamos no ponto "problemático" da ROM, perto da rotina que vai "congelar" o BASIC até a liberação da tecla pressionada. Então vamos modificar o conteúdo de SP+8 e SP+9 para que o controle não volte para KEY? ao final de SKEY?, mas vá para uma rotina própria que vai decidir o que fazer. Se o usuário não estiver pressionando tecla nenhuma, o controle deverá retornar para $DD61. Se o usuário estiver apertando alguma tecla, colocamos seu código no registrador A e chamamos a rotina $DDDF (pulando a famigerada instrução CALL KEY) antes de retornar o controle para $DD61.

A rotina foi projetada para ser POKEada em uma linha REM, que deve ser a primeira do programa (vide abaixo).

key0:   equ $011b
job:    equ $0120
resto:  equ $dd61
teclap: equ $dddf
 
        org $03da
 
init:
; Direciona o hook JOB (chamado em SKEY?) para a rotina "checa".
        ld hl,checa
        ld (job+1),hl
        ld a,$c3
        ld (job),a
        ret
 
checa:
; Verifica se estamos em meio ao CALL KEY? feito em #DD5A.
        push ix
        ld ix,$ffff ; Estas duas linhas substituem ld ix,$0000.
        inc ix      ; Não pode haver byte $00 na linha REM.
        add ix,sp
        ld a,$5d
        cp (ix+16)
        jr nz,sai
        ld a,$dd
        cp (ix+17)
        jr nz,sai
; Sim: Modifica a pilha do Z80 para que, ao final da
; rotina SKEY?, o controle não volte para KEY?, mas
; para nossa rotina personalizada "desvia".
        push hl
        ld hl,desvia
        ld (ix+10),l
        ld (ix+11),h
        pop hl
sai:
        pop ix
        ret
 
desvia:
; Restaura pilha e registradores como estariam
; ao final da execução de KEY?, e desvia
; para os pontos apropriados do interpretador
; BASIC, evitando o CALL KEY em #DDDC.
        pop bc ; Desempilha valores empilhados por KEY?
        pop de
 
        inc sp ; Descarta o ponto de retorno original.
        inc sp
 
        ld (key0+2),a ; FUNCIONALIDADE ADICIONAL:
                      ; Salva em KEY0 + 2 a informação sobre se
                      ; uma tecla foi pressionada (#FF) ou não (#00).
        or a
 
        ld a,(key0)   ; Copia código da tecla em KEY0 + 1.
        ld (key0+1),a ; (É algo que a rotina KEY? faz.)
 
        call nz,teclap ; Se tecla pressionada, chama rotina
                       ; de tratamento situada logo depois a problemática
                       ; instrução CALL KEY.
        jp resto       ; Retorna para o interpretador BASIC.

Eis abaixo o início de um programa em BASIC. As linhas de 1 a 6 "instalam" o programa acima em uma linha REM, que deve ser a primeira do programa e ter 70 caracteres. A linha 7 ativa o programa.

0  REM --------10--------20--------30--------40--------50--------60--------70
1  DATA 33,230,3,34,33,1
2  DATA 62,195,50,32,1,201,221,229,221,33,255,255,221,35,221,57
3  DATA 62,93,221,190,16,32,18,62,221,221,190,17,32,11,229,33
4  DATA 12,4,221,117,10,221,116,11,225,221,225,201,193,209,51,51
5  DATA 50,29,1,183,58,27,1,50,28,1,196,223,221,195,97,221
6  FOR I = 0 TO 69: READ B: POKE 986 + I,B: NEXT I
7  CALL 986

As linhas de 1 a 6 podem até ser apagadas depois da primeira execução, pois então o programa em código de máquina já estará instalado na linha REM.

Pode-se também apenas digitar a linha 0 com 70 caracteres e a instrução CALL 986, e adicionar o programa em código de máquina manualmente via DEBUG:

03DA 21 E6 03 22 21 01
03E0 3E C3 32 20 01 C9 DD E5 DD 21 FF FF DD 23 DD 39
03F0 3E 5D DD BE 10 20 12 3E DD DD BE 11 20 0B E5 21
0400 0C 04 DD 75 0A DD 74 0B E1 DD E1 C9 C1 D1 33 33
0410 32 1D 01 B7 3A 1B 01 32 1C 01 C4 DF DD C3 61 DD

Uma funcionalidade adicional desta rotina é salvar na posição de memória KEY0+2 ($011D / 285) a indicação sobre se uma tecla está sendo pressionada ou não. Se não estiver, PEEK(285) retorna 0; se estiver, retorna 255.

Agora a função INKEY$ dos outros micros pode ser implementada assim:

I$ = "": IF PEEK (285) THEN I$ =  CHR$ ( PEEK (283))

Vamos reescrever o programa no início desta página. Digita as linhas 0 a 7 e acrescenta:

...
10  HOME
20 C = 15:L = 7
30 T$ = "": IF  PEEK (285) THEN T$ =  CHR$ ( PEEK (283))
40 CC = (C + ABS (T$ = "D") - ABS (T$ = "A")) AND 31
50 LL = (L + ABS (T$ = "S") - ABS (T$ = "W")) AND 15
60  PRINT  CHR$ (27);"="; CHR$ (L); CHR$ (C + 32);" ";
70  PRINT  CHR$ (27);"="; CHR$ (LL); CHR$ (CC + 32);"#"; CHR$ (8);
80  POKE 910,0
90 C = CC : L = LL
100  GOTO 30

Executa ambos os programas e sente a diferença.


(Nova versão desenvolvida em 2 de julho de 2015.)

Esta é uma versão da rotina para se localizar na parte alta da VRAM, na área de 256 bytes que o MC1000 reserva para o usuário no final da RAM.

key0:   equ $011b
keysw:  equ $012e
resto:  equ $dd61
teclap: equ $dddf

; Para ativar: Configurar hook JOB ($0120) com um jump para "checa".

        .org $3f00
 
checa:
; Verifica se estamos em meio ao CALL KEY? feito em #DD5A.
        ld hl,+14
        add hl,sp
        ld a,(hl)
        cp $5d
        ret nz
        inc hl
        ld a,(hl)
        cp $dd
        ret nz
; Sim: Modifica a pilha do Z80 para que, ao final da
; rotina SKEY?, o controle não volte para KEY?, mas
; para nossa rotina personalizada "desvia".
		ld hl,+8
        add hl,sp
        ld de,desvia
        ld (hl),e
        inc hl
        ld (hl),d
        ret

desvia:
; Restaura pilha e registradores como estariam
; ao final da execução de KEY?, e desvia
; para os pontos apropriados do interpretador
; BASIC, evitando o CALL KEY em #DDDC.
        pop bc ; Desempilha valores empilhados por KEY?
        pop de

        inc sp ; Descarta o ponto de retorno original.
        inc sp
 
        ld (key0+2),a ; FUNCIONALIDADE ADICIONAL:
                      ; Registra em KEY0+2 a informação sobre se
                      ; uma tecla foi pressionada (#FF) ou não (#00).
        or a
 
        ld a,(key0)   ; Copia código da tecla em KEY0 + 1.
        ld (key0+1),a ; (É algo que a rotina KEY? faz.)
 
        call nz,teclap ; Se tecla pressionada, chama rotina
                       ; de tratamento situada logo depois a problemática
                       ; instrução CALL KEY.
        jp resto       ; Retorna para o interpretador BASIC.

Eis um programa BASIC que insere a rotina na memória e o ativa:

1  RESTORE 2:A0 =  PEEK (914) + 256 *  PEEK (915) + 1:A1 = A0 + 18:A2 = A0 + 24: POKE 288,201: FOR A = A0 TO A0 + 43: READ B: POKE A,B: NEXT : POKE A1,A2 AND 255: POKE A1 + 1,A2 / 256: POKE 289,A0 AND 255: POKE 290,A0 / 256: POKE 288,195
2  DATA 33,14,,57,126,254,93,192,35,126,254,221,192,33,8,,57,17,24,63,115,35,114,201,193,209,51,51,50,29,1,183,58,27,1,50,28,1,196,223,221,195,97,221

A variável A0 calcula o início da área do usuário no topo da RAM, que varia conforme a memória do micro (por padrão, $3F00 / 16128 nas máquinas com 16KiB, e $BF00 / 48896 nas máquinas com 64KiB) e também pode ser redefinido pela instrução CLEAR.

Alternativamente, pode-se inserir a rotina em uma primeira linha REM do programa BASIC com 44 caracteres, substituindo o cálculo de A0 por A0 = PEEK (841) + 256 * PEEK (842) + 5:

0  REM --------10--------20--------30--------40--44
1  RESTORE 2:A0 =  PEEK (841) + 256 *  PEEK (842) + 5:A1 = A0 + 18:A2 = A0 + 24: POKE 288,201: FOR A = A0 TO A0 + 43: READ B: POKE A,B: NEXT : POKE A1,A2 AND 255: POKE A1 + 1,A2 / 256: POKE 289,A0 AND 255: POKE 290,A0 / 256: POKE 288,195
2  DATA 33,14,,57,126,254,93,192,35,126,254,221,192,33,8,,57,17,24,63,115,35,114,201,193,209,51,51,50,29,1,183,58,27,1,50,28,1,196,223,221,195,97,221

Ainda outra possibilidade é colocá-la num espaço no início da RAM (a partir de $003b / 59).

1  RESTORE 2:A0 = 59:A1 = A0 + 18:A2 = A0 + 24: POKE 288,201: FOR A = A0 TO A0 + 43: READ B: POKE A,B: NEXT : POKE A1,A2 AND 255: POKE A1 + 1,A2 / 256: POKE 289,A0 AND 255: POKE 290,A0 / 256: POKE 288,195
2  DATA 33,14,,57,126,254,93,192,35,126,254,221,192,33,8,,57,17,24,63,115,35,114,201,193,209,51,51,50,29,1,183,58,27,1,50,28,1,196,223,221,195,97,221

Cassete

Para representar informação em fita cassete, o MC1000 converte informação digital em som usando as convenções abaixo.

Bits

|1|
┌┐
 └┘

Um "período curto" para representar bits "1". A parte alta e a parte baixa da onda sonora duram cerca de 3,628118×10−4 segundos cada uma (16 amostras em um arquivo .wav com 44.100 quadros por segundo).

| 0 |
┌─┐
  └─┘

Um "período longo", com o dobro da duração do período curto, para representar bits "0".

Bytes

Para armazenar um byte, a convenção é a seguinte:

  1. Um período curto para marcar o início do byte.
  2. Os oito bits do byte, do menos ao mais significativo (em outras palavras, lido da direita para a esquerda).
  3. Um bit de paridade ("1" se o byte tem uma quantidade par de bits "1"; "0" se a quantidade for ímpar).

Por exemplo, o byte 209 (em hexadecimal $D1, em binário %11010001), que tem quatro bits "1" (paridade par), seria representado da seguinte forma:

| |1| 0 | 0 | 0 |1| 0 |1|1|1|
┌┐┌┐┌─┐ ┌─┐ ┌─┐ ┌┐┌─┐ ┌┐┌┐┌┐
 └┘└┘ └─┘ └─┘ └─┘└┘ └─┘└┘└┘└┘marca de iníciobit de paridade

Arquivo

Isto posto, um "arquivo" em fita cassete é armazenado da seguinte forma:

  1. Um sinal piloto composto de 4096 períodos curtos seguidos de 256 períodos longos. O MC1000 usará estes períodos para tentar se ajustar à velocidade do toca-fitas no momento da leitura.
  2. Cabeçalho:
    1. Até 14 bytes contendo o "nome" do arquivo. Se o nome tiver menos de 14 bytes, é finalizado com o caracter de controle CR (carriage return, CHR$(13)).
    2. Dois bytes contendo o endereço de memória do primeiro byte do bloco de dados salvo.
    3. Dois bytes contendo o endereço de memória seguinte ao último byte do bloco de dados salvo.
  3. O bloco de dados propriamente dito, cuja quantidade de bytes é determinada pela diferença entre os dois endereços acima.

Por exemplo, o programa BASIC:

10  HOME 
20  PRINT "OLA, MUNDO!"

é armazenado na memória do endereço $03D5 ao endereço $03EF:

03D5 DB 03 0A
03D8 00 93 00 EE 03 14 00 97
03E0 22 4F 4C 41 2C 20 4D 55
03E8 4E 44 4F 21 22 00 00 00

Ao se emitir um comando SAVE OLA, o MC1000 produz:

Sinal piloto:

┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐ ... (4096 períodos curtos)
 └┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘

┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐  ... (256 períodos longos)
  └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘

Nome do arquivo: "OLA". Nomes de programas BASIC são salvos com 5 caracteres, sendo as posições não usadas preenchidas com espaços. Como tem menos de 14 caracteres, é terminado por um caracter CR.

| |1|1|1|1| 0 | 0 |1| 0 | 0 |
┌┐┌┐┌┐┌┐┌┐┌─┐ ┌─┐ ┌┐┌─┐ ┌─┐          (byte $4F = "O")
 └┘└┘└┘└┘└┘ └─┘ └─┘└┘ └─┘ └─┘
| | 0 | 0 |1|1| 0 | 0 |1| 0 | 0 |
┌┐┌─┐ ┌─┐ ┌┐┌┐┌─┐ ┌─┐ ┌┐┌─┐ ┌─┐      (byte $4C = "L")
 └┘ └─┘ └─┘└┘└┘ └─┘ └─┘└┘ └─┘ └─┘
| |1| 0 | 0 | 0 | 0 | 0 |1| 0 |1|
┌┐┌┐┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌┐┌─┐ ┌┐     (byte $41 = "A")
 └┘└┘ └─┘ └─┘ └─┘ └─┘ └─┘└┘ └─┘└┘
| | 0 | 0 | 0 | 0 | 0 |1| 0 | 0 | 0 |
┌┐┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌┐┌─┐ ┌─┐ ┌─┐  (byte $20 = " ")
 └┘ └─┘ └─┘ └─┘ └─┘ └─┘└┘ └─┘ └─┘ └─┘
| | 0 | 0 | 0 | 0 | 0 |1| 0 | 0 | 0 |
┌┐┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌┐┌─┐ ┌─┐ ┌─┐  (byte $20 = " ")
 └┘ └─┘ └─┘ └─┘ └─┘ └─┘└┘ └─┘ └─┘ └─┘
| |1| 0 |1|1| 0 | 0 | 0 | 0 | 0 |
┌┐┌┐┌─┐ ┌┐┌┐┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐      (byte $0D = CR)
 └┘└┘ └─┘└┘└┘ └─┘ └─┘ └─┘ └─┘ └─┘

Endereço de início do bloco de dados salvo: $03D5.

| |1| 0 |1| 0 |1| 0 |1|1| 0 |
┌┐┌┐┌─┐ ┌┐┌─┐ ┌┐┌─┐ ┌┐┌┐┌─┐          (byte $D5)
 └┘└┘ └─┘└┘ └─┘└┘ └─┘└┘└┘ └─┘
| |1|1| 0 | 0 | 0 | 0 | 0 | 0 |1|
┌┐┌┐┌┐┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌┐     (byte $03)
 └┘└┘└┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘└┘

Endereço de fim do bloco de dados salvo: $03F0 (= $03EF + 1). (Programas em BASIC trazem um byte adicional além da área do BASIC.)

| | 0 | 0 | 0 | 0 |1|1|1|1|1|
┌┐┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌┐┌┐┌┐┌┐┌┐         (byte $F0)
 └┘ └─┘ └─┘ └─┘ └─┘└┘└┘└┘└┘└┘
| |1|1| 0 | 0 | 0 | 0 | 0 | 0 |1|
┌┐┌┐┌┐┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌┐     (byte $03)
 └┘└┘└┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘└┘

Bloco de dados propriamente dito, contendo 28 (= $03F0 − $03D5 + 1) bytes. (Assumimos aqui que o endereço $03F0 contivesse o byte $00.)

| |1|1| 0 |1|1| 0 |1|1|1|
┌┐┌┐┌┐┌─┐ ┌┐┌┐┌─┐ ┌┐┌┐┌┐             (byte $DB)
 └┘└┘└┘ └─┘└┘└┘ └─┘└┘└┘└┘
...                                  (...)
| | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |1|
┌┐┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌┐ (byte $00)
 └┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘└┘

Utilitários em Java

Com base nestas informações, foi criado um conjunto de aplicativos em Java para extrair o conteúdo de arquivos .wav, e vice-versa:

Uso:

  • java MC1000CasTools <opções> <arq_origem> -<formato>
  • java MC1000CasTools <opções> <arq_origem> -list
  • java MC1000CasTools <opções> <arq_origem> <arq_destino>

Converte arquivos entre formatos diferentes:

*.bas
Arquivo contendo código-fonte de programa BASIC do MC1000.
*.bin
Arquivo contendo dados brutos de um bloco de memória (no caso de um programa BASIC, é o programa em formato tokenizado).
*.cas
Arquivo contendo dados tal como gerados/lidos pelos comandos SAVE, LOAD e TLOAD do MC1000: cabeçalho (nome de arquivo em cassete, endereço de início, endereço de fim) + dados brutos.
*.wav
Som produzido pelo MC-1000 correspondente aos dados de cassete.

Opções:

-b
Na conversão de BIN para CAS ou WAV, indica que o conteúdo do arquivo é um programa BASIC, para que o nome de arquivo em cassete seja adequadamente formatado (até 5 caracteres, completados com espaços).
-n <nome>
Na conversão de BAS ou BIN para CAS ou WAV, especifica o nome de arquivo em cassete (até 14 caracteres). O valor predefinido é vazio.
-i <número>
Se um arquivo WAV contém mais de um arquivo de cassete, indica qual deles converter. O valor predefinido é 1.
-v
Modo verboso. Exibe diversas informações sobre o processo de conversão.

Outros parâmetros:

<arq_origem>
O arquivo a ser convertido. O formato será reconhecido pela extensão.
-<formato>
O formato final da conversão: -bas, -bin, -cas ou -wav. Se esta opção for usada, o nome do arquivo de destino será o mesmo do arquivo de origem com a extensão devidamente modificada.
-list
Converte para formato BASIC e exibe na tela, sem gerar arquivo.
<arq_destino>
Se não for especificado um formato de conversão, deve-se fornecer o nome do arquivo de destino. O formato da conversão será detectado pela extensão.

Exemplos de uso:

java MC1000CasTools -n prog programa.bas -wav

Converte o arquivo "programa.bas" (código fonte de programa BASIC) para WAV (o nome de arquivo, calculado automaticamente, será "programa.bas.wav"). O nome de arquivo em cassete será "PROG" (ou seja, para carregar o programa no MC1000 pela porta EAR usar-se-á LOAD PROG.

java MC1000CasTools -b -n prog programa.bin -cas

Converte o arquivo "programa.bin" (contendo um programa BASIC em forma tokenizada) em formato de cassete, acrescentando o cabeçalho de informações que o MC1000 salva em cassete antes dos dados. O nome do arquivo em cassete será "PROG". Como a extensão do arquivo original é BIN, a ferramenta não sabe que o conteúdo é um programa BASIC; é preciso acrescentar a opção -b para que o nome do arquivo seja inserido no cabeçalho num formato próprio para nomes de cassete de programas BASIC (até 5 caracteres completados com espaços). O nome do arquivo resultante, calculado automaticamente, será "programa.cas".

java MC1000CasTools -i 4 -v fita_digitalizada.wav -bin

Dado um arquivo WAV contendo mais de um arquivo de cassete, por padrão apenas o primeiro arquivo de cassete é extraído. Aqui a opção -i 4 está selecionando o quarto arquivo de cassete contido no arquivo WAV. A opção -v indica que o processo será "verboso", exibindo na tela informações durante o processo de conversão. O nome do arquivo resultante, calculado automaticamente, será "fita_digitalizada(4).bin".

java MC1000CasTools batnav.cas BatalhaNaval.bin

Aqui foi especificado o nome do arquivo de destino. Converte o arquivo em formato de cassete "batnav.cas" para o formato de dados brutos, com o nome "BatalhaNaval.bin". As extensões dos nomes de arquivos definem o tipo de conversão.

java MC1000CasTools calculo.wav -list

Converte o arquivo WAV para código-fonte BASIC e o lista na tela, sem gerar nenhum arquivo novo.


Python

Em 20/07/2022 uma versão em linguagem Python 3 foi disponibilizada na pasta "/py" do projeto.

A sintaxe de comandos e opções é a mesma. Naturalmente, sem a palavra "java" no início.


Download e desenvolvimento

Quem quiser baixar e/ou contribuir com o desenvolvimento destes utilitários pode encontrá-los no GitHub:

"MC-1000", "MC 1000" ou "MC1000"?

No gabinete do computador, nos acessórios e nos materiais impressos (caixa, capas dos manuais, rótulos e capas das fitas, propagandas etc.) ...