-
Notifications
You must be signed in to change notification settings - Fork 0
/
sorteio2600.asm
402 lines (368 loc) · 16.7 KB
/
sorteio2600.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
;
; sorteio.asm
;
; Programa que sorteia um número de três dígitos, feito para o
; evento Dev In Vale
;
; O código é livre (vide final do arquivo). Para compilar use o DASM
; (http://dasm-dillon.sourceforge.net/), através do comando:
;
; dasm sorteio2600.asm -osorteio2600.bin -f3
;
PROCESSOR 6502
INCLUDE "vcs.h"
; Constantes
SCANLINES_POR_LINHA = 6 ; # de scanlines por linha do desenho do dígito
MODO_SELECT = %0001 ; Mostra/altera o limite superior (GAME_SELECT)
MODO_RODANDO = %0010 ; Incrementa os dígitos a cada scanline vazia da tela
MODO_PARANDO = %0100 ; Incrementa um dígito a cada n frames (FIRE)
MODO_PARADO = %1000 ; Não incrementa mais os dígitos
CHAVE_GAME_SELECT = %10
CHAVE_GAME_RESET = %01
CHAVES_GAME_SELECT_RESET = %11
MAX_FRAMES_POR_INCREMENTO = 25
COR_SELECT = $6F
COR_DEFAULT = $FF
; RAM (variáveis)
digito0 = $80 ; Centena (0 a 9) exibida na tela usando PF0+PF1
digito1 = $81 ; Dezena (0 a 9) exibida na tela usando PF1+PF2
digito2 = $82 ; Unidade (0 a 9) exibida na tela usando PF2
indiceIMGD0 = $83 ; Índice dos dígitos 0, 1 e 2 nas tabelas de
indiceIMGD1 = $84 ; imagens para a scanline atual
indiceIMGD2 = $85
contadorAlturaLinha = $86 ; Contador de altura (em scanlines) da linha atual
modoAtual = $87 ; Indica se estamos em MODO_SELECT (escolhendo o limite
; máximo do sorteio), MODO_RODANDO (animando em velocidade
; total), MODO_PARANDO (reduzindo até parar) ou MODO_PARADO
limiteDigito0 = $88 ; Valor máximo para a centena do número sorteado
valorSelectReset = $89 ; Guarda status das chaves select/reset no frame anterior
; (armazenando os bits correspondentes a eles do SWCHB)
framesPorIncremento = $8A ; Quantidade de frames que vamos aguardar antes de um incremento
; (apenas no MODO_PARANDO)
contadorFrames = $8B ; Contador regressivo de frames (framesPorIncremento a 0) para
; o MODO_PARANDO
flagAtualizaDigito = $8C ; No MODO_PARANDO, informa ao kernel que o dígito deve ser
; incrementado (uma única vez).
flagResetDigitos = $8D ; No MODO_RESET, informa que
; os digitos devem ser inicializados com as sementes.
contadorSom = $8E ; Conta # de frames em que o som deve se manter ligado
sementeRandom = $8F ; Semente usada para inicializar a dezena/unidade e garantir a
; aleatoriedade. É necessário porque incrementamos a cada
; scanline e paramos a contagem sempre no início da tela -
; se começarmos sempre no final 00, vamos ter um subconjunto
; fixo de possíveis resultados
; ROM
ORG $F000 ; Início do cartucho (vide Mapa de Memória do Atari)
;;;;; Inicialização do cartucho (roda só uma vez) ;;;;;
InicializaRAM:
ldx #0 ; Começa limpando toda a RAM e registros do TIA
lda #0
LoopZeraVariaveisERegistros:
sta 0,x
inx
bne LoopZeraVariaveisERegistros
VariaveisNaoZero:
lda #MODO_SELECT ; Ao ligar o Atari, vamos começar no modo select...
sta modoAtual
lda #1 ; ...e com o limite em 100 (i.e., sorteando de 000 a 099)
sta limiteDigito0
sta digito0
InicializaSom:
lda #10 ; Som mais "percussivo"
sta AUDC0
lda #40 ; Pitch escolhido bem aleatoriamente, confesso
sta AUDF0
lda #0 ; Vamos variar o volume para fazer o som, começa desligado
sta AUDV0
InicializaGraficos:
sta WSYNC ; Vamos usar um missle para cobrir um pedacinho do playfield
ldy #8 ; que aparece por conta da imprecisão do Score Mode, e pra
LoopPosicaoMissile: ; isso é preciso contar os ciclos para posicionar
dey
bne LoopPosicaoMissile
sta RESM0 ; Posiciona o missile *quase* no lugar certo
lda #%00110000 ; Stretch do missile
sta NUSIZ0
lda #%11000000 ; Deslocamento para corrigir o "quase" acima
sta WSYNC
sta HMM0
sta HMOVE ; Executa o deslocamento
sta WSYNC
lda #%00000010
sta CTRLPF ; Score mode=1; PF priority=0; Reflect=0;
sta ENAM0 ; Habilita missile
;;;;; VSYNC ;;;;;
InicioFrame:
lda #%00000010 ; VSYNC inicia setando o bit 1 deste endereço
sta VSYNC
REPEAT 3 ; ...e dura 3 scanlines (WSYNC=fim da scanline)
sta WSYNC
REPEND
lda #0
sta VSYNC ; VSYNC finaliza limpando o bit 1
;;;;; VBLANK ;;;;;
ProcessaChavesSelectEReset: ; Primeira linha do VBLANK
lda SWCHB ; Carrega status das chaves GAME SELECT/GAME RESET no A,
and #CHAVES_GAME_SELECT_RESET ; zerando bits que não sejam estes
eor #CHAVES_GAME_SELECT_RESET ; Inverte status (para ficar 1=pressionado, 0=solto)
tax ; Copia pro X para guardar o valor no final
cmp valorSelectReset
beq FimProcessaChaves ; Nenhuma chave pressionada/solta, vai pra próxima
DefineChavePressionada:
cmp #CHAVE_GAME_RESET
beq ResetPressionado
cmp #CHAVE_GAME_SELECT
bne FimProcessaChaves
SelectPressionado:
lda #MODO_SELECT ; GAME SELECT pressionada - se já estivermos em modo select,
cmp modoAtual ; incrementa o limite, senão só muda para este modo
bne SetaModoSelect
inc limiteDigito0 ; Incrementa o limite do dígito da centena, que é o valor que
ldy #11 ; ele não pode atingir. Se passar de 10 (limite para
cpy limiteDigito0 ; centena = 9), volta ele para 1
bne FimProcessaChaves
sta limiteDigito0 ; "lda #1" suprimido, aproveitando que MODO_SELECT=1
SetaModoSelect:
sta modoAtual
jmp FimProcessaChaves
ResetPressionado:
lda #MODO_RODANDO ; Começa a rodar os dígitos
sta modoAtual
sta flagResetDigitos ; ...mas garante que eles serão ressetados antes com a semente
FimProcessaChaves:
stx valorSelectReset ; Guarda o status das chaves pro próximo frame
sta WSYNC
ProcessaBotaoJoystick: ; Segunda linha do VBLANK
lda modoAtual ; Se estamos no MODO_RODANDO...
cmp #MODO_RODANDO
bne FimProcessaBotaoJoystick
lda INPT4 ; ...e o botão do joystick foi presionado...
bmi FimProcessaBotaoJoystick
lda #MODO_PARANDO ; ...muda para o modo parando, começando por 2 frames
sta modoAtual ; por incremento
lda #2
sta framesPorIncremento
sta contadorFrames
FimProcessaBotaoJoystick:
sta WSYNC
ModoParando: ; Terceira linha do VBLANK
lda modoAtual
cmp #MODO_PARANDO
bne FimModoParando
dec contadorFrames ; Marca que um frame dos que faltavam passou
bne FimModoParando ; Se passamos os frames que faltavam, aumenta a quantidade
inc framesPorIncremento ; de frames por incremento
lda framesPorIncremento
cmp #MAX_FRAMES_POR_INCREMENTO
bne ResetContadorFrames
MudaParaModoParado:
lda #MODO_PARADO ; Se chegamos no limite de lentidão, muda para o modo parado
sta modoAtual
jmp FimModoParando
ResetContadorFrames:
lda framesPorIncremento ; Garante que iremos aguardar tantos frames quanto indicado
sta contadorFrames ; pelo valor atual de framesPorIncremento antes de incrementar
sta flagAtualizaDigito ; Executa um incremento no frame atual
FimModoParando:
sta WSYNC
MiscStuff: ; Quarta linha do VBlank
AjustaCor: ; O modo select tem uma cor distinta, o rodando/parando
lda modoAtual ; têm outra, e o parado usa a semente como cor (fazendo
cmp #MODO_SELECT ; com que o placar mude de cor a cada frame, o que é
bne CorNaoESelect ; um bom indicador de que é o resultado final).
ldy #COR_SELECT
jmp CorDefinida
CorNaoESelect:
cmp #MODO_PARADO
bne CorNaoEParado
ldy sementeRandom
jmp CorDefinida
CorNaoEParado:
ldy #COR_DEFAULT
CorDefinida:
sty COLUP1
IncrementaSemente:
sed ; Ao invés de dar um INC, vamos mudar pro modo decimal (BCD)
lda sementeRandom ; e somar 1. Assim, sempre temos um dígito válido em cada
clc ; nibble da semente
adc #1
sta sementeRandom
cld ; Volta ao modo de aritmética normal, senão o caos impera
DesligaSom:
lda contadorSom ; O som é desligado sempre que o contador zera, tornando o
beq FimMiscStuff ; que seria um tom contínuo em um "tic" para cada frame
dec contadorSom ; em que aconteça pelo menos um incremento nos dígitos.
lda #0
sta AUDV0
FimMiscStuff:
sta WSYNC
AjustaDigitos: ; Quinta linha do VBLANK
lda modoAtual
cmp #MODO_SELECT
beq AjustaDigitosSelect
cmp #MODO_RODANDO
bne FimAjustaDigitos
AjustaDigitosAposReset:
lda flagResetDigitos ; Se acabamos de dar um RESET, a flag estará ligado, e vamos
beq FimAjustaDigitos ; colocar cada nibble de uma semente (BCD) no dígito apropriado
lda sementeRandom ; digito1 = nibble da direita da semente
and #%00001111
sta digito1
lda sementeRandom ; digito2 = nibble da esquerda da semente
lsr
lsr
lsr
lsr
sta digito2
lda #0
sta digito0 ; digito0 = 0 (é o único valor seguro, por conta do limite)
sta flagResetDigitos ; Reset da flag
jmp FimAjustaDigitos
AjustaDigitosSelect:
lda limiteDigito0 ; Os dígitos do MODO_SELECT informam o limite superior, i.e.:
sta digito0 ; a centena (digito0) tem que ser um a menos que o limite
dec digito0
lda #9 ; e a dezena/unidade têm que ser "9". Ex.: para o limite
sta digito1 ; 200 vamos exibir 199.
sta digito2
FimAjustaDigitos:
sta WSYNC
PreparaIndicesEContadores: ; Sexta linha do VBLANK
lda digito0 ; Posicao na tabela é 8 vezes o valor do dígito
asl ; 3 shifts = 8 vezes
asl
asl
sta indiceIMGD0
lda digito1 ; Mesma coisa para os dígitos 1 e 2
asl
asl
asl
sta indiceIMGD1
lda digito2
asl
asl
asl
sta indiceIMGD2
lda #SCANLINES_POR_LINHA ; Inicaliza contador de altura da linha
sta contadorAlturaLinha
ldx #0 ; X é o nosso contador de scanlines (0-191)
sta WSYNC
FinalizaVBLANK:
REPEAT 31 ; VBLANK tem 37 linhas, mas usamos 6 acima
sta WSYNC
REPEND
lda #0 ; Finaliza o VBLANK, "ligando o canhão"
sta VBLANK
;;;;; DISPLAY KERNEL ;;;;;
Scanline:
cpx #[SCANLINES_POR_LINHA*8] ; Se estamos na parte superior desenha os digitos, caso
bcs DecideIncremento ; contrário o modo atual decide se vai incrementar
DesenhaDigitos:
ldy indiceIMGD0 ; Parte do digito0 vai no PF0
lda (IMGD0PF0,y)
sta PF0
lda (IMGD0PF1,y) ; O resto do digito0 vai no PF1, junto com parte do digito1...
ldy indiceIMGD1
ora (IMGD1PF1,y)
sta PF1
lda (IMGD1PF2,y) ; ...e o resto do digito1 vai no PF2, junto o digito2
ldy indiceIMGD2
ora (IMGD2PF2,y)
sta PF2
dec contadorAlturaLinha ; Quando ultrapassarmos SCANLINES_POR_LINHA de altura
bne FimScanline ; incrementamos os índices de linha e zeramos
lda #SCANLINES_POR_LINHA ; o contador de altura
sta contadorAlturaLinha
inc indiceIMGD0
inc indiceIMGD1
inc indiceIMGD2
jmp FimScanline
DecideIncremento:
lda modoAtual
cmp #MODO_RODANDO ; No MODO_RODANDO incrementamos o dígito para cada scanline...
bne NaoEModoRodando
cpx #191 ; ...exceto na última (porque o incremento pode precisar de duas)
bcc IncrementaDigitos
NaoEModoRodando:
cmp #MODO_PARANDO ; No MODO_SELECT e MODO_PARADO nunca incrementamos o dígito
bne FimScanline
lda flagAtualizaDigito ; No MODO_PARANDO incrementamos o dígito apenas quando a
beq FimScanline ; flag indicar que isso deve ser feito (com valor não-zero)
IncrementaDigitos:
SomIncremento:
lda #8 ; Basta aumentar o volume (o início do frame zera ele novamente
sta AUDV0 ; quando o contador zerar)
lda #200
sta contadorSom
LogicaIncremento:
lda #10 ; Dígitos "estouram" quando chegam a 10
ldy #0
sty flagAtualizaDigito ; Se incrementou porque a flag foi acionada, desliga
inc digito2 ; Incrementa unidade
cmp digito2
bne FimScanline
sty digito2 ; Estourou unidade, incrementa dezena
inc digito1
cmp digito1
bne FimScanline
sta WSYNC ; A essa altura já queimamos quase uma scanline,
inx ; vamos usar a próxima, incrementando o contador
sty digito1 ; Estourou dezena, incrementa centena
inc digito0
lda limiteDigito0 ; (o estouro da centena não é no 10, e sim no limte)
cmp digito0
bne FimScanline
sty digito0 ; Estourou centena, zera todo mundo
sty digito1
sty digito2
FimScanline:
sta WSYNC ; Aguarda o final do scanline
inx ; Incrementa o contador e repete até completar a tela
cpx #192
bcs Overscan
jmp Scanline
;;;;; OVERSCAN ;;;;;
Overscan:
lda #%01000010 ; "Desliga o canhão:"
sta VBLANK
REPEAT 30 ; 30 scanlines de overscan...
sta WSYNC
REPEND
jmp InicioFrame ; ...e começamos tudo de novo!
; Tabelas de bits que devem ser setados para desenhar cada um dos dígitos
; em cada registro do playfield (construídas pelo gera_tabelas.py) a partir
; da fonte do ZX Spectrum
#include "tabelas.asm"
; Configurações no finalzinho do cartucho:
ORG $FFFA
.WORD InicializaRAM ; NMI
.WORD InicializaRAM ; RESET
.WORD InicializaRAM ; IRQ
END
;
; Copyright 2011 Carlos Duarte do Nascimento (Chester). All rights reserved.
;
; Redistribution and use in source and binary forms, with or without modification, are
; permitted provided that the following conditions are met:
;
; 1. Redistributions of source code must retain the above copyright notice, this list of
; conditions and the following disclaimer.
;
; 2. Redistributions in binary form must reproduce the above copyright notice, this list
; of conditions and the following disclaimer in the documentation and/or other materials
; provided with the distribution.
;
; THIS SOFTWARE IS PROVIDED BY CHESTER ''AS IS'' AND ANY EXPRESS OR IMPLIED
; WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
; FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
; ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
; ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;
; The views and conclusions contained in the software and documentation are those of the
; authors and should not be interpreted as representing official policies, either expressed
; or implied, of Chester.
;