diff options
author | Konstantin Aladyshev <aladyshev22@gmail.com> | 2022-09-29 14:54:29 +0300 |
---|---|---|
committer | Konstantin Aladyshev <aladyshev22@gmail.com> | 2022-09-29 14:54:29 +0300 |
commit | 6a5ce3785ad5afde00142bb9c72da7c2fa9e32e3 (patch) | |
tree | 6193a683908492c8e6372e9fe6fc2a59e30abb00 /Lessons_uncategorized/Lesson_Varstore_3 | |
parent | 2c51c2df2eb047405fcedb0b7a16d6595aee9890 (diff) | |
download | UEFI-Lessons-6a5ce3785ad5afde00142bb9c72da7c2fa9e32e3.tar.gz UEFI-Lessons-6a5ce3785ad5afde00142bb9c72da7c2fa9e32e3.tar.bz2 UEFI-Lessons-6a5ce3785ad5afde00142bb9c72da7c2fa9e32e3.zip |
Create a driver with 'Buffer Storage' - Part 3
Signed-off-by: Konstantin Aladyshev <aladyshev22@gmail.com>
Diffstat (limited to 'Lessons_uncategorized/Lesson_Varstore_3')
-rw-r--r-- | Lessons_uncategorized/Lesson_Varstore_3/1.png | bin | 0 -> 8603 bytes | |||
-rw-r--r-- | Lessons_uncategorized/Lesson_Varstore_3/2.png | bin | 0 -> 8583 bytes | |||
-rw-r--r-- | Lessons_uncategorized/Lesson_Varstore_3/README.md | 401 |
3 files changed, 401 insertions, 0 deletions
diff --git a/Lessons_uncategorized/Lesson_Varstore_3/1.png b/Lessons_uncategorized/Lesson_Varstore_3/1.png Binary files differnew file mode 100644 index 0000000..5e02e52 --- /dev/null +++ b/Lessons_uncategorized/Lesson_Varstore_3/1.png diff --git a/Lessons_uncategorized/Lesson_Varstore_3/2.png b/Lessons_uncategorized/Lesson_Varstore_3/2.png Binary files differnew file mode 100644 index 0000000..6fbcbbf --- /dev/null +++ b/Lessons_uncategorized/Lesson_Varstore_3/2.png diff --git a/Lessons_uncategorized/Lesson_Varstore_3/README.md b/Lessons_uncategorized/Lesson_Varstore_3/README.md new file mode 100644 index 0000000..b2f8985 --- /dev/null +++ b/Lessons_uncategorized/Lesson_Varstore_3/README.md @@ -0,0 +1,401 @@ +# `BlockToConfig()`/`ConfigToBlock()` + +With the `Buffer Storage` you have a freedom about what you do on store/update operations, but still your form storage is just some data packed in a structure. + +So add it as a global variable to our `UefiLessonsPkg/HIIFormDataElementsVarstore/HIIFormDataElementsVarstore.c` file: +```cpp +VARIABLE_STRUCTURE FormStorage; +``` + +Now let's understand our task in the `Extractonfig()` function: we need to parse incoming request, get some parts of our structure and return them in a configuration response. + +In other words we need to correspond `OFFSET=<...>&WIDTH=<...>` combinations from the request to the data in the structure. But you've already know that there can be many such pairs in the request, and parsing all of them manually can be a real burden. I'm not even speaking about creating appropriate response string. + +Therefore UEFI specification offers us a helper function to ease our life: +``` +EFI_HII_CONFIG_ROUTING_PROTOCOL.BlockToConfig() + +Summary: +This helper function is to be called by drivers to map configuration data stored in byte array (“block”) formats such as UEFI Variables into current configuration strings. + +Prototype: + +typedef +EFI_STATUS + (EFIAPI * EFI_HII_BLOCK_TO_CONFIG ) ( + IN CONST EFI_HII_CONFIG_ROUTING_PROTOCOL *This, + IN CONST EFI_STRING ConfigRequest, + IN CONST UINT8 *Block, + IN CONST UINTN BlockSize, + OUT EFI_STRING *Config, + OUT EFI_STRING *Progress + ); + +Parameters: +This Points to the EFI_HII_CONFIG_ROUTING_PROTOCOL instance +ConfigRequest A null-terminated string in <ConfigRequest> format +Block Array of bytes defining the block’s configuration +BlockSize Length in bytes of Block +Config Filled-in configuration string. String allocated by the function. Returned only if call is + successful. The null-terminated string will be in <ConfigResp> format +Progress A pointer to a string filled in with the offset of the most recent ‘&’ before the first + failing name / value pair (or the beginning of the string if the failure is in the first + name / value pair) or the terminating NULL if all was successful + +Description: +This function extracts the current configuration from a block of bytes. To do so, it requires that the ConfigRequest string consists of a list of <BlockName> formatted names. It uses the offset in the +name to determine the index into the Block to start the extraction and the width of each name to determine the number of bytes to extract. These are mapped to a string using the equivalent of the C +“%x” format (with optional leading spaces). The call fails if, for any (offset, width) pair in ConfigRequest, offset+value >= BlockSize +``` + +With it you can finish our `ExtractConfig` function like this: +```cpp +STATIC +EFI_STATUS +EFIAPI +ExtractConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Request, + OUT EFI_STRING *Progress, + OUT EFI_STRING *Results +) +{ + if (Progress == NULL || Results == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Here can be additional custom operations to get you VARIABLE_STRUCTURE data + + EFI_STATUS Status = gHiiConfigRouting->BlockToConfig(gHiiConfigRouting, + Request, + (UINT8*)&FormStorage, + sizeof(VARIABLE_STRUCTURE), + Results, + Progress); + + return Status; +} +``` +To use `BlockToConfig` we need to get `EFI_HII_CONFIG_ROUTING_PROTOCOL` pointer. You can receive it via the `LocateProtocol` call in the driver's entry point function. After that just save the pointer as a global variable, so you could use it in other places like `ExtractConfig` function. Although here I've decided to take another approach. Here I just use `UefiHiiServicesLib` that locates several HII related protocols for us and provides them in pointers like `gHiiConfigRouting` for our usage. I've mentioned it in the earlier lessons. To use it add `UefiHiiServicesLib` to the `[LibraryClasses]` in the `INF` file and `#include <Library/UefiHiiServicesLib.h>` to the start of the `*.c` file. + +In the code above I've marked a place where can be additional custom operations to get you storage data structure. In our simple driver we don't have anything like that, but speaking in terms of our previous examples, this is the place where you would read you PCIe device memory, get state of GPIO LEDs, etc. We can even mimic `efivarstore` behaviour if here we would load our structure from some variable in the global UEFI variable storage. + + +Now let's get to our `RouteConfig()` function. In this function we need to parse incoming request and modify some parts of our storage structure based on the request. Once again a process of parsing `OFFSET=<...>&WIDTH=<...>&VALUE=<...>` combinations from the configuration request and cooresponding them to the real data is not easy. Luckily UEFI specification offers us a helper function for that case too: +``` +EFI_HII_CONFIG_ROUTING_PROTOCOL.ConfigToBlock() + +Summary: +This helper function is to be called by drivers to map configuration strings to configurations stored in byte array (“block”) formats such as UEFI Variables. + +Prototype: + +typedef +EFI_STATUS + (EFIAPI * EFI_HII_CONFIG_TO_BLOCK ) ( + IN CONST EFI_HII_CONFIG_ROUTING_PROTOCOL *This, + IN CONST EFI_STRING *ConfigResp, + IN OUT CONST UINT8 *Block, + IN OUT UINTN *BlockSize, + OUT EFI_STRING *Progress + ); + +Parameters: +This Points to the EFI_HII_CONFIG_ROUTING_PROTOCOL instance. +ConfigResp A null-terminated string in <ConfigResp> format. +Block A possibly null array of bytes representing the current block. Only bytes referenced in the ConfigResp string in the block are modified. If this parameter is null or if the + *BlockSize parameter is (on input) shorter than required by the Configuration string, only the BlockSize parameter is updated and an appropriate status (see below) is returned. +BlockSize The length of the Block in units of UINT8. On input, this is the size of the Block. On output, if successful, contains the largest index of the modified byte in the Block, or + the required buffer size if the Block is not large enough. +Progress On return, points to an element of the ConfigResp string filled in with the offset of the most recent ‘&’ before the first failing name / value pair (or the beginning of the + string if the failure is in the first name / value pair) or the terminating NULL if all was successful. + +Description: +This function maps a configuration containing a series of <BlockConfig> formatted name value pairs in ConfigResp into a Block so it may be stored in a linear mapped storage such as a UEFI Variable. If +present, the function skips GUID, NAME, and PATH in <ConfigResp>. It stops when it finds a non-<BlockConfig> name / value pair (after skipping the routing header) or when it reaches the end of the string. +``` + +So let's use it in our `RouteConfig()` function: +```cpp +STATIC +EFI_STATUS +EFIAPI +RouteConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Configuration, + OUT EFI_STRING *Progress +) +{ + if (Configuration == NULL || Progress == NULL) { + return EFI_INVALID_PARAMETER; + } + + UINTN BlockSize = sizeof(VARIABLE_STRUCTURE); + EFI_STATUS Status = gHiiConfigRouting->ConfigToBlock(gHiiConfigRouting, + Configuration, + (UINT8*)&FormStorage, + &BlockSize, + Progress); + + // Here can be additional custom operations to update you storage data structure + + return Status; +} +``` +Once again I've marked a place where can be additional functionality for your storage. Speaking in terms of our previous examples, this is the place where you would update you PCIe device memory, set state of GPIO LEDs, etc. If you want to mimic `efivarstore` behaviour here you would update the variable in the global UEFI variable storage. + +Now we are ready to build and test our driver. + +Load it in the UEFI shell: +``` +FS0:\> load HIIFormDataElementsVarstore.efi +``` + +Check the form browser. Now our form is filled with zeros rather than default values. We didn't give any data to our `VARIABLE_STRUCTURE FormStorage` variable, so it is expected output: +![1](1.png?raw=true "1") + +And now if you'll try to set the checkbox element and submit a form, you'll see that submit also works fine: +![2](2.png?raw=true "2") + +As in our case the `VARIABLE_STRUCTURE FormStorage` is a simple local driver variable the changes are pesistent only in the context of a current boot. If you'll reboot QEMU, you'll start from 0 values in the storage again. + +# Setting defaults for the form elements + +As now our `ExtractConfig`/`RouteConfig` functions are working we can use `HiiSetToDefaults` helper to set defaults to our form elements non-interactively, like we did it in the `efivarstore` case earlier. For example let's initialize our form from the manufacture defaults: +```cpp +EFI_GUID StorageGuid = STORAGE_GUID; +EFI_STRING StorageName = L"FormData"; + +<...> + +EFI_STATUS +EFIAPI +HIIFormDataElementsVarstoreEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + <...> + + EFI_STRING ConfigStr = HiiConstructConfigHdr(&StorageGuid, StorageName, mDriverHandle); + UINT16 DefaultId = 1; + if (!HiiSetToDefaults(ConfigStr, DefaultId)) { + Print(L"Error! Can't set default configuration #%d\n", DefaultId); + } + + return EFI_SUCCESS; +} +``` + +With that our form will start with the manufacture default values: +![5](5.png?raw=true "5") + + +# Checking the form with `HIIConfig.efi` + +As another test let's try to use our `HIIConfig.efi` program to work with our `varstorage` driver. + +Output for the request for all of the driver configuration data looks like this: +``` +FS0:\> HIIConfig.efi extract 37807592-733A-4F1B-9557-F22AF743E8C2 FormData VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8) + +Request: GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 +Response: GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&OFFSET=0000&WIDTH=0001&VALUE=00&OFFSET=0001&WIDTH=0002&VALUE=0008&OFFSET=0003&WIDTH=0014&VALUE=006f0072007000200067006e0069007200740053&OFFSET=0019&WIDTH=0004&VALUE=160507e5&OFFSET=001<...> + + +GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2) +NAME=0046006f0072006d0044006100740061 (FormData) +PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8)) +OFFSET=0000 WIDTH=0001 VALUE=00 +00 | . +OFFSET=0001 WIDTH=0002 VALUE=0008 +08 00 | .. +OFFSET=0003 WIDTH=0014 VALUE=006f0072007000200067006e0069007200740053 +53 00 74 00 72 00 69 00 6E 00 67 00 20 00 70 00 | S.t.r.i.n.g. .p. +72 00 6F 00 | r.o. +OFFSET=0019 WIDTH=0004 VALUE=160507e5 +E5 07 05 16 | .... +OFFSET=001d WIDTH=0003 VALUE=213717 +17 37 21 | .7! +OFFSET=0020 WIDTH=0001 VALUE=33 +33 | 3 +OFFSET=0021 WIDTH=0003 VALUE=0a0b0c +0C 0B 0A | ... + +GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2) +NAME=0046006f0072006d0044006100740061 (FormData) +PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8)) +ALTCFG=0000 +OFFSET=0000 WIDTH=0001 VALUE=01 +01 | . +OFFSET=0001 WIDTH=0002 VALUE=0007 +07 00 | .. +OFFSET=0003 WIDTH=0014 VALUE=00660065006400200067006e0069007200740053 +53 00 74 00 72 00 69 00 6E 00 67 00 20 00 64 00 | S.t.r.i.n.g. .d. +65 00 66 00 | e.f. +OFFSET=0019 WIDTH=0004 VALUE=160507e5 +E5 07 05 16 | .... +OFFSET=001d WIDTH=0003 VALUE=213717 +17 37 21 | .7! +OFFSET=0020 WIDTH=0001 VALUE=55 +55 | U +OFFSET=0021 WIDTH=0003 VALUE=0a0b0c +0C 0B 0A | ... + +GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2) +NAME=0046006f0072006d0044006100740061 (FormData) +PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8)) +ALTCFG=0001 +OFFSET=0000 WIDTH=0001 VALUE=00 +00 | . +OFFSET=0001 WIDTH=0002 VALUE=0008 +08 00 | .. +OFFSET=0003 WIDTH=0014 VALUE=006f0072007000200067006e0069007200740053 +53 00 74 00 72 00 69 00 6E 00 67 00 20 00 70 00 | S.t.r.i.n.g. .p. +72 00 6F 00 | r.o. +OFFSET=0019 WIDTH=0004 VALUE=160507e5 +E5 07 05 16 | .... +OFFSET=001d WIDTH=0003 VALUE=213717 +17 37 21 | .7! +OFFSET=0020 WIDTH=0001 VALUE=33 +33 | 3 +OFFSET=0021 WIDTH=0003 VALUE=0a0b0c +0C 0B 0A | ... +``` + +Here is example that accesses individual element (`numeric` in this case): +``` +FS0:\> HIIConfig.efi extract 37807592-733A-4F1B-9557-F22AF743E8C2 FormData VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8) 3 14 + +Request: GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&OFFSET=3&WIDTH=14 +Response: GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&OFFSET=3&WIDTH=14&VALUE=006f0072007000200067006e0069007200740053&GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5<...> + + +GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2) +NAME=0046006f0072006d0044006100740061 (FormData) +PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8)) +OFFSET=3 WIDTH=14 VALUE=006f0072007000200067006e0069007200740053 +53 00 74 00 72 00 69 00 6E 00 67 00 20 00 70 00 | S.t.r.i.n.g. .p. +72 00 6F 00 | r.o. + +GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2) +NAME=0046006f0072006d0044006100740061 (FormData) +PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8)) +ALTCFG=0000 +OFFSET=0003 WIDTH=0014 VALUE=00660065006400200067006e0069007200740053 +53 00 74 00 72 00 69 00 6E 00 67 00 20 00 64 00 | S.t.r.i.n.g. .d. +65 00 66 00 | e.f. + +GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2) +NAME=0046006f0072006d0044006100740061 (FormData) +NAME=0041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8)) +ALTCFG=0001 +OFFSET=0003 WIDTH=0014 VALUE=006f0072007000200067006e0069007200740053 +53 00 74 00 72 00 69 00 6E 00 67 00 20 00 70 00 | S.t.r.i.n.g. .p. +72 00 6F 00 | r.o. +``` + +We can change this element value: +``` +FS0:\> HIIConfig.efi route 37807592-733A-4F1B-9557-F22AF743E8C2 FormData VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8) 1 2 1234 +Request: GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&OFFSET=1&WIDTH=2&VALUE=1234 +``` + +And verify that it indeed was changed: +``` +FS0:\> HIIConfig.efi extract 37807592-733A-4F1B-9557-F22AF743E8C2 FormData VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8) + 1 2 +Request: GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&OFFSET=1&WIDTH=2 +Response: GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&OFFSET=1&WIDTH=2&VALUE=1234&GUID=927580373a731b4f9557f22af743e8c2&NAME=0046006f0072006d0044006100740061&PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400&ALTCFG=0000&OFFSET=0001&WI<...> + + +GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2) +NAME=0046006f0072006d0044006100740061 (FormData) +PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8)) +OFFSET=1 WIDTH=2 VALUE=1234 +34 12 | 4. + +GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2) +NAME=0046006f0072006d0044006100740061 (FormData) +PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8)) +ALTCFG=0000 +OFFSET=0001 WIDTH=0002 VALUE=0007 +07 00 | .. + +GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2) +NAME=0046006f0072006d0044006100740061 (FormData) +PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8)) +ALTCFG=0001 +OFFSET=0001 WIDTH=0002 VALUE=0008 +08 00 | .. +``` + +`ExportConfig` call also works correctly. Yes, the `varstore` configurations is seen in the `ExportConfig` output opposed to the `efivarstore` configurations: +``` +FS0:\> HIIConfig.efi dump +Full configuration for the HII Database (Size = 43266): + +<...> + +GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2) +NAME=0046006f0072006d0044006100740061 (FormData) +PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8)) +OFFSET=0000 WIDTH=0001 VALUE=00 +00 | . +OFFSET=0001 WIDTH=0002 VALUE=0008 +34 12 | .. +OFFSET=0003 WIDTH=0014 VALUE=006f0072007000200067006e0069007200740053 +53 00 74 00 72 00 69 00 6E 00 67 00 20 00 70 00 | S.t.r.i.n.g. .p. +72 00 6F 00 | r.o. +OFFSET=0019 WIDTH=0004 VALUE=160507e5 +E5 07 05 16 | .... +OFFSET=001d WIDTH=0003 VALUE=213717 +17 37 21 | .7! +OFFSET=0020 WIDTH=0001 VALUE=33 +33 | 3 +OFFSET=0021 WIDTH=0003 VALUE=0a0b0c +0C 0B 0A | ... + +GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2) +NAME=0046006f0072006d0044006100740061 (FormData) +PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8)) +ALTCFG=0000 +OFFSET=0000 WIDTH=0001 VALUE=01 +01 | . +OFFSET=0001 WIDTH=0002 VALUE=0007 +07 00 | .. +OFFSET=0003 WIDTH=0014 VALUE=00660065006400200067006e0069007200740053 +53 00 74 00 72 00 69 00 6E 00 67 00 20 00 64 00 | S.t.r.i.n.g. .d. +65 00 66 00 | e.f. +OFFSET=0019 WIDTH=0004 VALUE=160507e5 +E5 07 05 16 | .... +OFFSET=001d WIDTH=0003 VALUE=213717 +17 37 21 | .7! +OFFSET=0020 WIDTH=0001 VALUE=55 +55 | U +OFFSET=0021 WIDTH=0003 VALUE=0a0b0c +0C 0B 0A | ... + +GUID=927580373a731b4f9557f22af743e8c2 (37807592-733A-4F1B-9557-F22AF743E8C2) +NAME=0046006f0072006d0044006100740061 (FormData) +PATH=01041400641982fbb4ac7b439fe666aa7ad7c5d87fff0400 (VenHw(FB821964-ACB4-437B-9FE6-66AA7AD7C5D8)) +ALTCFG=0001 +OFFSET=0000 WIDTH=0001 VALUE=00 +00 | . +OFFSET=0001 WIDTH=0002 VALUE=0008 +08 00 | .. +OFFSET=0003 WIDTH=0014 VALUE=006f0072007000200067006e0069007200740053 +53 00 74 00 72 00 69 00 6E 00 67 00 20 00 70 00 | S.t.r.i.n.g. .p. +72 00 6F 00 | r.o. +OFFSET=0019 WIDTH=0004 VALUE=160507e5 +E5 07 05 16 | .... +OFFSET=001d WIDTH=0003 VALUE=213717 +17 37 21 | .7! +OFFSET=0020 WIDTH=0001 VALUE=33 +33 | 3 +OFFSET=0021 WIDTH=0003 VALUE=0a0b0c +0C 0B 0A | ... +``` + +So as you can see everything works correctly. But there is still some room to improve. + |