I was just starting out in my high tech career in 1982 when the Commodore-64, the first commercially successful home computer, was introduced. It was cheap, at under $600, and a large number of applications and games were produced for it. Though I wanted one, I couldn’t afford it with a young family while transitioning careers. However, the schools were anxious for programs that could be used as teaching aids and were generous about lending them to anyone who could write a few lines of code. I took full advantage of that opportunity and wrote many little games that could be used in the classroom. Eventually, I was given a retired Commodore-PET—a much more limited version of the C-64—and so happily wrote little programs for my boys to play with.
Like the Apple II, released about the same time, the Commodore did not have an operating system as we know the term today. It came with a BASIC interpreter and, by using low-level “poke” and “peek” commands, programmers could access the very limited monitor and sound chip. Magazines, like Byte, regularly printed the hex codes for accessing various functions of the C-64, and printed out entire programs for users to type into their computers. The C-64 did not come with a disk drive; Programs had to be stored on cassette tapes. If you were a fast typist you could input most of an entire program in the time it took for the tape drive to load its contents into RAM.
Though there was a huge gap between the home computer and the serious mainframe computers I used at work, I did learn some valuable programming lessons on the C-64 and PET. Because it was such a primitive and limiting interface programmers had to be constantly aware of what they were putting into RAM. A program would simply run out of room at about 32 KB as the rest of RAM was given over to the BASIC interpreter and some low level functions, such as communicating with the keyboard. In other words, from the programmer’s point of view, every bit counted.
This meant that one had to learn how to write efficient code. However, it reinforced another bad programmer habit: it discouraged the imbedding of comments in the code to explain what was going on because comments took up valuable RAM. There was an entire class of programmers who could write dense complex code that was unintelligible to anyone else. If it broke, you were stuck because chances were good that you would not be able to unravel its secrets without a lot of help from the original programmer.
The monitor was wide enough to display 40 characters and characters were generally 8 by 8 bits. So, we never had reason to count pixels. But, within the limitations of the chunky slow graphics all sorts of small applications could be written. Most of the games I wrote involved a moving graphic, either under control of the user through keyboard commands, or control of inner logic. Given the type of programming limitations, the most effective way to discover where the graphic was in relation to other “objects” on the screen was to “peek” ahead to the next character position. If you had planned correctly you could figure out which was the bat or paddle and which was the edge of the screen. Movement was controlled by incremented or decrementing simple x and y coordinates.
It didn’t take very much code at all to replicate some of the commercially-available games. A “pong” program (bouncing ball with two paddles) could be written in less than 20 lines of code. I wrote a successful copy of the “Breakout” game (a bouncing ball smashes its way through a brick wall of differently-coloured and -valued bricks.) It was fun. But here’s something important that I learned.
I mentioned that to determine where one object on the screen was in relation to other objects on the screen, I could use a “peek” command to see what was coming next. As long as I was “peeking” ahead only one character position at a time, there was no problem. I would “collide” with another object or the screen border and reverse my coordinates to move the ball, or other object, away from the “barrier.” No problem.
But, something odd would happen if, in order to speed up my moving object, I were to increment or decrement by twos—or even some larger number. The program would “work” for a few moments and then the ball would disappear from the screen. After another few moments, the program would freeze, usually with an annoying whiny sound.
The only way out was to “break” into the code and go looking for the problem. The first few times I did this, without really understanding what was going on, I was annoyed to discover that my code had been changed. Sections of lines had been erased and some characters had been turned into unrecognizable junk. I fixed the code, ran it again, same result, same changes in the code.
Hum….
I eventually figured out what was happening. The Commodore, in its primitive state, did no error-checking on its own. Whatever you wrote, it would do—or die trying (which it often did). When I started peeking ahead by more than one character position at a time, there was a good chance that I would miss the actual screen boundary—and my “ball” would keep moving in the direction it had been going. No, the virtual ball would not emerge from the side of the monitor, but it would leave the area of code that described the monitor to the system. There wasn’t a heck of a lot more in RAM in those days, so the ball would “move” through the area of RAM where the program code resided. As it bashed into areas it had no business being, it made changes until the program could no longer function.
Modern programs cannot behave like that. The operating system makes sure that code instructions do not access areas in RAM where they shouldn’t be. The operating system itself is protected. The areas allotted to other users or applications are out of bounds. The program code itself is out of bounds. If a program does increment or decrement outside of the area it is supposed to be confined to, an immediate fatal error is issued bringing the program to a halt. That’s why programmers are supposed to always know where their program is operating and what, exactly, it is doing inside of RAM.
Of course, they usually don’t have a clue. Programming has become automated and the layers of complexity between the program code and the underlying machinery have become so thick that no one can really know. “Code generation” programs abound so that the programmer doesn’t need to know anything about the physical nature of the computer. “Drivers” (which seem to be permanently out-of-date) wrap every piece of hardware in nice thick mushy layers of code so that no metal is seen by the programmer—or user. A result: users aren’t the only ones with no understanding of the physical processes that make their computers function.
No comments:
Post a Comment