Forth Lesson 19
This lesson is based on a transcript of a conversation in #olpc-devel with Mitch Bradley.
Further Uses of CREATE DOES>
The create ... does> construct is also used in the linked-list library. Consider the definition of this word which stores a pointer to the list of unused nodes of a given type and stores the size of a node of this type.
: nodetype: \ name ( size -- ) aligned create 2 /n* user#, 0 over ! na1+ ! \ Free list, size does> >user ;
Here is an example use of this word. It builds on the idea of a struct from the previous lesson:
list: wifi-list listnode /ssid field >ssid \ SSID /mac-adr field >my-mac \ My mac address /mac-adr field >his-mac \ Target mac address /c field >valid? \ Validity flag /c field >channel \ Channel /c field >ktype \ Security type /c field >ctype-p \ Pairwise key cipher type /c field >ctype-g \ Group key cipher type /c field >atype \ Authentication & AKM suite type /rcnt field >last-rcnt \ Last replay counter /ptk field >ptk \ Pairwise temporal key (PTK) nodetype: wifi-node \ Data to persist between opens
The list: word is an alias for the word variable. The first line creates a new variable named wifi-list. Listnode reserves enough space at the beginning of the struct for a pointer to the next node. Each field maintains a running offset into the struct. That offset is on the top of the stack at the beginning of the final line. 'nodetype:' pads that to an alignment boundary, reserves space for a pointer and a 32-bit integer in the user data area, and creates a new word 'wifi-node' which pushes the address of this space in the user data area onto the stack. The first four bytes of the reserved space in the user data area point to the free-node list and the second four bytes store the alignment-padded length of a node of this type.
Now that you have some idea what the word does, you might ask why bother with the user data area. Storing information there makes 'nodetype:' complicated, doesn't it? I asked Mitch why it wasn't just:
: nodetype: \ name ( size -- ) aligned 0 create , , \ Free list, size does> ;
He had an excellent answer, and he explained why the user data area is a better home for the free list and size.
Debugging and the User Data Area
The dictionary contains code and information on the layout of the user data area. The user data area itself contains, well, data. For efficient multithreading, it makes sense to limit writes to the dictionary and share it between threads, while providing each thread with its own stacks and user data area. The stacks and the user data area are relatively small, so it's lightweight to rely on them for thread-local storage.
The user data area also is involved in debugging. The first thing in the user data area is a short code fragment:
lodsd \\ eax = esi++; eax jmp \\ jump to the address in eax
When the debugger is on, this fragment is replaced with a larger shard which checks whether the debugger should engage on the word to which the CPU is about to jump. Placing this fragment at the beginning of the user data area has the following benefits: jumping through a register is faster and smaller than jumping with a compiled-in offset, sharing this register with the user data area pointer saves a register, and placing the load and jump here ensures that it is in writable memory so that it can be replaced efficiently for debugging.
Thus endeth the lesson.