1*99e0aae7SDavid Rees# Emboss User Guide 2*99e0aae7SDavid Rees 3*99e0aae7SDavid Rees 4*99e0aae7SDavid Rees## Getting Started 5*99e0aae7SDavid Rees 6*99e0aae7SDavid ReesFirst, you must identify a data structure you want to read and write. These are 7*99e0aae7SDavid Reesoften documented in hardware manuals a bit like [this one, for the fictional 8*99e0aae7SDavid ReesBN-P-6000404 illuminated button panel](BogoNEL_BN-P-6000404_User_Guide.pdf). We 9*99e0aae7SDavid Reeswill use the BN-P-6000404 as an example. 10*99e0aae7SDavid Rees 11*99e0aae7SDavid Rees 12*99e0aae7SDavid Rees### A Caution 13*99e0aae7SDavid Rees 14*99e0aae7SDavid ReesEmboss is still beta software. While we believe that we will not need to make 15*99e0aae7SDavid Reesany more breaking changes before 1.0, you may still encounter bugs and there are 16*99e0aae7SDavid Reesmany missing features. 17*99e0aae7SDavid Rees 18*99e0aae7SDavid ReesYou can contact `[email protected]` with any issues. Emboss is not an 19*99e0aae7SDavid Reesofficially supported Google product, but the Emboss authors will try to answer 20*99e0aae7SDavid Reesemails. 21*99e0aae7SDavid Rees 22*99e0aae7SDavid Rees 23*99e0aae7SDavid Rees### System Requirements 24*99e0aae7SDavid Rees 25*99e0aae7SDavid Rees#### Running the Emboss Compiler 26*99e0aae7SDavid Rees 27*99e0aae7SDavid ReesThe Emboss compiler requires Python 3.8 or later -- the minimum supported 28*99e0aae7SDavid Reesversion tracks the support timeline of the Python project. On a Linux-like 29*99e0aae7SDavid Reessystem with Python 3 installed in the usual place (`/usr/bin/python3`), you 30*99e0aae7SDavid Reescan run the embossc script at the top level on an `.emb` file to generate 31*99e0aae7SDavid ReesC++, like so: 32*99e0aae7SDavid Rees 33*99e0aae7SDavid Rees``` 34*99e0aae7SDavid Reesembossc --generate cc --output-path path/to/object/dir path/to/input.emb 35*99e0aae7SDavid Rees``` 36*99e0aae7SDavid Rees 37*99e0aae7SDavid ReesIf your project is using Bazel, the `build_defs.bzl` file has an 38*99e0aae7SDavid Rees`emboss_cc_library` rule that you can use from your project. 39*99e0aae7SDavid Rees 40*99e0aae7SDavid Rees 41*99e0aae7SDavid Rees#### Using the Generated C++ Code 42*99e0aae7SDavid Rees 43*99e0aae7SDavid ReesThe code generated by Emboss requires a C++11-compliant compiler, and a 44*99e0aae7SDavid Reesreasonably up-to-date standard library. Emboss has been tested with GCC and 45*99e0aae7SDavid ReesClang, libc++ and libstd++. In theory, it should work with MSVC, ICC, etc., but 46*99e0aae7SDavid Reesit has not been tested, so there are likely to be bugs. 47*99e0aae7SDavid Rees 48*99e0aae7SDavid ReesThe generated C++ code lives entirely in a `.h` file, one per `.emb` file. All 49*99e0aae7SDavid Reesof the generated code is in C++ templates or (in a very few cases) `inline` 50*99e0aae7SDavid Reesfunctions. The generated code is structured this way in order to implement 51*99e0aae7SDavid Rees"pay-as-you-use" for code size: any functions, methods, or views that are not 52*99e0aae7SDavid Reesused by your code won't end up in your final binary. This is often important 53*99e0aae7SDavid Reesfor environments like microcontrollers! 54*99e0aae7SDavid Rees 55*99e0aae7SDavid ReesThere is an Emboss runtime library (under `runtime/cpp`), which is also 56*99e0aae7SDavid Reesheader-only. You will need to add the root of the Emboss source tree to your 57*99e0aae7SDavid Rees`#include` path. 58*99e0aae7SDavid Rees 59*99e0aae7SDavid ReesNote that it is *strongly* recommended that you compile your release code with 60*99e0aae7SDavid Reesat least some optimizations: `-Os` or `-O2`. The Emboss generated code leans 61*99e0aae7SDavid Reesfairly heavily on your C++ compiler's inlining and common code elimination to 62*99e0aae7SDavid Reesproduce fast, lean compiled code. 63*99e0aae7SDavid Rees 64*99e0aae7SDavid Rees 65*99e0aae7SDavid Rees#### Contributing to the Compiler 66*99e0aae7SDavid Rees 67*99e0aae7SDavid ReesIf you want to contribute features or bugfixes to the Emboss compiler itself, 68*99e0aae7SDavid Reesyou will need Bazel to run the Emboss test suite. 69*99e0aae7SDavid Rees 70*99e0aae7SDavid Rees 71*99e0aae7SDavid Rees### Create an `.emb` file 72*99e0aae7SDavid Rees 73*99e0aae7SDavid ReesNext, you will need to translate your structures. 74*99e0aae7SDavid Rees 75*99e0aae7SDavid Rees``` 76*99e0aae7SDavid Rees[$default byte_order: "LittleEndian"] 77*99e0aae7SDavid Rees[(cpp) namespace: "bogonel::bnp6000404"] 78*99e0aae7SDavid Rees``` 79*99e0aae7SDavid Rees 80*99e0aae7SDavid ReesThe BN-P-6000404 uses little-endian numbers, so we can set the default byte 81*99e0aae7SDavid Reesorder to `LittleEndian`. There is no particular C++ namespace implied by the 82*99e0aae7SDavid ReesBN-P-6000404 user guide, so we use one that is specific to the BN-P-6000404. 83*99e0aae7SDavid Rees 84*99e0aae7SDavid ReesThe BN-P-6000404, like many devices with serial interfaces, uses a framed 85*99e0aae7SDavid Reesmessage system, with a fixed header and a variable message body depending on a 86*99e0aae7SDavid Reesmessage ID. For the BN-P-6000404, this framing looks like this: 87*99e0aae7SDavid Rees 88*99e0aae7SDavid Rees<!-- TODO(bolms): finalize the "magic value initialization" feature, document it 89*99e0aae7SDavid Reeshere. --> 90*99e0aae7SDavid Rees 91*99e0aae7SDavid Rees``` 92*99e0aae7SDavid Reesstruct Message: 93*99e0aae7SDavid Rees -- Top-level message structure, specified in section 5.3 of the BN-P-6000404 94*99e0aae7SDavid Rees -- user guide. 95*99e0aae7SDavid Rees 96*99e0aae7SDavid Rees 0 [+1] UInt sync_1 97*99e0aae7SDavid Rees [requires: this == 0x42] 98*99e0aae7SDavid Rees 99*99e0aae7SDavid Rees 1 [+1] UInt sync_2 100*99e0aae7SDavid Rees [requires: this == 0x4E] 101*99e0aae7SDavid Rees 102*99e0aae7SDavid Rees 2 [+1] MessageId message_id 103*99e0aae7SDavid Rees -- Type of message 104*99e0aae7SDavid Rees 105*99e0aae7SDavid Rees 3 [+1] UInt message_length (ml) 106*99e0aae7SDavid Rees -- Length of message, including header and checksum 107*99e0aae7SDavid Rees 108*99e0aae7SDavid Rees # ... body fields to follow ... 109*99e0aae7SDavid Rees``` 110*99e0aae7SDavid Rees 111*99e0aae7SDavid ReesWe could have chosen to put the header fields into a separate `Header` structure 112*99e0aae7SDavid Reesinstead of placing them directly in the `Message` structure. 113*99e0aae7SDavid Rees 114*99e0aae7SDavid ReesThe `sync_1` and `sync_2` fields are required to have specific magic values, so 115*99e0aae7SDavid Reeswe add the appropriate `[requires: ...]` attributes to them. This tells Emboss 116*99e0aae7SDavid Reesthat if those fields do not have those values, then the `Message` `struct` is 117*99e0aae7SDavid Reesill-formed: in the client code, the `Message` will not be `Ok()` if those fields 118*99e0aae7SDavid Reeshave the wrong values, and Emboss will not allow wrong values to be written into 119*99e0aae7SDavid Reesthose fields using the checked (default) APIs. 120*99e0aae7SDavid Rees 121*99e0aae7SDavid ReesUnfortunately, BogoNEL does not provide a nice table of message IDs, but 122*99e0aae7SDavid Reesfortunately there are only a few, so we can gather them from the individual 123*99e0aae7SDavid Reesmessages: 124*99e0aae7SDavid Rees 125*99e0aae7SDavid Rees``` 126*99e0aae7SDavid Reesenum MessageId: 127*99e0aae7SDavid Rees -- Message type idenfiers for the BN-P-6000404. 128*99e0aae7SDavid Rees IDENTIFICATION = 0x01 129*99e0aae7SDavid Rees INTERACTION = 0x02 130*99e0aae7SDavid Rees QUERY_IDENTIFICATION = 0x10 131*99e0aae7SDavid Rees QUERY_BUTTONS = 0x11 132*99e0aae7SDavid Rees SET_ILLUMINATION = 0x12 133*99e0aae7SDavid Rees``` 134*99e0aae7SDavid Rees 135*99e0aae7SDavid ReesNext, we should translate the individual messages to Emboss. 136*99e0aae7SDavid Rees 137*99e0aae7SDavid Rees``` 138*99e0aae7SDavid Reesstruct Identification: 139*99e0aae7SDavid Rees -- IDENTIFICATION message, specified in section 5.3.3. 140*99e0aae7SDavid Rees 141*99e0aae7SDavid Rees 0 [+4] UInt vendor 142*99e0aae7SDavid Rees # 0x4F474F42 is "BOGO" in ASCII, interpreted as a 4-byte little-endian 143*99e0aae7SDavid Rees # value. 144*99e0aae7SDavid Rees [requires: this == 0x4F47_4F42] 145*99e0aae7SDavid Rees 146*99e0aae7SDavid Rees 0 [+4] UInt:8[4] vendor_ascii 147*99e0aae7SDavid Rees -- "BOGO" for BogoNEL Corp 148*99e0aae7SDavid Rees # The `vendor` field really contains the four ASCII characters "BOGO", so we 149*99e0aae7SDavid Rees # could use a byte array instead of a single UInt. Since it is valid to 150*99e0aae7SDavid Rees # have overlapping fields, we can have both `vendor` and `vendor_ascii` in 151*99e0aae7SDavid Rees # our Emboss specification. 152*99e0aae7SDavid Rees 153*99e0aae7SDavid Rees 4 [+2] UInt firmware_major 154*99e0aae7SDavid Rees -- Firmware major version 155*99e0aae7SDavid Rees 156*99e0aae7SDavid Rees 6 [+2] UInt firmware_minor 157*99e0aae7SDavid Rees -- Firmware minor version 158*99e0aae7SDavid Rees``` 159*99e0aae7SDavid Rees 160*99e0aae7SDavid Rees<!-- TODO(bolms): fixed-length, ASCIIZ, and variable-length string support? --> 161*99e0aae7SDavid Rees 162*99e0aae7SDavid ReesThe `Identification` structure is fairly straightforward. In this case, we 163*99e0aae7SDavid Reesprovide an alternate view of the `vendor` field via `vendor_ascii`: 0x4F474F42 164*99e0aae7SDavid Reesin little-endian works out to the ASCII characters "BOGO". 165*99e0aae7SDavid Rees 166*99e0aae7SDavid ReesNote that `vendor_ascii` uses `UInt:8[4]` for its type, and not `UInt[4]`. For 167*99e0aae7SDavid Reesmost fields, we can use plain `UInt` and Emboss will figure out how big the 168*99e0aae7SDavid Rees`UInt` should be, but for an array we must be explicit that we want 8-bit 169*99e0aae7SDavid Reeselements. 170*99e0aae7SDavid Rees 171*99e0aae7SDavid Rees``` 172*99e0aae7SDavid Reesstruct Interaction: 173*99e0aae7SDavid Rees -- INTERACTION message, specified in section 5.3.4. 174*99e0aae7SDavid Rees 175*99e0aae7SDavid Rees 0 [+1] UInt number_of_buttons (n) 176*99e0aae7SDavid Rees -- Number of buttons currently depressed by user 177*99e0aae7SDavid Rees 178*99e0aae7SDavid Rees 4 [+n] ButtonId:8[n] button_id 179*99e0aae7SDavid Rees -- ID of pressed button. A number of entries equal to number_of_buttons 180*99e0aae7SDavid Rees -- will be provided. 181*99e0aae7SDavid Rees``` 182*99e0aae7SDavid Rees 183*99e0aae7SDavid Rees<!-- TODO(bolms): reserved field support --> 184*99e0aae7SDavid Rees 185*99e0aae7SDavid Rees`Interaction` is also fairly straightforward. The only tricky bit is the 186*99e0aae7SDavid Rees`button_id` field: since `Interaction` can return a variable number of button 187*99e0aae7SDavid ReesIDs, depending on how many buttons are currently pressed, the `button_id` field 188*99e0aae7SDavid Reesmust has length `n`. It would have been OK to use `[+number_of_buttons]`, but 189*99e0aae7SDavid Reesfull field names can get cumbersome, particularly when the length involves are 190*99e0aae7SDavid Reesmore complex expression. Instead, we set an *alias* for `number_of_buttons` 191*99e0aae7SDavid Reesusing `(n)`, and then use the alias in `button_id`'s length. The `n` alias is 192*99e0aae7SDavid Reesnot visible outside of the `Interaction` message, and won't be available in the 193*99e0aae7SDavid Reesgenerated code, so the short name is not likely to cause confusion. 194*99e0aae7SDavid Rees 195*99e0aae7SDavid Rees``` 196*99e0aae7SDavid Reesenum ButtonId: 197*99e0aae7SDavid Rees -- Button IDs, specified in table 5-6. 198*99e0aae7SDavid Rees BUTTON_A = 0x00 199*99e0aae7SDavid Rees BUTTON_B = 0x04 200*99e0aae7SDavid Rees BUTTON_C = 0x08 201*99e0aae7SDavid Rees BUTTON_D = 0x0C 202*99e0aae7SDavid Rees BUTTON_E = 0x01 203*99e0aae7SDavid Rees BUTTON_F = 0x05 204*99e0aae7SDavid Rees BUTTON_G = 0x09 205*99e0aae7SDavid Rees BUTTON_H = 0x0D 206*99e0aae7SDavid Rees BUTTON_I = 0x02 207*99e0aae7SDavid Rees BUTTON_J = 0x06 208*99e0aae7SDavid Rees BUTTON_K = 0x0A 209*99e0aae7SDavid Rees BUTTON_L = 0x0E 210*99e0aae7SDavid Rees BUTTON_M = 0x03 211*99e0aae7SDavid Rees BUTTON_N = 0x07 212*99e0aae7SDavid Rees BUTTON_O = 0x0B 213*99e0aae7SDavid Rees BUTTON_P = 0x0F 214*99e0aae7SDavid Rees``` 215*99e0aae7SDavid Rees 216*99e0aae7SDavid ReesWe had to prefix all of the button names with `BUTTON_` because Emboss does not 217*99e0aae7SDavid Reesallow single-character enum names. 218*99e0aae7SDavid Rees 219*99e0aae7SDavid ReesThe QUERY IDENTIFICATION and QUERY BUTTONS messages don't have any fields other 220*99e0aae7SDavid Reesthan `checksum`, so we will handle them a bit differently. 221*99e0aae7SDavid Rees 222*99e0aae7SDavid Rees``` 223*99e0aae7SDavid Reesstruct SetIllumination: 224*99e0aae7SDavid Rees -- SET ILLUMINATION message, specified in section 5.3.7. 225*99e0aae7SDavid Rees 226*99e0aae7SDavid Rees 0 [+1] bits: 227*99e0aae7SDavid Rees 0 [+1] Flag red_channel_enable 228*99e0aae7SDavid Rees -- Enables setting the RED channel. 229*99e0aae7SDavid Rees 230*99e0aae7SDavid Rees 1 [+1] Flag blue_channel_enable 231*99e0aae7SDavid Rees -- Enables setting the BLUE channel. 232*99e0aae7SDavid Rees 233*99e0aae7SDavid Rees 2 [+1] Flag green_channel_enable 234*99e0aae7SDavid Rees -- Enables setting the GREEN channel. 235*99e0aae7SDavid Rees 236*99e0aae7SDavid Rees 1 [+1] UInt blink_duty 237*99e0aae7SDavid Rees -- Sets the proportion of time between time on and time off for blink 238*99e0aae7SDavid Rees -- feature. 239*99e0aae7SDavid Rees -- 240*99e0aae7SDavid Rees -- Minimum value = 0 (no illumination) 241*99e0aae7SDavid Rees -- 242*99e0aae7SDavid Rees -- Maximum value = 240 (constant illumination) 243*99e0aae7SDavid Rees [requires: 0 <= this <= 240] 244*99e0aae7SDavid Rees 245*99e0aae7SDavid Rees 2 [+2] UInt blink_period 246*99e0aae7SDavid Rees -- Sets the blink period, in milliseconds. 247*99e0aae7SDavid Rees -- 248*99e0aae7SDavid Rees -- Minimum value = 10 249*99e0aae7SDavid Rees -- 250*99e0aae7SDavid Rees -- Maximum value = 10000 251*99e0aae7SDavid Rees [requires: 10 <= this <= 10_000] 252*99e0aae7SDavid Rees 253*99e0aae7SDavid Rees 4 [+4] bits: 254*99e0aae7SDavid Rees 0 [+32] UInt:2[16] intensity 255*99e0aae7SDavid Rees -- Intensity values for the unmasked channels. 2 bits of intensity for 256*99e0aae7SDavid Rees -- each button. 257*99e0aae7SDavid Rees``` 258*99e0aae7SDavid Rees 259*99e0aae7SDavid Rees`SetIllumination` requires us to use bitfields. The first bitfield is in the 260*99e0aae7SDavid ReesCHANNEL MASK field: rather than making a single `channel_mask` field, Emboss 261*99e0aae7SDavid Reeslets us specify the red, green, and blue channel masks separately. 262*99e0aae7SDavid Rees 263*99e0aae7SDavid ReesAs with `sync_1` and `sync_2`, we have added `[requires: ...]` to the 264*99e0aae7SDavid Rees`blink_duty` and `blink_period` fields: this time, specifying a range of valid 265*99e0aae7SDavid Reesvalues. `[requires: ...]` accepts an arbitrary expression, which can be as 266*99e0aae7SDavid Reessimple or as complex as desired. 267*99e0aae7SDavid Rees 268*99e0aae7SDavid ReesIt is not clear from BogoNEL's documentation whether "bit 0" means the least 269*99e0aae7SDavid Reessignificant or most significant bit of its byte, but a little experimentation 270*99e0aae7SDavid Reeswith the device shows that setting the least significant bit causes 271*99e0aae7SDavid Rees`SetIllumination` to set its red channel. Emboss always numbers bits in 272*99e0aae7SDavid Reesbitfields from least significant (bit 0) to most significant. 273*99e0aae7SDavid Rees 274*99e0aae7SDavid ReesThe other bitfield is the `intensity` array. The BN-P-6000404 uses an array of 275*99e0aae7SDavid Rees2 bit intensity values, so we specify that array. 276*99e0aae7SDavid Rees 277*99e0aae7SDavid ReesFinally, we should add all of the sub-messages into `Message`, and also take 278*99e0aae7SDavid Reescare of `checksum`. After making those changes, `Message` looks like: 279*99e0aae7SDavid Rees 280*99e0aae7SDavid Rees``` 281*99e0aae7SDavid Reesstruct Message: 282*99e0aae7SDavid Rees -- Top-level message structure, specified in section 5.3 of the BN-P-6000404 283*99e0aae7SDavid Rees -- user guide. 284*99e0aae7SDavid Rees 285*99e0aae7SDavid Rees 0 [+1] UInt sync_1 286*99e0aae7SDavid Rees [requires: this == 0x42] 287*99e0aae7SDavid Rees 288*99e0aae7SDavid Rees 1 [+1] UInt sync_2 289*99e0aae7SDavid Rees [requires: this == 0x4E] 290*99e0aae7SDavid Rees 291*99e0aae7SDavid Rees 2 [+1] MessageId message_id 292*99e0aae7SDavid Rees -- Type of message 293*99e0aae7SDavid Rees 294*99e0aae7SDavid Rees 3 [+1] UInt message_length (ml) 295*99e0aae7SDavid Rees -- Length of message, including header and checksum 296*99e0aae7SDavid Rees 297*99e0aae7SDavid Rees if message_id == MessageId.IDENTIFICATION: 298*99e0aae7SDavid Rees 4 [+ml-8] Identification identification 299*99e0aae7SDavid Rees 300*99e0aae7SDavid Rees if message_id == MessageId.INTERACTION: 301*99e0aae7SDavid Rees 4 [+ml-8] Interaction interaction 302*99e0aae7SDavid Rees 303*99e0aae7SDavid Rees if message_id == MessageId.SET_ILLUMINATION: 304*99e0aae7SDavid Rees 4 [+ml-8] SetIllumination set_illumination 305*99e0aae7SDavid Rees 306*99e0aae7SDavid Rees 0 [+ml-4] UInt:8[] checksummed_bytes 307*99e0aae7SDavid Rees 308*99e0aae7SDavid Rees ml-4 [+4] UInt checksum 309*99e0aae7SDavid Rees``` 310*99e0aae7SDavid Rees 311*99e0aae7SDavid ReesBy wrapping the various message types in `if message_id == ...` constructs, 312*99e0aae7SDavid Reesthose substructures will only be available when the `message_id` field is set to 313*99e0aae7SDavid Reesthe corresponding message type. This kind of selection is used for any 314*99e0aae7SDavid Reesstructure field that is only valid some of the time. 315*99e0aae7SDavid Rees 316*99e0aae7SDavid ReesThe substructures all have the length `ml-8`. The `ml` is a short alias for the 317*99e0aae7SDavid Rees`message_length` field; these short aliases are available so that the field 318*99e0aae7SDavid Reestypes and names don't have to be pushed far to the right. Aliases may only be 319*99e0aae7SDavid Reesused directly in the same structure definition where they are created; they may 320*99e0aae7SDavid Reesnot be used elsewhere in an Emboss file, and they are not available in the 321*99e0aae7SDavid Reesgenerated code. The length is `ml-8` in this case because the `message_length` 322*99e0aae7SDavid Reesincludes the header and checksum, which left out of the substructures. 323*99e0aae7SDavid Rees 324*99e0aae7SDavid ReesNote that we simply don't have any subfield for QUERY IDENTIFICATION or QUERY 325*99e0aae7SDavid ReesBUTTONS: since those messages do not have any fields, there is no need for a 326*99e0aae7SDavid Reeszero-byte structure. 327*99e0aae7SDavid Rees 328*99e0aae7SDavid ReesWe also added the `checksummed_bytes` field as a convenience for computing the 329*99e0aae7SDavid Reeschecksum. 330*99e0aae7SDavid Rees 331*99e0aae7SDavid Rees 332*99e0aae7SDavid Rees### Generate code 333*99e0aae7SDavid Rees 334*99e0aae7SDavid ReesOnce you have an `.emb`, you will need to generate code from it. 335*99e0aae7SDavid Rees 336*99e0aae7SDavid ReesThe simplest way to do so is to run the `embossc` tool: 337*99e0aae7SDavid Rees 338*99e0aae7SDavid Rees``` 339*99e0aae7SDavid Reesembossc -I src --generate cc --output-path generated bogonel.emb 340*99e0aae7SDavid Rees``` 341*99e0aae7SDavid Rees 342*99e0aae7SDavid ReesThe `-I` option adds a directory to the *include path*. The input file -- in 343*99e0aae7SDavid Reesthis case, `bogonel.emb` -- must be found somewhere on the include path. 344*99e0aae7SDavid Rees 345*99e0aae7SDavid ReesThe `--generate` option specifies which back end to use; `cc` is the C++ back 346*99e0aae7SDavid Reesend. 347*99e0aae7SDavid Rees 348*99e0aae7SDavid ReesThe `--output-path` option specifies where the generated file should be placed. 349*99e0aae7SDavid ReesNote that the output path will include all of the path components of the input 350*99e0aae7SDavid Reesfile: if the input file is `x/y/z.emb`, then the path `x/y/z.emb.h` will be 351*99e0aae7SDavid Reesappended to the `--output-path`. Missing directories will be created. 352*99e0aae7SDavid Rees 353*99e0aae7SDavid Rees 354*99e0aae7SDavid Rees<!-- #### Using Bazel --> 355*99e0aae7SDavid Rees 356*99e0aae7SDavid Rees<!-- TODO(bolms): Make this usable from Bazel. --> 357*99e0aae7SDavid Rees 358*99e0aae7SDavid Rees 359*99e0aae7SDavid Rees### Include the generated C++ code 360*99e0aae7SDavid Rees 361*99e0aae7SDavid ReesEmboss generates a single C++ header file from your `.emb` by appending `.h` to 362*99e0aae7SDavid Reesthe file name: to use the BogoNEL definitions, you would `#include 363*99e0aae7SDavid Rees"path/to/bogonel.emb.h"` in your C++ code. 364*99e0aae7SDavid Rees 365*99e0aae7SDavid ReesCurrently, Emboss does not generate a corresponding `.cc` file: the code that 366*99e0aae7SDavid ReesEmboss generates is all templates, which exist in the `.h`. Although the Emboss 367*99e0aae7SDavid Reesmaintainers (e.g., bolms@) like the simplicity of generating a single file, this 368*99e0aae7SDavid Reescould change at some point. 369*99e0aae7SDavid Rees 370*99e0aae7SDavid Rees 371*99e0aae7SDavid Rees### Use the generated C++ code 372*99e0aae7SDavid Rees 373*99e0aae7SDavid ReesEmboss generates *views*, which your program can use to read and write existing 374*99e0aae7SDavid Reesarrays of bytes, and which do not take ownership. For example: 375*99e0aae7SDavid Rees 376*99e0aae7SDavid Rees```c++ 377*99e0aae7SDavid Rees#include "path/to/bogonel.emb.h" 378*99e0aae7SDavid Rees 379*99e0aae7SDavid Reestemplate <typename View> 380*99e0aae7SDavid Reesbool ChecksumIsCorrect(View message_view); 381*99e0aae7SDavid Rees 382*99e0aae7SDavid Rees// Handles BogoNEL BN-P-6000404 device messages from a byte stream. Returns 383*99e0aae7SDavid Rees// the number of bytes that were processed. Unprocessed bytes should be 384*99e0aae7SDavid Rees// passed into the next call. 385*99e0aae7SDavid Reesint HandleBogonelPanelMessages(const char *bytes, int byte_count) { 386*99e0aae7SDavid Rees auto message_view = bogonel::bnp6000404::MakeMessageView(bytes, byte_count); 387*99e0aae7SDavid Rees 388*99e0aae7SDavid Rees // IsComplete() will return true if the view has enough bytes to fully 389*99e0aae7SDavid Rees // contain the message; i.e., that byte_count is at least 390*99e0aae7SDavid Rees // message_view.message_length().Read() + 4. 391*99e0aae7SDavid Rees if (!message_view->IsComplete()) { 392*99e0aae7SDavid Rees return 0; 393*99e0aae7SDavid Rees } 394*99e0aae7SDavid Rees 395*99e0aae7SDavid Rees // If Emboss is happy with the message, we still need to check the checksum: 396*99e0aae7SDavid Rees // Emboss does not (yet) have support for automatically checking checksums and 397*99e0aae7SDavid Rees // CRCs. 398*99e0aae7SDavid Rees if (!message_view->Ok() || !ChecksumIsCorrect(message_view)) { 399*99e0aae7SDavid Rees // If the message is complete, but not correct, we need to log an error. 400*99e0aae7SDavid Rees HandleBrokenMessage(message_view); 401*99e0aae7SDavid Rees return message_view->Size(); 402*99e0aae7SDavid Rees } 403*99e0aae7SDavid Rees 404*99e0aae7SDavid Rees 405*99e0aae7SDavid Rees // At this point, we know the message is complete and (basically) OK, so 406*99e0aae7SDavid Rees // we dispatch it to a message-type-specific handler. 407*99e0aae7SDavid Rees switch (message_view->message_id().Read()) { 408*99e0aae7SDavid Rees case bogonel::bnp6000404::MessageId::IDENTIFICATION: 409*99e0aae7SDavid Rees HandleIdentificationMessage(message_view); 410*99e0aae7SDavid Rees break; 411*99e0aae7SDavid Rees 412*99e0aae7SDavid Rees case bogonel::bnp6000404::MessageId::INTERACTION: 413*99e0aae7SDavid Rees HandleInteractionMessage(message_view); 414*99e0aae7SDavid Rees break; 415*99e0aae7SDavid Rees 416*99e0aae7SDavid Rees case bogonel::bnp6000404::MessageId::QUERY_IDENTIFICATION: 417*99e0aae7SDavid Rees case bogonel::bnp6000404::MessageId::QUERY_BUTTONS: 418*99e0aae7SDavid Rees case bogonel::bnp6000404::MessageId::SET_ILLUMINATION: 419*99e0aae7SDavid Rees Log("Unexpected host to device message type."); 420*99e0aae7SDavid Rees break; 421*99e0aae7SDavid Rees 422*99e0aae7SDavid Rees default: 423*99e0aae7SDavid Rees Log("Unknown message type."); 424*99e0aae7SDavid Rees break; 425*99e0aae7SDavid Rees } 426*99e0aae7SDavid Rees 427*99e0aae7SDavid Rees return message_view->Size(); 428*99e0aae7SDavid Rees} 429*99e0aae7SDavid Rees 430*99e0aae7SDavid Reestemplate <typename View> 431*99e0aae7SDavid Reesbool ChecksumIsCorrect(View message_view) { 432*99e0aae7SDavid Rees uint32_t checksum = 0; 433*99e0aae7SDavid Rees for (int i = 0; i < message_view.checksum_bytes().ElementCount(); ++i) { 434*99e0aae7SDavid Rees checksum += message_view.checksum_bytes()[i].Read(); 435*99e0aae7SDavid Rees } 436*99e0aae7SDavid Rees return checksum == message_view.checksum().Read(); 437*99e0aae7SDavid Rees} 438*99e0aae7SDavid Rees``` 439*99e0aae7SDavid Rees 440*99e0aae7SDavid Rees<!-- TODO(bolms): solidify support for checksums, so that the Ok() call in the 441*99e0aae7SDavid Reesexample actually checks them. --> 442*99e0aae7SDavid Rees 443*99e0aae7SDavid ReesThe `message_view` object in this example is a lightweight object that simply 444*99e0aae7SDavid Reesprovides *access* to the bytes in `message`. Emboss views are very cheap to 445*99e0aae7SDavid Reesconstruct because they only contain a couple of pointers and a length -- they do 446*99e0aae7SDavid Reesnot copy or take ownership of the underlying bytes. This also means that you 447*99e0aae7SDavid Reeshave to keep the underlying bytes alive as long as you are using a view -- you 448*99e0aae7SDavid Reescan't let them go out of scope or delete them. 449*99e0aae7SDavid Rees 450*99e0aae7SDavid ReesViews can also be used for writing, if they are given pointers to mutable 451*99e0aae7SDavid Reesmemory: 452*99e0aae7SDavid Rees 453*99e0aae7SDavid Rees```c++ 454*99e0aae7SDavid Reesvoid ConstructSetIlluminationMessage(const vector<bool> &lit_buttons, 455*99e0aae7SDavid Rees vector<char> *result) { 456*99e0aae7SDavid Rees // The SetIllumination message has a constant size, so SizeInBytes() is 457*99e0aae7SDavid Rees // available as a static method. 458*99e0aae7SDavid Rees int length = bogonel::bnp6000404::SetIllumination::SizeInBytes() + 8; 459*99e0aae7SDavid Rees result->clear(); 460*99e0aae7SDavid Rees result->resize(length); 461*99e0aae7SDavid Rees 462*99e0aae7SDavid Rees auto view = bogonel::bnp6000404::MakeMessageView(result); 463*99e0aae7SDavid Rees view->sync_1().Write(0x42); 464*99e0aae7SDavid Rees view->sync_2().Write(0x4E); 465*99e0aae7SDavid Rees view->message_id().Write(bogonel::bnp6000404::MessageId::SET_ILLUMINATION); 466*99e0aae7SDavid Rees view->message_length().Write(length); 467*99e0aae7SDavid Rees view->set_illumination().red_channel_enable().Write(true); 468*99e0aae7SDavid Rees view->set_illumination().blue_channel_enable().Write(true); 469*99e0aae7SDavid Rees view->set_illumination().green_channel_enable().Write(true); 470*99e0aae7SDavid Rees view->set_illumination().blink_duty().Write(240); 471*99e0aae7SDavid Rees view->set_illumination().blink_period().Write(10000); 472*99e0aae7SDavid Rees for (int i = 0; i < view->set_illumination().intensity().ElementCount(); 473*99e0aae7SDavid Rees ++i) { 474*99e0aae7SDavid Rees view->set_illumination().intensity()[i].Write(lit_buttons[i] ? 3 : 0); 475*99e0aae7SDavid Rees } 476*99e0aae7SDavid Rees} 477*99e0aae7SDavid Rees``` 478*99e0aae7SDavid Rees 479*99e0aae7SDavid Rees 480*99e0aae7SDavid Rees### Use the `.emb` Autoformatter 481*99e0aae7SDavid Rees 482*99e0aae7SDavid ReesYou can use the `.emb` autoformatter to avoid manual formatting. For now, it is 483*99e0aae7SDavid Reesavailable at `compiler/front_end/format.py`. 484*99e0aae7SDavid Rees 485*99e0aae7SDavid Rees*TODO(bolms): Package the Emboss tools for easy workstation installation.* 486