Gerência de
Memória no MSX (I)
Resumo
O objetivo deste artigo é mostrar como a memória é distribuída no MSX, desde a ROM,
passando pelos cartuchos e chegando a RAM. São 2 artigos, onde o primeiro fala sobre
slots primários e o segundo sobre slots secundários, incluindo a Mapper e Megaram.
1- Introdução
Sabemos que a memória do MSX tem 64 Kb. Mas a ROM tem 32 Kb, a RAM 64, fora
os cartuchos. Não são mais de 64 Kb? A resposta é sim. Então, como o Z-80 pode acessar
diretamente mais de 64 Kb? A resposta é ele não acessa!
De fato, o Z-80 só enxerga 64 Kb. Para acessar todas estas memórias, será necessário
comutar (trocar) a memória ativa para outra que se deseje acessar. Por exemplo, o Z-80
deixa de enxergar a ROM e passa a enxergar a RAM.
Para facilitar este mecanismo, os projetistas do MSX dividiram a memória em 4 partes,
chamadas de páginas e cada fatia desta pode ser comutada independentemente das outras. A
figura 1.1 mostra a memória principal, que é enxergada pelo Z-80. A vantagem desta
divisão será comprovada mais adieante.
Fig.1.1 - Memória principal
As páginas são numeradas de 0 a 3, têm 16 Kb cada uma e seus limites estão expressos
na figura 1.1.
Mas, quantas memórias de 64 Kb podem ser compartilhadas na memória principal?
Podemos compartilhar 4 memórias. Cada memória é chamada de slot, e varia de 0 a 3. No
MSX 1, Expert, a configuração de slots é dada, conforme a figura 1.2.
Fig.1.2 - Configuração dos slots no Experts
Esta configuração pode variar em outros modelos de MSX, mas a ROM está sempre no
slot 0.
2- Chaveamento de memória
Cada página da memória principal pode enxergar somente a região de memória de
qualquer um dos 4 slots correspondentes ao seus limites. Por exemplo, a página 0 enxerga
de 0000 a 3FFF de qualquer um dos 4 slots, a página 1 enxerga de 4000 a 7FFF de qualquer
um dos 4 slots, etc.
Fica claro aqui que não existe uma quinta memória de 64 Kb. A memória principal
referida aqui é o que o Z-80 está enxergando, conforme o chaveamento da memória. As
memórias são somente os 4 slots físicos.
Foi dito na seção anterior, que podemos chavear as páginas independentemente umas
das outras. De fato podemos ter a página 0 enxergando o slot 0, a página 1 enxergando o
slot 3, a página 2 enxergando o slot 1 e a página 3 enxergando o slot 2.
Afinal, como eu posso chavear a memória, para que cada página enxergue a fatia de
memória do slot desejado?
O chip PPI é o responsável por chavear a memória. A porta A8 controla o chaveamento
conforme a figura 2.1.
Fig.2.1 - Porta A8
A porta A8 é um número de 8 bits, onde os 2 primeiros indicam qual slot a página 3
enxerga, os 2 seguintes indicam qual slot a página 2 enxerga, etc. Se lermos esta porta e o
resultado for 128 (&B10000000), temos que a página 3 está enxergando o slot 2 (&B10) e
as demais estão enxergando o slot 0.
Quando damos a partida no Expert, a memória é configurada conforme a figura 2.2. Os
32 Kb de ROM ficam nas páginas 0 e 1 e 32 Kb de RAM ficam nas páginas 2 e3. Portanto,
a porta A8 tem o seguinte valor: 160 (&B10100000)
Fig2.2 - Configuração inicial do Expert.
Quando mudamos o slot de uma página, estamos mudando todo o acesso à memória nos
limites correspondentes aquela página. Dizemos para o sistema: “esquece o slot X e use o
slot Y”. Portanto, todo cuidado é pouco! Este assunto será abordado adiante.
3- O Perigo da Troca de Slots
Nesta seção, será abordado como é percorrida a memória, no par busca-execução em
assembly.
Assim como no Basic, quando nosso programa roda, ele começa em uma dada posição
e, caso não haja desvios (goto, then <linha>, return, etc), segue para a posição logo abaixo.
No caso específico do assembly, a busca consome n bytes (referentes a instrução, onde
normalmente são 1, 2 ou 3 bytes) e a próxima instrução será executada em p+n.
Seja o programa abaixo:
1800
1802
1803
3E C8
80
92
LD A,C8
ADD A,B
SUB D
Se o programa começar em 1800, irá executar a instrução LD A,C8. A próxima
instrução será em 1802 e depois em 1803.
Se estivermos em 1800 e a página que for trocada for a mesma que o programa estiver
rodando, neste caso a página 0, todo conteúdo de memória entre 0000 e 3FFF será trocado
para o conteúdo de memória do novo slot. Com isso, a linha 1802 não será mais ADD A,B
e sim o que estiver na página, no momento da troca. E esta será a próxima linha de
execução. Como o programa rodará a partir da posição 1802 da nova página, buscará as
instruções relativas ao que for encontrado lá, e dali em diante. Portanto, estaremos
perdendo o controle de nosso programa, pois a página inicial não voltará sozinha!
Para fazer leituras em páginas de outro slot, devemos estar fora da página a ser trocada.
4- Estratégia de jogos e cartuchos
A BIOS contém instruções básicas, como escrever na tela, som, sprites, joystick etc, já
prontas de fábrica para que o usuário não se preocupe em gastar tempo para fazê-las e nem
espaço em seu programa com elas. A BIOS está na ROM, na página 0.
Quando programamos em Basic, precisamos das páginas 0 e 1 da ROM, que contém
este interpretador. Porém, quando rodamos em assembly, o máximo que iremos precisar é
da BIOS, que está integralmente na página 0.
Caso você queira, você poderá colocar seu programa na página 0, mas deverá fazer
todos os acessos diretos aos chips de som, vídeo, PPI etc, sem utilizar a BIOS.
Normalmente, os jogos em assembly utilizam a memória a partir da posição 4000H, ou
seja, página 1. Isto, porque este programa não precisa do interpretador Basic.
Os jogos utilizam uma estratégia simples para se carregar em tais posições. Quando
chamados do Basic, são carregados na página 2 (RAM usada pelo Basic), são executados a
partir de um trecho perto do final do bloco, trocando o slot 1 da página 1 para RAM e
copiando o bloco de programa do jogo para o endereço 4000H.
Caso a página já seja RAM, basta mover o bloco para a posição desejada.
A figura 4.1 mostra como é um arquivo de um bloco de um jogo. A figura 4.2 mostra
como o bloco é carregado na memória e copiado.
Fig.4.2 - Arquivo de jogo
Fig.4.2 Esquerda: Carregamento pelo Basic. Trecho verde roda, trocando slot da página 1 de 0 para 2. Direita: trecho verde copia
bloco amarelo para página 1. O bloco em amarelo tem exatos 16 Kb (quando não é o último).
Se o segundo bloco for carregado na página 2, basta que o trecho em verde da figura 4.2
apenas copie o bloco amarelo para a posição 8000H. Ao final, deverá pular para a posição
4000H para que o jogo se inicie. O jogo na verdade são os 32 Kb, desde a posição 4000H.
Se o primeiro bloco do jogo for carregado no Basic sem a extensão “,r”, obviamente, o
jogo não rodará. Para testar o que foi dito, vejamos o que os dois blocos do jogo Konami’s
Soccer faz. Primeiramente, são apresentados os blocos, se lidos em um editor hexadecimal.
Bloco 1:
000000
000010
000020
000030
000040
000050
000060
000070
000080
000090
0000A0
0000B0
0000C0
0000D0
0000E0
0000F0
000100
000110
000120
FE
00
E0
00
46
CD
C3
5C
03
0C
9B
ED
E0
03
E0
CF
E0
41
69
00
00
00
00
20
3E
47
40
4F
B1
FD
B0
CD
E0
79
41
1F
10
48
90
00
00
C3
0D
01
00
5E
06
26
31
3E
3E
34
FE
08
D0
0C
CD
60
00
00
C4
34
B7
85
23
00
80
80
01
01
3A
03
42
CD
21
59
D0
00
00
4B
FB
F3
6F
56
21
CD
F3
32
FB
02
30
CD
D8
04
46
00
00
00
CD
CD
FC
D0
EB
C1
24
21
05
18
E0
01
42
4B
E0
CD
D0
00
00
3E
87
16
24
E9
FC
00
00
E0
FE
E6
E5
DD
C0
35
27
41
41
00
01
48
4F
C9
F3
09
3E
E0
CD
00
40
CD
42
11
C0
4A
42
42
00
F3
CD
FB
83
ED
B6
C3
11
56
00
20
66
E8
2A
CD
CD
70
07
00
CD
C8
C9
5F
56
4F
32
01
48
00
03
40
43
49
A1
B6
40
32
00
16
40
00
D0
CD
23
9A
E0
CD
00
21
F5
2A
CD
4C
4B
00
00
05
4F
3E
00
14
38
23
FD
01
33
00
55
40
45
A0
AF
C3
00
67
00
21
00
00
C9
01
23
21
7F
5A
C3
45
27
10
47
C3
CA
00
00
00
05
32
00
E1
0F
23
2E
13
AF
4C
ED
41
13
AF
F3
41
00
E0
00
E0
05
00
87
0F
7E
40
36
32
4E
4B
A2
3A
C3
41
CD
00
02
00
CB
E0
00
CD
E6
E6
22
00
05
21
00
41
03
C7
CD
94
···`···ABp@·····
·······AB·2·g···
················
····K·>····O!···
F ·4···H··@>·2··
·>·····O········
·G··o·$··_······
\@^#V····V·8····
·O··!····O####~·
··&··$·>·2··!.@"
··1··!······•·6·
··>·2···VH·3Z·2·
··>··········LN!
··4:···@ ·!UE·K·
·y··0···f@·@'A·A
·A·B·B·B·C*E··:·
·····K··*I··G···
A··!··5···L···A·
iH·YF·'J··K··A··
FE
FF
09
35
F8
F8
F8
F8
FB
FB
FF
F7
17
0D
E5
F8
F8
F8
F8
00
EF
82
80
00
00
00
00
FB
FB
FC
00
FE
00
80
00
00
00
00
90
EB
5D
75
5C
5C
5C
5C
D4
D4
7C
78
94
A4
75
6C
6C
6C
6C
57
F7
82
80
02
02
02
02
03
03
03
02
04
03
80
02
02
02
02
D0
F6
B1
85
F8
F8
F8
F8
08
08
07
FF
FD
18
85
F8
F8
F8
F8
00
EA
82
80
00
00
00
00
00
00
00
FC
FC
00
80
00
00
00
00
D0
21
05
95
60
60
60
60
CC
CC
84
7C
98
A0
95
74
74
74
74
E3
80
83
80
03
03
03
03
04
04
04
03
03
04
80
03
03
03
03
01
21
35
A9
08
08
08
08
0D
0D
10
07
F7
E5
A9
08
08
08
08
F5
80
80
80
FE
00
FC
FF
00
00
FC
FD
00
80
80
FE
FA
FB
FA
01
D1
45
BD
64
80
88
90
D8
D8
80
90
78
F5
BD
64
AC
A0
98
FF
80
80
80
03
03
03
03
03
03
03
04
02
80
80
03
03
03
03
FF
25
55
08
08
08
08
F8
F8
F7
17
0F
08
05
08
08
08
08
39
01
81
80
FE
00
FC
00
F8
F8
00
00
FD
FF
81
FE
FB
F9
FC
81
01
79
65
68
84
8C
94
D0
E0
78
88
8C
9C
15
68
A8
B0
BC
49
01
81
80
04
04
04
04
02
02
02
04
03
04
81
04
04
04
04
81
···W············
······!·!···%·y·
··]·····5·E·U·e·
5·u···········h·
··\···`···d·····
··\···`·········
··\···`·········
··\···`·········
················
··············x·
··|·············
··x···|·········
··········x·····
················
··u···········h·
··l···t···d·····
··l···t·········
··l···t·········
··l···t·····9·I·
Bloco 2
006000
006010
006020
006030
006040
006050
006060
006070
006080
006090
0060A0
0060B0
0060C0
0060D0
0060E0
0060F0
006100
006110
006120
Depois de carregados, inspecionamos a memória do MSX, através do debug do
BrMSX, para compararmos os resultados:
+¦
¦
¦
¦
¦
¦
¦
¦
¦
¦
¦
¦
¦
¦
¦
¦
¦
¦
+-
BrMSX debugger -------------------------- SCC ------- Stack ---- VDP BASE() +
¦
¦
¦
4000 BE 42 70 40 00 00 00 00 .Bp@.... ¦ F1 0000 ¦ SP+6 E077 Name
3800 ¦
4008 00 00 00 00 00 00 00 00 ........ ¦ F2 0000 ¦ SP+4 5A76 Pattrn 2000 ¦
4010 41 42 07 32 00 67 00 E0 AB.2.g.. ¦ F3 0000 ¦ SP+2 40BE Color 0000 ¦
4018 02 E0 00 00 00 00 00 00 ........ ¦ F4 0000 ¦ SP A8D3 SpAttr 3B00 ¦
4020 00 00 00 00 05 00 00 00 ........ ¦ F5 0000 ¦ SP-2 185E SpImag 1800 ¦
4028 00 00 00 C3 C4 4B CD 3E .....K.> ¦ V1 00 0 ¦ SP-4 D303
¦
4030 01 F3 CD 16 4F 21 05 E0 ....O!.. ¦ V2 00 0 ¦ SP-6 73A8 Screen 2
¦
4038 CB 46 20 0D 34 FB CD 87 .F .4... ¦ V3 00 0 +- Advanced Info --------¦
4040 48 CD C8 40 3E 00 32 05 H..@>.2. ¦ V4 00 0 ¦
¦
4048 E0 CD 3E 01 B7 F3 FC 16 ..>..... ¦ V5 00 0 ¦ Clocks Left
0E90B ¦
4050 4F FB C9 00 00 00 00 00 O....... ¦
¦ Interrupt Pending NO ¦
4058 00 C3 47 00 85 6F D0 24 ..G..o.. +- Drive ---¦ VDP Waiting
NO ¦
4060 C9 83 5F D0 14 C9 E1 87 .._..... ¦ D0 D0
¦ VDP Low Byte
6C ¦
4068 CD 5C 40 5E 23 56 EB E9 .\@^#V.. ¦ D1 00
¦ VDP Lookahead
20 ¦
4070 F3 ED 56 CD 38 01 0F 0F ..V.8... ¦ D2 09
¦ VDP Address
3974 ¦
4078 E6 03 4F 06 00 21 C1 FC ..O..!.. ¦ D3 00
¦ VDP Access
Write ¦
¦ D4 61
¦ VDP Status Byte
04 ¦
BrMSX settings -------------------------------------------------------------¦
¦ Resolution: 320x200 Frame skipping: 0001 Bar Graph: OFF Emulation: NORMAL ¦
¦ Joy: - Sound: ON
VSync: OFF Session: SINGLE COM: 1 Video Cache: ON
¦
¦ Command:
SCC: OFF
¦
+------------------------------------------------------------------------------+
+- BrMSX debugger -------------------------- SCC ------- Stack ---- VDP BASE() +
¦
¦
¦
¦
¦ 8000 E3 01 F5 01 FF FF 01 01 ........ ¦ F1 0000 ¦ SP+6 E077 Name
3800 ¦
¦ 8008 01 FF EF EB F7 F6 EA 21 .......! ¦ F2 0000 ¦ SP+4 5A76 Pattrn 2000 ¦
¦ 8010 80 21 80 D1 80 25 81 79 .!...%.y ¦ F3 0000 ¦ SP+2 40BE Color 0000 ¦
¦ 8018 81 09 82 5D 82 B1 82 05 ...].... ¦ F4 0000 ¦ SP A8D3 SpAttr 3B00 ¦
¦ 8020 83 35 80 45 80 55 80 65 .5.E.U.e ¦ F5 0000 ¦ SP-2 185E SpImag 1800 ¦
¦ 8028 80 35 80 75 80 85 80 95 .5.u.... ¦ V1 00 0 ¦ SP-4 D303
¦
¦ 8030 80 A9 80 BD 80 08 FE 68 .......h ¦ V2 00 0 ¦ SP-6 73A8 Screen 2
¦
¦ 8038 04 F8 00 5C 02 F8 00 60 ...\...` ¦ V3 00 0 +- Advanced Info --------¦
¦ 8040 03 08 FE 64 03 08 00 84 ...d.... ¦ V4 00 0 ¦
¦
¦ 8048 04 F8 00 5C 02 F8 00 60 ...\...` ¦ V5 00 0 ¦ Clocks Left
0E90B ¦
¦ 8050 03 08 00 80 03 08 FC 8C ........ ¦
¦ Interrupt Pending NO ¦
¦ 8058 04 F8 00 5C 02 F8 00 60 ...\...` +- Drive ---¦ VDP Waiting
NO ¦
¦ 8060 03 08 FC 88 03 08 00 94 ........ ¦ D0 D0
¦ VDP Low Byte
6C ¦
¦ 8068 04 F8 00 5C 02 F8 00 60 ...\...` ¦ D1 00
¦ VDP Lookahead
20 ¦
¦ 8070 03 08 FF 90 03 F8 F8 D0 ........ ¦ D2 09
¦ VDP Address
3974 ¦
¦ 8078 02 FB FB D4 03 08 00 CC ........ ¦ D3 00
¦ VDP Access
Write ¦
¦
¦ D4 61
¦ VDP Status Byte
04 ¦
+- BrMSX settings -------------------------------------------------------------¦
¦ Resolution: 320x200 Frame skipping: 0001 Bar Graph: OFF Emulation: NORMAL ¦
¦ Joy: - Sound: ON
VSync: OFF Session: SINGLE COM: 1 Video Cache: ON
¦
¦ Command:
SCC: OFF
¦
+------------------------------------------------------------------------------+
5- Conclusões
O jogo apresentado, não utiliza nenhum acesso à ROM na página 1. Logo, o problema
de troca de slots não existe aqui. Este problema nem mesmo existe no carregamento do
bloco, pois a cópia se dá da página 2 para a 1. Se estivéssemos na página 1 e trocássemos o
slot, aí sim o programa perderia o controle.
Caso desejássemos usar a página 0 para nosso programa, poderíamos chamar a BIOS da
página 3, por exemplo. Isto é, desviaríamos nosso programa para um endereço maior que
C000H e de lá chamaríamos a BIOS, antes comutando a página 0. O retorno seria para a
página 3 (que não foi comutada) e voltaríamos a página 0 para RAM. Em seguida, um RET
retornaria para a posição da página 0, corretamente setada para RAM.
Este truque pode ser feito em qualquer página diferente da 0. A sugestão da página 3 se
deve ao fato de que, provavelmente, seu programa já terminou muito antes e que seja usada
uma área livre de memória.
6- Créditos
Este artigo foi escrito por Marcelo Teixeira Silveira, engenheiro de sistemas e
computação formado pela UERJ, Universidade do Estado do Rio de Janeiro, Brasil.
http://www.marmsx.cjb.net
Download

Gerência de Memória no MSX (I)