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.

361 lines
7.7KB

  1. ; shell
  2. ;
  3. ; Runs a shell over a block device interface.
  4. ; The shell spits a welcome prompt, wait for input and compare the first 4 chars
  5. ; of the input with a command table and call the appropriate routine if it's
  6. ; found, an error if it's not.
  7. ;
  8. ; To determine the correct routine to call we first go through cmds in
  9. ; shellCmdTbl. This means that we first go through internal cmds, then cmds
  10. ; "grafted" by glue code.
  11. ;
  12. ; If the command isn't found, SHELL_CMDHOOK is called, which should set A to
  13. ; zero if it executes something. Otherwise, SHELL_ERR_UNKNOWN_CMD will be
  14. ; returned.
  15. ;
  16. ; See constants below for error codes.
  17. ;
  18. ; All numerical values in the Collapse OS shell are represented and parsed in
  19. ; hexadecimal form, without prefix or suffix.
  20. ; *** REQUIREMENTS ***
  21. ; err
  22. ; core
  23. ; parse
  24. ; stdio
  25. ; *** DEFINES ***
  26. ; SHELL_EXTRA_CMD_COUNT: Number of extra cmds to be expected after the regular
  27. ; ones. See comment in COMMANDS section for details.
  28. ; SHELL_RAMSTART
  29. ; *** CONSTS ***
  30. ; number of entries in shellCmdTbl
  31. .equ SHELL_CMD_COUNT 6+SHELL_EXTRA_CMD_COUNT
  32. ; *** VARIABLES ***
  33. ; Memory address that the shell is currently "pointing at" for peek, load, call
  34. ; operations. Set with mptr.
  35. .equ SHELL_MEM_PTR SHELL_RAMSTART
  36. ; Places where we store arguments specifiers and where resulting values are
  37. ; written to after parsing.
  38. .equ SHELL_CMD_ARGS SHELL_MEM_PTR+2
  39. ; Pointer to a hook to call when a cmd name isn't found
  40. .equ SHELL_CMDHOOK SHELL_CMD_ARGS+PARSE_ARG_MAXCOUNT
  41. ; Pointer to a routine to call at each shell loop iteration
  42. .equ SHELL_LOOPHOOK SHELL_CMDHOOK+2
  43. .equ SHELL_RAMEND SHELL_LOOPHOOK+2
  44. ; *** CODE ***
  45. shellInit:
  46. xor a
  47. ld (SHELL_MEM_PTR), a
  48. ld (SHELL_MEM_PTR+1), a
  49. ld hl, noop
  50. ld (SHELL_CMDHOOK), hl
  51. ld (SHELL_LOOPHOOK), hl
  52. ; print welcome
  53. ld hl, .welcome
  54. jp printstr
  55. .welcome:
  56. .db "Collapse OS", ASCII_CR, ASCII_LF, "> ", 0
  57. ; Inifite loop that processes input. Because it's infinite, you should jump
  58. ; to it rather than call it. Saves two precious bytes in the stack.
  59. shellLoop:
  60. ; First, call the loop hook
  61. ld ix, (SHELL_LOOPHOOK)
  62. call callIX
  63. ; Then, let's wait until something is typed.
  64. call stdioReadC
  65. jr nz, shellLoop ; not done? loop
  66. ; We're done. Process line.
  67. call printcrlf
  68. call stdioGetLine
  69. call shellParse
  70. ld hl, .prompt
  71. call printstr
  72. jr shellLoop
  73. .prompt:
  74. .db "> ", 0
  75. ; Parse command (null terminated) at HL and calls it
  76. shellParse:
  77. ; first thing: is command empty?
  78. ld a, (hl)
  79. or a
  80. ret z ; empty, nthing to do
  81. push af
  82. push bc
  83. push de
  84. push hl
  85. push ix
  86. ; Before looking for a suitable command, let's make the cmd line more
  87. ; usable by replacing the first ' ' with a null char. This way, cmp is
  88. ; easy to make.
  89. push hl ; --> lvl 1
  90. ld a, ' '
  91. call findchar
  92. jr z, .hasArgs
  93. ; no arg, (HL) is zero to facilitate processing later, add a second
  94. ; null next to that one to indicate unambiguously that we have no args.
  95. inc hl
  96. .hasArgs:
  97. xor a
  98. ld (hl), a
  99. pop hl ; <-- lvl 1, beginning of cmd
  100. ld de, shellCmdTbl
  101. ld b, SHELL_CMD_COUNT
  102. .loop:
  103. push de ; we need to keep that table entry around...
  104. call intoDE ; Jump from the table entry to the cmd addr.
  105. ld a, 4 ; 4 chars to compare
  106. call strncmp
  107. pop de
  108. jr z, .found
  109. inc de
  110. inc de
  111. djnz .loop
  112. ; exhausted loop? not found
  113. ld a, SHELL_ERR_UNKNOWN_CMD
  114. ; Before erroring out, let's try SHELL_HOOK.
  115. ld ix, (SHELL_CMDHOOK)
  116. call callIX
  117. jr z, .end ; oh, not an error!
  118. ; still an error. Might be different than SHELL_ERR_UNKNOWN_CMD though.
  119. ; maybe a routine was called, but errored out.
  120. jr .error
  121. .found:
  122. ; we found our command. DE points to its table entry. Now, let's parse
  123. ; our args.
  124. call intoDE ; Jump from the table entry to the cmd addr.
  125. ; advance the HL pointer to the beginning of the args.
  126. xor a
  127. call findchar
  128. inc hl ; beginning of args
  129. ; Now, let's have DE point to the argspecs
  130. ld a, 4
  131. call addDE
  132. ; We're ready to parse args
  133. ld ix, SHELL_CMD_ARGS
  134. call parseArgs
  135. or a ; cp 0
  136. jr nz, .parseerror
  137. ; Args parsed, now we can load the routine address and call it.
  138. ; let's have DE point to the jump line
  139. ld hl, SHELL_CMD_ARGS
  140. ld a, PARSE_ARG_MAXCOUNT
  141. call addDE
  142. push de \ pop ix
  143. ; Ready to roll!
  144. call callIX
  145. or a ; cp 0
  146. jr nz, .error ; if A is non-zero, we have an error
  147. jr .end
  148. .parseerror:
  149. ld a, SHELL_ERR_BAD_ARGS
  150. .error:
  151. call shellPrintErr
  152. .end:
  153. pop ix
  154. pop hl
  155. pop de
  156. pop bc
  157. pop af
  158. ret
  159. ; Print the error code set in A (in hex)
  160. shellPrintErr:
  161. push af
  162. push hl
  163. ld hl, .str
  164. call printstr
  165. call printHex
  166. call printcrlf
  167. pop hl
  168. pop af
  169. ret
  170. .str:
  171. .db "ERR ", 0
  172. ; *** COMMANDS ***
  173. ; A command is a 4 char names, followed by a PARSE_ARG_MAXCOUNT bytes of
  174. ; argument specs, followed by the routine. Then, a simple table of addresses
  175. ; is compiled in a block and this is what is iterated upon when we want all
  176. ; available commands.
  177. ;
  178. ; Format: 4 bytes name followed by PARSE_ARG_MAXCOUNT bytes specifiers,
  179. ; followed by 3 bytes jump. fill names with zeroes
  180. ;
  181. ; When these commands are called, HL points to the first byte of the
  182. ; parsed command args.
  183. ;
  184. ; If the command is a success, it should set A to zero. If the command results
  185. ; in an error, it should set an error code in A.
  186. ;
  187. ; Extra commands: Other parts might define new commands. You can add these
  188. ; commands to your shell. First, set SHELL_EXTRA_CMD_COUNT to
  189. ; the number of extra commands to add, then add a ".dw"
  190. ; directive *just* after your '.inc "shell.asm"'. Voila!
  191. ;
  192. ; Set memory pointer to the specified address (word).
  193. ; Example: mptr 01fe
  194. shellMptrCmd:
  195. .db "mptr", 0b011, 0b001, 0
  196. shellMptr:
  197. push hl
  198. ; reminder: z80 is little-endian
  199. ld a, (hl)
  200. ld (SHELL_MEM_PTR+1), a
  201. inc hl
  202. ld a, (hl)
  203. ld (SHELL_MEM_PTR), a
  204. ld hl, (SHELL_MEM_PTR)
  205. ld a, h
  206. call printHex
  207. ld a, l
  208. call printHex
  209. call printcrlf
  210. pop hl
  211. xor a
  212. ret
  213. ; peek the number of bytes specified by argument where memory pointer points to
  214. ; and display their value. If 0 is specified, 0x100 bytes are peeked.
  215. ;
  216. ; Example: peek 2 (will print 2 bytes)
  217. shellPeekCmd:
  218. .db "peek", 0b001, 0, 0
  219. shellPeek:
  220. push bc
  221. push hl
  222. ld a, (hl)
  223. ld b, a
  224. ld hl, (SHELL_MEM_PTR)
  225. .loop: ld a, (hl)
  226. call printHex
  227. inc hl
  228. djnz .loop
  229. call printcrlf
  230. .end:
  231. pop hl
  232. pop bc
  233. xor a
  234. ret
  235. ; poke specified number of bytes where memory pointer points and set them to
  236. ; bytes typed through stdioGetC. Blocks until all bytes have been fetched.
  237. shellPokeCmd:
  238. .db "poke", 0b001, 0, 0
  239. shellPoke:
  240. push bc
  241. push hl
  242. ld a, (hl)
  243. ld b, a
  244. ld hl, (SHELL_MEM_PTR)
  245. .loop: call stdioGetC
  246. jr nz, .loop ; nothing typed? loop
  247. ld (hl), a
  248. inc hl
  249. djnz .loop
  250. pop hl
  251. pop bc
  252. xor a
  253. ret
  254. ; Calls the routine where the memory pointer currently points. This can take two
  255. ; parameters, A and HL. The first one is a byte, the second, a word. These are
  256. ; the values that A and HL are going to be set to just before calling.
  257. ; Example: run 42 cafe
  258. shellCallCmd:
  259. .db "call", 0b101, 0b111, 0b001
  260. shellCall:
  261. push hl
  262. push ix
  263. ; Let's recap here. At this point, we have:
  264. ; 1. The address we want to execute in (SHELL_MEM_PTR)
  265. ; 2. our A arg as the first byte of (HL)
  266. ; 2. our HL arg as (HL+1) and (HL+2)
  267. ; Ready, set, go!
  268. ld ix, (SHELL_MEM_PTR)
  269. ld a, (hl)
  270. ex af, af'
  271. inc hl
  272. ld a, (hl)
  273. exx
  274. ld h, a
  275. exx
  276. inc hl
  277. ld a, (hl)
  278. exx
  279. ld l, a
  280. ex af, af'
  281. call callIX
  282. .end:
  283. pop ix
  284. pop hl
  285. xor a
  286. ret
  287. shellIORDCmd:
  288. .db "iord", 0b001, 0, 0
  289. push bc
  290. ld a, (hl)
  291. ld c, a
  292. in a, (c)
  293. call printHex
  294. xor a
  295. pop bc
  296. ret
  297. shellIOWRCmd:
  298. .db "iowr", 0b001, 0b001, 0
  299. push bc
  300. ld a, (hl)
  301. ld c, a
  302. inc hl
  303. ld a, (hl)
  304. out (c), a
  305. xor a
  306. pop bc
  307. ret
  308. ; This table is at the very end of the file on purpose. The idea is to be able
  309. ; to graft extra commands easily after an include in the glue file.
  310. shellCmdTbl:
  311. .dw shellMptrCmd, shellPeekCmd, shellPokeCmd, shellCallCmd
  312. .dw shellIORDCmd, shellIOWRCmd