@@ -6,60 +6,53 @@ TODO | |||||
# Programming AVR chips | # 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. | 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 | # 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 | # 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! | 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 | in RAM by a bootloader. That binary, when executed, initializes | ||||
itself to a Forth interpreter. In most cases, that Forth | itself to a Forth interpreter. In most cases, that Forth | ||||
interpreter will have some access to a mass storage device, | 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: | 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- | 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 | It also of course does core initialization: set RSP/PSP, HERE | ||||
CURRENT, then call BOOT. | CURRENT, then call BOOT. | ||||
It also contains what we call the "stable ABI" in its first | 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 | layer because EXIT, (br), (?br) and (loop) are part of the | ||||
stable ABI. | stable ABI. | ||||
# Boot words (B305) | |||||
# Boot words | |||||
Then come the implementation of core Forth words in native | Then come the implementation of core Forth words in native | ||||
assembly. Performance is not Collapse OS' primary design goal, | 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 | However, some words are in this section for performance | ||||
reasons. Sometimes, the gain is too great to pass up. | 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. | Then comes the part where we begin defining words in Forth. | ||||
Core words are designed to be cross-compiled (B260), from a | 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- | to a pretty good vocabulary and they're also give the oppor- | ||||
tunity to provide (emit) and (key). | tunity to provide (emit) and (key). | ||||
# Core words (high) (B350) | |||||
# Core words (high) | |||||
Then come EMIT, KEY and everything that depend on it, until | Then come EMIT, KEY and everything that depend on it, until | ||||
we have a full Forth interpreter. At the very end, we define | 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- | anatomy. First, we have constants. Some of them are device- | ||||
specific, but some of them are always there. SYSVARS is the | specific, but some of them are always there. SYSVARS is the | ||||
address at which the RAM starts on the system. System variables | 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 | HERESTART determines where... HERE is at startup. 0 means | ||||
"same as CURRENT". | "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. | 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 | 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" | After the last word of the dictionary comes the "source init" | ||||
part. The boot sequence is designed to interpret whatever comes | 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 | # 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 | modify the block currently being loaded. Block saving happens | ||||
automatically: Whenever you load a new block, the old block, if | automatically: Whenever you load a new block, the old block, if | ||||
changed, is saved to disk first. You can force that with FLUSH. | 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 | ple, "i foo" inserts "foo" at cursor. Text to the right of it | ||||
is shifted right. Any content above 64 chars is lost. | 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 | 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 | 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 | 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. | U xxx: insert typed IBUF on selected line. | ||||
F xxx: find typed FBUF in block, starting from current | F xxx: find typed FBUF in block, starting from current | ||||
position+1. If not found, don't move. | 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 | Y: Copy n characters after cursor into IBUF, n being length of | ||||
FBUF. | FBUF. | ||||
X ( n -- ): Delete X chars after cursor and place in IBUF. | 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 forward by "modifier" words. 'b' moves backward. | ||||
'W' moves to end-of-word. 'B', backwards. | '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. | '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 | On boot, we jump to the "main" routine in B289 which does | ||||
very few things. | 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. | 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 | In a normal system, BOOT is in core words at B396 and does a | ||||
few things: | few things: | ||||
1. Initialize all overrides to 0. | 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 | 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 | 5. Initialize rdln buffer, _sys entry (for EMPTY), prints | ||||
"CollapseOS" and then calls (main). | "CollapseOS" and then calls (main). | ||||
6. (main) interprets from rdln input (usually from KEY) until | 6. (main) interprets from rdln input (usually from KEY) until | ||||
EOT is met, then calls BYE. | 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 | # 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 | 04 BOOT addr 06 (uflw) addr 08 LATEST | ||||
13 (oflw) addr 2b (s) wordref 33 2>R wordref | 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. | negligible amount of resource usage. | ||||
The reasoning behind this intentional limit is that huge | 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 | simplified. So here's one more constraint for you to help you | ||||
towards simplicity. | towards simplicity. | ||||
@@ -41,9 +41,9 @@ from KEY, puts it in a buffer, then yields the buffered line, | |||||
one character at a time. | one character at a time. | ||||
Both C< and KEY can be overridden by setting an alternate | 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 | KEY overrides can be used to, for example, temporarily give | ||||
prompt control to a RS-232 device instead of the keyboard. | 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 | 1. Block 0 contains documentation discovery core keys to the | ||||
uninitiated. | 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. | 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. |