Learning Computer Science via Assembly Language
johnnyb writes "
A new book was just released which is based on a new concept - teaching computer science through assembly language (Linux x86 assembly language, to be exact). This book teaches how the machine itself operates, rather than just the language. I've found that the key difference between mediocre and excellent programmers is whether or not they know assembly language. Those that do tend to understand computers themselves at a much deeper level.
Although unheard of today, this concept isn't really all that new -- there used to not be much choice in years past. Apple computers came with only BASIC and assembly language, and there were books available on assembly language for kids.
This is why the old-timers are often viewed as 'wizards': they had to know assembly language programming. Perhaps this current obsession with learning using 'easy' languages is the wrong way to do things. High-level languages are great, but learning them will never teach you about computers. Perhaps it's time that computer science curriculums start teaching assembly language first."
Although unheard of today, this concept isn't really all that new -- there used to not be much choice in years past.
While starting Computer Science students off with assembly (without first introducing them to a high-level language) may be a relatively new concept these days, the idea of teaching low-level languages to Computer Science students is not a revolutionary technique whatsoever. Every decent Computer Science curriculum includes several semesters of courses in which assembly language is required, to demonstrate their knowledge of basic computer processes.
That reminds me of a great fortune:
"The C Programming Language -- A language which combines the
flexibility of assembly language with the power of assembly language."
It is in the same fashion that win32 asm is different from linux asm. The core is the same but knowing the core of x86 assembler is going to get you far if what you are wanting to do is talk to the kernel.
As copyright owner of this comment, I authorize everyone to defeat any technological measure which limits access to it.
What you meant to say was that your new book has just been released. If you're going to pimp your wares on Slashdot, at least put an appropriate disclaimer on. That said, I completely agree with the premise of the book. I've met a lot of mediocre programmers, and a few good ones. But I've never yet met a real star that didn't have some background in assembly language programming. Personally, I haven't written anything in assembly in well over a decade. But that fact that I can do so if needed makes me a better programmer, and I'd recommend it to any aspiring coder as a key skill to learn. I wouldn't say IA32 is a particularly nice introduction (I'd start with a cleaner, simpler architecture, such as 6502), but it is at least widely available to anyone that wants to study it...
"The invisible and the non-existent look very much alike." -- Delos B. McKown
http://savannah.nongnu.org/projects/pgubook/
It's also being used at Princeton
Get it to your Valentine on time! Choose UPS 2 DAY and pay the price of Ground.
Yeah. Give my GF a book on Linux Assembly programming. That should get those panties off in a hurry.
"Quotation is a serviceable substitute for wit." --Oscar Wilde
I think it's a little weird to call this "Learning Computer Science via Assembly Language." It's programming, not computer science. Computer science is really only marginally about computers. It has to do more with algorithms, logic, and mathematics.
You can study computer science, and produce new knowledge in the field, without ever touching a computer.
This misunderstanding is, I think, part of the reason so many students drop out of CompSci. They head into it thinking it's about programming, and are startled to find that computation and programming are not equivalent.
That's why the Compilers course at PSU is considered the "filter" which kills all the students who aren't really interested in computer science. They really need to spin off a seperate "Software engineering" school for these students, since what they really want to study is programming.
Well, for starters the syntax for assemblers is different. There are two standards, the AT&T standard (which is used by the GNU assembler) and the other one that is more familiar to DOS/Windows x86 assembly programmers (which is used by the NASM assmebler).
Second, OS interfaces for making system calls (e.g., to read files, open network connections, etc) are different in Linux versus DOS or Windows).
My other first post is car post.
Is "Linux x86 assembly" any different to any other kind of "x86 assembly"?
Yes. Although it requires understanding the CPU's native capabilities to the same degree, Linux uses AT&T syntax, whereas most of the Wintel world uses (unsurprisingly) Intel/Microsoft syntax.
Personally, although I far prefer coding C under Linux, I prefer Intel syntax assembly. Even with many years of coding experience, I find AT&T syntax unneccessarily convoluted and somewhat difficult to quickly read through.
The larger idea holds, however, regardless of what assembler you use. I wholeheartedly agree with the FP - People who know assembly produce better code by almost any measurement except "object-oriented-ness", which assembly makes difficult to an extreme. On that same note, I consider that as one of the better arguments against OO code - It simply does not map well to real-world CPUs, thus introducing inefficiencies in the translation to something the CPU does handle natively.
Real programmers learn machine code.
/dev/kmem
REAL programmers use cat >
"Straddling the sword of technology..."
The idea isn't to actually use the language but rather to learn it to help you understand other languages.
It's like learning Latin. Nobody actually uses it, but it can give you a deeper understanding of the languages that are based on it.
TW
His motto is "Anyone who doesn't know machine language has no business using a computer."
Just say to him "Well Grandpa, my motto is anyone who can't describe, with exacting detail, all the functions of every organ in the human body doesn't deserve to live."
You have a good point. I code assembly on many different CPU's, and I can only see a minor difference between them
For example, on the 6502 family (like the 6510 from the C64), you have only three registers; X, Y and A. These registers can only hold a byte each. Most of the variables you have are stored in zero pointers, a 255-byte range from address $00-$FF.
Then the 68k CPU (as in the Amiga, Atari, etc) you have several more registers which can be used more freely. You have D0-D7 data registers and A0-A7 address registers. These can be operated as bytes, words or longwords as you wish, from wherever you want.
The x86 assembly is written the "wrong way", and is pretty confusing at times. Where I would say "move.l 4,a6" on the 68k, I have to say "mov dx,4" on the x86. Takes a few minutes to adjust each time.
Once you master assembly language on one CPU, it's pretty easy to switch to another.
I still think the 680x0 series are the best.
www.6502asm.com - Code 6502 assembly or.. DIE!!
``Bad Idea: Teaching CS by starting with one of the most cryptic languages around, and then trying to teach basic CS fundamentals.''
I completely disagree. Assembly is actually one of the simplest languages around. There is little syntax, and hardly any magic words that have to be memorized. Assembly makes an excellent tool for learning basic CS fundamentals; you get a very direct feeling for how CPUs work, how data structures can be implemented, and why they behave the way they do. I wouldn't recommend assembly for serious programming, but for getting an understanding of the fundamentals, it's hard to beat.
Please correct me if I got my facts wrong.
On that same note, I consider that as one of the better arguments against OO code - It simply does not map well to real-world CPUs, thus introducing inefficiencies in the translation to something the CPU does handle natively
maxim: cycles are cheap, people are expensive. For the *vast majority* of software it is significantly better value to design and build a well architected OO solution than to optimise for performance in languages and methodologies that are more difficult to implement and maintain. Who cares if it's not very efficient - it'll run twice as fast in 18 months, and will be a lot cheaper to change when the client figures out what the actually wanted in the first place. But I guess you already knew that.
"The new wave is not value-added; it's garbage-subtracted" - Esther Dyson, Dec 1994
maxim: cycles are cheap, people are expensive.
True. This topic, however, goes beyond mere maximizing of program performance. Pur simply, if you know assembler, you can take the CPU's strengths and weaknesses into consideration while still writing readable, maintainable, "good" code. If you do not know assembly, you might produce simply beautiful code, but then have no clue why it runs like a three-legged dog.
it is significantly better value to design and build a well architected OO solution
Key phrase there, "well-architected". In practice, the entire idea of "object reuse" counts as a complete myth (I would say "lie", but since it seems like more of a self-deception, I woun't go that far). I have yet to see a project where more than a handful of objects from older code would provide any benefit at all, and even those that did required subclassing them to add and/or modify over half of their existing functionality. On the other hand, I have literally hundreds of vanilla-C functions I've written over the years from which I draw with almost every program I write, and that require no modification to work correctly (in honesty, the second time I use them, I usually need to modify them to generalize better, but after that, c'est fini).
Who cares if it's not very efficient - it'll run twice as fast in 18 months
Y'know, I once heard an amusing joke about that... "How can you tell a CS guy from a programmer?" "The CS guy writes code that either won't run on any machine you can fit on a single planet, or will run too slowly to serve its purpose until technology catches up with it in few decades". Something like tha - I killed the joke, but you get the idea.
Yeah, computers constantly improve. But the clients want their shiny new software to run this year (if not last year, or at least on 5-year old equipment), not two years hence.
Well, some of us code assembly on bare hardware. We have to roll our own 'api' and include it in there with the rest of the code.
I've worked before with programmers who had little experience in programming 'bare hardware'- they do really foolish things like not initing timers, setting up stack pointers, and the like.
Writing bare ASM code for a processor (where it boots up out of your own EPROM or on an emulator) is good experience in minimalism. It can give you a good feeling when the project is all done and you can say you did it all yourself.
For those interested in getting into this kind of thing, start with a PIC embedded controller and a cheap programmer. You can get PIC assembly language tools for free, and build a programmer, or buy a kit for a programmer, that plugs into your serial or parallel port. Your first PIC machine can be the CPU, a clock crystal, a few resistors and capacitors, and the LED you want to blink, or whatever other intrigues you. If you're not into complex soldering, and/or layout and complex schematics, you can buy pre-etched boards you just plug the PIC into.
Another easy-start processor would be the 68HC11. It has a bootstrap built into ROM. Basically, you can jumper the chip so it wakes up listening on the serial port for code you send down the wire at it, and burns it into the EEPROM memory in the 'HC11 chip itself. Move the jumper and reboot the chip, and it's running your code.
I think this is far more interesting that just writing apps that run on an Operating System you didn't roll yourself.
---
People who know assembly produce better code by almost any measurement except "object-oriented-ness", which assembly makes difficult to an extreme.
Actually, they don't.
A study was done, some decades ago, on the issue of whether compilers were approaching the abilities of a good assembly programmer. The results were surprising:
While a good assembly programmer could usually beat the compiler if he really hunkered down and applied himself to the particular piece of code, on the average his code would be worse - because he didn't maintain that focus on every line of every program.
The programmer might know all the tricks. But the compiler knew MOST of the tricks, and applied them EVERYWHERE, ALL THE TIME.
Potentially the programmer could still beat the compiler in reasonable time by focusing on the code that gets most of the execution. But the second part of Knuth's Law applies: "95% of the processor time is spent in 5% of the code - and it's NOT the 5% you THOUGHT it was." You have to do extra tuning passes AFTER the code is working to find and improve the REAL critical 5%. This typically was unnecessary in applications (though it would sometimes get done in OSes and some servers).
This discovery lead directly to two things:
1) Because a programmer can get so much more done and working right with a given time and effort using a compiler than using an assembler, and the compiler was emitting better assembly on the average, assember was abandoned for anything where it wasn't really necessary. That typically means:
- A little bit in the kernel where it can't be avoided (typically bootup, the very start of the interrupt handling, and maybe context switching). (Unix System 6 kernel was 10k lines, of which 1.5k was assembler - and the assembly fraction got squeezed down from then on.)
- A little bit in the libraries (typically the very start of a program and the system call subroutines)
- Maybe a few tiny bits embedded in compiler code, to optimize the core of something slow.
2) The replacement of microcoded CISC processors (i.e. PDP11, VAX, 68K) with RISC processors (i.e. SPARC, MIPS). (x86 was CISC but hung in there due to initera and cheapness.)
Who cares if it takes three instructions instead of one to do some complex function, or if execution near jumps isn't straightforward? The compiler will crank out the three instructions and keep track of the funny execution sequence. Meanwhile you can shrink the processor and run the instructions at the microcode engine's speed - which can be increased further by reducing the nubmer of gates and length of wiring, and end up with a smaller chip (which means higher yeilds, which means making use of the next, faster, FAB technology sooner.)
CISC pushed RISK out of general purpose processors again once the die sizes got big: You can use those extra gates for pipelining, branch prediction, and other stuff that lets you gain back more by parallelism than you lost by expanding the execution units. But it's still alive and well in embedded cores (where you need SOME crunch but want to use most of the silicon for other stuff) and in systems that don't need the absolute cutting-edge of speed or DO need a very low power-per-computation figure.
The compiler advantage over an assembly programmer is extreme both with RISC and with a poorly-designed CISC instruction set (like the early x86es). Well-designed CISC instruction sets (like PDP11, VAX, and 68k) are tuned to simplify the compilers' work - which makes them understandable enough that the tricks are fewer and good code is easier for a human to write. This puts an assembly programmer back in the running. But on the average the compiler still wins.
(But understanding how assembly instruction sets work, and how compilers work, are both useful for writing better code at the compiler level. Less so now that optimizers are really good - but the understanding is still helpful.)
Bantam Dominique roosters crow a four-note song. Once you've heard it as "Happy BIRTHday" you can't NOT hear it that way
This was posted to USENET by its author, Ed Nather, on May 21, 1983.
A recent article devoted to the *macho* side of programming
made the bald and unvarnished statement:
Real Programmers write in FORTRAN.
Maybe they do now,
in this decadent era of
Lite beer, hand calculators, and "user-friendly" software
but back in the Good Old Days,
when the term "software" sounded funny
and Real Computers were made out of drums and vacuum tubes,
Real Programmers wrote in machine code.
Not FORTRAN. Not RATFOR. Not, even, assembly language.
Machine Code.
Raw, unadorned, inscrutable hexadecimal numbers.
Lest a whole new generation of programmers
grow up in ignorance of this glorious past,
I feel duty-bound to describe,
as best I can through the generation gap,
how a Real Programmer wrote code.
I'll call him Mel,
because that was his name.
I first met Mel when I went to work for Royal McBee Computer Corp.,
a now-defunct subsidiary of the typewriter company.
The firm manufactured the LGP-30,
a small, cheap (by the standards of the day)
drum-memory computer,
and had just started to manufacture
the RPC-4000, a much-improved,
bigger, better, faster --- drum-memory computer.
Cores cost too much,
and weren't here to stay, anyway.
(That's why you haven't heard of the company, or the computer.)
I had been hired to write a FORTRAN compiler
Mel didn't approve of compilers.
"If a program can't rewrite its own code",
he asked, "what good is it?"
Mel had written,
in hexadecimal,
the most popular computer program the company owned.
It ran on the LGP-30
and played blackjack with potential customers
at computer shows.
Its effect was always dramatic.
The LGP-30 booth was packed at every show,
and the IBM salesmen stood around
talking to each other.
Whether or not this actually sold computers
was a question we never discussed.
Mel's job was to re-write
the blackjack program for the RPC-4000.
(Port? What does that mean?)
The new computer had a one-plus-one
addressing scheme,
in which each machine instruction,
in addition to the operation code
and the address of the needed operand,
had a second address that indicated where, on the revolving drum,
the next instruction was located.
In modern parlance,
every single instruction was followed by a GO TO!
Put *that* in Pascal's pipe and smoke it.
Mel loved the RPC-4000
because he could optimize his code:
that is, locate instructions on the drum
so that just as one finished its job,
the next would be just arriving at the "read head"
and available for immediate execution.
There was a program to do that job,
an "optimizing assembler",
but Mel refused to use it.
"You never know where it's going to put things",
he explained, "so you'd have to use separate constants".
It was a long time before I understood that remark.
Since Mel knew the numerical value
of every operation code,
and assigned his own drum addresses,
every instruction he wrote could also be considered
a numerical constant.
He could pick up an earlier "add" instruction, say,
and multiply by it,
if it had the right numeric value.
His code was not easy for someone else to modify.
I compared Mel's hand-optimized programs
with the same code massaged by the optimizing assembler program,
and Mel's always ran faster.
That was because the "top-down" method of program design
hadn't been invented yet,
and Mel wouldn't have used it anyway.
He wrote the innermost parts of his program loops first,
so they would get first choice
of the optimum address locations on the drum.
A slashdotter who didn't build his own computer is like a Jedi who didn't build his own lightsaber.
The problem is that computer scientists don't make good programmers and vice-versa. If you're good with code and hunker down to write lots of programs, then you tend to clash with the all-theory-no-code camp that delights in big-O notation and graph theory. Of course there is a lot of middle ground, but in general the PHd professor types that staff CompSci departments I've been in tend to have stopped learning about computers as soon as they finished their doctorate and instead concentrate on internecine politics, incomprehensible papers, and teaching the occaisional class (leaving most of that to T.A.'s who actually teach the class and understand how to compile programs).
Meanwhile the coder types graduate with a B.S. or maybe a masters then go into commercial development shops and crank out code, forgetting as much as they can about red-black trees and other subtle CompSci concepts.
So if you want to crank out programmers, then assembly is probably a good thing. God knows I learned a lot from the assembly classes I took.
If you're trying to scare students away then assembly is also a good tactic. Nothing like a good hex dump to get some non CompSci students eyes to glaze over. Sort of like making people take Biology or Physics, but instead of teaching about cells and newtonian motion, jump right into the finer points of quantum mechanics or amino acid chemistry.
On the other hand, for 2nd year CompSci students, Assembly is probably a good thing to get out of the way. It really sucks, for example to take economics for 4 years only to learn at the end "just kidding, reality is too complex to model so these are all just gross oversimplifications." Sort of like thinking programming == Java then finding out how it all _really_ works.
"But actually trying to use m4 as a general-purpose langage would be deeply perverse" --ESR
When *I* was young we didn't *have* vacuum in space yet! You post-big-bang kids don't know how easy you have it!
--AROS is an Open Source AmigaOS clone, and source compatible with AmigaOS! Try the x86 build at http://www.aros.org
The correct answers are down there, but just to collect them and clarify - you can build anything using nothing but NANDS. Alternatively, you can build anything using nothing but XORS. You can prove this easily using demorgan's theorem.
However, in the real world, NANDS are cheap (2-3 transistors), so that's what everyone uses.
To make laws that man cannot, and will not obey, serves to bring all law into contempt.
--E.C. Stanton
Assembly has little to nothing to do with either programming or computer science anymore. Computer science (IMHO) deals with the study of software engineering and algorithms
If it truly is a science, then someone who finishes a Bachelors and Masters program in Computer Science had better be capable of contributing to the advancement of this field.
This could be through the development of new languages, in which case I hope they know a thing or two about assembly in the first place.
Otherwise, a couple of Computer Science degrees would simply mean someone is a techno-wonk, a professional student, or just a technician rather than a professional engineer/scientist.
--
Disclaimer: 90% of the programmers out there do not
need a Computer Science degree, and 90% of the jobs
out there for developers don't need CS graduates
However, in the real world, NANDS are cheap (2-3 transistors), so that's what everyone uses.
Well, NANDs are easy to make with MOSFETs or vacuum tubes.
But I suggest that, in order to simplify the learning of digital logic and avoid this whole nastiness of DeMorgan, we should adopt relays as our primary logic device.
Think about it: two relays with their contacts in parallel = OR. Two relays with their contacts in series = AND. A relay with normally-closed contacts = NOT.
In this way, all design work can be done with natural logic (AND, OR, NOT) rather than "efficient" NAND, NOR, etc.
On top of that, your computer would make satisfying clicking sounds reminiscent of a pinball machine's scorekeeping system or an old elevator contoller, while you're crunching SETI@Home units.
I'm building a 4-bit binary full adder with nothing but relays in order to demonstrate their sheer computing power, and was hoping that someone could write me drivers to allow it to have practical uses.
Fire and Meat. Yummy.