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.

328 lines
6.3KB

  1. #include "user.inc"
  2. ; *** Consts ***
  3. ARGSPEC_SINGLE_CNT .equ 7
  4. ARGSPEC_TBL_CNT .equ 12
  5. INSTR_TBL_PRIMARYC_CNT .equ 25
  6. ; *** Code ***
  7. .org USER_CODE
  8. call parseLine
  9. ld b, 0
  10. ld c, a ; written bytes
  11. ret
  12. ; Sets Z is A is ';', CR, LF, or null.
  13. isLineEnd:
  14. cp ';'
  15. ret z
  16. cp 0
  17. ret z
  18. cp 0x0d
  19. ret z
  20. cp 0x0a
  21. ret
  22. ; Sets Z is A is ' ' or ','
  23. isSep:
  24. cp ' '
  25. ret z
  26. cp ','
  27. ret
  28. ; Sets Z is A is ' ', ',', ';', CR, LF, or null.
  29. isSepOrLineEnd:
  30. call isSep
  31. ret z
  32. call isLineEnd
  33. ret
  34. ; read word in (HL) and put it in (DE), null terminated. A is the read
  35. ; length. HL is advanced to the next separator char.
  36. readWord:
  37. push bc
  38. ld b, 4
  39. .loop:
  40. ld a, (hl)
  41. call isSepOrLineEnd
  42. jr z, .success
  43. call JUMP_UPCASE
  44. ld (de), a
  45. inc hl
  46. inc de
  47. djnz .loop
  48. .success:
  49. xor a
  50. ld (de), a
  51. ld a, 4
  52. sub a, b
  53. jr .end
  54. .error:
  55. xor a
  56. ld (de), a
  57. .end:
  58. pop bc
  59. ret
  60. ; (HL) being a string, advance it to the next non-sep character.
  61. ; Set Z if we could do it before the line ended, reset Z if we couldn't.
  62. toWord:
  63. .loop:
  64. ld a, (hl)
  65. call isLineEnd
  66. jr z, .error
  67. call isSep
  68. jr nz, .success
  69. inc hl
  70. jr .loop
  71. .error:
  72. ; we need the Z flag to be unset and it is set now. Let's CP with
  73. ; something it can't be equal to, something not a line end.
  74. cp 'a' ; Z flag unset
  75. ret
  76. .success:
  77. ; We need the Z flag to be set and it is unset. Let's compare it with
  78. ; itself to return a set Z
  79. cp a
  80. ret
  81. ; Read arg from (HL) into argspec at (DE)
  82. ; HL is advanced to the next word. Z is set if there's a next word.
  83. readArg:
  84. push de
  85. ld de, tmpVal
  86. call readWord
  87. push hl
  88. ld hl, tmpVal
  89. call matchArg
  90. pop hl
  91. pop de
  92. ld (de), a
  93. call toWord
  94. ret
  95. ; Read line from (HL) into (curWord), (curArg1) and (curArg2)
  96. readLine:
  97. push de
  98. xor a
  99. ld (curWord), a
  100. ld (curArg1), a
  101. ld (curArg2), a
  102. ld de, curWord
  103. call readWord
  104. call toWord
  105. jr nz, .end
  106. ld de, curArg1
  107. call readArg
  108. jr nz, .end
  109. ld de, curArg2
  110. call readArg
  111. .end:
  112. pop de
  113. ret
  114. ; Returns length of string at (HL) in A.
  115. strlen:
  116. push bc
  117. push hl
  118. ld bc, 0
  119. ld a, 0 ; look for null char
  120. .loop:
  121. cpi
  122. jp z, .found
  123. jr .loop
  124. .found:
  125. ; How many char do we have? the (NEG BC)-1, which started at 0 and
  126. ; decreased at each CPI call. In this routine, we stay in the 8-bit
  127. ; realm, so C only.
  128. ld a, c
  129. neg
  130. dec a
  131. pop hl
  132. pop bc
  133. ret
  134. ; find argspec for string at (HL). Returns matching argspec in A.
  135. ; Return value 1 holds a special meaning: arg is not empty, but doesn't match
  136. ; any argspec (A == 0 means arg is empty). A return value of 1 means an error.
  137. matchArg:
  138. call strlen
  139. cp 0
  140. ret z ; empty string? A already has our result: 0
  141. push bc
  142. push de
  143. push hl
  144. cp 1
  145. jr z, .matchsingle ; Arg is one char? We have a "single" type.
  146. ; Not a "single" arg. Do the real thing then.
  147. ld de, argspecTbl
  148. ; DE now points the the "argspec char" part of the entry, but what
  149. ; we're comparing in the loop is the string next to it. Let's offset
  150. ; DE by one so that the loop goes through strings.
  151. inc de
  152. ld b, ARGSPEC_TBL_CNT
  153. .loop1:
  154. ld a, 4
  155. call JUMP_STRNCMP
  156. jr z, .found ; got it!
  157. ld a, 5
  158. call JUMP_ADDDE
  159. djnz .loop1
  160. ; exhausted? we have a problem os specifying a wrong argspec. This is
  161. ; an internal consistency error.
  162. ld a, 1
  163. jr .end
  164. .found:
  165. ; found the matching argspec row. Our result is one byte left of DE.
  166. dec de
  167. ld a, (de)
  168. jr .end
  169. .matchsingle:
  170. ld a, (hl)
  171. ld hl, argspecsSingle
  172. ld bc, ARGSPEC_SINGLE_CNT
  173. .loop2:
  174. cpi
  175. jr z, .end ; found! our result is already in A. go straight
  176. ; to end.
  177. jp po, .loop2notfound
  178. jr .loop2
  179. .loop2notfound:
  180. ; something's wrong. error
  181. ld a, 1
  182. jr .end
  183. .end:
  184. pop hl
  185. pop de
  186. pop bc
  187. ret
  188. ; Compare primary row at (DE) with string at curWord. Sets Z flag if there's a
  189. ; match, reset if not.
  190. matchPrimaryRow:
  191. push hl
  192. push ix
  193. ld hl, curWord
  194. ld a, 4
  195. call JUMP_STRNCMP
  196. jr nz, .end
  197. ; name matches, let's see the rest
  198. ld ixh, d
  199. ld ixl, e
  200. ld a, (curArg1)
  201. cp (ix+4)
  202. jr nz, .end
  203. ld a, (curArg2)
  204. cp (ix+5)
  205. .end:
  206. pop ix
  207. pop hl
  208. ret
  209. ; Parse line at (HL) and write resulting opcode(s) in (DE). Returns the number
  210. ; of bytes written in A.
  211. parseLine:
  212. call readLine
  213. push de
  214. ld de, instrTBlPrimaryC
  215. ld b, INSTR_TBL_PRIMARYC_CNT
  216. .loop:
  217. ld a, (de)
  218. call matchPrimaryRow
  219. jr z, .match
  220. ld a, 7
  221. call JUMP_ADDDE
  222. djnz .loop
  223. ; no match
  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. instrTBlPrimaryC:
  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. ; *** Variables ***
  289. ; enough space for 4 chars and a null
  290. curWord:
  291. .db 0, 0, 0, 0, 0
  292. ; Args are 3 bytes: argspec, then values of numerical constants (when that's
  293. ; appropriate)
  294. curArg1:
  295. .db 0, 0, 0
  296. curArg2:
  297. .db 0, 0, 0
  298. ; space for tmp stuff
  299. tmpVal:
  300. .db 0, 0, 0, 0, 0