aboutsummaryrefslogtreecommitdiff
path: root/src/example/vcs.asm
blob: 379722c188ba52077ea12820de3bf26333196d11 (plain)
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
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
;---------------------------------------------------------------------------

;   * Subject: [stella] 2600 Digital Clock (source code)
;   * From: crackers@hwcn.org
;   * Date: Sun, 5 Oct 1997 14:31:50 -0400 (EDT)

;---------------------------------------------------------------------------

; Here's the source code for that final digital clock programme.
; Feel free to employ and distribute this code however you may wish.
; Both the source code and the binary are public domain.

; -----------------------------------------------------------------------------
        processor 6502

	option	output-file,vcs.bin

	option	+list
	option	+list-hex
	option	+list-pc

VSYNC   equ       $00
VBLANK  equ       $01
WSYNC   equ       $02
NUSIZ0  equ       $04
NUSIZ1  equ       $05
COLUPF  equ       $08
COLUBK  equ       $09
PF0     equ       $0D
PF1     equ       $0E
PF2     equ       $0F
SWCHA   equ       $280
INTIM   equ       $284
TIM64T  equ       $296
CTRLPF  equ       $0A
COLUP0  equ       $06
COLUP1  equ       $07
GP0     equ       $1B
GP1     equ       $1C
HMOVE   equ       $2a
RESP0   equ       $10
RESP1   equ       $11

;RAM

TEMP    equ       $80     ;2 bytes for temporary data
SECS    equ       $82     ;seconds counter
MINS    equ       $83     ;minutes counter
HOURS   equ       $84     ;hours counter
JOYDEL  equ       $85     ;joystick delay variable
JOY1ST  equ       $86     ;joystick first move variable
SPRITEA equ       $87     ;8 bytes for the first sprite
SPRITEB equ       $8F     ;8 bytes for the second sprite
RMINS   equ       $97     ;real minutes
RHOURS  equ       $98     ;real hours
FRAMES  equ       $99     ;frames counter

        org  $F000

start   SEI
        CLD
        LDX  #$FF
        TXS
        LDA  #$00

zero    STA  $00,X      ;looks familiar, right?
        DEX             ;typical zeroing routine
        BNE  zero

        lda  #$01       ;now we set up all our variables
        sta  CTRLPF
        lda  #$0C       ;set our starting time at 12:00
        sta  HOURS      ;just like a VCR, eh? Except it doesn't blink
        lda  #$3C       ;00 minutes
        sta  MINS
        lda  #$ca       ;nice pretty green for our sprites
        sta  COLUP0
        sta  COLUP1
        lda  #$07       ;make them good and fat
        sta  NUSIZ0
        sta  NUSIZ1
        lda  #$3C       ;initialize the frame and seconds counters
        sta  FRAMES
        sta  SECS

main    JSR  vertb      ;main loop
        JSR  time
        JSR  draw
        JSR  clear
        JMP  main

vertb   LDX  #$00       ;vertical blank, We all know what this is about
        LDA  #$02
        STA  WSYNC
        STA  WSYNC
        STA  WSYNC
        STA  VSYNC
        STA  WSYNC
        STA  WSYNC
        LDA  #$2C
        STA  TIM64T
        LDA  #$00
        STA  WSYNC
        STA  VSYNC
        RTS

time    ldy  #06        ;just load Y ahead of time for #of sprite lines
        lda  #$3C       ;60
        sec
        sbc  MINS       ;subtract the clock minutes from 60 to get the
        sta  RMINS      ;real minutes since clock counts down
        cmp  #$00       ;see if it's 00 minutes
        beq  min0
        cmp  #$32       ;see if it's more than 50 minutes
        bpl  min5
        cmp  #$28       ;see if it's more than 40 minutes
        bpl  min4
        cmp  #$1E       ;see if it's more than 30 minutes
        bpl  min3
        cmp  #$14       ;see if it's more than 20 minutes
        bpl  min2
        cmp  #$0A       ;see if it's more than 10 minutes
        bpl  min1

min0    lda  zeros,y    ;minutes must be less than 10 so load 00 sprite
        and  #$F0       ;strip the first 4 bits
        sta  SPRITEA,y  ;store it to sprite A memory
        dey
        bpl  min0       ;get next sprite line
        lda  #$00       ;less than 10 minutes
        jmp  minload    ;go to where we load the first 4 bits of sprite

min5    lda  fives,y    ;minutes must be 50+ so load 55 sprite
        and  #$F0       ;strip 1st four bits
        sta  SPRITEA,y  ;store it to sprite A memory
        dey
        bpl  min5       ;get next sprite line
        lda  #$32       ;50+ minutes - you'll need this number later to
        jmp  minload    ;load the second half the sprite data

min4    lda  fours,y    ;minutes must be 40+
        and  #$F0
        sta  SPRITEA,y
        dey
        bpl  min4
        lda  #$28       ;40+ minutes
        jmp  minload

min3    lda  threes,y   ;minutes must be 30+
        and  #$F0
        sta  SPRITEA,y
        dey
        bpl  min3
        lda  #$1E       ;30+ minutes
        jmp  minload

min2    lda  twos,y     ;minutes must be 20+
        and  #$F0
        sta  SPRITEA,y
        dey
        bpl  min2
        lda  #$14
        jmp  minload    ;20+ minutes

min1    lda  ones,y     ;minutes must be 10+
        and  #$F0
        sta  SPRITEA,y
        dey
        bpl  min1
        lda  #$0A       ;10+ minutes

minload STA  TEMP       ;the accumulator had the 10s of minutes
        LDA  RMINS      ;now we subtract the 10s minutes from the real
        sec             ;minutes to get the 1s minutes to act as a pointer
        SBC  TEMP       ;for the data tables for 2nd half of sprite
        ASL             ;double the number
        TAX
        LDA  numblk,x   ;load the first half of the sprite data address
        sta  TEMP
        lda  numblk+1,x ;load the second half of the sprite table address
        sta  TEMP+1

        ldy  #$06       ;number of lines in the sprite (-1)
msload  lda  (TEMP),y   ;get the sprite data
        and  #$0F       ;strip off the last 4 bits
        ora  SPRITEA,y  ;combine the 1st half with the 2nd half
        sta  SPRITEA,y  ;put it back in the sprite memory
        dey
        bpl  msload     ;get the next line of data

        ldy  #$06       ;preload number of sprite lines (-1)
        lda  #$18       ;24 hours
        sec
        SBC  HOURS      ;subtract the counter hours to get
        STA  RHOURS     ;the real hours value
        cmp  #$00       ;see if it's 12:00 am
        beq  hour0
        cmp  #$14       ;see if it's 20+ hours
        bpl  hour2
        cmp  #$0A       ;see if it's 10+ hours
        bpl  hour1

hour0   lda  zeros,y    ;load the zeros sprite data
        and   #$F0      ;strip the 1st four bits
        sta  SPRITEB,y  ;store to the 2nd sprite memory
        dey
        bpl  hour0
        lda  #$00       ;same deal as with the minutes
        jmp  loadhrs    ;but now we load the second half of the hours data

hour1   lda  ones,y
        and  #$F0
        sta  SPRITEB,y
        dey
        bpl  hour1
        lda  #$0A
        jmp  loadhrs

hour2   lda  twos,y
        and  #$F0
        sta  SPRITEB,y
        dey
        bpl  hour2
        lda  #$14
        jmp  loadhrs

loadhrs STA  TEMP
        LDA  RHOURS
        sec
        SBC  TEMP
        asl
        tax
        lda  numblk,x
        sta  TEMP
        lda  numblk+1,x
        sta  TEMP+1

        ldy  #$06
hsload  lda  (TEMP),y
        and  #$0F
        ora  SPRITEB,y
        sta  SPRITEB,y
        dey
        bpl  hsload
        rts

numblk  .word  zeros    ;where all the sprites are at
        .word  ones
        .word  twos
        .word  threes
        .word  fours
        .word  fives
        .word  sixes
        .word  sevens
        .word  eights
        .word  nines

draw    LDA  INTIM      ;check to see if it's time to draw a frame
        BNE  draw
        sta  WSYNC
        sta  HMOVE
        sta  VBLANK     ;turn the screen on!

;insert  display kernal

        ldx  #$3F       ;okay, this display kernal sucks, but I'm not doing
blow1   sta  WSYNC      ;much here so I didn't go for anything fancy since
        dex             ;this is just a demo. This wouldn't be the way you
        bpl  blow1      ;do things in a game, but it works for this.
        sta  WSYNC
        nop             ;See... you'd never do something weenie like this
        nop             ;in a real programme
        nop             ;
        nop             ;
        nop             ;but when I was experimenting with this programme
        nop             ;I just had a whole bunch of ";nop" lines here
        nop             ;and I removed the ";" until I got the spacing more
        nop             ;or less where I wanted it
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        sta  RESP0
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        sta  RESP1

        ldy  #$06
sload   lda  SPRITEB,y
        sta  GP0
        lda  SPRITEA,y
        sta  GP1
        sta  WSYNC      ;you wouldn't do something weenie like this
        sta  WSYNC      ;either in a real programme, but it was an
        sta  WSYNC      ;easy way to make each sprite 8 lines high
        sta  WSYNC      ;and I was more concerned with making a working
        sta  WSYNC      ;and accurate clock than a nice display.
        sta  WSYNC
        sta  WSYNC
        sta  WSYNC
        dey
        bpl  sload
        lda  #$00
        sta  GP0
        sta  GP1

        ldx  #$48
blow2   sta  WSYNC      ;now we just blow the rest of the unused scanlines.
        dex
        bpl  blow2
        rts


clear   LDA  #$24       ;set timer for overscan
        STA  TIM64T
        LDA  #$02       ;clear the screen and turn off the video
        STA  WSYNC
        STA  VBLANK
        LDA  #$00
        STA  PF0
        STA  PF1
        STA  PF2
        sta  COLUPF
        sta  COLUBK

        LDA  #$3C       ;this is the clock routine itself. it counts
        DEC  FRAMES     ;down from 60 frames, and then decreases the
        bne  joy        ;seconds, which count down the minutes and then
        lda  #$3C       ;the hours.. etc. For whatever reason my 2600
        STA  FRAMES     ;wasn't running at exactly 60 frames a second
        DEC  SECS       ;so there were two lines inserted to correct
        bne  joy        ;timing accuracy problems
        STA  SECS
        DEC  SECS       ;here's one. Kept me from losing a second every
        DEC  MINS       ;minute
        bne  joy
        STA  MINS
        LDA  #$18
        INC  SECS       ;here's the other. It kept me from gaining a
        DEC  HOURS      ;second every hour.
        bne  joy
        STA  HOURS
                        ;now my timing inaccuracies may have been caused
                        ;by either my V-blank, V-sync, Overscan, or
                        ;display being a few scanlines too long or short.
                        ;theoretically if all my lines were bang on,
                        ;I wouldn't have needed those two seconds counter
                        ;corrections. But with them inplace, it allows me
                        ;to be a little looser with my code which works for
                        ;me. It may still gain or lose a second every 60
                        ;hours, but I can live with that. And since I'll
                        ;be employing this clock in a virtual pet game and
                        ;not a swiss made olympic time piece, a little
                        ;inaccuracy won't matter.

joy     lda  SWCHA      ;load joysticks
        ora  #$0f       ;strip the data for player #2 joystick
        cmp  #$ef       ;up
        beq  up
        cmp  #$df       ;down
        beq  down
        cmp  #$bf       ;left
        beq  left
        cmp  #$7f       ;right
        beq  right
        lda  #$00       ;no movement
        sta  JOYDEL     ;reset the joystick delay variable
        lda  #$01       ;reset the first move variable
        sta  JOY1ST
        jmp  oscan      ;finish off the overscan

up      lda  HOURS      ;check to see if we've run out our hours
        cmp  #$01
        beq  oscan      ;yep, then ignore the movement
        inc  JOYDEL     ;increase the joystick delay variable
        lda  JOY1ST     ;check to see if this is the first move in this
        cmp  #$01       ;direction.
        beq  now1       ;if it is then change the variable now
        lda  #$1E       ;nope then see if there's been enough of a delay
        cmp  JOYDEL     ;to change the variable yet.
        bne  oscan
now1    lda  #$00       ;reset the joystick delay and set the first move
        sta  JOY1ST     ;indicator to "no"
        sta  JOYDEL
        dec  HOURS      ;decrease the hours counter
        jmp  oscan

down    lda  HOURS
        cmp  #$18
        beq  oscan
        inc  JOYDEL
        lda  JOY1ST
        cmp  #$01
        beq  now2
        lda  JOYDEL
        cmp  #$1E
        bne  oscan
now2    lda  #$00
        sta  JOY1ST
        sta  JOYDEL
        inc  HOURS      ;increase the hours counter
        jmp  oscan

left    lda  MINS
        cmp  #$01
        beq  oscan
        inc  JOYDEL
        lda  JOY1ST
        cmp  #$01
        beq  now3
        lda  #$1E
        cmp  JOYDEL
        bne  oscan
now3    lda  #$00
        sta  JOY1ST
        sta  JOYDEL
        dec  MINS       ;decrease the minutes counter
        jmp  oscan

right   lda  MINS
        cmp  #$3c
        beq  oscan
        inc  JOYDEL
        lda  JOY1ST
        cmp  #$01
        beq  now4
        lda  #$1E
        cmp  JOYDEL
        bne  oscan
now4    lda  #$00
        sta  JOY1ST
        sta  JOYDEL
        inc  MINS       ;increase the minutes counter

oscan   lda  INTIM      ;see if the timer has run out
        BNE  oscan
        STA  WSYNC
        RTS

zeros   .byte 11100111b ;sprites are stored upsidedown, and there
        .byte 10100101b ;are two copies of each number in each sprite
        .byte 10100101b ;location. The unwanted number is stripped
        .byte 10100101b ;with the AND command (AND #$0F for the right
        .byte 10100101b ;number stripped, AND #F0 for the left)
        .byte 10100101b ;then any two numbers can be combined with an
        .byte 11100111b ;OR command. Neat huh?

ones    .byte 11100111b
        .byte 01000010b
        .byte 01000010b
        .byte 01000010b
        .byte 01000010b
        .byte 11000110b
        .byte 01000010b

twos    .byte 11100111b
        .byte 10000100b
        .byte 10000100b
        .byte 11100111b
        .byte 00100001b
        .byte 00100001b
        .byte 11100111b

threes  .byte 11100111b
        .byte 00100001b
        .byte 00100001b
        .byte 11100111b
        .byte 00100001b
        .byte 00100001b
        .byte 11100111b

fours   .byte 00100001b
        .byte 00100001b
        .byte 00100001b
        .byte 11100111b
        .byte 10100101b
        .byte 10100101b
        .byte 10000100b

fives   .byte 11100111b
        .byte 00100001b
        .byte 00100001b
        .byte 11100111b
        .byte 10000100b
        .byte 10000100b
        .byte 11100111b

sixes   .byte 11100111b
        .byte 10100101b
        .byte 10100101b
        .byte 11100111b
        .byte 10000100b
        .byte 10000100b
        .byte 11000110b

sevens  .byte 10000100b
        .byte 10000100b
        .byte 10000100b
        .byte 01000010b
        .byte 00100001b
        .byte 00100001b
        .byte 11100111b

eights  .byte 11100111b ;This code is (c)1997 by Chris "Crackers" Cracknell
        .byte 10100101b ;and is placed in the Public Domain by the author.
        .byte 10100101b ;Anyone is free to employ and distribute this code
        .byte 11100111b ;as they see fit.
        .byte 10100101b ;
        .byte 10100101b ;
        .byte 11100111b ;
                        ;
nines   .byte 00100001b ;Well... if you're going to use this code in a
        .byte 00100001b ;"Doomsday Machine" to destroy the world, then
        .byte 00100001b ;I would rather you didn't. But otherwise, knock
        .byte 11100111b ;yourself out with it.
        .byte 10100101b ;
        .byte 10100101b ;Actually... if the "Doomsday Machine" is just in
        .byte 11100111b ;a game, then it's okay to use the code.
                        ;
        org $FFFC       ;Unless it's like the movie "War Games" where the
        .word start     ;computer running the game is hooked up to a real
        .word start     ;"Doomsday Machine" then it wouldn't be a good idea.