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.

243 lines
5.9KB

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