; Phantom Star ; by ; Joe Pemberton ; ; This source is public domain. Feel free to modify it or use it however you ; like, but please credit me. ; .nolist #define randData cmdshad+80+(3*8) ;#define DEBUG ;#define QUEUEDEBUG ;#define VTI_VERSION #define equ .EQU #define EQU .EQU #define end .END #define END .END #include "ion.inc" #include "keys.inc" #include "levelequ.z80" #ifdef TI83P #define myRandom Random INTERRUPT_TABLE equ $9900 INTERRUPT_TABLE_BYTE equ $99 INTERRUPT_VECTOR equ $9a9a INTERRUPT_VECTOR_BYTE equ $9a #else #define myRandom ionRandom INTERRUPT_TABLE equ $8300 INTERRUPT_TABLE_BYTE equ $83 INTERRUPT_VECTOR equ $8484 INTERRUPT_VECTOR_BYTE equ $84 #endif #define tempbuf INTERRUPT_VECTOR+interrupt_end-interrupt_start #define tempbuf2 tempbuf + (5 * 12) KD_MASK equ %11110000 NUM_SHIP_COORD_BUFS equ 6 ; must be the same as at the bottom of this file POWERUP_RATE equ 4 ;1 powerup per every ~4 ships NUM_WEAPONS equ 6 ; a max of 7 weapon powerups (NUM_WEAPONS+1) DEATH_LENGTH equ 14 ; INVICIBILITY_LENGTH equ 10 ;10 frames of invincibility GAME_OVER_LENGTH equ 210 ;end of the game lasts 210 frames DEATH_PTS equ 100 ;lose 100 points when you die BOSS_POINTS equ 50 ;gain 50 points for killing a boss POWERUP_LENGTH equ 65 ;powerups last 65 frames before disapearing Q_UPDATE_SHOTS equ 0 Q_UPDATE_SHIP equ 1 Q_DO_CONTRAST equ 2 Q_UPDATE_ENEMIES equ 3 Q_UPDATE_STARS equ 4 Q_UPDATE_HEALTH equ 5 Q_ENEMY_SHOOT equ 6 Q_UPDATE_ENEMY_SHOTS equ 7 Q_DO_LEVEL equ 8 Q_UPDATE_SCORE equ 9 Q_MISC equ 10 Q_UPDATE_TIMER equ 11 QUEUE_SIZE equ 50 ; how many slots for events there are QUEUE_LENGTH equ 12 ; how many events we have #ifdef VTI_VERSION VTI_VERSION_DIV equ 2 #else VTI_VERSION_DIV equ 1 #endif QDEFAULT_UPDATE_SHOTS equ 9 / VTI_VERSION_DIV QDEFAULT_UPDATE_SHIP equ 11 / VTI_VERSION_DIV QDEFAULT_DO_CONTRAST equ 62 / VTI_VERSION_DIV QDEFAULT_UPDATE_ENEMIES equ 29 / VTI_VERSION_DIV QDEFAULT_UPDATE_STARS equ 13 / VTI_VERSION_DIV QDEFAULT_UPDATE_HEALTH equ 7 / VTI_VERSION_DIV QDEFAULT_ENEMY_SHOOT equ 60 / VTI_VERSION_DIV QDEFAULT_UPDATE_ENEMY_SHOTS equ 21 / VTI_VERSION_DIV QDEFAULT_DO_LEVEL equ QDEFAULT_UPDATE_ENEMIES ;MUST be syncronized with update_enemies QDEFAULT_UPDATE_SCORE equ 29 / VTI_VERSION_DIV QDEFAULT_MISC equ 20 / VTI_VERSION_DIV QUPDATE_TIMER equ 140 / VTI_VERSION_DIV DefaultXSpriteHeight equ 8 SCREEN_POINTER equ gbuf SCRNBUF_HEIGHT equ 96 ;in theory, you should be able to change this equate ;to any 1 byte value greater than VISIBLE_HEIGHT you ;want and have the game respond with a different sized ;screen. However, it should stay at 96 because external ;levels will expect it to be 96. SCRNBUF_SIZE equ SCRNBUF_HEIGHT*12 VISIBLE_HEIGHT equ 64-8 OFFSCREEN_POINTER equ SCREEN_POINTER+(VISIBLE_HEIGHT*12) OFFSCREEN_STRING equ OFFSCREEN_POINTER+12 #define BAR_BUFFER barspr ;gbuf+(12*VISIBLE_HEIGHT) #define SHIELD_LOCATION BAR_BUFFER+(12*1)+1 #define HEALTH_LOCATION BAR_BUFFER+(12*6)+1 STAR_RATE equ 8 MAX_STARS equ 18 ; x, size, y STAR_SIZE equ 3 ;stars are 3 bytes long STAR_TABLE_SIZE equ MAX_STARS*STAR_SIZE MAX_SHOTS equ 30 SHOT_SIZE equ 8 ;timer(1), pathptr(2), x(1), y(1), spriteptr(2), damage(1) SHOT_TABLE_SIZE equ MAX_SHOTS*SHOT_SIZE ; a quick note about the enemies: I use bit 7 of the flag ; to determine whether or not the enemy is an explosion. ; I use bit 6 to determine if the enemy is a boss enemy or ; not (bosses are worth more points) bits 0-3 are for the explosion ; counter, IF and ONLY IF bit 7 is set. ; bit 5 is for whether or not it is a powerup. IF and ONLY IF bit ; 5 is set, then bits 0-3 are for the type of powerup. ;bit 7: ; 0 = normal enemy ; 1 = explosion ;bit 6: ; 0 = normal enemy ; 1 = boss ;bit 5: ; 0 = normal enemy ; 1 = powerup ; ; if the flag ever reaches %1____000, the enemy is dead for ; good, and must be removed. ENEMY_SIZE equ 19 ;flag(1), health(1), startpathcnt(1), startpathptr(2), pathcnt(1), pathcntmax(1), pathptr(2) ;x(1), y(1), height(1), spritecnt(1), spriteptr(2), shottimermax(1), shottimer(1), shotroutineptr(2) MAX_ENEMIES equ 30 ENEMY_TABLE_SIZE equ MAX_ENEMIES*ENEMY_SIZE MAX_ENEMY_SHOTS equ 20 ENEMY_SHOT_SIZE equ 12 ;timer(1), xinc(2), x(2), yinc(2), y(2), spriteptr(2), damage(1) ENEMY_SHOT_TABLE_SIZE equ MAX_ENEMY_SHOTS*ENEMY_SHOT_SIZE SHIP_EXPLODE_DAMAGE equ 6 ;how much damage your ship is dealt ;when you crash into an enemy ENEMY_EXPLODE_DAMAGE equ 15 ;how much damage the enemy is dealt ;when you crash into it SHIP_BOUNDS equ 80+8 KDB_DOWN equ 0 KD_DOWN equ %11111110 KDB_LEFT equ 1 KD_LEFT equ %11111101 KDB_RIGHT equ 2 KD_RIGHT equ %11111011 KDB_UP equ 3 KD_UP equ %11110111 KDB_2ND equ 4 KD_2ND equ %11101111 KDB_MODE equ 5 KD_MODE equ %11011111 KDB_CLEAR equ 6 KD_CLEAR equ %10111111 KDB_ENTER equ 7 KD_ENTER equ %01111111 .list #ifdef TI83P .org ProgStart-2 .db $BB,$6D #else .org ProgStart #endif ret jr nc,begin .db "Phantom Star 0.81b" save: .db 0 ;$20 = do not initialize mem, just keep going .db "- saved",0 begin: ld a,(contrast) ;get current contrast add a,$18+$c0 ;make it usable.. ld (gamecont),a ;save it for later use set 7,(iy+20) ;write to the buffer start: ld a,(save) or a jr nz,start2 ld (curlevel),a start2: call setupint ld a,(save) or a jp nz,skipload ;don't initialiZe the memory, just go to the game bcall(_grbufclr) ; this code is set up to take a string, display it to the graphbuffer via ; _vputs, and then save the 5*12 bitmap to a temporary buffer to be displayed ; later and to conserve cpu time. ld hl,(3*256)+32 ; ld (pencol),hl ; ld hl,press2ndtxt ; bcall(_vputs) ld hl,(15*256)+11 ld (pencol),hl ld hl,contrasttxt bcall(_vputs) ld hl,(21*256)+13 ld (pencol),hl ld hl,inverttxt bcall(_vputs) ld hl,(33*256)+20 ; ld (pencol),hl ; ld hl,authortxt ; bcall(_vputs) ld hl,(39*256)+34 ; ld (pencol),hl ; ld hl,copyrighttxt bcall(_vputs) ld de,textbuffer ;copy the bitmap to textbuffer ld hl,gbuf ld bc,(8*6)*12 ldir bcall(_grbufclr) ld hl,var_defaults ld de,game_vars ld bc,var_defaults_end-var_defaults-1 ldir ;set up the default values for the game's vars ld hl,queuedefaults ld de,queuecounters ld bc,QUEUE_LENGTH ldir ;set up the defaults for the queue ld hl,level_var ld de,level_var+1 ld bc,level_var_end-level_var-1 ld (hl),0 ldir ;clear the level vars ld hl,tempenemy ld de,tempenemy+1 ld bc,ENEMY_SIZE-1 ld (hl),0 ldir ld hl,(progptr) ld ix,progstr ld b,0 countlevels: inc b push bc call findlevel ;find a level pop bc ex de,hl ;hl->next level jr c,countlevels nomorelevels: ld a,b ;a = how many levels ld (numlevels),a ;save the number of levels found call _L_END ;start 1st level ld b,96 loadloop: push bc call update_stars ;make the stars cover the screen pop bc djnz loadloop call draw_health_shields call display_score call display_lives call barFastCopy titlelevel: call clearoffscreen ld hl,myleveldesc push hl ld a,(curlevel) ;a = curlevel or a jr z,use_builtin_level pop hl ld hl,(progptr) ld ix,progstr titlelevelL: push af ; call findlevel ;go to next level ex de,hl ;hl->next level, de->level data pop af ; dec a ;decrement the counter jr nz,titlelevelL ;if we didn't just look at the right level, continue searching titleLevelFound: ex de,hl ;hl->level data (description) push hl ; push hl ;save it twice ld h,b ld l,c ;hl->VAT entry ld a,(hl) ;a = type ld (savename),a ;save it ld de,savename+1 #ifdef TI83P ld bc,-6 #else ld bc,-3 #endif add hl,bc ;(hl) = name length ld b,(hl) dec hl namecopyloop: ld a,(hl) ;copy the name to (savename) ld (de),a ; dec hl ; inc de ; djnz namecopyloop ; xor a ld (de),a ;finish it with a zero pop hl ;hl->description use_builtin_level: ld bc,-(progstr_end-progstr)+1 add hl,bc ;move hl back to the start of the data call calculatelevelpointers ld (levelpointer),hl ld (leveloffset),de ld hl,(56*256)+5 ; ld (pencol),hl ; ld hl,selectleftstr ; bcall(_vputs) ;display "< " pop hl ;hl->description bcall(_vputs) ;display the description push hl ;save the pointer to the highscore table ld hl,selectrightstr ; bcall(_vputs) ;display " >" ld de,10 ld (pencol),de ld a,(select_high) add a,$31 ;fix the number ld hl,highscorenumtxt ld (hl),a bcall(_vputs) pop hl ;hl->highscore table ld a,(select_high) add a,a ;*2 ld b,a ; add a,a ;*4 add a,a ;*8 add a,b ;*10 ld e,a ld d,0 ;de = select_high * 10 add hl,de ld e,(hl) inc hl ld d,(hl) ;de = highscore push de inc hl ;hl->highscore name ld de,op1 ld bc,8 ldir xor a ld (de),a ;zero terminate it ld hl,op1 bcall(_vputs) ;display the name ld hl,highscoretxt ;display " - " bcall(_vputs) pop de ;de = highscore call bintobcd16 ;(bcd16) = unformatted number string ld hl,op1 push hl ld de,bcd16 ld b,5 fixbcd16: ld a,(de) add a,$30 ld (hl),a inc hl inc de djnz fixbcd16 ld (hl),0 ;zero-terminate it pop hl ;hl -> string bcall(_vputs) ld hl,plotsscreen+12 push hl ld de,tempbuf2 ld bc,5*12 push bc ldir pop bc pop hl ld de,plotsscreen+12+1 ld (hl),0 ldir titleloop: ei ld hl,titlespr ld de,SCREEN_POINTER+(12*4) ld bc,10 ld a,8 titledrawloop: inc de push bc ldir pop bc inc de dec a jr nz,titledrawloop ld hl,OFFSCREEN_STRING ;offscreen_string is where the level desc. is stored ld de,SCREEN_POINTER+(12*40) ld bc,5*12 ldir ld hl,tempbuf2 ;tempbuf2 is where the highscore name is stored ld de,SCREEN_POINTER+(12*47) ld bc,5*12 ldir ld a,(curqueue) or a jr z,titlenoqueue ld b,a ;b=number of items in the queue titlequeueloop: push bc ;save the counter ld hl,titlequeueret ; push hl ;set up the return address for the routines di ; ld de,queue ; ld a,(de) ;get the event ld hl,queue+1 ; ld bc,(curqueue) ;c = curqueue (always > 0) ld b,0 ;bc = how many bytes to move back ldir ld hl,curqueue dec (hl) ei cp Q_UPDATE_STARS jp z,update_stars cp Q_DO_CONTRAST jp z,do_contrast cp Q_MISC jp z,misc pop de titlequeueret: pop bc djnz titlequeueloop titlenoqueue: call display_stars ld a,(myfastcopyinverse) ld (scrollInverse),a ld hl,SCREEN_POINTER+(12*22) ld de,SCREEN_POINTER+(12*22)+1 ld bc,11 ld (hl),$ff ldir ld hl,textbuffer ld de,SCREEN_POINTER+(12*23) ld bc,12*12 scrollTextCopyL: ld a,(hl) scrollInverse: cpl ld (de),a inc hl inc de dec bc ld a,b or c jr nz,scrollTextCopyL ld hl,SCREEN_POINTER+(12*22) ld de,SCREEN_POINTER+(12*35) ld bc,12 ldir ld hl,scrollFix+1 inc (hl) scrollFix: ld a,0 and %1111 jr nz,noscrolltext ld hl,textbuffer ld de,textbuffer+((8*6)*12) ld bc,12 ldir ld hl,textbuffer+12 ld de,textbuffer ld bc,(8*6)*12 ldir noscrolltext: call myFastCopy call getkdh2 ld a,b cp KD_RIGHT jp z,NextLevel cp KD_LEFT jp z,PrevLevel cp KD_UP jp z,HighscoreUp cp KD_DOWN jp z,HighscoreDown cp KD_CLEAR jp z,quit cp KD_2ND jp nz,titleloop ld hl,(levelpointer) ld de,(levelvaroffset) add hl,de ld de,60 add hl,de ;hl->gametimer ld a,(hl) inc hl ld h,(hl) ld l,a ld (gametimer),hl jr startgame skipload: ld a,(curlevel) or a jr z,startgame ld hl,savename bcall(_mov9toop1) bcall(_chkfindsym) jp c,savednotfound ;it was deleted #ifdef TI83P ld a,b ;on the 83+, we must check the page or a jp nz,savedarchived #endif ;now de -> variable data inc de inc de ;get past the size.. ld b,d ld c,e ld ix,progstr skipload_chkname: ld a,(ix) or a jr z,skipload_goodname ld a,(bc) ;a = variable byte cp (ix) ;compare it to the string jp nz,quit ;didn't match.. quit inc bc inc ix jr skipload_chkname skipload_goodname: ld (levelpointer),de startgame: xor a ld (deadfix+1),a inc a ld (barflag),a ;mark bar as dirty call draw_health_shields call display_score main: noqueue: ei #ifdef QUEUEDEBUG ld a,DReset out (1),a ld a,Group7 out (1),a in a,(1) cp kdGraph call z,debugme #endif call getkdh ld a,b cp KD_CLEAR jp z,highscorequit cp KD_MODE jp z,pausegame bit KDB_2ND,b call z,shoot ld a,DReset out (1),a ld a,Group7 out (1),a in a,(1) ;poll for del keypress cp kdDel jp z,savequit ld a,(curqueue) ;this next part is what really drives the entire game. or a ;events are triggered by my interrupt routine and placed jr z,noqueue ;in a queue, and here is where I handle these events. ld b,a queueloop: push bc di ld de,queue ld a,(de) ld hl,queue+1 ld bc,(curqueue) ;c = curqueue (always > 0) ld b,0 ;bc = how many bytes to move back ldir ld hl,curqueue dec (hl) ei add a,a ld l,a ld h,0 ld de,mainRoutineTable add hl,de ld a,(hl) inc hl ld h,(hl) ld l,a call callhl ;faster than "ld de,retlabel \ push de \ jp (hl)" pop bc djnz queueloop ld a,(lives) ;if we have -1 lives inc a ; jp z,highscoredie ;leave the game call display_shots call display_enemy_shots call display_enemies call display_stars call display_ship ld a,(levelend) or a jr z,nolevelend ld hl,levelstring ld de,36*256+24 call putsbcd nolevelend: call myFastCopy ld hl,barflag ld a,(hl) or a jr z,bar_clean ld (hl),0 ;mark bar as not dirty call barfastcopy bar_clean: ld hl,gameover ld a,(hl) or a jp z,main dec (hl) jp nz,main inc (hl) ;increment it so that health and score can still be tallied ld a,(healthqueue) or a jp nz,main ;if the health is still being tallied.. ld hl,(scorequeue) ld a,h or l jp nz,main ;if the score is still being tallied.. jr highscoredie highscorequit: ld a,-1 ;so that you don't get points for having lots of lives ld (lives),a ;when you press clear highscoredie: im 1 ei ld a,(level_complete) ;if you already finished the entire level or a ;then.. jp nz,restart_game ;no highscore for you bcall(_clrscrf) bcall(_homeup) ld hl,scoretxt bcall(_puts) ;"Score: " push hl ld hl,(score) bcall(_disphl) bcall(_newline) pop hl bcall(_puts) ;"Bonus: " push hl ld hl,0 ;default of 0 bonus ld a,(lives) ;if we have -1 lives inc a ; jr z,highnobonus ;no bonus ld hl,(gametimer) ld b,h ld c,l ;bc = gametimer add hl,hl ;*2 add hl,hl ;*4 add hl,bc ;hl = gametimer * 5 (5 pts per second left) push hl ld hl,(lives) ld h,0 add hl,hl ;*2 add hl,hl ;*4 ld d,h ld e,l ;de = lives * 4 add hl,hl ;*8 add hl,hl ;*16 add hl,hl ;*32 ld b,h ld c,l ;bc = lives * 32 add hl,hl ;*64 add hl,bc ;*96 add hl,de ;*100 ex de,hl pop hl add hl,de ;hl = bonus + lives * 100 highnobonus: push hl bcall(_disphl) bcall(_newline) pop de ;de = bonus pop hl bcall(_puts) ;"Total: " ld hl,(score) add hl,de ;hl is the total now push hl bcall(_disphl) call waitenter ld ix,(levelvaroffset) ld de,(levelpointer) add ix,de ;ix->highscore table pop de ;de = score ld a,6 ld bc,10 ;move forward 10 bytes each time findhsL: dec a ;decrement the counter, clear the carry cp -1 jp z,display_high ld l,(ix+0) ld h,(ix+1) ;hl = highscore sbc hl,de jr c,foundhs ;if carry, score was higher than highscore add ix,bc ;look at the next highscore jr findhsL foundhs: or a jr z,foundhsnocopy ;if this is the last highscore, no need to copy push de ;save the score for later add a,a ;*2 ld b,a ; add a,a ;*4 add a,a ;*8 add a,b ;*10 ld c,a ld b,0 ;bc = bytes to copy ld hl,(levelvaroffset) ld de,(levelpointer) add hl,de ;hl->highscore table ld de,50-1 add hl,de ;hl->end of 2nd to last highscore push hl ld de,10 add hl,de ex de,hl ;de->end of last highscore pop hl ;hl->end of 2nd to last highscore lddr pop de ;de = score foundhsnocopy: push ix pop hl ;hl->highscore to write to ld (hl),e inc hl ld (hl),d ;save the new highscore inc hl ;hl->name strinput: push hl bcall(_clrscrf) bcall(_homeup) ld hl,highscoremsg set textinverse,(iy+textflags) bcall(_puts) res textinverse,(iy+textflags) pop hl ld bc,(0*256)+3 ;b = counter, c = 3 strinputloopre: ld (currow),bc ld a,' ' bcall(_putmap) strinputloop: push bc push hl bcall(_getk) pop hl pop bc or a jr z,strinputloop ;wait for a keypress cp skDel ; jr z,strback ; cp skleft jr z,strback cp skenter ; jr z,strfinish ; ld (strinwrite+1),a ld a,b cp 8 jr z,strinputloop ;have we reached the max number of letters? strinwrite: ld a,0 ;SMC cp $2F+1 jr nc,strinputloop sub $0B ;make "W" key = 0 jr c,strinputloop ld e,a ;e = key-$0B ld d,0 ld ix,charTable add ix,de ld a,(ix) or a jr z,strinputloop ld (hl),a bcall(_putc) inc hl inc b jr strinputloop strback: ld a,b or a jr z,strinputloop dec b dec hl jr strinputloopre ;loop back and write over the character strfinish: ld a,b or a jr z,strinputloop cp 8 ;if there are 8 characters, don't zero terminate jr z,strnozero ld (hl),0 ;zero terminate it strnozero: display_high: bcall(_homeup) bcall(_clrscrf) ld hl,highscoretitle set textinverse,(iy+textflags) bcall(_puts) res textinverse,(iy+textflags) ld hl,(levelpointer) ld de,(levelvaroffset) add hl,de ld b,6 writehighL: ld a,8 sub b ld e,a ld d,0 ld (currow),de push bc ld a,7+$30 sub b push hl ld hl,highscorenumtxt2 ld (hl),a bcall(_puts) pop hl ld a,(hl) inc hl push hl ld h,(hl) ld l,a bcall(_disphl) ld hl,curcol inc (hl) pop hl inc hl ;hl is past the score now ld de,op1 ld bc,8 ldir ;hl->next entry xor a ld (de),a ;zero terminate the string for the case of the 8 char string with no 0 push hl ld hl,op1 bcall(_puts) pop hl pop bc djnz writehighL call waitenter restart_game: call getkdh ld a,(lives) inc a jp z,leave_game ld (level_complete),a ;level_complete != 0 ld (barflag),a ld hl,(levelpointer) call calculatelevelpointers ld (leveloffset),de xor a ld (level),a ld (gameover),a call _L_END call setupint jp main NextLevel: ld a,(numlevels) ld hl,curlevel inc (hl) cp (hl) jp nz,titlelevel ld (hl),0 nextlevelsk: jp titlelevel PrevLevel: ld hl,curlevel ld a,(hl) or a jr nz,prevlevelsk ld a,(numlevels) ld (hl),a prevlevelsk: dec (hl) jp titlelevel HighscoreUp: ld hl,select_high ld a,(hl) or a jp z,titleLoop dec (hl) jp titlelevel HighscoreDown: ld hl,select_high ld a,(hl) cp 5 jp z,titleLoop inc (hl) jp titlelevel waitenter: im 1 ei bcall(_getk) waiteL: bcall(_getk) cp skclear ret z cp skenter jr nz,waiteL ret ;=================== quit stuff =================== #ifdef TI83P savedarchived: im 1 bcall(_homeup) ld hl,savedarchivedtxt bcall(_puts) push hl ld hl,savename+1 bcall(_puts) bcall(_newline) pop hl bcall(_puts) savedarchivedL: bcall(_getcsc) cp skLog jr z,savequit cp sk1 jr nz,savedarchivedL savedarchivedY: xor a ld (save),a jp start #endif savednotfound: im 1 bcall(_homeup) ld hl,savednotfoundtxt bcall(_puts) push hl ld hl,savename+1 bcall(_puts) bcall(_newline) pop hl bcall(_puts) savednotfoundL: bcall(_getcsc) cp skenter jr nz,savednotfoundL jp start savequit: ld a,' ' ld (save),a jr quit2 quit: xor a ld (save),a quit2: ld a,(gamecont) sub $18+$c0 ld (contrast),a im 1 res 7,(iy+20) ;write to the LCD ret pausegame: ld hl,0 ld (currow),hl ld hl,pausetxt bcall(_puts) pausel: ld a,Dreset out (1),a ld a,Group2 out (1),a in a,(1) bit 0,a jr z,pausel pauser: ld a,Dreset out (1),a ld a,Group2 out (1),a in a,(1) cp kdEnter jr nz,pauser jp main pausetxt: .db " Paused ",0 leave_game: xor a ld (save),a jp start2 #ifdef QUEUEDEBUG debugme: di bcall(_homeup) ld hl,(gametimer) bcall(_disphl) debugl: ld a,Dreset out (1),a ld a,Group7 out (1),a in a,(1) cp kdGraph jr z,debugl ei ret #endif ;hl->start of variable data (after size bytes) calculatelevelpointers: push hl ld bc,255 xor a ld de,0 leveldescL: inc de cpi jr nz,leveldescL ld (levelvaroffset),de ex de,hl ;hl->highscores ld de,60+2 ;get past highscores, timer add hl,de ex de,hl ;de->game data pop hl ret ;============= misc. stuff ================ misc: ld hl,xtonflag ld a,DReset out (1),a ld a,Group5 out (1),a in a,(1) bit 7,a ;poll for XTON key press jr nz,misc_no_xton ld a,(hl) ;if XTON is still pressed.. or a ; ret nz ;return inc (hl) ; jp inverse_screen ;else, inverse the screen misc_no_xton: ld (hl),0 ;XTON not pressed.. reset the flag ret update_timer: ld hl,(gametimer) ld a,h or l ret z dec hl ld (gametimer),hl ret ;============= contrast stuff ============= do_contrast: ld a,DRESET out (1),a ld a,Group2 out (1),a in a,(1) cp kdPLUS jr z,contup cp kdMinus ret nz contdown: ld hl,gamecont ld a,(hl) cp $e0 ret z dec (hl) dec a out ($10),a ;update the contrast ret contup: ld hl,gamecont ld a,(hl) cp $ff ret z inc (hl) inc a out ($10),a ;update the contrast ret ;============= star routines ============== ; x, size, y update_stars: ld a,r and %111 call z,putstar ld b,MAX_STARS ld hl,startable ustarL: ld a,(hl) or a jr z,ustarS inc hl ld c,(hl) sub c dec a dec hl ld (hl),a ustarS: inc hl inc hl inc hl djnz ustarL ret display_stars: ld b,MAX_STARS ld hl,startable dstarL: ld a,(hl) or a jr z,dstars push bc ;bc = counter push hl ;hl->star inc hl ;(hl) = size ld b,(hl) ; ld e,b ;e=size inc b ;b=size for ionputsprite srl a ;a=xcoord/2 ld d,a ;save xcoord inc hl ;(hl) = ycoord ld a,(hl) ;a=ycoord ld hl,screencoord ;(hl)=screen coord bit 0,e ;if bit zero of the size is set jr nz,dstarB ;its a big one; skip it ld c,(hl) ; srl c ;c=screen coord/2 add a,c ; dstarB: sub (hl) ;just to make sure we dont jr c,dstarj ; cp VISIBLE_HEIGHT-1 ; jr nc,dstarj ;write off the end of our buffer :) ld l,a ;l=ycoord ld a,d ;a=xcoord ld d,0 ;de=size sla e ;de=size*2 ld ix,starspr ; add ix,de ; call putSpriteOr ; dstarj: pop hl pop bc dstars: inc hl inc hl inc hl djnz dstarL ret putstar: ld hl,startable ;hl -> startable ld b,MAX_STARS ;b = how many times to try pstarL: ld a,(hl) ;a = star x coord or a ;if it is zero, jr z,pstarg ;put the star here inc hl ; inc hl ;skip star data inc hl ; djnz pstarL ;loop back ret ;return; all the star slots were full pstarg: ld (hl),96*2-2 ;(hl) = x coord <--THIS MUST BE EVEN! inc hl ;(hl) = size ld a,r ;for our purposes, this is random enough (and fast enough) and %00000001 ld (hl),a ; inc hl ;(hl) = y coord ld b,SCRNBUF_HEIGHT-2 ; call myRandom ;get a random y coord ld (hl),a ret ;================= ship routines ====================== update_ship: ld hl,dead ld a,(hl) or a jr nz,alreadydead ;already dead, update the counters ld a,(health) ;a=health or a ; jr nz,deadfinish ;not dead yet ld (leftfriend),a ;kill your friend ld a,INVICIBILITY_LENGTH ld (invincible),a ;make you invincible for a bit ld a,15+DEATH_LENGTH ld (dead),a ld a,edone-deadfix-2 ld (deadfix+1),a ;SMC to skip the collision detection when you're dead ld (barflag),a ld hl,(score) ld de,-DEATH_PTS ;lose DEATH_PTS points when you die add hl,de jr c,die_nohigh ld hl,0 die_nohigh: ld (score),hl call display_score ld a,(weapon) srl a adc a,0 ret z ld b,a die_loop: push bc ld a,%00100000 + 1 ;powerup, weapon ld (powerup_template_flag),a ld b,8 call myRandom add a,a ;a = a*2 ld l,a ld h,0 ld de,die_pu_path add hl,de ld (powerup_template_path),hl ld a,(shipx) ld (powerup_template_x),a ld a,(shipy) ld (powerup_template_y),a ld de,powerup_template call putenemy pop bc djnz die_loop xor a ld (weapon),a ret alreadydead: dec (hl) ;decrement the dead counter ret nz ;return if you're still dead ld hl,lives dec (hl) ld hl,16*256+16 ; ld (shields),hl ;fill health/shields xor a ; ld (healthqueue),a ;erase the damage ld (deadfix+1),a inc a ; ld (barflag),a ;mark the bar as dirty call draw_health_shields call display_lives deadfinish: ld hl,invincible ld a,(hl) or a jr z,update_ship_skinvinc dec (hl) ;decrement the invincibility counter update_ship_skinvinc: ld hl,friendx-2 ld de,friendx ld bc,2*NUM_SHIP_COORD_BUFS lddr call getkdh bit KDB_DOWN,b call z,mdown bit KDB_UP,b call z,mup bit KDB_RIGHT,b call z,mright bit KDB_LEFT,b ret nz mleft: call _mleft _mleft: ld hl,shipx ld a,(hl) cp 9 ret c dec (hl) ret mright: call _mright _mright: ld hl,shipx ld a,(hl) cp SHIP_BOUNDS ret nc inc (hl) ret mup: call _mup _mup: ld hl,shipy ld a,(hl) cp 9 ret c dec (hl) ld hl,screencoord sub (hl) cp 20+8 ret nc jp scrlup ;scroll the screen up and return mdown: call _mdown _mdown: ld hl,shipy ld a,(hl) cp SCRNBUF_HEIGHT-7+8 ret nc inc (hl) ld a,(screencoord) add a,VISIBLE_HEIGHT sub (hl) cp 20 ret nc jp scrldown update_health: ld hl,update_health_ret push hl ld hl,healthqueue ld a,(hl) or a ret z ld (barflag),a ;set the bar to dirty bit 7,a jr z,uhheal jr uhhurt update_health_ret: draw_health_shields: ld a,(health) call getbmp ;de = health bmp (little endian, big endian) ld (HEALTH_LOCATION),de ld a,(shields) call getbmp ;de = health bmp (little endian, big endian) ld (SHIELD_LOCATION),de ret uhhurt: inc (hl) ld hl,shields ld a,(hl) ;a = current shields or a ;if we dont have shields jr z,uh_hurt_noshields ;hurt the health dec (hl) ;hurt the shields ret uh_hurt_noshields: inc hl ;hl->health or (hl) ;a = health ret z dec (hl) ;hurt the health ret uhheal: dec (hl) ld hl,shields ld a,(hl) cp 16 jr nc,uh_heal_max inc (hl) ret uh_heal_max: ld (hl),16 xor a ld (healthqueue),a ret getbmp: ld de,0 or a ret z ld b,a getbmpL: scf rr e rr d djnz getbmpL ret ;a=adjustment adjustHealth: ld c,a ld a,(invincible) or a jr z,adjustHealth_sk bit 7,c ret nz adjustHealth_sk: ld a,c push hl ld hl,healthqueue add a,(hl) ld (hl),a pop hl ret ;de = pts to add addscore: push hl ld hl,(scorequeue) add hl,de ld (scorequeue),hl pop hl ret display_score: ld de,(score) jr ud_score update_score: ld hl,(scorequeue) ; ld a,h ; or l ; ret z ;no need to update the score ld (barflag),a ;set the bar to dirty ld de,-2 ;check for 2 pts left ld bc,2 ;add 2 pts (maybe) add hl,de ;if there is a carry jr c,update_score_g ;we have sufficient pts in the queue to add 2 pts ld bc,(scorequeue) ;else, add (scorequeue) pts ld hl,0 update_score_g: ld (scorequeue),hl ld hl,(score) add hl,bc ld (score),hl ex de,hl ;de = score ud_score: call bintobcd16 ld hl,bcd16 ld de,(76*256)+2 jp sputsbcd display_lives: ld hl,lives ld de,(49*256)+2 jp sputsbcd ;================== ship shot routines ================== ;timer(1), pathptr(2), x(1), y(1), spriteptr(2), damage(1) ; shoot: ld a,(dead) or a ret nz ld hl,(weapon) ;l=weapon ld h,0 ;hl=weapon add hl,hl ;hl=weapon*2 ld de,weaponoffsettable add hl,de ;hl points to a pointer to the shot data ld a,(hl) ;a=LSB inc hl ld h,(hl) ;h=MSB ld l,a ;l=LSB ld b,(hl) ;b=number of shots inc hl ex de,hl ;de->shot data ld ix,shipy shootL: call putshot djnz shootL ld a,(leftfriend) or a ret z ld ix,(friendcoordptr) ld de,(friendshotptr) ;timer(1), pathptr(2), x(1), y(1), spriteptr(2), damage(1) ; ;inputs: ;de -> shot data ;ix -> y, x ; ;returns de -> next shot data ;preserves bc, ix putshot: ld hl,numshots ld a,(hl) cp MAX_SHOTS ;if we're full at the moment ret nc ;return push bc inc (hl) ;otherwise, add the shot ld hl,(shotptr) ;hl->open slot ex de,hl ;hl->shot data, de->open slot ldi ;copy timer ldi ;copy path ptr LSB ldi ;copy path ptr MSB ld a,(ix+1) add a,(hl) ;fix xcoord ld (de),a inc hl inc de ld a,(ix) add a,(hl) ;fix ycoord ld (de),a inc hl inc de ldi ldi ldi ;shot is copied, de->next slot in table, hl->next shot data ld (shotptr),de ex de,hl ;de->next shot data pop bc ret ;timer(1), pathptr(2), x(1), y(1), spriteptr(2), damage(1) display_shots: ld a,(numshots) or a ;if there are no shots ret z ;we don't have to display them :) ld b,a ;b=number of shots to display ld hl,shottable hshotL: push bc ld de,3 add hl,de ;(hl) = xcoord ld b,(hl) ;b=xcoord inc hl ;(hl) = ycoord ld c,(hl) ;c=ycoord inc hl ;(hl) = LSB of spriteptr ld a,(hl) inc hl ;(hl) = MSB of spriteptr push hl ;save pointer to the msb ld h,(hl) ;h=MSB of spriteptr ld l,a ;hl->sprite data call putsprclpAND pop hl ;hl->MSB of spriteptr inc hl inc hl ;hl->next shot pop bc djnz hshotL ret ;timer(1), pathptr(2), x(1), y(1), spriteptr(2), damage(1) update_shots: ld a,(numshots) or a ;if there are no shots ret z ;we don't have to display them :) ld b,a ;b=number of shots to display ld hl,shottable ushotL: push bc push hl ;hl->shot ld c,(hl) ;c=timer dec (hl) ;decrement the timer jp z,ushotk ;if timer reached zero, kill the shot inc hl ;(hl) = LSB of pathptr ld a,(hl) ;a = LSB of pathptr inc hl ;(hl) = MSB of pathptr push hl ;save pointer to MSB of pathptr ld h,(hl) ;h = MSB of pathptr ld l,a ;hl->path data ld a,(hl) ;a=mask and c ;a = timer & mask ld d,0 ; add a,a ; rl d ; ld e,a ;de=mask & timer * 2 inc hl ;hl->path coord data add hl,de ;hl->current coord data ld d,(hl) ;d=x coord data inc hl ld e,(hl) ;e=y coord data pop hl ;hl->MSB of pathptr inc hl ;(hl) = xcoord ld a,(hl) add a,d ld d,a ;d = xcoord (for collision detection) ld (hl),a ;save new xcoord and %01111111 ;make it unsigned cp 96+8+10 jr nc,ushotk ;shot went off the left bounds of the screen; kill it inc hl ;(hl) = ycoord ld a,(hl) add a,e ;a=new ycoord ld e,a ;e = ycoord (for collision detection) ld (hl),a ;save new ycoord inc hl ;LSB inc hl ;MSB inc hl ld a,(hl) ;a = damage ld (_shdam+1),a ;save it for later ;timer(1), pathptr(2), x(1), y(1), spriteptr(2), damage(1) ;flag(1), health(1), startpathcnt(1), startpathptr(2), pathcnt(1), pathcntmax(1), pathptr(2) ;x(1), y(1), height(1), spritecnt(1), spriteptr(2), shottimermax(1), shottimer(1), shotroutineptr(2) ld a,(numenemies) ;a=how many enemies to check or a jr z,usnoe ld b,a ld hl,enemytable us_eL: push bc ld a,(hl) and %10100000 ;dont shoot powerups or explosions jr nz,useSK push hl ld bc,9 add hl,bc ;(hl) = enemy xcoord ld a,d ;a = shot xcoord add a,8 ;a = right side of shot cp (hl) ; jr c,useNo ;shot hasn't reached the enemy yet ld c,(hl) ;c = enemy xcoord inc hl ;(hl) = enemy ycoord ld a,e add a,3 cp (hl) jr c,useNo ld a,(hl) ; inc hl ; add a,(hl) ;+height cp e ; jr c,UseNo ; ld a,c add a,8 cp d jr c,useNo ;shot is past the enemy pop hl ;hl->enemy inc hl ;(hl) = enemy health ld a,(hl) ;a = health _shdam: sub 0 ;SMC damage from above / a = new health ld (hl),a ;a = new health dec hl jr nc,donotexplode ;if nc, there is still health left ld de,5 ;default is 5 pts bit 6,(hl) ;if it is not a boss jr z,explodeNormal ;continue ld de,BOSS_POINTS ;otherwise, you get BOSS_POINTS pts explodeNormal: call addscore ;increase score call explodeenemy ;explode the enemy donotexplode: pop bc ;no need to continue looping, so pop the inner counter jr ushotk useNo: pop hl useSK: ld bc,ENEMY_SIZE add hl,bc ;move hl to the next enemy pop bc ; djnz us_eL ;loop back usnoe: pop hl ld de,SHOT_SIZE add hl,de ;hl->next shot ushotC: pop bc ;restore pointer dec b ; jp nz,ushotL ;there is too much data for a djnz ret ushotk: pop de ;de->shot push de ;save pointer call killshot ;kill the shot pop hl ;restore pointer jr ushotC ;skip the pointer update, continue through loop ;timer(1), pathptr(2), x(1), y(1), spriteptr(2), damage(1) ;de points to shot to kill ;preserves hl ; killshot: push hl or a ;clear the carry flag ld hl,shottable+SHOT_TABLE_SIZE-SHOT_SIZE sbc hl,de ;hl=how many bytes to copy jr z,kshots ;if we're looking at the last shot, no need to copy back ld b,h ld c,l ;bc=bytes to copy ld l,e ld h,d ;hl->shot to kill push de ld de,SHOT_SIZE add hl,de pop de ;hl->shot after one to kill ldir ;copy all the shots after the one we're killing back a slot kshotS: ld hl,(shotptr) ;we killed a shot, so the shotptr must decrement SHOT_SIZE bytes ld de,-SHOT_SIZE add hl,de ld (shotptr),hl ; ld hl,numshots dec (hl) pop hl ret ;================= enemy routines ================== ;flag(1), health(1), startpathcnt(1), startpathptr(2), pathcnt(1), pathcntmax(1), pathptr(2) ;x(1), y(1), height(1), spritecnt(1), spriteptr(2), shottimermax(1), shottimer(1), shotroutineptr(2) display_enemies: ld a,(numenemies) or a ret z ld b,a ld hl,enemytable ld a,(screencoord) ld (de_fix2+1),a add a,VISIBLE_HEIGHT+9 ld (de_fix+1),a enemyL: push bc ld de,9 add hl,de ;hl->x ld b,(hl) ;b = xcoord inc hl ;hl->y ld a,(hl) ;a = ycoord inc hl ;(hl) = height de_fix: cp 0 ;SMC jr nc,display_e_sk ld c,a add a,(hl) ;a = y+height de_fix2: cp 0 jr c,display_e_sk inc hl ;(hl) = spritecnt ld a,(hl) and %00000110 ld e,a ld d,0 ;de=spritecnt offset inc hl ;hl->LSB of spriteptr ld a,(hl) ;a=LSB inc hl ;hl->MSB push hl ld h,(hl) ;h = MSB ld l,a ;hl -> sprite pointer table add hl,de ;hl -> pointer to the sprite data ld a,(hl) inc hl ld h,(hl) ld l,a ;hl -> sprite data call putsprclpAND pop hl ;hl->MSB ld bc,5 add hl,bc ;hl->next enemy pop bc djnz enemyL ;loop back ret display_e_sk: ld de,ENEMY_SIZE-11 add hl,de pop bc djnz enemyL ret ;=== routine for shield powerup us_get_powerup0: ld de,1 call addscore ld a,4 jp adjusthealth ;=== routine for weapon powerup us_get_powerup1: ld de,1 call addscore ld hl,weapon ld a,(hl) cp NUM_WEAPONS ret z inc (hl) ret ;=== routine for 5 pts powerup us_get_powerup2: ld de,5 call addscore ret ;=== routine for friend powerup us_get_powerup3: ld de,1 call addscore ld hl,leftfriend ld a,(hl) cp 3 ret z inc (hl) add a,a ld e,a ld d,0 ld hl,friendweapontable add hl,de ld a,(hl) inc hl ld h,(hl) ld l,a ld (friendshotptr),hl ret ;=== routine for 10 pts powerup us_get_powerup4: ld de,10 jp addscore ;=== routine for extra life powerup us_get_powerup5: ld de,1 call addscore ld hl,lives ld a,(hl) cp 9 ret z inc (hl) inc a ld (barflag),a jp display_lives ;hl -> enemy us_get_powerup: push hl ld a,(hl) and %00001111 add a,a ld l,a ld h,0 ld de,us_get_powerup_table add hl,de ld a,(hl) inc hl ld h,(hl) ld l,a call callhl pop de push de call removeEnemy ;remove the powerup pop hl pop bc djnz uenL ret ; This routine is set up so that it *shouldn't* crash if the size of the enemy is changed. ; However, everything before the spriteptr should not be moved to insure that this routine ; works. ; ;flag(1), health(1), startpathcnt(1), startpathptr(2), pathcnt(1), pathcntmax(1), pathptr(2) ;x(1), y(1), height(1), spritecnt(1), spriteptr(2), shottimermax(1), shottimer(1), shotroutineptr(2) uenP: inc hl dec (hl) ;health = timer for powerups dec hl jp z,uenr jr uenG update_enemies: ld a,(numenemies) or a ret z ld b,a ld hl,enemytable uenL: bit 5,(hl) ; jr nz,uenP ;this is a powerup, so do not do the explosion stuff bit 7,(hl) ;(hl) = enemy if bit 7 is set, this is an explosion jr z,uenG ;if it is an enemy, continue ld a,(hl) ;a=flag and %00000111 ;a=explosion counter jp z,uenrex ;if it's zero, remove this explosion dec (hl) ;decrement the explosion counter uenG: push bc ;save the counter push hl inc hl ;(hl) = health inc hl ;(hl) = startpathcnt ld a,(hl) ;a = startpathcnt cp $ff ;if it is -1 jr z,egetfrompath ;get the coord updates from the normal path egetfromstartpath: dec (hl) ;decrement the startpathcnt inc hl ;(hl) = LSB of startpathptr ld c,(hl) ;c=LSB inc hl ;(hl) = MSB of startpathptr push hl ld h,(hl) ; ld l,c ;hl->path data and (hl) inc hl ld e,a ld d,0 ;de=offset add hl,de ;hl=hl+de add hl,de ;hl=hl+(de*2) / hl->path coord data ld b,(hl) ;b=x offset inc hl ld c,(hl) ;c=y offset pop hl ;hl->MSB of startpathptr ld de,5 add hl,de ;hl->xcoord jr egotten egetfrompath: ;flag(1), health(1), startpathcnt(1), startpathptr(2), pathcnt(1), pathcntmax(1), pathptr(2) ;x(1), y(1), height(1), spritecnt(1), spriteptr(2), shottimermax(1), shottimer(1), shotroutineptr(2) inc hl ;LSB inc hl ;MSB inc hl ;(hl) = pathcnt ld a,(hl) ;a=pathcnt inc (hl) ;increment the pathcnt inc hl ;(hl) = pathcntmax cp (hl) ;if pathcnt != pathcntmax jr nz,estillgood ;we're still good dec hl ;else ld (hl),0 ;reset pathcnt inc hl ; estillgood: inc hl ;(hl) = LSB of pathptr ld c,(hl) ;c = LSB inc hl ;(hl) = MSB of pathptr push hl ; ld h,(hl) ; ld l,c ;hl->path data ld e,a ld d,0 ;de=offset add hl,de ; add hl,de ;hl = hl + (de * 2) ld b,(hl) ;b=x offset inc hl ; ld c,(hl) ;c=y offset pop hl ;hl->MSB of pathptr inc hl ;hl->xcoord egotten: ;hl must point to the x coord at this point ld a,(hl) ;a=xcoord add a,b ;a=new xcoord ld b,a ;b=new xcoord (for collision detection) ld (hl),a ;save new xcoord inc hl ;(hl) = ycoord ld a,(hl) ;a = ycoord add a,c ;a=new ycoord ld c,a ;c=new ycoord ld (hl),a ;save new ycoord inc hl ;(hl) = height add a,(hl) ;a = y+height inc hl ;(hl) = spritecnt inc (hl) ;increment the sprite counter ;b=enemy x, c=enemy y deadfix: jr echeckship echeckship: ld h,8 ;number to add ld de,(shipY) ;e = ship y, d = ship x cp e ;enemy bottom < ship top? jr c,edone ;if so, continue ld a,e ;a = ship y add a,h ;a = bottom of ship cp c ;ship bottom < enemy top? jr c,edone ld a,d ;a = ship x add a,h ;a = right side of ship cp b ;ship right < enemy left? jr c,edone ld a,b ;a = enemy x add a,h ;a = right side of enemy cp d ;enemy right < ship left? jr c,edone pop hl ;hl->enemy flag bit 5,(hl) ;if it's a powerup jp nz,us_get_powerup ;get it! bit 7,(hl) jr nz,edonenohl push hl inc hl ;(hl) = health us_still_kill: ld a,-SHIP_EXPLODE_DAMAGE call adjustHealth ld a,(hl) ;a=health sub ENEMY_EXPLODE_DAMAGE ld (hl),a jr nc,us_still_kill dec hl call explodeenemy edone: pop hl edonenohl: ld de,ENEMY_SIZE add hl,de uenfbc: pop bc uenfin: dec b jp nz,uenL ret ;hl->start of enemy uenr: ld d,h ld e,l ;de->enemy push hl call removeEnemy ;preserves bc pop hl jr uenfin ;flag(1), health(1), startpathcnt(1), startpathptr(2), pathcnt(1), pathcntmax(1), pathptr(2) ;x(1), y(1), height(1), spritecnt(1), spriteptr(2), shottimermax(1), shottimer(1), shotroutineptr(2) uenrex: push bc ld b,POWERUP_RATE call myRandom or a pop bc jr nz,uenr ;remove the explosion push bc ld b,POWERUP_TABLE_SIZE call myRandom ld ix,powerup_table ld e,a ld d,0 add ix,de ;ix = powerup type ld a,(numactualenemies) dec a ld (numactualenemies),a ld a,(ix+0) ld (hl),a ;set the powerup type set 5,(hl) ;make it a powerup inc hl ;hl = health (timer) ld (hl),POWERUP_LENGTH ;lasts for POWERUP_LENGTH frames ld de,10 add hl,de ld (hl),7 ;set the height inc hl inc hl ;hl -> spriteptr add a,a add a,a add a,a ;a = a*8 ex de,hl ;de ->spriteptr ld hl,powerupsprtable ld c,a ld b,0 ;bc add hl,bc ;hl->powerupsprtableXXX ex de,hl ;hl->spriteptr, de->powerupsprtableXXX ld (hl),e inc hl ld (hl),d ld de,5 add hl,de ;hl->next enemy jr uenfbc ;hl->enemy ; ;flag(1), health(1), startpathcnt(1), startpathptr(2), pathcnt(1), pathcntmax(1), pathptr(2) ;x(1), y(1), height(1), spritecnt(1), spriteptr(2), shottimermax(1), shottimer(1), shotroutineptr(2) ; ;bc,de are preserved explodeEnemy: push de ld (hl),%10000111 ;explosion, lasts %111 (7) frames inc hl ;(hl) = health inc hl ;(hl) = startpathcnt ld (hl),$ff ;so the explosion uses the pathptr, not the startpathptr ($ff = -1) inc hl ;startpathptr LSB inc hl ;startpathptr MSB inc hl ;pathcnt ld (hl),0 ;reset the pathcnt inc hl ld (hl),3 ;set the pathcntmax to 3 inc hl ;(hl) = pathptr LSB push hl ld a,r ;a = pseudorandom number and %00000110 ld e,a ld d,0 ld hl,explosionpathtable add hl,de ld e,(hl) inc hl ld d,(hl) pop hl ld (hl),e inc hl ;(hl) = pathptr MSB ld (hl),d inc hl ;x inc hl ;y inc hl ;height ld a,(hl) sub 8 jr c,explodeskip srl a ;a = height/2 dec hl add a,(hl) ld (hl),a inc hl explodeskip: inc hl ;sprite cnt ld (hl),0 ;reset the sprite counter inc hl ; ld de,explosionsprtable ld (hl),e ; inc hl ; ld (hl),d ; pop de ret ;bit 7: ; 0 = normal enemy ; 1 = explosion ;bit 6: ; 0 = normal enemy ; 1 = boss ;bit 5: ; 0 = normal enemy ; 1 = powerup ;de points to enemy to remove ;preserves bc ; removeEnemy: push bc ld a,(de) bit 5,a jr nz,removeEnemySK ld hl,numactualenemies dec (hl) removeEnemySK: or a ;clear the carry flag ld hl,enemytable+ENEMY_TABLE_SIZE-ENEMY_SIZE sbc hl,de ;hl=how many bytes to copy jr z,renemys ;if we're looking at the last enemy, no need to copy back ld b,h ld c,l ;bc=bytes to copy ld l,e ld h,d ;hl->enemy to remove push de ld de,ENEMY_SIZE add hl,de ;hl->enemy after one to remove pop de ldir ;copy all the enemies after the one we're removing back a slot renemyS: ld hl,(enemyptr) ;we removeed an enemy, so the enemyptr must decrement ENEMY_SIZE bytes ld de,-ENEMY_SIZE ; add hl,de ; ld (enemyptr),hl ; ld hl,numenemies ;we must also update the number of enemies dec (hl) pop bc ret ;flag(1), health(1), startpathcnt(1), startpathptr(2), pathcnt(1), pathcntmax(1), pathptr(2) ;x(1), y(1), height(1), spritecnt(1), spriteptr(2), shottimermax(1), shottimer(1), shotroutineptr(2) ; ; ;inputs: ;de -> enemy data ; ;returns hl -> next enemy data ;preserves bc ;destroys a putenemy: ld hl,numenemies ld a,(hl) cp MAX_ENEMIES ;if we're full at the moment ret nc ;return push bc inc (hl) ;otherwise, add an enemy ld a,(de) ;a = flag and %10100000 ;make sure it isn't an explosion/powerup jr nz,putenemy2 ld hl,numactualenemies inc (hl) ;it is an enemy, increase the numactualenemies putenemy2: ld hl,(enemyptr) ;hl->open slot ex de,hl ;hl->enemy data, de->open slot ld bc,ENEMY_SIZE ;bc=number of bytes to copy ldir ;insert enemy, de->next slot ld (enemyptr),de pop bc ret ;================ enemy shot routines ===================== ;flag(1), health(1), startpathcnt(1), startpathptr(2), pathcnt(1), pathcntmax(1), pathptr(2) ;x(1), y(1), height(1), spritecnt(1), spriteptr(2), shottimermax(1), shottimer(1), shotroutineptr(2) enemy_shoot: ld a,(numenemies) or a ret z ld b,a ld hl,enemytable eshootL: ld a,(hl) ;a = flag and %10100000 jr nz,eshootsk push bc push hl inc hl ;(health) inc hl ;(startpathcnt) ld a,(hl) inc a jr nz,eshootretpop ld bc,13 add hl,bc ld c,(hl) ;c = shottimermax inc hl ;(hl) = shottimer ld a,(hl) ;a = shottimer inc (hl) sub c jr nz,eshootretpop ld (hl),a inc hl ld e,(hl) inc hl ld d,(hl) ;de->shot routine push de ;push the shot routine onto the stack ld bc,-9 add hl,bc ;hl->xcoord ld d,(hl) ;d = xcoord inc hl ; ld e,(hl) ;e = ycoord inc hl ;(hl) = height ld c,(hl) ;c = height pop hl ;hl->shot routine call callhl eshootretpop: pop hl pop bc eshootsk: ld de,ENEMY_SIZE add hl,de djnz eshootL ret ;timer(1), xinc(2), x(2), yinc(2), y(2), spriteptr(2), damage(1) display_enemy_shots: ld a,(numenemyshots) ; or a ;if there are no enemy shots ret z ;return ld b,a ; ld hl,enemyshottable ; deshotL: push bc ld de,4 add hl,de ;(hl) = x MSB ld b,(hl) ;b = x add hl,de ;(hl) = y MSB ld c,(hl) ;c = y inc hl ;(hl) = LSB ld a,(hl) ;a = LSB inc hl ;(hl) = MSB push hl ;save pointer to MSB ld h,(hl) ld l,a ;hl->sprite data call putsprclpAND ;display the sprite pop hl inc hl ;(hl) = damage inc hl ;hl->next shot pop bc djnz deshotL ret ;timer(1), xinc(2), x(2), yinc(2), y(2), spriteptr(2), damage(1) update_enemy_shots: ld a,(numenemyshots) or a ret z ld b,a ld hl,enemyshottable ueshot: push bc push hl ld a,(hl) dec (hl) ;if timer has expired jr z,ueremove ;remove this shot inc hl ;(hl) = xinc LSB ld e,(hl) ;e = xinc LSB inc hl ;(hl) = xinc MSB ld d,(hl) ;d = xinc MSB inc hl ;(hl) = x LSB ld a,(hl) ;a = x LSB push hl inc hl ;(hl) = x MSB ld h,(hl) ; h = x MSB ld l,a ;hl = x add hl,de ld d,h ld e,l ;de = new x pop hl ld (hl),e inc hl ld (hl),d inc hl ;(hl) = yinc LSB ld c,(hl) ;c = yinc LSB inc hl ;(hl) = yinc MSB ld b,(hl) ;bc = yinc inc hl ;(hl) = y LSB ld a,(hl) ;a = y LSB push hl inc hl ;(hl) = y MSB ld h,(hl) ; h = y MSB ld l,a ;hl = y add hl,bc ld e,h ld b,l ;eb = new y pop hl ld (hl),b inc hl ld (hl),e ld a,e ld bc,(shipy) ;c = shipy, b = shipx add a,3 ;a = bottom of shot cp c jr c,uecont ;shot is above the ship ld a,c ;a = ship y coord add a,6 ;a = bottom of ship cp e jr c,uecont ;ship is above the shot ld a,d ;a = shot x coord add a,7 ;a = right side of shot cp b ; jr c,uecont ld a,b ;a = ship x add a,6 ;a = right side of ship cp d jr c,uecont ld a,(dead) ; or a ; jr nz,uecont ;if ship is dead, don't do collision detection inc hl ;LSB inc hl ;MSB inc hl ;damage ld a,(hl) call adjustHealth pop hl ;restore pointer to the start of the shot push hl ;save if for the removeEnemyShot jr ueremove uecont: pop hl ld bc,ENEMY_SHOT_SIZE add hl,bc uecontx: pop bc djnz ueshot ret ;hl->enemy shot ;hl has been 'push'ed ueremove: ex de,hl call removeEnemyShot pop hl jr uecontx ;de points to enemy shot to remove ;preserves bc ; removeEnemyShot: push bc or a ;clear the carry flag ld hl,enemyshottable+ENEMY_SHOT_TABLE_SIZE-ENEMY_SHOT_SIZE sbc hl,de ;hl=how many bytes to copy jr z,reshots ;if we're looking at the last enemy shot, no need to copy back ld b,h ld c,l ;bc=bytes to copy ld l,e ld h,d ;hl->enemy shot to remove push de ld de,ENEMY_SHOT_SIZE add hl,de ;hl->enemy shot after one to remove pop de ldir ;copy all the enemy shots after the one we're removing back a slot reshotS: ld hl,(enemyshotptr) ;we removeed an enemy shot, so the enemyshotptr must decrement ENEMY_SHOT_SIZE bytes ld de,-ENEMY_SHOT_SIZE ; add hl,de ; ld (enemyshotptr),hl ; ld hl,numenemyshots ;we must also update the number of enemy shots dec (hl) pop bc ret ;timer(1), xinc(2), x(2), yinc(2), y(2), spriteptr(2), damage(1) ;inputs: ;de -> enemy shot structure ; ;preserves bc ; putenemyshot: ld hl,numenemyshots ld a,(hl) cp MAX_ENEMY_SHOTS ;if we're full at the moment ret nc ;return push bc inc (hl) ;otherwise, add an enemy shot ex de,hl ;hl -> shot structure ld de,(enemyshotptr) ;de->open slot ld bc,ENEMY_SHOT_SIZE ;bc=number of bytes to copy ldir ;insert enemy shot, de->next slot ld (enemyshotptr),de pop bc ret ;inputs: ;de=xy ;c =height enemyshotAI: ;the normal AI. Just fires straight, with a normal sprite. ; ;timer(1), xinc(2), x(2), yinc(2), y(2), spriteptr(2), damage(1) enemyshotAI0: ld b,4 call myRandom or a ret nz enemyshotAI1: srl c ;c = height / 2 dec c ld a,d ; sub 8 ; ld (enemyshotAI0data_x+1),a ;x MSB ld a,e add a,c ;a = y + (height/2) -1 ld (enemyshotAI0data_y+1),a ;y MSB ld de,enemyshotAI0data jp putenemyshot enemyshotAI2: ld b,4 call myRandom or a ret nz enemyshotAI3: srl c ;c = height / 2 dec c ld a,d ; sub 8 ; ld (enemyshotAI2data_x+1),a ;x MSB ld a,e add a,c ;a = y + (height/2) -1 ld (enemyshotAI2data_y+1),a ;y MSB ld de,enemyshotAI2data jp putenemyshot enemyshotAI4: ld b,4 call myRandom or a ret nz enemyshotAI5: ld a,-4 ld (enemyshotAI4data_dam),a enemyshotAI5_skipdam: srl c ;c = height / 2 dec c ld a,d sub 8 ld (enemyshotAI4data_x+1),a ;x MSB ld a,e add a,c ld (enemyshotAI4data_y+1),a ;y MSB ld b,7 call myRandom ;0 <= a <= 6 sub 3 ;-3 <= a <= 3 ld (enemyshotAI4data_Yinc + 1),a jr nc,enemyshotAI4_sign neg ;-a enemyshotAI4_sign: add a,-4 ld (enemyshotAI4data_Xinc + 1),a ld de,enemyshotAI4data jp putenemyshot ;stronger normal shot enemyshotAI6: ld b,4 call myRandom or a ret nz enemyshotAI7: ld a,-6 ld (enemyshotAI4data_dam),a jr enemyshotAI5_skipdam ;1 shot, aimed directly at the player's ship enemyshotAI8: ld b,4 call myRandom or a ret nz enemyshotAI9: srl c ;c = height / 2 dec c ld a,d sub 8 ld d,a ld (enemyshotAI8data_x+1),a ;x MSB ld a,e add a,c ld e,a ;de = xy ld (enemyshotAI8data_y+1),a ;y MSB ld bc,-3*256+0 call enemyshot_aim ld (enemyshotAI8data_xinc),bc ld (enemyshotAI8data_yinc),hl ld de,enemyshotAI8data jp putenemyshot ;1 shot, aimed around the player's ship enemyshotAI10: ld b,4 call myRandom or a ret nz enemyshotAI11: srl c ;c = height / 2 dec c ld a,d sub 8 ld d,a ld (enemyshotAI8data_x+1),a ;x MSB ld a,e add a,c ld e,a ;de = xy ld (enemyshotAI8data_y+1),a ;y MSB ld bc,-3*256+0 call enemyshot_aim push bc ld b,3 call myRandom dec a pop bc add a,b ld b,a ld (enemyshotAI8data_xinc),bc ;ld b,3 ;call myRandom ;dec a ;add a,h ;ld h,a ld (enemyshotAI8data_yinc),hl ld de,enemyshotAI8data jp putenemyshot ;========== shot templates =============== enemyshotAI0data: .db 50 ;timer .dw -3*256+128 ;xinc enemyshotAI0data_x: .dw 0 ;x .dw 0 ;yinc enemyshotAI0data_y: .dw 0 ;y .dw enemyshotspr0 .db -4 ;damage enemyshotAI2data: .db 50 ;timer .dw -3*256+200 ;xinc enemyshotAI2data_x: .dw 0 ;x .dw 0 ;yinc enemyshotAI2data_y: .dw 0 ;y .dw enemyshotspr0 .db -6 ;damage enemyshotAI4data: .db 50 ;timer enemyshotAI4data_Xinc .dw -3*256+128 ;xinc enemyshotAI4data_x: .dw 0 ;x enemyshotAI4data_Yinc: .dw 0 ;yinc enemyshotAI4data_y: .dw 0 ;y .dw enemyshotspr1 enemyshotAI4data_dam: .db -4 enemyshotAI8data: .db 40 ;timer enemyshotAI8data_Xinc: .dw 0 ;xinc enemyshotAI8data_x: .dw 0 ;x enemyshotAI8data_Yinc: .dw 0 ;yinc enemyshotAI8data_y: .dw 0 ;y .dw enemyshotspr3 .db -4 ;damage enemyshotAIbossdata: .db 40 ;timer enemyshotAIbossdata_Xinc: .dw -3*256+128 ;xinc enemyshotAIbossdata_x: .dw 0 ;x enemyshotAIbossdata_Yinc: .dw 0 ;yinc enemyshotAIbossdata_y: .dw 0 ;y .dw enemyshotspr1 enemyshotAIbossdata_dam: .db -4 enemyshotAIboss2: ld a,c srl a ld (enemyshotAIboss_fix+1),a ld hl,enemyshotAIbossret push hl jr enemyshotaibosssk ;timer(1), xinc(2), x(2), yinc(2), y(2), spriteptr(2), damage(1) enemyshotAIboss: ld a,c sub 3 ld (enemyshotAIboss_fix+1),a ld hl,enemyshotAIbossret enemyshotaibosssk: push hl enemyshotAIbossret: push de ld ix,shipy ;ix -> ship coords ld b,d ;b = x ld c,0 ld (enemyshotAIbossdata_x),bc ;set the x coord ld b,e ;b = y ld (enemyshotAIbossdata_y),bc ;set the y coord ld bc,-4*256+0 ;default x inc call enemyshot_aim ld (enemyshotAIbossdata_Xinc),bc ld (enemyshotAIbossdata_Yinc),hl ld de,enemyshotAIbossdata call putenemyshot pop de ld a,e enemyshotAIboss_fix: add a,0 ;SMC ld e,a ret ;bc = default xinc ;de = xy to aim from ;will aim at player's ship ; ;returns bc = xinc, hl = yinc enemyshot_aim: ld hl,0 ;default y inc ld ix,shipy ld a,d ;a = shot x add a,4 ;to aim at the middle of the ship sub (ix+1) ;a = difference in x coords ret z ret c ld b,a ld c,0 sra b rr c sra b rr c sra b rr c sra b rr c ld a,b cpl ld b,a ld a,c cpl ld c,a inc bc enemyshot_aim_nox: ld a,e ;a = shot y sub (ix) ;a = shot y - ship y add a,2 ld h,a sra h rr l sra h rr l sra h rr l sra h rr l ld a,h cpl ld h,a ld a,l cpl ld l,a inc hl ret ;==================== find level stuff ======================== #ifdef TI83P ;hl -> place to start ;ix -> detection string ; ;nc = not found ;c = found ;if found, it returns: ; bc -> VAT entry ; hl -> variable's data after the detection string ; de -> next variable ; a = variable type ; ;ix is left intact ; findlevelBad: pop ix ;ix->string pop hl ;hl->name length pop de ;de->data findlevelSk: pop bc ;bc->start of entry (just to get it off the stack) ld b,(hl) dec hl findlevelName: dec hl ;skip the name djnz findlevelName ;loop, then continue findlevel: findlevelL: ld a,(hl) ;if byte is zero or a ;clear carry ret z ;and return push hl ;save pointer to start of the entry dec hl dec hl dec hl ;pointer LSB ld e,(hl) ; dec hl ; ld d,(hl) ;de -> var data dec hl ;(hl) = flash page ld a,(hl) ;a = flash page dec hl ;(hl) = name length or a ; jr nz,findlevelSk ;skip this one if it's archived push de ;save pointer to the data inc de inc de ;skip the size push hl ;save pointer to the name length push ix ;save pointer to the string findlevelcmpL: ld a,(ix) ;see if we're at the end of the detection string or a ;if we are, we've found a good variable jr z,findlevelGood ; ld a,(de) ;a = variable byte cp (ix) ;compare it to the string jr nz,findlevelBad ;if they aren't the same, skip this one inc de ;de = next byte in the variable inc ix ;ix = next byte in the string jr findlevelcmpL ; findlevelGood: pop ix ;restore the string pointer pop hl ;hl->name length ld b,(hl) ;b = name length dec hl findlevelGoodName: dec hl djnz findlevelGoodName ex de,hl ;de = next variable, hl = variable data after string pop bc ;bc = variable data pop bc ;bc = variable vat entry ld a,(bc) ;a = type scf ;set the carry, we found one! ret #else findlevelbad: pop ix ;ix->string pop hl ;hl->VAT entry dec hl ;lsb dec hl ;msb dec hl ;name length ld b,(hl) ;b = name length dec hl findlevelsk: dec hl ;skip the name djnz findlevelsk findlevel: ld a,(hl) or a ret z push hl ;hl->vat entry dec hl ;LSB ld e,(hl) dec hl ;MSB ld d,(hl) ;de->prog data inc de inc de ;get past the size push ix findlevelcmp: ld a,(ix) ;if we've found the terminating zero or a ;we found a good variable jr z,findlevelfound ld a,(de) cp (ix) jr nz,findlevelbad inc de inc ix jr findlevelcmp findlevelfound: ;de->variable data after the string pop ix ;ix->string pop hl ;bc->VAT entry push hl dec hl dec hl dec hl ld b,(hl) dec hl findlevelfoundsk: dec hl djnz findlevelfoundsk pop bc ex de,hl ld a,(bc) ;a = type scf ret #endif ;================= screen routines ================ clearoffscreen: ld hl,OFFSCREEN_STRING ld de,OFFSCREEN_STRING+1 ld bc,5*12-1 ld (hl),0 ldir ret ;these routines must preserve B scrlup: ld hl,screencoord ld a,(hl) or a ;carry is reset ret z ;return if we're at the top already dec (hl) ret scrldown: ld hl,screencoord ld a,(hl) cp SCRNBUF_HEIGHT-VISIBLE_HEIGHT ret z inc (hl) ret inverse_screen: ld a,(myfastcopyinverse) or a jr z,whitescreen normal_screen: xor a ld (myfastcopyinverse),a ld hl,contup push hl push hl push hl push hl ret whitescreen: ld a,$2f ;cpl ld (myfastcopyinverse),a ld hl,contdown push hl push hl push hl push hl ret display_ship: ld hl,(friendcoordptr) ld c,(hl) inc hl ld b,(hl) dec b dec b ld a,(leftfriend) or a jr z,noleftfriend ld a,c sub 7 ld c,a ld hl,friendspr call putsprclpAND noleftfriend: ld hl,shipspr ld a,(dead) or a jr z,display_ship2 sub DEATH_LENGTH ret c srl a and %11111110 ld l,a ld h,0 ld de,shipsprtable add hl,de ld a,(hl) inc hl ld h,(hl) ld l,a display_ship2: ld bc,(shipy) ;c=ship y, b=shipx ;---------------------------------------------------------------------------- ;[ PutSprClpAND ] [ABCDEFIX] [ 139 bytes ] [ CrASH_Man ] ;---------------------------------------------------------------------------- ; Draws a sprite using only AND data, with clipping ; ; parameters: HL -> sprite (first byte is height), (B,C) = coordinates ; returns: Puts sprite in SCREEN_POINTER ; ;PutSprClpAND_no8 ; parameters: HL -> sprite, (B,A) = coordinates ; PutSprClpAND: ld a,(hl) inc hl LD (__XChange_1+1), A DEC A LD (__XChange_2+1), A NEG LD (__XChange_3+1), A ld a,b sub 8 ld b,a ld a,c sub 8 ld de,(screencoord) ;e = screen coord sub e ld c,a XOR A __XChange_1: LD DE, DefaultXSpriteHeight ; D = 0, E = Height OR C ; If C < 0 JP M, _SCX_NoBotClp ; No bottom clip. LD A, VISIBLE_HEIGHT-1 ; Is C is off of the visible screen? SUB C RET C __XChange_2: CP DefaultXSpriteHeight-1 ; If C + 7 < VISIBLE_HEIGHT JR NC, _SCX_NoVertClp ; No vertical clip. INC A LD E, A JR _SCX_NoVertClp ; Height = VISIBLE_HEIGHT - C _SCX_NoBotClp: __XChange_3: CP -(DefaultXSpriteHeight-1) ; Is C is offscreen? RET C ADD A, E ; Find how many lines LD C, A ; to actually draw SUB E NEG LD E, A ADD HL, DE ; Move HL down LD E, C ; by -C lines LD C, D _SCX_NoVertClp: PUSH HL ; IX -> Sprite POP IX LD A, $77 ; OP code for LD (_SCX_OPchg_1), A ; LD (HL), A LD (_SCX_OPchg_2), A XOR A ; Is B > 0? OR B JP M, _SCX_NoRightClp CP 89 ; Is B < 89? JR C, _SCX_ClpDone CP 96 RET NC LD HL, _SCX_OPchg_1 ; Modify LD to NOP JR _SCX_ClpModify _SCX_NoRightClp:CP -7 ; Is B is offscreen? RET C LD HL, _SCX_OPchg_2 ; Modify LD to NOP _SCX_ClpModify: LD (HL), D _SCX_ClpDone: LD B, D LD H, B LD L, C ADD HL, BC ; HL = Y * 12 ADD HL, BC ADD HL, HL ADD HL, HL LD C, A ; HL = Y*12 + X/8 SRA C SRA C SRA C INC C ADD HL, BC LD BC,SCREEN_POINTER ADD HL, BC LD B, E ; B = number of rows CPL AND %00000111 ; find number of LD E, A ; instructions to jump add a,a ;*2 add a,e ;*3 LD (_SCX_OPchg_3 + 1), A ; 3 * (7 - number) LD DE, 13 _SCX_LineLoop: LD C, (IX) XOR A _SCX_OPchg_3: JR _SCX_OPchg_3 ; modify RR C RRA RR C RRA RR C RRA RR C RRA RR C RRA RR C RRA RR C RRA or (HL) ; or with background _SCX_OPchg_1: LD (HL), A ; Write DEC HL ; HL -> next 8 pixels LD A, C or (HL) ; or with background _SCX_OPchg_2: LD (HL), A ; Write ADD HL, DE ; HL -> next row INC IX ; Increment to next data DJNZ _SCX_LineLoop RET ;-----> Draw a sprite ; b=size of sprite ; l=yc ; a=xc ; ix holds pointer putSpriteOr: ld e,l ld h,$00 ld d,h add hl,de add hl,de add hl,hl add hl,hl ld e,a and $07 ld c,a srl e srl e srl e add hl,de ld de,SCREEN_POINTER add hl,de putSpriteLoop1or: sl1And: ld d,(ix) ld e,0 ld a,c or a jr z,putSpriteSkip1or putSpriteLoop2or: srl d rr e dec a jr nz,putSpriteLoop2or putSpriteSkip1or: ld a,(hl) or d ld (hl),a inc hl ld a,(hl) or e ld (hl),a ld de,$0B add hl,de inc ix djnz putSpriteLoop1or ret ; ;-----> Draw a sprite ; b=size of sprite ; l=yc ; a=xc ; ix holds pointer putSpriteBar: ld e,l ld h,$00 ld d,h add hl,de add hl,de add hl,hl add hl,hl ld e,a and $07 ld c,a srl e srl e srl e add hl,de ld de,BAR_BUFFER add hl,de putSpriteLoop1Bar: push bc sl1Bar: ld d,(ix) ld e,0 ld a,c ld b,%00001111 ld c,%11111111 or a jr z,putSpriteSkip1Bar putSpriteLoop2Bar: scf rr b rr c srl d rr e dec a jr nz,putSpriteLoop2Bar putSpriteSkip1Bar: ld a,(hl) and b or d ld (hl),a inc hl ld a,(hl) and c or e ld (hl),a ld de,$0B add hl,de inc ix pop bc djnz putSpriteLoop1Bar ret setupint: di ld hl,INTERRUPT_TABLE ld de,INTERRUPT_TABLE+1 ld bc,$100 ld (hl),INTERRUPT_VECTOR_BYTE ldir ld hl,interrupt_start ld de,INTERRUPT_VECTOR ld bc,interrupt_end-interrupt_start ldir ld a,INTERRUPT_TABLE_BYTE ld i,a im 2 ret ;-----> Copy my buffer to the screen (fast) ;Input: nothing ;Output: my buffer is copied to the screen myFastCopy: ld hl,SCREEN_POINTER-12-(-(12*VISIBLE_HEIGHT)+1) ld a,$20 ; 7 ld c,a ; 4 inc hl ; 6 waste dec hl ; 6 waste myfastCopyAgain: push hl ;11 waste push af ;11 waste ld a,$80 ; 7 out ($10),a ; 11 pop af ;10 waste inc hl ;6 waste dec hl ;6 waste pop hl ;10 waste ld b,VISIBLE_HEIGHT ; 7 inc c ; 4 ld de,-(12*VISIBLE_HEIGHT)+1 ; 10 out ($10),a ; 11 add hl,de ; 11 ld de,11 ; 10 myfastCopyLoop: add hl,de ; 11 inc hl ; 6 waste ld a,(hl) ; 7 ld (hl),0 ; 10 myfastcopyinverse: nop ; 4 SMC out ($11),a ; 11 djnz myfastCopyLoop ; 13/8 ld a,c ; 4 cp $2B+1 ; 7 jr nz,myfastCopyAgain ; 10/1 ret ; 10 ;-----> Copy the gbuf to the screen (fast) ;Input: nothing ;Output:graph buffer is copied to the screen barFastCopy: ld hl,BAR_BUFFER-12-(-(12*8)+1) ld a,$20 ; 7 ld c,a ; 4 inc hl ; 6 waste dec hl ; 6 waste barfastCopyAgain: push hl push af ;11 waste ld a,$80+56 ; 7 out ($10),a ; 11 pop af ;10 waste pop hl inc hl ;6 waste dec hl ;6 waste ld b,8 ; 7 inc c ; 4 ld de,-(12*8)+1 ; 10 out ($10),a ; 11 add hl,de ; 11 ld de,10 ; 10 barfastCopyLoop: add hl,de ; 11 inc hl ; 6 waste inc hl ; 6 waste inc de ; 6 ld a,(hl) ; 7 out ($11),a ; 11 dec de ; 6 djnz barfastCopyLoop ; 13/8 ld a,c ; 4 cp $2B+1 ; 7 jr nz,barfastCopyAgain ; 10/1 ret ; 10 ;=========== general routines ================== ;de = xy ;hl -> numbers putsbcd: putsbcdL: ld a,(hl) inc hl cp $ff ret z push hl push de ld c,a ld a,e sla c sla c sla c ld b,0 ld ix,putnumspr add ix,bc ld l,a ld a,d ld b,7 call putSpriteOr pop de ld a,d add a,8 ld d,a pop hl jr putsbcdL ret ;de = xy ;hl -> numbers sputsbcd: sputsbcdL: ld a,(hl) inc hl cp $ff ret z push hl push de ld c,a add a,a ;*2 add a,a ;*4 add a,c ;*5 ld c,a ld b,0 ld ix,sputnumspr add ix,bc ld l,e ld a,d ld b,5 call putSpriteBar pop de ld a,d add a,4 ld d,a pop hl jr sputsbcdL ret ; BINTOBCD16 ; by Joe Pemberton ; ;input: ;de=number to convert ; ;returns (bcd16) as TEN THOUSANDS, THOUSANDS, HUNDREDS, TENS, ONES ;b = 0 ;de = 0 ;destroys af, bc, de, hl bintobcd16: push de ld hl,bcd16 ld de,bcd16+1 ld bc,4 ld (hl),0 ldir pop de ld b,16 bcd16L: ld hl,bcd16 push bc ld b,5 bcd16I: ld a,(hl) cp 5 jr c,bcd16a add a,3 ld (hl),a bcd16a: inc hl djnz bcd16I dec hl ;hl now points to the ONES ld b,5 sla e rl d bcd16b: rl (hl) bit 4,(hl) jr z,bcd16c res 4,(hl) scf bcd16c: dec hl djnz bcd16b pop bc djnz bcd16L ret bcd16: .db 0,0,0,0,0,$ff ; BINTOBCD ; by Joe Pemberton ; ;input: ;c=number to convert ; ;returns H,L,E as HUNDREDS, TENS, ONES ;(bcdstring) contains the formatted bcd ascii string ;bc = 0 bintobcd: ld b,8 ;loop for 8 bits ld hl,0 ;h=hundreds, l=tens ld e,l ;e=ones bcdL: ld a,e cp 5 jr c,bcd1 add a,3 ld e,a bcd1: ld a,l cp 5 jr c,bcd2 add a,3 ld l,a bcd2: sla c ;shift the bit from the number off rl e ;shift the bit into the ones bit 4,e jr z,bcdb1 res 4,e scf bcdb1: rl l bit 4,l jr z,bcdb2 res 4,l scf bcdb2: rl h djnz bcdL ld a,h ld (bcdstring),a ld a,l ld (bcdstring+1),a ld a,e ld (bcdstring+2),a ret bcdstring: .db $00,$00,$00,$ff ;$ff = terminating getkdh2: call getkd ld hl,getkdflag ld a,b ld c,b or (hl) ld b,a ld a,c cpl and %11111111 ld (hl),a ret getkdh: call getkd ld hl,getkdflag ld a,b ld c,b or (hl) ld b,a ld a,c cpl and KD_MASK ld (hl),a ret ;FAST keypress routine ;1=key up, 0=key down ; ;returns b and a: ;bit 0 - down ;bit 1 - left ;bit 2 - right ;bit 3 - up ;bit 4 - 2nd ;bit 5 - mode ;bit 6 - clear ;bit 7 - enter ; getkd: ld a,Dreset ld b,a out (1),a ld a,Group1 out (1),a in a,(1) and b ld b,a ;bits 0,1,2,3 are the arrows ld a,Dreset out (1),a ld a,Group2 out (1),a in a,(1) ;catch enter/clear keypress or %10111110 ;only get enter/clear keypress rrca ;enter keypress is in bit 7, clear in bit 5 bit 5,a jr nz,getkd2 xor %01100000 getkd2: and b ld b,a ;bit 7 is the enter key ld a,Dreset out (1),a ld a,Group7 out (1),a in a,(1) rra or %11001111 ;only get 2nd/mode and b ld b,a ret inputName: .db "Name: " charTable: ; .db 0,0,0,0 ;arrows ; .db 0,0,0,0,0 ;nothing ; .db 0 .db "WRMH",0 ; .db 0,0,0,"VQLG",0,0 ; .db ".ZUPKFC",0, .db " YTOJEB",0,0 .db "XSNIDA" ; .db 0,0,0,0,0,0,0,0,0 #ifdef TI83P ;-----> Generate a random number ; input b=upper bound ; ouput a=answer 0<=aopen slot in the queue ld (hl),c ;add to the queue ld hl,curqueue inc (hl) ;increment the number in the queue ld hl,queuedefaults add hl,bc ;(hl) = default timer for this event ld a,(hl) pop hl ld (hl),a ;reset the timer interrupt_full: interrupt_skip: inc hl inc c ld a,c cp QUEUE_LENGTH jr nz,interrupt_loop ex af,af' exx reti interrupt_end: queue: .ds QUEUE_SIZE queuecounters: queue_update_shots: .db 0 queue_update_ship: .db 0 queue_do_contrast: .db 0 queue_update_enemies: .db 0 queue_update_stars: .db 0 queue_update_health: .db 0 queue_enemy_shoot: .db 0 queue_update_enemy_shots: .db 0 queue_do_level: .db 0 queue_update_score: .db 0 queue_misc: .db 0 queue_update_timer: .db 0 mainRoutineTable: .dw update_shots .dw update_ship .dw do_contrast .dw update_enemies .dw update_stars .dw update_health .dw enemy_shoot .dw update_enemy_shots .dw process_level .dw update_score .dw misc .dw update_timer queuedefaults: .db QDEFAULT_UPDATE_SHOTS .db QDEFAULT_UPDATE_SHIP .db QDEFAULT_DO_CONTRAST .db QDEFAULT_UPDATE_ENEMIES .db QDEFAULT_UPDATE_STARS .db QDEFAULT_UPDATE_HEALTH .db QDEFAULT_ENEMY_SHOOT .db QDEFAULT_UPDATE_ENEMY_SHOTS .db QDEFAULT_DO_LEVEL .db QDEFAULT_UPDATE_SCORE .db QDEFAULT_MISC .db QUPDATE_TIMER curqueue: .db 0 ;open slot in the queue ;===================================================== us_get_powerup_table: .dw us_get_powerup0 .dw us_get_powerup1 .dw us_get_powerup2 .dw us_get_powerup3 .dw us_get_powerup4 .dw us_get_powerup5 powerup_table: .db 0 ;shields .db 0 ;shields .db 0 ;shields .db 0 ;shields .db 0 ;shields .db 0 ;shields .db 0 ;shields .db 0 ;shields .db 0 ;shields .db 1 ;weapon .db 1 ;weapon .db 1 ;weapon .db 1 ;weapon .db 2 ;5 pts .db 2 .db 2 .db 2 .db 2 .db 2 .db 2 .db 2 .db 2 .db 2 .db 2 .db 2 .db 2 .db 2 .db 2 .db 2 .db 2 .db 2 .db 2 .db 2 .db 2 .db 3 ;helper .db 3 ;helper .db 3 ;helper .db 4 ;10pts .db 4 .db 4 .db 4 .db 4 .db 4 .db 4 .db 4 .db 4 .db 4 .db 4 .db 4 .db 4 .db 5 ;extra life powerup_table_end: POWERUP_TABLE_SIZE equ powerup_table_end-powerup_table numlevels: .db 0 curlevel: .db 0 gamecont: .db 0 getkdflag: .db 0 var_defaults: .db 0 ;level complete .db 0 ;select_high .db 0,0,0,0,0,0,0,0,0,0 ;savename .db 0 ;titleflag .db 0 ;xtonflag .db 0 ;invincible .dw 0 ;score .dw 0 ;scorequeue .db 0 ;dead .db 3,$ff ;lives .db (SCRNBUF_HEIGHT/2)-4+8 ;shipy .db 10 ;shipx .db (SCRNBUF_HEIGHT/2)-4+8 ; .db 10 ; .db (SCRNBUF_HEIGHT/2)-4+8 ; .db 10 ; .db (SCRNBUF_HEIGHT/2)-4+8 ;ship coord bufs .db 10 ; .db (SCRNBUF_HEIGHT/2)-4+8 ; .db 10 ; .db (SCRNBUF_HEIGHT/2)-4+8 ; .db 10 ; .db (SCRNBUF_HEIGHT/2)-4+8 ;\__friend coords .db 10 ;/ .dw friendy ;friendcoordptr .db 0 ;leftfriend .dw leftfriendweapondata0 ;friendshotptr .db 16 ;shields .db 16 ;health .db 0 ;healthqueue .db 0 ;weapon .db 0 ;maxweapon .dw 0 ;levelpointer .dw 0 ;leveloffset .dw 0 ;levelvaroffset .db 0 ;numactualenemies .db 0 ;numshots .dw shottable ;shotptr .db 0 ;numenemies .dw enemytable ;enemyptr .db 0 ;numenemyshots .dw enemyshottable ;enemyshotptr .db (SCRNBUF_HEIGHT-VISIBLE_HEIGHT)/2 ;screencoord .db 0 ;barflag var_defaults_end: game_vars: level_complete: .db 0 select_high: .db 0 savename: .db 0,0,0,0,0,0,0,0,0,0 titleflag: .db 0 xtonflag: .db 0 invincible: .db 0 score: .dw 0 scorequeue: .dw 0 dead: .db 0 ;if dead > 0, you are dead lives: .db 0,$ff ;life string ending shipY: .db 0 shipX: .db 0 shipcoordbuf: .db 0 .db 0 .db 0 .db 0 .db 0 .db 0 .db 0 .db 0 .db 0 .db 0 friendy: .db 0 friendx: .db 0 friendcoordptr: .dw 0 leftfriend: .db 0 ;flags for the helper ships friendshotptr: .dw 0 shields: .db 0 health: .db 0 healthqueue: .db 0 ;queue of damage/restoration weapon: .db 0 maxweapon: .db 0 levelpointer: .dw 0 leveloffset: .dw 0 levelvaroffset: .dw 0 ;a pointer to the highscore value/name ;=========== pointers and counters that MUST stay intact numactualenemies: .db 0 numshots: .db 0 ;number of shots in the shottable shotptr: .dw 0 ;pointer to the first open slot in the shottable numenemies: .db 0 enemyptr: .dw 0 ;pointer to the first open slot in the enemytable numenemyshots: .db 0 enemyshotptr: .dw 0 ;=========== screencoord: .db 0 barflag: .db 0 ;non-zero = bar is dirty gametimer: .dw 0 barspr: .db %11111111,%11111111,%11111111,%10011111,%11111111,%11111111,%11111111,%11111111,%11111111,%11111111,%11111111,%11111111 .db %10010001,%00000000,%00000000,%10100000,%10101010,%10000000,%00000101,%01010101,%00000000,%00000000,%00000000,%00000000 .db %00100001,%11111111,%11111111,%00100000,%01010101,%00111000,%01110010,%10101010,%00110011,%00000000,%00000000,%00000000 .db %01111010,%00000000,%00000000,%01001010,%01101011,%01100001,%01010011,%01010110,%01000100,%01000000,%00000000,%00000000 .db %00010010,%00000000,%00000000,%01011111,%01010101,%01111100,%01010010,%10101010,%00100100,%00000000,%00000000,%00000000 .db %00100100,%11111111,%11111111,%10001110,%01101011,%01100001,%01010011,%01010110,%00010100,%01000000,%00000000,%00000000 .db %00000101,%00000000,%00000000,%10000100,%01010101,%00111000,%01110010,%10101010,%01100011,%00000000,%00000000,%00000000 .db %11111001,%11111111,%11111111,%10000000,%10101010,%10000000,%00000101,%01010101,%00000000,%00000000,%00000000,%00000000 titlespr: .db %00001111,%00110000,%00000000,%00000110,%00000000,%00000000,%00000001,%11000110,%00000000,%00000000 .db %00001111,%10110000,%00000000,%00000110,%00000000,%00000000,%00000011,%11000110,%00000000,%00000000 .db %00001101,%10111110,%01111011,%11101111,%00111001,%11111110,%00000110,%00001111,%00111101,%11110000 .db %00001111,%10111110,%11111011,%11101111,%01111101,%11111110,%00000111,%11001111,%01111101,%11110000 .db %00001111,%00110110,%11011011,%01100110,%01101101,%10110110,%00000011,%11100110,%01101101,%10110000 .db %00001100,%00110110,%11011011,%01100110,%01101101,%10110110,%00001000,%01100110,%01101101,%10000000 .db %00001100,%00110110,%11111011,%01100110,%01111101,%10110110,%00001111,%11100110,%01111101,%10000000 .db %00001100,%00110110,%01111011,%01100110,%00111001,%10110110,%00000111,%10000110,%00111101,%10000000 putnumspr: putnum0: .db %01111000 .db %11111100 .db %11001100 .db %11001100 .db %11001100 .db %11111100 .db %01111000 .db 0 putnum1: .db %00110000 .db %01110000 .db %11110000 .db %00110000 .db %00110000 .db %11111100 .db %11111100 .db 0 putnum2: .db %01111000 .db %11111100 .db %11001100 .db %00011000 .db %00110000 .db %01111100 .db %11111100 .db 0 putnum3: .db %01111000 .db %11111100 .db %00001100 .db %01111100 .db %00001100 .db %11111100 .db %01111000 .db 0 putnum4: .db %01001000 .db %11001100 .db %11001100 .db %11111100 .db %01111100 .db %00001100 .db %00001100 .db 0 putnum5: .db %11111100 .db %11111100 .db %11000000 .db %11111000 .db %00001100 .db %11011100 .db %11111000 .db 0 putnum6: .db %01111000 .db %11111100 .db %11000000 .db %11111000 .db %11001100 .db %11001100 .db %01111000 .db 0 putnum7: .db %11111100 .db %11111100 .db %11001100 .db %00011000 .db %00110000 .db %01100000 .db %11000000 .db 0 putnum8: .db %01111000 .db %11001100 .db %11001100 .db %01111000 .db %11001100 .db %11001100 .db %01111000 .db 0 putnum9: .db %01111000 .db %11001100 .db %11001100 .db %01111100 .db %00001100 .db %00001100 .db %00011000 .db 0 sputnumspr: sputnum0: .db %11100000 .db %10100000 .db %10100000 .db %10100000 .db %11100000 sputnum1: .db %00100000 .db %00100000 .db %00100000 .db %00100000 .db %00100000 sputnum2: .db %11000000 .db %00100000 .db %01000000 .db %10000000 .db %11100000 sputnum3: .db %11100000 .db %00100000 .db %01100000 .db %00100000 .db %11100000 sputnum4: .db %10100000 .db %10100000 .db %11100000 .db %00100000 .db %00100000 sputnum5: .db %11100000 .db %10000000 .db %11100000 .db %00100000 .db %11000000 sputnum6: .db %11100000 .db %10000000 .db %11100000 .db %10100000 .db %11100000 sputnum7: .db %11100000 .db %00100000 .db %01000000 .db %01000000 .db %01000000 sputnum8: .db %11100000 .db %10100000 .db %11100000 .db %10100000 .db %11100000 sputnum9: .db %11100000 .db %10100000 .db %11100000 .db %00100000 .db %11100000 press2ndtxt: .db "Press 2nd",0 authortxt: .db "By Joe Pemberton",0 copyrighttxt: .db "(C) 2002",0 contrasttxt: .db "+/- to adjust contrast",0 inverttxt: .db "XTON to invert screen",0 savednotfoundtxt: .db " External Level ",0 .db " Not Found!",0 #ifdef TI83P savedarchivedtxt: .db " External Level ",0 .db " Is Archived! " .db " Erase Saved " .db " Game? (Y/N)",0 #endif highscorenumtxt: .db "1. ",0 highscorenumtxt2: .db "1.",0 highscoretxt: .db " - ",0 highscoremsg: .db " Highscore! " .db "Enter your name:",0 scoretxt: .db "Score: ",0 bonustxt: .db "Bonus: ",0 totaltxt: .db "Total: ",0 selectleftstr: .db $cf," ",0 selectrightstr: .db " ",$05,0 highscoretitle: .db " Highscores ",0 progstr: #ifdef TI83P .db $BB,$6D #endif .db $c9,"PSv0.3",0 progstr_end: ;=========== object tables ============= tables: startable: .ds STAR_TABLE_SIZE ; x, size, y temptable: textbuffer: shottable: .ds SHOT_TABLE_SIZE ; timer(1), pathptr(2), x(1), y(1), spriteptr(2), damage(1) enemytable: .ds ENEMY_TABLE_SIZE ; flag(1), health(1), startpathcnt(1), startpathptr(2), pathcnt(1), pathcntmax(1), pathptr(2) ; x(1), y(1), height(1), spritecnt(1), spriteptr(2), shottimermax(1), shottimer(1), shotroutineptr(2) enemyshottable: .ds ENEMY_SHOT_TABLE_SIZE ; timer(1), xinc(2), x(2), yinc(2), y(2), spriteptr(2), damage(1) .end END