Maxfield - Bebop Bytes Back
Maxfield - Bebop Bytes Back
Publication
ii Bebop BYTES Back
ISBN 0-9651934-0-3
Library of Congress Catalog Card Number: 97-65591
The authors and publisher have used their best efforts in preparing this book, the CD-ROM accompanying
this book, and the programs and data contained therein. However, the authors and publisher make no
warranties of any kind, expressed or implied, with regard to the documentation or programs or data con-
tained in this book or CD-ROM, and specifically disclaim, without limitation, any implied warranties of
merchantability and fitness for a particular purpose with respect to the CD-ROM, the programs and/or data
contained therein, or the techniques described in the book. In no event shall the authors or publisher be
responsible or liable for any loss of profit or any other commercial damages, including but not limited to
special, incidental, consequential, or any other damages in connection with or arising out of furnishing,
performance, or use of this book or the contents of the CD-ROM.
Doone
Publication
7950 Hwy 72W, #G106,
P.O. Box 857, Madison, AL 35758, USA
Madison,
Tel/Fax: USA 205-837-0580
AL 35758,USA
or 1-800-311-3753
Email: [email protected]
Email: [email protected]
Bebop BYTES Back iii
Alvin Brown
iv Bebop BYTES Back
Foreword
This book is not for everyone.
It’s for people who want to make a dinosaur dance... to exactly the tune that
suits them at the moment. Who want to take the most complex, precision-built
machines available and make them do something bizarre, inexplicable, or totally
unexpected.
It’s for people who keep looking for what’s underneath the surface, who never
stop wondering “how does this work?” And when they find what’s underneath,
they want to know how that works.
It’s for young people, or more exactly, people who haven’t grown old. People who
don’t believe that they’ve already done everything remarkable they’re ever going
to do. It’s for people with a future.
If you’re the right person for this book, you need to know a few facts:
1. The more time you spend with this book and its accompanying CD-ROM, the
more you’ll get out of it. Skimming through it won’t take you where you want to
go. Paying serious attention, on the other hand, will teach you more about
computers than you can imagine. (You might also see a few beautiful sunrises.)
2. The labs work on two levels: on and under the surface. When you’re
performing the labs you’ll need to look for patterns that build up from individual
events.
3. When you’re done, you won’t look any different. You won’t get a trophy or a
certificate to hang on your wall. You’ll have some knowledge, and some skill, and
you’ll be ready to find more knowledge and develop more skill. Much of this will
be recognizable only to someone who has the same knowledge and skill.
This book will admit you to the company of people who have deep knowledge of
computer technology. It is only a beginning (there is no end), and there are
many possible directions for you to go.
And this book makes it fun. Written by a couple of wise-cracking English
computer engineers with overactive imaginations, it is rich in jokes, trivial
information, and overblown vocabulary (with a lexicon). Maxfield and Brown have
masterfully made the task of learning computer technology engaging.
Good luck, enjoy your voyage of discovery, and I expect to see some of you in
the near future.
Lee Felsenstein
Moderator, Homebrew Computer Club
Designer, Osborne-1 and Sol-20 computers, Pennywhistle -103 modem
Co-founder, Community Memory Project
Bebop BYTES Back v
About the Authors
Clive “Max” Maxfield and Alvin Montrose Brown are both eminent in the field of
electronics, although Alvin was already eminent when Max’s eminence was
merely imminent.
Max is 6’1" tall, outrageously handsome, English and proud of it. In addition to
being a hero, trend setter, and leader of fashion, he is widely regarded as an
expert in all aspects of electronics (at least by his mother). After receiving his
B.Sc. in Control Engineering in 1980 from Sheffield Polytechnic (now Sheffield
Hallam University), England, Max began his career as a designer of central
processing units for mainframe computers. To cut a long story short, he now
finds himself Member of the Technical Staff (MTS) at Intergraph Computer
Systems, Huntsville, Alabama, USA, where he gets to play with their high-
performance 3D graphics workstations. To occupy his spare time (Ha!), Max is a
contributing editor to Electronic Design News (EDN) magazine and a member of
the advisory board to the Computer History Association of California (CHAC). In
addition to numerous technical articles and papers appearing in magazines and
at conferences around the world, Max is also the author of the outstandingly
successful prequel to this tome, Bebop to the Boolean Boogie (An
Unconventional Guide to Electronics). On the off-chance that you’re still not
impressed, Max was once referred to as an “industry notable” and a
“semiconductor design expert” by someone famous who wasn’t prompted,
coerced, or remunerated in any way!
Alvin is a well-traveled English gentleman, being born in Assam India where his
father managed several tea plantations. After enjoying a lifestyle that most of us can
only dream of, Alvin returned to England at the age of 9 to suffer the rigors of the
English educational system. Upon leaving college, Alvin spent 10 years acquiring
an extensive knowledge of electronics computer-aided design whilst working for a
UK-based defense contractor. During this time Alvin devoted himself to developing
thick and thin film hybrid microelectronics circuits and writing computer aided
design software, and he subsequently held a number of managerial positions in
software development and consultative services groups. Alvin moved to the United
States in 1989, where he lives with his wife Susan and their two children, Barnaby
and Holly. Alvin is currently a Product Manager for Intergraph Computer Systems,
Huntsville, Alabama, USA, where he is responsible for the technical marketing of
high performance 3D graphics workstations specializing in Virtual Simulation
(VIZSIM) and Distributed Interactive Simulation (DIS). Alvin has also presented
papers at international conferences and has published a number of technical
articles.
Acknowledgments
When one has spent more than two years on a project such as Bebop BYTES Back,
slaving every evening and weekend over a hot computer keyboard, it’s easy to fall
into the trap of believing that you’ve done the whole thing single-handedly. But
when the authors brushed the cobwebs off their shoulders and reemerged from their
studies into the grim light of day, it quickly became apparent that many people had
contributed to this work.
In the case of the book itself, our thanks go to Preston Jett and John Huggins for
their sage advice on every subject under the sun, and to Andy “Crazy” Glew,
microprocessor architect at Intel Corporation, for his insights into arcane computer
lore. Thanks also go to Julie Feazel and Barbara Castlen for reviewing the early
versions of the text; to Kip Crosby, President of the Computer History Society of
California (CHAC) (http://www.chac.org/index.html), for his consultations on the
history of computers, his sterling work as “first reader,” and for his enthusiastic
support throughout the course of this project; and to Stephanie Maxfield for leaping
into the breach at the last minute to help with proof reading and the index. We
would also like to offer our appreciation to Doug Brewster for providing the
photographs in Chapter 1, to graphics artist extraordinaire Bob Sallee for furnishing
the “Dinosaur and Walnut” image in Chapter 1 and the outstanding caricatures of
famous people in Chapter 15, and to Victor Johnson for his help in digitally
processing all of the images. Thanks also to Chuck and Rita Paglicco for the “Best
Clam Chowder in the World” recipe that appears in Appendix J, and to Alvin’s wife
and Max’s mother for their contributions to the Lexicon in Appendix K.
Turning our attention to the Beboputer Computer Simulator, we offer grateful thanks
to Ed Smith for designing and implementing the core of the Beboputer’s assembler
utility, and to digital effects and graphics czars David Biedny and Nathan Moody
(IDIG, http://www.microweb.com/idig) for designing the toolbar icons. When
David first glimpsed at the prototype interface, he forced a rictus smile to his face
and said between clenched teeth: “It’s really very nice and I don’t want you to take
this the wrong way, but can I ask if you’re totally committed to this particular
color scheme......?” A few days later, a superb set of hand-crafted, color-
coordinated icons were winging their way to us across the Internet. Thanks also go
to David and Nathan for the superb graphics they created for the Beboputer’s Web
pages, and to Terri Gates for organizing and formatting the content of these pages.
We would also like to thank Alvin’s son, Barnaby, for entering the contents of the
Beboputer’s Character ROM, and to Kip Crosby and Chris Lott (professional bit-
manipulator at Phase IV Systems Inc., Huntsville, AL) for the tremendous efforts they
expended beta-testing the Beboputer.
Last but certainly not least, our grateful thanks to the “cast of thousands” who
provided us with the myriad video clips and animations featured in the multimedia:
IBM for their archive videos of 1950s and 1960s computers in lab 1 (and also the
photos of early computational devices and other memorabilia that appear in
Chapter 15 of the book); Thiokol Corporation, Huntsville, AL, for allowing us to
video the switch panel in lab 1; Vince Mazur for the video of his Altair computer in
lab 1; Bryan Wiersma for the “Theater” animation in lab 1, United Printed Circuits,
Huntsville, AL for allowing us to video their paper tape punch in lab 2; Patrick
Breithaupt for his Morse Code skills in lab 2; David Duberman (Motion Blur Media,
[email protected]) for the “Laser” animation in lab 3 (David is the author of “3D
Modeling Construction Kit,” Waite Group Press); Martin Foster (Animatrix,
[email protected]), for the “Captain Carnage” animation in lab 4 (Captain
Carnage is based on an original 2D character designed by Jeff Cook); Martin Foster
again for the live action/animation “Leopard” composite in lab 7 (film footage
courtesy of Ashok Amritraj from his film “Jungle Boy”); Tim Forcade (Forcade and
Associates, [email protected]) for the “Alien” animation in lab 5 (Tim
is the author of a number of books, his most recent being the “3D Studio® IPAS
Plug-in Reference,” New Riders); Linda Case for demonstrating her touch-typing
skills in lab 5; Dan Stiles for the “Flat Cow” animation in Lab 9; and Bill
Farnsworth, bon vivant and raconteur, who created the “Flying Logo” animation for
lab 6 and the “Talking Cow” animation for lab 9...... Phew! Also please note that
the “Band” animation in lab 8 was created by Corel Corporation, and all of the
videos and animations mentioned above (along with the original music by Lucas
Wilson) are copyrighted by their respective owners and may not be used without
express permission in writing from said owners.
As usual, we take full credit for everything that came out right, and any errors that
may have slithered in can only be attributed to cosmic rays and spurious events of
unknown origin.
viii Bebop BYTES Back
Contents
Chapter 0: What is a Beboputer? ................................................................................... 0-1
We commence our voyage into the unknown by briefly summarizing the way in
which computers became available to society at large, and then presenting the
concept of the Beboputer virtual computer. Along the way we also discover why
this chapter is numbered ‘0’.
Index
xii Bebop BYTES Back
Chapter
0
What is a Beboputer?
In this chapter we will discover:
Why the prospect of computers weighing only
1.5 tons used to be exciting
▼
How computers became available to society
at large
▼
What we mean by the term “Beboputer”
▼
How each Beboputer lab has a multimedia
introduction
▼
In what way the Beboputer is “Internet-ready”
▼
Why this chapter is numbered ‘0’
0-2 Bebop BYTES Back
Contents of Chapter 0
The relentless march of science ........................................................................................... 0-3
So, just what is a Beboputer? .................................................................................................... 0-6
We’re multimedia-equipped and Internet-ready ...................................... 0-7
Why is this chapter numbered ‘0’? ................................................................................. 0-7
All things considered, there’s a lot of it about ............................................... 0-8
Except where such interpretation is inconsistent with the
context... ....................................................................................................................................................... 0-8
What is a Beboputer? 0-3
The relentless march of science
“Computers of the future may weigh no more than 1.5 tons!” trumpeted an
article forecasting the relentless march of science in a 1949 edition of
Popular Mechanics magazine. To many of their readers this prediction
seemed wildly optimistic, considering that a typical computer of that era
might occupy 1,000 square feet of floor-space, weigh-in at approximately
30 tons, and require enough power to light a small town!
In those days of yore, computers were a mystery to the vast majority of
people – very few understood what a computer was and even fewer had
actually seen one “in the flesh.” By comparison, these days a tremendous
number of people have access to home computers and this number is
increasing all the time. We’re also used to working with computers in a
highly interactive fashion, and we’ve grown to expect, nay demand, systems
equipped with an abundance of features, such as large quantities of
memory, high-resolution graphics monitors, CD-ROM drives, fax modems,
sound cards, and so forth.
Many youngsters seem to assume that things have been this way since
ancient times, but nothing could be further from the truth. During the 1950s
and 1960s computers were physically huge (you’d be hard pushed to fit one
in a barn), extremely expensive (they typically cost millions of dollars), and
you could count the people who even considered computers for the home
on the fingers of one foot, but the times they were a’changin’. Following the
development of the transistor in 1947 and the integrated circuit in 1958, the
first microprocessor (or, in loose terms, computer-on-a-chip) made its debut
in 1971, and the world of computing began to undergo a dramatic
transformation.
The mid-1970s saw the arrival of the first home computers. By today’s
standards these were incredibly simple affairs – they had a very small
amount of usable memory (only enough to store between 256 and 1,024
characters of information), they didn’t have any way to remember programs
when they were turned off, and they didn’t have a keyboard or a computer
screen. In fact, the only way to program these early home computers was
by means of a bank of switches called a switch panel (Figure 0.1), and the
only way to see what they were doing was by means of flashing lights. Also,
these little rascals usually arrived as a kit of parts in a zip-lock bag and it
was up to their proud owners to assemble them.
0-4 Bebop BYTES Back
15 Address 0
Off
0 1 2 3
4 5 6 7
8 9 A B
C D E F
On = On/Off Rst = Reset
On Ad Da Clr Ad = Address Ent = Enter
Da = Data Stp = Step
Clr = Clear Run = Run
Rst Ent Stp Run
Figure 0.2: Hex keypad
(home computer circa late-1975)
1
Many of the early systems actually employed octal (base-8) keypads, but we aren’t going to
discuss octal in this book for reasons that will become apparent in the fullness of time.
What is a Beboputer? 0-5
In addition to the hex keypad, these rudimentary single-board systems
typically featured a simple microprocessor; a small amount of read-only
memory (ROM), which contained a “hard-wired” program to monitor the
keypad; a minuscule quantity of random access memory (RAM), which held
the user’s programs; and a development area for the user to add their own
bits and pieces of hardware.
Developments in home computing continued to be fast and furious, and by
1977 (only twenty years ago as we pen these words) a few lucky souls could
boast home systems sporting typewriter-style keyboards and antediluvian
monitors that could display a few lines of chunky-looking text (Figure 0.3).
Monitor with
"chunky" text
Video card
Keyboard
Main system
circuit board
While these systems may not seem outrageously impressive today, they
were considered to be revolutionary at the time. Of course, few people in
those days had any conception of how quickly computers would evolve and
how powerful they would become, to the extent that even a run-of-the-mill
home system of today would have caused a hardened expert of yesteryear to
squeal in delight.
However, although the majority of us now have access to home computers
and use them for a tremendous range of tasks, only a very small proportion
of users have more than a rudimentary comprehension as to how they
actually function. This is unfortunate, because computers can be a
tremendous amount of fun, and the fun increases the more one understands
what’s going on “under the hood.”
0-6 Bebop BYTES Back
2
Note that, to persuade your Beboputer to speak as described in Chapter 9, your main
computer system must boast a real sound card.
What is a Beboputer? 0-7
Title Bar
Beboputer Computer
File Setup Display Memory T ools H elp Menu Bar
Tool Bar
QWERTY Keyboard
Hex Keypad
7-Segment
Display
Status Bar
gabble gregariously and hold your own in any conversation. Yes, you too
will be able to wantonly wield words like: hardware, software, firmware,
vaporware, opcode, operand, bit, tayste, nybble, byte, playte, and ...... the
list goes on.
▼
What computers are and what they do
▼
The difference between analog and digital
computers
▼
That computers can be constructed using
electronic, mechanical, and even
pneumatic components
▼
Transistors and integrated circuits
▼
How Grandma passes the evenings
ensconced in her virtual reality system
1-2 Bebop BYTES Back
Contents of Chapter 1
Fearsome warriors or slaves to fashion? ................................................................... 1-3
What is a computer? ............................................................................................................................ 1-3
Transistors and integrated circuits ...................................................................................... 1-5
A typical home computer ............................................................................................................ 1-9
Quick quiz #1 ................................................................................................................................................. 1-11
What is a Computer? 1-3
Fearsome warriors or slaves to fashion?
Many of us are used to thinking of the Vikings as fearsome warriors (this
isn’t your mother’s computer book), who descended from the northlands
and rampaged and pillaged across Europe. These fearless warriors are
popularly believed to have laughed at danger and scoffed at the elements,
so the recent archaeological discovery that many Vikings wore red woolly
socks is, to many of us, somewhat disconcerting. However, we digress ......
What is a computer?
In the 1800s, mathematical tables such as logarithmic and trigonometric
functions were generated by teams of mathematicians working day and
night on primitive mechanical calculators. Due to the fact that these people
performed computations they were referred to as computers. But over the
course of time, the term computer became associated with machines that
could perform the computations automatically.
In its broadest sense, a computer is a device that can accept information
from the outside world, process that information, make decisions based on
the results of it’s processing, and then return the information to the outside
world in its new form (Figure 1.1).
ts
utpu
O
r
esso
c
Pro
ry
ts emo
In pu M
Ultimately, the development of the integrated circuit paved the way for the
proliferation of personal computers. Today, systems small enough to fit in
the palm of your hand can be created with far more processing power than
monsters weighing tens of tons only a decade or two ago.
2
Silicon accounts for approximately 28% of the earth’s crust.
What is a Computer? 1-9
A typical home computer system
If we were to take a stroll through the mists of time, sometime after the
Jurassic period when dinosaurs ruled the earth (say around the middle of the
1970s), we would find relatively few people with access to any form of
computer system. But time flies when you’re having fun, and an
increasingly large proportion of us now have access to a computer in our
own homes. Although details vary, a typical home system may resemble the
one shown in Figure 1.6.
Monitor
Mouse
Processor
Memory
Hard disk
CD-ROM
Floppy disk
Keyboard
3
What he actually said in a tribute to the Royal Air Force at the House of Commons (August 20th,
1940) was: “Never in the field of human conflict was so much owed by so many to so few.”
What is a Computer? 1-11
Quick quiz #1
1) What color socks did some Vikings wear?
2) When did the term computer originate?
3) What is a general definition of a computer?
4) What technologies can be used to implement computers?
5) What is the difference between analog and digital information?
6) Which devices acted as switches in the first true electronic
computers?
7) What is a semiconductor and what is the most commonly used
semiconductor?
8) When was the first point-contact transistor constructed?
9) In what way can a transistor be considered to act like a switch?
10) What is an integrated circuit?
For those who are interested and want to know more about
transistors, integrated circuits, and the best time of day to eat
smoked fish; these topics are presented in greater detail in this
book’s companion volume: Bebop to the Boolean Boogie (An
Unconventional Guide to Electronics), ISBN 1-878707-22-1.
1-12 Bebop BYTES Back
Chapter
2
Roaming Around
a Computer
In this chapter we will discover:
The central processing unit (CPU), and why it’s considered
to be the “brain” of the computer
▼
The control, data, and address busses
▼
The binary, quinary, and hexadecimal numbering systems
▼
How to convert from hexadecimal to binary (and back
again) while retaining a sense of humor
▼
How binary numbers can be used to represent different
types of data
▼
How the computer stores information in its RAM and ROM
memory devices
▼
How the computer communicates with the outside world
using its input and output ports
2-2 Bebop BYTES Back
Contents of Chapter 2
There’s nothing to fear but fear itself .......................................................................... 2-3
The central processing unit (CPU) ................................................................................... 2-4
The data bus .................................................................................................................................................... 2-7
An overview of number systems ........................................................................................ 2-9
The binary number system ...................................................................................................... 2-11
The hexadecimal number system ................................................................................ 2-14
The Beboputer’s calculator utility .................................................................................. 2-16
Different types of data ................................................................................................................. 2-18
The address bus ....................................................................................................................................... 2-22
The control bus ......................................................................................................................................... 2-24
The memory (RAM and ROM) ........................................................................................... 2-28
Memory sizes ........................................................................................................................................... 2-30
Memory address decoding ............................................................................................. 2-30
The memory map ........................................................................................................................... 2-32
The input and output ports .................................................................................................... 2-33
Input ports ..................................................................................................................................................... 2-34
Output ports ............................................................................................................................................ 2-35
Input/output (bidirectional) ports ........................................................................... 2-36
Quick quiz #2 ............................................................................................................................................... 2-37
Roaming Around a Computer 2-3
There’s nothing to fear but fear itself
So here we are, seconds away from plunging into the bowels of a computer
system, our knees aquiver and butterflies in our stomachs. Indeed, some of
the more faint-hearted amongst us may feel the same dread as occurs when
we find ourselves with one foot dangling over the edge of a bottomless pit
and the other balanced on a bar of wet soap. But fear not my braves,
because there’s nothing to fear but fear itself. As my dear old dad used to
tell me when I was but knee-high to a grasshopper: “A coward dies a
thousand deaths – a brave man only once.” So let’s bite the bullet and take
a quick peek through our splayed fingers at Figure 2.1.
M Control Bus
RA Address Bus
Data Bus
M
RO
t
Ou ort
P
In rt To the
Po Outside World
U
CP
From the
Outside World
Clock and ~Reset
2
It is not uncommon for an engineer to select one component in preference to another based
purely on its color or aesthetically pleasing shape!
3
Some computers employ more complex clocking schemes involving multiple clock signals.
Roaming Around a Computer 2-5
Now turn your attention to the CPU’s ~reset input. When power is first
applied to the CPU causing it to “wake up,” it feels somewhat disorientated
to say the least. To gain some
idea of the CPU’s predicament, The reason the ~reset signal has a tilde
imagine being awakened by a character (“~”) (pronounced “till-da”) as part
bucket of freezing cold water of its name is to indicate that this signal’s
hurled in your face, only to find a active state is a logic 0. Similarly, the small
high-school marching band circle (called a bobble or bubble) on the side
parading around your bedroom of the CPU symbol is also used to indicate
(I hate it when that happens). that this signal’s active state is a logic 0. The
To cut a long story short, the poor reasons for using logic 0 as the active state
are many, varied, long, and tortuous, so you
little rascal hasn’t got a clue
may be grateful that we won’t go into them
where it is or what it is supposed here. However, throughout the course of our
to do. This is where the ~reset discussions you will see that many control
signal comes into play, because, signals share this characteristic.
much like someone sneaking up
and bursting a paper bag just
behind your head, it causes the
CPU to pause for a moment of quiet reflection and to look at things from a
new perspective.
Before we proceed, we first need to recall our discussions in Chapter 1, in
which it was noted that digital computers are constructed from transistor
switches. These switches can be in one of two states, OFF or ON, which
physically correspond to two different voltage levels. However, we
generally have only minimal interest in the actual voltages used and, for
reasons that will become apparent, the terms OFF and ON are not
particularly useful or relevant in this context. So rather than thinking in
terms of voltage levels or in terms of OFF and ON, we generally use more
abstract logical terms called logic 0 and logic 1.
Now, consider the circuit connected to the ~reset input in Figure 2.2. The
switch S is usually open, which means that the ~reset input is connected to
a logic 1 value through the resistor R. When the switch is closed it connects
the ~reset input directly to a logic 0, which causes the CPU to be initialized
into a well-known state. The switch is of a type that springs open when
released, thereby returning ~reset to a logic 1 value and permitting the CPU
to start to do its cunning stuff.
Sometimes even the best computer becomes hopelessly lost and confused,
usually due to programming errors caused by its human operators. In this
case the system may be reinitialized by means of the ~reset signal as
2-6 Bebop BYTES Back
From ROM
= Connection
To Out Port
The more abstract view of the data bus shows arrows indicating the
directions in which signals can travel between the various components
attached to the bus. The less abstract view omits these arrows to emphasize
the fact that the bus is physically composed of simple wires. In reality, the
only thing that determines which way signals are traveling on the bus at any
particular time is the CPU. By means of its control and address busses, the
CPU can dictate which component is currently permitted to “talk” (drive
signals onto the data bus) and which component is allowed to “listen”
(receive those signals from the data bus). Due to the fact that signals can
travel in either direction on the data bus, this bus is said to be bidirectional.
Remembering that we are considering conventional digital systems,(4) each
wire may be used to represent one of two values: logic 0 or logic 1. The
actual values being driven onto the wires may change from one moment to
the next, but, at any particular moment in time, a group of n wires can carry
2n different combinations of these values (where 2n means “two to the
power of n” or “two multiplied by itself n times”). For example, two wires
can be used to represent 22 = (2 x 2) = 4 different patterns of 0s and 1s
(00, 01, 10, and 11). Similarly, the eight wires in our data bus can carry
4
Some evaluations have been performed on tertiary logic; that is, logic gates based on three
distinct voltage levels. Tertiary logic opens up all manner of mind-boggling possibilities
which, you can thank your lucky stars, we aren’t going to consider here.
2-8 Bebop BYTES Back
Thousands column
Hundreds column
Tens column
Ones column
Quinary
3 2 3 45 = (3 x 53) + (2 x 52) + (3 x 51) + (4 x 50)
number
= (3 x 125) + (2 x 25) + (3 x 5) + (4 x 1)
= 375 + 50 + 15 + 4
Decimal
= 44410
equivalent
When we use systems with bases other than ten, subscripts are used to
indicate the relevant base; e.g. 32345 = 44410 (3234QUINARY = 444DECIMAL).
By convention, any value without a subscript is assumed to be in decimal.
Roaming Around a Computer 2-11
Counting in quinary commences at 0 and
progresses up to 45, at which point all of the
available digits have been used. Thus, the next Quinary Decimal
Eights column
Fours column
Twos column
Ones column
1 1 1 02 Binary
number = (1 x 23) + (1 x 22) + (1 x 21) + (0 x 20)
= (1 x 8) + (1 x 4) + (1 x 2) + (0 x 1)
= 8 + 4 + 2 + 0
Decimal
= 1410
equivalent
Wires
0 0 0 0 0 0 0 02 = 0 10
0 0 0 0 0 0 0 12 = 1 10
0 0 0 0 0 0 1 02 = 2 10
0 0 0 0 0 0 1 12 = 3 10
0 0 0 0 1 0 0 02 = 4 10
: : :
111 111 0 12 = 253 10
111 111 1 02 = 254 10 Figure 2.9: Using the data bus to
111 111 1 12 = 255 10 represent binary numbers
Hexadecimal 0 1 2 3 4 5 6 7 8 9 A B C D E F
Decimal 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Binary and hexadecimal numbers are often prefixed by leading zeros to pad
them to some desired width, thereby providing an indication of the physical
number of bits used to represent these
values within the computer. Thus, the Decimal Binary Hexadecimal
binary values in Figure 2.11 are 0 0000 0000 00
1 0000 0001 01
padded to make them 8-bits wide, 2 0000 0010 02
because this is the width we’ve 3 0000 0011 03
4 0000 0100 04
chosen for our data bus. Similarly, 5 0000 0101 05
any single-digit hexadecimal values 6 0000 0110 06
7 0000 0111 07
are padded with a leading zero, 8 0000 1000 08
where each hexadecimal digit is 9 0000 1001 09
10 0000 1010 0A
equivalent to four binary digits. 11 0000 1011 0B
12 0000 1100 0C
The binary values shown in 13 0000 1101 0D
14 0000 1110 0E
Figure 2.11 have spaces dividing 15 0000 1111 0F
them into two 4-bit sections. This 16 0001 0000 10
17 0001 0001 11
representation is not outrageously 18 0001 0010 12
common, but is used here to : : :
etc. etc. etc.
emphasize the fact that each
hexadecimal digit is equivalent to
Figure 2.11: Counting in hexadecimal
four binary digits. For example,
016 = 00002, 916 = 10012, C16 = 11002, and F16 = 11112. This means that it is
very easy to convert hexadecimal numbers into their binary equivalents, and
vice versa (Figure 2.12).
C4F516 11000100111101012
11000100111101012 C4F516
Just to make life a little more interesting, these examples involve the
conversion of a 16-bit number from hexadecimal to binary and back again.
In the case of the hexadecimal to binary conversion, each hexadecimal digit
is mapped into its corresponding 4-bit binary equivalent, and these 4-bit
groups are then stuck back together to form the full 16-bit value. By
comparison, in the case of the
In the original digital computers, bus widths binary to hexadecimal
were often multiples of three bits (such as 9-bits, conversion, the 16-bit binary
12-bits, 18-bits, 24-bits, and so on). As an octal value is split up into groups of
digit can be directly mapped onto three bits, four bits, each of these groups
these bus values were easily represented in is mapped to its corresponding
octal, which provides one of the reasons behind hexadecimal digit, and these
the original popularity of the octal system.
digits are then glued together to
More recently, computers have tended to give the result.
standardize on bus widths that are multiples of
eight bits (such as 8-bits, 16-bits, 32-bits, and so Performing simple arithmetic
on). Although octal can be used to represent the operations in binary or
values on these busses, hexadecimal is more hexadecimal is fairly
commonly used because each hexadecimal digit straightforward once you’ve got
can be directly mapped onto four bits. This is the hang of it, but it can be a
one of the main reasons for the decline in bit of a stumbling block for
popularity of the octal system and the beginners. Rather than confuse
corresponding ascendancy of hexadecimal. the issue by delving into this
topic now, we’ve relegated it to
Chapter 7 and Appendix G.
6
All of the instructions in this book are based on the Microsoft Windows® 95 operating
system (the only system for which the Beboputer is certified).
7
Microsoft and Windows are registered trademarks of Microsoft Corporation.
Roaming Around a Computer 2-17
d) Click on the Calculator item in the resulting pop-up menu. This will
invoke your Beboputer’s calculator utility as shown in Figure 2.13.
61642
0 1 2 3 C AND ROL
4 5 6 7 CE OR ROR
8 9 A B + - XOR SHL
C D E F = NOT SHR
The ROL and ROR calculator buttons work slightly differently to the Beboputer’s ROLC and
8
RORC instructions, which are discussed in detail in Chapter 8 (you can find out more about
the calculator in the online help provided with the Beboputer).
2-18 Bebop BYTES Back
Bit 7 Bit 0
Bit 7 ON Bit 0
0 1 0 0 0 0 0 1 Add
0 1 0 0 0 0 1 0 Subtract 0 0 1 0 0 1 0 0
0 1 0 0 0 0 1 1 Rotate OFF
: :
etc. etc.
(a) Instructions (b) Bit Patterns
0 1 0 0 0 0 0 1 = 65 10 0 1 0 0 0 0 0 1 = 'A'
0 1 0 0 0 0 1 0 = 66 10 0 1 0 0 0 0 1 0 = 'B'
0 1 0 0 0 0 1 1 = 67 10 0 1 0 0 0 0 1 1 = 'C'
: :
etc. etc.
(c) Positive binary numbers (d) ASCII Characters
The first thing the CPU does after it’s been reset is to request an instruction,
which duly appears on the data bus (this process is discussed in more detail
a little later on). Figure 2.14a illustrates some possible instructions.(10) In
addition to directing the computer as to what to do, an instruction also tells
the CPU whether the next byte of information to appear on the data bus will
be another instruction, or whether it will be an additional piece of data
associated with this first instruction (once again, we will examine this
process in more detail a little later on).
The data associated with an instruction can come in many forms. As we
have already discussed, the patterns of 0s and 1s on the data bus may be
used to represent positive binary numbers as illustrated in Figure 2.14c.
Similar patterns may also be used to represent negative binary numbers, but
that topic has been largely relegated to Chapter 7.
Alternatively, the patterns of 0s and 1s may be a simple bit pattern as shown
in Figure 2.14b. For example, the CPU may direct this pattern to one of the
output ports where it could be used to drive something in the outside world,
10
Note that the particular patterns of 0s and 1s shown here were selected only for the purpose
of illustrating certain points in these examples.
Roaming Around a Computer 2-21
such as a set of light bulbs in your house. In this case each bit in the pattern
might be used to control an individual light – a logic 1 on that bit might
turn a particular light ON, while a logic 0 could turn that light OFF. Of
course, we need to keep in mind the fact that the values logic 0 and logic 1
have little relevance in the outside world. Thus, we might decide that a
logic 0 should turn the light ON and a logic 1 should turn it OFF. The actual
actions caused by the logical values depend on whatever is present on the
other side of the output port (the use of output ports will also be covered in
more detail later on).
Last but not least (and remembering that only a few possibilities have been
discussed here), we might use different patterns of 0s and 1s to represent
characters such as ‘A’, ‘B’, ‘C’, and so on (Figure 2.14d). However, unlike
the mapping of positive binary numbers which is pretty intuitive, the
mapping of characters is somewhat less obvious. The problem is that
everybody has to agree that we’re all talking about the same thing. For
example, your program may direct the CPU to send the binary pattern
010000012 to an output port that’s connected to a printer. In this case,
assuming that you are assuming that this pattern represents a letter ‘A’, it
would be sort of useful if the printer assumed the same thing.
Now, your first reaction might be why don’t we make 000000002
correspond to an ‘A’ and proceed from there, but someone else might
suggest that we start with a lowercase ‘a’. Yet another person might wish to
commence with the
characters that represent Another code that deserves some mention is the
decimal digits (‘0’, ‘1’, ‘2’, Extended Binary Coded Decimal Interchange Code
‘3’, and so on), and then (EBCDIC) from IBM, primarily because of the pain
there are such things as and suffering it has caused the authors over the
punctuation characters years.
and special characters like The beauty of ASCII is that all of the alpha
‘!’, ‘@’, ‘#’, and ‘$’. characters are numbered sequentially; that is, 65 =
‘A’, 66 = ‘B’, 67 = ‘C’, and so on until the end of the
What we need is a alphabet. Similarly, 97 = ‘a’, 98 = ‘b’, 99 = ‘c’,
standard, but there is an and so forth. This means that you can perform
engineering joke that programming tricks like saying (‘A’ + 23), and
goes: “Standards are great, having a reasonable expectation of ending up with
everyone should have the letter ‘X’. To cut a long story short, if you were
one” – the problem being thinking of doing this with EBCDIC ...... don’t.
that almost everyone does! Fortunately, the chances of your running into
However, there is one EBCDIC are relatively slight (and they’re close to
code that is very widely zero in this book).
2-22 Bebop BYTES Back
0 1 2 3 4 5 6 7
etc.
By some strange quirk of fate, this brings us to the reason why we used ‘$’
characters to indicate hexadecimal values in Figure 2.16. Although it is
relatively easy for humans to recognize subscripted numbers such as BC8316
and to decide what they mean, computers aren’t tremendously good at this
sort of thing. For example, consider a program used to read text into a
computer. Unless it was particularly clever, such a program could easily
become confused and “see” the subscripts as being normal numbers. To
circumvent this problem a variety of conventions may be used to indicate
numbers in bases other than decimal. In our case, we decided to prefix
hexadecimal numbers with ‘$’ characters (e.g. $BC83) and binary numbers
with ‘%’ characters (e.g. %1011110010000011).
clock
reset
logic 1
~reset
logic 0
read #1 read #2
~read X
write #1
~write X
time
t0 t1 t2 t3 t4 t5 t6 t7 t8 t 9 t 10 t 11 t12 t13
Resetting the Computer: When power is first applied to the system at time
t0 everything is in disarray. The CPU feels as though it has been hit over the
head with a mallet and it doesn’t know whether it’s coming or going. This is
indicated by the X values shown on the ~read and ~write signals and on the
address and data busses, where X’s are used to indicate unknown values.
In a real system a special power-on reset circuit would be used to ensure
that the ~reset signal was active from the word go, but we’ve ignored this to
better illustrate the effect of this signal. In our example the ~reset input is
placed in its active state (which is a logic 0) at time t1. This causes the CPU
to place its ~read and ~write signals in their inactive states (which are
logic 1s). It also causes the CPU to direct the address bus to point to
memory location $0000 (remember that we’re using ‘$’ characters to
indicate hexadecimal values), and for all of the wires on the data bus to be
driven to a special high-impedance state which is usually denoted by Z
characters.(12) In fact, although the expression “driven to high impedance” is
commonly used, it is somewhat inappropriate in this case, because all of
the devices connected to the data bus have actually been disconnected from
the bus by electronic means, and are therefore not driving anything. Finally,
at time t2, the ~reset signal is placed in its inactive state which allows the
CPU to start to do its funky stuff.
Read Cycle #1: Following a reset, the first thing that the CPU does is to read
an instruction from the memory location at address $0000. To do this the
CPU places the ~read signal in its active state (which is a logic 0) at time t3.
When the memory location at address $0000 “sees” the active ~read signal,
it responds by placing a copy of its contents onto the data bus. We’ve
shown these contents as being $?? to indicate that we don’t really care what
they are at this stage in our discussions; suffice it to say that they represent
an instruction for the CPU to act upon. Once the CPU has acquired the
instruction and stored a copy internally, it places the ~read signal in its
inactive state (which is a logic 1) at time t4. When the memory location at
address $0000 realizes that the ~read signal has gone inactive, it
disconnects itself from the data bus and returns to driving Z values onto the
bus.
Read Cycle #2: Depending on the instruction it received during the
previous read cycle, the CPU may now perform either a read cycle or a
write cycle – in our case we’re assuming another read cycle. Additionally,
the previous instruction may have informed the CPU that the next read cycle
will return data associated with that instruction, or it may have informed the
12
The concept of high impedance is discussed in more detail in Chapter 6.
Roaming Around a Computer 2-27
CPU to expect a completely new instruction. In our case we’re assuming
that the CPU expects a new instruction which will, by default, be in the
next memory location.
At time t5 the CPU increments the address bus to point to memory location
$0001. At time t6 the CPU places the ~read signal in its active state. When
the memory location at address $0001 “sees” that the ~read signal is active,
it places a copy of its contents onto the data bus. Once again we’ve shown
these contents as being $?? to indicate that we don’t really care what they
are at this stage in our discussions. At time t7 the CPU places the ~read
signal in its inactive state. When the memory realizes that the ~read signal
has gone inactive, it disconnects itself from the data bus and returns to
driving Z values onto the bus.
Write Cycle #1: For the purposes of this discussion, we will assume that the
instruction from the second read cycle told the CPU to write some data to
memory location $DFFF. Thus, at time t8 the CPU changes the address bus
to point to memory location $DFFF. At time t9 the CPU places the
information it wishes to write onto the data bus (as usual we’ve used $?? to
indicate that we don’t really care what this value is at the moment). At time
t10 the CPU places the ~write signal in its active state. When the memory
location at address $DFFF “sees” that the ~write signal is active, it takes a
copy of the value on the data bus and squirrels it away. After waiting a few
moments to give the memory time to store the data, the CPU places the
~write signal in its inactive state at time t11. At time t12 the CPU stops driving
its data onto the data bus and returns to driving Z values onto the bus.
Finally, at time t13 the CPU places a new value onto the address bus and is
ready to commence a new cycle.
If the preceding discussion made perfect sense to you, then you can award
yourself ten points and a prize coconut. But don’t worry if it seemed
confusing on this first pass, because you really don’t need to know a
computer system at this level of detail to be able to use it.(13) As we proceed
you’ll find yourself returning to this timing diagram of your own accord, and
each time you return it will make more and more sense. Trust us on this –
have we ever lied to you before?
13
This is fortunate, as otherwise computers would not be nearly so popular as they are.
2-28 Bebop BYTES Back
14
See also the discussion on PROMs, EPROMs, and EEPROMs in Chapter 13.
Roaming Around a Computer 2-29
Remember that this is simply a pictorial representation that provides us with
a way in which to view the world. In reality, each cell in the array is
composed of a number of transistors, and all of the cells (and the
connections between them) are fabricated on a single integrated circuit.
The width w of the memory is the number of bits used to form a word, where
the bits are usually numbered from 0 to (w - 1); thus, an 8-bit wide word has
bits numbered from 0 to 7. Similarly, the depth d of a memory is the number
of words used to form the array, where the words are usually numbered from
0 to (d - 1); thus, the 16-word array illustrated in Figure 2.19 would have its
words numbered from 0 to 15.
In addition to the memory array itself, some decoding and control logic is
also needed to make the device function as required, but we will gloss over
this for the nonce. Although their internal construction is quite different,
packaged ROM and RAM devices are very similar in external appearance
(Figure 2.20).
s s
~c rd ~c rd
~
M ~ wr M
RO RA
~
t t
lec lec
_se read _se read e
hip ~ hip ~ rit
~c ~c ~w
] da da
0 ta[ 0 ] ta[
11: 7:0 11: 7:0
dr[ ] dr[ ]
ad ad
Both ROMs and RAMs have a ~cs input, which is used to inform them when
their services are required (where “cs” is a common abbreviation for “chip
select”). Similarly, both devices have a ~rd input, which informs them when
the CPU wishes to perform a read operation. However, only the RAM has a
~wr input, which is used to inform it when the CPU wishes to perform a
write operation. The ROM does not have this input because, as was
previously noted, you can’t write new data into a read-only memory. As the
tilde characters in their names and the bobbles on the diagrams would
suggest, the active states for the ~cs, ~rd, and ~wr inputs are logic 0s, which
is usually the case for control signals.
For the CPU to reference a particular word in one of these memory devices,
it must specify that word’s address by placing appropriate values onto the
device’s address bus (this address is subsequently decoded inside the device
to select the appropriate word). Remembering that our CPU’s address bus
has 16-bits called addr[15:0], you may have wondered why the individual
2-30 Bebop BYTES Back
devices are shown as only having 12-bit address inputs called addr[11:0].
The reason is fairly simple – it’s possible to obtain memory devices with a
bewildering variety of widths and depths, and it’s certainly possible to get a
device with 65,536 words (each 8-bits wide) which would require a 16-bit
address. But this would mean that we could only use a single memory
device in our system – either a ROM or a RAM. Obviously this is not
acceptable, because a general-purpose computer requires both types of
memory, so we have to use smaller chunks of memory and connect them
together in such a way that we can achieve our heart’s desire.
Memory sizes
Without diverting too far from the path we’re trying to tread, we now need
to consider an interesting quirk associated with ROMs and RAMs. Memory
devices are constrained to have a depth of 2n words, where ‘n’ is the
number of bits used to form their address. For example, a device with a
10-bit address could internally decode that address to select one of 1,024
words (210 = 1,024). Now the suffix K (Kilo) is generally taken to represent
one thousand; but, as we have seen, the closest power of two to one
thousand is 210, which equals 1,024. Therefore a 1 K-word memory actually
refers to a device containing 1,024 words.
For reasons of our own, we made the decision to use devices with 12-bit
addresses as illustrated in the previous figure . These 12-bit address can be
internally decoded to select one of 4,096 words (212 = 4,096).
Remembering that our words are 8-bits wide, and that 8-bits is usually
referred to as a byte, this means that we are using 4 K-byte ROMs and RAMs
in our system. Even to the untrained eye (or ear), it is obviously easier to say
“We’re using four kilo-byte memory devices,” than it would be to say
“We’re using memory devices with four-thousand and ninety-six words.”
addr[15:12] ~cs[15:0]
5]
s[1
~c 0000 1111111111111110
0001 1111111111111101
0010 1111111111111011
0011 1111111111110111
] 0100 1111111111101111
6 s[0
~c
4:1 der 0101 1111111111011111
co 0110 1111111110111111
De 0111 1111111101111111
1000 1111111011111111
] 1001 1111110111111111
:12
15 1010 1111101111111111
dr[
ad 1011 1111011111111111
1100 1110111111111111
1101 1101111111111111
Figure 2.21: 4-to-16 Decoder 1110 1011111111111111
1111 0111111111111111
This diagram and its associated truth table make the operation of the
decoder clear for all to see. The device has four inputs connected to
addr[15:12], and sixteen outputs connected to a set of wires called ~cs[15:0].
As you should know by now, the tilde characters in their names and the
bobbles on the diagram indicate that the active states of the outputs are
logic 0s. Each pattern of 0s and 1s on the inputs causes an individual output
to be driven to its active state, and each of these outputs is used to select a
particular memory device. The only thing that remains to be done is to
connect the decoder to sixteen of our 4 K-byte memory devices in such a
way that the CPU is fooled into thinking that it is looking at a single
64 K-byte chunk of memory (4 K-bytes x 16 = 64 K-bytes) (Figure 2.22).
In this configuration the four most-significant bits of the address bus,
addr[15:12], are fed into our 4:16 decoder. Each of the outputs from the
decoder can be used to select a 4 K-byte ROM or RAM device.(15) The
remaining address bits, addr[11:0], which are connected to all of the
memory devices, are used to point to a particular word within whichever
device is selected by the decoder. Due to lack of space, this diagram only
shows three ROM devices – the remaining ROMs and RAMs would be
connected in a similar fashion. Note that the ~read signal from the CPU is
connected to all of the ROMs and RAMs, but the ~write signal would only
be connected to the RAMs.
15
As you will soon discover, we’re actually only going to use the ~cs[14:0] outputs to select
memory devices, because we’re saving ~cs[15] to select the input and output ports.
2-32 Bebop BYTES Back
5]
s[1
~c ]
~c
s[3 ing
rite m ain AM
~w ead
~r
re nd R
To a s
M ice
~c
s[2
]
RO dev
s
~c d R 0 ]
~r OM 11:
] dr[
s[1 ad
~c ]
7:0
s
~c d ta[
da
~ r RO
] M
s[0
4 ~c
De : 1 6 s
co ~c d
de ~ r ROM
r
ad
rite dr[
]
~w read 15
:12 :0
~ 11
] dr[
ad
U
CP
From 15
:0]
dr[
ad ]
7:0 Figure 2.22: Memory address decoding
ta[
da
The main point to note is that the CPU doesn’t know anything about the
tricks we’re playing here. As far as it’s concerned the address bus appears to
be pointing to 64 K-bytes of contiguous memory, which means that the CPU
is as happy as a clam.
ROM
starting at address $0000. $2000
Following the ROM there are
$3000
44 K-bytes of RAM (in eleven
4 K-byte chunks) starting at $4000
address $4000.
$5000
Note that the last 4 K-byte chunk
of the map doesn’t actually $6000
final topic isn’t so much of a hurdle as a leisurely stroll that will lead us
onwards and upwards into Chapter 3 – a chapter which promises to be
absolutely jam-packed with oodles of scrumptious fun.
Imagine that you lock yourself in a room, disconnect your telephone, radio,
television, and music center, draw the drapes (or curtains if you’re in
England), turn off the lights, sit in a chair, and don’t move a muscle. While
you’re hanging around you could think the most amazingly interesting
thoughts that anyone ever “thunked,” but what’s the point if you can’t
communicate your ideas to someone else?(16) Similarly, it would be possible
to create a computer that performed cunningly complicated calculations and
was isolated from the outside world. But if there’s no way to see the results
or tell the little rascal to do something else, then once again, what’s the
point?(17) Thus, for a computer to be of any use it has to be able to
communicate with the outside world, and for this purpose it employs input
and output ports.
Input ports
As their name suggests, the computer uses its input ports to allow the CPU
to receive information from the outside world. As we noted above, there are
a variety of ways in which systems can be configured, but one of the most
common, known as memory-mapped I/O, is to fool the CPU into believing
that the ports are actually standard memory locations. First consider an
input port (Figure 2.24).
da
ta[
0] tri-state buffer
da
ta[
7:0
] da
ta[
6]
da in_
ta[ da
7] ta[
0]
in_ in_
le da da
ab ta[
n 7:0 ta[ rld
~i_e ] in_ 6]
e wo
da
ta[ tsid
ble 7] ou
ena the
~i_ m
Fro
Figure 2.24: An input port
16
Of course the French philosopher Jean Paul Sartre might have a few words to say on this
subject – he usually did. If you really want to learn how to suffer, you could do worse than
reading his book, “Being and Nothingness.”
17
Yes, Jean Paul, we know, we know.
Roaming Around a Computer 2-35
Note that this input port is 8-bits wide, which is the same width as our data
bus. The inputs to the port, in_data[7:0], would be driven by something in
the outside world; for example, switches connected in such a way that they
can represent either logic 0 or logic 1 values. The outputs from the port,
data[7:0], are connected to the CPU’s data bus.
Inside the port are logic gates known as tri-state buffers, one for each of the
data signals.(18) These buffers act in such a way that when their control
inputs are in their active state (which is a logic 0) they pass whatever is on
their data inputs through to their data outputs. However, when their control
inputs are in their inactive state, the buffers effectively disconnect
themselves from the data bus and start to drive high-impedance Z values
(similar buffers are used inside memory devices to disconnect them from the
data bus when they are not selected).
When the CPU wishes to read the values being presented to the port’s
inputs, it must somehow cause a logic 0 to be applied to that port’s
~i_enable signal. In the case of the Beboputer, we have decided that there
are going to be thirty-two such input ports located at addresses $F000 to
$F01F. In order for the CPU to individually address these ports, it must be
able to generate a unique ~i_enable signal for each port.
There are numerous ways of generating such signals and, to be honest, we
really aren’t all that bothered with this here. However, in order to tickle
your curiosity we’ll give you a few clues. First of all we can use the most
significant output from the 4:16 decoder, ~cs[15], to indicate that we wish
to address a location greater than or equal to $F000. We can then combine
this signal with some additional logic that decodes the least significant
address bus bits to give us thirty-two signals corresponding to addresses
$F000 to $F01F. Finally, we could combine each of these signals with the
CPU’s ~read signal to give us thirty-two unique control signals, one for each
input port.
Output ports
In the case of an output port, the inputs to the port, data[7:0], are connected
to the CPU’s data bus, while the port’s outputs, out_data[7:0], would be
used to drive something in the outside world; for example, lights connected
in such a way that a logic 0 value would turn them OFF and a logic 1 value
would turn them ON (or vice versa) (Figure 2.25).
18
Tri-state buffers are discussed in more detail in Chapter 6.
2-36 Bebop BYTES Back
da
ta[
0] latch
da
ta[
7:0
] da
ta[
6]
da ou
ta[ t_d
7] ata
[0]
ou
t_d ou
ble ata t_d
na [7: ata
_e 0] [6] orl
d
~o ble ou
ena t_d
idew
ata
~o_ [7] outs
the
To
Figure 2.25: An output port
Inside the port are logic functions known as latches,(19,20) one for each of the
data signals. These latches act in such a way that, when their control inputs
are in their active state (which is a logic 0), they pass whatever is on their
data inputs through to their data outputs. However, when their control
inputs are in their inactive state the latches remember the last values they
were driving and continue to drive them to the outside world (similar
functions may be used inside RAM devices to store data).
When the CPU wishes to send a value to the port’s outputs, it must
somehow cause a logic 0 to be applied to that port’s ~o_enable signal. In
the case of our Beboputer, we have decided that there are going to be
thirty-two such output ports located at addresses $F020 to $F03F. In order
for the CPU to individually address these ports, it must be able to generate a
unique ~o_enable signal for each port.
Once again there are numerous ways of generating such signals. For
example, we can use the most significant output from the 4:16 decoder,
~cs[15], to indicate that we wish to address a location greater than or equal
to $F000. We can then combine this signal with some additional logic that
decodes the least significant address bus bits to give us thirty-two signals
corresponding to addresses $F020 to $F03F. Finally, we could combine
each of these signals with the CPU’s ~write signal to give us thirty-two
unique control signals, one for each output port.
Quick quiz #2
1) How many deaths does a coward die?
2) Which number system is used inside digital computers?
3) Which part of the computer makes decisions and performs logical
and arithmetic operations?
4) What are the data, control, and address busses used for?
5) Name four different types of information that might be conveyed by
the data bus.
6) What are the clock, ~reset, ~read, and ~write signals used for?
7) What are ROMs and RAMs used for and what are the major
differences between them?
8) What does “1 K-byte” actually mean?
9) What is a memory map used for?
10) What do input and output ports do and how do they do it?
For those who are interested and want to know more about number systems,
logic gates, memory devices, and musical socks; these topics are presented in
greater detail in this book’s companion volume: Bebop to the Boolean Boogie
(An Unconventional Guide to Electronics), ISBN 1-878707-22-1.
2-38 Bebop BYTES Back
Chapter
3
The Switch Panel
In this chapter we will discover:
How a 1960s computer would compare to
your home computer
▼
Why early computers used magnetic
beads to store information
▼
How early computer operators used switch
panels to control computers
▼
What flowcharts are and how to use them
▼
How to invoke your Beboputer
▼
How to enter and run a program using
a switch panel
▼
How to speed up or slow down your
Beboputer’s system clock
▼
The Memory Walker display and how
1
to use it
▼
b
down to dinner
3-2 Bebop BYTES Back
Contents of Chapter 3
Peering through the mists of time ..................................................................................... 3-3
A 1960s computer versus your home computer ......................................... 3-4
We’re almost ready to rock and roll, but... ......................................................... 3-9
Winding up the Beboputer ....................................................................................................... 3-10
What should you do if your mother calls you down to
dinner? ......................................................................................................................................................... 3-14
Using the switch panel to enter a program ................................................... 3-14
Using the switch panel to run a program ......................................................... 3-20
Modifying the Beboputer’s system clock ............................................................ 3-22
Using the switch panel to modify a program ............................................. 3-23
The memory walker display .................................................................................................... 3-26
Introducing the monitor program ................................................................................. 3-29
Hardware, software, firmware, wetware, and vaporware ..... 3-30
Exiting the lab and saving your environment .............................................. 3-31
Quick quiz #3 ................................................................................................................................................ 3-32
The Switch Panel 3-3
Peering through the mists of time
If we were to give our crystal ball a stern rubbing and peer back through the
mists of time, say to the early 1960s, very few people knew what a
computer was and even fewer had actually seen one “in the flesh.” In those
days computers used to cost tens of millions of dollars and they were bigger
than big. In fact, by present day standards they were outrageously,
gynormously, humongously huge. The cooling systems alone could weigh
several tons, and you’d be hard pushed to squeeze even a medium-sized
computer into a large barn!
powerful than a typical home computer system is today, with none of the
bells and whistles that we have come to take for granted. You will see what
we mean as we proceed with our first laboratory, in which we use the
Beboputer to emulate some of the characteristics of its great-grandfathers.
3
To avoid any misunderstanding, we should note that even the most rudimentary of monitor
programs in the form described in this chapter were not available on the first computers
(whose switch panels were hard-wired directly into the “guts” of the system), but the concept
of a monitor program serves as a basis for our discussions here. A more detailed look at the
role of monitor programs is presented in Chapter 12.
3-6 Bebop BYTES Back
:
MA Row 0
MA
Row 1
1
IA MB I
2 A
Row 2
IB :
etc Sense wire
1
I
2 A
First consider the case where such a bead has a single wire threaded
through it (Figure 3.2a). If a small current is passed through the wire then
nothing happens. But if a current of sufficient magnitude, which we will call
IA, is passed through the wire, it causes the bead to be magnetized in the
direction MA. Similarly, if a current of equal magnitude called IB is passed
through the wire in the opposite direction, the bead will be magnetized in
the direction MB. A bead magnetized in direction MA can be used to
represent a logic 0, while a bead magnetized in direction MB can be used to
represent a logic 1 (or vice versa).
The point is that a magnetized bead will remain magnetized after the
current is removed, and will therefore “remember” the logic value it is being
used to represent.(4) As it happens this single-wire scheme is not particularly
useful to us, because if we were to string a number of beads on the wire,
then every time we applied a current to the wire they would all be
magnetized in the same direction.
To solve this problem, consider the case in which a bead has two wires
threaded through it (Figure 3.2b). If a current of 1/2IA is passed through each
wire, the total effect is identical to that of a single wire carrying IA, and the
bead is again magnetized in the direction MA (similarly, if each wire were
used to carry a current of 1/2IB, the bead would be magnetized in direction
MB). This dual-wire scheme is of use, because it allows us to construct an
4
In this respect a core store is similar to a ROM, because it is non-volatile and any stored
data is retained after power has been removed from the system.
The Switch Panel 3-7
array of such beads (Figure 3.2c), each of which can be individually
magnetized by applying signals to its associated row and column wires.
The ability to individually magnetize each bead in direction MA or MB is only
part of the story, because it is also necessary to be able to “read” a bead to
determine the direction in which it was magnetized; that is, to see whether
it is currently storing a logic 0 or a logic 1. This is where the sense wire
comes into play (Figure 3.2c).
Assume that a bead magnetized in direction MA represents a logic 0, while a
bead magnetized in direction MB represents a logic 1. Now assume that we
want to detect which logic value is currently being represented by the bead
in the middle of our array. One technique is to apply signals of 1/2IA to the
wires Row1 and Col1 which, irrespective of the bead’s current state, will
cause it to be magnetized in direction MA. However, there is a cunning trick
here, because if the bead was already magnetized in direction MA then
nothing will happen, but if the bead was initially magnetized in direction
MB, then causing it to reverse its state results in a pulse of current appearing
on the sense wire.
Thus, by detecting the absence or presence of a pulse on the sense wire,
we can determine whether the bead was originally storing a logic 0 or a
logic 1, respectively. The only problem
with this technique is that if the bead The concept of using magnetic toroids for
was initially storing a logic 1, then digital memory originated in 1950 and is
we’ve just overwritten it with a logic 0 credited to Jay Forrester at MIT. Although
his invention was originally based on
(that is, if the bead was originally
Permalloy tape-wound cores, the idea was
magnetized in direction MB, then it’s
quickly extended to use ferrite beads
now magnetized in direction MA). This which were smaller, faster, and easier to
is referred to as a destructive read, mass produce.
which means that each read operation
in a core store is normally followed by a write cycle to restore the original
data.(5)
The invention of the core store meant that, for the first time, a reasonably
useful form of memory was available to computer designers. Although the
idea of core stores seems rather strange today ...... they worked. If you
think that these are bad, you ought to have seen what designers were
obliged to use before! Previous attempts to create computer memory had
involved such esoteric technologies as mercury delay lines, which were
5
Semiconductor RAMs typically do not have a destructive read cycle; that is, the act of
reading data out of a RAM does not affect the master copy of the data stored in the device.
3-8 Bebop BYTES Back
sequential in nature (see also Chapter 15). To differentiate core stores from
these sequential techniques, the term random access memory (RAM) was
coined to emphasize the fact that data could be written to and read from any
part of the core’s array. This is why we use the term RAM to this day, as
opposed to the possibly more meaningful appellation “read-write memory
(RWM)” which was mentioned in Chapter 2.
In addition to being a lot slower than today’s semiconductor memory devices,
core stores were physically much larger and they could only hold a relatively
small amount of data. The first core stores were effectively “knitted” together
by teams of ladies with the appropriate skills, although automated techniques
soon took over. The outer diameter of the original beads was around 2 mm
(which is actually quite small), and they eventually shrank to around 0.2 mm
(which is really, really small), but it still took a whole bunch of them to make
a memory that could hold a reasonable amount of data.
A typical array from a core store might occupy a circuit board 50 cm x 50 cm
(note that the size and configurations of core stores varied enormously). The
array itself, which might be approximately 15 cm x 15 cm, would sit in the
middle of the board like a spider in the center of its web, while the rest of the
circuit board would be occupied by a gaggle of other electronic components
required to drive signals into the array and sense any responses from the
array. Additionally, if you consider the portion of an array shown in
Figure 3.3c, you will observe that the entire array is served by a single sense
wire, which means that the array could only be connected to a single bit of
the data bus. Thus, each bit of the data bus would require its own array, so
an 8-bit bus would require eight such circuit boards.
Your home computer probably contains at least 4M-bytes of RAM (that’s
4,194,304 bytes), and it possibly contains a whole lot more. By comparison,
users of our medium-sized 1960s computer would be lucky if they had access
to as much as 8K-bytes of core store (that’s only 8,192 bytes!). Furthermore,
unlike your RAM, which occupies a few cubic centimeters and weighs only a
few grams, core store required one or more cabinets, each of which was the
size of a large washing machine, and each of which required a fork-lift truck
to move it around!
We know, we know – we’ve wandered off into the weeds again, but you
have to admit that it’s quite interesting. As a final point in our comparison,
we noted that your home computer has a bulk storage device called a hard
disk, which can be used to store hundreds of millions of bytes of information
for later use. This technology simply wasn’t available to the designers of the
early computers. Instead, they used a variety of more mundane techniques,
The Switch Panel 3-9
most of which involved punching holes in paper tapes or semi-rigid paper
cards. We’ll return to consider these storage techniques in Chapter 4.
Terminal Enter
Flowcharts get their name from the fact that
they chart the flow, or passing of control,
from one operation to the next. By default,
Count the flow is assumed to be from top-to-
Action
to 10
bottom and from left-to-right, but this may
be modified by the use of the arrows on the
flowlines linking the symbols.
Is he
Decision
gone? no Flowcharts can employ a wide variety of
symbols, but we will generally employ a
yes limited subset. The two main types of
symbols are the action symbol (rectangle),
Call him
Action which indicates some form of activity or
a name!
processing; and the decision symbol
(diamond), which indicates some form of
Terminal Exit
evaluation or test. The terminal symbols
simply indicate the entry point to, and exit
Figure 3.3: Flowcharts
point from, the flowchart.
The first action in our example is to count to ten, which is always a good
course to follow in these situations. The next stage in the process is to
decide whether the bully has left the vicinity; if he isn’t out of hearing range
then we count to ten again. It is only when we are sure he has departed the
scene that we decide to bravely speak our minds (using a controlled acting
technique that the uninitiated may confuse with a whisper).
3-10 Bebop BYTES Back
Tool Bar
Switch Panel
Display Panel
Status Bar
In the case of this first lab, the working area of the project window contains
just three items: a switch panel, a display panel, and an 8-bit LED display(6)
(just to give you a clue, the switch panel is the one with all of the switches).
In ye olden tymes, the switch panel was one of the main methods for the
operator to instruct the computer as to which operations it was to perform.
Correspondingly, the display panel was one of the main techniques for the
operator to see what the computer had actually decided to do (as we have
all learnt to our cost, what you want and what you get aren’t necessarily the
same thing).
Running along the upper half of the switch panel are sixteen switches,
which correspond to the signals forming the Beboputer’s 16-bit address bus.
6
LED is the abbreviation for light-emitting diode. These devices are discussed in more detail
in Chapter 5.
3-12 Bebop BYTES Back
Similarly, the lower left-hand side of the switch panel contains eight
switches, which correspond to the Beboputer’s 8-bit data bus. In the
bottom right-hand corner is the main ON/OFF switch, which is used to
power-up the Beboputer (we’ll look at the other buttons in due course).
15 Address 0
Off
there really wasn’t much of anything that acted in the way you’re used to
seeing computers behave today. But fear not my braves, because you’ll find
that there’s more to this simple user interface than meets the eye.
you clicked on the Enter button you caused the values $4000 $90
RAM
on the data switches ($90) to be copied into the $4001 $XX
memory location pointed to by the address switches
($4000). As it happens, the contents of address $4000 Figure 3.11: Load
are going to be the first byte in the first instruction in instruction
our program ($90 directs the CPU to load the contents of the next memory
location into its accumulator). Of course we’ve still got a long way to go,
but we have successfully converted the first half of the first action in the
flowchart in Figure 3.7 into something that the Beboputer can understand.
Our program actually requires us to load nine bytes of data into the RAM.
We’ve already loaded the first byte, so we only have eight more bytes to go.
To speed things up a little, simply execute the following instructions. Once
we’ve loaded the entire program we’ll return to discuss what each of these
bytes mean.
And that’s the lot, you’ve entered the entire program. Unfortunately, there’s
no way (or none that we’ve told you about yet) for you to peer inside the
Beboputer’s memory to make sure that you didn’t make any mistakes.
However, if you think that you did made an error at a particular location,
you can always reset the address and data switches to their correct positions
for that location and click on the Enter button again.
We’re almost ready to run the program, but before we do, it would be
appropriate to compare our original flowchart with its actual realization that
we just loaded into the RAM (Figure 3.12)
As we can see, the program consists of four instructions, which correspond
to the four action blocks in the flowchart: a load, a store, a rotate, and a
jump. Each instruction requires a different number of bytes: the type of load
we used requires two bytes, the store and jump take three bytes each, while
the rotate only occupies a single byte.
The Switch Panel 3-19
Enter
Rotate
$4005 $78 $78 = Rotate accumulator 1-bit left
ACC 1-bit
In the case of the rotate instruction, the $78 opcode instructs the CPU to
rotate the contents of its accumulator one bit to the left. Once again, the
effects of this instruction will become apparent when we come to actually
run the program. The rotate instruction contains everything that needs to be
said as far as the CPU is concerned, so this instruction does not have any
operand bytes.
Finally, in the case of the jump instruction, the $C1 opcode at address
$4006 instructs the CPU to jump back to the memory location specified by
the address contained in the following two bytes. The contents of the two
bytes stored at addresses $4007 and $4008 ($40 and $02 respectively) are
understood by the CPU to represent the address $4002. If you look at the
flowchart in Figure 3.12, you’ll see that this is the address of the first byte in
the store instruction. When we decide to run a program, the CPU will
commence at whichever initial address we give it ($4000 in the case of this
example). The CPU will automatically continue to proceed through
ascending addresses until something occurs to change the flow of the
program. In this program the jump instruction at address $4006 causes just
such a change to occur.
One point that you may have been wondering about is the opcodes
themselves. Why is it that the codes $90, $99, $78, and $C1 represent
load, store, rotate, and jump instructions respectively. Did this just happen?
Are these international standards? Do these codes work with every
computer? Well as fate would have it, the answers to these questions are
No, No, and ...... wait for it ...... No. Each type of computer understands
its own set of opcodes which are hard-wired into the core of the CPU by its
designers. The opcodes $90, $99, $78, and $C1 just happen to be the ones
that we (in our omnipotence) decided the Beboputer should use. We will
return to consider opcodes in more detail in Chapter 8.
To actually display the new contents of the accumulator, first click the Step
button to execute the jump instruction. Remembering that the jump
instruction directed the CPU to jump back to address $4002, it should come
as little surprise to find that the address and data lights update to reflect the
fact that they are now pointing to the first byte of the store instruction again.
Clicking the Step button one more time executes the store instruction,
which causes the accumulator’s new contents to be copied to the 8-bit LED
display.
You may continue to click the Step button to your heart’s content. Once
your clicking frenzy has run its course, click on the Run button to unleash
the full power of the Beboputer. Apart from anything else, note that the
status bar at the bottom of the project window now indicates that we are in
the run mode.
Enter
Increment
$4005 $80 $80 = Increment the accumulator
ACC
From the flowchart we see that the first action is to load the accumulator
with an initial value of $00. As before, the second action is to store the
contents of the accumulator to the 8-bit LED display. The third action is to
increment the contents of the accumulator by one; that is, to add the
number 1 to whatever the accumulator already contains. The fourth and
final action is to jump back to the store instruction.
Fate is obviously smiling down on us, because it appears that we only have
to modify the contents of two bytes to coerce our original program into this
new manly-man’s version (how lucky can you get?). The first byte to be
modified is the initial value that’s loaded into the accumulator, where this
value was stored at address $4001 – we need to change this byte from $03
to $00. The second byte to be modified is at address $4005; we need to
change this byte from $78 (the original opcode that rotated the accumulator)
to $80 (a new opcode that increments the accumulator).
However, we’re still in the run mode, and we can’t change anything if
we’re in either the run mode or the step mode. Try clicking on the address
switches if you like. You’ll find that although they’ll toggle, they don’t have
any other effect. The problem is that the Beboputer’s monitor program isn’t
paying any attention to us – the only thing on its mind is to continue
executing our first program like a gerbil running endlessly around a wheel.
Somehow we have to get the Beboputer’s attention, and the way to do this
is to click your mouse on the Reset button on the switch panel. (Please,
please, PLEASE .... whatever you do .... Do Not press the physical reset
button on your real computer). Clicking on our Reset button has the same
The Switch Panel 3-25
effect on the Beboputer that a handful of ice cubes dropped down the back
of your shirt would have on you – it causes it to sit up and pay attention! In
fact, in an identical manner to the power-on reset, the Reset button causes
the Beboputer to return to address $0000, which is the location of the first
instruction in our monitor program. (We’ll discuss the monitor program in
more detail a little later). Note that the 8-bit LED display retains the last
pattern that was written to it – for reasons of our own we decided that
resetting the Beboputer should not affect the output ports.
As usual, the status bar at the bottom of the project window indicates that
we’re now back in the reset mode. Also, as we’ll see, pulling a reset did not
affect the contents of the RAM, so our original program is still there. All you
have to do to modify the program is perform the following actions:
Once again, in order to run our program we first need to set the address
switches on the switch panel to the program’s start address of $4000
(Figure 3.16). As before, to execute our program we have two options: to
Step through the program or to Run the program; and due to the fact that
we’re currently in the reset mode, both the Step and
Run buttons on the switch panel will cause the
program to be executed from the start address as Figure 3.16: Start address
specified by the address switches.
But before we run this program, let’s make sure that we’re all tap-dancing to
the same set of bongos and we’re one-hundred percent sure as to what it’s
going to do. We already know that the program will load the accumulator
with an initial value of $00, and then loop around adding one to the
contents of the accumulator on each pass through the loop. Thus, we may
reasonably expect that the lights on the 8-bit LED display will follow the
sequence shown in Figure 3.17.
Obviously this is a standard binary count, which is pretty much what we’d
anticipate from our flowchart. However, one interesting question would be:
“When all of the lights on the 8-bit LED display are green (corresponding to
the accumulator containing 1111 1111 in binary), then what do you expect
3-26 Bebop BYTES Back
bars appear on the main project window. This is because the project
window supports a virtual workspace, which can represent a much larger
area than can be displayed on your screen. We would urge caution here,
because it’s easy to lose things in a virtual workspace if you forget where
you left them (but you’ll probably discover that for yourselves).
In addition to the scroll bar, there are a variety of other techniques that
allow you to quickly navigate around the memory. At the top of the memory
walker display is a toolbar. Clicking on the O/P Port icon will cause the
display to scroll to the area in memory occupied by the output ports, such
as the one driving our 8-bit LED display at address $F020. Similarly,
clicking on the related I/P Port icon will cause the display to jump to the
memory locations dedicated to our input ports, which commence at address
$F000.
Next click on the ROM icon, which automatically scrolls the display to the
start of the ROM at address $0000. Note that there is some data representing
the monitor program in the ROM (we’ll return to consider this shortly).
Now click on the Goto icon. This brings up a dialog window which allows
you to enter an address in hexadecimal. When you’ve entered your target
address, either click on the OK button or press the <Enter> key on your
keyboard.
Finally (for the moment), click on the RAM icon to return us to the start of
our program at address $4000. Now if you’ve been keeping pace with us
and not wandered off into the boondocks to do your own thing, the address
switches on the switch panel should still be set up to point to location
$4000. (You might want to check that they still appear as shown in
Figure 3.16; if not then please change them now).
Click the Step button on the switch panel, which causes the Beboputer to
execute the instruction at address $4000 and pause at the beginning of the
next instruction at address $4002. Note the chevron characters at address
$4002 (in the second column from the left in the memory walker).
Whenever the Beboputer is in its step mode, these chevrons appear in the
memory walker and point to the first byte of the instruction that is waiting to
be executed. Perform a few more steps, and observe both the chevrons and
the 8-bit LED display.
Next click on the Run button in the switch panel, thereby unleashing the full
spine-tingling power of the Beboputer. Note that the memory walker
display “grays out,” because continually updating this type of display while
The Switch Panel 3-29
the Beboputer is in its run mode would bring your computer to its knees
and slow everything down to an unacceptable level. (Any data values
shown in the memory walker are not guaranteed to be valid whilst the
display is grayed out.)
Now, while your mother isn’t looking, click the Reset button on the switch
panel. As you would expect, this forces the Beboputer back into its reset
mode and the contents of memory walker are updated and returned to us.
However, the most important point is that the memory walker gives us a
visual confirmation that pulling the reset did not affect the contents of the
Beboputer’s memory.
But wait, there’s more. Boldly click the ON/OFF switch on the switch panel.
As soon as the power is cut the display lights on the switch panel flicker out
and the memory walker goes gray; thereby indicating that it has nothing
meaningful to say. Wait a few seconds to savor the moment, then click the
ON/OFF switch again to bring the Beboputer back online. Good grief! Look
at the memory walker! All of the memory locations show ‘X’ characters!
What have you done? Where has our program gone? Is this the end of all
life in the universe as we know it? Well, it’s not quite as bad as that, but our
program has certainly exited the stage and moved on to another plane of
existence.
The problem is that our Beboputer’s memory is based on (virtual)
semiconductor RAMs, which lose their contents when power is removed
from the system, and which initialize with unknown, random logic 0s and
logic 1s when power is reapplied. How could we have forgotten that? Well,
there’s no use crying over spilt milk. Obviously we need some way to store
our programs, but we’ll leave that problem for Chapter 4.
Let’s take these points in order. In a real machine of the 1960s vintage, the
switch and display panels would be connected into input and output ports
(unless they were hard-wired into the “guts” of the computer – see also
Chapter 12). For example, the switch panel would require four input ports:
two to handle the address switches, one to handle the data switches, and
one to handle the Enter, Step, and Run buttons (each of these buttons would
only require one bit of the port, while the Reset button would be hard-
wired into the CPU’s reset input). Similarly, the display panel would require
three output ports: two for the address lights and one for the data lights (this
is discussed in more detail in Chapter 9).
Thus, in a real machine the monitor program would simply loop around,
reading the various switch ports, writing to the various display ports,
loading data into a memory location (when instructed to do so by an Enter
command), and passing control to another program (when instructed to do
so by a Step or Run command). So in reality, a monitor program for one of
these old machines wouldn’t need to contain very many instructions at all.
In a similar manner to their real counterparts, our switch and display panels
are plugged into four of our input ports and three of our output ports,
respectively. However, as we just noted, what you see here isn’t really a
monitor program – it could be, but it isn’t. In fact the bytes you see in the
ROM are there for just one purpose, which is to give you something to look
at.(11) For technical reasons that are beyond our power to explain here, it
proved to be advantageous for us to usurp the monitor program’s function
and to control the Beboputer from afar.
11
This isn’t to say that the data in these bytes doesn’t mean anything at all because it does,
but it’s up to you to work out what it is!
The Switch Panel 3-31
In addition to “hardware,” “software,” and “firmware,” a number of other
“–wares” have thrust their way onto the stage. The term wetware is often
used to refer to any software that is still relatively new and untested
(unfortunately, commercially released programs can sometimes fall into this
category),(12) while vaporware refers to any hardware or software that exist
only in the minds of the people who are trying to sell it to you. Other
“–wares” that you may stumble across are shareware (for which you pay a
nominal charge, based largely on an honor system) and freeware which, as
its name would suggest, is software that is placed in the public domain at
no charge.(13)
12
”Wetware” is also sometimes used to refer to people’s brains (or the thoughts and ideas therein).
13
Some freeware is actually tremendously useful and well-written, but a lot is worth exactly what
you pay for it. Hey, what do you want for nothing – your money back?
3-32 Bebop BYTES Back
Quick quiz #3
1) What did computer memory have to do with ladies knitting?
2) What was magnetic core store and how did it work?
3) What were switch and display panels used for in early computers?
4) What does a RAM contain when power is first applied to a
computer system?
5) What is a hard disk?
6) What are flowcharts and what are they used for?
7) What is a program?
8) What do you understand by the terms “opcode” and “operand” at
this stage in our discussions?
9) What were the main tasks performed by a monitor routine?
10) Where would you expect to find an accumulator and what does it do?
Chapter
4
Paper Tapes and
Punched Cards
In this chapter we will discover:
Who invented the accordion
▼
When paper tapes were first used as a
medium for the preparation, storage, and
transmission of data
▼
How to use the output ports dialog to
invoke new output devices
▼
How to save a program from the
Beboputer’s RAM onto a (virtual)
paper tape
▼
How to load a program from a paper tape
into the Beboputer’s RAM
▼
The differences between main (core) store
2
and bulk storage technologies
▼
b
Contents of Chapter 4
Perforated paper products save the day ........................................................... 4-3
The origin of paper tapes .............................................................................................................. 4-3
Winding up the Beboputer .......................................................................................................... 4-6
Saving our program on paper tape .......................................................................... 4-11
Reloading our program from paper tape ........................................................ 4-14
Teleprinters and batch modes ............................................................................................ 4-16
The origin of punched cards ................................................................................................. 4-20
Alternative storage technologies ................................................................................... 4-23
Perforated paper products never die, they simply .... ...................... 4-26
Quick quiz #4 ................................................................................................................................................. 4-27
Paper Tapes and Punched Cards 4-3
Perforated paper products save the day
As was amply illustrated in the previous chapter, it was somewhat
inconvenient to have a computer which forgot everything it knew if its
operator turned it off before stepping out for a bite of lunch. Similarly,
imagine a programmer’s frustration if, after spending countless hours entering
a program containing hundreds or thousands of bytes, the janitor carelessly
disconnected the computer in order to vacuum the office. A few choice
words would be (and often were) the order of the day, let me tell you!
There were also a variety of other considerations. For example, since the
early computers had very little memory anyway, it was necessary to have
some mechanism to store large amounts of data outside of the main memory.
A program could then access and process small portions of the data on an
as-needed basis. Also, if the operator had a number of different programs,
but there wasn’t enough memory to contain them all at the same time, then
it was necessary to have some technique for storing the inactive programs.
There was also the question of long-term archiving; that is, being able to
store programs and data for use sometime in the future.
Yet another concern was being able to transport programs and data between
computers located at different sites. For example, a programmer who created
an interesting routine in Boston may well have wanted to share the fruits of
his or her labors with colleagues in San Francisco. Although some flavors of
early memories, such as magnetic core stores, were non-volatile, it was still
somewhat less than practical to slip something the size of a large washing
machine into an envelope and drop it into the mail.
For all of these reasons, it was obvious to everyone that it would be
advantageous to have some kind of reliable, cheap, and efficient media for
storing large amounts of computer data (and preferably something that
weighed-in at substantially less than a ton). To satisfy these requirements, two
techniques became very widely used: paper tapes and punched cards, both
of which involved perforating paper-based products (try saying that ten times
quickly).
electric telegraph.(1,2) This instrument made use of five wires, each of which
drove a pointer at the receiver, where these pointers were used to indicate
different letters. In the same year, the American inventor Samuel Finley
Breese Morse developed the first American telegraph, which was based on
simple patterns of “dots” and “dashes” called Morse Code being transmitted
over a single wire. Morse’s system was eventually adopted as the standard
technique, because it was easier to construct and more reliable than
Wheatstone’s (Table 4.1).
A N 0
B O 1
C P 2
D Q 3
E R 4
F S 5
G T 6
H U 7
I V 8
J W 9
K X , comma
L Y . period
M Z ?
The duration of a “dash” is three times the duration of a “dot.” Note that this
is only a subset of the code (although it’s quite a large subset), but it’s
enough to give the general idea. Also note that this table shows International
Morse Code, which is a slightly different flavor to American Morse Code.
Morse Code has a number of interesting features and, knowing us, you’ll be
lucky to escape this book without our mentioning at least a few of them. One
little nugget of trivia we can’t resist pertains to the code for the letter ‘V’. In
his early years, Morse was more attracted to the arts than he was to science.
The rumor on the street is that Morse attended a performance of Beethoven’s
Fifth Symphony on one of his trips to England. Idle speculation further has it
that this performance so impressed him that the “dot dot dot dash” code he
1
Sir Charles was a busy man. Amongst other things, he also invented the accordion in 1829
and three-dimensional photographs in the form of his stereoscope in 1838.
2
Apropos of nothing at all, 1837 was also the year that another “Charles”, Charles Dickens,
first published a story under his given name (prior to this he’d been using the pen-name “Boz”).
Paper Tapes and Punched Cards 4-5
used for the letter ‘V’ (which is also the Roman numeral for “five”) was
intended to emulate the symphony’s opening sequence, which goes:
“Da Da Da Daaa”.(3,4)
The telegraph quickly proliferated thanks to the relative simplicity of Morse’s
system. However, a problem soon arose in that operators could only
transmit around ten words a minute, which meant that they couldn’t keep
up with the public’s seemingly insatiable desire to send messages to each
other. This was a classic example of a communications bottleneck. Thus, in
1857, only twenty years after the invention of the telegraph, Sir Charles
Wheatstone introduced the first application of paper tapes as a medium for
the preparation, storage, and transmission of data (Figure 4.1).
B E B O P R U L E S
Dots
Feed holes
Dashes
This
row Figure 4.2: Computer
illustration Da
ta
paper tape
represents
one of the D
tap irect
e m ion
more popular IBM ov o
em f
en
standards – a one-inch t
wide tape supporting eight
channels (numbered from 0 to 7)
with 0.1 inches between the punched
holes. The first paper tape readers accessed Ch
an
the data by means of springy wires (one per ne
Sp l0
roc
channel), which could make electrical connections to k et
ho
Ch les
conducting plates under the tape wherever a hole was an
ne
l7
present. These readers were relatively slow and could only
operate at around fifty characters per second. Later models used opto-
electronic techniques, in which a light source was placed on one side of the
tape and optical cells located on the other side were used to detect the light
and thereby recognize the presence or absence of any holes.
In the original slower-speed readers, the small sprocket holes running along
the length of the tape between channels 2 and 3 were engaged by a toothed
wheel to advance the tape. The higher-speed opto-electronic models used
rubber rollers to drive the tape, but the sprocket holes remained, because
light passing through them could be detected and used to generate
synchronization pulses. On the off-chance that you were wondering, the
reason the sprocket holes were located off-center between channels 2 and 3
(as opposed to being centered between channels 3 and 4) was to enable the
operator to know which side of the tape was which. Of course, it was still
necessary to be able to differentiate between the two ends of the tape, so
the operators used scissors to shape the front-end into a triangular point,
thereby indicating that this was the end to be stuck into the tape reader.
Tool Bar
Switch Panel
Status Bar
The main switch panel, the memory walker, and the 8-bit LED device are
all familiar to us from the previous lab. In fact the only new tool is a simple
8-bit switch device that we’ve plugged into one of our input ports. Note that
the display panel (not shown here) also arrives on the screen, because it’s
intimately related to the switch panel. But the fact that we have the memory
walker means that we no longer need the display panel, so dismiss this
device by clicking the exit button in its title bar. Exit
button
The program we are going to write will perform
an endless loop, reading the state of the switches Title bar
on the 8-bit input device and writing this state to Menu bar
the 8-bit LED display. The flowchart for this
program, along with its associated opcodes and data bytes, is shown in
Figure 4.4.
4-8 Bebop BYTES Back
Enter
From the flowchart we see that the first action is to load the accumulator
with whatever value is represented by the 8-bit switch input device. This is
a slightly different load instruction to the one we used in our previous
laboratory; in this flavor of a load, the $91 opcode at address $4000
instructs the CPU to load the accumulator with the contents of the memory
location which is pointed to by the following two bytes.
As was discussed in Chapter 3, we (as designers of the Beboputer) decided
that addresses would be stored in memory with the most-significant byte
first. Thus, the contents of the two bytes stored at addresses $4001 and
$4002 ($F0 and $00, respectively) are understood by the CPU to represent
the address $F000. However, once again we’re going to trick the CPU,
because although it thinks it’s reading from memory, the address we’re
using actually points to an input port into which we’ve connected the 8-bit
switch device.
The other two instructions are identical to our previous program (with the
exception of the address associated with the jump instruction). The $99
opcode at address $4003 instructs the CPU to copy the contents of its
accumulator into the memory location specified by the address contained in
the following two bytes (which actually contain the address of the output
port that drives the 8-bit LED display). Finally, the $C1 opcode at address
$4006 instructs the CPU to jump back to the memory location specified by
the address contained in the following two bytes. In the case of this
program, we’ve decided that the jump instruction should cause the CPU to
return to address $4000, which is both the beginning of the program and
the beginning of the loop.
By some strange quirk of fate, this program requires us to load only nine
bytes of data, just like the programs we played with earlier, and the sooner
Paper Tapes and Punched Cards 4-9
we start the sooner we’ll finish. First, click the ON/OFF switch on the main
switch panel with your mouse to power-up the Beboputer, then perform the
following actions to load the program. (Note that every time you click the main
switch panel’s Enter button, the memory walker updates to show that data):
Don’t forget that you can always correct any errors by overwriting a
location’s contents with new data. Once you’ve entered the program and
you’re happy that all is as it should be, return the
address switches to the program’s start address of
$4000 (Figure 4.5), then click the Run button to let
Figure 4.5: Start address
the Beboputer rip.
Now, although not a lot seems to be happening, the Beboputer is actually
working furiously, reading the values of the switches on the 8-bit input
device and writing what it finds there to the 8-bit LED display. (The ways in
which the switches and the LED display work are discussed in more detail
in Chapter 5.) Use your mouse to click one of the 8-bit switches and note
that the corresponding LED appears to respond almost immediately.
Play around for a while toggling the switches and watching the LEDs
respond. When you’re ready to proceed further, click the main switch
panel’s Step button to place the Beboputer in its step mode. As we know,
it’s impossible to predict the point at which you will break into the program
– the Beboputer will simply halt at the beginning of whichever instruction it
was about to perform when you dropped into the step mode. So once
you’ve entered this mode, keep on clicking the Step button until the
chevrons in the memory walker fall on address $4006. Now perform the
following sequence of actions:
a) Make a note of the current state of the lights on the 8-bit LED display;
that is, which ones are red and which ones are green.
b) Click one of the 8-bit switches to change its state and record which
one you changed. Note that nothing happens to the 8-Bit LEDs.
c) Click the Step button to execute the jump instruction, which results
in the chevrons pointing to address $4000 in the memory walker.
Note that nothing happens to the 8-Bit LEDs.
d) Click another of the 8-bit switches to change its state. Make sure that
this isn’t the same switch that you changed last time and record
which one you changed this time. Note that nothing happens to the
8-Bit LEDs.
e) Click the Step button to execute the load instruction, which results in
the chevrons in the memory walker pointing to address $4003. Note
that nothing happens to the 8-Bit LEDs.
f) Click another of the 8-bit switches to change its state. Make sure that
Paper Tapes and Punched Cards 4-11
this isn’t the same as either of the switches that you changed above
and record which one you changed this time. Note that nothing
happens to the 8-Bit LEDs.
g) Click the Step button one last time to execute the store instruction,
which results in the chevrons in the memory walker pointing to
address $4006. Note that two of the LEDs did change this time.
The main point of the above exercise is to illustrate the fact that the CPU
doesn’t inherently know exactly when something changes in the outside
world.(5) Thus, when you altered the first switch at point (b) the CPU didn’t
actually pay any attention. Similarly, when you stepped through the
instruction at point (c), the CPU didn’t look at the switches, it just executed
the jump instruction. Once again, when you modified the second switch at
point (d), the CPU didn’t actually notice that you’d done anything. It was
only when you executed the load instruction at point (e) that the CPU
copied the state of the switches into its accumulator. Thus, when you
changed the third switch in point (f), the CPU didn’t pay any attention,
because it had already loaded the switches’ values on the previous
instruction. So when you finally executed the store instruction at point (g),
the only LEDs to be updated were those corresponding to the first two
switches that you had altered.
Without touching the 8-bit switches, click the Step button three more times
and observe that the third LED does change. If the reason for this is not
immediately obvious, then spend a few moments going through the
sequence in your mind until you’re satisfied that you understand what’s
going on. Last but not least, the reason why we didn’t notice any of this
when we were happily toggling the switches in the run mode was that the
Beboputer was racing around the loop, reading the switches and updating
the LEDs faster that you could say: “Buy the authors a three course meal
and put it on my tab!”
6
The concept of subroutines is introduced in greater detail in Chapters 8 and 12.
4-14 Bebop BYTES Back
writer subroutine. At this point you’ll observe a blank tape being loaded into
the hole puncher mechanism, then the subroutine will copy the contents of
the Beboputer’s RAM, one byte at a time, to the output port that drives the
paper tape writer. Every time the writer receives a byte from the Beboputer’s
output port it punches the corresponding holes into the paper tape (our
particular monitor program will only copy the 512 bytes occupying
addresses $4000 to $41FF onto the tape).
Once the subroutine has copied all of the bytes comprising our program to
the paper tape writer, it returns control to the main monitor program and
retires gracefully from the scene. Actually, to be absolutely honest, much of
the above is complete subterfuge, because in the same way that our
Beboputer doesn’t really have a main monitor program (see Chapter 3), it
also doesn’t really have a paper tape writer subroutine. Once again we’re
playing the puppet master by controlling the Beboputer from afar.
However, if we did have a real monitor program and a paper tape writer
subroutine, then this is the general way in which everything would hang
together.
One further point that we might be moved to mention is that real paper
tapes used to stream out of the writer at prodigious rates, and they usually
ended up in a humongous tangle on the floor. Even in high-tech institutions,
a strategically placed waste bucket usually served to gather the tape and
prevent passers-by from trampling all over it. A brand-new tape would be
approximately 1,000 feet in length, but the operator would tear off the tape
a few inches after the last holes had been punched. Also, an extremely
useful gadget to own was a hand-operated winder, which greatly speeded
up the process of coiling your punched tape onto a reel.
7
This process is discussed in far greater detail in Chapter 12.
8
Teleprinters were often referred to as teletype machines or teletypes. However, this was a
brand name (much as “Hoover” is for vacuum cleaners) for a series of teleprinters
manufactured by International Telephone and Telegraph (ITT).
Paper Tapes and Punched Cards 4-17
Due to the fact that teleprinters were relatively inexpensive (compared to
the millions of dollars invested in the computer), a typical installation
usually included a large number of them, thereby allowing many people to
create programs at the same time. Groups of programs were subsequently
presented to the computer operators in a batch, and the computer was said
to be running in a batch mode.(9) As computers evolved and became more
powerful, teleprinters began to be connected directly to them. This allowed
the operators and the computer to communicate directly with each other,
which was one of the first steps along the path toward the interactive way in
which we use computers today.
By some strange quirk of fate, the use of The terms uppercase and lowercase
were handed down to us by the printing
teleprinters for program preparation brings
industry, from the compositors’ practice
us to an interesting diversion that will help of storing the type for capital letters and
to explain the origins of certain terms used small letters in two separate cases.
in future chapters. But first let’s note that When working at the type-setting table,
in order to handle textual documents, a the compositors invariably kept the
complete and adequate set of characters capital letters and small letters in the
should include the following: upper and lower cases, respectively;
hence, uppercase and lowercase. Prior
a) 26 uppercase alphabetic characters to this, scholars referred to capital letters
(‘A’ through ‘Z’) as majuscules and small letters as
minuscules, while everyone else simply
b) 26 lowercase alphabetic characters called them capital letters and small
(‘a’ through ‘z’) letters.
c) 10 numeric digits (‘0’ through ‘9’)
d) Approximately 25 special characters for punctuation and other
special symbols, such as ‘&’, ‘%’, ‘@’, ‘#’, and so forth.
To be machine-readable (that is, to be understandable by an electronic
device), each of these characters has to be assigned a unique binary code.
Additionally, as these character codes have to be recognized by diverse
devices such as computers, printers, and teleprinters (to name but a few), it
would obviously be useful if everybody could agree on a common standard.
Of course, in the spirit of free enterprise, everyone and his dog came up
with their own standard! However, to cut a long story short, one standard
that came to be very widely used is the American Standard Code for
Information Interchange (ASCII). We will be considering this standard in
9
The phrase batch mode is now commonly used to refer to a program that’s running as a
background task, using whatever resources are available when interactive users aren’t
hogging all of the computer’s capacity
4-18 Bebop BYTES Back
more detail in Chapters 10 and 11, but for the moment we need only note
that the ASCII codes corresponding to the characters we use to represent
hexadecimal digits are as shown in Table 4.2:
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
= 1 0 0 1 0 0 0 1
ASCII '9'
Holes represent
binary 1 values
Row 11
Figure 4.13 shows one of the early 80 column IBM cards (not to scale). Each
card contains 12 rows of 80 columns, and each column is typically used to
represent a single piece of data such as a character. The top row is called
the “12” or “Y” row; the second row from the top is called the “11” or “X”
row; and the remaining rows are called the “0” to “9” rows (indicated by
the numbers printed on the cards). This figure (which took a long time to
draw let me tell you) illustrates one of the early, simpler coding schemes, in
which each character could be represented using no more than three holes.
(Note that we haven’t shown all of the different characters that could be
represented.) Over the course of time, more sophisticated coding schemes
were employed to allow these cards to represent different character sets
such as ASCII and EBCDIC; the rows and columns stayed the same, but
different combinations of holes were used.
One advantage of punched cards over paper tapes was that the textual
equivalent of the patterns of holes could be printed along the top of the card
(one character above each column). Another advantage was that it was easy
to replace any cards containing errors. However, the major disadvantage of
working off-line (with both punched cards and paper tapes) was that the
turn-around time to actually locate and correct any errors was horrendous.
Generally speaking, if you make a programming error on one of today’s
interactive systems, the system quickly informs you of your mistake and you
can fix it almost immediately. By comparison, in the days of the batch
mode, you might slave for hours at a teleprinter with a card puncher
attachment, march miles through wind and rain to the computer building
carrying a one-foot high stack of cards, only to hear: “We’re a bit busy at
the moment, can you come back next Monday?“ So you left your cards
with the operator and spent the weekend in delightful anticipation, but on
returning the following week to collect your results, you’d probably receive
a few inches of computer printout carrying the words: “Syntax error on
card 2: missing comma.” Arrgggh – if the computer knew enough to tell
that there was a missing comma, why didn’t the callous swine know enough
to stick one in for you? The result was that debugging even a trivial program
could take weeks and weeks. In fact, by the time you eventually got a
program to work, you were often hard-pushed to recall what had prompted
you to write it in the first place!
Although punched cards are rarely used now, we endure their legacies to
this day. For example, the first computer monitors were constructed so as to
display 80 characters across the screen. This number was chosen on the
basis that you certainly wouldn’t want to display fewer characters than were
Paper Tapes and Punched Cards 4-23
on an IBM punched card, and there didn’t appear to be any obvious
advantage to being able to display more characters than were on a card.
Similarly, long after interactive terminals became commonly available, the
formatting of certain computer languages continued to follow the rules laid
down in the era of punched cards. To this day, many assembly languages
(see Chapter 12) have unnecessarily restrictive rules along the lines of
“Labels can only occupy columns 1 through 8.” Even the first high-level
languages such as FORTRAN (an abbreviation of Formula Translation) had
comparable rules.
As a final example, consider the case of the program called SPICE
(Simulation Program with Integrated Circuit Emphasis), which is used by
engineers for evaluating analog circuits. The first generation of this program
appeared commercially around the beginning of the 1970s and its
descendants are used to this day. The point is that it is still common practice
to refer to the data used by this program as a “SPICE deck,” which is a
hangover from those times when such data was stored using punched cards
(“deck of cards” – get it?).(11)
16
In 1976, a company called iCOM started to advertise an eight-inch floppy drive called the
“frugal floppy,” which sold for $1,200.
4-26 Bebop BYTES Back
▼
How to use LEDs to construct our simple 8-bit
output display
▼
How to rearrange our LEDs to form an
undecoded 7-segment display
▼
How to augment an undecoded 7-segment
display to form its decoded counterpart
▼
How we can attach two decoded 7-segment
displays to a single 8-bit output port
▼
How to create a program that can display
3
values from $00 to $FF on our two
7-segment displays
b
La
5-2 Bebop BYTES Back
Contents of Chapter 5
Diodes can be rather cunning when you get to know them 5-3
Diodes and light-emitting diodes (LEDs) ................................................................. 5-3
8-Bit LED displays ......................................................................................................................................... 5-5
8-Bit switch devices ................................................................................................................................. 5-8
Winding up the Beboputer .......................................................................................................... 5-9
Undecoded 7-segment displays ...................................................................................... 5-11
Decoded 7-segment displays .............................................................................................. 5-16
Dual decoded 7-segment displays ............................................................................. 5-18
Quick quiz #5 ................................................................................................................................................. 5-21
Seven-Segment Displays 5-3
Diodes can be rather cunning when you
get to know them
In the preceding chapters we used an output device that we dubbed an 8-bit
LED display. This display consisted of a panel of eight lights, one for each of
the bits coming from its associated output port (Figure 5.1).
Corresponds to Corresponds to
output bit 7 output bit 0
We also noted that a red light indicates that its corresponding output port bit
is driving a logic 0, while a green light indicates a logic 1. Now we could
simply assume that this display is constructed using standard incandescent
light bulbs, and we could completely ignore the way in which the output
port drives the display. However, as we will be using a number of
derivations of this type of display in future laboratories, it won’t cause you
irreparable harm to learn what LEDs are, how they work, and how we’re
using them (in our virtual world of course).
First of all the term LED is the abbreviation for light-emitting diode, which
(it may not surprise you to learn) is similar to a normal diode except that it
emits light. “But what is a normal diode?” you cry plaintively (and rather
pathetically, we might add). Well, if you’ll settle down for a moment and
make yourselves comfortable we’ll tell you, but first we need a stirring
subtitle to make us strong.
5 volts 5 volts
In all three cases we’re considering the same bit of the port; that is, the
output from the latch that’s connected to the data[0] signal on the
computer’s data bus. In the first example (Figure 5.4a), we’ve connected a
red LED between the output from the latch and +5 volts. The way in which
we’ve connected this LED means that when the latch is driving a logic 0
(0 volts), the diode’s anode will be at +5 volts, its cathode will be at 0 volts,
and the diode will be turned on and glow red. However, when the latch is
driving a logic 1 (+5 volts), both of the diode’s terminals will be at the same
potential, so nothing will happen and the diode will be turned off. Note that
if this diode had been flipped over such that it’s anode were connected to
the latch’s output and its cathode were connected to +5 volts, then it would
never turn on (remember that diodes only conduct in one direction).
6
Output ports were introduced in Chapter 2
Seven-Segment Displays 5-7
Similarly, in the second example
There’s no particular reason why we used red
(Figure 5.4b), we’ve connected a and green LEDs where we did; we could have
green LED between the output from swapped them over or even exchanged them for
the latch and 0 volts. The way in different colors (so long as we continued to
which we’ve connected this LED observe the same polarity). It’s simply that when
means that when the latch is driving we designed our output displays, we took a vote
a logic 1, the diode’s anode will be and decided that logic 0s and logic 1s would be
at +5 volts, its cathode will be at represented by red and green, respectively. For
0 volts, and the diode will be turned those who are into this sort of thing, we should
on and glow green. However, when note that if the integrated circuit used for the
output port were constructed using a technology
the latch is driving a logic 0, both of
called transistor-transistor logic (TTL), then only
the diode’s terminals will be at the the red LEDs in Figure 5.3 would work. This is
same potential, so nothing will because TTL can’t output a logic 1 that’s strong
happen and the diode will be enough to drive a LED (the green LEDs in this
turned off. case). However, we’re assuming that our output
port is constructed using another very common
Finally, in the third example (Figure integrated circuit technology called
5.4c), we’ve attached both red and complementary metal-oxide semiconductor
green diodes to the latch’s output. (CMOS), which is capable of driving both the red
In this case a logic 0 in the latch and green LEDs as shown.
will turn the red diode ON and the
green diode OFF, while a logic 1 in
the latch will turn the green diode ON and the red diode OFF. Returning to
our 8-bit display, we’re assuming that each of the output port’s eight bits
have both red and green LEDs attached to them. As the red LEDs have their
anode terminals connected together, they are said to be in a common anode
configuration. Similarly, the green LEDs have their cathode terminals
connected together, so they are said to be in a common cathode
configuration (Figure 5.5).
5 volts
Red diodes in
common anode
configuration
Output
Port out_data[0]
data[7:0]
{From CPU}
out_data[7]
~o_enable
Green diodes in
common cathode
configuration
0 volts
For the purposes of our 8-bit display, we’re assuming that each bit on the
actual display panel consists of its associated green and red LEDs mounted
together under a shared transparent plastic “window.”
Once again, because we’re using these switches to interface the computer
to the outside world, it’s necessary to view any signals as real voltage
values. To be consistent with our earlier discussions, we will continue to
assume that our abstract logic 0 and logic 1 values equate to 0 volts and
+5 volts, respectively. Fortunately this device is quite simple; each switch
has one terminal connected directly to a logic 1, while its other terminal is
connected to a resistor, which is, in turn, connected to a logic 0. Each bit
on the input port is connected to a point between a switch and its
associated resistor (Figure 5.7).
5 volts
Switches
Input
Port in_data[0]
data[7:0]
{To CPU}
in_data[7]
~i_enable
Resistors
0 volts
Figure 5.7: Connecting the 8-bit switch input device to the input port
Seven-Segment Displays 5-9
When a switch is OPEN, its corresponding resistor pulls the wire connected
to the input port down to logic 0 (0 volts), but when the switch is CLOSED,
it overrides the resistor and connects that bit of the input port directly to
logic 1 (+5 volts). (Had we wished to, we could have easily have reversed
the positions of the switches and resistors, in which case a CLOSED switch
would have equated to a logic 0 and an OPEN switch to a logic 1.)
Note that this circuit diagram does not give any indication as to how the
physical positions of the switches correspond to OPEN or CLOSED. In the
case of our input device, we decided that a switch in the DOWN position
would be OPEN (logic 0) and a switch in the UP position would be
CLOSED (logic 1). Also note that this relationship was based on a purely
arbitrary value judgment with no merit whatsoever, which is that light
switches in America tend to follow the convention: UP = CLOSED = light is
ON, while DOWN = OPEN = light is OFF.(7)
However, be warned that while the terms UP and DOWN do have some
meaning for us (in the case of this particular input device), the terms ON and
OFF are much trickier customers. The only thing we are really in a position
to say is that, if things are indeed connected together and are physically
oriented as we described them to be in the discussions above, then a switch
that is DOWN is OPEN and represents a logic 0, while a switch that is UP is
CLOSED and represents a logic 1. The point is that it’s up to us (and the
computer programs we write) to decide whether a logic 0 means OFF and a
logic 1 means ON, or vice versa. In fact we can change our minds “on the
fly” as often as we like, although we tend to try to avoid doing this sort of
thing in order to hold onto what little sanity we have left!
7
Light switches tend to work the opposite way around in Great Britain, and you take your
chances in the rest of the world.
5-10 Bebop BYTES Back
Title Bar
Beboputer Computer
File Setup Display Memory T ools Help Menu Bar
Tool Bar
Paper tape
reader/writer
Switch Panel
Status Bar
Once again, the main display panel (not shown here) also arrives on the
screen, because it’s intimately related to the switch panel; but we don’t
need the display panel for this lab, so dismiss it by clicking the exit button
on its title bar. To kick-off the laboratory we’re going to load the program
that you saved on your virtual paper tape in the previous chapter. First click
the ON/OFF switch on the main switch panel to power-up the Beboputer.
Next set the address switches on the
main switch panel to point to start
Figure 5.9: Start address of address of our paper tape reader
paper tape reader subroutine subroutine at $0050 (Figure 5.9).
Now click the switch panel’s Run button. This opens up the usual dialog
window prompting you to enter the name of the paper tape you wish to
load. Click on mytape1, or whatever name you gave your program in the
previous chapter (if you skipped the previous chapter or simply didn’t
bother to save your program, then click on the lab3_p1 entry, which is a
paper tape that we previously created for you). Finally, click the dialog’s
Open button, which causes the main monitor program to enter the run
mode and to hand control over to the paper tape reader subroutine.
In turn, this subroutine reads the data, one byte at a time, from an input
port that’s connected to the paper tape reader. Every time the subroutine
reads a byte from the input port, it copies that byte into the next free
location in the RAM. As usual, once the subroutine has copied all of the
Seven-Segment Displays 5-11
bytes from the paper tape into the RAM, it returns control to the main
monitor program and exits stage left.
As you may recall, our program performs an endless loop, reading the state
of the switches on the 8-bit input device and writing this value to the 8-bit
LED display. The flowchart for this program, along with its associated
opcodes and data bytes, is shown once more in Figure 5.10.
Enter
From the flowchart we see that the $91 opcode at address $4000 instructs
the CPU to load the accumulator with whatever value is represented by the
8-bit switch input device. The $99 opcode at address $4003 instructs the
CPU to copy the contents of the accumulator to the 8-bit display. Finally,
the $C1 opcode at address $4006 instructs the CPU to jump back to the
start of the loop, from whence it will proceed to do the whole thing all
over again.
To remind yourself as to how this program actually performs, set the address
switches on the main switch panel to point to our program’s start address of
$4000 (Figure 5.11) and click on the Run button. Play around for a while
toggling the switches on the 8-bit switch panel
and watching the 8-bit display respond. When
you’re ready to proceed further, click on the
main switch panel’s Reset button to return the Figure 5.11: Start address
Beboputer to its reset mode.
our existing green LEDs in a very rudimentary way, we can come up with a
very useful device called an undecoded 7-segment display (Figure 5.12).
Output
Port out_data
data[7:0] [0] A
[1]
[2]
[3]
[4] F B
[5]
{From CPU} [6] G
[7]
~o_enable G F E D C B A E C
Unused D
Enter
segments, which means that the output port bit corresponding to the most-
significant switch isn’t connected to anything.
As the display contains seven individually-controllable segments, you can
compose 27 = 128 different patterns! However, for our purposes we’re only
interested in sixteen of these patterns; the ones that we can use to represent
the hexadecimal digits 0 through 9 and A through F (Figure 5.14).
Figure 5.14: Generating hex digits with the undecoded 7-segment display
As you’ll see, the program now writes to both of the displays, which makes
it a little easier to visualize what’s happening behind the scenes. When
you’re ready to move on to the next exercise, click the main switch panel’s
Reset button to return the Beboputer to its reset mode.
~o_enable G F E D C B A
out_data[7:4]
unused
0 volts
As usual this really isn’t as complicated as it might first appear. From our
discussions in Chapter 2 we know that any four bits (such as the four least-
significant bits from the output port, out_data[3:0] ), can assume 24 = 16
different patterns of logic 0s and logic 1s. We can use these patterns to drive
a special decoder device, which, in turn, drives the 7-segment display.
When any of the sixteen binary patterns are presented to the decoder’s
inputs, it generates the appropriate signals on its dec_out_data[6:0] outputs,
and these signals are used to drive the seven segment display in such a way
as to present the appropriate hexadecimal digit (Figure 5.17).
Seven-Segment Displays 5-17
Note that we’ve been assuming
out_data[3:0] Hex dec_out_data[6:0] that the LEDs in our 7-segment
0 0 0 0 0 0 1 1 1 1 1 1 displays are connected in a
0 0 0 1 1 0 0 0 0 1 1 0 common cathode configuration,
0 0 1 0 2 1 0 1 1 0 1 1
0 0 1 1 3 1 0 0 1 1 1 1
but common anode configurations
0 1 0 0 4 1 1 0 0 1 1 0 are also available. If we did
0 1 0 1 5 1 1 0 1 1 0 1 happen to be using a common
0 1 1 0 6 1 1 1 1 1 0 1 anode device, then we could still
0 1 1 1 7 0 1 0 0 1 1 1
1 0 0 0 8 1 1 1 1 1 1 1 obtain the required results by
1 0 0 1 9 1 1 0 0 1 1 1 simply inverting all of the logic 0s
1 0 1 0 A 1 1 1 0 1 1 1 and logic 1s on the decoder’s
1 0 1 1 B 1 1 1 1 1 0 0
1 1 0 0 C 0 1 1 1 0 0 1
outputs.
1 1 0 1 D 1 0 1 1 1 1 0
1 1 1 0 E 1 1 1 1
Using the truth table in
0 0 1
1 1 1 1 F 1 1 1 0 Figure 5.17, it would be quite a
0 0 1
simple task to extract the Boolean
Figure 5.17: Decoder truth table equations for the decoder, and
then to implement it using a
couple of handfuls of primitive logic gates (which are introduced in the next
chapter).(9) In fact it’s possible to obtain a decoder such as this in the form of
a special integrated circuit. Alternatively, the decoder logic could be
bundled up inside the 7-segment display’s package. In this latter case the
display would be referred to as a decoded 7-segment display.
As usual, we just happen to have a decoded 7-segment display for you to
play with. To access this display, click the Setup item on the project
window’s menu bar followed by the Output Ports entry in the resulting pull-
down menu. This will invoke the same dialog window that you used earlier.
Next click the button associated with Port 1 to close the undecoded
7-segment display we’ve just been playing with, then click the button
associated with Port 2 to open our new decoded 7-segment display, and
finally click the Dismiss button to close the Output Ports dialog window.
Once again, use your mouse to drag the decoded 7-segment display by its
title bar and place it just above the 8-bit display. Of course, we now need
to modify our program such that it writes to the appropriate output port for
our new display, but this really is a “no-brainer.” If you glance back to
Figure 5.15, you’ll see that it’s only necessary to modify the data at address
$4005 from its current contents of $21 to its new contents of $22, and we
can quickly do this using the memory walker. Double-click the $21 data
9
Extracting Boolean equations from truth tables is discussed in this book’s companion
volume, Bebop to the Boolean Boogie (An Unconventional Guide to Electronics).
5-18 Bebop BYTES Back
entry associated with address $4005 in the memory walker, enter the new
value of $22, and click another row in the memory walker to make it accept
the new data.
Ensure that the address switches on the main switch panel still represent
address $4000, click on the switch panel’s Run button, then play around
with the 8-bit switch input device for a while. Use the truth table in
Figure 5.17 to confirm that the right-hand four switches (and the associated
bits on the 8-bit display) correspond to the hexadecimal digits appearing on
the decoded 7-segment display. Note that although the left-hand four
switches do modify their associated bits on the 8-bit display, they don’t
affect the 7-segment display (for reasons that are pretty obvious if you refer
back to Figure 5.16). When you’re ready to proceed to the next part of this
laboratory, click the main switch panel’s Reset button to return the
Beboputer to its reset mode.
data[7:0]
out_data[3:0]
~o_enable
Decoder Decoder
At this stage in the game our new program should require relatively little
explanation. The first instruction at address $4000 loads the accumulator
with zero; the second and third instructions at $4002 and $4005 store the
contents of the accumulator to the dual 7-segment and 8-bit displays,
respectively; the fourth instruction at $4008 increments the contents of the
accumulator; and the fifth instruction at $4009 causes the program to jump
back to the first store instruction in the loop.
5-20 Bebop BYTES Back
Enter
Increment
$4008 $80 $80 = Increment the accumulator
ACC
Modify the address switches on the main switch panel such that they
represent address $4000, click the switch panel’s Run button, and watch the
two displays counting up (each in their own way) from $00 to $FF.
Remember that when the count reaches $FF, the next increment instruction
will cause the accumulator to overflow and to roll around to $00 again.
Also remember that if the display is counting too fast, you can modify the
speed of the Beboputer’s system clock as discussed in the previous chapter.
And finally, you may wish to bid a fond farewell to the main switch panel
and the paper tape reader, because we’re soon to discover more
sophisticated ways of doing things. So, with tears rolling down your cheeks,
power-down the Beboputer by clicking the ON/OFF switch on the main
switch panel, then exit this laboratory by clicking the File item on the
project window’s menu bar followed by the Exit entry in the resulting pull-
down menu.
Seven-Segment Displays 5-21
Quick quiz #5
1) What is the average life span of any electronic product in an
environment containing small children?
2) What are diodes and what do they do?
3) What is the main difference between standard diodes and LEDs?
4) What are the main differences between incandescent light bulbs
and LEDs?
5) Why did early digital calculators and watches use red LEDs as opposed
to other colors?
6) In the case of our simple 8-bit displays, why did we use red and green
LEDs to represent logic 0 and logic 1 values respectively?
7) Are 7-segment displays always preferable to simple 8-bit displays?
8) What do the terms “common anode” and “common cathode” mean in
the context of diodes?
9) The single decoded 7-segment display we used in this lab was said to be
based on a common cathode configuration; hence the truth table shown
in Figure 5.17. Create the corresponding truth table for a single decoded
7-segment display based on a common anode configuration.
10) What are the relative advantages and disadvantages of undecoded and
decoded 7-segment displays?
5-22 Bebop BYTES Back
Chapter
6
Primitive Logic Gates
In this chapter we will discover:
NOT gates
▼
AND, OR, and XNOR gates
▼
NAND, NOR, and XNOR gates
▼
Tri-state gates and bi-directional buffers
▼
How to use primitive gates to build other
functions
▼
The difference between combinational
and sequential functions
6-2 Bebop BYTES Back
Contents of Chapter 6
Are the hairs on the back of your neck starting to quiver? ...... 6-3
NOT gates ............................................................................................................................................................. 6-3
AND, OR, and XOR gates ............................................................................................................. 6-5
NAND, NOR, and XNOR gates ............................................................................................... 6-5
Using primitive gates to build other functions ................................................. 6-6
Decoders .................................................................................................................................................... 6-7
Multiplexers .............................................................................................................................................. 6-8
Latches and flip-flops .............................................................................................................. 6-9
Tri-state gates .................................................................................................................................... 6-10
Quick quiz #6 ................................................................................................................................................. 6-13
Primitive Logic Gates 6-3
Are the hairs on the back of your neck
starting to quiver?
Previously we noted that digital computers are essentially comprised of a
large number of transistor-based switches, which are connected together in
such a way that they can perform logical and arithmetic functions. The
problem is that a computer can contain a huge number of these switches,
and comprehending the inner workings of a computer would be almost
impossible if we were to continue to think of it in these terms. The solution
is to view things at higher levels of abstraction, the first such level being a
group of logical elements called primitive logic gates. Fairly soon we’re
going to plunge into the bowels of the computer’s central processing unit
(CPU), and having even a rudimentary understanding of what logic gates are
and what they do will definitely help us on our way.
Several different families of transistors are available to designers and,
although the actual implementations vary, each can be used to construct
primitive logic gates. One transistor family is known as metal-oxide
semiconductor field-effect transistors (MOS FETs). This is a bit of a mouthful
to say the least, so we’ll try to avoid saying it again. The main point as far
as we’re concerned is that there are two basic flavors of these devices called
PMOS and NMOS transistors. There are a number of ways in which these
transistors can be used to create primitive logic gates, but one very common
technique, referred to as complementary metal-oxide semiconductor
(CMOS), employs both PMOS and NMOS transistors connected together in
a complementary manner.
If the hairs on the back of your neck are starting to quiver and you’re
beginning to break out into a cold sweat, then settle down and try to relax,
because this is going to be a lot simpler than you fear. So make yourself
comfortable, and we’ll begin ......
NOT gates
As we’ve already mentioned, when dealing with logic gates inside a
computer, rather than thinking in terms of voltage levels or in such terms as
OFF and ON, we generally prefer the more abstract concepts of logic 0 and
logic 1. However, when we move to consider an actual implementation, it
once again becomes necessary to view signals as real voltage values. On
this basis, and because these are reasonably common, we will assume that
our logic 0 and logic 1 values actually equate to 0 volts and +5 volts,
6-4 Bebop BYTES Back
respectively. Bearing this in mind, consider one of the simplest logic gates,
which is called a NOT (Figure 6.1).
Logic 1
(+5 Volts)
NOT Tr 1
a y
a y a y
0 1
1 0
Tr 2
(a) Symbol (b) Truth table Logic 0
(0 Volts)
(c) Implementation
a y Tr 1 Tr 1
a=0 y=1 a=1 y=0
a y
0 1 Tr 2 Tr 2
1 0 Logic 0 Logic 0
(0 Volts) (0 Volts)
a AND a OR a XOR
y y y
b & b | b |
a b y a b y a b y
0 0 0 0 0 0 0 0 0
0 1 0 0 1 1 0 1 1
1 0 0 1 0 1 1 0 1
1 1 1 1 1 1 1 1 0
a b y a b y a b y
0 0 1 0 0 1 0 0 1
0 1 1 0 1 0 0 1 0
1 0 1 1 0 0 1 0 0
1 1 0 1 1 0 1 1 1
~y[2]
select[1:0] ~y[3:0] |
0 0 1 1 1 0
~y[1]
0 1 1 1 0 1 |
1 0 1 0 1 1
1 1 0 1 1 1 ~y[0]
|
The 00, 01, 10, and 11 annotations on the decoder symbol represent the
various patterns of 0s and 1s that can be presented to the select[1:0] inputs,
and are used to associate each input pattern with a particular output.
The truth table shows that when an output is selected it is asserted to a
logic 0 value (unselected outputs therefore assume logic 1 values). Due to
the fact that its outputs are asserted to logic 0s, this type of function is said
to have active-low outputs (similar functions can be created with active-high
outputs). The active-low nature of the outputs is also indicated by the
bobbles on the symbol and the tilde characters in the names of the output
signals.
For the purposes of these discussions we are not overly concerned with the
method by which we extracted the Boolean (logic) equations from the truth
table to obtain the actual logic gates used to implement this function,(1) but
rather with the fact that only a few primitive gates are required to do the
job. It would also be quite easy to create larger versions of this function,
such as 3:8 (three-to-eight) or 4:16 (four-to-sixteen) decoders. In fact, you
may recall that we used just such a 4:16 decoder to implement our memory
address decoding in Chapter 2, while the special decoders used to drive our
decoded 7-segment displays in Chapter 5 could be constructed using similar
techniques.
1
This is just one of a number of possible implementations.
6-8 Bebop BYTES Back
Multiplexers
Another function of interest is a multiplexer, which uses a binary value, or
address, to select between a number of inputs, and then conveys the data
from the selected input to the output. For example, consider a 2:1
(two-to-one) multiplexer (Figure 6.6).
The 0 and 1 annotations on the multiplexer symbol represent the values that
can be presented to the select input, and are used to indicate which data
input will be selected.
The ? characters in the truth table are used to indicate don’t care values.
When the select input is presented with a logic 0, the output from the
multiplexer depends only on the value on dataA, and we don’t care about
the value on dataB. Similarly, when the select input is presented with a
logic 1, the output from the multiplexer depends only on the value on
dataB, and we don’t care about the value on dataA.
As for the decoder, we are not particularly concerned with the way in
which we extracted the Boolean equations from the truth table to obtain the
actual logic gates used to implement the multiplexer,(2) but rather with the
fact that only a few gates are necessary to perform the task. It would also be
quite easy to create larger versions of this function, such as 4:1 (four-to-one)
or 8:1 (eight-to-one) multiplexers. Although we haven’t explicitly discussed
any cases where we’ve used multiplexers thus far, you can bet your little
cotton socks that we’ll be tripping all over the little rascals in the not-so-
distant future.
2
This is just one of a number of possible implementations.
Primitive Logic Gates 6-9
Latches and flip-flops
Digital logic functions may be categorized as being either combinational
(otherwise called combinatorial) or sequential. In the case of a
combinational function, the logic values at that function’s outputs are
directly related to the current combination of values on its inputs (decoders
and multiplexers fall into this category). By comparison, the logic values on
a sequential function’s outputs depend not only on its current input values,
but also on previous input values; that is, the output values depend on a
sequence of input values.
The bottom line is that sequential functions can act as memory elements.
Although there are many flavors of these functions, we shall concentrate our
attentions on only two: D-type latches and D-type flip-flops (Figure 6.7).
D-type latch D-type flip-flop
data data
q q
~enable clock
Tri-state gates
And finally (for the moment), we come to a special class of functions known
as tri-state gates, whose outputs can adopt three values: logic 0, logic 1,
and logic Z (hence the “tri-state” appellation). In fact all of the primitive
3
It is also possible to “hand-craft” latches and flip-flops that require very few transistors.
Primitive Logic Gates 6-11
gates discussed thus far can be constructed so as to support tri-statable
outputs, but at this time we’re only interested in a simple function called a
tri-state buffer (Figure 6.8).
Symbol Implementation Logic 1
(+5 Volts)
data y
| Tr 1
~enable OR y
data
~enable | Tr 2
~enable data y
NOR Logic 0
0 0 0 (0 Volts)
0 1 1
1 ? Z
Figure 6.8: Tri-state buffer
presented with a logic 0, transistor Tr2 is turned ON, thereby connecting the
output y to logic 0. Similarly, if the data input is presented with a logic 1,
transistor Tr1 is turned ON, thereby connecting the output y to a logic 1.
As usual, don’t worry if this all appears somewhat confusing at first. In
reality we are not particularly concerned with the way in which these gates
are constructed, but only in what they do and what they can be used for.
The important point here is that when the tri-state buffer is disabled it is
effectively disconnected from its own output. This means that the outputs
from multiple tri-state buffers can be connected to the same wire, so long as
only one of them is enabled at any particular time. In fact, if you return to
our discussions in Chapter 2, you will realize that tri-state gates are used to
buffer any device that can drive signals onto the data bus, such as the CPU,
RAMs, ROMs, and input ports. The control signals generated by the CPU
dictate which particular device is enabled (can “talk”) at any particular time,
while the rest of the devices are automatically disabled (forced to “listen”).
Only one question remains. It is easy to see how tri-state buffers can be
used by devices such as ROMs or input ports to allow them to drive signals
onto the data bus or to isolate them from the bus. But what about devices
such as RAMs or the CPU itself, which, in addition to being able to drive
signals onto the data bus, also have to be capable of reading signals from
the bus? The answer is to use two tri-state gates connected in a back-to-back
configuration, thereby creating a bidirectional buffer (Figure 6.9).
By disabling the output buffer and enabling the input buffer, the device can
accept a signal from the data bus (this signal would then be used to drive
other logic gates inside the device).
Similarly, by enabling the
Device System
output buffer and
~input_enable
input
disabling the input buffer,
buffer the device can drive a
input signal
into the device signal onto the data bus
data bus bit (this signal would then be
acquired by whichever
output signal
device the CPU decides
from the device should be in its listening
output
buffer mode).
~output-enable
If both the input and
Figure 6.9: Bidirectional buffer output buffers are
disabled, then the device
Primitive Logic Gates 6-13
is effectively disconnected from the data bus; that is, it is neither “talking” or
“listening.” Finally we should observe that the bidirectional buffer illustrated
in Figure 6.9 only applies to a single bit on the data bus. Remembering that
our hypothetical system’s data bus is eight bits wide, each device connected
to the bus would require eight of these buffers. Note however that all eight
of the ~input-enable signals (inside the device) would be connected together,
and would be driven by a single control signal from the outside world
(similarly for the ~output-enable signals).
Quick quiz #6
1) Why is it preferable to view digital logic in terms of primitive gates as
opposed to transistors?
2) What does a NOT gate do and how does it do it?
3) What does a bobble on the output of the symbol for a primitive gate
indicate?
4) What is the difference between an AND gate and a NAND gate?
5) Describe the similarities and differences between decoders and
multiplexers.
6) Create the symbol and truth table for a 3:8 decoder with active-high
outputs.
7) Describe the difference between combinational and sequential
functions.
8) Create the symbols and truth tables for a D-type latch with an active-
high enable and a D-type flip-flop with a negative-edge clock.
9) What is a tri-state buffer used for and what does a logic Z state
represent?
10) Create symbols and truth tables for tri-statable OR and NOR gates.
For those who are interested and want to know more about logic gates,
extracting and using Boolean equations, and seafood gumbo; these topics are
presented in greater detail in this book’s companion volume: Bebop to the
Boolean Boogie (An Unconventional Guide to Electronics). ISBN 1-
878707-22-1
6-14 Bebop BYTES Back
Chapter
7
Binary Arithmetic
In this chapter we will discover:
The difference between signed and
unsigned binary numbers
▼
How to perform binary additions
▼
How to use primitive logic gates to
construct an adder function
▼
Nine’s, ten’s, one’s, and two’s
complement numbers
▼
How to perform binary subtractions using
complement techniques
▼
The ancient process called
“ Casting out the nines ”
7-2 Bebop BYTES Back
Contents of Chapter 7
Cease your whimpering and whining ........................................................................ 7-3
Unsigned binary numbers .............................................................................................................. 7-3
Binary addition ............................................................................................................................................... 7-4
Binary subtraction ................................................................................................................................... 7-10
Signed binary numbers .................................................................................................................. 7-13
Be afraid, be very afraid ............................................................................................................. 7-17
Quick quiz #7 ................................................................................................................................................. 7-17
Binary Arithmetic 7-3
Cease your whimpering and whining
As fate would have it, we’ve arrived at the point in our discussions where
we must consider the way in which a computer visualizes and manipulates
numbers. Now settle down, wipe your nose, and cease your whimpering
and whining, because there’s simply no other way to proceed; if we don’t
tackle this subject and show it who’s in charge here and now, then it will
sneak up behind us while we’re not looking and give us an unpleasant
surprise. On the brighter side, we are only going to consider addition and
subtraction in this chapter – we’ll return to look at multiplication and
division some way down the road.(1)
2 10
0000 0 0 1 02 = 2 10
As you may 0000 0 0 1 12 = 3 10
recall from our 0000 0 1 0 02 = 4 10 Figure 7.1: Using the data bus to
: : :
discussions in 1111 1 1 0 12 = 253 10
represent unsigned binary numbers
Chapter 2, the right- 1111 1 1 1 02 = 254 10
1111 1 1 1 12 = 255 10
hand bit is known
1
Multiplication and division are discussed in Appendix G.
7-4 Bebop BYTES Back
Binary addition
Binary numbers may be added together using a process identical to that
used for decimal addition. However, even though such additions are
basically simple, they can prove confusing to a rookie, so we’ll take things
one step at a time. First of all, consider a series of simple one-digit additions
using the decimal system with which we’re all comfortable (Figure 7.2).
As we would expect, there’s
nothing too surprising here. The
0 0 8 8 only special case that has to be
+ 0 + 6 + 0 + 6
taken into account is when the
= 0 = 6 = 8 = 1 4 result of an addition overflows
the maximum value that can be
Figure 7.2: Simple 1-digit
decimal additions
stored in a decimal digit, which
happens in the last example of
8 + 6.
In fact we’re so familiar with the decimal system that you probably didn’t
even blink when you glanced at this case, but it’s key to what is to come.
One way to view this last example is to say it aloud in the way of students
at school, which is: “Eight plus six equals four, with one carried forward
into the next column.” Now hold this thought as we consider some one-
digit additions in binary (Figure 7.3).
Note that since we explicitly
stated that these are binary 0 0 1 1
numbers, it is permissible to + 0 + 1 + 0 + 1
omit the customary ‘2’ = 0 = 1 = 1 = 1 0
subscripts. The “odd man out”
Figure 7.3: Simple 1-digit
in this figure is the last binary additions
example, which may have
caused you to pause for thought, but which really isn’t too different from its
decimal equivalent. The point to remember is that a binary digit can only
Binary Arithmetic 7-5
represent two values (0 and 1), which means that any result greater than 1
causes a carry into the next column. Thus, 1 + 1 = 102 (where 102 in binary
equals 2 in decimal); or, to put this another way: “One plus one in binary
equals zero, with one carried forward into the next column.”
Now consider what happens when we extend this concept to multi-digit
binary numbers. Using the same technique that we’d employ for a decimal
addition, the two least-significant bits are added together to give a sum and,
possibly, a carry-out to the next stage. This process is repeated for the
remaining bits progressing towards the most-significant. For each of these
remaining bits there may be a carry-in from the previous stage and a carry-
out to the next stage. To fully illustrate this process, consider the step-by-
step addition of two 4-bit binary numbers (Figure 7.4).
Decimal
Bit 0 Bit 1 Bit 2 Bit 3
equivalent
1 0 0 0 10 00 10 0 0 1 0 00 8 10
+ 0 1 1 0 + 01 10 + 01 1 0 + 0 1 10 + 6 10
= 0 = 10 = 1 1 0 = 1 1 10 = 1 4 10
00 11 0 0 1 1 00 11 0 0 1 1 3 10
+ 10 11 + 1 0 1 1 + 10 11 + 1 0 1 1 + 1 1 10
= 0 = 1 0 = 1 10 = 1 1 1 0 = 1 4 10
0 11 1 0 1 1 1 0 11 1 0 11 1 7 10
+ 0 11 1 + 0 1 1 1 + 0 11 1 + 0 11 1 + 7 10
= 0 = 1 0 = 11 0 = 1 11 0 = 1 4 10
In this case we’re really caught between a rock and a hard place, because
the addition of each of the first three bit positions results in a carry to the
next column (bit 0 is 0 + 1 + 1 = 102, bit 1 is 1 + 1 + 1 = 112,
bit 2 is 1 + 1 + 1 = 112, and bit 3 is 1 + 0 + 0 = 1).
Now let’s change direction slightly to consider how we might implement an
addition function with our primitive logic gates. Our first task is to
determine how to handle simple 1-digit additions. A designer might proceed
to do this by (a) drawing a symbol, (b) writing out the truth table associated
with the symbol, and (c) extracting the logical equations from the truth table
and implementing them as primitive gates (Figure 7.7).
The signals cin and cout are common abbreviations for “carry-in” and
“carry-out,” respectively. As usual, the primitive gates used here illustrate
only one of a variety of possible implementations. Also as usual, we are not
overly concerned here with the method by which the designer would
extract the Boolean (logic) equations from the truth table and decide which
gates to use. In fact the only thing that we are interested in here is the way
in which we can use primitive logic gates to perform arithmetic operations.
Binary Arithmetic 7-7
Let’s extend this concept to perform 4-digit binary additions; for example,
assume that we wish to add two 4-bit binary numbers called a[3:0] and
b[3:0] (Figure 7.8).
cout cin a b sum cout
One way to achieve this is to 0 0 0 0 0
connect four of our 1-digit a co 0 0 1 1 0
a 0 1 0 1 0
adders together in series. Note sum
s 0 1 1 0 1
that we’ve connected the first b 1 0 0 1 0
b
digit’s carry-in input to a logic ci 1 0 1 0 1
0, which means that this input cin 1 1 0 0 1
doesn’t have any effect on the 1 1 1 1 1
a b a b a b a b
Bit 3 Bit 2 Bit 1 Bit 0
co ci co ci co ci co ci
s s s s
This form of adder is known as a ripple adder, because the result ripples
down through the chain. Consider what happens when we present two
binary numbers to the a[3:0] and b[3:0] inputs. The bit 0 adder immediately
starts to add a[0] and b[0], the bit 1 adder immediately starts to add a[1] and
b[1], the bit 2 adder immediately starts to add a[2] and b[2], and the bit 3
adder immediately starts to add a[3] and b[3]. The problem is that the bit 1
adder cannot generate the correct result until the bit 0 adder has finished its
calculation and generated its carry-out signal. Similarly, the bit 2 adder has
to wait for the bit 1 adder to finish its calculation and generate its carry-out
signal, and so on down the line.
Thus, the s[1], s[2], and s[3] outputs may toggle backwards and forwards
between logic 0 and logic 1 for a while as the intermediate carry signals
propagate through the chain. The end result is that, even though adders of
this type can perform calculations in a few millionths of a second, they
remain relatively slow in computer terms and their delay increases as more
stages are added. (We could redesign our adder using additional logic gates
such that it generated the result for all four bits simultaneously, but this is
beyond the scope of this book.) Now bearing in mind what we’ve just
learned, let’s consider one final 4-digit binary addition (Figure 7.9).
Bit 0 Bit 1 Bit 2 Bit 3 Decimal
equivalent
1 1 1
0 11 0 0 1 1 0 0 11 0 0 11 0 6 10
+ 1 10 0 + 1 1 0 0 + 1 10 0 + 1 10 0 + 1 2 10
= 0 = 1 0 = 01 0 = 0 01 0 = 2 10 ?
4-bit adder 01 10
+ 11 00
a[7:4] %0011 a[7:0] cout s[3:0] = 1 00 10
a[3:0] %0110 %0011 0110 cout
cin
b[7:4] %0001 b[7:0] a[7:4] b[7:4] cin
b[3:0] %1100 %0001 1100 1 1 1
4-bit adder 0 01 1
+ 0 00 1
s[7:4] %0101 s[7:0] s[7:4]
cout = 0 0 10 1
s[3:0] %0010 %01010010
(discard)
discard
The CPU reads the least significant 4 bits from the numbers in memory,
a[3:0] and b[3:0], and adds them together. For the purposes of this first
addition, the CPU ensures that the carry-in into the adder is forced to a
logic 0. The result from this addition is written back into the memory as
s[3:0], while the value on the carry-out signal is stored inside the CPU in a
1-bit register (either a latch or a flip-flop), which is known as the carry flag.
The CPU next reads the most significant 4 bits from the numbers in
memory, a[7:4] and b[7:4], and adds these together using the same adder
function (that is, the same physical logic gates) as for the first addition.
However, in this case the CPU ensures that the carry-in into the adder is
driven by the carry flag register, which currently contains the carry-out from
the first addition. The result from this addition is written back into the
memory as s[7:4]. Note that the value on the carry-out signal from this
second addition would also be stored in the carry flag register, thereby
overwriting its existing contents. Thus, if we have the ability to store the
carry-out from each 4-bit addition and to use that value as a carry-in to a
subsequent addition, then we can effectively represent numbers of any size
by partitioning them into 4-bit quantities.
Of course our Beboputer is actually based on an 8-bit data bus, which
means that handling 8-bit binary numbers is not a problem. However,
should we wish the Beboputer to handle any larger values, such as 16- or
32-bit numbers, then we can do so using similar techniques to those
described here.
Binary subtraction
Unsigned binary numbers may be subtracted from each other using a
process identical to that used for decimal subtraction. However, for reasons
of efficiency, computers rarely perform subtractions in this way, but instead
these operations are typically performed by means of complement
techniques.
There are two forms of complement associated with every number system,
the radix complement and the diminished radix complement, where the
term “radix” refers to the base of the number system. Under the decimal
(base-10) system, the radix complement is also known as the ten’s
complement and the diminished radix complement is known as the nine’s
complement. First consider a decimal subtraction performed using the
nine’s complement technique – a process known in ancient times as
“Casting out the nines” (Figure 7.12).
Binary Arithmetic 7-11
The standard way of Standard subtraction Nine's complement equivalent
performing this operation
would be to subtract the 64 7 99 9 64 7
− 28 3 − 28 3 + 71 6
subtrahend (283) from the = 36 4 = 71 6 = 1 36 3 end-
around-
minuend (647), which, as 1 carry
Take nines
in this example, may require the complement 36 4
because we won’t ever be doing one – simply take our word as to the
result.) In order to perform the equivalent operation using the one’s
complement technique, each of the digits of the subtrahend is first
subtracted from a 1. The resulting one’s complement value is added to the
minuend, then an end-around-carry operation is performed.
Standard subtraction One's complement equivalent
0 0 11 10 01 1 1 1 1 1 1 11 0 0 1 1 1 0 0 1
− 0 0 01 11 10 − 0 0 0 1 1 1 10 + 1 1 1 0 0 0 0 1
end-
= 0 0 01 10 11 = 1 1 1 0 0 0 01 = 1 0 0 0 1 1 0 1 0
around
1 carry
Take ones
complement 0 0 0 1 1 0 1 1
57 10 − 30 10 = 27 10
00 11 10 01 1 0 0 0 0 00 00 00 1 1 1 0 0 1
− 00 01 11 10 − 0 0 0 1 11 10 + 11 1 0 0 0 1 0
drop
= 00 01 10 11 = 1 1 1 0 00 10 = 1 00 0 1 1 0 1 1
any
Take twos
00 0 1 1 0 1 1 carry
complement
57 10 − 30 10 = 27 10
Value to be
0 0 0 1 1 1 1 0
complemented
Copy from LSB up to
and including the first 1
- - - - - - 1 0
values represented by the MSB = "Sign bit" - 27 = -128's column = data[7] = MSB
+ 26 = +64's column = data[6]
remaining bits, an 8-bit signed + 25 = +32's column = data[5]
binary number can be used to + 24 = +16's column = data[4]
+ 23 = +8's column = data[3]
represent values in the range + 22 = +4's column = data[2]
-12810 through +12710. + 21 = +2's column = data[1]
+ 20 = +1's column = data[0] = LSB
To illustrate the
differences between Wires
the sign-magnitude
and signed binary 00000000 =
00000001 =
2
0
1
10
2 10
formats, consider a 00000010 = 2
2 10
: : :
positive sign- 0 1 1 1 1 1 1 0 = 126
2 10
magnitude decimal 0 1 1 1 1 1 1 1 = 127
2 10
1 0 0 0 0 0 0 0 = -128
number and its 2
1 0 0 0 0 0 0 1 = -127
10
2 10
negative equivalent; 1 0 0 0 0 0 1 0 = -126
2 Figure 7.17: Using the data bus to
10
: : :
for example, +27 and 1 1 1 1 1 1 1 0 = -2 represent signed binary numbers
2 10
digits are identical for both cases and only the sign changes. Now consider
the same values represented as signed binary numbers (Figure 7.18).
Positive value Negative value
+27 10 −27 10
0 0 0 1 1 0 1 12 1 1 1 0 0 1 0 12
In this case the bit patterns of the two binary numbers are very different.
This is because the sign bit represents an actual quantity (-12810) rather than
a simple plus or minus; thus, the signed equivalent of -2710 is formed by
combining -12810 with +10110.
At a first glance the signed binary concept may appear to be an outrageously
complex solution to a fairly simple problem. In addition to representing an
asymmetrical range of negative and positive numbers (-12810 through +12710
in the case of an 8-bit value), the way in which these values are formed is,
to put it mildly, alien to the way we’re used to thinking of numbers. Why
then, you may ask, don’t we simply use the most-significant bit to represent
the sign of the number (for example, 0 = positive and 1 = negative) and
leave it at that?
Binary Arithmetic 7-15
Well as you may expect, there’s reason behind our madness. First, if we did
use the most significant bit to represent only the sign of the number, then
such numbers would accommodate both +0 and -0 values. Although this
may not seem like a particularly significant point, computers are essentially
dumb and it would introduce complications in recognizing whether or not a
given value was less than zero or equal to zero. But there’s a lot more to
signed binary numbers than this. Now pay attention, because this is the
clever part; closer investigation of the two binary values in Figure 17.18
reveals that each bit-pattern is the two’s complement of the other! To put
this another way; taking the two’s complement of a positive signed binary
value returns its negative equivalent, and vice versa (the only problem with
this scheme being that, due to the asymmetrical range, the largest negative
number can’t be negated; for example, in the case of an 8-bit signed binary
number, you can’t negate -12810 to get +12810, because the maximum
positive value that’s supported is +12710).
The end result of all this is that using signed binary numbers (which may be
referred to as two’s-complement numbers) greatly reduces the complexity of
operations within a computer. To Decimal sign-
Signed binary
illustrate why this should be so, magnitude
consider one of the simpler 5 7 00 11 10 01
operations: addition. Compare the + 3 0 + 00 01 11 10
following additions of positive and = 8 7 = 01 01 01 11
negative decimal values in sign-
magnitude form with their signed 5 7 00 11 10 01
binary counterparts (Figure 7.19). + −3 0 + 11 10 00 10
= 2 7 = 00 01 10 11
First examine the standard decimal
calculations – the one at the top is −5 7 11 00 01 11
easy to understand, because it’s a + 3 0 + 00 01 11 10
straightforward addition of two = −2 7 = 11 10 01 01
positive values. However, even
though you are familiar with −5 7 11 00 01 11
decimal addition, you probably + −3 0 + 11 10 00 10
found the other three a little = −8 7 = 10 10 10 01
harder, because you had to Figure 7.19: Comparison of sign-magnitude
decide exactly what to do with versus signed binary additions
the negative values. By
comparison, the signed binary calculations on the right are all simple
additions, irrespective of whether the individual values are positive or
negative.
7-16 Bebop BYTES Back
2
For the sake of simplicity, only the case of a - b is discussed here. However, the operations
a - b, a - (-b), (-a) - b, and (-a) - (-b) are all performed in exactly the same way, by simply taking
the two’s complement of b and adding the result to a, irrespective of whether a or b represent
positive or negative values.
Binary Arithmetic 7-17
Be afraid, be very afraid
If all of the above seems too good to be true, you’re right – it is! One of the
biggest problems when using computers is mapping the numbers that we
use (which are potentially infinite) into the way a computer thinks about
them (which is as finite chunks).
Generally speaking these issues aren’t outrageously complicated, but they
do require a certain amount of lateral thought and mental gymnastics. So,
instead of washing our clothes in public at this moment in time, we’ll
postpone any in-depth discussions for the nonce and return to consider this
topic in more detail in Appendix G.
Quick quiz #7
1) Why do digital computers use the binary number system?
2) Why are numbers in a computer different from numbers written on a
piece of paper?
3) What is the difference between unsigned and signed binary numbers?
4) What range of numbers can be represented by a 4-bit field if it (a)
represents an unsigned quantity and (b) represents a signed quantity?
5) What is the two’s complement of 001101012?
6) What is the carry flag used for in binary additions?
7) Use two’s complement techniques to generate the result of
00110101 2 - 01001010 2.
8) Show how a 6-bit data bus could be used to represent signed and
unsigned binary values (yes, we really mean six bits).
9) What range of numbers could be represented by a 6-bit bus if we were
using a signed-magnitude format, as opposed to standard signed binary
numbers?
10) What are the advantages and disadvantages of standard signed binary
numbers, as compared to using a signed-magnitude format?
7-18 Bebop BYTES Back
Binary Arithmetic 7-19
Chapter
8
Rampaging around
a CPU
In this chapter we will discover:
The accumulator and its related instructions
▼
The grim truth about the origin of opcodes
▼
The status register: what it is and what it does
▼
The ALU: what it is and how it works
▼
The instruction register and the instruction
decoder
▼
The addressing logic, including the program
counter, index register, stack pointer, and
interrupt vector
▼
What subroutines are and what they do
▼
What addressing modes are and how
they’re used
8-2 Bebop BYTES Back
Contents of Chapter 8
Rampaging around a CPU ................................................................................................................ 8-3
The accumulator (ACC) .......................................................................................................................... 8-3
Introducing the implied, immediate, and absolute
addressing modes .................................................................................................................................... 8-16
The grim truth about the origin of opcodes ......................................................... 8-22
The status register (SR) and status flags ....................................................................... 8-24
The arithmetic-logic unit (ALU) .................................................................................................. 8-36
The “core” ALU .......................................................................................................................................... 8-37
Extending the “core” ALU to perform
subtractions and stuff ............................................................................................................... 8-41
Extending the “core” ALU to perform shifts and rotates ............ 8-46
Connecting the accumulator and data bus to the ALU .................. 8-50
Connecting the status register to the ALU and data bus ................ 8-55
Adding the instruction register (IR) and control logic ............................ 8-58
Adding the addressing logic ........................................................................................................ 8-60
The program counter (PC) ...................................................................................................... 8-63
The implied addressing mode .................................................................................. 8-64
The immediate addressing mode ....................................................................... 8-64
Temporary program counter A (TPCA) ................................................................ 8-65
The absolute addressing mode .............................................................................. 8-66
Unconditional jumps ................................................................................................................ 8-67
Conditional jumps ....................................................................................................................... 8-69
Temporary program counter B (TPCB) ................................................................... 8-71
The indirect addressing mode .................................................................................. 8-72
The index register (X) ........................................................................................................................ 8-74
The indexed addressing mode (abs-x) ....................................................... 8-75
The gory details of connecting the index register ..................... 8-78
The pre-indexed indirect and indirect
post-indexed addressing modes ........................................................................ 8-79
The stack pointer (SP) ..................................................................................................................... 8-82
Introducing subroutines ....................................................................................................... 8-86
The interrupt vector (IV) ............................................................................................................... 8-87
Our fingers were crossed behind our backs ......................................................... 8-88
Quick quiz #8 ......................................................................................................................................................... 8-89
Rampaging around a CPU 8-3
Rampaging around a CPU
Everything we’ve discussed thus-far has merely been limbering-up exercises
to prepare our mental muscles for the ordeals to come. Our real journey
starts here and now as we quiver at the brink, poised to hurl ourselves into
the bowels of the computer’s central
You are
processing unit (CPU) where we’ll rampage here
around to our heart’s content. Our path will
carry us on a rollicking roller-coaster ride
(thrill-seekers only need apply), commencing
at a slow crawl to lull us into a false sense of
security, followed by a screaming plunge into
the nether regions where we’ll disappear for a while ...... until we rocket
out of the other side, clutching our stomachs and gasping for more.
One of the first things we need to be able to do is to load data into the
accumulator; that is, to read a byte of data from the system’s memory (either
ROM or RAM) and place a copy of that data into the accumulator. This
8-4 Bebop BYTES Back
process overwrites any data that was already in the accumulator, but does
not affect the data in the memory location that’s being copied. Similarly, we
also need to be able to store the contents of the accumulator; that is, to
write a copy of the accumulator into a byte in the system’s memory (just the
RAM in this case, because the ROM is “read-only”). The process of storing
the accumulator overwrites any data that was already in the target memory
location, but does not affect the contents of the accumulator.
In addition to loading and storing, we also need the ability to perform a
variety of arithmetic, logical, and other miscellaneous operations on the
data contained within the accumulator. A selection of accumulator
operations we might wish to perform are summarized in Table 8.1.
1
See also the discussions on the carry flag later in this chapter.
Rampaging around a CPU 8-5
When engineers sit down to design a new CPU, one of their main tasks is to
decide which instructions it will be capable of performing, where these
instructions are referred to collectively as the instruction set. Selecting the
instructions that will comprise the instruction set is non-trivial, because
there are a variety of competing factors to be taken into account. In the case
of the Beboputer, we desired an instruction set with the following attributes:
a) To be as small and compact as possible, thereby making it easy to
understand, learn, and use.
b) To be comprehensive enough to adequately illustrate computer
concepts and create meaningful and understandable programs.
These criteria are obviously somewhat contradictory, so we will endeavor to
describe the various tradeoffs that we considered during the course of the
project. At this point it is probably worth noting that the Beboputer is, to all
intents and purposes, a real computer that the authors designed from the
ground up, and also that the Beboputer is conceptually very similar to
several early microprocessors dating from the mid- to late-1970s. The only
really unusual aspect of the Beboputer is that we implemented it in software
(as a program) rather than in hardware (as silicon chips).
Another aspect of the design process is assigning mnemonics to the various
instructions, where a mnemonic is an abbreviated name suggestive of the
operation to be performed; for example, LDA meaning “load the
accumulator” and STA meaning “store the accumulator.” Mnemonics serve
as an aid to memory, and allow us to document programs and communicate
their intent in an extremely efficient way. Once again there are tradeoffs,
such as the fact that we want our mnemonics to be as short as possible, but
we don’t want them to be cryptic or obscure.
Unfortunately, each type of computer has its own unique instruction set and
associated mnemonics. If all of this strikes you as being a minefield of
potential confusion, then you’re really beginning to get into the swing of
things, because ...... it is! As we progress through the remainder of the
book, we’ll highlight some of the reasons why computers are designed this
way, some of the problems that arise because computers are designed this
way, and some of the ways in which we get around the problems that arise
because computers are designed this way (phew!).
8-6 Bebop BYTES Back
AND OR XOR
& | ^
0 0 0 0 0 1 0 1 0 1 0 1 1 1 1 1 0 1 0 1 1 0 1 0
New Accumulator New Accumulator New Accumulator
(a) AND (b) OR (c) XOR
XOR
! NOT ^
Results are
identical
1 1 0 0 1 0 1 0 1 1 0 0 1 0 1 0
New Accumulator New Accumulator
(a) NOT (Not available) (b) Pseudo-NOT (using XOR)
AND
&
NAND
!& 0 0 0 0 0 1 0 1 ^ XOR
Intermediate Accumulator
1 1 1 1 1 0 1 0 1 1 1 1 1 0 1 0
Results are identical
New Accumulator New Accumulator
(b) Pseudo-NAND (using
(a) NAND (Not available)
AND and XOR)
OR
S3 Sets bit 3 |
Results are
identical
1 1 1 0 1 0 1 1 1 1 1 0 1 0 1 1
New Accumulator New Accumulator
(a) SET 'n' (Not available) (b) Pseudo-SET 'n' (using OR)
AND
Clears bit 6 C6 &
Results are
identical
1 0 1 0 0 0 1 1 1 0 1 0 0 0 1 1
New Accumulator New Accumulator
(a) CLR 'n' (Not available) (b) Pseudo-CLR 'n' (using AND)
Figure 8.8: The CLR ‘n’ instruction (which we don’t have)
Note that if the targeted accumulator bit was already a 0, then this AND
would have no effect. The end result is that, as we can easily achieve the
same effect as SET ‘n’ and CLR ‘n’ instructions using ORs and ANDs,
respectively, we decided that there was no pressing need to equip the
Beboputer with these instructions.
Old Accumulator
0 0 1 1 1 0 0 1
Pseudo-
NOT XOR
^
Old Accumulator
Intermediate
0 0 1 1 1 0 0 1 1 1 0 0 0 1 1 0 value in the
accumulator
Twos
2's comp +1 INCA
Results are
identical
1 1 0 0 0 1 1 1 1 1 0 0 0 1 1 1
New Accumulator New Accumulator
(a) COMP (Not available) (b) Pseudo-COMP (using
XOR and INCA)
(a) ROLC 1 1 0 0 1 1 1 0 1 0 0 1 1 1 0 x
x 1
(b) RORC 1 1 0 0 1 1 1 0 x 1 1 0 0 1 1 1
x 0
Figure 8.10: The ROLC and RORC (rotate through Carry) instructions
The rotate instructions are relatively simple to understand, with the possible
exception of the carry flag, which we haven’t formally introduced yet. For
the nonce, let’s simply assume that the carry flag is a 1-bit register that can
contain either a logic 0 or a logic 1 (which is indicated as an ‘x’ in this
figure). In the case of a ROLC, the original contents of bit[0] of the
accumulator move into bit[1], the original contents of bit[1] move into bit[2],
and so on down the line until we arrive at bit[7], which moves into the carry
flag, which, in turn, rolls back into bit[0]. Similarly, in the case of a RORC,
the original contents of bit[7] move into bit[6], the original contents of bit[6]
move into bit[5], and so forth until we arrive at bit[0], which moves into the
carry flag, which, in turn, rolls back into bit[7]. (We’ll return to these
instructions and the carry flag when we come to consider the status register
later in this chapter.)
right”). However, on the basis that the Beboputer only supports arithmetic
shifts, we decided to call our mnemonics SHL and SHR for simplicity
(Figure 8.11).
Original value Shift Resulting value
in accumulator in accumulator
(a) SHL 1 1 0 0 1 1 1 0 1 0 0 1 1 1 0 0
Discard Logic 0
(b) SHR 1 1 0 0 1 1 1 0 1 1 1 0 0 1 1 1
Discard
At this point you may be wondering whether the lack of logical shift
instructions will be detrimental to your ability to write useful programs and
your general standing in the community. As it happens you don’t need to be
overly concerned, not the least that arithmetic and logical shift lefts are
actually identical, because they both shift a logic 0 into bit[0] and they both
discard the original bit[7].(2) In fact the main reason we differentiate between
arithmetic and logical shift lefts is for aesthetic considerations (to counter-
balance the shift rights). By comparison, shift rights are slightly more
interesting (or not, depending on your point of view). In the case of an
arithmetic shift right (the type supported by the Beboputer), the most
significant bit of the accumulator (bit[7] in our case) is copied back into itself
– we’ll discuss the rationale behind this in a moment. In the case of a
logical shift right (which isn’t supported by the Beboputer), a logic 0 would
be shifted into bit[7]. Both flavors of shift right end up discarding bit[0].(3)
It now becomes obvious why we don’t need to worry too much about the
lack of a logical shift right instruction, because we can achieve exactly the
same effect by performing our arithmetic shift right followed by AND-ing the
result with 011111112, where this AND-ing operation has the effect of
clearing bit[7] to a logic 0 (return to our earlier discussions on the CLR n
instruction if you’re at all unsure of what we’re talking about).
Beyond the fact that they’re generally useful, one interesting facet of shift
left instructions is that they can be used to multiply the value in the
accumulator by two. For example, if the original contents of the
accumulator were 000110112 (27 in decimal), then performing a SHL would
2
To be perfectly honest, saying that we “discard bit[7]” is a bit of an overstatement, but we’ll
return to investigate this point when we come to consider the carry flag later in this chapter.
3
Once again, saying that we “discard bit[0]” is not strictly true as we shall come to see.
Rampaging around a CPU 8-15
result in the accumulator’s containing 001101102 (54 in decimal)
(Figure 8.12a).
This also works with negative (two’s complement) numbers; for example, if
the accumulator originally contained 111000102 (-30 in decimal), then
performing a SHL would result in 110001002 (-60 in decimal)
(Figure 8.12b).
Discard Logic 0
Discard Logic 0
In the same way that shift lefts can be used to multiply the value in the
accumulator by two, shift right instructions can be used to divide by two.
For example, if the original contents of the accumulator were 010101002
(84 in decimal), then performing a SHR would result in the accumulator
containing 001010102 (or 42 in decimal) (Figure 8.13a).
Discard
Discard
Memory Memory
In the case of the Beboputer’s instruction set, the $70 opcode instructs the
CPU to perform a SHL (“shift left”) on any data currently stored within its
accumulator. Thus, when the CPU “sees” this opcode, it immediately
realizes that it’s dealing with a one-byte instruction; that is, the CPU
inherently understands that this instruction is using the implied addressing
mode. (Don’t concern yourself with the reason why $70 means SHL at the
moment – we’ll come to consider this in a little while.)
Note that as soon as the CPU has read the opcode and performed the SHL
instruction, it automatically increments its address bus to point to the next
memory location (‘n + 1’ in this example).(4) Due to the fact that the CPU
understands $70 is a one-byte instruction, it will automatically assume that
the byte at location ‘n + 1’ is the first byte (the opcode) of a new instruction,
so it will read that byte and continue to do its cunning stuff.
4
To be perfectly honest, the CPU may well increment its address bus at the same time that its
performing the SHL, but you’ll find it easier to visualize these things as occurring
sequentially.
Rampaging around a CPU 8-19
instruction. So the first thing the CPU will do is read the opcode from the
memory location pointed to by its address bus (Figure 8.15).
Memory Memory
In the case of the Beboputer’s instruction set, the $38 opcode instructs the
CPU to OR the contents of the next memory location with the current
contents of the accumulator (and to store the result in the accumulator).
Thus, when the CPU “sees” the $38 opcode, it immediately realizes that it’s
dealing with a two-byte instruction; that is, the CPU inherently understands
that this instruction is an OR that’s using the immediate addressing mode.
(Once again, don’t concern yourself with the reason why $38 indicates an
OR using the immediate addressing mode for the moment; just take our
word for it.)
As soon as the CPU has read the opcode, it automatically increments its
address bus to point to the next memory location (‘n + 1’ in this example).
Because the CPU understands that $38 is a two-byte instruction, it will
automatically read the operand byte at location ‘n + 1’ (which happens to be
$66 in hexadecimal or %01100110 in binary for this example) and perform
the OR operation using this data.
As usual, as soon as the CPU has read the operand and performed the OR
instruction, it automatically increments its address bus to point to the next
memory location (‘n + 2’ in this example). Due to the fact that the CPU
understands $38 was a two-byte instruction, it will automatically assume
that the byte at location ‘n + 2’ is the first byte (the opcode) of a new
instruction, so it will read that byte and continue on its merry way.
might expect by now, the CPU will automatically assume that the byte
following a three-byte instruction is the first byte (the opcode) of a new
instruction, so it will read that byte and start the process all over again.
Enter
SHL
$4002 $70 $70 = Shift accumulator 1-bit left
(implied)
OR ADD
| +
Bit pattern Bit pattern or
or +8310 +14210or -11410
0 0 1 0 1 0 0 1 1 1 1 0 0 0 1 1 1 0
N flag New Accumulator N flag New Accumulator
(a) Logical operation (OR) (b) Arithmetic operation (ADD)
This status flag is called the negative flag because, if the value in the
accumulator is considered to be a signed (two’s complement) binary
number, then a logic 1 in the most-significant bit indicates a negative
number. Similarly, the reason this flag is sometimes referred to as the sign
flag is that the most-significant bit of a signed binary number is referred to as
the sign bit.
However, this leads us to an excruciatingly important point, which is that a
value stored in the accumulator can represent whatever we want it to at any
particular time. For example, in the case of the logical operation shown in
Figure 8.20a, we can regard the resulting value in the accumulator as being
either a simple bit pattern or a binary number representing +8310. Similarly,
in the case of the arithmetic operation illustrated in Figure 8.20b, even
though this is an arithmetic operation, we can still view the final value
stored in the accumulator as being a non-numerical bit pattern if we so
desire. Alternatively, we might wish to consider this value as representing
either the unsigned binary number +14210 or the signed (two’s complement)
binary number -11410.
The point is that the CPU doesn’t have a clue as to how we’re viewing the
contents of the accumulator at any particular time. All the CPU can do is
ensure that the negative status flag contains a copy of the most significant
bit in the accumulator. Thus, if we (the programmers) decide to consider the
value in the accumulator as representing a two’s complement binary
Rampaging around a CPU 8-27
number, then we may also say that a logic 1 in the negative flag indicates
that this number is negative. But if we decide to regard the value in the
accumulator as representing a simple bit pattern or an unsigned binary
number, then all we can say is that the negative flag contains a copy of the
most-significant bit in the accumulator and leave it at that.
SUB AND
- &
1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 1 1
Zero flag New Accumulator (010) Zero flag New Accumulator
(a) Arithmetic operation (SUB) (b) Logical operation (AND)
ADD ADD
+ +
0 1 1 0 1 0 0 1 1 1 0 0 0 0 1 1 1 0
Carry flag New Accumulator (21110) Carry flag New Accumulator (1410)
(a) No carry generated (b) Carry is generated
Figure 8.22: The carry status flag (carries from unsigned additions)
Remember that if the value in the accumulator is considered to represent an
unsigned binary number, then our 8-bit accumulator can represent values in
the range 010 through +25510. So if we add two numbers such as 18410 and
2710 together (Figure 8.22a), the result of 21110 can be accommodated in the
accumulator and the carry flag is cleared to logic 0, thereby saying: “It’s
false (not true) that this addition resulted in a carry out.”
By comparison, if we attempt to add two numbers such as 18410 and 8610
together (Figure 8.22b), the result of 27010 cannot be accommodated in the
Rampaging around a CPU 8-29
accumulator (whose 8-bit field would actually end up containing 1410). In
this case the carry flag is set to logic 1, thereby saying: “It’s true that this
addition resulted in a carry out.” Of course the way in which the carry flag
is subsequently employed is now in the hands of the programmer. For
example, a logic 1 in the carry flag can simply be used to indicate an error
condition along the lines of: “The accumulator wasn’t big enough to hold
the result generated by adding these two numbers.” The programmer could
then use this information to issue an error message or to pursue another
course of action. Alternatively, the carry flag can be used to allow the
programmer to perform multi-byte additions (which are discussed in more
detail in Appendix G). As a point of interest, if we consider the combination
of the 1-bit carry flag and the 8-bit accumulator to form a 9-bit field, then
the value 1000011102 does indeed represent 27010, which would be the
correct result from the operation in Figure 8.22b (and which is the basis for
multi-byte additions in the first place).
SUB SUB
- −
18410 - 8610 8610 - 18410
1 0 1 1 0 0 0 1 0 0 1 0 0 1 1 1 1 0
Carry flag New Accumulator (9810) Carry flag New Accumulator (15810)
(a) No borrow generated (b) Borrow is generated
Figure 8.23: The carry status flag (borrows from unsigned subtractions)
8-30 Bebop BYTES Back
(a) ROLC (Rotate left through carry) (b) RORC (Rotate right through carry)
Figure 8.24: The carry status flag’s relationship to the rotate instructions
As you may recall, in the case of a ROLC (“rotate left through the carry
bit”), the original contents of bit[0] of the accumulator move into bit[1], the
original contents of bit[1] move into bit[2], and so on down the line until we
arrive at bit[7], which moves into the carry flag, which, in turn, rolls around
into bit[0]. Similarly, in the case of a RORC (“rotate right through the carry
bit”), the original contents of bit[7] move right into bit[6], the original
contents of bit[6] move right into bit[5], and so forth until we arrive at bit[0],
which moves into the carry flag, which, in turn, rolls around into bit[7].
Although it may not be immediately obvious, performing our rotate
instructions through the carry flag in this way becomes extremely useful
should we wish to perform multi-byte rotates (classic examples of this occur
in our multiplication subroutines as described in Appendix G).
Logic 0
Figure 8.25: The carry status flag’s relationship to the shift instructions
In fact the relationship between the shift instructions and the carry flag is
somewhat similar to that of the rotates. Whichever bit conceptually “drops
off the end” from a SHL (“shift left”) or SHR (“shift right”) is copied into the
carry flag, which becomes extremely useful should we wish to perform
multi-byte shifts.
8-32 Bebop BYTES Back
ADD ADD
+ +
Overflow Overflow
0 1
flag flag
0 1 1 0 1 1 1 1 1 0 0 0 1 1 0 1
Carry Carry
0 New Accumulator (11110) 0 Old Accumulator (-11510)
flag flag
(a) ADD without overflow (b) ADD with overflow
Figure 8.26: The carry status flag’s (for signed arithmetic operations)
6
Although we said that the carry flag has “little meaning” with regard to arithmetic operations
on two’s complement numbers, we didn’t say it was “meaningless.” In fact the carry flag
does serve a useful purpose when we come to consider multi-byte operations on such
numbers (see also Appendix G).
Rampaging around a CPU 8-33
Remember that our 8-bit accumulator can be used to represent two’s
complement numbers in the range -12810 through +12710. Thus, if we add
two numbers such as 8410 and 2710 together (Figure 8.26a), the result of
11110 can be accommodated in the accumulator and the overflow flag is
cleared to logic 0, thereby saying: “It’s false (not true) that this addition
resulted in an overflow.” By comparison, if we attempt to add two numbers
such as 8410 and 5710 together (Figure 8.26b), the result of 14110 cannot be
accommodated in the accumulator (whose 8-bit field would actually end up
containing -11510). In this case the overflow is set to logic 1, thereby saying:
“It’s true that this addition resulted in an overflow.”
An important point to note here is that the carry flag was cleared to logic
zero in both of these examples. The reason for this is that if the values were
considered to be unsigned binary values, then both of the operations
actually generated the correct result. That is, the final value in the
accumulator only represents -11510 if we consider that value to be a two’s
complement number, but if we consider it to be an unsigned binary number
then the bit pattern does equate to 14110.
Although this may appear to be somewhat confusing at first, the underlying
mechanism is actually quite simple. Let’s return to the fact that the CPU
doesn’t know whether the programmer is considering the data values to
represent unsigned or two’s complement numbers. To resolve this
conundrum, we might visualize the CPU as saying:
a) “First I’ll regard these values as being unsigned binary numbers, so
I’ll perform an unsigned binary addition (or subtraction) and load
the carry flag accordingly.”
b) “Next I’ll regard the values as being two’s complement numbers, so
I’ll perform a two’s complement addition (or subtraction) and load
the overflow flag accordingly.”
Of course this isn’t the way in which the CPU actually works, because it
would be dreadfully inefficient to perform two operations every time we
wanted to add (or subtract) a pair of numbers. In reality the CPU only
performs a single addition (or subtraction), because the arithmetic processes
for unsigned and signed numbers are absolutely identical, excepting the way
in which the carry and overflow flags are set. The carry flag is simply treated
as an additional (ninth) accumulator bit, so any carry out from bit[7] of the
result is simply dumped into this flag. By comparison, the overflow flag is
generated as an XOR of any carry into, and out of, bit[7] of the result.
8-34 Bebop BYTES Back
However, we don’t really need to understand exactly how the overflow flag
is generated at this time (we will return to consider this later). For the
moment we need only understand that the overflow flag is used to indicate
when the result of an arithmetic operation using two’s complement numbers
produces a result that cannot be represented correctly. For example, if two
positive numbers are added together such that the result would be greater
than +12710, or if two negative numbers are added together such that the
result would be less than -12810, then the overflow flag would be set (this
latter case is sometimes referred to as an underflow). It’s also worth pointing
out that the overflow flag works in exactly the same way for both ADD and
SUB instructions, and it could care less whether or not the data values
represent positive or negative numbers. In every case the overflow flag will
be set to logic 1 if the result of the operation cannot be correctly
represented as a two’s complement number in the accumulator.
CMPA CMPA
:0]
F[7
Figure 8.28: The ALU is where the “number crunching” takes place
Note that the ALU is completely asynchronous, which means that it is not
directly controlled by the main system’s clock. As soon as any changes are
presented to the ALU’s data, instruction, or carry-in inputs, these changes
will immediately start to ripple through its logic gates and will eventually
appear at the data and status outputs.
Note that the CMP function shown here will eventually be used to
implement the CPU’s CMPA (“compare accumulator”) instruction that we
introduced in our discussions on the status register. The instruction-bit
patterns we might assign to these functions are not important for our
purposes at this time; suffice it to say that the five functions shown here
would only require three instruction bits. As we shall see, implementing a
core ALU to perform these tasks is really not too complex. First consider
how we could implement the AND function, which actually requires only
eight 2-input AND gates (Figure 8.29).
B[ B[7
A[0 0] :0]
B[ ]
A[1 1] & A[7
] :0]
& Aa
B[ nd
B[0 &
A[7 7] Aa ]
] nd
B[1
& ]
Aa
Aa nd
nd B[7
B[7 :0]
]
(a) Gate-level view (b) Abstract view
Figure 8.29: Implementing the ALU’s AND function using primitive gates
Similarly, the OR and XOR functions would only require eight 2-input OR
gates and XOR gates, respectively. Things are a little more complex in the
case of the ADD function, but not unduly so. Assuming we decide to use the
technique described in Chapter 7, then our basic 8-bit adder will require a
total of sixteen AND gates and twenty-four XOR gates, plus an additional
XOR gate to generate the overflow output.(7) Similarly, the CMP (compare) is
a little more complex than the primitive logical functions, but nothing that a
few handfuls of cunningly connected gates can’t handle. Thus, it wouldn’t
take us long to generate our five core functions as individual entities
(Figure 8.30).
Note that the Oadd (“overflow from the adder”) output from the ADD
function would be directly connected to the main O (“overflow”) output
coming out of the core ALU. However, the ADD function’s CIadd (“carry-in
to the adder”) input and COadd (“carry-out from the adder”) output would
not be connected to the core ALU’s CI (“carry in”) and CO (“carry-out”)
status flags, respectively, because we have other plans for these signals as
we shall see.
7
From our discussions earlier in this chapter, you will recall that the overflow output for an
8-bit adder can be generated by XOR-ing the carry-in and carry-out associated with bit[7] of
the result.
Rampaging around a CPU 8-39
B[7 B[7 B[7
A[7 :0] A[7 :0] A[7 :0]
:0] :0] :0]
D R
AN OR XO
Aa Ao Ax
nd rB[ orB
B[7 7:0 [7:
(a) AND :0] (b) OR ] (c) XOR 0]
B[7 B[7
:0] dd :0]
A[7 Cla A[7
:0] :0]
D P
AD CM
Ae
Aa qB
dd dd Ag
Oa add B[7 tB
CO (d) AND :0] (e) CMP
Figure 8.30: The Beboputer’s five core ALU functions in isolation
In the case of the CMP (“compare”) function, the A[7:0] and B[7:0] inputs are
considered to be unsigned binary numbers. On this basis, the AgtB output
will be driven to logic 1 if A[7:0] is greater than B[7:0], while the AeqB
output will be driven to logic 1 if A[7:0] is equal to B[7:0].
So at this stage in the proceedings we know how to implement the core
ALU functions in isolation (although admittedly we’ve skimped on some of
the nitty-gritty details). The next point to ponder is the means by which we
can “glue” them all together to form the ALU itself. One approach would be
to hurl a multiplexer into our cauldron of logic gates, stir things up a little,
and see what develops (Figure 8.31). ] :0
B[7
dd
Cla n
dd tio
Oa d ruc
ad D ins
t
CO AD
R
XO )
w ide
bits
OR e r (8
iplex
:0] ult
A[7 D M :0]
AN 4:1 F[7
P
CM Figure 8.31: The Beboputer’s five core
Ae ALU functions connected together
qB
Ag (but no status logic yet)
tB
8-40 Bebop BYTES Back
In this scenario we use two instruction bits (which can represent four
patterns of 0s and 1s) to control a 4:1 multiplexer, where each of the input
channels feeding the multiplexer is 8 bits wide. The A[7:0] and B[7:0] signals
are presented to all of the functions, but only the outputs from the function
of interest are selected by the multiplexer. The reason we only need a
4:1 multiplexer is that the fifth function, the CMP, only outputs status
information, but doesn’t generate any data as such.
The advantage of this multiplexer-based approach is that it’s easy to
understand, but, in fact, we would be unlikely to use it in a real-world
implementation. This is because we’re only interested in being able to
perform a single function at any particular time, so we would examine the
functions to find areas of commonality allowing us to share gates between
them. To put this another way, instead of having multiple distinct functions
feeding a multiplexer, we’d probably lose the multiplexer and “scrunch” all
of the functions together into one “super function,” thereby allowing us to
reduce the ALU’s total gate count and increase its speed. On the other
hand, there’s nothing intrinsically wrong with our multiplexer-based
technique, so we’ll stick with it for the purposes of these discussions.
So now we know how to implement the data-processing portion of our core
ALU, but we’ve yet to decide how we’re going to use the AgtB, AeqB, and
COadd signals, and also how we’re going to generate the CO, N, and Z
status outputs (Figure 8.32). ve
,
ti
ga nd gs
Ne ro, a ut fla LU
N ze rry-o re A
ca m co
fro
Z
2:1
Mu
L U t x
eA Zin CO
cor 2:1
ux in | Ae
qB Mu
x
4:1M d
ad t
CO tB lec
Ag Se
F[0
]
uts
F[7 o utp LU
] ta A
Da core
m
fro
] n
Complementor [7:0 uc
tio
block BB str
In
uff
] th'
st tus
[7:0 Ari Sta
AA
:0]
B[7
Extended U
ALU :0] AL
A[7 Core
:0]
Figure 8.33: Extending the core ALU for F[7
additions and subtractions
Before delving into this new complementor block, it may be best to
determine exactly what we want it to do. In the case of instructions such as
AND, OR, XOR, ADD, and ADDC, we want the new block to pass whatever
value is on the BB[7:0] inputs directly through to its outputs without any
modification. In the case of SUB and SUBC
Note that we are eventually instructions, our new block must negate the value
going to require our extended on the BB[7:0] inputs before passing it on to the
ALU to perform sixteen different
core ALU. Finally, in the case of instructions such
operations, which will therefore
require four instruction bits. We
as INCA and DECA, we want our new block to
can subsequently decode the generate the appropriate value to be added to, or
various multiplexer controls and subtracted from, the accumulator (which, as we
suchlike from these four bits previously stated, is going to be connected to the
inside the ALU. AA[7:0] inputs) (Figure 8.34).
d
de
d eco n
+1 ect ctio
$0
1 i t sel nstru ed
i cod
0 3-b rom e
f td on
} $0
0
s e lec tructi C}
[7:0 -2 it ins UB
BB $F
E
e) 2-b rom , S
f DC A}
s wid { AD , INC CA }
r bit CI DD DE
g ato r (8 0 {A B,
Ne lex
e
n 3:1
1{
S U
Co ultip uts
o Mu
x
mp p
lem 5: 1M 0]
i n LU Cla
dd
en B [7: ore A
tor To the c
blo Additional status logic in
ck the core ALU
One question that is probably on your lips is: “Why does our complementor
block only contain a negator (which generates the one’s complement of the
value on BB[7:0]) instead of a two’s complementor?” After all, we devoted a
lot of effort in Chapter 7 to sing the praises of two’s versus one’s
complement representations (“In four-part harmony and stuff like that”).(9)
Similarly, if a DECA operation is intended to subtract 1 from the contents of
the accumulator (which we’re going to connect to the ALU’s AA[7:0] inputs),
then why does this operation actually cause the multiplexer to select the
hard-wired value $FE, which equates to -2 in decimal. These questions are
related, and the answers will make you begin to appreciate the wily ways of
the CPU designer. But before we answer these points, let’s first review what
we know from Chapter 7:
i) We can perform a subtraction such as a - b by converting b into its
negative equivalent and then performing an addition; that is, another
way to represent a - b is to say a + (-b).
9
From the song Alice’s Restaurant by Arlo Guthrie
8-44 Bebop BYTES Back
f) So forcing the CIadd input to the adder in the core ALU to logic 1
means that the operation we’re actually performing is
a[7:0] + ((-b[7:0]) - 1) + 1
If we cancel out the -1 and +1 in step (f), then we’re left with an identical
expression to that shown in step (b), which is what we wanted in the first
place. It now becomes apparent why, in
d B C]
cod
e U Figure 34, we added the 3:1 multiplexer
,S ]
c t de ion DC INCA ] to the core ALU (Figure 8:37). This allows
e ct D A
i t sel nstru C l { A DD, D EC us to force the CIadd signal to a logic 1
i A
2-b rom 0{ B,
f 3:1 { SU for SUB and DECA operations. It also
Mu 1
dd x becomes apparent why we force the
Cla multiplexer in our complementor block
Figure 8.37: The multiplexer we
to select $FE (-2 in decimal) for the DECA
added to the core ALU operation; in this case, the logic 1 on the
CIadd signal means that the operation
actually performed by the extended ALU is A[7:0] -2 + 1, which equals
A[7:0] -1, which is what we want our DECA operation to do (phew!).(11)
11
There are a myriad ways to implement this sort of thing, this just happens to be the one we
chose.
Rampaging around a CPU 8-47
reattached to the extended ALU’s FF[7:0] outputs (the logic for the Z and N
status flags was illustrated in detail in Figure 8.32).
Instruction
0]
Complementor [7: Status
BB
block
uff
0] th' st
[7: Ari
AA
:0]
B[7
U
ed :0]
AL
end ore :0]
t
Ex LU A[7 C F[7
A
Shifter/Rotator
block
0]
[7:
FF
Figure 8.38: Extending the core ALU for shifts and rotates
Before leaping headfirst into this new block, there’s one more point we
should discuss. As you may recall, the multiplexer in our complementor
block had one set of inputs connected to a hard-wired value of $00 (see
figure 8.34), but we never got around to explaining why. Well remember
that we’re going to connect our accumulator to the extended ALU’s AA[7:0]
inputs. If we decide to perform a shift or rotate operation, then we want the
values on the AA[7:0] inputs to be passed through the core ALU and fed
directly into the shifter/rotator block without modification. But we know that
the core ALU is always going to try to perform some sort of function on this
data; after all, that’s what it’s there for! In fact there are a number of
different ruses we could employ to solve this problem, but the technique we
decided to use is as follows:
a) Assume that the extended ALU is presented with a SHL, SHR, ROLC,
or RORC instruction.
b) This instruction is decoded in such a way that the multiplexer in the
complementor block selects the hard-wired $00 value, while the core
ALU is instructed to perform an ADD without carry (CIadd = logic 0).
c) Thus, the core ALU simply adds $00 to the value on the AA[7:0]
inputs, which has no effect whatsoever! The unmodified AA[7:0]
value is then handed on to the shifter/rotator block for it to perform
the real operation (pretty cunning, huh?).
8-48 Bebop BYTES Back
Now let’s dive into the shifter/rotator block itself. In the case of instructions
such as AND, OR, ADD, and SUB, we want our new block to simply pass
whatever comes out of the core ALU straight through without modification.
It is only in the case of a shift or rotate instruction that the new block comes
into play (Figure 8.39).
}
ect S HL }
Note: All multiplexer select signals are sel 0{ OLC
it {R
decoded from the ALU's instruction bits 1-b 2:1 Cl
om bit0
0 ] fr U elect
: it s
:1] F[7 re AL 2-b
F[7 co
bit7
bit0
x 0]
Mu F[6:
3:1
bit7
0]
[7:
2:1 FF
]
} F[7 lec
t
HR CI se
{S } 1-bit
LR Figure 8.39: The contents of the
O
{R shifter/rotator block
The mainstay of our shifter/rotator is a 3:1 multiplexer, in which each input
channel is 8 bits wide. In the case of instructions like AND, OR, ADD, and
SUB, we decode our instruction bits such that they cause this multiplexer to
choose the F[7:0] outputs from the core ALU and pass them straight through
to its FF[7:0] outputs. That is, the value on F[7] appears on FF[7], the value
on F[6] appears on FF[6], and so on (this is represented by the multiplexer
selecting the central set of inputs in Figure 8.39).
When we turn our attention to the SHR (“shift right”) or RORC (“rotate right
through the carry bit”) instructions, a little thought reveals that we want
both of them to shift whatever is coming out of the core ALU one bit to the
right. That is, we want the value on F[7] to appear on FF[6], the value on
F[6] to appear on FF[5], and so on (this is represented by the main
multiplexer selecting the left-hand set of inputs in Figure 8.39). In fact the
only difference between these instructions is the value that comes out of
FF[7], which needs to be a copy of whatever was on F[7] for a SHR, or a
copy of whatever is on the ALU’s main CI (“carry in”) input for a RORC.(12)
To achieve all of this we use a simple 2:1 multiplexer to generate a signal
called bit7; a SHR causes this multiplexer to select the input connected to
12
You might wish to glance back to our earlier discussions on these instructions at this point.
Rampaging around a CPU 8-49
F[7], while a RORC causes it to select the input being driven by CI. Now
consider the magnified view of the left-hand inputs to the main multiplexer
shown in Figure 8.39. As we see, the most-significant input is connected to
the bit7 signal, while the remaining seven inputs are connected to F[7:1].
Thus, when the main multiplexer selects these inputs and passes them
through to its outputs, the effect is to shift the bits coming out of the core
ALU one bit to the right, and to insert whatever value is on the bit7 signal
into the most-significant bit.
Similarly, we want both the SHL (“shift left”) and ROLC (“rotate left through
the carry bit”) instructions to shift whatever is coming out of the core ALU
one bit to the left. That is, we want the value on F[0] to appear on FF[1], the
value on F[1] to appear on FF[2], and so on (this is represented by the main
multiplexer selecting the right-hand set of inputs in Figure 8.39). In this case
the only difference between these instructions is the value that comes out of
FF[0], which needs to be a logic 0 for a SHL, or a copy of whatever is on
the ALU’s main CI (“carry in”) input for a ROLC.
Once again, we use a simple 2:1 multiplexer to generate a signal called
bit0; a SHL causes this multiplexer to select the input connected to a
logic 0, while a ROLC causes it to select the input being driven by CI. Now
consider the magnified view of the right-hand inputs to the main
multiplexer. As we see, the most-significant inputs are connected to the
F[6:0] signals, while the least-significant input is connected to bit0. Thus,
when the main multiplexer selects these inputs and passes them through to
its outputs, the effect is to shift the bits coming out of the core ALU one bit
to the left, and to insert whatever value is on the bit0 signal into the least-
significant bit.
The only remaining task needed to complete our shifter/rotator is to modify
the logic used to drive the CO (“carry-out”) signal
4:1 CO
generated by the ALU. In our earlier discussions Mu
d
we used a 2:1 multiplexer to select O ad B x
} C g t ] t
A lec
between the COadd signal from the
, etc PA } } F[7 0] s e
ADD function and the AgtB CA { CM OLC F[ it
, IN R C} 2-b
signal from the CMP function. B , R
SU HL RO
To satisfy the requirements of D D, { S HR,
{A {S
our shifter/rotator, we now
need to replace that 2:1 Figure 8.40: Modifying the carry-out logic to
multiplexer with a 4:1 accomodate shift and rotate instructions
version (Figure 8.40).
8-50 Bebop BYTES Back
As usual, the select inputs controlling this multiplexer are decoded from the
instruction bits driving the ALU. The ADD, OR, XOR, ADD, ADDC, SUB,
SUBC, INCA, and DECA instructions all cause the multiplexer to select the
COadd signal as before, while the CMPA instruction causes it to select the
AgtB signal. In the case of the SHL or ROLC instructions, the multiplexer
selects the input connected to the F[7] signal coming out of the core ALU,
where F[7] is the bit that conceptually “drops off the end” when we shift
everything to the left. Similarly, the SHR or RORC instructions cause the
multiplexer to select the [F0] signal coming out of the core ALU, which is
the bit that “drops off the end” when we shift everything to the right.
And that’s your lot. The combination of the core ALU
Phew! with the complementor and shifter/rotator blocks
provides us with everything we need to satisfy all of
the Beboputer’s ALU-related instructions.
Instruction
(a) Drive $2A onto bus (b) Load ACC (c) Stop driving onto bus
The first sub-action is to drive a value of $2A onto the internal data bus
(Figure 8.42a). Note that we’ve only highlighted the path we’re interested in
at the moment; in reality this data would also be presented to the inputs of
the temporary register (and to the outputs of the
Remember that the ALU itself is tri-state buffer, T-Buf). Next we force a binary
asynchronous and is therefore pattern that’s sort-of equivalent to an LDA
constantly doing something, so it instruction onto the instruction bits driving the
will always take whatever is ALU (we’ll explain what we mean by “sort-of”
presented to its data inputs and in a moment), then we load the value on the
generate something at its outputs. data bus into the accumulator by triggering its
However, once again we’ve only
clock input (Figure 8.42b). As soon as we’ve
highlighted the particular paths that
are of interest to us at the moment. loaded the accumulator, this data appears at its
outputs and slides into the ALU, thereby
causing new values to appear on the Z and N
status outputs (we’ll consider what we do with this status information in the
next section). Finally, we stop driving the $2A value onto the internal data
bus, but the accumulator remembers this value and continues to drive it into
the ALU (Figure 8.42c).
The previous paragraph stated: “...... force a binary pattern that’s sort-of
equivalent to an LDA instruction onto the instruction bits driving the ALU.”
The reason we said this is that the opcode for an LDA instruction will
actually be processed by control logic we haven’t considered yet, and it is
this control logic that would generate the requisite patterns to be presented
to the ALU’s instruction bits.
After loading the accumulator, our next task is to manually perform the
same sequence of actions as would be caused by an ADD (“add data value
to accumulator”) instruction. This is a little more complex than our LDA,
because it requires two sequences of sub-actions, the first of which is to
load the value we wish to add into our temporary register (Figure 8.43).
ACC TMP
Instruction
(a) Drive $14 onto bus (b) Load TMP (c) Stop driving onto bus
(a) Present the ADD (b) Enable T-buf (c) Clock ACC
the accumulator do respond, it takes some finite amount of time for the
effect to ripple through the ALU and work its way back to the accumulator’s
inputs. Thus, by the time these unwanted signals present themselves to the
accumulator, the result we’re interested in is already safely stored away
inside it. To complete the sequence, we would now disable our T-Buf
tri-state buffers and remove the ADD from the ALU’s instruction bits (these
actions are not shown in Figure 8.44).
Once we’ve performed the ADD, the last task that we set ourselves when we
commenced this journey into the unknown was to retrieve the result (which
is now stored in the accumulator). The way in which we achieve this is to
manually perform the same sequence of actions as would be caused by a
STA (“store accumulator”) instruction (Figure 8.45).
ACC TMP
(a) Present the STA (b) Enable T-buf (c) Disable T-buf
tion )
s t ruc logic Data System
n l latch data bus
U I tro o )
AL con Inf l
f r o m
a t us ontro
( St m c
/fro ste
m
(to Sy
P
TM SR CP
U
C
AC U Internal
AL data bus
u f
T-B
were affected. The fact is that, although the ALU is always outputting values
on all of its status outputs, this doesn’t mean that we’re obliged to save
them. Thus, depending on the particular operation that we’re trying to
perform, the control logic (that we promise to describe shortly) will only
cause the appropriate status register bits to be loaded.
When we power-up or reset the Beboputer, all of the status bits are cleared
to logic 0s; any subsequent values in the status bits depend on the results
generated by whatever instruction has most recently been performed. For
the vast majority of the time, only our elusive control logic typically pays
much attention to the status register, because it uses the values in the status
flags to make decisions, such as: “If the carry flag is set (logic 1) I’ll do one
thing, but if it’s cleared (logic 0) I’ll do something else.” Having said this,
we do occasionally require the ability to directly read values from, and
write values to, the status register.(16) Thus, we need to link our status
register to the internal data bus (Figure 8.47).
ion
t r uct ogic) Data System
ns ll latch data bus
U I tro fo
AL con
o m t u s In ntrol)
r
(f Sta m co
/fro ste
m
(to Sy
P
TM SR CP
U
uf
C T-B
AC U
AL Internal
f
data bus
u
T-B
Tri-state buffers
U
AL in)
m (C
Fro LU 1
0
A 2
To 3 bu
s
0 4 ta
gic 5 l da
Lo 7
6
erna
int
Figure 8.48: A closer look at the logic To
surrounding the staus register
Note that although some wires are shown as crossing over each other, the
only points at which they are connected are those indicated by small black
dots. Also remember that although we’ve only shown a single clock driving
the status register, each bit forming the register would have an individual
clock (or a common clock and an individual clock-enable).
Another point worth noting is that our internal data bus is 8 bits wide, but
our status register only contains 5 bits. This isn’t a problem in the case of
writing a value to status register, because all we have to do is connect bits 0
to 4 of the internal data bus to the multiplexer inputs and forget about bits 5
through 7. However, things are a little trickier when we wish to read a
value from the status register, because we only have 5 register bits available
to drive our 8-bit bus. The solution to this problem is quite simple, because
although the status register itself contains only 5 bits, we can make the tri-
state buffer 8 bits wide and connect its three most-significant inputs to a
logic value of our choice (we’ll assume that they’re connected to logic 0).
The circuit in Figure 8.48 also allows us to see how the carry flag can be
driven by the ALU’s carry-out (Cout) output and, at the same time, can be
used to drive the ALU’s carry-in (Cin) input. For example, in the case of one
of our rotate instructions (ROLC and RORC), the bit that’s shifted into the
accumulator comes from the carry flag, while the bit that “drops off the
8-58 Bebop BYTES Back
end” of the accumulator is stored in the carry flag. Once again, this is
possible due to the delays in the circuit, which mean that the original
contents of the carry flag have already been utilized by the time the new
contents overwrite them.
imp imm abs flags
Finally, note once again that the
interrupt mask flag is not op # op # op # I O N Z C
connected to the ALU,
SETIM $08 1 1 - - - -
but is instead driven
directly by the control CLRIM $09 1 0 - - - -
logic. The concept of Table 8.5: Interrupt mask instruction
interrupts is discussed in
detail in Appendix D, so at this time we need only note that our CPU
supports two special instructions called SETIM (“set interrupt mask flag to
logic 1”) and CLRIM (“clear interrupt mask flag to logic 0). Both of these
instructions use the implied addressing mode and therefore only occupy one
byte (Table 8.5).
Some microprocessors support similar instructions for all of the status flags;
for example, SETC and CLRC for the clear flag. If we had chosen to do this
for the Beboputer, we would have been obliged to add eight more
instructions to our instruction set, so we decided to live without them,
because we’re trying to keep our instruction set “mean and lean.” On the
other hand, if we had decided to support SETC and CLRC, then we could
lose our ADD and SUB instructions, because we could combine SETC and
CLRC with ADDC and SUBC to achieve the same results.
Data System
latch data bus
n g
ssi
d dre ic ste
m
A Log u f
Sy
T-B
IR U
e CP
cod u f
De and ute T-B
ec
Ex
P
TM SR
u f
C T-B
AC U
AL
u f
T-B
Remember that the Beboputer has a 16-bit address bus, which is why we
show this bus as being twice the width of our 8-bit data bus. Inside the
addressing logic block are a number of 16-bit registers, each of which may
be loaded into the address latch and used to drive the address bus. Also,
each of these 16-bit registers can be treated as two 8-bit registers (for the
purposes of writing data into them and reading data out of them), which is
why this figure shows two paths from the internal data bus into, and out of,
the addressing logic block.
Not surprisingly, the complexity (or lack thereof) of its addressing logic
dictates the sophistication (or lack thereof) of the addressing modes that can
be supported by the CPU. As fate would have it, the educational nature of
the Beboputer persuaded us that we should offer a reasonably varied
potpourri of commonly used addressing modes, with the result that the
addressing logic is quite ..... “hairy” (Figure 8.51).
A few moments ago we said that the addressing logic contains a number of
16-bit registers; however, for the sake of simplicity, Figure 8.51 only shows
Rampaging around a CPU 8-61
the main one which is referred to as the program counter (PC). The program
counter is actually formed from two 8-bit registers, which we’ve called
PC-MS and PC-LS for the most- and least-significant bytes, respectively. The
two halves of the program counter can be clocked independently or
together (all of the control signals are driven by the instruction decoder that
we introduced in the previous section). Thus, by means of the 2:1
multiplexers driving their inputs, we can individually load each half of the
program counter with a value from the internal data bus. Similarly, the
T-Buf tri-state buffers allow us to read the contents of PC-MS or PC-LS back
onto the internal data bus. tem s
Sy
U
CP
Addressing Address
logic block bus
Ad
ff lat dress System
Stu ch data bus
r&
de
Ad
M ux D
lat ata
2:1 ch
PC S)
x (L
Mu
2:1 s
bu
PC S) f data
(M u al
T-b ern
Int
u f
T-b
The ALU, ACC, SR, IR,
decode logic, and other
bits and pieces that we
looked at earlier go here
Figure 8.51: Inside the addressing
logic block (simplied view)
In order to point to a location in memory, the instruction decoder loads the
outputs from both halves of the program counter into a 16-bit address latch,
which, in turn, drives these signals onto the main system’s address bus.
Once the current value in the program counter is safely stored in the address
latch, the instruction decoder may wish to modify its contents. For example,
the instruction decoder might decide to increment the program counter to
point to the next location in memory. In this case the decoder would use
the 16-bit adder block to add $0001 to the current value in the program
counter, and then store the result back into the program counter via the
2:1 multiplexers.
In the remaining sections of this chapter we’ll investigate the ways in which
the program counter (and other registers yet to be introduced) are used to
8-62 Bebop BYTES Back
als
l s ign ion
o ct
=0 ntr tru ic
Cin Co ins r log
m
Adder fro code
er de
dd and stuff
-bit a
16
2
To M ux 00
$0 2)
mu the 5:1 1 (=+
ltip 2:1 00
lex $0 1)
ers 0 (=+
00
$0 0)
F (=+
(or From FF
$F -1)
oth th
er e P (=
reg C
iste Top ret
rs) sec
Address
bus
al
ern ff Ad
int us tu latdress
m b &S ch
Fro ata de
r
m
d Ad ste
ux Sy
2: 1M U
CP
PC
When the CPU is reset, the instruction decoder initializes the program
counter to cause it to point to a specific memory location (address $0000 in
the case of the Beboputer), and it is from this location that the CPU will
retrieve its first instruction. To do this, the instruction decoder loads the
outputs from the program counter into a 16-bit address latch, which, in
turn, drives these signals onto the main system’s address bus. Once the
current value in the program counter has been safety squirreled away in the
address latch, the instruction decoder increments the program counter such
that it’s ready to point to the next memory location, and for this we use our
16-bit adder block. In the real world we can save time by performing both
tasks concurrently; that is, at the same time as we’re loading the value
8-64 Bebop BYTES Back
coming out of the program counter into the address latch, we can also be
incrementing it using the adder block and feeding it back into the program
counter via the 2:1 multiplexers. As usual, the reason this works is because
all of the logic blocks in the loop have internal delays (albeit small ones),
which means that the original value in the program counter has been safely
stored in the address latch by the time the new value comes out.
(a)
Opcode Opcode IR
(c) (b)
The sequence commences when the program counter reaches the opcode
for an implied instruction (Figure 8.54a), loads that opcode into the
instruction register (IR) (Figure 8.54b), and increments the program counter
(Figure 8.54c). Recognizing that this is an implied instruction, the CPU
executes it and continues on to the next instruction. Examples of
instructions that use implied addressing are: DECA, INCA, ROLC, RORC,
SHL, and SHR.
(b)
(a) Opcode IR
Opcode
(c)
Data
(e) Result ACC
(d)
Figure 8.55: Immediate addressing
CA
All control signals from TP
the instruction decoder
2:1 output
PC multiplexer
h
s latc k
re s loc
a dd der b
Figure 8.57: Adding the TPCA register To d ad
an
As we see, the signals from our original 2:1 multiplexers are used to drive
both the program counter and the temporary program counter (both halves
of which can be loaded individually or at the same time, just like the main
program counter). Meanwhile, the outputs from the main program counter
and temporary program counter are fed into a new 2:1 output multiplexer.
As usual, this multiplexer is controlled by the instruction decoder, which
can therefore choose to pass either the contents of the main program
counter or the temporary program counter to the address latch and adder
block (not to mention the tri-state buffers driving the internal data bus).
18
The ‘A’ serves to inform us that there’s a batting chance we’ll have a TPCB (“temporary
program counter B”) before the sun goes down.
19
The syntax “ADD [$4B06]” is more fully defined in Chapter 12.
Rampaging around a CPU 8-67
reads the least-significant address byte from memory, stores it in the least-
significant byte of the temporary program counter (f), and increments the
main program counter once more (g).
Memory
PC MS LS
(a) (b)
Opcode Opcode IR
(c) (d)
MS Addr
(e) (f)
LS Addr
(g)
TPCA
Figure 8.58: Absolute MS Addr LS Addr
addressing
(i) (h)
ACC Result Data
The main program counter is now “put on hold” while the CPU uses the
temporary program counter to point to the target address containing the
data (h). The CPU executes the original instruction using this data, stores
the result into the accumulator (i), and then returns control to the main
program counter to look for the next instruction. Examples of instructions
that use absolute addressing are: ADD, ADDC, AND, CMPA, LDA, OR, STA,
SUB, SUBC, and XOR. Note that in the case of a STA (“store
accumulator”), the contents of the accumulator would be copied (stored)
into the data byte in memory.
Unconditional jumps
The ability to use the temporary program counter to implement absolute
addressing is obviously an important step forward, but there are more
ramifications to this mode than the ability to access remote data. Without
absolute addressing our programs would only ever be able to proceed in a
linear manner, commencing at address $0000 (from a reset condition) and
then casually strolling through succeeding locations, one after the other,
until we eventually ran out of memory (Figure 8.59).
8-68 Bebop BYTES Back
Memory
In fact, to create any sort of meaningful program, the
$0000
CPU must have the ability to change the flow of the
$0001
program. The simplest example of this is a JMP
$0002
(“unconditional jump”) instruction, in which the CPU
$0003
jumps directly to a new memory location. To illustrate
this concept, consider a really simple program
Figure 8.59: Strolling
(Figure 8.60).
through the memory $4000 $90
LDA $00
The program commences when the CPU copies $4001 $00
the $90 opcode stored in location $4000 into the
instruction register and increments the program
INCA $4002 $80
counter to point to location $4001. Recognizing
that the $90 opcode means “load the
accumulator using the immediate addressing Jump back
$4003 $C1
$4004 $40
mode,” the CPU copies the $00 value stored in and loop
$4005 $02
location $4001 into the accumulator and
increments the program counter to point to Figure 8.60:
location $4002. Unconditional jump
When the CPU reads the $80 value from location $4002, it recognizes this
as being the opcode for an INCA (“increment accumulator”) instruction,
which it proceeds to do with alacrity, leaving the program counter pointing
at location $4003.
Now this is the clever part. When the CPU reads the $C1 value at location
$4003, it recognizes this as being an unconditional jump, so it uses the
program counter to point to location $4004, and copies the $40 value it
finds there into the most-significant byte of the temporary program counter.
The CPU then uses the main program counter to point to location $4005,
and copies the $02 value it finds there into the least-significant byte of the
temporary program counter, which now therefore contains the full target
address of $4002. Finally, the CPU copies the entire contents of the
temporary program counter into the main program counter, and then
continues to read the new opcode pointed to by the main program counter
as if nothing had happened.(20)
Before we proceed further, the act of copying the temporary program
counter into the main program counter deserves a few words, not the least
that it explains the reason why we gave our adder block the ability to add
$0000 to whatever value was passed into it (you may wish to refer back to
20
If you’re on your toes, you’ll have realized that this program isn’t particularly meaningful,
because it never actually outputs anything, but it serves our purposes here, so “what the hey?”
Rampaging around a CPU 8-69
Figure 8.52 at this juncture). First the control logic selects the outputs from
the temporary program counter and feeds them into the adder block. At the
same time, the control logic instructs the adder block to add $0000 to this
value. This of course has no effect on the value coming out of the temporary
program counter, which is just what we want. Finally, the control logic
causes our original 2:1 multiplexers to select the value coming out of the
adder block, then it clocks both halves of the main program counter,
thereby loading it with the contents of the temporary program counter.
Finally, note that although Figure 8.60 only showed us jumping back in the
memory, we can in fact jump in any direction. Also, programs can contain
any number of jumps, so we might execute a few instructions then jump to
a new location, execute a few more instructions and jump somewhere else,
and so forth.
Conditional jumps
Unconditional jumps are very useful as you’ll discover when you start to
write your own programs, but we need something more. To be really
effectual, the CPU must have the ability to change the flow of the program
based on the result from an earlier operation. For example, after
decrementing the accumulator, we might want the CPU to say: “Hmmm, if
the result from that operation wasn’t zero then I should continue doing
whatever it was that I was doing, but if the result was zero then I should
start to do something else.” (Figure 8.61). $4000 $90
LDA $FF
$4001 $FF
Once again, this simple (if meaningless)
example serves to illustrate the point. First we
load the accumulator with $FF, then we enter a DECA $4002 $81
loop that decrements the accumulator and tests
its contents to see if they’re zero. As long as the
accumulator’s contents are non-zero we jump no
$4003 $D6
back to location $4002, otherwise we continue =0? $4004 $40
$4005 $02
on to the next instruction at location $4006.
yes
$4006 $??
The act of testing the accumulator and deciding
what to do is performed by a conditional jump Figure 8.61: Conditional jump
instruction. In this particular example, the $D6 stored in location $4003 is
the opcode for a JNZ (“jump if not zero”), which bases its decision on the
contents of the zero status flag. If the zero flag contains a logic 0 (indicating
a non-zero condition), then we perform the jump; otherwise, if the zero flag
contains a logic 1 (indicating that all of the bits in the accumulator are
zero), then we fall through to the next instruction.
8-70 Bebop BYTES Back
Now let’s examine how the JNZ instruction works in a little more detail.
When the main program counter arrives at location $4003, the CPU loads
the $D6 opcode into the instruction register and increments the program
counter to point to location $4004. Recognizing that this is a JNZ, the
instruction decoder checks the value in the zero status flag. If the zero flag
contains logic 0 (indicating a non-zero condition) then the CPU proceeds as
for a normal jump:
a) Copy the $40 value from location $4004 into the most-significant
byte of the temporary program counter.
b) Increment the main program counter to point to location $4005.
c) Copy the $02 value from location $4005 into the least-significant
byte of the temporary program counter.
d) Transfer the entire contents of temporary program counter ($4002)
into the main program counter.
e) Read the new opcode pointed to by the main program counter as if
nothing had happened.
Alternatively, if the zero flag contains a logic 1 (indicating that all of the bits
in the accumulator are zero), then the CPU simply uses the addressing
logic’s adder block to add $0002 to the current contents of the main
program counter and stores the result back into the main program counter,
thereby leaving it pointing at location $4006, from whence the CPU will
read its next opcode.
In fact, with regard to the last point, some microprocessors seem to sport
every possible instruction and addressing mode combination known to man
(including some that you wouldn’t recognize if they bit you on the leg). This
is usually due to the fact that every time a new generation of a CPU is in the
works, its designer’s listen to every wacky idea that walks through the door,
and they never seem to learn to say something like: “If you can show me
three people who will actually use this instruction in my lifetime, then
maybe, just maybe I’ll consider implementing it.”
(a) (b)
Opcode Opcode IR
(c) (d)
MS Addr
(e) (f)
LS Addr
(g)
TPCA Index Register (X)
MS Addr LS Addr MS LS
+
(h)
(i)
ACC Result Data
The sequence commences when the program counter reaches the opcode
for an indexed instruction (a), loads that opcode into the instruction
register (b), and increments the program counter (c). Recognizing that this is
an indexed instruction, the CPU reads the most-significant address byte from
memory, stores it in the most-significant byte of temporary program
counter A (d), and increments the main program counter (e). The CPU then
reads the least-significant address byte from memory, stores it in the least-
significant byte of temporary program counter A (f), and increments the
main program counter again (g).
The main program counter is now “put on hold” while the CPU adds the
contents of the index register to the contents of temporary program
counter A and uses the result to point to the target address containing the
data (h). (The act of adding the index register to temporary program counter
A does not affect the contents of the index register.) The CPU now executes
the original instruction using this data and stores the result into the
accumulator (i). Finally, the CPU returns control to the main program
counter to look for the next instruction.
Examples of instructions that use indexed addressing are: ADD, ADDC, AND,
CMPA, JMP, LDA, OR, STA, SUB, SUBC, and XOR. Note that, in the case
of a STA (“store accumulator”), the contents of the accumulator would be
copied (stored) into the data byte in memory. Also note that there’s no
particular reason why we shouldn’t supply indexed versions of our
conditional jump instructions; we just decided not to bother.
23
The syntax “ADD [$4B06,X]” is more fully defined in Chapter 12.
8-76 Bebop BYTES Back
Of course, we have to load a value into the index register before we can use
it, so the Beboputer is equipped with a BLDX (“big load index register”)
instruction. The reason we call this a “big load” is that we have to load two
bytes (16 bits) into the register. In fact there are
Load X $4000 $A0
with $0C35 $4001 $0C
two flavors of this instruction, thereby allowing
$4002 $35 us to load the index register using either
immediate or absolute addressing. For
example, consider the immediate mode
Figure 8.66: BLDX (immediate)
(Figure 8.66).
In this example, the $A0 stored in location $4000 is the opcode for a BLDX
using immediate addressing. As we see, this is similar to a standard
immediate instruction, except that there are two data operand bytes instead
of one. Similarly, in the case of the absolute form of this instruction, the two
address operand bytes point to the first of a pair of data bytes.
In addition to the BLDX instructions, we also have INCX and DECX implied
instructions to increment and decrement the index register, respectively.
These instructions make the indexed addressing mode particularly useful for
manipulating arrays or tables of values. Additionally, INCX and DECX affect
the contents of the zero flag, which enables us to use the index register to
control how many times we go around a loop. One consideration related to
incrementing or decrementing the index register is that we might lose track
of what it contains. Generally speaking this isn’t a problem, but every now
and again we might wish to peer inside this register to determine its current
value. For this reason we also have a BSTX (“big store index register”)
instruction, which is similar to an absolute-mode STA (“store accumulator”),
except that it copies the contents of the index register into a pair of adjacent
bytes somewhere in the Beboputer’s memory.(24)
As we shall see in future labs, the index register can be exceptionally useful,
often leaping to the fore when you least expect it. However, you will also
find that you spend a lot of time using indexed versions of instructions such
as ADD or LDA followed by an INCX or a DECX (alternatively, you may use
an INCX or a DECX followed by an indexed instruction). For this reason,
some CPUs support several additional opcodes that combine logical and
arithmetic indexed instructions with the act of incrementing or
decrementing the index register. In fact there are four flavors of these
additional addressing modes:
24
The BSTX instruction is also of use in preserving the contents of the index register should
we call a subroutine that modifies this register (the concept of subroutines is introduced later
in this chapter).
Rampaging around a CPU 8-77
a) Pre-increment indexed (Increment the index register then perform
the instruction)
b) Pre-decrement indexed (Decrement the index register then perform
the instruction)
c) Indexed post-increment (Perform the instruction then increment the
index register)
d) Indexed post-decrement (Perform the instruction then decrement the
index register)
Important! This discussion on pre- and post-incremented/decremented
indexed addressing modes is for interest only. The Beboputer doesn’t
support any of these additional modes, because they’re really “cream on the
cake” and we can survive quite happily without them.
op # op # op # op # op # I O N Z C
BSTX $A9 3 - - - - -
INCX $82 1 - - - Z -
DECX $83 1 - - - Z -
25
The JSR instruction can also use the indexed mode, but we haven’t come to this instruction yet.
8-78 Bebop BYTES Back
As we see, the index register is still connected to the 4:1 output multiplexer,
which feeds it into the adder block in the usual way, thereby allowing us to
increment or decrement its contents to perform our INCX and DECX
instructions, respectively. However, we also take the outputs from the index
register and feed them directly into a second set of inputs to the adder
block. This second set of inputs form the “Top Secret” signals that we
introduced in Figure 8.52. Thus, we can use the 4:1 output multiplexer to
select the outputs from temporary program counter A (or one of the other
registers) and feed them to the adder block, where we now have the ability
to add them to the contents of the index register if we so desire.
Rampaging around a CPU 8-79
Also, we’ve added a new 2:1 multiplexer to feed the address latch. This
multiplexer can either select the signals from the 4:1 output multiplexer as
before, or it can select the outputs from the adder block when we wish to
load the sum of the index register and one of the other registers into the
address latch.
(a) (b)
Opcode Opcode IR
(c) (d)
MS Addr
(e) (f)
LS Addr
(g)
TPCA Index Register (X)
MS Addr LS Addr MS LS
+
(i) (h)
MS Addr
(k) (j)
LS Addr
TPCB
Figure 8.68: Pre-indexed
MS Addr LS Addr
indirect addressing
(l) (m)
Data Data ACC
26
The syntax “LDA [[$4B06,X]]” is more fully defined in Chapter 12.
8-80 Bebop BYTES Back
When the program counter reaches a pre-indexed indirect opcode (a), the
CPU loads that opcode into the instruction register (b), and increments the
program counter (c). Next the CPU reads the most-significant address byte
from memory, stores it in the most-significant byte of temporary program
counter A (d), and increments the main program counter again (e). Now the
CPU reads the least-significant address byte from memory, stores it in the
least-significant byte of temporary program counter A (f), and increments the
main program counter once more (g).
The CPU next adds the contents of the index register to the contents of
temporary program counter A, uses the result to point to the most-significant
byte of the second address (h), and stores this byte in the most-significant
byte of temporary program counter B (i). The CPU then points to the least-
significant byte of the second address (j), stores it in the least-significant byte
of temporary program counter B (k), uses temporary program counter B to
point to the target data (l), and loads this data into the accumulator (m).
Finally, the CPU returns control to the main program counter to look for the
next instruction.
Indirect post- Memory
indexed addressing PC MS LS
is similar in concept
to pre-indexed indirect addressing.
However, in this case the address (a) (b)
Opcode Opcode IR
in the opcode bytes points to a (c) (d)
MS Addr
second address, and it is this (e) (f)
LS Addr
second address that is added to (g)
TPCB
the contents of the index register
MS Addr LS Addr
to generate the address of the
target data. For example, consider
an LDA [[$4B06],X] (Figure 8.69).(27) (i)
MS Addr
(h)
(k) (j)
LS Addr
Index Register (X) TPCA
MS LS MS Addr LS Addr
+
(l) (m)
Data Data ACC
Figure 8.69: Indirect post-indexed addressing
27
The syntax “LDA [[$4B06],X]” is more fully defined in Chapter 12.
Rampaging around a CPU 8-81
When the program counter reaches an indirect post-indexed opcode (a), the
CPU loads that opcode into the instruction register (b) and increments the
program counter (c). Now the CPU reads the most-significant address byte
from memory, stores it in the most-significant byte of temporary program
counter A (d), and increments the main program counter again (e). The CPU
next reads the least-significant address byte from memory, stores it in the
least-significant byte of temporary program counter A (f), and increments the
main program counter once more (g).
The CPU now uses the contents of temporary program counter A to point to
the most-significant byte of the second address (h), and stores this byte in
the most-significant byte of temporary program counter B (i). The CPU then
increments temporary program counter A to point to the least-significant
byte of the second address (j), and stores this byte in the least-significant
byte of temporary program counter B (k). Now the CPU adds the contents of
the index register to the contents of temporary program counter B, uses the
result to point to the target data (l), and loads this data into the accumulator
(m). Finally, the CPU returns control to the main program counter to look
for the next instruction.
You may be pleased to learn that you probably won’t find much use for the
pre-indexed indirect and indirect post-indexed modes in the course of an
average day, but they do tend to be applicable to the machine code that’s
generated from higher-level languages using automatic compilers
(as discussed in Chapter 12)
op # op # op # op # op # op # op # I O N Z C
2:1 rs
m
Fro iplexe Register and multiplexer control
lt
mu signals have been omitted for
simplicity (as have the subtleties of
SP the index register's extra outputs)
X
5:1 output
TPCB multiplexer
TPCA
PC
tch
e s s la ock
Figure 8.70: Adding the dr er bl
ad
stack pointer (SP) To d add
an
Rampaging around a CPU 8-83
The stack pointer comes in handy for a wide variety of tasks, but the way in
which it works can be a little confusing to the uninitiated, so we’ll start off
with a simple example. Suppose we have a program that contains a loop,
and every time it strolls around the loop it generates a value in the
accumulator that we are going to need sometime in the future. This means
that each time we pass through the loop, we have to copy whatever value is
in the accumulator to some location in the Beboputer’s memory, otherwise
it will be overwritten the next time we cycle through the loop.
One approach would be to reserve a number of bytes in the memory and
store the accumulator values there (employing instructions that use absolute,
indexed, indirect, or similar addressing modes), but this means that we’d
have to know the maximum (worse-case) number of values that we are ever
going to want to play with. Apart from potentially tying up a lot of our
available memory resources (which may not actually be required), there’s a
strong possibility that we would have to delve back into our program and
modify it in the future if conditions ever change such that we need to store
more values. Also, keeping track of the next free location in the area we’ve
reserved is probably going to take a fair amount of messing around with
additional instructions, which will slow our program down.
This is where the stack pointer leaps to the fore, because we have an
immediate mode instruction called PUSHA (“push the accumulator onto
the stack”), which causes the CPU to copy the contents of the accumulator
to the memory location pointed to by the stack pointer, and to then
automatically decrement the stack pointer to point to the next free location.
For example, assume that the stack pointer’s starting address is $4FFF (we’ll
discuss how this comes about in a little while), and further assume that our
program has strolled around the loop three times, thereby executing three
PUSHA instructions and placing three copies of the accumulator into the
memory (Figure 8.71).
Memory
$4FFB
Final value of
the stack pointer
$4FFC
$4FFD ACC #3 Value in the accumulator from pass #3 round the loop
$4FFE ACC #2 Value in the accumulator from pass #2 round the loop
Initial value of
the stack pointer
$4FFF ACC #1 Value in the accumulator from pass #1 round the loop
$5000
Figure 8.71: The state of the stack after three PUSHA instructions
8-84 Bebop BYTES Back
The area occupied by this data is known as the stack, because we can
visualize the bytes as being stacked one on top of the other. Note that the
fact that the CPU automatically decrements the stack pointer means that it
always ends up pointing to the first free location on the top of the stack.
To complement PUSHA, we also have a POPA (“pop the accumulator off the
top of the stack”) instruction, which causes the CPU to increment the stack
pointer to point to the last byte placed on the stack, and to then copy this
byte into the accumulator. Once again, this results in the stack pointer
pointing to the first free location on the top of the stack.
Using single-byte PUSHA and POPA implied mode instructions is much
more efficient than using 3-byte absolute, indexed, or indirect mode
versions. Additionally, this technique means that we don’t have to define
any specific number of bytes to be reserved, because the stack only uses the
minimum amount of memory that it needs. Correspondingly, the stack can
grow to be as big as necessary until it runs out of available memory.
One point that can cause confusion is the fact that $4000
the top of the stack occupies a lower address value Program
than the bottom, because the stack “grows” in the
opposite direction to the program. For example, assume
that we initialize the stack pointer to address $4FFF, and
Direction
then consider the portion of the memory map between the of program's
start of the RAM at addresses $4000 and the bottom of the growth
stack at address $4FFF (Figure 8.72).
Note that we’ve tended to draw the memory map with
address zero at the top and the most-significant address at Direction
of stack's
the bottom. Some books do flip the map over and draw it growth
the other way round, but the style we’ve used is the more
common. One reason for this is that the addresses appear
in the same order that we’d typically represent a binary
count sequence, but a more important consideration Stack
is that this tends to reflect the way in which we $4FFF
document our programs.
Figure 8.72: Program vs stack
Of course, programs don’t “grow” in the same sense
that the stack does, but the effect is kind-of the same. For example, the
programs we write for the Beboputer typically commence at address $4000,
which is the first location in our RAM. If we subsequently decide to modify
a program by adding more instructions, then our program will effectively
“grow” down the memory map.
Rampaging around a CPU 8-85
The reason the stack grows in the opposite direction to the program is to
save programmers a lot of pain. If we commence the program at the lowest
possible address and we initialize the stack to the highest possible address,
then we can use all of our available memory to its maximum advantage
until we push too much data onto the stack, at which point the top of the
stack will collide with the end of the program and start to overwrite it. This
condition, which is known as a stack overflow, is generally not considered
to be a good thing to occur. Similarly, if we try to “pop” more data off the
stack than we “pushed” onto it, then the result will be a stack underflow (for
example, two PUSHAs followed by three POPAs will do the trick).
In addition to the PUSHA and POPA instructions, we also have their
PUSHSR and POPSR counterparts, which push and pop the contents of the
status register on and off the stack, respectively. Apart from anything else,
this allows us to play some useful tricks, because the CPU only keeps track
of where the stack pointer is currently pointing, but it doesn’t retain
knowledge as to the origin of any data on the stack. Thus, if we perform a
PUSHSR followed by a POPA, we end up with a copy of the status register
in the accumulator. Similarly, we can coerce the status register (and thus the
status flags) to contain whatever values we wish, by first loading these
values into the accumulator and then performing a PUSHA followed by a
POPSR.
One point we should note is the stack pointer’s initial address, which is
shown as being $4FFF in the examples above. In the case of some
computers, the stack pointer’s initial address is hardwired into the CPU,
such that it’s automatically loaded into the stack pointer following a power-
up or reset condition. However, other CPUs allow the stack pointer to be
loaded under program control, and this is the tack we took with the
Beboputer. Thus, the Beboputer is equipped with a BLDSP (“big load stack
pointer”) instruction, which allows us to load a 2-byte (16-bit) value into the
stack pointer using either immediate or absolute addressing. This means that
we can initialize the Beboputer’s stack pointer to any location in the RAM
portion of the memory, and we just happened to choose $4FFF for the sake
of these examples.
The stack pointer usually takes care of itself, but every now and again we
might wish to determine its current value. For this reason we also have a
BSTSP (“big store stack pointer”) instruction, which copies the contents of
the stack pointer into a pair of adjacent bytes somewhere in the Beboputer’s
memory. (The BSTSP instruction is similar to the BSTX (“big store index
register”) we discussed earlier.)
8-86 Bebop BYTES Back
Introducing subroutines
Assume that we wish to create a program to perform a certain task.
Generally speaking such a program will be composed of a sequence of sub-
tasks that are strung together to achieve the desired result. Further assume
that we already created one of these sub-tasks for use in an earlier program,
and that we’d like to re-use this set of instructions in our new program. One
way to do this would be by means of JMP (“unconditional jump”)
instructions (Figure 8.73a).
Main Main
program program
JMP JSR
Useful Useful
sub-task JMP sub-task RTS
(a) Using JMP instructions (b) Using JSR and RTS instructions
op # op # op # op # op # op # op # I O N Z C
BSTSP $59 3 - - - - -
PUSHA $B2 1 - - - - -
POPA $B0 1 - - - - -
PUSHSR $B3 1 - - - - -
POPSR $B1 1
RTS $CF 1 - - - - -
onto the top of the stack (followed by a copy of the status register), then the
contents of the interrupt vector are copied into the program counter.
2:1
r o m xers Register and multiplexer control
F iple
lt signals have been omitted for
mu simplicity (as have the subtleties of
IV the index register's extra outputs)
SP
6:1 output
X
multiplexer
TPCB
TPCA
h
latc k
PC
s
re s loc
a dd der b
To d ad
Figure 8.74: Adding the an
interrupt vector (IV)
▼
How to use our hex keypad to enter and run
a program that converts 8-bit binary numbers
into their 3-digit decimal equivalents
▼
How to make our program speak numbers
aloud using the Beboputer’s virtual sound card
▼
Why our lives would have been easier if our
forebears had decided to use the word
4
“ onety ” instead of “ ten ”
b
La
9-2 Bebop BYTES Back
Contents of Chapter 9
What can you do when a cornucopia of forces are afoot? 9-3
The hex keypad versus switch and display panels ................................. 9-4
Mapping the hex keypad’s buttons .............................................................................. 9-5
Encoding the hex keypad’s buttons ............................................................................ 9-6
Driving the hex keypad’s 7-segment displays ................................................ 9-8
Winding up the Beboputer ....................................................................................................... 9-10
Program to convert a binary number into its decimal
equivalent ...................................................................................................................................................... 9-11
The master (top-level) flowchart ........................................................................... 9-12
Initializing the variables ........................................................................................................ 9-13
Reading and storing the switches ...................................................................... 9-14
Extracting the “hundreds,” “tens,” and “ones” .............................. 9-15
Displaying the results ............................................................................................................... 9-18
Entering the program with the hex keypad ........................................ 9-21
Short-cut technique for loading the program .................................. 9-27
Editing the program with the hex keypad ............................................. 9-28
Running the program with the hex keypad ........................................ 9-29
Augmented program that speaks the numbers ..................................... 9-31
The big red button ..................................................................................................................... 9-32
The Beboputer’s sound card ....................................................................................... 9-33
What do we want our augmented program to do
(in a general sort of way)? ........................................................................................ 9-36
The augmented program flowchart ............................................................... 9-37
The augmented program listing ............................................................................ 9-39
Entering and running the augmented program ........................... 9-44
Quick quiz #9 ................................................................................................................................................. 9-45
The Hex Keypad 9-3
What can you do when a cornucopia of
forces are afoot?
For a variety of reasons (which are discussed in more detail in Chapter 15),
the advent of the microprocessor was not as assured as one might have
expected. But, a cornucopia of forces were afoot,(1) some of which resulted
in the introduction of the first microprocessor by Intel in 1971, and a brave
new world of opportunities was born.
One of the more surprising developments, which was only envisaged by a
few bold souls, was the emergence of the first home (hobbyist)
microcomputers and development systems. The early versions of these
systems were Development
ROM
programmed using a bank RAM
area
Power
supply
of switches similar to a CPU
0 1 2 3
results were presented on
a set of lights not unlike a 4 5 6 7
1
”What’s afoot? It’s a dirty smelly thing on the end of your leg!” – The Goon Show, circa1958.
9-4 Bebop BYTES Back
switch and display panels. The four hexadecimal digits on the left of the
display area indicate the current address, while the two digits on the right of
the display show the memory’s contents at that location.
Figure 9.2: The ports used by the switch and display panels
In the case of the switch panel, we require four input ports to access all of
the information from the switches. Two 8-bit ports are used to read the
sixteen address switches, another 8-bit input port reads the eight data
switches, and three bits of a final input port are required to read the Enter,
Step, and Run buttons (the ON/OFF switch is connected to the power
supply, while the Reset button is plugged directly into the CPU’s reset
input). When you had occasion to access the Input Ports dialog window in
previous laboratories, you may have noticed that ports 12 through 15 were
designated as being “System Assigned.” In fact these are the ports that we
use to handle the switch panel (Figure 9.2a).
The Hex Keypad 9-5
The way this works is that the monitor program loops around reading the
port connected to the Enter, Step, and Run buttons. When one of these
buttons is activated, the monitor program reads the three ports connected to
the address and data switches and uses this information to perform whatever
task it’s been instructed to do.
If we ignore the ON/OFF switch and the Reset button, the switch panel has
twenty-seven switches and buttons. By comparison, the hex keypad has
twenty-two buttons, but instead of assigning each button to an individual bit
of an input port – which would again require the use of multiple ports – it is
more efficacious to encode them and to only use a single port. Encoding the
keypad’s buttons makes our lives easier, because these buttons are used to
represent both the address and the data, so it’s more efficient for the monitor
program to access a single port and then process the information internally.
use, but this one has a certain Figure 9.3: Mapping the
keypad’s buttons
9-6 Bebop BYTES Back
Logic 1
Resistors
(+5 volts)
Buttons
'0'
'1'
Diode
'2' matrix
"Run"
Input port
Logic 0 Bit Bit Bit Bit Bit Bit Bit Bit
(0 volts) 7 6 5 4 3 2 1 0
To CPU
Figure 9.4: Encoding the (Data bus)
keypad’s buttons
2
One should never underrate the ingenuity and creative genius of an inspired fool.
3
Half of the safety features that engineers design into systems are there to ensure that some
drongo hasn’t turned the other half off.
The Hex Keypad 9-7
As we see, each bit of the input port is connected to a “column” wire,
which is, in turn, connected to a logic 1 value via a resistor. Similarly, one
side of each button on the keypad is connected to a logic 0 value, while the
other side is connected to a “row” wire. The important point to note is that
the row and column wires aren’t directly connected together; instead,
selected row and column intersections are linked using diodes. It would
certainly be possible to hand-build this type of diode matrix using individual
components; alternatively, one could use a special programmable device
called a PROM.(4)
If none of the buttons on the keypad are pressed, then the value $FF
(or %11111111 in binary) would be presented to the input port. By
comparison, when one of the buttons on the keypad is pressed, the row
wire corresponding to that button is connected directly to a logic 0 value.
Wherever there is a diode at an intersection between this row wire and one
of the column wires, that diode will conduct, thereby overriding the column
wire’s resistor and forcing the wire to assume a logic 0 value. Thus, pressing
a button on the keypad will cause a pattern of logic 0s and logic 1s to be
presented to the input port, where the individual 0s and 1s correspond to
the presence or absence of diodes, respectively.
The way in which this all works is that the monitor program starts off by
happily looping around reading the input port and doing nothing as long as
it sees a value of $FF. As soon as the monitor program sees any value other
than $FF it knows that one of the buttons has been pressed, at which point
it will use the ensuing pattern of logic 0s and logic 1s to determine which
button is active and to perform any appropriate actions (Figure 9.5).
Wait for the user Do whatever needs Wait for the user to
to press a button to be done release the button
? ?
Read the no Perform Read the yes
Enter $FF $FF
Input port an action Input port
? ?
yes no
Figure 9.5: The monitor program reading the hex keypad’s input port
Remembering that the CPU can execute millions of operations a second, we
understand that the monitor program can perform an action and return to
looking at the input port in an incredibly short time, long before the user is
able to release the button. So the monitor program would be tempted to
treat a single press of a button as though the user had actually pressed it
4
PROMs are discussed in more detail in Chapter 13.
9-8 Bebop BYTES Back
O/P port
(4)
From CPU
(8)
(4)
Decoder
Figure 9.7: Driving the keypad’s 7-segment displays (schemeB)
As yet another alternative, we could use the four least-significant bits from
an output port to drive a chain of shift registers (Figure 9.8). In this case, the
first time the monitor program writes a byte of data to the output port, this
data would be loaded into the first set of 4-bit registers and the right-most
digit (in this figure) would appear. The next time the monitor writes to the
output port, the data in the second set of registers would be loaded with the
data from the first set of registers, which would, in turn, be loaded with the
new data from the port. Thus, each time the monitor program writes to the
port, all of the digits will be shifted one place to the left. (Once again,
we’ve omitted some nitty-gritty details from this figure for reasons of
simplicity; for example, we haven’t specified the origin of the load control
which would be derived from the output port’s enable signal.)
5
”Well, there’s certainly something screwy going on around here”– The Marx Brothers, Room
Service, (1938)
9-10 Bebop BYTES Back
4-bit Registers
(4) (4) (4) (4) (4) (4)
O/P port
(4)
From CPU
Not
(8)
used
(4)
Load
Tool Bar
Status Bar
Program
6
These displays were introduced in Chapter 5.
9-12 Bebop BYTES Back
The heart of the task is to convert a value that’s represented in one number
system (base-2, or binary) into another number system (base-10, or
decimal). Unfortunately, unlike the mapping between binary and
hexadecimal, which we’ve performed in previous laboratories, mapping
between binary and decimal presents a trickier problem. Before we
continue, take a few moments to ponder how you might solve this
assignment. However, don’t worry that this is going to be more complicated
than it is, because it’s not (if you can follow this sentence, you shouldn’t
have any difficulty with our program).
#2
This portion of the program is quite simple and doesn’t contain anything
that we haven’t seen before. The first instruction loads the accumulator with
a value of $00, while the following three instructions save this value to the
memory locations at addresses $4002 through $4004. In fact, due to the
way in which our program works, it’s only strictly necessary to initialize the
variables at addresses $4002 and $4003. However, as we aren’t particularly
concerned with creating the smallest program possible (at least in this
instance), we decided to go ahead and initialize the variable at address
$4004 as well.
Note that instead of containing the labels “Enter” and “Exit,” the terminal
symbols in this flowchart hold the labels “#1” and “#2”, respectively. Using
numerical labels such as these will help us understand how the various
portions of the flowchart (and thus the program) are connected together.
#3
#3b
To
Read/Store Extract 1s, Extract
#4 "Display
switches 10s, & 100s the 1s
Results"
yes
$20 = Subtract the data in the following byte
Sub 100 $401E $20
from the contents of the accumulator (where
from ACC $401F $64
the data is $64, or 100 in decimal)
Increment
$4026 $80 $80 = Increment the value in the accumulator
ACC
discover in Chapter 12, but we aren’t there yet. Also there’s no gain without
pain, and working with machine code (as we are here) will build a profound
understanding that will serve you well in the future.
Moving on ...... the program segment to extract the “tens” will be almost
identical to the segment that extracts the “hundreds,” except that its CMPA
instruction will be used to compare the contents of the accumulator with
9 instead of 99, the location used to store the “tens” value will be at address
$4003, and the target addresses for the JNC and JMP instructions will have
to be modified. (A listing of the entire program is shown a little later.)
Finally, the program segment that extracts the “ones” will be extremely
simple, because whatever is left in the accumulator after we’ve extracted the
“hundreds” and the “tens” has to be the number of “ones.” Thus, all this
segment actually does is to store whatever value remains in the accumulator
into the location we reserved to hold the “ones” value (that is, the location
at address $4004).
From CPU
digits, we’re using our dual 7-segment display to
display both the number of “tens” and the
Figure 9.16: A small quirk
number of “ones.” The problem here is that, as
both of the digits on this display are driven by a single output port, and as
our values for the “tens” and “ones” are stored in separate memory
locations, we’re going to have to perform a little jiggery-pokery.
The Hex Keypad 9-19
Now this next bit might be a little difficult to wrap your brain around if
you’re a newbie, so let’s take things one step at a time. The first thing to
realize is that we know our “ones” location will only ever contain binary
values in the range of 00002 through 10012 (which equates to 0 through 9 in
decimal). If our original number contained any value greater than nine, then
this would be reflected in the “tens” location (and possibly the “hundreds”
location). To put this another way, ten “ones” really means one “ten” and
no “ones,” eleven “ones” really means one “ten” and one “one,” twelve
“ones” really means one “ten” and two “ones,” and so on. Thus, we know
that the number stored in our “ones” location will only ever occupy the four
least-significant bits of this location.
Similarly, we know that our “tens” location will only ever contain binary
values in the range of 00002 through 10012. These values equate to 0
through 9 in decimal, which we’re taking to represent “no tens” through
“nine tens”; that is, 0 = “no tens,” 1 = “one ten,” 2 = “two tens,” and so
on. We know there can’t be more than nine “tens,” because any greater
value would be reflected in our “hundreds” location; so we know that the
number stored in our “tens” location will only ever occupy the least-
significant four bits of this location. Bearing all of this in mind, we are now
poised to solve the problem ...... hmmm, perhaps it’s time for another
illustration (Figure 9.17).(8)
"Tens" Location "Ones" Location
0 0 0 0 a a a a 0 0 0 0 b b b b
Load (LDA) OR
Shift left
(SHL) four |
times
0 0 0 0 a a a a a a a a 0 0 0 0 a a a a b b b b
New Accumulator New Accumulator New Accumulator
(a) Load "tens" (b) Shift "tens" (c) Merge "ones"
characters in the “Old Accumulator” indicate that we don’t care what its
contents were, because the load instruction will overwrite them.
The next step is to shift the contents of the accumulator four bits to the left
(Figure 9.17b). However, as our shift left (SHL) instruction only shifts the
accumulator one bit at a time, we’ll have to use four of these instructions to
achieve the desired result. When we finish the last of these shift left
operations, the four least-significant bits of the accumulator will contain
logic 0s, because one of the things a SHL does is to shift a zero into the
accumulator’s least-significant bit.
Last but not least, we OR the contents of the accumulator with the contents
of the “ones” location (Figure 9.17c). The point to remember here is that
logical instructions like OR operate on a bit-by-bit basis, so the original bit[0]
in the accumulator is OR-ed with bit[0] of the “ones” location to generate the
new bit[0] in the accumulator; the original bit[1] in the accumulator is OR-ed
with bit[1] of the “ones” location to generate the new bit[1] in the
accumulator; and so on.
The final result of these operations is that the accumulator’s four most-
significant bits end up containing the “tens” value, while its four least-
significant bits contain the “ones” value. Thus, if we now save the contents
of the accumulator to the output port driving our dual 7-segment display,
we will achieve the effect we’ve been striving for (Figure 9.18).
The LDA ($91 at address $404A) loads the accumulator with the contents of
the “hundreds” location at address $4002. Next we use a STA ($99 at
address $404D) to store the contents of the accumulator to the output port
at address $F022, which is the port driving the single 7-segment display.
Now comes the tricky bit, which commences with an LDA ($91 at address
$4050) to load the accumulator with the contents of the “tens” location at
address $4003. The contents of the accumulator are then shifted four bits to
the left by means of four SHL instructions (the $70s at addresses $4053
through $4056). To complete the merge, the contents of the “ones” location
at address $4004 are folded into the accumulator using the OR instruction
($39 at address $4057).
Following the merging of the “ten” and “ones” values into the accumulator,
the STA ($99 at address $405A) is used to store the contents of the
accumulator to the output port at address $F023, which is the port driving
the dual 7-segment display. Finally, we use a JMP ($C1 at address $405D)
to direct the CPU to jump back to the beginning of the main body of the
The Hex Keypad 9-21
program at address $4008. Thus, the program will continue to loop around,
reading binary values from the switches and displaying their decimal
equivalents on the 7-segment displays. (Once again, note that the reason
the program returns to location $4008, as opposed to $4005, is because we
only need to initialize the value of the stack pointer a single time. Also note
that we haven’t actually used the stack pointer in the code we’ve described
thus far, but we will ...... Oh yes, we will).
#4
$4053 $70
Shift ACC $70 = Shift the contents of the accumulator
$4054 $70
left by left by one bit, so these four instructions shift
$4055 $70
four bits the accumulator left by four bits
$4056 $70
#1
9
Buzz words come and go with the seasons. Towards the end of the 1980s, the word on
everyone’s lips was paradigm, meaning “pattern” or “model,” such as “I’m working on a
new design paradigm.” Unfortunately, although it sounded clever when one person used it,
it started to sound ridiculous when everyone was spouting forth (often pronouncing it
incorrectly). By the early 1990s, “paradigm” had returned to relative obscurity (which means
that we, the chosen few, can start using it again to sound intelligent).
The Hex Keypad 9-27
JNC Jump if not carry 2 Both absolute (the only mode)
CMPA Compare accumulator 2 Both immediate
INCA Increment accumulator 2 Both implied (the only mode)
SUB Subtract from accumulator 2 Both immediate
OR Logical OR with accumulator 1 Absolute
BLDSP Load stack pointer 1 Big immediate (loads two bytes)
Is there anything we can conclude from this breakdown? This is a trick
question, whose answer is obviously “No.” It isn’t possible to determine
anything useful on the basis of a single program, because a sample of one
item is statistically meaningless. All we can say with any certainty is that
this particular program uses ten different types of instructions, and that each
instruction is used a certain number of times. However, it is interesting to
note that there are more loads and stores than all of the other instructions
combined. You might wish to perform a similar breakdown on future
programs (both ours and your own) to see if you can spot any trends.
xxxx_xx.ram
xxxx_xx.ram
xxxx_xx.ram
lab4_p1.ram
xxxx_xx.ram
xxxx_xx.ram
xxxx_xx.ram
xxxx_xx.ram
Load Cancel
The scrolling list on the left-hand side of this dialog shows all of the
programs that are available for you to load. (When you save your own
9-28 Bebop BYTES Back
programs using the corresponding Save RAM feature, they will also appear
in this list.) The program we’re interested in at the moment is called
“lab4_p1,” so locate this entry in the scrolling list. Now we wish to move
this program into the list of programs to be loaded on the right-hand side of
the dialog. There are two ways to do this as follows:
a) Single-click the “lab4_p1” entry in the scrolling list (the entry will be
highlighted to indicate that it’s been selected), then click the button
with the right-pointing arrow located between the two lists.
b) Double-click the “lab4_p1” entry in the scrolling list, which
automatically moves this entry into the right-hand list.
Whichever of these techniques you use, the right-hand list updates to reflect
the addresses this program will occupy ($4005 through $405F). (Remember
that locations $4000 through $4004 are used by the program to store data,
but they aren’t really a part of the body of the program.) In fact, this dialog
will allow you to simultaneously load a selection of programs into different
portions of the RAM, but we’ll leave that feature for later consideration. For
the moment, simply click the Load button, which will load the selected
program(s) and automatically dismiss the dialog window.
displays light up to show three ‘0’s, because this is the value that’s currently
represented by our 8-bit switches. Click the appropriate switches to build up
an unsigned binary value of 111101102 (which equates to 246 in decimal)
(Figure 9.24).
128's column
64's column
32's column (1 x 128) + (1 x 64) + (1 x 32) + (1 x 16)
16's column + (0 x 8) + (1 x 4) + (1 x 2) + (0 x 1)
8's column
4's column = 246 10
2's column
1's column
Program
Menu bar
but every display you have open on the screen will
have a significant impact on the Beboputer’s speed.
When you’ve finished and are ready to proceed to the next part of this
laboratory, click the keypad’s Reset (Rst) button, which will halt the
program and return the Beboputer to its reset mode.
no
up the number you require. The second point is that once you’ve set up a
number, you might want to listen to it several times. This is where “Big
Red” comes into play (Figure 9.25b). By default, our augmented program
will continue to loop around reading the 8-bit switches and displaying the
equivalent decimal number. However, after displaying this number, we are
going to make the program check the state of “Big Red,” which, if you’ve
pressed it, will cause the number to be spoken.
Resistor LED
Bits 2 to 7
Logic 0
;;
Beboputer’s output ports (Port 5 at address $F025) is connected to the sound
card. When the Beboputer writes a byte of data to this port, the sound card
uses that byte to access a corresponding sound file on your real computer’s
;;
hard disk, and the sound card then plays this file through your real, physical
loudspeaker.
Loudspeaker
Sound card
From Beboputer
To Beboputer
Figure 9.28: The Beboputer’s
To/from main system sound card
As a byte can represent 256 different patterns of logic 0s and logic 1s, we
can potentially use the Beboputer to play 256 different sound files, each of
which could contain an interesting tone, a single word, a monologue, or
Beethoven’s 5th if we so desired. For the purposes of this laboratory, the
sound files we’re interested in are as follows:
Contents of byte from Beboputer Contents of associated sound file
0 10 (Binary 00000000, hex 00) Says the word “zero”
1 10 (Binary 00000001, hex 01) Says the word “one”
2 10 (Binary 00000010, hex 02) Says the word “two”
3 10 (Binary 00000011, hex 03) Says the word “three”
4 10 (Binary 00000100, hex 04) Says the word “four”
5 10 (Binary 00000101, hex 05) Says the word “five”
6 10 (Binary 00000110, hex 06) Says the word “six”
7 10 (Binary 00000111, hex 07) Says the word “seven”
8 10 (Binary 00001000, hex 08) Says the word “eight”
9 10 (Binary 00001001, hex 09) Says the word “nine”
In order to resolve this problem, we have also connected the sound card to
one of the Beboputer’s input ports (Port 5 at address $F005). Much like our
Big Red Switch, the sound card only drives the least-significant bit of this
port (the card drives a logic 0 if it’s free and a logic 1 if it’s busy). Thus, if
the Beboputer reads this port when the sound card isn’t doing anything, it
will receive a value of $00 (or 00000000 in binary); but if the sound card is
currently being used, then the Beboputer will receive a value of $01
(or 00000001 in binary). As we will see, we can use this feature to cause
the Beboputer to wait for the sound card to complete whatever word it’s
currently working on before instructing it to begin speaking the next word.
no yes yes
Say
#6
"and"
after we’ve displayed the results, thereby overwriting the final unconditional
jump back to the start of the main body of the program.
The first thing we do is to check our Big Red Switch. If “Big Red” has
remained undisturbed, we immediately jump back to the start of the main
body of the program (indicated by the “#1” label); but if “Big Red” has been
pressed, then we know we’ve got to speak the number.
Remembering that we stored the original number at the beginning of the
program, it’s easy for us to test whether it was zero, in which case we
simply say “zero” and return to the beginning of the main body of the
program.
If our original number was non-zero, the next thing we do is to check to see
if there are any “hundreds.” If there aren’t any, we can leave this part of the
program and continue on our way (indicated by the “#6” label). If there are
some “hundreds,” then we need to say their number followed by the word
“hundred.” Now we check to see whether there are any “tens” or “ones.” If
there aren’t any we’ve completed our mission, so we return to the beginning
of the main body of the program. Alternatively, if there are some “tens” or
“ones,” we say the word “and” and proceed to the next part of the program
(indicated by the “#6” label).
The next part of our solution (with which we intend to win the “novelty
flowchart of the year” competition) is concerned with speaking the “tens”
and “ones” (Figure 9.30). This is where we decide whether part of our
number falls into the range 11 through 19, in which case we will have to
give it special treatment.
#6
The point to remember is that,
due to the way in which we any Say " eleven "
"10s" yes yes
extracted the “tens” component =1?
"1s" thru #1
? "nineteen "
of the number in our initial
program, we know that the no no
yes any
Say No. Say No.
"1s"
of "1s" of "10s"
?
yes
#1
Figure 9.30: Speak-the-numbers (part 2)
The Hex Keypad 9-39
through 9. Also remember that we’re taking these numbers to represent “no
tens” through “nine tens”; that is, 0 = “no tens,” 1 = “one ten,” 2 = “two
tens,” and so on.
Thus, the first thing we do is to check whether the “tens” value equals 1
(which actually represents 10), and if it does, we next check to see if there
are any “ones.” If both of these tests prove positive, then we know we’re
going to have to say a number in the range “eleven” through “nineteen,”
after which we can return to the beginning of the main body of the
program. But if either of these tests fails, then we can simply proceed to say
the number of “tens” followed by the number of “ones,” and then return to
the beginning of the program.
The only other point to consider is the part of the program that actually says
the numbers. As we previously discussed, in order to cause the Beboputer
to say a word, we have to send a byte to the sound card causing it to play
the appropriate sound file (Figure 9.31).
yes
Since we wish to say
?
words at a number of Enter
Store ACC Read S-Card
$01
no
Exit
to S-Card I/P Port
different points in our ?
program, we decided to
Figure 9.31: Speak-the-numbers
use a subroutine, and the way in “say word” subroutine
which this particular subroutine
works is really simple.(11) The main program is ensures that the correct byte
is stored in the accumulator, then it calls our “say word” subroutine. As
soon as it’s invoked, this subroutine immediately writes the contents of the
accumulator to the output port that’s connected to the sound card. The
subroutine then reads the input port connected to the sound card and
checks to see whether the value it receives is $00 or $01 (the sound card
returns $01 if it’s busy playing a sound file). So the subroutine will keep
looping around checking the sound card until it reads a value of $00, at
which point it knows the sound card has finished playing the current sound
file (saying the current word). The subroutine then terminates and returns
control to the main program at the point from which it was called.
11
Subroutines were introduced in Chapter 8.
9-40 Bebop BYTES Back
we’ll have to be brief.(12) The main points to note about the following listing
are that it’s bolted on to the end of our original program, and that it starts at
address $405D, which means that it overwrites the unconditional jump
instruction we originally used to return to the beginning of the program.
Address Data Instruction Check “Big Red”
$405D $91 LDA (abs) Load the accumulator from the input port
$405E $F0 connected to “Big Red” at address $F002
$405F $02
$4060 $D1 JZ (abs) If the value is $00, then “Big Red” hasn’t been
$4061 $40 pushed, so jump back to the beginning of the
$4062 $08 main body of the program at address $4008
and do it all again
Address Data Instruction Check for zero
$4063 $91 LDA (abs) Load the accumulator from the location
$4064 $40 that we used to store the original value on
$4065 $00 the 8-bit switches at address $4000
$4066 $D6 JNZ (abs) If the value isn’t zero ($00), then jump to
$4067 $40 the “Say Hundreds” portion of the program
$4068 $6F at address $406F, otherwise continue to
the next instruction at $4069
$4069 $C9 JSR (abs) Jump to the “Say Word” subroutine at
$406A $40 address $40BB to say “zero” (because ACC
$406B $BB contains $00). Note that the return address
$406C is automatically saved on the stack
$406C $C1 JMP (abs) Jump back to the beginning of the main
$406D $40 body of the program at address $4008 and
$406E $08 do it all again
Address Data Instruction Say “hundreds” (if any)
$406F $91 LDA (abs) Load the accumulator with the contents of
$4070 $40 the “hundreds” location at address $4002
$4071 $02
$4072 $D1 JZ (abs) If the value is zero, then no “hundreds”, so
$4073 $40 jump to the “Eleven through Nineteen” part
$4074 $8E of the program at address $408E, otherwise
continue to address $4075
$4075 $C9 JSR (abs) Jump to the “Say Word” subroutine at address
$4076 $40 $40BB to say the number of “hundreds”. Note
$4077 $BB that the return address of $4078 is automatically
saved on the stack
12
”I will be so brief that, in fact, I’ve already finished!” – Salvador Dali, on being invited to
say a “few brief words.”
The Hex Keypad 9-41
$4078 $90 LDA (imm) Load the accumulator with $1E, which, when
$4079 $1E sent to the sound card, will cause it to say the
word “hundred”
$407A $C9 JSR (abs) Jump to the “Say Word” subroutine at
$407B $40 address $40BB to say the word “hundred”.
$407C $BB Note that the return address of $407D is
automatically saved on the stack
Address Data Instruction Do we need to say “and” ?
$407D $91 LDA (abs) Load the accumulator with the contents of
$407E $40 the “tens” location at address $4003
$407F $03
$4080 $D6 JNZ (abs) If the value isn’t zero, then there are some“tens”,
$4081 $40 so jump to the part of the program in charge of
$4082 $89 saying the word “and” at $4089, otherwise continue
to $4083
$4083 $91 LDA (abs) Load the accumulator with the contents of
$4084 $40 the “ones” location at address $4003
$4085 $04
$4086 $D1 JZ (abs) If the value is zero, then there aren’t any “ones”
$4087 $40 either, so jump back to the beginning of the
$4088 $08 program at address $4008, otherwise continue to
$4089
$4089 $90 LDA (imm) Load the accumulator with $20, which, when
$408A $20 sent to the sound card, will cause it to say the
word “and”
$408B $C9 JSR (abs) Jump to the “Say Word” subroutine at address
$408C $40 $40BB to say the word “and”. Note that the
$408D $BB return address of $408E is automatically saved on
the stack
Address Data Instruction Check for “eleven” through “nineteen”
$408E $91 LDA (abs) Load the accumulator with the contents of
$408F $40 the “tens” location at address $4003
$4090 $03
$4091 $60 CMPA(imm) Compare the contents of the accumulator
$4092 $01 with $01 to see whether we have one “ten”
$4093 $D6 JNZ (abs) If the result of the comparison isn’t zero, then
$4094 $40 there’s either no “tens” or more than one “ten”,
$4095 $A4 so jump to address $40A4, otherwise continue to
address $4096
$4096 $91 LDA (abs) Obviously we’ve got one “ten”, so now
$4097 $40 load the accumulator with the contents of
$4098 $04 the “ones” location at address $4004
9-42 Bebop BYTES Back
$4099 $D1 JZ (abs) If the value of “ones” is zero, then we don’t have
$409A $40 a number between “eleven” and “nineteen”, so
$409B $A4 jump to the “tens and ones” at $40A4, else
continue to $409C
$409C $10 ADD (imm) We know we’ve got a number between “eleven”
$409D $0A and “nineteen”, so add $0A (decimal 10) to the
accumulator
$409E $C9 JSR (abs) Jump to the “Say Word” subroutine at address
$409F $40 $40BB to say a number between “eleven” and
$40A0 $BB “nineteen”. Note the return address $40A1 is
automatically saved on the stack
$40A1 $C1 JMP (abs) As the number was between “eleven” and
$40A2 $40 “nineteen”, our job here is done, so jump
$40A3 $08 back to the beginning of the program at
address $4008
Address Data Instruction Say “tens” and “ones”
$40A4 $91 LDA (abs) Load the accumulator with the contents of
$40A5 $40 the “tens” location at address $4003
$40A6 $03
$40A7 $D1 JZ (abs) If the value of “tens” is zero, then we don’t have
$40A8 $40 any of them, so jump to the bit in charge of saying
$40A9 $AF the “ones” at $40AF, otherwise continue to
address $40AA
$40AA $10 ADD (imm) We know that we’ve got some number of
$40AB $14 “tens”, so add $14 (decimal 20) to the
accumulator
$40AC $C9 JSR (abs) Jump to the “Say Word” subroutine at address
$40AD $40 $40BB to say “ten”, “twenty”, “thirty”, or
$40AE $BB whatever. Note that the return address $40AF
is automatically saved on the stack
$40AF $91 LDA (abs) Load the accumulator with the contents of
$40B0 $40 the “ones” location at address $4004
$40B1 $04
$40B2 $D1 JZ (abs) If the value of “ones” is zero, then we don’t have
$40B3 $40 any of them, so jump back to the beginning
$40B4 $08 of the program at address $4008, otherwise
continue to address $40B5
$40B5 $C9 JSR (abs) Jump to the “Say Word” subroutine at address
$40B6 $40 $40BB to say “one”, “two”, “three”, or whatever.
$40B7 $BB Note that the return address $40B8 is
automatically saved on the stack
$40B8 $C1 JMP (abs) Jump back to the beginning of the main body
$40B9 $40 of the program at address $4008, and do it all
$40BA $08 again, and again, and ...
The Hex Keypad 9-43
Address Data Instruction Code for “say word” subroutine
$40BB $99 STA (abs) Store (send) whatever value is currently in
$40BC $F0 the accumulator to the output port that’s
$40BD $25 connected to the sound card at address $F025
$40BE $91 LDA (abs) Load the accumulator from the input port that’s
$40BF $F0 connected to the sound card at address $F005
$40C0 $05
$40C1 $D6 JNZ (abs) If the value is not zero, then the sound card is
$40C2 $40 still busy saying a word, so jump back to address
$40C3 $BE $40BE and try again, otherwise continue to
address $40C4
$40C4 $CF RTS (imp) Automatically retrieves the return address from the
stack (the point from which the subroutine was
called) and returns to that address.
It’s fair to say that a program listing like the one shown here can be a bit
hairy the first time you see it. However, if you’ve diligently stuck with us
from the beginning of the book, then you should be fairly comfortable with
most of it. In fact the only points that might be a little puzzling occur at
addresses $409C and $40AA, where we add something to the contents of
the accumulator just before we call our “say word” subroutine.
As it happens, the reason for these additions is fairly simple. By the time we
get to the part of the program at address $409C, we know that we’re going
to have to say a number between “eleven” and “nineteen,” and we also
know that the accumulator currently contains the number of “ones.” If you
glance back to the list of sound files earlier in this chapter, you’ll see that
the bytes that we send to the sound card for the words “eleven” through
“nineteen” are numbered 11 through 19 (or $0B through $13 in
hexadecimal). So, if we add $0A (decimal 10) to the contents of the
accumulator, this will give us the value we need to send to the sound card.
Similarly, by the time we get to the part of the program at address $40AA,
we know that we have some “tens,” and we also know that the accumulator
contains the number of tens in the form 1 = “one ten,” 2 = “two tens,”
3 = “three tens,” and so on. Obviously, what we really want to do is to say
a word like “ten,” “twenty,” “thirty,” and so forth. If you glance back to the
list of sound files, you’ll see that the bytes we send to the sound card for the
words “ten,” “twenty,” “thirty,” ...... through “ninety” are numbered 21
through 29 (or $15 through $1D in hexadecimal). So if we add $14
(decimal 20) to the contents of the accumulator, this will give us the value
we need to send to the sound card.
9-44 Bebop BYTES Back
Quick quiz #9
1) What are the advantages and disadvantages of the hex keypad
compared to the switch and display panels?
2) How many diodes would be required to encode the hex keypad’s
buttons (excluding the ON/OFF and Reset buttons) using the mapping
described in this chapter?
3) What are the advantages of using hierarchical flowcharts?
4) Why did we choose to load the stack pointer with $4FFF at the
beginning of the program?
5) In the “Initializing the Variables” section of the program, we noted that it
wasn’t strictly necessary to initialize the variable in memory location
$4004. Why is this the case?
6) Describe how a JNC (“jump not carry”) instruction works.
7) When we were extracting the “hundreds” and the “tens,” we compared
the value in the accumulator to 99 and 9, respectively. Why didn’t we
compare the value in the accumulator to 100 and 10, respectively?
9-46 Bebop BYTES Back
8) What would you expect to happen if you click on “Big Red” while the
Beboputer is speaking a number? Check whether what actually happens
is what you expected to happen.
9) Why is it easier to map from binary to hexadecimal than it is to map
from binary to decimal?
10) The programs in this chapter assumed that the 8-bit switches represented
unsigned binary numbers in the range 010 through 25510. How would
you approach rewriting these programs such that they treated the 8-bit
switches as representing signed binary numbers in the range -12810
through +12710?
Chapter
10
The QWERTY Keyboard
In this chapter we will discover:
Why the first commercial typewriters looked like
sewing machines
▼
How typewriters and Morse telegraphs were
combined to form early printing telegraphs
▼
How printing telegraphs mutated into
teleprinters, which themselves evolved into
computer keyboards
▼
Why keys on computer keyboards are laid out
in such a user-unfriendly manner
▼
What we mean by the ASCII code, and how a
computer keyboard might use it
▼
5
How to program computer keyboards so as
to be usable by one-handed typists
b
La
10-2 Bebop BYTES Back
Contents of Chapter 10
Why is a typewriter like a sewing machine? ............................................... 10-3
The Sholes (QWERTY) keyboard ...................................................................................... 10-5
The Dvorak keyboard ..................................................................................................................... 10-6
The printing telegraph ................................................................................................................... 10-9
The advent of the teleprinter ........................................................................................... 10-10
The computer keyboard ......................................................................................................... 10-12
The ASCII code ...................................................................................................................................... 10-13
Winding up the Beboputer .................................................................................................. 10-15
Program to read the QWERTY keyboard ....................................................... 10-16
Entering the program with the hex keypad ................................... 10-17
Running the program with the hex keypad ................................... 10-18
Programmable and one-handed keyboards ......................................... 10-21
Quick quiz #10 ........................................................................................................................................ 10-22
The QWERTY Keyboard 10-3
Why is a typewriter like a sewing
machine?
One of the most ubiquitous techniques for interactively entering data into a
computer is by means of a keyboard. However, although the fingers of an
expert typist leap from key to key with the agility of a mountain goat and the
dexterity of a concert pianist, newcomers usually spend the vast bulk of
their time desperately trying to locate the next key they wish to press. It is
actually not uncommon for strong words to ensue describing the way in
which the keys are laid out, including the assertion that whoever came up
with the scheme we employ must have been a blithering idiot. So why is it
that a device we use so much is constructed in such a way that anyone who
lays their hands on one is immediately reduced to uttering expletives and
banging their heads against the nearest wall? Ah, there’s the question and,
as with so many things, the answer is shrouded in the mists of time ....
The first references to what we would call a typewriter are buried in the
records of the British patent office. In 1714, by the grace of Queen Anne, a
patent was granted to the English engineer Henry Mill. In a brave attempt
towards the longest sentence in the English language with the minimum use
of punctuation, the wording of this patent’s title was: “An artificial machine
or method for the impressing or transcribing of letters singly or progressively
one after another, as in writing, whereby all writing whatever may be
engrossed in paper or parchment so neat and exact as not to be
distinguished from print.” Unfortunately, after all the labors of the long-
suffering patent clerk (a man who could have benefited from access to a
typewriter if ever there was one), Mill never got around to actually
manufacturing his machine.
Following a few sporadic attempts from different parts of the globe, the first
American patent for a typewriter was granted in 1829 to William Austin Burt
from Detroit. However, the path of the inventor is rarely a smooth one as
Burt was to discover. We may only picture the scene in the patent office:
Clark: “Hello there, Mr. Burt, I have both good news and bad news.
Which would you like first sir?”
Burt: “I think I’ll take the good news, if it’s all the same to you.”
Clark: “Well the good news is that you’ve been granted a patent for
the device which you are pleased to call your Typographer.”
Burt: “Good grief, I’m tickled pink, and the bad news?”
10-4 Bebop BYTES Back
Clark: “Sad to relate, the only existing model of your machine was
destroyed in a fire at our Washington patent office!”
To be perfectly honest, the patent office (along with the Typographer) burned
down in 1836, seven years after Burt received his patent. But as we all learn
at our mother’s knee, one should never allow awkward facts to get in the
way of a good story.
As fate would have it, the fire probably caused no great loss to civilization
as we know it. Burt’s first Typographer was notably ungainly, so much so
that it was possible to write much faster than one could type with this
device. Undaunted, Burt produced a second model the size of a present-day
pinball machine, which, if nothing else, would have made an interesting
conversation piece if given to a friend as a Christmas stocking-filler. Perhaps
not surprisingly, no one was interested, and Burt eventually exited the stage
to make room for younger contenders.
Following Burt, numerous other innovators leapt into the fray with truly
Heath Robinson offerings. Some of these weird and wonderful contraptions
were as difficult to use as a Church organ, while others printed by keeping
the paper stationary and hurling the rest of the machine against it ...... the
mind boggles.
The first practical machine was conceived by three American inventors and
friends who spent their evenings tinkering together. In 1867, Christopher
Latham Sholes, Carlos Glidden, and Samual W. Soule invented what they
called the Type-Writer (the hyphen was discarded some years later). Soule
eventually dropped out, but the others kept at it, producing close to thirty
experimental models over the next five years. Unfortunately, Sholes and
Glidden never really capitalized on their invention, but instead sold the
rights to a smooth-talking entrepreneur called James Densmore, who, in
1873, entered into a contract with a gun and sewing machine manufacturer
to produce the device.
Strangely, the manufacturers, E. Remington and Sons from the state of New
York, had no experience building typewriters. This was primarily because
no one had ever produced a typewriter before, but there’s also the fact that
(let’s face it) Remington and Sons made guns and sewing machines. The first
thing they did was to hunt for the best artist-mechanic they could find, and
they eventually settled on William K. Jenne. However, Jenne’s expertise was
in the design of sewing machines, with the result that the world’s first
commercial typewriter, released in 1874, ended up with a foot pedal to
advance the paper and sweet little flowers on the sides!
The QWERTY Keyboard 10-5
The Sholes (QWERTY) keyboard
It is commonly believed that the original layout of keys on a typewriter was
intended to slow the typist down, but this isn’t strictly true. Sholes and
Glidden obviously wished to make their typewriters as fast as possible in
order to convince people to use them. However, one problem with the first
machines was that the keys jammed when the operator typed at any real
speed, so Sholes invented what was to become known as the Sholes
keyboard (Figure 10.1).
Blank keys are
punctuation and
2 3 4 5 6 7 8 9 0 special characters
Q W E R T Y U I O P 52%
A S D F G H J K L 32%
Z X C V B N M 16%
SPACE
Usage of alpha
characters by row
typewriter (in which uppercase and lowercase letters are made available on
the same key) didn’t appear on the market until 1878, and it was quickly
challenged by another flavor which contained twice the number of keys,
one for every uppercase and lowercase character. For quite some time these
two alternatives vied for the hearts and minds of the typing fraternity, but
the advent of a technique known as touch-typing favored the shift-key
solution, which thereafter reigned supreme.
Speaking of which, Figure 10.1 shows the ‘A’, ‘S’, ‘D’, and ‘F’ keys in white
to indicate that these are the home keys for the left hand. Similarly, the
other four keys shown in white are the home keys for the right hand. The
terms home keys and home row refer to the base position for your fingers
(excluding thumbs, which are used to hit the space bar) when you’re
practicing touch typing, which means that you type by touch without
looking at the keyboard.
However, Sholes didn’t invent these terms, because he actually gave very
little thought to the way in which people would use his invention. The end
result was that everyone was left to their own devices, effectively meaning
that two-fingered typists using the “hunt-and-peck” method ruled the world.
It was not until 1888 that a law clerk named Frank E. McGurrin won a
highly publicized typing contest with his self-taught touch-typing technique,
and a new era was born.(3)
Finally, lest you still feel that the QWERTY keyboard is an unduly harsh
punishment that’s been sent to try us, it’s worth remembering that the early
users had a much harder time than we do, not the least that they couldn’t
even see what they were typing! The first typewriters struck the paper from
the underside, which obliged their operators to raise the carriage whenever
they wished to see what had just been typed, and so-called “visible-writing”
machines didn’t become available until 1883.
P Y F G C R L 22%
A O E U I D H T N S 70%
Q J K X B M W V Z 8%
SPACE
Usage of alpha
characters by row
Note that Dvorak’s keyboard had shift keys, but they are omitted here for
reasons of clarity. The results of Dvorak’s innovations were tremendously
effective. Using his layout, the typist’s fingers spend 70% of their time on
the home row and 80% of this time on their home keys. Thus, as compared
to the approximately 120 words that can be constructed from the home row
keys of the QWERTY keyboard, it is possible to construct more than 3,000
words on Dvorak’s home row (or 10,000 words if you’re talking to someone
who’s trying to sell you one). Also, Dvorak’s scheme reduces the motion of
the hands by a factor of three, and improves typing accuracy and speed by
approximately 50%, and 20%, respectively.
10-8 Bebop BYTES Back
It was the best of times .... .... it was the worst of times ....
In a 1930s survey of 16 year Typewriter salesmen in the early
old girls across the United 1900s were held in the same
States, 32% stated that they contempt that accident-chasing
wanted to grow up to be lawyers are today,
typists (which was seen as a because they used to pursue
glamorous profession) as fire-fighters in the hope of selling
opposed to only 5% who new typewriters to
wanted to be film stars. burned-out businesses.
.... and, all things considered, there were some pretty weird
times as well ....
In the early 1920s, in order to indicate that businessmen were
very busy, it became common for their letters to close with the
words: “Dictated but not signed.” In an effort at one-upmanship
this was soon supplanted by the words: “Dictated but not read.”
Finally, in a surrealist attempt at Monty-Python foolishness, the
yuppie-equivalent of the era started to use the words: “Dictated
by transatlantic telephone and recorded on tape but not read and
not signed,” which, when you think about it, was almost a letter in
itself! (Lawyers occasionally use similar terms to this day, thereby
protecting themselves from inadvertent dictation and transcription
errors; the result being that irrespective of how badly they do
their job ...... it’s not their fault.)
4
For example, while penning these words, I found out that Barnaby, my co-author’s son, uses
a Dvorak keyboard at his junior high school (you could have knocked me down with an
ostrich).
The QWERTY Keyboard 10-9
The printing telegraph
As we discussed in Chapter 4, the first telegraph machines were invented in
1837 by Sir Charles Wheatstone in England and Samuel Finley Breese
Morse in America. Morse’s machine was eventually adopted as the
standard, because it was simpler, easier to construct, and more reliable.
Morse’s original machines kept a record of incoming messages using an
electromechanically controlled pencil that made marks on a moving strip of
paper. The paper was driven by clockwork, while the lengths of the marks
corresponded to the dots and dashes used in Morse Code. However,
operators quickly realized that they could recognize the message by sound
alone, so Morse’s recording devices returned to the nether regions from
whence they came.
Throughout the rest of the 1800s there continued to be a strong interest in
the idea of a printing telegraph. Much of the work toward realizing this
dream was based on the concept of a wheel with characters embossed
around its periphery. The idea was to use the incoming telegraph signals to
spin the wheel by fixed steps until the correct character faced the paper,
and to then propel that character onto an inked tape located in front of the
paper. There were a variety of techniques for controlling the wheel, such as
a single pulse for ‘A’, two pulses for ‘B’, three for ‘C’, and so on, with the
wheel returning to a home position after each character, but this technique
was very slow in terms of words-per-minute. Later techniques used a five-bit
code created by the French inventor Jean Maurice Emile Baudot in 1880,
which soon became known as the Baudot Code. The two-channel paper
tape technique pioneered by Sir Charles Wheatstone (see Chapter 4) was
subsequently extended to handle the Baudot Code (Figure 10.3).
LINE FEED
CAR. RET.
THRU
BELL
CITY
SPACE
LTRS.
Lowercase A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Feed holes
along the path toward the interactive way in which we use computers
today. By the middle of the 1960s, computers had become so powerful that
many operators could use the same machine simultaneously, and a new
concept called time-sharing was born. The computer could switch between
users so quickly that each user had the illusion they had sole access to the
machine. (Strangely enough, time-sharing is now only practiced in large
computing installations, because computers have become so powerful and
so cheap that everyone can have a dedicated processor for themselves.)
However, the days of the teleprinter in the computing industry were
numbered; they were eventually supplanted by the combination of
computer keyboards and video displays, and the sound of teleprinters
chuntering away in the back of computer rooms is now little more than
a nostalgic memory.
CPU Connecter
Hex keypad
$00 NUL $10 DLE $20 SP $30 0 $40 @ $50 P $60 ` $70 p
$01 SOH $11 DC1 $21 ! $31 1 $41 A $51 Q $61 a $71 q
$02 STX $12 DC2 $22 " $32 2 $42 B $52 R $62 b $72 r
$03 ETX $13 DC3 $23 # $33 3 $43 C $53 S $63 c $73 s
$04 EOT $14 DC4 $24 $ $34 4 $44 D $54 T $64 d $74 t
$05 ENQ $15 NAK $25 % $35 5 $45 E $55 U $65 e $75 u
$06 ACK $16 SYN $26 & $36 6 $46 F $56 V $66 f $76 v
$07 BEL $17 ETB $27 ' $37 7 $47 G $57 W $67 g $77 w
$08 BS $18 CAN $28 ( $38 8 $48 H $58 X $68 h $78 x
$09 HT $19 EM $29 ) $39 9 $49 I $59 Y $69 i $79 y
$0A LF $1A SUB $2A * $3A : $4A J $5A Z $6A j $7A z
$0B VT $1B ESC $2B + $3B ; $4B K $5B [ $6B k $7B {
$0C FF $1C FS $2C , $3C < $4C L $5C \ $6C l $7C |
$0D CR $1D GS $2D - $3D = $4D M $5D ] $6D m $7D }
$0E SO $1E RS $2E . $3E > $4E N $5E ^ $6E n $7E ~
$0F SI $1F US $2F / $3F ? $4F O $5F _ $6F o $7F DEL
5
To be more precise, this form of parity check will detect an odd number of errors (one bit,
three bits, five bits, and so on), but an even number of errors will cancel each other out.
10-16 Bebop BYTES Back
Title Bar
Beboputer Computer
File Setup Display Memory T ools H elp Menu Bar
Tool Bar
QWERTY Keyboard
Hex Keypad
Memory
Walker
7-Segment Display
Status Bar
As we see, the hex keypad and our old friends the memory walker and
7-segment displays have now been joined by the Beboputer’s virtual
QWERTY keyboard.
no
$4006 $99 $99 = Store the accumulator to the
Store ACC
$4007 $F0 address pointed to by the following two
to LEDs
$4008 $23 bytes (that is, $F023)
When a key is pressed, its non-zero ASCII code will be stored in the
keyboard’s 8-bit latch. The next time the program loads the accumulator
from the keyboard, the JZ instruction at address $4003 will fail, and the
program will continue on to the next instruction. This instruction ($99 at
address $4006) is a STA, which causes the Beboputer to store the value in
the accumulator to address $F023 (the address of the output port driving the
dual 7-segment displays). The fourth and last instruction ($C1 at address
$4009) is a JMP (“unconditional jump”), which causes the program to
return to address $4000 and start all over again.
One of the most important points to remember with regard to this program
is that the act of reading a value from the keyboard causes the latch inside
the keyboard to be reset to a $00 value. Different systems work in
significantly different ways, but this is how we’ve designed our virtual
machine. Thus, once we’ve copied a key’s hexadecimal value to our dual
7-segment display, the fact that we now have $00 stored in the keyboard’s
latch will once again cause the program to wait for us to press another key.
The first instruction in our program occurs at address $4000, so this is the
address that we need to enter. Remember that the keypad automatically
uses hexadecimal values, so click a ‘4’ followed by three ‘0’s and watch the
address build up in the address field. Also remember that, should you make
a mistake at any time, you can clear the address field by clicking the
keypad’s Clear (Clr) button.
Now click the keypad’s Data (Da) button to make this field active, then click
a ‘9’ followed by a ‘1’ and watch the data build up in the data field. To
complete this operation, click the keypad’s Enter (Ent) button. This causes
the monitor program to load the data displayed in the data field ($91) into
the memory location specified by the address field ($4000). The monitor
program automatically increments the value in the address field and the
Data (Da) button remains active, which allows you to alternate between
inputting data and clicking the Enter button. Continue to enter the
remainder of the program (from address $4001 onwards) as shown below.
Address Data Instruction Read keyboard and display ASCII value
$4000 $91 LDA (abs) Load the accumulator from the input port
$4001 $F0 connected to the keyboard at address $F008
$4002 $08
$4003 $D1 JZ (abs) If the value is $00, then no key has been pushed,
$4004 $40 so jump back to the beginning of the program
4005 $00 at address $4000 and do it all again
$4006 $99 STA (abs) Store the accumulator to the output port
$4007 $F0 connected to the dual 7-segment display
$4008 $23 at address $F023
$4009 $C1 JMP (abs) Jump back to the beginning of the program at
$400A $40 address $4000 and do the whole thing again
$400B $00
_
~ 1 2 3 4 5 6 7 8 9 0 = BSpace
TAB Q W E R T Y U I O P [ ] \
SHIFT Z X C V B N M , . /
Click the CAPS key and note that its annotation turns black to indicate that
this key is no longer active. Note that nothing happens to the dual
7-segment display. This is because CAPS is a modifier key, which means
that it doesn’t generate a code of its own, but rather modifies the codes
associated with some of the other keys. If the CAPS key is inactive, then the
letter keys generate the codes for lowercase letters. Click the ‘A’ key again,
and note that the 7-segment display now shows $61, which is the ASCII
code for a lowercase ‘a’.
SHIFT, CTRL, and ALT also act as modifier keys. On a real keyboard these
keys would not be sticky, which means that you’d have to continue to hold
them while you pressed another key. However, we can’t emulate this on
our virtual keyboard, so we’ve caused these keys to act like sticky keys.
Click the SHIFT key and note that its annotation turns red, thereby
indicating that this key is now active. Once again note that nothing
happened to the dual 7-segment display, because SHIFT is a modifier key.
10-20 Bebop BYTES Back
Click the ‘A’ key again and note that the 7-segment display now shows $41,
which is the ASCII code for an uppercase ‘A’. In this respect the SHIFT key
acts like the CAPS key. However, unlike CAPS which only affects the letter
keys, the SHIFT key on a real keyboard affects other characters as well. On
your main computer’s keyboard you will note that the key for the number
‘4’ also shows the ‘$ symbol (at least it does in America; keyboards in other
countries may sport different symbols). In the real world the shift key would
also allow you to select between these two characters, but we didn’t
implement this feature on the Beboputer’s keyboard. Now click both the
SHIFT and CAPS keys to return them to their inactive and active states,
respectively.
Compared to the SHIFT and CAPS keys, the actions of the CTRL and ALT
keys are not quite so well defined, in that they tend to behave differently on
different systems. In the case of the Beboputer, we’ve chosen to follow a
reasonably intuitive scheme. Click the CTRL key to make it active, then
click the ‘A’ key. The 7-segment display now shows $01, which is the ASCII
value for the SOH control code. In fact, in our system the CTRL key only
modifies the alphabetical keys such that ‘A’ generates $01, ‘B’ generates
$02, ‘C’ generates $03, and so on (note that identical codes are generated
for both uppercase and lowercase versions of each letter).
Now click the CTRL key to return it to its inactive state, then click the ALT
key to make it active. In our system, the ALT key modifies all of the keys
(except the cursor control keys)(6) by simply adding the value $80 (128 in
decimal) to whatever codes they would have generated had the ALT key
been inactive. For example, click on the ‘A’ key and note that the 7-segment
display now shows $C1, which is the sum of $41 and $80.
Continue to play around, pressing various key combinations until you’re
comfortable with the way in which our keyboard works. When you’ve had
enough fun, click the keypad’s ON button (which also turns it OFF), then
exit this laboratory by clicking the File item on the project window’s menu
bar followed by the Exit entry in the resulting pull-down menu.
6
The Beboputer’s cursor control keys, which are indicated as arrows in Figure 10.7, generate
the codes $D2, $D3, $D0, and $D1, for “right,” “left,” “up,” and “down,” respectively (see
also Chapter 11).
The QWERTY Keyboard 10-21
Programmable and one-handed
keyboards
That’s pretty much the end of this lab, but it’s certainly not the end of the
ASCII code, because we’ll be using our QWERTY keyboard to drive a virtual
terminal in the next chapter. But before we proceed, there are a couple of
interesting points that deserve mention. First, early keyboards employed
arrays of diodes to generate their codes, using similar techniques to those
presented in the previous chapter. By comparison, modern keyboards are
significantly more sophisticated; some of them are re-programmable and
may even contain their own dedicated microprocessor. Having a re-
programmable keyboard offers such capabilities as being able to change the
codes that are generated by each key (for example, allowing you to
reprogram your ‘F’ key to make it generate the code for a ‘U’), and some
allow a single key-press to generate an entire string of characters.
Now this may not strike you as being amazingly useful at first, but it implies
all sorts of possibilities. For example, you could reprogram your entire
keyboard so as to act in the Dvorak style (of course, you’d have to re-label
your keys as well). Even if your keyboard is not re-programmable, you can
achieve the same results by writing a program that modifies the codes as it
reads them from the keyboard; in fact you can obtain such software
commercially. You can also purchase software that makes your keyboard
suitable for one-handed typists. One such program is called Half-QWERTY(7)
and is available from the Matias Corporation, Rexdale, Ontario, Canada
(Figure 10.8).
This figure only shows the half of the keyboard that would be used by a left-
handed one-handed typist (and we’ve omitted some of the annotations for
clarity). With this portion of the keyboard you
use your left hand in the normal position to !
1 0
@
2 9
#
3 8
$
4 7
%
5 6
get the standard results. However, if you hold
Tab Q W E R T
the space bar down while pressing another Delete P O I U Y
But we’ve digressed again. Let’s close this chapter with a few things that are
really quite useful to remember, such as the fact that the ASCII code for ‘A’
is $41 (from which you can work out all of the other uppercase letters), and
that in order to calculate the code for a lowercase letter you need only add
$20 to the code for its uppercase counterpart. Similarly, it’s useful to
remember that the ASCII code for the number ‘0’ is $30 (which lets you
work out all of the other numbers). Of course, it would be handy to
memorize the complete table, but by remembering just these three codes
you can quickly work out the codes for sixty-two of the characters in your
head. It also usually comes in handy to remember that the ASCII code for a
space is $20 and the code for an ESC (“Escape”) character is $1B. Try to
memorize these few codes now, because we’ll be using them in the next
chapter.
Contents of Chapter 11
How to display moving pictures on a toaster .............................................. 11-3
The Nipkow disk ......................................................................................................................................... 11-4
The cathode ray tube (CRT) .................................................................................................. 11-5
Philo Farnsworth – a man lost in history ................................................................. 11-8
The video tube ....................................................................................................................................... 11-10
Color vision – one of nature’s wonders ............................................................. 11-12
Visual display units (VDUs) ..................................................................................................... 11-20
The memory-mapped display ......................................................................................... 11-23
Winding up the Beboputer ................................................................................................... 11-28
Program to clear the screen ............................................................................................. 11-29
Program to display all characters ............................................................................. 11-34
Program to display characters from the QWERTY
keyboard ..................................................................................................................................................... 11-40
Creating a very simple text editing program ........................................... 11-44
The medium-resolution color graphics mode .......................................... 11-50
The video card’s control codes ...................................................................................... 11-51
Program to flaunt the medium-resolution mode ................................ 11-54
Memory-mapped displays versus modern monitors ...................... 11-58
So, just what is a pixel anyway? ................................................................................... 11-59
Quick quiz #11 ......................................................................................................................................... 11-60
The Memory-Mapped Display 11-3
How to display moving pictures on a
toaster
Eeeek Alors! If you think that the stuff we’ve already done has been
interesting, then this lab is going to blow your socks off! The whole basis of
the Beboputer is that it’s a virtual machine that exists only in your physical
computer’s memory, and the only way to see the Beboputer is on the your
real computer’s screen. Displaying information on a screen is an incredibly
efficient way for a computer to communicate with us. However, as is often
the case, engineers employed an existing technology that was developed for
an entirely different purpose ...... television.
Television, which comes from the Greek tele, meaning “distant,” and the
Latin vision, meaning “seeing” or “sight,” has arguably become one of the
wonders of the 20th Century, so you may be surprised to learn that
television’s origins are firmly rooted in the Victorian era. In fact one of the
earliest examples of an image being captured, transmitted, and reproduced
by electromechanical means occurred in 1842, only five years after Queen
Victoria had ascended to the throne, when a Scotsman, Alexander Bain,
came up with a rather ingenious idea.
Bain created an image to be transmitted by snipping it out of a thin sheet of
tin, placing it on a moveable base, and connecting it to one side of a
battery. He then created a pendulum using a conducting metal wire and a
weight ending in a sharp point, and set this device swinging above the base.
The base was slowly moved under the pendulum, where the swinging
weight made periodic contact with the metal image, thereby completing the
electrical circuit and converting the dark and light areas of the image
(represented by the presence and absence of tin) into an electrical signal.
Bain then used this signal to control a relay, which was moving back and
forth in time with the pendulum. When activated, the relay pushed a pencil
down onto a piece of paper mounted on a second base moving at the same
rate as the first, thereby reproducing the image as a pencil drawing.
Obviously, Bain’s device had little application with regard to the
transmission of moving pictures, but it certainly wasn’t a wasted effort,
because he had essentially created the precursor to the modern Fax
machine. Sometime later in 1878, one Denis Redmond of Dublin, Ireland,
penned a letter to the English Mechanic and World of Science publication.
In his letter, Redmond described creating an array of selenium photocells,
each of which was connected via a voltage source to a corresponding
11-4 Bebop BYTES Back
Phototube
Nipkow’s idea was both simple
and cunning. A strong light
Photographic source was used to project a
Light image
source
photographic image onto the
surface of the Nipkow Disk,
Direction of which was spinning around.
rotation
As the outermost hole on the
disk passed through the image,
the light from the light source
Figure 11.1: Nipkow disk for a 16-line picture
passed through the hole to hit a
light-sensitive cell, such as a
silver-caesium phototube. The intensity of the light was modified by the
light and dark areas in the image as the hole traveled past, thereby
modulating the electrical signal generated by the phototube. The holes were
arranged such that as soon as the outermost hole had exited the image, the
The Memory-Mapped Display 11-5
next hole began its trek. Since the holes were arranged in a spiral formation,
each hole traversed a different slice, or line, across the image.
At the other end of the process was a brilliant lamp and a second spinning
Nipkow Disk. The electrical signal coming out of the phototube was used to
modulate the lamp, which was projected onto the second disk.
The modulated light passed through the holes in the second disk to
construct a line-by-line display on a screen. Although the resulting image
was constructed as a series of lines, the speed of the disk combined with
persistence of vision meant that an observer saw a reasonable (albeit low-
resolution) facsimile of the original picture. (The concept of persistence of
vision is discussed in a little more detail below.)
Leaping ahead to the year 1895, the Italian electrical engineer and inventor
Guglielmo Marconi extended the earlier research of such notables as the
British physicist James Clerk Maxwell and the German physicist Heinrich
Hertz by inventing the forerunner of radio as we know it today. In the early
part of the twentieth century, engineers constructed experimental systems
that could transmit images using a combination of Nipkow’s disks and radio
signals. The electrical signal coming out of the phototube was merged with
a synchronization pulse (which indicated the start of a rotation), and this
combined signal was then used to modulate the carrier wave from a radio
transmitter. At the other end of the process was a radio receiver and a
second spinning Nipkow Disk. The receiver first separated the
synchronization pulse from the video signal, and the synchronization pulse
was then used to ensure that the receiver disk was synchronized to the
transmitter disk. Meanwhile, the amplified video signal was once again used
to modulate a brilliant lamp, which was projected through holes in the
receiver disk to construct a line-by-line display on a screen.
tube called a triode, which could be used to amplify electronic signals (see
also Chapter 15). Even so, progress was hard fought for, and it wasn’t until
the latter half of the 1920s that the first rudimentary television systems based
on cathode ray tubes became operational in the laboratory.
The principles behind the cathode ray tube are quite simple (although
actually building one is less than trivial). The tube itself is formed from
glass, from which the air is evacuated to leave a strong vacuum
(Figure 11.2a). In the rear of the tube is a device called an electron gun,
which generates electrons. A positively charged grid mounted a little way in
front of the electron gun focuses the electrons into a beam and accelerates
them towards the screen. Thus, the name “cathode ray tube” is derived from
the electron gun (which forms the negative terminal, or cathode), the
electron beam (or ray), and the glass enclosure (or tube).
Vertical deflection plate
Electron gun
(cathode)
Grid
(anode)
Electron beam
Fluorescent layer
lining inside of screen
(b) Path of electron beam
(a) Cross-section of cathode ray tube (raster scan technique)
The inside face of the screen is lined with a layer of material called a
phosphor, which has the ability to fluoresce. Hmmm, this is going to take a
moment to explain. Phosphors are distinguished by the fact that when they
absorb energy from some source such as an electron beam, they release a
portion of this energy in the form of light. Depending on the material being
used, the time it takes to release the energy can be short (less than one-
hundred-thousandth of a second) or long (several hours). The effect from a
short-duration phosphor is known as fluorescence, while the effect from a
long-duration phosphor is referred to as phosphorescence. Televisions use
short-duration phosphors, and their screens’ lining is therefore known as the
fluorescent layer.
The end result is that the spot where the electron beam hits the screen will
glow. By varying the intensity of the electron beam, it’s possible to make
the spot glow brightly or hardly at all. Now on its own this would not be
particularly useful (there’s only so much you can do with an individual
spot), but, of course, there’s more. Note the two plates referred to as
The Memory-Mapped Display 11-7
vertical deflection plates in Figure 11.2a. If an electrical potential is applied
across these two plates, the resulting electric field will deflect the electron
beam. If the upper plate is more positive than the lower, it will attract the
negatively charged electrons forming the beam and the spot will move up
the screen. Conversely, if the lower plate is the more positive the spot will
move down the screen. Similarly, two more plates mounted on either side
of the tube can be used to move the spot to the left or the right of the screen
(these horizontal deflection plates are not shown here for clarity).
By combining the effects of the vertical and horizontal deflection plates, we
are able to guide the spot to any point on the screen. There are several ways
in which we can manipulate our spot to create pictures on the screen, but
by far the most common is the raster scan technique (Figure 11.2b). Using
this technique, the electron beam commences in the upper-left corner of the
screen and is guided across the screen to the right. The path the beam
follows as it crosses the screen is referred to as a line. When the beam
reaches the right-hand side of the screen it undergoes a process known as
horizontal flyback, in which its intensity is reduced and it is caused to
“fly back” across the screen. While the beam is flying back it is also pulled
a little way down the screen.(1)
The beam is now used to form a second line, then a third, and so on until it
reaches the bottom of the screen. The number of lines affects the resolution
of the resulting picture (that is, the amount of detail that can be displayed).(2)
When the beam reaches the bottom right-hand corner of the screen it
undergoes vertical flyback, in which its intensity is reduced, it “flies back”
up the screen to return to its original position in the upper left-hand corner,
and the whole process starts again. Thus, in a similar manner to Nipkow’s
technique, we can create pictures by varying the intensity of the beam as it
scans across the screen. For example, consider how we’d draw a simple
triangle (Figure 11.3).
In the real world the lines forming the picture would be very close together,
so this would actually be a very small triangle, but it serves to illustrate the
concept. When they are first introduced to this technique for creating
pictures, many people wonder why they can’t see the lines being drawn and
why the image doesn’t appear to flicker. The answer has three parts:
a) The electrons forming the electron beam travel at a tremendous
speed, and the beam itself can be manipulated very quickly. The
1
This description is something of a simplification, but it will serve our purposes
2
British television is based on 625 glorious lines, while American television only uses a
measly 405!
11-8 Bebop BYTES Back
beam used in a television set can scan the entire picture in a fraction
of a second, and the entire picture is actually redrawn approximately
thirty times a second.
b) The phosphor lining the inside of the screen is carefully chosen to
fluoresce for exactly the correct amount of time, such that any
particular point has only just stopped fluorescing by the time the
electron beam returns to that point on its next scan.
c) The combination of our eyes and nervous system exhibit persistence
of vision, which means we continue to see an image for a fraction of
a second. For example, if you look at a bright light for a short time
and then turn your head, an after-image of the light persists for a
while.
Lines
Screen
Phosphour
Shadow mask (b) A Christmas tree
(a) Cutaway view of video tube (sort of)
R R R R R
G G G G G
B B B B B
Lines
R R R R R
G G G G G
B B B B B
Now this is the clever bit. We might decide to make two of the electron
beams active and stimulate two of the dots in the group at the same time:
red-green, red-blue, or green-blue. Alternatively, we might decide to make
all three of the beams active and stimulate all three of the dots. The point is
that we can form different colors by using various combinations and
intensities of the three dots. In one way this is similar to mixing colored
paints, but in another way it’s quite different ......
Infrared Ultraviolet
Glass prism
White
light
Green Red
Yellow Magenta
So why does mixing light work one way while mixing paint works another?
Gosh, we were hoping you wouldn’t ask us that one. Well, here’s a
question right back to you – what colors come to mind when you hear the
words “tomato,” “grass,” and “sky”? You almost certainly responded with
red, green, and blue, respectively. Why? The main reason is that when you
were young, your mother told you that “Tomatoes are red, grass is green,
and the sky is blue,” and you certainly had no cause to doubt her word.
However, the terms “red,” “green,” and “blue” are just labels that we have
collectively chosen to assign to certain portions of the spectrum. If our
mothers had told us that “Tomatoes are blue, grass is red, and the sky is
green,” then we’d all quite happily use those labels instead. What we can
say is that, using an instrument called a spectrometer, we can divide the
visible part of the spectrum into different bands, and we’ve collectively
agreed to call certain of these bands “red,” “green,” and “blue.” Of course
everyone’s eyes are different, so there’s no guarantee that your companions
are seeing exactly the same colors that you are. Also, as we shall see, our
brains filter and modify the information coming from our eyes, so a number
of people looking at exactly the same scene will almost certainly perceive
the colors forming that scene in slightly different ways.
Here’s another question: “Why is grass green?” In fact we might go so far as
to ask: “Is grass really green at all?” Surprisingly, this isn’t as stupid a
question it seems, because from one point of view we might say that grass is
a mixture of red and blue; that is, anything and everything except green!
When we look at something like grass, what we actually see are the colors
that it didn’t absorb. Consider what happens when we shine white light on
patches of different colored paint (Figure 11.9).
White
Blue light
Green
Red White
light
White
light
White ck
Bla
light
White ite
Wh Wh
light ite
e
Blu Blue
n
Gree
Gre
en
ed
R
Red
Figure 11.9: Shining white
light on different paints
The Memory-Mapped Display 11-15
The red paint absorbs the green and blue light, but it reflects the red light,
which is what we end up seeing. Similarly, the green paint absorbs the red
and blue light and reflects the green, while the blue paint absorbs the red
and green and reflects the blue. The white paint reflects all of the colors and
the black paint absorbs them all, which means that black is really an
absence of any color. So returning to our original question about the color
of grass: We could say that grass is green, because that’s the color that it
reflects for us to see, or we could say that grass is both blue and red,
because those are the colors it absorbs.
This explains why mixing paints is different from mixing light. If we start off
with two tins of paint, say cyan and yellow, and shine white light at them,
then each of the paints absorbs some of the colors from the white light and
reflects others. If we now mix the two paints together, they each continue to
absorb the same colors that they did before, so we end up seeing whichever
colors neither of them absorbed, which is green in this case. This is why we
say mixing paints is subtractive, because the more paints we mix together,
the greater the number of colors the combination subtracts from the
white light.
Actually, there is a point to all of this (well, most ...... well, some of it),
although we won’t find out what that point is until later in this chapter. But
before we move on, it is perhaps appropriate to note that although colors
themselves are reasonably simple (being merely sub-bands in the visible
spectrum), color vision is amazingly complex. Our visual systems, which
engage our eyes, brains, and nervous systems, have evolved to such a
sophisticated level that for a long time we didn’t even begin to comprehend
the problems that nature had been compelled to overcome. In fact it was
only when we (the human race, not the authors) constructed the first
television cameras and television sets, and discovered they didn’t work as
expected, that we began to realize there was a problem in the first place.
First of all, it is commonly accepted (though not necessarily correct – see
the sidebar) that we have five senses: touch, taste, smell, hearing, and sight.
Of these senses, sight accounts for approximately 80% of the information we
receive, so our brains are particularly well-adapted at processing this
information and making assumptions based on it. For example, if you give
someone yellow jello, they will automatically assume that it will taste of
lemon; similarly for green jello and lime and red jello and strawberry.(7) This
association is so strong that if you give people yellow jello with a strawberry
7
What the Americans call “jello” would be referred to as “jelly” in England. (Also, what the
Americans call “jelly” would translate to “jam” in the mother-tongue.)
11-16 Bebop BYTES Back
Actually, there is a great deal of evidence that flavor, they often continue to believe
we have more than five senses (and we’re not it tastes of lemon. This is because
talking about extra-sensory perception); for their brains give more weight to what
example, the ability to detect magnetic fields. their eyes are telling them compared
In a series of experiments performed in the to what their taste buds are trying
early 1980s, groups of students who had lived to say.
in the same area for a number of years were
blindfolded and driven out into the country on When we use our eyes to look at
coaches that followed a bafflingly random, something, the data is pre-processed
twisted route. When they reached their by an area of the brain called the
destination (still blindfolded), they were asked visual cortex, followed by the rest of
to point in the direction they thought home to
the brain, which tries to make sense
be. Although some pointed exactly the wrong
way, the average of all of the students was
of what we’re seeing. The brain’s
within 5% of the true direction! Subsequent ability to process visual information is
refinements to the experiment revealed that nothing short of phenomenal. For
the subjects were unconsciously using the example, in a famous series of
earth’s magnetic field as a reference. experiments, subjects donned special
glasses which made everything
appear to be upside down.
Amazingly, within a few days their
brains began to automatically correct for the weird signals coming in and
caused objects to appear to be the right way up again. Similarly, when the
subjects removed their special glasses, things initially appeared to be upside
down, because their brains were locked into the new way of doing things.
Once again, within a short period of time their brains adapted and things
returned to normal. (Actually, the way in which the lenses in our eyes
function means that the images we see are inverted by the time they strike
the retina at the back of the eye, so our brains start off by having to process
“upside-down” data.)
This exemplifies the brain’s processing capabilities, but doesn’t begin to
illustrate how well we handle color. As a starting point, human eyes have
three different types of color receptors; some for red light, some for green,
and some for blue.(8) This is why we use red, green, and blue dots on our
television screens to generate all of the colors, because this directly maps
onto the way in which our eyes work. However, this physical portion of our
visual system is supplemented by an incredibly sophisticated color-
processing component within our brains.
Assume for the sake of argument that you particularly like the shade of green
you see in your lawn – so much so that you instruct a carpet manufacturer
8
Several forms of color blindness are caused by deficiencies in one or more of these receptors.
The Memory-Mapped Display 11-17
to make you a rug in exactly that color. The great day arrives and your new
rug is delivered. The first thing you do is to lay it next to your grass and
confirm that they are, in fact, the same color. Next you take the rug into
your house and place it on your recreation-room floor. Not surprisingly it
remains the same color ...... or at least, it appears to.
The fact that an object generally looks much the same color-wise
irrespective of where we put it and, within certain constraints, regardless of
ambient lighting, is something we tend to take for granted. We can therefore
only imagine the surprise of the creators of the first color television cameras
when they discovered that objects appeared to have strikingly different
colors according to whether they were filmed inside a building or outside.
While engineers worked to correct the problem, people started to question
why the same effect didn’t occur with our eyes. Eventually it was realized
that this effect did indeed occur, but our brains were correcting for it
without our even noticing. One of the most effective ways to demonstrate
exactly what it is our brains are doing to handle this color problem is
illustrated in Figure 11.10.
To perform this experiment, we Pure red, green,
and blue lamps
commence by painting a
board with a wide variety of
colors in various interlocking
geometric shapes. Next, three
light sources, which generate
pure red, green, and blue Spectrum
light, are all set to the same analyzer
(Figure 11.11a). Similarly, if we point the analyzer at areas that are painted
primary green or primary blue, it will see large green or blue components,
respectively (Figures 11.11b and 11.11c).
(c) Blue
(b) Green
(a) Red
Visual Display
Unit (VDU)
Video card
Keyboard
Main system
circuit board
A major consideration for the designers of these early monitors was the
amount of memory they required, because, unlike a television which
receives its pictures from afar, a computer needs somewhere to store
everything that it’s displaying. From our discussions on video tubes earlier
in this chapter, you may recall that images are created as a series of lines
7 dots containing “dots,” and this is the way in which a
monitor displays characters. For example, consider
how a monitor could be used to display the letter ‘E’
(Figure 11.14).
One configuration common in early monitors was a
9 dots
10 dots
Row 0
15 dots
16 dots
A single
character
Row 15
When you magnify one of our characters (the letter ‘B’ in this example), it
may seem that we’ve carelessly wasted a lot of our dots. But remember that
we require some way to form spaces between the rows and columns. Also,
some of the lowercase characters such as ‘q’, ‘y’, and ‘p’ have “tails” that
extend downward, and we have to reserve some space to accommodate
these as well.
Be this as it may, the problem remains that we’ve got 16 x 32 = 512
characters, each of which occupies 15 x 10 = 150 dots (which would
require 9,600 bytes if we used one bit to store each dot), yet we want to use
the smallest amount of our computer’s memory as possible. The solution to
our problem lies in that fact that the patterns of dots for each character are
pre-defined and relatively immutable (at least they were in the early days).
To put this another way, if we wish to display several ‘B’ characters at
different positions on the screen, then we know that each of them will use
11-24 Bebop BYTES Back
exactly the same pattern of dots. This means that we can divide our
problem into three distinct parts:
a) We need some way to remember which characters are being
displayed at each location on the screen; for example, “The character
in column 6 of row 3 is a letter ‘B’.” Due to the fact that we want to
be able to change the characters displayed at each location, this
information will have to be stored in our RAM.
b) We need some way to store a single master pattern of dots for each
character we wish to be able to display (for example, ‘A’, ‘B’, ... ‘Z’,
and so on). Assuming that we don’t wish to change the way our
characters look, then these master patterns can be stored in some
flavor of read-only memory (ROM) device.(11)
c) We need some mechanism to combine the information from points
(a) and (b). That is, if the system knows that the character in column
6 of row 3 should be a letter ‘B’, then it requires the ability to access
the master pattern of dots associated with this letter and display them
on the screen at the appropriate location.
The first thing we have to decide is which types of characters we wish to
use. Obviously we’ll want to be able to display the uppercase letters ‘A’
through ‘Z’, and it’s not beyond the bounds of possibility that we’d like to
use their lowercase counterparts ‘a’ through ‘z’. Similarly, we’d appreciate
the numbers ‘0’ through ‘9’, punctuation characters such as commas and
semi-colons, and special symbols such as ‘$’, ‘&’, and ‘#’. Just a moment,
doesn’t all of this seem strangely familiar? Are you experiencing a feeling of
deja-vu? Didn’t somebody just say that? Well you can flay us with wet
noodles if this isn’t beginning to sound like the specification for ASCII that
we introduced in the previous chapter (we love it when a plan comes
together).
ASCII is a 7-bit code and, as we tend to store our information in 8-bit bytes,
this means that we’ve got a spare bit in each byte to play with (but you can
bet your little cotton socks that we’ll find a use for these bits in the not-so-
distant future). The main point is that if we use ASCII codes to indicate the
characters that we wish to display at each location on the screen, then we’ll
only need to reserve 512 bytes (16 x 32 characters) of our RAM for the
entire screen, which is pretty efficient usage of our limited resources when
you come to think about it.
11
We will discuss ways in which we can modify these master patterns in Chapter 13
The Memory-Mapped Display 11-25
The next thing we need to consider is how we map locations in the
Beboputer’s memory to rows and columns on the screen. As you may
recall, the Beboputer perceives its world as consisting of 64 K-bytes of
memory (we’ve provided you with far more
memory than the early home users could hope $0000ROM to $3FFF
for in their wildest dreams) (Figure 11.16).
In the case of our virtual system, addresses
$0000 through $3FFF are occupied by ROM, RAM Video RAM
addresses $4000 through $EFFF are occupied $4000 to $EFFF $EE00 to $EFFF
the video card knows that whichever ASCII character occupies address
$EE00 is supposed to appear at row 0 column 0 on the screen, that the
character at address $EE01 is supposed to appear at row 0 column 1, and so
on. Similarly, because the video card knows how many rows and columns
our display supports, it understands that the character at address $EE1F is
supposed to appear at row 0 column 31 (since $1F = 3110), while the
character at address $EE20 is supposed to appear at the beginning of the
next line at row 1 column 0 (Figure 11.17).
Beboputer's RAM
$EE00 $42
$EE01 $45 Memory-mapped
$EE02 $42 screen
$EE03 $4F
$EE04 $50
:
$EFFD $?? ASCII
$EFFE $?? codes
$EFFF $??
B E B O P
Tool Bar
QWERTY
Keyboard
Memory Walker
Hex Keypad
Memory-
Mapped
Display
Status Bar
As usual, most of the tools that appear in the project working area are
familiar to us: the memory walker, the hex keypad, and the QWERTY
keyboard. In fact the only new addition to the team is our memory-mapped
display. Note that this display is initially black, because we haven’t
powered the Beboputer up yet.
The Memory-Mapped Display 11-29
Program to clear the screen
Click the ON button on the hex keypad to power the Beboputer up (this
also applies power to the memory-mapped display). Eeeek, why has the
screen filled with weird characters? The reason is that the Beboputer’s RAM
initially contains random logic 0s and logic 1s, but the video card doesn’t
know this, so it just does its job, which is to treat locations $EE00 through
$EFFF as valid ASCII codes and display them on the screen. This means that
the first thing we need to do is to write a program to clear the screen. For
reasons of our own, we’re going to create this program in two parts. First
we’ve going to specify a top-level program that sets things up the way we
wish. This top-level program will then call a subroutine to actually perform
all of the work (Figure 11.20).
Enter
To subroutine
From subroutine
Exit
The first instruction ($50 at address $4000) is a BLDSP (“big load stack
pointer”), which we use to initialize the stack pointer. We call this a “big
load” because the stack pointer requires two bytes of data (the use of the
stack pointer and subroutines was introduced in Chapter 8). As you may
recall, we could initialize the stack pointer to point almost anywhere in the
RAM (with the exception of addresses $EE00 through $EFFF, which we’ve
now designated as being reserved for use as the Video RAM). In fact it’s
generally considered to be good practice to locate the stack pointer as far
away from our programs as possible, because programs grow in the
opposite direction to the stack (see also Chapter 8). However, in this case
we know that both our programs and our stack are going to be relatively
small, so we’ve arbitrarily decided to initialize the stack pointer to address
$4FFF, which is the end of the first 4K block of RAM.
11-30 Bebop BYTES Back
Now we come to the subroutine itself. There are myriad ways in which we
could write a routine to clear the screen, but we’ve decided to feature the
index register in this particular example (Figure 11.21).
Enter
Load ACC $4D00 $90 $90 = Load accumulator with the data in
with $20 $4D01 $20 the following byte (which is $20)
Decrement
$4D05 $83 $83 = Decrement the index register
X register
yes
Return to
$4D0C $CF $CF = Return from subroutine
main prog
Exit
12
”There is only one difference between a madman and me. I am not mad.” – Salvador Dali
11-32 Bebop BYTES Back
The end result of all this is that our subroutine will load all 512 bytes of the
Video RAM with space characters, commencing at the last location in the
Video RAM and working its way up to the first location. Eventually, the
index register will be decremented to zero, the JNZ instruction at address
$4D09 will fail, and the program will continue on to the last instruction
($CF at address $4D0C), which is an RTS (“return from subroutine”). This
last instruction instructs the CPU to return control to the top-level program
that called it. While you’re entering this program as discussed below, take a
few moments to ponder two questions:
a) What do you expect to occur on the memory-mapped display when
we come to run this program?
b) Why on earth did we write the program to load the Video RAM from
the bottom up instead of from the top down?
Now click the keypad’s Data (Da) button to make this field active, click a ‘5’
followed by a ‘0’, and watch the data build up in the data field. To complete
this operation, click the keypad’s Enter (Ent) button. This causes the monitor
program to load the data displayed in the data field ($50) into the memory
location specified by the address field ($4000). The monitor program
automatically increments the value in the address field and the Data (Da)
button remains active, which allows you to alternate between inputting data
and clicking the Enter button. So, continue to enter the remainder of the
program from address $4001 onwards as follows:
The Memory-Mapped Display 11-33
Address Data Instruction Clear screen top-level program
$4000 $50 BLDSP (imm) Load the stack pointer with $4FFF (the stack
$4001 $4F pointer is a 16-bit register, so it requires two bytes)
$4002 $FF
$4003 $C9 JSR (abs) Jump to the subroutine that’s located at
$4004 $4D address $4D00
$4005 $00
$4006 $C1 JMP (abs) On returning from the subroutine, jump
$4007 $00 immediately to address $0000, which is the
$4008 $00 start of our monitor program in ROM (just like
hitting a reset)
This completes the top level program, so now we’ve got to change the
address in order to enter the subroutine. Click the Address (Ad) button on
the hex keypad to make this the active field. The first instruction in our
subroutine occurs at address $4D00, so click a ‘4’, a ‘D’, and two ‘0’s. Now
click the Data (Da) button again to make this field active, click a ‘9’ and a
‘0’, then click the keypad’s Enter (Ent) button. As usual, the monitor
program automatically increments the value in the address field and the
Data (Da) button remains active, so continue to enter the remainder of the
subroutine from address $4D01 onwards as follows:
Address Data Instruction Clear screen top-level program
$4D00 $90 LDA (imm) Load the accumulator with $20 (which is the
$4D01 $20 ASCII code for a space)
$4D02 $A0 BLDX (imm) Load the index register with $0200 (the index
$4D03 $02 register is a 16-bit register, so it requires two bytes)
$4D04 $00
$4D05 $83 DECX (imp) Decrement the index register
$4D06 $9A STA (abs-x) Store the contents of the accumulator to the
$4D07 $EE address $EE00 plus the current contents of the
$4D08 $00 index register
$4D09 $D6 JNZ (abs) If the zero flag isn’t set to logic 1 (which means
$4D0A $4D that the index register doesn’t contain zero), then
$4D0B $05 jump back to address $4D05
$4D0C $CF RTS (imp) Return to the main program
Increment
$400E $82 $82 = Increment the index register
X register
Increment
$400F $80 $80 = Increment the accumulator
ACC
yes
$4013 $C1 $C1 = Jump back to the address pointed
Jump to
$4014 $00 to by the following two bytes ($0000),
monitor
$4015 $00 which is the monitor program
Exit
Figure 11.22: Display characters program
The second instruction ($90 at address $4009) loads the accumulator with
$00, while the third instruction ($9A at address $400B) is a STA (“store
accumulator”) using the indexed addressing mode. Once again, this
instruction (which is identical to the
one we used in our subroutine) tells the If you’re alert (the world needs more “lerts”),
CPU to store the contents of the you’ll realize that this first instruction to load
accumulator to the address pointed to the index register with $0000 is superfluous
by the next two bytes (which is $EE00) in this case, because the clear screen
subroutine already leaves the index register
plus the current contents of the index
containing $0000. However, unless you’re
register. Now we know that $EE00 is trying to use as few memory locations as
the address of the first location in our possible or make your program run as fast
Video RAM, and we know that the as possible, it’s generally considered to be
index register starts off containing good practice to explicitly initialize any
$0000, so the first time around the loop registers you’re going to use at the time
we store the accumulator to address you’re going to use them. For example, at
$EE00 + $0000 = $EE00; that is, some time in the future we might decide to
row 0 col 0. rewrite the clear screen subroutine in a way
that doesn’t use the index register, in which
Next we increment the index register case we wouldn’t have a clue what that
using an INCX instruction ($82 at register contained.
address $400E), then we increment the
11-36 Bebop BYTES Back
Our luck just ran out, because this is the point where our original program
ended, so continue to enter the remainder of the program from address
$4009 onwards as shown below.
Address Data Instruction Display characters top-level program
$4000 $50 BLDSP (imm) Load the stack pointer with $4FFF (the stack
$4001 $4F pointer is a 16-bit register so it requires two bytes)
$4002 $FF
$4003 $C9 JSR (abs) Jump to the subroutine that’s located at address
$4004 $4D $4D00
$4005 $00
$4006 $A0 BLDX (imm) On returning from the subroutine, load the index
$4007 $00 register with $0000
$4008 $00
$4009 $90 LDA (imm) Load the accumulator with $00
$400A $00
$400B $9A STA (abs-x) Store the contents of the accumulator to the
$400C $EE address $EE00 plus the current contents of
$400D $00 the index register
$400E $82 INCX (imp) Increment the index register
$400F $80 INCA (imp) Increment the accumulator
$4010 $D6 JNZ (abs) If the zero flag isn’t set to logic 1 (which means
$4011 $40 that the accumulator doesn’t contain zero),
$4012 $0B then jump back to address $400B
$4013 $C1 JMP (abs) Jump immediately to address $0000, which is
$4014 $00 the start of our monitor program in ROM (just
$4015 $00 like hitting a reset)
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Note that there is no standard for this sort of thing, so computer designers
typically make up their own set as they go along (the sneaky ones copy
someone else’s). Remember that each of these codes will be represented as
some pattern of dots and spaces on the screen. Also remember that the size
of each character on the screen is 15 dots by 10 dots. In the case of codes
$80 through $BF, we decided to divide their associated characters into six
chunks, and to walk through every possible permutation of these chunks
being white and black; for example, consider the dots forming the character
associated with code $99 (Figure 11.24a).
Similarly, in the case of codes $C0 through $CF, we decided to create some
“line-art” characters (Figure 11.24b), and we also used the codes $D0
through $D3 to create “arrow” characters. We couldn’t think of anything
that we particularly wanted to do with the remaining codes, $D4 through
$FF, so we simply assigned another unusual character to them in order to
allow us to differentiate them from space characters.
11-40 Bebop BYTES Back
no
$400C $99 $99 = Store the accumulator to the
Store ACC
$400D $EE address pointed to by the following two
to V-RAM
$400E $A5 bytes (that is, $EEA5)
Now click the Step (Stp) button on the hex keypad to place the Beboputer
in it’s step mode, then use the scroll bar on the memory walker to center
address $EEA5 in the middle of the display (this is the location in the Video
RAM that corresponds to row 6 column 6). Note that this location currently
contains $20, which is the ASCII code for a space. Next click the Run (Ru)
button on the hex keypad to return the Beboputer to it’s run mode, then
click the letter ‘A’ on the Beboputer’s QWERTY keyboard and watch the
corresponding character appear on the screen. Return the Beboputer to its
step mode by clicking the Step (Stp) button on the hex keypad once again,
and note that address $EEA5 in the memory walker now contains $41,
which is the ASCII code for the letter ‘A’. Finally, return the Beboputer to
its run mode, then spend a while clicking keys on the keyboard and
watching them appear on the screen. Continue to experiment until you’re
sated, then click the Reset (Rst) button on the hex keypad to return us to
the monitor program.
(a) (e) H E L L
(b) H (f) H E L L P
Back-Space
(c) H E (g) H E L L key deletes
a character
(d) H E L (h) H E L L O
yes Is
Load ACC Load ACC
Z flag
with $C8 with $C8
set?
no
If the code If the code
Increment != BSpace Compare == BSpace Decrement
Index reg then add to BSpace then delete Index reg
a character a character
no Is yes
Store ACC Load ACC Store ACC
Z flag
to V-RAM with $20 to V-RAM
set?
the keyboard, and then checks to see if the zero flag has been set to logic 1.
If the zero flag is logic 1, then the accumulator contains $00 implying that
no key has been pressed, so the program loops back to read the keyboard
again.
Once the program detects that a key has been pressed, it compares the
contents of the accumulator with $08, which is the ASCII code for a “Back
Space” character. (Remember that a comparison instruction updates the
carry and zero flags, but does not affect the current contents of the
accumulator.) Following the comparison instruction, the program checks to
see if the zero flag is set, which would indicate that the value in the
accumulator is equal to the $08 “Back Space” character.
If the zero flag isn’t set, the program knows that this character should be
displayed (the left branch in our flowchart), so it stores the character to the
address pointed to by the index register, thereby overwriting the insertion
point character. The program then increments the index register, reloads the
accumulator with the $C8 insertion point character, stores this character to
the new address pointed to by the index register, and returns to the
beginning of the loop to wait for the next key to be pressed.
Alternatively, if the zero flag is set following the comparison operation, then
the program knows that this character is a “Back Space” (the right branch in
our flowchart). In this case the program loads the accumulator with $20 (the
ASCII code for a space) and saves this character to the address pointed to by
the index register, thereby overwriting the insertion point character. The
program then decrements the index register, reloads the accumulator with
the $C8 insertion point character, and stores this character to the new
address pointed to by the index register, thereby overwriting the original
character we wished to delete. This branch of the program than returns to
the beginning of the loop to wait for the next key to be pressed.
While you’re entering this program as discussed below, take a few moments
to ponder two more questions:
a) What do you expect to occur if you click more than thirty-two keys,
which will fill the first row on the memory-mapped display?
b) What do you expect to occur if you click the Bspace (“Back Space”)
key more times than the number of characters you’ve entered?
The Memory-Mapped Display 11-47
Entering the program with the hex keypad
Once again, the easiest way to enter our new program is to first remove the
old one. We’ll start by clicking the RAM icon in the memory walker’s
toolbar, which returns us to the beginning of our program at address $4000.
Now click the Memory item in the project window’s menu bar, then click
the Purge Memory entry in the resulting pull-down menu. Set the Start
Address and End Address fields in the ensuing dialog box to $4006 and
$4011, respectively, then click the OK button to purge these addresses and
dismiss the form. Note that this will not affect the first part of our existing
program in addresses $4000 through $4005 (the part that calls our clear
screen subroutine), and it will also leave our subroutine intact. Also note
that the memory walker now shows $XX values in the purged locations.
In order to enter the new program, click the Address (Ad) button on the hex
keypad to make this field active, then enter the address $4006. Next click
the keypad’s Data (Da) button to make this field active, enter the data $A0,
and click the keypad’s Enter (Ent) button. Continue to enter the remainder of
the program from address $4007 onwards as shown below.
Address Data Instruction Read keyboard and display ASCII value
$4000 $50 BLDSP (imm) Load the stack pointer with $4FFF (the stack
$4001 $4F pointer is a 16-bit register, so it requires two
$4002 $FF bytes)
$4003 $C9 JSR (abs) Jump to the subroutine that’s located at
$4004 $4D address $4D00
$4005 $00
$4006 $A0 BLDX (imm) Load the index register with $EE00, the base
$4007 $EE address of our Video RAM
$4008 $00
$4009 $90 LDA (imm) Load the accumulator with $C8, the ASCII
$400A $C8 code for our insertion point character
$400B $9A STA (abs-x) Store the contents of the accumulator (the
$400C $00 insertion point character) to address $0000
$400D $00 plus the current contents of the index register
$400E $91 LDA (abs) Load the accumulator from the input port
$400F $F0 connected to the keyboard at address $F008
$4010 $08
$4011 $D1 JZ (abs) If the zero flag is set, then no key has been
$4012 $40 pushed, so jump back to address $400E;
$4013 $0E otherwise continue to address $4014
$4014 $60 CMPA (imm) Compare the contents of the accumulator to
$4015 $08 $08, the ASCII code for a “Back Space”
11-48 Bebop BYTES Back
$4016 $D1 JZ (abs) If the zero flag is set, then this was a “Back
$4017 $40 Space” character, so jump to address $4025;
$4018 $25 otherwise continue to address $4019
$4019 $9A STA (abs-x) Store the contents of the accumulator (the
$401A $00 character to be added) to address $0000 plus
$401B $00 the current contents of the index register
$401C $82 INCX (imp) Increment the contents of the index register
$401D $90 LDA (imm) Load the accumulator with $C8, the ASCII
$401E $C8 code for our insertion point character
$401F $9A STA (abs-x) Store the contents of the accumulator (the
$4020 $00 insertion point character) to address $0000
$4021 $00 plus the current contents of the index register
$4022 $C1 JMP (abs) Jump back to the beginning of the loop at
$4023 $40 address $400E and wait for another key to
$4024 $0E be pressed
$4025 $90 LDA (imm) Load the accumulator with $20, which is
$4026 $20 the ASCII code for a space character
$4027 $9A STA (abs-x) Store the contents of the accumulator (the
$4028 $00 space character) to address $0000 plus the
$4029 $00 current contents of the index register
$402A $83 DECX (imp) Decrement the contents of the index register
$402B $90 LDA (imm) Load the accumulator with $C8, the ASCII
$402C $C8 code for our insertion point character
$402D $9A STA (abs-x) Store the contents of the accumulator (the
$402E $00 insertion point character) to address $0000
$402F $00 plus the current contents of the index register
$4030 $C1 JMP (abs) Jump back to the beginning of the loop at
$4031 $40 address $400E and wait for another key to
$4032 $0E be pressed
Now try clicking the letter ‘H’ again, and see the insertion character
reappear on the screen. In this case, clicking the ‘H’ key caused the $48
ASCII code associated with this key to be written into location $EDFF, while
the $C8 code associated with the insertion point was stored into location
$EE00, at which point it reappeared on the screen.
In this case no harm was done, because we weren’t using location $EDFF
anyway. But had we been using this location for something like the
beginning of our stack, the results could have been catastrophic
(in programming terms). Thus, if this had been a real text editor, we
would have included some error checking instructions to ensure that a
user couldn’t do this sort of thing.
Continue to experiment until you’re bored, then click the Reset (Rst) button
on the hex keypad to return us to the monitor program.
Row 0
5
A single
48 rows
"block"
Row 47
In turn, this means that when the video card is in its medium-resolution
graphics mode, the base address for the Video RAM is $E400. The reason
our mapping scheme is somewhat inefficient is that we’re only using the
three least-significant bits of each byte in the Video RAM (the video card
simply ignores the contents of the five most-significant bits). In the case of
the bits we’re using, each bit corresponds to a different primary color,
where a logic 0 means the color is OFF and a logic 1 means it’s ON.
Beboputer can also send instructions to the video card in the form of
control codes through the output port at address $F028 (Figure 11.30).
Port
Beboputer's RAM
Red
Control To
CPU circuitry
Green
display
Blue
ROM
$400D $E6 JNC (abs) If the carry bit isn’t set, then jump to address
$400E $40 $4012, otherwise continue to address $4010
$400F $12
$4010 $40 XOR (imm) XOR the contents of the accumulator with $1D
$4011 $1D
$4012 $99 STA (abs) Store the contents of the accumulator to
$4013 $40 address $4035. This location is going to form
$4014 $35 the least significant byte of our target address
Address Data Instruction Highlight the selected block (set it to
white)
$4015 $90 LDA (imm) Load the accumulator with $07, which equates
$4016 $07 to a white block
$4017 $9B STA (ind) Store the contents of the accumulator to the
$4018 $40 address pointed to by the two bytes at $4034
$4019 $34 and $4035 (note that this instruction uses the
indirect addressing mode)
Address Data Instruction Set the selected block to a random color
$401A $91 LDA (abs) Load the accumulator with the contents of
$401B $40 address $4035 (the least-significant byte of our
$401C $35 target address and also our random number)
$401D $30 AND (imm) AND the contents of the accumulator with $07
$401E $07 (to extract the three least-significant bits)
$401F $60 CMPA (imm) Compare the contents of the accumulator with
$4020 $07 $07 to see if the random color is white
$4021 $D6 JNZ (abs) If the zero flag isn’t set (which means that the
$4022 $40 color isn’t white), then jump to address $4026,
$4023 $26 otherwise continue to address $4024
$4024 $90 LDA (imm) The color was white, so load the accumulator
$4025 $01 with $01, which equates to a color of red
11-56 Bebop BYTES Back
$4026 $9B STA (ind) Store the contents of the accumulator to the
$4027 $40 address pointed to by the two bytes at $4034
$4028 $34 and $4035 (note that this instruction uses the
indirect addressing mode)
$4029 $91 LDA (abs) Load the accumulator with the contents of
$402A $40 address $4035 (the least-significant byte of our
$402B $35 target address and also our random number)
$402C $60 CMPA (imm) Compare the contents of the accumulator with
$402D $01 $01 to see if we’ve returned to our starting
point
$402E $D6 JNZ (abs) If the zero flag isn’t set (which means that there
$402F $40 are more random numbers), then jump to
$4030 $0C address $400C, otherwise continue to
address $4031
$4031 $C1 JMP (abs) Jump immediately to address $0000, which is
$4032 $00 the start address of our monitor program in
$4033 $00 ROM (just like hitting a reset)
Address Data Instruction Save two bytes to be used as indirect
address
$4034 $E4 ------- Don’t bother loading anything into address
$4035 $XX ------- $4035, because our program will do this for us
(see below)
The only really unusual aspect to this program (compared to those in earlier
labs) is the use of the indirect addressing mode for some of the load and
store instructions (indirect addressing was introduced in Chapter 8). Let’s
look at this in a little more detail. When we key in the program, we leave
the memory location at address $4035 containing unknown values
(indicated by $XX).
The first thing our program does is to write two control codes to our video
card. These codes instruct the video card to enter its medium-resolution
graphics mode and to clear the Video RAM. Next we generate a pseudo-
random number between 1 and 255 (or between $01 and $FF in
hexadecimal). Our program then stores this value to the location at address
$4035 using a STA instruction with the absolute addressing mode ($99 at
address $4012) (Figure 11.32a).
In this figure we’ve used $?? to indicate the fact that location $4035 has
now been loaded with some value (our pseudo-random number), but that
we aren’t particularly concerned as to exactly what this value is. The
program then loads the accumulator with $07, which equates to a color of
white ($90 at address $4015), and then stores this value using a STA
instruction with the indirect addressing mode ($9B at address $4017)
(Figure 11.32b).
The Memory-Mapped Display 11-57
Memory Memory
$EE?? =
address of
$4034 $EE $4034 $EE the data
$4035 $?? $4035 $??
the accumulator with the control code that will place our memory-mapped
display into its graphics mode. Note the chevrons that appear in the
memory walker at address $4002, which contains the opcode for our next
instruction.
Click the Step button again, thereby executing the instruction that writes our
control code to the memory-mapped display. As soon as the display enters
its graphics mode, it reads the contents of the Video RAM (which currently
contains random values) and draws the corresponding colored blocks on the
screen. This may take a few seconds, so sit back and watch the blocks
appear.
Now click the Step button once more, which causes the accumulator to be
loaded with our clear code, then click Step one more time to write this
code to the memory-mapped display. As we see, the screen clears almost
immediately, because this time our virtual graphics card is doing all of the
work. Use the memory walker to return to address $E400, and observe that
the Video RAM now contains $00 values, which were placed there by the
video card when it received the clear code.
Now click the Run button and watch our program do its cunning stuff. As
you will see, although our program does write the colored blocks to random
locations, the actual result consists of vertical colored lines, but this is just a
by-product of the particular shift-register algorithm we used.
When all of the blocks on the first four rows have been loaded with colored
blocks, our program will automatically return the Beboputer to its reset
mode. You can run this program again if you wish, although this time you
might decide to make greater use of the Step button, so as to examine what
the program is doing in more detail. When you’ve had your fill, click the
keypad’s ON button (which also turns it OFF), then exit this laboratory by
clicking the File item on the project window’s menu bar followed by the Exit
entry in the resulting pull-down menu.
with a default setting of 640 x 480 pixels. However, when you installed the
Beboputer (as discussed in Appendix A), we recommended that you change
this setting to a minimum of 800 x 600 pixels. Similarly, while you were
instigating this change, you may have noticed other options such as
720 x 512 pixels, 1024 x 768 pixels, and so on.
Obviously the number of physical dots on your screen remains constant, but
you can instruct your computer to use larger or smaller groups of these dots
to represent a pixel. Increasing the number of pixels that you’re using (by
using more groups, each of which contains fewer dots) means that you can
display finer details, but it also means that you require more memory on
your video card.
▼
La
Contents of Chapter 12
Machine code is a pain ............................................................................................................... 12-3
Varying levels of language abstraction ................................................................ 12-4
Assembly language specification ............................................................................. 12-12
Motley miscellaneous musings on assembling in general ..... 12-33
Starting at ground-zero ................................................................................................. 12-34
Assembling programs by hand ........................................................................ 12-37
One-pass versus two-pass assembly processes ....................... 12-38
Which comes first, the chicken or the egg? ................................ 12-42
Line editors versus screen editors .................................................................. 12-44
Pulling yourself up by your bootstraps ................................................... 12-49
Creating a cross assembler ................................................................................... 12-53
Introducing the Beboputer’s cross assembler ........................................... 12-55
Introducing the CPU register display ..................................................................... 12-60
The stack wants to be your friend ............................................................................. 12-62
The subterranean world of subroutines ............................................................. 12-64
Pushing the accumulator before calling a subroutine . 12-65
Pushing the accumulator after calling a subroutine ....... 12-66
Passing parameters into subroutines ........................................................ 12-67
Multiple RTS’s ................................................................................................................................ 12-70
Nested subroutines .............................................................................................................. 12-70
Recursion can make your brain ache ................................................... 12-71
Self-modifying code .......................................................................................................... 12-77
Creating an “Etch-A-Sketch-like” program (1) ............................................. 12-78
Introducing the list file .................................................................................................................. 12-84
Introducing the Beboputer’s breakpoint capability ....................... 12-87
Introducing the Beboputer’s messaging display utility ................ 12-90
Macro assemblers, meta-assemblers, disassemblers,
and stuff ................................................................................................................................................... 12-91
Linkers, loaders, and relocatable code ............................................................ 12-98
Quick quiz #12 ..................................................................................................................................... 12-102
1
Etch-A-Sketch is a registered trademark of the Ohio Art Company.
Assembly Language and Stuff 12-3
Machine code is a pain
Thus far we’ve entered all of our programs as a motley collection of
numerical values. This style of representation is known as machine
language or machine code, because this is the form that is directly
understood and executed by the machine (computer). It probably hasn’t
escaped your notice that working with machine code is only slightly more
fun than banging your head against a wall. Whenever we wish to use an
instruction like an LDA (load accumulator), we first have to decide which
addressing mode we wish to use
and then spend an inordinate
amount of time rooting around to Machine code
determine the opcode 01010110
corresponding to that particular 10100011
11001111
flavor of the instruction 01100001 CPU
(Figure 12.1). 10000011
10101110
Working with machine code is
time-consuming and prone to
error, and programs written Figure 12.1: Machine code is a pain
using this technique can be extremely difficult to understand and modify.
One of the main problems with machine code is that humans tend to find it
difficult to conceptualize things purely in terms of numbers; we much prefer
to describe things using symbolic representations consisting of words and
symbols. Thus, we would ideally prefer to describe our programs at a high
level of abstraction (along the lines of “First do this, then do that, then
do....”) and then translate them into machine code for the computer to
ruminate on (Figure 12.2).
Lowest of Highest of
Low-level Medium-level High-level
the low the high
C source code
If your source code is in assembly language, then the translator program that
converts it into machine code is called an assembler (because it
“assembles” the statements into machine code). By comparison, if your
source code is in a high-level language like C, then the translator program is
called a compiler (because it “compiles” the C statements into machine
code). In reality, assemblers and compilers are just special flavors of
translators which function in slightly different ways. Note that the machine
code generated by an assembler and a compiler would rarely be byte-for-
byte identical; we just illustrated it this way for simplicity. The reason that
the resulting machine code may differ is because the source code fed into
the C compiler is specified at a high level of abstraction, and the compiler
may well make different decisions to the ones you make whilst creating
your assembly source. In fact some compilers actually generate assembly
source code as their output, which is then fed into an assembler to be
translated into machine code. Irrespective of the route taken, the resulting
machine code should ultimately generate the same functional results.
To place this in perspective, let’s consider a simple analogy that’s
reasonably close to home. If you happen to be a devotee of American
football (and who amongst us isn’t?), you’ll know that each team has a set of
special codes they use to call or modify plays. For example, the quarterback
may shout something like “Red 32, Gold 57, Green 26, Hup” (where the
“Hup” sounds like a loud burp and signifies that he’s voiced all the wisdom
he wishes to impart at this time). In certain respects we might compare the
play to a program, while the team might be a machine whose task it is to
execute that program (Figure 12.5).
The point is that the codes above are team (machine) dependent. If we
magically dropped our quarterback in the midst of another team, either they
wouldn’t understand anything he said, or, as a worse-case scenario, they
Assembly Language and Stuff 12-7
might recognize some
of the codes, but they
would actually have
been trained to
respond to these codes
End Zone
End Zone
in different ways than
the first team. By
comparison, consider
what would happen if
the quarterback had
specified his play
(program) at a higher Figure 12.5: American football baffles the unwary
level of abstraction;
for example: “I want the wide receivers to run towards the other team’s end
zone, I want the guards to confuse the issue by running around in ever-
decreasing circles, and I want the full back to wander around in an aimless
sort of way.” Irrespective of whether such a play actually makes sense (and
remembering that any analogy is suspect and
this one doubly so), the fact is that specifying For those readers who aren’t familiar
the play at this high level of abstraction with American football, the following
means that it’s now portable across teams gems of wisdom will hopefully serve to
clarify the situation. American football
(machines).
bears a passing resemblance to
All of the above would lead us to assume English Rugby, except that the
that high-level languages are more Americans have a penchant for
efficacious in every way than assembly-level excessive amounts of body armor and
tend to pass the ball forwards (they
representations. However, learning a high-
also spend an inordinate amount of
level language like C to the extent that you’d time performing Monty Python-style
be considered dangerous isn’t something you walks when they actually score).
tend to do over the weekend, and writing a
compiler for such a language is a non-trivial By some strange quirk of fate, neither
task. Also, creating programs in assembly of these games in any way resembles
Australian Rules football, which is
teaches you an awful lot about how
played on an elliptical pitch and has
computers work, and can be a whole bunch
more goal-posts than you can swing a
of fun in its own self-effacing way. The stick at. In what many regard as one of
upshot of this is that the remainder of this the finest examples of Antipodean
chapter is devoted to specifying an assembly humor, Australian Rules football is
language for the Beboputer, and then using distinguished by the fact that it doesn’t
said language to create programs. appear to have any rules at all!
12-8 Bebop BYTES Back
Translate
.ORG $4000 $4000 $90
(Assemble)
LDA $03 $4001 $03
STA [$F020] $4002 $99
ROLC $4003 $F0
JMP [$4002] $4004 $20
.END $4005 $78
$4006 $C1
Assembly (source) code $4007 $40
$4008 $02
Figure 12.6: Example without labels
Machine (object) code
Note that we’re showing the machine code on the right in hexadecimal for
our convenience, but the computer would actually see this in binary. Also
note that the object file wouldn’t actually contain any addresses in the way
they’re shown here – we just added them for purposes of clarity. Bearing
these points in mind, the resulting machine code is identical to the very first
program you created for the Beboputer, way back in those mists of time we
used to call Chapter 3. Thus, the assembly code on the left is also identical
to our first program, it’s just presented in a slightly different way. First, the
.ORG $4000 statement informs us that this program will commence at
location $4000 (where ORG is an abbreviation for “origin”). Similarly,
the .END statement is used to let everyone know when we’ve finished
the program.
1
At some stage in the future you may decide that you can create a much better assembly
language and assembler program for the Beboputer than we did – and why not?
Assembly Language and Stuff 12-9
LDA is the mnemonic we decided we’d use to represent the load
accumulator instruction back in Chapter 8, but this is where things start to
get a little crafty. As you will recall, we have several flavors of the load
accumulator instruction, depending on the addressing mode we wish to use
(such as immediate, absolute, indexed, and so on). One way we could
handle this would be to create different mnemonics for each flavor of
instruction, such as LDA_imm, LDA_abs, and so forth, but this would be
something of a pain to use. One very common alternative is to employ only
a single mnemonic such as LDA, and to then qualify the flavor we want to
use by the way in which we specify the operand. In the case of our
language, we decided that if an instruction mnemonic is simply followed by
a number, then this will be understood to mean that we wish to use the
immediate addressing mode. Thus, LDA $03 means: “Load the accumulator
with the hexadecimal value $03.” However, if we decide to use the
absolute addressing mode to load the accumulator with the contents of a
particular memory location (for example, from address $F000, which is the
input port that’s connected to our 8-bit switch device), then we will say
LDA [$F000], where the square brackets indicate that the operand is actually
an address.
This explains the STA [$F020] statement, which translates to: “Store the
accumulator to the memory location at address $F020” (where you may
recognize address $F020 as being the output port that’s connected to our
8-bit LED display). Of course, the ROLC instruction doesn’t have an
operand, because this instruction only has an implied addressing mode and
means: “Rotate the contents of the accumulator one bit to the left and
through the carry flag.”
Last but not least, the JMP [$4002] instruction means: “Jump unconditionally
to address $4002.” Why address $4002? ...... because this is the address of
the first byte in the STA instruction (and this is what we did in Chapter 3).
We can work out the address of this instruction from the fact that the
.ORG $4000 statement tells us that the program starts at address $4000,
and we know that the LDA $03 instruction will only occupy two bytes
(at addresses $4000 and $4001), so the STA instruction has to start at
address $4002.
What a pain and what a swizz! This is scarcely easier than entering the
program in machine code. If this is all assembly code has to offer, then we
may as well pack everything up and stop now. In reality this only serves to
emphasize the fact that this was a rudimentary example, because there is, of
course, a lot more to our assembly language than this.
12-10 Bebop BYTES Back
Translate
.ORG $4000 $4000 $90
(Assemble)
LDA $03 $4001 $03
LOOP: STA [$F020] $4002 $99
ROL $4003 $F0
JMP [LOOP] $4004 $20
.END $4005 $78
$4006 $C1
$4007 $40
$4008 $02
Figure 12.7: Example with address labels
Although we’ve modified the source, the resulting machine code is identical
to that of our previous example. Also note that when a label is first
declared, it is terminated by a colon ‘:’ character, but this character doesn’t
form part of the label’s name and is not used thereafter. When the assembler
is wandering through our source file we say that it’s parsing the file, where
the term “parse” comes from the Latin pars, meaning “part.” In this context,
“parsing the file” means that the assembler analyses each of our source
statements and splits them into their component parts, then determines the
grammatical function of each part and its syntactic relationship to each of
the other parts. Now all of this may sound a bit highfalutin, but the point is
that when our assembler sees the label LOOP, it understands that we’re
associating this label with the STA instruction. Furthermore, the assembler
understands that we want to associate LOOP with the address of the first
byte in the STA instruction and remember that address for future reference.
(When we say the assembler “understands this” and “understands that,”
we actually mean that we’ve created it in such a way that this is what it
will do.)
The assembler can work out the address of the first byte of the STA
instruction in exactly the same way that we calculated it by hand. Like us,
the assembler knows that the program starts at address $4000 (from the
.ORG $4000 statement), and it knows that the LDA $03 instruction will only
occupy two bytes (at addresses $4000 and $4001), so it knows that the STA
instruction has to start at address $4002. Thus, when the assembler sees the
JMP [LOOP] statement later in the program, it will automatically replace the
label LOOP with the address $4002.
Obviously this saves us from the tedium of having to calculate the jump
address ourselves, which is a major step forward whichever way you look at
Assembly Language and Stuff 12-11
it, but there’s a lot more to address labels than this. For example, if we
suddenly decided to add a few more statements between the LDA and the
STA instructions, then the address of the STA would change. If we hadn’t
used the label LOOP, then we’d have to manually recalculate this address
and modify the jump instruction. But as we did use a label, all that would
be required would be to reassemble the program and our assembler would
take care of the rest. In the case of modifications to a larger program, this
can save you hours, days, or even weeks of effort, with the cream-on-the-
cake advantage that you’d still have a high degree of confidence (or at least
a batting chance) that your program would continue to work as planned.
But wait, there’s more! The label we used above was of a type called an
“address label,” because it’s intimately associated with the address of a
particular instruction. For your delectation and delight, we’ve also decided
to provide entities called “constant labels,” to which you can assign
constant values for later use. Consider a further incarnation of our program
that makes use of a constant label (Figure 12.8).
Operations Addresses
Labels Operands Data
Translate
LEDS: .EQU $F020 $4000 $90
(Assemble)
.ORG $4000 $4001 $03
LDA $03 $4002 $99
LOOP: STA [LEDS] $4003 $F0
ROL $4004 $20
JMP [LOOP] $4005 $78
.END $4006 $C1
$4007 $40
$4008 $02
Figure 12.8: Example with constant labels
Once again, although we’ve modified the source, the resulting machine
code is identical to that of our previous examples. The first thing you’ll
notice is that we’ve added a new statement to our repertoire – the .EQU on
the first line (where EQU is an abbreviation for “equates to”). Thus, this
particular example states that the constant label named LEDS is equated to
the hexadecimal number $F020, which just happens to be the address of
the output port connected to our 8-bit LED display. This means that when
we come to our STA instruction, we can replace the STA [$F020] from our
original program with STA [LEDS], and our assembler will automatically
substitute the LEDS label with $F020 when it comes to do its cunning stuff.
Even in a simple case like this, in which we only employ the label LEDS
once in the body of the program, the fact that we used this label certainly
increases the program’s readability; but once again there’s a lot more to
12-12 Bebop BYTES Back
Statements
Just as one of the fundamental building blocks of a natural language is the
sentence, the equivalent construct in a computer language is called a
statement, which, for the purposes of our language, we may regard as
encompassing a single thought or idea. An assembly source file is composed
of a series of these statements, each of which nominally consists of four
fields (Figure 12.9).
Many computer languages (including some assembly languages) allow
statements to span multiple lines, in which case they would be terminated
by a special character such as a semicolon. In the case of our simple
language, a statement may only occupy a single line and is terminated by a
carriage return <cr> character (which equates to the key marked “Enter” or
“Return” on your keyboard).
Assembly Language and Stuff 12-13
Source file
Statement
The majority of the early assembly languages had extremely restrictive rules,
such as specifying exactly on which column each field must commence. By
comparison, our syntax is relatively free-format – you can use as many
whitespace (<tab> and <space>) characters and be as messy as you wish.
Having said this, we strongly recommend that you keep your source as neat
and tidy as possible. You can follow the style we use in our examples or feel
free to develop your own, but whatever you do, try to be as consistent as
possible – you’ll find that consistency pays dividends in the long run
when you return to blow the dust off a neglected source file some time in
the future.
Many assemblers only allow you to create source files using uppercase
characters. In the case of our language, you may use uppercase, lowercase,
or a mixture of both. However, for the purposes of its machinations, our
assembler internally converts everything to uppercase, which means that it
will consider labels such as fred, Fred, FrEd and FRED to be identical.
Label names
As we discussed in our introductory examples, our language supports two
kinds of labels, called constant and address labels. We will examine the
differences in the way these labels are used in a little while; for the nonce
we need only note that they have identical naming conventions
(Figure 12.11).
The .ORG directive instructs the assembler as to the origin of the program;
that is, the memory location into which it should place the program’s first
12-16 Bebop BYTES Back
byte. Thus, this directive must have an operand in the form of a number
(as shown here) or a constant label from a declaration statement
(as described below).
The .END directive simply informs the assembler that it’s reached the end of
the program. Thus, this directive does not require any operand.
Neither .ORG or .END directives are allowed to have labels.
If declaration statements are used, they must appear before the .ORG
directive at the beginning of the program.
Each .EQU directive must have a label assigned to it. These constant labels
may be used in the body of the program instead of literal (numerical) values.
When the assembler is assembling the program, it will automatically
substitute any constant labels in the body of the program with their
numerical equivalents. For example, the assembler will automatically
substitute JOHN in the LDA instruction above for the address $4054.
With regard to the previous point, note that constant labels are only used
by the assembler, and they don’t appear in (or occupy any space in) the
resulting machine code. Also note that constant labels can be used to
represent addresses, data, or both (this will be discussed in more detail a
little later).
The assignment to an .EQU statement may be presented in the form of an
expression, as is illustrated by the assignment to JOHN in the above
example (expressions are introduced in more detail below). However, it is
important to note that forward-referencing is not allowed; that is, JOHN is
allowed to reference FRED and BERT, because they’ve already been
declared, but BERT isn’t allowed to reference JOHN, while FRED isn’t
allowed to reference either BERT or JOHN.
Assembly Language and Stuff 12-17
The .BYTE, .2BYTE, and .4BYTE directives
The .BYTE, .2BYTE, and .4BYTE directives are used in reserve statements
to set aside (reserve) memory locations for later use. Not surprisingly, the
.BYTE directive reserves a single byte, the .2BYTE directive reserves two
bytes, and the .4BYTE directive reserves ...... well, we’ll leave that as an
exercise for the reader (Figure 12.15).
Translate $4000 $XX
.ORG $4000
(Assemble)
TMPA: .4BYTE $4001 $XX
TMPA
TMPB: .2BYTE $4002 $XX
TMPC: .BYTE $4003 $XX
LDA $03 $4004 $XX
TMPB
STA [TMPC] $4005 $XX
$4006 $XX TMPC
$4007 $90
LDA
$4008 $03
Figure 12.15: The .BYTE, .2BYTE, $4009 $99
$400A $40 STA
and .4BYTE directives
$400B $06
If reserve statements are used they must appear in the body of the program;
that is, between the .ORG and .END directives. In this example we’ve
shown the reserve statements as appearing immediately after the .ORG
directive. However, although this is perfectly legal, it means that we can’t
run the program from address $4000, but instead have to remember to run
from address $4007. Thus, it is very common to find reserve statements
hanging around the end of the program instead of the beginning.
Each reserve statement may have an optional label assigned to it. These
address labels may be used in the body of the program instead of literal
(numerical) values. When the assembler is assembling the program, it will
automatically substitute numerical equivalents for any address labels in the
body of the program. For example, the assembler will automatically
substitute TMPC in the STA instruction for the address $4006, which is the
location the assembler decided to reserve for this byte.
Note that if a label is used with a .2BYTE or a .4BYTE directive, then the
address that the assembler associates with that label corresponds to the first
byte of that field. Thus, in the case of this example, the assembler associates
the labels TMPA, TMPB, and TMPC with addresses $4000, $4004, and
$4006, respectively.
With regard to the previous points, labels are optional with these directives,
because you may not always require the ability to individually reference
every location set aside by a reserve statement. This is due to the fact that
12-18 Bebop BYTES Back
In this example we first reserved a 2-byte field using a .2BYTE directive with
a label of TMP, and we immediately followed this by reserving a 1-byte field
reserved using a .BYTE directive without a label. From our previous
discussions, we know that the assembler will associate the label TMP with
the address of the first location in the 2-byte field (which happens to be
address $4000). Thus, even though the 1-byte field doesn’t have a label of
its own, we can still reference it using “TMP+2”.
When writing a program it is often necessary to reserve a number of
consecutive memory locations. As an alternative to painstakingly reserving
locations individually, the .BYTE, .2BYTE, and .4BYTE directives support an
optional operand in the form “*n”, where ‘n’ is any expression that resolves
into a positive integer. For example, suppose that we wished to reserve
three 1-byte fields and two 2-byte fields (Figure 12.17).
Translate $4000 $XX
FRED: .EQU $03 (Assemble) Three 1-byte
.ORG $4000 $4001 $XX fields
TMPA: .BYTE *FRED $4002 $XX
TMPB: .2BYTE *$02 $4003 $XX
LDA $42 $4004 $XX Two 2-byte
$4005 $XX fields
$4006 $XX
$4007 $90
LDA
Figure 12.17: Reserving multiple locations $4008 $42
In this case we first assign the numerical value $03 to the constant label
FRED. Thus, when the assembler comes to consider the reserve statement
associated with the address label TMPA, it will replace the “*FRED” with
“*$03” and reserve three 1-byte fields. Similarly, when the assembler comes
to consider the reserve statement associated with TMPB, it will understand
that we’re instructing it to reserve two 2-byte fields (it’s just that we’re a bit
more explicit in this case). Note that the labels will be associated with the
first byte in their corresponding groups; thus, the assembler will associate
Assembly Language and Stuff 12-19
TMPA with address $4000 and TMPB with address $4003. Also, although
we’ve not illustrated this point here, note that the ‘n’ in “*n” can be a full-
blown expression. Assuming that we’re still using FRED from the previous
example, the statement “.BYTE *(FRED + 5)” would reserve eight bytes of
memory.
All of the machine code resulting from the reserve statement examples
we’ve considered so far has contained $XX values, which indicate that we
have not defined the contents of these locations. As it happens, our
assembler will automatically cause these undefined locations to contain
zero values, but that’s largely beside the point. In fact our assembly
language allows us to associate values with these locations as a comma-
separated list (Figure 12.18).
.ORG $4000 Translate $4000 $42
(Assemble) Three 1-byte
TMPA: .BYTE $42, $23, $7 $4001 $23 fields
TMPB: .2BYTE $1046, $12 $4002 $07
LDA $77 $4003 $10
$4004 $46 Two 2-byte
$4005 $00 fields
$4006 $12
Figure 12.18: Assigning values in $4007 $90
LDA
$4008 $42
reserve statements
Once again, the labels will be associated with the first byte in their
corresponding groups; thus, the assembler will associate TMPA with address
$4000 and TMPB with address $4003. Additionally, as we promised, the
assembler accepts the values in the comma separated lists and inserts them
into the appropriate locations. Note that the assembler will automatically
zero-fill numbers to the requisite size, so the $7 in this example is
automatically coerced to $07, because it’s being assigned to a 1-byte field,
while the $12 is automatically coerced to $0012, because it’s being
assigned to a 2-byte field (we’ll return to consider this aspect of things in
more detail in a little while). Also note that any numbers assigned to multi-
byte fields associated with .2BYTE and .4BYTE directives will be stored with
their most-significant byte first. For reasons more fully discussed in Chapter
15, this style of storage is referred to as big-endian (because we’re storing
the numbers “big end” first). Finally, although we’ve not illustrated this
point here, note that each of the values in these comma-separated lists
could be a full-blown expression.
12-20 Bebop BYTES Back
Figure 12.20: The assembler checks the size of the target destination
The concept of expressions will be introduced shortly, but, as we’ve opened
this can of worms here, it behooves us to explain why the second LDA in
the above example passed muster. Remember that the assembler internally
regards every number as a 4-byte field, and it’s only when the assembler
comes to assign a value to a target destination that it checks to see whether
the value will fit. (Note that we’ll be discussing the assembler itself in more
detail later on.) Also, don’t forget that we’re the ones who are defining what
our assembly language looks like, and we’ve decided that our language
should support simple expressions. So in the case of the second LDA
instruction, we’ve decided that when our assembler sees an expression like
“FRED & $FF”, it will perform a logical AND operation between the two
values (Figure 12.21).
FRED = $A563 (so 4-byte value = $0000A563)
First, the 2-byte value assigned 00000000 00000000 10100101 01100011
to FRED is zero-filled by
the assembler to boost it up $FF (so 4-byte value = $000000FF)
to its 4-byte value of 00000000 00000000 00000000 11111111
$0000A563, while the $FF
value is zero-filled to its &
4-byte value of $000000FF. Result = $00000063
The logical AND operator 00000000 00000000 00000000 01100011
(‘&’ in our assembly language)
is a bit-wise operator Figure 12.21: The assembler performing
(it performs its actions on a bit- a logical AND on 4-byte values
by-bit basis) that functions in a similar manner to the logical AND in the
Beboputer’s instruction set. To put this another way, the least-significant bit
of the first number is AND’ed with the least-significant bit of the second
number to give the least-significant bit of the result, and so on for all of the
other bits. Remember the way in which an AND works is that its output is
only logic 1 if both of its the inputs are logic 1, so the three most-significant
12-22 Bebop BYTES Back
bytes of the result are forced to zero by the six 0’s in the $000000FF value.
The outcome of all of this is that only the least-significant eight bits of the
result contain a value, which will therefore fit into our 8-bit accumulator.
Expressions
Ah, so here we are at expressions, which can be exceptionally useful, so
long as you quickly establish who’s in charge ...... you (hopefully) or the
expressions (if you turn your back on them). The trick is to look them
straight in the eye and show no fear! First of all, we have decided that our
assembly language should support the following selection of binary
operators:
Arithmetic: + Add Logical: & AND
– Subtract | OR
* Multiply ^ Exclusive-OR
/ Divide
We call these “binary operators,” because each of them requires two
operands to work with; for example, in the expression “6 + 3” we see two
operands (numbers), one on either side of the operator.
The operands in expressions can be a mixture of numbers (binary, decimal,
and hexadecimal) and labels; for example, assuming that BERT is a label,
the expression “BERT & %11001100” is perfectly acceptable.
Our language only supports integer expressions, which are expressions
involving whole numbers. Thus, “BERT * 3” is legal, while “BERT * 3.5”
is not.
Any remainder from a division operation will be automatically discarded
by the assembler without any error messages being issued. Similarly, the
assembler will not report any errors if the actions from addition, subtraction,
and multiplication operations overflow or underflow the 4-byte fields it uses
to store the results. However, the assembler will make its feelings known in
no uncertain terms should you try to divide anything by zero.
Expressions can be of arbitrary length and employ any mixture of arithmetic
and logical operators, so long as they fit on a single line of source code; for
example, “BERT ^ 2 * JOHN & $A5 / HARRY” is a legal expression (assuming
that BERT, JOHN, and HARRY are labels that have been declared elsewhere
in the program).
Assembly Language and Stuff 12-23
With regard to the previous point, all of our binary operators have equal
precedence, and expressions are evaluated from left to right. What does this
mean? Consider the following example:
At school we’re taught that: 6 * 3 + 5 * 4 = 38
But, in our assembly language: 6 * 3 + 5 * 4 = 98
Eeeek, how can this be? Well, at school we were taught that multiplication
and division have a higher precedence (are more powerful) than addition or
subtraction. This means that we normally evaluate expressions by
performing any multiplications and divisions first, followed by any additions
and subtractions. Thus, in the first portion of this example, we’d multiply
6 by 3 to get 18, multiply 5 by 4 to get 20, then add the 18 and 20 to
generate the final result of 38. Had we so wished, we could easily have
decided to make our language (and therefore our assembler) work in just
this way. However, we decided that you need to broaden your horizons,
because it’s common for simple programs (like our assembler) to treat all of
the binary operators as having equal precedence (being equally powerful)
and, as we already noted, to evaluate expressions from left to right. This
means that in the case of our language, we would solve this expression by
multiplying 6 by 3 to get 18, then adding 5 to get 23, and then multiplying
by 4 to generate a final result of 98.
Although this may seem to be a pain, it’s really just another way of looking
at the world and you’ll soon learn to get used to it – let’s face it, what other
choice do you have? Actually, there are options open to you, because you
can use parenthesis to force your expressions to evaluate in any order you
like; for example, if you were to write your expression as “(6 * 3) + (5 * 4)”,
then the assembler will evaluate the contents of any parenthesis before
moving on to the rest of the expression. Also note that it’s possible to nest
parenthesis within each other, but each ‘(‘ must have a corresponding ‘)’.
For example, in addition to being legal, “(((6 * 3) + 5) * 4)” would force the
assembler to evaluate the expression in exactly the same order as if the
parenthesis weren’t there at all (from left to right), while “(6 * (3 + (5 * 4)))”
would force the assembler to evaluate the expression in reverse order (from
right to left).
Generally speaking you’ll find that most of the expressions you create will
be short, sweet, and to the point; but even simple expressions can be
incredibly useful. Expressions can be employed in a variety of roles,
including assignments to .EQU statements, modifying data values, and
calculating addresses. First consider an example involving expressions in
.EQU statements (Figure 12.22).
12-24 Bebop BYTES Back
2
Note the cunning use of nested parenthesis in this sentence. Your English teachers might not
approve, but did they ever?
12-26 Bebop BYTES Back
At the bottom of this program come the SUB and ADD instructions, which
are both using their immediate addressing modes, and which employ
expressions to calculate their data values. The “SUB WOW * 3” statement is
understood by the assembler to mean “SUB 7 * 3,” or “SUB 21” (“subtract 21
from the current contents of the accumulator”). The $20 at address $4008 is
the opcode for this instruction, while the $15 at address $4009 is the
hexadecimal equivalent of 21. Similarly, the “ADD WOW ^ $A” statement is
understood by the assembler to mean “ADD 7 ^ $A” (where ‘^’ is the
exclusive OR operator), which boils down to “ADD $0D” (“add $0D to the
current contents of the accumulator”). Thus, the $10 at address $400A is
the opcode for this instruction, while the $0D at address $400B is the value
to be added.
In common with many other assembly languages, ours includes a special
symbol in its syntax that allows you (well, the assembler, really) to access
the value that the Beboputer’s program counter would contain at any
particular point in the code. The symbol we decided to use is the ‘@’
character, and an example of its use is illustrated in Figure 12.24.
Translate $4000 $AC .2BYTE
.ORG $4000 (Assemble)
$4001 $69 (HAT)
HAT: .2BYTE $AC69
LDA [@ - 2] $4002 $91
JMP [@ + 5] $4003 $40 LDA
SUB $15 $4004 $00
MAT: ADD $0D $4005 $C1
$4006 $40 JMP
$4007 $0A
$4008 $20
SUB
$4009 $15
Figure 12.24: Using the ‘@’ symbol $400A $10 ADD
$400B $0D (MAT)
The way in which this works is that when the assembler sees the ‘@’
character, it substitutes this character with the address of the first byte (the
opcode byte) in that instruction. Thus, as the first byte of the LDA instruction
occurs at address $4002, the assembler will understand the statement “LDA
[@ - 2]” to mean “LDA [$4002 - 2],” or “LDA [$4000].” Similarly, as the first
byte of the JMP instruction occurs at address $4005, the assembler will
understand “JMP [@ + 5]” to mean “LDA [$4005 + 5],” or “LDA [$400A].”
Note that the ‘@’ symbol can only be used in expressions in the body of the
program, but it has no meaning in declaration (.EQU) statements.
Last but not least, it would be extremely remiss of us if we failed to mention
that our language supports two unary operators: the unary minus ‘-’ and the
unary negation ‘!’. (The exclamation mark we used to represent the unary
Assembly Language and Stuff 12-27
negation is often referred to as a bang, ping, or shriek.) The reason we call
these “unary operators” is that they only require a single operand to work
with (Figure 12:25).
.ORG $40000 Translate $4000 $90
(Assemble) LDA
LDA 53 $4001 $35
ADD -53 $4002 $10
ADD
SUB !53 $4003 $CB
$4004 $20
SUB
$4005 $CA
3
The concepts of one’s and two’s complements were introduced in Chapter 7.
12-28 Bebop BYTES Back
fields packed to bursting with 1s, we seem to be implying that the assembler
will let them slip past without so much as raising a metaphorical eyebrow.
Strange things are afoot indeed.
Bit
Bit 15
15 Bit 7
Bit 7 Bit0 0
Bit
Figure 12.26: How the assembler views the effects of the unary operators
The “big immediate” addressing mode: This mode is very similar to the
standard immediate mode, in that the data to be used is directly associated
12-30 Bebop BYTES Back
The indexed addressing mode: The indexed addressing mode is very similar
to the absolute mode, especially since these instructions also occupy three
bytes, one for the opcode and two for the address (Figure 12.31).
Assembly Language and Stuff 12-31
Translate
.ORG $4000 (Assemble) $4000 $92
LDA
LDA [$4ED2,X] $4001 $4E
(indexed)
$4002 $D2
The indirect addressing mode: When we play with the indirect addressing
mode, we’re really starting to cook on a hot stove (Figure 12.32).
Translate
.ORG $4000 (Assemble) $4000 $93
LDA
LDA [[$4ED2]] $4001 $4E
(indirect)
$4002 $D2
The indirect post-indexed addressing mode: Settle down and cease your
whimpering ...... this is the very last one. Once again, as its name might
suggest, the indirect post-indexed addressing mode is a combination of the
indirect and indexed addressing modes that we’ve already seen
(Figure 12.34).
Note that the previous pre-indexed indirect mode and this indirect post-
indexed mode are distinguished in the assembly source file by the
positioning of the “X” keyword. In this post-indexed case, the Beboputer
uses the address it finds in the instruction’s operand bytes to point to a
second address, it internally adds the current contents of the index register
to this second address, and it uses the result to point to the data. Thus,
Assembly Language and Stuff 12-33
“LDA [[$4ED2],X]” means “Load the accumulator with the data value that
will be found in the memory location that’s pointed to by a 2-byte address,
where this address is determined by adding the current contents of the
index register to the address whose first byte is located at address $4ED2.”
Translate
.ORG $4000 (Assemble) $4000 $95 LDA
LDA
LDA [[$4ED2],X] $4001 $4E (indirect
(indirect
$4002 $D2 post-indexed)
postindexed)
These last two modes are fairly esoteric and you might not find much use for
them in the normal course of your routine. You can actually write any
program using just the implied, immediate, and absolute addressing modes
(or just the implied and absolute modes at a pinch) – the other modes are
simply there to make our lives easier and our programs smaller and faster.
On the other hand, should you become enamored of creating your own
programs, you might be pleasantly surprised to discover just how frisky the
more sophisticated modes can be.
4
EDSAC is discussed in more detail in Chapter 15.
Assembly Language and Stuff 12-35
then press the Enter button to force-load this data into the target address in
your random-access memory. You can repeat this process for different
addresses and data values indefinitely (or until you run out of physical
addresses) to load a program in machine code, should you so desire.
Now let us suppose that your mother and a gaggle of her friends are visiting
on the morrow to see your latest invention. Not unnaturally, you determine
to put on a bit of a show. The problem is that watching a computer
performing calculations is intrinsically rather boring, so you decide to add
a display panel with flashing lights that reflect the current values on the
address and data busses. (Actually, this isn’t as weird an idea as it sounds –
the display panels on some of the early computers were primarily added to
give journalists something visual to “Ooh” and “Aah” about.) Next you jot
down a simple program on a piece of paper, where this program will
perform some cunning calculations and, eventually, present the result on
your display panel. (Naturally you’re writing your program in machine
code, because the concept of assembly language hasn’t dawned on
you yet).
Having designed your program, you spend some considerable time entering
the little rascal using your switch panel (Figure 3.35). The hour hand sweeps
around the clock in grand Hollywood style until, suddenly, the birds start
their dawn chorus outside your window and you realize that you’ve
finished. With trembling hands you enter your program’s start address on
the switch panel and press the Run button, thereby handing control over to
your creation. It works! (We decided to be charitable and give you the
benefit of the doubt.) Your mother and her cohorts duly arrive and ferret
around, praising your accomplishments in strident tones (whilst
surreptitiously running fingers across every available surface to check for
dust), and eventually depart in a maelstrom of missing purses and misplaced
umbrellas ...... sweet silence falls, and you take a few moments to bask in
the glow of a job well done.
However, apart from the fact that entering your program via the switch
panel was tedious and error-prone, you now have another major problem
on your hands, in that turning your computer off will cause it to forget
everything you’ve done. This means that the next time you wish to show off
your computer to your friends, you’re going to have to enter your program
into the system all over again (and it wasn’t all that much fun the first time
around). One solution would be to throw a tantrum; alternatively, you
could add a paper tape reader/writer to your computer, and then design a
subroutine that could copy a selected portion of your memory onto a paper
12-36 Bebop BYTES Back
tape. In order to create this subroutine, you would employ the same
technique that you used for your first program; that is, writing the machine
code down on a piece of paper and entering it into the computer by hand.
bytes, so you can still add three lines to your “Machine Code” paper:
“$4006 $C9,” “$4007 $??,” and “$4008 $??,” where the question marks
indicate that you aren’t sure what values should go into addresses $4007
and $4008 at this moment in time. Similarly, in your “Symbol Table” paper
you can add the name GROK, indicate that you don’t actually know its
value yet, and note that when you do eventually determine the value of
GROK you’ll need to copy it into locations $4007 and $4008
(Figure 12.38). Symbol Table
SWITCHES = $F000
LOOP = $4000
GROK = $????
-> $4007
Source code
Figure 12.40: The process you’d like to follow in the fullness of time
Editor Source
As we see, your editor program (when you got around to creating it, and
assuming this is the sort of editor you decided to create in the first place)
would reside somewhere in the RAM portion of your computer’s memory
map (which, as the fates would have it, looks remarkably similar to our
Beboputer’s memory map). The editor would loop around reading an input
port connected to the teleprinter and waiting for you to press a key on the
keyboard. Whenever you pressed a key, the editor would store the
corresponding ASCII value somewhere in the RAM, gradually building up a
machine-readable copy of your source file (or any other type of text file you
cared to create, such as a letter to your great-aunt).
Also, remember that the keyboard and printer portions of the teleprinter are
logically and physically distinct, which means that pressing a key on the
keyboard does not, of itself, cause that character to be printed out. Thus, in
addition to storing your characters in the RAM, it would also make sense for
your editor program to write their ASCII codes to an output port driving the
teleprinter (this process may be referred to as “echoing” the characters).
In addition to simply entering characters, you also need to be able to give
your editor commands, such as “save my source file to paper tape” or “load
a source file from paper tape.” Also, according to our original specification,
you need the ability to interactively view selected lines or portions of your
source file, add new lines, and delete unwanted lines. There are several
ways in which you could achieve this, but one of the simplest would be to
use control sequences. As you may recall from Chapter 10, when we
designed our keyboard we said that our CTRL key would modify the
alphabetical keys such that ‘A’ would generate ASCII code $01, ‘B’ would
generate ASCII $02, ‘C’ would generate ASCII $03, and so forth. Assuming
12-46 Bebop BYTES Back
that you designed your teleprinter in the same way, your editor could keep
a watchful eye open for the following codes:
a) The code $01, which equates to <CTRL-A>, could instruct the editor
to add a line.
b) The code $04, which equates to <CTRL-D>, could instruct the editor
to delete one or more lines.
c) The code $0C, which equates to <CTRL-L>, could instruct the editor
to list lines (in the context of this editor, “listing lines” means causing
the teleprinter to print them out).
d) The code $13, which equates to <CTRL-S>, could instruct the editor
to save the file to paper tape.
e) The code $15, which equates to <CTRL-U>, could instruct the editor
to upload a file from paper tape.
Each of these commands would require additional qualifiers known as
“arguments”; for example, after pressing “<CTRL-L>”, you would have to
tell the editor which lines you would like to list. Once again, there are
many possible alternatives, but a simple option in the case of the list
command would be to follow the control sequence with two colon-
separated numbers. Thus, “<CTRL-L>14:22” could instruct your editor
to list lines fourteen through twenty-two.
Since the editor we just described functions in a line-oriented manner, it is,
not surprisingly, referred to as a line editor. Editors of this type were very
common in the early days, largely because they were just about the only
such tool you could use in conjunction with a teleprinter. However, they
also have advantages in their own right, such as being reasonably simple to
create, not requiring much memory, and being very efficient in terms of
CPU resources (which means that they don’t place much demand on the
computer). Thus, even after the combination of keyboards and memory-
mapped displays flounced onto the stage, line editors still managed to find a
role to play (in fact there are modern variants around to this day). Even so,
the advent of memory-mapped displays also threw open the doors to
another style of editor called a screen editor (Figure 12.42).
Assembly Language and Stuff 12-47
Computer's memory map
Editor Source
Video
RAM
FR
D
.E
QU
LO .OR $F
P G 02
LD $80 0
A
$6 00
5
$D3 with these keys – see also Table 11.1 in the previous chapter.) When
one of these keys is pressed, the editor could move some sort of cursor
character around the screen. Similarly, to navigate around the file you might
use control sequences such as <CTRL-T> or <CTRL-B> to instruct the editor
to display the top or bottom of the file, respectively (and so forth for a
variety of other commands that you might decide would be of use in
your editor).
Remember that Figure 12.43 is just a convenient way to visualize what’s
happening, but you must not allow this illustration to mislead you. Flick
back to Figure 12.42 to remind yourself that the source (or text) file is stored
in one section of the computer’s RAM, and it’s up to your editor to copy a
selected portion of the file into the Video RAM. A little thought reveals that
the editor is going to have quite a lot of work on its hands (Figure 12.44).
$EE00 $48 = H
$EE01 $49 = I
$EE02 $20 = <SP>
$EE03 $48 = H
$8063 $48 = H
$EE04 $4F = O
$8064 $49 = I
$EE05 $20 = <SP>
$8065 $20 = <SP>
$8066 $48 = H
H I H O
$8067 $4F = O $EE1F $20 = <SP> Y O U
$8068 $0D = <CR> $EE20 $59 = 'Y'
$8069 $59 = 'Y' $EE21 $4F = 'O' Memory-mapped screen
$806A $4F = 'O' $EE22 $55 = 'U'
$806B $55 = 'U' $EE23 $20 = <SP>
what do you think might happen? Well as a best case your editor might just
sit there, in which case you’d quickly realize that there was a problem and
leap into the fray to start sorting it out. As a worse case, your program could
wreak havoc throughout the system, writing data where it wasn’t supposed
to and quite possibly trashing your hex keypad monitor and the editor itself,
before finally giving up the ghost and withdrawing into the nether regions
from whence it came. Thus, the first thing you’d do after entering your
editor’s machine code is to back it up by copying it onto a paper tape
(or a similar mechanism).
Once you’ve debugged your rudimentary editor and persuaded it to work,
your next task would be to create an equally simple assembler. Once again,
you would first determine which features you considered to be vital and
which you could live without for a while. For example, you don’t really
need the ability to use both uppercase and lowercase letters in your source
file, so you might decide that your first assembler will accept uppercase
letters only. Similarly, a minimum requirement would be to support address
labels, but you could restrict each label to consist of say only four
alphabetic characters (no more and no less). Having .ORG, .END, and
.BYTE directives would probably be a minimum requirement, but you could
get by without having any form of .EQU directives or equations (you’d have
to use more labels, but “so what?”). As usual, you’d now draw your
flowcharts, write your source code down on a piece of paper, hand-
assemble this source code into machine code on another piece of paper,
enter your machine code into the computer using your hex keypad, and
back it up onto a paper tape.
One of the tasks we didn’t mention earlier, but which you would hopefully
have undertaken before creating either the editor or the assembler, is to
define your proposed usage model, by which we mean a high-level
specification describing the way in which you anticipate using these tools
and the way in which they are going to interact. For example, if your
computer has only a limited amount of RAM, you may be forced to adopt a
somewhat disjoint usage model (Figure 12.45).
As you can imagine, this usage model would have its painful aspects, not
the least that it could take an awful long time to debug a program of any
size. On the other hand, if your computer doesn’t have much memory then
you may not have any choice, and this model certainly wouldn’t have
raised too many eyebrows in the early days.
Assembly Language and Stuff 12-51
have to use your hex keypad one last time to transfer control to your
keyboard monitor, and then you would really be ready to rock and roll.
When you come to think about it, a simple QWERTY keyboard monitor
would actually be ..... well, quite simple. For example, your first
implementation might loop around checking the keyboard, waiting for you
to press one of three keys: ‘E’, ‘A’, or ‘R’. When you pressed the ‘E’ key the
monitor could transfer control to your editor program, which you could then
use to enter some new source code into the memory. When you exited the
editor it would return control to the keyboard monitor. Similarly, pressing
the ‘A’ key could instruct the keyboard monitor to hand control over to your
assembler program, which could read the source code in memory and
generate the associated machine code. Once the assembler had done its
deed, it also could return control back to the keyboard monitor. Finally,
pressing the ‘R’ key could cause the keyboard monitor to run your new
machine code. Obviously we’ve simplified certain things in this description,
such as how the keyboard monitor knows where to find the editor and the
assembler, and how these tools know where to locate the source and
machine code. However, it wouldn’t take you much effort to solve these
problems, and one of the fun things about computers is that everyone can
come up with their own ideas and techniques.
Anyway, let’s assume that you’re now in possession of an editor and an
assembler. Irrespective of their simplicity, they will greatly ease the task of
creating any future programs. Furthermore, it probably wouldn’t take long
before you started contemplating ways in which you could improve these
tools. For example, you might decide that life would be a great deal sweeter
if your assembler could support labels with up to eight characters, and that
you really, really need the ability to use .EQU directives and rudimentary
expressions in your source code.
Improving things in this way is like an itch that you’ve got to scratch, and
before long you would come to believe that life is meaningless without
these enhancements. You would find yourself using your existing editor to
create the source code for your new assembler, and then use your existing
assembler to assemble this source code and generate the machine code for
your new assembler. Next, you might use your existing editor and your new
assembler to create a better editor, and then ...... carry on around and
around the circle, using your existing tools to create better tools and using
these tools to create still-better tools, which, once again, is why we refer to
the process as: “Pulling yourself up by your bootstraps.”(8)
Assembly Language and Stuff 12-53
Creating a cross-assembler
Assume that you awake one morning with a song in your heart, fire in your
belly, and a brilliant idea for a brand new computer. Flushed with
enthusiasm, you immediately set sail on your latest voyage of discovery.
The days fly by, and before long your new masterpiece stands before you in
all its glory. As for your first computer, this new beast initially hangs around
waiting for you to come up with the software tools required for it to do its
cunning stuff. Do you have to go through the entire frustrating process of
creating new monitors, editors, and assemblers from the ground up? “No,”
you cry, “a thousand times no!” In fact, you can use the existing tools on
your first computer to create a fresh set for your new computer.
Of course there are certain complications, but aren’t there always? Possibly
the biggest consideration is that you’ve learnt a lot from your original
project, so your new computer will almost certainly have a different
architecture and instruction set than your old machine. This doesn’t pose
insurmountable problems, but it can, as usual, involve some convoluted
thinking.
One of the first tasks you might set yourself is to create an assembler for
your new computer, but this is where things start to get a little tricky.
Assuming that your new computer does have a different instruction set
(which may well include different addressing modes), then you’re going to
have to specify a new assembly language with its own mnemonics, syntax,
and semantics. Once you’ve defined your new language, the next stage is to
write an assembly program that will accept this language and generate your
new machine code (Figure 12.47).
In this scenario, you have to write this new assembler in your original
source code (Figure 12.47a) and assemble it through your original assembler
(Figure 12.47b); the resulting machine code is your new assembler. Note
that your new assembler will only actually run on your original computer,
because its machine code was generated by your original assembler.
However, you can now create the source code for some other program in
your new assembly language (Figure 12.47c) and assemble it through your
new assembler (Figure 12.47d). Interestingly enough, the resulting machine
code won’t run on your original computer, because your new assembler
8
One of the interesting quirks about pulling yourself up by your bootstraps in this manner is
that you end up riding the crest of a wave and, although you know where you are, you can’t
quite remember how you got there. To put this another way, it’s quite common for earlier
incarnations of your tools to become lost in the mists of time, to the extent that it becomes
well-nigh impossible to re-create your path through the labyrinth.
12-54 Bebop BYTES Back
generated the machine code used by your new computer, which means that
you now have to copy this machine code over to your new computer in
order to run it (Figure 12.47e).
Original Computer New Computer
This is a
Original Source Original cross-assembler
editor code assembler
(c) Use original editor to (d) Use new assembler (e) Copy machine
create source code to generate machine code code for "some
(in new language) (in new instruction set) program" over to
for "some program" for "some program" new computer
Tool Bar
QWERTY
Keyboard
Hex Keypad
Memory-
Mapped
Display
Status Bar
The tools that appear in the working area are familiar to us: the memory-
mapped display, the hex keypad, and the QWERTY keyboard. Note that the
memory-mapped display is initially black, because the Beboputer hasn’t
been powered up yet. To rectify this situation, click the ON button on the
hex keypad, which powers up the Beboputer and the memory-mapped
display. As we observed in the previous lab, the display powers up to show
random characters, which reflect the uninitialized contents of the
Beboputer’s video RAM. Now rather than using the hex keypad to enter a
machine code program to clear the display, we are going to use our cross-
assembler to create this machine code for us.
Toolbar
Working
area
Status and
message bar
you in the future. The reason we required you to use our existing source
code is that, for some inexplicable reason, it contains two errors that we’d
like you to correct (Figure 12.52). Both of these errors are fairly obvious, but
play along with us for a while. The first error occurs in the second line,
where the .EQU directive is spelt incorrectly. Let’s assume that you spot this
error on your own. Our editor works in the standard way for such tools, so
move your mouse cursor to the left of the ‘U’, press-and-hold the left mouse
button and drag the cursor to the right to highlight both the ‘U’ and the ‘Q’,
then release the mouse button and type “QU” to correct the problem.
MONITOR: .EQU $0000 MONITOR: .EQU $0000
CONTROL: .EUQ $F028 CONTROL: .EQU $F028
.ORG $4000 .ORG $4000
LDA $110 LDA $10
STA [CONTROL] STA [CONTROL]
JMP [MONITOR] JMP [MONITOR]
.END .END
c) Use the Memory → Load RAM pull-down menu to load the machine
code into the Beboputer’s RAM.
d) Use the hex keypad to run the machine code program.
In the real world the machine code generated from step (b) would have to
be transported to the Beboputer by some means. In the past this would
probably have involved paper tapes or punched cards, whilst today we
might use something akin to a floppy disk or a direct network connection
between the two machines. However, in our virtual world, we’re making
the assumption that step (c) encompasses the act of copying the machine
code from your main computer to the Beboputer.
get used to is the fact that the program counter (PC) field is always left
pointing at the first byte of the next instruction to be executed, while all of
the other fields reflect the state of registers after the previous instruction was
executed. Thus, as you’ve just stepped through the first instruction
(the “LDA CLEARSCR”), the accumulator (ACC) field shows $10, which is
the value you just loaded. Meanwhile, any of the status flags that were
affected by this instruction are set to the appropriate values, and the
program counter field updates to show $4002, which is the address of the
next instruction. Note that any “X” values that remain in the display indicate
registers that have not been initialized yet (such as the stack pointer (SP) ),
but we’ll look at these in a little while.
You may have noticed that the assembly program you just entered is almost
identical to the program we wrote in Chapter 11 (Figure 11.25), but it was a
lot easier this time around. As you may recall, this program first clears the
memory-mapped screen, then loops around reading the QWERTY keyboard
until you press a key. Click the hex keypad’s Step button a few times and
watch the program reading $00 values from the QWERTY keyboard. Now
click a key on the keyboard, then continue to click the Step button until the
corresponding character appears in the sixth column of the sixth row on the
memory-mapped display. Keep on stepping through this program until
you’re relatively happy with your understanding of the way in which the
CPU register display operates, then click the Run button and play some
more. Finally, click the Reset (Rst) button to return us to the monitor
program.
instruction to retrieve (“pop”) the value off the top of the stack and load it
back into the accumulator. When the CPU sees a POPA instruction, it first
increments the stack pointer to point to the last value placed on top of the
stack, and then copies this value back into the accumulator.
There are several reasons why we might prefer to use this push and pop
technique as opposed to storing the accumulator to a temporary location,
not the least that the “STA [TEMP]” and “LDA [TEMP]” instructions we
considered earlier each require three bytes, while PUSHA and POPA
instructions only require a single byte. This means that a program using the
push-and-pop approach will be both smaller and faster than the store-and-
load alternative. Also, the push-and-pop method saves us from littering our
program with temporary locations, which can be more advantageous than
it might at first appear.
We will find a use for this technique and variations of it in the next section.
Also, we should mention that a PUSHSR instruction can be used to copy
the contents of the status register onto the stack, while a POPSR instruction
can be used to retrieve the value on the top of the stack and load it into the
status register. Apart from anything else, using a PUSHA followed by a
POPSR allows us to copy the contents of the accumulator into the status
register (which allows us to play cunning tricks like setting and clearing
status flags), while a PUSHSR followed by a POPA allows us to copy the
contents of the status register into the accumulator.(9)
.ORG $40000
BLDSP $4FFF
LDA $65 $4FFC $XX
JSR [CLEAR] Stack pointer
$4FFD $XX
INCA (just after the JSR)
$4FFE $40
$4FFF $08 Previous top of stack
$5000 $XX
CLEAR: LDA $10 $5001 $XX
STA $F028
RTS
Figure 12.57: Calling a simple subroutine
When we enter the subroutine itself, the first thing it does is to load the
accumulator with $10 and store this value to address $F028. You may
recognize this sequence as being the one we use to clear the memory-
mapped display. The last instruction in the subroutine is the RTS (“return
from subroutine”), which instructs the CPU to reload the program counter
with the return address from the top of the stack (during which process the
stack pointer is automatically incremented by two memory locations).
value. Similarly, when the RTS instruction causes the CPU to retrieve the
return address from the stack, it automatically leaves the stack pointer in the
right place for the POPA instruction to be able to retrieve the original value
in the accumulator. (Note that the addition of the 1-byte PUSHA instruction
causes the return address (the address of the POPA instruction) to be
modified to $4009.)
.ORG $40000
BLDSP $4FFF
LDA $65
PUSHA $4FFC $XX Stack pointer
(just after the JSR)
JSR [CLEAR] $4FFD $40
POPA $4FFE $09
INCA $4FFF $65 Initial top of stack
$5000 $XX
$5001 $XX
CLEAR: LDA $10
STA $F028
Figure 12.58: Pushing the accumulator
RTS
before calling the subroutine
.ORG $4000
$4FFB $XX Stack pointer
BLDSP $4FFF
$4FFC $40 {just after the JSR}
LDA $32
PUSHA $4FFD $0C
LDA $45 $4FFE $45
PUSHA
$4FFF $32 Initial top of stack
JSR [GETBIG]
POPA $5000 $XX
Note that in the case of this particular example, we are explicitly defining
the two numbers to be compared as being $32 and $45. Your first reaction
may be that this program is pretty meaningless, because we already know
which of these two numbers is the larger. You’re right. However, in the real
world the numbers under consideration would typically be the results of
previous calculations, so their values at any particular time would be
unknown to the programmer.
This program works in a reasonably straightforward fashion. After initializing
the stack pointer with the BLDSP instruction, we load the accumulator with
$32 and push this value onto the stack. Next we load the accumulator with
$45 and push this value onto the stack. Then we call a subroutine named
12-68 Bebop BYTES Back
BIGONE, which will determine which of these numbers is the larger and
return it to us. The act of calling the subroutine automatically causes its
return address of $400C to be put on top of the stack, where this is the
address of the POPA instruction immediately following the JSR. Now let’s
turn our attention to the subroutine itself (Figure 12.61).
RADDR: .2BYTE
TEMP: .BYTE
Take a firm grip on your seats, because this might appear to be a little
bit scary at first, but it really isn’t as bad as it seems. The first thing the
subroutine does is to pop the top byte off the stack (the most-significant
byte of the return address) into the accumulator. Next we store this byte
into a temporary location called RADDR (meaning “return address”). Note
that RADDR is a 2-byte field and, as usual, the label is associated with the
first byte. We continue by popping the least-significant byte of the return
address off the stack, and storing this into RADDR+1 (the second byte of the
RADDR field).
Next we pop the first number ($45) off the stack and store it into a
temporary location called TEMP, then we pop the second number ($32) off
the stack and leave it in the accumulator. Now we use a CMPA instruction
to compare the value in the accumulator to the value in TEMP. Remember
that a CMPA will set the carry flag to logic 1 if the value in the accumulator
is the larger; otherwise, it will clear the carry flag to a logic 0. Thus, the JC
(“jump if carry”) instruction following the CMPA says: “If the value in the
accumulator is the larger, then jump to the label RETURN.” In this
particular example, the value in the accumulator is actually the smaller of
Assembly Language and Stuff 12-69
the two, so the jump never occurs and we end up loading the value in
TEMP back into the accumulator. Thus, whichever path we follow to reach
RETURN, we know that the accumulator now contains the larger value.(11)
If you return to consult Figure 12.61a, you’ll see the state of the stack at this
stage in the game. As we’ve popped all of the values off the stack, the stack
pointer has returned to its initial value. Note that the act of popping a value
off the stack doesn’t physically erase that value from the memory, which is
why these values continued to be displayed. The fact that the data values
are still hanging around can be of use in certain circumstances, but we
aren’t particularly interested in this aspect of things here.
In any case, we’re currently at the label RETURN, and we know that the
accumulator contains the larger of our two numbers, so we use a PUSHA
instruction to copy this value back onto the stack. Next we load the
accumulator with the least-significant byte of the return address (which we
previously stored in our temporary location RADDR+1), and we push this
value back onto the stack. Now we load the accumulator with the most-
significant byte of the return address (which we previously stored in the
temporary location RADDR) and push this value onto the stack. Thus, Figure
12.61b shows the state of the stack just before we use the RTS instruction to
return from the subroutine. Now consider the state of the stack upon our
return to the main body of the program (Figure 12.62).
.ORG $4000
$4FFB $XX
BLDSP $4FFF
LDA $32 $4FFC $40
PUSHA $4FFD $40
LDA $45 $4FFE $0C Stack pointer
PUSHA {just after the RTS}
$4FFF $45
JSR [GETBIG]
POPA $5000 $XX
Multiple RTS’s
One point that may not be self-evident to a beginner is that a subroutine is
allowed to contain multiple RTS instructions. This can be very useful
because, depending on the results from a test, we may decide to return to
the main body of the program immediately or wander off to perform other
actions and then return (Figure 12.63).
In this particular example, we AND the
AND $0F
JZ [CONT] contents of the accumulator with $0F, and
RTS then perform a test to see if the result is
CONT: LDA $42 zero. If the contents of the accumulator are
RTS
zero the JZ (“jump if zero”) instruction will
cause the CPU to jump to the label CONT;
Figure 12.63: Subroutines can
otherwise, the test will fail and the program
have multiple RTS instructions
will execute the first RTS instruction to exit
the subroutine. However, if the program does jump to the CONT label,
it will first load the accumulator with $42, then execute the second RTS
instruction and exit the subroutine. (Note that none of the data values
shown here have any particular meaning beyond the scope of this example.)
Nested subroutines
Another point to consider is that there’s nothing to stop one subroutine
calling another, where we refer to this situation as nesting or nested
subroutines. For example, suppose that we want to call a subroutine called
PANIC from the main body of the program, where the purpose of PANIC is
to display a warning message on our memory-mapped display. The first task
we might wish the PANIC subroutine to perform is to clear the screen. We
could include the code to do this in PANIC itself, or we could simply ask
PANIC to call the CLEAR subroutine we created earlier (Figure 12.64).
When the main body of the program calls the PANIC subroutine, the return
address for PANIC is automatically pushed onto the top of the stack. In this
example, the first thing PANIC does is call the CLEAR subroutine, which
causes the return address for CLEAR to be pushed onto the new top of the
stack. After CLEAR has done its stuff, its RTS instruction will pop its return
address off the top of the stack and return us to the location in the PANIC
subroutine which called it. Similarly, when PANIC has completed its
remaining actions, its RTS instruction will pop its return address off the top
of the stack and return us to the location in the main body of the program
which called it. In the case of the Beboputer, you can nest subroutines to
Assembly Language and Stuff 12-71
any convenient depth, limited only by your stamina and the space available
for your stack; that is, subroutine “A” can call subroutine “B,” which can
call subroutine “C,” ad infinitum, until you begin to run out of memory and
your stack starts to overwrite your main program.
# This is the main
# (calling) program
JSR [PANIC]
$5000 $XX
.ORG $4000
BLDSP $4FFF # Initialize the stack pointer
LDA $73 # Load ACC with 1st 8-bit number
PUSHA # and push it onto the stack
LDA $16 # Load ACC with the 2nd 8-bit number
PUSHA # and push it onto the stack
JSR [_UMULT8] # Call the _UMULT8 subroutine
Purely for the sake of this discussion, we will make the assumption that the
first instruction in the FACTOR subroutine occurs at address $4100. Also,
although we haven’t shown the code for the _UMULT8 subroutine here, you
may assume that it’s hanging around somewhere in the program.
Note the declaration statement at the beginning of the main program, in
which we assign $03 to the label VALUE. This is the number for which we
intend to calculate the factorial (that is, 3!). Thus, by simply changing this
assignment to $01, $02, $04 or $05, we can make our program calculate
1!, 2!, 4!, or 5!, respectively. The first instruction in the program is a
BLDSP, which we use to initialize the stack pointer to $4FFF. Next we load
the accumulator with the value for which we wish to calculate the factorial,
then we call the subroutine FACTOR, which will actually perform the
calculations. When this subroutine returns control to the main program in
the fullness of time, it will leave the result of its calculations in the
12-74 Bebop BYTES Back
The _UMULT8 subroutine takes the two $01 bytes that we left on the top of
the stack (Figure 12.68g), multiplies them together, and replaces them with
the 16-bit result $0001 (Figure 12.69a). Remembering that data isn’t
physically obliterated from the stack, the $?? entries in Figure 12.69 are
12-76 Bebop BYTES Back
Self-modifying code
Before we commence this topic, we should note that self-modifying code
really has no relation to subroutines per se, but we couldn’t think of
anywhere else to stick it (which may explain why so few books get around
to mentioning it at all).
By now we’re familiar with the concept that a program can modify its own
data, but one point that many newbies don’t intuitively grasp is that a
program can also modify its own instructions!
.ORG $4000 Consider the simple example in Figure 12.70.
LDA $20
STA [MODIFY] If we ignore the first LDA and the STA for the
LDA $45 moment, you will see that the second LDA in
MODIFY: ADD $16
the source code directs the computer to load
the accumulator with $45, then an ADD
Figure 12.70: Self-modifying code instruction directs the computer to add $16 to
this value. However, if we were to run this
program, we would find that the computer would actually subtract $16 from
the value in the accumulator. This is because the first LDA loaded the
accumulator with $20, then the STA stored this value into the location
associated with the label MODIFY. As it happens, $20 is the opcode for a
SUB instruction in its immediate addressing mode. Thus, our program will
12-78 Bebop BYTES Back
replace the opcode for an ADD with the opcode for a SUB, and then
proceed to execute this new instruction.
Note that we’re in no way recommending this as an everyday practice.
Generally speaking this sort of coding would be considered to be very, very
naughty, and programmers who use this technique usually deserve to be
soundly and painfully chastised (unfortunately we live in strange times, and
this may actually be an inducement to some of them).
However, now that we’ve opened this Pandora’s box, we may as well
mention that there are several different flavors of self-modifying code. One
flavor is a program which changes existing instructions as discussed above,
while another is “on-the-fly code generation,” whereby a program may
generate new instructions (and possibly data) in some free area of the
memory and then transfer control to these new instructions. Although this
may seem a little weird, it is actually not uncommon in such things as
graphical applications, in which the programmer is striving for extreme
speed. In this case, it may be possible to generate a routine to perform a
specific graphical operation and then execute this routine faster than it
would be to use a pre-created general-purpose routine, because a general-
purpose routine may be carrying a lot of “baggage” around to make it
“general-purpose.”
Creating an “Etch-A-Sketch-like”
program(12)
As we begin to move toward the close of this chapter (they said hopefully),
we are going to create a slightly more sophisticated program that will allow
us to demonstrate some additional utilities that are provided with your
Beboputer. What we are going to do is to design an Etch-a-Sketch-like
program that responds to the cursor-control keys on your virtual QWERTY
keyboard. This program will commence by placing a dot in the middle of
your screen, and then loop around waiting for you to click one of the
cursor-control keys, at which time it will place a new dot on the screen in
the direction corresponding to whichever cursor-control key you selected.
Pause for a moment to consider how you would implement such a program,
then perform the following actions.
First, click the Assembler item on the Windows 95™ task bar to return it to
its former glory. Next click the File item on the assembler’s menu bar (not
12
Etch-A-Sketch is a registered trademark of the Ohio Art Company.
Assembly Language and Stuff 12-79
the project window’s menu bar), click the New entry in the resulting pull-
down menu, then enter the following source code. Feel free to perform a
Save As operation at any time during the process of entering the code and
save this program as “lab7_p3.asm.” Once you’ve performed the initial
Save As, you can use standard Save operations as and when you see fit
(you can also cut down on the comments if you wish).
# Etch-a-sketch-like program created by a very tired guy called
# Max who can’t wait to get to the end of this chapter :-)
hand side of the dialog window, you will find the Menu bar
new “lab7_p3.ram” file which you just created.
Double-click this entry to select this file and add it to the right-hand list,
then click the Load button to load this machine code into the Beboputer’s
RAM and dismiss the form.
Ensure that the hex keypad’s Address (Ad) button is active, enter the
program’s start address of $4000, then click the Run button. Our program
first instructs the memory-mapped display to enter its medium-resolution
mode, which may take a few seconds while the display loads all of the
random colored blocks to the screen (see also the discussions in the
previous chapter). The program then clears the screen (to black), places a
white dot in the middle of the display, and starts to loop around reading the
QWERTY keyboard waiting for you to click one of the cursor-control keys
(LE = left, RI = right, UP = up, DO = down). Try clicking a single time on
the UP key and wait for a second dot to appear above the first, then click
the RI key and wait for a third dot to appear. The amount of time it takes for
12-84 Bebop BYTES Back
the dot to appear will depend on the speed of your real computer – please
be patient if your computer is a little slow (emulating the Beboputer is a lot
of work for the poor little rapscallion), and try to wait for each dot to appear
before clicking another key.
While the program is still running, close the CPU Register display (also close
the memory walker if it’s active) and note the improvement in the
Beboputer’s response. When you’re ready to continue, click the hex
keypad’s Reset (Rst) button to return us to the Beboputer’s reset mode.
00020 # Initialize the memory mapped screen into the required state
00021 4000 90 03 START: LDA GPHCODE
00022 4002 99 F0 28 STA [MMSPORT]
00023 4005 90 10 LDA CLRCODE
Line No.
Address Operand Label Instruction Operand
(opcode)
Opcode
Following a few lines of introductory preamble, we enter the list file proper.
During the process of reading the source file the assembler assigns a number
to every line, and this number is displayed (in decimal) in the left-hand
column of the list file. The original source code is displayed on the right-
hand side of the list file; thus, we see that the body of our program
commenced on line 21 when we loaded our accumulator with the value
assigned to GPHCODE (note that the line numbers you see on your screen
may vary from those in these discussions).
Of particular interest is the fact that the machine code relating to each line
of source code appears between the line number and the source code. The
first value after the line number is the hexadecimal address of the
instruction. Following this address we may see one, two, or three values
depending on the instruction and its addressing mode, where the first value
is always an opcode (except in the case of .BYTE, .2BYTE, and .4BYTE
statements). For example, on line 21 we used a LDA instruction with an
immediate addressing mode; the $90 value is the opcode for this
instruction, while the $03 value is the immediate data (which, in this case,
12-86 Bebop BYTES Back
was the value we assigned to GPHCODE). (Note that ‘$’ characters are
omitted from the values in the list file to save space, but that only line
numbers are displayed in decimal.)
Moving on to line 22, we see that the address has jumped to $4002; this is
because the previous instruction started at address $4000 and required two
bytes. Following address $4002 we see $99, which is the opcode for a STA
instruction with an absolute addressing mode. This opcode is followed by
the two bytes $F0 and $28, which form the address of the memory-mapped
screen’s output port (from the value we assigned to MMSPORT).
One point we should note here is that the list file will only show comments
that commence in the first column of a line – the assembler will gobble up
any other comments. The reason we decided to do this is that the machine
code portion of the list file occupies a lot of space. Had we left the
comments in, they’d wrap around onto the next line when you printed the
file, which would make it an absolute swine to read.
Following the body of the program you will find the first cross-reference
table, which concerns itself with the constant labels associated with our
.EQU statements (Figure 12.72).
CONSTANT LABELS CROSS-REFERENCE
Click the Display item on the project window’s menu bar, then click the
Memory Walker entry in the resulting pull-down menu. Next use your mouse
to drag this display by its title bar to a
convenient location on the screen (trying
not to obscure the other tools and
BP Step Address Data
displays as far as possible). Now look at
$4000 $90 this display as if it were for the very first
$4001 $00 time, and note the left-hand column of
boxes which have thus far remained
Breakpoint column
empty – the breakpoint column
Figure 12.74: Memory-walker display (Figure 12.74).
Let’s assume that the program you entered above isn’t working exactly as
planned, and that you’re trying to work out exactly what’s going on. One
option would be to run the program and try to understand what it’s doing by
puzzling over its communications with the outside world. However, this
may not prove to be a particularly useful technique if the program “crashes-
and-burns” in a catastrophic manner. Similarly, this approach falters at the
first fence if your program gets itself locked up in a loop and sits there not
doing much of anything.
As an alternative, you might consider single-stepping through the program,
using both the memory walker and the CPU register display to try to
determine exactly what the program is doing. The problem here is that you
might have to step through hundreds or even thousands of instructions
before reaching the point in the program where the problem may lie. Yet
another option would be to start off by running the program, and then
dropping back into the step mode by clicking the Step button in the faint
hope that you’ll end up somewhere useful. Strange as it may seem, this
technique occasionally works to your favor, particularly if your problem
involves the program getting stuck in a loop, but most of the time this
approach leaves something to be desired.
For the sake of discussion, let’s assume that when you run your program it
displays the initial dot on the memory-mapped display, but nothing happens
after this point and the program doesn’t respond to your clicking the cursor-
control keys on the QWERTY keyboard. Glancing through your source
code, you would probably decide that everything is running smoothly until
the label GETNEXT; so what you’d like to do is to run the program up to
this point, and to then start single-stepping through the instructions to see
what’s going on.
Assembly Language and Stuff 12-89
Unfortunately, there’s no way to tell the address of the GETNEXT label from
your source code (unless you want to work it out by hand), so this is where
the list file starts to prove its worth. You can find the address of the
instruction at GETNEXT by simply looking this label up in the cross-
reference table at the end of the list file. Alternatively, you could scan the
body of the list file to find the appropriate section, which reveals that the
required address is $4010 (Figure 12.75)
00035 4010 91 40 78 GETNEXT: LDA [SQUARE+1]
00036 4013 B2 PUSHA
00037 4014 91 40 77 LDA [SQUARE]
00038 4017 B2 PUSHA
ignore these “pseudo breakpoints” in its frantic desire to reach the next
instruction.)
Once you’re happy with the portion of the code you’ve been scrutinizing,
you can use the breakpoint dialog window to clear specific breakpoints, or
you can clear them all in one fell swoop by clicking the Clear All option on
the form followed by the Apply button. Last but not least, when you turn the
Beboputer off, it will promptly forget any and all breakpoints from the
current session (but don’t turn it off just yet).
Click the hex keypad’s Step button to execute the next instruction. If it’s
active, the CPU register display updates to show the current contents of the
Beboputer’s registers. However, although the CPU register display is a very
useful device, it does tend to operate in a rather coarse-grained manner,
which means that it doesn’t tell us a great deal about what actually went
through the Beboputer’s mind while it was performing the instruction.
Assembly Language and Stuff 12-91
By comparison, the message system provides a wealth of information –
use its scroll bar (not shown here) to wander through all of the messages
that were generated during the execution of this instruction.
As you will see, the messages in this display have different colors (on the
screen) and indentations. The left-most text indicates a macro-action, such
as acquiring an opcode, decoding the opcode, and so on. The first level of
indented text reflects micro-actions, such as copying the program counter
into the address latch, while the second level of indented text describes any
additional points of interest, such as the fact that incrementing the program
counter doesn’t affect the contents of the address latch.
New messages will be added to the bottom of the list every time you click
the hex keypad’s Step button. You can use the icons in this display’s tool
bar to print the current contents of the file or to clear the display. Note that
the message system is automatically deactivated whenever you enter the
Beboputer’s run mode. When you’ve finished playing with this display,
click the hex keypad’s ON button (which also turns it OFF), then exit this
laboratory by clicking the File item on the project window’s menu bar
followed by the Exit entry in the resulting pull-down menu.
Conditional assembly
One way in which we could extend our capabilities would be to add the
concept of conditional assembly, whereby the assembler decides whether or
not to process portions of the source code “on-the-fly.” There are numerous
techniques we could use to achieve this; for example, we could add some
new directive statements: .IFT (“if true”), .IFF (“if false”), and .ENDIF
(“end if”) (Figure 12.77).
12-92 Bebop BYTES Back
Macro assemblers
Another way in which we could augment our assembly language would be
to add a macro capability, which essentially allows the user to extend the
language by adding new instructions. Assume that we introduce two new
directives .SMACRO (“start macro”) and .EMACRO (“end macro”) into our
language, and consider how we might use these directives to create a macro
to compare two unsigned 8-bit numbers and leave the larger value in the
accumulator (Figure 12.78).
FRED: .EQU $32
In the case of our BERT: .EQU $45
hypothetical language
extensions, we’ve decided BIGONE .SMACRO {1}, {2}
that any macros have to be LDA {1}
CMPA {2} Macro
defined before the .ORG JC [@+5] definition
statement. This particular LDA {2}
example only contains one .EMACRO
macro definition called
.ORG $4000
BIGONE. The syntax we’re
BIGONE{FRED}, {BERT} Macro call
using is that the .SMACRO
directive would instruct the
Figure 12.78: Defining macros
Assembly Language and Stuff 12-93
assembler that this is the start of a macro definition, while the label
associated with the directive will become the name of the macro.
Following the .SMACRO directive are a number of comma-separated
arguments in squiggly parenthesis “{}.” In the case of our BIGONE macro,
we have two arguments: “{1}” and “{2}.” These arguments instruct the
assembler as to the number of values this macro will expect and the order it
will expect them in. The body of the macro definition contains standard
assembly-language instructions; however, in addition to any standard
arguments, our macro arguments are also allowed to form the operands for
these instructions. Finally, the .EMACRO directive informs the assembler
when it reaches the end of the macro definition.
The important point to understand is that the assembler doesn’t actually
generate any machine code from the macro definition per se, all it does is
memorize the macro’s contents. Only if this macro name subsequently
appears in the main body of the program will the assembler use a process
called macro expansion to insert the macro into the code. In our example,
we actually use the BIGONE macro immediately following the .ORG
statement. The assembler will replace this statement with the series of
instructions in the macro definition, and it will substitute the appropriate
arguments in the macro for the labels FRED and BERT ($32 and $45,
respectively).
Once a macro has been defined, it can be used multiple times in the body
of the program. Macros allow the user to extend the language by defining
new instructions, and they can reduce the size of the source code and
improve its legibility. Also, the code for a macro will run faster than the
code for a subroutine performing an equivalent function, because we don’t
have the overhead of pushing parameters onto the stack and popping them
back off again (see the subroutine corresponding to this macro in the
“Passing parameters into subroutines” section of this chapter). On the other
hand, macros don’t do anything towards reducing the size of the resulting
machine code, because they’re always expanded into in-line instructions
during the assembly process.
Not surprisingly, an assembler with the ability to handle macros is called a
macro assembler. The example we just discussed was a relatively simple
representative of the macro fraternity; there is nothing to prohibit macros
calling other macros (that is, nested macros) or even macros calling
themselves. Also, note that we are just introducing concepts here, while
desperately trying to avoid becoming enmeshed in the complexities. For
example, we used the “JC [@+5]” statement in our macro which means:
12-94 Bebop BYTES Back
“If the carry flag is logic 1, jump to the current address plus five locations”
(where the “current address”, which is indicated by the @ operator, is the
address of the first byte in the JC instruction). The reason we employed this
technique was to avoid using a label inside our macro, because calling the
macro multiple times in the body of the program would result in multiple
copies of the same label, which isn’t allowed. Of course there are ways
around this, such as instructing the assembler to replace any macro labels
with automatically generated versions, in which case it would be the
assembler’s responsibility to ensure that each instantiation of the macro had
its own unique labels.
Meta-assemblers
Earlier in this chapter we introduced the concept of a cross-assembler,
which is an assembler that runs on one computer but generates machine
code for use on another. We can extend this concept in a number of ways;
for example, by creating a tool known as a meta-assembler, which can
assemble programs for multiple systems. Of course, the world being what it
is, there are a variety of alternative meta-assembly techniques, which can
serve to make life somewhat confusing. For our purposes here, we will only
introduce two such approaches: adaptive and generative.
Adaptive meta-assemblers: A key concept underlying meta-assemblers is
that they support a meta-syntactic language, which allows you to define
your own instruction set. Assume that we wished to use a meta-assembler to
generate machine code for our Beboputer. The first thing we would do
would be to create a file defining our instruction set: mnemonics,
addressing modes, opcodes, and so on (Figure 12.79).
MNEMONIC = "AND", ADDRESSING_MODE = IMMEDIATE, OPCODE = $30
MNEMONIC = "AND", ADDRESSING_MODE = ABSOLUTE, OPCODE = $31
MNEMONIC = "AND", ADDRESSING_MODE = INDEXED, OPCODE = $32
Thus, the fact that we’ve used the meta-assembler’s language definition
capability to describe our Beboputer-specific instruction set doesn’t alter the
fact that we will still have to use the meta-assembler’s syntax when creating
a new source file, it just means that we are able to employ our own
mnemonics as part of this syntax.
Once we’ve created a language definition that the meta-assembler can
understand, the next step is to create a source program that we wish to
assemble. Every time the assembler is run, it has to be provided with both
the language definition file and the source code file (Figure 12.80).
The meta-assembler first reads the language definition file in order to learn
the instructions we wish to use, the addressing modes they support, and the
opcodes that are associated with them. The meta-assembler then reads the
source code program and uses its newfound knowledge of the language to
assemble it into the target machine code.
12-96 Bebop BYTES Back
System "X"
language
definition
System "X"
Adaptive
machine
meta-
code
assembler
System "X" program
source
code
program
Figure 12.80: Adaptive meta-assembler
Once again, the fact that we’ve used the meta-assembler’s language
definition capability to describe a system-specific instruction set doesn’t alter
the fact that we will still have to use the meta-assembler’s syntax when it
comes to creating our source code programs; it just means that we can now
use our own mnemonics as part of this syntax.
Assembly Language and Stuff 12-97
Disassemblers
A disassembler, as its name might suggest, performs the complementary
function to an assembler, in that it accepts a machine code program as
input and regenerates a source code equivalent as output (Figure 12.82).
Program in Program in
Machine Disassembler source
code code
As we see, this program segment makes use of two addresses: the input port
connected to the keyboard at address $F008 and the label LOOP at address
$4000. Now assume that we want our loader to take this machine code,
which was intended to start at address $4000, and actually load it starting at
address $6000. We obviously don’t want to loader to do anything to the
address $F008 stored in locations $4001 and $4002, because the port
connected to the keyboard isn’t going anywhere; on the other hand, we do
want the loader to change the address stored in locations $4004 and $4005
from $4000 to $6000. But how is the loader supposed to know which
values to change and which it should leave alone?
One technique for achieving this is to modify our assembler such that it
generates an extra byte for every opcode. If the assembler recognizes an
absolute address that must not be changed (such as a constant label), then it
could set this extra byte to zero. Similarly, if the assembler recognizes an
address that that would need to be changed if the program were relocated
(such as an address label), then it could set the extra byte to a specific non-
zero value. Now when we come to run the relocating loader, we could
design it to strip out these extra bytes and throw them away, but first it
would use them to decide whether or not to modify the their associated
operands by adding the difference between the original start address and the
new start address. (It probably doesn’t need to be said that this technique
causes the output from the assembler to be bloated and ungainly, but we’ll
say it anyway: “This technique causes the ......”).(13)
13
See also the discussion on relocatable code later in this chapter.
12-100 Bebop BYTES Back
Linked
Unlinked
Linking machine
machine code
modules loader code
program
The linking loader (or link-loader) reads all of the machine code modules,
links them together into a single machine code program, and loads the
resulting program into the system’s memory. Generally speaking this
technique only makes sense if the link-loader can relocate all of the
modules as and where it sees fit, so link-loaders require all of the
capabilities of relocating loaders and then some.
There are also other considerations, such as the fact that a link-loader needs
to be able to recognize the main module that starts the program. Also, the
individual modules need the ability to pass data between themselves. This
means that we need to modify our source language to differentiate between
local labels and constants that can only be used within a particular module,
as opposed to global labels and constants that can be accessed by multiple
modules. For example, we might decide to have a .GLOBAL directive that
can be used to make specific labels available to the outside world. This
would then entail modifying our assembler such that it appended each
machine code module with additional information as to any of its attributes
that could be accessed by other modules. The link-loader would then use
this information during the process of constructing the final machine code
program.
In fact, the concept of differentiating between label and constant names that
are local to a module versus names that are visible to other modules has a
number of implications, one of them being the opportunity to come up with
Assembly Language and Stuff 12-101
a whole new set of terms such as scope (meaning the extent to which a
name is visible) and namespace (meaning much the same thing). More
importantly, it allows us to use the same name for local labels in multiple
modules without confusion, because their scope is limited to the module in
which they are declared. Unfortunately, the Beboputer doesn’t support the
concept of modules, which means that we are obliged to use a somewhat
painful naming convention to prevent multiple subroutines having the same
label (you’ll discover more about this when you start to rummage through
the pre-defined subroutines we’ve provided in Appendix G).
Linkage editors: In many respects a linkage editor is similar to a linking
loader, except that once it has assembled the sub-modules into a single
machine code program, it then saves that program to a storage device
instead of loading it into memory. In fact link-loaders often have the ability
to save their output to disk, while linkage editors often have the ability to
load their output into memory, so the boundary between the two can be
rather fuzzy and gray.
Relocatable code
Apart from the implied and immediate addressing modes, the Beboputer’s
instructions are all based on some form of absolute addressing. For example,
when we use a statement like “JMP [FRED]” in the Beboputer’s source
code, the resulting machine code effectively says: “Jump immediately to
address $4065” (assuming of course that the label FRED is declared at
address of $4065). As we discussed in the “Relocating Loaders” section
above, this means that it’s difficult to change the start address of the
machine code program, because we have to “tweak” any references to this
type of address within the program.
However, some computers support relative addressing modes, which
involves special opcodes whose operands are offsets from their current
position. To illustrate this, assume that we have a computer with both
standard and relative instructions, such as JMP (“jump immediate”) and
JMPR (“jump relative”). Further assume that the address of the opcode for
the JMP instruction in the “JMP [FRED]” statement we discussed previously
occurred at address $4053. If our language supported relative instructions,
our assembler could automatically translate this instruction into the opcode
for a “JMPR [$0012],” meaning: “Jump forward $0012 bytes from your
current location.” Using this technique, the loader could simply load the
program into another area of the memory .... and it would still work!
12-102 Bebop BYTES Back
▼
How to use the character editor tool to
create a file containing new
character patterns
▼
How to use our character patterns to burn a
new Character EEPROM device
▼
How to plug our new Character EEPROM into
the Beboputer’s virtual video card
▼
How to create a new system EEPROM
8
b
La
13-2 Bebop BYTES Back
Contents of Chapter 13
ROMs, PROMs, EPROMs, and ...... wait for it ...... EEPROMs ................... 13-3
Winding up the Beboputer ....................................................................................................... 13-4
Creating a file containing our new patterns ................................................ 13-5
Burning a new Character ROM ........................................................................................ 13-8
Using your new Character ROM .................................................................................. 13-10
Creating a new System ROM ........................................................................................... 13-11
Quick quiz #13 ......................................................................................................................................... 13-12
Burning Character and Monitor EEPROMS 13-3
ROMs, PROMs, EPROMs, and ...... wait for
it ...... EEPROMs
When we first introduced our memory-mapped display in Chapter 11, we
noted that the patterns of 0s and 1s used to generate the characters on the
screen are stored in an integrated circuit called a Character ROM. In reality
this is just a standard read-only memory (ROM) device; the only reason we
call it a “Character ROM” is that ...... well you can work it out for yourself
(Figure 13.1).
Character
ROM
Video card
Main system
circuit board
Now, hold onto your hat, because in this chapter you’re going to learn how
to modify the patterns stored in the Character ROM, thereby allowing you to
create your own set of characters. But before we start, it’s important to note
that there are a number of different flavors of ROM devices:(1)
ROM: Read-Only Memory
The patterns of 0s and 1s in a ROM are formed when the
device is constructed and they cannot be modified by the
user.
PROM: Programmable Read-Only Memory
In the case of a PROM, the device is delivered to the user
containing all 0s or all 1s. By means of a special
programming tool used to apply certain signals to the
device’s pins, the user can program the contents of a
PROM. Once programmed the contents cannot be
1
All of the memory devices introduced here are discussed in greater detail in this book’s
companion volume: Bebop to the Boolean Boogie (An Unconventional Guide to Electronics).
13-4 Bebop BYTES Back
Tool Bar
Memory-Mapped Display
Hex Keypad
Status Bar
pattern associated with ASCII code $00. As you may recall from our earlier
discussions in Chapters 10 and 11, this pattern is just something that we
made up to represent the NULL control code.
Character Editor
Pattern file
File Edit Help
"defchar.jed"
1111111110
1000100010
1000100010 ASCII Code
1000100010 $00
1001110010
1111111110
1001110010
"Life-size"
character
Figure 13.3: Loading an existing pattern file into the character editor
Before you do anything else, click the character editor’s File pull-down
menu and select the Save As option. Then save this file our under a new
name such as “mychar.jed,” thereby ensuring that you can’t do anything to
mess up our default character set.
Now click on the arrow button next to the character’s ASCII code, then use
the resulting scrolling list to select ASCII code $41. The pattern that appears
in the character editor is
Remember that the ASCII code is simply a obviously that of a letter ‘A’,
convention that we’ve all decided to live with. which is what we’d expect for
Instead of having a pattern of dots forming a this code.
letter ‘A’ associated with ASCII code $41, you
could modify this pattern to form another letter. Note that five lines of dots are
This means that when you send the code $41 to left blank under the letter ‘A’.
the memory-mapped display you’d see your new These blank lines are used to
letter. However, you would no longer be using the form a gap between this letter
ASCII code, but just another home-brewed code and any other character that
of which the world has more than enough may be located under it when
already, and the end result would be confusion
we eventually replicate them on
for all concerned.
the memory-mapped display.
You may wish to take a few
moments to glance back to Table 10.1 in Chapter 10 to refresh your
memory as to the characters associated with the various ASCII codes, then
pick a few of these characters at random and check the patterns of dots we
Burning Character and Monitor EEPROMS 13-7
used to represent them. After we’ve finished this lab, you may decide that
you can improve on our character set – maybe you’ll think that the “curly”
letters should be just a bit more curly (or a bit less so) – and why not? Feel
free to have at it with gusto and abandon, and be sure to email us to let us
know how well you did.(2)
Now glance back to Table 11.1 in Chapter 11, which shows our “chunky
graphics” codes. Once again, pick a few of these characters at random and
check the patterns of dots we used to represent them. In particular, note that
the characters with codes of $D4 and above all contain the same “dummy
pattern,” because we couldn’t think of anything else to put in them ...... but
you’re about to change all that.
Click the arrow button next to the ASCII code in the character editor, then
use the resulting scrolling list to select character $D4, which is the first of
the characters we didn’t know what to do with. Once again, this initially
contains the dummy character that we employed to differentiate the unused
characters from a space character. One way to clear this character is to
click the Edit item on the character editor’s menu bar, then click the Clear
entry in the resulting pull-down menu (there’s also an icon on the Character
editor’s toolbar for this purpose).
Move your mouse cursor over one of the squares in the grid and click the
left mouse button to turn that square black. As an alternative to clicking on
the squares individually, you can hold the left mouse button down and drag
the cursor over a number of squares. If you wish to change squares back
from black to white, you can do so using the right mouse button.
Now clear the grid again, then use all of your artistic skill to create an
image of the Mona Lisa. If you can’t handle this, just do your best to
achieve a smiley face (it really doesn’t matter how good it is). When you’ve
finished, click the character editor’s File pull-down menu followed by the
Save option, thereby saving your edits in the “mychar.jed” file (Figure 13.4).
Once you’ve generated your new file, dismiss the character editor using the
rightmost button in its title bar, or click the File item on its menu bar then
click the Exit entry in the resulting pull-down menu.
2
If you’re feeling particularly frisky, why not try your hand at creating an italic character set?
13-8 Bebop BYTES Back
Character Editor
New pattern file
File Edit Help
"mine.jed"
1111111110
1000100010
ASCII Code 1000100010
$D4 1000100010
1001110010
1111111110
1001110010
Figure 13.4: Generating a new pattern file from the character editor
If you do create any programs you’d like to load into your System ROM,
then you can use the appropriate entry on the EPROM Burner tool to select
these programs and burn a new device. Once you’ve created this device,
you then have to click the Setup item on the main project window’s
menubar followed by the Swap EPROM entry in the resulting pull-down
menu, and then follow a similar path to that used to create a Character
ROM. To actually invoke one of the programs in a System ROM, you have
to use the hex keypad to specify that program’s start address, and then
select the Step or Run options.
Important! Note that the effects of exchanging the default Character ROM
or System ROM for a user-defined version will only persist for the duration
of the current session. To put this another way, whenever you invoke the
Beboputer or open a new project, the Beboputer will automatically
initialize containing its original Character and System ROM devices.
▼
How much you’ve actually understood thus far
▼
How much you thought you’d understood.....
but didn’t
▼
What it feels like when a program finally works
after you’ve slaved over it for hours
▼
What the ancient Incas had to say about this
sort of thing
9
b
La
14-2 Bebop BYTES Back
Contents of Chapter 14
Using everything you’ve learnt thus far ................................................................. 14-3
Winding up the Beboputer ....................................................................................................... 14-3
The calculator shell .............................................................................................................................. 14-4
Your mission ....... should you decide to accept it ................................. 14-5
Quick quiz #14 ............................................................................................................................................. 14-6
Calculator Sign-Off Project 14-3
Using everything you’ve learned thus far
As we noted at the beginning of this book, computers really aren’t as
difficult to understand as many people believe – it’s largely a case of
learning the basic concepts and building on them one piece at a time. On
the other hand there are a lot of fiddly bits to remember and it can be quite
a lot of effort to learn them sufficiently to grasp the big picture. So if you’ve
worked your way through the preceding chapters and performed all of the
labs, then we’d like to extend our congratulations for a job well done!
In this, the penultimate chapter, we’re going to present you with a sign-off
project that will allow you to use everything that you’ve learnt thus far, add
a large dollop of your own imagination, stir it all together, and create a real-
world application: a four-function calculator.
Tool Bar
Calculator Keypad
Hex Keypad
Status Bar
you’re free to employ any and all of the Beboputer’s tools, but it’s up to you
to decide which ones to use.
(a) Input Port (to Beboputer) (b) Output Port (from Beboputer)
Also note that the calculator’s display does have a limited amount of
hardware-assist to make your life a little easier. For example, assume that
you’ve cleared the display by writing $10 (the ‘Clr’ code) to the output port
driving the calculator, and that you now wish to display the number “42.” If
you first write $04 to this port, the number ‘4’ will appear in the right-hand
column of the display. If you next write $02 to the port, the number ‘4’ will
automatically move one place to the left and the number ‘2’ will appear in
the right-hand column. (Writing the $20 code to the port will cause a minus
sign to appear in the display, while a $70 code will make the calculator
“beep” in an annoying way.)
Finally, note that the calculator keypad doesn’t have an ON/OFF button of
its own, because it’s powered by the main Beboputer. Thus, when you
click the ON button on the hex keypad, this will also serve to power up the
calculator.
For your interest we’ve offered one possible solution to this problem in
Appendix I. However, we strongly recommend that you don’t take even the
briefest peek at our example until you’ve created your own (working)
version, because you will learn far more by struggling with your own
specification and implementation than you ever will by looking at what
someone else has done; you might even come up with a much better
solution that we did (if so, please drop us a line to let us know).
So, that’s your lot ...... good luck ...... and while you’re slaving away, be
sure you don’t forget the ancient Inca saying: “Pthwesa nwe nimegos vah
yubetha” – words we’re sure you’ll agree are as profound and relevant in
today’s world as when they were originally voiced deep in the mists of time.
Contents of Chapter 15
Taking a cruise through history ........................................................................................... 15-3
The first aids to calculation ....................................................................................................... 15-3
Tally sticks – the hidden dangers ..................................................................................... 15-4
The decimal number system .................................................................................................. 15-5
The ancient Egyptians ..................................................................................................................... 15-7
The ancient Babylonians .............................................................................................................. 15-8
The concept of zero and negative numbers ............................................... 15-9
Aztecs, Eskimos, and Indian merchants ............................................................ 15-10
Jobs abound for time-travelers ...................................................................................... 15-10
The first mechanical calculators .................................................................................. 15-11
The first mechanical computers ................................................................................... 15-15
Boolean algebra .................................................................................................................................. 15-18
The American census of 1890 .......................................................................................... 15-19
The invention of the vacuum tube .......................................................................... 15-20
The first electromechanical computers ............................................................ 15-22
Logic diagrams and machines ...................................................................................... 15-26
The first electronic computers .......................................................................................... 15-31
The first “bug” and the worst “bug” ....................................................................... 15-36
The first transistors ................................................................................................................................ 15-38
The first integrated circuits .................................................................................................... 15-39
The first microprocessors ........................................................................................................... 15-40
The first personal computers .............................................................................................. 15-42
Further reading ....................................................................................................................................... 15-46
Quick quiz #15 ......................................................................................................................................... 15-48
A Sort of History of Computers 15-3
Taking a cruise through history
In this, the closing chapter, we take a cruise through history to discover
some of the key developments that have brought us to our present state of
computing, including the development of numbers, the introduction of
mechanical aids to calculation, the evolution of electronics, and the impact
of electronics on computing. No one person may be credited with the
invention of computers, but several names stand proud in the crowd.
The following offers some of the more notable developments and
individuals, with great regret for any omissions.
1
The term ”Cro-Magnon” comes from caves of the same name in Southern France, in which
the first skeletons of this race were discovered in 1868.
15-4 Bebop BYTES Back
Also of interest is a piece of bone dating from around 8,500 BC, which was
discovered in Africa, and which appears to have notches representing the
prime numbers 11, 13, 17, and 19. Prime numbers are those that are only
wholly divisible by the number one and themselves, so it is not surprising
that early man would have attributed them with a special significance. What
is surprising is that someone of that era had the mathematical sophistication
to recognize this quite advanced concept and took the trouble to write it
down – not the least that prime numbers had little relevance to everyday
problems of gathering food and staying alive.
3
For your edification, Reverse Polish Notation (RPN) is a style of data entry favored by some
calculators, in which the operators and operands are input in a backwards sort-of fashion
(we kid you not).
A Sort of History of Computers 15-7
The ancient Egyptians
Number systems with bases other than ten have sprouted up throughout
history. For example, the ancient Egyptians experimented with duo-decimal
(base-12) systems, because they counted finger-joints instead of fingers.
Each of our fingers has three joints (at least they do in my branch of the
family), so if you use your thumb to point to the joints of the other fingers
on the same hand, you can count one-two-three on the first finger, four-five-
six on the next, and so on up to twelve on your little finger.
If a similar technique is used with both hands, you can represent values
from one through twenty-four. This explains why the ancient Egyptians
divided their days into twenty-four periods, which is, in turn, why we have
twenty-four hours in a day. Strangely
enough, an Egyptian hour was only In addition to the Egyptians, many other
approximately equal to one of our people experimented with water clocks and
hours. This was because the Egyptians some interesting variations sprouted forth.
liked things to be nice and tidy, so For example, in some cases a float was
they decided to have twelve hours of connected to a wheel and, as the float
daylight and twelve hours of changed its level, the wheel turned to
indicate the hour on a dial.
nighttime. Unfortunately, as the
amount of daylight varies throughout Water clocks were also a standard means of
the year, they were obliged to adjust keeping time in Korea as early as the “Three
the lengths of their hours according to Kingdoms” period, and it was here that one
the seasons. of the first known automatic water clocks was
devised in 1434. This clock was called
One of the methods that the Egyptians Chagyongnu, which literally translates as
used to measure time was the water “self-striking water clock.” When the water
clock, or Clepsydra,(4) which reached a certain level, a trigger device
consisted of a container of water with released a metal ball, which rolled down a
a small hole in the bottom through chute into a metal drum to “gong the hour.”
which the water escaped. Units of
time were marked on the side of the
container, and the length of the units corresponding to day and night could
be adjusted by varying the distance between the markings or by modifying
the shape of the container; for example, by having the top wider than the
bottom.
In addition to their base-twelve system, the Egyptians also experimented
with a sort-of-base-ten system. In this system the numbers 1 through 9 were
4
The term “Clepsydra” is derived from the Greek klepto, meaning “thief,” and hydro,
meaning “water.” Thus, Clepsydra literally means “water thief.”
15-8 Bebop BYTES Back
The use of zero as an actual value, along with the concept of negative
numbers, first appeared in India around 600 AD. Although negative
numbers appear reasonably obvious to us today, they were not well-
understood until modern times. As recently as the eighteenth century, the
great Swiss mathematician Leonhard Euler (pronounced “Oiler” in America)
believed that negative numbers were greater than infinity, and it was
common practice to ignore any negative results returned by equations on
the assumption that they were meaningless!
6
In 1658, Pascal created a scandal when, under the pseudonym of Amos Dettonville, he
challenged other mathematicians to a contest and then awarded the prize to himself!
7
Complement techniques were introduced in Chapter 7.
8
Kepler, a German astronomer and natural philosopher, was the first person to realize (and
prove) that the planets travel around the sun in elliptical orbits.
15-14 Bebop BYTES Back
In the 1670s, a German Baron called Gottfried von Leibniz (Figure 15.10)
took mechanical calculation a step further. Leibniz, who entered university
at fifteen years of age and received his bachelor’s degree at seventeen, said:
“It is unworthy of excellent men to lose
hours like slaves in the labor of
calculation which could be safely
relegated to anyone else if machines
were used.” Leibniz developed
Pascal’s ideas and, in 1671,
introduced the Step Reckoner, a
device which, as well as performing
additions and subtractions, could
multiply, divide, and evaluate square
roots by series of stepped additions
(Figure 15.11). Leibniz also strongly
advocated the use of the binary
number system, which is
fundamental to the operation of
modern computers.
Figure 15.10: Gottfried von Leibniz
A Sort of History of Computers 15-15
the term computer became associated with machines that could perform
the computations on their own.(9) In 1822, Babbage proposed building a
machine called the Difference Engine to automatically calculate these tables.
The Difference Engine was only partially completed when Babbage
conceived the idea of another, more sophisticated machine called an
Analytical Engine. (Some texts refer to this machine as an Analytical Steam
Engine, because Babbage intended that it would be powered by steam.) The
Analytical Engine was intended to use loops of Jacquard’s punched cards to
control an automatic calculator, which could make decisions based on the
results of previous computations. This machine was also intended to employ
several features subsequently used in modern computers, including
sequential control, branching, and looping.
Working with Babbage was Augusta Ada
Lovelace (Figure 15.13),(10) the daughter of
the English poet Lord Byron. Ada, who
was a splendid mathematician and one of
the few people who fully understood
Babbage’s vision, created a program for
the Analytical Engine. Had the machine
ever actually worked, this program would
have been able to compute a mathematical
sequence known as Bernoulli numbers.
Based on this work, Ada is now credited as
being the first computer programmer and, in
1979, a modern programming language was
named ADA in her honor.
Babbage worked on his Analytical Engine from
around 1830 until he died, but sadly it was
never completed. It is often said that Babbage
was a hundred years ahead of his time, and Figure 15.13: Ada Lovelace
that the technology of the day was inadequate for the task. Refuting this is
the fact that, in 1834, two Swedish engineers called Georg and Edward
Scheutz built a small Difference Engine based on Babbage’s description.
In his book, Engines of the Mind, Joel Shurkin stated that:
9
In fact the term computer was used as a job description (rather than referring to the
machines themselves) well into the 1940s.
10
In their spare time, Babbage and Ada also attempted to create a system for predicting the
winners of horse races, but it is said that they lost a lot of money!
A Sort of History of Computers 15-17
“One of Babbage’s most serious flaws was his inability to stop
tinkering. No sooner would he send a drawing to the machine shop
than he would find a better way to perform the task and would order
work stopped until he had finished pursuing the new line. By and large
this flaw kept Babbage from ever finishing anything.”
Further supporting this theory is the fact that, in 1876, only five years after
Babbage’s death, an obscure inventor called George Barnard Grant
exhibited a full-sized difference engine of his own devising at the
Philadelphia Centennial Fair. Grant’s machine was 8 feet wide, 5 feet
tall, and contained over 15,000 moving parts.
Interestingly enough, more than one hundred and fifty years after its
conception, one of Babbage’s earlier Difference Engines was eventually
constructed from original drawings by a team at London’s Science Museum.
The final machine, which was constructed from cast iron, bronze and steel,
consisted of 4,000 components, weighed three tons, and was 10 feet wide
15-18 Bebop BYTES Back
and 61/2 feet tall. It performed its first sequence of calculations in the early
1990’s and returned results to 31 digits of accuracy, which is far more
accurate than the standard pocket calculator. However, each calculation
requires the user to turn a crank hundreds, sometimes thousands of times,
so anyone employing it for anything more than the most rudimentary
calculations is destined to become one of the fittest computer operators on
the face of the planet!
Boolean algebra
Around the time that Babbage was struggling with his Analytical Engine, one
of his contemporaries, a British mathematician called George Boole, was
busily inventing a new form of mathematics. Boole made significant
contributions in several areas of mathematics, but was immortalized for two
works in 1847 and 1854, in which he represented logical expressions in a
mathematical form now known as Boolean Algebra. Boole’s work was all the
more impressive because, with the exception of elementary school and a
short time in a commercial school, he was almost completely self-educated.
In conjunction with Boole, another British mathematician, Augustus
DeMorgan, formalized a set of logical operations now known as DeMorgan
transformations. As the Encyclopedia Britannica says: “A renascence of
logical studies came about almost entirely because of Boole and DeMorgan.”
In fact the rules we now attribute to DeMorgan were known in a more
primitive form by William of Ockham in the 14th Century. To celebrate
Ockham’s position in history, the OCCAM computer programming language
was named in his honor.(11)
Unfortunately, with the exception of students of philosophy and symbolic
logic, Boolean Algebra was destined to remain largely unknown and unused
for the better part of a century. It was not until 1938 that Claude E. Shannon
published an article based on his master’s thesis at MIT. (Shannon’s thesis has
been described as: “Possibly the most important Master’s thesis of the
twentieth century.”) In his paper, which was widely circulated, Shannon
showed how Boole’s concepts of TRUE and FALSE could be used to represent
the functions of switches in electronic circuits. Shannon is also credited with
the invention of the rocket-powered Frisbee, and is famous for riding down
the corridors at Bell Laboratories on a unicycle while simultaneously juggling
four balls.(12)
11
OCCAM is the native programming language for the British-developed INMOS transputer.
12
One of the authors can (almost) ride a unicycle, and both can juggle five china plates ......
but only for a very short period of time!
A Sort of History of Computers 15-19
The American census of 1890
It is often said that necessity is the mother of invention, and this was
certainly true in the case of the American census. Following the population
trends established by previous surveys, it was estimated that the census of
1890 would be required to handle data from more than 62 million
Americans. In addition to being prohibitively expensive, the existing system
of making tally marks in small squares on rolls of paper and then adding the
marks together by hand was extremely time consuming. In fact it was
determined that, if the system remained unchanged, there was no chance of
collating the data from the 1890 census into any useful form until well after
the 1900 census had taken place, by which time the 1890 data would be of
little value.
The solution to this problem was developed during
the 1880s by an American inventor called Herman
Hollerith (Figure 15.15). Hollerith’s idea was to
use Jacquard’s punched cards to represent the
census data, and to then read and collate this
data using an automatic machine. While he was
a lecturer at MIT, Hollerith developed a simple
prototype which employed cards he punched
using a tram conductor’s ticket punch, where
each card was intended to contain the data
associated with a particular individual. From this
prototype, he evolved a mechanism that could read
the presence or absence of holes in the cards by
using spring-mounted nails that passed through
the holes to make electrical connections.
Many references state that Hollerith originally
made his punched cards the same size as
dollar bills of that era, because he realized that
it would be convenient and economical to buy
Figure 15.15: Herman Hollerith
existing office furniture, such as desks and
cabinets, that already contained receptacles to accommodate stacks of bills.
However, some other sources are of the opinion that this is no more than a
popular fiction.
Hollerith’s final system included an automatic electrical tabulating machine
with a large number of clock-like counters that accumulated the results. By
means of switches, operators could instruct the machine to examine each
15-20 Bebop BYTES Back
13
If you ever happen to be in Dearborn, Michigan, you should take the time to visit the
Henry Ford Museum, which happens to contain the world’s largest collection of light bulbs.
15-22 Bebop BYTES Back
Another device that was to prove useful in early computers was the relay,
which is formed from a metal bar surrounded by a coil. When current is
passed through the coil, the resulting electromagnetic field causes the metal
bar to move (a spring is used to return the bar to its original position when
the current is removed). The bar can be used to activate switches, which
can be used to control other devices; for example, to apply current to the
coils of other relays.
As a nugget of trivia, in 1921, the Czech author Karel Capek produced his
best known work, the play R.U.R. (Rossum’s Universal Robots), which
featured machines created to simulate human beings. Some references state
that term robot was derived from the Czech word robota, meaning “work,”
while others propose that robota actually means “forced workers” or
“slaves.” This latter view would certainly fit the point that Capek was trying
to make, because his robots eventually rebelled against their creators, ran
amok, and tried to wipe out the human race. However, as is usually the
case with words, the truth of the matter is a little more convoluted. In the
days when Czechoslovakia was a feudal society, “robota” referred to the
two or three days of the week that peasants were obliged to leave their own
fields to work without remuneration on the lands of noblemen. For a long
time after the feudal system had passed away, robota continued to be used
to describe work that one wasn’t exactly doing voluntarily or for fun, while
today’s younger Czechs and Slovaks tend to use robota to refer to work
that’s boring or uninteresting.
15
The use of Karnaugh Maps is discussed in this book’s companion volume: Bebop to the
Boolean Boogie (An Unconventional Guide to Electronics).
A Sort of History of Computers 15-27
Possibly the first person in the history of formal logic to use a mechanical
device to generate (so-called) logical proofs was the Spanish theologian
Ramon Lull. In 1274, Lull climbed Mount Randa in Majorca in search of
spiritual sustenance. After fasting and contemplating his navel for several
days, Lull experienced what he believed to be a divine revelation, and he
promptly rushed back down the mountain to pen his famous Ars Magna.
This magnum opus described a number of eccentric logical techniques, but
the one of which Lull was most proud (and which received the most
attention) was based on concentric disks of card, wood, or metal mounted
on a central axis.
Lull’s idea was that each disk should
y
contain a number of different words or
love
I
th
symbols, which could be combined in
different ways by rotating the disks
gs
ts
(Figure 15.19). In the case of our
ca
kiss do
somewhat jocular example, we can m hate
fro
ic
achieve 4 x 4 x 4 = 64 different e
gs
sentences along the lines of “I love
eat
mice,” “You hate cats,” and “They eat yo w
u e
frogs.”
Of course, Lull had a more serious
purpose in mind, which was to prove
the truth of everything contained within Figure 15.19: Ramon Lull’s disks
the Bible. For example, he used his
disks to show that “God’s mercy is infinite,” “God’s mercy is mysterious,”
“God’s mercy is just,” and so forth.
Lull’s devices were far more complex than our simple example might
suggest, with several containing as many as sixteen different words or
symbols on each disk. His masterpiece was the figura universalis, which
consisted of fourteen concentric circles – the mind boggles at the range of
combinations that could be generated by this device. Strange as it may seem
to us, Lull’s followers (called Lullists) flourished in the late middle ages and
the renaissance, and Lullism spread far and wide across Europe.
Why is all of this of interest to us? Well by some strange quirk of fate, Lull’s
work fired the imagination of several characters with whom we are already
familiar, such as Gottfried von Leibniz who invented the Step Reckoner.
Although Leibniz had little regard for Lull’s work in general, he believed
there was a chance it could be extended to apply to formal logic. In a rare
flight of fancy, Leibniz conjectured that it might be possible to create a
15-28 Bebop BYTES Back
universal algebra that could represent just about everything under the sun,
including (but not limited to) moral and metaphysical truths. In 1666, at the
age of 19, Leibniz wrote his Dissertio de Arte Combinatoria, from which
comes a famous quote describing the way in which he believed the world
could be in the future: “If controversies were to arise,” said Leibniz, “there
would be no more need of disputation between two philosophers than
between two accountants. For it would suffice to take their pencils in their
hands, and say to each other: Let us calculate.”
Of course Lull also has his detractors (which is a kind way of saying that
many people considered him to be a raving lunatic). In 1726, the Anglo-
Irish satirist Jonathan Swift (Figure 15.20) wrote Gulliver’s Travels,(16) which
was originally intended as an attack on the hypocrisy of the establishment
(including the government, the courts, and the
clergy – Swift didn’t like to restrict himself
unduly), but which was so pleasingly
written that it immediately became a
children’s favorite.(17) In part III, chapter 5
of the tale, a professor of Laputa shows
Gulliver a machine that generates random
sequences of words. This device was
based on a 20 foot square frame
supporting wires threaded through wooden
cubes, where each face of every cube had
a piece of paper bearing a word pasted
onto it. Students randomly changed the
words using forty handles mounted around
Figure 15.20: Johnathan Swift the frame. The students then examined the
cubes, and if three or four adjacent words
formed part of a sentence that made any sense, they were immediately
written down by scribes. The professor told Gulliver that by means of this
technique: “The most ignorant person at a reasonable charge, and with
little bodily labor, may write books in philosophy, poetry, law,
mathematics, and theology, without the least assistance from genius or
study.” The point is that Swift is believed to have been mocking Lull’s art
when he penned this part of his story. (Having said this, computer programs
have been used to create random poetry and music ...... which makes you
wonder what Swift would have written about us.)
16
On the off chance you were wondering, Swift penned his great work nine years before the
billiard que was invented. Prior to this, players used to strike the balls with a small mace.
17
It’s a funny old world when you come to think about it.
A Sort of History of Computers 15-29
In fact Swift continues to affect us in strange and wondrous ways to this day.
When a computer uses multiple bytes to represent a number, there are two
main techniques for storing those bytes in memory: either the most-
significant byte is stored in the location with the lowest address (in which
case we might say it’s stored “big-end-first”), or the least-significant byte is
stored in the lowest address (in which case we might say it’s stored “little-
end-first”). Not surprisingly, some computer designers favor one style while
others take the opposite tack. This didn’t really matter until people became
interested in creating heterogeneous computing environments, in which
multiple diverse machines were connected together, at which point many
acrimonious arguments ensued. In 1980, a famous paper written by Danny
Cohen entitled “On Holy Wars and a Plea for Peace” used the terms big-
endian and little-endian to refer to the two techniques for storing data.
These terms, which are still in use today, were derived from that part of
Gulliver’s tale whereby two countries go to war over which end of a hard-
boiled egg should be eaten first – the little end or the big end!
Leaping from one subject to another with the agility of a mountain goat, we
might also note that Lewis Carroll (Figure 15.21)
enjoyed posing logical conundrums in many of his
books, such as Alice’s Adventures in
Wonderland (1865), Through the Looking-
Glass (1872), and The Hunting of the Snark
(1876). For example, consider this scene
from the Mad Hatter’s tea party in
Chapter 7 of Alice’s Adventures in
Wonderland:(18)
“Take some more tea,” the March Hare
said to Alice, very earnestly.
“I’ve had nothing yet,” Alice replied in
an offended tone: “so I can’t take
more.”
“You mean you can’t take less,” said the
hatter: “it’s very easy to take more than
nothing.”
Figure 15.21: Lewis Carroll
18
The phrase “As mad as a Hatter” comes from the fact that, in ye olden tymes, the
manufacturers of men’s top hats used mercury compounds as part of the process. Over time
the mercury accumulated in their bodies causing severe impairment to their mental functions.
15-30 Bebop BYTES Back
20
Following the invention of his logic machine, Marquand abandoned logical pursuits to
become a professor of art and archeology at Princeton University.
21
An example of one such program is a logic synthesizer, which can be used to translate
high-level descriptions of circuits into optimized gate-level representations.
22
It is also said that those who fail to learn the lessons of history are doomed to repeat them
(this is particularly true in the case of history courses at high school).
15-32 Bebop BYTES Back
In the process of creating the device, Atanasoff and Berry evolved a number
of ingenious and unique features. For example, one of the biggest problems
for computer designers of the time was to be able to store numbers for use in
the machine’s calculations. Atanasoff’s design utilized capacitors to store
electrical charge that could represent numbers in the form of logic 0s and
logic 1s. The capacitors were mounted in rotating bakelite cylinders, which
had metal bands on their outer surface. These cylinders, each approximately
12 inches tall and 8 inches in diameter, could store thirty binary numbers,
which could be read off the metal bands as the cylinders rotated.
Input data was presented to the machine in the form of punched cards,
while intermediate results could be stored on other cards. Once again,
Atanasoff’s solution to storing intermediate results was quite interesting – he
used sparks to burn small spots onto the cards. The presence or absence of
these spots could be automatically determined by the machine later,
because the electrical resistance of a carbonized spot varied from that of
the blank card.
Some references report that Atanasoff and Berry had a fully working model
of their machine by 1942. However, while some observers agreed that the
machine was completed and did work, others reported that it was almost
completed and would have worked, while still others stated that it was just
a collection of parts that never worked. So unless more definitive evidence
comes to light, it’s a case of: “You pays your money
and you takes your choice.”
Many of the people who designed the early
computers were both geniuses and eccentrics
of the first order, and the English
mathematician Alan Turing was first among
equals (Figure 15.22). In 1937, while a
graduate student, Turing wrote his ground-
breaking paper “On Computable Numbers with
an Application to the Entscheidungsproblem.”
One of the premises of Turing’s paper was that
some classes of mathematical problems do not
lend themselves to algorithmic representations,
and are therefore not amenable to solution by
automatic computers. Since Turing did not
have access to a real computer (not
unreasonably as they didn’t exist at the time),
he invented his own as an abstract “paper
Figure 15.22: Alan Turing
A Sort of History of Computers 15-33
exercise.” This theoretical model, which became known as a Turing
Machine, was both simple and elegant, and subsequently inspired many
“thought experiments.”
During World War II Turing worked as a cryptographer, decoding codes and
ciphers at one of the British government’s top-secret establishments located
at Bletchley Park. During this time, Turing was a key player in the breaking
of the German’s now-famous ENIGMA code. However, in addition to
ENIGMA, the Germans had another cipher that was employed for their
ultra-top-secret communications. This cipher, which was vastly more
complicated than ENIGMA, was generated by a machine called a
Geheimfernschreiber (secret telegraph), which the allies referred to
as the “Fish.”
In January 1943, along with a number of colleagues, Turing began to
construct an electronic machine to decode the Geheimfernschreiber cipher.
This machine, which they dubbed COLOSSUS, comprised 1,800 vacuum
tubes and was completed and working by December of the same year!
By any standards COLOSSUS was one of the world’s earliest working
programmable electronic digital computers. But it was a special-purpose
machine that was really only suited to a narrow range of tasks (for example,
it was not capable of performing decimal multiplications). Having said this,
although COLOSSUS was built as a special-purpose computer, it did prove
flexible enough to be programmed to execute a variety of different routines.
By the mid-1940s, the majority of computers were being built out of
vacuum tubes rather than switches and relays. Although vacuum tubes were
fragile, expensive, and used a lot of power, they were much faster than
relays (and much quieter). If we ignore Atanasoff’s machine and
COLOSSUS, then the first true general-purpose electronic computer was the
electronic numerical integrator and computer (ENIAC), which was
constructed at the University of Pennsylvania between 1943 and 1946.
ENIAC, which was the brainchild of John William Mauchly and J. Presper
Eckert Jr., was a monster – it was 10 feet tall, occupied 1,000 square feet of
floor-space, weighed in at approximately 30 tons, and used more than
70,000 resistors, 10,000 capacitors, 6,000 switches, and 18,000 vacuum
tubes. The final machine required 150 kilowatts of power, which was
enough to light a small town.
One of the greatest problems with computers built from vacuum tubes was
reliability; 90% of ENIAC’s down-time was attributed to locating and
replacing burnt-out tubes. Records from 1952 show that approximately
15-34 Bebop BYTES Back
19,000 vacuum tubes had to be replaced in that year alone, which averages
out to about 50 tubes a day!
During the course of developing ENIAC, Mauchly and Eckert recognized a
variety of improvements and new techniques, which they determined to use
in any subsequent machines. For example, one of the main problems with
ENIAC was that it was hard-wired; that is, it did not have any internal
memory as such, but needed to be physically programmed by means of
switches and dials. Around the summer of 1943, Mauchly and Eckert
discussed the concept of creating a stored-program computer, in which an
internal read-write memory would be used to store both instructions and
data. This technique would allow the program to branch to alternate
instruction sequences based on the results of previous calculations, as
opposed to blindly following a pre-determined sequence of instructions.
Eckert’s idea was to use mercury delay lines (which he already knew a great
deal about) for the memory. These delay lines were constructed using a thin
tube of mercury sealed with quartz crystals at each end. Applying an
electric current to a quartz crystal causes it to vibrate. Similarly, vibrating a
quartz crystal causes it to generate an electric current. The principle behind
the mercury delay line was to briefly apply a current to the crystal at one
end of the tube, which generated a pulse that propagated through the
mercury at a known speed. When the pulse reached the far end of the delay
line, it caused the crystal at that end to generate a corresponding current.
By amplifying the output from the second crystal and feeding it back to the
first crystal, a continuous loop could be established. Moreover, a number of
individual pulses could be maintained in a single delay line, similar in
concept to a column of people marching down a corridor in single file.
In fact 1000 bits could be stored in a delay line 5 feet long. Around the
beginning of 1944, Eckert wrote an internal memo on the subject and,
in August 1944, Mauchly and Eckert proposed the building of another
machine called the electronic discrete variable automatic computer
(EDVAC). As we will see, the dates associated with these activities are of
some significance.
In June 1944, the Hungarian-American mathematician Johann (John) von
Neumann (Figure 15.23) first became aware of ENIAC. Von Neumann, who
was a consultant on the Manhattan Project, immediately recognized the role
that could be played by a computer like ENIAC in solving the vast arrays of
complex equations involved in designing atomic weapons. A brilliant
mathematician, von Neumann crossed mathematics with subjects such as
philosophy in ways that had never previously been conceived; for example,
A Sort of History of Computers 15-35
he was a pioneer of Game Theory, which continues to find numerous and
diverse applications to this day.
Von Neumann was tremendously excited by ENIAC, and quickly became
a consultant to both the ENIAC and EDVAC projects. In June 1945, he
published a paper entitled “First Draft of a report to the EDVAC,” in which
he presented all of the basic elements of a stored-program computer:
a) A memory containing both data and instructions. Also to allow both
data and instruction memory locations to be read from, and written
to, in any desired order.
b) A calculating unit capable of performing both arithmetic and logical
operations on the data.
c) A control unit, which could interpret an instruction retrieved from the
memory and select alternative courses of action based on the results
of previous operations.
The key point made by the paper was that the
computer could modify its own programs, in much
the same way as was originally suggested by
Babbage. The computer structure resulting from
the criteria presented in this paper is popularly
known as a von Neumann Machine, and
virtually all digital computers from that time
forward have been based on this architecture.
The “First Draft” was a brilliant summation of the
concepts involved in stored-program
computing; indeed, many believe it to be one
of the most important documents in the
history of computing. It is said that the paper
was written in a way that possibly only von Figure 15.23: John von Neumann
Neumann could have achieved at that time.
However, although there is no doubt that von Neumann made major
contributions to the EDVAC design, the result of the “First Draft” was that he
received almost all of the credit for the concept of stored-program
computing, while Mauchly and Eckert received almost none. But Mauchly
and Eckert discussed stored-program computers a year before von Neumann
arrived on the scene, and Eckert wrote a memo on the subject six months
before von Neumann had even heard about ENIAC. It has to be said that
there is no evidence that von Neumann intended to take all of the credit
15-36 Bebop BYTES Back
(not the least that his paper was titled “First Draft ....”), but it also cannot be
denied that he didn’t go out of his way to correct matters later.
Unfortunately, although the conceptual design for EDVAC was completed by
1946, several key members left the project to pursue their own careers, and
the machine did not become fully operational until 1952. When it was
finally completed, EDVAC contained approximately 4,000 vacuum tubes and
10,000 crystal diodes. A 1956 report shows that EDVAC’s average error-free
up-time was approximately 8 hours.
In light of its late completion, some would dispute EDVAC’s claim-to-fame
as the first stored-program computer. A small experimental machine based
on the EDVAC concept consisting of 32 words of memory and a
5-instruction instruction set was operating at Manchester University,
England, by June 1948. Another machine called EDSAC (Electronic Delay
Storage Automatic Calculator) performed its first calculation at Cambridge
University, England, in May 1949. EDSAC contained 3,000 vacuum tubes
and used mercury delay lines for memory. Programs were input using paper
tape and output results were passed to a teleprinter. Additionally, EDSAC is
credited as using one of the first assemblers called Initial Orders, which
allowed it to be programmed symbolically instead of using machine code.
Last but not least, the first commercially available computer, UNIVAC I
(Universal Automatic Computer), was also based on the EDVAC design.
Work started on UNIVAC I in 1948, and the first unit was delivered in 1951,
which therefore predates EDVAC’s becoming fully operational.
Apropos of nothing at all, a jazz style known as Bebop became highly
popular in the decade following World War II. Charlie Parker, Dizzy
Gillespie and Thelonius Monk were especially associated with this form of
music, which is known for its fast tempos and agitated rhythms. We may
only speculate if it was but a coincidence that many of the most significant
ideas and discoveries in the history of computing occurred alongside the
flourishing Bebop.
24
I’m glad I wasn’t that programmer!
A Sort of History of Computers 15-39
By the late 1950s, bipolar transistors were being manufactured out of silicon
rather than germanium (although germanium had certain electrical
advantages, silicon was cheaper and easier to work with). Bipolar junction
transistors are formed from the junction of three pieces of doped silicon
called the collector, base, and emitter. The original bipolar transistors were
manufactured using the mesa process, in which a doped piece of silicon
called the mesa (or base) was mounted on top of a larger piece of silicon
forming the collector, while the emitter was created from a smaller piece of
silicon embedded in the base.
In 1959, the Swiss physicist Jean Hoerni invented the planar process, in
which optical lithographic techniques were used to diffuse the base into the
collector and then diffuse the emitter into the base. One of Hoerni’s
colleagues, Robert Noyce, invented a technique for growing an insulating
layer of silicon dioxide over the transistor, leaving small areas over the base
and emitter exposed and diffusing thin layers of aluminum into these areas
to create wires. The processes developed by Hoerni and Noyce led directly
to modern integrated circuits.
In 1962, Steven Hofstein and Fredric Heiman at the RCA research laboratory
in Princeton, New Jersey, invented a new family of devices called metal-
oxide semiconductor field-effect transistors (MOS FETs for short). Although
these transistors were somewhat slower than bipolar transistors, they were
cheaper, smaller and used less power. Also of interest was the fact that
modified metal-oxide semiconductor structures could be made to act as
capacitors or resistors.
cassette recorder for storing programs. This unit, which was only available
in fully-assembled form, was initially priced at $245, but this soon fell to an
astoundingly $170.
The introduction of new microcomputers proceeded apace. Sometime after
the KIM-1 became available, the Sphere Corporation introduced its Sphere 1
kit, which comprised a 6800 microprocessor, 4 K-bytes of RAM, a
QWERTY keyboard, and a video interface (but no monitor) for $650.
In March 1976, two guys called Steve Wozniak and Steve Jobs (who had
been fired with enthusiasm by the Altair 8800) finished work on a home-
grown 6502-based computer which they called the
In 1975, an IBM mainframe Apple 1 (a few weeks later they formed the Apple
computer that could perform Computer Company on April Fools day). Although
10,000,000 instructions per it was not tremendously sophisticated, the Apple 1
second(26) cost around attracted sufficient interest for them to create the
$10,000,000. In 1995 (only Apple II, which many believe to be the first
twenty years later), a
personal computer that was both affordable and
computer video game
capable of performing usable. The Apple II, which became available in
500,000,000 million April 1977 for $1,300, comprised 16 K-bytes of
instructions per second was ROM, 4 K-bytes of RAM, a keyboard, and a color
available for approximately display. Apple was one of the great early success
$500! stories – in 1977 they had an income of $700,000
(which was quite a lot of money in those days),
and just one year later this had soared tenfold to $7 million! (which was a
great deal of money in those days).
Also in April 1977, Commodore Business Machines presented their 6502-
based Commodore PET, which contained 14 K-bytes of ROM, 4 K-bytes of
RAM, a keyboard, a display, and a cassette tape drive for only $600.
Similarly, in August of that year, Tandy/Radio Shack announced their
Z80-based TRS-80, comprising 4 K-bytes of ROM, 4 K-bytes of RAM, a
keyboard, and a cassette tape drive for $600.
One point that may seem strange today is that there were practically no
programs available for these early machines (apart from the programs
written by the users themselves). In fact it wasn’t until late in 1978 that
commercial software began to appear. Possibly the most significant tool of
that time was the VisiCalc spreadsheet program, which was written for the
26
Due to the fact that the amount a computer can do with an “average” instruction depends
strongly on its architecture, many engineers believe that MIPS, which is officially the
abbreviation of millions of instructions per second, should more properly stand for
meaningless interpretation of processor speed.
A Sort of History of Computers 15-45
Apple II by a student at the Harvard Business School, and which appeared
in 1979. It is difficult to overstate the impact of this program, but it is
estimated that over a quarter of the Apple machines sold in 1979 were
purchased by businesses solely for the purpose of running VisiCalc. In
addition to making Apple very happy, the success of VisiCalc spurred the
development of other applications such as wordprocessors.
When home computers first began to appear, existing manufacturers of large
computers tended to regard them with disdain (“It’s just a fad ..... it will
never catch on”). However, it wasn’t too long before the sound of money
changing hands began to awaken their interest. In 1981, IBM(27) launched
their first PC for $1,365, which, if nothing else, sent a very powerful signal
to the world that personal computers were here to stay.
Unfortunately, we’ve only been able to touch on a few systems here, but
hopefully we’ve managed to illustrate both the public’s interest in, and the
incredible pace of development of, the personal computer. The advent of
the general-purpose microprocessor heralded a new era in computing –
microcomputer systems small enough to fit on a desk could be endowed
with more processing power than monsters weighing tens of tons only a
decade before. The effects of these developments are still unfolding, but it is
not excessive to say that digital computing and the personal computer have
changed the world more significantly than almost any other human
invention, and many observers believe that we’ve only just begun our
journey into the unknown!
27
Many people refer to IBM affectionately as “Big Blue,” but for some reason IBM never
bothered to trademark this sobriquet. In 1995, another enterprising company did just that,
which probably caused a certain amount of consternation in the corridors of power!
15-46 Bebop BYTES Back
Further reading
There are a great number of books available on the history of computers and
computing, so many in fact that it can be difficult to know where to begin.
Fortunately, Kip Crosby, the president of the Computer History Association
of California (CHAC), was kind enough to provide his personal “top ten”
titles and commentary for your delectation and delight.
Survey Histories
Engines of the Mind: A History of the Computer
By Joel Shurkin,
W. W. Norton, 1984
A lively, anecdotal history of computers and computing, essentially
from the sixteenth century to the 1960s. Quirky and partisan in spots,
but magnificently inclusive and a great read.
The Dream Machine: Exploring the Computer Age
By Jon Palfreman and Doron Swade
BBC Press (UK), 1991
A computer history coffee-table book! Based on the BBC-TV series, with
useful and engaging emphasis on computer history in Europe. Odd
typography, good quotes, and great pictures.
“Big-Iron” Histories
Father, Son, and Co.
By T.J. Watson Jr. and Peter Petre
Bantam Books, New York, 1991
If the history of IBM appeals to you, this is the book to get it from,
because the man who knew it best was also a really good writer.
Candid, compelling, unsparing, and important.
Alan Turing: The Enigma
By Andrew Hodges
Simon and Schuster, 1983
Not only computer history, of course; yet this one rockets into the “Ten
Best” on its technical content alone. Add in meticulous biography and
penetrating character study, and the result is a permanent classic.
A Sort of History of Computers 15-47
The Computer from Pascal to von Neumann
By Herman H. Goldstine
Princeton University Press, 1972
Primarily the story of ENIAC and its immediate successors, and probably
the best book on early computer history in the United States. (Unless
you understand calculus you may have to skip around a bit).
IBM’s Early Computers
By C.J. Bashe, L.R. Johnson, J.H. Palmer, and E.W. Pugh
MIT Press, 1986 (Vol. 3 in the History of Computing series)
Make no mistake: this is solid, unadorned technical history, and a real
slog. Yet no other book can match this description of the agony and
glory of computer design at a time when even its most basic principles
were still topics of conjecture.
The Computer Establishment
By Katharine Davis Fishman
McGraw-Hill, 1982
A fine-grained portrait of a special time – the few years when the IBM
hegemony slipped and the so-called “Seven Dwarfs” and the plug-
compatible manufacturers rushed in to make fortunes. Packed with
illuminating detail and scathing war stories.
Micro Histories
Hackers
By Steven Levy
Doubleday, 1984
The rise and maturity of the Hacker Ethic, from the Tech Model Railroad
Club to the Free Software Foundation. Sometimes exalted, sometimes
dismissed, always argued over, this remains a seminal work in the
social history of computing.
Fire in the Valley: The Making of the Personal Computer
By Paul Freiberger and Michael Swaine
Osborne/McGraw-Hill, 1984
Yes, the desktop computer was invented by fanatics in garages. Written
not long after the fact, this is the most vivid rendering of a heart-
warming story, even if some of the details will be disputed forever.
15-48 Bebop BYTES Back
Accidental Empires
By Robert X. Cringely
Addison-Wesley, 1992
....and the titans moved from garages to tilt-ups, got rich, and still wore
jeans. How Silicon Valley startled the world, as written by an insider
with plenty of opinions. A good read by itself, and fascinating when
read back-to-back with “Fire In The Valley.”
As a point of interest, Kip Crosby is also the publisher of the Analytical
Engine, which is the official magazine of CHAC. The Analytical Engine is
replete with nuggets of historical information and trivia, and contains some
outstandingly interesting interviews with those people who designed or used
the great machines of the past. Should you wish to know more about CHAC
or the Analytical Engine, you can email Kip at [email protected]
Contents of Appendix A
Mandatory system requirements ......................................................................................... A-3
Installation guide ......................................................................................................................................... A-3
Installing your Beboputer A-3
Mandatory system requirements
In order to use the Beboputer Computer Simulator V1.00 for Windows 95™,
you will need:
• Microsoft® Windows 95™
• 8 MB RAM (16 MB recommended).
• A Windows 95™ compatible mouse.
• A VGA or higher graphics adapter capable of displaying resolutions of
a 800 x 600 or better.
• 15 MB of available space on your hard drive.
Installation guide
The installation program (setup.exe) decompresses and copies the Beboputer
and associated files to a directory on your hard drive (some program and
multimedia files will remain on the CD-ROM).
When you are ready to install your Beboputer, ensure Windows 95™ is up
and running, insert the Beboputer CD-ROM into your CD-ROM drive, then
complete the following steps (note that only the main dialog windows are
shown below; in the case of any other dialogs, simply click the Next button
to proceed):
A-4 Bebop BYTES Back
4) Click the Finish button to move on, then click the Next buttons of any
subsequent forms to complete the installation.
Note that before running your Beboputer, we strongly recommend that your
screen resolution is set to 800 x 600 or higher.
Important: If you know that you don’t have Microsoft’s “Video for
Windows” loaded on your system (or if you do have it loaded, but you only
receive sound without any video when you attempt to play our multimedia
lab introductions), then:
a) Recommence the installation process as described above, but ....
b) Once you’ve invoked the Run Installation Program dialog as
discussed in point 3) above, use the Browse button on the form to
locate the file called either “setup.exe” or “install.exe” in the d:\vfm
directory (substitute d: for the drive letter of your CD-ROM as
necessary).
c) Keep clicking the Next or Finish buttons to complete the installation
as required.
A-6 Bebop BYTES Back
Appendix
B
File Formats and Stuff
B-2 Bebop BYTES Back
Contents of Appendix B
Directory structure .................................................................................................................................... B-3
Common file extensions ................................................................................................................... B-4
Assembler source and list files ................................................................................................ B-5
Configuration files ..................................................................................................................................... B-5
Character ROM and JEDEC files ........................................................................................ B-8
Paper tape files ........................................................................................................................................ B-10
RAM files .............................................................................................................................................................. B-12
ROM files .............................................................................................................................................................. B-13
System ROM files ..................................................................................................................................... B-14
Files delivered with the Beboputer in the \data directory ........... B-15
File Formats and Stuff B-3
Directory structure
As described in Appendix A, during the process of installing your
Beboputer, the installation program will create the following directory
structure on your hard disk (Figure B.1).
Beboputer
Beboputer, you may be overtaken by the desire to create your own utility
programs (say a disassembler), in which case you will need to know the
internal formats of certain files as discussed below.
Assembler
The formats of the source and list files are fully discussed in Chapter 12,
while the formats of the RAM and ROM files are discussed later in this
appendix.
Configuration files
Excepting project-specific data, any configuration files are located in the
\config directory on your hard disk. Two such files of particular interest are
sound.cfg and video.cfg, which are used by the Beboputer’s sound and
video cards, respectively.
The sound.cfg file: The sound configuration file is used to associate sound
(*.wav) files with any data values that the Beboputer outputs to its virtual
sound card (Figure B.3).
*.wav files
sound.cfg
Comment lines start with a single quote and you may add as many
comments as you wish, but only at the beginning of the file. The field
[Wave_files] is a keyword that must be present in the file, and the data value
to sound file associations follow this keyword. The files num_0.wav,
num_1.wav, num_2.wav, and so forth are the ones that we (the authors)
created for use in lab 4 (Chapter 9), while the others are just extra sounds
we threw in for you to play with. Thus, if your program writes a value of
$FF (255 in decimal) to the sound card, it will play the whistle.wav file
located in the \sound directory. Note that it isn’t necessary to reference
every possible data value in the sound.cfg file ...... only the ones for which
you wish to associate *.wav files.
Feel free to add your own *.wav files to the \sound directory and to modify
the sound.cfg file accordingly. There are a lot of free data bytes left over for
you to use, so we strongly recommend that you don’t change any of the
associations that we’ve already made, because you could mess up existing
and future laboratories.
File Formats and Stuff B-7
The video.cfg file: The video configuration file is used to associate video
(*.avi) files with any data values that the Beboputer outputs to its virtual
video card (Figure B.4).
*.avi files
video.cfg
Once again, comment lines start with a single quote and you may add as
many comments as you wish, but only at the beginning of the file. The field
[Video_files] is a keyword that must be present in the file, and the data value
to video file associations follow this keyword. As for the sound.cfg file, it
isn’t necessary to reference every possible data value...... only the ones for
which you wish to associate *.avi files.
In the example above we’ve associated a video file called to2160.avi with
multiple data values, just to illustrate that fact that this can be done
B-8 Bebop BYTES Back
(to2160.avi is just a video we threw in for you to play with). Thus, if your
program writes a value of $80 (128 in decimal) to the video card, it will
display the to2160.avi video located in the \video directory.
As before, feel free to add your own *.avi files to the \video directory and to
modify the video.cfg accordingly. In this case it doesn’t really matter if you
change any of the associations we’ve already made, because we haven’t
used the video card in any of our labs.
xxx.jed xxx.cro
Figure B.5: Character ROM (*.cro) files
Once you’ve created a *.cro file, you have to use the Setup → Swap EPROM
pull-down to “plug” your new Character ROM device into the Beboputer.
In reality, the Beboputer’s *.cro files actually contain bit-maps that we
BitBlit to the screen, so their internal format is that of a standard bit-map,
which is beyond the scope of this book. (Note that the use of the character
editor, the EPROM Programmer, and the Swap EPROM pull-down are fully
discussed in Chapter 13).
With exception of *.cro files, the format of our *.jed files is the most
complicated of all of the Beboputer’s data files, which isn’t saying much,
because it’s really quite simple as you will see from the following:
File Formats and Stuff B-9
BEBOPUTER JEDEC FILE Version 1.0.0.0
Character Code 0
511
273
273
273
313
511
313
273
273
273
511
0
0
0
0
Character Code 1
511
273
:
:
etc
:
:
END OF DATA
The first line identifies this as being one of our pseudo JEDEC files(1) and
indicates the version of the Beboputer that generated it, while the last line
(“END OF DATA”) is self-explanatory. The body of the file consists of 256
blocks of data describing the patterns of 0s and 1s associated with each
character. Each of these blocks comprises a line of text indicating the
character’s code (in hexadecimal), followed by fifteen decimal numbers,
where each number relates to a row in that character. For example, on the
following page, consider the contents associated with Character Code 41
(that is, ASCII code $41, which equates to an uppercase letter ‘A’) in the
default defchar.jed file we provided with your Beboputer (the defchar.jed file
is located in the \data directory, along with it’s corresponding Character
ROM file defchar.cro) (Figure B.6).
Each column in the character has a weight associated with it, where these
weights are powers of two, commencing with 1 for the left-most column and
ending with 512 for the right-most column. The decimal value for each row
is determined by adding all the weights associated with any black squares
1
Remember that these files are in a format of our devising, and that this is not the real JEDEC
format.
B-10 Bebop BYTES Back
on that row. A little thought reveals that these weights are the exact
opposite to their standard binary counterparts (hence the appellation
“Reverse binary equivalent” in Figure B.6). This format does requires a little
“lateral thinking,” but it’s easy to follow once you’ve got the hang of it.(2)
1 2 4 8 512
...........
Character code 41
0000100000 16
0000100000 16
0001110000 56
0001110000 56
0011011000 108
0011011000 108
0110001100 198
0111111100 254
1100000110 387
1100000110 387
0000000000 0
0000000000 0
0000000000 0
0000000000 0
0000000000 0
2
Max would like to make it absolutely clear that Alvin bears full responsibility for the
Beboputer’s data file formats in general (and this one in particular). Alvin has assured Max
that this “Reverse Binary Notation” was painstakingly conceived and conveys innumerable
benefits of an imponderable nature, and who amongst us would doubt the word of an English
gentleman?
File Formats and Stuff B-11
Similarly, paper tape files are used by the paper tape reader, which is
activated when the Beboputer is instructed to jump to address $0050 in the
System ROM. The paper tape reader copies the contents of a named paper
tape back into the Beboputer’s RAM (Figure B.7b). The format of a paper
tape file is as follows:
BEBOPUTER PAPER TAPE FILE Version 1.0.0.0
16384
16895
Paper Tape File C:\work\BOOK\DATA\Test1.pta
Paper Tape File
START OF DATA
144
3
153
240
:
:
etc
:
:
END OF DATA
The first line identifies this as being a paper tape file and indicates the
version of the Beboputer that generated it. The second and third lines of the
file reflect the start and end RAM addresses (in decimal) of the data that was
written to the file, while the fourth and fifth lines are comments that will be
ignored by the Beboputer. Note that the start and end address values are
used by the paper tape reader to determine where to load the data back into
the Beboputer’s RAM. The data itself consists of a series of decimal values
(one for each address) prefixed by the “START OF DATA” line and
terminated by the “END OF DATA” line. The data values are typically in the
range 0 through 255 ($00 through $FF in hexadecimal). However, data
values may also be set to 256 ($100 in hexadecimal), which the Beboputer
will interpret and display as unknown ‘X’ values.
You may think that the format of paper tape files is somewhat bulky and
inefficient (similarly for the other Beboputer data files discussed below). You
are correct; we could have employed much more efficient techniques.
However, our formats have the benefit of being simple, easy to understand,
and easy to replicate with any tools you might wish to create for yourself.
B-12 Bebop BYTES Back
RAM files
Any *.ram files are located in the \data directory. These files are generated
by the Beboputer’s assembler when the .ORG statement in the assembly
source program is specified as being greater than or equal to $4000, which
is the start of the Beboputer’s RAM (Figure B.8).
Assembler
The format of a *.ram file is almost identical to that of a paper tape file as
discussed in the previous section. The first line identifies this as being a
RAM file and indicates the version of the Beboputer that generated it. The
second and third lines of the file reflect the start and end RAM addresses (in
decimal) of the data that was written to the file, while the fourth and fifth
lines are comments that will be ignored by the Beboputer. Note that the
File Formats and Stuff B-13
start and end address values are used by the Memory → Load RAM
command to determine where to load the data back into the Beboputer’s
RAM (Figure B.9).
Note that the Memory → Load RAM command allows you to load multiple
*.ram files at the same time if you so desire. The data in each *.ram file
consists of a series of decimal values (one for each address) prefixed by the
“START OF DATA” line and terminated by “END OF DATA” line. The data
values are typically in the range 0 through 255 ($00 through $FF in
hexadecimal). However, data values may also be set to 256 ($100 in
hexadecimal), which the Beboputer will interpret and display as unknown
‘X’ values.
Memory (RAM)
Pull-down menu
Memory
*.ram files Load RAM
ROM files
Any *.rom files are located in the \data directory. These files are generated
by the Beboputer’s assembler when the .ORG statement in the assembly
source program is specified as being in the range $1000 to $3FFF, which is
the user-definable area of the Beboputer’s ROM (Figure B.10).
Assembler
Note that we (the authors) have reserved ROM locations $0000 through
$0FFF for our own nefarious purposes, so the assembler won’t allow you to
set a .ORG in this region. Also note that the only way that you (the reader)
can employ *.rom files is to generate a System ROM as discussed in the next
section. Meanwhile, an example *.rom file follows:
B-14 Bebop BYTES Back
Apart from the title and various comment lines, the format of *.rom files is
almost identical to that of paper tape and RAM files as discussed above.
However, note that although *.rom data values are typically in the range
0 through 255 ($00 through $FF in hexadecimal), these data values may
also be set to 257 ($101 in hexadecimal), which the Beboputer will
interpret and display as ‘#’ values (indicating that these are unused locations
in the ROM).
Note that *.sro files always cover the address range 4,096 through 16,383
($1000 through $3FFF in hexadecimal), which represents the full range of
user-definable ROM locations. As for standard *.rom files, data values are
typically in the range 0 through 255 ($00 through $FF in hexadecimal), but
some data values may also be set to 257 ($101 in hexadecimal), which the
Beboputer will interpret and display as ‘#’ values (indicating that these are
unused locations in the ROM).
Ram files
lab4_p1.ram Program that loops around reading from the 8-bit
switches and displaying the result in decimal using
the solo and dual 7-segment displays
lab4_p2.ram As for lab4_p1 but also speaks the numbers out loud
lab8_p1.ram Program to display a new character in the Character
ROM
Subroutines (assembly source)
add16.asm 16-bit add. (Appendix G)
sub16.asm 16-bit subtract. (Appendix G)
smult8.asm 8-bit signed multiplication. (Appendix G)
smult16.asm 16-bit signed multiplication. (Appendix G)
umult8.asm 8-bit unsigned multiplication. (Appendix G)
umult10.asm 10-bit unsigned multiplication. (Appendix I)
umult16.asm 16-bit unsigned multiplication. (Appendix G)
sdiv16.asm 16-bit signed division. (Appendix G)
udiv10.asm 10-bit unsigned division. (Appendix I)
udiv16.asm 16-bit unsigned division. (Appendix G)
Programs (assembly source)
lab1_p1.asm Loops around rotating the accumulator and
displaying on 8-bit LEDs
lab1_p2.asm Loops around incrementing the accumulator and
displaying on 8-bit LEDs
lab2_p1.asm Loops around reading 8-bit switches and writing to 8-bit
LEDs
lab3_p1.asm Identical to lab2_p1 (only included for completeness)
lab3_p2.asm Loops around reading 8-bit switches and writing to the
undecoded 7-segment display
lab3_p3.asm As for lab3_p2 but writes to 8-bit LEDs and undecoded
7-segment display
lab3_p4.asm As for lab3_p3 but writes to 8-bit LEDs and solo
decoded 7-segment display
lab3_p5.asm As for lab3_p4 but writes to 8-bit LEDs and dual
decoded 7-segment display
lab3_p6.asm As for lab1_p2 but writes to 8-bit LEDs and dual
decoded 7-segment display
lab4_p1.asm Loops around reading binary numbers from the 8-bit
switches and displaying them in decimal on the solo
and dual 7-segment displays
lab4_p2.asm As for lab4_p1 but also uses Big Red and speaks the
numbers aloud
lab5_p1.asm Loops around reading keys from the QWERTY keyboard
and displaying their ASCII codes on the dual 7-segment
display
File Formats and Stuff B-17
lab6_p1.asm Program (and subroutine) to clear the memory-
mapped display
lab6_p2.asm Program to display all the characters on the memory-
mapped display
lab6_p3.asm Loops around reading keys from the QWERTY keyboard
and display the corresponding characters in row 6,
column 6 of the memory-mapped display
lab6_p4.asm Very simple text editor program
lab6_p5.asm Tests the medium-resolution color mode of the memory-
mapped display
lab7_p1b.asm Test program containing errors in assembly language
lab7_p2b.asm Identical to lab5_p1 (only included for completeness)
lab7_p3b.asm Etch-A-Sketch-type program(3)
lab8_p1.asm Displays the new character we create in the Character
ROM
lab9_p1.asm Reads and displays numbers on the calculator (Appendix I)
lab9_p2.asm Converts numbers to internal 16-bit representation
(Appendix I)
lab9_p3.asm Converts numbers from internal 16-bit representation
and displays them (Appendix I)
lab9_p4.asm Full solution for calculator sign-off project (Appendix I)
irupt_1b.asm First test of interrupt servicing (Appendix D)
irupt_2b.asm Service an interrupt and then disable it (Appendix D)
irupt_3b.asm Nested interrupt service routines (Appendix D)
quiz_12.asm Solution to assembly question in quick quiz #12
(Appendix H)
3
Etch-A-Sketch is a registered trademark of the Ohio Art Company.
B-18 Bebop BYTES Back
Appendix
C
Addressing Modes and
Instruction Set
C-2 Bebop Bytes Back
Contents of Appendix C
Addressing modes .................................................................................................................................. C-3
Implied addressing (imp) ................................................................................................ C-3
Standard immediate addressing (imm) ................................................... C-4
Big immediate addressing (imm) ....................................................................... C-5
Standard absolute addressing (abs) ............................................................ C-5
Big absolute addressing (abs) ................................................................................. C-6
Indexed addressing (abs-x) ........................................................................................ C-8
Indirect addressing (ind) .................................................................................................. C-9
Pre-indexed indirect addressing (x-ind) ................................................. C-10
Indirect post-indexed addressing (ind-x) ............................................. C-11
Beboputer instruction set summary ............................................................................ C-13
(a)
Opcode Opcode IR
(c) (b)
Instructions that use implied addressing are: CLRIM, DECA, DECX, HALT,
INCA, INCX, NOP, POPA, POPSR, PUSHA, PUSHSR, ROLC, RORC, RTI,
RTS, SETIM, SHL, and SHR.
(b)
(a) Opcode IR
Opcode
(c)
Data
Figure C.2: Standard (e)
(d)
Result ACC
immediate addressing
(a) (b)
Opcode Opcode IR
(c) (d)
MS Data
(e) (f)
LS Data
(g)
MS Data LS Data SP
(a) (b)
Opcode Opcode IR
(c) (d)
MS Addr
(e) (f)
LS Addr
(g)
(i) (h)
ACC Result Data
The main PC is now “put on hold” while the CPU uses the temporary PC to
point to the target address containing the data (h). The CPU executes the
original instruction using this data, stores the result into the accumulator (i),
and returns control to the main PC to look for the next instruction.
Instructions that use standard absolute addressing are: ADD, ADDC, AND,
CMPA, LDA, OR, STA, SUB, SUBC, and XOR. Note that, in the case of a
STA (“store accumulator”), the contents of the accumulator would be copied
(stored) into the data byte in memory. Also note that the jump instructions
JMP, JC, JNC, JN, JNN, JO, JNO, JZ, JNZ, and JSR can use absolute
addressing. However, in this case, the address operand bytes point to the
target address which will be loaded into the main PC (see Chapter 8 for
more details on the jump instructions).
(a) (b)
Opcode Opcode IR
(c) (d)
MS Addr
(e) (f)
LS Addr
(g)
Temp PC
Figure C.5: Big absolute addressing MS Addr LS Addr
(using BLDSP as an example)
(i) (h)
MS Data
(k) (j)
LS Data
SP MS Data LS Data
The sequence commences when the PC reaches the opcode for an absolute
instruction (a), loads that opcode into the IR (b), and increments the PC (c).
Recognizing that this is a big absolute instruction, the CPU reads the MS
address byte from memory, stores it in the MS byte of one of our temporary
PCs (d), and increments the main PC (e). The CPU then reads the LS address
byte from memory, stores it in the LS byte of the temporary PC (f), and
increments the main PC (g).
The main PC is now “put on hold” while the CPU uses the temporary PC to
point to the target address containing the MS data byte (h) and store it in the
MS byte of our target register (i). The CPU then increments the temporary
PC so as to point to the LS data byte (j) and store it in the LS byte of our
target register (k). The CPU now returns control to the main PC to look for
the next instruction.
Remember that the above sequence described a “big load” of one of our
16-bit registers (the stack pointer in this example). In the case of a “big
store”, the contents of the 16-bit register in question would be copied
(stored) into the two data bytes in memory.
Instructions that use big absolute addressing are: BLDSP, BLDX, BLDIV,
BSTSP, and BSTX.
C-8 Bebop BYTES Back
(a) (b)
Opcode Opcode IR
(c) (d)
MS Addr
(e) (f)
LS Addr
(g)
Temp PC Index Register (X)
MS Addr LS Addr MS LS
The sequence commences when the PC reaches the opcode for an indexed
instruction (a), loads that opcode into the IR (b), and increments the PC (c).
Recognizing that this is an indexed instruction, the CPU reads the MS
address byte from memory, stores it in the MS byte of one of our temporary
PCs (d), and increments the main PC (e). The CPU then reads the LS address
byte from memory, stores it in the LS byte of the temporary PC (f), and
increments the main PC (g).
The main PC is now “put on hold” while the CPU adds the contents of the
temporary PC to the contents of the index register and uses the result to
point to the target address containing the data (h). The CPU now executes
the original instruction using this data and stores the result into the
accumulator (i). Finally, the CPU returns control to the main PC to look for
Registers Flags Addressing Modes Other
ACC = Accumulator Z = Zero imp = Implied LS = Least-signif
icant
PC = Program Counter N = Negative imm = Immediate MS = Most-signif
icant
IR = Instruction Register
C = Carry abs = Absolute Addr = Address
X = Index Register O = Overflow abs-x = Indexed
SP = Stack ointer
P I = Interrupt Mask ind = Indirect
IV = Interruptector
V x_ind = Pre-indexed indirect
ind-x = Indirect post-indexed
Addressing Modes and Instruction Set C-9
the next instruction. (Note that the act of adding the temporary PC to the
index register does not affect the contents of the index register. Also note
that the index register must have been loaded with a valid value prior to the
first indexed instruction).
Instructions that use indexed addressing are: ADD, ADDC, AND, CMPA, LDA,
OR, STA, SUB, SUBC, and XOR. Note that, in the case of a STA (“store
accumulator”), the contents of the accumulator would be copied (stored)
into the data byte in memory. Also note that the jump instructions JMP and
JSR can use indexed addressing; however, in this case, the result of adding
the contents of the temporary program counter to the index register forms
the target jump address, which is loaded into the main PC (see Chapter 8 for
more details on the jump instructions).
(a) (b)
Opcode Opcode IR
(c) (d)
MS Addr
(e) (f)
LS Addr
(g)
Temp PC A
addressing
(i) (h)
MS Addr
(k) (j)
LS Addr
Temp PC B
MS Addr LS Addr
(l) (m)
Data Data ACC
C-10 Bebop BYTES Back
When the PC reaches an indirect opcode (a), the CPU loads that opcode
into the IR (b), and increments the PC (c). Now the CPU reads the MS
address byte from memory, stores it in the MS byte of temporary PC A (d),
and increments the main PC (e). Next the CPU reads the LS address byte
from memory, stores it in the LS byte of temporary PC A (f), and increments
the main PC (g).
The CPU now employs temporary PC A to read the MS byte of the second
address (h), store it in the MS byte of temporary PC B (i), and increment
temporary PC A (j). Next the CPU reads the LS byte of the second address,
stores it in the LS byte of temporary PC B (k), uses temporary PC B to point
to the target data (l), and loads this data into the accumulator (m). Finally,
the CPU returns control to the main PC to look for the next instruction.
Instructions that use indirect addressing are LDA and STA. Also, the jump
instructions JMP and JSR can use indirect addressing; however, in this
case, the second address is the target jump address which is loaded into the
main PC (see Chapter 8 for more details on the jump instructions).
(a) (b)
Opcode Opcode IR
(c) (d)
MS Addr
(e) (f)
LS Addr
(g)
Temp PC A Index Register (X)
MS Addr LS Addr MS LS
+
(i) (h)
MS Addr
(k) (j)
LS Addr
Temp PC B
Figure C.8: Pre-indexed
MS Addr LS Addr
indirect addressing
(l) (m)
Data Data ACC
The CPU now adds the contents of temporary PC A to the contents of the
index register, uses the result to point to the MS byte of the second address
(h), and stores this byte in the MS byte of temporary PC B (i). The CPU then
points to the LS byte of the second address (j), stores it in the LS byte of
temporary PC B (k), uses temporary PC B to point to the target data (l), and
loads this data into the accumulator (m). Finally, the CPU returns control to
the main PC to look for the next instruction.
Instructions that use pre-indexed indirect addressing are LDA and STA. Also,
the jump instructions JMP and JSR can use this form of addressing;
however, in this case, the address pointed to by the combination of
temporary PC A and the index register is the target jump address which is
loaded into the main PC (see Chapter 8 for more details on the jump
instructions).
(a) (b)
Opcode Opcode IR
(c) (d)
MS Addr
(e) (f)
LS Addr
(g)
Temp PC A
Figure C.9: Indirect MS Addr LS Addr
post-indexed addressing
(i) (h)
MS Addr
(k) (j)
LS Addr
Index Register (X) Temp PC B
MS LS MS Addr LS Addr
+
(l) (m)
Data Data ACC
The CPU uses the contents of temporary PC A to point to the MS byte of the
second address (h), and stores this byte in the MS byte of temporary PC B
(i). The CPU then increments temporary PC A to point to the LS byte of the
second address (j), and stores this byte in the LS byte of temporary PC B (k).
Now the CPU adds the contents of temporary PC B to the contents of the
index register, uses the result to point to the target data (l), and loads this
data into the accumulator (m). Finally, the CPU returns control to the main
PC to look for the next instruction.
Instructions that use indirect post-indexed addressing are LDA and STA.
Also, the jump instructions JMP and JSR can use this form of addressing;
(see Chapter 8 for more details on the jump instructions).
Loads & BLDX Load data in memory into the index register.
Stores BSTX Store data in the index register into memory.
BLDSP Load data in memory into the stack pointer.
BSTSP Store data in the stack pointer into memory.
BLDIV Load data in memory into the interrupt vector,
op # op # op # op # op # op # op # I O N Z C
BSTSP $59 3 - - - - -
BSTX $A9 3 - - - - -
CLRIM $09 1 0 - - - -
DECA $81 1 - - N Z -
DECX $83 1 - - - Z -
HALT $01 1 - - - - -
INCA $80 1 - - N Z -
INCX $82 1 - - - Z -
JC $E1 3 - - - - -
JN $D9 3 - - - - -
JNC $E6 3 - - - - -
JNN $DE 3 - - - - -
JNO $EE 3 - - - - -
JNZ $D6 3 - - - - -
op # op # op # op # op # op # op # I O N Z C
JO $E9 3 - - - - -
JZ $D1 3 - - - - -
NOP $00 1 - - - - -
POPA $B0 1 - - N Z -
POPSR $B1 1
PUSHA $B2 1 - - - - -
PUSHSR $B3 1 - - - - -
ROLC $78 1 - - N Z
RORC $79 1 - - N Z
RTI $C7 1
RTS $CF 1 - - - - -
SETIM $08 1 1 - - - -
SHL $70 1 - - N Z
SHR $71 1 - - N Z
Legend
op = Opcode
$ = Hexadecimal nu
mber
# = Nu mber of bytes
- = No change
≥ = Magnitude comparison (special case)
↔ = Shift or rotate through carry bit
Φ = Restored by popping status register
C-16 Bebop BYTES Back
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imm 2 $10 ADD $03 Add $03 to the ACC.
abs 3 $11 ADD [$4C76] Add the contents of memory
location $4C76 to the ACC
abs-x 3 $12 ADD [$4C76,X] Add the contents of a memory
location to the ACC, where the
address of the memory location
is $4C76 plus the contents of the
X register
Flags affected:
O Set to 1 if the result overflows, otherwise cleared to 0
N Set to 1 if the MS bit of the result is 1, otherwise cleared to 0
Z Set to 1 if all of the bits in the result are 0, otherwise cleared to 0
C Set to 1 if there is a carry out from the addition, otherwise cleared to 0
Bit 7 Bit 0
New ACC
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imm 2 $18 ADDC $03 Add $03 to the ACC
abs 3 $19 ADDC [$4C76] Add the contents of memory
location $4C76 to the ACC
abs-x 3 $1A ADDC [$4C76,X] Add the contents of a memory
location to the ACC, where the
address of the memory location
is $4C76 plus the contents of the
X register
Flags affected:
O Set to 1 if the result overflows, otherwise cleared to 0
N Set to 1 if the MS bit of the result is 1, otherwise cleared to 0
Z Set to 1 if all of the bits in the result are 0, otherwise cleared to 0
C Set to 1 if there is a carry out from the addition, otherwise cleared to 0
C-18 Bebop BYTES Back
AND
Bit 7 Bit 0
New ACC
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imm 2 $30 AND $03 Logically AND $03 with the ACC
abs 3 $31 AND [$4C76] Logically AND the contents of
memory location $4C76 with
the ACC
abs-x 3 $32 AND [$4C76,X] Logically AND the contents of a
memory location with the ACC,
where the address of the memory
location is $4C76 plus the
contents of the X register
Flags affected:
N Set to 1 if the MS bit of the result is 1, otherwise cleared to 0
Z Set to 1 if all of the bits in the result are 0, otherwise cleared to 0
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imm 3 $F0 BLDIV $6100 Load the IV with $6100
abs 3 $F1 BLDIV [$4C76] Load the IV with two bytes
from memory, where the
first (MS) byte is located at
address $4C76
Flags affected:
None
C-20 Bebop BYTES Back
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imm 3 $50 BLDSP $4FFF Load the SP with $4FFF
abs 3 $51 BLDSP [$4C76] Load the SP with two bytes
from memory, where the first
(MS) byte is located at address
$4C76
Flags affected:
None
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imm 3 $A0 BLDX $0C64 Load the X register with $0C64
abs 3 $A1 BLDX [$4C76] Load the X register with two
bytes from memory, where the
first (MS) byte is located at
address $4C76
Flags affected:
None
C-22 Bebop BYTES Back
Addr (n)
Addr (n + 1)
2 bytes in memory
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
abs 3 $59 BSTSP [$4C76] Store the SP into two bytes of
memory, where the first (MS)
byte is located at address $4C76
Flags affected:
None
Addr (n)
Addr (n + 1)
2 bytes in memory
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
abs 3 $A9 BSTX [$4C76] Store the X register into two
bytes of memory, where the first
(MS) byte is located at address
$4C76
Flags affected:
None
C-24 Bebop BYTES Back
? ? ? ? ? CLRIM 0 ? ? ? ?
I O N Z C I O N Z C
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $09 CLRIM Load the interrupt mask with 0
Flags affected:
I Loaded with logic 0
Compare
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imm 2 $60 CMPA $03 Compare the contents of the
ACC with $03
abs 3 $61 CMPA [$4C76] Compare the contents of the
ACC with the contents of
memory location $4C76
abs-x 3 $62 CMPA [$4C76,X] Compare the contents of the
ACC with the contents of a
memory location, where the
address of the memory location
is $4C76 plus the contents of the
X register
Flags affected:
Z Set to 1 if the two values are equal, otherwise cleared to 0
C Set to 1 if the (unsigned) value in the accumulator is the greater, otherwise
cleared to 0
C-26 Bebop BYTES Back
-1
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $81 DECA Decrement (subtract 1 from) the
contents of the ACC
Flags affected:
N Set to 1 if the MS bit of the result is 1, otherwise cleared to 0
Z Set to 1 if all of the bits in the result are 0, otherwise cleared to 0
-1
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $83 DECX Decrement (subtract 1 from) the
contents of the X register
Flags affected:
Z Set to 1 if all of the bits in the result (the new contents of the X register) are
0, otherwise cleared to 0.
C-28 Bebop BYTES Back
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $01 HALT Halts the CPU and causes it to
perform internal NOP
instructions
Flags affected:
None
+1
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $80 INCA Increment (add 1 to) the contents
of ACC
Flags affected:
N Set to 1 if the MS bit of the result is 1, otherwise cleared to 0.
Z Set to 1 if all of the bits in the result are 0, otherwise cleared to 0.
Note that the reason incrementing the ACC can result in it containing zero
is if its original value were all 1s; that is, $FF in hexadecimal.
C-30 Bebop BYTES Back
+1
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $82 INCX Increment (add 1 to) the contents
of the X register
Flags affected:
Z Set to 1 if all of the bits in the result (the new contents of the X register) are
0, otherwise cleared to 0. Note that the reason incrementing the X register
can result in it containing zero is if its original value were all 1s; that is,
$FFFF in hexadecimal.
JC (Jump if carry)
Description:
This instruction is used to change the “flow” of the program by causing the
CPU to jump to a new address if the carry status flag is TRUE (contains a
logic 1, thereby indicating that the previous instruction generated a carry),
otherwise the CPU ignores the operand and continues to the next
instruction. See also the corresponding JNC instruction.
From last instruction
yes
=1? Jump to new address
Carry flag
no
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
abs 3 $E1 JC [$4C76] If the carry flag contains a
logic 1, then jump to address
$4C76, otherwise continue to
the next instruction
Flags affected:
None
C-32 Bebop BYTES Back
Description:
This is the instruction that occupies the
memory location(s) before the JMP.
This instruction is used to
change the “flow” of the
JMP Jump to a new address program by causing the CPU
This is the instruction that occupies the to unconditionally jump to a
memory location(s) after the JMP. The
only way the program will ever reach this new address. See also the
instruction is if another jump instruction
targets it. somewhat related JSR
instruction.
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
abs 3 $C1 JMP [$4C76] Jump to address $4C76
abs-x 3 $C2 JMP [$4C76,X] Add $4C76 to the contents of
the X register to form the target
address $xxxx and jump to this
target address
ind 3 $C3 JMP [[$4C76]] Read the target address $xxxx
stored in the two bytes starting at
address $4C76, and then jump
to this target address
x-ind 3 $C4 JMP [[$4C76,X]] Add $4C76 to the contents of
the X register to form a new
address $zzzz. Read the target
address $xxxx stored in the two
bytes starting at address $zzzz
and jump to this target address
ind-x 3 $C5 JMP [[$4C76],X] Read the address $zzzz stored in
the two bytes starting at address
$4C76, then add $zzzz to the
contents of the X register to form
the target address $xxxx and
jump to this target address
Flags affected:
None
Registers Flags Addressing Modes Other
ACC = Accumulator Z = Zero imp = Implied LS = Least-signif
icant
PC = Program Counter N = Negative imm = Immediate MS = Most-signif
icant
IR = Instruction Register
C = Carry abs = Absolute Addr = Address
X = Index Register O = Overflow abs-x = Indexed
SP = Stack ointer
P I = Interrupt Mask ind = Indirect
IV = Interruptector
V x_ind = Pre-indexed indirect
ind-x = Indirect post-indexed
Addressing Modes and Instruction Set C-33
JN (Jump if negative)
Description:
This instruction is used to change the “flow” of the program by causing the
CPU to jump to a new address if the negative status flag is TRUE (contains a
logic 1, thereby indicating that the result from the previous instruction was
negative), otherwise the CPU ignores the operand and continues to the next
instruction. See also the corresponding JNN instruction.
From last instruction
yes
=1? Jump to new address
Negative flag
no
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
abs 3 $D9 JN [$4C76] If the negative flag contains a
logic 1, then jump to address
$4C76, otherwise continue to
the next instruction
Flags affected:
None
C-34 Bebop BYTES Back
yes
=0? Jump to new address
Carry flag
no
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
abs 3 $E6 JNC [$4C76] If the carry flag contains a
logic 0, then jump to address
$4C76, otherwise continue to
the next instruction
Flags affected:
None
yes
=0? Jump to new address
Negative flag
no
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
abs 3 $DE JNN [$4C76] If the negative flag contains a
logic 0, then jump to address
$4C76, otherwise continue to
the next instruction
Flags affected:
None
C-36 Bebop BYTES Back
yes
=0? Jump to new address
Overflow flag
no
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
abs 3 $EE JNO [$4C76] If the overflow flag contains a
logic 0, then jump to address
$4C76, otherwise continue to
the next instruction
Flags affected:
None
yes
=0? Jump to new address
Zero flag
no
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
abs 3 $D6 JNZ [$4C76] If the zero flag contains a
logic 0, then jump to address
$4C76, otherwise continue to
the next instruction
Flags affected:
None
C-38 Bebop BYTES Back
JO (Jump if overflow)
Description:
This instruction is used to change the “flow” of the program by causing the
CPU to jump to a new address if the overflow status flag is TRUE (contains a
logic 1, thereby indicating that the previous instruction generated an
overflow), otherwise the CPU ignores the operand and continues to the next
instruction. See also the corresponding JNO instruction.
From last instruction
yes
=1? Jump to new address
Overflow flag
no
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
abs 3 $E9 JO [$4C76] If the overflow flag contains a
logic 1, then jump to address
$4C76, otherwise continue to
the next instruction
Flags affected:
None
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
abs 3 $C9 JSR [$4C76] Jump to the subroutine at address
$4C76
abs-x 3 $CA JSR [$4C76,X] Add $4C76 to the contents of the X
register to form the target address of
the subroutine ($xxxx) and jump to
this target address
ind 3 $CB JSR [[$4C76]] Read the target address of the
subroutine ($xxxx) stored in the
two bytes starting at address
$4C76, and then jump to this
target address
x-ind 3 $CC JSR [[$4C76,X]] Add $4C76 to the contents of the X
register to form a new address
$zzzz. Read the target address of the
subroutine ($xxxx) stored in the two
bytes starting at address $zzzz and
jump to this target address
ind-x 3 $CD JSR [[$4C76],X] Read the address $zzzz stored in
the two bytes starting at address
$4C76, then add $zzzz to the
contents of the X register to form
the target address of the subroutine
($xxxx) and jump to this target
address
Flags affected:
None
C-40 Bebop BYTES Back
JZ (Jump if zero)
Description:
This instruction is used to change the “flow” of the program by causing the
CPU to jump to a new address if the zero status flag is TRUE (contains a
logic 1, thereby indicating that the result from the previous instruction was
zero), otherwise the CPU ignores the operand and continues to the next
instruction. See also the corresponding JNZ instruction.
From last instruction
yes
=1? Jump to new address
Zero flag
no
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
abs 3 $D1 JZ [$4C76] If the zero flag contains a
logic 1, then jump to address
$4C76, otherwise continue to
the next instruction
Flags affected:
None
Flags affected:
N Set to 1 if the MS bit of the ACC is 1, otherwise cleared to 0
Z Set to 1 if all of the bits in the ACC are 0, otherwise cleared to 0
C-42 Bebop BYTES Back
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $00 NOP Doesn’t do a thing
Flags affected:
None
OR (Logical operation)
Description:
This instruction logically ORs the contents of a byte of data in memory with
the current contents of the accumulator and stores the result in the
accumulator (the contents of the memory are not affected). Note that this is
a bit-wise operation, which means that bit 0 of the old ACC is OR-ed with
bit 0 of the memory to generate bit 0 of the new ACC. Similarly, bit 1 is
OR-ed with bit 1, bit 2 with bit 2, and so forth. See also the AND and XOR
instructions.
Old ACC Byte in memory
OR
Bit 7 Bit 0
New ACC
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imm 2 $38 OR $03 Logically OR $03 with the ACC
abs 3 $39 OR [$4C76] Logically OR the contents of
memory location $4C76 with the
ACC
abs-x 3 $3A OR [$4C76,X] Logically OR the contents of a
memory location with the ACC,
where the address of the
memory location is $4C76 plus
the contents of the X register
Flags affected:
N Set to 1 if the MS bit of the result is 1, otherwise cleared to 0
Z Set to 1 if all of the bits in the result are 0, otherwise cleared to 0
C-44 Bebop BYTES Back
Bit 7 Bit 0
ACC
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $B0 POPA Pops the byte on the top of the
stack into the ACC (I’m a poet
and I never knew-it)
Flags affected:
N Set to 1 if the MS bit of the ACC is 1, otherwise cleared to 0
Z Set to 1 if all of the bits in the ACC are 0, otherwise cleared to 0
Bit 7 Bit 0
SR
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $B1 POPSR Pops the byte on top of the stack
into the SR
Flags affected:
I Loaded with whatever was in bit 4 of the byte on top of the stack
O Loaded with whatever was in bit 3 of the byte on top of the stack
N Loaded with whatever was in bit 2 of the byte on top of the stack
Z Loaded with whatever was in bit 1 of the byte on top of the stack
C Loaded with whatever was in bit 0 of the byte on top of the stack
C-46 Bebop BYTES Back
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $B2 PUSHA Pushes the ACC onto the stack
Flags affected:
None
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $B3 PUSHSR Pushes the SR onto the stack
Flags affected:
None
C-48 Bebop BYTES Back
Bit 7 Bit 0
Carry flag
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $78 ROLC Rotates the ACC 1 bit left and
through the carry flag
Flags affected:
N Set to 1 if the MS bit of the ACC is 1, otherwise cleared to 0
Z Set to 1 if all of the bits in the ACC are 0, otherwise cleared to 0
C Loaded with whatever was in bit 7 of the ACC
Bit 7 Bit 0
Carry flag
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $79 RORC Rotates the ACC 1 bit right and
through the carry flag
Flags affected:
N Set to 1 if the MS bit of the ACC is 1, otherwise cleared to 0
Z Set to 1 if all of the bits in the ACC are 0, otherwise cleared to 0
C Loaded with whatever was in bit 0 of the ACC
C-50 Bebop BYTES Back
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $C7 RTI Exits the interrupt service routine
and returns control to the main
program
Flags affected:
All The SR is reloaded with whatever its contents were when the interrupt
caused it to be pushed onto the stack (assuming the programmer hasn’t
used the interrupt service routine to modify the copy of the SR on the stack)
Calling program
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $CF RTS Exits the subroutine and returns
control to the calling program
Flags affected:
None
C-52 Bebop BYTES Back
? ? ? ? ? SETIM 1 ? ? ? ?
I O N Z C I O N Z C
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $08 SETIM Load the interrupt mask with 1
Flags affected:
I Loaded with logic 1
Logic 0
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $70 SHL Shifts the accumulator 1 bit left
Flags affected:
N Set to 1 if the MS bit of the ACC is 1, otherwise cleared to 0
Z Set to 1 if all of the bits in the ACC are 0, otherwise cleared to 0
C Loaded with whatever was in bit 7 of the ACC
C-54 Bebop BYTES Back
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imp 1 $71 SHR Shifts the accumulator 1 bit right
(arithmetic shift)
Flags affected:
N Set to 1 if the MS bit of the ACC is 1, otherwise cleared to 0
Z Set to 1 if all of the bits in the ACC are 0, otherwise cleared to 0
C Loaded with whatever was in bit 0 of the ACC
Byte in memory
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
abs 3 $99 STA [$4C76] Stuff the contents of the ACC
into address $4C76
abs-x 3 $9A STA [$4C76,X] Add $4C76 to the contents of
the X register to form the target
address $xxxx, then stuff the
contents of the ACC into the
target address
ind 3 $9B STA [[$4C76]] Read the target address $xxxx
stored in the two bytes starting at
address $4C76, then stuff the
contents of the ACC into the
target address
x-ind 3 $9C STA [[$4C76,X]] Add $4C76 to the contents of
the X register to form a new
address $zzzz. Read the target
address $xxxx stored in the two
bytes starting at address $zzzz,
then stuff the contents of the
ACC into the target address
ind-x 3 $9D STA [[$4C76],X] Read the address $zzzz stored in
the two bytes starting at address
$4C76, then add $zzzz to the
contents of the X register to form
the target address $xxxx, then
stuff the contents of the ACC into
the target address
Flags affected:
None
C-56 Bebop BYTES Back
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imm 2 $20 SUB $03 Subtract $03 from the ACC.
abs 3 $21 SUB [$4C76] Subtract the contents of memory
location $4C76 from the ACC
abs-x 3 $22 SUB [$4C76,X] Subtract the contents of a
memory location from the ACC,
where the address of the
memory location is $4C76 plus
the contents of the X register
Flags affected:
O Set to 1 if the result overflows, otherwise cleared to 0
N Set to 1 if the MS bit of the result is 1, otherwise cleared to 0
Z Set to 1 if all of the bits in the result are 0, otherwise cleared to 0
C Set to 1 if there is a carry out (really a “borrow-not”) from the subtraction,
otherwise cleared to 0 (indicating a “borrow”)
New ACC
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imm 2 $28 SUBC $03 Subtract $03 from the ACC
abs 3 $29 SUBC [$4C76] Subtract the contents of memory
location $4C76 from the ACC
abs-x 3 $2A SUBC [$4C76,X] Subtract the contents of a
memory location from the ACC,
where the address of the
memory location is $4C76 plus
the contents of the X register
Flags affected:
O Set to 1 if the result overflows, otherwise cleared to 0
N Set to 1 if the MS bit of the result is 1, otherwise cleared to 0
Z Set to 1 if all of the bits in the result are 0, otherwise cleared to 0
C Set to 1 if there is a carry out (really a “borrow-not”) from the subtraction,
otherwise cleared to 0 (indicating a “borrow”)
C-58 Bebop BYTES Back
New ACC
Addressing modes:
Mode #Bytes Opcode Assembly example Comments
imm 2 $40 XOR $03 Logically XOR $03 with
the ACC
abs 3 $41 XOR [$4C76] Logically XOR the
contents of memory
location $4C76 with the
ACC
abs-x 3 $42 XOR [$4C76,X] Logically XOR the
contents of a memory
location with the ACC,
where the address of the
memory location is
$4C76 plus the contents
of the X register
Flags affected:
N Set to 1 if the MS bit of the result is 1, otherwise cleared to 0
Z Set to 1 if all of the bits in the result are 0, otherwise cleared to 0
Contents of Appendix D
Just another one of those days .......................................................................................... D-3
Using a polling strategy ................................................................................................................... D-3
The interrupt request (IRQ) input ........................................................................................ D-8
Non-maskable interrupts (NMIs) ...................................................................................... D-11
Software interrupts (SWIs) .......................................................................................................... D-12
The HALT instruction ........................................................................................................................... D-12
The interrupt acknowledge (IACK) output ..................................................... D-14
Interrupt-driven Input/Output ............................................................................................. D-14
Handling multiple interrupt request signals .................................................... D-16
Priority encoding .................................................................................................................................... D-17
The Beboputer’s interrupt capability ........................................................................ D-20
Two last tricks before we close ........................................................................................ D-23
Interrupts and Interrupt Handling D-3
Just another one of those days
Before we commence, we should note that there are a multitude of
strategies for handling interrupts, so we can only hope to cover some of the
more common techniques here. Also note that we’ll start by discussing
these techniques at a generic level, then we’ll return to consider how the
Beboputer handles interrupts.
Let’s begin our discussions by considering a (hopefully) fictitious scenario.
Suppose that you’ve just taken possession of a brand-new car equipped with
an on-board computer, whose tasks include closing the windows (when
instructed to do so) and activating the airbag (in the event of a crash). Now
assume that you’re merrily cruising down the highway and you flick the
“Close Window” button, which causes the computer to enter a loop saying
“Is the window closed yet? If not, I’ll keep on closing it.”
Suddenly, as if from nowhere, a herd of rampaging grockles appear!
Swerving to avoid them you rocket off the road, screech across a farmyard,
and collide rather forcibly with an extremely large pile of manure (don’t you
hate it when that happens?). It’s fortunate indeed that you’re wearing your
seat belt, because your airbag sadly fails to make an appearance (your
computer is still looping around saying “Is the window closed yet? ....”).
Thanking your lucky stars,(1) you reach for the steaming-hot coffee that you
recently acquired from a well-known purveyor of fast foods. But at the self-
same moment that you raise the coffee to your lips, the window finishes
closing and the computer finally gets around to check what’s happening in
the outside world. Realizing that there’s a problem, the computer
immediately activates the airbag, you unexpectedly find yourself taking a
somewhat larger gulp of coffee than was your original intent, and you’re
well on the way to having another “one of those days”.
Unfortunately, this scenario is not as uncommon (in general terms) as you
might think, because it can be tricky to ensure that a computer is made
aware of external events in a timely manner so as to handle them
appropriately.
that, whilst performing this task, we also want our computer to act as a
burglar alarm that monitors the state of a switch connected to the front door
of our house. For the purposes of these discussions, let’s assume that
opening the door will cause the switch to close, in which case we want the
computer to respond by ringing a bell.
Thus far, we’ve considered the CPU’s primary mechanism for “seeing”
things occurring in the outside world as being via its input ports. On this
basis, we might decide to connect our burglar alarm switch to a bit on one
of these ports, say bit[0], and to connect the other bits to logic 0
(Figure D.1).
us s
lb bu bu
s
tro ress ta log
Co
n
Add Da ic 1
rt in[
po 0]
In Switch
CP
U log
ic 0
rt
po
In ic 0
set log
~re TY
ER
ck QW oard
clo m b
o
Fr key
? ?
yes yes
$00 $00
? ?
no no
? ? ?
yes yes yes
$00 $01 $01
? ? ?
no no no
(a) Original flowchart (b) Not a good idea (c) A better solution
Figure D.2: Augmenting our program to monitor the switch and ring bell
In our first-pass solution (Figure D.2b), we might simply add the test to read
the alarm switch onto the end of our original program, but this isn’t a
particularly good idea ...... can you see why? The problem is that this
version of the program only checks the state of the switch after you activate
a key on the keyboard. So while you’re pondering which key to press, a
burglar could have entered your abode and be creeping up behind you ......
Imagine the scene when you eventually press a key on the keyboard: the
bell rings, you leap to your feet shouting “Don’t panic, we’ve got a burglar,
don’t panic,” you turn around, and there he is! (This may well be the time
when you contemplate investing in a better alarm system). As an alternative
scenario, the burglar could slip into your house and close the door while
you’re contemplating the keyboard. In this case, the alarm won’t be
sounded even after you’ve pressed a key, because the door will be closed
by the time the computer finally comes to look at it. So now you’ve got a
burglar roaming wild and free throughout your house, while your computer
is essentially saying: “Don’t worry about a thing my little fruit-bat, because
the front door is safely closed.”
Jocularity aside, this latter point is quite important. A key aspect of external
signals, such as the switch forming our burglar alarm, is that they’re
typically asynchronous. This means that they can occur at any time and are
not synchronized to the computer system’s clock, which therefore means
that we usually have to latch such signals. In this particular scenario, we
could place a latch between the switch and the port (Figure D.3).
D-6 Bebop BYTES Back
bu
s The act of opening the door will
data change the state of the latch, which
To Latch
po
rt in[0] log
will retain this new state even when
In ic 1
the door is closed again. Thus,
ic 0 when our program eventually
log
manages to limp around to check
the state of the door, the value in
ic 0
the latch will tell it that the door is
Figure D.3: Latching the log either currently open or has been
external signal
opened. (We could also arrange the
circuit such that the act of reading from this port would automatically reset
the latch). Unfortunately, even if we did add a latch to our circuit, the
program described in Figure D.2b still would not warn us that the door has
been opened until we press a key on the keyboard, which makes it next to
useless as a burglar alarm. The solution is to check for the state of the door
every time we go around the loop that tests to see if a key has been pressed
(Figure D.2c).
Thus we see that ensuring the CPU recognizes the door’s opening in a
timely manner does require a little thought, and the problems can only
become more pronounced as we increase the number of signals from the
outside world. For example, we might decide to add burglar alarm switches
to all of the doors and windows in our house. We might also decide to
connect a few smoke detectors to our computer, and perhaps even add a
sensor to warn us if the Jacuzzi in the master bedroom starts to overflow.
Thus, we now have to perform a process known as polling (meaning
surveying or sampling), which requires Loop
us to modify our program to
check for each of these Check the keyboard and
signals in turn maybe do something
Switch
Figure D.6: Connecting an external
signal (such as a burglar alarm switch)
ic 0 to the IRQ input
log
When the IRQ enters its active state, this fact is stored in a special latching
circuit inside the CPU, thereby circumventing the problem of the IRQ going
inactive before the CPU manages to check it (this is similar to the external
latch we considered in Figure D.3, except that this one’s inside the CPU). In
some CPUs this interrupt latch can be programmed to look for active-high
(logic 1) or active-low (logic 0) signals, but many simply assume that the
IRQ’s active state is a logic 0.
Interrupts and Interrupt Handling D-9
The CPU also contains a special status flag called the interrupt mask,(3)
which is used to enable or disable interrupts, and which can be set or
cleared under program control. For example, the Beboputer has two
instructions, SETIM and CLRIM, which set or clear its interrupt mask,
respectively. By default, the CPU powers up with the interrupt mask in its
inactive state (which is typically a logic 0). Thus, in order for the CPU to be
able to “see” an IRQ, the programmer has to use a SETIM (“set interrupt
mask”) instruction to place the mask in its active state. Similarly, if the
programmer subsequently wishes to prevent the CPU from responding to
IRQs, then he or she can use a CLRIM (“clear interrupt mask”) instruction to
return the mask to its inactive state.
The CPU checks the state of the interrupt mask every time it completes a
machine-code level instruction. If the mask is inactive the CPU simply
proceeds to the next instruction; but if the mask is active, then the CPU
takes a peek inside the interrupt latch to determine whether or not an
interrupt has been requested (Figure D.7).
Do next
Read the next opcode and execute the instruction
instruction
yes
yes
registers onto the stack, such as the accumulator and the index register,
because there’s a good chance that the act of servicing the interrupt will
modify the contents of these registers. If the CPU doesn’t do this
automatically, then it’s up to the programmer to save the contents of any
registers he or she deems to be important as soon as the interrupt service
routine is entered.
But what is an interrupt service routine and where might one be found? In
fact this routine, which is very similar to a subroutine, is a sequence of
instructions that has been created by the programmer and stored somewhere
in the computer’s memory. As soon as the CPU has placed copies of the
program counter and status register (and any other registers) on the top of
the stack, it loads a hard-wired address into the program counter, then uses
this address to point to a location in memory (Figure D.8).
Memory
Interrupt
vector
Program
counter us
lb
ntro s
Co bu
re ss
Add
s
bu
ta
Da Interrupt
service
t routine
se
~re
ck
clo
Hard-wired
IRQ address
(yes, it’s turning out to be yet another “one of those days”). The problem is
that when the CPU “sees” the IRQ generated by the Jacuzzi, it will
immediately leap into action and start performing the appropriate interrupt
service routine. But, as you may recall, one of the first things the CPU does
when it responds to an IRQ is to disable the interrupt mask, thereby
preventing any other IRQs from being seen (we’ll consider ways to get
around this later). So if the smoke detector also generated an IRQ, the
computer wouldn’t see it because it would be too busy working on the
Jacuzzi problem. However, if the smoke detector generates an NMI, then
this will take precedence over anything else that the computer is doing,
including servicing an IRQ.(4)
SWIs have a variety of uses, not the least that they allow the programmer to
perform some level of testing on the interrupt service routines without
having to physically trigger an external interrupt (such as burning the house
down). Also, these instructions may find application in debugging the body
of a program. For example, we could create an interrupt service routine
whose only task was to display the current values of the CPU’s registers on
some form of output device (such as our memory mapped display). We
could then insert SWI instructions at strategic locations within our program,
such that whenever the CPU sees one of these instructions it will leap to the
interrupt service routine, display the current contents of the registers, then
return to the body of the program.
4
If CPU has NMI input but we’re not using it, then we can just “tie it off” to its inactive state.
Interrupts and Interrupt Handling D-13
program by creating some sort of a dummy loop; consider the following
assembly statement (Figure D.10a).
Translate
(Assemble)
$4F05 $C1
DUMMY: JMP [DUMMY] $4F06 $4F
$4F07 $05
Interrupt-driven Input/Output
During our investigations of devices such as a QWERTY keyboard (Chapter
10), we’ve tended to handle them using a polling strategy. For example,
we’ve created programs that loop around reading the port connected to the
keyboard until a key has been pressed; then we’ve passed the code for this
key to an output device (such as our memory-mapped display) and returned
to looping around waiting for the next key.
But a modern computer can execute many millions of instructions a second,
which means that 99.9% of the time our CPU is just hanging around
twiddling its metaphorical thumbs. This is not to say that there’s anything
particularly wrong with this technique, providing we only want to perform
simple tasks like copying characters from the keyboard to the display.
However, instead of recklessly squandering all of this processing power, we
might wish to employ it in a gainful way. For example, while the CPU is
Interrupts and Interrupt Handling D-15
waiting for us to press the next key, we could be using it to perform some
useful task like reformatting the contents of the display to line all of the
words up nicely.
The problem is that if we do create a routine to reformat the screen, then
this routine will need to keep on checking the keyboard to see if we’ve
pressed another key. What we’d really like is to leave the reformatting
routine free to perform its machinations, and break in as soon as a key is
pressed on the keyboard. Just a moment, doesn’t this sound suspiciously like
a task for an interrupt? In fact, that’s exactly where we’re leading, in that we
could easily equip our keyboard with the ability to generate an interrupt
whenever a key is pressed (Figure D.12).
s
bu
tr ol
on us
C
s sb
dre
Ad
s
bu
ta
Da
CP
t U
se rt
~re ck In
po
clo
NMI
IRQ K
IAC
Int
err
up
ta rd
ckn oa
Int ow
led eyb
err
up ge T Yk
t re ER
qu
es QW
t
In this scenario, the CPU can be happily performing some task or other
without having to monitor the state of the keyboard. Whenever a key is
pressed, the keyboard would issue an interrupt request, which would cause
the CPU to hand control over to the associated interrupt service routine. In
turn, this routine would read the input port connected to the keyboard,
copy the resulting value to the display, then return control to the main
program. Also, when the CPU starts to respond to the interrupt request, it
would activate its interrupt acknowledge output, thereby informing the
keyboard that things were on the move. As soon as the service routine had
terminated, the CPU would return the interrupt acknowledge to its inactive
state, which would inform the keyboard that it is now free to clear its
internal latch.
D-16 Bebop BYTES Back
us s
lb bu bu
s
tro ress ta
n dd Da
Co A
rt
po
In
CP me
set U So vice
~re ck port de
clo I In
NM
IRQ K me
IAC So vice
de
ic 1
Pull-up log
resistor
Priority encoding
As we noted early in this chapter, there are many different strategies for
handling interrupts and it isn’t possible to cover them all here. However, it
would be remiss of us to neglect the topic of priority encoding, if only
because it’s quite an interesting subject. We commence by attaching a
special device called a priority encoder to the data bus (Figure D.14).
s s
bu bu bu
s
trol ress ta
Con Add Da
r
CP co
de
se
t U rity
en
~re ck Pri
o
clo I
NM :0]
IRQ K Q[15
IAC XIR
Now here’s one of the clever bits. The priority encoder converts its sixteen
inputs into a 4-bit binary code (the most-significant four bits of the data byte
can be set to logic 0), and it’s this
XIRQ[15:0] data[7:0] code the CPU sees when it reads a
0000000000000001 00000000 value from the encoder (Figure D.15).
0000000000000010 00000001
0000000000000100 00000010 Note that this figure only illustrate
0000000000001000 00000011 those cases in which a single external
0000000000010000 00000100
interrupt request is activated; we’ll
0000000000100000 00000101
0000000001000000 00000110 consider what happens when multiple
0000000010000000 00000111 interrupts occur in a little while
0000000100000000 00001000 (also note that we’re going to simplify
0000001000000000 00001001 things just a tad for the sake of
: :
etc etc
understandability). Somewhere in the
system’s memory are sixteen interrupt
Figure D.15: Codes generated vectors organized as a table, and the
by priority encoder hard-wired address in the CPU points
to the “base” interrupt vector in this table (Figure D.16a). When the CPU
receives an interrupt request and reads the value from the priority encoder,
it adds this value to its hard-wired address, thereby generating a new
address which points to the appropriate interrupt vector in the table
(Figure D.16b). This combined address is then loaded into the program
counter and used by the CPU to retrieve an interrupt vector, which in turn
points to the appropriate interrupt service routine.
Memory
(b)
8-bit value from
priority encoder
One small point to consider is that, if we assume that our CPU has a 16-bit
address bus and an 8-bit data word, then each interrupt vector will occupy
two bytes in memory, which means that the CPU has to multiply the value
from the priority encoder by two before adding it to the hard-wired address
(it can easily achieve this by automatically shifting the value left by one bit).
Interrupts and Interrupt Handling D-19
All of this can be a little tricky to understand at first, so let’s walk through a
simple example. Purely for the sake of discussion, we’ll assume that the
base address of the interrupt vector table is located at address $9000, which
is therefore the value represented by the CPU’s hard-wired address. This
means that the first interrupt vector occupies addresses $9000 and $9001,
the second interrupt vector occupies $9002 and $9003, the third occupies
$9004 and $9005, and so forth.
Now assume that the external device connected to the XIRQ[2] signal
requests an interrupt, which causes the priority encoder to activate the main
interrupt request signal to the CPU. After completing its current instruction,
the CPU pushes the values in its program counter and status register onto
the stack, and then reads a value from the priority encoder. As XIRQ[2] was
the signal that called the interrupt, the code generated by the encoder will
be $02 (or 00000010 in binary). The CPU multiplies this value by two (by
shifting it one bit to the left) to generate $04 (or 00000100 in binary). The
CPU then adds this value to the hard-wired address to generate a new
address of $9004, which it loads into the program counter in order to point
to the appropriate interrupt vector. Finally, the CPU performs an
unconditional jump to address $9004 using the indirect addressing mode,
which causes it to end up at the first instruction in the relevant interrupt
service routine.
Let’s now return to consider what happens if the priority encoder receives
multiple requests on its sixteen XIRQ[15:0] inputs, of which there are
216 = 65,536 potential combinations. By some strange quirk of fate, the
reason this device is called a priority encoder is that it prioritizes things.
Let’s assume that, by default, XIRQ[0] is considered to have a higher priority
than XIRQ[1], which, in turn, has a higher priority than XIRQ[2], and so
forth. Thus, if the priority encoder should happen to simultaneously receive
interrupt requests on XIRQ[15], XIRQ[12], and XIRQ[9], the value it
eventually hands over to the CPU will be the $09 (or 00001001 in binary)
corresponding to XIRQ[9], because this input has the higher priority.
Also, if the system is already dealing with an interrupt when another,
higher-priority interrupt occurs, then there are techniques we can use to
permit this new signal to interrupt the first (but you’ll forgive us if we don’t
go into that here). Another interesting point is that the CPU can write values
to the priority encoder, because, in addition to acting like an input port, this
device can also behave in a similar fashion to an output port. Why would
we wish to do this? Well, one common scenario is that the priority encoder
would contain its own 16-bit interrupt mask register (completely distinct
D-20 Bebop BYTES Back
from the interrupt mask in the CPU), thereby giving it the power to enable
or disable the external interrupt requests on an individual basis. For
example, if we loaded this interrupt mask register to contain 0000 0100
0000 1001 in binary, then the priority encoder would only respond to
interrupt requests on the XIRQ[10], XIRQ[3], and XIRQ[0], signals. The next
problem relates to how the CPU can address this 2-byte field in order to
write to it when our data bus is only 8-bits wide. As is usually the case,
there are a number of alternatives; for example, we could trick the system
into thinking that the priority encoder was actually two separate output
ports, one for each byte in its interrupt mask register, where each of these
“ports” would have it’s own address in the memory map and its own
~write_enable input on the device.(5)
As we see, the main body of the program is quite simple. First we define a
couple of constant labels called SOLO7SEG and DUAL7SEG, which we
assign to the port addresses of our output displays. After defining the origin
of the program to be $4000, we load the stack pointer with $4FFF and we
initialize the interrupt vector by assigning the label SERVICE to it (the
assembler will automatically substitute this label for the start address of our
interrupt service routine which is defined later in the program). The final
step in the initialization is to use a SETIM instruction to load the interrupt
mask with a logic 1, thereby permitting the CPU to see an interrupt
(the SETIM also clears the CPU’s interrupt latch, so as to ensure that the
CPU only responds to any new interrupt request).
D-22 Bebop BYTES Back
The remainder of the main body of the program initializes the accumulator
to contain zero, and then enters a loop which copies the contents of the
accumulator to the dual 7-segment display, increments the accumulator,
then jumps back to do it again. Thus, we expect to see our dual display
counting up from $00 to $FF, at which point it will automatically wrap
around to $00 (because the accumulator can’t contain a value bigger than
$FF) and start the count again.
The main body of the program will continue to loop around forever, unless
the Beboputer is reset or an interrupt happens to disturb it. Remember that
the CPU checks the state of the interrupt mask after every instruction and, as
we’ve loaded the mask with a logic 1 (using our SETIM instruction), it will
then proceed to check the state of the interrupt latch. Thus, whenever we
click the interrupt icon in the toolbar, the CPU will see the interrupt as soon
as it finishes whatever instruction it’s currently working on. At this point, the
CPU will push the current contents of the program counter onto the stack,
followed by the current contents of the status register. The CPU will then
automatically load the interrupt mask with a logic 0 to prevent additional
interrupts from having any effect.
The first thing we do upon entering our interrupt service routine is to use a
PUSHA instruction to push a copy of the current contents of the
accumulator onto the stack. We do this because we know that this particular
routine is going to modify the accumulator, which would interfere with the
main body of the program when we eventually return to it. Purely for the
sake of this example, the only task performed by our interrupt service
routine is to count down from $0F to $00 on the solo 7-segment display.
Once this has been completed, we use a POPA instruction to restore the
contents of the accumulator to their original value when we entered the
routine, followed by an RTI instruction to terminate the interrupt service
routine and return us to the main body of the program. The RTI causes the
CPU to pop the original value of the status register back off the stack, and to
then restore the program counter from the stack. Note that the act of
popping the status register off the stack will return the interrupt mask to a
logic 1 (which was its value when the status register was originally pushed
onto the stack), thereby re-enabling the CPU’s ability to see any future
interrupts. Also, the other status flags will be returned to whatever states
they were in at the point when the interrupt was first activated.
But enough of this idle chit-chat, let’s run the program to see what happens
first hand. Click the File pull-down in the assembler window followed by
the Save As option, save this file under the name irupt_1.asm, then assemble
Interrupts and Interrupt Handling D-23
it to generate the resulting irupt_1.ram file. Next click the ON button on the
hex keypad to power up the Beboputer, then use the Memory pull-down to
load the irupt_1.ram file into the Beboputer’s RAM.
Ensure that the hex keypad’s Ad (Address) button is active, enter the
program’s start address of $4000, and click the Ru (Run) button. This will
cause the Beboputer to start counting and displaying the result on the dual
7-segment display. Whenever you’re ready, click the interrupt icon in the
tool bar. Observe that the dual display ceases counting, while the solo
display commences to count down. As soon as the solo display reaches
zero, the interrupt service routine returns control to the main body of the
program, which resumes counting on the dual display.
There are two points we should note in regard to this interrupt service
routine example. First, clicking the interrupt icon has no significant effect
whilst we’re currently servicing an interrupt, because the interrupt mask was
automatically disabled (loaded with a logic 0) by the CPU when it began to
respond to the first interrupt. Second, the act of returning from the interrupt
service routine automatically re-enables the CPU’s ability to respond to
future interrupts, because the status register’s original contents are restored
from the stack, and these contents include the fact that the interrupt mask
originally contained a logic 1. Test both of these features for yourself, then
break out of this program by clicking the Beboputer’s Rst (Reset) button.
In this version, the first instruction in the service routine stores the contents
of the accumulator to a temporary location called TEMPACC. The reason we
don’t want to push the accumulator onto the stack as we did before is that
we wish to have access to the original contents of the status register, which
are presently on the top of the stack. The second instruction is a POPA,
which retrieves the old contents of the status register from the top of the
stack and copies them into the accumulator. Next we AND the contents of
the accumulator with $EF, which has the effect of clearing bit[4], the
interrupt mask, to a logic 0. Finally, we use a PUSHA to place the new
contents of the accumulator back onto the top of the stack.
The remainder of the routine is almost identical to our first version, except
that before executing the RTI instruction, we restore the original contents of
the accumulator from our temporary location instead of from the top of the
stack.
All of this means that when we do eventually execute the RTI instruction,
and the CPU reloads the status register with the byte on the top of the stack,
the interrupt mask bit will now contain a logic 0, thereby preventing any
future interrupts from being seen. You can test this by modifying the original
program to reflect our changes, saving it to irupt_2.asm, assembling it, and
loading the resulting irupt_2.ram file into the RAM as before. When you run
this new version, the dual display begins to count as usual. Similarly, when
you click the interrupt icon, the interrupt service routine counts down from
$0F to $00 on the solo display, and then returns control to the main
program. However, nothing will happen if you click the interrupt icon again,
because our machinations have caused the interrupt service routine to leave
the interrupt mask containing a logic 0. Before moving on, break out of the
program by clicking the Beboputer’s Rst (Reset) button.
Finally, let’s suppose that we wish to be able to respond to a second
interrupt whilst we’re already in the process of responding to an interrupt.
Interrupts and Interrupt Handling D-25
The easiest way to explain this is to look at an example assembly program
as follows:
# Yet another program to test the Beboputer’s interrupt
# capability. This one supports a nested interrupt
SOLO7SEG: .EQU $F022 # Define addr for solo display
DUAL7SEG: .EQU $F023 # Define addr for dual display
EIGHTLED: .EQU $F020 # Define addr of 8-bit LED display
# Here’s the main body of the program
.ORG $4000 # Specify the program’s origin
BLDSP $4FFF # Load the stack pointer
BLDIV SERVICEA # Load the first interrupt
# vector
SETIM # Enable interrupts
LDA $00 # Load accumulator with zero
MAINLOOP: STA [DUAL7SEG] # Store accumulator to dual
# display
INCA # Increment the accumulator
JMP [MAINLOOP] # Jump back and continue
# Here’s the first interrupt service routine
SERVICEA: PUSHA # Push accumulator onto the stack
BLDIV SERVICEB # Load the second interrupt vector
SETIM # Re-enable interrupts
LDA $0F # Load accumulator with decimal 15
SUBLOOPA: STA [SOLO7SEG] # Store accumulator to solo
# display
DECA # Decrement the accumulator
JNN [SUBLOOPA] # If accumulator >= 0 loop back
CLRIM # Disable interrupts
BLDIV SERVICEA # Reload the first interrupt
# vector
POPA # Retrieve the original value
# in the accumulator off the
# stack
RTI # Return to the main program
# This is the end of the first interrupt service routine
# Here’s the second interrupt service routine
SERVICEB: PUSHA # Push accumulator onto the stack
LDA $0F # Load accumulator with decimal 15
SUBLOOPB: STA [EIGHTLED] # Store accumulator to 8-bit
# display
DECA # Decrement the accumulator
JNN [SUBLOOPB] # If accumulator >= 0 loop back
POPA # Retrieve the original value
# in the accumulator off the stack
RTI # Return to first service routine
# This is the end of the second interrupt service routine
.END # End of the program
D-26 Bebop BYTES Back
Now this is going to require a little thought, but it’s really not too difficult.
As usual, the main body of the program uses a BLDIV instruction to load the
interrupt vector with the start address of our first interrupt service routine,
which we’ve called SERVICEA in this program. The main body of the
program then uses a SETIM instruction to enable the CPU to see an interrupt
request, and it then starts to count on the dual 7-segment display.
When an interrupt occurs, the CPU responds as usual, by placing a copy of
the program counter and status register onto the stack, clearing the interrupt
mask to disable any future interrupts, copying the current contents of the
interrupt vector into the program counter, and handing control off to the
SERVICEA interrupt service routine.
The first action in the SERVICEA routine is to push the current contents of
the accumulator onto the stack, so that when we eventually return to the
main body of the program it will be able to continue counting from the
point at which it left off. However, we now come to something interesting,
in that we use a new BLDIV instruction to reload the interrupt vector with
the start address or our second interrupt service routine, which we’ve called
SERVICEB. This is followed by a new SETIM instruction which re-enables
the CPU’s ability to see an interrupt request.
Let’s assume for the moment that no additional interrupt request is
forthcoming (that is, no one clicks the interrupt icon until we return to the
main body of the program), then SERVICEA will count down from $F to $0
on the solo 7-segment display as before. Once this count has finished, we
use a CLRIM instruction to clear the interrupt mask followed by a BLDIV to
re-load the interrupt vector with the start address of SERVICEA. Finally, we
use a POPA to retrieve the original value of the accumulator off the stack
and an RTI to return to the main body of the program.
Now let’s consider what will happen if an interrupt request does occur
while we’re in the process of executing SERVICEA (assuming that it occurs
after the BLDIV and SETIM instructions). In this case, the CPU will respond
by placing a copy of the program counter and status register onto the
current top of the stack, clearing the interrupt mask to disable any future
interrupts, copying the current contents of the interrupt vector into the
program counter, and handing control off to the SERVICEB routine.
Interestingly enough, our first task upon entering SERVICEB is to push the
current contents of the accumulator onto the stack, so that when we
eventually return to the SERVICEA routine it will be able to continue its
count from the point at which it left off. Next we perform a simple count
Interrupts and Interrupt Handling D-27
sequence using our 8-bit LED display
(just to show that we’re doing Important:Note that the reason we
something), then we pop the accumulator use theCLRIM bef BLDIV and
ore the
off the stack and use an RTI to terminate POPA in theSERVICEA routine is to
prevent the CPU from responding to
SERVICEB and return us to SERVICEA,
an interrupt while we’re preparing to
which will continue from the point at return to the main body of the
which it was interrupted. program. or
F example, consider what
All of this can seem a little convoluted at would happen if we omitted the
CLRIM, and an interrupt occurred
first, so it may be easier to understand
while we’re in the middle of executing
when you see it actually happening. Use theBLDIV. In this case, the CPU would
the assembler to enter our latest program, complete theBLDIV, and then respond
save it to irupt_3.asm, assemble it, and to the interrupt requestmping
by to
ju
load the resulting irupt_3.ram file into the SERVICEA whilst still
it’s SERVICEA
in
RAM as before. Remember that this new (not that there would be anything
program requires the use of our 8-bit LED terribly wrong with this if it was wha
display, so use the output ports form to we were trying to do, but it’s not).
add this display to our ensemble.
When you first run this latest version of the program, the dual 7-segment
display starts counting as before. Click once on the interrupt icon, which
causes the main program to hand control to SERVICEA, which performs its
count sequence on the solo display and then hands control back to the main
program.
Now here’s the clever part. Click the interrupt icon to invoke the SERVICEA
routine, but this time, as soon as the solo display has commenced its count,
click the interrupt icon again. This causes SERVICEA to pass control to
SERVICEB, which performs its count sequence on the 8-bit display before
returning control to SERVICEA, which in turn completes its actions before
handing the baton back to the main program.
This concept of enabling a second interrupt to be attended to whilst another
interrupt is already in the process of being serviced is referred to as nested
interrupts. Note that the example above only showed a simple case of
nesting, but we could extend this such that SERVICEB enabled another
routine called SERVICEC, which in turn enabled SERVICED, and so forth.
D-28 Bebop BYTES Back
Appendix
E
TheASCII and EBCDIC
Character Sets
E-2 Bebop BYTES Back
Contents of Appendix E
The ASCII character set .................................................................................................................... E-3
The chunky graphics character set ............................................................................... E-6
The EBCDIC character set ............................................................................................................ E-8
TheASCII and EBCDIC Character Sets E-3
The ASCII character set
During the course of this book, we have repeatedly encountered the
American Standard Code for Information Interchange (ASCII) (pronounced
“ass-key”) character set, particularly in regard to our discussions on the
QWERTY keyboard and the memory-mapped display. This character set is
summarized below, followed by illustrations showing the patterns we used
to realize these characters for our memory-mapped display.(1) The meaning
of the special control codes $00 through $1F are detailed in Chapter 10,
while Chapter 11 explains the strange pattern we use to display these
characters on the screen.
$00 NUL $10 DLE $20 SP $30 0 $40 @ $50 P $60 ` $70 p
$01 SOH $11 DC1 $21 ! $31 1 $41 A $51 Q $61 a $71 q
$02 STX $12 DC2 $22 " $32 2 $42 B $52 R $62 b $72 r
$03 ETX $13 DC3 $23 # $33 3 $43 C $53 S $63 c $73 s
$04 EOT $14 DC4 $24 $ $34 4 $44 D $54 T $64 d $74 t
$05 ENQ $15 NAK $25 % $35 5 $45 E $55 U $65 e $75 u
$06 ACK $16 SYN $26 & $36 6 $46 F $56 V $66 f $76 v
$07 BEL $17 ETB $27 ' $37 7 $47 G $57 W $67 g $77 w
$08 BS $18 CAN $28 ( $38 8 $48 H $58 X $68 h $78 x
$09 HT $19 EM $29 ) $39 9 $49 I $59 Y $69 i $79 y
$0A LF $1A SUB $2A * $3A : $4A J $5A Z $6A j $7A z
$0B VT $1B ESC $2B + $3B ; $4B K $5B [ $6B k $7B {
$0C FF $1C FS $2C , $3C < $4C L $5C \ $6C l $7C |
$0D CR $1D GS $2D - $3D = $4D M $5D ] $6D m $7D }
$0E SO $1E RS $2E . $3E > $4E N $5E ^ $6E n $7E ~
$0F SI $1F US $2F / $3F ? $4F O $5F _ $6F o $7F DEL
1
Don’t forget that you have the ability to create your own character set for the memory-
mapped display, as described in Chapter 13.
E-4 Bebop BYTES Back
2
Once again, don’t forget that you have the ability to create your own character set for the
memory-mapped display, as described in Chapter 13.
TheASCII and EBCDIC Character Sets E-7
Note that the remaining characters occupying codes $D8 through $FF have
identical patterns to those shown for codes $D4 through $D7 above. We
used this distinctive pattern to distinguish these characters from spaces, in
order to make them easy to recognize when they appear on your memory-
mapped display. As we have created all of the chunky graphics that are of
interest to us, we left these codes free for you to create your own characters
(as discussed in Chapter 13).
3 ETX TM c l t C L T 3
4 PF RES BYP PN d m u D M U 4
5 HT NL LF RS e n v E N V 5
2nd hex digit
6 LC BS ETB UC f o w F O W 6
2nd hex digit
8 CAN h q y H Q Y 8
9 EM i r z ` I R Z 9
C
A SMM CC SM
CENT
! :
3
Someone (who shall remain nameless) once told the authors that, for various unfathomable
reasons, there were several versions of EBCDIC, but the methods of translating among them
were kept a tip-top secret by IBM for decades.
E-10 Bebop BYTES Back
Contents of Appendix F
Backus-Naur notation ........................................................................................................................... F-3
Formal syntax summary ..................................................................................................................... F-4
Names ............................................................................................................................................................... F-5
Text, comments, and blank lines .............................................................................. F-5
Literals ................................................................................................................................................................ F-6
Expressions .................................................................................................................................................. F-7
Instruction mnemonics ............................................................................................................. F-8
Instruction statements ............................................................................................................... F-9
Directive statements ................................................................................................................ F-10
File structure ......................................................................................................................................... F-11
Reserved words ......................................................................................................................................... F-12
Directive keywords ..................................................................................................................... F-12
Instruction keywords ................................................................................................................. F-12
Special keywords .......................................................................................................................... F-12
The Beboputer’s Assembly Language F-3
Backus-Naur notation
The Beboputer’s assembly language is defined using style of notation
known as Backus-Naur. This allows us to represent the language using a
combination of syntactic entities and meta-syntactic symbols, where the
meta-syntactic symbols are used to define how the syntactic entities can be
combined. An individual syntactic definition is composed of a name in
italic font, followed by the definition symbol ‘≡‘ (meaning “is defined as”),
followed by the definition itself; for example:
alpha_char ≡ ‘A’ through ‘Z’ and ‘a’ through ‘z’
num_char ≡ ‘ 0’ through ‘9’
label_char ≡ alphanum_char | _
Literals
bin_num_char ≡ ‘0’ through ‘1’
hex_num_char ≡ ‘0’ through ‘9’ and ‘A’ through ‘F’ (or ‘a’ through ‘f’)
Expressions
arithmetic_operator ≡ + | – | * | / {Add, Subtract, Multiply, Divide}
logical_operator ≡ & | | |^ {AND, OR, Exclusive-OR}
Instruction mnemonics
implied_mnemonic ≡ CLRIM | SETIM | INCA | DECA | INCX |
DECX | HALT | NOP | PUSHA | POPA |
PUSHSR | POPSR | SHL | SHR | ROLC |
RORC | RTI |RTS
Instruction statements
instruction_statement ≡ implied_instruction | immediate_instruction
| big_immediate_instruction
| absolute_instruction | indexed_instruction
| indirect_instruction
| preindexed_indirect_instruction
| indirect_postindexed_instruction
big_immediate_instruction ≡ [address_label:]
big_immediate_mnemonic
integer_expression [ comment]
preindexed_indirect_instruction ≡ [address_label:]
preindexed_indirect_mnemonic
[[integer_expression , X]]
[ comment ]
indirect_postindexed_instruction ≡ [label:]
indirect_postindexed_mnemonic
[[integer_expression ] , X]
[comment]
In the absence of a comment, any instruction can have trailing
horizontal_whitespace characters. Also, every instruction is terminated by a
vertical_whitespace character.
An integer_expression in an instruction_statement may employ forward-
referencing. That is, these expressions may employ both constant_labels
and address_labels (and, of course, literal values).
The ‘X’ character in the indexed, preindexed indirect, and indirect
postindexed instruction statements is a special keyword that is understood
by the assembler to refer to the index register, thereby causing the assembler
to select the appropriate opcode for that instruction (see also the “Reserved
words” section at the end of this appendix).
Directive statements
origin_statement ≡ .ORG integer_ref [comment]
end_statement ≡ .END [comment]
declaration_statement ≡ constant_label: .EQU integer_expression
[commen t]
reserve_statement ≡ [address_label:] ( .BYTE | .2BYTE | .4BYTE)
[*integer_expression | integer_expression
{, integer_expression} ] [comment]
An integer_expression in a declaration_statement may not employ any
forward-referencing, but may only employ previously declared integer_refs.
To put this another way, these expressions may only employ literal values or
previously declared constant_labels.
The Beboputer’s Assembly Language F-11
A “.BYTE” statement without an associated operand will reserve a single
byte of memory for the program’s future use. By comparison, a “.BYTE *n”
will reserve ‘n’ bytes, where ‘n’ can either be a literal value (for example,
“.BYTE *10”, which will reserve ten bytes), or an integer expression
(for example, “.BYTE *(FRED + 3)”, which, assuming FRED equals 7,
will also reserve ten bytes).
By default, any locations set aside by reserve_statements will be initialized
by the assembler to contain zero values. Alternatively, they may be
explicitly initialized as part of the statement; for example, the statement
“.BYTE 2, 32, 14, 42” will cause the assembler to reserve four bytes and
initialize these bytes with the values 2, 32, 14, and 42, respectively.
Additionally, any of the values in this comma-separated list could be
full-blown integer_expressions.
The “.2BYTE” and “.4BYTE” versions of reserve_statements work in a similar
way to their “.BYTE” counterpart, except that (not surprisingly) they reserve
two and four bytes, respectively. For example, the statement “.2BYTE $A42”
will reserve a single 2-byte field and initialize that field to contain $0A42
(the assembler will automatically zero-extend the value to fit the 2-byte
field). Note that our assembly language is based on a “big-endian”
approach, in that multi-byte numbers are stored with their most-significant
byte in the lowest address.(1)
File structure
declaration_section ≡ {declaration_statement | blank_line |
comment }
1
See Chapters 12 and 15 for further discussions of the “big-endian” and “little-endian”
concepts.
F-12 Bebop BYTES Back
Reserved words
Although all of the reserved words are shown below in uppercase, they are
in fact case-insensitive. Our assembler internally converts all characters to
uppercase for the purposes of its machinations, and will therefore consider
reserved words such as add, Add, and ADD to be identical.
Directive keywords
.ORG .END .EQU .BYTE .2BYTE .4BYTE
Instruction keywords
ADD ADDC AND BLDSP BLDX BLDIV BSTSP BSTX
CLRIM CMPA DECA DECX HALT INCA INCX JC
JNC JN JNN JO JNO JZ JNZ JMP
JSR LDA NOP POPA POPSR PUSHA PUSHSR ROLC
RORC RTI RTS SETIM SHL SHR STA SUB
SUBC XOR
Special keywords
The only special keyword is ‘X’, which is used when an instruction is
employing one of the indexed addressing modes. There is no theoretical
reason why a label could not be named ‘X’, or indeed given the same name
as any of the instruction keywords. However, in practice, prohibiting such
label names reduces confusion on the part of both the user and the
assembler.
Appendix
G
Assembly Language
Subroutines
G-2 Bebop BYTES Back
Contents of Appendix G
General-purpose subroutines ............................................................................................... G-3
Subroutine label naming conventions .................................................................... G-3
16-bit Addition ( _ADD16 ) ......................................................................................................... G-5
16-bit Subtraction ( _SUB16 ) ............................................................................................... G-9
8-bit Unsigned Multiplication ( _UMULT8 ) .................................................... G-12
8-bit Signed Multiplication ( _SMULT8 ) ............................................................ G-19
16-bit Unsigned Multiplication ( _UMULT16 ) ............................................. G-26
16-bit Signed Multiplication ( _SMULT16 ) ..................................................... G-30
16-bit Unsigned Division ( _UDIV16 ) ....................................................................... G-35
16-bit Signed Division ( _SDIV16 ) ............................................................................... G-44
Tradeoffs involved in using subroutines ............................................................. G-48
Can you beat our subroutines? .................................................................................... G-49
Assembly Language Subroutines G-3
General-purpose subroutines
The concept of subroutines was first introduced in Chapter 8, and these little
rascals have reappeared sporadically throughout subsequent chapters. But
the examples we discussed during the course of the book have primarily
been “one-of-a-kind” subroutines that we created on the fly to address
particular requirements for specific programs. To remedy this situation, this
appendix presents a small selection of general-purpose subroutines that are
applicable to a wide variety of programs.
For your delectation and delight, we’ve created the routines introduced
below and delivered them with the Beboputer. As we discussed in
Chapter 12, you can insert these routines into your programs by means of
the Beboputer’s assembler (or indeed, any editor of your choice). If you
have a modem and can access the Internet, you can also use the Web
button on the Beboputer’s toolbar to bounce over to our Web pages.
Amongst myriad other goodies on these pages, you will also find additional
subroutines that we’ll be creating over the course of time. Also, if you feel
moved to create any interesting programs or subroutines of your own, you
can email the assembly source code to us at [email protected]. If we agree
that your offerings will be of general interest, we will gladly add them to our
web pages for other Beboputer users to peruse and download (see also the
“Subroutine label naming conventions” section below).
Note that it will be to your advantage to wander through the following
subroutines sequentially – starting at the beginning and strolling through the
middle until you reach the end. The reason for this is that we’ve written
copious notes for the earlier examples, but we’ve tried to avoid repeating
the same discussions for identical points in later examples.
Finally, remember that nothing comes for free in this world. Although
subroutines can be incredibly useful, there are always compromises
involved in their deployment. So once you’ve waded through the examples,
it would be well worth your while to spend a few moments glancing at the
“Tradeoffs involved in using subroutines” section at the end of this
appendix.
The main beauty of this scheme is that, now that you know it, you can use
any labels you like in your own subroutines without fear that they’ll clash
with ours, so long as your labels don’t commence with “_AA_”, “_AB_”,
“_AC_”, through “_ZZ_”, which shouldn’t unduly restrict your creative
potential. In fact the only thing that we ask is that, if you do decide to email
one of your subroutines to us for possible inclusion on our web pages, then
please make sure that all of its labels have the same four characters,
Assembly Language Subroutines G-5
irrespective of what those characters may be. This will allow us to quickly
modify your labels to adopt the next unique code in our sequence.
Last but not least, you should note that similar problems can occur when
using higher level languages, and that the solution with modern systems is
to limit the scope of names by declaring whether they’re local (only
available to a particular module) or global (available to multiple modules).
(See also the discussions on the concepts of scope and namespaces in the
“Linking, loading, and relocatable code” section of Chapter 12.)
Remember that the stack “grows” in the direction of address $0000. Thus,
every time we PUSH a value onto the stack, that value is stored at the
location pointed to by the stack pointer, which is then modified
(decremented) to point to the next free location. Similarly, every time we
POP a value off the stack, the stack pointer is first modified (incremented) to
point to the last piece of data that was stored on the stack, and this value is
then copied into the appropriate register.
Assembly Language Subroutines G-7
Also remember that when the main program calls the subroutine, the
Beboputer automatically stores the return address on the top of the stack
and modifies the stack pointer accordingly. Thus, when we enter our
subroutine, the two bytes at the top of the stack will contain the return
address $4019, which happens to be the location of the first POPA
instruction following the JSR (“jump to subroutine”).
Finally, note that popping a value off the stack doesn’t physically obliterate
that value, which is why we’ve shown these old values as persisting in
Figure G.1b. Of course, this raises an interesting question, which is:
“Why does the return address of $4019 now appear on the stack twice?”
Take a few moments to see whether you can solve this brain-teaser before
plunging into the body of the subroutine as follows:
##################################################################
# Name: _ADD16 #
# #
# Function: Adds two 16-bit signed or unsigned numbers together #
# and returns a 16-bit signed or unsigned result. #
# #
# Entry: Top of stack #
# Most-significant byte of return address #
# Least-significant byte of return address #
# Most-significant byte of first number #
# Least-significant byte of first number #
# Most-significant byte of second number #
# Least-significant byte of second number #
# #
# Exit: Top of stack #
# Most-significant byte of result #
# Least-significant byte of result #
# #
# Modifies: Accumulator #
# #
# Size: Program = 41 bytes #
# Data = 5 bytes #
##################################################################
Note that we reserve the locations for our temporary variables _AA_RADD,
_AA_NUMA, and _AA_NUMB at the end of the subroutine where they won’t
get in the way.
Also note that when we add the least-significant bytes of our numbers
together we use an ADD instruction, because this automatically forces a
logic 0 to be used for the “carry” into this addition. By comparison, when
we add the most-significant bytes of our numbers together we use an ADDC
instruction, because this employs the value stored in the carry flag (which
was set by the first addition) as the “carry” into this addition.
Remember that the stack pointer “grows” toward address $0000 and
dwindles in the opposite direction. This means that “pushing” a value onto
the stack causes the stack pointer to decrement to point to the next free
location, while “popping” a value off the stack causes the stack pointer to
increment to point to the previous location.
This means that once we’ve retrieved and stored the six bytes of data at the
beginning of the subroutine (the two return address bytes and the four data
bytes), the stack pointer is left pointing to the same location that it was
before the main (calling) program placed the data on the stack in the first
place. During the process of performing the addition, the subroutine stores
the 2-byte result on the stack. Finally, the subroutine retrieves the return
Assembly Language Subroutines G-9
address from its temporary location, places it on the stack, and then returns
to the main program.
The RTS instruction, which terminates the subroutine, automatically
retrieves the return address from the stack and increments the stack pointer
by two bytes, leaving it in the right position for the main program to be able
to retrieve the result. (All of this explains why the subroutine’s exit condition
illustrated in Figure G.1 shows two copies of the return address on the
stack.)
Pay attention to the way in which we commented this subroutine. Some
programmers might consider this to be over-enthusiastic, while others might
feel that we were on the stingy side. We feel that this is a pretty reasonable
median level that you should aim at if you expect people to be able to
understand whatever it is you are trying to do.
After the subroutine has done its minor magic, we can retrieve the 16-bit
result ($512C in this example) from the stack and do whatever we wish with
it. See also the discussions on _ADD16 above for more details on the way
the stack is used for both of these subroutines.
##################################################################
# Name: _SUB16 #
# #
# Function: Subtracts two 16-bit signed or unsigned numbers #
# and returns a 16-bit signed or unsigned result. #
# #
# Entry: Top of stack #
# Most-significant byte of return address #
# Least-significant byte of return address #
# Most-significant byte of first number } Sub this No.#
Assembly Language Subroutines G-11
# Least-significant byte of first number } from 2nd No.#
# Most-significant byte of second number #
# Least-significant byte of second number #
# #
# Exit: Top of stack #
# Most-significant byte of result #
# Least-significant byte of result #
# #
# Modifies: Accumulator #
# #
# Size: Program = 41 bytes #
# Data = 5 bytes #
##################################################################
00 01 01 10 01110011 22 x 115
- - - - - - - - 0 0 0 1 0 1 1 0
- - - - - - - 0 0 0 1 0 1 1 0 -
- - - - - - 0 0 0 0 0 0 0 0 - -
Partial - - - - - 0 0 0 0 0 0 0 0 - - -
products - - - - 0 0 0 1 0 1 1 0 - - - -
- - - 0 0 0 1 0 1 1 0 - - - - -
- - 0 0 0 1 0 1 1 0 - - - - - -
- 0 0 0 0 0 0 0 0 - - - - - - -
0 0 0 0 1 0 0 1 1 1 1 0 0 0 1 0 2350
2530
16-bit result
To illustrate the situation, consider the partial product associated with bit 4
of the multiplier (remember that we number bits from right to left starting
at 0) (Figure G.4).
A number of instructions
00000001 01100000 would be required to
create these two bytes
Don’t worry if you find the above to be a bit mind-boggling at first – after a
while you’ll find that this sort of “wheels-within-wheels” thinking starts to
come naturally (and this would be the time to start worrying). So with these
words of comfort, let’s proceed to our subroutine , which retrieves two 8-bit
numbers from the stack, multiplies them together, and places the 16-bit
result on the top of the stack. The subroutine only works for unsigned
numbers (see also _SMULT8 later on), and assumes that any 16-bit values
are stored in the usual Beboputer style with the most-significant byte
“on top” of the least-significant byte. Before considering _UMULT8 itself,
let’s first examine an example of a program that might call this subroutine:
# The following program declares two 8-bit unsigned numbers called
# VALUEA and VALUEB. The program sets things up in such a way
# that,when it calls the subroutine, VALUEA is multiplied by
# VALUEB
.ORG $4000 # Start of program is address $4000
VALUEA: .BYTE $16 # First 8-bit number ( 22 in
# decimal)
VALUEB: .BYTE $73 # Second 8-bit number (115 in
# decimal)
ANSWER: .2BYTE # Save a 16-bit field for the result
# Set everything up so that the
# subroutine will multiply VALUEA
# by VALUEB
LDA [VALUEB] # Load ACC with VALUEB and push it
PUSHA # onto the stack
LDA [VALUEA] # Load ACC with VALUEA and push it
PUSHA # onto the stack
JSR [_UMULT8] # Jump to the “_UMULT8” subroutine
# and multiply VALUEA by VALUEB (the
# code for the subroutine would be
# somewhere else in the program)
POPA # Retrieve the MS byte of the result
STA [ANSWER] # from the stack and store it
POPA # Retrieve the LS byte of the result
STA [ANSWER+1] # from the stack and store it
: : :
Before calling the subroutine, we first have to place the two 8-bit numbers
that we want to multiply together on the stack ($16 and $73 in this
example). Note that the order in which the numbers are put on the stack
doesn’t particularly matter in the case of a multiplication (Figure G.7).
After the subroutine has performed its curious machinations, we can retrieve
the 16-bit result ($09E2 in this example) from the stack and do whatever we
wish with it. Alternatively, we could leave it on the stack and use it as one
Assembly Language Subroutines G-17
of the values to be passed into another subroutine, such as the _ADD16 we
introduced earlier.
0 1 Multiplexer 0 1 Multiplexer
Multiplier array
0 1 Multiplexer
Final result
After the subroutine has done its clever turn, we can retrieve the 16-bit
signed result ($F61E in this example) from the stack and do whatever we
Assembly Language Subroutines G-23
wish with it. Alternatively, we could leave it on the stack and use it as one
of the values to be passed into another subroutine, such as the _ADD16 we
introduced earlier.
##################################################################
# Name: _SMULT8 #
# #
# Function: Multiplies two 8-bit signed numbers (in the range #
# -127 to +127) and returns a 16-bit signed result. #
# #
# Entry: Top of stack #
# Most-significant byte of return address #
# Least-significant byte of return address #
# First 8-bit number (multiplicand) #
# Second 8-bit number (multiplier) #
# #
# Exit: Top of stack #
# Most-significant byte of result #
# Least-significant byte of result #
# #
# Modifies: Accumulator #
# Index register #
# #
# Size: Program = 128 bytes #
# Data = 6 bytes #
##################################################################
_SMULT8: BLDX 9 # Load the index register with 9,
# which equals the number of times
# we want to go around the loop +1
POPA # Retrieve MS byte of return
STA [_AD_RADD] # address from stack and store it
POPA # Retrieve LS byte of return
STA [_AD_RADD+1] # address from stack and store it
POPA # Retrieve multiplicand from stack
STA [_AD_MAND] # and store it
POPA # Retrieve multiplier from stack
STA [_AD_RES+1] # and store it in LS byte of result
LDA 0 # Load the accumulator with 0 and
STA [_AD_RES] # store it in the MS byte of result
####
#### Invert input values if necessary and load the output flag
####
_AD_TSTA: LDA [_AD_MAND] # Load the multiplicand and save
STA [_AD_FLAG] # it to the flag
JNN [_AD_TSTB] # If multiplicand is positive then
# jump to ‘_AD_TSTB’, otherwise ..
XOR $FF # ..invert the contents of the ACC
INCA # ..add 1 to ACC
STA [_AD_MAND] # ..store now-positive multiplicand
G-24 Bebop BYTES Back
_AD_TSTC part of the routine, we can use a JNN (“jump if not negative”)
instruction, which only considers the state of the sign bit.
2
You should not actually modify our subroutine, because any programs that use it would
become confused. The correct approach would be to make a copy of our subroutine, give it
a new name, modify the names of all of the labels, and then modify the size of the value
returned by the subroutine.
Assembly Language Subroutines G-27
# Set everything up so that the
# subroutine will multiply VALUEA
# by VALUEB
If we did wish to return all 4 bytes of the result, all we would have to do
would be to add two more pairs of LDA and PUSHA instructions in the
_AE_SAVE section of the subroutine (just before we push the return
address onto the stack).
3
Again, you should not actually modify our subroutine, because any programs that use it
would become confused. The correct approach would be to make a copy our subroutine,
give it a new name, modify the names of all of the labels, and then modify the size of the
value returned by the subroutine.
Assembly Language Subroutines G-31
with the most-significant byte “on top” of the least-significant byte. Before
considering _SMULT16 itself, let’s examine an example of a program that
might call this subroutine:
# The following program declares two 16-bit unsigned numbers
# called VALUEA and VALUEB. The program sets things up in such a
# way that, when it calls the subroutine, VALUEA is multiplied by
# VALUEB.
.ORG $4000 # Start of program is address $4000
VALUEA: .2BYTE $0056 # 1st 16-bit number ( 86 in decimal)
VALUEB: .2BYTE $0171 # 2nd 16-bit number (369 in decimal)
ANSWER: .2BYTE # Save a 16-bit field for the result
# Set everything up so that the
# subroutine will multiply VALUEA
# by VALUEB
LDA [VALUEB+1] # Load ACC with LS byte of VALUEB
PUSHA # and push it onto the stack
LDA [VALUEB] # Load ACC with MS byte of VALUEB
PUSHA # and push it onto the stack
LDA [VALUEA+1] # Load ACC with LS byte of VALUEA
PUSHA # and push it onto the stack
LDA [VALUEB] # Load ACC with MS byte of VALUEA
PUSHA # and push it onto the stack
JSR [_SMULT16] # Jump to the “_SMULT16” subroutine
# to multiply VALUEA by VALUEB
# (the code for the subroutine
# would be somewhere else in the
# program)
##################################################################
# Name: _SMULT16 #
# #
# Function: Multiplies two 16-bit signed numbers (in the range #
# -32767 to +32767) and returns a 16-bit signed result.#
# #
# Entry: Top of stack #
# Most-significant byte of return address #
# Least-significant byte of return address #
# MS Byte of 1st 16-bit number (multiplicand) #
# LS Byte of 1st 16-bit number (multiplicand) #
# MS Byte of 2nd 16-bit number (multiplier) #
# LS Byte of 2nd 16-bit number (multiplier) #
# #
# Exit: Top of stack #
# Most-significant byte of result #
# Least-significant byte of result #
# #
# Modifies: Accumulator #
# Index register #
# #
# Size: Program = 201 bytes #
# Data = 9 bytes #
##################################################################
_SMULT16: BLDX 17 # Load the index register with 17,
# which equals the number of times
# we want to go around the loop +1
POPA # Retrieve MS byte of return
STA [_AF_RADD] # address from stack and store it
POPA # Retrieve LS byte of return
STA [_AF_RADD+1] # address from stack and store it
POPA # Retrieve MS byte of multiplicand
STA [_AF_MAND] # from stack and store it
POPA # Retrieve LS byte of multiplicand
STA [_AF_MAND+1] # from stack and store it
# Note that the result is 4 bytes in size (_AF_RES+0, +1, +2,
# and +3), where _AF_RES+0 is the most-significant byte
POPA # Retrieve MS multiplier from stack
STA [_AF_RES+2] # and store it in byte 2 of result
POPA # Retrieve LS multiplier from stack
STA [_AF_RES+3] # and store it in byte 3 of result
LDA 0 # Load the accumulator with 0 and
STA [_AF_RES] # store it in byte 0 of result then
STA [_AF_RES+1] # in byte 1 of result
####
#### Invert input values if necessary and load the output flag
####
_AF_TSTA: LDA [_AF_MAND] # Load MS multiplicand and save
STA [_AF_FLAG] # it to the flag
Assembly Language Subroutines G-33
JNN [_AF_TSTB] # If multiplicand is positive then
# jump to ‘_AF_TSTB’, otherwise ..
LDA 0 # ..load the accumulator with 0
SUB [_AF_MAND+1] # ..subtract LS byte of multiplicand
STA [_AF_MAND+1] # ..(no carry in) store result
LDA 0 # ..load the accumulator with 0
SUBC [_AF_MAND] # ..subtract MS byte of multiplicand
STA [_AF_MAND] # ..(yes carry in) store result
_AF_TSTB: LDA [_AF_FLAG] # Load the flag,
XOR [_AF_RES+2] # XOR it with MS byte of multiplier,
STA [_AF_FLAG] # then store the flag again
LDA [_AF_RES+2] # Load MS multiplier into the ACC
JNN [_AF_DUMY] # If multiplier is positive then
# jump to ‘_AF_DUMY’, otherwise ..
LDA 0 # ..load the accumulator with 0
SUB [_AF_RES+3] # ..subtract LS byte of multiplier
STA [_AF_RES+3] # ..(no carry in) store result
LDA 0 # ..load the accumulator with 0
SUBC [_AF_RES+2] # ..subtract MS byte of multiplier
STA [_AF_RES+2] # ..(yes carry in) store result
_AF_DUMY: ADD 0 # Add zero to the accumulator (dummy
# instruction whose sole purpose is
# to set the carry flag to 0)
####
#### Hold tight - this is the start of the main multiplication
#### loop
####
_AF_LOOP: JNC [_AF_SHFT] # If carry=0, jump to start shifting
LDA [_AF_RES+1] # otherwise add 16-bit multiplicand
ADD [_AF_MAND+1] # to the 16 MS bits of the result.
STA [_AF_RES+1] # Add LS 8-bits first and store
LDA [_AF_RES] # Now add MS 8-bits (with carry)
ADDC [_AF_MAND] # and store (note we’re interested
STA [_AF_RES] # in any carry out)
_AF_SHFT: LDA [_AF_RES] # Might not already be loaded
RORC # Rotate the accumulator (MS byte of
# result) 1-bit right. This shifts
# the carry flag into the MS bit and
# also updates the carry flag with
# the bit that “falls off the end”
STA [_AF_RES] # Now store the MS byte of result
# (doesn’t affect the carry flag)
LDA [_AF_RES+1] # Load ACC with next byte of result
# (doesn’t affect the carry flag)
RORC # Rotate this byte of the result
# 1-bit right. This shifts the carry
# flag into the MS bit and also
# updates the carry flag with the
G-34 Bebop BYTES Back
So now we know that the first digit of our result is 4 (from the 4 x 14). Next
we subtract 56 from 66 leaving 10, drop the 2 down to form 102, and go
through the process again, saying: “6 x 14 = 84, but that’s too small;
8 x 14 = 112, but that’s too big; 7 x 14 = 98, and that’s as close as we can
get.” Thus, we now know that the second digit of our result is 7. Finally we
subtract 98 from 102 leaving 5, which is too small to divide by 14
(remember that we’re performing an integer division), so we know that our
result is 47 with a remainder of 5.
The point is that our CPU has to go through a similar process, which means
that it has to iterate on the result by taking a stab in the dark, seeing if it
went too far, and backtracking if it did. To confuse the issue even further,
we sort of end up doing everything backwards, because we use the same
sort of cunning tricks that we employed to make our multiplications easier
for the CPU to handle. To cut a long story short, assume that we have two
16-bit numbers called the dividend and the divisor, and we wish to divide
the former by the latter (Figure G.11).
Add or Subtract
The way in which this works may seem kind of difficult to follow, but
everything will come out in the wash. First, we reserve a 2-byte field in
which to store our divisor and a 4-byte field in which to store our result.
Also, we initialize the most-significant two bytes of the result to contain all
zeros, and we load the least-significant two bytes with our dividend (the
number to be divided). Once we’ve initialized everything, we perform the
following sequence of operations sixteen times:
a) Shift the entire 32-bit result one bit to the left (shift a zero into the
least-significant bit).
Assembly Language Subroutines G-37
b) Subtract the 2-byte divisor from the most-significant 2 bytes of the
result and store the answer back into these 2 bytes.
c) If the carry flag is 0 following step (b), then this indicates a positive
result, which means that the divisor was smaller than the 2 most-
significant bytes if the result. In this case, force the least-significant bit
of the result to 1.
Otherwise, if the carry flag is 1, then this indicates a negative result,
which means that the divisor was bigger than the 2 most-significant
bytes if the result. In this case, leave the least-significant bit of the
result as a 0 (from the shift), add the 2-byte divisor back to the most-
significant 2 bytes of the result, and store the answer back into
these 2 bytes.
Note particularly the case in which the carry flag contains a 1 when we
enter step (c). Every time this occurs, it means that the divisor was too big to
subtract from the portion of the dividend that we’re currently examining.
But we’ve discovered this too late, because we’ve already performed the
subtraction, which means that we have to add the divisor back in. In fact
this is known as a restoring-division algorithm for just this reason; namely,
that we have to keep on restoring things every time we “go too far.” There
are also nonrestoring-division algorithms which are somewhat more
efficient, but also a tad more complicated ...... so we’ll ignore them in the
hope that they’ll go away.
Now, unless you’ve got a size-16 brain (and one of the models equipped
with turbo cooling at that), the above has probably left you feeling
overheated, confused, lost, and alone. Don’t be afraid, because computer
divisions can bring the best of us to our knees. The easiest way to
understand this (or, at a minimum, to convince ourselves that it actually
works as promised) is to examine a much simpler test-case based on 4-bit
numbers. For example, consider how we’d divide 10112 (11 in decimal) by
00112 (3 in decimal). The first step would be to set up our initial conditions
(Figure G.12).
Carry Zero Dividend
Remembering that our thought-experiment is
based on 4-bit numbers, this means that the most-
x 0000 1011
significant 4 bits of what will eventually be our 8-bit
result are set to zero, while our dividend will be 0011 Divisor
loaded into the least-significant 4 bits of the result.
Now consider what happens during the first cycle of Figure G.12: Initial
Conditions
the process (Figure G.13).
G-38 Bebop BYTES Back
0011 0011
(a) Initial conditions at (b) Shift the entire 8-bit
start of this cycle result 1-bit to the left
0011 0011
(c) Subtract divisor from the (d) Add divisor back into
4 MS bits of the result the 4 MS bits of result
Commencing with our initial conditions at the start of this cycle (Figure
G.13a), the first thing we do is to shift the entire 8-bit result one bit to the
left, and also shift a logic 0 into the least-significant bit during the process
(Figure G.13b). Next we subtract the divisor from the 4 most-significant bits
of the result (Figure G.13c), but this sets the carry flag to a logic 1, which
tells us that we’ve gone too far. Thus, we complete this cycle by adding the
divisor back into the 4 most significant bits of the result. As fate would have
it, the second cycle offers another helping of the same thing (Figure G.14).
0011 0011
(a) Initial conditions at (b) Shift the entire 8-bit
start of this cycle result 1-bit to the left
0011 0011
(c) Subtract divisor from the (d) Add divisor back into
4 MS bits of the result the 4 MS bits of result
Once again, the first thing we do is to shift the entire 8-bit result one bit to
the left, and also shift a logic 0 into the least-significant bit during the
process. (Figure G.14b). Next we subtract the divisor from the 4 most-
significant bits of the result (Figure G.14c), but this sets the carry flag to a
logic 1, which tells us that we’ve gone too far. Thus, we complete this cycle
by adding the divisor back into the 4 most significant bits of the result. We
Assembly Language Subroutines G-39
can but hope that the third cycle will do something to break the monotony
(Figure G.15).
0011 0011
(a) Initial conditions at (b) Shift the entire 8-bit
start of this cycle result 1-bit to the left
0011 0011
(c) Subtract divisor from the (d) Set LS bit of result
4 MS bits of the result to logic 1
0011 0011
(a) Initial conditions at (b) Shift the entire 8-bit
start of this cycle result 1-bit to the left
0011 0011
(c) Subtract divisor from the (d) Set LS bit of result
4 MS bits of the result to logic 1
Figure G.16: Fourth and final cycle of our 4-bit division test case
Did you doubt us for a moment? We start off by shifting the 8-bit result one
bit to the left (Figure G.16b) and subtracting the divisor from the 4 most-
significant bits of the result (Figure G.16c). Once again, the carry flag is left
containing a logic 0, which means that the only thing we have to do is to
force the least-significant bit of the result to a logic 1 (Figure G.16d).
G-40 Bebop BYTES Back
4
If you’re feeling frisky with your newfound knowledge, try picking on a passerby and attempt
to explain just how it works!
Assembly Language Subroutines G-41
Before calling the subroutine, we first have to place our 16-bit dividend on
the stack followed by our 16-bit divisor ($7BF6 and $0056, respectively in
this example). Note that the order in which the numbers are put on the
stack is important in the case of a division. Once the subroutine has finished
its task, we can retrieve the least-significant 2 bytes of the 4-byte result from
the stack and do whatever we wish with them (these least-significant 16-bits
will contain $0171 in this example). Alternatively, we could leave this value
on the stack and use it as one of the values to be passed into another
subroutine, such as the _ADD16 we introduced earlier.
###################################################################
# Name: _UDIV16 #
# #
# Function: Divides two 16-bit unsigned numbers (in the range #
# 0 to 65,535) and returns a 16-bit unsigned result. #
# #
# Entry: Top of stack #
# Most-significant byte of return address #
# Least-significant byte of return address #
# MS Byte of 1st 16-bit number (divisor) #
# LS Byte of 1st 16-bit number (divisor) #
# MS Byte of 2nd 16-bit number (Dividend) #
# LS Byte of 2nd 16-bit number (Dividend) #
# #
# Exit: Top of stack #
# Most-significant byte of result #
# Least-significant byte of result #
# #
# Modifies: Accumulator #
# Index register #
# #
# Size: Program = 148 bytes #
# Data = 8 bytes #
##################################################################
_UDIV16: BLDX 16 # Load the index register with 16,
# which equals the number of times
# we want to go around the loop
POPA # Retrieve MS byte of return
STA [_AG_RADD] # address from stack and store it
POPA # Retrieve LS byte of return
STA [_AG_RADD+1] # address from stack and store it
POPA # Retrieve MS byte of the divisor
STA [_AG_DIV] # from the stack and store it
POPA # Retrieve LS byte of the divisor
STA [_AG_DIV+1] # from the stack and store it
# Note that the result is 4 bytes in size (_AG_RES+0, +1, +2,
# and +3), where _AG_RES+0 is the most-significant byte
POPA # Retrieve MS dividend from stack
G-42 Bebop BYTES Back
Unfortunately, our division algorithm will not perform correctly if faced with
negative values, so we have to perform similar tricks to those we used in the
case of our signed multiplication subroutines. Thus, we need to check the
signs of the numbers first, change any negative values into their positive
counterparts using twos complement techniques, perform the division, then
correct the sign of the result if necessary (see also the notes at the end of the
subroutine).
The main body of a program would call this _SDIV16 subroutine in exactly
the same way that we’d call its _UDIV16 cousin, so what say we cut to the
chase and leap directly into the body of the subroutine itself?
##################################################################
# Name: _SDIV16 #
# #
# Function: Divides two 16-bit signed numbers (in the range #
# -32,767 to +32,767): returns a 16-bit signed result #
# #
# Entry: Top of stack #
# Most-significant byte of return address #
# Least-significant byte of return address #
# MS Byte of 1st 16-bit number (divisor) #
# LS Byte of 1st 16-bit number (divisor) #
# MS Byte of 2nd 16-bit number (Dividend) #
# LS Byte of 2nd 16-bit number (Dividend) #
# #
# Exit: Top of stack #
# Most-significant byte of result #
# Least-significant byte of result #
Assembly Language Subroutines G-45
# #
# Modifies: Accumulator #
# Index register #
# #
# Size: Program = 226 bytes #
# Data = 9 bytes #
##################################################################
_SDIV16: BLDX 16 # Load the index register with 16,
# which equals the number of times
# we want to go around the loop
POPA # Retrieve MS byte of return
STA [_AH_RADD] # address from stack and store it
POPA # Retrieve LS byte of return
STA [_AH_RADD+1] # address from stack and store it
6
”If I had more time, I would have written you a shorter letter.” – Blaise Pascal
G-50 Bebop BYTES Back
Appendix
H
Answers to “Quick
Quiz” Questions
H-2 Bebop BYTES Back
Contents of Appendix H
Answers to Quick Quiz #1 ............................................................................................................ H-3
Answers to Quick Quiz #2 ............................................................................................................ H-4
Answers to Quick Quiz #3 ............................................................................................................ H-6
Answers to Quick Quiz #4 ............................................................................................................ H-9
Answers to Quick Quiz #5 ........................................................................................................ H-11
Answers to Quick Quiz #6 ........................................................................................................ H-13
Answers to Quick Quiz #7 ........................................................................................................ H-16
Answers to Quick Quiz #8 ........................................................................................................ H-20
Answers to Quick Quiz #9 ........................................................................................................ H-24
Answers to Quick Quiz #10 ..................................................................................................... H-27
Answers to Quick Quiz #11 ..................................................................................................... H-30
Answers to Quick Quiz #12 ..................................................................................................... H-35
Answers to Quick Quiz #13 ..................................................................................................... H-38
Answers to Quick Quiz #14 ..................................................................................................... H-40
Answers to Quick Quiz #15 ..................................................................................................... H-43
Answers to “Quick Quiz” questions H-3
Quick quiz #1
1) What color socks did some Vikings wear?
From scraps of fabric discovered at archeological digs, it appears that
more than a few Vikings were partial to woolen socks of the bright red
persuasion.
2) When did the term computer originate?
Mathematical tables in the 1800s were created by teams of mathematicians
working day and night on primitive mechanical calculators. Due to the fact
that these people performed computations they were referred to as
computers.
3) What is a general definition of a computer?
In its broadest sense, a computer is a device that can accept information
from the outside world, process that information, make decisions based on
the results of its processing, and then return the information to the outside
world in its new form.
4) What technologies can be used to implement computers?
Computers can theoretically be implemented in almost any technology,
including mechanical, fluidic, and pneumatic variants. However, the
overwhelming majority of today’s computers are electronic, because
electronic components are much smaller and faster than their counterparts
in the other technologies.
5) What is the difference between analog and digital information?
Analog information represents a continuously varying quantity, such as
the brightness of a lamp controlled by a dimmer, while digital information
represents a quantity that can be considered as being in one of a number
of discrete states, such as a light switch which is either ON or OFF.
6) Which devices acted as switches in the first true electronic
computers?
The first truly electronic computers, such as ENIAC, which was constructed
between 1943 and 1946 at the University of Pennsylvania, employed
vacuum tubes in the role of switches.
7) What is a semiconductor and what is the most commonly used
semiconductor?
A semiconductor is a material that can be persuaded to act as both a
conductor and an insulator, which makes it suitable for use in constructing
H-4 Bebop BYTES Back
Quick quiz #2
1) How many deaths does a coward die?
Cowards only physically die a single time, just like the rest of us. The
saying: “A coward dies a thousand deaths – a brave man only once,” refers
to the fact that cowards spend so much time worrying about the possible
outcome of a particular course of action that they suffer defeat in their own
minds long before they actually get around to attempting anything.
2) Which number system is used inside digital computers?
Although some experiments have been performed with tertiary (three-state)
logic, the vast majority of today’s digital computers use the binary number
system, which employs two digits: 0 and 1. Also, any number system
Answers to “Quick Quiz” questions H-5
having a base that is a power of two (2, 4, 8, 16, 32, and so forth) can be
easily mapped into its binary equivalent, and vice versa. For this reason,
humans often use the hexadecimal (base-16) system to represent numbers
inside computers.
3) Which part of the computer makes decisions and performs
logical and arithmetic operations?
The central processing unit (CPU) is where all of the number crunching and
decision making is performed. In the majority of today’s home computers,
the entire CPU is implemented as a single integrated circuit. These single-
chip CPUs are known as microprocessors, and computers based on
microprocessors are called microcomputers.
4) What are the data, control, and address busses used for?
The CPU uses its address bus to point to a location in the memory (or to
other components in the system) from which it wishes to read or write data;
the data bus is used to convey the data; and the control bus is used to
orchestrate operations.
5) Name four different types of information that might be conveyed
by the data bus.
A pattern of logic 0s and 1s can be used to depict anything that we wish it
to represent at any particular time. Thus, values on the data bus may be
used to represent instructions, numbers (or portions thereof), alphanumeric
characters, simple patterns of 0s and 1s, or anything else we desire.
6) What are the clock, ~reset, ~read, and ~write signals used for?
The clock signal is used to synchronize the internal actions of the CPU, and
also to synchronize the actions of the CPU with other units in the system.
(Asynchronous CPUs that do not use clocks have been created for
experimental purposes, but commercial digital computers are almost
universally based on synchronous, or clocked techniques). The ~reset signal
is used to initialize the CPU and the rest of the system (the reason this signal
has a tilde character (“~”) as part of its name is to indicate that its active
state is a logic 0). The CPU generates the ~read and ~write signals, and uses
them to inform other components in the system as to whether it wishes to
read data from them or write data to them.
7) What are ROMs and RAMs used for and what are the major
differences between them?
Read-only memories (ROMs) and random-access memories (RAMs) are
special types of integrated circuits that can store binary data for use by the
H-6 Bebop BYTES Back
Quick quiz #3
1) What did computer memory have to do with ladies knitting?
One of the first forms of computer memory that might be considered to be
reasonably useful by today’s standards was magnetic core memory (also
Answers to “Quick Quiz” questions H-7
known as core store or simply core), which consisted of tiny ferromagnetic
beads threaded onto wires. The early core stores were effectively “knitted”
together by teams of ladies with the appropriate skills, but automated
techniques soon took over.
2) What was magnetic core store and how did it work?
As was noted in the previous question, magnetic core store was an early
form of computer memory which consisted of tiny ferromagnetic beads
threaded onto wires. The wires were arranged as a matrix of “rows” and
“columns,” with a bead at each row-column intersection. If a sufficient
amount of current was passed through a pair of row and column wires, the
bead at the intersection between them would be magnetized. The polarity
of the current determined the direction in which the bead was magnetized,
so it was possible to use each bead to represent a logic 0 or logic 1 value
for later use.
3) What were switch and display panels used for in early computers?
Early computers were not equipped with sophisticated input and output
devices such as typewriter-type keyboards and television-style screens.
Instead, operators used a bank of switches called a switch panel to instruct
the computer as to which operations it was to perform. Similarly, a display
panel of flashing lights was one of the main techniques for the operator to
see what was actually going on inside the computer.
4) What does a RAM contain when power is first applied to a
computer system?
Modern semiconductor RAM devices are volatile, which means that any
data they contain is lost when power is removed from the system. When
power is re-applied to the system, these devices initialize containing
random logic 0 and 1 values. Thus, any meaningful data stored inside a
RAM must be written into it by the computer after the system has been
powered up.
5) What is a hard disk?
A hard disk is a bulk-storage device that can be used to store a large
amount of data relatively cheaply. This form of media maintains its data
when power is removed from the system, so it is said to be non-volatile.
The hard disk in a home computer consists of a circular disk a few inches in
diameter, which is covered with a magnetic material and which is spun at
high speed. The hard disk unit also contains special read/write heads
(similar in concept to the record/playback heads in a music cassette
recorder). These read/write heads can move across the surface of the disk,
H-8 Bebop BYTES Back
record data onto the disk, and recover it later. A home computer can use its
hard disk to store hundreds of millions of bytes (megabytes) of information.
6) What are flowcharts and what are they used for?
Flowcharts are a graphical technique for describing a sequence of
operations; they do this by charting the flow, or passing of control, from
one operation to the next. Flowcharts are extremely useful for documenting
the operation of a computer program and they provide an excellent means
of communicating the intent of a program, but although they are strong on
“what” and “how,” they are a little weaker on “when” and “why.”
7) What is a program?
The term “program” refers to a sequence of logical, arithmetic, and related
operations that are to be performed by a computer in order to solve a
computational problem or accomplish some other task. The term may be
used to refer to the source code specified in some programming language,
and also to the resulting machine code that is executed by the computer.
8) What do you understand by the terms “opcode” and “operand”
at this stage in our discussions?
The first byte of each instruction is called the opcode, which is an
abbreviation of “operation code.” The opcode instructs the CPU as to the
type of operation it is to perform. Any additional data bytes associated with
an opcode are referred to as the operand. (Note that this answer assumes a
simple computer, such as the Beboputer, which is based on an 8-bit data
bus and 8-bit instructions).
9) What were the main tasks performed by a monitor routine?
In the context of the discussions in Chapter 3, a monitor routine
(or program) would be used to read the state of the switch panel, write
values to the lights on the display panel, load data from the switch panel
into specified memory locations, and eventually hand control over to
another program.
10)Where would you expect to find an accumulator and what does
it do?
The accumulator, which is a general-purpose register inside a CPU, is used
to gather, or accumulate intermediate results.
Answers to “Quick Quiz” questions H-9
Quick quiz #4
1) Who invented the accordion (and why)?
The accordion was invented in 1829 by the British physicist and
inventor Sir Charles Wheatstone, but we may only speculate as to why
he unleashed this monster on an unsuspecting world (unless he was a
secret devotee of Polka music).
2) What were the main reasons for the widespread use of paper
tapes and punched cards in the 1960s and 1970s?
Computer designers needed some mechanism for storing and
transporting programs and data. Paper tapes and punched cards offered
a reliable, low-cost solution.
3) Can you spot any interesting features associated with the subset
of Morse Code shown in Table 4.1?
If you stare at this table long enough you can spot any number of
interesting things, some of which may even be meaningful. But perhaps
the most obvious point is that the characters that are used most
frequently tend to have the shortest codes. For example, the most
commonly used letter in the English language is ‘E’, whose equivalent
Morse Code is a single dot.
4) When was paper tape first used for the preparation, storage,
and transmission of data?
In 1857, only twenty years after the American inventor Samuel Finley
Breese Morse developed his Morse Telegraph, Sir Charles Wheatstone
introduced the first application of paper tapes as a medium for the
preparation, storage, and transmission of telegraph data. Sir Charles’
paper tape used two rows of holes to represent Morse’s dots and dashes.
Outgoing messages could be prepared off-line on paper tape and
transmitted later.
5) What were the major features of an IBM 1-inch paper tape?
Well it’s certainly safe to say that one of the IBM 1-inch paper tape’s
major features was the fact that it was one inch wide. Data was stored
on the tape by punching rows of holes across the width of the tape. The
pattern of the holes in each data row represented a single data value or
character. The individual hole positions forming the data rows were
referred to as channels or tracks, and IBM’s 1-inch tapes supported eight
channels, with 0.1 inches between the holes.
H-10 Bebop BYTES Back
Quick quiz #5
1) What is the average life span of any electronic product in an
environment containing small children?
Unfortunately, there’s no empirical data on this subject, so we have to rely
on personal experience, which indicates that the average life-span of any
article in an environment containing small children is typically measured in
hours if you’re lucky.
2) What are diodes and what do they do?
A diode is an electronic component that has two terminals and that only
conducts electricity in one direction. The first diodes were created using
vacuum tubes, but these were eventually superseded by semiconductor
equivalents.
3) What is the main difference between standard diodes and LEDs?
Standard diodes and light-emitting diodes (LEDs) function in a similar
manner, in that they both conduct electricity in only one direction. The
main difference between these devices is that LEDs emit light when they are
conducting. Another difference is that a LED must be packaged in a
material that is transparent to the wavelength of light that it emits (otherwise
we couldn’t see it glow, which would sort of defeat the point of the whole
thing).
4) What are the main differences between incandescent light bulbs
and LEDs?
Incandescent light bulbs are comparatively large, power-hungry, and tend
to have relatively limited life-spans, while LEDs are small, energy-efficient,
and exceptionally durable.
5) Why did early digital calculators and watches use red LEDs as
opposed to other colors?
Depending on the materials used, it is possible to create LEDs that emit red,
green, yellow, orange, and, most recently, blue light. The red ones are the
cheapest and the easiest to make, while the blue ones are comparatively
H-12 Bebop BYTES Back
rare and expensive. In addition to being cheap and easy to make, red LEDs
were the first to become commercially available, which is why all of the
original calculators and digital watches used them.
6) In the case of our simple 8-bit displays, why did we use red and
green LEDs to represent logic 0 and logic 1 values respectively?
There’s no particular reason for us to have used red and green LEDs where
we did – we could have swapped them over or even exchanged them for
different colors. It’s simply that, when we designed our output displays, we
took a vote and decided that logic 0s and logic 1s would be represented by
red and green, respectively.
7) Are 7-segment displays always preferable to simple 8-bit displays?
No. Your choice of display depends on the information you’re trying to
convey to the observer. If you desire to present simple ON/OFF conditions,
then 8-bit displays usually have the edge, but 7-segment displays tend to be
preferably if your intent is to communicate numerical data.
8) What do the terms “common anode” and “common cathode”
mean in the context of diodes?
Diodes have two terminals, which are called the anode and the cathode.
When we’re using a group of LEDs to create a device such as a 7-segment
display, we might connect all of their anodes to a common power supply
and use individual signals to drive their cathodes, in which case this would
be referred to as a common anode configuration. By comparison, if all of
the cathodes terminals are connected to a common supply, the LEDs are
said to be in a common cathode configuration.
9) The single decoded 7-segment display
out_data[3:0] Hex dec_out_data[6:0]
we used in lab 3 was said to be based
0 0 0 0 0 1 0 0 0 0 0 0
on a common cathode configuration; 0 0 0 1 1 1 1 1 1 0 0 1
0 0 1 0 2 0 1 0 0 1 0 0
hence the truth table shown in Figure 0 0 1 1 3 0 1 1 0 0 0 0
5.17. Create the corresponding truth 0 1 0 0 4 0 0 1 1 0 0 1
0 1 0 1 5 0 0 1 0 0 1 0
table for a single decoded 7-segment 0 1 1 0 6 0 0 0 0 0 1 0
0 1 1 1 7 1 0 1 1 0 0 0
display based on a common anode 1 0 0 0 8 0 0 0 0 0 0 0
configuration (Figure H.1). 1 0 0 1 9 0 0 1 1 0 0 0
1 0 1 0 A 0 0 0 1 0 0 0
1 0 1 1 B 0 0 0 0 0 1 1
1 1 0 0 C 1 0 0 0 1 1 0
1 1 0 1 D 0 1 0 0 0 0 1
1 1 1 0 E 0 0 0 0 1 1 0
1 1 1 1 F 0 0 0 1 1 1 0
Quick quiz #6
1) Why is it preferable to view digital logic in terms of primitive
gates as opposed to transistors?
Complex digital functions, such as microprocessors, can contain a huge
number of transistor-based switches, and it would be almost impossible to
design or comprehend a system represented at this level of abstraction. The
solution is to view things at higher levels of abstraction, the first such level
being the primitive logic gates NOT, AND, NAND, OR, NOR, XOR, and
XNOR.
2) What does a NOT gate do and how does it do it?
A NOT gate, which has a single input and output, is constructed in such a
way that a logic 0 presented to its input will enable or disable the transistors
forming the gate so as to cause the output to drive a logic 1; similarly, a
logic 1 on the input will cause a logic 0 on the output.
3) What does a bobble on the output of the symbol for a primitive
gate indicate?
A small circle (referred to as a bobble or bubble) on the output of the
symbol for a primitive logic gate indicates that this is an inverting function;
that is, the output from such a function is the logical negation of that
function’s non-inverting counterpart.
4) What is the difference between an AND gate and a NAND
gate?
An AND gate is the logical counterpart of a NAND gate and vice versa. In
the case of an AND, the output of the gate only drives a logic 1 value if all
of the inputs are logic 1; if any of the inputs are logic 0, the output is also
H-14 Bebop BYTES Back
We can coerce an AND gate to act like a NAND by inverting its output with
a NOT. Similarly, a NAND can be caused to act like an AND by inverting
its output with a NOT. If the gates are implemented using the CMOS
technology, a NAND gate will require fewer transistors than an AND; for
example, 2-input NAND and AND gates require 4 and 6 transistors,
respectively.
5) Describe the similarities and differences between decoders and
multiplexers.
Both multiplexers and decoders have control inputs, and applying a binary
value, or address, to these inputs causes each of the functions to perform a
certain action. In the case of a multiplexer, the control inputs select
between a number of data inputs, and convey the logical value on the
selected input to the function’s output. By comparison, a decoder doesn’t
have any data inputs; instead, the control signals are used to select between
a number of outputs, and to assert the selected output by driving it to its
active logic value.
6) Create the symbol and truth table for a 3:8 decoder with active-
high outputs (Figure H.3).
2:4 Decoder
y[7]
111 select[2:0] y[7:0]
y[6]
110 0 0 0 0 0 0 0 0 0 0 1
y[5] 0 0 1 0 0 0 0 0 0 1 0
101
y[4] 0 1 0 0 0 0 0 0 1 0 0
select[2:0] 100 0 1 1 0 0 0 0 1 0 0 0
y[3] 1 0 0 0 0 0 1 0 0 0 0
011
y[2] 1 0 1 0 0 1 0 0 0 0 0
010 1 1 0 0 1 0 0 0 0 0 0
y[1] 1 1 1 1 0 0 0 0 0 0 0
001
y[0]
000
9) What is a tri-state buffer used for and what does a logic Z value
represent?
A tri-state buffer is similar to its standard counterpart, except that it has an
additional control input. When the buffer is enabled it conveys whatever
signal is presented to its data input through to its output, while disabling the
gate means that it is effectively disconnected from its own output. The result
is that the outputs from multiple tri-state buffers can be used to drive the
same wire, so long as only one of the buffers is enabled at any particular
time. In the case of a computer system, tri-state buffers act as interfaces
between the data bus and the devices that drive it. With regards to the
logic Z, this isn’t a real value at all. Unlike logic 0s and logic 1s, which are
H-16 Bebop BYTES Back
c a b y c a b y
OR 0 0 0 0 OR 0 0 0 1
a a
y 0 0 1 1 y 0 0 1 0
b | 0 1 0 1 b | 0 1 0 0
0 1 1 1 0 1 1 0
c c
1 1 1 Z 1 1 1 Z
Quick quiz #7
1) Why do digital computers use the binary number system?
Digital computers are constructed from logic gates that can represent only
two states; thus, they are obliged to employ the binary number system,
which employs only two digits: 0 and 1. Note that there have been
experiments with logic gates based on three voltage levels, which therefore
employ the tertiary (base-3) number system, but systems based on these
techniques have never become commercially viable or available. Also note
that the high-impedance Z is typically not considered in the context of this
question (see also the answer to Quiz #6, Question 9).
2) Why are numbers in a computer different from numbers written
on a piece of paper?
Numbers written on paper (in any number system) can be as large as one
wishes. By comparison, numbers within a computer have to be mapped
onto a physical system of logic gates and wires. Thus the maximum value of
an individual numerical quantity inside a computer is dictated by the width
of the computer’s data bus; that is, by the number of bits available to
describe that value.
3) What is the difference between unsigned and signed binary
numbers?
Unsigned binary numbers can only be used to represent positive values,
while the signed binary format can be used to represent both positive and
negative values.
Answers to “Quick Quiz” questions H-17
4) What range of numbers can be represented by a 4-bit field if it
(a) represents an unsigned quantity and (b) represents a signed
quantity?
If an n-bit field is used to portray an unsigned binary number, it can
represent values in the range 0 through (2n - 1), so a 4-bit field can represent
values of 0 through 15. By comparison, if the n-bit field is used to portray
signed binary numbers, it can represent values in the range -2(n - 1) through
(2(n -1) - 1), so a 4-bit field can represent values of -8 through +7.
5) What is the two’s complement of 001101012?
One way to generate the two’s complement of a binary number is to invert
all of the bits and to then add 1 to the result. As an alternative, we can
commence with the least-significant bit of the value to be complemented,
copy each bit each bit up to and including the first 1, and then invert the
remaining bits. Using either of these techniques reveals that the two’s
complement of 001101012 is 110010112 (Figure H.6).
MSB LSB
Value to be
0 0 1 1 0 1 0 1
complemented
Copy from LSB up to
and including the first 1
- - - - - - - 1
Wires Wires
0 0000 02 = 0 10 0 0000 02 = 0 10
0 0000 12 = 1 10 0 0000 12 = 1 10
0 0001 02 = 2 10 0 0001 02 = 2 10
: : : : : :
0 1111 02 = 30 10 0 1111 02 = 30 10
0 1111 12 = 31 10 0 1111 12 = 31 10
1 0000 02 = 32 10 1 0000 02 = −32 10
1 0000 12 = 33 10 1 0000 12 = −31 10
1 0001 02 = 34 10 1 0001 02 = −30 10
: : : : : :
1 1111 02 = 62 10 1 1111 02 = −2 10
1 1111 12 = 63 10 1 1111 12 = −1 10
Wires Wires
Quick Quiz #8
1) If we were designing a computer that could only support two
addressing modes, which ones would we choose?
We would chose the implied and absolute addressing modes. Actually, we
wouldn’t have a lot of choice in the case of the implied mode, because this
mode is fundamental to the way in which instructions like HALT and INCA
work. Although the immediate mode is certainly useful the absolute mode
can easily replace it, but the reverse isn’t true. Similarly, modes such as
indexed and indirect definitely have their advantages, but we can finagle
our way around them using the absolute mode.
2) Is there any particular reason why instructions such as ADD and
SUB don’t support the indirect, pre-indexed indirect, and indirect
post-indexed addressing modes?
In the case of the Beboputer, the main reason these instructions don’t
support these modes is that its designers (the authors of this book) decided
not to bother. To be more precise, the authors decided not to offer these
instruction/addressing mode combinations to emphasize the fact that CPU
designers can dictate which instructions and modes will be supported and
which won’t.
3) Why doesn’t the Beboputer support NOT, NAND, NOR, and
XNOR instructions?
a) To keep the Beboputer’s instruction set as small and concise as
possible.
b) Generally speaking these instructions tend to be used less frequently
than their counterparts (such as AND and OR), which are included in
the Beboputer’s instruction set.
c) The functionality of these instructions can be replicated using
combinations of other instructions, and doing so is educational
in its own right.
Answers to “Quick Quiz” questions H-21
4) In our discussions on the extended ALU, we forced the multiplexer
in the negator block to select $FE (-2 in decimal) for a DECA; is
there another way in which we could have implemented this
instruction?
Yes. If you refer to Figure 8.34, you’ll see that the DECA instruction causes
the multiplexer generating the Ciadd signal to feed a logic 1 value into the
adder function’s carry-in input. Thus, the resulting operation is equivalent to
accumulator + (-2) + 1, which boils down to accumulator -1.
As an alternative, we could have forced the multiplexer in the negator block
to select $FF (-1 in decimal), and caused the multiplexer generating the
Ciadd signal to feed a logic 0 value into the adder function’s carry-in input.
In this case, the resulting operation would have been equivalent to
accumulator + (-1), which once again boils down to accumulator -1.
The reason we didn’t use this alternative approach was that we found it
aesthetically pleasing to gather the ADD and INCA instructions into one
group and the SUB and DECA instructions into another (insofar as
controlling the multiplexer generating the Ciadd signal). This also gave us
the advantage of causing the SUB and DECA instructions to behave in a
similar manner, thereby providing consistency to the discussions on these
instructions.
5) Which instructions would be most affected if the Beboputer’s
instruction set also included SETC (“set carry flag to 1”) and
CLRC (“clear carry flag to 0”) instructions?
The instructions that would be most affected would be ADD and SUB,
because we would no longer need them! For example, the main reason we
have an ADD is to allow us to implicitly force the carry-in input to the adder
function to a logic 0 when performing an addition, but we could replace the
ADD with a CLRC followed by an ADDC. Similarly, the main reason we
have an SUB is to allow us to implicitly force the carry-in input (acting in
the role of a borrow in this case) to the adder function to a logic 1 when
performing a subtraction, but we could replace the SUB with a SETC
followed by an SUBC.
6) Describe two or more ways in which we might augment or
enhance the Beboputer’s CMPA (“compare accumulator”)
instruction.
The existing CMPA instruction sets the carry flag to logic 1 if the value in
the accumulator is greater than the value we’re comparing it to, and it sets
the zero flag to a logic 1 if the two values are equal. This means that
H-22 Bebop BYTES Back
determining whether the value in the accumulator is less than the value
we’re comparing it to requires us to perform two tests: one on the carry flag
followed by one on the zero flag (or vice versa). Thus, one way in which
we could enhance the CMPA instruction would be to set another flag (say
the overflow flag) to a logic 1 if the value in the accumulator is less than the
value we’re comparing it to.
Also, the existing CMPA instruction assumes that both of the values being
compared represent unsigned binary numbers. Thus, we could augment this
instruction (or, more precisely, we could augment the Beboputer’s
instruction set) by providing two versions, such as CMPAU (“compare
accumulator unsigned”) and CMPAS (“compare accumulator signed”).
7) Why do we use certain status flags to represent multiple conditions
(for example, the zero flag is also used to indicate equality in the
case of the CMPA instruction)?
Providing individual flags for every type of condition we wish to capture
would increase the complexity of the CPU’s physical implementation. Also,
we would have to add more conditional jump instructions (two for each
flag: “jump if condition is true” and “jump if condition is false”), which
would also increase the complexity of the CPU.
8) Describe a way in which we might enhance the Beboputer’s SHR
(“shift accumulator right”) instruction.
One way in which we could enhance the SHR instruction would be to
allow ourselves to specify by how many bits the accumulator should be
shifted. To illustrate this, consider two techniques by which we could
extend our instruction set:
Method #1 Method #2 Comment
SHR1 or SHR 1 # Shift accumulator 1 bit right
SHR2 or SHR 2 # Shift accumulator 2 bits right
SHR3 or SHR 3 # Shift accumulator 3 bits right
SHR4 or SHR 4 # Shift accumulator 4 bits right
: : :
Method #1 would require us to add a whole slew of new mnemonics, while
method #2 allows us to append the number of bits to be shifted to our
original three-letter mnemonic (this technique would be the preferred
approach). Irrespective of the way in which we choose to represent these
instructions, and assuming that we want these instructions to continue to
use the implied addressing mode, each shift (1 bit, 2 bits, 3 bits, and so
forth) would require its own unique opcode.
Answers to “Quick Quiz” questions H-23
Alternatively, we might decide to modify the CPU such that the SHR
instruction used the immediate addressing mode. In this case, the SHR
opcode would now be followed by an operand byte to specify the number
of bit-positions to be shifted. Furthermore, if we do decide to allow the
SHR instruction to use immediate addressing, then we could also extend it
to support other modes, such as absolute and indirect addressing.
9) What are the advantages and disadvantages of the ROLC and
RORC instructions rotating the accumulator through the carry
flag?
The advantage of rotating through the carry flag is that this greatly facilitates
multi-byte operations. The main disadvantage is that it makes life awkward
if we simply want the accumulator to “wrap-around” on itself. In fact many
CPUs also support ROL and ROR instructions, which do cause the
accumulator to “wrap around” (the bit that ”falls off the end” would still be
copied into the carry flag). Fortunately, we can achieve the same effect as
an ROL or ROR by performing a ROLC or RORC, testing the state of the
carry flag, and then using an AND or OR instruction to clear or set the
appropriate bit in the accumulator. Although this is somewhat ungraceful,
life would be a whole lot worse if the Beboputer only supported ROL and
ROR instructions, because finagling these to achieve the same ends as their
ROLC and RORC counterparts is really painful. If you don’t believe us, you
can easily try it out for yourself. Pretend that you only have ROL and ROR
instructions (no ROLC or RORC), and then see how much fun you have
trying to implement the _UMULT16 subroutine in Appendix G.
10)How is it possible for us to use the value in the accumulator to
drive the ALU, to perform an operation using the ALU, and to
store the result back in the accumulator without messing
everything up?
This is possible because all of the functional blocks forming the CPU have
some amount of internal delay. This means that it takes a finite amount of
time for the signals to propagate through the ALU and back to the
accumulator’s inputs. Thus, when we load the result from an operation
back into the accumulator, it can store this data before the act of changing
its contents ripple through the system and reappear at its inputs.
H-24 Bebop BYTES Back
Quick quiz #9
1) What are the advantages and disadvantages of the hex keypad
compared to the switch and display panels?
The only advantages of the switch and display panels are that they’re
conceptually very simple to understand, and they map almost directly onto
the way in which the computer processes information internally. In almost
every other respect the hex keypad trounces them soundly: it’s faster, easier,
and more efficient to enter data using a keypad, and its also easier to detect
errors, because we find it difficult to relate to binary data in the form
employed by the switch and display panels.
2) How many diodes would be required to encode the hex keypad’s
buttons (excluding the ON/OFF and Reset buttons) using the
mapping described in Chapter 9?
From Figure 9.3 we know that the mapping for the buttons is as follows:
Button Binary Value Button Binary Value Button Binary Value
‘0’ 00000000 ‘8’ 00001000 Address 00010000
‘1’ 00000001 ‘9’ 00001001 Data 00100000
‘2’ 00000010 ‘A’ 00001010 Clear 00110000
‘3’ 00000011 ‘B’ 00001011 Enter 01000000
‘4’ 00000100 ‘C’ 00001100 Step 10000000
‘5’ 00000101 ‘D’ 00001101 Run 11000000
‘6’ 00000110 ‘E’ 00001110
‘7’ 00000111 ‘F’ 00001111
From Figure 9.4 we can see that we require a diode at every intersection
between row and column wires at which we wish to introduce a logic 0.
Thus, we require a diode for each ‘0’ in the binary values above, which
gives us a total of 136 diodes.
3) What are the advantages of using hierarchical flowcharts?
Hierarchical flowcharts allow us to represent programs at a high level of
abstraction and to partition them into manageable “chunks”. Representing a
program in this way facilitates its implementation, and also makes it easier
to understand and modify in the future.
4) Why did we choose to load the stack pointer with $4FFF at the
beginning of the program?
This was a purely arbitrary decision. We could have initialized the stack
pointer to point to almost anywhere in the RAM portion of the memory map
(as long as we ensure that there is no chance of the data we eventually put
Answers to “Quick Quiz” questions H-25
on the stack overwriting our program). In this particular case, our program
commenced at location $4000, so we just decided to initialize the stack
pointer to the end of this 4K block of RAM at address $4FFF.
5) In the “Initializing the Variables” section of the program, we noted
that it wasn’t strictly necessary to initialize the variable in memory
location $4004. Why is this the case?
Locations $4002, $4003, and $4004 are used to store the number of
“hundreds,” “tens”, and “ones”, respectively. The way in which this
particular program is written requires us to initialize the “hundreds” location
to zero, because the program uses this value as a starting point when it
extracts the number of “hundreds” (similarly for the “tens” location).
Whatever value remains after we’ve extracted the “hundreds” and “tens” is
loaded directly into the “ones” location, thereby overwriting its existing
contents, so the initial value of this location is immaterial.
6) Describe how a JNC (“jump if not carry”) instruction works.
The JNC instruction tests the value in the carry flag, which may have been
cleared to a logic 0 or set to a logic 1 during the course of a previous
instruction. If the carry flag contains a 0, then the JNC (“jump if not carry”)
causes the CPU to jump to the address specified in the operand bytes;
otherwise the CPU skips the operand bytes and proceeds to the next
instruction.
7) When we were extracting the “hundreds” and the “tens,” we
compared the value in the accumulator to 99 and 9, respectively.
Why didn’t we compare the value in the accumulator to 100 and
10, respectively?
Consider the case of the “hundreds”. What we wish to do is determine if
the value in the accumulator is greater than or equal to 100. The most
appropriate instruction available to us is the CMPA, which compares the
value in the accumulator to another value in memory. CMPA loads the carry
flag with a logic 1 if the value in the accumulator is the larger, or it loads
zero flag with a logic 1 if the two values are equal. Thus, if we compared
the value in the accumulator to 100, we would potentially have to perform
two tests: one on the carry flag to see if the accumulator contained a value
greater than 100 and, if this failed, a second test on the zero flag to see if
the value in the accumulator was equal to 100. However, if we compare
the value in the accumulator to 99, the carry flag will be set to logic 1 if the
accumulator’s contents are greater than 99, which is another way of saying
“greater than or equal to 100” (which is the condition we’re really testing
H-26 Bebop BYTES Back
for). A similar argument applies to the case of the “tens”; by comparing the
value in the accumulator to 9, the carry flag will be loaded with logic 1 if
the accumulator’s contents are greater than or equal to 10.
8) What would you expect to happen if you click on “Big Red’ while
the Beboputer is speaking a number?
Whenever you click Big Red it will become active, irrespective of whether
the Beboputer is speaking a number or not. The fact that Big Red has been
activated will be stored in the latch associated with this switch, thereby
causing Big Red to light up. However, activating Big Red won’t affect the
Beboputer until the next time the CPU happens to read a value from Big
Red’s input port.
9) Why is it easier to map from binary to hexadecimal than it is to
map from binary to decimal?
The hexadecimal (base-16) number system has a base that is a power of two
(16 = 24). Any number system having a base that is a power of two (2, 4, 8,
16, 32, and so on) can be easily mapped into its binary equivalent, and vice
versa. By comparison, mapping between binary and a number system
whose base is not a power of two (such as decimal) requires more effort.
10)The programs in Chapter 9 assumed that the 8-bit switches
represented unsigned binary numbers in the range 010 through
25510. How would you approach rewriting these programs such
that they treated the 8-bit switches as representing signed binary
numbers in the range -12810 through +12710?
The easiest way to do this would be to modify the portion of the program
that reads and stores the value on the 8-bit switch input device. When we
load the value from the switches into the accumulator, the negative flag will
be set if the most significant bit in the accumulator is a logic 1, thereby
indicating a negative value (assuming that we’re considering the switches to
represent signed binary numbers). In this case, we could use a temporary
location to store the fact that we have a negative number, and we could
then convert the value in the accumulator into its two’s complement. Thus,
the accumulator always ends up containing a positive value, and the tasks
of extracting the “hundreds”, “tens”, and “ones” would remain unchanged.
The act of displaying the number would also remain unchanged insofar as
using the solo display to represent the “hundreds” and the dual display to
represent the “tens” and “ones”. However, we would also have to employ
our undecoded 7-segment device to represent the minus sign for any
negative numbers (Figure H.10).
Answers to “Quick Quiz” questions H-27
Undecoded 7-segment
Thus, the only real modification to Solo 7-segment
the display routine would be to Dual 7-segment
we would first check our temporary Figure H.10: Modifying the program to
location to decide whether or not handle signed binary values
to say the work “minus” before speaking the rest of the number.
Using a Dvorak keyboard, the typist’s fingers spend 70% of their time on
the home row and 80% of this time on their home keys. Thus, as compared
to the approximately 120 words that can be constructed from the home row
keys of the Sholes keyboard, it is possible to construct more than 3,000
words on Dvorak’s home row. Also, Dvorak’s scheme reduces the motion
of the hands by a factor of three, improves typing accuracy by
approximately 50%, and typing speed by up to 20%.
Thus, the only advantage that can really be claimed for the Sholes keyboard
is that there are a lot of them about and everyone is used to this layout,
which means that it would be expensive and time-consuming to replace all
of the existing keyboards and retrain everybody.
4) What does “ASCII” stand for?
ASCII stands for the American Standard Code for Information Interchange.
5) Why is ASCII said to be a 7-bit code?
Standard ASCII only defines 128 codes for alphabetic, numeric,
punctuation, and special control characters, which means that we only
require a 7-bit field to represent these codes.
6) What are the ASCII codes for ‘A’, ‘a’, ‘Z’, and ‘z’ in hexadecimal,
decimal, and binary?
Character Hexadecimal Decimal Binary
‘A’ $41 65 01000001
‘a’ $61 97 01100001
‘Z’ $5A 90 01011010
‘z’ $7A 122 01111010
7) What is the difference between even parity and odd parity?
Parity generation and checking is one of the simplest techniques that can be
used for error detection. This form of checking requires the presence of an
extra bit called the parity bit to be associated with the field under
consideration. For example, in the case of a 7-bit ASCII code, the most-
significant bit in the 8-bit byte can be used to represent the parity bit. In the
case of even parity, special logic is used to count the number of logic 1s in
the field under consideration. If there are an even number of logic 1s, the
parity bit would be loaded with a logic 0, but if there were an odd number
of logic 1s, the parity bit would be loaded with a logic 1. The end result of
an even parity check is that there are always an even number of logic 1s in
the final value, which comprises both the original field and the parity bit.
(By comparison, an odd parity check ensures that there are always an odd
number of logic 1s in the final value).
Answers to “Quick Quiz” questions H-29
8) What does the term “sticky” mean in the context of a key on a
computer keyboard?
A “sticky” key is one that toggles back and forth between two modes of
operation. For example, pressing the CAPS key causes it to become active,
and it will remain active until it is pressed again. By comparison, a non-
sticky key, such as the SHIFT key, is only active as long as the operator
continues to hold it down, and it automatically returns to its inactive state
as soon as it is released.
9) How can we modify our program such that, irrespective of the
state of the CAPS and SHIFT keys, whenever an alpha key is
pressed the program will always display the uppercase code for
that character (for example, if the user clicks on ‘a’, the code for
‘A’ will be displayed)?
As we know, the ASCII code for an ‘A’ is $41, and the codes increase
sequentially up to $5A for ‘Z’. Similarly, the ASCII code for ‘a’ is $61, and
the codes increase sequentially up to $7A for ‘z’. Thus, whenever we read a
code from the keyboard into the accumulator, all we need to do is to test
the value in the accumulator to see if it’s both greater than or equal to $61
and less than or equal to $7A. If the value is in this range, then we know
that we have a lower-case letter. In this case, we can subtract $20 from the
value in the accumulator to generate the ASCII code for the upper-case
version of the letter, and then write our new code to the dual 7-segment
displays.
10)How could we write a program to cause our virtual keyboard to
act like a Dvorak keyboard?
This is similar to the last question, but it’s a little bit trickier. Consider the
illustrations of the Sholes and Dvorak keyboards shown in Figures 10.1 and
10.2, respectively. If we press the number ‘2’ on the Beboputer’s Sholes-
style keyboard, it will (not surprisingly) generate the ASCII code for a ‘2’,
but we actually want to end up with the ASCII code for a ‘7’, which is the
corresponding character on the Dvorak keyboard. Thus, whenever we find
the ASCII code for a ‘2’ in our accumulator, we’re going to have to replace
it with the code for a ‘7’, and similarly for all of the other keys on our
keyboard. One way in which we could achieve this is to perform a
laboriously sequence of tests as follows (assume that we’ve just loaded the
accumulator with a value from the keyboard, and note that the syntax
shown here is more fully discussed in Chapter 12):
H-30 Bebop BYTES Back
With the exception of the first pattern, each curve is formed by taking four
“shrunk-down” copies of the preceding pattern, rotating them by various
amounts, and linking them with three connecting lines (the first pattern is a
special case, but it could be regarded as four “empty” patterns linked by
three straight lines).
Why is this of interest? Well we could use a similar recursive pattern to
describe the path our electron beam follows as it wends its way across the
screen. Also, it has been suggested that similar techniques would provide
the means to upgrade our computer and television equipment in the future.
For example, suppose that a new television were developed that supports
four times the number of pixels as existing sets. This would in turn require
television signals to contain four times the information, which will
necessitate higher frequencies and so forth. But what about all of the
H-32 Bebop BYTES Back
people who had the old television sets, and who therefore don’t have
enough pixels to display all of this data. The idea is that the new television
signals could be based on the next highest order of the recursive pattern,
which essentially means that the beam covers four times the pixels in the
same area. Existing users would only require a new receiver/decoder
module that would perform some simple interpolation algorithm to filter the
data so as to be suitable for the lower-resolution screen. (Note that none of
the above is commercially available, it’s just an idea).
4) Describe the difference between the additive, subtractive, and
psychological primary colors.
Primary colors are those which can be combined to form all of the other
colors. In the case of light, the primary colors are red, green, and blue.
Adding different colored lights together increases the components of the
spectrum that reach our eyes, so these are referred to as the additive
primaries. By comparison, the primary colors for pigments are yellow,
magenta, and cyan. Adding different colored pigments together decreases
the components of the spectrum that reach our eyes, so these are referred to
as the subtractive primaries. It is also common to refer to red, yellow,
green, blue, white, and black as being the psychological primaries,
because we subjectively and instinctively believe that these are the basis
for all of the other colors.
5) Summarize the main features of a memory-mapped display?
A memory-mapped display regards the screen as consisting of rows and
columns of “boxes,” where each box can contain a character. The patterns
of dots for each character are stored in a special character ROM device, and
the display’s controller uses ASCII codes stored in the main system’s
memory to select the particular characters of interest in the character ROM.
Memory-mapped displays are extremely efficient in terms of their memory
requirements, and are still employed in such applications as bank teller
machines. However, this technique find little use in modern computers,
which require the ability create sophisticated graphics and to control each
pixel on an individual basis.
6) Why did the early memory-mapped displays commonly support
80 columns of characters?
Because one of the main techniques for storing data at that time was by
means of IBM punched cards, which supported 80 columns of holes (or 80
characters of information, depending on your point of view). The designers
of the first computer terminals certainly didn’t want to display fewer
Answers to “Quick Quiz” questions H-33
characters than were on an IBM punched card, and there didn’t seem to be
any major advantage to displaying more characters than were on a card.
7) Assuming that the video card is in its graphics mode and using
its default base address for this mode, how would you calculate
the video RAM address for the graphic block in the mth column
of the nth row?
a) We know that the default base address of our video RAM in its
graphics mode (the address of the block at row 0 column 0) is $E400
b) We know that we have forty-eight rows numbered from 0 through 47,
which means that the first row is actually referred to as row 0, the
second row as row 1, the third row as row 2, and so on.
c) We know that each row contains sixty-four columns numbered from 0
through 63, which means that the first column is actually referred to
as column 0, the second column as column 1, and so on.
d) So, we can quickly calculate the address of column 0 on any row ‘n’
using the formula: address = base address of video RAM + (‘n’ x $40),
where $40 is the hexadecimal equivalent of 64 in decimal:
Row (‘n’) Formula Resulting address
0 $E400 + (0 x $40) = $E400 (row 0 col 0)
1 $E400 + (1 x $40) = $E440 (row 1 col 0)
2 $E400 + (2 x $40) = $E480 (row 2 col 0)
3 $E400 + (3 x $40) = $E4C0 (row 3 col 0)
: : :
e) Similarly, for any column ‘m’, we know that the address of that
column is going to be equal to the address of column 0 on that row
plus ‘m’. Once again, it’s easy to see how this works by looking at the
following examples for the third row (row 2):
Column (‘m’) Formula Resulting address
0 $E480 + 0 = $E480 (row 2 col 0)
1 $E480 + 1 = $E481 (row 2 col 1)
2 $E480 + 2 = $E482 (row 2 col 2)
3 $E480 + 3 = $E483 (row 2 col 3)
4 $E480 + 4 = $E484 (row 2 col 4)
5 $E480 + 5 = $E485 (row 2 col 5)
: : :
H-34 Bebop BYTES Back
no
Have we looped no
Write color block to Is the random
back to our original =$01? White?
target address color white?
random number?
yes yes
a) We use the index register to point to both the next character in the
phrase and the next free location on the memory mapped display.
b) There’s no particular reason why we used two .BYTE statements to
separate the words onto two separate lines in the source code (except
that it looked nicer this way).
c) We have to include $20 codes wherever we want a space to appear.
d) The way this program works requires us to terminate the phrase with
the code for a NULL character, $00, which allows us to perform an
easy test to determine whether or not we’ve reached the end of the
message.
Contents of Appendix I
Restating the problem ........................................................................................................................ I-3
Reading numbers and displaying them at the same time ......... I-4
Converting numbers into internal representations ................................... I-8
Scrunching 16-bit numbers and displaying them ................................... I-15
The main body of the program ......................................................................................... I-23
Solution for Calculator Sign-Off Project I-3
Output port
(address $F027)
From Beboputer
7 8 9 /
4 5 6 *
To Beboputer
1 2 3 -
(a) Input Port (to Beboputer) (b) Output Port (from Beboputer)
Figure I.2: Calculator codes
Our mission is to write a program that uses the calculator keypad to add,
subtract, multiply, and divide numbers. For the sake of simplicity, we’ve
decided that this calculator is only required to work with numbers in the
range -32,767 through +32,767.
Write no
?First?
number
Once we’ve initialized the display, we enter a loop which waits for a key to
be pressed on the calculator’s keypad. When a key is pressed, the first thing
we do is to check whether it is the ‘Clr’ key and, if it is, we jump back to
the start of the program and re-initialize the display (remember we’re only
interested in the ‘0’ through ‘9’ and ‘Clr’ keys in this simple test case).
Alternatively, if the key represents a decimal digit, we perform a second test
to determine whether or not it is the first digit in
our new number. If it is the first digit, we clear the Initial
value
display again (to rid ourselves of the ‘0’ that’s
currently there) and write the digit, otherwise we 2
simply write the digit. To illustrate this process in a
slightly different way, consider the sequence of 4
events that we wish to occur if we enter the
number 2467, then click the ‘Clr’ key, then enter 6
the number 42 (Figure I.4).
7
From our flowchart we know that the first thing we
want our program to do is clear the display and Clr
write ‘0’ to it, so this will be our initial value. We
then want our program to loop around waiting for 4
a digit to be pressed (‘2’ in this example). As this is
the first digit in our number, our program should 2
clear the display (so as to lose the ‘0’) and then
writes the ‘2’ to it. The program should continue Figure I.4: Reading and
to loop around reading and writing digits until it displaying numbers
sees a ‘Clr’, at which point we want it to clear the display and write a ‘0’
again. And so we find ourselves back where we started; that is, with an
initial value of ‘0’, waiting for a new number to be entered.
Note that our flowchart does not include any tests to determine whether the
numbers we’re entering are legal (less than or equal to 32,767). Thus, if we
I-6 Bebop BYTES Back
were to enter the number 99,999, our program simply wouldn’t realize that
there was a problem. Similarly, even though we know that our display can
only contain five digits and an optional minus sign, our program would
happily allow us to enter a number like 653,482,997. In this case the
program would merrily keep on reading and writing the digits, which would
eventually start to “fall off” the left-hand side of the display. It would
certainly be possible to add tests to the program and make the display
“beep” if there were a problem, but we’ll leave that as an exercise for the
reader (you). Instead, we’ll just concentrate on the basic program, which
could look something like the following:
CLR_KEY: .EQU $10 # Code associated with ‘Clr’ key
CLR_CODE: .EQU $10 # Code to clear the display
####
#### This is the start of subroutine ‘GET_NUM’, which keeps on
#### reading keys from the input port and writing them to the
#### display until it sees a control code (anything greater than
#### ‘9’)
GET_NUM: LDA 1 # Load ACC with ‘1’ and write it
# to a location called ‘FLAG’,
STA [FLAG] # which will tell us if this
# is the first digit (‘0’ = NO,
# ‘1’ = YES)
GN_LOOP: LDA [IN_PORT] # Read a value from the input port
JN [GN_LOOP] # If the most significant bit is
# ‘1’,then no key pressed, so jump
# back to ‘GN_LOOP’ and try again
Solution for Calculator Sign-Off Project I-7
You may wish to compare this code to our flowchart and satisfy yourself that
they match up. First we use some .EQU directives to assign values to
constant labels that will make our program easier to read. The reset
sequence following the .ORG loads the stack pointer with $4FFF (we have to
initialize the stack pointer if we want to use a subroutine ...... and we do),
clears the calculator’s display and writes the number ‘0’ to it, then calls the
subroutine GET_NUM.
This subroutine kicks itself off by writing a value of ‘1’ into a 1-byte location
called FLAG that we reserve at the end of the program; we’re going to use
this flag to tell us whether or not we’re looking for the first digit of a
I-8 Bebop BYTES Back
number. Next we start to loop around at GN_LOOP waiting for the user to
press a key. We can save a little effort here, because we don’t have to use a
CMPA instruction to test for a value of $FF (which indicates that no key has
been pressed). Instead, all we have to do is test whether the most-significant
bit is a ‘1’ using the JN (“jump if negative”) instruction, because pressing
any of the calculator’s keys will set this bit to ‘0’.
Once a key is pressed, we use a CMPA (“compare accumulator”) instruction
at GN_COMP to check to see whether or not this is a digit in the range ‘0’ to
‘9’ or whether it’s a control code (all of the control codes have values
greater than ‘9’). If this was a control key, then its code will be greater than
‘9’ and the carry flag will be set to a logic 1, so we can use a JC (“jump if
carry”) instruction to jump to GN_RET, from whence we will exit out of the
subroutine.
If the key isn’t a control code, then we fall through to GN_TEST, where we
store the key’s value to the temporary location TEMP8. We do this because
we’re just about to modify the contents of the accumulator, and we don’t
want to lose the value of this key. Next we load the accumulator with the
contents of FLAG. If these contents are ‘0’ (indicating that this isn’t the first
digit in a number), then we use the JZ (“jump if zero”) instruction to jump to
GN_CONT, otherwise we store a ‘0’ in FLAG, clear the display, then fall
through to GN_CONT. Whichever way we reach GN_CONT, all we do is to
retrieve the value of the key from our TEMP8 location, write it to the
display, then jump back to GN_LOOP to await the next key to be pressed.
The main “shortcut” we’ve used in this example occurs when we exit from
the GET_NUM, subroutine. For the purposes of this example we’ve assumed
that the user will only press the keys ‘0’ through ‘9’ and ‘Clr’. Thus, when
we return to the main body of the program, we don’t actually bother to test
for ‘Clr’, but instead jump straight back into our initialization sequence at
RESET. The advantage of this approach is that we don’t run into any
problems if the user happens to select one of the other control keys
(‘+’, ‘–’, ‘*’, ‘/’, or ‘=‘).
####
#### This is the start of subroutine ‘GET_NUM’, which keeps on
#### reading keys from the input port and writing them to the
#### display until it sees a control code (anything greater than
#### ‘9’)
####
GET_NUM: POPA # Retrieve MS byte of return address
STA [RET_ADDR] # and store it
POPA # Retrieve LS byte of return address
STA [RET_ADDR+1] # and store it
LDA 0 # Load ACC with zero and push two
PUSHA # copies of this onto the top of
PUSHA # the stack
LDA 1 # Load ACC with ‘1’ and write it to
# a location called ‘FLAG’,
STA [FLAG] # which will tell us if this is the
# first digit (‘0’ = NO, ‘1’ = YES)
GN_LOOP: LDA [IN_PORT] # Read a value from the input port
JN [GN_LOOP] # If the most significant bit is
# ‘1’,then no key pressed, so jump
# back to ‘GN_LOOP’ and try again
GN_COMP: CMPA 9 # Compare the value in the ACC to
# ‘9’.
JC [GN_RET] # If ACC is greater than ‘9’, then
# we’re dealing with a control key
# so return to the calling program
# ...
GN_TEST: STA [TEMP8] # Store ACC to a temporary location
# called ‘TEMP8’
LDA [FLAG] # Load ACC with contents of flag. If
JZ [GN_CONT] # ‘0’ then jump to ‘GN_CONT
# (continue),
LDA 0 # otherwise load ACC with 0 and
# store it back to the flag,
STA [FLAG] # then load the
LDA CLR_CODE # ACC with the clear code and use it
STA [OUT_PORT] # to clear the display
GN_CONT: LDA [TEMP8] # Retrieve the value from the
# temporary
STA [OUT_PORT] # location, write it to the display,
JSR [_UMULT10] # Multiply the 16-bit number on the
# top of the stack by ten
LDA [TEMP8] # Retrieve the value from the
# temporary location and push it
Solution for Calculator Sign-Off Project I-11
PUSHA # on the stack then push a 0 on
LDA 0 # the stack to form
PUSHA # the MS byte of a 2-byte value
JSR [_ADD16] # Call a 16-bit addition subroutine
JMP [GN_LOOP] # then jump back and wait for next
# key
GN_RET: PUSHA # Push the control code onto the top
# of the stack
LDA [RET_ADDR+1] # Load ACC with LS byte of return
PUSHA # address and push it onto the stack
LDA [RET_ADDR] # Load ACC with MS byte of return
PUSHA # address and push it onto the stack
RTS # Return to the main calling program
####
#### This is the end of subroutine ‘GET_NUM’
####
FLAG: .BYTE # Reserve location to be used as a
# flag
TEMP8: .BYTE # Reserve a temporary 8-bit location
RET_ADDR: .2BYTE # Reserve a 2-byte location to hold
# the return address from our
# subroutine
####
#### This is where we’d insert our subroutines _UMULT10 and _ADD16
#### (using the technique discussed in Chapter 12)
####
.END
Neglecting the addition of a new 2-byte temporary location called
RET_ADDR (“return address”) at the end of the program, there are only
three areas of modifications. The first occurs at the beginning of our
GET_NUM subroutine. When the program commences running, the stack
is empty and the stack pointer is pointing at location $4FFF (Figure I.6a).
When the body of the program uses the JSR (“jump to subroutine”)
instruction to call GET_NUM, the CPU automatically places the return
address on the top of the stack, which is the way things are when we enter
GET_NUM (Figure I.6b). Unlike our first example program, this new version
of GET_NUM is going to juggle things around on the stack, so the first thing
we do upon entering the subroutine is to pop the return address off the top
of the stack and store it in our 2-byte temporary location RET_ADDR
(Figure I.6c). The last modification in this batch occurs when we load the
accumulator with zero and push two copies of this value onto the top of
the stack (Figure I.6d).
I-12 Bebop BYTES Back
$4FFC $4FFC
Stack
$4FFD $4FFD
pointer
$4FFE $4FFE 2-byte
Stack return
$4FFF $4FFF address
pointer
$4FFC $4FFC
Stack
$4FFD $4FFD
pointer
$4FFE $4FFE 2 bytes
Stack containing
$4FFF $4FFF zero
pointer
$4FFC $4FFC
Stack Stack
$4FFD $4FFD
pointer pointer
$4FFE $00 $4FFE $00
$4FFF $00 $4FFF $00
Stack
$4FFB $4FFB
pointer
$4FFC $00 $4FFC
Stack
$4FFD $02 $4FFD
pointer
$4FFE $00 $4FFE $00
$4FFF $00 $4FFF $02
Figure I.7: The condition of the stack as we call the _UMULT10 and _ADD16
subroutines the first time round the loop
I-14 Bebop BYTES Back
Now, this may not appear to be tremendously exciting on our first pass
around the loop, but remember that we’ve only entered the first digit (‘2’) of
our number 2467. Now consider what happens the next pass around the
loop as we enter our second digit (‘4’).
Note that _ADD16 and _UMULT10 both modify When we reach GN_CONT this time,
the contents of the accumulator and leave it in the top of the stack contains the $0002
an undefined state (_UMULT10 also modifies that we left there from our last trek
the contents of the index register). Thus, if we through the loop (Figure I.8a), so when
desire to use the contents of these registers in
we call _UMULT10, it will multiply the
the future, we have to take steps to preserve
them before calling these subroutines, which is $0002 on the top of the stack by 10
one of the reasons we saved the value of the and return $0014 (20 in decimal)
key into TEMP8. (Figure I.8b). Next we retrieve the
value of our key (‘4’ in this case), and
push it onto the stack, followed by the
zero byte required to form a 16-bit number (Figure I.8c). Finally we call our
_ADD16 subroutine which retrieves the two 16-bit values from the top of
the stack, adds them together, and returns the 16-bit result of $0018
(24 in decimal) (Figure I8.d).
$4FFC $4FFC
Stack Stack
$4FFD $4FFD
pointer pointer
$4FFE $00 $4FFE $00
$4FFF $02 $4FFF $14
Stack
$4FFB $4FFB
pointer
$4FFC $00 $4FFC
Stack
$4FFD $04 $4FFD
pointer
$4FFE $00 $4FFE $00
$4FFF $14 $4FFF $18
Figure I.8: The condition of the stack as we call the _UMULT10 and _ADD16
subroutines the second time round the loop
Stack
$4FFA $4FFA
pointer
$4FFB Return $4FFB
address Stack
$4FFC $4FFC
pointer
$4FFD Ctrl key $4FFD Ctrl key
$4FFE $09 $4FFE $09
$4FFF $A3 $4FFF $A3
(a) Just before the RTS (b) Just after the RTS
Figure I.9: The condition of the stack as we return from GET_NUM
The very last instruction in GET_NUM is the RTS (“return from subroutine”),
which informs the CPU that it’s time to exit the subroutine and return to the
main body of the program. To do this, the CPU automatically retrieves the
return address from the top of the stack and modifies the stack pointer
accordingly.(1) Thus, when we come to return to the main body of the
program after GET_NUM has performed its role in life, we know that the
three bytes on the top of the stack will contain the value of whatever control
key was pressed along with our 16-bit number (Figure I.9b). Of course, the
current implementation of our program doesn’t actually use this data yet,
but it will, Oh yes, it will.
+2467 = $09A3
Remainder
10 7 Store the individual
remainders then
+246 = $00F6 write them out in
reverse order
Remainder
10 6
+24 = $0018
Remainder
10 4
+2 = $0002
Remainder
10 2
0 = $0000
Figure I.10: Algorithm used to scrunch 16-bit numbers and display them
Our first action is to test the number to see if it represents a negative value.
Remember that signed numbers are stored internally in a twos complement
form, but we wish to display our results using traditional sign-magnitude
decimal representations. Thus, if the internal value is negative, we need to
negate it (that is, convert it into its positive equivalent) and also write a
minus sign out to the display.
Next we repeatedly divide our internal value by ten, storing the remainder
from each division operation, until our value has wound down to zero. The
reason we need to store the remainders is that we have to write them out to
the display in reverse order. As we see, with the exception of a few minor
niggles, this process is almost the mirror image of the one used to construct
our internal representations in the previous section.
Solution for Calculator Sign-Off Project I-17
Without further ado, let’s examine some code that can implement this
algorithm. Note that, for the sake of simplicity, we’ve chopped out the
GET_NUM subroutine and temporarily added a few lines of dummy code to
provide us with a number to scrunch up and display. As before, any
additions to our program are shown shaded grey:
CLR_KEY: .EQU $10 # Code associated with ‘Clr’ key
CLR_CODE: .EQU $10 # Code to clear the display
NEG_CODE: .EQU $20 # Code for a minus sign
IN_PORT: .EQU $F007 # Address of input port from keypad
OUT_PORT: .EQU $F027 # Address of output port to display
.ORG $4000 # Start of program is address $4000
RESET: BLDSP $4FFF # Initialize the stack pointer
LDA CLR_CODE # Load ACC with clear code
STA [OUT_PORT] # Clear the display
LDA 0 # Load ACC with code for number ‘0’
STA [OUT_PORT] # Write it to the display
#### The next four instructions are simply used to push
#### a number on the stack for us to play with
LDA $5D # Load ACC with LS byte of 2-byte
PUSHA # value and push onto the stack
LDA $F6 # followed by the MS byte (the full
PUSHA # 2-byte (16-bit) number is $F65D)
JSR [DISP_NUM] # Jump to the subroutine ‘DISP_NUM’
JMP [$0000] # to scrunch the number and display
# it. Afterwards jump straight to
# address $0000 to kill the program
####
#### This is the start of subroutine ‘DISP_NUM’, which grabs a
#### 2-byte (16-bit) value from the top of the stack, scrunches
#### it up, and writes it out to the calculator’s output display
####
DISP_NUM: POPA # Retrieve MS byte of return address
STA [RET_ADDR] # and store it
POPA # Retrieve LS byte of return address
STA [RET_ADDR+1] # and store it
POPA # Retrieve MS byte of number to be
STA [TEMP16] # scrunched and store it
POPA # Retrieve LS byte of number to be
STA [TEMP16+1] # scrunched and store it, then push
PUSHA # it back onto the top of the stack
LDA [TEMP16] # Now reload ACC with stored MS byte
PUSHA # of number and push it onto the
# stack
#### The next four instructions start off by loading
#### our temporary location ‘TEMP8’ with 0. We’re going
I-18 Bebop BYTES Back
2
Don’t answer that!
I-20 Bebop BYTES Back
Stack
$4FFD $4FFD
pointer
$4FFE $4FFE $F6
Stack
$4FFF $4FFF $5D
pointer
(a) Store two zeros (b) Store 16-bit number (c) Call _SUB16
The way in which _SUB16 works is fully described in Appendix G. For the
purposes of these discussions, we need only know that it subtracts the
16-bit value it finds on the top of the stack from the 16-bit value below it,
and replaces both of these values with a 16-bit result. Thus, in this case,
we’ve subtracted our negative value of $F65D (-2467 in decimal) from
$0000, which results in our obtaining the positive equivalent of $09A3
(2467 in decimal).
To recap, if our original number had been positive, we’d have stored it
directly onto the top of the stack. As it wasn’t, we wrote a minus sign to our
output display, negated the number, and left that on the top of the stack. In
both cases we end up with a positive version of our number on the top of
the stack, at which point we find ourselves at the label DN_LOOPA.
From the algorithm we introduced in Figure I.10, we know that we’re going
to have to repeatedly divide our number by ten. In fact we’ve provided two
general-purpose division subroutines called _UDIV16 and _SDIV16 with your
Beboputer, which can be used to perform unsigned and signed 16-bit
divisions, respectively. However, once again, it proved to be more
efficacious for us to take a copy of one of these general-purpose subroutines
(_UDIV16 in this case), rename it to _UDIV10 (“unsigned divide-by-ten”),
I-22 Bebop BYTES Back
and tailor it to our needs. (As usual, we supplied this subroutine with your
Beboputer, so you can use the assembler to root around to see how it
works).
Our first action at DN_LOOPA is to call _UDIV10, which takes whatever
16-bit number it finds on the top of the stack and divides it by ten. Now this
is the clever part, because _UDIV10 returns four bytes of data on the top of
the stack (Figure I.13b). The byte at the bottom of the pile is the remainder
from the division; the next two bytes are the quotient from the division
(the original value divided by ten); and the last byte (on the top of the stack)
is a flag. If this byte is a zero, then there’s nothing left to do, but if it’s non-
zero, then we’ve got to perform at least one more division. Thus, following
the call to _UDIV10, we increment our count in TEMP8 (which we’re using
to keep track of the number of remainders we’ve found), then we pop our
flag off the top of the stack (Figure I.13c).
SP
$4FF9 $4FF9 $4FF9
SP
$4FFA $4FFA Flag $4FFA
SP
$4FFB $4FFB $00 $4FFB $00
$4FFC $09 $4FFC $F6 $4FFC $F6
$4FFD $A3 $4FFD $07 $4FFD $A3
$4FFE $F6 $4FFE $F6 $4FFE $F6
$4FFF $5D $4FFF $5D $4FFF $5D
(a) Before _UDIV10 (b) After _UDIV10 (c) After popping the flag
Figure I.13: The condition of the stack on the first pass round DN_LOOPA
As we see, the remainder from this first division is $07, which is sort of
what we’d expect if we divide $09A3 (2467 in decimal) by ten. Similarly,
the quotient from the division is $00F6 (246 in decimal), which is also what
we’d expect. The clever part is that the 16-bit number we now need to
divide is just where we want it to be ...... on the top of the stack. The end
result is that, as we cycle around this loop, we leave a trail of remainders
growing up the stack, with whatever number we next wish to divide by ten
riding on top of them.
In the case of this particular example, after four passes around the loop
we’re presented with four remainders on the stack and nothing left to
divide, where this “nothing left to divide” appears in the form of two bytes
containing zero on the top of the stack. Thus, when we arrive at label
DN_DISP, the first thing we do is to pop these two zero bytes off the top of
the stack and throw them away. Next we enter the loop called DN_LOOPB,
which cycles around popping the remainders off the stack and writing them
Solution for Calculator Sign-Off Project I-23
to our display. The way in which we stored the remainders on the stack
means that they arrive at the display in the reverse order to the one in which
we generated them, which is just what our algorithm called for.
Note that once we’ve popped our last remainder off the stack and written it
to the display, we’re still left with our original number (positive or negative)
at the new top of the stack. This means that when we eventually return to
the main body of our program, DISP_NUM will leave the stack in exactly the
same condition that it found it. Last but not least, we retrieve DISP_NUM’s
return address from the temporary location RET_ADDR, push it onto the top
of the stack, and return to the main body of the program.
2 4 3 - 3 8 2 x 1 4 =
Figure I.14: Exactly what should happen every time we press a key
We commence in our reset state with a ‘0’ on the display, then, as we begin
to enter the number 243, the ‘2’ replaces the ‘0’ and the ‘4’ and ‘3’ slide in
from the right-hand side. Next we enter the ‘-’, but nothing happens until
we begin to enter the number 382, at which time the ‘3’ replaces the 243
and the ‘8’ and ‘2’ slide in from the right.
The really interesting thing occurs when we enter the ‘x’, because this is the
point where the previous ‘–’ operation is performed and the intermediate
result of -139 appears on the display. This value remains on the display until
we begin to enter the next number, 14. The reason we say this is “the really
interesting thing,” is that it defines the way the calculator works most of the
time. That is, when we’re in the middle of a sequence of commands, every
time we select one of the ‘+’, ‘-’, ‘x’, and ‘/’ operators, the calculator
actually executes the previous operation and displays the results.
The only tricky bits occur at the beginning and the end of the sequence. Not
surprisingly, these are known as the end conditions, and, when you begin
to create your own programs, you’ll quickly find that it’s the end conditions
that are the bane of a programmer’s life. Consider what happens following
the reset condition when we select the ‘-’ operator after entering the number
243. How can we execute the previous operation when we haven’t entered
one yet. Even worse, suppose that when a user commences, she or he
decides to use the ‘0’ that’s already on the display, so (s)he simply enters the
sequence “+ 243”. Eeek, now our calculator receives an operator before it’s
even been presented with a number. Similarly, we have to decide what we
wish to happen following an ‘=‘ if the user:
a) Commences a new sequence with a number.
b) Kicks off the new sequence with another operator.
c) Decides to click on ‘=‘ again just to see what will happen.
Solution for Calculator Sign-Off Project I-25
In fact point (c) opens up a whole new realm of questions, such as what do
we wish to occur if the user clicks the same operator two or more times in a
row (for example “123 + + 123”), or what should happen if the user clicks
two or more different operators one after the other (for example
“123 + x 123”).
Fortunately, assuming that you’re not working on a mission-critical project
in which lives are involved, there is a pretty good way out for the majority
of questions like this, which is to say: “It’s not designed to work like that, so
the results are undefined in these cases.” This is not a joke. Practice
chanting these words like a mantra until you can reel them off without
pausing for thought, because they can literally save you hundreds of hours
of work. Otherwise, a moment’s hesitation can result in your boss uttering
the dread words: “We really ought to tie this sort of thing down.” Once this
happens you’re lost. You can now look forward to spending days, weeks,
months, or the rest of your working life specifying every conceivable way in
which an idiot might misuse your product; modifying your program to
handle such situations; and then, the final humiliation of all, documenting it
in a user manual so that other programmers can laugh at you until the tears
roll down their cheeks.
But we’ve wandered off into the weeds again. If we put our minds into gear,
it’s usually possible to play a few cunning tricks so as to minimize any
problems associated with the end conditions. So let’s use everything we’ve
learnt thus far to hammer out one of the many possible solutions to our
calculator problem. Once again, any new material is shown shaded grey.
CLR_KEY: .EQU $10 # Code for ‘Clr’ key
SUB_KEY: .EQU $20 # Code for ‘Subtract’ key (‘-’)
ADD_KEY: .EQU $30 # Code for ‘Add’ key (‘+’)
DIV_KEY: .EQU $40 # Code for ‘Divide’ key (‘/’)
MULT_KEY: .EQU $50 # Code for ‘Multiply’ key (‘*’)
EQU_KEY: .EQU $60 # Code for ‘Equals’ key (‘=’)
CLR_CODE: .EQU $10 # Code to clear the display
NEG_CODE: .EQU $20 # Code for a minus sign
BEL_CODE: .EQU $70 # Code to make a “beep”
IN_PORT: .EQU $F007 # Address of input port from keypad
OUT_PORT: .EQU $F027 # Address of output port to display
.ORG $4000 # Start of program is address $4000
RESET: BLDSP $4FFF # Initialize the stack pointer
LDA CLR_CODE # Load ACC with clear code
STA [OUT_PORT] # Clear the display
LDA 0 # Load ACC with code for number ‘0’
STA [OUT_PORT] # Write it to the display
I-26 Bebop BYTES Back
operation, and leaves the 16-bit result on the top of the stack.
(a) Before GET_NUM (b) After GET_NUM (c) After popping the key
Finally, we retrieve the key code that we stored in THIS_OP and squirrel it
away in LAST_OP, thereby positioning ourselves for the next pass around
the loop. Next we use our DISP_NUM subroutine to display the result of the
operation, then we jump back to the beginning of the main loop to do the
whole thing again. The reason why all of this is so cunning is that whenever
we reach the start of the main loop (either from a reset condition or because
we’ve already gone around the loop), we always commence with a single
16-bit value on the top of the stack and some operation stored in LAST_OP.
As we said, this goes a long way towards solving the problems of the end
conditions.
And there you have it. Note that out solution isn’t one hundred percent
bullet proof; for example, what does happen if we click multiple operators
one after the other?(3) As it happens, our program is reasonably robust, in
that it won’t crash if you do something silly like this, but the results might
be “interesting.” Last but not least, there are many ways in which you could
improve our program, such as modifying it to “beep” (by writing BEL_CODE
to the output port) if the user does something inopportune, such as
attempting to divide a number by zero.
3
As a wise man once said: “It’s not designed to work like that, so the results are undefined in
these cases.”
Appendix
J
“The Best Clam Chowder
in the World”
J-2 Bebop BYTES Back
Contents of Appendix J
Maintaining your brain at full root-ti-toot-toot power .......................... J-3
Preparing the clams ............................................................................................................................... J-4
Preparing the stock .................................................................................................................................. J-4
Making the chowder ............................................................................................................................. J-5
Serving your taste-fest sensation .......................................................................................... J-6
“The Best Clam Chowder in the World” J-3
Maintaining your brain at full root-ti-toot-
toot power
By the time you’ve draped your cerebral cortex around the myriad juicy
topics in Bebop Bytes Back, you’ll be in sore need of sustenance to enable
your brain to keep barreling along at its full root-ti-toot-toot power. As we
all know from our mother’s knee, seafood is brain food, so what could be
better than wrapping your laughing tackle around a fishy dish? But for what
kind of fishy dish could we wish?
When the prequel to this illustrious tome hit the streets,(1) it included the
recipe for a “No-holds-barred Seafood Gumbo” that promised to “Swing
you sybaritically around the room in a syncopated symphony of delight,
and leave you groveling on your knees, gnashing your teeth, and gasping
for more.” Well that recipe lived up to its reputation, and more than one
reviewer stated: “This book is well worth the price for the gumbo recipe
alone ......”
Only one man was less than happy! After plowing through the book, a
friend of the authors, Chuck Paglicco, stumbled across the seafood gumbo
recipe in the wee hours of the morning. Chuck was so disturbed that he was
moved to come beating on our doors
screaming “Seafood gumbo? Seafood gumbo?
Beware ! If you are under 21, male,
How could you stoop so low when we [Chuck
or a politician, don’t attempt to do
and his wife Rita] have the recipe for the best anything on the culinery front
clam chowder in the world?” without your mother’s permission
and supervision, because kitchens
Being a somewhat volatile Bostonian of Italian
contain sharp things, hot things,
decent, Chuck isn’t a man to be argued with
and a variety of other potentially
when it comes to matters of haute cuisine, so dangerous things.
we prostrated ourselves humbly, apologized
profusely, and promised faithfully that our very
next book would revel in concoctions of
chowder-like nature ...... so here we are.
1
Bebop to the Boolean Boogie (An Unconventional Guide to Electronics)
J-4 Bebop BYTES Back
Enjoy!
2
For your edification, zero degrees in the Fahrenheit scale was originally defined as the
freezing point of fortified wine.
3
We strongly advise your closing the drapes first, otherwise the neighbors are sure to talk
Appendix
K
Lexicon
K-2 Bebop BYTES Back
Contents of Appendix K
A treasure-trove of words ............................................................................................................ K-3
antipodes (antipodean) ...................................................................................................... K-3
apposite ........................................................................................................................................................ K-4
a r c a n e ............................................................................................................................................................ K-4
b e h o o v e ....................................................................................................................................................... K-4
caprice ........................................................................................................................................................... K-4
conjecture ................................................................................................................................................. K-4
constituent ................................................................................................................................................ K-5
cornucopia .............................................................................................................................................. K-5
d e c i m a t e .................................................................................................................................................... K-5
delectation ............................................................................................................................................... K-6
digress .............................................................................................................................................................. K-6
ensconce .................................................................................................................................................... K-6
gregarious .................................................................................................................................................. K-6
hierarchy ...................................................................................................................................................... K-7
inexorable .................................................................................................................................................. K-7
j a p e ..................................................................................................................................................................... K-7
legerdemain ........................................................................................................................................... K-7
malapropos ............................................................................................................................................. K-8
metaphor .................................................................................................................................................... K-8
paradox ........................................................................................................................................................ K-8
p e n c h a n t .................................................................................................................................................... K-8
pillage .............................................................................................................................................................. K-8
sequence .................................................................................................................................................... K-9
transmogrify ............................................................................................................................................. K-9
Utopia ............................................................................................................................................................... K-9
w a n t o n ........................................................................................................................................................... K-9
Lexicon K-3
A treasure-trove of words
Our original intention when writing this book was to accommodate younger
readers by avoiding over-complex or unfamiliar words, but sadly we failed.
The problem is that words are fun, and the fun increases the more you
know. Sometimes a particular word is just so ...... it’s just so ...... golly
gosh, it’s just so efficacious (there, we said it and we just don’t care).
There’s a certain satisfaction that comes when one knows the most apposite
words for the task and can wield them with confidence and disdain. For
example, rather than responding to an unkind jibe with the retort “Yeah, sez
you!,” imagine the gratification that could be yours if the following were to
roll resoundingly off your tongue: “It is my conjecture that you are a
perpilocutionist of no small repute and a trombenik of distinction.”(1)
In a more serious vein (as Count Dracula might say), this book is intended to
educate and illuminate on multiple levels. Thus, every now and then we’ve
slipped in the occasional poser of a word to stimulate the old gray matter.
However, it’s not our intention to make your life more torturous than it
already is, so to save you constantly scurrying to the dictionary we’ve
compiled this short lexicon to increase your delectation and delight.
1
A perpilocutionist is someone who doesn’t know what he or she is talking about, while a
trombenik is a loud and boring person (from the Polish word for a brass horn). (These really
are proper words, but they’re sort of old, so you rarely find them in modern dictionaries.)
K-4 Bebop BYTES Back
2
A gobemouche (literally a “fly swallower”) is someone who will believe anything you tell
them, no matter how ridiculous. For example, did you know that the word gullible isn’t to be
found in an English dictionary?
K-6 Bebop BYTES Back
diodes (continued)
vacuum tubes .............................................................. 15-21 E
directive instructions ................................ 12-15, F-10 earth’s magnetic field ................................................ 11-16§
disassemblers ........................................................................ 12-97 EBCDIC ............................................................................. 2-21§, E-8
discs Eckert, J. Presper Jr ......................................................... 15-33
CD-ROMs ............................................................................. 4-25 Ed Roberts ................................................................................. 15-42
versus disks ........................................................................ 4-25 Edison
disks Effect ....................................................................................... 15-21
hard ............................................................................... 3-4, 4-24 Thomas Alva ................................................................. 15-20
removable ............................................................................ 4-25 editing programs with the
versus discs ........................................................................ 4-25 hex keypad ......................................................................... 9-28
display switch panel ...................................................................... 3-23
7-segment LED displays editor
decoded character editor ............................................................. 13-5
dual .............................................................................. 5-18 line editor .................................................... 12-44, 12-46
solo ............................................................................... 5-16 linkage-editor ........................................................... 12-101
undecoded ................................................................... 5-11 screen editor ............................................ 12-44, 12-47
8-bit LED displays ......................................................... 5-5 simple program .......................................................... 11-44
memory walker display ....................................... 3-26 EDSAC ...................................................................... 12-34, 15-36
breakpoints ............................................................. 12-87 EDVAC ........................................................................................... 15-34
panel .......................................................................................... 3-11 Edward Scheutz ................................................................. 15-16
versus hex keypad ................................................. 9-4 EEPROM .......................................................................................... 13-4
Dissertio de Arte Combinatoria ................... 15-28 Egyptians ........................................................................................ 15-7
divide-by-2 (SHR) ................................................................ 8-16 eldritch rituals .......................................................................... 1-10
division, binary electrically-erasable PROM ...................................... 13-4
signed ................................................................................... G-44 Electrical World Magazine ........................................... 5-6§
unsigned ............................................................... G-35, I-21 electromagnetic spectrum ...................................... 11-12
Dizzy Gillespie .................................................................. 15-36 electromechanical computers ........................... 15-22
Dodgson, Charles Lutwidge ................................ 15-26 electron guns ........................................................................... 11-6
dollar color television .......................................................... 11-11
bills ......................................................................... 4-21, 15-19 electronic computers ................................................. 15-31
characters ............................................................................. 2-24 encoding hex keypad buttons .................................. 9-6
dots and dashes (Morse Code) .............................. 4-4 encyclopedias, interactive ............................................ 1-9
Dracula ................................................................................................ K-3 .END directive .................................................... 12-15, F-10
drum engineer ...................................................................................... 11-23
master ........................................................................................ 2-4 Engines of the Mind ...................................................... 15-16
rotating drums ................................................................. 4-24 English Mechanic and World of Science
Dummer, G.W.A .................................................. 1-7, 15-39 Magazine ................................................................................. 11-3
Dvorak ENIAC ........................................................................... 1-5, 15-33
August ..................................................................................... 10-6 ENIGMA ..................................................................................... 15-33
keyboard ................................................................................ 10-7 entering programs with the
club ..................................................................................... 10-8 hex keypad ......................................................................... 9-21
dynner ............................................................................................... 2-12§ switch panel ...................................................................... 3-14
Entscheidungsproblem .............................................. 15-32
JSR instruction ................................... 8-86, 12-64, C-39 Korea (water clock) ............................................................ 15-7§
juggling ........................................................................................ 15-18 Krum
Julius Edgar Lilienfield ................................................ 15-38 Charles ................................................................................. 10-10
jumps Howard ............................................................................... 10-11
conditional .......................................................................... 8-69
unconditional ................................................................... 8-67
Jurassic period ........................................................................... 1-9
L
JZ instruction ........................................................... 8-70, C-40 labels ................................................................................... 12-14, F-5
naming conventions .................................................. G-3
K laboratories
lab #1 ....................................................................................... 3-11
K (Kilo) ............................................................................................. 2-30 lab #2 .......................................................................................... 4-7
Karel Capek ............................................................................. 15-22 lab #3 ....................................................................................... 5-10
Karnaugh lab #4 ....................................................................................... 9-11
Maps ...................................................................................... 15-26 lab #5 ................................................................................... 10-16
Maurice .............................................................................. 15-26 lab #6 ................................................................................... 11-28
Kenbak-1 .................................................................................... 15-43§ lab #7 ................................................................................... 12-56
Kennedy, John F. ............................................................... 15-37‡ lab #8 ....................................................................................... 13-5
Kepler, Johannes ............................................................... 15-13 lab #9 ....................................................................................... 14-3
keyboard ladies
AZERTY .................................................................................. 10-5‡ knitting ................................................................... 3-8, 15-24
computer ........................................................................... 10-12 little old ladies ................................................................... 1-9
Dvorak ..................................................................................... 10-7 languages
Half-QWERTY ............................................................. 10-21 ADA ......................................................................................... 15-16
Sholes ........................................................................................ 10-5 BASIC 2.0 ........................................................................ 15-43
QWERTY ............................................................................... 10-5 C .................................................................................................... 12-4
QWERTZU .......................................................................... 10-5‡ COBOL ............................................................................... 15-37§
keypad FORTRAN ............................................................................ 4-23
calculator (sign-off) ....................................... 14-4, I-3 natural ..................................................................................... 12-4
driving 7-segment displays ................................. 9-8 OCCAM .............................................................................. 15-18
editing programs .......................................................... 9-28 Pkankalk I ........................................................................ 15-25
encoding buttons ............................................................ 9-6 latch, interrupt ......................................................................... D-8
entering programs ....................................................... 9-21 latches .................................................................................................. 6-9
hexadecimal ......................................................................... 9-3 laundromat ............................................................................... 15-43
mapping buttons ............................................................. 9-5 LDA instruction ........................................................ 8-5, C-41
modifying programs ................................................. 9-28 least-significant bit .............................................................. 2-13
running programs ........................................................ 9-29 LEDs ....................................................................................................... 5-5
versus switch panel ..................................................... 9-4 7-segment LED displays
Kilby, Jack ................................................................... 1-7, 15-39 decoded
Kilo (K) .............................................................................................. 2-30 dual .............................................................................. 5-18
KIM-1 ............................................................................................. 15-44 decoded (continued)
Kip Crosby ............................................................ 15-46, 15-48 solo ............................................................................... 5-16
KISS principle ....................................................................... 12-44 undecoded ................................................................... 5-11
knitting .............................................................................. 3-8, 15-24 8-bit LED displays ......................................................... 5-5
Konrad Zuse ........................................................................... 15-25
bold number = key definition, ‡ = footnote, § = sidebar
Lee / Lullists
Marx
M Brothers ..................................................................................... 9-9‡
M (mega) .......................................................................................... 3-4§ Groucho ................................................................................. 9-16‡
machine masters of the mystic arts .......................................... 4-6
code ............................................................................................ 12-3 Matias Corporation ......................................................... 10-21
dependent ............................................................................ 12-5 Mauchly, John William ............................................ 15-33
independent ........................................................................ 12-6 Maurice Karnaugh .......................................................... 15-26
readable ................................................................................. 4-17 Maxwell, James Clerk ..................................................... 11-5
macro assemblers ........................................................... 12-92 McGurrin, Frank ................................................................... 10-6
mad as a hatter ................................................................... 15-29‡ mechanical
magazines calculators ....................................................................... 15-11
Electrical World ................................................................ 5-6§ computers ........................................................................ 15-15
English Mechanic and Analytical Engine ............................................. 15-16
World of Science .................................................. 11-3 Difference Engine ............................................ 15-16
Popular Mechanics ....................................................... 0-3 Differential Analyzer . 1-4, 10-11, 15-22
Radio Electronics ..................................................... 15-42 Product Intergraph .......................................... 15-22
magenta ...................................................................................... 11-13‡ television ............................................................................... 11-4
magnetic medium-resolution mode ...................................... 11-50
beads ............................................................................................ 3-6 mega (M) ........................................................................................... 3-4§
core store ................................................................................. 3-6 memory
disks address decoding ........................................................ 2-30
hard ......................................................................................... 3-4 bulk storage ....................................................................... 4-23
removable .................................................................... 4-25 cell ................................................................................................ 2-28
drums ......................................................................................... 4-24 Character ROM ......................................... 11-26, 13-8
field, earth’s ................................................................... 11-16§ core store
media .......................................................................... 3-4, 4-24 magnetic ........................................................................... 3-6
tapes ............................................................................................. 4-24 non-magnetic ........................................................... 4-23
toroids ......................................................................................... 3-7§ depth .......................................................................................... 2-29
Maharashtra, India ........................................................... 2-10 EEPROM ................................................................................. 13-4
mainframe computers ........................................................ 3-3‡ EPROM ..................................................................................... 13-4
Majorca ......................................................................................... 15-27 functions
majuscules ..................................................................................... 4-17§ flip-flops ............................................................................ 6-9
Manchester University ............................................... 15-36 latches .................................................................................. 6-9
Manhattan Project ........................................................... 15-34 hard disk ................................................................................... 3-4
mapping hex keypad buttons .................................... 9-5 magnetic core ..................................................................... 3-6
Marcian “Ted” Hoff ........................................................ 15-41 map ............................................................................................. 2-32
Marconi, Guglielmo ......................................................... 11-5 -mapped I/O ...................................................................... 2-33
Mariner 1 ................................................................................... 15-37 -mapped display ....................................................... 11-23
Mark 8 ........................................................................................... 15-42 control codes ........................................................ 11-51
Marlene Dietrich .............................................................. 15-25 mercury delay lines ............................. 4-24, 15-34
Marquand PROM ..................................................................................... 13-3
Allan .................................................................... 15-26, 15-30 RAM ............................................................................... 2-28, 3-8
Logic Machine ............................................................ 15-30 removable disks ............................................................ 4-25
Mars ......................................................................... 15-37§, 15-38§ ROM ........................................................................... 2-28, 13-3
RWM ........................................................................... 2-28, 3-8
V
W
vacuum
cleaners .................................................................................. 4-16‡ wagon wheels .......................................................................... 11-8
tubes ................................................... 11-5, 15-20, 15-33 walnuts, jet-propelled ........................................................ 1-8
Vannevar Bush ............................................... 10-11, 15-22 Walter Brattain ..................................................................... 15-38
vaporware ..................................................................................... 3-31 warp and weft .......................................................................... 4-20
VDU ................................................................................................ 11-20 warrior chieftain ................................................................ 15-11
vector, interrupt .............................. D-10, D-18, D-20 washing machines .............................................................. 4-25
Venn water clock ................................................................................. 15-7
Diagrams ........................................................................... 15-26 self-striking .......................................................................... 15-7§
John ......................................................................................... 15-26 Web ...................................................................................................... 3-12
Venus .............................................................................................. 15-37 browser icon ..................................................................... 3-12
vertebrates ..................................................................................... 15-5 wetware ........................................................................ 3-31, 3-31‡
vertical Wheatstone, Sir Charles ................................. 4-3, 10-9
deflection width, memory ....................................................................... 2-29
coils ................................................................................. 11-10 Wilhelm Schickard ......................................................... 15-13
plates .................................................................................. 11-7 William
flyback ................................................................... 11-7, 11-9 Austin Burt .......................................................................... 10-3
video Fothergill Cooke ............................................................... 4-3
configuration file ............................................................. B-7 Jenne .......................................................................................... 10-4
bold number = key definition, ‡ = footnote, § = sidebar
William / Zworkin
X
X (index register) ................................................................ 8-74
connecting, gory details ...................................... 8-78
If I am not completely satisfied I can return the book for a full refund within 15 days
of delivery.
Name .....................................................................................................................................................................................
Company ....................................................................................................................................................................
Address .......................................................................................................................................................................
City ...........................................................................................................................................................................................
State .................................................................................................................................................................................
Zip ...............................................................................................................................................................................................
Daytime phone no.............................................................................................................................
Discounts are available for orders of 3 or more books: contact the publisher.