; Miner for BK0010 / САПЕР ДЛЯ БК0010
; (c) 5-03-2012 VINXRU (aleksey.f.morozov@gmail.com)

;Target assembler: Turbo8
;----------------------------------------------------------------------------
; МЕНЮ

EntryPoint:      ; Игра очень прожорлива до стека
		MOV #16384., SP

		; Включение режима 256x256
		MOV #0233, R0
		EMT 016

		; Отключение курсора
		MOV #0232, R0
		EMT 016

		; Запуск таймера
		MOV #731., @#0177706
		MOV #0160, @#0177712

		; Выключаем прерывание клавиатуры
		MOV #64., @#0177660

		; Очистка экрана
Menu:		JSR PC, @#clearScreen

		; Вывод лого		
		MOV #bmpLogo, R0
		MOV #045020, R1
		MOV #37., R3
drawLogo0:	MOV #16., R2
drawLogo1:	MOV (R0)+,(R1)+
		SOB R2, drawLogo1
		ADD #32., R1
		SOB R3, drawLogo0

		; Вывод меню
		MOV #txtMenu, R0
		JSR PC, @#Print

MenuLoop:	; Инициализируем генератор случайных чисел.
                JSR PC, @#rand

		; Обрабатываем клавиши
		MOV @#0177662, R0

		; Анализ клавиши
		MOV #menuItems, R5
Menu2:		MOV (R5)+, R1
		BEQ MenuLoop
		CMPB R0, R1
		BEQ StartGame
		ADD #8., R5
		BR Menu2

;----------------------------------------------------------------------------
; ДАННЫЕ МЕНЮ

txtMenu:	.byte 10.,12.
		.asciz	<0222>"0. Лошара"
		.byte 10.,13.
		.asciz	<0222>"1. Новичок"
		.byte 10.,14.
		.asciz	"2. Любитель"
		.byte 10.,15.
		.asciz	"3. Профессионал"
		.byte  9.,22.
		.asciz	<0221>"(c) 2012 VINXRU"
		.byte  3.,23.
		.asciz	<0223>"aleksey.f.morozov@gmail.com"
		.byte	255.
		.even

menuItems:	.word '0, 9.,   9.,  3., 21006.	; клавиша, ширина, высота, кол-во бомб, положение на экране
		.word '1, 9.,   9., 10., 21006.
	 	.word '2, 13., 10., 20., 20486.
		.word '3, 16., 14., 43., 18432.
		.word 0

;----------------------------------------------------------------------------
; ИГРА

StartGame:      ; Параметры игрового поля
		MOV (R5)+, gameWidth
		MOV (R5)+, gameHeight
		MOV (R5)+, bombsCnt
		MOV (R5)+, playfieldVA

		; Очистка экрана
		JSR PC, @#clearScreen
		JSR PC, @#fillBlocks

		; Устанавливаем курсор в центр поля
		MOV gameWidth, R0
		ASR R0
		MOV R0, cursorX
		MOV gameHeight, R0
		ASR R0
		MOV R0, cursorY

		; Очистка основных переменных
		CLR bombsPutted
		CLR gameOverFlag
		CLR time

		; Очистка игрового поля
		CLR R0
		MOV #254., R1
StartGame1:	MOVB R1, playfield(R0)
		CLRB userMarks(R0)
		INCB R0
		BNE StartGame1

		; Вывод смайлика
		MOV #bmpGood, R0
		JSR PC, @#drawSmile

		; Рисование игрового поля
		JSR PC, @#drawPlayField
	
		; Вывод чисел
		JSR PC, @#leftNumber
		JSR PC, @#rightNumber
		
		; Время
mainLoop:       CMP gameOverFlag, #1
		BEQ mainLoop1
		CMP time, #999.
		BEQ mainLoop1

		; Прошла секунда?
		CLR R0
		CMP @#0177710, #365.
		ADC R0
		CMP lastTimer, R0
		BEQ mainLoop1
		MOV R0, lastTimer

		; Да прошла. Увеличиваем переменную и перерисовываем экран
		INC time
		JSR PC, @#rightNumber

mainLoop1:      ; Нажата ли клавиша
		BIT #128., @#0177660
		BEQ mainLoop

		; Анализируем клавиатуру
		MOV @#0177662, R0
		CMP R0, #10
		BEQ cursorLeft
		CMP R0, #31
		BEQ cursorRight
		CMP R0, #32
		BEQ cursorUp
		CMP R0, #33
		BEQ cursorDown
		CMP R0, #40
		BEQ leftClick

		BR rightClick

;----------------------------------------------------------------------------
; ЛЕВАЯ КНОПКА МЫШИ

leftClick:	; Если бомбы не установлены, установить бомбы
		MOV bombsPutted, R0
		BEQ putBombs

		; Если игра завершена, то выйти в меню
putBombsRet:	CMP gameOverFlag, #1
		BEQ MenuFar

		; Запустить функцию Open
		MOV cursorX, R0
		MOV cursorY, R1
		JSR PC, @#open

		; Возможно курсор стерт
		JSR PC, @#drawCursor

		; Проверить, выиграли ли мы
		JSR PC, @#checkWin
	
		; Основной цикл игры
		BR mainLoop

;----------------------------------------------------------------------------

MenuFar:	JMP @#Menu

;----------------------------------------------------------------------------

cursorLeft:     CMP cursorX, #0
		BEQ mainLoop
		JSR PC, @#hideCursor
		DEC cursorX
		JSR PC, @#drawCursor
		BR mainLoop

;----------------------------------------------------------------------------

cursorRight:    MOV cursorX, R0
		INC R0
		CMP R0, gameWidth
		BEQ mainLoop
		JSR PC, @#hideCursor
		INC cursorX
		JSR PC, @#drawCursor
		BR mainLoop

;----------------------------------------------------------------------------

cursorUp:    	CMP cursorY, #0
		BEQ mainLoop
		JSR PC, @#hideCursor
		DEC cursorY
		JSR PC, @#drawCursor
		BR mainLoop

;----------------------------------------------------------------------------

cursorDown:	MOV cursorY, R0
		INC R0
		CMP R0, gameHeight
		BEQ mainLoop
		JSR PC, @#hideCursor
		INC cursorY
		JSR PC, @#drawCursor
		BR mainLoop

;----------------------------------------------------------------------------
; ЛЮБАЯ КНОПКА КЛАВИАТУРЫ - УСТАНОВКА ФЛАГА

rightClick:	; if(gameOver) return;
		CMP gameOverFlag, #1
		BEQ mainLoop
		; a=x+y*miner_w
		MOV cursorX, R0
		MOV cursorY, R1
		JSR PC, @#mul01
		; userMarks[a] = (userMarks[a]+1)%3;
		MOVB userMarks(R2), R3
		INC R3
		CMP R3, #4
		BNE rightClick1
		  CLR R3
rightClick1:	MOVB R3, userMarks(R2)
		; redraw();
		JSR PC, @#hideCursor
		JSR PC, @#drawCursor
rightClickRet:	JSR PC, @#leftNumber
	 	JMP @#mainLoop

;----------------------------------------------------------------------------
; ПОМЕСТИТЬ БОМБЫ НА ПОЛЕ

putBombs:	INC bombsPutted

                ; Цикл
		MOV bombsCnt, R5
putBombs1:      MOV R5, -(SP)
	
                ; Координата X
putBombs2:	JSR PC, @#rand
		MOV gameHeight, R1
		JSR PC, @#div
		MOV R0, R5

                ; Координата Y
		JSR PC, @#rand
		MOV gameWidth, R1
		JSR PC, @#div

		; Бомба не должна быть под курсором
		CMP cursorY, R5
		BNE putBombs3
		CMP cursorX, R0
		BEQ putBombs2

		; Расчет адреса в массиве
putBombs3:	MOV R5, R1
		JSR PC, @#mul01

		; Бомба в этой клетке уже есть
		CMPB #255., playfield(R2)
		BEQ putBombs2

		; Ставим бомбу
		MOVB #255., playfield(R2)

		MOV (SP)+, R5
		SOB R5, putBombs1

		JMP @#putBombsRet

;----------------------------------------------------------------------------
; ПЕРЕРИСОВАТЬ КЛЕТКУ (СТЕРЕТЬ КУРСОР)

hideCursor:	MOV cursorX, R0
		MOV cursorY, R1
		JSR PC, @#mul01		; R0+R1*16 -> R2
		JSR PC, @#calcCell2	; R0,R1 -> R1. Портит R3
		MOVB userMarks(R2), R5
		MOVB playfield(R2), R2
		JSR PC, @#getBitmap
		CMP R0, #bmpUn
		BEQ drawCursor4
drawCursor5:	JSR PC, @#drawImage ; Используется R0,R1. Портит R2.
                RTS PC

;................................

drawCursor4:	CMP R5, #1
		BEQ drawCursorF
		CMP R5, #2
		BNE drawCursor5
		mov #bmpQ, R0
		BR drawCursor5

;................................
	
drawCursorF:	mov #bmpF, R0
		BR drawCursor5

;----------------------------------------------------------------------------
; НАРИСОВАТЬ КУРСОР ПОВЕРХ КЛЕТКИ

drawCursor:	MOV cursorX, R0
		MOV cursorY, R1
		JSR PC, @#calcCell2
		MOV #bmpCursor, R0
		JSR PC, @#drawTransImage
		RTS PC

;----------------------------------------------------------------------------
; РАСЧЕТ АДРЕСА В ВИДЕОПАМЯТИ ЯЧЕЙКИ ИГРОВОГО ПОЛЯ
; R0,R1 - Координаты => R1 - адрес, Портит R3

calcCell2:	MOV R1, R3
		SWAB R3
		ROL R3
		ROL R3
		ADD R0, R3
		ADD R0, R3
		ADD R0, R3
		ADD R0, R3
		ADD playfieldVA, R3
		MOV R3, R1
		RTS PC

;----------------------------------------------------------------------------
; ЛОГИКА ИГРЫ
; R0,R1 - координаты. R3 - счетчик => R2 - портит

check:		; if(x>=8 || y>=8) return;
		CMP R0, gameWidth
		BCC checkRet
                CMP R1, gameHeight
		BCC checkRet
		; a = x+y*miner_w
		JSR PC, @#Mul01
		; if(playfield[a]==-1) R3++;
		CMPB playfield(R2), #255.
		BNE openRet
		INC R3
checkRet:	RTS PC

;----------------------------------------------------------------------------
; ЛОГИКА ИГРЫ

call8:		DEC R1
		JSR PC, @#call81
		DEC R0
		INC R1
		JSR PC, (R5)
		INC R0
		INC R0
		JSR PC, (R5)
		DEC R0
		INC R1
		JSR PC, @#call81
		DEC R1
		RTS PC

;----------------------------------------------------------------------------
; ЛОГИКА ИГРЫ

call81:         DEC R0
		JSR PC, (R5)
		INC R0
		JSR PC, (R5)
		INC R0
		JSR PC, (R5)
		DEC R0
		RTS PC

;----------------------------------------------------------------------------
; R0+R1*16 => R2

mul01:		MOV R1, R2
		ASL R2
		ASL R2
		ASL R2
		ASL R2
		ADD R0, R2
		RTS PC

;----------------------------------------------------------------------------
; ЛОГИКА ИГРЫ

open:		CMP R0, gameWidth
		BCC openRet
                CMP R1, gameHeight
		BCC openRet
		; a=x+y*miner_w
		JSR PC, @#Mul01
	        ; if(playfield[a]==-1) miner_gameOver=true;
	        CMPB playfield(R2), #255.
	        BEQ die
	        ; if(playfield[a]!=-2) return;
		CMPB playfield(R2), #254.
		BNE openRet
		; push
		MOV R5, -(SP)
                MOV R0, -(SP)
                MOV R1, -(SP)
		; playfield[a]=call8(check,x,y);
		MOV #check, R5
		CLR R3
		JSR PC, @#call8
		JSR PC, @#mul01
		MOVB R3, playfield(R2)
		; redraw
		MOV R3, R4
		JSR PC, @#redrawCell012
		; if(playfield[a]!=0) return;
		MOV R4, R4
		BNE openRets
		; call8(open,x,y);
                MOV (SP)+, R1
                MOV (SP)+, R0
		MOV #open, R5
		JSR PC, @#call8
                MOV (SP)+, R5
		RTS PC

openRets:       MOV (SP)+, R1
                MOV (SP)+, R0
		MOV (SP)+, R5
openRet:        RTS PC

;----------------------------------------------------------------------------
; ИГРОК УМЕР

die:		; Вывод смайлика
		MOV #bmpBad, R0
		JSR PC, @#drawSmile
		JMP @#gameOver

;----------------------------------------------------------------------------
; ПЕРЕРИСОВАТЬ ЯЧЕЙКУ НА ЭКРАНЕ
; R0,R1 - координаты. R2 - число. => портит все регистры

redrawCell012:   MOV R3, R2
		 JSR PC, @#calcCell2 ; R0,R1 -> R1. Портит R3
		 JSR PC, @#getBitmap ; R2 -> R0
		 JSR PC, @#drawImage ; Используется R0,R1. Портит R2.
		 RTS PC

;----------------------------------------------------------------------------
; ПОЛУЧИТЬ УКАЗАТЕЛЬ НА ИЗОБРАЖЕНИЕ ПО НОМЕРУ ИЗОБРАЖЕНИЯ
; R0 => R0

getBitmap:      MOV R2, R0
		INC R0
		INC R0
		CMP gameOverFlag, #1
		BNE getBitmap3
getBitmap2:	SWAB R0
		ASR R0
		ASR R0
		ADD #bmpUn, R0
                RTS PC

getBitmap3:     CMPB R0, #1
		BNE getBitmap2
		DEC R0
		BR getBitmap2

;----------------------------------------------------------------------------
; ГЕНЕРАТОР СЛУЧАЙНЫХ ЧИСЕЛ
; нет => R0 - случайное число. R1 - портит.

rand_state:	.word 11105

rand:           MOV rand_state, R0
		MOV R0, R1
		ASL R0
		ASL R0
		ASR R1
		ASR R1
		ASR R1
		ASR R1
		ASR R1
		XOR R1, R0
		MOV R0, rand_state

		MOV R0, R1
		SWAB R0
		XOR R1, R0
		MOV @#0177710, R1
		XOR R1, R0
		BIC #177400, R0

		RTS PC

;----------------------------------------------------------------------------
; ДЕЛЕНИЕ
; R0/R1 => R2, остаток в R0

div:		CLR R2
div1:		SUB R1, R0
		BCS div2
		INC R2
		BR div1
div2:		ADD R1, R0
		RTS PC

;----------------------------------------------------------------------------
; ПРОВЕРКА, ВЫИГРАЛ ЛИ ИГРОК
; => Портит все регистры

checkWin:	; Подсчет не открытых клеток без бомб.
		MOV #254., R3
		CLR R1
checkWin2:	 CLR R0
checkWin1:	  JSR PC, @#mul01
		  CMPB playfield(R2), R3
		  BEQ checkWin3
	  	 INC R0
  	  	 CMP gameWidth, R0
	  	 BNE checkWin1
		INC R1
		CMP gameHeight, R1
		BNE checkWin2
		
		; Пользователь выиграл

		; Рисуем смайлик
		MOV #bmpWin, R0
		JSR PC, @#drawSmile

gameOver:	; Конец игры
		MOV #1, R0
		MOV R0, gameOverFlag

		; Показать все бомбы
		JSR PC, @#drawPlayField

checkWin3:	RTS PC

;----------------------------------------------------------------------------
; РАСЧЕТ И ВЫВОД НА ЭКРАН ЛЕВОГО ЧИСЛА
; => Портит все регистры

leftNumber:	; Подсчет кол-ва флагов
		CLR R0
		MOV bombsCnt, R1
leftNumber1:	 CMPB userMarks(R0), #1
		 BNE leftNumber3
		  DEC R1
		  BEQ leftNumber4
leftNumber3:	INCB R0
		BNE leftNumber1

		; Вывод числа на экран
leftNumber4:	MOV R1, R0
	        MOV #040510, R3		; Адрес в видеопамяти
		JSR PC, @#drawNumber

		RTS PC

;----------------------------------------------------------------------------
; ВЫВОД НА ЭКРАН ПРАВОГО ЧИСЛА
; => Портит все регистры
		
rightNumber:	MOV time, R0		; Исходное число
	        MOV #040573, R3		; Адрес в видеопамяти
		JSR PC, @#drawNumber
		RTS PC

;----------------------------------------------------------------------------
; ВЫВОД ТРЕХЗНАЧНОГО ЧИСЛА НА ЭКРАН
; R0 - число, R3 - адрес в видеопамяти. => Портит все регистры.

drawNumber:	MOV #3, R5		; Кол-во чисел

drawNumber0:	; Получаем первую цифру
		MOV #10., R1
		JSR PC, @#div

		; Расчет положения битмепа
		SWAB R0
		ASR R0
		ASR R0
		ADD #bmpN0, R0

		; Вывод изображения
		MOV #21., R4
drawNumber1:    MOVB (R0)+, (R3)+
		MOVB (R0)+, (R3)+
		MOVB (R0)+, (R3)+
		ADD #61., R3
		SOB R4, drawNumber1

		; Положение следующего символа
		SUB #1347., R3

		; Цикл
		MOV R2, R0
		SOB R5, drawNumber0
		
		RTS PC

;-----------------------------------------------
; Вывод смайлика
; => R0 - изображение. Портит R1,R2

drawSmile:	MOV #040435, R1
		MOV #24., R2
drawGood:	MOV (R0)+, (R1)+
		MOV (R0)+, (R1)+
		MOV (R0)+, (R1)+
		ADD #58., R1
		SOB R2, drawGood
		RTS PC

;----------------------------------------------------------------------------
; Рисование игрового поля и курсора
; => Портит все регистры

drawPlayField:	CLR R1
LOOP2:    	 CLR R0
LOOP1:	    	  MOV R0, -(SP)
	    	  MOV R1, -(SP)
  	     	   JSR PC, @#mul01
		   MOVB playfield(R2), R3
  	     	   JSR PC, @#redrawCell012
	    	  MOV (SP)+, R1
	    	  MOV (SP)+, R0
	  	 INC R0
  	  	 CMP gameWidth, R0
	  	 BNE LOOP1
		INC R1
		CMP gameHeight, R1
		BNE LOOP2
		
		JMP @#drawCursor

;----------------------------------------------------------------------------
; ОЧИСТИТЬ ЭКРАН
; => Портит R0, R2

clearScreen:	MOV #040000, R0
		MOV #2048., R2
clearScreen1:	CLR (R0)+
		CLR (R0)+
		CLR (R0)+
		CLR (R0)+
		SOB R2, clearScreen1
		RTS PC

;----------------------------------------------------------------------------
; ЗАМОСТИТЬ ЭКРАН

fillBlocks:	MOV #044000, R0
		MOV #14., R4
fillBlocks3:	MOV #bmpBlock, R1
		MOV #16., R3
fillBlocks2:	MOV #16., R2
fillBlocks1:	MOV (R1)+, (R0)+
		MOV (R1)+, (R0)+
		SUB #4, R1
		SOB R2, fillBlocks1
		ADD #4, R1
		SOB R3, fillBlocks2
		SOB R4, fillBlocks3
		RTS PC

;----------------------------------------------------------------------------
; НАРИСОВАТЬ ИЗОБРАЖЕНИЕ 16x16 С ПРОЗРАЧНОСТЬЮ
; R0 - Изображение, R1 - Куда => Портит R1, R2

drawTransImage: MOV     #16., R2
drawTransImag1:	BIC     (R0)+, (R1)
		BIS     (R0)+, (R1)+
		BIC     (R0)+, (R1)
		BIS     (R0)+, (R1)+
        	ADD     #60., R1
		SOB	R2, drawTransImag1
		RTS	PC

;----------------------------------------------------------------------------
; НАРИСОВАТЬ ИЗОБРАЖЕНИЕ 16x16
; R0 - Изображение, R1 - Куда => Портит R1, R2

drawImage:      MOV     #16., R2
drawImage1:	MOV     (R0)+, (R1)+
		MOV     (R0)+, (R1)+
        	ADD     #60., R1
		SOB	R2, drawImage1
		RTS	PC

;----------------------------------------------------------------------------
; ВЫВОД ТЕКСТА

Print:		; Установка координат
		CLR R1
		MOVB (R0)+, R1
		CLR R2
		MOVB (R0)+, R2
		EMT 024

		; Вывод текста
		MOV R0, R1
		MOV #377, R2
		EMT 020

		; Поиск след строки
Print1:		MOVB (R0)+, R1
		BNE Print1
		CMPB (R0), #255.
		BNE Print

		RTS PC

pfSize = 256 ; Удобное число для буфера, хотя максимальный размер поля 16x14

gameWidth:	.word 0
gameHeight:	.word 0
gameOverFlag:  	.word 0
cursorX:    	.word 0
cursorY:    	.word 0
playfieldVA:	.word 0
bombsCnt:   	.word 0
bombsPutted:	.word 0
time:	    	.word 0
lastTimer:      .word 0
playfield:    	.blkb pfSize
userMarks:    	.blkb pfSize

		.end

