* --------------- * IDOUDO 29/06/2001 - 17/08/2001 (24/08/2001 cleaned up a few probs) * * Rednuht 2001 * www.jumpstation.co.uk * idoudo@jumpstation.co.uk * * idoudo is created from my space invaders game and may inherit the same faults :) * * the game has two distinct phases * phase 1: the CPU plays back the current sequence (icon=play) * phase 2: (icon=rec) player must re-enter the sequence as played by the CPU * Get it right and the sequence gets longer * get it wrong and its 'game over baby' * * I am especialy proud of the dodgy randomisation stuff which i wrote from scratch. * and the large numbers which should have been easier to do. * * I claim full credit for of all the work here excluding; * The FINDLINE rountine : Alessandro * GETKEYS and SETSCR : Marcus (he also supplied the original sfr.i) * The initialiation comments etc : Tyro * (without the skeleton source I would not have even started coding my space invaders game) * * this code is released under !GNU, cos I really could not give a damm what u do with it. * (U should have not recieved a copy of the !GNU licence in this archive as there is not one) * --------------- .include "sfr.i" ; main defintions for registers etc BSWAP EQU 0 ; VRMAD2 bank swapper ;========================================================================= dbug = $22 ; temp var 4 debuging bcc = $25 ; buffer counter, number of bytes in the key buffer sprto = $26 ; used to draw the two byte icons yyy = $27 ; temp var used to hold y pos 4 icons ans = $28 ; holds the current answer seed = $29 ; current seed will be appended by half seconds gimble = $2A ; starts as 1000 and the 0100 0010 0001 resets to 1000 (used as a mask) gimble2= $2B ; starts as 1000 and the 0100 0010 0001 resets to 1000 (used as positive) gimbleT= $2C ; as above but tempary 0100 0010 0001 resets to 1000 (used as positive) rowc = $2D ; row counter for the score digit display bytec = $2E ; byte counter for the score digit display disbo = $2F ; display offset for the score digit display digo = $30 ; Digit offset for the score digit display (left or right) (10's or 1's) bw = $31 ; when clearing, is it black or white, ah init cute!(not used) ; no time limit cos im lazy and i do not expect ppl 2 record the sequences by hand before trying them, that wood b sad, like i did on the classic sifi Amiga game B.A.T. gmode = $32 ; is it the CPU or your go? - 0 = CPU non 0 = you level = $33 ; level currently played - also offset to last seqmap entry seqcnt = $34 ; counter for sequences seqmap = $35 ; previous sequences - max 32 buffer = $55 ; buffer for key strokes - max 100 * ----------- * Default initialisation stuff * leave well alone, until you need it) * .org 0 jmpf start .org $3 jmp nop_irq .org $b jmp nop_irq .org $13 jmp nop_irq .org $1b jmp t1int .org $23 jmp nop_irq .org $2b jmp nop_irq .org $33 jmp nop_irq .org $3b jmp nop_irq .org $43 jmp nop_irq .org $4b clr1 p3int,0 clr1 p3int,1 nop_irq: reti .org $130 t1int: push ie clr1 ie,7 not1 ext,0 jmpf t1int pop ie reti .org $1f0 goodbye: not1 ext,0 jmpf goodbye ; leave game mode * ---------------- * File information * .org $200 .byte "-I-DO--U-DO- " ; 16 byte .byte "http://www.jumpstation.co.uk " ; 32 byte * ---------------- * File icon stuff * .org $240 .word 1 ; number of icons (max = 3) .word 10 ; animation speed * Icon file created with img2byteicon - see http://www.jumpstation.co.uk/img2 for more details .include "icon.i" * more default initialisation stuff start: mov #$a1,ocr ; write $a1 into Oscillation Control Register (OCR) mov #$09,mcr ; write $09 into Mode Control Register (MCR) mov #$80,vccr ; write $80 into LCD Contrast Control Register (VCCR) clr1 p3int,0 ; clear bit 0 in Port 3 Interrupt Control Register ; (P3INT) clr1 p1,7 ; clear bit 7 in Port 1 Latch (P1) mov #$ff,p3 ; write $ff in Port 3 Latch (P3) * set seed as one off MOV #$97,seed ; got to start somewhere MOV #%00000001,gimble ; needed for ... MOV #%00001000,gimble2 ; ... the randomisation stuff CALL osclear CALL clrkey * Each time you play the game start here resetgame: MOV #$00,bw MOV #$00,level ; its not level one until next level is called. CALL loadscr ; startup the loading screen ;.testing ; ;CALL chkkey ; LD bcc ; ST level ; CALL osclear ; CALL printscore ; CALL osblit ; CALL halfsec ; CALL halfsec ; CALL halfsec ; CALL halfsec ; CALL halfsec ; ;CALLF getkeys ; BR .testing CALL seedup ; change the random seed based on the time the user took to press A for a start .next: CALL nextlevel ; all the per level initialasion stuff goes here MOV #$00,seqcnt * the main game engine all runs from here .gameloop: MOV #$01,gmode ; mode = empty with record MOV #cempty,trh ; high address CALL setscr ; draw CALL drawicons ; draw icons, uses global mode CALL osblit ; show ; CALL wait4D ; wait until they have let go of the D pad before checking to see what they r doing now ; read current sequence entry MOV #seqmap,1 ; load address of map into @R1 pointer. LD 1 ; sequence offset ADD seqcnt ; added to the address ST 1 LD @R1 ; value from map into ACC ST ans ; saves it again CALL checkD ; load ACC with answer and see what we get back. BNZ .nope ; oh dear .good:; / ;show \/ CALL seedup ; change the random seed based on the time the user took INC seqcnt ; next seq? LD level BNE seqcnt,.gameloop MOV #$02,gmode ; mode = correct CALL drawicons ; draw icons, uses global mode CALL osblit ; show ; CALL halfsec CALL halfsec BR .next ; always go back .nope:; \/ ;show X /\ CALL seedup ; change the random seed based on the time the user took MOV #$03,gmode ; mode = failed attempt MOV #cempty,trh ; high address CALL setscr ; draw CALL drawicons ; draw icons, uses global mode CALL osblit ; show CALL halfsec CALL halfsec CALL halfsec CALL halfsec CALL showscore CALL gameover BR resetgame ; only way out is to quit * --------- * Seed updater * uses the half second bit and changes the seed by rotaing this in. * only call after the user has done something, * as all other actions will product calulatable results. * i.e. after waiting for the user to press a button. * never resets * seedup: PUSH ACC CLR1 PSW,RAMBK0 ; switch to the other bank, LD $1D SET1 PSW,RAMBK0 ; switch to the other bank, BN ACC,0,.even ; even number of seconds .odd: SET1 PSW,CY ; set CY BR .rci .even: CLR1 PSW,CY ; clear CY .rci: LD seed ROLC ; rotates in the carry flag ST seed POP ACC RET * --------- * Gimble rotator * takes 1 as the address of the gimble and rotates it * can only be one of four values * 0000 1000 -> 0000 0100 * 0000 0100 -> 0000 0010 * 0000 0010 -> 0000 0001 * 0000 0001 -> 0000 1000 * these bit patterns are then used to mask different direction u,d,l,r * grotate: PUSH ACC LD @R1 ; load the gimble into ACC ROR ; note lmsb is pushed round so 00000001 becomes 10000000 BNE #%10000000,.quit ; not at the end yet MOV #%00001000,ACC ; default start value .quit: ST @R1 ; update the gimble POP ACC RET * --------- * take ans as the correct answer for current sequence * checks what the player is actualy pressing and returns 0 for failure * happy 2 wait 4 ever checkD: ; check each posibility for a positve result CALLF getkeys .gkey: BN ACC,0,.pup ; positive up test BN ACC,1,.pdn ; positive down test BN ACC,2,.plt ; positive left test BN ACC,3,.prt ; positive right test BR .negs .pup: MOV #$01,gmode ; mode = empty MOV #cup,trh ; high address CALL setscr ; draw CALL drawicons ; draw icons, uses global mode ; CALL vssec CALL osblit ; show ; wait for key to be released .w4U: CALLF getkeys ; whats happen'n man? BN ACC,0,.w4u ; if its on, go back until its off LD ans ; load the answer BNE #$00,.neg ; no match XOR ACC ; clear the acc ready 4 return BR .quiz ; yeah! u did g00d .pdn: MOV #$01,gmode ; mode = empty MOV #cdown,trh ; high address CALL setscr ; draw CALL drawicons ; draw icons, uses global mode ; CALL vssec CALL osblit ; show .w4D: CALLF getkeys ; howes it hang'n? BN ACC,1,.w4d ; if its on, go back until its off LD ans ; load the answer BNE #$01,.neg ; no match XOR ACC ; clear the acc ready 4 return BR .quiz ; yeah! u did g00d .plt: MOV #$01,gmode ; mode = empty MOV #cleft,trh ; high address CALL setscr ; draw CALL drawicons ; draw icons, uses global mode ; CALL vssec CALL osblit ; show .w4L: CALLF getkeys ; sunny side up? BN ACC,2,.w4l ; if its on, go back until its off LD ans ; load the answer BNE #$02,.neg ; no match XOR ACC ; clear the acc ready 4 return BR .quiz ; yeah! u did g00d .negs: BR .nothing .prt: MOV #$01,gmode ; mode = empty MOV #cright,trh ; high address CALL setscr ; draw CALL drawicons ; draw icons, uses global mode ; CALL vssec CALL osblit ; show .w4R: CALLF getkeys ; u what? BN ACC,3,.w4r ; if its on, go back until its off LD ans ; load the answer BNE #$03,.neg ; no match XOR ACC ; clear the acc ready 4 return BR .quiz ; yeah! u did g00d .neg: ; and if nothing then look for a negitive MOV #$FF,ACC ; uh oh ! u did not enter the correct sequence entry BR .quiz .nothing: BRF checkD ; and finaly if nothing then loop, we are waiting here .quiz: ; CALL wait4D ; wait for the user to let go of the direction ; CALL halfsec RET ; chow * --------- * wait until id10t lets go of the D pad before reading the actual response. * obsolete now we have the buffering system ;wait4D: ; PUSH ACC ;.w4U: CALLF getkeys ; whats happen'n man? ; BN ACC,0,.w4u ; if its on, go back until its off ;.w4D: CALLF getkeys ; howes it hang'n? ; BN ACC,1,.w4d ; if its on, go back until its off ;.w4L: CALLF getkeys ; sunny side up? ; BN ACC,2,.w4l ; if its on, go back until its off ;.w4R: CALLF getkeys ; u what? ; BN ACC,3,.w4r ; if its on, go back until its off ; POP ACC ; RET ; go bye bye now * --------- * an offscreen buffer system * last thing to be added * it takes image/screen data from work RAM and blits it to the LCD * all existing screen related operations can refer to the work RAM * the off screen data is formated so that it can be byte for byte directly copied to the LCD osblit: CALL chkkey CLR1 ocr,5 ; speed PUSH ACC PUSH B PUSH C PUSH 2 PUSH xbnk PUSH VSEL PUSH VRMAD1 PUSH VRMAD2 SET1 VSEL,4 ; autoincrement please MOV #$80,VRMAD1 ; the pointer to our off screen buffer MOV #$80,2 ; the pointer to the LCD memory XOR ACC ; clear the ACC ST xbnk ; start with the top 16 pixels CLR1 VRMAD2,BSWAP ; start with the top 16 pixels .blit: LD VTRBF ; load 8 pixels of screen data ST @R2 ; save it to the LCD memory LD VRMAD1 ; get ready BNZ .continue ; if its zero then we need to change the bank MOV #$80,2 ; the pointer to the LCD memory MOV #$80,VRMAD1 ; the pointer to our off screen buffer SET1 VRMAD2,BSWAP ; next memory bank please BN xbnk,BSWAP,.b2 ; have we been here before? BR .quitin ; lets get out of here .b2: SET1 xbnk,BSWAP ; next LCD memory bank BR .blit ; more blittin .continue: INC 2 ; next LCD memory location BR .blit ; still more to go .quitin: POP VRMAD2 POP VRMAD1 POP VSEL POP xbnk POP 2 POP C POP B POP ACC SET1 ocr,5 ; put on the breaks RET ; finished blitting image data * ----------------- * clears the offscreen buffer system osclear: CALL chkkey CLR1 ocr,5 ; go faster now PUSH ACC PUSH B PUSH C SET1 VSEL,4 ; autoincrement please MOV #$80,VRMAD1 ; the pointer to our off screen buffer XOR ACC ; clear the ACC ST VRMAD2 ; start with the top 16 pixels ST B ; save it MOV #$81,C ; setup counter .clear: LD bw ; is it night or day? ST VTRBF ; clear 8 pixels of the offscreen buffer DEC C ; one less LD C ; better check BNZ .continue ; if its zero then we need to change the bank MOV #$81,C ; just so we get here again MOV #$80,VRMAD1 ; the pointer to our off screen buffer SET1 VRMAD2,BSWAP ; next memory bank please INC B ; next 16 pixels please LD B ; get ready BE #$02,.quitin ; we are updating the LCD icon memory today .continue: BR .clear ; still more to go .quitin: POP C POP B POP ACC CLR1 VSEL,4 ; disable autoincrement SET1 ocr,5 ; wooh horsey RET ; finished blitting image data * ------------- * initialise the level * setup what happens next * includes play back of current code. nextlevel: * add to the code list * play back current code list. * draw on main blank screen * xor on icon for play back and greyed out rec. * draw directional screen plus xor icons. INC level CALL a2cl ; add a new sequence to the code list. CALL playback ; play the current play list RET *------------- * Play back the current sequence * Read offset in map show correct img and wait, repeat * Preserves all regs used. playback: PUSH ACC PUSH 1 ; @R1 MOV #seqmap,1 ; load address of map into @R1 pointer. MOV #$00,gmode ; mode = play back MOV #$00,seqcnt .nplay: MOV #cempty,trh ; high address CALL setscr ; draw CALL drawicons ; draw icons, uses global mode CALL osblit ; show ; wait a second or two ; CALL halfsec ; CALL halfsec MOV #seqmap,1 ; load address of map into @R1 pointer. LD 1 ; sequence offset ADD seqcnt ; added to the address ST 1 LD @R1 ; load value in map BE #$00,.showup ; up BE #$01,.showdown ; down BE #$02,.showleft ; left BE #$03,.showright ; right BR .quit .showup: MOV #cup,trh ; high CALL setscr ; draw to off screen BR .showdelay .showright: MOV #cright,trh ; high CALL setscr ; draw to off screen BR .showdelay .showdown: MOV #cdown,trh ; high CALL setscr ; draw to off screen BR .showdelay .showleft: MOV #cleft,trh ; high CALL setscr ; draw to off screen BR .showdelay ; waste of space but it keeps everything the same ;) .showdelay: CALL drawicons ; draw icons, uses global mode CALL osblit ; show to customer ; wait a second or two CALL halfsec ; CALL halfsec ; CALL halfsec ; CALL halfsec INC seqcnt ; next seq? LD level BNE seqcnt,.nplay .quit: POP 1 POP ACC RET ; get out of here *------------- * draws icons as necessary * expects trl and trh to be populated with the address of the icon. * ACC is 0 for top pos, else scond pos * drawicon: CALL chkkey CLR1 ocr,5 ; go faster now PUSH ACC BZ .toppos MOV #$14,yyy ; row 20 BR .n00l .toppos: MOV #$02,yyy ; row 2 .n00l: MOV #$00,sprto ; offset in sprite data .nlne: LD yyy CALL findline ST VRMAD1 LD sprto ; offset to sprite data LDC ; huge address ST B ; save it (sprite data that is) MOV #32,ACC ; x CALL findbytes ; takes the input of X coord and returns ; the offset for byte 1, byte one shifted and byte two shifted ADD VRMAD1 ; add current offset with new offset ST VRMAD1 ; save it ready for screen access LD B ; sprite data 1 ;XOR VTRBF ; remove/mask what is already on the screen. ST VTRBF ; OUTPUT !! LD C ; load sprite 2 BZ .sbyte ; skip the next bit as the sprite2 has no new bits INC VRMAD1 ; second sprite byte please (increment the byte address to the lcd memory) XOR VTRBF ; join ST VTRBF ; OUTPUT !! .sbyte: ; second part of the 2 byte sprite/icon INC sprto LD yyy CALL findline ST VRMAD1 LD sprto ; offset to sprite data LDC ; huge address ST B ; save it (sprite data that is) MOV #40,ACC ; x CALL findbytes ; takes the input of X coord and returns ; the offset for byte 1, byte one shifted and byte two shifted ADD VRMAD1 ; add current offset with new offset ST VRMAD1 ; save it ready for screen access LD B ; sprite data 1 ;XOR VTRBF ; remove/mask what is already on the screen. ST VTRBF ; OUTPUT !! LD C ; load sprite 2 BZ .tbyte ; skip the next bit as the sprite2 has no new bits INC VRMAD1 ; second sprite byte please (increment the byte address to the lcd memory) XOR VTRBF ; join ST VTRBF ; OUTPUT .tbyte: ; is that all ? INC yyy INC sprto LD sprto BNE #$16,.nlne ; fini POP ACC SET1 ocr,5 ; go slow RET ; quit'ola *------------- * Sorts which icons need to be displayed and calls drawicon with them setup as needed. * uses gmode; 0=play,1=rec+empty,2=rec+tick,>3=rec+cross * drawicons: CALL chkkey LD gmode BNE #$00,.n0 ; not 0? MOV #$00,ACC ; top MOV #iplay,trh ; high CALL drawicon BR .jobdone .n0: MOV #$00,ACC ; top MOV #irec,trh ; high CALL drawicon LD gmode BNE #$01,.n1 ; not 1? MOV #$01,ACC ; bottom MOV #iempty,trh ; high CALL drawicon BR .jobdone .n1: BNE #$02,.n2 ; not 2? MOV #$02,ACC ; bottom MOV #itick,trh ; high CALL drawicon BR .jobdone .n2: MOV #$02,ACC ; bottom MOV #icross,trh ; high CALL drawicon .jobdone: RET ; quit'ola *------------- * Adds a new value to the code list ready 4 play back * gets new value from random'ing * the gimble 1 stuff fails in that it may never goto the next 1 * a2cl: CALL chkkey LD gimble BNE #%00000001,.one LD seed ; source AND gimble ; BNE gimble,.one ; check MOV #$00,B ; set mini BR .setit ; ok .one: BNE #%00000010,.two LD seed ; source AND gimble ; BNE gimble,.two ; check MOV #$01,B ; set mini BR .setit ; ok .two: BNE #%00000100,.thr LD seed ; source AND gimble ; BNE gimble,.thr ; check MOV #$02,B ; set mini BR .setit ; ok .thr: BNE #%00001000,.extr LD seed ; source AND gimble ; BNE gimble,.extr ; check MOV #$03,B ; set mini BR .setit ; ok .extr: ; no match has been found so fall back ; ******************* ; .byte $50,gimble2 ; ******************* MOV #gimble2,1 CALL grotate ; next LD gimble2 ; absolute BNE #%00001000,.one1 MOV #$00,B BR .setit ; ok .one1: BNE #%00000100,.two1 MOV #$01,B BR .setit ; ok .two1: BNE #%00000010,.thr1 MOV #$02,B BR .setit ; ok .thr1: BNE #%00000001,.ngh MOV #$03,B BR .setit ; ok .ngh: ; ngh - Never Gonna Happen .setit: ; B holds the value to append MOV #seqmap,1 ; load address of map LD 1 ADD level ; add current eos DEC ACC ; adjust for non zero offset ST 1 XCH B ; get new value ST @R1 ; add it to the map INC 1 MOV #$04,@R1 ; force the gimble to rotate MOV #gimble,1 CALL grotate ; next RET ; get f**k out of here * ------------ * loading screen plus scrolling credits * loadscr: .nfre: CALLF getkeys BN ACC,4,.nfre ; wait until they let go of A ;show load screen wait time MOV #credits,trh CALL setscr CALL osblit ; wait a second or two CALL halfsec CALL halfsec CALL halfsec CALL halfsec MOV #$05,B ; BR .nfre .lmbp: MOV #pressa, trh CALL drawlabel ; draw the [press A] label CALL osblit CALL halfsec ; wait 1/2 a sec CALLF getkeys BN ACC,4,.scrollq ; branch if button A is pressed MOV #credits,trh CALL setscr CALL osblit CALL halfsec CALLF getkeys BN ACC,4,.scrollq ; branch if button A is pressed DEC B LD B BZ .lmoq ; only five times b4 scrolling BR .lmbp .stopg: BR loadscr .lmoq: ; now we get down to a little scrolling MOV #credits,TRH MOV #$00,rowc .morescroll: CALL vssec ; on the emu this delay is unnessesary CALLF getkeys BN ACC,4,.scrollq ; branch if button A is pressed CALL setscr CALL osblit LD TRL ADD #$06 ; bytes per line ST TRL LD TRH ADDC #$00 ; will be 1 if the CY flag was set from the last addition ST TRH INC rowc LD rowc BNE #173,.morescroll CALL halfsec ; wait 1/2 a sec CALL halfsec ; wait 1/2 a sec CALL halfsec ; wait 1/2 a sec CALL halfsec ; wait 1/2 a sec BR .stopg .scrollq: ; it all ends here ! onto the game ! RET * ------------ * game over screen + [Press A] * show gameover screen wait time, A pressed to return gameover: MOV #gmoverscr,trh CALL setscr ; this copies an image to the LCD screen CALL osblit ; wait a second of two CALL halfsec CALL halfsec CALL halfsec CALL halfsec .gimp: CALLF getkeys BP ACC,4,.gimp ; loop until button A is released .gmbp: MOV #pressa, trh CALL drawlabel ; draw the [press A] label CALL osblit CALL halfsec ; wait 1/2 a sec CALLF getkeys BN ACC,4,.gmoq ; branch if button A is pressed MOV #gmoverscr,trh CALL setscr ; this copies an image to the LCD screen CALL osblit CALL halfsec CALLF getkeys BN ACC,4,.gmoq ; branch if button A is pressed BR .gmbp .gmoq: RET * find bytes ; Input ACC as X coordinate ; Input B as single byte representing pixels of a sprite ; Output ACC as offset to first byte for sprite data to be chucked at ; Output B as first byte of sprite data rotated to be correct on screen. ; Output C as second byte of sprite data rotated from the first findbytes: PUSH B ; can't lose this ST B ; back up the x coord ; Shift right to get divide by 8 CLR1 PSW,CY ; clear CY RORC ; divide by 2 i.e. 22 / 2 = 11 CLR1 PSW,CY ; clear CY RORC ; divide by 2 i.e. 11 / 2 = 5 CLR1 PSW,CY ; clear CY RORC ; divide by 2 i.e. 5 / 2 = 2 ; this is the answer to 1st byte ST C ; save it for later. CLR1 PSW,CY ; clear CY ROLC ; times 2 i.e. 2 * 2 = 4 CLR1 PSW,CY ; clear CY ROLC ; times 2 i.e. 4 * 2 = 8 CLR1 PSW,CY ; clear CY ROLC ; times 2 i.e. 8 * 2 = 16 ; pop ACC and times the answer, sub answer, whats left is bits to shift. ; i.e. x=22, 22/8=2, 2*8=16, 22-16=6 XCH B ; swap ACC with B SUB B ; i.e. 22-16 = 6 ; ACC now contains number of bits to shift by. POP B ; is once again the sprite data PUSH C ; protect offset number of bytes MOV #$00,C ; clear, ready for some shift'in .shapeshifter: BZ .quitin ; finished? XCH B ; the sprite data swapped for the loop counter CLR1 PSW,CY ; clear CY RORC ; bit 0 is push into the CY flag XCH C ; sprite 1 is swaped with sprite data 2 RORC ; previously mention CY flag is shifted into bit 7 XCH C ; swap sprite 2 back with sprite 1 XCH B ; swap sprite 1 back with loop counter DEC ACC ; decrement the loop counter BR .shapeshifter ; try again .quitin: POP ACC ; damm im naughty, pop C from the stack into the ACC. ; so B and C are the two sprite bytes shifted ; and ACC is the offset to the first byte RET ; "thank you, come again" * --------- * chkkey - checks for new imput and stores selection in growinging buffer * getkeys then reads from this buffer as apposed to the P3 directly * This function is expected to be called often (10x per sec seems ideal) * Presvers all registers so can be called fomr most places. * chkkey: PUSH ACC PUSH B LD P3 ; get input BE buffer,.nochg ; is it the same as bufffer +0 XCH B ; save new input 4 later MOV #buffer,1 ; no, #$FF release of a key LD bcc BE #100,.nochg ; buffers full ignore new requests INC bcc ; next free slot LD 1 ADD bcc ; counter ST 1 LD buffer ; hold buffer BE #$FF,.nochgi ; ST @R1 ; store it in the buffer+bcc XCH B ; ah, I wondered where we left that :) ST buffer ; save new value in the hold buffer BR .nochg .nochgi: XCH B ; ah, I wondered where we left that :) ST buffer ; save new value in the hold buffer DEC bcc .nochg: POP B POP ACC RET ; go away ! * -------- * Gets a value from the buffer and shuffles if necessary * returns value from buffer in ACC as if getkeys * getbuff: CALL chkkey ; do an update asap LD bcc BE #$00,.r0 ; return hold buffer BE #$01,.r1 ; returns buffer+1 but no shuffling occurs and we dec the counter. MOV #buffer+1,1 ; start of the real stack LD @R1 ; store it in the buffer+bcc CALL bufshuf ; sorts the buffer out BR .ooh ; out of here .r0: LD buffer ; hold buffer BR .ooh ; out of here .r1: MOV #buffer+1,1 ; start of the real stack LD @R1 ; store it in the buffer+bcc DEC bcc ; buffer empty again ; note: no shuffling .ooh: RET * ------- * shuffles the buffer to be a FIFO stack * ie moves all the bytes down a peg so 2 becomes 1, 3 becomes 2 etc * if we got here the pos 1 is disposible and counter is at least 2 * we decrement * bufshuf: PUSH ACC ; ******************* ; .byte $50,bcc ; .byte $51 ; ******************* MOV #$01,yyy .next: LD yyy BE bcc,.done ; check counter INC yyy MOV #buffer,1 ; no, #$FF release of a key LD 1 ADD yyy ; counter ST 1 LD @R1 ; store it in the buffer+bcc DEC 1 ST @R1 ; value moved back one in the buffer BR .next .done: DEC bcc POP ACC RET * ---------- * clears the input buffer * clrkey: MOV #$FF,buffer ; hold buffer empty MOV #$00,bcc ; di we need a whole sub for this? RET * --------------- * Routine kindly donated by Alessandro * my routine to do the same jobs was about 10 times larger * FindLine: ; ; takes a line-number in ACC (0 - 31) ; ; returns line-start in ACC ($80, $86, $90, $96 etc.) ; and sets VRMAD2-register to $00/$FF ; ; no other registers will be affected ; rol ; multiply with 8 rol rol ; bn acc,3,.fl_l1 sub #2 ; line start correction, if uneven line number .fl_l1: mov #0,VRMAD2 ; default: bank 0 bn acc,7,.fl_l2 mov #$FF,VRMAD2 ; select bank 1 if line-number was 16 or more .fl_l2: or #%10000000 ; add $80 (start of XRAM) ret ; because we use call we need to return * ----------- * half second delay(ish) * i changed this to be more accetable in the game its is no longer 1/2 a second * more like 1/4 sec halfsec: PUSH ACC ; MOV #$1F,ACC ; 1F for emu, 4 for real MOV #$01,ACC ; 1F for emu, 4 for real CALL delay POP ACC RET * ----------- * delay * delay: PUSH B .delay: CALL vssec DBNZ ACC,.delay POP B RET * ----------- * a delay of the delay loop :) * ?th of a second delay * edited to be more key press aware vssec: PUSH ACC MOV #$FF,ACC .vssec: DBNZ ACC,.vssec CALL chkkey POP ACC RET * ------ * divi - gives me a decimal score/level * example * input ACC = 56, B = 10 * output C = 5, ACC = 6 divi: MOV #$00,C .m: SUB B BP PSW,CY,.nomore ; is this still going INC C BR .m .nomore: ADD B ; get back to where we were RET * ----- * showscore - just handles showing the score * showscore: CALL osclear CALL printscore CALL osblit ; show to customer ; wait a second or two CALL halfsec CALL halfsec CALL halfsec CALL halfsec .gmbp: MOV #pressa, trh CALL drawlabel ; draw the [press A] label CALL osblit CALL halfsec ; wait 1/2 a sec CALLF getkeys BN ACC,4,.gmoq ; branch if button A is pressed CALL osclear CALL printscore CALL osblit CALL halfsec CALLF getkeys BN ACC,4,.gmoq ; branch if button A is pressed BR .gmbp .gmoq: RET * ---------- * Print level * idoudo version has nice big characters * printscore: ; run level through decmialiser LD level MOV #10,B ; tens CALL divi XCH C ; calc offset in number table MOV #numtab,trh .tten: BZ .stens XCH B LD TRL ADD #50 ; bytes per digit ST TRL LD TRH ADDC #$00 ; if we went over the limit then the carry flag is added to the high byte ST TRH XCH B DEC ACC BR .tten .stens: ; output tens address of digit is TRH(*256)+TRL+ACC MOV #$01,digo ; 1 byte offset (8 pixels) CALL digit ; run level through decmialiser XCH C ; as stored earlier ; calc offset in number table MOV #numtab,trh .tunt: BZ .sunt XCH B LD TRL ADD #50 ; bytes per digit ST TRL LD TRH ADDC #$00 ; if we went over the limit then the carry flag is added to the high byte ST TRH XCH B DEC ACC BR .tunt .sunt: ; output units MOV #$03,digo ; 3 byte offset (24 pixels) CALL digit RET * ---------- * digit takes TRH(*256)+TRL+ACC and outputs the gfx data in a 2x25 byte matrix (16x25 pixels) * digit: ST B MOV #$00,rowc MOV #$00,disbo MOV #$00,bytec .mre: MOV #$04,ACC ; start line for digits to be drawn ADD rowc ; rowcounter CALL findline ADD disbo ; 1st or 2nd byte of this 2 byte width object ADD digo ; left or right digit (10's or 1's) ST VRMAD1 ; set the address to start drawing in memory ; add the digit start offset LD B ADD bytec ; actual byte we want in the digit array LDC ; loads the byte from the big address ; not bit shuffling, we have it just where we want it XOR VTRBF ; remove/mask what is already on the screen. ST VTRBF ; OUTPUT !! LD disbo XOR #$01 ; if its 1 it will be 0 and visa versa ST disbo BNZ .skr INC rowc ; another line ? .skr: INC bytec MOV #50,ACC BNE bytec,.mre ; more to go RET * ----------- * drawlabel * draws label from trl,trh with bitmask * direct rip of setscr just a few changes * ANDs the bitmask first * then ORs the label data drawlabel: CALL chkkey PUSH VSEL CLR1 VSEL,4 ; disable autoincrement ; CLR1 ocr,5 PUSH acc PUSH c MOV #$D0,VRMAD1 ; start at line 10 XOR ACC ST VRMAD2 ST c .lsloop: LDC AND VTRBF ; take current screen data and AND it with the bit mask ST VTRBF INC VRMAD1 LD VRMAD1 AND #$f BNE #$c,.lsskip LD VRMAD1 ADD #4 ST VRMAD1 BNZ .lsskip MOV #$FF,VRMAD2 MOV #$80,VRMAD1 .lsskip: INC c LD c BNE #$42,.lsloop ; just eleven lines * 6 = $42 MOV #$D0,VRMAD1 ; start at line 10 XOR ACC ST VRMAD2 LD C ; keep the data pointer (the data follows the bitmask) .dsloop: LDC OR VTRBF ; take current screen data and OR it with the label ST VTRBF INC VRMAD1 LD VRMAD1 AND #$f BNE #$c,.dsskip LD VRMAD1 ADD #4 ST VRMAD1 BNZ .dsskip MOV #$FF,VRMAD2 MOV #$80,VRMAD1 .dsskip: INC c LD c BNE #$84,.dsloop ; just eleven lines POP c POP acc ; SET1 ocr,5 POP VSEL RET * ------------------------------------------------------ * Set Screen - Marcus's original * modified to store everything in work RAM * and as it is so common added the buffer system setscr: PUSH VSEL CLR1 VSEL,4 ; disable autoincrement clr1 ocr,5 push acc push VRMAD2 push c push VRMAD1 mov #$80,VRMAD1 xor acc CLR1 VRMAD2,BSWAP st c .sloop: ldc st VTRBF inc VRMAD1 ld VRMAD1 and #$f bne #$c,.sskip ld VRMAD1 add #4 st VRMAD1 bnz .sskip SET1 VRMAD2,BSWAP mov #$80,VRMAD1 .sskip: inc c ld c bne #$c0,.sloop pop VRMAD1 pop c pop VRMAD2 pop acc set1 ocr,5 POP VSEL ret pressA: .include "pressa.i" ; press A pop-up and bit mask (bitmask first) credits: .include "credit.i" ; main screen and scolling credits gmoverscr: ; game over screen .include "gameover.i" numtab: ; number table (fonts) .include "numbers.i" cempty: ; cross empty .include "cross_empty.i" cleft: ; cross left .include "cross_left.i" cright: ; cross right .include "cross_right.i" cup: ; cross up .include "cross_up.i" cdown: ; cross down .include "cross_down.i" iplay: ; icon play .include "icon_play.i" irec: ; icon rec .include "icon_rec.i" iempty: ; icon empty .include "icon_empty.i" itick: ; icon tick .include "icon_tick.i" icross: ; icon cross .include "icon_cross.i" * ---------------------------------------------------------------------- * getkeys - Marcus's original but using my buffering system * getkeys: bp p7,0,quit ; Quit, if Dreamcast Connection ; ld p3 ; read key status CALLF getbuff bn acc,6,quit ; if Mode Key pressed, Quit bn acc,7,sleep ; if Sleep Key pressed, then Pause ret ; otherwise return with pressed key in ACC quit: jmpf goodbye ; Long Jump, in case we are too far away ; for a 'normal' branch sleep: set1 pcon,0 ; activate HALT mode (saves power) bn p3,7,sleep ; wait until Sleep Key is released mov #0,vccr ; turn off LCD ; sleepmore: set1 pcon,0 ; activate HALT mode (saves power) bp p7,0,quit ; Docked? bp p3,7,sleepmore ; no Sleep Key pressed yet mov #$80,vccr ; turn on LCD again ; waitsleepup: set1 pcon,0 ; activate HALT modus (saves power) bn p3,7,waitsleepup br getkeys ; continue to wait for keypress * ---------------------------------------------------------------------- * End .cnop 0,$200 ; pad to an even number of blocks