.title Mattel Football Proto ;www.seanriddle.com/footballproto.html ;from patent wrapper for US4162792 ;prototype Mattel Football game using 6502 ;RAM at 0000 (at least 64 bytes) ;ROM at 0080-00ff, 0200-059F 0600-???? and 0C00-E2B (probably 1K at 0200, 0600 and 0C00) ;reset code at 0200, IRQ at 0227 ;also needs RAM at 0100 for stack ;two 6530 RRIOTs at 1000 and 1700 ;seven 7-segment LEDs ## ### ## ;27 LEDs for players (9x3) (possibly nine 7-segment LEDs, using only horizontal segments A G and D) ;probably a 7442 to select digit/player column 0-8 ;my hardware has two 6532 RIOTs, one 2732 EPROM and a GAL for address decoding ;see footballproto.pld for GAL programming ;128 bytes RAM at 0000, 128 bytes RAM at 0100-017f (mirrored to 0180-01ff) ;4K ROM from 0000-0FFF (accessed at 0080-00ff, 0200-09FF and 0c00-0fff) ;hand-written on printout: ;Cross-assembled on Digital Equip. Corp. PDP-10 ;To be run on MOS Technology 6502 ;George J. Klose {listed as one of the inventors in the patent} ;1/11/77 ;assembly was created 03-SEP-76 at CalTech ;originally two separate code files plus missing data ;code issues ;lots of undefined symbols in patent listing ;missing initializers, tables ;names were converted from >6 chars to 6 chars, some missed ;some typos ;some code seems incorrect (look for === in comments) ;partial conversion from MM:SS time display to TT.T ;pages out of order ;no schematics or part list ;some vestiges of the driving game remain: Y is horizontal because the display was rotated from vertical, ;there's an unused variable named "ogear", and player LEDs are accessed with "carseg" ;differences from Mattel game: ;no Kick button ;no difficulty switch ;beeps when first down achieved ;starts on 40 yard line instead of 20 ;no change in possession at half ;no game over after 4th quarter ;version 1.0 11/19/2016 SKR ;assembled with as6500 v511 http://shop-pdp.net/ashtml/asxxxx.htm ;as6500 -l -o -s footballproto.asm ;aslink -s -m -u -b zprom=0x0080 -b lowrom=0x0200 -b midrom=0x0600 -b highrom=0x0c00 -b vectors=0x0ffa footballproto.rel ;srec_cat footballproto.s19 -o footballproto.bin -binary ;my notes are in {brackets} ;;;page 1 ; ; variables ; who=2 looker=3 oldsw=4 temp=5 trialx=6 trialy=7 manx=8 ; {8 bytes - up to 8 men - value = row 1 to 3 for top to bottom} many=0x10 ; {8 bytes - up to 8 men - value = column 0 to 8 for left to right} bar=0x18 odist=0x1a ; ogear=0x1b ; {for driving game - not used for football} mcycle=0x1c whostp=0x1d ; {was wstep} tkler=0x1e state=0x1f defh=0x20 defl=0x21 blinky=0x22 note=0x23 rate=0x24 tempoh=0x25 tempol=0x26 down=0x27 togo=0x28 los=0x29 poss=0x2a sleft=0x2b sright=0x2c jump1=0x2d ; {was jumper - next byte needs to be initialized with address high byte of table, dntab, scrtab (4)} bptime=0x2f ;;;page 2 nposs=0x30 timel=0x31 timeh=0x32 timeyl=0x33 timeyh=0x34 buzzy=0x35 bztime=0x36 losfix=0x37 button=0x38 whom=0x39 shifty=0x3f ; {not defined} timedp=0x50 ; {added - display DP in time remaining} rand=0x5e ; {written in} addend=0x5f ; {written in} ; {RIOT1} swipia=0x1700 ; {Up/Down/Adv/Score/Downs/x/x/spkr} manpia=0x1702 ; {vertical field position 1, 2, 4} ; {RIOT0} digpia=0x1000 ; {horizontal field position 0-8 to 7442} segpia=0x1002 ; {A/B/C/D/E/F/G/DP} tmrreg=0x101e ; {RIOT0 timer, divide by 64, IRQ} ; {ROM @ $0080} .area zprom (REL,CON) ; {table of values and addresses to set up PIAs} ; {RIOT0 A=digpia=uuuuOOOO, B=segpia=OOOOOOOO} ; {RIOT1 A=swipia=IIIIIuuO, B=manpia=uuuuuOOO} {0=Input, 1=Output u=Unused, set to Output} iotab: .db 7 .dw swipia+1 ; {RIOT1 DDRA:buttons and speaker} .db 0xff .dw manpia+1 ; {RIOT1 DDRB: field LED row 1=top, 2=middle, 4=bottom} .db 0xff .dw digpia+1 ; {RIOT0 DDRA: file LED column 0=left, 8=right; also digits 12-345-78 for my display} .db 0xff .dw segpia+1 ; {RIOT0 DDRB: segments A/B/C/D/E/F/G/DP} .db 0 ; {RIOT1 disable A7 edge detect} .dw swipia+4 .db 0 ; {RIOT0 disable A7 edge detect} .dw digpia+4 .db 8 ; {approx 2 KHz : 6530/6532 timer 1MHz / 64 / 8 = 1953Hz} .dw tmrreg iotabn: ; {ROM @ $0200} .area lowrom (REL,CON) ; ; main ; here to cold start the program ; main: sei ; protect ourselves ldx #0xff ; {added; stack was not initialized} txs jsr setio ; set up PIA, vectors, etc jsr setvar ; set up impure data base cli jmp . ; this is the entire background program ; ; ; init hardware ; this routine uses a table of addresses and data ; to all of the mixed up memory references ; required to init a PIA ;;;;;;; ; of course it would be possible to replace this ; with a lot of LDA#...STA... code ; {this code assumes iotab in ZP} setio: ldx #>> note: this routine is highly non-reentrant ; it uses the counter 'whom' to indicate ; which man gets shown during this tick ; shomen: lda whom ; negative values used to intensify bpl notbrt ; {added} ldx #0 ; the QB who is man #0 beq shomn2 ; {added} notbrt: tax ; {added} shomn2: ; {added} ; now we have chosen who to show jsr show1 ; set up the LEDs ; now update counter for next time dec whom lda whom cmp #0xf0 ; this number sets qb intensity ; the more negative, the brighter bne shms ; {shms not in source, so guessed at location below} lda #0x0a ; 7 plus three for clock sta whom shms: ; {is this the correct place?} rts ; ; ; state shifters ; ; these routines look at the mode switch ; and alter the state variable accordingly ; ; they respect the mcyclee flag ; ss: dec shifty ;;;page 7 beq .+3 rts lda ishfty sta shifty ; jsr gtmode ; (1234) cmp #3 bpl .+5 jmp req2 ; so he wants mode 2! ; sta state lda #20 sta button lda mcycle bne .+3 rts lda #0 sta mcycle jsr setmen rts ; ; here to read and decode the mode bits ; this routine returns a number (1234) ; representing the mode the guy wants ; {downs=3, score=4, movement=2} ; gtmode: lda swipia eor #0xff lsr lsr lsr clc adc #0x01 and #0x03 clc adc #0x01 rts ; ; ; set men to their lineup position ; setmen: ldx #0x10 ; {16 bytes, so both manx and many} setl: dex lda imanx,x sta manx,x cpx #0 ;;;page 8 bne setl ; lda nposs sta poss rts ; here when no mode switches pushed ; i.e. he wants mode 2 ; ; we won't let him have mode 2 until ; the hold timer expires ; req2: lda button bne .+3 rts dec button beq .+3 rts lda #2 sta state rts ; ; ; here to set LEDs to show one man ; call with whom index in X ; show1: ; **kludge** ; the following code checks to see if X indicates ; that we should not display a man ; if so, display a clock digit instead cpx #8 bcc sh1s ; {was blt} txa sec sbc #6 ; man #8 goes to clock 2 sta who jsr shoscr sh1s: jsr farout lda poss ; {was possess} bne strate ; {was bnz} ; here if the field is not reflected lda #8 sec sbc many,x bpl sh1x ; always branch! ; ;;;page 9 strate: lda many,x sh1x: sta digpia lda manx,x bpl sh1s3 lda #0 sh1s3: tax lda carseg,x ; {carseg=[0,1,2,4]} sta manpia rts ; ; ; here to blank the whole display ; in such a way that no 'ghosts' are left ; farout: lda #0 sta segpia ; {was segport} sta manpia ; {was manport} lda #0xf sta digpia ; {was digport} rts ; ; ; here to keep the game clock up to date ; timer: ; first, we count interrupts ; so that we tick at the proper rate inc timeyl beq .+3 rts inc timeyh beq .+3 rts lda itimyh sta timeyh ; here at half second ticks ; check if the timer expired last time ; if so, cause a long buzz sound lda timel ora timeh ; if both zero, then expired ;;;page 10 beq buzz ; now check if it has been expired for a long time ; if so, do not keep counting ; let it stop at zero!!! lda timeh bpl .+3 rts ; here if sign bit was set ; here if we are going to decrement the timer lda #5 ; cause a ticking sound sta bztime lda timel clc sed adc #0x99 ; decrement decimal {was #99} sta timel cld cmp #0x99 ; necessary {was #0} bne quit ; {was bpl} ; here if the seconds part underflowed ; set it to 59 and 'borrow' out of minutes ; {orig game is not MM:SS; there are 150 ticks, shown as 15.0} ; lda #0x59 ; {not needed} ; sta timel ; {not needed} lda timeh sed clc adc #0x99 sta timeh ; quit: cld rts ; here when we first notice it has gone to zero ; make a noise and stop the clock by turning on ; the sign bit buzz: lda #0xff sta bztime lda #0x80 sta timeh ; stop timer at zero rts ; ; ; audio routines ;** ;first, the low frequency buzz ; ;;;page 11 buzzer: dec buzzy beq .+3 ; {was bne ===} rts lda ibuzzy sta buzzy ; lda bztime bne .+3 rts ; jsr audio dec bztime rts ; ; ; high frequency beep ; beeper: lda bptime bne .+3 rts jsr audio dec bptime rts ; ; ; general purpose routine to output half a cycle ; to the speaker ; audio: lda swipia eor #0x01 sta swipia rts ; ; ; calliope ; ; high class routine to play music ; ; songs are stored in RAM ; one note per byte ; 02 would be a high note, 40 a low note ; the song is terminated by a zero byte ; call with index to first byte of song in AC ; (index = address - 0600) ; ; note that this routine has to turn off the ; interrupt system (then restores it) ; it would be wise to call farout before ;;;page 12 ; playing a song of any length ; but that is the caller's responsibility ; ; calliope has a fast tempo (used for whistles) ; calliope2 has a slower tempo (used for charge) cliope: ldx #0xfe ; fast rate bne clipes cliop2: ldx #0xe0 ; slower {was f7} clipes: stx rate stx tempoh ; {added so first note is correct ===} ; php ; save interrupt mask sei ; we need the whole CPU ourselves tax jsr pipeld plp ; restore interrupt mask rts ; ; ; pipe***the guts of calliope ; ; the entry point is at pipeload ; do not jsr playit!!!! ; ; this routine is organized as two independent counters ; the first one counts how long between ; switching the audio ; the other counts how long we hold the note ; ; naturally, the frequency counter underflows many times per note ; so we have to restore it and count again ; until the tempo counter expires ; then we increment x and use the next byte of the song ; to determine the frequency ; playit: lda note bmi nope ;rest a note dec note bne nope ; here when the note overflowed ; reset counter and goose the speaker pipeld: lda song,x bne .+3 ; {was bmi - typo? ===} rts sta note jsr audio ;;;page 13 ; here if we don't want to output anything this time through ; count the tempo nope: inc tempol bne playit inc tempoh bne playit ; reset temp counter and go to next note lda rate sta tempoh inx ;go to next note of song jmp pipeld ; ; ; this routine marks the QB and tackler ; so that they will not be displayed ; ; it sets the sign bit of the manx ; blink: inc blinky beq .+3 rts lda iblnky sta blinky ; ldx tkler lda manx bmi blinkq ; here to turn off the QB and turn on the tackler ora #0x80 sta manx lda manx,x and #0x7f sta manx,x rts ; here to do the reverse blinkq: and #0x7f sta manx lda manx,x ora #0x80 sta manx,x rts ; ; ; show downs ; ; this is gigantic signpost branch ; depending on the variable 'who' ; showdn: jsr showkl ; calculate who*3 clc adc #, LOS > 50 (47') ; 2) -->, LOS < 50 ('47) ; 3) <--, LOS > 50 ('47) ; 4) -->, LOS < 50 (47') ; ; this sets losfix to be the yardage ; in the other notation and returns with x set to ff ; in cases 2 and 3 so you can do an inx and branch ; to decide which half of the field you are on fixup: lda los ldx #0 cmp #0x50 bmi nofix lda #0 sed sec sbc los cld sta losfix lda poss bne .+4 ldx #0xff rts ; nofix: sta losfix lda poss beq .+4 ldx #0xff rts ; ; ; these two routines must check for the ; special case where LOS>>Note: this means that if the player ; managed to hit switches simultaneously, all but one ;;;page 3b ; would be 'lost', because this routine grabs all bits ; but only one will get processed. ; ; ; here with a bit in the AC ; indicating which direction to go ; doplay: pha ldx #0 ; we want to move man #0 jsr try ; pla ; {added} asl bcc notup ; here if he wants to move 'up' dec trialx jmp fin ; notup: asl bcc notdn ; here to move down inc trialx jmp fin ; notdn: asl bcc rts1 jsr adv jmp fin ; ; ; common code for all QB motions ; fin: jsr nforce ; check for off sides jsr hitdef ; check for collisions bcs tkl ; here if motion is illegal ; move QB to his new place ; ;>>>>>this ldx not in manuscript!!??? ldx #0 jsr untry rts1: rts ; ; here when there is a tackle due to motion of QB ; (as opposed to motion of tackler!) tkl: txa ; whodunnit? jsr tackle rts ; ;;;page 4b ; ; here to advance QB one step ; (a subroutine of stepqb) ; adv: dec trialy bpl advs jsr offend ; wrap around to beginning ; advs: jsr hitdef ; if someone's in your way bcc yards rts ; you cannot make yards!! ; yards: lda togo beq line2 ; {was line, already used in low ROM} clc sed ; {subtract 1 in decimal mode} adc #0x99 ; {was #99} cld sta togo cmp #0 ; necessary!!!! ;>>>>decimal mode add does not set Z flag correctly bne line2 ; {was line} jsr frstdn ; line2: lda los ; {was line} clc sed adc #0x99 ; {was #99} cld sta los cmp #0 bne .+5 jsr td rts ; ; here when QB gets tackled ; AC contains who did it ; tackle: sta tkler ; {was from tackler} jsr farout ; check to see if he went ten yards or more ; if so, give him a first&ten ; lda togo beq frst10 ; no first ---- let's see which song to play ; one tweet if down 1, 2, 3 ; two tweets if 4th down, loss of possession inc down ; he got tackled, new down # ;;;page 5b lda down cmp #5 beq loss ; lda #0 is defender ; here with candidate in AC ; check how many players are actually on the field sta whostp tax lda manx,x beq getx ; non-existent man {was getsome} ; here with real man in whostp ; and in x jsr try jsr stphim ; here to see if it was a legal step ; first, check for tackle lda trialx cmp manx bne team lda trialy cmp many bne team lda whostp jsr tackle rts ; team: jsr hitdef ; did he hit a teammate? bcs rts2 ; yes, oh, well; nobody moves this time ldx whostp ; no, good, put him in a new place jsr untry rts2: rts ; ; ; here with some defender named in whostep ; this routine calls trial step until it ; finds a step that puts defender closer to the QB stphim: jsr gmunu ; the metric ;;;page 9b sta odist stepl: ldx whostp jsr try jsr trystp jsr nforce ; {moved here to fix code below} jsr gmunu cmp odist bpl stepl rts ; ; ; here to step trialx/y in a random direction ; no guarantee that it is toward QB ; trystp: jsr random asl asl and #0x0c ; {rand # is 0, 4, 8 or C} clc adc #