Write Great Code: Understanding the Machine, Volume I ISBN:1593270038 by Randall Hyde No Starch Press © 2004 (410 pages) This first volume teaches important concepts of machine organization in a languageindependent fashion, giving programmers what they need to know to write great code in any language, without the usual overhead of learning assembly language Table of Contents Write Great Code—Understanding the Machine, Volume I Chapter 1 - What you Need to Know to Write Great Code Chapter 2 - Numeric Representation Chapter 3 - Binary Arithmetic and Bit Operations Chapter 4 - Floating-Point Representation Chapter 5 - Character Representation Chapter 6 - Memory Organization and Access Chapter 7 - Composite Data Types and Memory Objects Chapter 8 - Boolean Logic and Digital Design Chapter 9 - CPU Architecture Chapter 10 - Instruction Set Architecture Chapter 11 - Memory Architecture and Organization Chapter 12 - Input and Output (I/O) Appendix A - ASCII Character Set Index List of Figures List of Tables Back Cover This, the first volume in Randall Hyde’s Write Great Code series, dives into machine organization without the extra overhead of learning assembly language programming Written for C/C++, VB, Pascal, Java, and other high-level language programmers, Volume I, “Understanding the Machine,” fills in the low-level details of machine organization that are often left out of computer science and engineering courses Learn: How the machine represents numbers, strings, and high-level data structures, so you’ll know the inherent cost of using them How to organize your data, so the machine can access it efficiently How the CPU operates, so you can write code that works the way the machine does How I/O devices operate, so you can maximize your application’s performance when accessing those devices How to best use the memory hierarchy to produce the fastest possible programs Great code is efficient code But before you can write truly efficient code, you must understand how computer systems execute programs and how abstractions in programming languages map to the machine’s low-level hardware After all, compilers don’t write the best machine code; programmers do The information in this first volume of the Write Great Code series gives you the foundation upon which all great software is built About the Author Randall Hyde is the author of The Art of Assembly Language (No Starch Press), one of the most highly recommended resources on assembly He is also the co-author of The Waite Group’s MASM 6.0 Bible He has written for Dr Dobb’s Journal, Byte, as well as professional journals Write Great Code-Understanding the Machine, Volume I Randall Hyde NO STARCH PRESS San Francisco Copyright © 2004 Randall Hyde All rights reserved No part of this work may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or by any information storage or retrieval system, without the prior written permission of the copyright owner and the publisher 1 2 3 4 5 6 7 8 9 10 - 07 06 05 04 No Starch Press and the No Starch Press logo are registered trademarks of No Starch Press, Inc Other product and company names mentioned herein may be the trademarks of their respective owners Rather than use a trademark symbol with every occurrence of a trademarked name, we are using the names only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark Publisher: William Pollock Managing Editor: Karol Jurado Cover and Interior Design: Octopod Studios Developmental Editor: Hillel Heinstein Technical Reviewer: Mark de Wever Copyeditor: Andy Carroll Compositor: Riley Hoffman Proofreader: Stephanie Provines For information on book distributors or translations, please contact No Starch Press, Inc directly: No Starch Press, Inc 555 De Haro Street, Suite 250, San Francisco, CA 94107 phone: 415-863-9900; fax: 415-863-9950; info@nostarch.com; http://www.nostarch.com The information in this book is distributed on an 'As Is' basis, without warranty While every precaution has been taken in the preparation of this work, neither the author nor No Starch Press, Inc shall have any liability to any person or entity with respect to any loss or damage caused or alleged to be caused directly or indirectly by the information contained in it Library of Congress Cataloguing-in-Publication Data Hyde, Randall Write great code : understanding the machine / Randall Hyde p cm ISBN: 1593270038 Computer programming Computer architecture I Title QA76.6.H94 2004 005.1 dc22 2003017502 Acknowledgments A book such as the one you are now holding is rarely the work of one person, even if only one name appears on the cover Producing this book has been a team effort, and I would like to take this opportunity to acknowledge the other individuals who have contributed greatly to its quality Mary Philips, a wonderful friend who helped proofread several of the earlier chapters Bill Pollock, who read and offered suggestions for Chapters 1 through 6 Karol Jurado, my editor, who shepherded this project from conception toproduction Hillel Heinstein, the developmental editor, who kept this book on the right track and helped clean up the writing Andy Carroll, the copyeditor, who also helped improve my writing Mark de Wever, the technical reviewer, who caught a large number of little typos and technical problems to help ensure the accuracy of the material Riley Hoffman, who handled the page layout chores and helped ensure that the book (including the listings) was readable Stephanie Provines, whose proofreading caught several typographical and layout errors Leigh Sacks, who has done a great job of marketing this book and my earlier book, The Art of Assembly Language And of course, all the great people at No Starch Press who've been supportive of this project from the very beginning Last, but not least, I would like to thank my wife, Mandy, who allowed meto get away with not spending as much time working around the house asI should have, so that I could get this book out the door Thanks to all of you, Randall Hyde Thinking Low-Level, Writing High-Level The goal of this volume, Understanding the Machine, was to get you thinking at the level of the machine Of course, one way to force yourself to write code at the machine level is to write your applications in assembly language When you've got to write code statement by statement in assembly language, you're going to have a pretty good idea of the cost associated with every statement Unfortunately, using assembly language isn't a realistic solution for most applications The disadvantages of assembly language have been well publicized (and blown out of proportion) over the past several decades, so most people are quite familiar with the drawbacks, real or imagined Assembly just isn't an option for most people Though writing code in assembly language forces you to think down at the machine level, writing code in a high-level language does not force you to think at a high level of abstraction There is nothing preventing you from thinking in low-level terms while writing high-level code The goal of this book was to provide you with the background knowledge you need to do exactly that - think in low-level terms while writing high-level code By learning how the computer represents data, you've learned how highlevel language data types translate to the machine level By learning how the CPU executes machine instructions, you've learned the costs of various operations in your high-level language applications By learning about memory performance, you've learned how to organize your highlevel language variables and other data to maximize cache and memory access There's only one piece missing from this puzzle: 'Exactly how does a particular compiler map high-level language statements to the machine level?' Unfortunately, that topic is sufficiently large that it deserves an entire book on its own And that's the purpose of the next volume in the Write Great Code series: Thinking Low-Level, Writing HighLevel Write Great Code: Thinking Low-Level, Writing High-Level will pick up right where this volume leaves off Thinking Low-Level, Writing HighLevel will teach you how each statement in a typical high-level language maps to machine code, how you can choose between two or more highlevel sequences to produce the best possible machine code, and how to analyze that machine code to determine its quality and the quality of the high-level code that produced it And while doing all of this, it will give you a greater appreciation of how compilers do their job and how you can assist them in doing their job better Congratulations on your progress thus far towards knowing how to write great code See you in volume 2 Chapter 1: What you Need to Know to Write Great Code Write Great Code will teach you how to write code you can be proud of, code that will impress other programmers, code that will satisfy customers and prove popular with users, and code that people (customers, your boss, and so on) won't mind paying top dollar to obtain In general, the volumes in the Write Great Code series will discuss how to write software that achieves legendary status, eliciting the awe of other programmers Figure 8-18: Building an n-bit adder using half and full adders Figure 8-19: Seven-segment display Figure 8-20: Seven-segment values for '0' through '9' Figure 8-21: Adding four 256-MB memory modules to a system Figure 8-22: Instruction (opcode) format for a very simple CPU Figure 8-23: Encoding the MOV(EAX, EBX); instruction Figure 8-24: Decoding simple machine instructions Figure 8-25: Set/reset flip flop constructed from NAND gates Figure 8-26: Implementing a D flip-flop with NAND gates Figure 8-27: An 8-bit register implemented with eight D flip-flops Figure 8-28: A 4-bit shift register built from D flip-flops Figure 8-29: A 4-bit counter built from D flip-flops Figure 8-30: A simple 16-state sequencer Chapter 9: CPU Architecture Figure 9-1: Patch board programming Figure 9-2: Encoding instructions Figure 9-3: Encoding instructions with source and destination fields Figure 9-4: CPU design with a prefetch queue Figure 9-5: A pipelined implementation of instruction execution Figure 9-6: Instruction execution in a pipeline Figure 9-7: A pipeline stall Figure 9-8: A typical Harvard machine Figure 9-9: Using separate code and data caches Figure 9-10: A data hazard Figure 9-11: How a CISC CPU handles a data hazard Figure 9-12: A CPU that supports superscalar operation Chapter 10: Instruction Set Architecture Figure 10-1: Separating an opcode into several fields to ease decoding Figure 10-2: Encoding instructions using a variable-length opcode Figure 10-3: Basic Y86 instruction encoding Figure 10-4: Single-operand instruction encodings (iii = %000) Figure 10-5: Zero-operand instruction encodings (iii = %000 and rr = %00) Figure 10-6: Jump instruction encodings Figure 10-7: Encoding the add( cx, dx ); instruction Figure 10-8: Encoding the add( 5, ax ); instruction Figure 10-9: Encoding the add( [$2ff+bx], cx ); instruction Figure 10-10: Encoding the add ( [1000], ax ); instruction Figure 10-11: Encoding the add ( [bx], bx ); instruction Figure 10-12: Encoding the not(AX); instruction Figure 10-13: Using a prefix byte to extend the instruction set Figure 10-14: 80x86 instruction encoding Figure 10-15: 80x86 add opcode Figure 10-16: mod-reg-r/m byte Figure 10-17: The sib (scaled index byte) layout Figure 10-18: Encoding the add( al, cl ); instruction Figure 10-19: Encoding the add( eax, ecx ); instruction Figure 10-20: Encoding the add( disp, edx ); instruction Figure 10-21: Encoding the add( [ebx], edi ); instruction Figure 10-22: Encoding the add( [esi+disp8], eax ); instruction Figure 10-23: Encoding the add( [ebp+disp32], ebx ); instruction Figure 10-24: Encoding the add( [disp32+eax*1], ebp ); instruction Figure 10-25: Encoding the add( [ebx+edi*4], ecx ); instruction Figure 10-26: Encoding an add immediate instruction Chapter 11: Memory Architecture and Organization Figure 11-1: The memory hierarchy Figure 11-2: Possible organization of an 8-KB cache Figure 11-3: Selecting a cache line in a direct-mapped cache Figure 11-4: A two-way set associative cache Figure 11-5: Translating a virtual address to a physical address Figure 11-6: Typical Windows run-time memory organization Figure 11-7: Heap management using a list of free memory blocks Figure 11-8: Memory fragmentation Figure 11-9: Freeing a memory block Chapter 12: Input and Output (I/O) Figure 12-1: A typical output port Figure 12-2: An output port that supports read/write access Figure 12-3: An input port and output device that share the same address (a dual I/O port) Figure 12-4: Outputting data to a port by simply accessing that port Figure 12-5: Outputting data using the read/write control as the data to output Figure 12-6: Connection of the PCI and ISA buses in a typical PC Figure 12-7: The AGP bus interface Figure 12-8: Tracks and sectors on a hard disk platter Figure 12-9: Multiple platter hard disk assembly Figure 12-10: A hard disk cylinder Figure 12-11: Interleaving sectors Figure 12-12: Block list for small files Figure 12-13: Block list for medium-sized files Figure 12-14: Three-level block list for large files (up to 4 GB) List of Tables Chapter 2: Numeric Representation Table 2-1: Binary/Hexadecimal Conversion Chart Table 2-2: Binary/Octal Conversion Chart Table 2-3: Number of Values Representable with Bit Strings Table 2-4: Powers of Two Table 2-5: Sign Extension Examples Table 2-6: Zero Extension Examples Chapter 3: Binary Arithmetic and Bit Operations Table 3-1: AND truth table Table 3-2: OR truth table Table 3-3: XOR truth table Table 3-4: NOT truth table Chapter 4: Floating-Point Representation Table 4-1: Binary Representations for NaN Table 4-2: Operations Involving Infinity Table 4-3: Dealing with Operands That Have Different Signs Chapter 5: Character Representation Table 5-1: ASCII Character Groups Determined by Bits Five and Six Table 5-2: ASCII Codes for the Numeric Digits Table 5-3: Common Character Set Functions Table 5-4: The HyCode Character Set Chapter 6: Memory Organization and Access Table 6-1: 80x86 Addressing Capabilities Chapter 8: Boolean Logic and Digital Design Table 8-1: AND truth table Table 8-2: OR truth table Table 8-3: Truth Table Format for a Function of Three Variables Table 8-4: Common Names for Boolean Functions of Two Variables Table 8-5: Truth table for F = AB + C Table 8-6: Generating Minterms from Binary Numbers Table 8-7: Truth table for F53,504 Table 8-8: OR truth table for two variables Table 8-9: S/R Flip-Flop Output States Based on Current Inputs and Previous Outputs Chapter 10: Instruction Set Architecture Table 10-1: reg Field Encodings Table 10-2: mod Field Encodings Table 10-3: mod-r/m Encodings Table 10-4: Scale Values Table 10-5: Register Values for sib Encoding Table 10-6: Base Register Values for sib Encoding Table 10-7: The Scaled Indexed Addressing Modes ... For example, we will write the binary value 10 1 011 111 011 0 010 2 as 10 10 _11 11_ 1 011 _0 010 2 2.2.3.3 Binary Representation in Programming Languages This chapter has been using the subscript notation embraced by mathematicians to denote binary values (the lack of a subscript indicates... In general, the volumes in the Write Great Code series will discuss how to write software that achieves legendary status, eliciting the awe of other programmers 1. 1 The Write Great Code Series Write Great Code: Understanding the Machine is the first of four volumes... Congratulations on your progress thus far towards knowing how to write great code See you in volume 2 Chapter 1: What you Need to Know to Write Great Code Write Great Code will teach you how to write code you can be proud of, code that will impress other programmers, code that will satisfy