collapseos/apps
Virgil Dupras 80ab395823 forth: Simplify execution model
Change the mainloop so that words are executed immediately after they're read.
This greatly simplifies execution model and allow the "DEFINE" word to become
an IMMEDIATE and stop its "copy from compiled words" scheme.

The downside to this is that flow control words no longer work when being used
directly in the input buffer. They only work as part of a definition.

It also broke "RECURSE", but I've replaced it with "BEGIN" and "AGAIN".

Another effect of this change is that definitions can now span multiple lines.

All in all, it feels good to get rid of that COMPBUF...
2020-03-15 22:46:17 -04:00
..
at28w at28w: don't use lib/args 2019-12-29 21:02:04 -05:00
basic trs80/floppy: implement write 2020-02-22 12:09:43 -05:00
ed zasm: use printcrlf instead of hardcoded CRLF 2020-02-23 18:52:25 -05:00
forth forth: Simplify execution model 2020-03-15 22:46:17 -04:00
lib forth: add "UNWORD" flag 2020-03-12 11:39:27 -04:00
memt recipes/rc2014/zasm: use BASIC shell 2019-12-11 13:01:51 -05:00
sdct recipes/rc2014/zasm: use BASIC shell 2019-12-11 13:01:51 -05:00
zasm forth: add "UNWORD" flag 2020-03-12 11:39:27 -04:00
README.md lib/expr: make EXPR_PARSE "tail" HL 2019-12-30 19:24:53 -05:00

User applications

This folder contains code designed to be "userspace" application. Unlike the kernel, which always stay in memory. Those apps here will more likely be loaded in RAM from storage, ran, then discarded so that another userspace program can be run.

That doesn't mean that you can't include that code in your kernel though, but you will typically not want to do that.

Userspace convention

We execute a userspace application by calling the address it's loaded into.

This means that userspace applications must be assembled with a proper .org, otherwise labels in its code will be wrong.

The .org, it is not specified by glue code of the apps themselves. It is expected to be set either in the user.h file to through zasm 3rd argument.

That a userspace is called also means that an application, when finished running, is expected to return with a regular ret and a clean stack.

Whatever calls the userspace app (usually, it will be the shell), should set HL to a pointer to unparsed arguments in string form, null terminated.

The userspace application is expected to set A on return. 0 means success, non-zero means error.

A userspace application can expect the SP pointer to be properly set. If it moves it, it should take care of returning it where it was before returning because otherwise, it will break the kernel.

Memory management

Apps in Collapse OS are design to be ROM-compatible, that is, they don't write to addresses that are part of the code's address space.

By default, apps set their RAM to begin at the end of the binary because in most cases, these apps will be ran from RAM. If they're ran from ROM, make sure to set USER_RAMSTART properly in your user.h to ensure that the RAM is placed properly.

Applications that are ran as a shell (the "shell" app, of course, but also, possibly, "basic" and others to come) need a manual override to their main RAMSTART constant: You don't want them to run in the same RAM region as your other userspace apps because if you do, as soon as you launch an app with your shell, its memory is going to be overwritten!

What you'll do then is that you'll reserve some space in your memory layout for the shell and add a special constant in your user.h, which will override the basic one (remember, in zasm, the first .equ for a given constant takes precedence).

For example, if you want a "basic" shell and that you reserve space right after your kernel RAM for it, then your user.h would contain .equ BAS_RAMSTART KERNEL_RAMEND.

You can also include your shell's code directly in the kernel by copying relevant parts of the app's glue unit in your kernel's glue unit. This is often simpler and more efficient. However, if your shell is a big program, it might run into zasm's limits. In that case, you'd have to assemble your shell separately.

Common features

The folder lib/ contains code shared in more than one apps and this has the effect that some concepts are exactly the same in many application. They are therefore sharing documentation, here.

Number literals

There are decimal, hexadecimal and binary literals. A "straight" number is parsed as a decimal. Hexadecimal literals must be prefixed with 0x (0xf4). Binary must be prefixed with 0b (0b01100110).

Decimals and hexadecimal are "flexible". Whether they're written in a byte or a word, you don't need to prefix them with zeroes. Watch out for overflow, however.

Binary literals are also "flexible" (0b110 is fine), but can't go over a byte.

There is also the char literal ('X'), that is, two quotes with a character in the middle. The value of that character is interpreted as-is, without any encoding involved. That is, whatever binary code is written in between those two quotes, it's what is evaluated. Only a single byte at once can be evaluated thus. There is no escaping. ''' results in 0x27. You can't express a newline this way, it's going to mess with the parser.

Expressions

An expression is a bunch of literals or symbols assembled by operators. Supported operators are +, -, *, /, % (modulo), & (bitwise and), | (bitwise or), ^ (bitwise xor), { (shift left), } (shift right). Bitwise operator always operate on the whole 16-bits.

Shift operators break from the << and >> tradition because the complexity if two-sized operator is significant and deemed not worth it. The shift operator shift the left operand X times, X being the right operand.

There is no parenthesis support yet.

Symbols have a different meaning depending on the application. In zasm, it's labels and constants. In basic, it's variables.

Expressions can't contain spaces.

Expressions can have an empty left operand. It will then be considered as 0. This allows signed integers, for example, -42 to be expressed as expected. That form doesn't work well everywhere and is mostly supported for BASIC. In zasm, you're safer with 0-42.