I should be posting about the reverse engineering project I'm working on. I have a Cidco #MailStation and want to do homebrew development for it. (See https://en.wikipedia.org/wiki/Cidco_MailStation). I've got firmware version 2.53YR loaded into Ghidra and am working on an annotated disassembly. Lots of reverse engineering has already been done by others, but I don't fully understand their documentation, hence I need to take a look at the firmware myself to understand how to make API calls into it.
Cidco MailStation - Wikipedia

The #MailStation uses a #Z80, and whatever C compiler was used to produce this firmware, let's just say it... did not... emit compact code. (Not pictured, about seven more repetitions of near-identical code.)

For reference, this assembly code comes from a larger function that finds the intersection between two boxes. In C, it'd be:

struct box {
uint16_t x0;
uint16_t y0;
uint16_t x1;
uint16_t y1;
};

void intersect(box* src1, box* src2, box* dest) {
dest->x0 = max(src1->x0, src2->x0);
dest->y0 = max(src1->y0, src2->y0);
dest->x1 = min(src1->x1, src2->x1);
dest->y1 = min(src1->y1, src2->y1);

if (dest->x0 > dest->x1 || dest->y0 > dest->y1) {
dest->x0 = dest->y0 = dest->x1 = dest->y1 = 0;
}
}

But everything is inlined, so the overall function size is 495 bytes, ouch! That's gotta hurt!

Well, beats me why the compiler loves to generate this instead of,

LD HL, 0x10
ADD HL, SP
INC (HL)

Insted, it's doing a 16-bit INC and immediately throwing out the high byte, because the underlying variable is a byte.

Sadly, this is part of the generated code for every "for" loop, so I recognize it on sight now.

#z80 #mailstation

I've heard it said the #mailstation firmware was produced by an 8080 compiler that was unaware of #z80 extensions, so I checked an #intel8080 instruction table and nope, even the 8080 could INC (HL)! So chalk up some more bytes and cycles for a less-than-brilliant compiler.
It's always a good feeling getting to rename a struct field from something like "12listfunc" to, say, "widget_type". Roughly a million of these to go. #reverseengineering

Old function signature: byte get_tab16.1_ram_etc???nogo???_verify_assoc_wozthis(byte tab16row)

New function signature: byte get_ram_page_and_flags(byte widget_num)

Maybe you can see why I'm going through and renaming all these.

Of course, I am super glad that others have reverse engineered this enough to give me any function names and entry points to start with. Let alone ones that make a certain kind of sense if you understand the naming scheme.

"tab16" was the name of a particular table with 16-byte entries. The original reversers later discovered this to be a table of widget structs, but didn't rename it. "tab16row", therefore, is widget number.

"1_ram_etc???nogo???" is the name of the first field of the widget struct and some notes about its potential purpose. I gave it the more streamlined name of "ram_page_and_flags".

"wozthis" is the name of a global variable that was later discovered to be the index of the currently running app, but again, not renamed. "verify_assoc_wozthis" is referring to logic in the function that falls back to returning the current app's rampage_and_flags field, instead of looking it up in the widget, if a placeholder widget number (-1) is specified.

So the name makes perfect sense but only if you already kinda understand what the function did  
I'm not really smart enough to intuit all this, so I have to re-rename everything with names that make sense TO ME!

Woweee, #ghidra's built-in emulator is super cool! Works great for checking that little snippets of z80 code do what you think.

box2->x0 = (box1->x1 - 30)*foo / (fbuf[0] + fbuf[1])

Sometime soon, hopefully, I will understand the significance of this value that #MailStation widget #4 has calculated.

Okay, that's hello world on the #MailStation.
Sweet jesus #MailStation that's not honey! You're eating uninitialized memory!
Okay that's more like it. (Apparently it helps if you don't goof up and push a function's stack parameters in reverse order.)