Mirror of CollapseOS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

306 lines
6.5KB

  1. ; *** Requirements ***
  2. ; lib/util
  3. ; *** Code ***
  4. ; Parse the hex char at A and extract it's 0-15 numerical value. Put the result
  5. ; in A.
  6. ;
  7. ; On success, the carry flag is reset. On error, it is set.
  8. parseHex:
  9. ; First, let's see if we have an easy 0-9 case
  10. add a, 0xc6 ; maps '0'-'9' onto 0xf6-0xff
  11. sub 0xf6 ; maps to 0-9 and carries if not a digit
  12. ret nc
  13. and 0xdf ; converts lowercase to uppercase
  14. add a, 0xe9 ; map 0x11-x017 onto 0xFA - 0xFF
  15. sub 0xfa ; map onto 0-6
  16. ret c
  17. ; we have an A-F digit
  18. add a, 10 ; C is clear, map back to 0xA-0xF
  19. ret
  20. ; Parses 2 characters of the string pointed to by HL and returns the numerical
  21. ; value in A. If the second character is a "special" character (<0x21) we don't
  22. ; error out: the result will be the one from the first char only.
  23. ; HL is set to point to the last char of the pair.
  24. ;
  25. ; On success, the carry flag is reset. On error, it is set.
  26. parseHexPair:
  27. push bc
  28. ld a, (hl)
  29. call parseHex
  30. jr c, .end ; error? goto end, keeping the C flag on
  31. rla \ rla \ rla \ rla ; let's push this in MSB
  32. ld b, a
  33. inc hl
  34. ld a, (hl)
  35. cp 0x21
  36. jr c, .single ; special char? single digit
  37. call parseHex
  38. jr c, .end ; error?
  39. or b ; join left-shifted + new. we're done!
  40. ; C flag was set on parseHex and is necessarily clear at this point
  41. jr .end
  42. .single:
  43. ; If we have a single digit, our result is already stored in B, but
  44. ; we have to right-shift it back.
  45. ld a, b
  46. and 0xf0
  47. rra \ rra \ rra \ rra
  48. dec hl
  49. .end:
  50. pop bc
  51. ret
  52. ; Parse the decimal char at A and extract it's 0-9 numerical value. Put the
  53. ; result in A.
  54. ;
  55. ; On success, the carry flag is reset. On error, it is set.
  56. ; Also, zero flag set if '0'
  57. ; parseDecimalDigit has been replaced with the following code inline:
  58. ; add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff
  59. ; sub 0xff-9 ; maps to 0-9 and carries if not a digit
  60. ; Parse string at (HL) as a decimal value and return value in IX under the
  61. ; same conditions as parseLiteral.
  62. ; Sets Z on success, unset on error.
  63. ; To parse successfully, all characters following HL must be digits and those
  64. ; digits must form a number that fits in 16 bits. To end the number, both \0
  65. ; and whitespaces (0x20 and 0x09) are accepted. There must be at least one
  66. ; digit in the string.
  67. parseDecimal:
  68. push hl
  69. ld a, (hl)
  70. add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff
  71. sub 0xff-9 ; maps to 0-9 and carries if not a digit
  72. jr c, .error ; not a digit on first char? error
  73. exx ; preserve bc, hl, de
  74. ld h, 0
  75. ld l, a ; load first digit in without multiplying
  76. ld b, 3 ; Carries can only occur for decimals >=5 in length
  77. .loop:
  78. exx
  79. inc hl
  80. ld a, (hl)
  81. exx
  82. ; inline parseDecimalDigit
  83. add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff
  84. sub 0xff-9 ; maps to 0-9 and carries if not a digit
  85. jr c, .end
  86. add hl, hl ; x2
  87. ld d, h
  88. ld e, l ; de is x2
  89. add hl, hl ; x4
  90. add hl, hl ; x8
  91. add hl, de ; x10
  92. ld d, 0
  93. ld e, a
  94. add hl, de
  95. jr c, .end ; if hl was 0x1999, it may carry here
  96. djnz .loop
  97. inc b ; so loop only executes once more
  98. ; only numbers >0x1999 can carry when multiplied by 10.
  99. ld de, 0xE666
  100. ex de, hl
  101. add hl, de
  102. ex de, hl
  103. jr nc, .loop ; if it doesn't carry, it's small enough
  104. exx
  105. inc hl
  106. ld a, (hl)
  107. exx
  108. add a, 0xd0 ; the next line expects a null to be mapped to 0xd0
  109. .end:
  110. ; Because of the add and sub in parseDecimalDigit, null is mapped
  111. ; to 0x00+(0xff-'9')-(0xff-9)=-0x30=0xd0
  112. sub 0xd0 ; if a is null, set Z
  113. ; a is checked for null before any errors
  114. push hl \ pop ix
  115. exx ; restore original de and bc
  116. pop hl
  117. ret z
  118. ; A is not 0? Ok, but if it's a space, we're happy too.
  119. jp isWS
  120. .error:
  121. pop hl
  122. jp unsetZ
  123. ; Parse string at (HL) as a hexadecimal value and return value in IX under the
  124. ; same conditions as parseLiteral.
  125. parseHexadecimal:
  126. call hasHexPrefix
  127. ret nz
  128. push hl
  129. push de
  130. ld d, 0
  131. inc hl ; get rid of "0x"
  132. inc hl
  133. call strlen
  134. cp 3
  135. jr c, .single
  136. cp 4
  137. jr c, .doubleShort ; 0x123
  138. cp 5
  139. jr c, .double ; 0x1234
  140. ; too long, error
  141. jr .error
  142. .double:
  143. call parseHexPair
  144. jr c, .error
  145. inc hl ; now HL is on first char of next pair
  146. ld d, a
  147. jr .single
  148. .doubleShort:
  149. ld a, (hl)
  150. call parseHex
  151. jr c, .error
  152. inc hl ; now HL is on first char of next pair
  153. ld d, a
  154. .single:
  155. call parseHexPair
  156. jr c, .error
  157. ld e, a
  158. cp a ; ensure Z
  159. jr .end
  160. .error:
  161. call unsetZ
  162. .end:
  163. push de \ pop ix
  164. pop de
  165. pop hl
  166. ret
  167. ; Sets Z if (HL) has a '0x' prefix.
  168. hasHexPrefix:
  169. ld a, (hl)
  170. cp '0'
  171. ret nz
  172. push hl
  173. inc hl
  174. ld a, (hl)
  175. cp 'x'
  176. pop hl
  177. ret
  178. ; Parse string at (HL) as a binary value (0b010101) and return value in IX.
  179. ; High IX byte is always clear.
  180. ; Sets Z on success.
  181. parseBinaryLiteral:
  182. call hasBinPrefix
  183. ret nz
  184. push bc
  185. push hl
  186. push de
  187. ld d, 0
  188. inc hl ; get rid of "0b"
  189. inc hl
  190. call strlen
  191. or a
  192. jr z, .error ; empty, error
  193. cp 9
  194. jr nc, .error ; >= 9, too long
  195. ; We have a string of 8 or less chars. What we'll do is that for each
  196. ; char, we rotate left and set the LSB according to whether we have '0'
  197. ; or '1'. Error out on anything else. C is our stored result.
  198. ld b, a ; we loop for "strlen" times
  199. ld c, 0 ; our stored result
  200. .loop:
  201. rlc c
  202. ld a, (hl)
  203. inc hl
  204. cp '0'
  205. jr z, .nobit ; no bit to set
  206. cp '1'
  207. jr nz, .error ; not 0 or 1
  208. ; We have a bit to set
  209. inc c
  210. .nobit:
  211. djnz .loop
  212. ld e, c
  213. cp a ; ensure Z
  214. jr .end
  215. .error:
  216. call unsetZ
  217. .end:
  218. push de \ pop ix
  219. pop de
  220. pop hl
  221. pop bc
  222. ret
  223. ; Sets Z if (HL) has a '0b' prefix.
  224. hasBinPrefix:
  225. ld a, (hl)
  226. cp '0'
  227. ret nz
  228. push hl
  229. inc hl
  230. ld a, (hl)
  231. cp 'b'
  232. pop hl
  233. ret
  234. ; Parse string at (HL) and, if it is a char literal, sets Z and return
  235. ; corresponding value in IX. High IX byte is always clear.
  236. ;
  237. ; A valid char literal starts with ', ends with ' and has one character in the
  238. ; middle. No escape sequence are accepted, but ''' will return the apostrophe
  239. ; character.
  240. parseCharLiteral:
  241. ld a, 0x27 ; apostrophe (') char
  242. cp (hl)
  243. ret nz
  244. push hl
  245. push de
  246. inc hl
  247. inc hl
  248. cp (hl)
  249. jr nz, .end ; not ending with an apostrophe
  250. inc hl
  251. ld a, (hl)
  252. or a ; cp 0
  253. jr nz, .end ; string has to end there
  254. ; Valid char, good
  255. ld d, a ; A is zero, take advantage of that
  256. dec hl
  257. dec hl
  258. ld a, (hl)
  259. ld e, a
  260. cp a ; ensure Z
  261. .end:
  262. push de \ pop ix
  263. pop de
  264. pop hl
  265. ret
  266. ; Parses the string at (HL) and returns the 16-bit value in IX. The string
  267. ; can be a decimal literal (1234), a hexadecimal literal (0x1234) or a char
  268. ; literal ('X').
  269. ;
  270. ; As soon as the number doesn't fit 16-bit any more, parsing stops and the
  271. ; number is invalid. If the number is valid, Z is set, otherwise, unset.
  272. parseLiteral:
  273. call parseCharLiteral
  274. ret z
  275. call parseHexadecimal
  276. ret z
  277. call parseBinaryLiteral
  278. ret z
  279. jp parseDecimal