;--------------------------------------------------------------------------- ; * 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 VSYNC = $00 VBLANK = $01 WSYNC = $02 NUSIZ0 = $04 NUSIZ1 = $05 COLUPF = $08 COLUBK = $09 PF0 = $0D PF1 = $0E PF2 = $0F SWCHA = $280 INTIM = $284 TIM64T = $296 CTRLPF = $0A COLUP0 = $06 COLUP1 = $07 GP0 = $1B GP1 = $1C HMOVE = $2a RESP0 = $10 RESP1 = $11 ;RAM TEMP = $80 ;2 bytes for temporary data SECS = $82 ;seconds counter MINS = $83 ;minutes counter HOURS = $84 ;hours counter JOYDEL = $85 ;joystick delay variable JOY1ST = $86 ;joystick first move variable SPRITEA = $87 ;8 bytes for the first sprite SPRITEB = $8F ;8 bytes for the second sprite RMINS = $97 ;real minutes RHOURS = $98 ;real hours FRAMES = $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 %11100111 ;sprites are stored upsidedown, and there .byte %10100101 ;are two copies of each number in each sprite .byte %10100101 ;location. The unwanted number is stripped .byte %10100101 ;with the AND command (AND #$0F for the right .byte %10100101 ;number stripped, AND #F0 for the left) .byte %10100101 ;then any two numbers can be combined with an .byte %11100111 ;OR command. Neat huh? ones .byte %11100111 .byte %01000010 .byte %01000010 .byte %01000010 .byte %01000010 .byte %11000110 .byte %01000010 twos .byte %11100111 .byte %10000100 .byte %10000100 .byte %11100111 .byte %00100001 .byte %00100001 .byte %11100111 threes .byte %11100111 .byte %00100001 .byte %00100001 .byte %11100111 .byte %00100001 .byte %00100001 .byte %11100111 fours .byte %00100001 .byte %00100001 .byte %00100001 .byte %11100111 .byte %10100101 .byte %10100101 .byte %10000100 fives .byte %11100111 .byte %00100001 .byte %00100001 .byte %11100111 .byte %10000100 .byte %10000100 .byte %11100111 sixes .byte %11100111 .byte %10100101 .byte %10100101 .byte %11100111 .byte %10000100 .byte %10000100 .byte %11000110 sevens .byte %10000100 .byte %10000100 .byte %10000100 .byte %01000010 .byte %00100001 .byte %00100001 .byte %11100111 eights .byte %11100111 ;This code is (c)1997 by Chris "Crackers" Cracknell .byte %10100101 ;and is placed in the Public Domain by the author. .byte %10100101 ;Anyone is free to employ and distribute this code .byte %11100111 ;as they see fit. .byte %10100101 ; .byte %10100101 ; .byte %11100111 ; ; nines .byte %00100001 ;Well... if you're going to use this code in a .byte %00100001 ;"Doomsday Machine" to destroy the world, then .byte %00100001 ;I would rather you didn't. But otherwise, knock .byte %11100111 ;yourself out with it. .byte %10100101 ; .byte %10100101 ;Actually... if the "Doomsday Machine" is just in .byte %11100111 ;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.