@@ -6,60 +6,53 @@ TODO | |||
# Programming AVR chips | |||
To program AVR chips, you need a device that provides the SPI protocol. The | |||
device built in the rc2014/sdcard recipe fits the bill. Make sure you can | |||
override the SPI clock because the system clock will be too fast for most AVR | |||
chips, which are usually running at 1MHz. Because the SPI clock needs to be a | |||
4th of that, a safe frequency for SPI communication would be 250kHz. | |||
Because you will not be using your system clock, you'll also need to override | |||
SPI_DELAY in your xcomp unit: the default value for this is 2 NOP, which only | |||
works when you use the system clock. | |||
Alternatively, you could run your whole system at 250kHz, but that's going to be | |||
really slow. | |||
The AVR programmer device is really simple: Wire SPI connections to proper AVR | |||
pins as described in the MCU's datasheet. Note that this device will be the same | |||
as the one you'll use for any modern SPI-based AVR programmer, with RESET | |||
replacing SS. | |||
(TODO: design a SPI relay that supports more than one device. At the time of | |||
this writing, one has to disconnect the SD card reader before enabling the AVR | |||
programmer) | |||
To program AVR chips, you need a device that provides the SPI | |||
protocol. The device built in the rc2014/sdcard recipe fits the | |||
bill. Make sure you can override the SPI clock because the sys- | |||
tem clock will be too fast for most AVR chips, which are usually | |||
running at 1MHz. Because the SPI clock needs to be a 4th of | |||
that, a safe frequency for SPI communication would be 250kHz. | |||
The AVR programmer device is really simple: Wire SPI connections | |||
to proper AVR pins as described in the MCU's datasheet. Note | |||
that this device will be the same as the one you'll use for any | |||
modern SPI-based AVR programmer, with RESET replacing SS. | |||
The AVR programming code is at B690. | |||
Before you begin programming the chip, the device must be deselected. Ensure | |||
with "(spid)". | |||
Before you begin programming the chip, the device must be desel- | |||
ected. Ensure with "0 (spie)". | |||
Then, you initiate programming mode with "asp$", and then issue your commands. | |||
Then, you initiate programming mode with "asp$", and then issue | |||
your commands. | |||
Each command will verify that it's in sync, that is, that its 3rd exchange | |||
echoes the byte that was sent in the 2nd exchange. If it doesn't, the command | |||
aborts with "AVR err". | |||
Each command will verify that it's in sync, that is, that its | |||
3rd exchange echoes the byte that was sent in the 2nd exchange. | |||
If it doesn't, the command aborts with "AVR err". | |||
# Access fuses | |||
You get/set they values with "aspfx@/aspfx!", x being one of "l" (low fuse), | |||
"h" (high fuse), "e" (extended fuse). | |||
You get/set they values with "aspfx@/aspfx!", x being one of "l" | |||
(low fuse), "h" (high fuse), "e" (extended fuse). | |||
# Access flash | |||
Writing to AVR's flash is done in batch mode, page by page. To this end, the | |||
chip has a buffer which is writable byte-by-byte. | |||
Writing to AVR's flash is done in batch mode, page by page. To | |||
this end, the chip has a buffer which is writable byte-by-byte. | |||
Writing to the flash begins with a call to asperase, which erases the whole | |||
chip. It seems possible to erase flash page-by-page through parallel | |||
programming, but the SPI protocol doesn't expose it, we have to erase the whole | |||
chip. Then, you write to the buffer using aspfb! and then write to a page using | |||
aspfp!. Example to write 0x1234 to the first byte of the first page: | |||
Writing to the flash begins with a call to asperase, which | |||
erases the whole chip. It seems possible to erase flash page-by- | |||
page through parallel programming, but the SPI protocol doesn't | |||
expose it, we have to erase the whole chip. Then, you write to | |||
the buffer using aspfb! and then write to a page using aspfp!. | |||
Example to write 0x1234 to the first byte of the first page: | |||
asperase 0x1234 0 aspfb! 0 aspfp! | |||
Please note that aspfb! deals with *words*, not bytes. If, for example, you want | |||
to hook it to A!*, make sure you use AMOVEW instead of AMOVE. You will need to | |||
create a wrapper word around aspfb! that divides dst addr by 2 because AMOVEW | |||
use byte-based addresses but aspfb! uses word-based ones. You also have to make | |||
sure that A@* points to @ (or another word-based fetcher) instead of its default | |||
value of C@. | |||
Please note that aspfb! deals with *words*, not bytes. If, for | |||
example, you want to hook it to A!*, make sure you use AMOVEW | |||
instead of AMOVE. You will need to create a wrapper word around | |||
aspfb! that divides dst addr by 2 because AMOVEW use byte-based | |||
addresses but aspfb! uses word-based ones. You also have to make | |||
sure that A@* points to @ (or another word-based fetcher) | |||
instead of its default value of C@. |
@@ -7,32 +7,32 @@ What is Collapse OS? It is a binary placed either in ROM on | |||
in RAM by a bootloader. That binary, when executed, initializes | |||
itself to a Forth interpreter. In most cases, that Forth | |||
interpreter will have some access to a mass storage device, | |||
which allows it to access Collapse OS' disk blocks and come | |||
to this block to bootstrap itself some more. | |||
which allows it to access Collapse OS' disk blocks and bootstrap | |||
itself some more. | |||
This binary can be separated in 5 distinct layers: | |||
1. Boot code (B280) | |||
2. Boot words (B305) | |||
3. Core words (low) (B350) | |||
4. Drivers | |||
5. Core words (high) | |||
1. Arch-specific boot code (B280 for Z80) | |||
2. Arch-specific boot words (B305 for Z80) | |||
3. Arch-independant core words (low) (B350) | |||
4. Drivers, might contain arch-specific code | |||
5. Arch-independant core words (high) (B380) | |||
# Boot code (B280) | |||
# Boot code | |||
This part contains core routines that underpins Forth fundamen- | |||
tal structures: dict navigation and search, PSP/RSP bounds | |||
checks, word types. | |||
tal structures: dict navigation and FIND, PSP/RSP bounds checks, | |||
word types. | |||
It also of course does core initialization: set RSP/PSP, HERE | |||
CURRENT, then call BOOT. | |||
It also contains what we call the "stable ABI" in its first | |||
0x100 bytes. The beginning og the dict is intertwined in this | |||
0x100 bytes. The beginning of the dict is intertwined in this | |||
layer because EXIT, (br), (?br) and (loop) are part of the | |||
stable ABI. | |||
# Boot words (B305) | |||
# Boot words | |||
Then come the implementation of core Forth words in native | |||
assembly. Performance is not Collapse OS' primary design goal, | |||
@@ -42,7 +42,7 @@ to implement our words in Forth. | |||
However, some words are in this section for performance | |||
reasons. Sometimes, the gain is too great to pass up. | |||
# Core words (low) (B350) | |||
# Core words (low) | |||
Then comes the part where we begin defining words in Forth. | |||
Core words are designed to be cross-compiled (B260), from a | |||
@@ -63,7 +63,7 @@ precisely to fit drivers in there. This way, they have access | |||
to a pretty good vocabulary and they're also give the oppor- | |||
tunity to provide (emit) and (key). | |||
# Core words (high) (B350) | |||
# Core words (high) | |||
Then come EMIT, KEY and everything that depend on it, until | |||
we have a full Forth interpreter. At the very end, we define | |||
@@ -82,7 +82,7 @@ new xcomp (cross compilation) unit. Let's look at its | |||
anatomy. First, we have constants. Some of them are device- | |||
specific, but some of them are always there. SYSVARS is the | |||
address at which the RAM starts on the system. System variables | |||
will go there and use 0x80 bytes. See B80. | |||
will go there and use 0x80 bytes. See impl.txt. | |||
HERESTART determines where... HERE is at startup. 0 means | |||
"same as CURRENT". | |||
@@ -100,13 +100,10 @@ same way. Drivers are a bit tricker and machine specific. I | |||
can't help you there, you'll have to use your wits. | |||
After we've loaded the high part of the core words, we're at | |||
the "wrapping up" part. We add what we call a "hook word" (an | |||
empty word with a single letter name) which doesn't cost us | |||
much and can be very useful if we need to augment the binary | |||
with more words, and at that point we have our future boot | |||
CURRENT, which PC yields. That is why we write it to the | |||
LATEST field of the stable ABI: This value will be used at | |||
boot. | |||
the "wrapping up" part. We add what we call a "hook word", an | |||
empty word with a single letter name. This allows us to boot | |||
with CURRENT pointing to "source init" content rather than being | |||
an actual wordref. | |||
After the last word of the dictionary comes the "source init" | |||
part. The boot sequence is designed to interpret whatever comes | |||
@@ -19,7 +19,7 @@ ample, a machine with only a serial console can't. | |||
# Block editor | |||
The Block editor augments the built-in work LIST with words to | |||
The Block editor augments the built-in word LIST with words to | |||
modify the block currently being loaded. Block saving happens | |||
automatically: Whenever you load a new block, the old block, if | |||
changed, is saved to disk first. You can force that with FLUSH. | |||
@@ -37,6 +37,9 @@ You can insert text at the current position with "i". For exam- | |||
ple, "i foo" inserts "foo" at cursor. Text to the right of it | |||
is shifted right. Any content above 64 chars is lost. | |||
Why "i" and not "I"? Because "I" is already used and we don't | |||
want to overshadow it. | |||
You can "put" a new line with "P". "P foo" will insert a new | |||
line under the cursor and place "foo" on it. The last line of | |||
the block is lost. "U" does the same thing, but on the line | |||
@@ -68,8 +71,7 @@ P xxx: put typed IBUF on selected line. | |||
U xxx: insert typed IBUF on selected line. | |||
F xxx: find typed FBUF in block, starting from current | |||
position+1. If not found, don't move. | |||
i xxx: insert typed IBUF at cursor. "i" is to avoid shadowing | |||
core word "I". | |||
i xxx: insert typed IBUF at cursor. | |||
Y: Copy n characters after cursor into IBUF, n being length of | |||
FBUF. | |||
X ( n -- ): Delete X chars after cursor and place in IBUF. | |||
@@ -118,7 +120,8 @@ the previously opened block. | |||
'w' moves forward by "modifier" words. 'b' moves backward. | |||
'W' moves to end-of-word. 'B', backwards. | |||
'I', 'F', 'Y', 'X' and 'E' invoke the corresponding command | |||
'I', 'F', 'Y', 'X' and 'E' invoke the corresponding command from | |||
command-based editor. | |||
'o' inserts a blank line after the cursor. 'O', before. | |||
@@ -168,33 +168,31 @@ territory, identical) | |||
On boot, we jump to the "main" routine in B289 which does | |||
very few things. | |||
1. Set SP to PS_ADDR and IX to RS_ADDR | |||
2. Sets HERE to SYSVARS+0x80. | |||
3. Sets CURRENT to value of LATEST field in stable ABI. | |||
1. Set SP to PS_ADDR and IX to RS_ADDR. | |||
2. Set CURRENT to value of LATEST field in stable ABI. | |||
3. Set HERE to HERESTART const if defined, to CURRENT other- | |||
wise. | |||
4. Execute the word referred to by 0x04 (BOOT) in stable ABI. | |||
In a normal system, BOOT is in core words at B396 and does a | |||
few things: | |||
1. Initialize all overrides to 0. | |||
2. Write LATEST in BOOT C< PTR ( see below ) | |||
3. Set "C<*", the word that C< calls to (boot<). | |||
2. Write LATEST in BOOT C< PTR ( see below ). | |||
3. Set "C<*", the word that C< calls, to (boot<). | |||
4. Call INTERPRET which interprets boot source code until | |||
ASCII EOT (4) is met. This usually init drivers. | |||
ASCII EOT (4) is met. This usually initializes drivers. | |||
5. Initialize rdln buffer, _sys entry (for EMPTY), prints | |||
"CollapseOS" and then calls (main). | |||
6. (main) interprets from rdln input (usually from KEY) until | |||
EOT is met, then calls BYE. | |||
In RAM-only environment, we will typically have a | |||
"CURRENT @ HERE !" line during init to have HERE begin at the | |||
end of the binary instead of RAMEND. | |||
# Stable ABI | |||
Across all architectures, some offset are referred to by off- | |||
sets that don't change (well, not without some binary manipu- | |||
lation). Here's the complete list of these references: | |||
The Stable ABI lives at the beginning of the binary and prov- | |||
ides a way for Collapse OS code to access values that would | |||
otherwise be difficult to access. Here's the complete list of | |||
these references: | |||
04 BOOT addr 06 (uflw) addr 08 LATEST | |||
13 (oflw) addr 2b (s) wordref 33 2>R wordref | |||
@@ -24,7 +24,7 @@ a bit tight at first, having this limit saves us a non- | |||
negligible amount of resource usage. | |||
The reasoning behind this intentional limit is that huge | |||
branches are generally a indicator that a logic ought to be | |||
branches are generally an indicator that a logic ought to be | |||
simplified. So here's one more constraint for you to help you | |||
towards simplicity. | |||
@@ -41,9 +41,9 @@ from KEY, puts it in a buffer, then yields the buffered line, | |||
one character at a time. | |||
Both C< and KEY can be overridden by setting an alternate | |||
routine at the proper RAM offset (see B80). For example, C< | |||
overrides are used during LOAD so that input comes from | |||
disk blocks instead of keyboard. | |||
routine at the proper RAM offset (see impl.txt). For example, | |||
C< overrides are used during LOAD so that input comes from disk | |||
blocks instead of keyboard. | |||
KEY overrides can be used to, for example, temporarily give | |||
prompt control to a RS-232 device instead of the keyboard. | |||
@@ -108,8 +108,13 @@ try to strive towards a few goals: | |||
1. Block 0 contains documentation discovery core keys to the | |||
uninitiated. | |||
2. First section (up to B100) is usage documentation. | |||
3. B100-B200 are for runtime usage utilities | |||
4. B200-B500 are for bootstrapping | |||
2. B1-B4 are for a master index of blocks. | |||
3. B5-B199 are for runtime usage utilities | |||
4. B200-B599 are for bootstrapping | |||
5. The rest is for recipes. | |||
6. I'm not sure yet how I'll organize multiple arches. | |||
Blocks are currently not organized neatly. I'm planning the | |||
extraction of recipes into some kind of block "overlays" that | |||
would live in the recipes subfolder so each recipe would build | |||
its own specific blkfs which would contain only its recipe code, | |||
starting at B600. |