So, coming into this piece of code, a2 is set to a value representing the current 16 vertical pixels' states, and at the end, the current pixels' states are set to the value of a2?
Also, in your example, I think you meant the first vertical byte of M to be 0b1111,1110 (assuming the top is msb), not 0b1111,1111.
Here I will be speaking of using an OR, and I will only be talking about the left most pixels of each character (X==0).
Write a M to LCD_LINE2
from top to bottom, the bits (in the left most column) should be set to
0000,0000,1111,1110,0000,0000,0000,0000,00...
Write a M to LCD_LINE1-1
from top to bottom, the bits (in the left most column) should be set to
0111,1111,1111,1110,0000,0000,0000,0000,00...
because you take the top 16 bits which was 0000,0000,1111,1110, and you OR it with 0111,1111,0000,0000. However, you need to give the last 0 of the current character higher preference than the first 1 of the last character. Ideally, it would look like this: 0111,1111,0111,1110, where bit 7 (of 0-15) is a 0, not a 1.
Since all NXT ASCII characters are 6x8 pixels (even if some of them are white), it is important to clear out an 8 bit vertical area before we OR it with the new character.
Something like this: a2 &= ~((UWORD)0x00FF<<offset) would clear out the area we need to use, and then something like this: a2 |= (UWORD)a<<offset would put the correct pixels into the correct place. Or, combined, it would be a2 = (a2 & ~((UWORD)0x00FF<<offset))|((UWORD)a<<offset)
Edit: I see now you figured out a way before me