aboutsummaryrefslogtreecommitdiff
path: root/src/example/vcs.asm
diff options
context:
space:
mode:
Diffstat (limited to 'src/example/vcs.asm')
-rw-r--r--src/example/vcs.asm533
1 files changed, 533 insertions, 0 deletions
diff --git a/src/example/vcs.asm b/src/example/vcs.asm
new file mode 100644
index 0000000..379722c
--- /dev/null
+++ b/src/example/vcs.asm
@@ -0,0 +1,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.
+
+
+