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.

246 lines
5.7KB

  1. ; *** Variables ***
  2. ; A bool flag indicating that we're on first pass. When we are, we don't care
  3. ; about actual output, but only about the length of each upcode. This means
  4. ; that when we parse instructions and directive that error out because of a
  5. ; missing symbol, we don't error out and just write down a dummy value.
  6. .equ ZASM_FIRST_PASS ZASM_RAMSTART
  7. ; whether we're in "local pass", that is, in local label scanning mode. During
  8. ; this special pass, ZASM_FIRST_PASS will also be set so that the rest of the
  9. ; code behaves as is we were in the first pass.
  10. .equ ZASM_LOCAL_PASS @+1
  11. ; What IO_PC was when we started our context
  12. .equ ZASM_CTX_PC @+1
  13. ; current ".org" offset, that is, what we must offset all our label by.
  14. .equ ZASM_ORG @+2
  15. .equ ZASM_RAMEND @+2
  16. ; Takes 2 byte arguments, blkdev in and blkdev out, expressed as IDs.
  17. ; Can optionally take a 3rd argument which is the high byte of the initial
  18. ; .org. For example, passing 0x42 to this 3rd arg is the equivalent of beginning
  19. ; the unit with ".org 0x4200".
  20. ; Read file through blkdev in and outputs its upcodes through blkdev out.
  21. ; HL is set to the last lineno to be read.
  22. ; Sets Z on success, unset on error. On error, A contains an error code (ERR_*)
  23. zasmMain:
  24. ; Parse args in (HL)
  25. ; blkdev in
  26. call parseHexadecimal ; --> DE
  27. jr nz, .badargs
  28. ld a, e
  29. ld de, IO_IN_BLK
  30. call blkSel
  31. ; blkdev in
  32. call rdWS
  33. jr nz, .badargs
  34. call parseHexadecimal ; --> DE
  35. jr nz, .badargs
  36. ld a, e
  37. ld de, IO_OUT_BLK
  38. call blkSel
  39. ; .org high byte
  40. ld e, 0 ; in case we .skipOrgSet
  41. call rdWS
  42. jr nz, .skipOrgSet ; no org argument
  43. call parseHexadecimal ; --> DE
  44. jr nz, .badargs
  45. .skipOrgSet:
  46. ; Init .org with value of E
  47. ; Save in "@" too
  48. ld a, e
  49. ld (ZASM_ORG+1), a ; high byte of .org
  50. ld (DIREC_LASTVAL+1), a
  51. xor a
  52. ld (ZASM_ORG), a ; low byte zero in all cases
  53. ld (DIREC_LASTVAL), a
  54. ; And then the rest.
  55. ld (ZASM_LOCAL_PASS), a
  56. call ioInit
  57. call symInit
  58. ; First pass
  59. ld hl, .sFirstPass
  60. call ioPrintLN
  61. ld a, 1
  62. ld (ZASM_FIRST_PASS), a
  63. call zasmParseFile
  64. jr nz, .end
  65. ; Second pass
  66. ld hl, .sSecondPass
  67. call ioPrintLN
  68. xor a
  69. ld (ZASM_FIRST_PASS), a
  70. ; before parsing the file for the second pass, let's clear the const
  71. ; registry. See comment in handleEQU.
  72. ld ix, SYM_CONST_REGISTRY
  73. call symClear
  74. call zasmParseFile
  75. .end:
  76. jp ioLineNo ; --> HL, --> DE, returns
  77. .badargs:
  78. ; bad args
  79. ld a, SHELL_ERR_BAD_ARGS
  80. ret
  81. .sFirstPass:
  82. .db "First pass", 0
  83. .sSecondPass:
  84. .db "Second pass", 0
  85. ; Sets Z according to whether we're in first pass.
  86. zasmIsFirstPass:
  87. ld a, (ZASM_FIRST_PASS)
  88. cp 1
  89. ret
  90. ; Sets Z according to whether we're in local pass.
  91. zasmIsLocalPass:
  92. ld a, (ZASM_LOCAL_PASS)
  93. cp 1
  94. ret
  95. ; Set ZASM_ORG to specified number in HL
  96. zasmSetOrg:
  97. ld (ZASM_ORG), hl
  98. ret
  99. ; Return current PC (properly .org offsetted) in HL
  100. zasmGetPC:
  101. push de
  102. ld hl, (ZASM_ORG)
  103. ld de, (IO_PC)
  104. add hl, de
  105. pop de
  106. ret
  107. ; Repeatedly reads lines from IO, assemble them and spit the binary code in
  108. ; IO. Z is set on success, unset on error. DE contains the last line number to
  109. ; be read (first line is 1).
  110. zasmParseFile:
  111. call ioRewind
  112. .loop:
  113. call parseLine
  114. ret nz ; error
  115. ld a, b ; TOK_*
  116. cp TOK_EOF
  117. jr z, .eof
  118. jr .loop
  119. .eof:
  120. call zasmIsLocalPass
  121. jr nz, .end ; EOF and not local pass
  122. ; we're in local pass and EOF. Unwind this
  123. call _endLocalPass
  124. jr .loop
  125. .end:
  126. cp a ; ensure Z
  127. ret
  128. ; Parse next token and accompanying args (when relevant) in I/O, write the
  129. ; resulting opcode(s) through ioPutB and increases (IO_PC) by the number of
  130. ; bytes written. BC is set to the result of the call to tokenize.
  131. ; Sets Z if parse was successful, unset if there was an error. EOF is not an
  132. ; error. If there is an error, A is set to the corresponding error code (ERR_*).
  133. parseLine:
  134. call tokenize
  135. ld a, b ; TOK_*
  136. cp TOK_INSTR
  137. jp z, _parseInstr
  138. cp TOK_DIRECTIVE
  139. jp z, _parseDirec
  140. cp TOK_LABEL
  141. jr z, _parseLabel
  142. cp TOK_EOF
  143. ret z ; We're finished, no error.
  144. ; Bad token
  145. ld a, ERR_UNKNOWN
  146. jp unsetZ ; return with Z unset
  147. _parseInstr:
  148. ld a, c ; I_*
  149. jp parseInstruction
  150. _parseDirec:
  151. ld a, c ; D_*
  152. jp parseDirective
  153. _parseLabel:
  154. ; The string in (scratchpad) is a label with its trailing ':' removed.
  155. ld hl, scratchpad
  156. call zasmIsLocalPass
  157. jr z, .processLocalPass
  158. ; Is this a local label? If yes, we don't process it in the context of
  159. ; parseLine, whether it's first or second pass. Local labels are only
  160. ; parsed during the Local Pass
  161. call symIsLabelLocal
  162. jr z, .success ; local? don't do anything.
  163. ld ix, SYM_GLOBAL_REGISTRY
  164. call zasmIsFirstPass
  165. jr z, .registerLabel ; When we encounter a label in the first
  166. ; pass, we register it in the symbol
  167. ; list
  168. ; At this point, we're in second pass, we've encountered a global label
  169. ; and we'll soon continue processing our file. However, before we do
  170. ; that, we should process our local labels.
  171. call _beginLocalPass
  172. jr .success
  173. .processLocalPass:
  174. ld ix, SYM_LOCAL_REGISTRY
  175. call symIsLabelLocal
  176. jr z, .registerLabel ; local label? all good, register it
  177. ; normally
  178. ; not a local label? Then we need to end local pass
  179. call _endLocalPass
  180. jr .success
  181. .registerLabel:
  182. push hl
  183. call zasmGetPC
  184. ex de, hl
  185. pop hl
  186. call symRegister
  187. jr nz, .error
  188. ; continue to .success
  189. .success:
  190. xor a ; ensure Z
  191. ret
  192. .error:
  193. call unsetZ
  194. ret
  195. _beginLocalPass:
  196. ; remember were I/O was
  197. call ioSavePos
  198. ; Remember where PC was
  199. ld hl, (IO_PC)
  200. ld (ZASM_CTX_PC), hl
  201. ; Fake first pass
  202. ld a, 1
  203. ld (ZASM_FIRST_PASS), a
  204. ; Set local pass
  205. ld (ZASM_LOCAL_PASS), a
  206. ; Empty local label registry
  207. ld ix, SYM_LOCAL_REGISTRY
  208. jp symClear
  209. _endLocalPass:
  210. ; recall I/O pos
  211. call ioRecallPos
  212. ; recall PC
  213. ld hl, (ZASM_CTX_PC)
  214. ld (IO_PC), hl
  215. ; unfake first pass
  216. xor a
  217. ld (ZASM_FIRST_PASS), a
  218. ; Unset local pass
  219. ld (ZASM_LOCAL_PASS), a
  220. cp a ; ensure Z
  221. ret