Accessing Memory and Peripheral Registers
Forth is great for debugging embedded hardware. We saw previously how you can use fetch and store to modify variables in memory. Memory locations, and peripheral registers which also exist in the memory space, may be examined and modified in the same way.
@ (“fetch”) and ! (“store”) work with 16-bit wide locations. To access 8-bit wide locations, use the words c@ (“c-fetch”) and c! (“c-store”) respectively. To access 32-bit wide locations, use 2@ and 2!.
You can use Forth to examine the contents of memory, or peripheral registers:
@ (“fetch”) and ! (“store”) work with 16-bit wide locations. To access 8-bit wide locations, use the words c@ (“c-fetch”) and c! (“c-store”) respectively. To access 32-bit wide locations, use 2@ and 2!.
You can use Forth to examine the contents of memory, or peripheral registers:
$110c @
Note: Don't be tempted to access arbitrary locations. Accessing certain regions of the memory space can generate an Address Error and cause you Udamonic computer to restart. So only access memory or register locations that you know you can.
Fetch (@) and store (!) must be given even addresses. Using an odd address will cause an address error in the processor and your Scamp will reset. If you need to access odd addresses, use c! and c@.
Fetch (@) and store (!) must be given even addresses. Using an odd address will cause an address error in the processor and your Scamp will reset. If you need to access odd addresses, use c! and c@.
Fetch takes the top entry on the stack as the address, in this case $0180, and fetches the 16-bit value at that address.
You can set memory or peripheral registers using c! for 8-bit registers and ! for 16-bit registers and memory locations. In this example, c! stores the byte $41 to address in memory $110c:
You can set memory or peripheral registers using c! for 8-bit registers and ! for 16-bit registers and memory locations. In this example, c! stores the byte $41 to address in memory $110c:
$41 $110c c!
Parameters and addresses may be in any mix of decimal, binary or hex. For example, storing an 8-bit binary number to an address specified in hex:
%11011001 $110c c!
Remember that you can do this from within a word, or interactively from the command prompt. This means that from the prompt, you can interactively check, modify and tinker with peripheral registers - a very powerful debugging tool!
Let’s create a Forth program called status that will read the content of a 16-bit register located at address $02e4 and place the result onto the stack.
Let’s create a Forth program called status that will read the content of a 16-bit register located at address $02e4 and place the result onto the stack.
: status $02e4 @ ;
This can be manually run from the prompt to examine the register during debugging:
status u.
And since Forth's base can easily be changed to hex or binary, the bits of the register can be viewed in whatever base is most useful:
hex status u.
binary status u.
Perhaps you would like to continually monitor the status register while you tweak some hardware:
: showstatus ( -- , output status until a key is pressed )
begin
cr
status u.
key?
until
;
Once debugging is complete, the word status can be used as part of the main application, and showstatus can be forgotten.
Working with Blocks of Memory
The word dump is useful for examining a region of memory. dump takes two operands from the stack, in the form address size dump. For example, to output 96 values starting at the current scratchpad location:
pad 96 dump
will produce something like:
4288 :00 00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 ..%.............
4304 :00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
4320 :00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
4336 :00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
4352 :00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
4368 :00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ ok
Note that a size value less than 15 ($0f) will be ignored by dump.
For each line, dump prints out an address, followed by 16 bytes of data and the ASCII interpretation of those bytes. Any hex byte that doesn’t have an ASCII representation appears as a dot.
Memory can be cleared (each location made equal to 0) using the erase word. For example, to write 32 zeroes starting from pad address:
Memory can be cleared (each location made equal to 0) using the erase word. For example, to write 32 zeroes starting from pad address:
pad 32 erase
Similar to erase, fill can be used to fill a region of memory with a particular value. In this example, we store the value 255 to 32 locations starting from pad:
pad 32 255 fill