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.

532 lines
9.5KB

  1. ; *** Variables ***
  2. ; Value of `SP` when basic was first invoked. This is where SP is going back to
  3. ; on restarts.
  4. .equ BAS_INITSP BAS_RAMSTART
  5. ; Pointer to next line to run. If nonzero, it means that the next line is
  6. ; the first of the list. This is used by GOTO to indicate where to jump next.
  7. ; Important note: this is **not** a line number, it's a pointer to a line index
  8. ; in buffer. If it's not zero, its a valid pointer.
  9. .equ BAS_PNEXTLN @+2
  10. ; Points to a routine to call when a command isn't found in the "core" cmd
  11. ; table. This gives the opportunity to glue code to configure extra commands.
  12. .equ BAS_FINDHOOK @+2
  13. .equ BAS_RAMEND @+2
  14. ; *** Code ***
  15. basInit:
  16. ld (BAS_INITSP), sp
  17. call varInit
  18. call bufInit
  19. xor a
  20. ld (BAS_PNEXTLN), a
  21. ld (BAS_PNEXTLN+1), a
  22. ld hl, unsetZ
  23. ld (BAS_FINDHOOK), hl
  24. ret
  25. basStart:
  26. ld hl, .welcome
  27. call printstr
  28. call printcrlf
  29. jr basLoop
  30. .welcome:
  31. .db "Collapse OS", 0
  32. basLoop:
  33. ld hl, .sPrompt
  34. call printstr
  35. call stdioReadLine
  36. call printcrlf
  37. call parseDecimalC
  38. jr z, .number
  39. ld de, basCmds1
  40. call basCallCmds
  41. jr z, basLoop
  42. ; Error
  43. call basERR
  44. jr basLoop
  45. .number:
  46. call rdSep
  47. call bufAdd
  48. jp nz, basERR
  49. jr basLoop
  50. .sPrompt:
  51. .db "> ", 0
  52. ; Tries to find command specified in (DE) (must be null-terminated) in cmd
  53. ; table in (HL). If found, sets IX to point to the associated routine. If
  54. ; not found, calls BAS_FINDHOOK so that we look through extra commands
  55. ; configured by glue code.
  56. ; Destroys HL.
  57. ; Z is set if found, unset otherwise.
  58. basFindCmd:
  59. .loop:
  60. call strcmp
  61. call strskip
  62. inc hl ; point to routine
  63. jr z, .found ; Z from strcmp
  64. inc hl \ inc hl ; skip routine
  65. ld a, (hl)
  66. inc a ; was it 0xff?
  67. jr nz, .loop ; no
  68. dec a ; unset Z
  69. ret
  70. .found:
  71. call intoHL
  72. push hl \ pop ix
  73. ret
  74. ; Call command in (HL) after having looked for it in cmd table in (DE).
  75. ; If found, jump to it. If not found, try (BAS_FINDHOOK). If still not found,
  76. ; unset Z. We expect commands to set Z on success. Therefore, when calling
  77. ; basCallCmd results in NZ, we're not sure where the error come from, but
  78. ; well...
  79. basCallCmd:
  80. ; let's see if it's a variable assignment.
  81. call varTryAssign
  82. ret z ; Done!
  83. push de ; --> lvl 1.
  84. ld de, SCRATCHPAD
  85. call rdWord
  86. ; cmdname to find in (DE)
  87. ; How lucky, we have a legitimate use of "ex (sp), hl"! We have the
  88. ; cmd table in the stack, which we want in HL and we have the rest of
  89. ; the cmdline in (HL), which we want in the stack!
  90. ex (sp), hl
  91. call basFindCmd
  92. jr z, .skip
  93. ; not found, try BAS_FINDHOOK
  94. ld ix, (BAS_FINDHOOK)
  95. call callIX
  96. .skip:
  97. ; regardless of the result, we need to balance the stack.
  98. ; Bring back rest of the command string from the stack
  99. pop hl ; <-- lvl 1
  100. ret nz
  101. ; cmd found, skip whitespace and then jump!
  102. call rdSep
  103. jp (ix)
  104. ; Call a series of ':'-separated commands in (HL) using cmd table in (DE).
  105. ; Stop processing as soon as one command unsets Z.
  106. basCallCmds:
  107. ; Commands are not guaranteed at all to preserve HL and DE, so we
  108. ; preserve them ourselves here.
  109. push hl ; --> lvl 1
  110. push de ; --> lvl 2
  111. call basCallCmd
  112. pop de ; <-- lvl 2
  113. pop hl ; <-- lvl 1
  114. ret nz
  115. call toEnd
  116. ret z ; no more cmds
  117. ; we met a ':', we have more cmds
  118. inc hl
  119. call basCallCmds
  120. ; move the the end of the string so that we don't run cmds following a
  121. ; ':' twice.
  122. call strskip
  123. ret
  124. basERR:
  125. ld hl, .sErr
  126. call printstr
  127. jp printcrlf
  128. .sErr:
  129. .db "ERR", 0
  130. ; *** Commands ***
  131. ; A command receives its argument through (HL), which is already placed to
  132. ; either:
  133. ; 1 - the end of the string if the command has no arg.
  134. ; 2 - the beginning of the arg, with whitespace properly skipped.
  135. ;
  136. ; Commands are expected to set Z on success.
  137. basLIST:
  138. call bufFirst
  139. jr nz, .end
  140. .loop:
  141. ld e, (ix)
  142. ld d, (ix+1)
  143. ld hl, SCRATCHPAD
  144. call fmtDecimal
  145. call printstr
  146. ld a, ' '
  147. call stdioPutC
  148. call bufStr
  149. call printstr
  150. call printcrlf
  151. call bufNext
  152. jr z, .loop
  153. .end:
  154. cp a ; ensure Z
  155. ret
  156. basRUN:
  157. call .maybeGOTO
  158. jr nz, .loop ; IX already set
  159. call bufFirst
  160. ret nz
  161. .loop:
  162. call bufStr
  163. ld de, basCmds2
  164. push ix ; --> lvl 1
  165. call basCallCmds
  166. pop ix ; <-- lvl 1
  167. jp nz, .err
  168. call .maybeGOTO
  169. jr nz, .loop ; IX already set
  170. call bufNext
  171. jr z, .loop
  172. cp a ; ensure Z
  173. ret
  174. .err:
  175. ; Print line number, then return NZ (which will print ERR)
  176. ld e, (ix)
  177. ld d, (ix+1)
  178. ld hl, SCRATCHPAD
  179. call fmtDecimal
  180. call printstr
  181. ld a, ' '
  182. call stdioPutC
  183. jp unsetZ
  184. ; This returns the opposite Z result as the one we usually see: Z is set if
  185. ; we **don't** goto, unset if we do. If we do, IX is properly set.
  186. .maybeGOTO:
  187. ld de, (BAS_PNEXTLN)
  188. ld a, d
  189. or e
  190. ret z
  191. ; we goto
  192. push de \ pop ix
  193. ; we need to reset our goto marker
  194. ld de, 0
  195. ld (BAS_PNEXTLN), de
  196. ret
  197. basPRINT:
  198. ; Do we have arguments at all? if not, it's not an error, just print
  199. ; crlf
  200. ld a, (hl)
  201. or a
  202. jr z, .end
  203. ; Is our arg a string literal?
  204. call spitQuoted
  205. jr z, .chkAnother ; string printed, skip to chkAnother
  206. ld de, SCRATCHPAD
  207. call rdWord
  208. push hl ; --> lvl 1
  209. ex de, hl
  210. call parseExpr
  211. jr nz, .parseError
  212. ld hl, SCRATCHPAD
  213. call fmtDecimalS
  214. call printstr
  215. pop hl ; <-- lvl 1
  216. .chkAnother:
  217. ; Do we have another arg?
  218. call rdSep
  219. jr z, .another
  220. ; no, we can stop here
  221. .end:
  222. cp a ; ensure Z
  223. jp printcrlf
  224. .another:
  225. ; Before we jump to basPRINT, let's print a space
  226. ld a, ' '
  227. call stdioPutC
  228. jr basPRINT
  229. .parseError:
  230. ; unwind the stack before returning
  231. pop hl ; <-- lvl 1
  232. ret
  233. basGOTO:
  234. ld de, SCRATCHPAD
  235. call rdWord
  236. ex de, hl
  237. call parseExpr
  238. ret nz
  239. call bufFind
  240. jr nz, .notFound
  241. push ix \ pop de
  242. ; Z already set
  243. jr .end
  244. .notFound:
  245. ld de, 0
  246. ; Z already unset
  247. .end:
  248. ld (BAS_PNEXTLN), de
  249. ret
  250. ; evaluate truth condition at (HL) and set A to its value
  251. ; Z for success (but not truth!)
  252. _basEvalCond:
  253. push hl ; --> lvl 1. original arg
  254. ld de, SCRATCHPAD
  255. call rdWord
  256. ex de, hl
  257. call parseTruth
  258. pop hl ; <-- lvl 1. restore
  259. ret
  260. basIF:
  261. call _basEvalCond
  262. ret nz ; error
  263. or a
  264. ret z
  265. ; expr is true, execute next
  266. ; (HL) back to beginning of args, skip to next arg
  267. call toSepOrEnd
  268. call rdSep
  269. ret nz
  270. ld de, basCmds2
  271. jp basCallCmds
  272. basWHILE:
  273. push hl ; --> lvl 1
  274. call _basEvalCond
  275. jr nz, .stop ; error
  276. or a
  277. jr z, .stop
  278. ret z
  279. ; expr is true, execute next
  280. ; (HL) back to beginning of args, skip to next arg
  281. call toSepOrEnd
  282. call rdSep
  283. ret nz
  284. ld de, basCmds2
  285. call basCallCmds
  286. pop hl ; <-- lvl 1
  287. jr basWHILE
  288. .stop:
  289. pop hl ; <-- lvl 1
  290. ret
  291. basINPUT:
  292. ; If our first arg is a string literal, spit it
  293. call spitQuoted
  294. call rdSep
  295. call stdioReadLine
  296. call parseExpr
  297. ld (VAR_TBL), de
  298. call printcrlf
  299. cp a ; ensure Z
  300. ret
  301. basPEEK:
  302. call basDEEK
  303. ret nz
  304. ; set MSB to 0
  305. xor a ; sets Z
  306. ld (VAR_TBL+1), a
  307. ret
  308. basPOKE:
  309. call rdExpr
  310. ret nz
  311. ; peek address in IX. Save it for later
  312. push ix ; --> lvl 1
  313. call rdSep
  314. call rdExpr
  315. push ix \ pop hl
  316. pop ix ; <-- lvl 1
  317. ret nz
  318. ; Poke!
  319. ld (ix), l
  320. ret
  321. basDEEK:
  322. call rdExpr
  323. ret nz
  324. ; peek address in IX. Let's peek and put result in DE
  325. ld e, (ix)
  326. ld d, (ix+1)
  327. ld (VAR_TBL), de
  328. cp a ; ensure Z
  329. ret
  330. basDOKE:
  331. call basPOKE
  332. ld (ix+1), h
  333. ret
  334. basOUT:
  335. call rdExpr
  336. ret nz
  337. ; out address in IX. Save it for later
  338. push ix ; --> lvl 1
  339. call rdSep
  340. call rdExpr
  341. push ix \ pop hl
  342. pop bc ; <-- lvl 1
  343. ret nz
  344. ; Out!
  345. out (c), l
  346. cp a ; ensure Z
  347. ret
  348. basIN:
  349. call rdExpr
  350. ret nz
  351. push ix \ pop bc
  352. ld d, 0
  353. in e, (c)
  354. ld (VAR_TBL), de
  355. ; Z set from rdExpr
  356. ret
  357. basGETC:
  358. call stdioGetC
  359. ld (VAR_TBL), a
  360. xor a
  361. ld (VAR_TBL+1), a
  362. ret
  363. basPUTC:
  364. call rdExpr
  365. ret nz
  366. push ix \ pop hl
  367. ld a, l
  368. call stdioPutC
  369. xor a ; set Z
  370. ret
  371. basPUTH:
  372. call rdExpr
  373. ret nz
  374. push ix \ pop hl
  375. ld a, l
  376. call printHex
  377. xor a ; set Z
  378. ret
  379. basSLEEP:
  380. call rdExpr
  381. ret nz
  382. push ix \ pop hl
  383. .loop:
  384. ld a, h ; 4T
  385. or l ; 4T
  386. ret z ; 5T
  387. dec hl ; 6T
  388. jr .loop ; 12T
  389. basADDR:
  390. call rdWord
  391. ex de, hl
  392. ld de, .specialTbl
  393. .loop:
  394. ld a, (de)
  395. or a
  396. jr z, .notSpecial
  397. cp (hl)
  398. jr z, .found
  399. inc de \ inc de \ inc de
  400. jr .loop
  401. .notSpecial:
  402. ; not found, find cmd. needle in (HL)
  403. ex de, hl ; now in (DE)
  404. ld hl, basCmds1
  405. call basFindCmd
  406. jr z, .foundCmd
  407. ; no core command? let's try the find hook.
  408. ld ix, (BAS_FINDHOOK)
  409. call callIX
  410. ret nz
  411. .foundCmd:
  412. ; We have routine addr in IX
  413. ld (VAR_TBL), ix
  414. cp a ; ensure Z
  415. ret
  416. .found:
  417. ; found special thing. Put in "A".
  418. inc de
  419. call intoDE
  420. ld (VAR_TBL), de
  421. ret ; Z set from .found jump.
  422. .specialTbl:
  423. .db '$'
  424. .dw SCRATCHPAD
  425. .db 0
  426. basUSR:
  427. call rdExpr
  428. ret nz
  429. push ix \ pop iy
  430. ; We have our address to call. Now, let's set up our registers.
  431. ; HL comes from variable H. H's index is 7*2.
  432. ld hl, (VAR_TBL+14)
  433. ; DE comes from variable D. D's index is 3*2
  434. ld de, (VAR_TBL+6)
  435. ; BC comes from variable B. B's index is 1*2
  436. ld bc, (VAR_TBL+2)
  437. ; IX comes from variable X. X's index is 23*2
  438. ld ix, (VAR_TBL+46)
  439. ; and finally, A
  440. ld a, (VAR_TBL)
  441. call callIY
  442. basR2Var: ; Just send reg to vars. Used in basPgmHook
  443. ; Same dance, opposite way
  444. ld (VAR_TBL), a
  445. ld (VAR_TBL+46), ix
  446. ld (VAR_TBL+2), bc
  447. ld (VAR_TBL+6), de
  448. ld (VAR_TBL+14), hl
  449. cp a ; USR never errors out
  450. ret
  451. ; Command table format: Null-terminated string followed by a 2-byte routine
  452. ; pointer.
  453. ; direct only
  454. basCmds1:
  455. .db "list", 0
  456. .dw basLIST
  457. .db "run", 0
  458. .dw basRUN
  459. .db "clear", 0
  460. .dw bufInit
  461. ; statements
  462. basCmds2:
  463. .db "print", 0
  464. .dw basPRINT
  465. .db "goto", 0
  466. .dw basGOTO
  467. .db "if", 0
  468. .dw basIF
  469. .db "while", 0
  470. .dw basWHILE
  471. .db "input", 0
  472. .dw basINPUT
  473. .db "peek", 0
  474. .dw basPEEK
  475. .db "poke", 0
  476. .dw basPOKE
  477. .db "deek", 0
  478. .dw basDEEK
  479. .db "doke", 0
  480. .dw basDOKE
  481. .db "out", 0
  482. .dw basOUT
  483. .db "in", 0
  484. .dw basIN
  485. .db "getc", 0
  486. .dw basGETC
  487. .db "putc", 0
  488. .dw basPUTC
  489. .db "puth", 0
  490. .dw basPUTH
  491. .db "sleep", 0
  492. .dw basSLEEP
  493. .db "addr", 0
  494. .dw basADDR
  495. .db "usr", 0
  496. .dw basUSR
  497. .db 0xff ; end of table