* --------------- 
* IDOUDO  29/06/2001 - 17/08/2001
*
* 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
keyp   = $25	; storage for current keypresses (should be cleared when used)
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)

; 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  = $37	; is it the CPU or your go? - 0 = CPU non 0 = you 
level  = $38	; level currently played - also offset to last seqmap entry
seqcnt = $39	; counter for sequences
seqmap = $4C	; previous sequences - 
bw	 = $6F	; when clearing, is it black or white, ah init cute!(not used)

* -----------
* 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

* 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
	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
	MOV	#$FF,keyp
* the main game engine all runs from here
.gameloop:
	MOV	#$01,gmode			; mode = empty with record
      MOV  	#<cempty,trl		; empty
      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 
	MOV	#$02,gmode			; mode = correct
	CALL	drawicons			; draw icons, uses global mode
	CALL 	osblit			; show
;	CALL 	halfsec
	CALL 	halfsec
	INC	seqcnt			; next seq?
	LD	level
	BNE	seqcnt,.gameloop
	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,trl		; empty
      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
	LD	keyp
	BNE	#$FF,.gkey	
	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,trl		; 
      MOV  	#>cup,trh		; high address
      CALL 	setscr		; draw
	CALL	drawicons		; draw icons, uses global mode
	CALL 	osblit		; show
	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,trl		; 
      MOV  	#>cdown,trh		; high address
      CALL 	setscr		; draw
	CALL	drawicons		; draw icons, uses global mode
	CALL 	osblit		; show
	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,trl		; 
      MOV  	#>cleft,trh		; high address
      CALL 	setscr		; draw
	CALL	drawicons		; draw icons, uses global mode
	CALL 	osblit		; show
	LD	ans			; load the answer
	BNE	#$02,.neg		; no match
	XOR	ACC			; clear the acc ready 4 return
	BR	.quiz			; yeah! u did g00d
.prt:
	MOV	#$01,gmode		; mode = empty
      MOV  	#<cright,trl		; 
      MOV  	#>cright,trh		; high address
      CALL 	setscr		; draw
	CALL	drawicons		; draw icons, uses global mode
	CALL 	osblit		; show
	LD	ans			; load the answer
	BNE	#$03,.neg		; no match
	XOR	ACC			; clear the acc ready 4 return
	BR	.quiz			; yeah! u did g00d
.negs:
	BR	.nothing
.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:		
	MOV	#$FF,keyp		; clear		
	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.
*
wait4D:
	PUSH	ACC
	LD	keyp
	BNE	#$FF,.gkey	
.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 
.gkey:
	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:
      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:
      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,trl			; empty
      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,trl			; load address of screen
      MOV  #>cup,trh			; high
      CALL setscr				; draw to off screen
	BR	.showdelay
.showright:
      MOV  #<cright,trl			; load address of screen
      MOV  #>cright,trh			; high
      CALL setscr				; draw to off screen
	BR	.showdelay
.showdown:
      MOV  #<cdown,trl			; load address of screen
      MOV  #>cdown,trh			; high
      CALL setscr				; draw to off screen
	BR	.showdelay
.showleft:
      MOV  #<cleft,trl			; load address of screen
      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:
	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
	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:
	LD    gmode
	BNE   #$00,.n0		; not 0?
	MOV   #$00,ACC		; top
      MOV   #<iplay,trl		; load address of screen
      MOV   #>iplay,trh		; high
	CALL	drawicon
	BR	.jobdone
.n0:
	MOV   #$00,ACC		; top
      MOV   #<irec,trl		; load address of screen
      MOV   #>irec,trh		; high
	CALL	drawicon
	LD    gmode
	BNE   #$01,.n1		; not 1?
	MOV   #$01,ACC		; bottom
      MOV   #<iempty,trl	; load address of screen
      MOV   #>iempty,trh	; high
	CALL	drawicon
	BR	.jobdone
.n1:
	BNE   #$02,.n2		; not 2?
	MOV   #$02,ACC		; bottom
      MOV   #<itick,trl		; load address of screen
      MOV   #>itick,trh		; high
	CALL	drawicon
	BR	.jobdone
.n2:
	MOV   #$02,ACC		; bottom
      MOV   #<icross,trl	; load address of screen
      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:
; *******************
;	LD	seed
;	ST	dbug
;	.byte $50,dbug
;	.byte $50,gimble
; *******************
	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,trl
      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,trl
      MOV  #>pressa, trh
      CALL drawlabel          ; draw the [press A] label
	CALL osblit
	MOV  #$FF,keyp
	CALL halfsec	      ; wait 1/2 a sec
	LD   keyp
      BN   ACC,4,.scrollq	      ; branch if button A is pressed
      MOV  #<credits,trl
      MOV  #>credits,trh
      CALL setscr             
	CALL osblit
	MOV  #$FF,keyp
	CALL halfsec
	LD   keyp
      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,TRL
      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,trl
      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
.gmbp:
      MOV  #<pressa,trl
      MOV  #>pressa, trh
      CALL drawlabel             ; draw the [press A] label
	CALL osblit
	MOV  #$FF,keyp
	CALL halfsec	; wait 1/2 a sec
	LD   keyp
      BN   ACC,4,.gmoq	      ; branch if button A is pressed
      MOV  #<gmoverscr,trl
      MOV  #>gmoverscr,trh
      CALL setscr             ; this copies an image to the LCD screen
	CALL osblit
	MOV  #$FF,keyp
	CALL halfsec
	LD   keyp
      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"



* ---------------
* 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  #$04,ACC		; 1F for emu, 4 for real
	CALL delay
	POP  ACC
	RET


* -----------
*  delay
* while delaying gets inputs and stores them in keyp
delay:
	PUSH B
.delay:
	CALL vssec
	ST   B
	LD   keyp
	BNE  #$FF,.ndel		; have we got a key press yet
      CALLF getkeys		; nope
	ST   keyp			; ah, now we have
.ndel:
	LD   B
	DBNZ ACC,.delay
	POP  B
	RET


* -----------
* a delay of the delay loop :)
* ?th of a second delay
vssec:
	PUSH ACC
	MOV  #$FF,ACC
.vssec:
	DBNZ ACC,.vssec
	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,trl
      MOV  #>pressa, trh
      CALL drawlabel             ; draw the [press A] label
	CALL osblit
	MOV  #$FF,keyp
	CALL halfsec	; wait 1/2 a sec
	LD   keyp
      BN   ACC,4,.gmoq	      ; branch if button A is pressed
	CALL	osclear
	CALL	printscore
	CALL osblit
	MOV  #$FF,keyp
	CALL halfsec
	LD   keyp
      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,trl
      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,trl
      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:
	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
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 - loads Port 3 data into ACC
*           also handles Mode-button (QUIT), Dreamcast Connect and Sleep
* direct copy of Marcus's Tetris code (as part of the skeleton code)

getkeys:
       bp p7,0,quit            ; Quit, if Dreamcast Connection
       ld p3                   ; read key status
       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


