# HAL buffered writes

Writing bytes to memory one at a time is often slower than writing an aligned
32-bit value to memory, and in some cases, much much slower.

If your source data is a stream of bytes, manually wrangling data into a 32-bit
buffer can be complicated, and with HAL's 3 registers, can result in sub-par
performances.

This unit implements an efficient way to do so. It provides words that follow
the semantics of "move," "fill," etc. where W=src and A=dst and compile words
that have no HAL operands and always operate on the same registers: A is the
destination address and S is the byte to write.

There are two ways to use this API: the static way and the dynamic way.

If your loop starts with an aligned destination and that you have the
possibility of including 4 source fetch in it, the static way is the fastest.
In the example below, W is a source address:

  begin W) 8b) S>) @+, bwr0, W) 8b) S>) @+, bwr1,
        W) 8b) S>) @+, bwr2, W) 8b) S>) @+, bwr3, again

In each iteration of this loop, a value is read byte by byte from W and "ORed"
in R0) [hal/vreg]. When "bwr3," is called, that 32-bit value is written to A and
A is incremented by 4.

Sometimes, it's complicated to hardcode 4 source fetch and we'd prefer to have a
single source fetch for a single buffered write call. For this purpose, there is
also the dynamic API:

  begin W) 8b) S>) @+, bwr, again

This loop will dynamically check the alignment of register A and execute
bwr0/1/2/3 depending on that alignment. Unlike the static counterpart, it
increases A by 1 at every step to keep track of where it is, bytewise.

This can only work if A is aligned at the beginning of the loop and is the
number of bytes input is divisible by 4.

Sometimes, that's not the case and you can go with this:

  ?bwr@, begin W) 8b) S>) @+, bwr, again ?bwr!,

"?bwr@," checks if A is aligned. If not, it *reads* from its destination address
in order to populate R0) so that the unaligned bytes being written to it add
themselves to it.

In a similar manner, "?bwr!," checks if A is aligned. If it's not it means there
there is a partial buffer to write. It *reads* from the destination buffer and
"OR" in the missing part to the buffer and then writes.

## 16-bit writes

What if we want to write 16-bit value? Use the "w" variants of the words. With
those variants, is is assumed that A is always divisible by 2. Examples:

  begin W) 16b) S>) @+, wbwr0, W) 16b) S>) @+, wbwr2, again
  ?bwr@, begin W) 16b) S>) @+, wbwr, again ?bwr!,

## API

bwr0, ( -- )
  Set buffer to 0 and copy S register to its "slot 0".

bwr1, ( -- )
  Copy S to buffer's slot 1

bwr2, ( -- )
  Copy S to buffer's slot 2

bwr3, ( -- )
  Copy S to buffer's slot 3, write the buffer to address A in a 32-bit write,
  then increase A by 4.

wbr0, ( -- )
  Set buffer to 0 and copy 16-bit S to buffer slots 0 and 1.

wbr2, ( -- )
  Copy 16-bit S to buffer slots 2 and 3, write the buffer to address A in a
  32-bit write, then increase A by 4.

bwr, ( -- ) *Destroys R1*
  Copy S to slot X, X being 0/1/2/3 depending on the alignment of A. Then,
  increase A by 1.

  When X=0, the buffer is reset before copying. When X=3, the buffer is written
  to address A-3 after the copy.

wbwr, ( -- ) *Destroys R1*
  Like bwr, but S is a 16-bit value. It is assumed that A is always divisible
  by 2. If it isn't, behavior is undefined.

?bwr@, ( -- ) *Destroys R1*
  Check alignment of A as X. If X=0, do nothing. Otherwise, read the 32-bit
  value of aligned A into the buffer and then mask the "remaining slots" to 0.
  That is, if X=1, mask slots 1-3, X=2 slots 2-3, X=3 slot 3.

?bwr!, ( -- ) *Destroys R1 and S*
  Check alignment of A as X. If X=0, do nothing. Otherwise, read the 32-bit
  value of aligned A into a temporary register, mask the slots already present
  in buffer and OR those values together. Then write the result to aligned A.
