xref: /aosp_15_r20/external/emboss/doc/guide.md (revision 99e0aae7469b87d12f0ad23e61142c2d74c1ef70)
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