microuiHelpers.mod 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. IMPLEMENTATION MODULE microuiHelpers;
  2. FROM SYSTEM IMPORT ADDRESS, CAST, ADDADR, BYTE;
  3. TYPE
  4. ADDRESSPTR = POINTER TO BYTE;
  5. (*========================================================================*)
  6. (* Bitwise operations *)
  7. (*========================================================================*)
  8. PROCEDURE BITAND(a, b : CARDINAL) : CARDINAL;
  9. VAR sa, sb : BITSET;
  10. BEGIN
  11. sa := CAST(BITSET, a);
  12. sb := CAST(BITSET, b);
  13. RETURN CAST(CARDINAL, sa * sb)
  14. END BITAND;
  15. PROCEDURE BITOR(a, b : CARDINAL) : CARDINAL;
  16. VAR sa, sb : BITSET;
  17. BEGIN
  18. sa := CAST(BITSET, a);
  19. sb := CAST(BITSET, b);
  20. RETURN CAST(CARDINAL, sa + sb)
  21. END BITOR;
  22. PROCEDURE BITXOR(a, b : CARDINAL) : CARDINAL;
  23. VAR sa, sb : BITSET;
  24. BEGIN
  25. sa := CAST(BITSET, a);
  26. sb := CAST(BITSET, b);
  27. RETURN CAST(CARDINAL, sa / sb)
  28. END BITXOR;
  29. PROCEDURE BITNOT(a : CARDINAL) : CARDINAL;
  30. BEGIN
  31. RETURN BITXOR(a, MAX(CARDINAL))
  32. END BITNOT;
  33. PROCEDURE BITLSL(a, n : CARDINAL) : CARDINAL;
  34. (* Logical shift left: returns a * 2^n, truncated to CARDINAL width. *)
  35. VAR result : CARDINAL;
  36. i : CARDINAL;
  37. BEGIN
  38. result := a;
  39. FOR i := 1 TO n DO
  40. result := result * 2
  41. END;
  42. RETURN result
  43. END BITLSL;
  44. PROCEDURE BITASR(a, n : CARDINAL) : CARDINAL;
  45. (* Arithmetic shift right: returns a DIV 2^n (unsigned). *)
  46. VAR result : CARDINAL;
  47. i : CARDINAL;
  48. BEGIN
  49. result := a;
  50. FOR i := 1 TO n DO
  51. result := result DIV 2
  52. END;
  53. RETURN result
  54. END BITASR;
  55. PROCEDURE HasFlag(val, flag : CARDINAL) : BOOLEAN;
  56. BEGIN
  57. RETURN CAST(BITSET, val) * CAST(BITSET, flag) # BITSET{}
  58. END HasFlag;
  59. PROCEDURE HasNoFlag(val, flag : CARDINAL) : BOOLEAN;
  60. BEGIN
  61. RETURN CAST(BITSET, val) * CAST(BITSET, flag) = BITSET{}
  62. END HasNoFlag;
  63. (*========================================================================*)
  64. (* String / memory utilities *)
  65. (*========================================================================*)
  66. PROCEDURE StrLen(s : ARRAY OF CHAR) : CARDINAL;
  67. VAR i : CARDINAL;
  68. BEGIN
  69. i := 0;
  70. WHILE (i <= HIGH(s)) AND (s[i] # 0C) DO
  71. i := i + 1
  72. END;
  73. RETURN i
  74. END StrLen;
  75. PROCEDURE CopyBytes(src, dst : ADDRESS; len : CARDINAL);
  76. VAR sp, dp : ADDRESSPTR;
  77. i : CARDINAL;
  78. BEGIN
  79. sp := CAST(ADDRESSPTR, src);
  80. dp := CAST(ADDRESSPTR, dst);
  81. IF len > 0 THEN
  82. FOR i := 0 TO len - 1 DO
  83. dp^ := sp^;
  84. sp := ADDADR(sp, 1);
  85. dp := ADDADR(dp, 1)
  86. END
  87. END
  88. END CopyBytes;
  89. (*========================================================================*)
  90. (* Integer -> real conversions (work around SHORTREAL(x) on integers) *)
  91. (*========================================================================*)
  92. PROCEDURE IntToReal(n : INTEGER) : SHORTREAL;
  93. VAR r : SHORTREAL;
  94. BEGIN
  95. r := FLOAT(n);
  96. RETURN r
  97. END IntToReal;
  98. PROCEDURE CardToReal(n : CARDINAL) : SHORTREAL;
  99. VAR r : SHORTREAL;
  100. BEGIN
  101. r := FLOAT(n);
  102. RETURN r
  103. END CardToReal;
  104. (*========================================================================*)
  105. (* Real-to-string formatting *)
  106. (*========================================================================*)
  107. PROCEDURE PutChar(VAR buf : ARRAY OF CHAR; VAR pos : CARDINAL; ch : CHAR);
  108. BEGIN
  109. IF pos <= HIGH(buf) THEN
  110. buf[pos] := ch
  111. END;
  112. pos := pos + 1
  113. END PutChar;
  114. PROCEDURE PutDigits(VAR buf : ARRAY OF CHAR; VAR pos : CARDINAL;
  115. n : CARDINAL);
  116. VAR tmp : ARRAY [0..19] OF CHAR;
  117. k, j : CARDINAL;
  118. BEGIN
  119. IF n = 0 THEN
  120. PutChar(buf, pos, '0');
  121. RETURN
  122. END;
  123. k := 0;
  124. WHILE n > 0 DO
  125. tmp[k] := CHR(ORD('0') + n MOD 10);
  126. k := k + 1;
  127. n := n DIV 10
  128. END;
  129. j := k;
  130. WHILE j > 0 DO
  131. j := j - 1;
  132. PutChar(buf, pos, tmp[j])
  133. END
  134. END PutDigits;
  135. PROCEDURE IntToStr(VAR buf : ARRAY OF CHAR; val : INTEGER) : CARDINAL;
  136. VAR pos : CARDINAL;
  137. BEGIN
  138. pos := 0;
  139. IF val < 0 THEN
  140. PutChar(buf, pos, '-');
  141. (* avoid literal negation: use 0 - val at runtime *)
  142. PutDigits(buf, pos, 0 - val)
  143. ELSE
  144. PutDigits(buf, pos, val)
  145. END;
  146. PutChar(buf, pos, 0C);
  147. RETURN pos - 1
  148. END IntToStr;
  149. PROCEDURE HexByte(VAR buf : ARRAY OF CHAR; val : CARDINAL) : CARDINAL;
  150. VAR pos : CARDINAL;
  151. i : CARDINAL;
  152. d : CARDINAL;
  153. BEGIN
  154. pos := 0;
  155. FOR i := 1 TO 0 BY -1 DO (* high nibble then low nibble *)
  156. d := val;
  157. IF i = 1 THEN
  158. d := d DIV 16
  159. END;
  160. d := d MOD 16;
  161. IF d < 10 THEN
  162. PutChar(buf, pos, CHR(ORD('0') + d))
  163. ELSE
  164. PutChar(buf, pos, CHR(ORD('A') + d - 10))
  165. END
  166. END;
  167. PutChar(buf, pos, 0C);
  168. RETURN 2
  169. END HexByte;
  170. PROCEDURE RealToStr(VAR buf : ARRAY OF CHAR; val : SHORTREAL; prec : INTEGER) : CARDINAL;
  171. (* Formats val into buf.
  172. prec >= 0: fixed-point with prec decimal places (rounded).
  173. prec < 0 : %g style with |prec| significant digits (clamped to 1..6).
  174. Returns number of characters written (excluding NUL). *)
  175. VAR pos : CARDINAL;
  176. v : SHORTREAL;
  177. m : SHORTREAL;
  178. whole : CARDINAL;
  179. frac : CARDINAL;
  180. scale : SHORTREAL;
  181. neg : BOOLEAN;
  182. i, digits, exp, decimals : INTEGER;
  183. pow, d, total : INTEGER;
  184. n, p : CARDINAL;
  185. BEGIN
  186. pos := 0;
  187. neg := val < 0.0;
  188. IF neg THEN v := 0.0 - val ELSE v := val END;
  189. IF prec >= 0 THEN
  190. (* fixed-point with `prec` decimals, rounded to nearest *)
  191. scale := 1.0;
  192. pow := 1;
  193. FOR i := 1 TO prec DO
  194. scale := scale * 10.0;
  195. pow := pow * 10
  196. END;
  197. total := TRUNC(v * scale + 0.5);
  198. whole := VAL(CARDINAL, total DIV pow);
  199. frac := VAL(CARDINAL, total MOD pow);
  200. IF neg THEN PutChar(buf, pos, '-') END;
  201. PutDigits(buf, pos, whole);
  202. IF prec > 0 THEN
  203. PutChar(buf, pos, '.');
  204. d := pow DIV 10;
  205. WHILE d > 1 DO
  206. IF frac < CARDINAL(d) THEN PutChar(buf, pos, '0') END;
  207. d := d DIV 10
  208. END;
  209. PutDigits(buf, pos, frac)
  210. END;
  211. PutChar(buf, pos, 0C);
  212. RETURN pos - 1
  213. END;
  214. (* %g style *)
  215. digits := 0 - prec;
  216. IF digits < 1 THEN digits := 1 END;
  217. IF digits > 6 THEN digits := 6 END;
  218. IF v = 0.0 THEN
  219. IF neg THEN PutChar(buf, pos, '-') END;
  220. PutChar(buf, pos, '0');
  221. PutChar(buf, pos, 0C);
  222. RETURN pos - 1
  223. END;
  224. (* exponent such that 10^exp <= v < 10^(exp+1) *)
  225. exp := 0;
  226. scale := 1.0;
  227. WHILE v >= scale * 10.0 DO
  228. scale := scale * 10.0;
  229. exp := exp + 1
  230. END;
  231. WHILE v < scale DO
  232. scale := scale / 10.0;
  233. exp := exp - 1
  234. END;
  235. IF (exp < -4) OR (exp >= digits) THEN
  236. (* scientific notation; the recursive call keeps the sign *)
  237. m := v / scale;
  238. IF neg THEN m := 0.0 - m END;
  239. n := RealToStr(buf, m, digits - 1);
  240. IF (n > 2) AND (buf[n - 1] = '0') THEN
  241. WHILE (n > 0) AND (buf[n - 1] = '0') DO n := n - 1 END;
  242. IF (n > 0) AND (buf[n - 1] = '.') THEN n := n - 1 END;
  243. buf[n] := 0C
  244. END;
  245. pos := n;
  246. PutChar(buf, pos, 'e');
  247. IF exp < 0 THEN
  248. PutChar(buf, pos, '-');
  249. PutDigits(buf, pos, CARDINAL(0 - exp))
  250. ELSE
  251. PutChar(buf, pos, '+');
  252. PutDigits(buf, pos, CARDINAL(exp))
  253. END;
  254. PutChar(buf, pos, 0C);
  255. RETURN pos - 1
  256. ELSE
  257. (* fixed notation with the right number of significant digits *)
  258. decimals := digits - 1 - exp;
  259. IF decimals < 0 THEN decimals := 0 END;
  260. n := RealToStr(buf, val, decimals);
  261. IF decimals > 0 THEN
  262. p := n;
  263. WHILE (p > 0) AND (buf[p - 1] = '0') DO p := p - 1 END;
  264. IF (p > 0) AND (buf[p - 1] = '.') THEN p := p - 1 END;
  265. buf[p] := 0C;
  266. n := p
  267. END;
  268. RETURN n
  269. END
  270. END RealToStr;
  271. (*========================================================================*)
  272. (* StrToReal: minimal strtod *)
  273. (*========================================================================*)
  274. PROCEDURE StrToReal(s : ARRAY OF CHAR; endptr : ADDRESS) : SHORTREAL;
  275. VAR i : CARDINAL;
  276. val : SHORTREAL;
  277. neg : BOOLEAN;
  278. dig : CARDINAL;
  279. haveDot : BOOLEAN;
  280. fracScale : SHORTREAL;
  281. BEGIN
  282. val := 0.0;
  283. neg := FALSE;
  284. i := 0;
  285. haveDot := FALSE;
  286. fracScale := 1.0;
  287. (* skip leading whitespace *)
  288. WHILE (i <= HIGH(s)) AND (s[i] = ' ') DO
  289. i := i + 1
  290. END;
  291. (* optional sign *)
  292. IF (i <= HIGH(s)) AND (s[i] = '-') THEN
  293. neg := TRUE;
  294. i := i + 1
  295. ELSIF (i <= HIGH(s)) AND (s[i] = '+') THEN
  296. i := i + 1
  297. END;
  298. (* integer part *)
  299. WHILE (i <= HIGH(s)) AND (s[i] >= '0') AND (s[i] <= '9') DO
  300. val := val * 10.0 + CardToReal(ORD(s[i]) - ORD('0'));
  301. i := i + 1
  302. END;
  303. (* fractional part *)
  304. IF (i <= HIGH(s)) AND (s[i] = '.') THEN
  305. haveDot := TRUE;
  306. i := i + 1;
  307. WHILE (i <= HIGH(s)) AND (s[i] >= '0') AND (s[i] <= '9') DO
  308. fracScale := fracScale / 10.0;
  309. val := val + fracScale * CardToReal(ORD(s[i]) - ORD('0'));
  310. i := i + 1
  311. END
  312. END;
  313. IF neg THEN
  314. val := 0.0 - val
  315. END;
  316. (* Note: endptr handling is simplified — we don't write to endptr
  317. because it's an ADDRESS and we can't reliably dereference it
  318. without knowing the target type. Callers that need endptr
  319. should inspect the string position themselves. *)
  320. RETURN val
  321. END StrToReal;
  322. END microuiHelpers.