O exame da ROM do MC1000 revela algumas características que foram programadas, mas que não funcionam devido a erros de lógica.
Para cada erro apresenta-se um “patch”, um “remendo”, que poderia consertar o erro numa versão melhorada da ROM.
Pela programação na ROM, o caracter de controle FF deveria avançar o cursor, isto é, movê-lo uma coluna à direita; se estivesse no fim de uma linha, iria para o início da linha seguinte; e se estivesse no fim da tela, continuaria no mesmo lugar e um bip soaria para avisar da impossibilidade do cursor avançar.
Mas há um erro na rotina de verificação de fim de tela. A posição do cursor é subtraída em um (equivalendo a um movimento para a esquerda), e quando finalmente se soma um para fazê-lo mover-se à direita... o resultado é que o cursor não sai do lugar!
pc ← POSIÇÃO_DO_CURSOR
ft ← FIM_DA_TELA + 1
pc ← pc - 1
(deveria ser: ft ← ft - 1)
se pc = ft então
produz bip
senão
pc ← pc + 1
POSIÇÃO_DO_CURSOR ← pc
fim se
Como efeito colateral, este bug inutilizou o uso das teclas <H>, <J>, <K> e <L> associadas a <CTRL> para mover o cursor enquanto se digita uma linha. A lógica está toda implementada, e quando se pressiona <CTRL>+<L> a posição do cursor é até avançada no buffer de linha, mas não se move na tela.
CA69 2B DEC HL
CA69 1B DEC DE
DEL deveria mover o cursor para a esquerda, apagando (isto é, substituindo por espaço) o caracter que estivesse na posição do cursor. Mas uma verificação mal feita faz com que a porção de código que interpreta DEL como um caracter de controle nunca seja executada. Por isso ele acaba sendo interpretado como um caracter comum.
c ← CARACTER_A_IMPRIMIR
se c < 32 então
(deveria ser: se c < 32 ou c = 127 então)
(interpreta caracteres de controle)
se c = 7 então
produz bip
fim se
(...)
se c = 127 então
(...)
fim se
senão
imprime c como um caracter comum
fim se
C8BE C2F9C8 JP NZ,$C8F9
C8BE C2F4C8 JP NZ,$C8F4
Usando CHR$(27)
(ESC) é possível posicionar o cursor no MC1000. Para programar o cursor em modo de 32 colunas, dever-se-ia usar CHR$(27);"=";CHR$(
linha);CHR$(
coluna)
. E em modo de 80 colunas, CHR$(27);"=";CHR$(
linha);CHR$(32+
coluna)
.
Um erro faz com que o posicionamento de cursor para 32 colunas não funcione corretamente: o cursor é posicionado uma linha abaixo de onde deveria ficar.
li ← NÚMERO_DA_LINHA
co ← NÚMERO_DA_COLUNA
se co < 32 então
(para 32 colunas)
co ← co + 32
(esta soma não deveria ser feita!)
senão
(para 80 colunas)
co ← co - 32
fim se
posiciona o cursor nas coordenadas (li,co)
Use o posicionamento para 80 colunas (CHR$(27);"=";CHR$(
linha);CHR$(32+
coluna)
). Ele funciona mesmo quando o vídeo está em 32 colunas.
C994 C620 ADD A,$20
C994 00 NOP
C995 00 NOP
O MC1000 tem um par de instruções não documentadas no manual de BASIC: FAST
e SLOW
. FAST
(“rápido”) é o modo normal de execução do MC1000. O SLOW
(“lento”) faz com que o computador adicione uma brevíssima pausa entre os comandos. Mas, por um erro de lógica, o comando não funciona. Primeiro ele verifica (corretamente) se não há nenhum parâmetro a seguir (se houver, ele produz erro de sintaxe, “?SN ERRO”); depois disso, ele “invade” a programação do comando SET, produzindo um erro de acordo com o modo de vídeo. Em modo TEXT ele produz erro de tipo incompatível (“?TI ERRO”); em modo gráfico (GR ou HGR) ele produz erro de falta de operando (“?FO ERRO”).
FAST: x ← 0
FAST_SLOW: endereço de memória(864) ← x
retorna
SLOW: x ← 1
(faltou: desvia para FAST_SLOW)
SET: (...)
É possível entrar em modo SLOW
por meio da instrução POKE 864,1
.
Este patch exige alguns bytes do espaço não utilizado no fim da ROM.
D74D 22D2 DB $D222
F563 FFFFFF DB $FF,$FF,$FF ; (Lixo no fim da ROM)
F566 FFFFFF DB $FF,$FF,$FF
D74D 63F5 DB $F563
F563 C0 RET NZ
F564 3E01 LD A,$01
F566 C31ED2 JP $D21E
VTAB
é uma cláusula da instrução PRINT
não documentada no Manual do BASIC.
Deveria servir para posicionar o cursor verticalmente, mas não funciona por uma sucessão de erros na lógica que calcula a nova posição do cursor.
Tenta isto e vê o cursor desaparecer... 🙁
PRINT VTAB(12);
Para recuperar o cursor, digita (às cegas mesmo) o comando HOME
seguido da tecla <RETURN>, ou então pressiona <CTRL>+<↑>.
Pode-se usar a sequência de escape para posicionamento do cursor, obtendo-se a coluna atual do cursor da variável de sistema LCNT: PRINT CHR$(27);"=";CHR$(
linha);CHR$(32+PEEK(349));
E141 210080 LD HL,$8000
E144 ED52 SBC HL,DE
E146 13 INC DE
E147 01 DB $01 ; "LD BC,..." (oculta as duas instruções seguintes)
E148 7B LD A,E
E149 3D DEC A
E14A 2803 JR Z,$E14F
E14C 09 ADD HL,BC
E14D 18FA JR $E149
E141 2A6301 LD HL,($0163) ; SNAM = início da VRAM.
E144 ED5B1301 LD DE,($0113) ; DLNG = largura da linha.
E148 04 INC B ; parâmetro de VTAB() + 1.
E149 1801 JR $E14C
E14B 19 ADD HL,DE
E14C 10FD DJNZ $E14B
E14E 00 NOP
Esta é uma rotina da ROM do MC1000 (no endereço $CC2C) voltada a permitir que um ou dois jogadores joguem compartilhando o teclado.
Jogador | 🡄 | 🡅 | 🡇 | 🡆 | Ação |
---|---|---|---|---|---|
Esquerda | <A> | <S> | <D> | <F> | <Q> <W> <E> <R> <T> |
Direita | <K> | <L> | <;> | <:> | <I> <O> <P> <↑> <RETURN> |
A rotina traduz um código de tecla para um byte cujos bits indicam a qual dos controles ela corresponde, se algum.
A listagem desta rotina na ROM está no Manual de Referência, e vemos claramente que o erro surgiu por erro de digitação de um dos rótulos de desvio internos da rotina. Foi digitado “K6” em vez de “J6”, e por azar o rótulo “K6” existia em outro lugar da listagem, então o erro não foi detectado durante a compilação. O resultado foi um desvio para fora da rotina (para o endereço $C3FB em vez de $CC83), inutilizando-a. Eis a listagem original (em assembly 8080) na página 96:
LOC OBJ LINE SOURCE STATEMENT
[...]
CC59 FE41 2175 CPI 'A'
CC5B C260CC 2176 JNZ J1
CC5E 0604 2177 MVI B,4
2178 J1:
CC60 FE46 2179 CPI 'F'
CC62 C267CC 2180 JNZ J2
CC65 0608 2181 MVI B,8
2182 J2:
CC67 FE53 2183 CPI 'S'
CC69 C26ECC 2184 JNZ J3
CC6C 0610 2185 MVI B,10H
2186 J3:
CC6E FE43 2187 CPI 'C'
CC70 C275CC 2188 JNZ J4
CC73 0610 2189 MVI B,10H
2190 J4:
CC75 FE44 2191 CPI 'D'
CC77 C27CCC 2192 JNZ J5
CC7A 0620 2193 MVI B,20H
2194 J5:
CC7C FE4B 2195 CPI 'K'
CC7E C2FBC3 2196 JNZ K6 (rótulo errado, seria J6!)
CC81 0605 2197 MVI B,5
2198 J6:
CC83 FE3A 2199 CPI ':'
[...]
(No assembly do Z80, CPI
é CP
, JNZ
é JP NZ
e MVI
é LD
.)
CC7E C2FBC3 JP NZ,$C3FB
CC7E C283CC JP NZ,$CC83
No modo HGR
, quando as instruções de desenho tentam desenhar/apagar uma linha em 45° com uma extremidade na coordenada horizontal 255, as instruções não conseguem terminar de desenhar a linha e o computador trava.
Por exemplo, executar HGR : PLOT 255-7,0 TO 255,7
deveria produzir apenas um pequeno traço diagonal no canto superior direito da tela, mas a linha continua sendo desenhada tela abaixo:
O problema ocorre porque nas rotinas específicas que tratam as linhas em 45°, identifica-se a chegada ao fim da linha quando, após somar 1 ao registrador contendo a coordenada horizontal do pixel recém desenhado, seu valor é superior ao da coordenada final. Mas como o em 8 bits 255 + 1 = 0, seu valor NUNCA é maior do que 255!
x ← MENOR_COORDENADA_X
y ← COORDENADA_Y_CORRESPONDENTE
faça
desenha ponto nas coordenadas (x,y)
x ← x + 1 (em 8 bits, 256 é automaticamente transformado em 0)
y ← y + 1
se LINHA_ASCENDENTE
y ← y - 2
fim se
até que x > MAIOR_COORDENADA_X
(esta condição nunca é satisfeita quando a MAIOR_COORDENADA_X é 255!)
A solução é comparar a coordenada atual com a coordenada final antes de somar 1.
D2B6 7C LD A,H
D2B7 24 INC H
D2B8 45 LD B,L
D2B9 2C INC L
D2BA CDDAD3 CALL $D3DA ; PLOTAB
D2BD F1 POP AF
D2BE F5 PUSH AF
D2BF 3802 JR C,$D2C3
D2C1 2D DEC L
D2C2 2D DEC L
D2C3 7C LD A,H
D2C4 B9 CP C
D2C5 28EF JR Z,$D2B6
D2C7 38ED JR C,$D2B6
D2B6 7C LD A,H
D2B7 45 LD B,L
D2B8 CDDAD3 CALL $D3DA ; PLOTAB
D2BB 7C LD A,H
D2BC B9 CP C
D2BD 280A JR Z,$D2C9
D2BF 24 INC H
D2C0 2C INC L
D2C1 F1 POP AF
D2C2 F5 PUSH AF
D2C3 38F1 JR C,$D2B6
D2C5 2D DEC L
D2C6 2D DEC L
D2C7 18ED JR $D2B6