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.

270 lines
6.1KB

  1. ; Manages both constants and labels within a same namespace and registry.
  2. ;
  3. ; Local Labels
  4. ;
  5. ; Local labels during the "official" first pass are ignored. To register them
  6. ; in the global registry during that pass would be wasteful in terms of memory.
  7. ;
  8. ; What we do instead is set up a separate register for them and have a "second
  9. ; first pass" whenever we encounter a new context. That is, we wipe the local
  10. ; registry, parse the code until the next global symbol (or EOF), then rewind
  11. ; and continue second pass as usual.
  12. ; *** Constants ***
  13. ; Size of each record in registry
  14. .equ SYM_RECSIZE 3
  15. .equ SYM_REGSIZE ZASM_REG_BUFSZ+1+ZASM_REG_MAXCNT*SYM_RECSIZE
  16. .equ SYM_LOC_REGSIZE ZASM_LREG_BUFSZ+1+ZASM_LREG_MAXCNT*SYM_RECSIZE
  17. ; *** Variables ***
  18. ; A registry has three parts: record count (byte) record list and names pool.
  19. ; A record is a 3 bytes structure:
  20. ; 1b - name length
  21. ; 2b - value associated to symbol
  22. ;
  23. ; We know we're at the end of the record list when we hit a 0-length one.
  24. ;
  25. ; The names pool is a list of strings, not null-terminated, associated with
  26. ; the value.
  27. ;
  28. ; It is assumed that the registry is aligned in memory in that order:
  29. ; names pool, rec count, reclist
  30. ; Global labels registry
  31. .equ SYM_GLOB_REG SYM_RAMSTART
  32. .equ SYM_LOC_REG SYM_GLOB_REG+SYM_REGSIZE
  33. .equ SYM_CONST_REG SYM_LOC_REG+SYM_LOC_REGSIZE
  34. .equ SYM_RAMEND SYM_CONST_REG+SYM_REGSIZE
  35. ; *** Registries ***
  36. ; A symbol registry is a 5 bytes record with points to the name pool then the
  37. ; records list of the register and then the max record count.
  38. SYM_GLOBAL_REGISTRY:
  39. .dw SYM_GLOB_REG, SYM_GLOB_REG+ZASM_REG_BUFSZ
  40. .db ZASM_REG_MAXCNT
  41. SYM_LOCAL_REGISTRY:
  42. .dw SYM_LOC_REG, SYM_LOC_REG+ZASM_LREG_BUFSZ
  43. .db ZASM_LREG_MAXCNT
  44. SYM_CONST_REGISTRY:
  45. .dw SYM_CONST_REG, SYM_CONST_REG+ZASM_REG_BUFSZ
  46. .db ZASM_REG_MAXCNT
  47. ; *** Code ***
  48. symInit:
  49. ld ix, SYM_GLOBAL_REGISTRY
  50. call symClear
  51. ld ix, SYM_LOCAL_REGISTRY
  52. call symClear
  53. ld ix, SYM_CONST_REGISTRY
  54. jp symClear
  55. ; Sets Z according to whether label in (HL) is local (starts with a dot)
  56. symIsLabelLocal:
  57. ld a, '.'
  58. cp (hl)
  59. ret
  60. symRegisterGlobal:
  61. push ix
  62. ld ix, SYM_GLOBAL_REGISTRY
  63. call symRegister
  64. pop ix
  65. ret
  66. symRegisterLocal:
  67. push ix
  68. ld ix, SYM_LOCAL_REGISTRY
  69. call symRegister
  70. pop ix
  71. ret
  72. symRegisterConst:
  73. push ix
  74. ld ix, SYM_CONST_REGISTRY
  75. call symRegister
  76. pop ix
  77. ret
  78. ; Register label in (HL) (minus the ending ":") into the symbol registry in IX
  79. ; and set its value in that registry to the value specified in DE.
  80. ; If successful, Z is set. Otherwise, Z is unset and A is an error code (ERR_*).
  81. symRegister:
  82. push hl ; --> lvl 1. it's the symbol to add
  83. call _symIsFull
  84. jr z, .outOfMemory
  85. ; First, let's get our strlen
  86. call strlen
  87. ld c, a ; save that strlen for later
  88. call _symFind
  89. jr z, .duplicateError
  90. ; Is our new name going to make us go out of bounds?
  91. push hl ; --> lvl 2
  92. push de ; --> lvl 3
  93. ld d, 0
  94. ld e, c
  95. add hl, de ; if carry set here, sbc will carry too
  96. ld e, (ix+2) ; DE --> pointer to record list, which is also
  97. ld d, (ix+3) ; the end of names pool
  98. ; DE --> names end
  99. sbc hl, de ; compares hl and de destructively
  100. pop de ; <-- lvl 3
  101. pop hl ; <-- lvl 2
  102. jr nc, .outOfMemory ; HL >= DE
  103. ; Success. At this point, we have:
  104. ; HL -> where we want to add the string
  105. ; IY -> target record where the value goes
  106. ; DE -> value to register
  107. ; SP -> string to register
  108. ; Let's start with the record
  109. ld (iy), c ; strlen
  110. ld (iy+1), e
  111. ld (iy+2), d
  112. ; Good! now, the string. Destination is in HL, source is in SP
  113. ex de, hl ; dest is in DE
  114. pop hl ; <-- lvl 1. string to register
  115. ; Copy HL into DE until we reach null char
  116. call strcpyM
  117. ; Last thing: increase record count
  118. ld l, (ix+2)
  119. ld h, (ix+3)
  120. inc (hl)
  121. xor a ; sets Z
  122. ret
  123. .outOfMemory:
  124. pop hl ; <-- lvl 1
  125. ld a, ERR_OOM
  126. jp unsetZ
  127. .duplicateError:
  128. pop hl ; <-- lvl 1
  129. ld a, ERR_DUPSYM
  130. jp unsetZ ; return
  131. ; Assuming that IX points to a registry, find name HL in its names and make IY
  132. ; point to the corresponding record. If it doesn't find anything, IY will
  133. ; conveniently point to the next record after the last, and HL to the next
  134. ; name insertion point.
  135. ; If we find something, Z is set, otherwise unset.
  136. _symFind:
  137. push de
  138. push bc
  139. call strlen
  140. ld c, a ; save strlen
  141. ex de, hl ; easier if needle is in DE
  142. ; IY --> records
  143. ld l, (ix+2)
  144. ld h, (ix+3)
  145. ; first byte is count
  146. ld b, (hl)
  147. inc hl ; first record
  148. push hl \ pop iy
  149. ; HL --> names
  150. ld l, (ix)
  151. ld h, (ix+1)
  152. ; do we have an empty reclist?
  153. xor a
  154. cp b
  155. jr z, .nothing ; zero count? nothing
  156. .loop:
  157. ld a, (iy) ; name len
  158. cp c
  159. jr nz, .skip ; different strlen, can't possibly match. skip
  160. call strncmp
  161. jr z, .end ; match! Z already set, IY and HL placed.
  162. .skip:
  163. ; ok, next!
  164. push de ; --> lvl 1
  165. ld de, 0x0003
  166. add iy, de ; faster and shorter than three inc's
  167. ld e, (iy-3) ; offset is also compulsory, so no extra bytes used
  168. ; (iy-3) holds the name length of the string just processed
  169. add hl, de ; advance HL by (iy-3) characters
  170. pop de ; <-- lvl 1
  171. djnz .loop
  172. ; end of the chain, nothing found
  173. .nothing:
  174. call unsetZ
  175. .end:
  176. pop bc
  177. pop de
  178. ret
  179. ; For a given symbol name in (HL), find it in the appropriate symbol register
  180. ; and return its value in DE. If (HL) is a local label, the local register is
  181. ; searched. Otherwise, the global one. It is assumed that this routine is
  182. ; always called when the global registry is selected. Therefore, we always
  183. ; reselect it afterwards.
  184. symFindVal:
  185. push ix
  186. call symIsLabelLocal
  187. jr z, .local
  188. ; global. Let's try consts first, then symbols
  189. push hl ; --> lvl 1. we'll need it again if not found.
  190. ld ix, SYM_CONST_REGISTRY
  191. call _symFind
  192. pop hl ; <-- lvl 1
  193. jr z, .found
  194. ld ix, SYM_GLOBAL_REGISTRY
  195. call _symFind
  196. jr nz, .end
  197. .found:
  198. ; Found! let's fetch value
  199. ld e, (iy+1)
  200. ld d, (iy+2)
  201. jr .end
  202. .local:
  203. ld ix, SYM_LOCAL_REGISTRY
  204. call _symFind
  205. jr z, .found
  206. ; continue to end
  207. .end:
  208. pop ix
  209. ret
  210. ; Clear registry at IX
  211. symClear:
  212. push af
  213. push hl
  214. ld l, (ix+2)
  215. ld h, (ix+3)
  216. ; HL --> reclist count
  217. xor a
  218. ld (hl), a
  219. pop hl
  220. pop af
  221. ret
  222. ; Returns whether register in IX has reached its capacity.
  223. ; Sets Z if full, unset if not.
  224. _symIsFull:
  225. push hl
  226. ld l, (ix+2)
  227. ld h, (ix+3)
  228. ld l, (hl) ; record count
  229. ld a, (ix+4) ; max record count
  230. cp l
  231. pop hl
  232. ret