Why char 0x80 == 0xFFFFFF80?
To illustrate the reasons better let's look at the actual content of the data.

0x80 is 0b10000000 in binary. A char is a signed variable, which means that the most significant bit is the sign bit, with two's complement representation

So in 8-bit signed representation the value 0b10000000 is a sign bit and zero value - which is -128 in decimal notation, which is "-128 + the 7 least significant bits", or -128 + 0 in this case.

When you print a signed value on the Arduino as HEX there is no 8-bit specific function for it. Neither is there a 16 bit function. However there is a 32-bit signed function, which is the closest match available. So the compiler performs type promotion to 32-bit signed, then represent it as hexadecimal.

Now -128 in signed 32-bit is calculated in the same way - take the value of the most significant bit, negate it, and add the value of all the other bits to it.  With 32-bits the value of the most significant bit, in decimal, is 2147483648, so negate that into -2147483648. Now we need to make that into -128, so we need the difference between the two to add to it. That's 2147483520.

So you get the MSB + 2147483520, which in hexadecimal, is 0xFFFFFF80.

Of course, the compiler doesn't go through that whole routine - instead it uses something called sign extension. This method basically means copying the sign bit from the original smaller (char) type into every bit to the left of it in the larger type:

                           10000000 8 bit signed
                  11111111 10000000 16 bit signed
11111111 11111111 11111111 10000000 32 bit signed


All those are -128 in two's complement, just stored in different size variables. So when you come to print it, that is just what you get - the two's complement representation of -128 in the variable size that has been accepted by the print function.

You might find this site useful for experimentation and furthering your understanding of variable data storage.