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.

487 line
9.9KB

  1. ; *** Collapse OS lib copy ***
  2. ; In the process of Forth-ifying Collapse OS, apps will be slowly rewritten to
  3. ; Forth and the concept of ASM libs will become obsolete. To facilitate this
  4. ; transition, I make, right now, a copy of the routines actually used by Forth's
  5. ; native core. This also has the effect of reducing binary size right now and
  6. ; give us an idea of Forth's compactness.
  7. ; These routines below are copy/paste from apps/lib.
  8. ; Ensures that Z is unset (more complicated than it sounds...)
  9. ; There are often better inline alternatives, either replacing rets with
  10. ; appropriate jmps, or if an 8 bit register is known to not be 0, an inc
  11. ; then a dec. If a is nonzero, 'or a' is optimal.
  12. unsetZ:
  13. or a ;if a nonzero, Z reset
  14. ret nz
  15. cp 1 ;if a is zero, Z reset
  16. ret
  17. ; copy (HL) into DE, then exchange the two, utilising the optimised HL instructions.
  18. ; ld must be done little endian, so least significant byte first.
  19. intoHL:
  20. push de
  21. ld e, (hl)
  22. inc hl
  23. ld d, (hl)
  24. ex de, hl
  25. pop de
  26. ret
  27. intoDE:
  28. ex de, hl
  29. call intoHL
  30. ex de, hl ; de preserved by intoHL, so no push/pop needed
  31. ret
  32. ; add the value of A into HL
  33. ; affects carry flag according to the 16-bit addition, Z, S and P untouched.
  34. addHL:
  35. push de
  36. ld d, 0
  37. ld e, a
  38. add hl, de
  39. pop de
  40. ret
  41. ; make Z the opposite of what it is now
  42. toggleZ:
  43. jp z, unsetZ
  44. cp a
  45. ret
  46. ; Copy string from (HL) in (DE), that is, copy bytes until a null char is
  47. ; encountered. The null char is also copied.
  48. ; HL and DE point to the char right after the null char.
  49. strcpyM:
  50. ld a, (hl)
  51. ld (de), a
  52. inc hl
  53. inc de
  54. or a
  55. jr nz, strcpyM
  56. ret
  57. ; Like strcpyM, but preserve HL and DE
  58. strcpy:
  59. push hl
  60. push de
  61. call strcpyM
  62. pop de
  63. pop hl
  64. ret
  65. ; Compares strings pointed to by HL and DE until one of them hits its null char.
  66. ; If equal, Z is set. If not equal, Z is reset. C is set if HL > DE
  67. strcmp:
  68. push hl
  69. push de
  70. .loop:
  71. ld a, (de)
  72. cp (hl)
  73. jr nz, .end ; not equal? break early. NZ is carried out
  74. ; to the caller
  75. or a ; If our chars are null, stop the cmp
  76. inc hl
  77. inc de
  78. jr nz, .loop ; Z is carried through
  79. .end:
  80. pop de
  81. pop hl
  82. ; Because we don't call anything else than CP that modify the Z flag,
  83. ; our Z value will be that of the last cp (reset if we broke the loop
  84. ; early, set otherwise)
  85. ret
  86. ; Compares strings pointed to by HL and DE up to A count of characters. If
  87. ; equal, Z is set. If not equal, Z is reset.
  88. strncmp:
  89. push bc
  90. push hl
  91. push de
  92. ld b, a
  93. .loop:
  94. ld a, (de)
  95. cp (hl)
  96. jr nz, .end ; not equal? break early. NZ is carried out
  97. ; to the called
  98. cp 0 ; If our chars are null, stop the cmp
  99. jr z, .end ; The positive result will be carried to the
  100. ; caller
  101. inc hl
  102. inc de
  103. djnz .loop
  104. ; We went through all chars with success, but our current Z flag is
  105. ; unset because of the cp 0. Let's do a dummy CP to set the Z flag.
  106. cp a
  107. .end:
  108. pop de
  109. pop hl
  110. pop bc
  111. ; Because we don't call anything else than CP that modify the Z flag,
  112. ; our Z value will be that of the last cp (reset if we broke the loop
  113. ; early, set otherwise)
  114. ret
  115. ; Given a string at (HL), move HL until it points to the end of that string.
  116. strskip:
  117. push bc
  118. ex af, af'
  119. xor a ; look for null char
  120. ld b, a
  121. ld c, a
  122. cpir ; advances HL regardless of comparison, so goes one too far
  123. dec hl
  124. ex af, af'
  125. pop bc
  126. ret
  127. ; Borrowed from Tasty Basic by Dimitri Theulings (GPL).
  128. ; Divide HL by DE, placing the result in BC and the remainder in HL.
  129. divide:
  130. push hl ; --> lvl 1
  131. ld l, h ; divide h by de
  132. ld h, 0
  133. call .dv1
  134. ld b, c ; save result in b
  135. ld a, l ; (remainder + l) / de
  136. pop hl ; <-- lvl 1
  137. ld h, a
  138. .dv1:
  139. ld c, 0xff ; result in c
  140. .dv2:
  141. inc c ; dumb routine
  142. call .subde ; divide using subtract and count
  143. jr nc, .dv2
  144. add hl, de
  145. ret
  146. .subde:
  147. ld a, l
  148. sub e ; subtract de from hl
  149. ld l, a
  150. ld a, h
  151. sbc a, d
  152. ld h, a
  153. ret
  154. ; DE * BC -> DE (high) and HL (low)
  155. multDEBC:
  156. ld hl, 0
  157. ld a, 0x10
  158. .loop:
  159. add hl, hl
  160. rl e
  161. rl d
  162. jr nc, .noinc
  163. add hl, bc
  164. jr nc, .noinc
  165. inc de
  166. .noinc:
  167. dec a
  168. jr nz, .loop
  169. ret
  170. ; Parse string at (HL) as a decimal value and return value in DE.
  171. ; Reads as many digits as it can and stop when:
  172. ; 1 - A non-digit character is read
  173. ; 2 - The number overflows from 16-bit
  174. ; HL is advanced to the character following the last successfully read char.
  175. ; Error conditions are:
  176. ; 1 - There wasn't at least one character that could be read.
  177. ; 2 - Overflow.
  178. ; Sets Z on success, unset on error.
  179. parseDecimal:
  180. ; First char is special: it has to succeed.
  181. ld a, (hl)
  182. ; Parse the decimal char at A and extract it's 0-9 numerical value. Put the
  183. ; result in A.
  184. ; On success, the carry flag is reset. On error, it is set.
  185. add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff
  186. sub 0xff-9 ; maps to 0-9 and carries if not a digit
  187. ret c ; Error. If it's C, it's also going to be NZ
  188. ; During this routine, we switch between HL and its shadow. On one side,
  189. ; we have HL the string pointer, and on the other side, we have HL the
  190. ; numerical result. We also use EXX to preserve BC, saving us a push.
  191. exx ; HL as a result
  192. ld h, 0
  193. ld l, a ; load first digit in without multiplying
  194. .loop:
  195. exx ; HL as a string pointer
  196. inc hl
  197. ld a, (hl)
  198. exx ; HL as a numerical result
  199. ; same as other above
  200. add a, 0xff-'9'
  201. sub 0xff-9
  202. jr c, .end
  203. ld b, a ; we can now use a for overflow checking
  204. add hl, hl ; x2
  205. sbc a, a ; a=0 if no overflow, a=0xFF otherwise
  206. ld d, h
  207. ld e, l ; de is x2
  208. add hl, hl ; x4
  209. rla
  210. add hl, hl ; x8
  211. rla
  212. add hl, de ; x10
  213. rla
  214. ld d, a ; a is zero unless there's an overflow
  215. ld e, b
  216. add hl, de
  217. adc a, a ; same as rla except affects Z
  218. ; Did we oveflow?
  219. jr z, .loop ; No? continue
  220. ; error, NZ already set
  221. exx ; HL is now string pointer, restore BC
  222. ; HL points to the char following the last success.
  223. ret
  224. .end:
  225. push hl ; --> lvl 1, result
  226. exx ; HL as a string pointer, restore BC
  227. pop de ; <-- lvl 1, result
  228. cp a ; ensure Z
  229. ret
  230. ; *** Forth-specific part ***
  231. ; Return address of scratchpad in HL
  232. pad:
  233. ld hl, (HERE)
  234. ld a, PADDING
  235. jp addHL
  236. ; Advance (INPUTPOS) until a non-whitespace is met. If needed,
  237. ; call fetchline.
  238. ; Set HL to newly set (INPUTPOS)
  239. toword:
  240. ld hl, (INPUTPOS)
  241. ; skip leading whitespace
  242. dec hl ; offset leading "inc hl"
  243. .loop:
  244. inc hl
  245. ld a, (hl)
  246. or a
  247. ; When at EOL, fetch a new line directly
  248. jr z, .empty
  249. cp ' '+1
  250. jr c, .loop
  251. ret
  252. .empty:
  253. call fetchline
  254. jr toword
  255. ; Read word from (INPUTPOS) and return, in HL, a null-terminated word.
  256. ; Advance (INPUTPOS) to the character following the whitespace ending the
  257. ; word.
  258. ; When we're at EOL, we call fetchline directly, so this call always returns
  259. ; a word.
  260. readword:
  261. call toword
  262. push hl ; --> lvl 1. that's our result
  263. .loop:
  264. inc hl
  265. ld a, (hl)
  266. ; special case: is A null? If yes, we will *not* inc A so that we don't
  267. ; go over the bounds of our input string.
  268. or a
  269. jr z, .noinc
  270. cp ' '+1
  271. jr nc, .loop
  272. ; we've just read a whitespace, HL is pointing to it. Let's transform
  273. ; it into a null-termination, inc HL, then set (INPUTPOS).
  274. xor a
  275. ld (hl), a
  276. inc hl
  277. .noinc:
  278. ld (INPUTPOS), hl
  279. pop hl ; <-- lvl 1. our result
  280. ret ; Z set from XOR A
  281. ; Sets Z if (HL) == E and (HL+1) == D
  282. HLPointsDE:
  283. ld a, (hl)
  284. cp e
  285. ret nz ; no
  286. inc hl
  287. ld a, (hl)
  288. dec hl
  289. cp d ; Z has our answer
  290. ret
  291. HLPointsNUMBER:
  292. push de
  293. ld de, NUMBER
  294. call HLPointsDE
  295. pop de
  296. ret
  297. HLPointsLIT:
  298. push de
  299. ld de, LIT
  300. call HLPointsDE
  301. pop de
  302. ret
  303. HLPointsBR:
  304. push de
  305. ld de, FBR
  306. call HLPointsDE
  307. jr z, .end
  308. ld de, BBR
  309. call HLPointsDE
  310. .end:
  311. pop de
  312. ret
  313. ; Skip the compword where HL is currently pointing. If it's a regular word,
  314. ; it's easy: we inc by 2. If it's a NUMBER, we inc by 4. If it's a LIT, we skip
  315. ; to after null-termination.
  316. compSkip:
  317. call HLPointsNUMBER
  318. jr z, .isNum
  319. call HLPointsBR
  320. jr z, .isBranch
  321. call HLPointsLIT
  322. jr nz, .isWord
  323. ; We have a literal
  324. inc hl \ inc hl
  325. call strskip
  326. inc hl ; byte after word termination
  327. ret
  328. .isNum:
  329. ; skip by 4
  330. inc hl
  331. ; continue to isBranch
  332. .isBranch:
  333. ; skip by 3
  334. inc hl
  335. ; continue to isWord
  336. .isWord:
  337. ; skip by 2
  338. inc hl \ inc hl
  339. ret
  340. ; Find the entry corresponding to word where (HL) points to and sets DE to
  341. ; point to that entry.
  342. ; Z if found, NZ if not.
  343. find:
  344. push hl
  345. push bc
  346. ld de, (CURRENT)
  347. ld bc, CODELINK_OFFSET
  348. .inner:
  349. ; DE is a wordref, let's go to beginning of struct
  350. push de ; --> lvl 1
  351. or a ; clear carry
  352. ex de, hl
  353. sbc hl, bc
  354. ex de, hl ; We're good, DE points to word name
  355. ld a, NAMELEN
  356. call strncmp
  357. pop de ; <-- lvl 1, return to wordref
  358. jr z, .end ; found
  359. call .prev
  360. jr nz, .inner
  361. ; Z set? end of dict unset Z
  362. inc a
  363. .end:
  364. pop bc
  365. pop hl
  366. ret
  367. ; For DE being a wordref, move DE to the previous wordref.
  368. ; Z is set if DE point to 0 (no entry). NZ if not.
  369. .prev:
  370. dec de \ dec de \ dec de ; prev field
  371. call intoDE
  372. ; DE points to prev. Is it zero?
  373. xor a
  374. or d
  375. or e
  376. ; Z will be set if DE is zero
  377. ret
  378. ; Write compiled data from HL into IY, advancing IY at the same time.
  379. wrCompHL:
  380. ld (iy), l
  381. inc iy
  382. ld (iy), h
  383. inc iy
  384. ret
  385. ; Spit name + prev in (HERE) and adjust (HERE) and (CURRENT)
  386. ; HL points to new (HERE)
  387. entryhead:
  388. call readword
  389. ld de, (HERE)
  390. call strcpy
  391. ex de, hl ; (HERE) now in HL
  392. ld de, (CURRENT)
  393. ld a, NAMELEN
  394. call addHL
  395. call DEinHL
  396. ; Set word flags: not IMMED, not UNWORD, so it's 0
  397. xor a
  398. ld (hl), a
  399. inc hl
  400. ld (CURRENT), hl
  401. ld (HERE), hl
  402. ret
  403. ; Sets Z if wordref at HL is of the IMMEDIATE type
  404. HLisIMMED:
  405. dec hl
  406. bit FLAG_IMMED, (hl)
  407. inc hl
  408. ; We need an invert flag. We want to Z to be set when flag is non-zero.
  409. jp toggleZ
  410. ; Sets Z if wordref at HL is of the UNWORD type
  411. HLisUNWORD:
  412. dec hl
  413. bit FLAG_UNWORD, (hl)
  414. inc hl
  415. ; We need an invert flag. We want to Z to be set when flag is non-zero.
  416. jp toggleZ
  417. ; Sets Z if wordref at (HL) is of the IMMEDIATE type
  418. HLPointsUNWORD:
  419. push hl
  420. call intoHL
  421. call HLisUNWORD
  422. pop hl
  423. ret
  424. ; Checks flags Z and S and sets BC to 0 if Z, 1 if C and -1 otherwise
  425. flagsToBC:
  426. ld bc, 0
  427. ret z ; equal
  428. inc bc
  429. ret m ; >
  430. ; <
  431. dec bc
  432. dec bc
  433. ret
  434. ; Write DE in (HL), advancing HL by 2.
  435. DEinHL:
  436. ld (hl), e
  437. inc hl
  438. ld (hl), d
  439. inc hl
  440. ret
  441. fetchline:
  442. call printcrlf
  443. call stdioReadLine
  444. ld (INPUTPOS), hl
  445. ret