C - From The Top (part 11)

What's in a name?

You will have noticed by now that there are definite names given to a variable type (such as int, double etc). While there is nothing wrong with this, on occasion, it would be better if a double could be called something other than a double or a char something other than a char. This is where typedef steps in.

typedef name newname;

for instance

typedef char tapes;
typedef struct {
               } name;

This has the upshot that you can give your variables a symbolic meaning (which is usually simpler to remember!)

You can also use typedef statements to define other typedefs. For instance, if you use:

typedef int size;
typedef size weight;
typedef weight happiness;
happiness sad;

then sad will be an int.

Enumeration

Enumeration (or enum) uses a defined set of integer constants. The format is:

enum tag {list} listname;

where either tag or listname can be omitted.

If we had the statement

enum car_types {citroen,renault,fiat} cars;

then the constants citroen, renault and fiat are created, and the tag name is car_types with one variable called cars.

Normally, the compiler will start at the left hand side and assign this a value of zero, then move one name to the right and give this the value one and so on until the end of the list is found. However, this can be bypassed:

enum car_types {citroen=2,renault=5,fiat=65} cars;

A nice thing about enum is that once it's defined, you can use the tag name to declare other enumeration variables within the program, e.g.

enum acorn {rpc=700,a=3000,master=128};
int main(void)
{
 enum acorn computers;
 computers=a;
 printf("%d",computers);
 return 0;
}

If a program such as this were to be run, then the output for computers would be 3000.

In the example above, computers has been declared as a variable type belonging to acorn.

Enumerations provide self-documenting code (the code means something) and clarifies the structure of the program.

typedef enum {verify ,
               READ_SECTOR,
               WRITE_SECTOR,
               READ_TRACKID,
               WRITE_TRACK,
               SEEK,
               RESTORE}
 discop_reasoncodes;
typedef enum {copy_files=26 ,
              READ_DEFECT_LIST=41,
              MAP_OUT_DEFECT,
              READ_FREESPACE_MAP=46,
              READ_FREESPACE=49,
              READ_FREESPACE64=55,
              MAP_OUT_DEFECT64=57}
             FSControlOps;

These two examples further illustrate that using enum will increase the readability of the main code and so make debugging far simpler (these two examples are both from part of the filer).

Operation bitwise

There are four operators which work on a bit by bit level.

OperatorBitwise meaning
&and
|or
^XOR (eXclusive OR)
~1's complement

These will only work with int and char variables (if you remember, a bit can either be a one or a zero), so floating point numbers won't work.

What follows is known as a truth table. This shows how each of the operators works.

One's complement (~)

valueresult
10
01

(in other words, it's the opposite!)

AND (&)

value 1value 2result
000
010
100
111

OR (|)

value 1value 2result
000
011
101
111

XOR (^)

value 1value 2result
000
011
101
110

Try this simple & sum:

1010 0110
0011 1011 &

and you should get 0010 0010.

XOR has one interesting thing about it. If you start with a number, XOR it by another number,
then XOR it again with this same number, you will end up with the original number.

These bitwise operators can be used in the production of some forms of file encryption (or text encryption).

Possibly the simplest form of data encryption is using the 1's complement (~) operator. While the output will look gibberish, it is simple to decode.

A far more secure method of encryption is the use of a seeded XOR. Here the seed is provided by either a number (input) or by a key press. Failure to give the correct key press (or code number) will result in the file either being spat out or XORd again (encrypting it further). The whole file is encrypted by XORing it byte by byte with the seed value. Decryption is performed with the correct seed and the XOR process repeated.

By using a combination of ^ and ~, it should be possible to create a very secure file (especially if they have seeds [except for ~]). Imagine having to crack a code which has three numbers (or characters) and getting it wrong further encrypts the code. The addition of a few more bitwise operators (with seeds) would further make the decryption terrible!

A small aside

Back in the 1980's, there was a popular home micro called the ZX Spectrum (OK, most of us will remember this machine, but this scene setting is only for the younger readers!). Like most home micros, the Spectrum had a problem with copying the software. As it was a tape based system, copying was as simple as placing the original and a blank into a double tape deck. No protection would stop this.

Some companies came up with a very cunning plan: the use of colour key code sheets to test after the program had been loaded. These would invariably be unphotocopiable and would normally be lost before a few weeks had passed. To start off with, these cards would be (say) four colours by four blocks by 40 numbers (a total of 640 combinations). These could be hand copied in an hour or so. The protection was useless.

To get around this, companies would use four colours by four blocks by 200 numbers (3200 combinations). This would not be easy to copy, so the protection was good (until the advent of the MultiFace that is!).

The method of checking was simple and can be thought of like this

enum colours {red=1,green=2,blue=3,yellow=4};

Each block would need a checksum. Therefore, if the block was red, blue, yellow, red the checksum would be nine. There would be another three blocks with another three checksums. This would give a total checksum for the line.

There would now be three lines of protection.

  1. Is the checksum for each block okay?
  2. Are colours in the correct order?
  3. Is the overall checksum correct?

Failure at any point and you'd need another go. Fail that and you need to reload the game.

To make things even tougher for the hacker would be that each block checksum would be either XORd with the checksum of the opposite block x lines down (this was defined by a code passed to it and would vary) or would have an OR or AND performed on it using the checksum from the opposite block. This meant that even if you managed to hack through to the key card codes, they would still be meaningless.

Given this choice of hours of toil or spending a tenner on a copy of the game, most spent the tenner!

(By opposite block I mean this: there are four blocks of four colours per line. The opposites are:

1 -> 43->2
2 -> 34->1

This code was finally fully hacked by a group eight months after starting. The group were professional programmers.)

Shift it

The final part of this tutorial deals with the shift operators, << and >>.

These shift a bit x places to the left or x places to the right.

You have to watch it when using these though. If you shift to the left, the bits move to the left, but then zeros are brought in on the right. A right shift does the same, except that the zeros are brought in from the left. The exception is if the number is negative, in which case ones are brought in. OK, the shift is supposed to do this, but if you're not used to using shift operators, the result may not have been what was expected.

A simpler way to think about the shifts is this. A right shift is equivalent to dividing by 2, while a left shift is the equivalent of multiplying by 2. For example:

i = i<<2;

will multiply i by 4.

This can be handy. The following will display a binary representation of a character (denoted by ch)

for (i=128;i>0;i=i/2)
 if (i & ch) printf("1 ");
 else
 printf("0 ");

By using the right operator, the loop can be re­written

for (i=128; i>0 ; i=i>>1)

This has meant that there is one less instruction to follow (think about it, i=i/2 is two instructions, i/2 and i=). OK, this may not sound that impressive, but imagine you have lots of these. On a RiscPC, you wouldn't notice it, but on an A30x0 you would.

Homework!

This time's task is not that bad (he says).

Take the simple telephone number program and encrypt the outgoing file using as many bitwise operations and seeds as you want - the more the merrier.

On reloading though, the file must be decrypted automatically (this is the hint on how you must do this encryption).

Next time, I will look at the preprocessor.


Source: Archive Magazine 13.11
Publication: Archive Magazine
Contributor: Paul Johnson