duskos

dusk os fork
git clone git://git.alexwennerberg.com/duskos
Log | Files | Refs | README | LICENSE

commit 9d0d5210813171c49cac98c732cb55285341f5c2
parent 96c3897cb8e3677a694e4e155f172ef11c86fdfb
Author: Virgil Dupras <hsoft@hardcoded.net>
Date:   Sat,  7 Jan 2023 09:39:59 -0500

Consolidate and document structures

Move the "meta" part of structs (for now, only the Struct and Field definitions
which are not actually used anywhere) in a new lib/struct unit. Move structure
documentation in its own doc/struct page and augment that documentation.

Diffstat:
Afs/doc/struct.txt | 219+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mfs/doc/usage.txt | 137++-----------------------------------------------------------------------------
Afs/lib/struct.fs | 14++++++++++++++
Mfs/tests/kernel.fs | 7-------
Mfs/tests/lib/all.fs | 3++-
Afs/tests/lib/struct.fs | 18++++++++++++++++++
Mfs/xcomp/bootlo.fs | 13-------------
7 files changed, 255 insertions(+), 156 deletions(-)

diff --git a/fs/doc/struct.txt b/fs/doc/struct.txt @@ -0,0 +1,219 @@ +# Structures + +Structures are an effective way to address offsets from base addresses while +keeping the general namespace clean. Structures have a name and a list of fields +and are declared thus: + + struct[ Foo + sfield bar + sfield baz + smethod :bleh + ]struct + +This describes an 12 byte wide struct with 3 fields. + +## Namespace + +Anything goes inside of a struct. Whatever word you define there will be +included in the struct's namespace. That is, a sub dictionary inside the system +dictionary. Those words will not be present in the system dictionary. To access +a word in a namespace, you begin by calling the struct name and write the name +of the word to call inside the namespace. This also works when compiling: + + Foo bar + : myword Foo :bleh ; + : err Foo ; \ error! ";" doesn't exist in Foo namespace + +While inside a struct definition, however, you can access words inside the +struct directly. + + : hello 42 . ; + struct[ Bar + : hello 54 . ; + hello \ prints 54 + : hey hello ; + hey \ prints 54 + ]struct + +## Fields and methods + +"sfield" and "smethod" have a special struct-specific behavior as they +automatically place themselves inside the struct at the correct offset and +increase the struct's size. + +A struct size can be obtained with the "SZ" word automatically added to every +struct. It returns the size, in bytes, of the fields included in the struct. It +can also be used inside a struct definition to get the struct size "up until +now". This can be useful for initialization methods: + + struct[ Foo + sfield bar + sfield baz + smethod :bleh + : :new ( -- 'foo ) here SZ allot0 ; + ]struct + +A struct holds no data by itself and can't be used directly to access fields +from memory. You refer to fields in a struct by supplying it with a source +pointer, like this: + + create data1 1 , 2 , ' mybleh , + create data2 3 , 4 , ' mybleh , + data1 Foo bar . \ prints 1 + data2 Foo baz . \ prints 4 + +Fields obey "to" semantics: + + 42 to+ data1 Foo bar + data1 Foo bar . --> prints 43 + +Field access can be compiled: + + : foobar data2 Foo baz ; + foobar . \ prints 4 + +A method is an alias to a word reference inside a struct. When the method is +invoked, it dereferences the alias and calls it, but it also pushes a reference +of the data structure on top of PS so that the method can work with its data. +By convention, method names start with ":", but nothing forces you to have it. +Example of a method that adds bar to baz: + + : mybleh ( 'data -- n ) dup Foo bar swap Foo baz + ; + +defined before the previous examples, then you could do: + + data1 Foo :bleh . \ prints 3 + data2 Foo :bleh . \ prints 7 + +## Structure binds + +You will often want to bind data to structs. You can do so with "structbind": + + data1 structbind Foo MyData1 + MyData1 :bleh . \ prints 3 + : someword MyData1 bar ; + someword . \ prints 2 + +A structbind is compiled with a level of indirection that allows it to be +rebound. So, you can rebind structbinds with the word "rebind" (we can't use +"to" because that applies to the original bind's field): + + data2 ' MyData1 rebind + MyData1 :bleh . \ prints 7 + someword . \ prints 4 + +All structs have a ":self" method which is a noop, and thus returns a reference +to the associated data. This can be used to get a structbind's data reference: + + data2 :self \ MyData1 is on PS TOS + +## Other field types + +There are several kinds of fields: + +* sfield a 4 byte field +* sfieldw a 2 byte field +* sfieldb a 1 byte field +* sconst a 4 byte field that doesn't obey "to" semantics +* sfield' a field that yields its address instead of a value. Useful for + buffers. It must be called with a size argument. +* smethod A 4 byte pointer to a word that behaves as described above. +* ssmethod A "static" method. Like a method, but we don't copy the struct + address to PS. + +You can also create gaps in the struct with "sallot": + +struct[ Gaps + sfield foo + 42 sallot + sfield bar +]struct + +In this struct, foo's offset is 0 and bar's is 46. + +## Extending a structure + +You can also extend a previous struct with a new struct: + + extends Foo struct[ Bar + sfield bazooka + ]struct + create data3 1 , 2 , ' mybleh , 1234 + data3 Bar bazooka . \ prints 1234 + data3 Bar bar . \ prints 1 + +Extended structs will have their "running size" pick up where the extended +struct left. They also inherit their whole namespace, which means that any word +in the extended namespace can be accessed directly without prefixing it with +the struct's name. Extending a struct does not modify the extended struct. + +If, instead of extending a struct, you want to "augment it", that is, to +supplement a struct that was defined earlier with new elements, you can use +struct+[ in this fashion: + + struct+[ Foo + sfield bazooka + ]struct + +This does not create a new struct, but rather adds a new field to the existing +struct. It is useful for struct that require partial declaration because of +inter-dependency with other pieces of code. + +Warning: do not augment a struct with new fields if it has already been extended +by another struct because this will generate slot conflicts. You can augment a +struct that has been extended, that will work, but only with non-field words. + +## Name conventions + +A structure begins with an uppercase letter and a word in a namespace that is +intended to be called from the outside (so, not only methods) begin with a ":". + +## API + +struct[ <name> -- Create a struct named "name" and enter its definition +]struct -- Exit the definition of the current structure +struct+[ <name> -- Re-open the definition of existing struct "name" +extends <name> -- Make the next struct definition extend struct "name" + +structbind <struct> <name> ( 'data -- ) + Create a new struct bind named "name" binding "'data" to the struct "struct". +rebind ( 'data 'bind -- ) + Rebind struct binding "'bind" to data "'data". + +The following words give meta information on a struct from the basis of its word +address, named "w" below. For a given struct "Foo", you get "w" with "' Foo". + +structdict' w -- a Address of struct namespace dictionary. +structsz w -- sz Size of the fields and methods in the struct. +structsz' w -- 'sz Address of the size field cell. +structlastfield' w -- a Address of the last field of the struct. + +Fields words, described above, only work inside struct[ definitions and must be +followed by their name: + +sfield +sfieldw +sfieldb +sconst +smethod +ssmethod + +These words (also described above) have a slightly different signature: + +sfield' <name> ( size -- ) +sallot ( size -- ) + +## Inspecting structures + +The unit lib/struct allows to go meta on structs and inspect them. It defines +the Struct and Field structures. + +Struct + dict Structure namespace dictionary + size Total size in bytes of struct fields + lastfield Link to last field of struct (a Field) + +Field + next Next field (this is a linked list) + offset Offset, in bytes, of that field + size Size, in bytes, of that field (1, 2 or 4) diff --git a/fs/doc/usage.txt b/fs/doc/usage.txt @@ -333,141 +333,8 @@ this way, we can avoid crashes, making the system a bit easier to debug. ## Structures -Structures are an effective way to address offsets from base addresses while -keeping the general namespace clean. Structures have a name and a list of fields -and are declared thus: - - struct[ Foo - sfield bar - sfield baz - smethod :bleh - ]struct - -This describes an 12 byte wide struct with 3 fields. - -Anything goes inside of a struct. Whatever word you define there will be -included in the struct's namespace. Those words will not be present in the -system dictionary. While inside a struct definition, however, you can access -words inside the struct directly. - -"sfield" and "smethod" have a special struct-specific behavior as they -automatically place themselves inside the struct at the correct offset and -increase the struct's size. - -A struct size can be obtained with the "SZ" word automatically added to every -struct. It returns the size, in bytes, of the fields included in the struct. It -can also be used inside a struct definition to get the struct size "up until -now". This can be useful for initialization methods: - - struct[ Foo - sfield bar - sfield baz - smethod :bleh - : :new ( -- 'foo ) here SZ allot0 ; - ]struct - -A struct holds no data by itself and can't be used directly to access fields -from memory. You refer to fields in a struct by supplying it with a source -pointer, like this: - - create data1 1 , 2 , ' mybleh , - create data2 3 , 4 , ' mybleh , - data1 Foo bar . \ prints 1 - data2 Foo baz . \ prints 4 - -Fields obey "to" semantics: - - 42 to+ data1 Foo bar - data1 Foo bar . --> prints 43 - -Field access can be compiled: - - : foobar data2 Foo baz ; - foobar . \ prints 4 - -A method is an alias to a word reference inside a struct. When the method is -invoked, it dereferences the alias and calls it, but it also pushes a reference -of the data structure on top of PS so that the method can work with its data. -By convention, method names start with ":", but nothing forces you to have it. -Example of a method that adds bar to baz: - - : mybleh ( 'data -- n ) dup Foo bar swap Foo baz + ; - -defined before the previous examples, then you could do: - - data1 Foo :bleh . \ prints 3 - data2 Foo :bleh . \ prints 7 - -You will often want to bind data to structs. You can do so with "structbind": - - data1 structbind Foo MyData1 - MyData1 :bleh . \ prints 3 - : someword MyData1 bar ; - someword . \ prints 2 - -A structbind is compiled with a level of indirection that allows it to be -rebound. So, you can rebind structbinds with the word "rebind" (we can't use -"to" because that applies to the original bind's field): - - data2 ' MyData1 rebind - MyData1 :bleh . \ prints 7 - someword . \ prints 4 - -All structs have a ":self" method which is a noop, and thus returns a reference -to the associated data. This can be used to get a structbind's data reference: - - data2 :self \ MyData1 is on PS TOS - -There are several kinds of fields: - -* sfield a 4 byte field -* sfieldw a 2 byte field -* sfieldb a 1 byte field -* sconst a 4 byte field that doesn't obey "to" semantics -* sfield' a field that yields its address instead of a value. Useful for - buffers. It must be called with a size argument. -* smethod A 4 byte pointer to a word that behaves as described above. -* ssmethod A "static" method. Like a method, but we don't copy the struct - address to PS. - -You can also create gaps in the struct with "sallot": - -struct[ Gaps - sfield foo - 42 sallot - sfield bar -]struct - -In this struct, foo's offset is 0 and bar's is 46. You can also extend a -previous struct with a new struct: - - extends Foo struct[ Bar - sfield bazooka - ]struct - create data3 1 , 2 , ' mybleh , 1234 - data3 Bar bazooka . \ prints 1234 - data3 Bar bar . \ prints 1 - -Extended structs will have their "running size" pick up where the extended -struct left. They also inherit their whole namespace, which means that any word -in the extended namespace can be accessed directly without prefixing it with -the struct's name. Extending a struct does not modify the extended struct. - -If, instead of extending a struct, you want to "augment it", that is, to -supplement a struct that was defined earlier with new elements, you can use -struct+[ in this fashion: - - struct+[ Foo - sfield bazooka - ]struct - -This does not create a new struct, but rather adds a new field to the existing -struct. It is useful for struct that require partial declaration because of -inter-dependency with other pieces of code. - -Warning: do not augment a struct with new fields if it has already been extended -by another struct because this will generate slot conflicts. You can augment a -struct that has been extended, that will work, but only with non-field words. +Structures are a wide subject and have their own documentation at doc/struct. +They're used everywhere and you should know about them. ## Input/Output diff --git a/fs/lib/struct.fs b/fs/lib/struct.fs @@ -0,0 +1,14 @@ +\ Utilities around structures. see doc/struct + +struct[ Struct + sfield dict + 1 sallot \ 1b that is always zero after dict link. See doc/arch + sfield size + sfield lastfield \ pointer to field *word* +]struct + +struct[ Field + sfield next + sfield offset + sfield size \ 1, 2 or 4 +]struct diff --git a/fs/tests/kernel.fs b/fs/tests/kernel.fs @@ -130,11 +130,4 @@ extends Foo struct[ Bazooka create data3 7 , 9 c, ' mybleh , 999 , data3 Bazooka bling 999 #eq data3 Bazooka baz 9 #eq - -\ we can iterate fields of a struct -' Bazooka does' Struct lastfield -S" bling" over wordname[] s[]= # -does' Field next S" baz" over wordname[] s[]= # -does' Field next S" bar" over wordname[] s[]= # -does' Field next 0 #eq testend diff --git a/fs/tests/lib/all.fs b/fs/tests/lib/all.fs @@ -2,10 +2,11 @@ f<< /tests/lib/core.fs f<< /tests/lib/bit.fs f<< /tests/lib/str.fs -f<< /tests/lib/crc.fs +f<< /tests/lib/struct.fs f<< /tests/lib/meta.fs f<< /tests/lib/arena.fs f<< /tests/lib/math.fs f<< /tests/lib/stack.fs f<< /tests/lib/tree.fs f<< /tests/lib/fmt.fs +f<< /tests/lib/crc.fs diff --git a/fs/tests/lib/struct.fs b/fs/tests/lib/struct.fs @@ -0,0 +1,18 @@ +?f<< /tests/harness.fs +?f<< /lib/meta.fs +?f<< /lib/struct.fs +testbegin +struct[ Foo + sfield bar + sfieldb baz + smethod :bleh + sfield bling +]struct + +\ we can iterate fields of a struct +' Foo does' Struct lastfield +S" bling" over wordname[] s[]= # +does' Field next S" baz" over wordname[] s[]= # +does' Field next S" bar" over wordname[] s[]= # +does' Field next 0 #eq +testend diff --git a/fs/xcomp/bootlo.fs b/fs/xcomp/bootlo.fs @@ -244,19 +244,6 @@ does> ( 'struct ) : smethod doer _cur e>w structsz , CELLSZ sallot does> @ over + @ execute ; : ssmethod doer _cur e>w structsz , CELLSZ sallot does> @ swap + @ execute ; -struct[ Struct - sfield dict - 1 sallot \ 1b that is always zero after dict link. See doc/arch - sfield size - sfield lastfield \ pointer to field *word* -]struct - -struct[ Field - sfield next - sfield offset - sfield size \ 1, 2 or 4 -]struct - \ 4b link to struct \ 4b link to data : structbind ( 'data -- ) ' doer , , immediate does> ( 'bind -- *to* )