; ; Space Invaders v1.0 ; David Phillips ; program started: 04/28/98 ; last update: 06/05/98 ; #include "ti86asm.inc" #include "asm86.h" #include "ti86und.inc" .org _asm_exec_ram ;------------------------------------------------------------------------- ShellTitle: nop ; for the title line in shells (ASE) jp Startup ; jump to real start of program .dw $0000 ; used by shell (might be used later for version numbers) .dw ShellTitleText ; where the title of the program is ShellTitleText: .db "Space Invaders by David P",0 ;------------------------------------------------------------------------- ; Variables: Bytes: ; player: player = _textShadow + 0 ; [1] X location of player (always on ground--same Y) lives = _textShadow + 1 ; [1] number of lives player has level = _textShadow + 2 ; [1] current level player is on score = _textShadow + 3 ; [2] score that player has p_shots = _textShadow + 5 ; [10] (X, Y) locations of player's shots -- max of 5 fired = _textShadow + 15 ; [1] flag to make player press fire for every shot ; space invaders: invader_x = _textShadow + 16 ; [1] flock's X location invader_y = _textShadow + 17 ; [1] flock's Y location invader_dir = _textShadow + 18 ; [1] invader's direction (-1 or 1) invader = _textShadow + 19 ; [30] 5 x 6 array of aliens -- their life i_shots = _textShadow + 49 ; [20] (X, Y) locations of invader's shots -- max of 10 i_left = _textShadow + 69 ; [1] number of invaders left shot_move = _textShadow + 70 ; [1] next time to move invaders shots ;------------------------------------------------------------------------- Startup: call BUSY_OFF ; turn off the busy indicator res appTextSave,(iy+appflags) ; we don't want our variables to be overwritten call _flushallmenus ; clear menus, to use last 2 lines for text call _clrLCD ; clear the display screen ld hl,$0000 ; load in the upper-left corner (1, 1) ld (_curRow),hl ; actually change the position ld hl,titletext ; load the address of the title screen call _puts ; print the title screen call Pause ; call our pause subroutine xor a ; start at level 1 -- NewLevel will up it ld (level),a ; set level ld (i_left),a ; this will force NewLevel to be called ld a,3 ; start with 3 lives ld (lives),a ; set lives ld hl,0 ; start with 0 score ld (score),hl ; set score MainLoop: ld a,(i_left) ; load Aliens left counter cp 0 ; are they all dead? call z,NewLevel ; if they are, time for a new level GetInput: call _getky ; read a keypress cp K_EXIT ; was it EXIT? jp z,ExitGame ; time to quit cp K_MORE ; was it MORE? call z,Pause ; if so, Pause the game di ; disable interrupts ld a,%00111110 ;\ (bitmask for the out (1),a ; | status of arrow keys in a,(1) ;/ and top keys in A) ei ; re-enable interrupts bit 1,a ; is left pressed? jr z,MoveLeft ; move left bit 2,a ; is right pressed? jr z,MoveRight ; move right jr OtherKeys ; no arrows pressed, check other keys MoveLeft: ld a,(player) ; get the player's position cp 1 ; are we at the left side? jr c,OtherKeys ; if we are, don't move over call ErasePlayer ; erase player's ship before we move him ld a,(player) ; re-load the player's position sub 2 ; move left two ld (player),a ; set the postion call DrawPlayer ; draw the player in the new position jr OtherKeys ; we're done here MoveRight: ld a,(player) ; get the player's position cp $58 ; are we all at the right side? jr nc,OtherKeys ; if we are, don't move over call ErasePlayer ; erase player's ship before we move him ld a,(player) ; re-load the player's position add a,2 ; move right two ld (player),a ; set the position call DrawPlayer ; draw the player in the new position OtherKeys: di ; disable interrupts ld a,%00111111 ;\ (bitmask for the out (1),a ; | status of 2nd key in a,(1) ;/ in A) ei ; re-enable interrupts bit 5,a ; is second pressed? jr z,Fire ; let them fire xor a ; faster than "ld a,0" ld (fired),a ; reset firing flag jp UpdateAliens ; don't want to fire Fire: ld a,(fired) ; load in fired flag ld d,a ; save it for a little while ld a,1 ; don't want them to fire ld (fired),a ; set fired flag so they can't fire ld a,d ; get back the old fired flag cp 1 ; is it set to no fire? jp z,UpdateAliens ; then don't let them fire ld hl,p_shots ; load in the invader's shots ld b,5 ; check up to 5 shots CheckFire: ld a,(hl) ; load in the shot's x value cp $ff ; is it empty? jr z,FireCDone ; then we're done inc hl ; add one to HL inc hl ; add another to HL (HL += 2) djnz CheckFire ; check another shot jr UpdateAliens ; if we're here, no shots are available FireCDone: ld a,(player) ; get player's x position ld (hl),a ; set shot's x value inc hl ; move to shot's y value ld a,$2d ; start shot above player ld (hl),a ; set shot's y value UpdateAliens: ld a,(invader_dir) ; get the aliens direction ld d,a ; save it in D cp 1 ; is it 1? jp z,MoveIRight ; then they're moving right ld a,(invader_x) ; get their x position cp 0 ; are they at the left edge? jp nz,MoveAliens ; if not, move we're ready to update position ld d,1 ; we're at edge, time to move right ld a,(invader_y) ; get their y postion inc a ; move down one pixel ld (invader_y),a ; set new position jr MoveAliens ; ready to update position MoveIRight: ld a,(invader_x) ; get their x position cp 40 ; are they at the right edge? jp nz,MoveAliens ; if not, update their position ld d,-1 ; we're at edge, let's move left MoveAliens: ld a,d ; get their new direction ld (invader_dir),a ; save their new direction ld a,(invader_x) ; get their x position add a,d ; update postion by adding current direction ld (invader_x),a ; save new position DrawAliens: ld ix,invader ; load pointer to invaders ld hl,SP_Enemy1 ; load pointer to their sprites ld b,5 ; there are 5 rows of invaders ld a,(invader_y) ; get their y position ld e,a ; load it for drawing DrawYL: push bc ; save alien y counter ld b,6 ; their are 6 aliens in a row ld a,(invader_x) ; get their x position ld d,a ; load it for drawing DrawXL: ld a,(ix+0) ; get the value at this alien cp 0 ; is the alien dead? jp z,DrawASkip ; if so, we don't want to draw it DrawCheckA: push bc ; save loop counter push hl ; save bullet pointer push de ; save draw location ld b,5 ; player has 5 bullets ld hl,p_shots ; pointer to player's shots DrawCheckAL: push bc ; save loop counter ld a,(hl) ; load shot's X coord sub d ; calc distance from player's X coord jr nc,DrawCheckANoCarryX ; if not negative, skip abs code ld b,a ; save negative value xor a ; set A equal to zero sub b ; make value positive DrawCheckANoCarryX: cp 8 ; are they closer than 8 pixels? jr nc,DrawCheckASkip ; if not, we're done inc hl ; move to shot's Y coord ld a,(hl) ; load shot's Y coord sub e ; calc distance from player's Y coord jr nc,DrawCheckANoCarryY ; if not negative, skip abs code ld b,a ; save negative value xor a ; set A equal to zero sub b ; make value positive DrawCheckANoCarryY: cp 8 ; are they closer than 8 pixels? jr nc,DrawCheckASkip ; if not, we're done ld a,$ff ; $FF means shot is unused dec hl ; fill shot's X value ld b,(hl) ; get shot's X value for erasing ld (hl),a ; this shot's done inc hl ; go back to shot's Y value ld c,(hl) ; get shot's Y value for erasing push hl ; save shot pointer ld hl,SP_Blank ; want to erase the shot call PutSprite ; draw the blank sprite pop hl ; restore shot pointer ld a,(ix+0) ; get invader's life dec a ; drop it by 1 cp 0 ; did we kill them? ld (ix+0),a ; save life jr nz,DrawCheckASkip ; only do killed stuff if life is at zero push hl ; save pointer call UpdateScore ; score the hit pop hl ; restore pointer ld a,(i_left) ; load aliens left counter dec a ; drop counter by 1 ld (i_left),a ; set counter DrawCheckASkip: inc hl ; move to next bullet's x coord pop bc ; restore loop counter djnz DrawCheckAL ; check all bullets pop de ; restore draw location pop hl ; restore bullet pointer pop bc ; restore loop counter push bc ; save alien x counter push de ; save drawing position push hl ; save sprite pointer ld hl,invader_dir ; find out which direction they're going ld a,d ; get their x position sub (hl) ; calc position before we moved them ld b,a ; save x position for drawing ld c,e ; save y position for drawing ld a,(invader_dir) ; get their direction again cp 1 ; see if they're going right jr nz,DrawANoDrop ; if not, then they didn't drop--drops on left side ld a,(invader_x) ; get their x position again cp 1 ; check if they're one position over jr nz,DrawANoDrop ; if not, we didn't drop them dec c ; we dropped them, draw blank up one pixel DrawANoDrop: ld hl,SP_Blank ; load in a blank sprite call PutSprite ; draw the blank pop hl ; restore sprite pointer pop de ; restore saved draw location pop bc ; restore draw location ld a,(ix+0) ; get life--might have been hit cp 0 ; did player just kill him? jp z,DrawASkip ; if so, don't draw--but we needed to erase it DrawCheckBottom: ld a,56 ; check if they are at screen bottom cp e ; do the check jr nc,DrawPlayerOK ; if they not, player isn't dead pop hl ; clear top of stack jp PlayerKilled ; kill the player DrawPlayerOK: ld a,r ; get a not-very-random number (memory refresh counter) cp 40 ; is it our number? jr nz,DrawNoFire ; if not, we're not going to fire push bc ; save loop counters push hl ; save pointer to alien's sprites DrawFire: ld hl,i_shots ; load in the invader's shots ld b,10 ; we need to check all 10 shots DrawCheckFire: ld a,(hl) ; load in the shot's x value cp $ff ; is it empty? jr z,DrawFireCDone ; then we're done inc hl ; add one to HL inc hl ; add another to HL (HL += 2) djnz DrawCheckFire ; check another shot jp DrawNoFreeShots ; if we're here, there are no free shots--can't fire DrawFireCDone: ld (hl),d ; set shot's x value inc hl ; move to shot's y value ld (hl),e ; set shot's y value DrawNoFreeShots: pop hl ; restore pointer to alien's sprites pop bc ; restore loop counters DrawNoFire: push bc ; save all regs before drawing push de ; still saving push hl ; once more, please ld b,d ; put x coord in for drawing ld c,e ; put y coord in for drawing call PutSprite ; draw the alien pop hl ; restore sprite pointer pop de ; restore saved draw location pop bc ; restore loop counter DrawASkip: ld a,d ; load in their x coord add a,9 ; next alien is 9 pixels right ld d,a ; save the coord inc ix ; move to next alien dec b ; ------------------------\ loop is too big ld a,b ; \ to use relative cp 0 ; / jump instruction jp nz,DrawXL ; loop through entire row / "djnz DrawXL" ld bc,$8 ; a sprite is 8 bytes long add hl,bc ; move to the next sprite pop bc ; restore loop counter ld a,e ; get their y coord add a,8 ; next alien is 8 pixels down ld e,a ; save the coord dec b ; ----------------------\ loop is too big ld a,b ; \ to use relative cp 0 ; / jump instruction jp nz,DrawYL ; loop through all rows / "djnz DrawYL" UpdatePShots: ld b,5 ; we want to draw all 5 shots ld hl,p_shots ; load in the shots UpdatePLoop: push bc ; save loop counter ld b,(hl) ; load in it's x value ld a,b ; load in the x value for check cp $ff ; check if the shot's active jr z,UpdatePSkip ; if not, skip it inc hl ; move to y value ld c,(hl) ; load in it's y value ld a,c ; get the shot's y value sub 3 ; move shot up by 3 ld (hl),a ; set shot y value cp $80 ; did it go past the top? jr nc,UpdatePExpired ; then kill it inc hl ; move to next shot's x value push hl ; save shot pointer push bc ; save shot's location ld hl,SP_Blank ; want to erase the shot ld a,c ; load in the shot's y value add a,3 ; erase 3 pixels down ld c,a ; set y draw value call PutSprite ; draw the blank sprite pop bc ; restore shot's location ld hl,SP_PShot ; load pointer to sprite of shot call PutSprite ; draw the shot pop hl ; restore shot pointer jr UpdatePNoSkip ; don't want to kill the shot UpdatePExpired: dec hl ; move back to x value ld a,$ff ; load in clear value ld (hl),a ; set shot to cleared push hl ; save shot pointer ld hl,SP_Blank ; want to erase the shot ld c,0 ; erase at top of screen call PutSprite ; draw the blank sprite pop hl ; restore shot pointer UpdatePSkip: inc hl ; skip the shot's x value inc hl ; skip the shot's y value UpdatePNoSkip: pop bc ; restore loop counter djnz UpdatePLoop ; loop for all shots UpdateIShots: ld a,(shot_move) ; load in invader shot move timer cp 0 ; is it at 0? jr z,UpdateTrue ; if it is, time to update xor a ; set a to 0 ld (shot_move),a ; move shots next frame jp LoopDone ; skip updating this time UpdateTrue: ld a,1 ; we don't want to be updated next frame ld (shot_move),a ; set shot move timer ld b,10 ; we want to draw all 10 shots ld hl,i_shots ; load in the shots UpdateILoop: push bc ; save loop counter ld b,(hl) ; load in it's x value ld a,b ; load in the x value for check cp $ff ; check if the shot's active jr z,UpdateISkip ; if not, skip it inc hl ; move to y value ld a,(hl) ; load in it's y value inc a ; move shot down one ld (hl),a ; set shot y value ld c,a ; load in the value for sprite drawing UpdateICheck: cp 49 ; see if it's at the player's height jr c,UpdateIDraw ; if not, no collision ld a,(player) ; get player's x value sub b ; calc distance between player and shot's x value jr nc,UpdateINoABS ; if not negative, skip abs code ld d,a ; save negative value xor a ; set A equal to zero sub d ; make value positive UpdateINoABS: cp 8 ; are they closer than 8 pixels? jr nc,UpdateIDraw ; if not, we're done here pop hl ; clear top of stack ld a,(level) ; get the current level dec a ; it will be incremented by NewLevel ld (level),a ; set new level jp PlayerKilled ; if we're still here, we collided UpdateIDraw: ld a,c ; get the shot's y value for compare cp 56 ; did it go past the bottom jr nc,UpdateIExpired ; then kill it inc hl ; move to next shot's x value push hl ; save shot pointer ld hl,SP_IShot ; load pointer to sprite of shot call PutSprite ; draw the shot pop hl ; restore shot pointer jr UpdateINoSkip ; don't want to kill the shot UpdateIExpired: dec hl ; move back to x value ld a,$ff ; load in clear value ld (hl),a ; set shot to cleared push hl ; save shot pointer ld hl,SP_Blank ; want to erase the shot ld c,56 ; erase at bottom of screen call PutSprite ; draw the blank sprite pop hl ; restore shot pointer UpdateISkip: inc hl ; skip the shot's x value inc hl ; skip the shot's y value UpdateINoSkip: pop bc ; restore loop counter djnz UpdateILoop ; loop for all shots LoopDone: call DrawScore ; print our score jp MainLoop ; time to do the next frame GameOver: call _clrLCD ; clear the screen ld hl,$0603 ; put text at 4,7 ld (_curRow),hl ; set new text position ld hl,T_gameover ; tell them the game's over call _puts ; print the text ld hl,$0505 ; put text at 6,6 ld (_curRow),hl ; set new text position ld hl,T_score ; load text "score" to print call _puts ; print the text ld hl,$0b05 ; put text at 6,11 ld (_curRow),hl ; set new text position ld hl,(score) ; load the score to print xor a ; clear high byte call _dispAHL ; print the number call Pause ; wait for a key ExitGame: set appTextSave,(iy+appflags) ; we want _textShadow to be cleared with _clrScrn call _clrScrn ; clear the screen before exit (including _textShadow) ret ; return to TI-OS or a shell UpdateScore: ld hl,(score) ; get the current score ld de,25 ; add 25 to it add hl,de ; do the add ld (score),hl ; store the score ret PlayerKilled: ld a,(lives) ; get the player's lives dec a ; make them lose a life ld (lives),a ; set new lives call _clrLCD ; clear the screen ld hl,$0603 ; place text at 4,7 ld (_curRow),hl ; set text position ld hl,T_died ; tell user they are dead call _puts ; print the string call Pause ; wait, so they can see the message ld a,(lives) ; does player have lives left? cp 0 ; check if lives are at 0 jp z,GameOver ; if they are, game is over call NewLevel ; restart the level--note: advances level by one jp GetInput ; ready for top of main loop ;------------------------------------------------------------------------- Reset: ClearIShots: ld b,10 ; run loop for all 10 shots ld a,$ff ; $FF will stand for an unused shot ld hl,i_shots ; hl points to the start of the invader's shots ClrILoop: ld (hl),a ; clear out the shot by putting $FF into it inc hl ; move to the next shot's X value inc hl ; easier than loading into a 16-bit reg, then adding djnz ClrILoop ; loop through all the shots ClearPShots: ld b,5 ; run loop for all 5 shots ld a,$ff ; $FF will stand for an unused shot ld hl,p_shots ; hl points to the start of the player's shots ClrPLoop: ld (hl),a ; clear out the shot by putting $FF into it inc hl ; move to the next shot's X value inc hl ; easier than loading into a 16-bit reg, then adding djnz ClrPLoop ; loop through all the shots ClearAliens: ld a,(level) ; get the level number dec a ; subtract one so level/life starts at zero sra a ; life is equal to level divided by two inc a ; add one to it--life can't be zero ld b,30 ; run loop for all 30 aliens ld hl,invader ; hl points to the start of the alien flock ClrALoop: ld (hl),a ; set the alien's life inc hl ; move to the alien djnz ClrALoop ; loop through all the aliens ret ; we're done with the Reset ;------------------------------------------------------------------------- ErasePlayer: ld a,(player) ; get player's position ld b,a ; that's the X value ld c,55 ; place at bottom row ld hl,SP_Blank ; put blank sprite call PutSprite ; draw it ret ; return DrawPlayer: ld a,(player) ; get player's position ld b,a ; that's the X value ld c,55 ; place at bottom row ld hl,SP_Player ; we want to draw the player's ship call PutSprite ; draw it ret ; return DrawStatus: ld hl,$1000 ; ***place at (1, 17) ld (_curRow),hl ; set text position ld hl,T_score ; write text "SCORE" call _puts ; draw the text ld hl,$1003 ; ***place at (4, 17) ld (_curRow),hl ; set text position ld hl,T_level ; write text "LEVEL" call _puts ; draw the text ld hl,$1004 ; ***place at (5, 17) ld (_curRow),hl ; set text position ld a,(level) ; load the level to print ld l,a ; move to low byte ld h,0 ; clear the middle byte xor a ; clear high byte call _dispAHL ; print the number ld hl,$1006 ; ***place at (7, 17) ld (_curRow),hl ; set text position ld hl,T_lives ; write text "LIVES" call _puts ; draw the text ld hl,$1007 ; ***place at (8, 17) ld (_curRow),hl ; set text position ld a,(lives) ; load the lives to print ld l,a ; move to low byte ld h,0 ; clear the middle byte xor a ; clear high byte res appAutoScroll,(iy+appflags) ; turn off auto scrolling of text call _dispAHL ; print the number set appAutoScroll,(iy+appflags) ; turn it back on (so TI-OS isn't screwed) DrawScore: ld hl,$1001 ; place at (2, 17) ld (_curRow),hl ; set text position ld hl,(score) ; load the score to print xor a ; clear high byte call _dispAHL ; print the number ret ; we're done here ;------------------------------------------------------------------------- NewLevel: ld a,30 ; there are 30 aliens ld (i_left),a ; set aliens left counter ld a,44 ; start player in center ld (player),a ; set player position xor a ; start invaders in upper left (a = 0) ld (invader_x),a ; set invader x ld (invader_y),a ; set invader y ld (fired),a ; let them fire ld a,1 ; start invaders moving right ld (invader_dir),a ; set invader direction ld a,(level) ; load the level number inc a ; up it by 1 ld (level),a ; set level number call Reset ; setup for the level ShowLevelScrn: call _clrLCD ; clear the display screen ld hl,$0102 ; place at (3, 2) ld (_curRow),hl ; set text position ld hl,T_enterlevel ; load the text to print call _puts ; print the text ld hl,$0704 ; place at (5, 8) ld (_curRow),hl ; set text position ld a,(level) ; load the level to print ld l,a ; move to low byte ld h,0 ; clear the middle byte xor a ; clear high byte call _dispAHL ; print the number ld hl,$0904 ; place at (5, 10) ld (_curRow),hl ; set text position ld a,'<' ; load char to print call _putc ; print the char ld hl,$0c04 ; place at (5, 13) ld (_curRow),hl ; set text position ld a,'>' ; load char to print call _putc ; print the char ld hl,$0507 ; place at (8, 6) ld (_curRow),hl ; set text position ld hl,T_pressenter ; load the text to print call _puts ; print the text call Pause ; wait for enter InitScreen: call _clrLCD ; clear the display screen...again call DrawStatus ; print text information call DrawPlayer ; draw the player the first time ret ; NewLevel is done ;------------------------------------------------------------------------- ; ASCR - Advanced Sprite Clipping Routines ; ; by Jimmy M†rdell 970304 Last update 970803 ; ; modified by David Phillips to remove clipping ; faster without clipping--the only routines that worked with this game ; PutSprite: ; BC = x,y HL = sprite push bc push de push hl push ix push hl pop ix ld a,8 push af call FIND_PIXEL Modify_6: ld de,$FC00 add hl,de ld d,a pop bc PSC_PutRow: push bc push hl ld a,$ff ld e,a ld b,8 ld c,(ix) inc ix PSC_PutCol: push bc rlc e jr nc,PSC_NextBit ld a,d rlc c jr c,PSC_BitOn cpl and (hl) ld (hl),a jr PSC_NextBit PSC_BitOn: or (hl) ld (hl),a PSC_NextBit: rrc d jr nc,PSC_SSB inc hl PSC_SSB: pop bc rlc c djnz PSC_PutCol pop hl ld bc,16 add hl,bc pop bc djnz PSC_PutRow EndPS: pop ix pop hl pop de pop bc ret FIND_PIXEL: push bc push de ld hl,ExpTable+1 ld d,0 ld a,b and $07 ld e,a add hl,de ld e,(hl) ld h,d srl b srl b srl b ld a,c add a,a add a,a ld l,a add hl,hl add hl,hl ld a,e ld e,b add hl,de pop de pop bc ret ExpTable: .db $01,$80,$40,$20,$10,$08,$04,$02,$01 ;------------------------------------------------------------------------- Pause: call _getky ; wait for a key to be pressed cp K_ENTER ; was it enter? jr z,PauseDone ; if it was, we're done cp K_MORE ; maybe it was more? jr z,PauseDone ; if so, we're done here also cp K_EXIT ; or was it exit? jr nz,Pause ; if it wasn't, wait for another key press PauseDone: ret ; return, since it had to be ;------------------------------------------------------------------------- SP_Blank: .db %00000000 .db %00000000 .db %00000000 .db %00000000 .db %00000000 .db %00000000 .db %00000000 .db %00000000 SP_Player: .db %00000000 .db %00000000 .db %00010000 .db %00111000 .db %10111010 .db %11111110 .db %10111010 .db %10000010 SP_Enemy1: .db %10000001 .db %10000001 .db %10111101 .db %11000011 .db %11111111 .db %01111110 .db %00111100 .db %00000000 SP_Enemy2: .db %00000000 .db %10000001 .db %11000011 .db %10111101 .db %10111101 .db %11011011 .db %10011001 .db %00000000 SP_Enemy3: .db %00000100 .db %01000010 .db %10100100 .db %00011000 .db %00011000 .db %00100101 .db %01000010 .db %00100000 SP_Enemy4: .db %00000000 .db %01000010 .db %10000001 .db %01000010 .db %00111100 .db %00111100 .db %00011000 .db %00000000 SP_Enemy5: .db %10000010 .db %10111010 .db %11010110 .db %10101010 .db %01010100 .db %00101000 .db %00010000 .db %00000000 SP_PShot: .db %00010000 .db %00010000 .db %01010100 .db %01010100 .db %01010100 .db %00000000 .db %00000000 .db %00000000 SP_IShot: .db %00000000 .db %00000000 .db %00000000 .db %00000000 .db %00000000 .db %00000101 .db %00000010 .db %00000101 T_score: .db "SCORE",0 T_level: .db "LEVEL",0 T_lives: .db "LIVES",0 T_enterlevel: .db "-=Entering level=-",0 T_pressenter: .db "[Hit Enter]",0 T_died: .db "You died!",0 T_gameover: .db "GAME OVER",0 titletext: .db " Space Invaders v1.0 ", .db " ", .db " by David Phillips ", .db " electrum@tfs.net ", .db " ", .db " Copyright 1998 ", .db " ", .db " [Ready?]",0 .end