Saturday, January 08, 2022

Pointers, A Look Back - 8 January 2022

Programming languages for computers come in many varieties and styles, each trying to solve a particular problem.  Although I started substantial programming in Dartmouth BASIC, then FORTRAN, and then MUMPS & assembler, I quickly latched on to the C programming language as a favorite when it became available to me.  This was about 1974 or 1975 at Purdue University using Kernighan & Ritchie C on a VAX running UNIX BSD.  Coming from BASIC, FORTRAN, & MUMPS, I was new when it came to the pointer data type, but I leveraged my assembler knowledge to get a working understanding.  Although I may have mastered the syntactical aspects of pointers and most of the operational aspects, there was a key concept that confused me.  (For those that like to jump ahead, "char *foo" is not the same as "char foo[]".)

I was working at Bell Labs on the AT&T 3B2 computer system that was still under development.  In fact, I had architected the first IO card and then started working on the development of software for the first smart IO card.  The smart IO card had an Intel 80186 on it; that is about it, because it was a prototype.  The intent was to design a test card that would accept commands and return results.  To this end, I wrote a small "IO application" that would run on the smart IO card.  I have forgotten quite what "work" the IO card was to do, but the on-card firmware needed to allocate an array and to some kind of work on it.  The style at the time was that one should have short files and short functions, on the order of a printed page (60 lines or so), so I wrote the firmware in two files.  One file to set up the programming environment and interface with the CPU, and a second file that contained the "worker" code that would be invoked by the main file.  As I recall, I wrote the main file to declare

    char    work[1000];

I was trying to be super-clever, so the worker file had a matching declaration of

    char    *work;

To be charitable to myself, I thought this bit of cleverness would get me around the problem of the size of the array - the data types and names aligned, and the programmer needed to set the right size in the main file.  My intentions were pure.

Unfortunately, this code did not run.  I thought I found a hardware bug in that the interrupt table that was so carefully constructed in the main file was coming up as all zeros as soon as the first IO command was dispatched.  Pat Walsh and a gentleman whose name I forget were the hardware guys for the IO bus.  I took my bug to them.  Aha, guys!  Your hardware is busted!  They accepted my analysis and started work to find the hardware bug.  After two days of careful work, they came back to me with a question: why did I have code that was writing zero to 1000 bytes starting with the interrupt vector table?  As I recall, the 80186 had important control tables starting at physical address zero, tables such as the interrupt vector table.  A little research reveals this is correct - "[addresses] 0000h - 03FFh are reserved for interrupt vectors."  My code was writing over the interrupt table.  When the next interrupt came in - crash!

Someone reading carefully will notice that the main declaration allocates 1000 bytes of memory named "work", while the worker file declared two bytes of space named "work" as a pointer type.  Oops.  As I learned, compilers at the time would set the initial value of "work" to zero, so when the code dereferenced "work" (*work=0 or work[0]=0), the code would write over the interrupt table at location zero, exactly like the code instructed.  The solution was simple: the code simply should to declare the array "work" consistentlly in both files.  After changing everything to "char work[WORK_SIZE]", the code worked.

I found out today that Patrick M. Walsh, 57, of Wheaton IL, passed away July 12, 2016 after a battle with cancer.  Rest in peace, Pat.

No comments: