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.

330 lines
6.3KB

  1. #include "user.inc"
  2. ; *** Consts ***
  3. ARGSPEC_SINGLE_CNT .equ 7
  4. ARGSPEC_TBL_CNT .equ 12
  5. ; *** Code ***
  6. .org USER_CODE
  7. call parseLine
  8. ld b, 0
  9. ld c, a ; written bytes
  10. ret
  11. ; Sets Z is A is ';', CR, LF, or null.
  12. isLineEnd:
  13. cp ';'
  14. ret z
  15. cp 0
  16. ret z
  17. cp 0x0d
  18. ret z
  19. cp 0x0a
  20. ret
  21. ; Sets Z is A is ' ' or ','
  22. isSep:
  23. cp ' '
  24. ret z
  25. cp ','
  26. ret
  27. ; Sets Z is A is ' ', ',', ';', CR, LF, or null.
  28. isSepOrLineEnd:
  29. call isSep
  30. ret z
  31. call isLineEnd
  32. ret
  33. ; read word in (HL) and put it in (DE), null terminated. A is the read
  34. ; length. HL is advanced to the next separator char.
  35. readWord:
  36. push bc
  37. ld b, 4
  38. .loop:
  39. ld a, (hl)
  40. call isSepOrLineEnd
  41. jr z, .success
  42. call JUMP_UPCASE
  43. ld (de), a
  44. inc hl
  45. inc de
  46. djnz .loop
  47. .success:
  48. xor a
  49. ld (de), a
  50. ld a, 4
  51. sub a, b
  52. jr .end
  53. .error:
  54. xor a
  55. ld (de), a
  56. .end:
  57. pop bc
  58. ret
  59. ; (HL) being a string, advance it to the next non-sep character.
  60. ; Set Z if we could do it before the line ended, reset Z if we couldn't.
  61. toWord:
  62. .loop:
  63. ld a, (hl)
  64. call isLineEnd
  65. jr z, .error
  66. call isSep
  67. jr nz, .success
  68. inc hl
  69. jr .loop
  70. .error:
  71. ; we need the Z flag to be unset and it is set now. Let's CP with
  72. ; something it can't be equal to, something not a line end.
  73. cp 'a' ; Z flag unset
  74. ret
  75. .success:
  76. ; We need the Z flag to be set and it is unset. Let's compare it with
  77. ; itself to return a set Z
  78. cp a
  79. ret
  80. ; Read arg from (HL) into argspec at (DE)
  81. ; HL is advanced to the next word. Z is set if there's a next word.
  82. readArg:
  83. push de
  84. ld de, tmpVal
  85. call readWord
  86. push hl
  87. ld hl, tmpVal
  88. call matchArg
  89. pop hl
  90. pop de
  91. ld (de), a
  92. call toWord
  93. ret
  94. ; Read line from (HL) into (curWord), (curArg1) and (curArg2)
  95. readLine:
  96. push de
  97. xor a
  98. ld (curWord), a
  99. ld (curArg1), a
  100. ld (curArg2), a
  101. ld de, curWord
  102. call readWord
  103. call toWord
  104. jr nz, .end
  105. ld de, curArg1
  106. call readArg
  107. jr nz, .end
  108. ld de, curArg2
  109. call readArg
  110. .end:
  111. pop de
  112. ret
  113. ; Returns length of string at (HL) in A.
  114. strlen:
  115. push bc
  116. push hl
  117. ld bc, 0
  118. ld a, 0 ; look for null char
  119. .loop:
  120. cpi
  121. jp z, .found
  122. jr .loop
  123. .found:
  124. ; How many char do we have? the (NEG BC)-1, which started at 0 and
  125. ; decreased at each CPI call. In this routine, we stay in the 8-bit
  126. ; realm, so C only.
  127. ld a, c
  128. neg
  129. dec a
  130. pop hl
  131. pop bc
  132. ret
  133. ; find argspec for string at (HL). Returns matching argspec in A.
  134. ; Return value 1 holds a special meaning: arg is not empty, but doesn't match
  135. ; any argspec (A == 0 means arg is empty). A return value of 1 means an error.
  136. matchArg:
  137. call strlen
  138. cp 0
  139. ret z ; empty string? A already has our result: 0
  140. push bc
  141. push de
  142. push hl
  143. cp 1
  144. jr z, .matchsingle ; Arg is one char? We have a "single" type.
  145. ; Not a "single" arg. Do the real thing then.
  146. ld de, argspecTbl
  147. ; DE now points the the "argspec char" part of the entry, but what
  148. ; we're comparing in the loop is the string next to it. Let's offset
  149. ; DE by one so that the loop goes through strings.
  150. inc de
  151. ld b, ARGSPEC_TBL_CNT
  152. .loop1:
  153. ld a, 4
  154. call JUMP_STRNCMP
  155. jr z, .found ; got it!
  156. ld a, 5
  157. call JUMP_ADDDE
  158. djnz .loop1
  159. ; exhausted? we have a problem os specifying a wrong argspec. This is
  160. ; an internal consistency error.
  161. ld a, 1
  162. jr .end
  163. .found:
  164. ; found the matching argspec row. Our result is one byte left of DE.
  165. dec de
  166. ld a, (de)
  167. jr .end
  168. .matchsingle:
  169. ld a, (hl)
  170. ld hl, argspecsSingle
  171. ld bc, ARGSPEC_SINGLE_CNT
  172. .loop2:
  173. cpi
  174. jr z, .end ; found! our result is already in A. go straight
  175. ; to end.
  176. jp po, .loop2notfound
  177. jr .loop2
  178. .loop2notfound:
  179. ; something's wrong. error
  180. ld a, 1
  181. jr .end
  182. .end:
  183. pop hl
  184. pop de
  185. pop bc
  186. ret
  187. ; Compare primary row at (DE) with string at curWord. Sets Z flag if there's a
  188. ; match, reset if not.
  189. matchPrimaryRow:
  190. push hl
  191. push ix
  192. ld hl, curWord
  193. ld a, 4
  194. call JUMP_STRNCMP
  195. jr nz, .end
  196. ; name matches, let's see the rest
  197. ld ixh, d
  198. ld ixl, e
  199. ld a, (curArg1)
  200. cp (ix+4)
  201. jr nz, .end
  202. ld a, (curArg2)
  203. cp (ix+5)
  204. .end:
  205. pop ix
  206. pop hl
  207. ret
  208. ; Parse line at (HL) and write resulting opcode(s) in (DE). Returns the number
  209. ; of bytes written in A.
  210. parseLine:
  211. call readLine
  212. push de
  213. ld de, instTBlPrimary
  214. .loop:
  215. ld a, (de)
  216. cp 0
  217. jr z, .nomatch ; we reached last entry
  218. call matchPrimaryRow
  219. jr z, .match
  220. ld a, 7
  221. call JUMP_ADDDE
  222. jr .loop
  223. .nomatch:
  224. xor a
  225. pop de
  226. ret
  227. .match:
  228. ld a, 6 ; upcode is on 7th byte
  229. call JUMP_ADDDE
  230. ld a, (de)
  231. pop de
  232. ld (de), a
  233. ld a, 1
  234. ret
  235. ; In instruction metadata below, argument types arge indicated with a single
  236. ; char mnemonic that is called "argspec". This is the table of correspondance.
  237. ; Single letters are represented by themselves, so we don't need as much
  238. ; metadata.
  239. argspecsSingle:
  240. .db "ABCDEHL"
  241. ; Format: 1 byte argspec + 4 chars string
  242. argspecTbl:
  243. .db 'h', "HL", 0, 0
  244. .db 'l', "(HL)"
  245. .db 'd', "DE", 0, 0
  246. .db 'e', "(DE)"
  247. .db 'b', "BC", 0, 0
  248. .db 'c', "(BC)"
  249. .db 'a', "AF", 0, 0
  250. .db 'f', "AF'", 0
  251. .db 'x', "(IX)"
  252. .db 'y', "(IY)"
  253. .db 's', "SP", 0, 0
  254. .db 'p', "(SP)"
  255. ; This is a list of primary instructions (single upcode) that lead to a
  256. ; constant (no group code to insert). Format:
  257. ;
  258. ; 4 bytes for the name (fill with zero)
  259. ; 1 byte for arg constant
  260. ; 1 byte for 2nd arg constant
  261. ; 1 byte for upcode
  262. instTBlPrimary:
  263. .db "ADD", 0, 'A', 'h', 0x86 ; ADD A, HL
  264. .db "CCF", 0, 0, 0, 0x3f ; CCF
  265. .db "CPL", 0, 0, 0, 0x2f ; CPL
  266. .db "DAA", 0, 0, 0, 0x27 ; DAA
  267. .db "DI",0,0, 0, 0, 0xf3 ; DI
  268. .db "EI",0,0, 0, 0, 0xfb ; EI
  269. .db "EX",0,0, 'p', 'h', 0xe3 ; EX (SP), HL
  270. .db "EX",0,0, 'a', 'f', 0x08 ; EX AF, AF'
  271. .db "EX",0,0, 'd', 'h', 0xeb ; EX DE, HL
  272. .db "EXX", 0, 0, 0, 0xd9 ; EXX
  273. .db "HALT", 0, 0, 0x76 ; HALT
  274. .db "INC", 0, 'l', 0, 0x34 ; INC (HL)
  275. .db "JP",0,0, 'l', 0, 0xe9 ; JP (HL)
  276. .db "LD",0,0, 'c', 'A', 0x02 ; LD (BC), A
  277. .db "LD",0,0, 'e', 'A', 0x12 ; LD (DE), A
  278. .db "LD",0,0, 'A', 'c', 0x0a ; LD A, (BC)
  279. .db "LD",0,0, 'A', 'e', 0x0a ; LD A, (DE)
  280. .db "LD",0,0, 's', 'h', 0x0a ; LD SP, HL
  281. .db "NOP", 0, 0, 0, 0x00 ; NOP
  282. .db "RET", 0, 0, 0, 0xc9 ; RET
  283. .db "RLA", 0, 0, 0, 0x17 ; RLA
  284. .db "RLCA", 0, 0, 0x07 ; RLCA
  285. .db "RRA", 0, 0, 0, 0x1f ; RRA
  286. .db "RRCA", 0, 0, 0x0f ; RRCA
  287. .db "SCF", 0, 0, 0, 0x37 ; SCF
  288. .db 0
  289. ; *** Variables ***
  290. ; enough space for 4 chars and a null
  291. curWord:
  292. .db 0, 0, 0, 0, 0
  293. ; Args are 3 bytes: argspec, then values of numerical constants (when that's
  294. ; appropriate)
  295. curArg1:
  296. .db 0, 0, 0
  297. curArg2:
  298. .db 0, 0, 0
  299. ; space for tmp stuff
  300. tmpVal:
  301. .db 0, 0, 0, 0, 0