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.

251 lines
5.4KB

  1. ; *** Consts ***
  2. .equ TOK_INSTR 0x01
  3. .equ TOK_DIRECTIVE 0x02
  4. .equ TOK_LABEL 0x03
  5. .equ TOK_EOF 0xfe ; end of file
  6. .equ TOK_BAD 0xff
  7. .equ SCRATCHPAD_SIZE 0x40
  8. ; *** Variables ***
  9. .equ scratchpad TOK_RAMSTART
  10. .equ TOK_RAMEND scratchpad+SCRATCHPAD_SIZE
  11. ; *** Code ***
  12. ; Sets Z is A is ';' or null.
  13. isLineEndOrComment:
  14. cp 0x3b ; ';'
  15. ret z
  16. ; continue to isLineEnd
  17. ; Sets Z is A is CR, LF, or null.
  18. isLineEnd:
  19. or a ; same as cp 0
  20. ret z
  21. cp CR
  22. ret z
  23. cp LF
  24. ret z
  25. cp '\'
  26. ret
  27. ; Sets Z is A is ' ', ',', ';', CR, LF, or null.
  28. isSepOrLineEnd:
  29. call isWS
  30. ret z
  31. jr isLineEndOrComment
  32. ; Checks whether string at (HL) is a label, that is, whether it ends with a ":"
  33. ; Sets Z if yes, unset if no.
  34. ;
  35. ; If it's a label, we change the trailing ':' char with a null char. It's a bit
  36. ; dirty, but it's the easiest way to proceed.
  37. isLabel:
  38. push hl
  39. ld a, ':'
  40. call findchar
  41. ld a, (hl)
  42. cp ':'
  43. jr nz, .nomatch
  44. ; We also have to check that it's our last char.
  45. inc hl
  46. ld a, (hl)
  47. or a ; cp 0
  48. jr nz, .nomatch ; not a null char following the :. no match.
  49. ; We have a match!
  50. ; Remove trailing ':'
  51. xor a ; Z is set
  52. dec hl
  53. ld (hl), a
  54. jr .end
  55. .nomatch:
  56. call unsetZ
  57. .end:
  58. pop hl
  59. ret
  60. ; Read I/O as long as it's whitespace. When it's not, stop and return the last
  61. ; read char in A
  62. _eatWhitespace:
  63. call ioGetB
  64. call isWS
  65. ret nz
  66. jr _eatWhitespace
  67. ; Read ioGetB until a word starts, then read ioGetB as long as there is no
  68. ; separator and put that contents in (scratchpad), null terminated, for a
  69. ; maximum of SCRATCHPAD_SIZE-1 characters.
  70. ; If EOL (\n, \r or comment) or EOF is hit before we could read a word, we stop
  71. ; right there. If scratchpad is not big enough, we stop right there and error.
  72. ; HL points to scratchpad
  73. ; Sets Z if a word could be read, unsets if not.
  74. readWord:
  75. push bc
  76. ; Get to word
  77. call _eatWhitespace
  78. call isLineEndOrComment
  79. jr z, .error
  80. ld hl, scratchpad
  81. ld b, SCRATCHPAD_SIZE-1
  82. ; A contains the first letter to read
  83. ; Are we opening a double quote?
  84. cp '"'
  85. jr z, .insideQuote
  86. ; Are we opening a single quote?
  87. cp 0x27 ; '
  88. jr z, .singleQuote
  89. .loop:
  90. ld (hl), a
  91. inc hl
  92. call ioGetB
  93. call isSepOrLineEnd
  94. jr z, .success
  95. cp ','
  96. jr z, .success
  97. djnz .loop
  98. ; out of space. error.
  99. .error:
  100. ; We need to put the last char we've read back so that gotoNextLine
  101. ; behaves properly.
  102. call ioPutBack
  103. call unsetZ
  104. jr .end
  105. .success:
  106. call ioPutBack
  107. ; null-terminate scratchpad
  108. xor a
  109. ld (hl), a
  110. ld hl, scratchpad
  111. .end:
  112. pop bc
  113. ret
  114. .insideQuote:
  115. ; inside quotes, we accept literal whitespaces, but not line ends.
  116. ld (hl), a
  117. inc hl
  118. call ioGetB
  119. cp '"'
  120. jr z, .loop ; ending the quote ends the word
  121. call isLineEnd
  122. jr z, .error ; ending the line without closing the quote,
  123. ; nope.
  124. djnz .insideQuote
  125. ; out of space. error.
  126. jr .error
  127. .singleQuote:
  128. ; single quote is more straightforward: we have 3 chars and we put them
  129. ; right in scratchpad
  130. ld (hl), a
  131. call ioGetB
  132. or a
  133. jr z, .error
  134. inc hl
  135. ld (hl), a
  136. call ioGetB
  137. cp 0x27 ; '
  138. jr nz, .error
  139. inc hl
  140. ld (hl), a
  141. jr .loop
  142. ; Reads the next char in I/O. If it's a comma, Set Z and return. If it's not,
  143. ; Put the read char back in I/O and unset Z.
  144. readComma:
  145. call _eatWhitespace
  146. cp ','
  147. ret z
  148. call ioPutBack
  149. call unsetZ
  150. ret
  151. ; Read ioGetB until we reach the beginning of next line, skipping comments if
  152. ; necessary. This skips all whitespace, \n, \r, comments until we reach the
  153. ; first non-comment character. Then, we put it back (ioPutBack) and return.
  154. ;
  155. ; If gotoNextLine encounters anything else than whitespace, comment or line
  156. ; separator, we error out (no putback)
  157. ; Sets Z if we reached a new line. Unset if EOF or error.
  158. gotoNextLine:
  159. .loop1:
  160. ; first loop is "strict", that is: we error out on non-whitespace.
  161. call ioGetB
  162. call isSepOrLineEnd
  163. ret nz ; error
  164. or a ; cp 0
  165. jr z, .eof
  166. call isLineEnd
  167. jr z, .loop3 ; good!
  168. cp 0x3b ; ';'
  169. jr z, .loop2 ; comment starting, go to "fast lane"
  170. jr .loop1
  171. .loop2:
  172. ; second loop is the "comment loop": anything is valid and we just run
  173. ; until EOL.
  174. call ioGetB
  175. or a ; cp 0
  176. jr z, .eof
  177. cp '\' ; special case: '\' doesn't count as a line end
  178. ; in a comment.
  179. jr z, .loop2
  180. call isLineEnd
  181. jr z, .loop3
  182. jr .loop2
  183. .loop3:
  184. ; Loop 3 happens after we reach our first line sep. This means that we
  185. ; wade through whitespace until we reach a non-whitespace character.
  186. call ioGetB
  187. or a ; cp 0
  188. jr z, .eof
  189. cp 0x3b ; ';'
  190. jr z, .loop2 ; oh, another comment! go back to loop2!
  191. call isSepOrLineEnd
  192. jr z, .loop3
  193. ; Non-whitespace. That's our goal! Put it back
  194. call ioPutBack
  195. .eof:
  196. cp a ; ensure Z
  197. ret
  198. ; Parse line in (HL) and read the next token in BC. The token is written on
  199. ; two bytes (B and C). B is a token type (TOK_* constants) and C is an ID
  200. ; specific to that token type.
  201. ; Advance HL to after the read word.
  202. ; If no token matches, TOK_BAD is written to B
  203. tokenize:
  204. call readWord
  205. jr z, .process ; read successful, process into token.
  206. ; Error. It could be EOL, EOF or scraptchpad size problem
  207. ; Whatever it is, calling gotoNextLine is appropriate. If it's EOL
  208. ; that's obviously what we want to do. If it's EOF, we can check
  209. ; it after. If it's a scratchpad overrun, gotoNextLine handles it.
  210. call gotoNextLine
  211. jr nz, .error
  212. or a ; Are we EOF?
  213. jr nz, tokenize ; not EOF? then continue!
  214. ; We're EOF
  215. ld b, TOK_EOF
  216. ret
  217. .process:
  218. call isLabel
  219. jr z, .label
  220. call getInstID
  221. jr z, .instr
  222. call getDirectiveID
  223. jr z, .direc
  224. .error:
  225. ; no match
  226. ld b, TOK_BAD
  227. jr .end
  228. .instr:
  229. ld b, TOK_INSTR
  230. jr .end
  231. .direc:
  232. ld b, TOK_DIRECTIVE
  233. jr .end
  234. .label:
  235. ld b, TOK_LABEL
  236. .end:
  237. ld c, a
  238. ret