Mirror of CollapseOS
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

446 строки
9.1KB

  1. ; shell
  2. ;
  3. ; Runs a shell over a block device interface.
  4. ; Status: incomplete. As it is now, it spits a welcome prompt, wait for input
  5. ; and compare the first 4 chars of the input with a command table and call the
  6. ; appropriate routine if it's found, an error if it's not.
  7. ;
  8. ; Commands, for now, are partially implemented.
  9. ;
  10. ; See constants below for error codes.
  11. ;
  12. ; All numerical values in the Collapse OS shell are represented and parsed in
  13. ; hexadecimal form, without prefix or suffix.
  14. ; *** DEFINES ***
  15. ; SHELL_GETC: Macro that calls a GetC routine
  16. ; SHELL_PUTC: Macro that calls a PutC routine
  17. ; SHELL_RAMSTART
  18. ; *** CONSTS ***
  19. ; number of entries in shellCmdTbl
  20. SHELL_CMD_COUNT .equ 4
  21. ; maximum number of bytes to receive as args in all commands. Determines the
  22. ; size of the args variable.
  23. SHELL_CMD_ARGS_MAXSIZE .equ 3
  24. ; The command that was type isn't known to the shell
  25. SHELL_ERR_UNKNOWN_CMD .equ 0x01
  26. ; Arguments for the command weren't properly formatted
  27. SHELL_ERR_BAD_ARGS .equ 0x02
  28. ; Size of the shell command buffer. If a typed command reaches this size, the
  29. ; command is flushed immediately (same as pressing return).
  30. SHELL_BUFSIZE .equ 0x20
  31. ; *** VARIABLES ***
  32. ; Memory address that the shell is currently "pointing at" for peek, load, call
  33. ; operations. Set with seek.
  34. SHELL_MEM_PTR .equ SHELL_RAMSTART
  35. ; Used to store formatted hex values just before printing it.
  36. SHELL_HEX_FMT .equ SHELL_MEM_PTR+2
  37. ; Places where we store arguments specifiers and where resulting values are
  38. ; written to after parsing.
  39. SHELL_CMD_ARGS .equ SHELL_HEX_FMT+2
  40. ; Command buffer. We read types chars into this buffer until return is pressed
  41. ; This buffer is null-terminated and we don't keep an index around: we look
  42. ; for the null-termination every time we write to it. Simpler that way.
  43. SHELL_BUF .equ SHELL_CMD_ARGS+SHELL_CMD_ARGS_MAXSIZE
  44. SHELL_RAMEND .equ SHELL_BUF+SHELL_BUFSIZE
  45. ; *** CODE ***
  46. shellInit:
  47. xor a
  48. ld (SHELL_MEM_PTR), a
  49. ld (SHELL_BUF), a
  50. ; print welcome
  51. ld hl, .welcome
  52. call printstr
  53. ret
  54. .welcome:
  55. .db "Collapse OS", ASCII_CR, ASCII_LF, "> ", 0
  56. shellLoop:
  57. ; First, let's wait until something is typed.
  58. SHELL_GETC
  59. ; got it. Now, is it a CR or LF?
  60. cp ASCII_CR
  61. jr z, .do ; char is CR? do!
  62. cp ASCII_LF
  63. jr z, .do ; char is LF? do!
  64. ; Echo the received character right away so that we see what we type
  65. SHELL_PUTC
  66. ; Ok, gotta add it do the buffer
  67. ; save char for later
  68. ex af, af'
  69. ld hl, SHELL_BUF
  70. call findnull ; HL points to where we need to write
  71. ; A is the number of chars in the buf
  72. cp SHELL_BUFSIZE
  73. jr z, .do ; A == bufsize? then our buffer is full. do!
  74. ; bring the char back in A
  75. ex af, af'
  76. ; Buffer not full, not CR or LF. Let's put that char in our buffer and
  77. ; read again.
  78. ld (hl), a
  79. ; Now, write a zero to the next byte to properly terminate our string.
  80. inc hl
  81. xor a
  82. ld (hl), a
  83. jr shellLoop
  84. .do:
  85. call printcrlf
  86. ld hl, SHELL_BUF
  87. call shellParse
  88. ; empty our buffer by writing a zero to its first char
  89. xor a
  90. ld (hl), a
  91. ld hl, .prompt
  92. call printstr
  93. jr shellLoop
  94. .prompt:
  95. .db "> ", 0
  96. printcrlf:
  97. ld a, ASCII_CR
  98. SHELL_PUTC
  99. ld a, ASCII_LF
  100. SHELL_PUTC
  101. ret
  102. ; Parse command (null terminated) at HL and calls it
  103. shellParse:
  104. push af
  105. push bc
  106. push de
  107. push hl
  108. push ix
  109. ld de, shellCmdTbl
  110. ld a, SHELL_CMD_COUNT
  111. ld b, a
  112. .loop:
  113. ld a, 4 ; 4 chars to compare
  114. call strncmp
  115. jr z, .found
  116. ld a, 7 + SHELL_CMD_ARGS_MAXSIZE
  117. call addDE
  118. djnz .loop
  119. ; exhausted loop? not found
  120. ld a, SHELL_ERR_UNKNOWN_CMD
  121. call shellPrintErr
  122. jr .end
  123. .found:
  124. ; we found our command. Now, let's parse our args.
  125. ld a, 4
  126. call addHL
  127. ; Now, let's have DE point to the argspecs
  128. ld a, 4
  129. call addDE
  130. ; We're ready to parse args
  131. call shellParseArgs
  132. cp 0
  133. jr nz, .parseerror
  134. ld hl, SHELL_CMD_ARGS
  135. ; Args parsed, now we can load the routine address and call it.
  136. ; let's have DE point to the jump line
  137. ld a, SHELL_CMD_ARGS_MAXSIZE
  138. call addDE
  139. ld ixh, d
  140. ld ixl, e
  141. ; Ready to roll!
  142. call callIX
  143. jr .end
  144. .parseerror:
  145. ld a, SHELL_ERR_BAD_ARGS
  146. call shellPrintErr
  147. .end:
  148. pop ix
  149. pop hl
  150. pop de
  151. pop bc
  152. pop af
  153. ret
  154. ; Print the error code set in A (in hex)
  155. shellPrintErr:
  156. push af
  157. push hl
  158. ld hl, .str
  159. call printstr
  160. ld hl, SHELL_HEX_FMT
  161. call fmtHexPair
  162. ld a, 2
  163. call printnstr
  164. call printcrlf
  165. pop hl
  166. pop af
  167. ret
  168. .str:
  169. .db "ERR ", 0
  170. ; Parse arguments at (HL) with specifiers at (DE) into (SHELL_CMD_ARGS).
  171. ; (HL) should point to the character *just* after the name of the command
  172. ; because we verify, in the case that we have args, that we have a space there.
  173. ;
  174. ; Args specifiers are a series of flag for each arg:
  175. ; Bit 0 - arg present: if unset, we stop parsing there
  176. ; Bit 1 - is word: this arg is a word rather than a byte. Because our
  177. ; destination are bytes anyway, this doesn't change much except
  178. ; for whether we expect a space between the hex pairs. If set,
  179. ; you still need to have a specifier for the second part of
  180. ; the multibyte.
  181. ; Bit 2 - optional: If set and not present during parsing, we don't error out
  182. ; and write zero
  183. ;
  184. ; Sets A to nonzero if there was an error during parsing, zero otherwise.
  185. ; If there was an error during parsing, carry is set.
  186. shellParseArgs:
  187. push bc
  188. push de
  189. push hl
  190. push ix
  191. ld ix, SHELL_CMD_ARGS
  192. ld a, SHELL_CMD_ARGS_MAXSIZE
  193. ld b, a
  194. xor c
  195. .loop:
  196. ; init the arg value to a default 0
  197. xor a
  198. ld (ix), a
  199. ld a, (hl)
  200. ; is this the end of the line?
  201. cp 0
  202. jr z, .endofargs
  203. ; do we have a proper space char?
  204. cp ' '
  205. jr z, .hasspace ; We're fine
  206. ; is our previous arg a multibyte? (argspec still in C)
  207. bit 1, c
  208. jr z, .error ; bit not set? error
  209. dec hl ; offset the "inc hl" below
  210. .hasspace:
  211. ; Get the specs
  212. ld a, (de)
  213. bit 0, a ; do we have an arg?
  214. jr z, .error ; not set? then we have too many args
  215. ld c, a ; save the specs for the next loop
  216. inc hl ; (hl) points to a space, go next
  217. call parseHexPair
  218. jr c, .error
  219. ; we have a good arg and we need to write A in (IX).
  220. ld (ix), a
  221. ; Good! increase counters
  222. inc de
  223. inc ix
  224. inc hl ; get to following char (generally a space)
  225. djnz .loop
  226. ; If we get here, it means that our next char *has* to be a null char
  227. ld a, (hl)
  228. cp 0
  229. jr z, .success ; zero? great!
  230. jr .error
  231. .endofargs:
  232. ; We encountered our null char. Let's verify that we either have no
  233. ; more args or that they are optional
  234. ld a, (de)
  235. cp 0
  236. jr z, .success ; no arg? success
  237. bit 2, a
  238. jr nz, .success ; if set, arg is optional. success
  239. jr .error
  240. .success:
  241. xor a
  242. jr .end
  243. .error:
  244. inc a
  245. .end:
  246. pop ix
  247. pop hl
  248. pop de
  249. pop bc
  250. ret
  251. ; *** COMMANDS ***
  252. ; When these commands are called, HL points to the first byte of the
  253. ; parsed command args.
  254. ; Set memory pointer to the specified address (word).
  255. ; Example: seek 01fe
  256. shellSeek:
  257. push af
  258. push de
  259. push hl
  260. ; z80 is little endian. in a "ld hl, (nn)" op, L is loaded from the
  261. ; first byte, H is loaded from the second.
  262. ld a, (hl)
  263. ld (SHELL_MEM_PTR+1), a
  264. inc hl
  265. ld a, (hl)
  266. ld (SHELL_MEM_PTR), a
  267. ld de, (SHELL_MEM_PTR)
  268. ld hl, SHELL_HEX_FMT
  269. ld a, d
  270. call fmtHexPair
  271. ld a, 2
  272. call printnstr
  273. ld a, e
  274. call fmtHexPair
  275. ld a, 2
  276. call printnstr
  277. call printcrlf
  278. pop hl
  279. pop de
  280. pop af
  281. ret
  282. ; peek byte where memory pointer points to any display its value. If the
  283. ; optional numerical byte arg is supplied, this number of bytes will be printed
  284. ;
  285. ; Example: peek 2 (will print 2 bytes)
  286. shellPeek:
  287. push af
  288. push bc
  289. push de
  290. push hl
  291. ld a, (hl)
  292. cp 0
  293. jr nz, .arg1isset ; if arg1 is set, no need for a default
  294. ld a, 1 ; default for arg1
  295. .arg1isset:
  296. ld b, a
  297. ld hl, (SHELL_MEM_PTR)
  298. .loop: ld a, (hl)
  299. ex hl, de
  300. ld hl, SHELL_HEX_FMT
  301. call fmtHexPair
  302. ld a, 2
  303. call printnstr
  304. ex hl, de
  305. inc hl
  306. djnz .loop
  307. call printcrlf
  308. .end:
  309. pop hl
  310. pop de
  311. pop bc
  312. pop af
  313. ret
  314. ; Load the specified number of bytes (max 0xff) from IO and write them in the
  315. ; current memory pointer (which doesn't change). For now, we can only load from
  316. ; SHELL_GETC, but a method of selecting IO sources is coming, making this
  317. ; command much more useful.
  318. ; Control is returned to the shell only after all bytes are read.
  319. ;
  320. ; Example: load 42
  321. shellLoad:
  322. push af
  323. push bc
  324. push hl
  325. ld a, (hl)
  326. ld b, a
  327. ld hl, (SHELL_MEM_PTR)
  328. .loop: SHELL_GETC
  329. ld (hl), a
  330. inc hl
  331. djnz .loop
  332. .end:
  333. pop hl
  334. pop bc
  335. pop af
  336. ret
  337. ; Calls the routine where the memory pointer currently points. This can take two
  338. ; parameters, A and HL. The first one is a byte, the second, a word. These are
  339. ; the values that A and HL are going to be set to just before calling.
  340. ; Example: run 42 cafe
  341. shellCall:
  342. push af
  343. push hl
  344. push ix
  345. ; Let's recap here. At this point, we have:
  346. ; 1. The address we want to execute in (SHELL_MEM_PTR)
  347. ; 2. our A arg as the first byte of (HL)
  348. ; 2. our HL arg as (HL+1) and (HL+2)
  349. ; Ready, set, go!
  350. ld a, (SHELL_MEM_PTR)
  351. ld ixl, a
  352. ld a, (SHELL_MEM_PTR+1)
  353. ld ixh, a
  354. ld a, (hl)
  355. ex af, af'
  356. inc hl
  357. ld a, (hl)
  358. exx
  359. ld h, a
  360. exx
  361. inc hl
  362. ld a, (hl)
  363. exx
  364. ld l, a
  365. ex af, af'
  366. call callIX
  367. .end:
  368. pop ix
  369. pop hl
  370. pop af
  371. ret
  372. ; Format: 4 bytes name followed by SHELL_CMD_ARGS_MAXSIZE bytes specifiers,
  373. ; followed by 3 bytes jump. fill names with zeroes
  374. shellCmdTbl:
  375. .db "seek", 0b011, 0b001, 0
  376. jp shellSeek
  377. .db "peek", 0b101, 0, 0
  378. jp shellPeek
  379. .db "load", 0b001, 0, 0
  380. jp shellLoad
  381. .db "call", 0b101, 0b111, 0b001
  382. jp shellCall