@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.
Files changed (216) hide show
  1. package/README.md +84 -37
  2. package/dist/server.js +101 -40
  3. package/dist/utils.js +42 -2
  4. package/dist/vectordb.js +61 -0
  5. package/package.json +8 -3
  6. package/resources/audio/msx-midi.md +872 -0
  7. package/resources/audio/psg_registers.md +281 -0
  8. package/resources/audio/sound_cartridge_scc.md +123 -0
  9. package/resources/audio/sound_cartridge_scci.md +250 -0
  10. package/resources/audio/toc.json +8 -4
  11. package/resources/book--msx2-technical-handbook/toc.json +1 -1
  12. package/resources/msx-dos/MSX-DOS_2_Environment_Variables.md +1368 -0
  13. package/resources/msx-dos/MSX-DOS_File_extensions.md +154 -0
  14. package/resources/msx-dos/toc.json +13 -0
  15. package/resources/msx-unapi/toc.json +2 -2
  16. package/resources/others/keyboard_matrices.md +243 -0
  17. package/resources/others/toc.json +6 -0
  18. package/resources/programming/asm_callbios.md +79 -0
  19. package/resources/programming/asm_docopy.md +115 -0
  20. package/resources/programming/asm_fast_loops.md +200 -0
  21. package/resources/programming/asm_getslot.md +143 -0
  22. package/resources/programming/asm_interrupts.md +202 -0
  23. package/resources/programming/asm_load_screen.md +240 -0
  24. package/resources/programming/asm_mult_div_shifts.md +487 -0
  25. package/resources/programming/asm_raminpage1.md +56 -0
  26. package/resources/programming/asm_vdp_detection.md +78 -0
  27. package/resources/programming/asm_vdp_routines.md +343 -0
  28. package/resources/programming/asm_z80_routines_collection.md +810 -0
  29. package/resources/programming/basic_wiki/ABS().md +36 -0
  30. package/resources/programming/basic_wiki/AND.md +71 -0
  31. package/resources/programming/basic_wiki/ASC().md +38 -0
  32. package/resources/programming/basic_wiki/ATN().md +36 -0
  33. package/resources/programming/basic_wiki/AUTO.md +39 -0
  34. package/resources/programming/basic_wiki/BASE().md +147 -0
  35. package/resources/programming/basic_wiki/BEEP.md +27 -0
  36. package/resources/programming/basic_wiki/BIN$().md +36 -0
  37. package/resources/programming/basic_wiki/BLOAD.md +63 -0
  38. package/resources/programming/basic_wiki/BSAVE.md +61 -0
  39. package/resources/programming/basic_wiki/CALL.md +391 -0
  40. package/resources/programming/basic_wiki/CALL_ADJUST.md +40 -0
  41. package/resources/programming/basic_wiki/CALL_IMPOSE.md +28 -0
  42. package/resources/programming/basic_wiki/CALL_OPTIONS.md +26 -0
  43. package/resources/programming/basic_wiki/CALL_PAUSE.md +119 -0
  44. package/resources/programming/basic_wiki/CALL_PCMPLAY.md +60 -0
  45. package/resources/programming/basic_wiki/CALL_PCMREC.md +70 -0
  46. package/resources/programming/basic_wiki/CDBL().md +36 -0
  47. package/resources/programming/basic_wiki/CHR$().md +51 -0
  48. package/resources/programming/basic_wiki/CINT().md +36 -0
  49. package/resources/programming/basic_wiki/CIRCLE.md +51 -0
  50. package/resources/programming/basic_wiki/CLEAR.md +39 -0
  51. package/resources/programming/basic_wiki/CLOAD.md +27 -0
  52. package/resources/programming/basic_wiki/CLOAD?.md +31 -0
  53. package/resources/programming/basic_wiki/CLOSE.md +44 -0
  54. package/resources/programming/basic_wiki/CLS.md +51 -0
  55. package/resources/programming/basic_wiki/COLOR.md +143 -0
  56. package/resources/programming/basic_wiki/COLOR=.md +93 -0
  57. package/resources/programming/basic_wiki/COLOR_SPRITE$().md +83 -0
  58. package/resources/programming/basic_wiki/COLOR_SPRITE().md +85 -0
  59. package/resources/programming/basic_wiki/CONT.md +23 -0
  60. package/resources/programming/basic_wiki/COPY.md +215 -0
  61. package/resources/programming/basic_wiki/COPY_SCREEN.md +61 -0
  62. package/resources/programming/basic_wiki/COS().md +37 -0
  63. package/resources/programming/basic_wiki/CSAVE.md +35 -0
  64. package/resources/programming/basic_wiki/CSNG().md +36 -0
  65. package/resources/programming/basic_wiki/CSRLIN.md +33 -0
  66. package/resources/programming/basic_wiki/DATA.md +47 -0
  67. package/resources/programming/basic_wiki/DEFDBL.md +40 -0
  68. package/resources/programming/basic_wiki/DEFINT.md +40 -0
  69. package/resources/programming/basic_wiki/DEFSNG.md +40 -0
  70. package/resources/programming/basic_wiki/DEFSTR.md +40 -0
  71. package/resources/programming/basic_wiki/DEF_FN.md +49 -0
  72. package/resources/programming/basic_wiki/DEF_USR.md +33 -0
  73. package/resources/programming/basic_wiki/DELETE.md +49 -0
  74. package/resources/programming/basic_wiki/DIM.md +59 -0
  75. package/resources/programming/basic_wiki/DRAW.md +77 -0
  76. package/resources/programming/basic_wiki/ELSE.md +45 -0
  77. package/resources/programming/basic_wiki/END.md +32 -0
  78. package/resources/programming/basic_wiki/EOF().md +36 -0
  79. package/resources/programming/basic_wiki/EQV.md +76 -0
  80. package/resources/programming/basic_wiki/ERASE.md +35 -0
  81. package/resources/programming/basic_wiki/ERL.md +34 -0
  82. package/resources/programming/basic_wiki/ERR.md +143 -0
  83. package/resources/programming/basic_wiki/ERROR.md +145 -0
  84. package/resources/programming/basic_wiki/EXP().md +38 -0
  85. package/resources/programming/basic_wiki/FIELD.md +48 -0
  86. package/resources/programming/basic_wiki/FIX().md +44 -0
  87. package/resources/programming/basic_wiki/FN.md +61 -0
  88. package/resources/programming/basic_wiki/FOR...NEXT.md +80 -0
  89. package/resources/programming/basic_wiki/FRE().md +66 -0
  90. package/resources/programming/basic_wiki/GET_DATE.md +60 -0
  91. package/resources/programming/basic_wiki/GET_TIME.md +34 -0
  92. package/resources/programming/basic_wiki/GOSUB.md +41 -0
  93. package/resources/programming/basic_wiki/GOTO.md +41 -0
  94. package/resources/programming/basic_wiki/HEX$().md +36 -0
  95. package/resources/programming/basic_wiki/IF...GOTO...ELSE.md +55 -0
  96. package/resources/programming/basic_wiki/IF...THEN...ELSE.md +50 -0
  97. package/resources/programming/basic_wiki/IMP.md +83 -0
  98. package/resources/programming/basic_wiki/INKEY$.md +65 -0
  99. package/resources/programming/basic_wiki/INP().md +33 -0
  100. package/resources/programming/basic_wiki/INPUT$().md +51 -0
  101. package/resources/programming/basic_wiki/INPUT.md +93 -0
  102. package/resources/programming/basic_wiki/INSTR().md +44 -0
  103. package/resources/programming/basic_wiki/INT().md +44 -0
  104. package/resources/programming/basic_wiki/INTERVAL.md +57 -0
  105. package/resources/programming/basic_wiki/KEY().md +51 -0
  106. package/resources/programming/basic_wiki/KEY.md +254 -0
  107. package/resources/programming/basic_wiki/LEFT$().md +39 -0
  108. package/resources/programming/basic_wiki/LEN().md +36 -0
  109. package/resources/programming/basic_wiki/LET.md +68 -0
  110. package/resources/programming/basic_wiki/LINE.md +74 -0
  111. package/resources/programming/basic_wiki/LINE_INPUT.md +79 -0
  112. package/resources/programming/basic_wiki/LIST.md +58 -0
  113. package/resources/programming/basic_wiki/LLIST.md +43 -0
  114. package/resources/programming/basic_wiki/LOAD.md +56 -0
  115. package/resources/programming/basic_wiki/LOCATE.md +67 -0
  116. package/resources/programming/basic_wiki/LOG().md +36 -0
  117. package/resources/programming/basic_wiki/LPOS().md +31 -0
  118. package/resources/programming/basic_wiki/LPRINT.md +46 -0
  119. package/resources/programming/basic_wiki/MAXFILES.md +39 -0
  120. package/resources/programming/basic_wiki/MERGE.md +54 -0
  121. package/resources/programming/basic_wiki/MID$().md +72 -0
  122. package/resources/programming/basic_wiki/MOD.md +39 -0
  123. package/resources/programming/basic_wiki/MOTOR.md +46 -0
  124. package/resources/programming/basic_wiki/NEW.md +27 -0
  125. package/resources/programming/basic_wiki/NOT.md +61 -0
  126. package/resources/programming/basic_wiki/OCT$().md +36 -0
  127. package/resources/programming/basic_wiki/ON...GOSUB.md +45 -0
  128. package/resources/programming/basic_wiki/ON...GOTO.md +42 -0
  129. package/resources/programming/basic_wiki/ON_ERROR_GOTO.md +61 -0
  130. package/resources/programming/basic_wiki/ON_INTERVAL_GOSUB.md +54 -0
  131. package/resources/programming/basic_wiki/ON_KEY_GOSUB.md +56 -0
  132. package/resources/programming/basic_wiki/ON_SPRITE_GOSUB.md +41 -0
  133. package/resources/programming/basic_wiki/ON_STOP_GOSUB.md +56 -0
  134. package/resources/programming/basic_wiki/ON_STRIG_GOSUB.md +70 -0
  135. package/resources/programming/basic_wiki/OPEN.md +103 -0
  136. package/resources/programming/basic_wiki/OR.md +75 -0
  137. package/resources/programming/basic_wiki/OUT.md +35 -0
  138. package/resources/programming/basic_wiki/PAD().md +110 -0
  139. package/resources/programming/basic_wiki/PAINT.md +66 -0
  140. package/resources/programming/basic_wiki/PDL().md +53 -0
  141. package/resources/programming/basic_wiki/PEEK().md +44 -0
  142. package/resources/programming/basic_wiki/PLAY().md +58 -0
  143. package/resources/programming/basic_wiki/PLAY.md +196 -0
  144. package/resources/programming/basic_wiki/POINT.md +52 -0
  145. package/resources/programming/basic_wiki/POKE.md +51 -0
  146. package/resources/programming/basic_wiki/POS().md +36 -0
  147. package/resources/programming/basic_wiki/PRESET.md +61 -0
  148. package/resources/programming/basic_wiki/PRINT.md +179 -0
  149. package/resources/programming/basic_wiki/PSET.md +82 -0
  150. package/resources/programming/basic_wiki/PUT_KANJI.md +93 -0
  151. package/resources/programming/basic_wiki/PUT_SPRITE.md +143 -0
  152. package/resources/programming/basic_wiki/READ.md +45 -0
  153. package/resources/programming/basic_wiki/REM.md +42 -0
  154. package/resources/programming/basic_wiki/RENUM.md +78 -0
  155. package/resources/programming/basic_wiki/RESTORE.md +52 -0
  156. package/resources/programming/basic_wiki/RESUME.md +45 -0
  157. package/resources/programming/basic_wiki/RETURN.md +47 -0
  158. package/resources/programming/basic_wiki/RIGHT$().md +39 -0
  159. package/resources/programming/basic_wiki/RND().md +51 -0
  160. package/resources/programming/basic_wiki/RUN.md +56 -0
  161. package/resources/programming/basic_wiki/SAVE.md +65 -0
  162. package/resources/programming/basic_wiki/SCREEN.md +164 -0
  163. package/resources/programming/basic_wiki/SET_ADJUST.md +66 -0
  164. package/resources/programming/basic_wiki/SET_BEEP.md +76 -0
  165. package/resources/programming/basic_wiki/SET_DATE.md +103 -0
  166. package/resources/programming/basic_wiki/SET_PAGE.md +52 -0
  167. package/resources/programming/basic_wiki/SET_PASSWORD.md +75 -0
  168. package/resources/programming/basic_wiki/SET_PROMPT.md +61 -0
  169. package/resources/programming/basic_wiki/SET_SCREEN.md +100 -0
  170. package/resources/programming/basic_wiki/SET_SCROLL.md +55 -0
  171. package/resources/programming/basic_wiki/SET_TIME.md +83 -0
  172. package/resources/programming/basic_wiki/SET_TITLE.md +87 -0
  173. package/resources/programming/basic_wiki/SET_VIDEO.md +49 -0
  174. package/resources/programming/basic_wiki/SGN().md +38 -0
  175. package/resources/programming/basic_wiki/SIN().md +36 -0
  176. package/resources/programming/basic_wiki/SOUND.md +188 -0
  177. package/resources/programming/basic_wiki/SPACE$().md +38 -0
  178. package/resources/programming/basic_wiki/SPC().md +34 -0
  179. package/resources/programming/basic_wiki/SPRITE$().md +50 -0
  180. package/resources/programming/basic_wiki/SPRITE.md +31 -0
  181. package/resources/programming/basic_wiki/SQR().md +32 -0
  182. package/resources/programming/basic_wiki/STICK().md +70 -0
  183. package/resources/programming/basic_wiki/STOP.md +70 -0
  184. package/resources/programming/basic_wiki/STR$().md +37 -0
  185. package/resources/programming/basic_wiki/STRIG().md +82 -0
  186. package/resources/programming/basic_wiki/STRING$().md +42 -0
  187. package/resources/programming/basic_wiki/SWAP.md +62 -0
  188. package/resources/programming/basic_wiki/TAB().md +38 -0
  189. package/resources/programming/basic_wiki/TAN().md +36 -0
  190. package/resources/programming/basic_wiki/TIME.md +59 -0
  191. package/resources/programming/basic_wiki/TROFF.md +21 -0
  192. package/resources/programming/basic_wiki/TRON.md +39 -0
  193. package/resources/programming/basic_wiki/USR().md +66 -0
  194. package/resources/programming/basic_wiki/VAL().md +36 -0
  195. package/resources/programming/basic_wiki/VARPTR().md +50 -0
  196. package/resources/programming/basic_wiki/VDP().md +103 -0
  197. package/resources/programming/basic_wiki/VPEEK().md +46 -0
  198. package/resources/programming/basic_wiki/VPOKE.md +48 -0
  199. package/resources/programming/basic_wiki/WAIT.md +38 -0
  200. package/resources/programming/basic_wiki/WIDTH.md +76 -0
  201. package/resources/programming/basic_wiki/XOR.md +72 -0
  202. package/resources/programming/basic_wiki/_toc.json +871 -0
  203. package/resources/programming/dos_error_handling.md +85 -0
  204. package/resources/programming/toc.json +51 -36
  205. package/resources/programming/vdp_commands_speed.md +147 -0
  206. package/resources/programming/vdp_programming_faq.md +55 -0
  207. package/resources/programming/vdp_programming_tutorial.md +390 -0
  208. package/resources/programming/vdp_screensplit_programming_guide.md +166 -0
  209. package/resources/programming/vdp_scrolling_on_msx.md +124 -0
  210. package/resources/programming/vdp_the_yjk_screen_modes.md +227 -0
  211. package/resources/programming/vdp_v9938_vram_timings.md +539 -0
  212. package/resources/programming/vdp_v9938_vram_timings_part_2.md +281 -0
  213. package/resources/sdcc/toc.json +1 -1
  214. package/vector-db/index.json +1 -0
  215. /package/resources/msx-unapi/{Ethernet_UNAPI_specification_1.1.md → Ethernet_UNAPI_specification_1_1.md} +0 -0
  216. /package/resources/msx-unapi/{MSX_UNAPI_specification_1.1.md → MSX_UNAPI_specification_1_1.md} +0 -0
@@ -0,0 +1,487 @@
1
+ # Multiplications, divisions and shifts
2
+
3
+ Because the Z80 does not have a built-in multiplication instructions, when a programmer wants to do a multiplication he has to it manually. The programmer might take a multiplication routine from a book or a magazine, which may not necessarily be optimized for the specific case (or optimized at all), or he might even uses a routine that works by adding the same value n times, which is pretty much the worst solution to the problem.
4
+
5
+ This article presents you with optimized methods for multiplication. First it introduces you to multiplications using shifts, which are very fast and can be used if one of the multiplication parameters is a fixed number. Next, it provides a number of optimized generic multiplication and division routines for various bit-depths, and even a square root routine.
6
+
7
+ - [8-bit multiplications using shifts](#8-bit-multiplications-using-shifts)
8
+ - [8-bit divisions using shifts](#8-bit-divisions-using-shifts)
9
+ - [16-bit shifts](#16-bit-shifts)
10
+ - [Fast generic multiplication routines](#fast-generic-multiplication-routines)
11
+ - [Left-rotating multiplication](#left-rotating-multiplication)
12
+ - [Right-rotating multiplication](#right-rotating-multiplication)
13
+ - [Unrolled left-rotating multiplication](#unrolled-left-rotating-multiplication)
14
+ - [Fast generic division routines](#fast-generic-division-routines)
15
+ - [Division by 9](#division-by-9)
16
+ - [Square root routine](#square-root-routine)
17
+
18
+ ## 8-bit multiplications using shifts
19
+
20
+ When you shift a register 1 bit to the left, you multiply the value of the register with 2. This shifting can be done using the SLA r instruction. By doing several shifts in sequence you can very easily multiply by any power of 2. For example:
21
+
22
+ ```assembly
23
+ ld b,3 ; Multiply 3 with 4
24
+ sla b ; x4
25
+ sla b ; result: b = 12
26
+ ```
27
+
28
+ If you use register A you can multiply faster by using the ADD A,A instruction, which is 5 T-states per instruction instead of 8. So ADD A,A is exactly the same as SLA A, or a multiplication by two. On a sidenote, instead of using ADD A,A, you can also use RLCA, which effectively behaves the same.
29
+
30
+ ```assembly
31
+ ld a,15 ; Multiply 15 with 8
32
+ add a,a ; x8
33
+ add a,a
34
+ add a,a ; result: a = 120
35
+ ```
36
+
37
+ When programming multiplications you must always make sure the result will never exeed 255, in other words a carry may not occur. In the case of that happening, RLCA actually acts different from SLA A or ADD A,A (in some cases more usable, in some cases less usable). But generally that isn’t of any concern because when register A overflows the result will generally not be of much use anymore.
38
+
39
+ If you want to multiply by another value than a power of two, you can almost always achieve the desired result by storing inbetween values during the shifting and adding or subtracting them up afterwards. A few examples:
40
+
41
+ ```assembly
42
+ ld a,5 ; Multiply 5 with 20 (= A x 16 + A x 4)
43
+ add a,a ; x16
44
+ add a,a
45
+ ld b,a ; Store value of A x 4 in B
46
+ add a,a
47
+ add a,a
48
+ add a,b ; Add A x 4 to A x 16, result: a = 100
49
+ ```
50
+
51
+ If you would want to multiply with 22, you would also save the value after the first add in a register and add it to the total afterwards.
52
+
53
+ Sometimes, you can also use substractions to achieve your goals faster. For example, the multiplication of A with 15. This can be done by using the method described above, however, in that case you’ll need 4 temporary registers, and four additional adds afterwards. This could better be done as follows, which requires 1 more multiplication but only uses 1 temporary register and 1 subtraction afterwards:
54
+
55
+ ```assembly
56
+ ld a,3 ; Multiply 3 with 15 (= A x 16 - A x 1)
57
+ ld b,a ; Store value of A x 1 in B
58
+ add a,a ; x16
59
+ add a,a
60
+ add a,a
61
+ add a,a
62
+ sub b ; result: a = 45
63
+ ```
64
+
65
+ ## 8-bit divisions using shifts
66
+
67
+ Now, divisions are very much like multiplications. If a multiplication routine is complex, a division routine is even more so. However, using shifts it’s all too easy to divide in Assembly. It is done by simply shifting the other way, to the right. For this, you should use the SRL r instruction. An example:
68
+
69
+ ```assembly
70
+ ld b,3 ; Divide 18 by 4
71
+ srl b ; x4
72
+ srl b ; result: b = 4 (rest 2 is lost)
73
+ ```
74
+
75
+ There is no real fast alternative to a shift right when using register A. As an alternative, you can use RRCA for that. However, when using RRCA you must make sure there will be no rest, otherwise the result won’t be correct. This can be achieved by ANDing the original value with a value clearing the lower bits (which would otherwise be rotated out), or by making sure you are using values which are always a multiple of the divider.
76
+
77
+ ```assembly
78
+ ld a,153 ; Divide 153 by 8
79
+ and a,%11111000 ; Clear bits 0-2 (equals 256 - 8)
80
+ rrca ; /8
81
+ rrca
82
+ rrca ; result: a = 19
83
+ ```
84
+
85
+ Dividing with values other than powers of 2 is trickier and is often not possible. If you want to see for yourself, try to construct a routine which divides 100 by 20. To know if a value can be divided, you must look at the amount of trailing zeroes in the binary representation of that value. The maximum number of RRCA’s you can use is then equal to that number. If you look at the beforementioned value 100 in binary (%01100100), you will see there are two trailing zeroes. Therefor, the division routine can only use 2 RRCA’s, while dividing by 20 needs 4 of them.
86
+
87
+ To be rather blunt about it, unless you have very tight control over the values which are given as a parameter, it is hardly possible to divide by values which are not a power of 2. Using a method like with multiplications, dividing by 3 for example is ofcourse possible, but only if all input values are even and all multiples of 3. If either of these conditions is not met, the result will be flawd.
88
+
89
+ ## 16-bit shifts
90
+
91
+ There are also ways to shift 16-bit registers. This is done using a 8-bit shift in combination with an 8-bit rotate and the carry bit. To shift a register DE one bit to the left you should use:
92
+
93
+ ```assembly
94
+ sla e
95
+ rl d
96
+ ```
97
+
98
+ To shift it one bit to the right (now with BC as example), use:
99
+
100
+ ```assembly
101
+ srl b
102
+ rr c
103
+ ```
104
+
105
+ Unfortunately, generally those 16-bit shifts are rather slow compared to 8-bit shifts (which will most often take place in the fast A register, which makes them almost 4 times as fast). However, just as with the 8-bit shifts, there is also the possibility to do faster 16-bit shifts to the left using the ADD instruction.
106
+
107
+ ```assembly
108
+ add hl,hl ; shift HL 1 bit left... hl = hl x 2
109
+ ```
110
+
111
+ So, the best way to multiply 16-bit values is by using register HL in combination with `ADD HL,HL` instructions.
112
+
113
+ ## Fast generic multiplication routines
114
+
115
+ Well, that’s about everything there is to tell about shifting and multiplications / divisions. There are a few more minor tricks, but really all I can say about that is to just be a little creative.
116
+
117
+ Finally I’ll give you the as far as I know fastest generic multiplication and division routines possible. These are the routines you should use when:
118
+ - both multiplication/division parameters are unknown.
119
+ - if you want to divide by another value than a power of 2 or need the rest value.
120
+ - if the known value is such a value that it can not feasibly be calculated using the methods described above.
121
+
122
+ You can also use them if you want your code to look really tidy and don’t really care about the speed. As for the inner workings of them, all I will say about it is that they work much like how you learned to solve multiplications and divisions in elementary school. But that really is of no importance. Just copy them into your program as (unrolled) subroutines or, if you feel like removing any notion of structured programming, as inline code or a macro.
123
+
124
+ Oh, and if you really have very low variable multiplication factors, you might want to use the dreaded multiply-by-adding-x-times-loop method. It’s probably faster.
125
+
126
+ ### Left-rotating multiplication
127
+
128
+ These are left-rotating multiplication routines. Their speed is basically quite constant, although depending on the number of 1’s in the primary multiplier there may be a slight difference in speed (a 1 usually takes 7 states longer to process than a 0).
129
+
130
+ ```assembly
131
+ ;
132
+ ; Multiply 8-bit values
133
+ ; In: Multiply H with E
134
+ ; Out: HL = result
135
+ ;
136
+ Mult8:
137
+ ld d,0
138
+ ld l,d
139
+ ld b,8
140
+ Mult8_Loop:
141
+ add hl,hl
142
+ jr nc,Mult8_NoAdd
143
+ add hl,de
144
+ Mult8_NoAdd:
145
+ djnz Mult8_Loop
146
+ ret
147
+ ```
148
+
149
+ ```assembly
150
+ ;
151
+ ; Multiply 8-bit value with a 16-bit value
152
+ ; In: Multiply A with DE
153
+ ; Out: HL = result
154
+ ;
155
+ Mult12:
156
+ ld l,0
157
+ ld b,8
158
+ Mult12_Loop:
159
+ add hl,hl
160
+ add a,a
161
+ jr nc,Mult12_NoAdd
162
+ add hl,de
163
+ Mult12_NoAdd:
164
+ djnz Mult12_Loop
165
+ ret
166
+ ```
167
+
168
+ ```assembly
169
+ ;
170
+ ; Multiply 16-bit values (with 16-bit result)
171
+ ; In: Multiply BC with DE
172
+ ; Out: HL = result
173
+ ;
174
+ Mult16:
175
+ ld a,b
176
+ ld b,16
177
+ Mult16_Loop:
178
+ add hl,hl
179
+ sla c
180
+ rla
181
+ jr nc,Mult16_NoAdd
182
+ add hl,de
183
+ Mult16_NoAdd:
184
+ djnz Mult16_Loop
185
+ ret
186
+ ```
187
+
188
+ ```assembly
189
+ ;
190
+ ; Multiply 16-bit values (with 32-bit result)
191
+ ; In: Multiply BC with DE
192
+ ; Out: BCHL = result
193
+ ;
194
+ Mult32:
195
+ ld a,c
196
+ ld c,b
197
+ ld hl,0
198
+ ld b,16
199
+ Mult32_Loop:
200
+ add hl,hl
201
+ rla
202
+ rl c
203
+ jr nc,Mult32_NoAdd
204
+ add hl,de
205
+ adc a,0
206
+ jp nc,Mult32_NoAdd
207
+ inc c
208
+ Mult32_NoAdd:
209
+ djnz Mult32_Loop
210
+ ld b,c
211
+ ld c,a
212
+ ret
213
+ ```
214
+
215
+ ### Right-rotating multiplication
216
+
217
+ Right-rotating multiplications are basically not very different from left-rotating multiplications. They use a very similar process, and the number of steps in the calculation is equal. However on the Z80 left-rotating multiplications can be coded much faster and compacter, which is why all the previous routines were using the left rotating variants.
218
+
219
+ However there is one rather nice advantage to right-rotating divisions, which is that it only has to loop until there are no bits left, after that it can be terminated without any further operations. So if a value actually only uses 4 bits (for example the number 11) the routine only has to loop 4 times and can be terminated afterwards by using a fast conditional check, so there is barely any extra effort involved in this potentially great speedup.
220
+
221
+ Looking at the table below with speed calculations of Mult12R routine and given the fact that the optimal left-rotating Mult12 (unrolled) takes 268 T-states (M1 states included, basically meaning 1 additional state for each instruction) you can see that in the efficiency boundary is at 4 bits. Above that, a normal Mult12 is faster. However, a 4-bit Mult12 will ofcourse be the faster one again, so to take advantage of this the majority but not all of the values should fall within the 4 bit range.
222
+
223
+ How long an n-bit multiplication takes (on average):
224
+ - 1-bit: 78.5 T-states
225
+ - 2-bit: 140 T-states
226
+ - 3-bit: 201.5 T-states
227
+ - 4-bit: 263 T-states
228
+ - 5-bit: 324.5 T-states
229
+ - 6-bit: 386 T-states
230
+ - 7-bit: 447.5 T-states
231
+ - 8-bit: 509 T-states
232
+
233
+ It is important to note that this may seem fast, but do not forget that the number of bits and the possible values are related to eachother on a logarithmic scale. So if you use random byte values, the number of values within the 4 bit range is only 1 out of every 16 values. To indicate a little better what this means, take a look at the following table which specifies the average speed you can expect for a random value within a certain domain.
234
+
235
+ Average speeds in the specified domain:
236
+
237
+ |||
238
+ |:-:|--:|
239
+ |<0,255>|447.980 T-states|
240
+ |<0,127>|386.961 T-states|
241
+ |<0,63>|326.422 T-states|
242
+ |<0,31>|266.844 T-states|
243
+ |<0,15>|209.188 T-states|
244
+ |<0,7>|155.375 T-states|
245
+ |<0,3>|109.25 T-states|
246
+ |<0,1>|78.5 T-states|
247
+
248
+ So, concluding, if you make a large number of multiplications of which one of the values is primarily smaller than 6 bits, for example if your data is logarithmic, using a right-rotating algorithm would be the fastest choice. To give a real-world usage example, this routine might for example be applicable to calculations on frequency tables within a music replayer, which are set on a logarithmic scale.
249
+
250
+ Well, here is the actual routine. Note that it can easily be converted to a Mult8R-routine by inserting an ld d,0 instruction right after the ld hl,0, and also that unrolling this routine will not give you additional speed. Oh, and by the way, although I’ve been talking about right-rotating all the time, this implementation is actually right-shifting ^_^.
251
+
252
+ ```assembly
253
+ ;
254
+ ; Multiply 8-bit value with a 16-bit value (right rotating)
255
+ ; In: Multiply A with DE
256
+ ; Put lowest value in A for most efficient calculation
257
+ ; Out: HL = result
258
+ ;
259
+ Mult12R:
260
+ ld hl,0
261
+ Mult12R_Loop:
262
+ srl a
263
+ jr nc,Mult12R_NoAdd
264
+ add hl,de
265
+ Mult12R_NoAdd:
266
+ sla e
267
+ rl d
268
+ or a
269
+ jp nz,Mult12R_Loop
270
+ ret
271
+ ```
272
+
273
+ ### Unrolled left-rotating multiplication
274
+
275
+ For completeness’ sake, here also an example of an unrolled left-rotating multiply routine. It takes a little more space, but is significantly faster. Actually it’s still quite compact, only 41 bytes. This one takes 268 T-states on average (M1 waits included). The minimum is 235 states, and the maximum is 301 ticks.
276
+
277
+ By the way, I looked at the possibility to use the same technique as with the right-rotating multiplication on this routine, it can fairly easy be done by putting jumps inbetween which jump into a list of add hl,hl’s. However, loss in speed the additional jumps cause don’t outweigh the gain, and it is hardly practical anyway since it would apply to the nr. of bits used from the left (the number 128 would use 1 bit, and 64 two).
278
+
279
+ ```assembly
280
+ ;
281
+ ; Multiply 8-bit value with a 16-bit value (unrolled)
282
+ ; In: Multiply A with DE
283
+ ; Out: HL = result
284
+ ;
285
+ Mult12U:
286
+ ld l,0
287
+ add a,a
288
+ jr nc,Mult12U_NoAdd0
289
+ add hl,de
290
+ Mult12U_NoAdd0:
291
+ add hl,hl
292
+ add a,a
293
+ jr nc,Mult12U_NoAdd1
294
+ add hl,de
295
+ Mult12U_NoAdd1:
296
+ add hl,hl
297
+ add a,a
298
+ jr nc,Mult12U_NoAdd2
299
+ add hl,de
300
+ Mult12U_NoAdd2:
301
+ add hl,hl
302
+ add a,a
303
+ jr nc,Mult12U_NoAdd3
304
+ add hl,de
305
+ Mult12U_NoAdd3:
306
+ add hl,hl
307
+ add a,a
308
+ jr nc,Mult12U_NoAdd4
309
+ add hl,de
310
+ Mult12U_NoAdd4:
311
+ add hl,hl
312
+ add a,a
313
+ jr nc,Mult12U_NoAdd5
314
+ add hl,de
315
+ Mult12U_NoAdd5:
316
+ add hl,hl
317
+ add a,a
318
+ jr nc,Mult12U_NoAdd6
319
+ add hl,de
320
+ Mult12U_NoAdd6:
321
+ add hl,hl
322
+ add a,a
323
+ ret nc
324
+ add hl,de
325
+ ret
326
+ ```
327
+
328
+ It is also worthwhile to unroll the other multiplication routines. For the Mult8 routine for example, it saves 115 out of 367 T-states on average, that’s 31% faster, at the cost of just 38 bytes (14 → 52).
329
+
330
+ ## Fast generic division routines
331
+
332
+ Before we get to the actual division routines, when dividing you don’t necessarily need to use the (slower) division routines. Don’t forget, multiplications and divisions are related. If you want to divide by 2, you can also multiply with 0.5. Translating this to these routines, to divide an 8-bit value by another 8-bit value (like Div8 does), you can call a Mult8 routine with parameter 1 being (1 / the first 8-bit value * 256) and parameter 2 being the other 8-bit value. The resulting word will be a fixed-point hexadecimal value, with the comma laying between the high and the low byte.
333
+
334
+ For example, calculate 55 / 11:
335
+
336
+ ```
337
+ Input A: #18 (1/11*256 = 23.272727, rounded up to 24)
338
+ Input B: #37 (55)
339
+ Output: #528 (#5.28 or 5,15625 decimal)
340
+ ```
341
+
342
+ Note that the output isn’t exactly the correct value (which should have been #500), this is because 1/11 is actually not a very nice number, both in decimal notation (.090909) as in hexadecimal notation (#.1745D1). If we hadn’t rounded up the value to #.18 but to #.17 which would be proper rounding, the result would have been #4F1. This would have been more exact than the current result, however our goal is to write optimum code, and to distill the correctly rounded whole number from this result (being 5) would be much easier when we have #528 as a result, because we could then simply take just the upper byte. Also the ‘proper rounding’ would require some additional code. This is why we rounded up the value.
343
+
344
+ If you take a base value too large, the error accumulates. If you for example try to divide 2200 by 11, the result will be 206, while it should have been 200. To solve this problem you can either increase the resolution (use a 16-bit division value (#.1746) and a 24- or 32-bit result) or divide by values which are ‘tidy’ in hexadecimal notation (being the powers of 2). However also remember that no matter which base you use, be it 10 or 16, you will always have divisions which produce errors. The values with which this happens differ, but you will have to deal with resolution limitations and rounding.
345
+
346
+ Anyways. These are the general division routines. Slower than the multiplication routines but still as fast as possible, and probably very useful.
347
+
348
+ ```assembly
349
+ ;
350
+ ; Divide 8-bit values
351
+ ; In: Divide E by divider C
352
+ ; Out: A = result, B = rest
353
+ ;
354
+ Div8:
355
+ xor a
356
+ ld b,8
357
+ Div8_Loop:
358
+ rl e
359
+ rla
360
+ sub c
361
+ jr nc,Div8_NoAdd
362
+ add a,c
363
+ Div8_NoAdd:
364
+ djnz Div8_Loop
365
+ ld b,a
366
+ ld a,e
367
+ rla
368
+ cpl
369
+ ret
370
+ ```
371
+
372
+ ```assembly
373
+ ;
374
+ ; Divide 16-bit values (with 16-bit result)
375
+ ; In: Divide BC by divider DE
376
+ ; Out: BC = result, HL = rest
377
+ ;
378
+ Div16:
379
+ ld hl,0
380
+ ld a,b
381
+ ld b,8
382
+ Div16_Loop1:
383
+ rla
384
+ adc hl,hl
385
+ sbc hl,de
386
+ jr nc,Div16_NoAdd1
387
+ add hl,de
388
+ Div16_NoAdd1:
389
+ djnz Div16_Loop1
390
+ rla
391
+ cpl
392
+ ld b,a
393
+ ld a,c
394
+ ld c,b
395
+ ld b,8
396
+ Div16_Loop2:
397
+ rla
398
+ adc hl,hl
399
+ sbc hl,de
400
+ jr nc,Div16_NoAdd2
401
+ add hl,de
402
+ Div16_NoAdd2:
403
+ djnz Div16_Loop2
404
+ rla
405
+ cpl
406
+ ld b,c
407
+ ld c,a
408
+ ret
409
+ ```
410
+
411
+ Thanks to Flyguille for the Div16 routine, taken from his MNBIOS source code.
412
+
413
+ The division routines can be unrolled as well to gain a nice speed increase. If you take the Div16 routine for example, it takes 1297 T-states to complete. When unrolled however, it only needs 1070 T-states, that’s an 18% speed increase. The additional cost in bytes is 146 (original routine is 27 bytes).
414
+
415
+ ### Division by 9
416
+ Ricardo Bittencourt provided us with a fast routine for division by 9. It is built for .dsk or Disk ROM routines. It’s very fast, but only works in the range 0-1440.
417
+
418
+ ```assembly
419
+ ;
420
+ ; division by nine
421
+ ; enter HL = number from 0 to 1440
422
+ ; exit A = HL/9
423
+ ; destroy HL,DE
424
+ ; Z80 R800
425
+ DIV9: INC HL ; 7 1
426
+ LD D,H ; 5 1
427
+ LD E,L ; 5 1
428
+ ADD HL,HL ; 12 1
429
+ ADD HL,HL ; 12 1
430
+ ADD HL,HL ; 12 1
431
+ SBC HL,DE ; 17 2
432
+ LD E,0 ; 8 2
433
+ LD D,L ; 5 1
434
+ LD A,H ; 5 1
435
+ ADD HL,HL ; 12 1
436
+ ADD HL,HL ; 12 1
437
+ ADD HL,DE ; 12 1
438
+ ADC A,E ; 5 1
439
+ XOR H ; 5 1
440
+ AND 03FH ; 8 2
441
+ XOR H ; 5 1
442
+ RLCA ; 5 1
443
+ RLCA ; 5 1
444
+ RET ; total = 157 22
445
+ ```
446
+
447
+ ## Square root routine
448
+
449
+ This is a faster square root routine than the one that was previously here, test results say that it’s 26% faster. It is written by Ricardo Bittencourt, so many thanks to him :).
450
+
451
+ ```assembly
452
+ ;
453
+ ; Square root of 16-bit value
454
+ ; In: HL = value
455
+ ; Out: D = result (rounded down)
456
+ ;
457
+ Sqr16:
458
+ ld de,#0040
459
+ ld a,l
460
+ ld l,h
461
+ ld h,d
462
+ or a
463
+ ld b,8
464
+ Sqr16_Loop:
465
+ sbc hl,de
466
+ jr nc,Sqr16_Skip
467
+ add hl,de
468
+ Sqr16_Skip:
469
+ ccf
470
+ rl d
471
+ add a,a
472
+ adc hl,hl
473
+ add a,a
474
+ adc hl,hl
475
+ djnz Sqr16_Loop
476
+ ret
477
+ ```
478
+
479
+ Well, that’s it. Thanks go to mr. Rodnay Zaks :), for writing his book "Programming of the Z80", which taught me about how to do multiplications on Z80, and has been the inspiration for the routines listed here. If you have any suggestions for speed improvements, or know of another method which might be faster under certain conditions, please let me know.
480
+
481
+ ~Grauw
482
+
483
+
484
+
485
+ ## Source
486
+
487
+ https://map.grauw.nl/articles/mult_div_shifts.php
@@ -0,0 +1,56 @@
1
+ # How to switch RAM/ROM in page 1
2
+
3
+ One of the problems when programming in a Basic environment is that it hasn’t exactly got a very large amount of RAM space available. One of the possible solutions for this problem is to switch away the Basic ROM in page 1 (the area from #4000-#7FFF), which isn’t used while executing machine code anyway, and replace it with RAM. This results in 16 kilobytes of additional addressing space.
4
+
5
+ First of all, this routine makes use of one of the System RAM variables which are initialized by the Disk BIOS, RAMAD1 to be specific. If this Disk BIOS is not available (for example on MSX computers without diskdrive), the variable will not be initialized. Therefor, one should officially always check on beforehand whether a Disk BIOS is present by comparing the byte at address H.PHYD (#FFA7) with #C9. If that value is there, the Disk BIOS is NOT present, so these routines can not be used. Ofcourse when you are a 100% certain a Disk BIOS is present, the check is not necessary, which is pretty much the case for every non-MSX1 program.
6
+
7
+ You can use this method to switch RAM:
8
+
9
+ ```assembly
10
+ ENASLT: EQU #0024
11
+ RAMAD1: EQU #F342
12
+ EXPTBL: EQU #FCC1
13
+
14
+ Enable_RAM: ld a,(RAMAD1)
15
+ ld h,#40
16
+ call ENASLT
17
+ ```
18
+
19
+ There is a slightly better method to switch RAM, by selecting the same slot in page 1 as you have in page 3, which will always be the system RAM. This will work on any MSX, even without DiskROM, provided there is at least 48kB of RAM available ofcourse.
20
+
21
+ It can be done as follows:
22
+
23
+ ```assembly
24
+ Enable_RAM2: ld a,(EXPTBL+3)
25
+ ld b,a ;check if slot is expanded
26
+ and a
27
+ jp z,Ena_RAM2_jp
28
+ ld a,(#FFFF) ;if so, read subslot value first
29
+ cpl ;complement value
30
+ and %11000000
31
+ rrca ;shift subslot bits to bits 2-3
32
+ rrca
33
+ rrca
34
+ rrca
35
+ or b
36
+ ld b,a
37
+ Ena_RAM2_jp: in a,(#A8) ;read slot value
38
+ and %11000000 ;shift slot bits to bits 0-1
39
+ rlca
40
+ rlca
41
+ or b
42
+ ld h,#40 ;select slot
43
+ call ENASLT
44
+ And, before returning to Basic, don’t forget to switch back the Basic ROM:
45
+
46
+ Enable_ROM: ld a,(EXPTBL)
47
+ ld h,#40
48
+ call ENASLT
49
+ ```
50
+
51
+ ~Grauw
52
+
53
+
54
+ ## Source
55
+
56
+ https://map.grauw.nl/sources/raminpage1.php
@@ -0,0 +1,78 @@
1
+ # Detecting VDP version
2
+
3
+ Here’s a routine to help you detect the VDP version. It takes about a frame to complete, so it is recommended to do it once at the start of the program.
4
+
5
+ ```assembly
6
+ ;
7
+ ; Detect VDP version
8
+ ;
9
+ ; a <- 0: TMS9918A, 1: V9938, 2: V9958, x: VDP ID
10
+ ; f <- z: TMS9918A, nz: other
11
+ ;
12
+ VDP_GetVersion:
13
+ call VDP_IsTMS9918A ; use a different way to detect TMS9918A
14
+ ret z
15
+ ld a,1 ; select s#1
16
+ di
17
+ out (99H),a
18
+ ld a,15 + 128
19
+ out (99H),a
20
+ in a,(99H) ; read s#1
21
+ and 00111110B ; get VDP ID
22
+ rrca
23
+ ex af,af'
24
+ xor a ; select s#0 as required by BIOS
25
+ out (99H),a
26
+ ld a,15 + 128
27
+ ei
28
+ out (99H),a
29
+ ex af,af'
30
+ ret nz ; return VDP ID for V9958 or higher
31
+ inc a ; return 1 for V9938
32
+ ret
33
+
34
+ ;
35
+ ; Test if the VDP is a TMS9918A.
36
+ ;
37
+ ; The VDP ID number was only introduced in the V9938, so we have to use a
38
+ ; different method to detect the TMS9918A. We wait for the vertical blanking
39
+ ; interrupt flag, and then quickly read status register 2 and expect bit 6
40
+ ; (VR, vertical retrace flag) to be set as well. The TMS9918A has only one
41
+ ; status register, so bit 6 (5S, 5th sprite flag) will return 0 in stead.
42
+ ;
43
+ ; f <- z: TMS9918A, nz: V99X8
44
+ ;
45
+ VDP_IsTMS9918A:
46
+ in a,(99H) ; read s#0, make sure interrupt flag (F) is reset
47
+ di
48
+ VDP_IsTMS9918A_Wait:
49
+ in a,(99H) ; read s#0
50
+ and a ; wait until interrupt flag (F) is set
51
+ jp p,VDP_IsTMS9918A_Wait
52
+ ld a,2 ; select s#2 on V9938
53
+ out (99H),a
54
+ ld a,15 + 128 ; (this mirrors to r#7 on TMS9918 VDPs)
55
+ out (99H),a
56
+ in a,(99H) ; read s#2 / s#0
57
+ ex af,af'
58
+ xor a ; select s#0 as required by BIOS
59
+ out (99H),a
60
+ ld a,15 + 128
61
+ out (99H),a
62
+ ld a,(0F3E6H) ; RG7SAV
63
+ out (99H),a ; restore r#7 if it mirrored (small flash visible)
64
+ ld a,7 + 128
65
+ ei
66
+ out (99H),a
67
+ ex af,af'
68
+ and 01000000B ; check if bit 6 was 0 (s#0 5S) or 1 (s#2 VR)
69
+ ret
70
+ ```
71
+
72
+ _Note: As you may know, the MSX VDPs have a race condition in the design of the F, FH and FL interrupt flags, which makes polling unreliable since the flag can be missed occasionally. If that situation occurs the detection will take a frame longer to complete, but still provide the correct result._
73
+
74
+ ~~Grauw
75
+
76
+ ## Source
77
+
78
+ https://map.grauw.nl/sources/vdp_detection.php