@nataliapc/mcp-openmsx 1.1.15 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -37
- package/dist/server.js +101 -40
- package/dist/utils.js +42 -2
- package/dist/vectordb.js +61 -0
- package/package.json +8 -3
- package/resources/audio/msx-midi.md +872 -0
- package/resources/audio/psg_registers.md +281 -0
- package/resources/audio/sound_cartridge_scc.md +123 -0
- package/resources/audio/sound_cartridge_scci.md +250 -0
- package/resources/audio/toc.json +8 -4
- package/resources/book--msx2-technical-handbook/toc.json +1 -1
- package/resources/msx-dos/MSX-DOS_2_Environment_Variables.md +1368 -0
- package/resources/msx-dos/MSX-DOS_File_extensions.md +154 -0
- package/resources/msx-dos/toc.json +13 -0
- package/resources/msx-unapi/toc.json +2 -2
- package/resources/others/keyboard_matrices.md +243 -0
- package/resources/others/toc.json +6 -0
- package/resources/programming/asm_callbios.md +79 -0
- package/resources/programming/asm_docopy.md +115 -0
- package/resources/programming/asm_fast_loops.md +200 -0
- package/resources/programming/asm_getslot.md +143 -0
- package/resources/programming/asm_interrupts.md +202 -0
- package/resources/programming/asm_load_screen.md +240 -0
- package/resources/programming/asm_mult_div_shifts.md +487 -0
- package/resources/programming/asm_raminpage1.md +56 -0
- package/resources/programming/asm_vdp_detection.md +78 -0
- package/resources/programming/asm_vdp_routines.md +343 -0
- package/resources/programming/asm_z80_routines_collection.md +810 -0
- package/resources/programming/basic_wiki/ABS().md +36 -0
- package/resources/programming/basic_wiki/AND.md +71 -0
- package/resources/programming/basic_wiki/ASC().md +38 -0
- package/resources/programming/basic_wiki/ATN().md +36 -0
- package/resources/programming/basic_wiki/AUTO.md +39 -0
- package/resources/programming/basic_wiki/BASE().md +147 -0
- package/resources/programming/basic_wiki/BEEP.md +27 -0
- package/resources/programming/basic_wiki/BIN$().md +36 -0
- package/resources/programming/basic_wiki/BLOAD.md +63 -0
- package/resources/programming/basic_wiki/BSAVE.md +61 -0
- package/resources/programming/basic_wiki/CALL.md +391 -0
- package/resources/programming/basic_wiki/CALL_ADJUST.md +40 -0
- package/resources/programming/basic_wiki/CALL_IMPOSE.md +28 -0
- package/resources/programming/basic_wiki/CALL_OPTIONS.md +26 -0
- package/resources/programming/basic_wiki/CALL_PAUSE.md +119 -0
- package/resources/programming/basic_wiki/CALL_PCMPLAY.md +60 -0
- package/resources/programming/basic_wiki/CALL_PCMREC.md +70 -0
- package/resources/programming/basic_wiki/CDBL().md +36 -0
- package/resources/programming/basic_wiki/CHR$().md +51 -0
- package/resources/programming/basic_wiki/CINT().md +36 -0
- package/resources/programming/basic_wiki/CIRCLE.md +51 -0
- package/resources/programming/basic_wiki/CLEAR.md +39 -0
- package/resources/programming/basic_wiki/CLOAD.md +27 -0
- package/resources/programming/basic_wiki/CLOAD?.md +31 -0
- package/resources/programming/basic_wiki/CLOSE.md +44 -0
- package/resources/programming/basic_wiki/CLS.md +51 -0
- package/resources/programming/basic_wiki/COLOR.md +143 -0
- package/resources/programming/basic_wiki/COLOR=.md +93 -0
- package/resources/programming/basic_wiki/COLOR_SPRITE$().md +83 -0
- package/resources/programming/basic_wiki/COLOR_SPRITE().md +85 -0
- package/resources/programming/basic_wiki/CONT.md +23 -0
- package/resources/programming/basic_wiki/COPY.md +215 -0
- package/resources/programming/basic_wiki/COPY_SCREEN.md +61 -0
- package/resources/programming/basic_wiki/COS().md +37 -0
- package/resources/programming/basic_wiki/CSAVE.md +35 -0
- package/resources/programming/basic_wiki/CSNG().md +36 -0
- package/resources/programming/basic_wiki/CSRLIN.md +33 -0
- package/resources/programming/basic_wiki/DATA.md +47 -0
- package/resources/programming/basic_wiki/DEFDBL.md +40 -0
- package/resources/programming/basic_wiki/DEFINT.md +40 -0
- package/resources/programming/basic_wiki/DEFSNG.md +40 -0
- package/resources/programming/basic_wiki/DEFSTR.md +40 -0
- package/resources/programming/basic_wiki/DEF_FN.md +49 -0
- package/resources/programming/basic_wiki/DEF_USR.md +33 -0
- package/resources/programming/basic_wiki/DELETE.md +49 -0
- package/resources/programming/basic_wiki/DIM.md +59 -0
- package/resources/programming/basic_wiki/DRAW.md +77 -0
- package/resources/programming/basic_wiki/ELSE.md +45 -0
- package/resources/programming/basic_wiki/END.md +32 -0
- package/resources/programming/basic_wiki/EOF().md +36 -0
- package/resources/programming/basic_wiki/EQV.md +76 -0
- package/resources/programming/basic_wiki/ERASE.md +35 -0
- package/resources/programming/basic_wiki/ERL.md +34 -0
- package/resources/programming/basic_wiki/ERR.md +143 -0
- package/resources/programming/basic_wiki/ERROR.md +145 -0
- package/resources/programming/basic_wiki/EXP().md +38 -0
- package/resources/programming/basic_wiki/FIELD.md +48 -0
- package/resources/programming/basic_wiki/FIX().md +44 -0
- package/resources/programming/basic_wiki/FN.md +61 -0
- package/resources/programming/basic_wiki/FOR...NEXT.md +80 -0
- package/resources/programming/basic_wiki/FRE().md +66 -0
- package/resources/programming/basic_wiki/GET_DATE.md +60 -0
- package/resources/programming/basic_wiki/GET_TIME.md +34 -0
- package/resources/programming/basic_wiki/GOSUB.md +41 -0
- package/resources/programming/basic_wiki/GOTO.md +41 -0
- package/resources/programming/basic_wiki/HEX$().md +36 -0
- package/resources/programming/basic_wiki/IF...GOTO...ELSE.md +55 -0
- package/resources/programming/basic_wiki/IF...THEN...ELSE.md +50 -0
- package/resources/programming/basic_wiki/IMP.md +83 -0
- package/resources/programming/basic_wiki/INKEY$.md +65 -0
- package/resources/programming/basic_wiki/INP().md +33 -0
- package/resources/programming/basic_wiki/INPUT$().md +51 -0
- package/resources/programming/basic_wiki/INPUT.md +93 -0
- package/resources/programming/basic_wiki/INSTR().md +44 -0
- package/resources/programming/basic_wiki/INT().md +44 -0
- package/resources/programming/basic_wiki/INTERVAL.md +57 -0
- package/resources/programming/basic_wiki/KEY().md +51 -0
- package/resources/programming/basic_wiki/KEY.md +254 -0
- package/resources/programming/basic_wiki/LEFT$().md +39 -0
- package/resources/programming/basic_wiki/LEN().md +36 -0
- package/resources/programming/basic_wiki/LET.md +68 -0
- package/resources/programming/basic_wiki/LINE.md +74 -0
- package/resources/programming/basic_wiki/LINE_INPUT.md +79 -0
- package/resources/programming/basic_wiki/LIST.md +58 -0
- package/resources/programming/basic_wiki/LLIST.md +43 -0
- package/resources/programming/basic_wiki/LOAD.md +56 -0
- package/resources/programming/basic_wiki/LOCATE.md +67 -0
- package/resources/programming/basic_wiki/LOG().md +36 -0
- package/resources/programming/basic_wiki/LPOS().md +31 -0
- package/resources/programming/basic_wiki/LPRINT.md +46 -0
- package/resources/programming/basic_wiki/MAXFILES.md +39 -0
- package/resources/programming/basic_wiki/MERGE.md +54 -0
- package/resources/programming/basic_wiki/MID$().md +72 -0
- package/resources/programming/basic_wiki/MOD.md +39 -0
- package/resources/programming/basic_wiki/MOTOR.md +46 -0
- package/resources/programming/basic_wiki/NEW.md +27 -0
- package/resources/programming/basic_wiki/NOT.md +61 -0
- package/resources/programming/basic_wiki/OCT$().md +36 -0
- package/resources/programming/basic_wiki/ON...GOSUB.md +45 -0
- package/resources/programming/basic_wiki/ON...GOTO.md +42 -0
- package/resources/programming/basic_wiki/ON_ERROR_GOTO.md +61 -0
- package/resources/programming/basic_wiki/ON_INTERVAL_GOSUB.md +54 -0
- package/resources/programming/basic_wiki/ON_KEY_GOSUB.md +56 -0
- package/resources/programming/basic_wiki/ON_SPRITE_GOSUB.md +41 -0
- package/resources/programming/basic_wiki/ON_STOP_GOSUB.md +56 -0
- package/resources/programming/basic_wiki/ON_STRIG_GOSUB.md +70 -0
- package/resources/programming/basic_wiki/OPEN.md +103 -0
- package/resources/programming/basic_wiki/OR.md +75 -0
- package/resources/programming/basic_wiki/OUT.md +35 -0
- package/resources/programming/basic_wiki/PAD().md +110 -0
- package/resources/programming/basic_wiki/PAINT.md +66 -0
- package/resources/programming/basic_wiki/PDL().md +53 -0
- package/resources/programming/basic_wiki/PEEK().md +44 -0
- package/resources/programming/basic_wiki/PLAY().md +58 -0
- package/resources/programming/basic_wiki/PLAY.md +196 -0
- package/resources/programming/basic_wiki/POINT.md +52 -0
- package/resources/programming/basic_wiki/POKE.md +51 -0
- package/resources/programming/basic_wiki/POS().md +36 -0
- package/resources/programming/basic_wiki/PRESET.md +61 -0
- package/resources/programming/basic_wiki/PRINT.md +179 -0
- package/resources/programming/basic_wiki/PSET.md +82 -0
- package/resources/programming/basic_wiki/PUT_KANJI.md +93 -0
- package/resources/programming/basic_wiki/PUT_SPRITE.md +143 -0
- package/resources/programming/basic_wiki/READ.md +45 -0
- package/resources/programming/basic_wiki/REM.md +42 -0
- package/resources/programming/basic_wiki/RENUM.md +78 -0
- package/resources/programming/basic_wiki/RESTORE.md +52 -0
- package/resources/programming/basic_wiki/RESUME.md +45 -0
- package/resources/programming/basic_wiki/RETURN.md +47 -0
- package/resources/programming/basic_wiki/RIGHT$().md +39 -0
- package/resources/programming/basic_wiki/RND().md +51 -0
- package/resources/programming/basic_wiki/RUN.md +56 -0
- package/resources/programming/basic_wiki/SAVE.md +65 -0
- package/resources/programming/basic_wiki/SCREEN.md +164 -0
- package/resources/programming/basic_wiki/SET_ADJUST.md +66 -0
- package/resources/programming/basic_wiki/SET_BEEP.md +76 -0
- package/resources/programming/basic_wiki/SET_DATE.md +103 -0
- package/resources/programming/basic_wiki/SET_PAGE.md +52 -0
- package/resources/programming/basic_wiki/SET_PASSWORD.md +75 -0
- package/resources/programming/basic_wiki/SET_PROMPT.md +61 -0
- package/resources/programming/basic_wiki/SET_SCREEN.md +100 -0
- package/resources/programming/basic_wiki/SET_SCROLL.md +55 -0
- package/resources/programming/basic_wiki/SET_TIME.md +83 -0
- package/resources/programming/basic_wiki/SET_TITLE.md +87 -0
- package/resources/programming/basic_wiki/SET_VIDEO.md +49 -0
- package/resources/programming/basic_wiki/SGN().md +38 -0
- package/resources/programming/basic_wiki/SIN().md +36 -0
- package/resources/programming/basic_wiki/SOUND.md +188 -0
- package/resources/programming/basic_wiki/SPACE$().md +38 -0
- package/resources/programming/basic_wiki/SPC().md +34 -0
- package/resources/programming/basic_wiki/SPRITE$().md +50 -0
- package/resources/programming/basic_wiki/SPRITE.md +31 -0
- package/resources/programming/basic_wiki/SQR().md +32 -0
- package/resources/programming/basic_wiki/STICK().md +70 -0
- package/resources/programming/basic_wiki/STOP.md +70 -0
- package/resources/programming/basic_wiki/STR$().md +37 -0
- package/resources/programming/basic_wiki/STRIG().md +82 -0
- package/resources/programming/basic_wiki/STRING$().md +42 -0
- package/resources/programming/basic_wiki/SWAP.md +62 -0
- package/resources/programming/basic_wiki/TAB().md +38 -0
- package/resources/programming/basic_wiki/TAN().md +36 -0
- package/resources/programming/basic_wiki/TIME.md +59 -0
- package/resources/programming/basic_wiki/TROFF.md +21 -0
- package/resources/programming/basic_wiki/TRON.md +39 -0
- package/resources/programming/basic_wiki/USR().md +66 -0
- package/resources/programming/basic_wiki/VAL().md +36 -0
- package/resources/programming/basic_wiki/VARPTR().md +50 -0
- package/resources/programming/basic_wiki/VDP().md +103 -0
- package/resources/programming/basic_wiki/VPEEK().md +46 -0
- package/resources/programming/basic_wiki/VPOKE.md +48 -0
- package/resources/programming/basic_wiki/WAIT.md +38 -0
- package/resources/programming/basic_wiki/WIDTH.md +76 -0
- package/resources/programming/basic_wiki/XOR.md +72 -0
- package/resources/programming/basic_wiki/_toc.json +871 -0
- package/resources/programming/dos_error_handling.md +85 -0
- package/resources/programming/toc.json +51 -36
- package/resources/programming/vdp_commands_speed.md +147 -0
- package/resources/programming/vdp_programming_faq.md +55 -0
- package/resources/programming/vdp_programming_tutorial.md +390 -0
- package/resources/programming/vdp_screensplit_programming_guide.md +166 -0
- package/resources/programming/vdp_scrolling_on_msx.md +124 -0
- package/resources/programming/vdp_the_yjk_screen_modes.md +227 -0
- package/resources/programming/vdp_v9938_vram_timings.md +539 -0
- package/resources/programming/vdp_v9938_vram_timings_part_2.md +281 -0
- package/resources/sdcc/toc.json +1 -1
- package/vector-db/index.json +1 -0
- /package/resources/msx-unapi/{Ethernet_UNAPI_specification_1.1.md → Ethernet_UNAPI_specification_1_1.md} +0 -0
- /package/resources/msx-unapi/{MSX_UNAPI_specification_1.1.md → MSX_UNAPI_specification_1_1.md} +0 -0
|
@@ -0,0 +1,810 @@
|
|
|
1
|
+
# Z80 Bits
|
|
2
|
+
|
|
3
|
+
Milos "baze" Bazelides, baze_at_baze_au_com
|
|
4
|
+
last updated 29.03.2006
|
|
5
|
+
|
|
6
|
+
I decided to create this collection of Z80 routines for one simple reason - I like magic. And of course, I was fed up with bad code one can find in many embedded devices, web pages, tutorials and such. The routines presented here is what I believe to be the best of its kind or at least very close to it.
|
|
7
|
+
|
|
8
|
+
However, if you find a bug or optimisation please let me know. My objective is to know and share the best stuff out there. Also, if you feel you've got something that should be posted here drop me a mail. Keep in mind though that any code you submit should be machine independent and of general use. Of course, you'll be guaranteed a honourable mention in the Credits :)
|
|
9
|
+
|
|
10
|
+
Please don't complain about lack of comments. This is not a coding tutorial but rather a collection of tricks for (more or less) experienced coders. I'm sure it's not that hard to figure out what's going on.
|
|
11
|
+
|
|
12
|
+
_Note: This document is by far not finished yet. I'll continue to add new code in near future. I also think of providing binary images of look-up tables in cases where table generator is not trivial. Also, I'd be glad if some native English speaker would help me to correct numerous grammar and spelling mistakes :)_
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- 1 [Integer Multiplication](#integer-multiplication)
|
|
17
|
+
- 1.1 Restoring 8-bit * 8-bit Unsigned
|
|
18
|
+
- 1.2 Restoring 16-bit * 8-bit Unsigned
|
|
19
|
+
- 1.3 Restoring 16-bit * 16-bit Unsigned
|
|
20
|
+
- 1.4 Square Table Driven 8-bit * 8-bit Signed
|
|
21
|
+
- 1.5 Square Table Driven 6-bit * 6-bit Signed
|
|
22
|
+
- 1.6 Logarithmic Table Driven 8-bit * 8-bit Signed](#
|
|
23
|
+
- [2 Integer Division](#integer-division)
|
|
24
|
+
- 2.1 Restoring 8-bit / 8-bit Unsigned
|
|
25
|
+
- 2.2 Restoring 16-bit / 8-bit Unsigned
|
|
26
|
+
- 2.3 Restoring 16-bit / 16-bit Unsigned
|
|
27
|
+
- 2.4 Restoring 24-bit / 8-bit Unsigned
|
|
28
|
+
- 2.5 Restoring 24-bit / 16-bit Unsigned
|
|
29
|
+
- 2.6 Restoring 32-bit / 8-bit Unsigned
|
|
30
|
+
- 3 [Integer Square Root](#integer-square-root)
|
|
31
|
+
- 3.1 Basic 8-bit Square Root
|
|
32
|
+
- 3.2 Basic 16-bit Square Root
|
|
33
|
+
- 3.3 Restoring 16-bit Square Root
|
|
34
|
+
- 3.4 16-bit Square Table Bisection
|
|
35
|
+
- 4 [Random Number Generators](#random-number-generators)
|
|
36
|
+
- 4.1 8-bit Random Number Generator
|
|
37
|
+
- 4.2 16-bit Random Number Generator
|
|
38
|
+
- 5 [Conversions Between Numbers and Strings](#conversions-between-numbers-and-strings)
|
|
39
|
+
- 5.1 16-bit Integer to ASCII (decimal)
|
|
40
|
+
- 5.2 16-bit Integer to ASCII (hexadecimal)
|
|
41
|
+
- 5.3 Memory dump (hexadecimal)
|
|
42
|
+
- 6 [Cyclic Redundancy Checks (CRC)](#cyclic-redundancy-checks-crc)
|
|
43
|
+
- 6.1 16-bit CRC-CCITT
|
|
44
|
+
- 6.2 Table Driven 16-bit CRC-CCITT
|
|
45
|
+
- 6.3 32-bit CRC-32
|
|
46
|
+
- 6.4 Table Driven 32-bit CRC-32
|
|
47
|
+
- [Credits](#credits)
|
|
48
|
+
|
|
49
|
+
## Integer Multiplication
|
|
50
|
+
|
|
51
|
+
### Restoring 8-bit * 8-bit Unsigned
|
|
52
|
+
|
|
53
|
+
Input: H = Multiplier, E = Multiplicand, L = 0, D = 0
|
|
54
|
+
Output: HL = Product
|
|
55
|
+
```assembly
|
|
56
|
+
sla h ; optimised 1st iteration
|
|
57
|
+
jr nc,$+3
|
|
58
|
+
ld l,e
|
|
59
|
+
|
|
60
|
+
add hl,hl ; unroll 7 times
|
|
61
|
+
jr nc,$+3 ; ...
|
|
62
|
+
add hl,de ; ...
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Restoring 16-bit * 8-bit Unsigned
|
|
66
|
+
|
|
67
|
+
Input: A = Multiplier, DE = Multiplicand, HL = 0, C = 0
|
|
68
|
+
Output: A:HL = Product
|
|
69
|
+
```assembly
|
|
70
|
+
add a,a ; optimised 1st iteration
|
|
71
|
+
jr nc,$+4
|
|
72
|
+
ld h,d
|
|
73
|
+
ld l,e
|
|
74
|
+
|
|
75
|
+
add hl,hl ; unroll 7 times
|
|
76
|
+
rla ; ...
|
|
77
|
+
jr nc,$+4 ; ...
|
|
78
|
+
add hl,de ; ...
|
|
79
|
+
adc a,c ; ...
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Restoring 16-bit * 16-bit Unsigned
|
|
83
|
+
|
|
84
|
+
Input: DE = Multiplier, BC = Multiplicand, HL = 0
|
|
85
|
+
Output: DE:HL = Product
|
|
86
|
+
```assembly
|
|
87
|
+
sla e ; optimised 1st iteration
|
|
88
|
+
rl d
|
|
89
|
+
jr nc,$+4
|
|
90
|
+
ld h,b
|
|
91
|
+
ld l,c
|
|
92
|
+
|
|
93
|
+
add hl,hl ; unroll 15 times
|
|
94
|
+
rl e ; ...
|
|
95
|
+
rl d ; ...
|
|
96
|
+
jr nc,$+6 ; ...
|
|
97
|
+
add hl,bc ; ...
|
|
98
|
+
jr nc,$+3 ; ...
|
|
99
|
+
inc de ; ...
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Square Table 8-bit * 8-bit Signed
|
|
103
|
+
|
|
104
|
+
Input: B = Multiplier, C = Multiplicand (both in range -128..127)
|
|
105
|
+
Output: HL = Product
|
|
106
|
+
|
|
107
|
+
_Note: Routine uses one of these two formulas: 2ab = (a + b)^2 - a^2 - b^2 or 2ab = a^2 + b^2 - (a - b)^2, depends if (a + b) overflows or not. Powering by 2 is done by table lookup. 512 bytes long table is aligned to 256 byte boundary and contains entries of form SqrTab[x] = x^2. If we treat one of the operands as fractional number -1..1 premultiplied by 128, 2ab performs native shift of the result into register H. That's especially useful e.g. for x * sin(y). Otherwise we have to shift HL right (divide it by 2). We could divide table entries by 2 instead but that causes loss of precision._
|
|
108
|
+
|
|
109
|
+
```assembly
|
|
110
|
+
Mul8x8
|
|
111
|
+
ld h,SqrTab/256
|
|
112
|
+
ld l,b
|
|
113
|
+
ld a,b
|
|
114
|
+
ld e,(hl)
|
|
115
|
+
inc h
|
|
116
|
+
ld d,(hl) ; DE = a^2
|
|
117
|
+
ld l,c
|
|
118
|
+
ld b,(hl)
|
|
119
|
+
dec h
|
|
120
|
+
ld c,(hl) ; BC = b^2
|
|
121
|
+
add a,l ; let's try (a + b)
|
|
122
|
+
jp pe,Plus ; jump if no overflow
|
|
123
|
+
|
|
124
|
+
sub l
|
|
125
|
+
sub l
|
|
126
|
+
ld l,a
|
|
127
|
+
ld a,(hl)
|
|
128
|
+
inc h
|
|
129
|
+
ld h,(hl)
|
|
130
|
+
ld l,a ; HL = (a - b)^2
|
|
131
|
+
ex de,hl
|
|
132
|
+
add hl,bc
|
|
133
|
+
sbc hl,de ; HL = a^2 + b^2 - (a - b)^2
|
|
134
|
+
|
|
135
|
+
; sra h ; uncomment to get real product
|
|
136
|
+
; rr l
|
|
137
|
+
ret
|
|
138
|
+
|
|
139
|
+
Plus
|
|
140
|
+
ld l,a
|
|
141
|
+
ld a,(hl)
|
|
142
|
+
inc h
|
|
143
|
+
ld h,(hl)
|
|
144
|
+
ld l,a ; HL = (a + b)^2
|
|
145
|
+
or a
|
|
146
|
+
sbc hl,bc
|
|
147
|
+
or a
|
|
148
|
+
sbc hl,de ; HL = (a + b)^2 - a^2 - b^2
|
|
149
|
+
|
|
150
|
+
; sra h ; uncomment to get real product
|
|
151
|
+
; rr l
|
|
152
|
+
ret
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
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.
|
|
156
|
+
|
|
157
|
+
```assembly
|
|
158
|
+
ld hl,SqrTab ; must be a multiple of 256
|
|
159
|
+
ld b,l
|
|
160
|
+
ld c,l ; BC holds odd numbers
|
|
161
|
+
ld d,l
|
|
162
|
+
ld e,l ; DE holds squares
|
|
163
|
+
|
|
164
|
+
SqrGen
|
|
165
|
+
ld (hl),e
|
|
166
|
+
inc h
|
|
167
|
+
ld (hl),d ; store x^2
|
|
168
|
+
ld a,l
|
|
169
|
+
neg
|
|
170
|
+
ld l,a
|
|
171
|
+
ld (hl),d
|
|
172
|
+
dec h
|
|
173
|
+
ld (hl),e ; store -x^2
|
|
174
|
+
ex de,hl
|
|
175
|
+
inc c
|
|
176
|
+
add hl,bc ; add next odd number
|
|
177
|
+
inc c
|
|
178
|
+
ex de,hl
|
|
179
|
+
|
|
180
|
+
cpl ; one byte replacement for NEG, DEC A
|
|
181
|
+
ld l,a
|
|
182
|
+
rla
|
|
183
|
+
jr c,SqrGen
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Square Table Driven 6-bit * 6-bit Signed
|
|
187
|
+
|
|
188
|
+
The first thing that should be pointed out here is that the topic is not particularly correct. Actually, the routine is able to multiply any pair of numbers x, y as long as (x + y) <= 127 and (x - y) >= -128. But if x, y are signed 6-bit values these rules are never violated, no overflows occur and no specific checking is needed.
|
|
189
|
+
|
|
190
|
+
The routine is based on a formula 4xy = (x + y)^2 - (x - y)^2 and uses the same lookup table (see previous chapter) except all table entries are pre-divided by 4 to avoid division (shifting) at the end. An explanation why it works can be found here. In case we leave the table as is, routine nicely handles fixed point multiplications. That means, if we treat one of the operands as fractional number in range (-1, 1) pre-multiplied by 64, integer part of the result gets shifted handily into register H.
|
|
191
|
+
|
|
192
|
+
_Note: SqrTab must be aligned to 256 byte boundary._
|
|
193
|
+
|
|
194
|
+
Input: B = Multiplier, C = Multiplicand
|
|
195
|
+
Output: BC = Product
|
|
196
|
+
|
|
197
|
+
```assembly
|
|
198
|
+
Mul6x6
|
|
199
|
+
ld h,SqrTab/256
|
|
200
|
+
ld d,h
|
|
201
|
+
ld a,b
|
|
202
|
+
add a,c ; A = x + y
|
|
203
|
+
ld l,a
|
|
204
|
+
ld a,b
|
|
205
|
+
sub c ; A = x - y
|
|
206
|
+
ld e,a
|
|
207
|
+
ld a,(de) ; subtract lower byte
|
|
208
|
+
sub (hl) ; lower byte of (x + y)^2 - (x - y)^2
|
|
209
|
+
ld c,a
|
|
210
|
+
inc h
|
|
211
|
+
inc d
|
|
212
|
+
ld a,(de)
|
|
213
|
+
sbc a,(hl) ; higher byte of (x + y)^2 - (x - y)^2
|
|
214
|
+
ld b,a
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
This is the fastest version I could come up with but there's also slightly slower one which preserves one register pair:
|
|
218
|
+
|
|
219
|
+
Input: B = Multiplier, C = Multiplicand
|
|
220
|
+
Output: HL = Product
|
|
221
|
+
|
|
222
|
+
```assembly
|
|
223
|
+
Mul6x6
|
|
224
|
+
ld h,SqrTab/256
|
|
225
|
+
ld a,b
|
|
226
|
+
sub c ; A = x - y
|
|
227
|
+
ld l,a
|
|
228
|
+
ld a,b
|
|
229
|
+
add a,c ; A = x + y
|
|
230
|
+
ld c,(hl)
|
|
231
|
+
inc h
|
|
232
|
+
ld b,(hl) ; BC = (x - y)^2
|
|
233
|
+
ld l,a
|
|
234
|
+
ld a,(hl)
|
|
235
|
+
dec h
|
|
236
|
+
ld l,(hl)
|
|
237
|
+
ld h,a ; HL = (x + y)^2
|
|
238
|
+
or a
|
|
239
|
+
sbc hl,bc ; HL = (x + y)^2 - (x - y)^2
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
It's also possible to speed up this routine by having two consecutive look-up tables where first table is negated. Question is, however, if 4 cycles are worth wasting another 512 bytes.
|
|
243
|
+
|
|
244
|
+
```assembly
|
|
245
|
+
Mul6x6
|
|
246
|
+
ld h,SqrTab/256
|
|
247
|
+
ld a,b
|
|
248
|
+
sub c ; A = x - y
|
|
249
|
+
ld l,a
|
|
250
|
+
ld a,b
|
|
251
|
+
add a,c ; A = x + y
|
|
252
|
+
ld c,(hl)
|
|
253
|
+
inc h
|
|
254
|
+
ld b,(hl) ; BC = -(x - y)^2, that's the trick
|
|
255
|
+
inc h
|
|
256
|
+
ld l,a
|
|
257
|
+
ld a,(hl)
|
|
258
|
+
inc h
|
|
259
|
+
ld h,(hl)
|
|
260
|
+
ld l,a ; HL = (x + y)^2
|
|
261
|
+
add hl,bc ; HL = (x + y)^2 - (x - y)^2
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Logarithmic Table Driven 8-bit * 8-bit Signed
|
|
265
|
+
(to do)
|
|
266
|
+
|
|
267
|
+
## Integer Division
|
|
268
|
+
|
|
269
|
+
Division is an awkward arithmetic operation even if it's directly supported by hardware. Thus, we can't expect blazing speed even from well written code. Before you attempt to use any of these routines, please consider these hints:
|
|
270
|
+
- If the divisor is a power of two, don't bother with division. Use shifts.
|
|
271
|
+
- If you divide by a constant, use lookup table indexed by dividend. As this is not always possible, try to "unroll" division so that it only contains terms with powers of two. For example, X / 10 = (X + X / 2 + X / 8 - X / 64 + ...) / 16. However, this approach often limits the range of numbers we can use (because of precision) and is only suitable for divisors that are close to powers of two.
|
|
272
|
+
- If you divide numbers from a limited range (i.e. only 6-bit numbers), use lookup table as well. Table can be organised several ways, depends on memory limitations and range of numbers. Typically, dividend/divisor would form higher/lower byte of address at which you pick up the result.
|
|
273
|
+
- Sometimes it's worth multiplying by reciprocal number (fixed point multiplication). Be careful though as you might lose precision.
|
|
274
|
+
- In some cases (especially for small numbers) it might be actually faster to subtract divisor from dividend in loop. However, in general this is the slowest and most naive method.
|
|
275
|
+
|
|
276
|
+
The routines presented here are mostly based on so-called restoring division algorithm. Although more sophisticated methods exist, implemetation of this algorithm (particularly its left-rotating version) on Z80 is very efficient and straightforward.
|
|
277
|
+
|
|
278
|
+
Some routines use "undocumented" instruction SLIA (Shift Left Inverted Arithmetic), sometimes also denoted as SLL (Shift Left Logical). SLIA shifts register left and sets the least significant bit to 1 (operation code is CBh 30h..37h). It is used in routines which subtract 16-bit divisor from the 16-bit remainder. As there is no simple way to test whether such subtraction will be successful without actually performing it, it's better to assume it will be successful (hence 1 in the LSB) and possibly make a correction to 0. Doing it the opposite way introduces the overhead of Carry complement (see bellow).
|
|
279
|
+
|
|
280
|
+
_Note: Most division routines leave register B untouched. It enables you to create loops using DJNZ in case you prefer compact code and couple of additional cycles is not an issue._
|
|
281
|
+
|
|
282
|
+
### Restoring 8-bit / 8-bit Unsigned
|
|
283
|
+
|
|
284
|
+
Input: D = Dividend, E = Divisor, A = 0
|
|
285
|
+
Output: D = Quotient, A = Remainder
|
|
286
|
+
|
|
287
|
+
```assembly
|
|
288
|
+
sla d ; unroll 8 times
|
|
289
|
+
rla ; ...
|
|
290
|
+
cp e ; ...
|
|
291
|
+
jr c,$+4 ; ...
|
|
292
|
+
sub e ; ...
|
|
293
|
+
inc d ; ...
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
The most awkward "feature" of this algorithm is the relation between Carry and the newly determined bit of the result. Successful subtraction (Carry = 0) means that we should set the bit to 1 and vice versa. One possible workaround is to leave Carry as is and complement whole result at the end. This introduces a little overhead but also saves one instruction in the main loop.
|
|
297
|
+
|
|
298
|
+
Input: D = Dividend, E = Divisor, A = 0, Carry = 0
|
|
299
|
+
Output: A = Quotient, E = Remainder
|
|
300
|
+
```assembly
|
|
301
|
+
rl d ; unroll 8 times
|
|
302
|
+
rla ; ...
|
|
303
|
+
sub e ; ...
|
|
304
|
+
jr nc,$+3 ; ...
|
|
305
|
+
add a,e ; ...
|
|
306
|
+
|
|
307
|
+
...
|
|
308
|
+
|
|
309
|
+
; ld e,a ; save remainder (if needed)
|
|
310
|
+
ld a,d ; complement all bits of the result
|
|
311
|
+
cpl
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Restoring 16-bit / 8-bit Unsigned
|
|
315
|
+
|
|
316
|
+
Input: HL = Dividend, C = Divisor, A = 0
|
|
317
|
+
Output: HL = Quotient, A = Remainder
|
|
318
|
+
```assembly
|
|
319
|
+
add hl,hl ; unroll 16 times
|
|
320
|
+
rla ; ...
|
|
321
|
+
cp c ; ...
|
|
322
|
+
jr c,$+4 ; ...
|
|
323
|
+
sub c ; ...
|
|
324
|
+
inc l ; ...
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Restoring 16-bit / 16-bit Unsigned
|
|
328
|
+
|
|
329
|
+
Input: A:C = Dividend, DE = Divisor, HL = 0
|
|
330
|
+
Output: A:C = Quotient, HL = Remainder
|
|
331
|
+
```assembly
|
|
332
|
+
slia c ; unroll 16 times
|
|
333
|
+
rla ; ...
|
|
334
|
+
adc hl,hl ; ...
|
|
335
|
+
sbc hl,de ; ...
|
|
336
|
+
jr nc,$+4 ; ...
|
|
337
|
+
add hl,de ; ...
|
|
338
|
+
dec c ; ...
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
We can use the forementioned trick with complementing the result also here but it's not that obvious how removed DEC C balances against additional overhead. In any case, this routine doesn't contain any "undocumented" instructions and might be preferable (?) for that reason.
|
|
342
|
+
|
|
343
|
+
Input: A:C = Dividend, DE = Divisor, HL = 0
|
|
344
|
+
Output: BC = Quotient, HL = Remainder
|
|
345
|
+
|
|
346
|
+
```assembly
|
|
347
|
+
rl c ; unroll 16 times
|
|
348
|
+
rla ; ...
|
|
349
|
+
adc hl,hl ; ...
|
|
350
|
+
sbc hl,de ; ...
|
|
351
|
+
jr nc,$+3 ; ...
|
|
352
|
+
add hl,de ; ...
|
|
353
|
+
|
|
354
|
+
...
|
|
355
|
+
|
|
356
|
+
rl c
|
|
357
|
+
rla
|
|
358
|
+
cpl
|
|
359
|
+
ld b,a
|
|
360
|
+
ld a,c
|
|
361
|
+
cpl
|
|
362
|
+
ld c,a
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Restoring 24-bit / 8-bit Unsigned
|
|
366
|
+
|
|
367
|
+
Input: E:HL = Dividend, D = Divisor, A = 0
|
|
368
|
+
Output: E:HL = Quotient, A = Remainder
|
|
369
|
+
```assembly
|
|
370
|
+
add hl,hl ; unroll 24 times
|
|
371
|
+
rl e ; ...
|
|
372
|
+
rla ; ...
|
|
373
|
+
cp d ; ...
|
|
374
|
+
jr c,$+4 ; ...
|
|
375
|
+
sub d ; ...
|
|
376
|
+
inc l ; ...
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Restoring 24-bit / 16-bit Unsigned
|
|
380
|
+
|
|
381
|
+
Input: A:BC = Dividend, DE = Divisor, HL = 0
|
|
382
|
+
Output: A:BC = Quotient, HL = Remainder
|
|
383
|
+
```assembly
|
|
384
|
+
slia c ; unroll 24 times
|
|
385
|
+
rl b ; ...
|
|
386
|
+
rla ; ...
|
|
387
|
+
adc hl,hl ; ...
|
|
388
|
+
sbc hl,de ; ...
|
|
389
|
+
jr nc,$+4 ; ...
|
|
390
|
+
add hl,de ; ...
|
|
391
|
+
dec c ; ...
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### Restoring 32-bit / 8-bit Unsigned
|
|
395
|
+
|
|
396
|
+
Input: DE:HL = Dividend, C = Divisor, A = 0
|
|
397
|
+
Output: DE:HL = Quotient, A = Remainder
|
|
398
|
+
```assembly
|
|
399
|
+
add hl,hl ; unroll 32 times
|
|
400
|
+
rl e ; ...
|
|
401
|
+
rl d ; ...
|
|
402
|
+
rla ; ...
|
|
403
|
+
cp c ; ...
|
|
404
|
+
jr c,$+4 ; ...
|
|
405
|
+
sub c ; ...
|
|
406
|
+
inc l ; ...
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## Integer Square Root
|
|
410
|
+
|
|
411
|
+
Although square roots don't appear in the code as often as multiplication or division, they represent a challenging problem when there's actual need for such calculation. From a coder's point of view it's interesting to explore the set of different approaches one can take and evaluate their pros and cons. Talking about integer square root, let's be specific what it means: given radicant N, the square root of N is the largest integer X that satisfies the condition X * X <= N.
|
|
412
|
+
|
|
413
|
+
### Basic 8-bit Square Root
|
|
414
|
+
|
|
415
|
+
As mentioned earlier in the chapter Integer Multiplication, integer squares can be generated iteratively by adding consecutive odd numbers. The simplest way to calculate square root is then just a reverse of this process; take a number and subtract successive odd integers from it. Number of iterations it takes until the result is negative is the radix.
|
|
416
|
+
|
|
417
|
+
Input: A = Radicand
|
|
418
|
+
Output: B = Radix
|
|
419
|
+
```assembly
|
|
420
|
+
ld b,-1
|
|
421
|
+
Sqrt8
|
|
422
|
+
inc b
|
|
423
|
+
inc b
|
|
424
|
+
sub b
|
|
425
|
+
jr nc,Sqrt8 ; use JP to save 2 T
|
|
426
|
+
srl b ; translate (2X + 1) to X
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Basic 16-bit Square Root
|
|
430
|
+
|
|
431
|
+
This routine is based exactly on the same method, except in 16-bits it's more effective to add negative number rather than to subtract it. The simplicity and virtually no memory requirements are the main advantages of this algorithm. The obvious shortcoming is running time; the time used is proportional to the magnitude of radix.
|
|
432
|
+
|
|
433
|
+
Input: HL = Radicand
|
|
434
|
+
Output: A = Radix
|
|
435
|
+
```assembly
|
|
436
|
+
ld a,-1
|
|
437
|
+
ld d,a
|
|
438
|
+
ld e,a
|
|
439
|
+
Sqrt16
|
|
440
|
+
add hl,de
|
|
441
|
+
inc a
|
|
442
|
+
dec e
|
|
443
|
+
dec de
|
|
444
|
+
jr c,Sqrt16 ; use JP to save 2 T
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### Restoring 16-bit Square Root
|
|
448
|
+
|
|
449
|
+
This elegant algorithm is similar to restoring division. One root digit is generated per iteration such that the partial root converges to the correct answer. Unlike in division, the "divisor" changes as solution progresses and two new digits are shifted into the partial remainder at a time.
|
|
450
|
+
|
|
451
|
+
Let R be the current remainder and X be the square root approximation found so far. The area that remains is then (R - X^2) and our new remainder becomes R' = 4 * (R - X^2) + AB, where AB are the next two radicand digits. We want to find a better root approximation by adding the largest new digit D such that (2X + D)^2 <= R' + 4X^2 (note that 2X represents shift one digit left in binary and 4X^2 is the area we subtracted in the previous step). This is identical to 4X^2 + 4XD + D^2 <= R' + 4X^2, or even better (4X + D) * D <= R'. In binary, the largest D we can try is 1 so our formula reduces to (4X + 1) <= R' and the trial step becomes R' - (4X + 1). If the result is non-negative, a "1" is generated. Otherwise a "0" is generated and the remainder is restored. Another simplification introduced by binary is that (4X + 1) is nothing else but "01" appended to the partial root.
|
|
452
|
+
|
|
453
|
+
I recommend searching the web for "integer square root algorithm" if you want to gain more thorough understanding of the math behind it. I created a figure that might help you to understand the basic idea from a geometric point of view.
|
|
454
|
+
|
|
455
|
+
Input: L:A = Radicand, Carry = 0
|
|
456
|
+
Output: D = Radix
|
|
457
|
+
```assembly
|
|
458
|
+
ld de,0040h ; 40h appends "01" to D
|
|
459
|
+
ld h,d
|
|
460
|
+
|
|
461
|
+
sbc hl,de ; unroll 7 times
|
|
462
|
+
jr nc,$+3 ; ...
|
|
463
|
+
add hl,de ; ...
|
|
464
|
+
ccf ; ...
|
|
465
|
+
rl d ; ...
|
|
466
|
+
rla ; ...
|
|
467
|
+
adc hl,hl ; ...
|
|
468
|
+
rla ; ...
|
|
469
|
+
adc hl,hl ; ...
|
|
470
|
+
|
|
471
|
+
...
|
|
472
|
+
|
|
473
|
+
sbc hl,de ; optimised last iteration
|
|
474
|
+
ccf
|
|
475
|
+
rl d
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### 16-bit Square Table Bisection
|
|
479
|
+
|
|
480
|
+
Although this method works best on processors with native multiply instruction, it's applicable to Z80 too. The algorithm is a trial and error process: starting with the most significant bit of the intermediate result X, we set the bit to "1" and check whether X * X <= radicand; if the test fails, we correct the bit to "0". Then we progress to the next lower bit (if any) and repeat the procedure. On a Z80, squaring is done best with 512 byte long lookup table (the algorithm in fact performs bisection search on it). This method should be marginally faster than the restoring algorithm but requires more memory.
|
|
481
|
+
|
|
482
|
+
_Note: We can achieve better code optimisation if the table entries are negative (ADD HL,DE is faster than SBC HL,DE). This requires only a minor modification to the square table generator presented in the chapter Integer Multiplication. Table must be aligned to 256 byte boundary._
|
|
483
|
+
|
|
484
|
+
```assembly
|
|
485
|
+
ld hl,NegSqrTab ; must be a multiple of 256
|
|
486
|
+
ld b,l
|
|
487
|
+
ld c,l ; BC holds odd numbers
|
|
488
|
+
ld d,l
|
|
489
|
+
ld e,l ; DE holds squares
|
|
490
|
+
|
|
491
|
+
SqrGen
|
|
492
|
+
ld (hl),e
|
|
493
|
+
inc h
|
|
494
|
+
ld (hl),d ; store x^2
|
|
495
|
+
dec h
|
|
496
|
+
ex de,hl
|
|
497
|
+
dec bc
|
|
498
|
+
add hl,bc ; add next odd number
|
|
499
|
+
dec c
|
|
500
|
+
ex de,hl
|
|
501
|
+
inc l
|
|
502
|
+
jr nz,SqrGen
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
Now for the actual square root code. First, we will take a look at compact (but slower) version:
|
|
506
|
+
|
|
507
|
+
Input: DE = Radicand
|
|
508
|
+
Output: A = Radix
|
|
509
|
+
```assembly
|
|
510
|
+
xor a
|
|
511
|
+
ld b,128
|
|
512
|
+
|
|
513
|
+
BiSqrt
|
|
514
|
+
xor b ; set the trial bit
|
|
515
|
+
ld h,NegSqrTab/256
|
|
516
|
+
ld l,a
|
|
517
|
+
ld c,(hl)
|
|
518
|
+
inc h
|
|
519
|
+
ld h,(hl)
|
|
520
|
+
ld l,c ; HL = A * A
|
|
521
|
+
add hl,de ; HL < DE ?
|
|
522
|
+
jr c,BitOK
|
|
523
|
+
xor b ; reset bit if the test fails
|
|
524
|
+
BitOK
|
|
525
|
+
srl b
|
|
526
|
+
jr nc,BiSqrt ; use JP to save 2 T
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
And here's the full-blown unrolled version. The immediate operands in each iteration are 128, 64, 32, 16, 8, 4, 2 and 1.
|
|
530
|
+
|
|
531
|
+
```assembly
|
|
532
|
+
ld b,NegSqrTab/256
|
|
533
|
+
|
|
534
|
+
ld a,128 ; optimised 1st iteration
|
|
535
|
+
ld h,b
|
|
536
|
+
ld l,a
|
|
537
|
+
ld c,(hl)
|
|
538
|
+
inc h
|
|
539
|
+
ld h,(hl)
|
|
540
|
+
ld l,c
|
|
541
|
+
add hl,de
|
|
542
|
+
jr c,$+3
|
|
543
|
+
xor a
|
|
544
|
+
|
|
545
|
+
xor 64 ; unroll 7x (but change immediate operands!)
|
|
546
|
+
ld h,b ; ...
|
|
547
|
+
ld l,a ; ...
|
|
548
|
+
ld c,(hl) ; ...
|
|
549
|
+
inc h ; ...
|
|
550
|
+
ld h,(hl) ; ...
|
|
551
|
+
ld l,c ; ...
|
|
552
|
+
add hl,de ; ...
|
|
553
|
+
jr c,$+4 ; ...
|
|
554
|
+
xor 64 ; ...
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
## Random Number Generators
|
|
558
|
+
|
|
559
|
+
### 8-bit Random Number Generator
|
|
560
|
+
|
|
561
|
+
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.
|
|
562
|
+
|
|
563
|
+
Input: none
|
|
564
|
+
Output: A = pseudo-random number, period 256
|
|
565
|
+
|
|
566
|
+
```assembly
|
|
567
|
+
Rand8
|
|
568
|
+
ld a,Seed ; Seed is usually 0
|
|
569
|
+
ld b,a
|
|
570
|
+
add a,a
|
|
571
|
+
add a,a
|
|
572
|
+
add a,b
|
|
573
|
+
inc a ; another possibility is ADD A,7
|
|
574
|
+
ld (Rand8+1),a
|
|
575
|
+
ret
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
### 16-bit Random Number Generator
|
|
579
|
+
|
|
580
|
+
This generator is based on similar method but gives much better results. It was taken from an old ZX Spectrum game and slightly optimised.
|
|
581
|
+
|
|
582
|
+
Input: none
|
|
583
|
+
Output: HL = pseudo-random number, period 65536
|
|
584
|
+
|
|
585
|
+
```assembly
|
|
586
|
+
Rand16
|
|
587
|
+
ld de,Seed ; Seed is usually 0
|
|
588
|
+
ld a,d
|
|
589
|
+
ld h,e
|
|
590
|
+
ld l,253
|
|
591
|
+
or a
|
|
592
|
+
sbc hl,de
|
|
593
|
+
sbc a,0
|
|
594
|
+
sbc hl,de
|
|
595
|
+
ld d,0
|
|
596
|
+
sbc a,d
|
|
597
|
+
ld e,a
|
|
598
|
+
sbc hl,de
|
|
599
|
+
jr nc,Rand
|
|
600
|
+
inc hl
|
|
601
|
+
Rand
|
|
602
|
+
ld (Rand16+1),hl
|
|
603
|
+
ret
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
## Conversions Between Numbers and Strings
|
|
607
|
+
|
|
608
|
+
### 16-bit Integer to ASCII (decimal)
|
|
609
|
+
|
|
610
|
+
Input: HL = number to convert, DE = location of ASCII string
|
|
611
|
+
Output: ASCII string at (DE)
|
|
612
|
+
```assembly
|
|
613
|
+
Num2Dec
|
|
614
|
+
ld bc,-10000
|
|
615
|
+
call Num1
|
|
616
|
+
ld bc,-1000
|
|
617
|
+
call Num1
|
|
618
|
+
ld bc,-100
|
|
619
|
+
call Num1
|
|
620
|
+
ld c,-10
|
|
621
|
+
call Num1
|
|
622
|
+
ld c,b
|
|
623
|
+
|
|
624
|
+
Num1
|
|
625
|
+
ld a,'0'-1
|
|
626
|
+
Num2
|
|
627
|
+
inc a
|
|
628
|
+
add hl,bc
|
|
629
|
+
jr c,Num2
|
|
630
|
+
sbc hl,bc
|
|
631
|
+
|
|
632
|
+
ld (de),a
|
|
633
|
+
inc de
|
|
634
|
+
ret
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
### 16-bit Integer to ASCII (hexadecimal)
|
|
638
|
+
|
|
639
|
+
Hexadecimal conversion operates directly on nibbles and takes advantage of nifty DAA trick.
|
|
640
|
+
|
|
641
|
+
Input: HL = number to convert, DE = location of ASCII string
|
|
642
|
+
Output: ASCII string at (DE)
|
|
643
|
+
```assembly
|
|
644
|
+
Num2Hex
|
|
645
|
+
ld a,h
|
|
646
|
+
call Num1
|
|
647
|
+
ld a,h
|
|
648
|
+
call Num2
|
|
649
|
+
ld a,l
|
|
650
|
+
call Num1
|
|
651
|
+
ld a,l
|
|
652
|
+
jr Num2
|
|
653
|
+
|
|
654
|
+
Num1
|
|
655
|
+
rra
|
|
656
|
+
rra
|
|
657
|
+
rra
|
|
658
|
+
rra
|
|
659
|
+
Num2
|
|
660
|
+
or F0h
|
|
661
|
+
daa
|
|
662
|
+
add a,A0h
|
|
663
|
+
adc a,40h
|
|
664
|
+
|
|
665
|
+
ld (de),a
|
|
666
|
+
inc de
|
|
667
|
+
ret
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
### Memory dump (hexadecimal)
|
|
671
|
+
|
|
672
|
+
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.
|
|
673
|
+
|
|
674
|
+
Input: HL = address of data
|
|
675
|
+
Output: memory dump
|
|
676
|
+
|
|
677
|
+
_Note: You'll have to replace the PRINT_CHAR macro by actual platform-specific code. Don't forget to preserve the contents of HL!_
|
|
678
|
+
```assembly
|
|
679
|
+
xor a
|
|
680
|
+
rld
|
|
681
|
+
call Nibble
|
|
682
|
+
|
|
683
|
+
Nibble
|
|
684
|
+
push af
|
|
685
|
+
daa
|
|
686
|
+
add a,F0h
|
|
687
|
+
adc a,40h
|
|
688
|
+
|
|
689
|
+
PRINT_CHAR ; prints ASCII character in A
|
|
690
|
+
|
|
691
|
+
pop af
|
|
692
|
+
rld
|
|
693
|
+
ret
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
## Cyclic Redundancy Checks (CRC)
|
|
697
|
+
|
|
698
|
+
### 16-bit CRC-CCITT
|
|
699
|
+
|
|
700
|
+
The following routine calculates standard CRC-CCITT bit-by-bit using polynomial 1021h. Another common scheme CRC-16 uses polynomial A001h and starts with value 0 (so it's likely that you misinterpret bunch of zeros as valid data). It might be useful to extend the code to use 16-bit byte counter.
|
|
701
|
+
|
|
702
|
+
Input: DE = address of input data, C = number of bytes to process
|
|
703
|
+
Output: HL = CRC
|
|
704
|
+
```assembly
|
|
705
|
+
Crc16
|
|
706
|
+
ld hl,FFFFh
|
|
707
|
+
Read
|
|
708
|
+
ld a,(de)
|
|
709
|
+
inc de
|
|
710
|
+
xor h
|
|
711
|
+
ld h,a
|
|
712
|
+
ld b,8
|
|
713
|
+
CrcByte
|
|
714
|
+
add hl,hl
|
|
715
|
+
jr nc,Next
|
|
716
|
+
ld a,h
|
|
717
|
+
xor 10h
|
|
718
|
+
ld h,a
|
|
719
|
+
ld a,l
|
|
720
|
+
xor 21h
|
|
721
|
+
ld l,a
|
|
722
|
+
Next
|
|
723
|
+
djnz CrcByte
|
|
724
|
+
dec c
|
|
725
|
+
jr nz,Read
|
|
726
|
+
ret
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
### Table Driven 16-bit CRC-CCITT
|
|
730
|
+
|
|
731
|
+
_Note: I haven't tested the results yet so there might be a bug somewhere (most likely wrong polynomial producing bad results)._
|
|
732
|
+
|
|
733
|
+
This is a much faster equivalent of the previous routine. It processes one byte at a time using 512 byte long table. There is a change in algorithm though. Intermediate results are shifted right and polynomial is reversed. It means that even results are reversed (the most significant bit is actually the least significant one and vice versa). Depending on actual use this might be a problem or not (for example, it's suitable if you interoperate with hardware as UARTs send least significant bit first). Even if you decide to adjust result back to correct value, you should still gain more than you loss.
|
|
734
|
+
|
|
735
|
+
Input: HL = address of input data, BC = number of bytes to process
|
|
736
|
+
Output: DE = CRC
|
|
737
|
+
|
|
738
|
+
_Note: CrcTab must be aligned to 256 byte boundary. Table generator uses reverse of 1021h, that is 8408h._
|
|
739
|
+
```assembly
|
|
740
|
+
Crc16
|
|
741
|
+
ld d,FFh
|
|
742
|
+
ld a,e
|
|
743
|
+
Read
|
|
744
|
+
xor (hl)
|
|
745
|
+
ex de,hl
|
|
746
|
+
ld l,a
|
|
747
|
+
ld a,h
|
|
748
|
+
ld h,CrcTab/256
|
|
749
|
+
xor (hl)
|
|
750
|
+
inc h
|
|
751
|
+
ld h,(hl)
|
|
752
|
+
ex de,hl
|
|
753
|
+
cpi
|
|
754
|
+
jp pe,Read
|
|
755
|
+
ld e,a
|
|
756
|
+
ret
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
CRC table generator:
|
|
760
|
+
```assembly
|
|
761
|
+
ld hl,CrcTab
|
|
762
|
+
CrcGen
|
|
763
|
+
ld d,0
|
|
764
|
+
ld e,l
|
|
765
|
+
ld b,8
|
|
766
|
+
CrcByte
|
|
767
|
+
srl d
|
|
768
|
+
rr e
|
|
769
|
+
jr nc,Next
|
|
770
|
+
ld a,d
|
|
771
|
+
xor 84h
|
|
772
|
+
ld d,a
|
|
773
|
+
ld a,e
|
|
774
|
+
xor 08h
|
|
775
|
+
ld e,a
|
|
776
|
+
Next
|
|
777
|
+
djnz CrcByte
|
|
778
|
+
ld (hl),e
|
|
779
|
+
inc h
|
|
780
|
+
ld (hl),d
|
|
781
|
+
dec h
|
|
782
|
+
inc l
|
|
783
|
+
jr nz,CrcGen
|
|
784
|
+
ret
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
### 32-bit CRC-32
|
|
788
|
+
(to do)
|
|
789
|
+
|
|
790
|
+
### Table Driven 32-bit CRC-32
|
|
791
|
+
(to do)
|
|
792
|
+
|
|
793
|
+
## Credits
|
|
794
|
+
|
|
795
|
+
My thanks goes to the following people who contributed to this document:
|
|
796
|
+
|
|
797
|
+
- Slavomir "Busy" Labsky
|
|
798
|
+
- Pavel "Zilogator" Cimbal
|
|
799
|
+
- Norbert "Noro" Grellneth
|
|
800
|
+
- Tomas "Universum" Vilim
|
|
801
|
+
- Patai "CoBB" Gergely
|
|
802
|
+
- Lawrence Chitty
|
|
803
|
+
- Petr "Poke" Petyovsky
|
|
804
|
+
- David Revelj
|
|
805
|
+
- Dan Englender
|
|
806
|
+
- Ricardo Bittencourt
|
|
807
|
+
|
|
808
|
+
## Source
|
|
809
|
+
|
|
810
|
+
https://map.grauw.nl/sources/external/z80bits.html
|