;**** Средства BCD-арифметики ********************************************
;*
;* Подпрограммы BCD-арифметики.
;* HexBCD: преобразовывает шестнадцатеричное слово (целочисленное,16 разрядов) 
;* в 5-разрядное BCD-число
;* XBruchD: преобразовывает простую шестнадцатеричную дробь (4 знака после 
;*          запятой) в BCD-дробь
;* BCDHex: преобразовывает BCD-число (5 целочисленных разрядов) в 
;*         шестнадцатеричное слово (16 разрядов)
;* BCDAdd: BCD-сложение двухразрядных чисел
;* BCDSub: BCD-вычитание двухразрядных чисел
;*
;*************************************************************************
.nolist
.include "8515def.inc"
.list

;***** Регистровые переменные
.def	CpyL = r11	; Младший байт промежуточного буфера
.def	CpyH = r12	; Старший байт промежуточного буфера
.def	BCD0 = r13	; BCD-разряды 2 и 1
.def	BCD1 = r14	; BCD-разряды 4 и 3
.def	BCD2 = r15	; BCD-разряды 6 и 5
.def	hexL = r16	; Младший байт шестнадцатеричного слова
.def	hexH = r17	; Старший байт шестнадцатеричного слова
.def	cnt  = r18	; Счетчик
.def	tmp1 = r19	; Рабочий регистр
.def	tmp2 = r20	; Рабочий регистр
.def	Digt = r21	; Рабочий регистр
.def	BCDa = r22	; 1. BCD-число
.def	BCDb = r23	; 2. BCD-число

RESET:
rjmp  Haupt	; Переход к главной программе

;*************************************************************************
;* HexBCD – 16-разрядное двоичное число (целое) -> BCD.
;* Эта подпрограмма преобразовывает 16-разрядное шестнадцатеричное число,
;* хранимое в hexH:hexL, в 5-разрядное упакованное BCD-число, сохраняемое 
;  в BCD2:BCD1:BCD0.
;*************************************************************************

HexBCD:	; xxxx (Hex) -> 0abcde (BCD)
rcall Div10	; hexH:hexL / 10, остаток - в tmp1
mov   BCD0,tmp1	; Результат - в hexH:hexL, tmp1 = разряд 1
rcall Div10	; Остаток = разряд 2
swap  tmp1	; Разряд 2 – в старший полубайт
or    BCD0,tmp1	; Объединяем разряды 2 и 1 в BCD0
rcall Div10	; Остаток = разряд 3
mov   BCD1,tmp1	; В BCD1
rcall Div10	; Остаток = разряд 4
swap  tmp1	; Разряд 4 – в старший полубайт
or    BCD1,tmp1	; Объединяем разряды 4 и 3 в BCD1
mov   BCD2,hexL	; Разряд 5 - в BCD2, старший полубайт = 0
ret

;*************************************************************************
;* XBruchD – 16-разрядная двоичная дробь -> BCD-дробь.
;* Эта подпрограмма преобразовывает 16-разрядную двоичную дробь "0,abcd"
;* (4 шестнадцатеричных знака после запятой), хранимую в регистровой паре 
;* hexH:hexL, в восьмиразрядную упакованную BCD-lhj,m "0,654321", сохраняемую
;* в регистрах BCD2:BCD1:BCD0.
;*************************************************************************

XBruchD:	; 0,abcd (HEX) -> 0,654321 (BCD)
rcall Mul10	; Умножаем hexH:hexL на 10 
swap  tmp1	; Переполнение в tmp1 = разряд 6
mov   BCD2,tmp1	; Разряд 6 – в BCD2
rcall Mul10	; Переполнение в tmp1 = разряд 5
or    BCD2,tmp1	; Объединяем разряды 6 и 5 в BCD2
rcall Mul10	; Переполнение в tmp1 = разряд 4
swap  tmp1	; Разряд 4 – в старший полубайт
mov   BCD1,tmp1	; Разряд 4 – в BCD1
rcall Mul10	; Переполнение в tmp1 = разряд 3
or    BCD1,tmp1	; Объединяем разряды 4 и 3 в BCD1
rcall Mul10	; Переполнение в tmp1 = разряд 2
swap  tmp1	; Разряд 2 – в старший полубайт
mov   BCD0,tmp1	; Разряд 2 – в BCD0
rcall Mul10	; Переполнение в tmp1 = разряд 1
or    BCD0,tmp1	; Объединяем разряды 2 и 1 в BCD0
ret

;*************************************************************************
;* BCDHex – 5-разрядное целое BCD-число -> шестнадцатеричное слово (16 бит).
;* Эта подпрограмма преобразовывает 5-разрядное BCD-число < 65536, хранимое в
;* регистрах BCD2:BCD1:BCD0, в 4-хзнаковое шестнадцатеричное число, хранимое 
;* в регистровой паре hexH:hexL.
;*************************************************************************

BCDHex:	; 0abcde (BCD) -> xxxx (Hex)
clr   hexH		
mov   hexL,BCD2	; hexH:hexL = a
mov   Digt,BCD1
rcall MulAddH	; hexH:hexL = 10a+b
mov   Digt,BCD1
rcall MulAddL	; hexH:hexL = (10a+b)10+c
mov   Digt,BCD0
rcall MulAddH	; hexH:hexL = ((10a+b)10+c)10+d
mov   Digt,BCD0
rcall MulAddL	; hexH:hexL = (((10a+b)10+c)10+d)10+e
ret

;*************************************************************************
;* BCDAdd – сложение двух BCD-чисел.
;* Складываются два упакованных двухзнаковых BCD-числа BCDa и BCDb.
;* Результат сохраняется в регистровой паре BCDb:BCDa.
;*************************************************************************

BCDAdd:	; BCDa + BCDb
add  BCDa,BCDb	; Двоичное сложение чисел
clr  BCDb	; Обнуляем BCDb (старший байт)
brcc BA1	; Переход, если флаг переноса С=0
inc  BCDb	; Записываем BCD-переполнение
BA1:
brhs BA2	; Переход, если флаг половинного переноса H=1
subi BCDa,-6	; Проверка: прибавляем 6 к младшей цифре
brhc BA3	; Если H=0, то младшая цифра > 9
subi BCDa,6	; Выполняем обратную проверку
rjmp BA3
BA2:	; Требуется BCD-коррекция:
subi BCDa,-6	;   Прибавляем к младшей цифре 6
BA3:
subi BCDa,-$60	; Проверка: прибавляем 6 к старшей цифре
brcc BA5	; Если C=0, то старшая цифра > 9
sbrs BCDb,0	; Если при двоичном сложении нет переполнения
subi BCDa,$60	;   выполняем обратную проверку
ret
BA5:	
inc  BCDb	; BCD-переполнение
ret

;*************************************************************************
;* BCDSub – вычитание для двух BCD-чисел
;* Упакованное двухзнаковое BCD-число BCDb вычитается из такого же числа BCDa.
;* Результат сохраняется в BCDa.
;*************************************************************************

BCDSub:	; BCDa - BCDb
sub  BCDa,BCDb	; Двоичное вычитание
clr  BCDb
brcc BS0	; Если флаг переноса C=1 (BCDa < BCDb), то
inc  BCDb	;   записываем в BCDb заем
BS0:
brhc BS1	; Переход, если флаг половинного переноса H=0
subi BCDa,$06	; BCD-коррекция: вычитаем 6 из младшей цифры
BS1:
sbrs BCDb,0	; Пропускаем следующую команду, если есть заем
ret		; Выход из подпрограммы
subi BCDa,$60	; BCD-коррекция: вычитаем 6 из старшей цифры
ldi  BCDb,1	; Проверка: установка заема в BCDb
brcc SB2	; Если не возникает заема, то
ldi  BCDb,1	;   выполняем обратную проверку
SB2:
ret			

;*************************************************************************
;* Вспомогательная программа
;*************************************************************************

Div10:	; Деление hexH:hexL на 10,
	; результат – в hexH:hexL, остаток - в tmp1
clr  tmp1
lsl  hexL
rol  hexH
rol  tmp1	; Деление старших трех разрядов на 10
lsl  hexL	;   может дать в результате 
rol  hexH	;   только 0
rol  tmp1
lsl  hexL
rol  hexH
rol  tmp1
ldi  cnt,13	; Счетчик для оставшихся 13 разрядовs
Loop10:
lsl  hexL	; Сдвиг делимого влево на 1 разряд
rol  hexH
rol  tmp1	; Переполнение в tmp1
subi tmp1,10	; Проверка: переполнение > 10 ?
brlo HB1	; Переход, если нет
inc  hexL  ; Увеличиваем счетчик деления на 1
rjmp HB2
HB1:
subi tmp1,-10	; Выполняем обратную проверку
HB2:
dec  cnt	; Декрементируем счетчик разрядов
brne Loop10	; Переход, если еще не все разряды
ret

Mul10:	; Умножение "hexH:hexL" (abcd) на 10 ...
clr  tmp1	; ... Переполнение - в tmp1
lsl  hexL	; Умножаем на 2
rol  hexH
rol  tmp1	; Переполнение
mov  cpyL,hexL	; Сохраняем в буфер
mov  cpyH,hexH
mov  tmp2,tmp1
lsl  cpyL	; Умножаем на 2 (всего - на 4)
rol  cpyH		
rol  tmp1	; Переполнение
lsl  cpyL	; Умножаем на 2 (всего - на 8)
rol  cpyH		
rol  tmp1	; Переполнение
add  hexL,cpyL	 ;Прибавляем промежуточный результат ( x 2)
adc  hexH,cpyH	
adc  tmp1,tmp2	; Переполнение 0...9 – в tmp1
ret	

MulAddH:	; hexH:hexL*10 + старший полубайт (Digt)
swap  Digt	; Меняем местами старший и младший полубайты
MulAddL:	; hexH:hexL*10 + младший полубайт (Digt)
rcall Mul10	; Умножаем на 10
andi  Digt,0x0f	; Обнуляем старший полубайт
add   hexL,Digt	; Прибавляем младший полубайт
brcc  MA1	; Переход, если переполнение
inc   hexH	; Инкрементируем старший байт
MA1:
ret

Haupt:
ldi   tmp1,High(RamEnd)
out   sph,tmp1
ldi   tmp1,Low(RamEnd)
out   spl,tmp1
ldi   hexH,$FE
ldi   hexL,$DC
H1:
rcall XBruchD	; r17:r16, $0,FEDC = 0,99554
ldi   hexH,$FE
ldi   hexL,$DC
rcall HexBCD	; r17:r16, $FEDC = 65244
rcall BCDHex	; BCD2:BCD1:BCD0, 65244 = $FEDC
ldi   BCDa,$98
ldi   BCDb,$87
rcall BCDadd	; BCDb:BCDa = 01:85
ldi   BCDa,$96
ldi   BCDb,$55
rcall BCDsub	; BCDb:BCDa = 00:41  
ldi   BCDa,$55
ldi   BCDb,$96
rcall BCDsub	; BCDb:BCDa = 01:59  (-41)
rjmp  H1
nop



