In memory of Atari, I decided to post something I've been messing around with for some time.... I got the source code to Combat on the Web somewhere, tacked onto the end of a few bits and pieces about programming the Atari 2600, but it had minimal commenting, I didn't know what was going on, so I decided to take a closer look and here is the restult. This is far from complete, mind you. --- combat.asm --- ; Written by ??? (I've looked everywhere for the author's name) ; Disassembled by ??? (would be nice) ; Commented further by Nick Bensema. ; Last Update: 08/04/96 ; Atari Combat Game ; suspected RAM addresses ; GAMVAR EQU A3 ;Game Variation BCDVAR EQU 81 ;Game Variation in BCD DIRECTN EQU 95 ;Players and missiles' current bearing. VELOC EQU 85 ;Doubles as shape number ;and velocity of the player. ;0 = Tank (slow) ;1 = Biplane (medium) ;2 = Jet Fighter (fast) SCROFF EQU E0 ;Score pattern offsets NUMG0 EQU DE ;Storage for current byte NUMG1 EQU DF ;of score number graphics. TMPSTK EQU D3 ;Temporary storage for stack. SHOWSCR EQU 87 ;Show/hide right player score PLFLIN EQU B4 ;Current scanline on the playfield. SHAPES EQU BB ;Pointer to player sprites SCORE EQU A1 ;Player scores in BCD. TNKY0 EQU A4 ;Tank 0's Y-position TNKY1 EQU A5 ; MSSY0 EQU A6 ;Missile 0's Y-position MSSY1 EQU A7 ; TEMP EQU D2 ;"score conversion temporary" ; ; 9B-9C sound pitch storage ; DA hi-res patterns XOFFS EQU B0 ;X-offset for Hmove. LORES EQU B5 ;lo-res indirect addresses. HIRES EQU BD ;Hi-res shape data. ;Left player's shape stored in ;all even bytes, right player's ;shape stored in all odd bytes. COLOR0 EQU D6 ;Tank colors. COLOR1 EQU D7 *= $1000 SEI CLD LDX #FF TXS LDX #5D JSR J15BD ; zero out $00 thru $A2 LDA #10 STA SWCHB+1 ;Port B data direction register STA 88 ;and $88... JSR J11A3 MLOOP JSR NWSCR ; $1014 JSR J1157 JSR J1572 JSR J12DA JSR J1444 JSR J1214 JSR J12A9 JSR BCD2SCR JSR DRAW JMP MLOOP ; NWSCR INC 86 ; initial blanking and retrace start STA HMCLR ;Clear horizontal move registers. LDA #02 STA WSYNC STA VBLANK STA WSYNC STA WSYNC STA WSYNC STA SYNC STA WSYNC STA WSYNC LDA #00 STA WSYNC STA SYNC LDA #2B STA TIM64T ;set 64 clock interval. RTS ; ; Drawing the screen in the following routine. ; We start with the score, then we render the ; playfield, tanks, and missiles simultaneously. ; All in all, an average day for a VCS. ; DRAW LDA #20 STA PLFLIN ;We're assuming scanline 20. STA WSYNC STA HMOVE ;Move sprites horizontally. B105C LDA INTIM BNE B105C STA WSYNC STA CXCLR ;Clear collision latches STA VBLANK ;We even have to do our own ;vertical blanking! Oh, the ;humanity! TSX STX TMPSTK ; Save stack pointer LDA #02 STA CTRLPF ; Double, instead of reflect. LDX DC B1070 STA WSYNC ; Skip a few scanlines... DEX BNE B1070 LDA DC CMP #0E BEQ B10CD ; ; Start by drawing the score. ; LDX #05 ;Score is five bytes high. LDA #00 ;Clear number graphics. STA NUMG0 ;They won't be calculated yet, STA NUMG1 ;but first time through the loop ;the game will try to draw with ;them anyway. DRWSCR STA WSYNC ;Start with a fresh scanline. LDA NUMG0 ;Take last scanline's left score, STA PF1 ;and recycle it, ;Here, we begin drawing the next scanline's ;left score, as the electron beam moves towards ;the right score's position in this scanline. LDY SCROFF+2 LDA NUMBERS,Y ;Get left digit. AND #F0 STA NUMG0 LDY SCROFF LDA NUMBERS,Y ;Get right digit. AND #0F ORA NUMG0 STA NUMG0 ;Left score is ready to ship. LDA NUMG1 ;Take last scanline's right score, STA PF1 ;and recycle it. LDY SCROFF+3 LDA NUMBERS,Y ;Left digit... AND #F0 STA NUMG1 LDY SCROFF+1 LDA NUMBERS,Y ;right digit... AND SHOWSCR ;I think I know why we used $87 instead of #$0F. ;If the game hasn't started yet, we're displaying ;the difficulty level as the left player's score, ;and we don't have any use for the right player's ;score, so in this case, $87 would contain zero, ;rendering it invisible. ;Now, we use our fresh, new score graphics in ;this next scanline. STA WSYNC ORA NUMG1 ;Finish calculating STA NUMG1 ;right score. LDA NUMG0 STA PF1 ;Left score is in place, ;We use this time to check whether we're at ;the end of our loop. DEX BMI B10CD ;If so, we're out of here. Don't worry, ;the score will be cleared immediately, so ;nobody will know that we've gone past five ;bytes and are displaying garbage. INC SCROFF INC SCROFF+2 ; Get ready to draw the next INC SCROFF+1 ; line of the byte. INC SCROFF+3 LDA NUMG1 STA PF1 ; Right score is in place. JMP DRWSCR ;Go to next scanline, ; and that is how we do that... ; ; See what it takes just to display a pair of two-digit ; scores? If you think that's rough, figure out how ; they displayed those six-digit scores in the early ; 1980's. Also consider Stellar Track.... well, if ; you've seen Stellar Track, you know exactly what I'm ; talking about. Full-fledged twelve-column text. ; B10CD LDA #00 ; Inner Display Loop STA PF1 ; Clear the score. STA WSYNC LDA #05 STA CTRLPF ;Reflecting playfield. LDA COLOR0 STA COLUP0 LDA COLOR1 STA COLUP1 DRWFLD LDX #1E TXS ;Very Sneaky - set stack to missle registers ;The above comment was in the original ;listing. It was a big help. 2600 ;programmers often sacrificed the stack so ;that they could have a shortcut. This also ;drives the emulator authors completely nuts. SEC LDA TNKY0 SBC PLFLIN ; A=TNKY0-PLFLIN AND #FE ; bit-0-ectomy TAX ; X=A ;X is now the line of the shape being drawn, ;times two of course, remembering the format ;in which they're stored, in memory, every two ;bytes. But this is only used ;if we're actually supposed to draw the tank ;yet. Let's check now.. AND #F0 ; Within the 16-byte threshold? BEQ B10F2 ; If not, LDA #00 ; blank the tank. BEQ B10F4 ; If so, B10F2 LDA HIRES,X ; draw the tank. B10F4 STA WSYNC ;---------------END OF ONE LINE------ STA GRP0 ; Just for player 0. LDA MSSY1 EOR PLFLIN AND #FE PHP ; This turns the missle 1 on/off LDA MSSY0 EOR PLFLIN AND #FE PHP ; This turns the missle 0 on/off ; If the above test results in a zero, ; as it would if it were the missile's ; to appear, the processor's zero flag ; is set, which is the same bit in the ; P register as the TIA needs to determine ; the missile's status. So instead of ; all that branching, like with the tanks, ; we just use one instruction, and in fact, ; just one byte, and furthermore, just two ; cycles, to accomplish this feat. ;We've got the missile taken care of. ;Now let's see which line of the playfield to draw. LDA PLFLIN ; BPL B110C ;If on the bottom half of the screen, EOR #F8 ;reverse direction so we can mirror. B110C CMP #20 ; BCC B1114 LSR A LSR A LSR A ;Divide by eight, TAY ;and stow it in the Y-register. ;By now, the electron beam is already at the next ;scanline, so we don't have to do a STA WSYNC. B1114 LDA TNKY1 ;TNKY1 is other player's position. SEC SBC PLFLIN INC PLFLIN ;One up in the loop... NOP ORA #01 ;ADD bit 0 instead of removing. TAX AND #F0 ;Within the threshold? BEQ B1127 ;If not, LDA #00 ;blank the tank, BEQ B1129 ;if so, B1127 LDA HIRES,X ;draw the tank B1129 BIT 82 STA GRP1 BMI B113B ;If 82 bit set to 1, skip draw. (?) LDA (LORES),Y STA PF0 LDA (LORES+2),Y STA PF1 LDA (LORES+4),Y STA PF2 B113B INC PLFLIN ;One more up in the loop. LDA PLFLIN EOR #EC ;When we've reached the $ECth line, BNE DRWFLD ;we've had enough. LDX TMPSTK ; Restore stack pointer TXS STA ENAM0 ; Clear a bunch of registers. STA ENAM1 STA GRP0 STA GRP1 STA GRP0 ;In case GRP0 isn't COMPLETELY zeroed. STA PF0 STA PF1 STA PF2 RTS ; J1157 LDA SWCHB ;Start/Reset button.... LSR A ;Shove bit 0 into carry flag, BCS B1170 ;and if it's pushed... LDA #0F STA SHOWSCR ;Show right score. LDA #FF ;and other various STA 88 ;game-beginning things. LDA #80 STA DD ;I bet DD is for attract mode. LDX #E6 JSR J15BD ; zero out $89 thru $A2 BEQ B11D0 B1170 LDY #02 LDA DD AND 88 CMP #F0 BCC B1182 LDA 86 AND #30 BNE B1182 LDY #0E B1182 STY DC LDA 86 AND #3F BNE B1192 STA 89 INC DD BNE B1192 STA 88 B1192 LDA SWCHB ; Select button. AND #02 BEQ B119D STA 89 BNE B11F1 B119D BIT 89 BMI B11F1 INC 80 ;Go to next game. J11A3 LDX #DF B11A5 JSR J15BD LDA #FF STA 89 LDY 80 LDA VARDATA,Y ;Get data for this variation. STA GAMVAR EOR #FF BNE B11BB LDX #DD BNE B11A5 ; which will only JSR to J15BD B11BB LDA BCDVAR ;Remember we have to increment with BCD... SED CLC ADC #01 STA BCDVAR STA SCORE CLD BIT GAMVAR BPL B11D0 ;if this is a plane game, INC VELOC ;increase VELOC. BVC B11D0 ;if this is a jet game, INC VELOC ;increase VELOC further still. B11D0 JSR INITFLD LDA #32 ;In plane games, right player STA TNKY1 ;flies slightly higher. LDA #86 STA TNKY0 BIT GAMVAR BMI B11F1 STA TNKY1 ;In tank games, however, STA RESP1 ;tanks are opposite each other LDA #08 STA DIRECTN+1 ;and right player faces left. LDA #20 STA HMP0 STA HMP1 STA WSYNC STA HMOVE B11F1 RTS ; ; convert BCD scores to score pattern offset. ; This involves the horrible, horrible implications ; involved in multiplying by five. ; ; If it weren't for the geniuses at NMOS using BCD, ; this routine would be a nightmare. ; BCD2SCR LDX #01 ;convert BCD scores to score pattern offset B11F4 LDA SCORE,X AND #0F STA TEMP ASL A ASL A CLC ADC TEMP STA SCROFF,X LDA SCORE,X AND #F0 LSR A LSR A STA TEMP LSR A LSR A CLC ADC TEMP STA SCROFF+2,X DEX BPL B11F4 RTS ; ; ; J1214 BIT 83 BVC B121C ;Branch if bit 6 of 83 is clear. LDA #30 BPL B121E ;JMP. B121C LDA #20 B121E STA B1 ;Either $30 or $20 goes here... LDX #03 JSR J1254 DEX ;X, I _think_, is now 2. JSR J1254 DEX ;And I _think_ it's now 1. B122A LDA 8D,X ;That means this is 8E. AND #08 LSR A LSR A STX D1 ;This means store #1 in D1...argh! CLC ADC D1 TAY LDA 00A8,Y ;We can't use zero-page? Waaah! SEC BMI B123D CLC ;^^ That's just a fancy way to ; transfer bit 7 to Carry Bit. B123D ROL A ;ROL, the wave of the future. STA 00A8,Y BCC B1250 LDA AC,X AND #01 ASL A ASL A ASL A ASL A STA B1 ; B1 = (AC & 1) << 4 JSR J1254 B1250 DEX BEQ B122A RTS ; ; This routine will move both tanks and missiles. ; Special cases are made for missiles, which are ; otherwise treated as players 2 and 3. ; ; It doesn't change the X register, but it does ; utilize it. ; J1254 INC AC,X LDA DIRECTN,X AND #0F CLC ADC B1 TAY LDA L15F7,Y ;This has offset information. STA XOFFS ;Store the X-offset. BIT 82 BVS B127A ;Branch if bit 6 of 82 is set. LDA DIRECTN,X SEC SBC #02 AND #03 BNE B127A LDA AC,X AND #03 BNE B127A ;if AC isn't set, we're go for move. LDA #08 STA XOFFS B127A LDA XOFFS J127C STA HMP0,X ;Use this to move the tank. AND #0F SEC SBC #08 STA D4 CLC ADC TNKY0,X BIT GAMVAR BMI B1290 ;Branch if a plane game. ;What follows is probably a bounds check. CPX #02 BCS B12A0 ;Branch if moving a player B1290 CMP #DB BCS B1298 CMP #25 BCS B12A0 B1298 LDA #D9 BIT D4 BMI B12A0 LDA #28 ;#28 if D4 is positive, #D9 if not B12A0 STA TNKY0,X ;The tank/missile is moved here. CPX #02 BCS B12A8 ;Skip if moving a missile. STA VDELP0,X ;Vertical Delay Player X... B12A8 RTS ; ; This subroutine sets up the sprite data for each ; player. The graphics data at the end of the ROM ; don't account for an entire 360 degrees of movement, ; it would take less memory to just draw 180 degrees' ; worth and write code to compensate for the other half ; of the circle. ; ; Bit 3 of DIRECTN for each player determines whether ; he is past 180 degrees, and when this bit is set, ; this subroutine compensates in the following ways: ; ; 1. It sets the TIA's reflection flag for that ; player, taking care of the horizontal aspect ; rather easily. ; ; 2. Instead of starting at byte 0 and ascending to 7, ; it starts at byte 7 by EOR'ing 7 into the address, ; and it descends to 0, using the carry bit as a ; flag to determine whether to increment or decrement. ; ; The X-register starts at 0x0E plus player number ; and goes down by two each time through the loop, ; until it hits zero. This way, after calling this ; subroutine twice, every even-numbered byte contains ; the left player shape, and every odd-numbered byte ; contains the right player shape. It seems odd, no ; pun intended, but the screen-drawing routine is ; designed to handle it. ; J12A9 LDA #01 AND 86 TAX LDA DIRECTN,X STA REFP0,X ;Step 1 taken care of. AND #0F TAY ;Y = DIRECTN[X] & 0x0F. BIT 83 BPL B12BB ;If bit 7 is set, STY DIRECTN+2,X ;then set missile bearings(?) B12BB TXA ; X ^= 0x0E, EOR #0E ; TAX ; TYA ASL A ASL A ASL A CMP #3F ;And so step 2 begins... CLC BMI B12CB ;Branch if <180 deg. SEC EOR #47 ;and it doesn't end here. ;The EOR sets bits 0-2, and clears bit 4 ;to subtract 180 degrees from the memory ;pointer, too. B12CB TAY ;Put all the shapes where they ought to be. B12CC LDA (SHAPES),Y STA HIRES,X BCC B12D4 DEY DEY B12D4 INY ;More of step 2. DEX DEX ;X-=2. BPL B12CC ;Keep going until X runs out. RTS ; ; Stir the tanks. :-) ; J12DA LDA 8A SEC SBC #02 BCC B130C ;If tank is not exploding, ;parse joystick instead. STA 8A CMP #02 BCC B130B ;RTS if tank has ;just finished exploding. AND #01 ;Stir the LOSER's tank. TAX ;One of these is the tank's bearings. INC DIRECTN,X LDA D8,X STA COLOR0,X LDA 8A CMP #F7 BCC B12F9 JSR J1508 B12F9 LDA 8A BPL B130B ;Don't start decrementing ;volume until halfway through. LSR A LSR A LSR A J1300 STA AUDV0,X ;Sound effects. BOOOM! LDA #08 STA AUDC0,X LDA L17FE,X STA AUDF0,X B130B RTS ; ; Process joysticks. ; B130C LDX #01 LDA SWCHB STA D5 ;Store switches in D5. LDA SWCHA ;Joysticks. B1316 BIT 88 BMI B131C ;If bit-checks minus with 88 (???) LDA #FF ;Reverse all bits. B131C EOR #FF AND #0F STA TEMP LDY VELOC LDA L170F,Y CLC ADC TEMP TAY LDA L1712,Y AND #0F STA D1 ;Remember D1 comes from L1712. BEQ B1338 CMP 91,X BNE B133C B1338 DEC 93,X BNE B1349 B133C STA 91,X LDA #0F STA 93,X LDA D1 CLC ADC DIRECTN,X STA DIRECTN,X B1349 INC 8D,X BMI B136B LDA L1712,Y LSR A LSR A LSR A LSR A BIT D5 BMI B137B B1358 STA 8B,X ASL A TAY LDA L1637,Y STA A8,X INY LDA L1637,Y STA AA,X LDA #F0 STA 8D,X B136B JSR J1380 LDA SWCHA ;Joysticks.. LSR A LSR A LSR A LSR A ASL D5 ;Ummm...??? DEX BEQ B1316 RTS ; B137B SEC SBC VELOC BPL B1358 ;If VELOC