ZXSRC
Z80 ZX-Spectrum
Sources Rough Collection
in progress...
Небольшой Блокнотик по программированию
Z80 на компьютере ZX-SPECTRUM,
и не только...
Математические операции
ADDITION
;a+hl unsigned slower
;4 bytes and 22 clock cycles
ld b,0 ;ld d,0 ;
ld c,a ;e,a ;
add hl,bc ;add hl,de ;
;a+hl faster unsigned from plutiedev
;5 bytes and 20 clock cycles
;but no other 16-bit register messed up
add a, l ; A = A+L
ld l, a ; L = A+L
adc a, h ; A = A+L+H+carry
sub l ; A = H+carry
ld h, a ; H = H+carry
;a+hl fastest addAtoHL:
;5 bytes, 19/20 clock cycles
add a,l
ld l,a
jr nc, $+3
inc h
a+hl ADD A, L
LD L, A
RET NC
INC H
RET
;a+hl signed from plutiedev
; If A is negative, we need to substract $100 from HL
; (since A's "upper byte" is $FF00)
or a
jp p, Positive
dec h
Positive: ; Then do addition as usual; (to handle the "lower byte")
add a, l
ld l, a
adc a, h
sub l
ld h, a
;(hl)=(hl)+a
add a,(hl)
ld (hl),a
;signed add
ld e, a ; DE = A
add a, a
sbc a
ld d, a
add hl, de ; HL = HL+DE
ADD 16bits ;andreadrian.de
LD HL,#1234 ; LOAD DECIMAL VALUE 12345 INTO REGISTER HL
LD DE,#5 ; LOAD DECIMAL VALUE 7 INTO REGISTER DE
; ADD ROUTINE 16+16BIT=16BIT
; HL = HL + DE
ADD16 ADD HL,DE ; 16-BIT ADD OF HL AND DE
;RESULT IS IN HL
RET ;RESULT IS IN HL
ADD 32bits ;andreadrian.de
LD HL,#1213 ; LOAD HEXADECIMAL 1213 TO HL (LOWER 16-BIT)
LD DE,#F000 ; LOAD HEXADECIMAL F000 TO DE (LOWER 16-BIT)
EXX
LD HL,#1011 ; LOAD HEXADECIMAL 1011 TO H'L' (UPPER 16-BIT)
LD DE,#0 ; LOAD VALUE 0 TO D'E' (UPPER 16-BIT)
EXX
; H'L'HL = H'L'HL + D'E'DE
ADD32 ADD HL,DE ; 16-BIT ADD OF HL AND DE
EXX
ADC HL,DE ; 16-BIT ADD OF HL AND DE WITH CARRY
EXX
RET
Kot Ltd, Кировск, 1996
Эта процедура приращения регистровой пары HL на величину, заданную в
регистре А, которая находится в пределах #00 - #FF.
LD A, приращение Это быстрее и короче,
ADD A,L чем:
LD L,A PUSH BC
ADC A,H LD BC, #
SUB L ADD HL, BC
LD H,A POP BC
SUBSTRACTION
;a-hl unsigned from plutiedev
; If A=0 do nothing Otherwise flip A's sign.
Since the upper byte becomes -1, also
; substract 1 from H.
neg
jp z, Skip
dec h
; Now add the low byte as usual
; Two's complement takes care of
; ensuring the result is correct
add a, l
ld l, a
adc a, h
sub l
ld h, a
Skip:
unsigned hl-de
ld e, a ; DE = A
ld d, 0
or a ; Clear carry
sbc hl, de ; HL = HL-DE
signed hl-de
ld e, a ; DE = A
add a, a
sbc a
ld d, a
or a ; Clear carry
sbc hl, de ; HL = HL-DE
; Compute (HL)=(DE)-(HL)
SUBHL LD A,E ;compute difference.
SUB L
LD L,A ;store low byte.
LD A,D
SBC A,H
LD H,A ;and then high byte.
RET
;via calc84maniac "Optimized routine for HL=A-HL
(the negate HL optimization can be derived
from this by setting A=0 first)
sub l
ld l,a
sbc a,a
sub h
ld h,a
;written by calc84maniac
;comment from calc84maniac:
; To clarify why I did a cpl/scf/adc instead
of a cpl/inc/add or neg/add,
; is that it handles the case of A=0 properly.
Typically, SUB N and
; ADD A,-N give opposite carry outputs,
but SUB 0 and ADD A,-0 both reset the
; carry flag. On the other hand, SCF \ ADC A,255
will set the carry flag like
; we want it to.
;NOTE: This is an in-line routine-- there is no RET
cpl
scf
adc a,c
ld c,a
jr c,$+3
dec b
; Here is a callable routine:
cpl
scf
adc a,c
ld c,a
ret c
dec b
ret
MULTIPLICATION
BC*2,4,8....
Умножить регистровую пару на 2 (4,8...) можно так:
AND A
RL C
RL B .
DE*2,4,8....
sla e
rl d
А если вам надо перемножить регистр HL,то
это лучше сделать командой сложения ADD :
ADD HL,HL умножить на два
ADD HL,HL на четыре
ADD HL,HL на восемь
........... и т.д.
Например:
HLx32 add hl,hl
HLx16 add hl,hl
HLx8 add hl,hl
HLx4 add hl,hl
HLx2 add hl,hl
ret
;B*2,4,8...
ld b,3 ; Multiply 3 with 4
sla b ; *4
sla b ; result: B = 12
;A*2,4,8...
ld a,15 ; Multiply 15 with 8
add a,a ; *8
add a,a
add a,a ; result: A =120
;A*20
ld a,5 ; Multiply 5 with 20 (= A * 16 + A * 4)
add a,a ; *16
add a,a
ld b,a ; Store value of A * 4 in B
add a,a
add a,a
add a,b ; Add A * 4 to A * 16, result: A = 100
A*15
ld a,3 ; Multiply 3 with 15 (= A * 16 - A * 1)
ld b,a ; Store value of A * 1 in B
add a,a ; *16
add a,a
add a,a
add a,a
sub b ; result: A = 45
HL = HL*A
;5.MHLA - умножение
;INPUT : HL <-- что A <-- на что
;OUTPUT: HL = HL*A;портятся DE,HL,A
;FASTED bY CREATOR
MHLA AND A
JR Z,M1H
EX DE,HL
LD HL,0
M2H SRL A
JP NC,M3H
ADD HL,DE
M3H SLA E
RL D
AND A
JP NZ,M2H
RET
M1H LD H,A
LD L,A
RET
;a*12=hl a*3=hl a*12=hl (fast)
; Although this specific case
could be even better as follows:
ld l,a
add a,a ; a*2
add a,l ; a*3
ld h,0
ld l,a ; hl=a*3
add hl,hl ; hl=a*6
add hl,hl ; hl=a*12
; 8 bytes, 45 clocks
;HL*10
Mult10HL
add hl,hl
push de
push hl
pop de
add hl,hl
add hl,hl
add hl,de
pop de
;HL=DE*A
MULT LD HL,0
EX DE,HL
DUP 8
RRA
JR NC,$+5
EX DE,HL
ADD HL,DE
EX DE,HL
ADD HL,HL
EDUP
EX DE,HL
RET
; Multiply 8-bit values H*E ~Grauw
; In: Multiply H with E
Out: HL = result
Mult8 ld d,0
ld l,d
ld b,8
Mult8_Loop add hl,hl
jr nc,Mult8_NoAdd
add hl,de
Mult8_NoAdd djnz Mult8_Loop
ret
; Multiply 8-bit value with a 16-bit value ~Grauw
; In: Multiply A*DE
Out: HL = result
Mult12:
ld l,0
ld b,8
Mult12_Loop:
add hl,hl
add a,a
jr nc,Mult12_NoAdd
add hl,de
Mult12_NoAdd:
djnz Mult12_Loop
ret
; Multiply 16-bit values (with 16-bit result) ~Grauw
; In: Multiply BC*DE
Out: HL = result
Mult16:
ld a,b
ld b,16
Mult16_Loop:
add hl,hl
sla c
rla
jr nc,Mult16_NoAdd
add hl,de
Mult16_NoAdd:
djnz Mult16_Loop
ret
; Multiply 16-bit values (with 32-bit result)
; ~Grauw
; In: BC*DE
; Out: BCHL = result
Mult32:
ld a,c
ld c,b
ld hl,0
ld b,16
Mult32_Loop:
add hl,hl
rla
rl c
jr nc,Mult32_NoAdd
add hl,de
adc a,0
jp nc,Mult32_NoAdd
inc c
Mult32_NoAdd:
djnz Mult32_Loop
ld b,c
ld c,a
ret
; Multiply 8-bit value with a 16-bit
value (right rotating) ~Grauw
; In: Multiply A*DE. Put lowest value
in A for most efficient calculation
; Out: HL = result
Mult12R:
ld hl,0
Mult12R_Loop:
srl a
jr nc,Mult12R_NoAdd
add hl,de
Mult12R_NoAdd:
sla e
rl d
or a
jp nz,Mult12R_Loop
ret
; Multiply 8-bit value with a
;16-bit value (unrolled) ~Grauw
; In: Multiply A*DE Out: HL = result
Mult12U:
ld l,0
add a,a
jr nc,Mult12U_NoAdd0
add hl,de
Mult12U_NoAdd0:
add hl,hl
add a,a
jr nc,Mult12U_NoAdd1
add hl,de
Mult12U_NoAdd1:
add hl,hl
add a,a
jr nc,Mult12U_NoAdd2
add hl,de
Mult12U_NoAdd2:
add hl,hl
add a,a
jr nc,Mult12U_NoAdd3
add hl,de
Mult12U_NoAdd3:
add hl,hl
add a,a
jr nc,Mult12U_NoAdd4
add hl,de
Mult12U_NoAdd4:
add hl,hl
add a,a
jr nc,Mult12U_NoAdd5
add hl,de
Mult12U_NoAdd5:
add hl,hl
add a,a
jr nc,Mult12U_NoAdd6
add hl,de
Mult12U_NoAdd6:
add hl,hl
add a,a
ret nc
add hl,de
ret
;HL*6 Умножение HL на шесть :
ADD HL,HL
LD B,H
LD C,L
ADD HL,HL
ADD HL,BC
;HL*40 умножение HL на сорок :
ADD HL,HL
ADD HL,HL
ADD HL,HL
LD B,H
LD C,L
ADD HL,HL
ADD HL,HL
ADD HL,BC
;by Axor
;HL=HL*128, 35 тактов
LD A,L
RRC H
RRA
LD H,A
LD L,0
RR L
;MUL: DE=DE*BC
MUL EX DE,HL
LD DE,0
LD A,16
MUL1 RR B
RR C
JR NC,MUL2
EX DE,HL
ADD HL,DE
EX DE,HL
MUL2 ADD HL,HL
DEC A
JR NZ,MUL1
RET
Classic 8-bit * 8-bit Unsigned
;Input:
H = Multiplier,
E = Multiplicand,
L = 0, D = 0
;Output: HL = Product
sla h ; optimised 1st iteration
jr nc,$+3
ld l,e
add hl,hl ; unroll 7 times
jr nc,$+3 ; ...
add hl,de ; ...
Classic 16-bit * 8-bit Unsigned
;Input:
A = Multiplier,
DE = Multiplicand,
HL = 0, C = 0
;Output: A:HL = Product
add a,a ; optimised 1st iteration
jr nc,$+4
ld h,d
ld l,e
add hl,hl ; unroll 7 times
rla ; ...
jr nc,$+4 ; ...
add hl,de ; ...
adc a,c ; ...
Classic 16-bit * 16-bit Unsigned
;Input:
DE = Multiplier,
BC = Multiplicand,
HL = 0
;Output: DE:HL = Product
sla e ; optimised 1st iteration
rl d
jr nc,$+4
ld h,b
ld l,c
add hl,hl ; unroll 15 times
rl e ; ...
rl d ; ...
jr nc,$+6 ; ...
add hl,bc ; ...
jr nc,$+3 ; ...
inc de ; ...
;MULTIPLY D*E=DE;
;0<=result<65535
LD DE,#FEFF
CALL MULT
RET
MULT XOR A
LD B,8
MU1 RRA
RR E
JR NC,MU0
ADD A,D
MU0 DJNZ MU1
RRA
RR E
LD D,A
RET
Предлагаю вариант процедуры,
который имеет большую длину,
но работает быстрее при умножении
больших чисел (B>15) и не имеет
синдрома нулевой ошибки. Числа в
комментариях означают:
числитель - длина (байт),
знаменатель - время (тактов).
LD L,C; 1/4
LD D,B; 1/4
LD H,0; 2/7
LD E,0; 2/7
LD B,8; 2/7
; ---------
; 8/29
;-----------------------------
LOOP LD A,1; 2/7
AND L; 1/4
JR Z,PASS; 2/7
ADD HL,DE; 1/11
PASS RR H; 2/8
RR L; 2/8
DJNZ LOOP; 2/13+8
; ----------
; 12/472
; или (58*8+8)
;------------------------------
RET ; 1/10
; ИТОГО: 21/511
При значении регистра B от 1 до 15
процедура умножения способом многократного
сложения (ZX96/4-5, стр.69) выполняется быстрее.
; Процедура знакового умножения D*E=HL
; На входе: DE=множители, BIT 7,D(E) знаковый
; На выходе: HL=результат, BIT 7,H знаковый
ORG #C000
MULTI LD A,D
XOR E
AND #80
EX AF,AF'
RES 7,D
RES 7,E
LD L,D
LD B,8
XOR A
RR L
L1 JR NC,$+3
ADD A,E
RRA
RRL
DJNZ L1
LD H,A
EX AF,AF'
OR H
LD H,A
RET
;from Neos
;chl=chl*b;length is 21 bytes
;maximal time is about 561 tact
_mul24_8 push ix
ex de,hl
xor a
ld l,a
ld h,a
ld xh,8
mul248_ add hl,hl
rla
rl b
jr nc,$+4
add hl,de
adc a,c
dec xh
jr nz,mul248_
ld c,a
pop ix
ret
;from Neos
; de*a=hl
FMULT ld hl,0
ld b,8
add hl,hl
rlca
jr nc,$+3
add hl,de
djnz FMULT+5
ret
; байт*слово=слово
; Вход - А-число 1, DE-число 2.
; Выход - HL-результат.
LD HL,0
DUP 8
ADD HL,HL
ADD A,A
JR NC,$+3
ADD HL,DE
EDUP
; слово*слово=слово
; Вход - BC-число 1,DE-число 2.
; Выход - HL-результат.
LD HL,0
LD A,B
DUP 8
ADD HL,HL
ADD A,A
JR NC,$+3
ADD HL,DE
EDUP
LD A,C
DUP 8
ADD HL,HL
ADD A,A
JR NC,$+3
ADD HL,DE
EDUP
; слово*слово=двойное слово
; Вход -
BC-число 1,DE-число 2
; Выход -
HL-младшая часть слова,
HL'-старшая часть слова.
LD HL,0
EXX
LD HL,0
LD DE,0
EXX
LD A,B
DUP 8
ADD HL,HL
EXX
ADC HL,HL
EXX
ADD A,A
JR NC,$+6
ADD HL,DE
EXX
ADC HL,DE
EDUP
LD A,C
DUP 8
ADD HL,HL
EXX
ADC HL,HL
EXX
ADD A,A
JR NC,$+6
ADD HL,DE
EXX
ADC HL,DE
EXX
EDUP
; двойное слово*двойное слово=двойное слово
; Вход - DE' DE-число 1, BC' BC-число 2
; Выход - HL' HL-результат
LD HL,0
EXX
LD HL,0
EXX
DUP 16
ADD HL,HL
EXX
ADC HL,HL
EX DE,HL
ADD HL,HL
EX DE,HL
EXX
JR NC,$+6
ADD HL,BC
EXX
ADC HL,BC
EXX
EDUP
DUP 16
ADD HL,HL
EXX
ADC HL,HL
EXX
EX DE,HL
ADD HL,HL
EX DE,HL
JR NC,$+6
ADD HL,BC
EXX
ADC HL,BC
EDUP
;andreadrian.de
LD DE,#01213
LD BC,#3
EXX
LD DE,#1011
LD BC,#0
EXX
; MULTIPLY ROUTINE 32*32BIT=32BIT
; H'L'HL = B'C'BC * D'E'DE
; NEEDS REGISTER A, CHANGES FLAGS
MUL32
AND A ; RESET CARRY FLAG
SBC HL,HL ; LOWER RESULT = 0
EXX
SBC HL,HL ; HIGHER RESULT = 0
LD A,B ; MPR IS AC'BC
LD B,32 ; INITIALIZE LOOP COUNTER
MUL32LOOP
SRA A ; RIGHT SHIFT MPR
RR C
EXX
RR B
RR C ; LOWEST BIT INTO CARRY
JR NC,MUL32NOADD
ADD HL,DE ; RESULT += MPD
EXX
ADC HL,DE
EXX
MUL32NOADD
SLA E ; LEFT SHIFT MPD
RL D
EXX
RL E
RL D
DJNZ MUL32LOOP
EXX
; RESULT IN H'L'HL
RET
ПРОЦЕДУРА УМНОЖЕНИЯ HL=B*C.
(ZX РЕВЮ 96/4-5, стр.69).
Предлагаю вариант процедуры,
который имеет большую длину, но
работает быстрее при умножении
больших чисел (B>15) и не имеет
синдрома нулевой ошибки.
Числа в комментариях озна-
чают: числитель - длина (байт),
знаменатель - время (тактов).
LD L,C; 1/4
LD D,B; 1/4
LD H,0; 2/7
LD E,0; 2/7
LD B,8; 2/7
; ---------
; 8/29
;-----------------------------
LOOP LD A,1; 2/7
AND L; 1/4
JR Z,PASS; 2/7
ADD HL,DE; 1/11
PASS RR H; 2/8
RR L; 2/8
DJNZ LOOP; 2/13+8
; ----------
; 12/472
; или (58*8+8)
;------------------------------
RET ; 1/10
; ==============================
; ИТОГО: 21/511
При значении регистра B от 1
до 15 процедура умножения спосо-
бом многократного сложения (ZX
96/4-5, стр.69) выполняется быс-
трее.
Алгоритм умножения способом
сложений со сдвигами подробно
описан в книге Р.Токхейм "Осно-
вы цифровой электроники", МИР,
1988г., стр. 238-240.
Практическое применение про-
цедры умножения могут найти,
например, при расчете адреса на-
чала строки двумерного массива.
(С) Жильцов Алексей,
Углич, 1995
Хочу предложить несколько своих программ в раздел "Этюды". Это короткие
процедуры вычисления произведения HL=B*C.
Mul_1 LD L,B
LD B,8
XOR A
RR L
L1 JR NC,L2
ADD A,C
L2 RRA
RR L
DJNZ L1
LD H,A
RET
Длина 16 байт, максимальная длительность выполнения Тм=328 м.т.
Mul_2 LD L,B
LD B,9
XOR A
L1 RR L
DEC B
RET Z
JR NC,L2
ADD A,C
L2 RRA
LD H,A
JR L1
Длина 15 байт, Тм=430 м.т. Короче предыдущей процедуры на 1 байт, зато медлен-
нее на 25%.
Mul_3 LD L,0
LD H,B
LD B,L
LD A,8
L1 ADD HL,HL
JR NC,L2
ADD HL,BC
L2 DEC A
JR NZ,L1
RET
Длина 14 байт, Тм=387 м.т. Реализован другой алгоритм по сравнению с Mul_1 и
Mul_2.
И наконец, самая короткая процедура, занимающая всего 11 байт:
Mul_4 XOR A
SBC HL,HL
LD D,A
OR B
RET Z
LD E,C
L1 ADD HL,DE
DJNZ L1
RET
Длина 11 байт, Тм=6161 м.т.
Очевидно, что последнюю процедуру не стоит использовать для активных мате-
матических вычислений вследствие ее низкого быстродействия. В некоторых же
случаях (при гарантии, что B <= 8 - 10) скорость процедуры сравнима с тремя
другими. При пониженных требованиях к быстродействию, эта процедура даже пред-
почтительней других по причине своей крайней примитивной простоты.
(С) Васильев Антон
(EARTH SOFTWARE)
HL= B*C
LD E,C
LD HL,0
LD D,0
L1 ADD HL,DE
DJNZ L1
RET
DIVISION
;деление А на 10 , C - результат by koshi?
Div10 LD BC,#0AFF
Div INC C
SUB B
RET C
JP Div
BC/2,4,8....
Деление регистровoй пары на 2, 4, 8 и т.д.
AND A
RR B
RR C
DE/2,4,8
SRL D
RR E
;div A/8,4,2... ????????????
ld a,153 ; Divide 153 by 8
and a,%11111000 ; Clear bits 0-2 (equals 256 - 8)
rrca ; /8
rrca
rrca ; result: a = 19
;hl/32 sam style ;ограничено max: #1fe0/#20
HLdiv32
ADD HL,HL
ADD HL,HL
ADD HL,HL
LD A,H
RET
;HL/A by Andrew Strikes Code
;Вход: HL - делимое, A - делитель
;Выход: HL - частное, A - остаток
DIV_21 NEG
LD C,A
LD B,#11
XOR A
DIV210 ADC A,A
ADD A,C
JR C,DIV211
SUB C
OR A
DIV211 ADC HL,HL
DJNZ DIV210
RET
BC/10 (in BC=-7fff)
bc_div10 ld a, c
sra b
rra
sra b
rra
sra b
rra
sra b
rra
ld c, a
ret
;by b2m
DIV32 ld a,10h ; DE = HLDE/BC, HL = HLDE%BC
DIV321 ex de,hl
add hl,hl
ex de,hl
adс hl,hl
jr c DIV322
push hl
sbс hl,bc
jr nc, DIV323
pop hl
jr DIV325
DIV322 ccf
sbс hl,bc
jr DIV324
DIV323 inc sp
inc sp
DIV324 inc de
DIV325 dec a
jr nz, DIV321
ret
;L=C/B, A=REST
DIVIS XOR A
DIVIS2 LD L,1
D1 RL C
RLA
CP B
JR C,ZER
SUB B
SLI L
JR NC,D1
RET
ZER SLA L
JR NC,D1
RET
;DIV: BC=DE=DE/HL
;from Perfect Commander
DIV LD BC,0
LD A,1
DIV1 BIT 7,H
JR NZ,DIV2
ADD HL,HL
INC A
JR DIV1
DIV2 EX DE,HL
DIV4 OR A
SBC HL,DE
CCF
JR C,DIV3
ADD HL,DE
OR A
DIV RL C
RL B
RR D
RR E
DEC A
JR NZ,DIV4
LD E,C
LD D,B
RET
;DE/BC=DE,HL
;на входе: de-делимое; bc-делитель
;на выходе: de-частное hl-остаток
;by Virtual/bw
divide ld a,b ;меняем
cpl ;
ld b,a ;знак
ld a,c ;
cpl ;у
ld c,a ;
inc bc ;делителя t=30
ld hl,0 ;обнулили новое делимое
ld a,d ;сначала двигаем
rla ;старший байт делимого ;t=18
rl l ;┐
add hl,bc ;│
jr c,$+4 ;│8 раз
sbc hl,bc ;│
rla ;┘ t=8*45=360
ld d,a ;ст. байт результата
ld a,e ;теперь двигаем
rla ;младший байт t=12
adc hl,hl ;┐
add hl,bc ;│
jr c,$+4 ;│8 раз
sbc hl,bc ;│
rla ;┘ t=8*52=416
ld e,a ;мл. байт результата ;hl-остаток от деления ;t=4
;итого: 30+18+360+12+416+4=840 тактов
;если остаток не нужен,
то конец процедуры должен выглядеть так:
adc hl,hl ;┐
add hl,bc ;│
jr c,$+4 ;│7 раз
sbc hl,bc ;│
rla ;┘ t=7*52=364
adc hl,hl ;
add hl,bc ;
rla ;
ld e,a ; t=34
;итого: 30+18+360+12+364+34=818 тактов
;p.s. если будете делить на ноль, то,
как и следовало ожидать, частное будет
;равно нулю, а остаток - делимому, т.е.
в этом плане у процедуры глюков нет
; Divide 8-bit values ~Grauw
; In: Divide E by divider C
Out: A = result, B = rest
Div8:
xor a
ld b,8
Div8_Loop:
rl e
rla
sub c
jr nc,Div8_NoAdd
add a,c
Div8_NoAdd:
djnz Div8_Loop
ld b,a
ld a,e
rla
cpl
ret
; Divide 16-bit values (with 16-bit result)
;~Grauw
; In: Divide BC by divider DE
;Out: BC = result, HL = rest
Div16:
ld hl,0
ld a,b
ld b,8
Div16_Loop1:
rla
adc hl,hl
sbc hl,de
jr nc,Div16_NoAdd1
add hl,de
Div16_NoAdd1:
djnz Div16_Loop1
rla
cpl
ld b,a
ld a,c
ld c,b
ld b,8
Div16_Loop2:
rla
adc hl,hl
sbc hl,de
jr nc,Div16_NoAdd2
add hl,de
Div16_NoAdd2:
djnz Div16_Loop2
rla
cpl
ld b,c
ld c,a
ret
;Thanks to Flyguille for the Div16 routine,
taken from his MNBIOS source code.
;Classic 8-bit / 8-bit Unsigned
;Input: D = Dividend, E = Divisor, A = 0
;Output: D = Quotient, A = Remainder
sla d ; unroll 8 times
rla ; ...
cp e ; ...
jr c,$+4 ; ...
sub e ; ...
inc d ; ...
;Input: D = Dividend, E = Divisor, A = 0, Carry = 0
;Output: A = Quotient, E = Remainder
rl d ; unroll 8 times
rla ; ...
sub e ; ...
jr nc,$+3 ; ...
add a,e ; ...
ld e,a ; save remainder
ld a,d ; complement the result
cpl
Classic 16-bit / 8-bit Unsigned
;Input: HL = Dividend, C = Divisor, A = 0
;Output: HL = Quotient, A = Remainder (see note)
add hl,hl ; unroll 16 times
rla ; ...
cp c ; ...
jr c,$+4 ; ...
sub c ; ...
inc l ; ...
Classic 16-bit / 16-bit Unsigned
;Input: A:C = Dividend, DE = Divisor, HL = 0
;Output: A:C = Quotient, HL = Remainder
slia c ; unroll 16 times
rla ; ...
adc hl,hl ; ...
sbc hl,de ; ...
jr nc,$+4 ; ...
add hl,de ; ...
dec c ; ...
Classic 24-bit / 8-bit Unsigned
;Input: E:HL = Dividend, D = Divisor, A = 0
;Output: E:HL = Quotient, A = Remainder
add hl,hl ; unroll 24 times
rl e ; ...
rla ; ...
cp d ; ...
jr c,$+4 ; ...
sub d ; ...
inc l ; ...
Classic 24-bit / 16-bit Unsigned
;Input: A:BC = Dividend, DE = Divisor, HL = 0
;Output: A:BC = Quotient, HL = Remainder
slia c ; unroll 24 times
rl b ; ...
rla ; ...
adc hl,hl ; ...
sbc hl,de ; ...
jr nc,$+4 ; ...
add hl,de ; ...
dec c ; ...
Classic 32-bit / 8-bit Unsigned
;Input: DE:HL = Dividend, C = Divisor, A = 0
;Output: DE:HL = Quotient, A = Remainder
add hl,hl ; unroll 32 times
rl e ; ...
rl d ; ...
rla ; ...
cp c ; ...
jr c,$+4 ; ...
sub c ; ...
inc l ; ...
В DE - делимое, в C - делитель,
результат в HL, остаток в A.
;DIVIDE DE/C=HL
LD DE,768
LD C,3
CALL DIV
RET
DIV LD B,16
LD HL,0
LD A,H
DV1 RL E
RL D
RLA
SUB C
JR NC,DV2
ADD A,C
DV2 CCF
RL L
RL H
DJNZ DV1
RET
Деление регистровoй пары
на /2, /4, /8 и т.д.
AND A
RR B
RR C
При повторе данного примера N раз
вы разделите число на 2 в степени N.
Таким методом возможно разделить и
отдельные регистры.
;A/6
деление A на шесть /6:
LD A,делимое
LD B,#00
LABEL SUB #06 (делитель)
RET C
INC B
JR LABEL
на выходе этой процедуры частное
будет находится в регистре B
(не забудте, что таким методом можно
выполнить только целочисленное деление!).
;BC/DE Divides BC by DE.
Gives result in BC, remainder in HL by hll
udiv16 ld a,d
or e
ret z ;Return NC if dividing by 0
ld hl,0
ld a,16
udiv1 scf
rl c
rl b
adc hl,hl
sbc hl,de
jr nc,udiv2
add hl,de
dec c
udiv2 dec a
jr nz,udiv1
scf
ret
C_Div_D:
;Inputs:
C is the numerator
D is the denominator
;Outputs:
A is the remainder
B is 0
C is the result of C/D
D,E,H,L are not changed
ld b,8
xor a
C_Div_D_loop:
sla c
rla
cp d
jr c,+_
inc c
sub d
_:
djnz C_Div_D_loop
ret
BC_Div_DE:
BC/DE ==> BC, remainder in HL
;min: 1166cc
;max: 1310cc
;avg: 1238cc
;22 bytes
ld hl,0
ld a,b
ld b,16
div_loop:
;shift the bits from BC into HL
sla c \ rla
adc hl,hl
sbc hl,de
jr nc,div_inc_acc
add hl,de
.db $FE ;this begins the instruction
;'cp *', so it eats the next byte.
div_inc_acc:
inc c
div_loop_done:
djnz div_loop
ld b,a
ret
;from action by vav
Вход:de-число1 (2 байта)/bc-65536-число2 (2 байта)
Выход:a-остаток от деления:de=число1/число2
DELENIE ld hl,0
ld a,e
add a,a
rl d
adc hl,hl
add hl,bc
jr c,NABA57
sbc hl,bc
NABA57 rla
rl d
NABA58 adc hl,hl
add hl,bc
jr c,NABA60
NABA59 sbc hl,bc
NABA60 rla
rl d
NABA61 adc hl,hl
add hl,bc
jr c,NABA62
sbc hl,bc
NABA62 rla
rl d
adc hl,hl
add hl,bc
jr c,NABA63
sbc hl,bc
NABA63 rla
rl d
adc hl,hl
add hl,bc
jr c,NABA64
sbc hl,bc
NABA64 rla
rl d
adc hl,hl
add hl,bc
jr c,NABA65
sbc hl,bc
NABA65 rla
rl d
adc hl,hl
add hl,bc
jr c,NABA66
sbc hl,bc
NABA66 rla
rl d
adc hl,hl
add hl,bc
jr c,NABA67
sbc hl,bc
NABA67 rla
rl d
adc hl,hl
add hl,bc
jr c,NABA68
sbc hl,bc
NABA68 rla
rl d
adc hl,hl
add hl,bc
jr c,NABA69
sbc hl,bc
NABA69 rla
rl d
adc hl,hl
add hl,bc
jr c,NABA70
sbc hl,bc
NABA70 rla
rl d
adc hl,hl
add hl,bc
jr c,NABA71
sbc hl,bc
NABA71 rla
rl d
adc hl,hl
add hl,bc
jr c,NABA72
sbc hl,bc
NABA72 rla
rl d
adc hl,hl
add hl,bc
jr c,NABA73
sbc hl,bc
NABA73 rla
rl d
adc hl,hl
add hl,bc
jr c,NABA74
sbc hl,bc
NABA74 rla
rl d
adc hl,hl
add hl,bc
jr c,NABA75
sbc hl,bc
NABA75 rla
rl d
ld e,a
ret
;from Neos
;chl=chl/d ;a-remainder ;length is 22 bytes
;maximal time is about 1200 tacts
_div24_8 xor a
sub d
ld d,a
xor a
ld b,24
div248_ adc hl,hl
rl c
rla
add a,d
jr c,$+3
sub d
djnz div248_
adc hl,hl
rl c
ret
;from Neos
; hl/d=a hl\d=l
FDIV xor a
ld e,a
ld b,8
fdiv1 rr d
rr e
sbc hl,de
jr nc,$+3
add hl,de
rla
djnz fdiv1
cpl
ret
;bc/de=bc,hl
;Divides BC by DE.
;Gives result in BC, remainder in HL.
udiv16 ld a,d
or e
ret z ;Return NC if dividing by 0
ld hl,0
ld a,16
udiv1 scf
rl c
rl b
adc hl,hl
sbc hl,de
jr nc,udiv2
add hl,de
dec c
udiv2 dec a
jr nz,udiv1
scf
ret
; A,HL=(A,HL)/(B,DE)
DIVISIO LD C,A
XOR A
EXX
LD HL,1
LD B,H
EXX
DIV1 SLA L
RL H
RL C
RLA
CP B
JR C,DIV2
JR NZ,DIV3
EX AF,AF'
LD A,C
CP D
JR C,DIV22
JR NZ,DIV32
LD A,H
CP E
JR C,DIV22
DIV32 EX AF,AF'
DIV3 EX AF,AF'
LD A,L
LD L,H
LD H,C
AND A
SBC HL,DE
LD C,H
LD H,L
LD L,A
JR NC,DIV33
EX AF,AF'
SUB B
DEC A
DIV34 EXX
SLI L
RL H
RL B
EXX
JP NC,DIV1
JP DIVEXIT
DIV22 EX AF,AF'
DIV2 EXX
SLA L
RL H
RL B
EXX
JP NC,DIV1
DIVEXIT EXX
PUSH HL
LD A,B
EXX
POP HL
RET
DIV33 EX AF,AF'
SUB B
JP DIV34
;HL = HL/DE
;1.DIV - деление
;INPUT : HL <-- что DE <-- на что
;OUTPUT: HL = HL/DE ;портятся DE,HL,A
DIV LD A,D
OR E
RET Z
PUSH DE
PUSH BC
LD A,1
DIV_0 PUSH HL
SBC HL,DE
JP C,HL0
SBC HL,DE
JP C,DIV_1
DIV_01 INC A
SLA E
RL D
POP HL
JP DIV_0
DIV_1 POP HL
LD BC,0
DIV_2 AND A
JP NZ,DIV_3
LD H,B
LD L,C
POP BC
POP DE
RET
DIV_3 SBC HL,DE
JP NC,DIV_4
ADD HL,DE
DIV_4 CCF
RL C
RL B
SRL D
RR E
DEC A
JP DIV_2
HL0 CP 1
JP NZ,DIV_01
POP HL
POP BC
POP DE
LD HL,0
RET
REVERSAL
Posted by John Metcalf
Fast Z80 Bit Reversal
For years I've been using the following
simple code to reverse the bits
in the A register by rotating
the bits left out of one register
and right into another:
; reverse bits in A
; 8 bytes / 206 cycles
ld b,#08
ld l,a
revlp rl l
rra
djnz revlp
ret
; reverse bits in A
; 17 bytes / 66 cycles
ld l,a ; A=76543210
rlca
rlca ; A=54321076
xor l
and #AA
xor l ; A=56341270
ld l,a
rlca
rlca
rlca ; A=41270563
rrc l ; L=05634127
xor l
and #66
xor l ; A=01234567
ret
SIN, COS
;by SerzhSoft;
;вычисление синуса HL
;A=sin(HL)*255, CF=sign (NC=[+], C=[-])
SIN_HL
dec h
jr z,GOSIN2
inc h
jr nz,SIN360
ld h,SINTAB/256
ld a,l
cp 180
jr nc,GOSIN1
ld a,(hl)
and a
ret
GOSIN1 sub 180
ld l,a
ld a,(hl)
scf
ret
GOSIN2 ld a,l
cp 104
jr nc,GOSIN3
ld l,a
ld a,104
sub l
ld l,a
ld h,SINTAB/256
ld a,(hl)
scf
ret
GOSIN3 inc h
SIN360 ld bc,360
and a
LPSIN1 sbc hl,bc
jr nc,LPSIN1
add hl,bc
jp SIN_HL
;-------
;вычисление косинуса HL
;A=cos(HL)*255, CF=sign (NC=[+], C=[-])
COS_HL
dec h
jr z,GOCOS3
inc h
jr nz,COS360
ld h,SINTAB/256
ld a,l
cp 180
jr nc,GOCOS2
sub 90
jr c,GOCOS1
ld l,a
ld a,(hl)
scf
ret
GOCOS1 add a,180
ld l,a
ld a,(hl)
and a
ret
GOCOS2 sub 90
ld l,a
ld a,(hl)
scf
ret
GOCOS3 ld a,l
cp 14
jr nc,GOCOS4
add a,166
ld l,a
ld h,SINTAB/256
ld a,(hl)
scf
ret
GOCOS4 cp 104
jr nc,GOCOS5
sub 14
ld l,a
ld h,SINTAB/256
ld a,(hl)
and a
ret
GOCOS5 inc h
COS360 ld bc,360
and a
LPCOS1 sbc hl,bc
jr nc,LPCOS1
add hl,bc
jp COS_HL
;выравнивание на сегмент (граница 256)
ds $/256*256+256-$
SINTAB ;insert "sincos.d"
db #0D,#12,#16,#1B,#1F,#23,#28,#2C,#31,#35,#39,#3E,#42,#46,#4A,#4F
db #53,#57,#5B,#5F,#64,#68,#6C,#70,#74,#78,#7C,#7F,#83,#87,#8B,#8E
db #92,#96,#99,#9D,#A0,#A4,#A7,#AB,#AE,#B1,#B4,#B7,#BA,#BD,#C0,#C3
db #C6,#C9,#CC,#CE,#D1,#D3,#D6,#D8,#DA,#DD,#DF,#E1,#E3,#E5,#E7,#E9
db #EB,#EC,#EE,#F0,#F1,#F2,#F4,#F5,#F6,#F7,#F8,#F9,#FA,#FB,#FC,#FC
db #FD,#FE,#FE,#FE,#FF,#FF,#FF,#FF,#FF,#FF,#FF,#FE,#FE,#FE,#FD,#FC
db #FC,#FB,#FA,#F9,#F8,#F7,#F6,#F5,#F4,#F2,#F1,#F0,#EE,#EC,#EB,#E9
db #E7,#E5,#E3,#E1,#DF,#DD,#DA,#D8,#D6,#D3,#D1,#CE,#CC,#C9,#C6,#C3
db #C0,#BD,#BA,#B7,#B4,#B1,#AE,#AB,#A7,#A4,#A0,#9D,#99,#96,#92,#8E
db #8B,#87,#83,#7F,#7C,#78,#74,#70,#6C,#68,#64,#5F,#5B,#57,#53,#4F
db #4A,#46,#42,#3E,#39,#35,#31,#2C,#28,#23,#1F,#1B,#16,#12,#0D,#09
db #04
;--------------------------------------;
; SIN-TABLE MAKING ALGORYTHMS ;
; conception & code by Kolotov Sergey ;
; (c) SerzhSoft, Shadrinsk, apr, 1998 ;
;--------------------------------------;
ORG #8000
;--------------------------------------;
STACK_A EQU #2D28
FP_TO_A EQU #2DD5
;
SIN_MAX EQU #7F
SIN_ADD EQU 0
;
SINTBL1 EQU #6000
SINTBL2 EQU #6100
;--------------------------------------;
MAINPRG
LD C,SIN_MAX
LD B,SIN_ADD
LD DE,SINTBL1
PUSH BC
CALL SINCALC
POP BC
LD DE,SINTBL2
CALL SINMAKE
RET
;--------------------------------------;
SINCALC
PUSH DE
PUSH BC
PUSH DE
LD A,C
CALL STACK_A
POP DE
LD A,E
CALL STACK_A
RST #28 ;calc
DB #A3 ;stk_pi/2
DB #34,#40,#B0,#00,#40 ;#40
DB #05 ;divide
DB #04 ;multiply
DB #1F ;sin
DB #04 ;multiply
DB #38 ;endcalc
CALL FP_TO_A
JR Z,$+4 ;->
NEG ; |
POP BC ;<-
ADD A,B
POP DE
LD (DE),A
INC E
JR NZ,SINCALC
RET
;--------------------------------------;
SINMAKE
INC C
LD HL,SIN_DAT
PUSH BC
LD B,E
LP_SMK1 PUSH HL
LD H,(HL)
LD L,B
LD A,#08
LP_SMK2 ADD HL,HL
JR NC,$+3
ADD HL,BC
DEC A
JR NZ,LP_SMK2
LD A,H
LD (DE),A
POP HL
INC HL
INC E
BIT 6,E
JR Z,LP_SMK1
LD H,D
LD L,E
DEC L
LD A,(HL)
LD (DE),A
INC E
LP_SMK3 LD A,(HL)
LD (DE),A
INC E
DEC L
JR NZ,LP_SMK3
LP_SMK4 LD A,(HL)
NEG
LD (DE),A
INC L
INC E
JR NZ,LP_SMK4
POP BC
LP_SMK5 LD A,(DE)
ADD A,B
LD (DE),A
INC E
JR NZ,LP_SMK5
RET
;-------;
SIN_DAT
DB #00,#06,#0D,#13,#19,#1F,#25,#2C
DB #32,#38,#3E,#44,#4A,#50,#56,#5C
DB #62,#67,#6D,#73,#78,#7E,#83,#88
DB #8E,#93,#98,#9D,#A2,#A7,#AB,#B0
DB #B4,#B9,#BD,#C1,#C5,#C9,#CD,#D0
DB #D4,#D7,#DB,#DE,#E1,#E4,#E7,#E9
DB #EC,#EE,#F0,#F2,#F4,#F6,#F7,#F9
DB #FA,#FB,#FC,#FD,#FE,#FE,#FF,#FF
;--------------------------------------;
; sincos.z80
; CORDIC for sin() and cos() for the Zilog Z80 and Assembler Z80DT
; Author: Andre Adrian
; Version: 12apr2011
; length is 341 bytes
; LOAD TEST VALUES
LD DE,12AFH ; Angle = -1.57079632679 (-90°)
LD BC,9B78H
CALL SINCOS
; expected sin = B'C'BC = 0C0000000H (-1.0)
; expected cos = D'E'DE = 000000000H (0.0)
; delivered sin = B'C'BC = 0BFFFFFFFH
; delivered cos = D'E'DE = 000000031H
HALT
;==================================================
; Constants
;
CN: EQU 22
CM: EQU 8
;==================================================
; variable storage (RAM)
;
Angle:
DEFW 0,0
Y:
DEFW 0,0
X:
DEFW 0,0
Yold:
DEFW 0,0
pATR:
DEFW 0
Step:
DEFB 0
;==================================================
; constant storage (ROM)
;
ATR:
DEFW 0F6A8H,03243H ; atan(1)
DEFW 06705H,01DACH ; atan(1/2)
DEFW 0BAFCH,00FADH ; atan(1/4)
DEFW 06EA6H,007F5H ; atan(1/8)
DEFW 0AB76H,003FEH ; ...
DEFW 0D55BH,001FFH
DEFW 0FAAAH,000FFH
DEFW 0FF55H,0007FH
DEFW 0FFEAH,0003FH
;==================================================
; N BIT ARITHMETRIC SHIFT RIGHT ROUTINE 32BIT = 32BIT
; BCDE >>= A
; CHANGES FLAGS
;
SRBCDE:
SUB 8 ; SET CARRY FLAG IF A < 8
JR C,SRBEBT ; NO MORE BYTES TO SHIFT
LD E,D ; SHIFT BITS 8..15 TO 7..0
LD D,C ; SHIFT BITS 16..23 TO 8..15
LD C,B ; SHIFT BITS 24..31 TO 16..23
LD B,0 ; ASSUME POSITIVE NUMBER
BIT 7,C ; SET ZERO FLAG IF BIT 7 == 0
JR Z,SRBEPS
DEC B ; CHANGE + TO - SIGN
SRBEPS:
JR SRBCDE
SRBEBT:
ADD A,8 ; UNDO SUB 8, SET ZERO FLAG IF A == 0
SRBELP:
JR Z,SRBERT ; NO MORE BITS TO SHIFT
SRA B
RR C
RR D
RR E
DEC A ; SET ZERO FLAG IF A == 0
JR SRBELP
SRBERT:
RET ; RESULT IS IN BCDE.
;==================================================
; SIN() COS() ROUTINE 2 * 32BIT = 32BIT
; B'C'BC, D'E'DE = f(BCDE)
; NEEDS REGISTERS A BC DE HL, CHANGES FLAGS
;
SINCOS:
LD (Angle),DE ; store argument
LD (Angle+2),BC
SUB A ; A = 0
LD (Step),A ; i = 0
LD L,A
LD H,A
LD (Y),HL ; y = 0;
LD (Y+2),HL
LD HL,03B6AH ; x = FIXED(0.607252935..);
LD (X),HL
LD HL,026DDH
LD (X+2),HL
LD HL,ATR ; pATR = ATR;
LD (pATR),HL
SNCSDO: ; do {
LD HL,(Y) ; yold = y;
LD (Yold),HL
LD HL,(Y+2)
LD (Yold+2),HL
; y += (angle >= 0)? x >> i: -(x >> i);
LD DE,(X) ; x >> i
LD BC,(X+2)
LD A,(Step)
CALL SRBCDE
LD A,(Angle+3) ; (angle >= 0)?
RLA ; Bit 7 to Carry
LD HL,(Y)
JR C,SNCSL1
ADD HL,DE ; y += x >> i
LD (Y),HL
LD HL,(Y+2)
ADC HL,BC
JR SNCSE1
SNCSL1:
AND A ; y -= x >> i
SBC HL,DE
LD (Y),HL
LD HL,(Y+2)
SBC HL,BC
SNCSE1:
LD (Y+2),HL
; x -= (angle >= 0)? yold >> i: -(yold >> i);
LD DE,(Yold) ; yold >> i
LD BC,(Yold+2)
LD A,(Step)
CALL SRBCDE
LD A,(Angle+3) ; (angle >= 0)?
RLA
LD HL,(X)
JR C,SNCSL2
AND A ; x -= yold >> i
SBC HL,DE
LD (X),HL
LD HL,(X+2)
SBC HL,BC
JR SNCSE2
SNCSL2:
ADD HL,DE ; x += yold >> i
LD (X),HL
LD HL,(X+2)
ADC HL,BC
SNCSE2:
LD (X+2),HL
; a = (i < M+1)? *pATR++: atr_fixed[M] >> (i-M);
LD A,(Step) ; (i < M+1)?
SUB CM+1 ; i-(M+1)
JR NC,SNCSL3
LD HL,(pATR) ; *pATR++
LD E,(HL)
INC HL
LD D,(HL)
INC HL
LD C,(HL)
INC HL
LD B,(HL)
INC HL
LD (pATR),HL
JR SNCSE3
SNCSL3:
LD DE,(ATR+32) ; atr[M] >> i-M
LD BC,(ATR+34)
INC A ; i-M
CALL SRBCDE
SNCSE3:
; angle -= (angle >= 0)? a: -a;
LD A,(Angle+3) ; (angle >= 0)?
RLA
LD HL,(Angle)
JR C,SNCSL4
AND A ; angle -= a
SBC HL,DE
LD (Angle),HL
LD HL,(Angle+2)
SBC HL,BC
JR SNCSE4
SNCSL4:
ADD HL,DE ; angle += a
LD (Angle),HL
LD HL,(Angle+2)
ADC HL,BC
SNCSE4:
LD (Angle+2),HL
; } while (++i <= N-1);
LD HL,Step ; ++i
INC (HL)
LD A,CN-1 ; N-1 >= ++i
CP (HL)
JP NC,SNCSDO
; RESULT IS IN MEM(X) AND MEM(Y)
LD DE,(X)
LD BC,(Y)
EXX
LD DE,(X+2)
LD BC,(Y+2)
EXX
RET ; RESULT IS IN D'E'DE AND B'C'BC
SerzhSoft
КАК СИHУС ЗАБАБАХАТЬ
Лучше в pуках синица,
чем под кpоватью утка!
Hастало вpемя поговоpить о наболев-
шем, а конкpетнее - о синусах. Дело в
том, что во многих мегадемных эффектах
используются именно синусные таблички.
Это может быть пpостенькая плазма, ка-
кое-нибудь тело, движущееся по плавной
тpаектоpии, полет над гоpами, тоннельчик
и куча еще всего дpугого, чего только
можно пожелать...
Тpадиционно, да и удобно, of course,
создавать синусную табличку с pазмеpом,
кpатным 256 байт, а еще лучше - pовно
#0100! : -) Пpи этом табличка должна на-
чинаться с адpеса, выpавненного на сег-
мент, то есть младший байт этого адpеса
pавен нолику (#?? 00).
Создать пpостейшую sin-table можно с
помощью обыкновенного бейсика. К пpимеpу,
нам необходима табличка, pасположенная с
адpеса 24576 (#6000), длиной 256 байт,
амплитудой (pазмахом) синуса от -127 до
+127 и pазмеpом в один пеpиод. Получаем:
10 FOR i=0 TO 255
20 POKE 24576+i, SIN (PI/128*i)*127
30 NEXT i
Тепеpь сгенеpиpованную табличку мож-
но сохpанить на диске и встpаивать в свои
ассемблеpные эффекты по INSERT, INCBIN и
т. д.
Hо вот вы pешили немного изменить
амплитуду, скажем от -63 до +63, или вам
вдpуг стукнуло в голову, что "сеpединной
точкой" должна быть не 0, а, скажем, 128.
То есть получить pазбpос значений таблич-
ки от 128-63 до 128+63. Конечно, можно
снова "загpузить" BASIC и набpать немного
дpугую пpогpамму:
10 FOR i=0 TO 255
20 POKE 24576+i, SIN (PI/128*i)*63+128
30 NEXT i
Затем опять сохpанить все на диске,
и пpодолжать кодить в асм'е. Hо, навеp-
ное, даже последнему лaмеpу стало ясно,
что тут что-то не так. Hеэффективно, по-
теpя вpемени, да еще место на диске вся-
кие "левые" файлы занимают. Плюс, к тому
же, тоpмознутость компиляции пpи подгpуз-
ке по INCBIN'у. Да и хpанить целый синус-
ный пеpиод в памяти, когда можно обойтись
и четвеpью... Hет, с этим надо что-то де-
лать!
И выход, конечно же, есть. Hе уж то
нам слабо накодить пpоцедуpу ГЕHЕPАЦИИ
синуса на ассемблеpе. Да pаз плюнуть!
Хлоп, и уже готова подпpогpамма с исполь-
зованием ПЗУ 'шного калькулятоpа. Похожая
вещь пpисутствовала в Deja Vu #03 ,
статья "Плавающие атpибуты" . Здесь же
пpиводится немного улучшенная пpоцедуpа:
STACK_A EQU #2D28
FP_TO_A EQU #2DD5
;
SINCALC
PUSH DE
PUSH BC
PUSH DE
LD A,C
CALL STACK_A
POP DE
LD A,E
CALL STACK_A
RST #28 ;calc
DB #A3 ;stk_pi/2
DB #34,#40,#B0,#00,#40 ;#40
DB #05 ;divide
DB #04 ;multiply
DB #1F ;sin
DB #04 ;multiply
DB #38 ;endcalc
CALL FP_TO_A
JR Z,$+4 ;->
NEG ; |
POP BC ;<-
ADD A,B
POP DE
LD (DE),A
INC E
JR NZ,SINCALC
RET
Hа входе в pегистpе C мы должны ука-
зать "pазмах" синуса (амплитуда: -С...
+С), в pегистpе B - центpальное значение
(сколько пpибавлять), а pегистpовая паpа
DE должна содеpжать адpес буфеpа под
табличку. Пpичем, этот адpес обязательно
должен быть кpатен 256 (выpавнен на сег-
мент). И вот, пpимеpно чеpез 15 секунд,
мы получим те заветные 256 байт!
"Так до-о-олго!!!" - спpаведливо за-
метите Вы и будете пpавы. Hу а что вы еще
хотели всего за 39 байт памяти, занимае-
мых пpоцедуpой. Hо, конечно же, данный
пpимеp был пpиведен лишь для полноты
каpтины, а настоящую пpоцедуpу мы pазбе-
pем именно сейчас. Ведь не будете же вы
в демке писать "Please, wait 15 sec...
Decrunching..." только pади какой-то таб-
лички в 256 байт! И пpавильно! Итак, вот
скоpостной аналог вышепpиведенной пpоце-
дуpы, pаботающий несpавненно быстpее, хо-
тя и занимающий уже 55 байт + 64 байта на
данные. Итого: 119 байт. Hо скоpость!
SINMAKE
INC C
LD HL,SIN_DAT
PUSH BC
LD B,E
LP_SMK1 PUSH HL
LD H,(HL)
LD L,B
LD A,#08
LP_SMK2 ADD HL,HL
JR NC,$+3
ADD HL,BC
DEC A
JR NZ,LP_SMK2
LD A,H
LD (DE),A
POP HL
INC HL
INC E
BIT 6,E
JR Z,LP_SMK1
LD H,D
LD L,E
DEC L
LD A,(HL)
LD (DE),A
INC E
LP_SMK3 LD A,(HL)
LD (DE),A
INC E
DEC L
JR NZ,LP_SMK3
LP_SMK4 LD A,(HL)
NEG
LD (DE),A
INC L
INC E
JR NZ,LP_SMK4
POP BC
LP_SMK5 LD A,(DE)
ADD A,B
LD (DE),A
INC E
JR NZ,LP_SMK5
RET
;
SIN_DAT
DB #00,#06,#0D,#13,#19,#1F,#25,#2C
DB #32,#38,#3E,#44,#4A,#50,#56,#5C
DB #62,#67,#6D,#73,#78,#7E,#83,#88
DB #8E,#93,#98,#9D,#A2,#A7,#AB,#B0
DB #B4,#B9,#BD,#C1,#C5,#C9,#CD,#D0
DB #D4,#D7,#DB,#DE,#E1,#E4,#E7,#E9
DB #EC,#EE,#F0,#F2,#F4,#F6,#F7,#F9
DB #FA,#FB,#FC,#FD,#FE,#FE,#FF,#FF
Тепеpь пpи инсталляции какого-либо
вашего FX'а достаточно вызвать SINMAKE с
нужными паpаметpами, и спустя секунду
табличка уже готова! Можно генеpить нес-
колько pазных табличек... Или одну, но
состоящую из pазных синусных пеpиодов, с
pазными амплитудами... А затем, скажем,
пустить по этой таблице scroller и отpы-
ваться на всю катушку...
В пpиложении вы найдете ассемблеpный
исходник с двумя этими пpоцедуpами. И
помните:
Лучше в пpогpамме синус,
чем в мегадеме глюки!!!
P.S. А ведь можно обойтись и без
таблички данных SIN_DAT! Кто догадается -
как? : -)
SQR
;извлечение квадратного корня
;из регистровой пары.
;на входе HL-число из которого
;необходимо извлечь квадратный корень
;на выходе A-квадратный корень из HL
;by Virtual/BW
sqrhl xor a ;обнулили a;)
ld de,64 ;этого требует алгоритм ; t=14
sla h ;берем два самых левых
adc a,a ;бита аргумента
sla h ;
adc a,a ;
jr z,$+4 ;если они не равны
dec a ;нулю, то увеличиваем
inc d ;результат ; t=39
sla h ;┐далее следуем
adc a,a ;│алгоритму извлечения
sla h ;│квадратного корня
adc a,a ;│"столбиком", только
sla d ;│
ld c,d ;│3 раза
sli c ;│
cp c ;│
jr c,$+4 ;│
sub c ;│
inc d ;┘ t=63*3=189
sla l ;┐проделываем похожую
adc a,a ;│операцию с
sla l ;│младшим байтом
adc a,a ;│аргумента
sla d ;│
ld c,d ;│2 раза
sli c ;│
cp c ;│
jr c,$+4 ;│
sub c ;│
inc d ;┘ t=63*2=126
ld h,a ;
or a ;обнулили флаг переноса ; t=8
sbc hl,de ;теперь нам не хватает
jr nc,$+3 ;одного регистра d для
add hl,de ;выполнения последних
ccf ;двух циклов, придется
rl d ;использовать более
add hl,hl ;медленные операции
add hl,hl ;с регистровыми парами ; t=67
sbc hl,de ;и последний раз
ccf ;выполняем расчеты, не
ld a,d ;восстанавливая hl
adc a,a ;в a-результат ; t=27
;итого: 14+39+189+126+8+67+27=470 тактов
;извлечение квадратного корня
;из трехбайтового числа.
;На входе: dhl=d*65536+hl - само число
;На выходе: hl - тот самый квадратный корень
;by Virtual/BW
sqrdhl xor a ;начало процедуры
ld bc,64 ;практически аналогично ;предыдущей ; t=14
sla d ;
adc a,a ;
sla d ;
adc a,a ; t=39
jr z,$+4 ;
dec a ;
inc b ;
sla d ;┐
adc a,a ;│
sla d ;│
adc a,a ;│
sla b ;│
ld e,b ;│3 раза
sli e ;│
cp e ;│
jr c,$+4 ;│
sub e ;│
inc b ;┘ t=3*63=189
sla h ;┐
adc a,a ;│
sla h ;│
adc a,a ;│
sla b ;│
ld e,b ;│2 раза
sli e ;│
cp e ;│
jr c,$+4 ;│
sub e ;│
inc b ;┘ t=63*2=126
ld d,l ;
ld l,h ;
ld h,a ;меняем регистры
or a ; t=16
sbc hl,bc;┐
jr nc,$+3;│
add hl,bc;│
ccf ;│2 раза
rl b ;│
add hl,hl;│
add hl,hl;┘ t=67*2=134
ld l,h ;есть подозрение, что
ld h,0 ;этот блок и предыдущий
ld c,b ;можно немного ускорить
ld b,h ;
rl h ;
ld a,d ; t=31
rla ;┐да и этот блок мне
adc hl,hl;│не нравится.
rla ;│очень вероятно, что
adc hl,hl;│его можно неплохо
sla c ;│ускорить. Но как?
rl b ;│
ld d,b ;│
ld e c ;│3 раза
sli e ;│
rl d ;│
sbc hl,de;│
jr nc,$+4;│
add hl,de;│
dec c ;│
inc c ;┘ t=3*119=357 (!) ;ужас! Думайте, как ;ускорить!
rla ;этот блок аналогичен
adc hl,hl;предыдущему с
rla ;небольшой разницей
adc hl,hl;в конце. Это сделано
ld d,b ;ради повышения
ld e,c ;скорости вычислений
sla e ;
rl d ;
sli e ;
rl d ;
sbc hl,de;
ccf ; t=97
ld h,b ;
ld l,c ;
adc hl,hl;в hl - результат ; t=23
;итого: 14+39+189+126+16+134+31+357+97+23=1026
Square table generator is based on observation that
differences between consecutive squares
(0, 1, 4, 9, 16, 25, ...)
form a sequence of odd numbers.
(1, 3, 5, 7, 9, ...).
Thus, by adding successive odd numbers
iteratively we generate integer squares.
ld hl,SqrTab; must be a multiple of 256
ld b,l
ld c,l ; BC holds odd numbers
ld d,l
ld e,l ; DE holds squares
SqrGen ld (hl),e
inc h
ld (hl),d ; store x^2
ld a,l
neg
ld l,a
ld (hl),d
dec h
ld (hl),e ; store -x^2
ex de,hl
inc c
add hl,bc ; add next odd number
inc c
ex de,hl
cpl ; one byte replacement for NEG, DEC A
ld l,a
rla
jr c,SqrGen
; Square root of 16-bit value
;by Ricardo Bittencourt
; In: HL = value
; Out: D = result (rounded down)
Sqr16 ld de,#0040
ld a,l
ld l,h
ld h,d
or a
ld b,8
Sqr16_Loop sbc hl,de
jr nc,Sqr16_Skip
add hl,de
Sqr16_Skip ccf
rl d
add a,a
adc hl,hl
add a,a
adc hl,hl
djnz Sqr16_Loop
ret
; Square Root (16 bit) HL=number to find
;square root of Returns result in A
SQR16 LD DE,1
XOR A
DEC A
SQ16 SBC HL,DE
INC DE
INC DE
INC A
JR NC,SQ16
RET
; Square Root (32 bit) BCDE=number to find
;square root of. Returns result in DE
SQR32 LD A,B
PUSH DE
POP IX
LD D,0
LD E,D
LD H,D
LD L,D
LD B,16
SQ32A SUB #40
SBC HL,DE
JR NC,SQ32B
ADD A,#40
ADC HL,DE
SQ32B CCF
RL E
RL D
ADD IX,IX
RL C
RLA
ADC HL,HL
DJNZ SQ32A
RET
sqrt32:
;Input: HLDE
;Output: DE is the sqrt, AHL is the remainder
;speed: 238+{0,1}+{0,44}+sqrtHL+3*sqrt32sub_2+sqrt32_iter15
;min: 1260
;max: 1506
;avg: 1377.75
push de
call sqrtHL
pop bc
add a,a
ld e,a
jr nc,+_
inc d
_:
ld a,b
call sqrt32sub_2
call sqrt32sub_2
;Now we have four more iterations
;The first two are no problem
ld a,c
call sqrt32sub_2
;On the next iteration, HL might temporarily overflow by 1 bit
call sqrt32_iter15
;On the next iteration, HL is allowed to overflow,
DE could overflow with our current routine,
but it needs to be shifted right at the end, anyways
sqrt32_iter16:
add a,a
ld b,a ;either 0x00 or 0x80
adc hl,hl
rla
adc hl,hl
rla
;AHL - (DE+DE+1)
sbc hl,de \ sbc a,b
inc e
or a
sbc hl,de \ sbc a,b
ret p
add hl,de
adc a,b
dec e
add hl,de
adc a,b
ret
sqrt32sub_2:
;min: 185cc
;max: 231cc
;avg: 208cc
call +_
_:
;min: 84cc
;max: 107cc
;avg: 95.5cc
sll e \ rl d
add a,a \ adc hl,hl
add a,a \ adc hl,hl
sbc hl,de
inc e
ret nc
dec e
add hl,de
dec e
ret
sqrt32_iter15:
;91+{8,0+{0,23}}
;min: 91cc
;max: 114cc
;avg: 100.75cc
sll e \ rl d ;sla e \ rl d \ inc e
add a,a
adc hl,hl
add a,a
adc hl,hl ;This might overflow!
jr c,sqrt32_iter15_br0
sbc hl,de
inc e
ret nc
dec e
add hl,de
dec e
ret
sqrt32_iter15_br0:
or a
sbc hl,de
inc e
ret
sqrtA: ;Written by Zeda
;Input: A
;Output: D is the squareroot, A is the remainder (input-D^2)
;Destroys: E
;speed: 118+{0,6}+{0,7}+{0,7}+{0,3}
;min: 118cc
;max: 141cc
;avg: 129.5cc
;38 bytes
ld de,5040h ; 10
sub e ; 4
jr nc,sqrtA_skip1 ;\
add a,e ; | branch 1: 12cc
ld d,10h ; | branch 2: 18cc
sqrtA_skip1: ;/
; ----------
cp d ; 4
jr c,sqrtA_skip2 ;\
sub d ; | branch 1: 12cc
set 5,d ; | branch 2: 19cc
sqrtA_skip2: ;/
; ----------
res 4,d ; 8
srl d ; 8
set 2,d ; 8
cp d ; 4
jr c,sqrtA_skip3 ;\
sub d ; | branch 1: 12cc
set 3,d ; | branch 2: 19cc
sqrtA_skip3: ;/
srl d ; 8
; ----------
inc a ; 4
sub d ; 4
jr nc,sqrtA_skip4 ;\
dec d ; | branch 1: 12cc
add a,d ; | branch 2: 15cc
sqrtA_skip4: ;/
srl d ; 8
ret ; 10
sqrtDE:;returns HL as the sqrt, DE as the remainder
;33 bytes;min: 928cc;max: 1120cc;avg: 1024cc;928+8{24,0}
ld b,$80
xor a
ld h,a
ld l,a
sqrt_loop:
srl b
rra
ld c,a
add hl,bc
ex de,hl
sbc hl,de
jr nc,+_
add hl,de
ex de,hl
or a
sbc hl,bc
.db $DA ;start of jp c,** which is 10cc to skip the next two bytes.
_:
ex de,hl
add hl,bc
srl h
rr l
srl b
rra
jr nc,sqrt_loop
ret
sqrtfixed_88:
;Inputs: A.C
;Output: D.E contains the squareroot
;speed: 1482+12{0,17}
;min: 1482cc
;max: 1686cc
;avg: 1584cc
;Adapted from Axe
;35 bytes
ld b,12
ld de,0
ld h,d
ld l,e
__Sqrt88Loop:
sub $40
sbc hl,de
jr nc,__Sqrt88Skip
add a,$40
adc hl,de
__Sqrt88Skip:
ccf
rl e
rl d
sla c
rla
adc hl,hl
sla c
rla
adc hl,hl
djnz __Sqrt88Loop
ret
sqrtfixed_88:
;Input: A.E ==> D.E
;Output: DE is the sqrt, AHL is the remainder
;Speed: 690+6{0,13}+{0,3+{0,18}}+{0,38}+sqrtA
;min: 855cc
;max: 1003cc
;avg: 924.5cc
;152 bytes
;Written by Zeda
call sqrtA
ld l,a
ld a,e
ld h,0
ld e,d
ld d,h
sla e
rl d
sll e \ rl d
add a,a \ adc hl,hl
add a,a \ adc hl,hl
sbc hl,de
jr nc,+_
add hl,de
dec e
.db $FE ;start of `cp *`
_:
inc e
sll e \ rl d
add a,a \ adc hl,hl
add a,a \ adc hl,hl
sbc hl,de
jr nc,+_
add hl,de
dec e
.db $FE ;start of `cp *`
_:
inc e
sll e \ rl d
add a,a \ adc hl,hl
add a,a \ adc hl,hl
sbc hl,de
jr nc,+_
add hl,de
dec e
.db $FE ;start of `cp *`
_:
inc e
sll e \ rl d
add a,a \ adc hl,hl
add a,a \ adc hl,hl
sbc hl,de
jr nc,+_
add hl,de
dec e
.db $FE ;start of `cp *`
_:
inc e
;Now we have four more iterations
;The first two are no problem
sll e \ rl d
add hl,hl
add hl,hl
sbc hl,de
jr nc,+_
add hl,de
dec e
.db $FE ;start of `cp *`
_:
inc e
sll e \ rl d
add hl,hl
add hl,hl
sbc hl,de
jr nc,+_
add hl,de
dec e
.db $FE ;start of `cp *`
_:
inc e
sqrtfixed_88_iter11:
;On the next iteration, HL might temporarily overflow by 1 bit
sll e \ rl d ;sla e \ rl d \ inc e
add hl,hl
add hl,hl
jr c,sqrtfixed_88_iter11_br0
sbc hl,de
jr nc,+_
add hl,de
dec e
jr sqrtfixed_88_iter12
sqrtfixed_88_iter11_br0:
or a
sbc hl,de
_:
inc e
;On the next iteration, HL is allowed to overflow,
DE could overflow with our current routine,
but it needs to be shifted right at the end, anyways
sqrtfixed_88_iter12:
ld b,a ;A is 0, so B is 0
add hl,hl
add hl,hl
rla
;AHL - (DE+DE+1)
sbc hl,de \ sbc a,b
inc e
or a
sbc hl,de \ sbc a,b
ret p
add hl,de
adc a,b
dec e
add hl,de
adc a,b
ret
;Adapted from Axe
sqrtHL:;Input: HL;Output: D is the square root,
cH is the remainder (c being the c flag),
A is 0, B is 0, L is 0
;speed: 758+8{0,6}
;min: 758cc;max: 806cc;avg: 782cc;26 bytes
p_Sqrt:
ld a,l
ld l,h
ld de,$0040
ld h,d
ld b,8
or a
__SqrtLoop:
sbc hl,de
jr nc,__SqrtSkip
add hl,de
__SqrtSkip:
ccf
rl d
add a,a
adc hl,hl
add a,a
adc hl,hl
djnz __SqrtLoop
ret
;written by Zeda
sqrtHL:;returns A as the sqrt, HL as the remainder, D = 0 fastest
;min: 352cc;max: 391cc;avg: 371.5cc
ld de,05040h ; 10
ld a,h ; 4
sub e ; 4
jr nc,sq7 ;\
add a,e ; | branch 1: 12cc
ld d,16 ; | branch 2: 18cc
sq7: ;/
cp d ; 4
jr c,sq6 ;\
sub d ; | branch 1: 12cc
set 5,d ; | branch 2: 19cc
sq6: ;/
res 4,d ; 8
srl d ; 8
set 2,d ; 8
cp d ; 4
jr c,sq5 ;\
sub d ; | branch 1: 12cc
set 3,d ; | branch 2: 19cc
sq5: ;/
srl d ; 8
inc a ; 4
sub d ; 4
jr nc,sq4 ;\
dec d ; | branch 1: 12cc
add a,d ; | branch 2: 19cc
dec d ; | <-- this resets the low bit of D, so `srl d` resets carry.
sq4: ;/
srl d ; 8
ld h,a ; 4
ld a,e ; 4
sbc hl,de ; 15
jr nc,sq3 ;\
add hl,de ; | 12cc or 18cc
sq3: ;/
ccf ; 4
rra ; 4
srl d ; 8
rra ; 4
ld e,a ; 4
sbc hl,de ; 15
jr c,sq2 ;\
or 20h ; | branch 1: 23cc
db 254 ; | <-- start of `cp *` which is 7cc to skip the next byte.
sq2: ; | branch 2: 21cc
add hl,de ;/
xor 18h ; 7
srl d ; 8
rra ; 4
ld e,a ; 4
sbc hl,de ; 15
jr c,sq1 ;\
or 8 ; | branch 1: 23cc
db 254 ; | <-- start of `cp *` which is 7cc to skip the next byte.
sq1: ; | branch 2: 21cc
add hl,de ;/
xor 6 ; 7
srl d ; 8
rra ; 4
ld e,a ; 4
sbc hl,de ; 15
jr nc,+_ ; \
add hl,de ; 15 |
srl d ; 8 |
rra ; 4 | branch 1: 38cc
ret ; 10 | branch 2: 40cc
_: ; |
inc a ; 4 |
srl d ; 8 |
rra ; 4 |
ret ; 10 /
; fast 16-bit isqrt by Zeda Thomas; Feel free to use for whatever :)
very fastest
sqrtHL:
;Input: HL;Output: A is the integer square root of HL
;Destroys: HL,DE (D is actually 0)
;min: 343cc
;max: 380cc
;avg: 361.5cc
;88 bytes
ld de,05040h ; 10
ld a,h ; 4
sub e ; 4
jr nc,sq7 ;\
add a,e ; | branch 1: 12cc
ld d,16 ; | branch 2: 18cc
sq7: ;/
; ----------
cp d ; 4
jr c,sq6 ;\
sub d ; | branch 1: 12cc
set 5,d ; | branch 2: 19cc
sq6: ;/
; ----------
res 4,d ; 8
srl d ; 8
set 2,d ; 8
cp d ; 4
jr c,sq5 ;\
sub d ; | branch 1: 12cc
set 3,d ; | branch 2: 19cc
sq5: ;/
srl d ; 8
; ----------
inc a ; 4
sub d ; 4
jr nc,sq4 ;\
dec d ; | branch 1: 12cc
add a,d ; | branch 2: 19cc
dec d ; | <-- this resets the low bit of D, so `srl d` resets carry.
sq4: ;/
srl d ; 8
ld h,a ; 4
; ----------
ld a,e ; 4
sbc hl,de ; 15
jr nc,sq3 ;\
add hl,de ; | 12cc or 18cc
sq3: ;/
ccf ; 4
rra ; 4
srl d ; 8
rra ; 4
; ----------
ld e,a ; 4
sbc hl,de ; 15
jr c,sq2 ;\
or 20h ; | branch 1: 23cc
db 254 ; | <-- start of `cp *` which is 7cc to skip the next byte.
sq2: ; | branch 2: 21cc
add hl,de ;/
xor 18h ; 7
srl d ; 8
rra ; 4
; ----------
ld e,a ; 4
sbc hl,de ; 15
jr c,sq1 ;\
or 8 ; | branch 1: 23cc
db 254 ; | <-- start of `cp *` which is 7cc to skip the next byte.
sq1: ; | branch 2: 21cc
add hl,de ;/
xor 6 ; 7
srl d ; 8
rra ; 4
; ----------
ld e,a ; 4
sbc hl,de ; 15
;This code would restore the square root
; jr nc,sq0 ;\
; add hl,de ; | 12cc or 18cc
; sq0: ;/
sbc a,255 ; 7
srl d ; 8
rra ; 4
ret ; 10
sqrtHLIX:
;Input: HLIX;Output: DE is the sqrt, AHL is the remainder
;speed: 751+6{0,6}+{0,3+{0,18}}+{0,38}+sqrtHL
;min: 1103;max: 1237;avg: 1165.5;166 bytes
call sqrtHL ;expects returns A as sqrt, HL as remainder, D = 0
add a,a
ld e,a
rl d
ld a,ixh
sll e \ rl d
add a,a \ adc hl,hl
add a,a \ adc hl,hl
sbc hl,de
jr nc,+_
add hl,de
dec e
.db $FE ;start of `cp *`
_:
inc e
sll e \ rl d
add a,a \ adc hl,hl
add a,a \ adc hl,hl
sbc hl,de
jr nc,+_
add hl,de
dec e
.db $FE ;start of `cp *`
_:
inc e
sll e \ rl d
add a,a \ adc hl,hl
add a,a \ adc hl,hl
sbc hl,de
jr nc,+_
add hl,de
dec e
.db $FE ;start of `cp *`
_:
inc e
sll e \ rl d
add a,a \ adc hl,hl
add a,a \ adc hl,hl
sbc hl,de
jr nc,+_
add hl,de
dec e
.db $FE ;start of `cp *`
_:
inc e
;Now we have four more iterations
;The first two are no problem
ld a,ixl
sll e \ rl d
add a,a \ adc hl,hl
add a,a \ adc hl,hl
sbc hl,de
jr nc,+_
add hl,de
dec e
.db $FE ;start of `cp *`
_:
inc e
sll e \ rl d
add a,a \ adc hl,hl
add a,a \ adc hl,hl
sbc hl,de
jr nc,+_
add hl,de
dec e
.db $FE ;start of `cp *`
_:
inc e
sqrt32_iter15:
;On the next iteration, HL might temporarily overflow by 1 bit
sll e \ rl d ;sla e \ rl d \ inc e
add a,a
adc hl,hl
add a,a
adc hl,hl ;This might overflow!
jr c,sqrt32_iter15_br0
sbc hl,de
jr nc,+_
add hl,de
dec e
jr sqrt32_iter16
sqrt32_iter15_br0:
or a
sbc hl,de
_:
inc e
;On the next iteration, HL is allowed to overflow,
DE could overflow with our current routine,
but it needs to be shifted right at the end, anyways
sqrt32_iter16:
add a,a
ld b,a ;either 0x00 or 0x80
adc hl,hl
rla
adc hl,hl
rla
;AHL - (DE+DE+1)
sbc hl,de \ sbc a,b
inc e
or a
sbc hl,de \ sbc a,b
ret p
add hl,de
adc a,b
dec e
add hl,de
adc a,b
ret
SqrtL:
;Inputs:; L is the value to find the square root of
;Outputs:
; C is the result
; B,L are 0
; DE is not changed
; H is how far away it is from the next smallest perfect square
; L is 0
; z flag set if it was a perfect square;Destroyed: A
ld bc,400h ; 10 10
ld h,c ; 4 4
sqrt8Loop: ;
add hl,hl ;11 44
add hl,hl ;11 44
rl c ; 8 32
ld a,c ; 4 16
rla ; 4 16
sub a,h ; 4 16
jr nc,$+5 ;12|19 48+7x
inc c
cpl
ld h,a
djnz sqrt8Loop ;13|8 47
ret ;10 10
;287+7x, x is the number of bits in the result;min: 287;max: 315;19 bytes
L_sqrd:
;Input: L;Output: L*L->A
;147 t-states;36 bytes
ld b,l
;First iteration, get the lowest 3 bits of -x^2
sla l
rrc b
sbc a,a
or l
ld c,a
;second iteration, get the next 2 bits of -x^2
rrc b
sbc a,a
xor l
and $F8
add a,c
ld c,a
;third iteration, get the next 2 bits of -x^2
sla l
rrc b
sbc a,a
xor l
and $E0
add a,c
ld c,a
;fourth iteration, get the eight bit of x^2
sla l
rrc b
sbc a,a
xor l
and $80
sub c
ret
sqrA:
;A*A->A
;Destroys: HL
;76cc or 79cc or 82cc
;Avg: 79cc;51 bytes
add a,a
add a,a
jr nc,$+4
neg
rrca
rrca
ld l,a
srl l
ld h,sqrLUT/256
jr c,$+4
neg
add a,(hl)
ret
sqrLUT:
;MUST BE ALIGNED to a 256-byte boundary.
;Can use:
; #if 0!=$&255
; .fill 256-($&255),0
; #endif
.db $00,$06,$14,$2A,$48,$6E,$9C,$D2
.db $10,$56,$A4,$FA,$58,$BE,$2C,$A2
.db $20,$A6,$34,$CA,$68,$0E,$BC,$72
.db $30,$F6,$C4,$9A,$78,$5E,$4C,$42
;|HL = SQR (HL)
;4.RAS - квадратный корень
;INPUT : HL <-- из чего
;OUTPUT: HL = SQR (HL)
;портятся DE,HL,BC,A
RAS LD A,H
OR L
JR Z,RAS_4
RAS1 LD A,H
AND A
JP NZ,RAS11
LD A,L
CP 1
JP NZ,RAS11
LD HL,1
RET
RAS11 LD B,H
LD C,L
SRL B
RR C
RAS_1 PUSH HL
LD D,B
LD E,C
CALL DIV
ADD HL,BC
SRL H
RR L
PUSH HL
LD D,B
LD E,C
SBC HL,DE
JP NC,RES_10
ADD HL,DE
EX DE,HL
SBC HL,DE
RES_10 LD A,H
AND A
JP NZ,RAS_0
LD A,L
CP 2
JP NC,RAS_0
POP HL
POP BC
RET
RAS_0 POP BC
POP HL
JP RAS_1
RAS_4 LD HL,0
RET
SQR OR A
LD A,L
LD L,H
LD DE,64
LD H,D
LD B,7
SQR0 SBC HL,DE
JR NC,$+3
ADD HL,DE
CCF
RL D
RLA
ADC HL,HL
JP C,0
RLA
ADC HL,HL
JP C,0
DJNZ SQR0
SBC HL,DE
;JR NC,$+3
;ADD HL,DE
CCF
RL D
;RLA
;ADC HL,HL
;RLA
;ADC HL,HL
RET
;sqr hl (from Inferno 4)
;HL=аргумент N
OR A
LD A,L
LD L,H
LD DE,64
LD H,D
LD B,8
sqr0 SBC HL,DE
JR NC,$+3
ADD HL,DE
CCF
RL D
ADD A,A
ADC HL,HL
ADD A,A
ADC HL,HL
DJNZ sqr0
;D=результат
Posted by John Metcalf
; 16-bit integer square root
; 34 bytes, 1005-1101 cycles (average 1053)
; call with de = number to square root
; returns hl = square root
; corrupts bc, de
ld bc,#8000
ld h,c
ld l,c
sqrlp srl b
rr c
add hl,bc
ex de,hl
sbc hl,de
jr c,sqrbit
ex de,hl
add hl,bc
jr sqrfi
sqrbit add hl,de
ex de,hl
or a
sbc hl,bc
sqrfi srl h
rr l
srl b
rr c
jr nc,sqrlp
ret
;
;
;
Posted by John Metcalf
; fast 16-bit integer square root
; 92 bytes, 344-379 cycles (average 362)
; v2 - 3 t-state optimization spotted by Russ McNulty
; call with hl = number to square root
; returns a = square root
; corrupts hl, de
ld a,h
ld de,#B0C0
add a,e
jr c,sq7
ld a,h
ld d,#F0
sq7
;
add a,d
jr nc,sq6
res 5,d
db 254
sq6 sub d
sra d
;
set 2,d
add a,d
jr nc,sq5
res 3,d
db 254
sq5 sub d
sra d
;
inc d
add a,d
jr nc,sq4
res 1,d
db 254
sq4 sub d
sra d
ld h,a
;
add hl,de
jr nc,sq3
ld e,#40
db 210
sq3 sbc hl,de
sra d
ld a,e
rra
;
or #10
ld e,a
add hl,de
jr nc,sq2
and #DF
db 218
sq2 sbc hl,de
sra d
rra
;
or #04
ld e,a
add hl,de
jr nc,sq1
and #F7
db 218
sq1 sbc hl,de
sra d
rra
;
inc a
ld e,a
add hl,de
jr nc,sq0
and #FD
sq0 sra d
rra
cpl
ret
NEG
neghl xor a
sub l
ld l,a
sbc a,h;a,a
sub l
ld h,a
ret
negde xor a
sub e
ld e,a
sbc a,d ;a,a
sub e
ld d,a
ret
RANDOM
;by DDp
;in: HL', DE' out: A, HL', DE'
RANDOM
EXX
LD C,L
LD A,H
RL C
RLA
RL C
RLA
LD B,A
RL C
RLA
RL C
RLA
RL C
RLA
XOR B
LD H,L
LD L,D
LD D,E
LD E,A
EXX
RET
Процедура jtn’а для двухбайтного рандома, с сидом.
Для однобайтного можно брать любой байт из двух:
rnd16
ld de,seed
ld a,d,h,e,l,253
or a:sbc hl,de,a,0,hl,de
ld d,0:sbc a,d:ld e,a:sbc hl,de
jr nc,rnd:inc hl
rnd ld (rnd16+1),hl
ret
random_color
ld a,r
and 7
or a
jr z,random_color
ret
Xorshift
Xorshift is a class of pseudorandom number generators
discover by George Marsaglia and detailed in his
2003 paper, Xorshift RNGs.
; 16-bit xorshift pseudorandom number generator
by John Metcalf, 20 bytes, 86 cycles (excluding ret)
; returns hl = pseudorandom number; corrupts a
; generates 16-bit pseudorandom numbers
with a period of 65535
; using the xorshift method:
hl ^= hl << 7; hl ^= hl >> 9; hl ^= hl << 8
; some alternative shift triplets which also
perform well are: 6, 7, 13; 7, 9, 13; 9, 7, 13.
xornd ld hl,1 ; seed must not be 0
ld a,h
rra
ld a,l
rra
xor h
ld h,a
ld a,l
rra
ld a,h
rra
xor l
ld l,a
xor h
ld h,a
ld (xornd+1),hl
ret
;You may use this routine, just be sure to
credit John Metcalf! Written by John Metcalf
;http://www.retroprogramming.com/2017/07/xorshift-pseudorandom-numbers-in-z80.html
; Annotated by Zeda Thomas,
fixed typo (86 cycles==> 82 cycles)
;Note: uses SMC 16-bit xorshift pseudorandom
number generator
; 20 bytes, 82 cycles (excluding ret)
; returns hl = pseudorandom number
; corrupts a
xorrnd
ld hl,1 ; Init the seed, must not be 0
ld a,h ;\
rra ; | Get the top bits of xs<<7 and xor with the top byte of HL
ld a,l ; | abcdefgh ijklmnop
rra ; | ^hijklmno 00000000
xor h ; | Note that we still need to xor the 'p' with the top byte of l
ld h,a ;/
ld a,l ;\
rra ; | we get 'p' in the carry flag, now shift that in when we do xs>>9
ld a,h ; | abcdefgh ijklmnop (new value)
rra ; | ^00000000 pabcdefg
xor l ; | the 'p' is leftover from the first step, so now Step 1 and 2 are done
ld l,a ;/
xor h ;\ Finally, xor the bottom byte with the top byte for step 3
ld h,a ;/
ld (xorrnd+1),hl ; write back the new value as the next seed
ret
;Dean Belfield
; 16 bit random number routine I found on the web
; Returns a pseudo-random number in the HL register
RND16_SEED: EQU 12345
RND16: LD DE,RND16_SEED
LD A,D
LD H,E
LD L,253
OR A
SBC HL,DE
SBC A,0
SBC HL,DE
LD D,0
SBC A,D
LD E,A
SBC HL,DE
JR NC,1F
INC HL
1f LD (RND16+1),HL
RET
RND_32 LD HL,(SEED)
CALL RND
RND LD A,H
ADD HL,HL
XOR H
ADD HL,HL
ADD HL,HL
ADD HL,HL
XOR H
ADD HL,HL
ADD HL,HL
XOR H
ADD HL,HL
ADD HL,HL
LD L,A
LD (SEED),HL
RET
SEED DEFW #FFFF ; НЕ НОЛЬ!
Следующая процедура является генератором RND.
На выходе из процедуры в
регистровой паре HL будет число от 0 до 65535.
10 ORG 40000
20 BEGIN LD HL,(TEMP)
30 LD C,16
40 S1 LD A,H
50 ADD HL,HL
60 AND 255
70 JP PE,S2
80 INC HL
90 S2 DEC C
100 JR NZ,S1
110 LD (TEMP),HL
120 TEMP DEFB 1,0
from Pplayers (Saracen)
RANDOM LD DE,(SEED)
LD H,E
LD L,253
LD A,D
OR A
LD B,0
SBC HL,DE
SBC A,B
SBC HL,DE
SBC A,B
LD E,A
LD D,B
SBC HL,DE
JP NC,RANDOM2
INC HL
RANDOM2 LD (SEED),HL
RET
by Baze
RANDOM
ld a,SEED ;SEED is usually 0
ld b,a
add a,a
add a,a
add a,b
inc a ;you may try also ADD A,7
ld (RANDOM+1),a
ret
8-bit Xor-Shift random number generator.
;Created by Patrik Rak in 2008 and revised in 2011/2012.
;http://www.worldofspectrum.org/forums/showthread.php?t=23070
org 40000
call rnd ; BASIC driver
ld c,a
ld b,0
ret
xsrnd ld hl,0xA280 ; yw -> zt
ld de,0xC0DE ; xz -> yw
ld (rnd+4),hl ; x = y, z = w
ld a,l ; w = w ^ ( w << 3 )
add a,a
add a,a
add a,a
xor l
ld l,a
ld a,d ; t = x ^ (x << 1)
add a,a
xor d
ld h,a
rra ; t = t ^ (t >> 1) ^ w
xor h
xor l
ld h,e ; y = z
ld l,a ; w = t
ld (xsrnd+1),hl
ret
pentis by tony raugas (далеко не самый лучший вариант)
; ;---
; ld a, r ;рандом но он раскидан
; dec a ;
; adc a, 1 ;
; ld (random_var), a ;
; ld hl, (random_var);теперь объеденил и рандом заработал как надо
; ld b, 5 ;раньше выдавал первую фигуру в тетрисе Т в петрисе L в пентисе Y (не суть)
; frn1 sla l
; rl h ;ВНИМАНИЯ-ВНИМАНЬЯ! рандом в самый первый раз НАДО ВЫЗВАТЬ ДВАЖДЫ!!!
; ld a, #C0 ;что в принципе и сделано
; and h ;зато после удаления фигур и режимов петриса и пентиса - заглючило опять
; jp pe, frn2
; inc l
; frn2 djnz frn1
; ld (random_var), hl
8-bit Random Number Generator
This is a very simple linear congruential generator.
The formula is x[i + 1] = (5 * x[i] + 1) mod 256.
Its only advantage is small size and simplicity.
Due to nature of such generators only a couple
of higher bits should be considered random.
Input: none ,
Output: A = pseudo-random number, period 256
Rand8 ld a,Seed ; Seed is usually 0
ld b,a
add a,a
add a,a
add a,b
inc a ; another possibility is ADD A,7
ld (Rand8+1),a
ret
;This code snippet is 9 bytes and 43cc
;Inputs:; HL is the input seed and must be non-zero
;Outputs:; A is the 8-bit pseudo-random number
; HL is the new seed value (will be non-zero)
;opcode cc
add hl,hl ; 29 11
sbc a,a ; 9F 4
and %00101101 ; E62D 7
xor l ; AD 4
ld l,a ; 6F 4
ld a,r ; ED5F 9
add a,h ; 84 4
;Technical details:
; The concept behind this routine is to combine an LFSR (poor RNG) with a
; counter. The counter improves the RNG quality, while also extending the period
; length.
; For this routine, I took advantage of the Z80's built-in counter, the `r`
; register. This means that we don't need to store the counter anywhere, and it
; is pretty fast to access!
; Some caveats:
; * r is a 7-bit counter
; * r will increment some number of times between runs of the RNG. In most
; cases, this will be constant, but if it increments an even number each
; time, then the bottom bit is always the same, weakening the effect of
; the counter. In the worst case, it increments a multiple of 128 times,
; effectively making your RNG just as good/bad as the LFSR. Ideally, you
; want `r` to increment an odd number of times between runs.
; * In the best case, the bottom 7 bits have 50/50 chance of being 0 or 1.
; The top bit is 1 with probability 1/2 + 1/(2^17-2) ~ .5000076295
; * In the event that your main loop waits for user input between calls,
; then congatulations, you might have a True RNG :)
Fast quality CMWC random number generator for Z80
I have created to weed out the crap RNGs out there.
32+10 bytes, 146..149 T cycles, period 253*2^59,
which is more than 2^66 or 10^20.
cmwc.asm
; 8-bit Complementary-Multiply-With-Carry (CMWC)
random number generator.
; Created by Patrik Rak in 2012, and revised in 2014/2015,
; with optimization contribution
from Einar Saukas and Alan Albrecht.
;http://www.worldofspectrum.org/forums/showthread.php?t=39632
org 40000
call rnd ; BASIC driver
ld c,a
ld b,0
ret
rnd ld hl,table
ld a,(hl) ; i = ( i & 7 ) + 1
and 7
inc a
ld (hl),a
inc l ; hl = &cy
ld d,h ; de = &q[i]
add a,l
ld e,a
ld a,(de) ; y = q[i]
ld b,a
ld c,a
ld a,(hl) ; ba = 256 * y + cy
sub c ; ba = 255 * y + cy
jr nc,$+3
dec b
sub c ; ba = 254 * y + cy
jr nc,$+3
dec b
sub c ; ba = 253 * y + cy
jr nc,$+3
dec b
ld (hl),b ; cy = ba >> 8, x = ba & 255
cpl ; x = (b-1) - x = -x - 1 = ~x + 1 - 1 = ~x
ld (de),a ; q[i] = x
ret
table db 0,0,82,97,120,111,102,116,20,15
if (table/256)-((table+9)/256)
error "whole table must be within single 256 byte block"
endif
4.2 16-bit Random Number Generator
This generator is based on similar method but
gives much better results.
It was taken from an old ZX Spectrum
game and slightly optimised.
Input: none
Output: HL = pseudo-random number, period 65536
Rand16 ld de,Seed ; Seed is usually 0
ld a,d
ld h,e
ld l,253
or a
sbc hl,de
sbc a,0
sbc hl,de
ld d,0
sbc a,d
ld e,a
sbc hl,de
jr nc,Rand
inc hl
Rand ld (Rand16+1),hl
ret
Z80 Psuedo-random number generator
ZX Spectrum Random Numbers
LD A,R ; Load the A register with the refresh register
LD L,A ; Copy register A into register L
AND %00111111 ; This masking prevents the address we are forming from accessing RAM
LD H,A ; Copy register A into register H
LD A,(HL) ; Load the pseudo-random value into A
; HOW THIS WORKS
; The refresh register in the Z80 is highly
unpredictable since it is incremented every cycle.
; Because it may be at any value when this
routine is called, it is very good for random numbers.
; This routine increases the randomness of the
number since it forms an address based on the
; refresh counter's current status and accesses
the memory at that address.
; It can also be modified to get a sixteen-bit
pseudo-random number by changing line 5 to LD D,(HL)
; and adding these two lines to the end:
; INC L
; LD E,(HL)
; This routine was written for the ZX Spectrum
which has a 16KB ROM. If you plan to use this routine
; on another Z80 system, change the binary value at
the AND instruction. For example, if you had a
; Z80 computer with an 8KB ROM, you would change the
binary value to %00011111.
; Fast RND
; An 8-bit pseudo-random number generator,
; using a similar method to the Spectrum ROM,
; - without the overhead of the Spectrum ROM.
; R = random number seed
; an integer in the range [1, 256]
; R -> (33*R) mod 257
; S = R - 1
; an 8-bit unsigned integer
ld a, (seed)
ld b, a
rrca ; multiply by 32
rrca
rrca
xor #1f
add a, b
sbc a, 255 ; carry
ld (seed), a
ret
This is a short peice of code that generates a maximal length,
8 bit, pseudo random number sequence.
It was originally used to switch off and on all the LED's in a 8x30 matrix
in a good random looking order (the un-needed values being skipped).
It's shorter, but slower, than using a lookup table and a counter.
; returns pseudo random 8 bit number in A. Only affects A.
; (r_seed) is the byte from which the number is generated and MUST be
; initialised to a non zero value or this function will always return
; zero. Also r_seed must be in RAM, you can see why......
rand_8 LD A,(r_seed) ; get seed
AND #B8h ; mask non feedback bits
SCF ; set carry
JP PO,no_clr ; skip clear if odd
CCF ; complement carry (clear it)
no_clr LD A,(r_seed) ; get seed back
RLA ; rotate carry into byte
LD (r_seed),A ; save back for next prn
RET ; done
r_seed DB 1 ; prng seed byte (must not be zero)
Ion Random
This is based off the tried and true pseudorandom
number generator featured in Ion by Joe Wingbermuehle
;-----> Generate a random number
; output a=answer 0<=a<=255
; all registers are preserved except: af
random:
push hl
push de
ld hl,(randData)
ld a,r
ld d,a
ld e,(hl)
add hl,de
add a,l
xor h
ld (randData),hl
pop de
pop hl
ret
randData dw 12345 ;here must be a 2 byte seed located in ram.
While this is a fast generator,
it's generally not considered very good
in terms of randomness.
Linear Feedback Shift Register
This particular prng is based on Linear
feedback shift register.
It uses a 64bit seed and generates 8 new
bits at every call.
LFSRSeed must be an 8 byte seed located in ram.
;------LFSR------
;James Montelongo
;optimized by Spencer Putt
;out: a = 8 bit random number
RandLFSR ld hl,LFSRSeed+4
ld e,(hl)
inc hl
ld d,(hl)
inc hl
ld c,(hl)
inc hl
ld a,(hl)
ld b,a
rl e \ rl d
rl c \ rla
rl e \ rl d
rl c \ rla
rl e \ rl d
rl c \ rla
ld h,a
rl e \ rl d
rl c \ rla
xor b
rl e \ rl d
xor h
xor c
xor d
ld hl,LFSRSeed+6
ld de,LFSRSeed+7
ld bc,7
lddr
ld (de),a
ret
While this may produces better numbers,
it is slower, larger and requires a bigger seed
than ionrandom. Assuming theres is a good seed to start,
it should generate ~2^56 bytes before repeating. However if t
here is not a good seed(0 for example), then the numbers crea
ted will not be adequate. Unlike Ionrandom and its use of the
r register, starting with the same seed the same numbers will
be generated. With Ionrandom
the code running may have an impact on the number generated.
This means this method requires more initialization.
You can initialize with TI-OS's seeds, stored at
seed1 and seed2, both are ti-floats but will serve the purpose.
Combined LFSR/LCG, 16-bit seeds
This is a very fast, quality pseudo-random number generator.
It combines a
16-bit Linear Feedback Shift Register
and a 16-bit LCG.
prng16:
;Inputs:
; (seed1) contains a 16-bit seed value
; (seed2) contains a NON-ZERO 16-bit seed value
;Outputs:
; HL is the result
; BC is the result of the LCG, so not that great of quality
; DE is preserved
;Destroys:
; AF
;cycle: 4,294,901,760 (almost 4.3 billion)
;160cc
;26 bytes
ld hl,(seed1)
ld b,h
ld c,l
add hl,hl
add hl,hl
inc l
add hl,bc
ld (seed1),hl
ld hl,(seed2)
add hl,hl
sbc a,a
and %00101101
xor l
ld l,a
ld (seed2),hl
add hl,bc
ret
On their own, LCGs and LFSRs don't pr
oduce great results and are generally
very cyclical, but they are very fast
to compute. The 16-bit LCG in the above
example will bounce around and reach each
number from 0 to 65535, but the lower bits
are far more predictable than the upper bits. Th
e LFSR mixes up the predictability of a given bit'
s state, but it hits every number except 0, meanin
g there is a slightly higher chance of any given b
it in the result being a 1 instead of a 0. It turn
s out that by adding together the outputs of these
two generators, we can lose the predictability of a
bit's state, while ensuring it has a 50% chance of
being 0 or 1. As well, since the periods,
65536 and 65535 are coprime, then the overall period of
the generator is 65535*65536, which is over 4 billion.
Combined LFSR/LCG, 32-bit seeds
This is similar to the one above, except that it use
s 32-bit seeds (and still returns a 16-bit result).
An advantage here is that they've been tested and
passed randomness tests (all of thee ones offered
by CAcert labs). As well, it is still very fast.
rand32:
;Inputs:
; (seed1_0) holds the lower 16 bits of the first seed
; (seed1_1) holds the upper 16 bits of the first seed
; (seed2_0) holds the lower 16 bits of the second seed
; (seed2_1) holds the upper 16 bits of the second seed
; **NOTE: seed2 must be non-zero
;Outputs:
; HL is the result
; BC,DE can be used as lower quality values,
; but are not independent of HL.
;Destroys:
; AF
;Tested and passes all CAcert tests
;Uses a very simple 32-bit LCG and 32-bit LFSR
;it has a period of 18,446,744,069,414,584,320
;roughly 18.4 quintillion.
;LFSR taps: 0,2,6,7 = 11000101
;291cc
seed1_0=$+1
ld hl,12345
seed1_1=$+1
ld de,6789
ld b,h
ld c,l
add hl,hl \ rl e \ rl d
add hl,hl \ rl e \ rl d
inc l
add hl,bc
ld (seed1_0),hl
ld hl,(seed1_1)
adc hl,de
ld (seed1_1),hl
ex de,hl
seed2_0=$+1
ld hl,9876
seed2_1=$+1
ld bc,54321
add hl,hl \ rl c \ rl b
ld (seed2_1),bc
sbc a,a
and %11000101
xor l
ld l,a
ld (seed2_0),hl
ex de,hl
add hl,bc
ret
rand32:
;Tested and passes all CAcert tests
;Uses a very simple 32-bit LCG and 32-bit LFSR
;it has a period of 18,446,744,069,414,584,320
;roughly 18.4 quintillion.
;LFSR taps: 0,2,6,7 = 11000101
;291cc
;Thanks to Runer112 for his help on optimizing the LCG
and suggesting to try the much simpler LCG. On their own,
the two are terrible, but together they are great.
;58 bytes
seed1_0=$+1
ld hl,12345
seed1_1=$+1
ld de,6789
ld b,h
ld c,l
add hl,hl \ rl e \ rl d
add hl,hl \ rl e \ rl d
inc l
add hl,bc
ld (seed1_0),hl
ld hl,(seed1_1)
adc hl,de
ld (seed1_1),hl
ex de,hl
;;lfsr
seed2_0=$+1
ld hl,9876
seed2_1=$+1
ld bc,54321
add hl,hl \ rl c \ rl b
ld (seed2_1),bc
sbc a,a
and %11000101
xor l
ld l,a
ld (seed2_0),hl
ex de,hl
add hl,bc
ret
rand24:
;;219cc
#ifdef smc
seed1_0=$+1
ld hl,12345
seed1_1=$+1
ld a,67
#else
ld hl,(seed1_0)
ld a,(seed1_1)
#endif
ld b,h
ld c,l
ld d,a
add hl,hl \ rla
add hl,hl \ rla
inc l
add hl,bc \ adc a,0
ld (seed1_0),hl
ld (seed1_1),a
ld c,b
ld b,a
#ifdef smc
seed2_0=$+1
ld hl,65432
seed2_1=$+1
ld a,10
#else
ld hl,(seed2_0)
ld a,(seed2_1)
#endif
add hl,hl
rla
ld (seed2_1),a
sbc a,a
and %10000111
xor l
ld l,a
ld (seed2_0),hl
add hl,bc
ret
;#define smc ;uncomment if you are using SMC
rand16:
;collaboration by Zeda with Runer112
;160cc or 148cc if using SMC
;26 bytes
;cycle: 4,294,901,760 (almost 4.3 billion)
#ifdef smc
seed1=$+1
ld hl,9999
#else
ld hl,(seed1)
#endif
ld b,h
ld c,l
add hl,hl
add hl,hl
inc l
add hl,bc
ld (seed1),hl
#ifdef smc
seed2=$+1
ld hl,9999
#else
ld hl,(seed2)
#endif
add hl,hl
sbc a,a
and %00101101
xor l
ld l,a
ld (seed2),hl
add hl,bc
ret
rand10:
;Returns A as a random integer on [0,9]
;Destroys: All
call rand5
sla h
rla
ret
rand5:
;Returns A on [0,4]
;Destroys: All
;Notes:
; This is a non-standard approach to generating random integers on [0,4].
; If you have a truly random number generator that generates bits (0 or 1)
; with equal probability, then standard approaches will still cause a slight
; bias. ("Standard": "rand mod 5" or int(5*rand)). For example, suppose we
; generate a 4-bit number. Then "rand mod 5" will cause 0 to be chosen
; 4/16 times, while 1, 2, 3, and 4 will be chosen 3/16 times (on average).
; A similar problem exists with int(5*rand). One way to mitigate this issue
; is just generating infintely many bits, but apparently that is impractical,
; so I came up with a compromise.
; My approach basically looks at the binary expansion of 1/5, 2/5, 3/5, and 4/5.
; 1/5 = .0011001100110011...
; 2/5 = .0110011001100110...
; 3/5 = .1001100110011001...
; 4/5 = .1100110011001100...
; So if I generate random bits and I get .001100, then a 0, then I know
; that no matter what all of the rest of the bits are, the number is less than
; 1/5, and so int(5*rand) is 0.
; By applying similar logic to the rest of the values, I can guarantee a uniform
; distribution on [0,4]. But there are four cases where this process might
; continue forever, specifically the cases that are like ...00110011...., but
; lucky for us, this happens 4/inf= 0% of the time. In fact, on average it
; takes 3 to 4 bits before the algorithm can assert which value to return.
; The one caveat is that on the Z80, we generally don't have truly random
; numbers :| On the otherhand, it is easy enough to generate pseudo-random
; bits with equal probability :)
call rand
ld a,h
and $C0
push af ;save the original value
ld c,a
rand5_start:
push bc
call rand
pop bc
ld b,15 ;I set this to 15 because I like to guarantee a bit is available for rand10.
rand5_loop:
ld a,h
xor c
jp p,rand5_end
add hl,hl
sla c
jr c,$+4
set 6,c
djnz rand5_loop
jr rand5_start
rand5_end:
pop af
rlca
rlca
sla h
adc a,0
ret
lfsr64:
;;Output: A is an 8-bit pseudo-random number.
ld hl,seed
sla (hl) \ inc hl
rl (hl) \ inc hl
rl (hl) \ inc hl
rl (hl) \ inc hl
rl (hl) \ inc hl
rl (hl) \ inc hl
rl (hl) \ inc hl
rl (hl)
ret nc
ld a,(seed)
xor %000011011
ld (seed),a
ret
lehmer:
;Input:
; (seed) has the seed value of the RNG
;Output:
; (seed) is updated, HL is the result
;Destroys:
; A,DE,BC
;Timing:
; if seed>0 231cc or 232cc, condition dependent
; if seed=0 91cc
; if SMC defined subtract 6cc
;Size: 44 bytes
;Notes:
; Uses the Lehmer RNG used by the Sinclair ZX81
; 75x mod 65537 -> x
#ifndef SMC
ld hl,(seed)
#else
seed = $+1
ld hl,0
#endif
;multiply by 75
ld c,l
ld b,h
xor a
adc hl,hl \ jr z,special \ ld d,a \ rla
add hl,hl \ rla
add hl,hl \ rla \ add hl,bc \ adc a,d
add hl,hl \ rla
add hl,hl \ rla \ add hl,bc \ adc a,d
add hl,hl \ rla \ add hl,bc
;modulo 65537, see note below on how this works
ld e,a
sbc hl,de ;No need to reset the c flag since it is already
jr nc,$+3
inc hl
ld (seed),hl
ret
special:
;In the case that HL=0,
this should be interpreted
as 65536 = -1 mod 65537,
so return -75 mod 65537 = -74 mod 65536 in HL
ld hl,-74
ld (seed),hl
ret
;mod by 2^16 + 1 (a prime)
;current form is A*2^16+HL
;need:
; (A*2^16+HL) mod (2^16+1)
;add 0 as +1-1
; (A*(2^16+1-1)+HL) mod (2^16+1)
;distribute
; (A*(2^16+1)-A+HL) mod (2^16+1)
;A*(2^16+1) mod 2^16+1 = 0, so remove
; (-A+HL) mod (2^16+1)
;Oh hey, that's easy! :P
;I use this trick everywhere, you should, too.
(из zx-review):
RND LD HL,0
LD A,H
ADD A,#77
LD H,A
RLС L
ADD A,L
LD L,A
LD (RND+1),HL
;rnd from Dave Perry's games
PUSH HL
PUSH HL
LD DE,(SEED)
LD H, E
LD L, #FD
LD A, D
AND A
SBC HL, DE
SBC A,0
SBC HL, DE
SBC A,0
LD E,A
LD D,0
SBC HL, DE
JR NC,$+3
INC HL
LD (SEED),HL
POP DE
POP HL
RET
SEED DW 0
А и B SEED останется RND
;from EXOLON by Rafaelle Cecco
;return A-random number
RND push hl
push de
push bc
ld hl, (rndSEED)
ld de, 7
add hl, de
ld e, l
ld d, h
add hl, hl
add hl, hl
ld c, l
ld b, h
add hl, hl
add hl, bc
add hl, de
ld (rndSEED), hl
xor h
pop bc
pop de
pop hl
ret
rndSEED dw 8EAAh
;from Lop Ears game by Players
RANDOM LD DE,(SEED)
LD H,E
LD L,253
LD A,D
OR A
LD B,0
SBC HL,DE
SBC A,B
SBC HL,DE
SBC A,B
LD E,A
LD D,B
SBC HL,DE
JP NC,RANDOM2
INC HL
RANDOM2 LD (SEED),HL
RET
Posted by John Metcalf
; 16-bit xorshift pseudorandom number generator
; 20 bytes, 86 cycles (excluding ret)
; returns hl = pseudorandom number
; corrupts a
xs16rnd
ld hl,1 ;must not be Zero
ld a,h
rra
ld a,l
rra
xor h
ld h,a
ld a,l
rra
ld a,h
rra
xor l
ld l,a
xor h
ld h,a
ld (xs16rnd+1),hl
ret
HEX, DEC, BIN, ASCII...
;a=0-3 bit of hex
or #f0
daa
add a,#a0
adc a,#40
;out A=ascii code of hex digit to print
;a=0-#0f
add a,#90
daa
adc a,#40
daa
;out A=ascii code of hex digit to print
;зажигает нужный бит в 16-разрядном регистре by Destr
;например:
;На входе A = 5
;На выходе HL = %0000000000100000
; SET A,HL
LD HL,0
RLA
RLA
CP #20
RLA
OR #C4
LD ($+4),A
DW #CBCB
;Или чуть медленней, но уже с игнором старшего полубайта А
; SET A,HL
LD HL,0
SLI A
SLA A
CP #20
RLA
OR #C4
LD ($+4),A
DW #CBCB
;from Fast Tracker
Decimal65535max ex de,hl
push hl
ld l,c
ld h,b
ld bc,#2710 ;10000
call decdigg
jr hd4096
Decimal4096max EX DE, HL
PUSH HL
LD L, C
LD H, B
hd4096 LD BC, #3E8 ;1000
CALL decdigg
LD BC, #64 ;100
CALL decdigg
LD BC, #A ;10
CALL decdigg
LD A, L
ADD A, #30
LD (DE), A
INC DE
POP HL
EX DE, HL
RET
decdigg LD A, #2F
OR A
decdigj INC A
SBC HL, BC
JR NC, decdigj
ADD HL, BC
LD (DE), A
INC DE
RET
;hex to Ascii из Fast Tracker
HexDigitToAsciiCode_X0 RRCA
RRCA
RRCA
RRCA
HexDigitToAsciiCode_0X AND #F
ADD A,#90;hex to ascii
DAA
ADC A,#40
DAA
CP #30 ;если ascii код это Ноль
RET NZ ;то
LD A,".";печатаем точки
RET
Библиотечка печати в различных системах счисления.
;------ PDES LIB ------
;Printing in Different Evaluation Systems LIBrary
;(c) SerzhSoft, Shadrinsk, V,3 1997
_NULL EQU 0 ;используется в изменяемых командах
;--- print in DEC system ---
PDEC_B ;Печать десятичного числа в A3 (000..255)
LD L,A ;поместили
LD H,#00 ; число в HL
LD DE,DECTB_W+4 ;адрес в таблице, начиная с сотни
LD B,#03 ;макс. возможное кол-во цифр - три
JR LP_PDW1 ;переход на цикл печати
;
PDEC_W ;Печать десятичного числа в HL3 (00000..65535)
PUSH HL ;закинули печатаемое число на стек
LD HL,DECTB_W ;адрес таблицы степеней десятки
LD B,#05 ;макс. возможное количество цифр
LP_PDW1 LD E,(HL) ;взяли текущую степень
INC HL ; десятки из таблицы
LD D,(HL) ; и поместили в DE
INC HL ;перешли к след. элементу таблицы
EX (SP),HL ;адрес эл-та <-> печатаемое число
XOR A ;обнулили счетчик и флаг C для SBC
LP_PDW2 INC A ;увеличиваем счетчик
SBC HL,DE ;вычитаем текущую степень десятки
JR NC,LP_PDW2 ;повторяем пока HL>=0
ADD HL,DE ;HL=HL mod DE; A=HL div DE
ADD A,"0"-1 ;перевод A в ASCII-код ("0".."9")
RST #10 ;печать десятичной цифры
EX (SP),HL ;HL=адрес эл-та, число -> на стек
DJNZ LP_PDW1 ;цикл по цифрам
POP HL ;убрали оставшийся ноль со стека
RET ;выход из процедуры
DECTB_W DW 10000,1000,100,10,1 ;Таблица степеней десятки
;--- print in HEX system ---
PHEX_W ;Печать шестнадцатеричного числа в HL3 (0000..FFFF)
LD A,H ;печать старшего байта числа
CALL PHEX_B ;вызов процедуры HEX-печати байта
LD A,L ;печать младшего байта числа
PHEX_B ;Печать шестнадцатеричного числа в A3 (00..FF)
CALL PHEX_HB ;печать по полубайтам
PHEX_HB ;Печать старшего полубайта в A3 (0..F)
RRCA ;меняем
RRCA ; местами
RRCA ; старший
RRCA ; и младший полубайты
PHEX_LB ;Печать младшего полубайта в A3 (0..F)
PUSH AF ;сохранили A на стеке
AND #0F ;отбросили лишние старшие биты
CP #0A ;если A<10,
JR C,GO_PHL ; то перепрыгнули
ADD A,"A"-"9"-1 ;корректировка: после "9" идет "A"
GO_PHL ADD A,"0" ;перевод в ASCII-код
RST #10 ;печать HEX-цифры ("0".."F")
POP AF ;восстановили A со стека
RET ;выход из процедуры
;--- print in BIN system ---
PBIN_W ;Печать двоичного числа в HL (16 разрядов 0/1)
LD A,H ;печать старшего байта числа
CALL PBIN_B ;вызов процедуры BIN-печати байта
LD A,L ;печать младшего байта числа
PBIN_B ;Печать двоичного числа в A (8 разрядов 0/1)
LD B,#08 ;в байте - 8 битов
LP_PBB RLCA ;поочередно 'выдвигаем' биты в CF
PUSH AF ;сохранить A на стеке
LD A,#18 ;в зависимости от флага C:
RLA ; A="0" или A="1"
RST #10 ;печать очередного бита
POP AF ;восстановить A со стека
DJNZ LP_PBB ;поразрядный цикл
RET ;выход из процедуры
;
Listing 1. Процедура печати байта в десятичной системе.
PDEC_B ;Печать десятичного числа в аккумуляторе3 (000..255)
LD HL,DECTB_B ;адрес таблицы степеней десятки
LD B,#03 ;макс. возможное количество цифр
LP_PDB1 LD C,"0"-1 ;инициализация счетчика
LP_PDB2 INC C ;увеличиваем счетчик
SUB (HL) ;вычитаем текущую степень десятки
JR NC,LP_PDB2 ;повторять, пока A>=0
ADD A,(HL) ;A=A mod (HL); C=STR$(A div (HL))
PUSH AF ;сохранить A на стеке
LD A,C ;текущая цифра
RST #10 ;печать цифры
POP AF ;восстановление аккумулятора
INC HL ;переход к след. степени десятки
DJNZ LP_PDB1 ;закрутили цикл по цифрам
RET ;выход из процедуры
DECTB_B DB 100,10,1 ;Таблица степеней3 10-ки для byte
;--------------------------------------------------------------;
; Printing in Different Evaluation Systems LIBrary v2.0 ;
; (c) SerzhSoft, Shadrinsk, august, 1997 ;
;--------------------------------------------------------------;
_NULL EQU 0 ;используется в изменяемых командах
;--------------------------------------------------------------;
; print in DEC system: ;
;--------------------------------------------------------------;
PDEC_3B ;Печать десятичного числа в AHL (00000000..16777215)
LD DE,34464 ;DE=100000-65536
LD BC,#FF01 ;B=-1 - счетчик сотен тысяч; C=1
LP_PD31 INC B ;увеличиваем счетчик сотен тысяч
AND A ;сбpасываем флаг пеpеноса для SUB
SBC HL,DE ;уменьшаем тpехбайтное число AHL
SBC A,C ; на одну сотню тысяч
JR NC,LP_PD31 ;повтоpяем пока нет пеpеполнения
ADD HL,DE ;восстанавливаем положительное
ADC A,C ; значение у AHL; AHL<100000
PUSH AF ;сохpаняем на стеке нужные нам
PUSH HL ; впоследствии pегистpы A, HL
LD A,B ;количество сотен тысяч в числе
CALL PDEC_B ;печатаем это значение
POP HL ;восстанавливаем со стека
POP AF ; pегистpы A, HL
DEC A ;если остаток<65536,
JR NZ,PDEC_W ; то пеpеходим на печать 0..65535
LD DE,5536 ;иначе - число 65536..99999
ADD HL,DE ;пpибавляем остаток от десятков
ADC A,6 ; тысяч, и их сами - отдельно
PUSH HL ;---------------------------------
LD HL,DECTB_W ; Обходимый фpагмент пp-pы PDEC_W
LD B,#05 ;---------------------------------
JR LP_PDW1+1 ;пpыжок чеpез обнуление дес. тысяч
;--------------------------------------------------------------;
PDEC_B ;Печать десятичного числа в A (000..255)
LD L,A ;поместили
LD H,#00 ; число в HL
PUSH HL ;закинули печатаемое число на стек
LD HL,DECTB_W+4 ;адрес в таблице, начиная с сотни
LD B,#03 ;макс. возможное кол-во цифр - три
JR LP_PDW1 ;переход на цикл печати
;--------------------------------------------------------------;
PDEC_W ;Печать десятичного числа в HL (00000..65535)
PUSH HL ;закинули печатаемое число на стек
LD HL,DECTB_W ;адрес таблицы степеней десятки
LD B,#05 ;макс. возможное количество цифр
LP_PDW1 XOR A ;обнулили счетчик и флаг C для SBC
LD E,(HL) ;взяли текущую степень
INC HL ; десятки из таблицы
LD D,(HL) ; и поместили в DE
INC HL ;перешли к след. элементу таблицы
EX (SP),HL ;адрес эл-та <-> печатаемое число
LP_PDW2 INC A ;увеличиваем счетчик
SBC HL,DE ;вычитаем текущую степень десятки
JR NC,LP_PDW2 ;повторяем пока HL>=0
ADD HL,DE ;HL=HL mod DE; A=HL div DE
ADD A,"0"-1 ;перевод A в ASCII-код ("0".."9")
RST #10 ;печать десятичной цифры
EX (SP),HL ;HL=адрес эл-та, число -> на стек
DJNZ LP_PDW1 ;цикл по цифрам
POP HL ;убрали оставшийся ноль со стека
RET ;выход из процедуры
;--------------------------------------------------------------;
DECTB_W DW 10000,1000,100,10,1 ;Таблица степеней десятки ;
;--------------------------------------------------------------;
; print in HEX system: ;
;--------------------------------------------------------------;
PHEX_W ;Печать шестнадцатеричного числа в HL (0000..FFFF)
LD A,H ;печать старшего байта числа
CALL PHEX_B ;вызов процедуры HEX-печати байта
LD A,L ;печать младшего байта числа
;--------------------------------------------------------------;
PHEX_B ;Печать шестнадцатеричного числа в A (00..FF)
CALL PHEX_HB ;печать по полубайтам
;--------------------------------------------------------------;
PHEX_HB ;Печать старшего полубайта в A (0..F)
RRCA ;меняем
RRCA ; местами
RRCA ; старший
RRCA ; и младший полубайты
;--------------------------------------------------------------;
PHEX_LB ;Печать младшего полубайта в A (0..F)
PUSH AF ;сохранили A на стеке
AND #0F ;отбросили лишние старшие биты
CP #0A ;если A<10,
JR C,GO_PHL ; то перепрыгнули
ADD A,"A"-"9"-1 ;корректировка: после "9" идет "A"
GO_PHL ADD A,"0" ;перевод в ASCII-код
RST #10 ;печать HEX-цифры ("0".."F")
POP AF ;восстановили A со стека
RET ;выход из процедуры
;--------------------------------------------------------------;
; print in BIN system: ;
;--------------------------------------------------------------;
PBIN_W ;Печать двоичного числа в HL (16 разрядов 0/1)
LD A,H ;печать старшего байта числа
CALL PBIN_B ;вызов процедуры BIN-печати байта
LD A,L ;печать младшего байта числа
;--------------------------------------------------------------;
PBIN_B ;Печать двоичного числа в A (8 разрядов 0/1)
LD B,#08 ;в байте - 8 битов
LP_PBB RLCA ;поочередно 'выдвигаем' биты в CF
PUSH AF ;сохранить A на стеке
LD A,#18 ;в зависимости от флага C:
RLA ; A="0" или A="1"
RST #10 ;печать очередного бита
POP AF ;восстановить A со стека
DJNZ LP_PBB ;поразрядный цикл
RET ;выход из процедуры
;--------------------------------------------------------------;
; print in USER system: ;
;--------------------------------------------------------------;
PUSE_W ;Печать числа в установленной MKUSETB системе сч-я в HL
PUSH HL ;закинули печатаемое число на стек
HL_PUW LD HL,_NULL ;адрес конца таблицы степеней
LP_PUW1 DEC HL ;загружаем в DE очередную
LD D,(HL) ; степень числа
DEC HL ; из таблицы,
LD E,(HL) ; двигаясь 'сверху-вниз'
EX (SP),HL ;адрес таблицы <-> печат-е число
XOR A ;обнуление A и сброс флага C
LP_PUW2 INC A ;вычисляем очередной
SBC HL,DE ; разряд числа и результат
JR NC,LP_PUW2 ; помещаем в A (A=1+(HL div DE))
ADD HL,DE ;восстанавливаем (+) значение
ADD A,"0"-1 ;переводим A в ASCII-код
CP "9"+1 ;если код меньше "9",
JR C,GO_PUW1 ; то переход на печать
ADD A,"A"-"9"-1 ;коррекция (после "9" идет "A")
GO_PUW1 RST #10 ;печать очередной цифры числа
EX (SP),HL ;HL=адрес таблицы, число -> стек
DEC DE ;если степень
LD A,D ; числа не равна единице
OR E ; (самый первый элемент таблицы),
JR NZ,LP_PUW1 ; то продолжаем работу
POP HL ;убираем со стека ненужный 0
RET ;выход из процедуры
;--------------------------------------------------------------;
MKUSETB ;Создание таблицы степеней с основанием в A
LD HL,USE_TBL ;адрес создаваемой таблицы
LD DE,#0001 ;инициализация счетчика степени
LP_MUT1 LD (HL),E ;запись текущего
INC HL ; значения степени
LD (HL),D ; в таблицу и переход
INC HL ; к ее следующей ячейке
PUSH HL ;сохраняем адрес на стеке
LD B,A ;осн-е степени -> в счетчик цикла
LD HL,#0000 ;обнуление результата
LP_MUT2 ADD HL,DE ;подсчитываем результат
JR C,GO_MUT ;если HL>65535, то прерываем счет
DJNZ LP_MUT2 ;повторяем <основание> раз
EX DE,HL ;результат -> в DE (новая степень)
GO_MUT POP HL ;восстанавливаем адрес таблицы
JR NC,LP_MUT1 ;если не прерывались, то повторяем
LD (HL_PUW+1),HL ;адрес конца таблицы -> в PUSE_W
RET ;выход из процедуры
;--------------------------------------------------------------;
USE_TBL DS 32 ;Таблица степеней текущей системы счисления
;--------------------------------------------------------------;
; print in RIM system: ;
;--------------------------------------------------------------;
PRIM_B ;Печать числа в римской записи в A (I..CCLV)
LD L,A ;скопировать
LD H,#00 ; A в HL
;--------------------------------------------------------------;
PRIM_W ;Печать числа в римской записи в HL (I..MMMCMXCIX)
PUSH HL ;закинули печатаемое число на стек
LD HL,RIM_TBL ;адрес таблицы знаков
DB #DD ;работаем с половинкой регистра IX
LD L,#07 ;LD XL,число знаков в р. счислении
LP_PRW1 LD E,(HL) ;считываем из таблицы значение
INC HL ; очередного знака римской системы
LD D,(HL) ; счисления и помещаем в DE
INC HL ;затем в регистр C считываем
LD C,(HL) ; ASCII-код знака
INC HL ;переход к следующему знаку
LD (HL_PRW+1),HL ;сохранили адрес следующего знака
EX (SP),HL ;адрес -> на стек, HL=печат. число
XOR A ;обнуление счетчика и сброс CF
LP_PRW2 INC A ;в цикле производим
SBC HL,DE ; деление HL на DE (вычитаем,
JR NC,LP_PRW2 ; пока нет переполнения)
ADD HL,DE ;восстанавливаем (+) значение
DEC A ;т.к. A на 1 больше HL div DE,
JR Z,GO_PRW1 ; то если A=1 - ничего не печатаем
LD B,A ;количество печатаемых символов
LP_PRW3 LD A,C ;код знака
RST #10 ;печатаем его
DJNZ LP_PRW3 ;сколько надо - столько и печатаем
GO_PRW1 DB #DD ;работаем с половинкой регистра IX
LD A,L ;LD A,XL - номер текущего знака
DEC A ;если это последний знак (I), то
JR Z,GO_PRW4 ; двухбуквенного сочетания нет
EX (SP),HL ;HL=адрес след. знака, число->стек
RRA ;если номер текущего знака четный,
JR C,GO_PRW2 ; то сочетание с следующим знаком
INC HL ;иначе - перепрыгнуть через один
INC HL ; знак. В результате получим адрес
INC HL ; знака, для получения двойного
GO_PRW2 LD A,C ; знакосочетания (IV,CM,XL...)
LD C,(HL) ;взяли из таблицы значение для
INC HL ; этого знака и получили
LD B,(HL) ; разность между значением
INC HL ; основного и этим значением.
EX DE,HL ; Например, для основного знака X
AND A ; дополнительным будет I, а их
SBC HL,BC ; разность соответственно:
EX DE,HL ; 10-1=9, т.е. IX
LD C,A ;код основного знака
LD A,(HL) ;код дополнительного знака
EX (SP),HL ;HL=печатаемое число
SBC HL,DE ;если оно < дополнительного
JR C,GO_PRW3 ; значения, то не печатаем
RST #10 ;печать дополнительного знака
LD A,C ;основной знак
RST #10 ;печать
JR GO_PRW4 ;результат - двухбукв. сочетание
GO_PRW3 ADD HL,DE ;восстановили (+) у числа
GO_PRW4 EX (SP),HL ;число -> на стек
HL_PRW LD HL,_NULL ;адрес следующего знака в таблице
DB #DD ;работаем с половинкой регистра IX
DEC L ;DEC XL - уменьшаем счетчик знаков
JR NZ,LP_PRW1 ;крутим цикл, пока есть еще знаки
POP HL ;сняли ненужный уже ноль со стека
RET ;выход из процедуры
;--------------------------------------------------------------;
RIM_TBL ;Таблица значимости букв в написании римских чисел
DW 1000
DB "M" ;M=1000 ;CM=900
DW 500
DB "D" ;D=500 ;CD=400
DW 100
DB "C" ;C=100 ;XC=90
DW 50
DB "L" ;L=50 ;XL=40
DW 10
DB "X" ;X=10 ;IX=9
DW 5
DB "V" ;V=5 ;IV=4
DW 1
DB "I" ;I=1 ;-nop-
;--------------------------------------------------------------;
; end of PDES LIB 2.0 ;
;--------------------------------------------------------------;
Эта программа распечатывает двухбайтовое
шестнадцатиричное
число (#0000...#FFFF),
которое задается в регистре HL на входе в
процедуру. Печать выполняется при
помощи RST 16, поэтому перед ее
использованием должен быть открыт
канал печати
(для экрана: LD A,2:CALL #1601)
PRH LD C,#04
PH0 LD B,#04
XOR A
PH1 RL L
RL H
RLA
DJNZ PH1
CP #0A
JR C,PH2
ADD A,#07
PH2 ADD A,#30
RST 16
DEC C
JR NZ,PH0
RET
As this is one of the most typical tasks,
why not to do it tricky way?
The code snippet here takes a byte from (HL) and prints it.
Note that it uses another (shorter) DAA trick as we know that
Half Carry is cleared before DAA.
Input: HL = address of data ; Output: memory dump
Note: You'll have to replace the PRINT_CHAR macro by actual
platform-specific code.
Don't forget to preserve the contents of HL!
xor a
rld
call Nibble
Nibble push af
daa
add a,F0h
adc a,40h
PRINT_CHAR ; prints ASCII character in A
pop af
rld
ret
; Convert single byte in A to two Ascii characters in B and C
TO_HEX LD B,A ; Save the byte original value
AND $0F ; Strip off the high order nybble
CP $0A ; 0-9 at this point?
JR C,TOHX01 ; We only need to add $30 then, or
ADD A,$07 ; Seven more if it's in the A-F range
TOHX01 ADD A,$30 ; Take the value on up to ASCII
LD C,A ; Store the converted lo order character to C
LD A,B ; Get the original value back
RRCA ; Rotate it to the right
RRCA
RRCA
RRCA
AND $0F ; Mask off the upper trash
CP $0A ; 0-9 (< $0A)?
JR C,TOHX02 ; Only add $30 then
ADD A,$07 ; And add it 7 if it's in the A-F range
TOHX02 ADD A,$30 ; Finish the conversion to ASCII
LD B,A ; Load the finished hi order character to B
RET
;Number in hl to decimal ASCII
;Thanks to z80 Bits
;inputs: hl = number to ASCII
;example: hl=300 outputs '00300'
;destroys: af, bc, hl, de used
DispHL ld bc,-10000
call Num1
ld bc,-1000
call Num1
ld bc,-100
call Num1
ld c,-10
call Num1
ld c,-1
Num1: ld a,'0'-1
Num2: inc a
add hl,bc
jr c,Num2
sbc hl,bc
call prt
ret
;adaptable for a DispA routine
or any other 8-bit register
DispA ld h,0
ld l,a
jp DispHL
;Display a 16- or 8-bit number in hex.
DispHLhex:
; Input: HL
ld c,h
call OutHex8
ld c,l
OutHex8:
; Input: C
ld a,c
rra
rra
rra
rra
call Conv
ld a,c
Conv:
and $0F
add a,$90
daa
adc a,$40
daa
call prt
ret
;ASCII to DEC
;HL-адрес числа
;Выход:DE-число
LD HL,TXT
CALL MakeNumber
RET
MakeNumber
LD DE,0
MkNum1 LD A,(HL)
CALL isdigit
RET NC
EX DE,HL
ADD HL,HL
LD B,H
LD C,L
ADD HL,HL
ADD HL,HL
ADD HL,BC
LD B,0
SUB "0"
LD C,A
ADD HL,BC
EX DE,HL
INC HL
JR MkNum1
isdigit CP "0"
CCF
RET NC
CP "9"+1
RET
TXT DB "768QWEIUSO"
Hexadecimal conversion operates directly
on nibbles and takes advantage of nifty DAA trick.
Input:
HL = number to convert,
DE = location of ASCII string
Output:
ASCII string at (DE)
Num2Hex ld a,h
call Num1
ld a,h
call Num2
ld a,l
call Num1
ld a,l
jr Num2
Num1 rra
rra
rra
rra
Num2 or F0h
daa
add a,A0h
adc a,40h
ld (de),a
inc de
ret
NUMBUFF DEFB "00000"
A2DEC8 ld hl,NUMBUFF
ld (hl),#2f
inc (hl)
sub 100
jr nc,$-3
add a,100
inc hl
ld (hl),#2f
inc (hl)
sub 10
jr nc,$-3
add a,10
inc hl
add a,"0"
ld (hl),a
ret
;Write a Hex number in BCHL, CHL, HL or A
;to memory at DE.
sphex8: ld a,b;Big multidecker fall-through...
call sphex2
sphex6: ld a,c
call sphex2
sphex4: ld a,h
call sphex2
ld a,l
sphex2: push af
rrca
rrca
rrca
rrca
call sphex1
pop af
sphex1: and 0fh
cp 0ah
jr c,sph1a
add a,7
sph1a: add a,'0'
ld (de),a
inc de
ret
;Write decimal numbers in HL to memory at DE
cphlbc: push hl
and a
sbc hl,bc
pop hl
ret
spdec: ld bc,10000
call cphlbc
jr nc,spdec5
ld bc,1000
call cphlbc
jr nc,spdec4
ld bc,100
call cphlbc
jr nc,spdec3
ld a,l
cp 10
jr nc,spdec2 ;>=10
jr spdec1
spdec5: ld bc,10000
call dodec
spdec4: ld bc,1000
call dodec
spdec3: ld bc,100
call dodec
spdec2: ld bc,10
call dodec
spdec1: ld a,l
add a,'0'
ld (de),a
inc de
ret
dodec: ld a,'0'
ddlp: inc a
and a
sbc hl,bc
jr nc,ddlp
dec a
add hl,bc
ld (de),a
inc de
ret
;
;by GRAND, 1999
;Печ.числа (0...65535)
;с незн нулями
;Вх.: HL - число.
PR16D XOR A
LD DE,10000
SBC HL,DE
INC A
JR NC,$-3
ADD A,"0"-1
CALL PRINTA
ADD HL,DE
XOR A
LD DE,1000
SBC HL,DE
INC A
JR NC,$-3
ADD A,"0"-1
CALL PRINTA
ADD HL,DE
;Печ.числа (0...255) с незн нулями
;Вх.: HL - число.
PR8D XOR A
LD DE,100
SBC HL,DE
INC A
JR NC,$-3
ADD A,"0"-1
CALL PRINTA
ADD HL,DE
;Печ.числа (0...99) с незн нулями
;Вх.: HL - число.
LD A,L
LD B,"0"-1
INC B
SUB 10
JR NC,$-3
ADD A,"0"+10
LD C,A
LD A,B
CALL PRINTA
LD A,C
PRINTA.....print proc here
; Routine for converting a 24-bit binary number to decimal
; In: E:HL = 24-bit binary number (0-16777215)
; Out: DE:HL = 8 digit decimal form (packed BCD)
; Changes: AF, BC, DE, HL & IX
; by Alwin Henseler
LD C,E
PUSH HL
POP IX ; input value in C:IX
LD HL,1
LD D,H
LD E,H ; start value corresponding with 1st 1-bit
LD B,24 ; bitnr. being processed + 1
FIND1: ADD IX,IX
RL C ; shift bit 23-0 from C:IX into carry
JR C,NEXTBIT
DJNZ FIND1 ; find highest 1-bit
; All bits 0:
RES 0,L ; least significant bit not 1 after all ..
RET
DBLLOOP: LD A,L
ADD A,A
DAA
LD L,A
LD A,H
ADC A,A
DAA
LD H,A
LD A,E
ADC A,A
DAA
LD E,A
LD A,D
ADC A,A
DAA
LD D,A ; double the value found so far
ADD IX,IX
RL C ; shift next bit from C:IX into carry
JR NC,NEXTBIT ; bit = 0 -> don't add 1 to the number
SET 0,L ; bit = 1 -> add 1 to the number
NEXTBIT: DJNZ DBLLOOP
RET
ПРЕОБРАЗОВАНИЕ ДВОИЧНОГО ЧИСЛА В ДВОИЧНО-ДЕСЯТИЧНОЕ.
Для отображения результатов вычислений необходимо числа
привести в удобный для вывода вид, т.е. преобразовать число в
десятичный код. Например, двоичное число 00001111 (#0F) удобно
представить в виде 0001 0101(#15), т.е. преобразовать его в
двоично-десятичную форму. Этим занимается следующая подпрограмма.
; BCD2B- подпрограмма перевода 2-х байтного числа.
; Двоичное число должно быть в регистре 'HL'.
; Результат :
; в 'A' - десятки тысяч;
; в 'B' - тысячи и сотни;
; в 'C' - десятки и единицы.
; Во время работы вызывается процедура CONV из
; подпрограммы BCD1B - перевод однобайтного числа,
; в которой;
; в 'H' - число для перевода , 'L'=0
; Результат работы: в 'A' - разряды сотен;
; в 'B' - десятки и единицы
; Вход в процедуру преобразования 2-х байтного числа:
BCD2B LD E,17 ; счетчик 1-го цикла
CALL CONV ; вычислить младший BCD байт
LD C,A ; сохранить в 'C'
LD E,17 ; счетчик 2-го цикла
JR PRODOL ; переход на вычисление
; процедура для преобразования 1-байтного числа
BCD1B LD E,9 ; цикл для 1-байтного числа
PRODOL CALL CONV ; вычислить два старших байта
LD B,A ; сохранить средний байт
LD A,L ; старший байт
RET ; выйти вообще
;-----
CONV XOR A ; очистить
SBIT DEC E ; уменьшим счетчик цикла
RET Z ; цикл весь - выйти
ADD HL,HL ; сдвинем старшие разряды в перенос
ADC A,A
DAA ; скорректируем
JR NC,SBIT ; результат больше 99 ?
INC HL ; да - увеличим на 1
JR SBIT ; вернемся в вычисления
from PENTIS by Tony Raugas print DEC
print_digits exx
ld hl, txt_00000
ld b, 5
prd1 ld (hl), #30
inc hl
djnz prd1
exx
ld b, #10
prd2 exx
ld hl, txt_00000+4
ld bc, #500
prd3 ld a, (hl)
add a, a
add a, c
sub #30
cp #3A
jr c, prd4
sub #A
ld c, 1
jr prd5
prd4 ld c, 0
prd5 ld (hl), a
dec hl
djnz prd3
exx
add hl, hl
exx
jr nc, prd7
ld hl, txt_00000+4
ld b, 5
prd6 ld a, (hl)
inc a
ld (hl), a
cp #3A
jr c, prd7
ld (hl), #30
dec hl
djnz prd6
prd7 exx
djnz prd2
ld hl, txt_00000
jp prtpz
txt_00000 db "00000",0
Печать десятичных чисел без нуля. 59 байт. print DEC w/o zeros
LD IX,Буфер 5 байт
LD HL,Число
LD BC,0
LD DE,0-10000
CALL num
LD DE,0-1000
CALL num
LD DE,0-100
CALL num
LD DE,0-10
CALL num
LD A,L
JR num2
num ADD HL,DE
INC B
JR C,$-2
DEC B
SBC HL,DE
LD A,B
OR C
RET Z
LD C,A
LD A,B
num2 ADD A,48
LD (IX),A
INC IX
LD B,0
RET
CISLO ld de,-10000 ;rad desetitisicu
call CIFRA
ld de,-1000 ;rad tisicu
call CIFRA
ld de,-100 ;rad stovek
call CIFRA
ld de,-10 ;rad desitek
call CIFRA
ld de,-1 ;rad jednicek
CIFRA ld a,'0'-1 ;do A znak 0 zmenseny o 1
CIFRA1 add hl,de ;pricti zaporny rad
inc a ;zvys pocitadlo
jr c,CIFRA1 ;jeste jsme jej neprekrocili
sbc hl,de ;pokud ano, oprav cislo
; call ZNAK ;a vytiskni pocitadlo
ret
NUM2DEC: ld e,' '
ld bc,-10000
call NUM1
ld bc,-1000
call NUM1
ld bc,-100
call NUM1
ld c,-10
call NUM1
ld e,'0'
ld c,b
NUM1: ld a,'0'-1
NUM2: inc a
add hl,bc
jr c,NUM2
sbc hl,bc
cp '0'
jr nz,NUM4
ld a,e
NUM3: jp ZNAK2
NUM4: ld e,'0'
jr NUM3
Input: HL = number to convert, DE = location of ASCII string
Output: ASCII string at (DE) ; Num2Dec ld bc,-10000
call Num1
ld bc,-1000
call Num1
ld bc,-100
call Num1
ld c,-10
call Num1
ld c,b
Num1 ld a,'0'-1
Num2 inc a
add hl,bc
jr c,Num2
sbc hl,bc
ld (de),a
inc de
ret
;in: HL
DECWORD LD IY,DECWTAB
XOR A
LD B,4
DECW3 LD E,(IY)
INC IY
LD D,(IY)
INC IY
DECW2 OR A
SBC HL,DE
JR C,DECW1
INC A
JR DECW2
DECW1 ADD HL,DE
CALL DECWPC
DJNZ DECW3
LD A,L
OR A,#30
JR PRCHAR
DECWPC OR A
RET Z
PUSH HL
PUSH BC
OR #30
CALL PRCHAR
LD A,#30
POP BC
POP HL
RET
DECWTAB DEFW 10000,1000,100,10
;in: HL
HEXWORD PUSH HL
LD A,H
CALL HEXBYTE
POP HL
LD A,L
;in: A
HEXBYTE PUSH AF
RRCA
RRCA
RRCA
RRCA
CALL HEXHALF
POP AF
HEXHALF AND #0F
CP #0A
JR C,HEXH1
ADD A,#07
HEXH1 ADD A,#30
; CALL PRCHAR
; Convert HEX ASCII characters in B C registers to a byte value in A
HEX2BIN LD A,B ; Move the hi order byte to A
SUB $30 ; Take it down from Ascii
CP $0A ; Are we in the 0-9 range here?
JR C,HX2B01 ; If so, get the next nybble
SUB $07 ; But if A-F, take it down some more
HX2B01 RLCA ; Rotate the nybble from low to high
RLCA ; One bit at a time
RLCA ; Until we
RLCA ; Get there with it
LD B,A ; Save the converted high nybble
LD A,C ; Now get the low order byte
SUB $30 ; Convert it down from Ascii
CP $0A ; 0-9 at this point?
JR C,HX2B02 ; Good enough then, but
SUB $07 ; Take off 7 more if it's A-F
HX2B02 ADD A,B ; Add in the high order nybble
RET
; Convert single byte in A to two Ascii characters in B and C
TO_HEX LD B,A ; Save the byte original value
AND $0F ; Strip off the high order nybble
CP $0A ; 0-9 at this point?
JR C,TOHX01 ; We only need to add $30 then, or
ADD A,$07 ; Seven more if it's in the A-F range
TOHX01 ADD A,$30 ; Take the value on up to ASCII
LD C,A ; Store the converted lo order character to C
LD A,B ; Get the original value back
RRCA ; Rotate it to the right
RRCA
RRCA
RRCA
AND $0F ; Mask off the upper trash
CP $0A ; 0-9 (< $0A)?
JR C,TOHX02 ; Only add $30 then
ADD A,$07 ; And add it 7 if it's in the A-F range
TOHX02 ADD A,$30 ; Finish the conversion to ASCII
LD B,A ; Load the finished hi order character to B
RET ;
;CLCN32 -> Converts 32Bit-Value in ASCII-String (terminated by 0)
;Input DE,IX=32bit value, IY=destination address
;Output IY=last char in destination string
;Destroyed AF,BC,DE,HL,IX
clcn32t dw 1,0, 10,0, 100,0, 1000,0, 10000,0
dw #86a0,1, #4240,#f, #9680,#98, #e100,#5f5, #ca00,#3b9a
clcn32z ds 4
clcn32 ld (clcn32z),ix
ld (clcn32z+2),de
ld ix,clcn32t+36
ld b,9
ld c,0
clcn321 ld a,"0"
or a
clcn322 ld e,(ix+0):ld d,(ix+1):ld hl,(clcn32z): sbc hl,de:ld (clcn32z),hl
ld e,(ix+2):ld d,(ix+3):ld hl,(clcn32z+2):sbc hl,de:ld (clcn32z+2),hl
jr c,clcn325
inc c
inc a
jr clcn322
clcn325 ld e,(ix+0):ld d,(ix+1):ld hl,(clcn32z): add hl,de:ld (clcn32z),hl
ld e,(ix+2):ld d,(ix+3):ld hl,(clcn32z+2):adc hl,de:ld (clcn32z+2),hl
ld de,-4
add ix,de
inc c
dec c
jr z,clcn323
ld (iy+0),a
inc iy
clcn323 djnz clcn321
ld a,(clcn32z)
add "0"
ld (iy+0),a
ld (iy+1),0
ret
; Combined routine for conversion of different sized binary numbers into
; directly printable ASCII(Z)-string
; Input value in registers, number size and -related to that- registers to fill
; is selected by calling the correct entry:
; entry inputregister(s) decimal value 0 to:
; B2D8 A 255 (3 digits)
; B2D16 HL 65535 5 "
; B2D24 E:HL 16777215 8 "
; B2D32 DE:HL 4294967295 10 "
; B2D48 BC:DE:HL 281474976710655 15 "
; B2D64 IX:BC:DE:HL 18446744073709551615 20 "
; The resulting string is placed into a small buffer attached to this routine,
; this buffer needs no initialization and can be modified as desired.
; The number is aligned to the right, and leading 0's are replaced with spaces.
; On exit HL points to the first digit, (B)C = number of decimals
; This way any re-alignment / postprocessing is made easy.
; Changes: AF,BC,DE,HL,IX
; P.S. some examples below
; by Alwin Henseler
B2D8: LD H,0
LD L,A
B2D16: LD E,0
B2D24: LD D,0
B2D32: LD BC,0
B2D48: LD IX,0 ; zero all non-used bits
B2D64: LD (B2DINV),HL
LD (B2DINV+2),DE
LD (B2DINV+4),BC
LD (B2DINV+6),IX ; place full 64-bit input value in buffer
LD HL,B2DBUF
LD DE,B2DBUF+1
LD (HL)," "
B2DFILC: EQU $-1 ; address of fill-character
LD BC,18
LDIR ; fill 1st 19 bytes of buffer with spaces
LD (B2DEND-1),BC ;set BCD value to "0" & place terminating 0
LD E,1 ; no. of bytes in BCD value
LD HL,B2DINV+8 ; (address MSB input)+1
LD BC,#0909
XOR A
B2DSKP0: DEC B
JR Z,B2DSIZ ; all 0: continue with postprocessing
DEC HL
OR (HL) ; find first byte <>0
JR Z,B2DSKP0
B2DFND1: DEC C
RLA
JR NC,B2DFND1 ; determine no. of most significant 1-bit
RRA
LD D,A ; byte from binary input value
B2DLUS2: PUSH HL
PUSH BC
B2DLUS1: LD HL,B2DEND-1 ; address LSB of BCD value
LD B,E ; current length of BCD value in bytes
RL D ; highest bit from input value -> carry
B2DLUS0: LD A,(HL)
ADC A,A
DAA
LD (HL),A ; double 1 BCD byte from intermediate result
DEC HL
DJNZ B2DLUS0 ; and go on to double entire BCD value (+carry!)
JR NC,B2DNXT
INC E ; carry at MSB -> BCD value grew 1 byte larger
LD (HL),1 ; initialize new MSB of BCD value
B2DNXT: DEC C
JR NZ,B2DLUS1 ; repeat for remaining bits from 1 input byte
POP BC ; no. of remaining bytes in input value
LD C,8 ; reset bit-counter
POP HL ; pointer to byte from input value
DEC HL
LD D,(HL) ; get next group of 8 bits
DJNZ B2DLUS2 ; and repeat until last byte from input value
B2DSIZ: LD HL,B2DEND ; address of terminating 0
LD C,E ; size of BCD value in bytes
OR A
SBC HL,BC ; calculate address of MSB BCD
LD D,H
LD E,L
SBC HL,BC
EX DE,HL ; HL=address BCD value, DE=start of decimal value
LD B,C ; no. of bytes BCD
SLA C ; no. of bytes decimal (possibly 1 too high)
LD A,"0"
RLD ; shift bits 4-7 of (HL) into bit 0-3 of A
CP "0" ; (HL) was > 9h?
JR NZ,B2DEXPH ; if yes, start with recording high digit
DEC C ; correct number of decimals
INC DE ; correct start address
JR B2DEXPL ; continue with converting low digit
B2DEXP: RLD ; shift high digit (HL) into low digit of A
B2DEXPH: LD (DE),A ; record resulting ASCII-code
INC DE
B2DEXPL: RLD
LD (DE),A
INC DE
INC HL ; next BCD-byte
DJNZ B2DEXP ; and go on to convert each BCD-byte into 2 ASCII
SBC HL,BC ; return with HL pointing to 1st decimal
RET
B2DINV: DS 8 ; space for 64-bit input value (LSB first)
B2DBUF: DS 20 ; space for 20 decimal digits
B2DEND: DS 1 ; space for terminating 0
END
EXAMPLES
(In these examples, it is assumed there exists a subroutine PRINT, that
prints a string (terminated by a 0-byte) starting at address [HL] )
Print 1 byte, as follows:
20
7
145 etc.
by: LD A,byte
CALL B2D8
LD HL,B2DEND-3
CALL PRINT
Print a 24-bit value, as follows:
9345
76856366
534331
by: LD E,bit23-16
LD HL,bit15-0
CALL B2D24
CALL PRINT
Print a 48-bit value, like
14984366484
49
123456789012345
3155556 etc.
by:
LD BC,bit47-32
LD DE,bi