http://www.charlespetzold.com/code/
I've only read a little bit of the 2nd book (IIRC I stopped reading and lost interest when I found a very obvious and repeated error), but all of the 1st one. The 1st one is highly recommended.
IMHO the trend of teaching things in CS top-down doesn't encourage understanding of how computers actually work, which turns out to be quite important in many situations, especially debugging and knowing the limitations of what computers can and cannot do. I believe that a lot of the problems with software today (bugs, inefficiency, overcomplexity) can be eliminated or at least reduced greatly via a deeper understanding of these basic concepts, since the majority of programmers seem to have only a very vague idea of e.g. memory, number representation, CPU operation, etc.
I think CS should start at the lowest level above EE, with binary representation of data and basic digital circuits (without e.g. going into the physics of how transistors work.) Have students build basic CPUs and write/enter programs for them and run them manually to see computation happening at this level. Then proceed up to Asm, C, and higher-level languages to write more complex programs, maybe even a simple operating system, accompanied by some of the more theoretical material (e.g. algorithmic complexity), continuing to keep the theme that there is a practical use for learning these things.