; Disassembly of STARMAST.BIN ; Disassembled Fri Dec 03 23:47:31 1999 ; Using DiStella v2.0 ; ; Command Line: C:\USR\ATARI\DEV\DISTELLA\DISTELLA.EXE -cstarmast.cfg STARMAST.BIN ; ; starmast.cfg contents: ; ; GFX fc39 fffc ; ; Analyzing, naming and commenting done ; by Thomas Jentzsch in 2000/2001 (v0.9) processor 6502 include vcs.h ;=============================================================================== ; A S S E M B L E R - S W I T C H E S ;=============================================================================== OPTIMIZE = 0 ; enable some possible optimizations ;=============================================================================== ; C O N S T A N T S ;=============================================================================== ID_NONE = 0 ; empy sector (1..3 for enemies) ID_BASE = 4 ID_SHIP = 5 ; why didn't he use 8 here? would have made a lot of things easier... MAP_WIDTH = 6 NUM_SECTORS = 6*6 NUM_BASES = 4 NUM_STARS = 8 WARP_TIME = 200 LASER_TIME = 23 NUM_LINES = 144 ; lines of space display FIRE_DISTANCE = 176 ; maximum distance for enemy fire SCREEN_WIDTH = 160 MIN_DIST_Y = 6 ; minimum y-distance between stars MASK_NONE = %1111 MOVE_RIGHT = %1000 MASK_RIGHT = ~MOVE_RIGHT & MASK_NONE MOVE_LEFT = %0100 MASK_LEFT = ~MOVE_LEFT & MASK_NONE MOVE_DOWN = %0010 MASK_DOWN = ~MOVE_DOWN & MASK_NONE MOVE_UP = %0001 BLACK = $00 WHITE = $0f BLUE = $84 RED = $44 ORANGE = $28 GREEN = $d4 BRIGHT_ORANGE = ORANGE+4 ; $2c BRIGHT_BLUE = BLUE+8 ; $8c ;=============================================================================== ; Z P - V A R I A B L E S ;=============================================================================== Level = $80 ; 0..3 SS_XOR = $81 ; screensaver xor-value ($00/$00, $02,.., $fe) SS_Mask = $82 ; screensaver mask ($ff/$f7) FrameCnt = $83 ; frame counter Random = $84 ; current random number ; = $85 ??? NoGameScroll = $86 ; 0 = game is running SS_Delay = $87 ; delay for screensaver (bit7 = 1 -> enabled) Temp = $88 Temp2 = $89 WarpTime = $8a ; when warping, decreased every 4th frame (200..0) SectorList = $8b ; -$9c 18 bytes to store the states of 36 sectors MeteorEnabled = $9d ; 0 = disabled CursorDelay = $9e DockCount = $9f ; number of dockings IsDocked = $a0 ; $00 / $99 ;--------------------------------------- MoveLst = $a1 EnemyMoveY = MoveLst+0 ; $a1 MeteorMoveY = MoveLst+1 ; $a2 EnemyMoveX = MoveLst+2 ; $a3 MeteorMoveX = MoveLst+3 ; $a4 EnemyMoveZ = MoveLst+4 ; $a5 MeteorMoveZ = MoveLst+5 ; $a6 SecondPosY = MeteorMoveX ;--------------------------------------- MeteorSound = $a7 EnemySound = $a8 BaseSound = $a9 ;--------------------------------------- LaserState = $aa ; current state of laser (0 = off, 23..1 = on) AttackedBase = $ab ; id of the current attacked starbase (0..3) LaserTop = $ac ; top y-pos of laser-fire LaserBottom = $ad ; bottom y-pos of laser-fire ;--------------------------------------- ; the next 6 variables contain the y-x-z-positions: ShapePosYLst = $ae ; -$af/b1 ShapePosX = $b0 StarCount = $b1 ; only one x-position (free for temporary use) ShapePosZLst = $b2 ; -$b3 ;--------------------------------------- AudF0Val = $b4 StarMoveDelay = $b5 ; determines the starfield speed TuneIndex = $b6 TuneDelay = $b7 LevelDelay = $b8 ; delay when SELECt is pressed ;--------------------------------------- DigitLst = $b9 ; -$c2 Damage = DigitLst ; $b9/$ba EnergyHi = DigitLst+2 ; $bb decreased every 64th frame (~1 sec) EnergyLo = DigitLst+3 ; $bc StarDateHi = DigitLst+4 ; $bd increased every 256th frame (~4 sec) StarDateLo = DigitLst+5 ; $be WarpEnergy = DigitLst+6 ; $bf/$c0 subtracted from energy every 8th frame while warping ScoreHi = $c1 ScoreLo = $c2 ;--------------------------------------- ShipSectorIdx = $c3 ; current ship sector WarpSectorIdx = $c4 ; target sector for warp BaseShield = $c5 ; -$c8 shield state of the four starbases (255..0) NumBases = $c9 ; number of alive starbases StarPosY = $ca ; -$d1 y-position of the eight stars StarPosX = $d2 ; -$d9 x-position of the eight stars ; = $da ; ??? ThreatedBase = $db ; uses when damaging base shields (3..0) BaseSector = $dc ; 0..7, offset for sectors arround base WalkDelay = $dd WalkSectorIdx = $de ; next sector to walk at attacked starbase ; = $df ??? ;--------------------------------------- StarMoveXLst = $e0 ; -$e7 values for positioning the stars (fine/coarse) BaseDir = $e1 ; direction of attacked base SectorId = $e2 ; only used for enemy walk NewSectorIdx = $e3 ; walk to sector HelpMap = $e4 ; help variable, used for bounds checking etc. ;--------------------------------------- MACCRows = $e8 ; used when displaying the MACC ShapeHeightLst = $e9 ; -$ea ShapePtrLst = $eb ; -$ec max. two different objects/frame ShapePtr = $ed ; -$f6 lo/hi SwitchState = $f9 ; MRS00000 (map, reset, select) Enemies = $fa ; number of remaining enemies LevelPtrLo = $fb ; lo-pointer to level character data (ELWS) ShapeHeight = Temp2 ;=============================================================================== ; R O M - C O D E ;=============================================================================== ORG $F000 START: SEI ;2 CLD ;2 LDX #$FF ;2 TXS ;2 INX ;2 JSR GameInit ;6 LDA #64 ;2 STA WarpTime ;3 maximum star speed at start of game STA NoGameScroll ;3 no game, scroll copyright LDA #18 ;2 STA TuneIndex ;3 MainLoop: ; set color of stars and background: ; (flashes while explosion is heard) LDX #$01 ;2 LDY #$01 ;2 LDA FrameCnt ;3 AND #$04 ;2 BEQ .setPFBKColor ;2 TAX ;2 x = 4 .loopCheckSound: LDA MeteorSound-2,X ;4 x = 4..2 BNE .setPFBKColor ;2 DEX ;2 CPX #$01 ;2 BNE .loopCheckSound ;2 .setPFBKColor: LDA PFBKColTab,X ;4 EOR SS_XOR ;3 AND SS_Mask ;3 STA COLUPF,Y ;5 DEX ;2 DEY ;2 BPL .setPFBKColor ;2 ; set hi data pointer: LDA WarpTime ;3 BNE .doDey ;2 JSR GetShipSectorId ;6 TAX ;2 CMP #ID_BASE+ID_SHIP ;2 starbase sector? BNE .doDey ;2 no, skip dey (y = $ff) LDA ShapePosZLst ;3 CMP #$50 ;2 starbase near? BCC .skipDey ;2 no, skip .doDey: DEY ;2 yes, use other data bank (y = $fe) .skipDey: STY ShapePtr+1 ;3 ; determine player (meteor, starbase, enemy) color: LDA #BRIGHT_ORANGE ;2 LDY WarpTime ;3 BNE .setColor ;2 LDA #BRIGHT_BLUE ;2 CPX #ID_BASE+ID_SHIP ;2 BEQ .setColor ;2 CPX #ID_SHIP ;2 BEQ .setColor ;2 LDA FrameCnt ;3 .setColor: EOR SS_XOR ;3 AND SS_Mask ;3 STA COLUP0 ;3 STA COLUP1 ;3 ; read map switches: LDA NoGameScroll ;3 game running? BNE .checkSwitches ;2 no, check swithces LDA WarpTime ;3 warping? BNE .skipSwitches ;2 yes, skip switches .checkSwitches: LDA SWCHB ;4 read switches LSR ;2 AND #%01100100 ;2 mask B/W and both difficulty switches STA Temp ;3 EOR SwitchState ;3 AND #%01100100 ;2 BEQ .skipSwitches ;2 LDA SwitchState ;3 EOR #$80 ;2 AND #$80 ;2 ORA Temp ;3 STA SwitchState ;3 LDA WarpSectorIdx ;3 move ship TAY ;2 from JSR SetSectorIdNoShip ;6 current to LDA ShipSectorIdx ;3 target- STA WarpSectorIdx ;3 sector .skipSwitches: LDA SwitchState ;3 BPL .spaceKernel ;2 JMP .waitTim1 ;3 goto map kernel ; *** space kernel: *** .spaceKernel: LDX #NUM_LINES-1 ;2 number of kernel-lines STX REFP1 ;3 enable reflection of player 1 TXS ;2 save x into stack-pointer ; calc 3d effect for fighter (move y-pos[0] based on z-pos[0]) : LDX #$00 ;2 LDA ShapePosZLst ;3 distance / 32 LSR ;2 LSR ;2 LSR ;2 LSR ;2 LSR ;2 ORA #%11111000 ;2 SEC ;2 ADC ShapePosYLst ;3 CMP #NUM_LINES-2 ;2 y-pos[0] out of bounds? BCS .draw1 ;2³ yes, draw second (or none) CMP ShapePosYLst+1 ;3 y-pos[0] > y-pos[1]? BCS .draw0 ;2³ yes, draw first LDA ShapePosYLst+1 ;3 CMP #NUM_LINES-2 ;2 y-pos[1] out of bounds? BCS .draw0 ;2³ yes, draw first (-> none!) .draw1: INX ;2 .draw0: LDA ShapePtrLst,X ;4 STA ShapePtr ;3 LDY ShapeHeightLst,X ;4 STY ShapeHeight ;3 ; check, if y-pos[x] in bounds: LDY #-NUM_LINES ;2 disable drawing SEC ;2 LDA ShapePosYLst,X ;4 SBC #NUM_LINES+1 ;2 in y-bounds? BCS .noShapeY ;2³ no, don't draw any object TAY ;2 yes, wait for first object .noShapeY: ; store variables of second object: TXA ;2 switch to other object EOR #$01 ;2 TAX ;2 LDA ShapePosYLst,X ;4 STA SecondPosY ;3 LDA ShapePtrLst,X ;4 STA ShapePtrLst ;3 LDA ShapeHeightLst,X ;4 STA ShapeHeightLst ;3 ; check x-bounds (same for both objects): LDA ShapePosX ;3 CMP #9 ;2 in left x-bound? BCC .noShapeX ;2³ no, don't draw any object CMP #162 ;2 in right x-bounds? BCC .withShapeX ;2³ yes, draw object(s) .noShapeX: LDY #-NUM_LINES ;2 disable drawing .withShapeX: LDX #NUM_STARS-1 ;2 STX StarCount ;3 .waitTim0: LDA INTIM ;4 BPL .waitTim0 ;2³ LDA #$00 ;2 STA WSYNC ;3 ;--------------------------------------- STA HMOVE ;3 STA VBLANK ;3 BEQ EnterKernel ;3 = 9 ;************************************************* ; top of space kernel-loop: ; 5 lines/loop .loopDrawStar: ;6 BCS .loopDrawStar2 ;2³ LDA (ShapePtr),Y ;5 STA GRP0 ;3 STA GRP1 ;3 .loopDrawStar2: LDA #%10 ;2 STA ENABL ;3 TSX ;2 restore x DEX ;2 = 18/25/28 ; en-/disable laser-missiles: LDA #%00 ;2 CPX LaserBottom ;3 BCC .disableM_0 ;2³ CPX LaserTop ;3 BCS .skipM_0 ;2³ LDA #%10 ;2 .disableM_0: STA ENAM0 ;3 STA ENAM1 ;3 .skipM_0: STA WSYNC ;3 = 17/16/23 (total: 34..51) ;--------------------------------------- STA HMOVE ;3 DEX ;2 INY ;2 CPY #$3C ;2 BCS .skipShape1 ;2³ LDA (ShapePtr),Y ;5 STA GRP0 ;3 STA GRP1 ;3 .skipShape1: ; = 12/22 DEX ;2 LDA #$00 ;2 INY ;2 CPY #$3C ;2 BCS .skipShape2 ;2³ LDA (ShapePtr),Y ;5 .skipShape2: ; STA WSYNC ;3 = 14/18 (total: 26..40) ;--------------------------------------- STA HMOVE ;3 STA GRP0 ;3 STA GRP1 ;3 = 9 ; en-/disable laser-missiles: LDA #%00 ;2 STA ENABL ;3 CPX LaserBottom ;3 BCC .disableM_1 ;2³ CPX LaserTop ;3 BCS .skipM_1 ;2³ LDA #%10 ;2 .disableM_1: STA ENAM0 ;3 STA ENAM1 ;3 .skipM_1: ; = 16/17/23 DEX ;2 TXS ;2 ; prepare next star (part 1/2): DEC StarCount ;5 LDX StarCount ;3 = 12 EnterKernel: LDA StarMoveXLst,X ;4 STA HMBL ;3 AND #$0F ;2 STA Temp ;3 INY ;2 LDA (ShapePtr),Y ;5 CPY #$3C ;2 STA WSYNC ;3 = 24 (total: 52..59) ;--------------------------------------- STA HMOVE ;3 BCS .skipShape3 ;2³ STA GRP0 ;3 STA GRP1 ;3 BCC .contShape3 ;3 .skipShape3: NOP ;2 NOP ;2 NOP ;2 NOP ;2 .contShape3: ; ; prepare next star (part 2/2): NOP ;2 LDA $0 ;3 just to wait 3 clocks LDX Temp ;3 = 22 .waitBall: DEX ;2 BPL .waitBall ;2³ STA RESBL ;3 = 7..42 .loopNoStar: TSX ;2 restore x DEX ;2 INY ;2 CPY #$3C ;2 STA WSYNC ;3 = 11 (total: 40..75/46..71) ;--------------------------------------- STA HMOVE ;3 BCS .skipShape4 ;2³ LDA (ShapePtr),Y ;5 STA GRP0 ;3 STA GRP1 ;3 .skipShape4: ; = 6/16/17 TXA ;2 AND #$01 ;2 BEQ .evenFrame ;2³ ; en-/disable laser-missiles: CPX LaserBottom ;3 a = %01 BCC .disableM_2 ;2³ CPX LaserTop ;3 BCS .disableM_2 ;2³ ASL ;2 a = %10 .disableM_2: STA ENAM0 ;3 STA ENAM1 ;3 JMP .contFrame ;3 .evenFrame: ;7 CPY #$3C ;2 BCS .contFrame ;2³ CPY ShapeHeight ;3 BCS .checkSecond ;2³ .contFrame: ; = 12..27 TXS ;2 TXA ;2 LDX StarCount ;3 STX HMBL ;3 CMP StarPosY,X ;4 BCS .loopNoStar ;2³= 16/17 CMP #MIN_DIST_Y ;2 more stars possible? BCC .exitKernel2 ;2³ no, exit kernel INY ;2 CPY #$3C ;2 STA WSYNC ;3 = 11 (total: 45..72) ;--------------------------------------- STA HMOVE ;3 JMP .loopDrawStar ;3 = 6 .checkSecond: ; = 23/33 LDY ShapeHeightLst ;3 STY ShapeHeight ;3 LDY #-NUM_LINES ;2 disable drawing object SEC ;2 LDA SecondPosY ;3 STX Temp ;3 SBC Temp ;3 second object in y-bounds? BPL .drawSecond ;2³ yes, draw second TAY ;2 no, disable second .drawSecond: ; = 22/23 LDA ShapePtrLst ;3 STA ShapePtr ;3 TXS ;2 TXA ;2 LDX StarCount ;3 STX HMBL ;3 STA WSYNC ;3 = 19 (total: 64..75) ;--------------------------------------- STA HMOVE ;3 CMP StarPosY,X ;4 BCS .noStar ;2³ CMP #MIN_DIST_Y ;2 more stars possible? BCC .exitKernel ;2³ no, exit kernel JMP .loopDrawStar2 ;3 = 16 .noStar: ;10 TSX ;2 DEX ;2 BNE .skipShape4 ;2³= 16/17 .exitKernel: SBC #0 ;2 .exitKernel2: TAX ;2 .loopExit: STA WSYNC ;3 ;--------------------------------------- STA HMOVE ;3 LDA #$00 ;2 clear player graphics STA GRP0 ;3 STA GRP1 ;3 DEX ;2 BPL .loopExit ;2³ TXS ;2 stack-pointer = $ff JSR SetupScore ;6 LDX #$01 ;2 draw only partial MACC display STX REFP1 ;3 disable player 1 reflection BNE .drawMACC ;3 jump to MACC ;************************************************* ; *** map kernel: *** .waitTim1: LDA INTIM ;4 BPL .waitTim1 ;2³ LDX #$04 ;2 .loopBlack: STA WSYNC ;3 ;--------------------------------------- DEX ;2 BNE .loopBlack ;2³ STX VBLANK ;3 LDA #46 ;2 y-pos of left three sectors JSR SetPosX ;6 INX ;2 LDA #94 ;2 y-pos of right three sectors (+48) JSR SetPosX ;6 LDY #%011 ;2 3 copies close STY NUSIZ0 ;3 STY NUSIZ1 ;3 STA WSYNC ;3 ;--------------------------------------- STA HMOVE ;3 LDX #12-2 ;2 LDA #$FF ;2 .loopSetHi: STA ShapePtr+1,X ;4 set hi shape pointer DEX ;2 DEX ;2 BPL .loopSetHi ;2³ LDA #BRIGHT_ORANGE ;2 EOR SS_XOR ;3 AND SS_Mask ;3 STA COLUP0 ;3 STA COLUP1 ;3 STA HMCLR ;3 STA WSYNC ;3 ;--------------------------------------- LDX #NUM_SECTORS-1 ;2 STX MACCRows ;3 .loopMapRows: LDY #12-2 ;2 .loopSetLo: LDA MACCRows ;3 JSR GetSectorId ;6 TAX ;2 LDA Damage+1 ;3 AND #$0F ;2 CMP #OFS_R ;2 radar damaged? BNE .radarOk ;2³ no, skip LDA NoRadarTab,X ;4 yes, show bases and ship only TAX ;2 .radarOk: LDA MapShapePtrTab,X STA ShapePtr,Y DEC MACCRows DEY DEY BPL .loopSetLo ;2³ JSR DisplayMapRow LDA MACCRows BPL .loopMapRows ;2³ ; draw 25 empty lines: LDX #24 .loopBlank: STA WSYNC LDA $0 ;3 just to waste 3 clocks NOP ;2 adjust timing before SetupScore NOP ;2 NOP ;2 NOP ;2 DEX ;2 BPL .loopBlank ;2³ NOP ;2 wait 13 extra clocks in total JSR SetupScore ; LDX #$04 ; draw complete MACC display ; *** draw state-display: *** .drawMACC: STX MACCRows LDA #BRIGHT_ORANGE EOR SS_XOR AND SS_Mask STA COLUP0 STA COLUP1 LDA # ~51 seconds/complete round with 4 bases) LDX ThreatedBase LDA BasePositionTab,X ; 10, 25, 1, 29 JSR GetSectorId STA HelpMap CMP #ID_BASE ; base still alive? BEQ .baseAlive CMP #ID_BASE+ID_SHIP ; base (with ship) still alive? BNE .skipBaseAttack ; no, don't attack .baseAlive: LDA ThreatedBase ; number of base ASL ASL ASL ; *8 IF OPTIMIZE NOP ELSE CLC ENDIF ADC BaseSector ; offset of sector around base TAX LDA BaseSectorsTab,X JSR GetSectorId ; get number of enemies in sector TAY LDX ThreatedBase LDA BaseShield,X SEC SBC BaseThreatTab,Y STA BaseShield,X BCS .skipBaseDestroyed ; destroy the attacked starbase: DEC NumBases LDA HelpMap SEC SBC #ID_BASE ; remove base in attacked sector TAY LDA BasePositionTab,X JSR SetSectorId LDA #$B0 ; start base STA BaseSound ; explosion sound .skipBaseDestroyed: DEC BaseSector BPL .skipBaseReset LDA #8-1 ; number of sectors around starbase STA BaseSector .skipBaseAttack: ; attack previous base: DEC ThreatedBase BPL .skipBaseReset LDA #NUM_BASES-1 STA ThreatedBase .skipBaseReset: .skipMoveAttack: ;*** countdown warp counter (every 4th frame): *** LDA FrameCnt AND #$03 BNE .skipCountdownWarp LDA WarpTime BEQ .skipCountdownWarp DEC WarpTime .skipCountdownWarp: ;*** countdown laser state: *** LDA LaserState BEQ .noLaserFire DEC LaserState BNE .contFireLaser .noLaserFire: ; set position of gunsight: LDA #NUM_LINES/2+6 ; gunsight-position (= 78) STA LaserTop LDA #NUM_LINES/2-3 ; (= 69) STA LaserBottom ; check fire laser LDA Damage ; laser damaged or ORA IsDocked ; docked at starbase? CMP #HI_DAMAGE BCS .skipLaser LDA WarpTime ; don't fire laser CMP #WARP_TIME-8 ; soon after start of warp BCS .skipLaser LDA INPT4-$30 ; button pressed and.. ORA SwitchState ; ..not pressed before? BMI .skipLaser ; no, skip! LDA #32 ; yes, fire laser, set start position of laser STA LaserTop LDA #6 STA LaserBottom STX CXCLR ; clear collison registers LDX #LASER_TIME ; STX LaserState ; enable laser .contFireLaser: LDA LaserBottom CLC ADC #$03 STA LaserBottom LDA LaserTop CLC ADC #$02 CMP #NUM_LINES/2+4 ; (=76) BCS .limitLaserTop STA LaserTop ; (34..74) .limitLaserTop: .skipLaser: .skipUpdate0: ; set laser size: LDA LaserState LSR LSR LSR TAX LDA LaserSizeTab,X STA NUSIZ0 ; missile size only STA NUSIZ1 ; *** starfield anímation: *** LDA SWCHA ; read joystick LSR LSR LSR LSR EOR #$0F TAY BEQ .noDirection STA SS_Delay ; reset screensaver when joystick is moved (0..15) .noDirection: LDA NoGameScroll ; game just started or ORA IsDocked ; ship docked at starbase? BEQ .useJoystick ; no, use joystick direction LDY #$00 ; yes, ignore joystick .useJoystick: STY Temp LDA IsDocked ; ship docked at starbase? BNE .skipMoveStars ; yes, don't move stars DEC StarMoveDelay BPL .skipMoveStars ; calculate star speed: LDA WarpTime BMI .minSpeed IF OPTIMIZE NOP NOP NOP NOP NOP ELSE EOR #$FF ; superfluos code, SEC ; because this always results SBC #$68 ; in negative StarMoveDelay! ENDIF .minSpeed: LSR LSR LSR SEC SBC #16 STA StarMoveDelay ; 9..0/-x ; move the starfield: LDX #NUM_STARS-1 .loopMoveStars: ; move y-pos: LDA StarPosY,X LSR LSR LSR ; a = y-pos/8 LDY Temp ; y = joystick direction CLC ADC StarDirYTab,Y ; = 0/2/4 TAY LDA StarMoveYTab,Y ; -6..+8 CLC ADC StarPosY,X STA StarPosY,X ; move x-pos: LDA StarPosX,X LSR LSR LSR LSR ; a = x-pos/16 TAY LDA StarMoveXTab,Y ; -5..+5 LDY Temp ; y = joystick direction CLC ADC StarDirXTab,Y CLC ADC StarPosX,X STA StarPosX,X DEX BPL .loopMoveStars .skipMoveStars: ; move visible objects (meteor, starbase, enemy, explosion; max. 2) ; move y-pos: LDX #1 LDY Temp ; y = joystick direction .loopMoveShapesY: LDA ShapePosYLst,X CLC ADC ShapeDirYTab,Y STA ShapePosYLst,X DEX BPL .loopMoveShapesY ; move x-pos: (there is only one x-pos for both possible shapes!) LDA ShapePosX CLC ADC ShapeDirXTab,Y STA ShapePosX .waitTim: LDY INTIM BPL .waitTim INC FrameCnt ; screensaver routine (part 1/2): BNE .exitSS_Delay INC SS_Delay ; increase every 256th frame (~4 sec) BNE .exitSS_Delay ; screensaver-mode enabled? SEC ; yes, keep bit7 set ROR SS_Delay .exitSS_Delay: LDX #$03 STX WSYNC STX VSYNC ; enable VSYNC STX VBLANK ; enabel VBLANK ;--------------- end of display kernel --------------- ; screensaver routine (part 2/2): LDY #$FF LDA #$00 BIT SS_Delay ; screensaver-mode enabled? BPL .noScreenSaver ; no, use neutral values for SS_registers LDY #$F7 ; yes, mask highest intensity bit (dark colors only).. LDA SS_Delay ; ..and slowly change colors ASL .noScreenSaver: STY SS_Mask STA SS_XOR STA WSYNC LDY #$40 STY VSYNC STY TIM64T LDA LaserState CMP #LASER_TIME-1 BNE .skipDecreaseHi LDA #$01 JSR DecEnergyHi .skipDecreaseHi: ; *** calculate starfield: *** LDX #NUM_STARS-1 .loopStars: LDA StarPosX,X CMP #20 ; SCREEN_WIDTH BCC .replaceStar CMP #136 ; SCREEN_WIDTH BCS .replaceStar LDA StarPosY,X CMP #NUM_LINES-MIN_DIST_Y; >= 138? BCS .replaceStar CMP #MIN_DIST_Y+1 ; <= 6? BCC .replaceStar .nextStar: DEX BPL .loopStars JMP .exitReplaceStar ; OPTIMIZE: replace with two BMI-branches ; replace out-of-bounds star: .replaceStar: STX Temp ; save replaced star index LDX #NUM_STARS-1 .findLowerStar: LDA StarPosY,X CMP #NUM_LINES/2 ; lower star? BCC .lowerStarFound ; yes, goto found DEX BPL .findLowerStar .lowerStarFound: INX STX Temp2 ; save index of middle star + 1 (0..7/8?) LDX Temp ; CPX Temp2 ; replaced < middle+1 BCC .lowerHalfStar ; yes, lower ; for (x = replace-1; x >= middle+1; x--) ; star[x+1] = star[x]; .loopMoveStarsNext: DEX BMI .posNewStarY ; exit, if < 0 (needed if middle-id+1 = 0) CPX Temp2 BCC .posNewStarY ; exit, if x < middle-id+1 LDA StarPosY,X STA StarPosY+1,X LDA StarPosX,X STA StarPosX+1,X BCS .loopMoveStarsNext;3 always taken jump .posNewStarY: LDX Temp2 LDA #NUM_LINES/2+11 ; STA StarPosY,X CPX #NUM_STARS/2 ; BCS .loopUp_dY LDA #NUM_LINES/2-9 ; STA StarPosY,X DEX IF OPTIMIZE BCC .loopDown_dY NOP ELSE JMP .loopDown_dY ENDIF .loopUp_dY: ; make sure, that y-distance >= 6 (kernel-limitation!): CLC ADC #MIN_DIST_Y ; new y-pos[new] + 6 .. CMP StarPosY+1,X ; .. < y-pos[new+1]? BCC .setRandomStarX ; yes, ok LDA StarPosX+1,X ; no, swap index STA StarPosX,X LDA StarPosY+1,X STA StarPosY,X CLC ADC #MIN_DIST_Y ; change y-pos[new] to.. STA StarPosY+1,X ; ..y-pos[new+1] + 6 INX CPX #NUM_STARS-1 BCC .loopUp_dY ; x-pos = middle of scrren +/- 32 pixel: .setRandomStarX: JSR NextRandom LDA Random AND #$3F ; a = 0..63 CLC ADC #[SCREEN_WIDTH-$3f]/2; +48 STA StarPosX,X LDX Temp IF OPTIMIZE BCC .nextStar NOP ELSE JMP .nextStar ENDIF .lowerHalfStar: DEX ; x = replace-1 DEC Temp2 ; Temp2 = middle ; for (x = replace; x < middle; x++) ; star[x] = star[x+1]; .loopMoveStarsPrev: INX CPX Temp2 BCS .posNewStarY LDA StarPosY+1,X STA StarPosY,X LDA StarPosX+1,X STA StarPosX,X BCC .loopMoveStarsPrev;3 always taken jump .loopDown_dY: ; a = NUM_LINES/2-9 ; make sure, that y-distance >= 6 (kernel-limitation!): SEC SBC #MIN_DIST_Y ; new y-pos[new] - 6 .. CMP StarPosY,X ; .. >= y-pos[new+1]? BCS .gotoRandomStarX ; yes, ok LDA StarPosX,X ; no, swap index STA StarPosX+1,X LDA StarPosY,X STA StarPosY+1,X SEC SBC #MIN_DIST_Y ; change y-pos[new] to.. STA StarPosY,X ; ..y-pos[new+1] - 6 DEX BPL .loopDown_dY .gotoRandomStarX: INX BNE .setRandomStarX .exitReplaceStar: ; calculate StarMoveXLst: LDY #$FF ; disable HMBx STY Temp2 ; in CalcPos1 LDX #NUM_STARS-1 .loopStars2: LDA StarPosX,X CMP #136 BCC .xPosOk LDA #135 .xPosOk: JSR CalcPos1 STA StarMoveXLst,X TYA ORA StarMoveXLst,X STA StarMoveXLst,X DEX BPL .loopStars2 ;*** check for map-mode: *** LDA SwitchState BMI .isMapMode .skipMapJmp: JMP .skipMoveMap ; *** bookkeeping for mad mode: *** .isMapMode: LDA NoGameScroll ; game running? BNE .skipMapJmp ; no, skip ;*** move ship on map: *** LDA SWCHA LSR LSR LSR LSR EOR #$0F LDX #MAP_WIDTH-1 .loopCheckBordersX: LDY Mult6Tab,X CPY WarpSectorIdx BNE .notAtLeft AND #MASK_LEFT .notAtLeft: LDY Mult6_1Tab,X CPY WarpSectorIdx BNE .notAtRight AND #MASK_RIGHT .notAtRight: DEX BPL .loopCheckBordersX TAX BNE .contMoveCursor STA CursorDelay BEQ .skipMoveCursor ;3 .contMoveCursor: LDA CursorDelay BEQ .resetCursorDelay DEC CursorDelay BPL .skipMoveCursor .resetCursorDelay: LDA #16 ; ~1/4 sec delay STA CursorDelay LDA WarpSectorIdx TAY CLC ADC MoveTab,X BMI .skipMoveCursor CMP #NUM_SECTORS ; out of y borders? BCS .skipMoveCursor ;2³ yes ,don't move cursor ; move the cursor: STA WarpSectorIdx TYA JSR SetSectorIdNoShip LDA WarpSectorIdx ; warp sector = CMP ShipSectorIdx ; current sector? BEQ .skipMoveCursor ; yes, don't move JSR GetSectorId CLC ADC #ID_SHIP TAY LDA WarpSectorIdx JSR SetSectorId ;*** calculate distance between current and target sector: *** .skipMoveCursor: ; calculate y-pos of both sectors: LDY #$01 .currentSector: STX Temp STA Temp2 LDA ShipSectorIdx,Y LDX #$00 .loopDiv6: CMP #MAP_WIDTH BCC .exitDiv6 INX SEC SBC #MAP_WIDTH BPL .loopDiv6 .exitDiv6: DEY BPL .currentSector ; calculate y-distance of sectors: SEC SBC Temp2 BPL .noNegY ; negative y-distance? EOR #$FF CLC ADC #$01 .noNegY: STA Temp2 ; calculate x-distance of sectors: TXA SEC SBC Temp BPL .noNegX ; negative x-distance? EOR #$FF CLC ADC #$01 .noNegX: SED CLC ADC Temp2 STA WarpEnergy ; save total distance LDY Damage+1 ; warp damaged? CPY #HI_DAMAGE BCC .warpOk ; no, skip CLC ; yes, ADC WarpEnergy ; double energy use .warpOk: CLD STA WarpEnergy LDY INPT4-$30 ; fire button pressed? BMI .skipMoveMap ; no, skip warp LDY WarpSectorIdx CPY ShipSectorIdx BEQ .skipMoveMap JSR GetShipSectorId ; remove SEC ; ship SBC #ID_SHIP ; from TAY ; current JSR SetShipSectorId ; sector LDA WarpSectorIdx ; and to STA ShipSectorIdx ; target sector LDA SwitchState ; switch from EOR #$80 ; map-mode to STA SwitchState ; cockpit-mode LDA #$FE ; set enemy at STA ShapePosZLst ; maximum distance-1 LDA #WARP_TIME ; start warping STA WarpTime ; to target sector LDA #$00 STA MeteorEnabled STA IsDocked .skipMoveMap: ;*** create new enemy: *** LDA EnemySound ; enemy explision finished? CMP #$01 BNE .skipNewEnemy ; no, skip new enemy ; calculate random positions: LDA Random AND #$7F STA ShapePosYLst JSR NextRandom LDA MeteorEnabled ; if enemy fire is still enabled.. BNE .keepPosX ; ..keep x-pos (hack!) LDA Random AND #$7F STA ShapePosX .keepPosX: LDA #$FC ; set enemy at STA ShapePosZLst ; maximum distance-3 .skipNewEnemy: ;*** move objects: *** LDA FrameCnt ; even frame AND #$01 ; and ORA NoGameScroll ; game running? BNE .skipMoveObjects ; no, skip move shapes LDX #$05 .loopMoveObjects: LDA MoveLst,X CLC ADC ShapePosYLst,X STA ShapePosYLst,X DEX BPL .loopMoveObjects .skipMoveObjects: LDX #$01 .loopSetShapePtr: LDA ShapePosZLst,X CMP #FIRE_DISTANCE BCC .limitDistance LDA #FIRE_DISTANCE .limitDistance: LSR LSR LSR LSR TAY STX Temp LDX WarpTime BNE .noBase JSR GetShipSectorId CMP #ID_BASE+ID_SHIP BNE .noBase TYA IF OPTIMIZE ADC #OFS_BASE-1 NOP ELSE CLC ADC #OFS_BASE ENDIF TAY .noBase: LDX Temp IF OPTIMIZE BEQ .noMeteor NOP NOP ELSE CPX #$01 BNE .noMeteor ENDIF TYA CLC ADC #OFS_METEOR TAY .noMeteor: LDA ShapeHeightTab,Y STA ShapeHeightLst,X LDA ShapePtrTab,Y STA ShapePtrLst,X DEX BPL .loopSetShapePtr ;*** update all variables: *** LDA NoGameScroll ; game running? BEQ .doUpdate1 ; yes, update JMP skipUpdate1 ; no, skip update .doUpdate1: ; decrease energy: LDA FrameCnt AND #$3F BNE .skipDecreaseLo LDA #$01 ; decrease energie every 64th frame JSR DecEnergyLo .skipDecreaseLo: LDA MeteorEnabled BNE .noMeteorEnabled STA ShapePosYLst+1 .noMeteorEnabled: JSR GetShipSectorId STA Temp2 LDA WarpTime BEQ .noWarp LDA FrameCnt AND #$07 BNE .skipDecWarp LDA WarpEnergy ; decrease energie every 8th frame when warping JSR DecEnergyLo .skipDecWarp: LDA #$FF ; set enemy (-> meteor) at STA ShapePosZLst ; maximum distance STA EnemyMoveZ ; and hi shape pointer = $ff BNE .contWarp ;3 .noWarp: LDA Temp2 CMP #ID_BASE+ID_SHIP BEQ .atBaseSector ; makes no sense to me! (.skipNewFire?) CMP #ID_SHIP BNE .contWarp LDA #$00 ; if ship only, STA ShapePosYLst ; set y-pos = 0 (-> no hit) .contWarp: LDA EnemySound ORA MeteorSound ORA MeteorEnabled BNE .skipNewFire LDA FrameCnt ; fire only AND #$7F ; every 128th frame (~2 sec) BNE .skipNewFire JSR CalcRandomMove .atBaseSector: LDA WarpTime CMP #80 ; warp soon over? BCS .newFire ; no, new meteor LDA Temp2 CMP #ID_BASE+ID_SHIP BEQ .skipNewFire CMP #ID_SHIP BEQ .skipNewFire LDA ShapePosZLst ; enemy to.. CMP #FIRE_DISTANCE+1 ; ..far away? BCS .skipNewFire ; yes, skip fire .newFire: LDA #0 ; enemy fires or new meteor STA MeteorMoveY ; no y-movement LDX Level LDA LevelDiffTab,X STA MeteorMoveZ STA MeteorEnabled ; <> 0 LDA ShapePosZLst ; copy enemy distance STA ShapePosZLst+1 ; to enemy fire/meteor distance LDY ShapePosYLst INY ; copy enemy y-pos STY ShapePosYLst+1 ; to enemy fire/meteor y-pos .skipNewFire: ; check, if the meteor/enemy fire is inside the hitting bounds: LDY #$02 LDX MeteorEnabled .loop: LDA ShapePosYLst+1 ; load y-pos[1] CPY #$02 ; check y-bounds? BNE .skipLoadX ; yes, skip LDA ShapePosX ; no, load x-pos .skipLoadX: CMP BoundsXYTab,Y BCS .inLowerBound1 LDX #$00 .inLowerBound1: CMP BoundsXYTab+1,Y BCC .inUpperBound1 LDX #$00 .inUpperBound1: DEY DEY BPL .loop STX MeteorEnabled ; 0/-5..-2 IF OPTIMIZE TXA NOP ELSE LDA MeteorEnabled ENDIF BEQ .notHitXY LDA ShapePosZLst+1 CMP #6 ; distance < 6 ? BCS .notHitZ ; no, not hit LDY #$7F STY BaseSound JSR DecEnergyHi ; -500 LDA #$00 ; disable STA MeteorEnabled ; enemy fire/meteor LDA Damage ; shield damaged? AND #$0F CMP #OFS_S BNE .randomDamage ; no, continue JMP GameOver ; yes, game over! .randomDamage: LDA Random CMP #$40 ; 25% chance BCS .skipDamage AND #$03 TAX ; x = 0..3 LSR TAY ; y = 0..1 LDA DamageTab,X AND DamageMask+1,X STA Temp EOR Damage,Y AND DamageMask+1,X BEQ .skipDamage ; item was already damaged LDA Damage,Y AND DamageMask,X ORA Temp STA Damage,Y LDA #$60 STA AudF0Val .skipDamage: ; meteor/enemy fire hit or is out of bounds, set maximum distance: .notHitXY: LDA #$FC ; set meteor/enemy fire STA ShapePosZLst+1 ; at maximum distance-3 .notHitZ: ; invert enemy movement, if out of bounds: (->EnemyMoveY, EnemyMoveX, EnemyMoveZ) LDY #$04 .loopCorrect: LDA ShapePosYLst,Y ; ShapePosYLst, ShapePosX, ShapePosZLst CMP BoundsXYTab,Y BCS .inLowerBound0 ; below lower bound: LDX BoundsMoveTab,Y ; always -1 !? STX MoveLst,Y BNE .inUpperBound0 .inLowerBound0: CMP BoundsXYTab+1,Y BCC .inUpperBound0 ; above upper bound: LDX BoundsMoveTab+1,Y ; always 1 !? STX MoveLst,Y .inUpperBound0: DEY DEY BPL .loopCorrect ; check collisons (enemy, meteor or starbase) LDA CXM0P-$30 ORA CXM1P-$30 AND #$C0 BNE .doCollisions .skipCollisionsJmp: JMP .skipCollisions .doCollisions: STA CXCLR ; check, if enemy fire/meteor is hit: LDA ShapePosX ; check if meteor is somewhere in the middle CMP #70 BCC .skipCollisionsJmp CMP #94 BCS .skipCollisionsJmp LDA LaserState ; laser fired? BEQ .skipMeteor ; no, skip LDA MeteorEnabled BEQ .skipMeteor LDA ShapePosYLst+1 CMP #$45 BCC .skipMeteor SEC SBC $EA CMP #$4C BCS .skipMeteor LDA #$F0 ; set meteor/enemy fire STA ShapePosZLst+1 ; at maximum distance-15 LDA #$00 ; stop the STA MeteorMoveY ; movement STA MeteorMoveX ; of the STA MeteorMoveZ ; meteor STA MeteorEnabled LDA #$5F STA MeteorSound BNE .skipCollisionsJmp .skipMeteor: ; check, if enemy fighter is hit: LDA EnemySound ORA WarpTime BNE .skipCollisions LDA ShapePosYLst CMP #69 ; [NUM_LINES] BCC .skipCollisions SEC SBC ShapeHeightLst CMP #76 ; [NUM_LINES] BCS .skipCollisions LDA Temp2 CMP #ID_BASE+ID_SHIP BNE .contEnemyHit LDA ShapePosZLst ; base near and.. ORA IsDocked ; ..ship not docked? CMP #$0F BCS .skipCollisions ; no, skip docking SED LDA DockCount ; increase docking-counter CLC ADC #$01 STA DockCount CLD LDA #$0F STA AudF0Val STA ShapePosZLst ; fix starbase distance LDA #$99 STA IsDocked STA EnergyHi ; refuel STA EnergyLo LDA #NO_DAMAGE STA Damage ; repair STA Damage+1 BNE .stopEnemy .contEnemyHit: LDA LaserState ; laser fireing? BEQ .skipCollisions ; no, skip ; GOTCHA! (the enemy is hit): LDA #$F0 ; set enemy at STA ShapePosZLst ; maximum distance-15 LDA #$7F STA EnemySound JSR GetShipSectorId ; decrease enemies in sector SEC SBC #$01 TAY JSR SetShipSectorId SED SEC ; decrease total number of enemies LDA Enemies SBC #$01 STA Enemies CLD BNE .stopEnemy JMP GameOver .stopEnemy: LDA #0 STA EnemyMoveY STA EnemyMoveX STA EnemyMoveZ .skipCollisions: skipUpdate1: LDX #0 LDA ShapePosX JSR SetPosX ; player 0 INX LDA ShapePosX CLC ADC #8 JSR SetPosX ; player 1 (= player 0 + 8) INX LDA #163 ; [NUM_LINES] JSR SetPosX ; missile 0 INX LDA #6 ; [NUM_LINES] JSR SetPosX ; missile 1 STA WSYNC STA HMOVE ; *** sound routines: *** ; update sound variables: LDX #2 .loopDecSound: LDA MeteorSound,X BEQ .skipSound DEC MeteorSound,X .skipSound: DEX BPL .loopDecSound ; check for damage or docking sound: LDA AudF0Val BEQ .laserSound DEC AudF0Val STA AUDF0 LDX #$0C STX AUDC0 BNE .setAUDV0 ; generate laser sound: .laserSound: LDX #$08 STX AUDC0 LDA LaserState ; laser fireing? BEQ .spaceSound ; no, skip LDA Random AND #$03 STA AUDF0 LDA #$0F STA AUDV0 BNE .checkMeteorSound ; generate space sound: .spaceSound: LDX #$00 LDA NoGameScroll ; game running? BNE .lowSpaceSound LDX #$08 LDA WarpTime BNE .warpSound LDA #$FF LDX #$03 BNE .lowSpaceSound .warpSound: CMP #22 BCS .lowSpaceSound EOR #$FF LDX #$06 .lowSpaceSound: LSR LSR LSR STA AUDF0 .setAUDV0: STX AUDV0 ; sound for incoming enemy fire/meteor: .checkMeteorSound: LDA MeteorEnabled BEQ .noMeteor1 LDA ShapePosZLst+1 LSR LSR LSR STA AUDF1 LSR EOR #$8F STA AUDV1 LDA #$08 STA AUDC1 BNE .exitSound .noMeteor1: ; generate explosion sound: LDA EnemySound ORA MeteorSound ORA BaseSound BEQ .playTunes LSR LSR LSR STA AUDV1 LDA Random ORA #$18 STA AUDF1 LDA #$08 STA AUDC1 BNE .exitSound ; play victory or lost tunes: .playTunes: LDA TuneIndex ; play tunes BEQ .noTune ; no, skip LDA FrameCnt AND #$07 BNE .exitSound LDA #$0C STA AUDC1 DEC TuneDelay LDA TuneDelay BEQ .noTune BPL .exitSound DEC TuneIndex LDX TuneIndex LDA AUDF1Tab,X BNE .skipStopTune STA TuneIndex ; TuneIndex = 0 BEQ .noTune .skipStopTune: STA AUDF1 LSR LSR LSR LSR LSR STA TuneDelay LDA #$08 .noTune: STA AUDV1 .exitSound: ;*** prepare missile positioning: *** STA HMCLR LDA #$10 ; -1 STA HMM0 LDA #$F0 ; +1 STA HMM1 JMP MainLoop DisplayMapRow SUBROUTINE LDY #$07 .loopDisplay: STA WSYNC LDA (ShapePtr),Y STA GRP0 LDA (ShapePtr+6),Y STA GRP1 LDA (ShapePtr+10),Y STA Temp LDA (ShapePtr+4),Y TAX LDA (ShapePtr+2),Y STA GRP0 NOP STX GRP0 LDX Temp LDA (ShapePtr+8),Y STA GRP1 NOP STX GRP1 DEY BNE .loopDisplay STY GRP0 STY GRP1 RTS DisplayCopyright SUBROUTINE LDY #$0F LDA #$07 STA $A4 LDA NoGameScroll LSR LSR LSR CMP #$14 ; scroll-animation BCS .ok LDY #$07 CMP #$0C BCC .ok SBC #$04 TAY .ok: STY Temp2 JMP .contCopyright DisplayMACC: LDA #$06 STA Temp2 STA $A4 .contCopyright: STA WSYNC LDA #$01 STA VDELP0 STA VDELP1 LDX #10 .wait: DEX BPL .wait LDA $00 ;3 just wait 3 cycles .loopDisplay: LDY Temp2 LDA (ShapePtr+10),Y STA $0188 ; = Temp LDA (ShapePtr+8),Y TAX LDA (ShapePtr),Y STA GRP0 LDA (ShapePtr+2),Y STA GRP1 LDA (ShapePtr+4),Y STA GRP0 LDA (ShapePtr+6),Y LDY Temp STA GRP1 STX GRP0 STY GRP1 STA GRP0 DEC Temp2 DEC $A4 BPL .loopDisplay LDA #$00 STA GRP0 STA GRP1 STA GRP0 STA GRP1 STA VDELP0 STA VDELP1 RTS SetSectorIdNoShip SUBROUTINE ; remove players-ship from SectorId: ; a = y = SectorIdx ; (this routine could be much more optimized!) CMP ShipSectorIdx BEQ .exit ; remove ship from ship-nibble: IF OPTIMIZE LSR NOP ELSE CLC ROR ; SectorIdx/2 ENDIF TAX LDA SectorList,X BCC .loNibble1 LSR LSR LSR LSR .loNibble1: AND #$0F SEC SBC #ID_SHIP STA Temp ; save nibble without ship ; get other nibble: TYA IF OPTIMIZE LSR NOP ELSE CLC ROR ; SectorIdx/2 ENDIF TAX LDA SectorList,X BCC .loNibble2 AND #$0F ASL Temp ASL Temp ASL Temp ASL Temp ORA Temp IF OPTIMIZE BCC .setId NOP ELSE JMP .setId ENDIF .loNibble2: ; combine both nibbles AND #$F0 ORA Temp .setId: STA SectorList,X ; save new SectorId .exit: RTS GameInit SUBROUTINE LDY #$00 STY NUSIZ0 STY NUSIZ1 .clearLoop: STY $00,X INX CPX #$B8 BNE .clearLoop LDX #$28 .initLoop: LDA InitTab,X STA $B8,X DEX BPL .initLoop LDY Level LDA EnemyNumTab,Y STA Enemies LDA LevelPtrTab,Y STA LevelPtrLo LDX #NUM_SECTORS/2-1 .sectorInitLoop: LDA SectorInitTab,X STA SectorList,X DEX LDA SectorInitTab,X ; the number of enenies ORA Level ; is increased by ORing STA SectorList,X ; every 4th sector with level DEX BPL .sectorInitLoop LDA SWCHB ; read switches LSR AND #%01100100 ; mask B/W and both difficulty switches LDY NoGameScroll BEQ .noGame ORA #%10000000 ; set bit7 to prevent repeat .noGame: STA SwitchState RTS NextRandom SUBROUTINE ; quite good pseudo random generator: LDA Random BNE .skipInit LDA #$FF .skipInit: ASL ASL ASL EOR Random ASL ROL Random RTS CalcPos1 SUBROUTINE CLC ADC #$F0 JMP .contPos1 SetPosX: LDY #$00 CLC ADC #$25 STY Temp2 .contPos1: TAY AND #$0F STA Temp TYA LSR LSR LSR LSR TAY CLC ADC Temp CMP #$0F BCC .ok SBC #$0F INY .ok: EOR #$07 ASL ASL ASL ASL BIT Temp2 BPL .setPos RTS .setPos: STA HMP0,X STA WSYNC .wait: DEY BPL .wait STA RESP0,X RTS SetupScore SUBROUTINE NOP LDA #$A0 STA HMP0 LDA #$B0 STA HMP1 STA RESP0 STA RESP1 STA WSYNC STA HMOVE ; determine background color: JSR GetShipSectorId LDY #GREEN CMP #ID_SHIP BEQ .noEnemy LDY #BLUE CMP #ID_BASE+ID_SHIP BEQ .noEnemy LDY #RED .noEnemy: TYA EOR SS_XOR AND SS_Mask TAX LDY #$80 STA HMCLR STA WSYNC STA HMOVE ; blink background when energy is low: LDA FrameCnt AND #$1F CMP #$0F BCS .noScreenSaver LDA EnergyHi CMP #$10 BCS .noScreenSaver LDY #$26 .noScreenSaver: TYA EOR SS_XOR AND SS_Mask STA WSYNC STX COLUBK STA COLUPF LDX #%1 STX CTRLPF ; reflect playfield DEX STX GRP0 STX GRP1 DEX STX PF2 LDA #%011 ; 3 copies close STA NUSIZ0 STA NUSIZ1 ; setup hi shape pointer: LDX #12-2 LDA #$FF .loop: STA ShapePtr+1,X DEX DEX BPL .loop RTS GetShipSectorId: LDA ShipSectorIdx GetSectorId SUBROUTINE ; returns state of sector a in a: CLC ROR TAX LDA SectorList,X BCC .lowNibble LSR LSR LSR LSR .lowNibble: AND #$0F RTS SetShipSectorId: LDA ShipSectorIdx SetSectorId SUBROUTINE STY Temp CLC ROR TAX LDA SectorList,X BCC .lowNibble AND #$0F ASL Temp ASL Temp ASL Temp ASL Temp ORA Temp JMP .set .lowNibble: AND #$F0 ORA Temp .set: STA SectorList,X RTS CalcRandomMove SUBROUTINE LDY #$04 .loop JSR NextRandom LDA Random AND #$07 TAX LDA RandomMoveTab,X STA MoveLst,Y DEY BPL .loop RTS DecEnergyLo SUBROUTINE STA Temp SED SEC LDA EnergyLo SBC Temp STA EnergyLo LDA EnergyHi SBC #$00 JMP .checkEmpty DecEnergyHi: STA Temp LDA EnergyHi SEC SED SBC Temp CLD .checkEmpty: BCS .notEmpty LDA #$00 STA EnergyLo STA EnergyHi PLA PLA JMP GameOver .notEmpty: STA EnergyHi CLD RTS GameOver SUBROUTINE LDY #12 ; tune index for lost SED SEC LDX Level LDA EnemyNumTab,X ; number of enemies in level $31 SBC Enemies ; number of remaining enemies CMP EnemyNumTab,X BNE .gameLost LDY #18 ; tune index for victory CLC ADC LevelScoreTab,X ; = $80 level-score $49 .gameLost: STY TuneIndex ; save tune index CLC ADC NumBases ; = $86 + 5 * number of remaining bases+2 ADC NumBases ; = $92 ADC NumBases ; = $98 ADC NumBases ; = $04 ADC NumBases ; = $11 C=1! SEC SBC #$10 ; = $01 SEC SBC DockCount ; = $00 BCS .posScore1 LDA #$00 .posScore1: STA ScoreHi LDA #$00 SEC SBC StarDateLo STA ScoreLo DEC NoGameScroll LDA ScoreHi SBC StarDateHi BCS .posScore2 LDA #0 .posScore2: STA ScoreHi ; Score = Enemies + BaseScore-$20 + 5*Bases - $10 - Docking - StarDate LDA #0 STA MeteorEnabled STA LaserTop STA LaserBottom STA ShapePosYLst STA ShapePosYLst+1 STA LaserState CLD JMP skipUpdate1 ;=============================================================================== ; R O M - T A B L E S ;=============================================================================== BaseDirTab: ; LFC39 .byte MOVE_RIGHT|MOVE_DOWN .byte MOVE_LEFT |MOVE_UP .byte MOVE_DOWN ; the next byte ($09 = %1001 = MOVE_RIGHT|MOVE_UP) us used too! EnemyNumTab: .byte $09, $17, $23, $31 ; bcd-numbers of enemies/level BaseThreatTab: ; damage of base-shields/round .byte 0, 5, 10, 15 ; without player in sector .byte 0 ; (dummy) .byte 0, 5, 10, 15 ; with player in sector AUDF1Tab: .byte $00, $FE, $3F, $7E, $3E, $7A, $3A, $79, $FE, $3E, $7E, $FE ; lost-tune .byte $00, $EC, $2B, $EE, $73, $FD ; victory-tune .byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 ; map organisation: ; 30 31 32 33 34 35 ; 24(25)26 27 28(29) ; 18 19 20 21 22 23 ; 12 13 14 15 16 17 ; 06 07 08 09(10)11 ; 00(01)02 03 04 05 BasePositionTab: .byte MAP_WIDTH*1 + 4 ; 10, lower right .byte MAP_WIDTH*4 + 1 ; 25, upper left .byte MAP_WIDTH*0 + 1 ; 01, lower left .byte MAP_WIDTH*4 + 5 ; 29, upper right BaseSectorsTab: ; numbers of sectors arround the starbase .byte 3, 4, 5, 9, 11, 15, 16, 17 .byte 18, 25, 20, 24, 26, 30, 31, 32 ; BUG! (25=$19 should be 19=$13! wrong '$' ?) .byte 1, 1, 1, 0, 2, 6, 7, 8 .byte 22, 23, 29, 29, 28, 34, 35, 29 StarDirYTab: ; joystick causes additional y-move: (index-change of StarMoveYTab) .byte 2, 4, 0, 2, 2, 4, 0, 2, 2, 4 ; $FC8B Mult6Tab: ; MAP_WIDTH*n (n = 0..MAP_WIDTH-1) .byte 0, 6,12,18,24,30 Mult6_1Tab: ; MAP_WIDTH*n-1 (n = 0..MAP_WIDTH-1) .byte 5,11,17,23,29,35 PFBKColTab: .byte WHITE ; normal .byte BLACK ; meteor hit .byte BLUE ; enemy hit .byte RED ; own hit .byte ORANGE StatePtrTab: .byte