Placeholder Image

Subtitles section Play video

  • [MUSIC PLAYING - "JESU, JOY OF MAN'S DESIRING" BY JOHANN

  • SEBASTIAN BACH]

  • PROFESSOR: Well, up 'til now, I suppose, we've been learning

  • about a lot of techniques for organizing big programs,

  • symbolic manipulation a bit, some of the technology that

  • you use for establishing languages, one in terms of

  • another, which is used for organizing very large

  • programs. In fact, the nicest programs I know look more like

  • a pile of languages than like a decomposition of a problem

  • into parts.

  • Well, I suppose at this point, there are still, however, a

  • few mysteries about how this sort of stuff works.

  • And so what we'd like to do now is diverge from the plan

  • of telling you how to organize big programs, and rather tell

  • you something about the mechanisms by which these

  • things can be made to work.

  • The main reason for this is demystification, if you will,

  • that we have a lot of mysteries left, like exactly

  • how it is the case that a program is controlled, how a

  • computer knows what the next thing to do is, or

  • something like that.

  • And what I'd like to do now is make that clear to you, that

  • even if you've never played with a physical computer

  • before, the mechanism is really very simple, and that

  • you can understand it completely with no trouble.

  • So I'd like to start by imagining that we--

  • well, the way we're going to do this, by the way, is we're

  • going to take some very simple Lisp programs, very simple

  • Lisp programs, and transform them into hardware.

  • I'm not going to worry about some intermediate step of

  • going through some existing computer machine language and

  • then showing you how that computer works, because that's

  • not as illuminating.

  • So what I'm really going to show you is how a piece of

  • machinery can be built to do a job that you have written down

  • as a program.

  • That program is, in fact, a description of a machine.

  • We're going to start with a very simple program, proceed

  • to show you some simple mechanisms, proceed to a few

  • more complicated programs, and then later show you a not very

  • complicated program, how the evaluator transforms into a

  • piece of hardware.

  • And of course at that point, you have made the universal

  • transition and can execute any program imaginable with a

  • piece of well-defined hardware.

  • Well, let's start up now, give you a real concrete feeling

  • for this sort of thing.

  • Let's start with a very simple program.

  • Here's Euclid's algorithm.

  • It's actually a little bit more modern

  • than Euclid's algorithm.

  • Euclid's algorithm for computing the greatest common

  • divisor of two numbers was invented 350 BC, I think.

  • It's the oldest known algorithm.

  • But here we're going to talk about GCD of A and B, the

  • Greatest Common Divisor or two numbers, A and B. And the

  • algorithm is extremely simple.

  • If B is 0, then the result is going to be A. Otherwise, the

  • result is the GCD of B and the remainder when A is divided by

  • B.

  • So this we have here is a very simple iterative process.

  • This a simple recursive procedure, recursively defined

  • procedure, recursive definition, which yields an

  • iterative process.

  • And the way it works is that every step, it determines

  • whether B was zero.

  • And if B is 0, we got the answer in A. Otherwise, we

  • make another step where A is the old B, and B is the

  • remainder of the old A divided by the old B. Very simple.

  • Now this, I've already told you some of the mechanism by

  • just saying it that way.

  • I set it in time.

  • I said there are certain steps, and that, in fact, one

  • of the things you can see here is that one of the reasons why

  • this is iterative is nothing is needed of the last step to

  • get the answer.

  • All of the information that's needed to run this algorithm

  • is in A and B. It has two well-defined state variables.

  • So I'm going to define a machine for you that can

  • compute you GCDs.

  • Now let's see.

  • Every computer that's ever been made that's a

  • single-process computer, as opposed to a multiprocessor of

  • some sort, is made according to the same plan.

  • The plan is the computer has two parts, a part called the

  • datapaths, and a part called the controller.

  • The datapaths correspond to a calculator that you might

  • have. It contains certain registers that remember

  • things, and you've all used calculators.

  • It has some buttons on it and some lights.

  • And so by pushing the various buttons, you can cause

  • operations to happen inside there among the registers, and

  • some of the results to be displayed.

  • That's completely mechanical.

  • You could imagine that box has no intelligence in it.

  • Now it might be very impressive that it can produce

  • the sine of a number, but that at least is apparently

  • possibly mechanical.

  • At least, I could open that up in the same way I'm

  • about to open GCD.

  • So this may have a whole computer inside of it, but

  • that's not interesting.

  • Addition is certainly simple.

  • That can be done without any further mechanism.

  • Now also, if we were to look at the other half, the

  • controller, that's a part that's dumb, too.

  • It pushes the buttons.

  • It pushes them according to the sequence, which is written

  • down on a piece of paper, and observes the lights.

  • And every so often, it comes to a place in a sequence that

  • says, if light A is on, do this sequence.

  • Otherwise, do that sequence.

  • And thereby, there's no complexity there either.

  • Well, let's just draw that and see what we feel about that.

  • So for computing GCDs, what I want you to think about is

  • that there are these registers.

  • A register is a place where I store a number, in this case.

  • And this one's called a.

  • And then there's another one for storing b.

  • Now we have to see what things we can do with these

  • registers, and they're not entirely obvious what you can

  • do with them.

  • Well, we have to see what things we

  • need to do with them.

  • We're looking at the problem we're trying to solve.

  • One of the important things for designing a computer,

  • which I think most designers don't do, is you study the

  • problem you want to solve and then use what you learn from

  • studying the problem you want to solve to put in the

  • mechanisms needed to solve it in the computer you're

  • building, no more no less.

  • Now it may be that the problem you're trying to solve is

  • everybody's problem, in which case you have to build in a

  • universal interpreter of some language.

  • But you shouldn't put any more in than required to build the

  • universal interpreter of some language.

  • We'll worry about that in a second.

  • OK, going back to here, let's see.

  • What do we have to be able to do?

  • Well, somehow, we have to be able to get B into A. We have

  • to be able to get the old value of B into the value of

  • A. So we have to have some path by which stuff can flow,

  • whatever this information is, from b to a.

  • I'm going to draw that with by an arrow saying that it is

  • possible to move the contents of b into a, replacing the

  • value of a.

  • And there's a little button here which you push which

  • allows that to happen.

  • That's what the little x is here.

  • Now it's also the case that I have to be able to compute the

  • remainder of a and b.

  • Now that may be a complicated mess.

  • On the other hand, I'm going to make it a small box.

  • If we have to, we may open up that box and look inside and

  • see what it is.

  • So here, I'm going to have a little box, which I'm going to

  • draw this way, which we'll call the remainder.

  • And it's going to take in a.

  • That's going to take in b.

  • And it's going to put out something, the remainder of a

  • divided by b.

  • Another thing we have to see here is that we have to be

  • able to test whether b is equal to 0.

  • Well, that means somebody's got to be looking at--

  • a thing that's looking at the value of b.

  • I have a light bulb here which lights up if b equals 0.

  • That's its job.

  • And finally, I suppose, because of the fact that we

  • want the new value of a to be the old value of b, and

  • simultaneously the new value of b to be something I've done

  • with a, and if I plan to make my machine such that

  • everything happens one at a time, one motion at a time,

  • and I can't put two numbers in a register, then I have to

  • have another place to put one while I'm interchanging.

  • OK?

  • I can't interchange the two things in my hands, unless I

  • either put two in one hand and then pull it back the other

  • way, or unless I put one down, pick it up, and put the other

  • one, like that, unless I'm a juggler, which I'm not, as you

  • can see, in which case I have a

  • possibility of timing errors.

  • In fact, much of the type of computer design people do

  • involves timing errors, of some potential timing errors,

  • which I don't much like.

  • So for that reason, I have to have a place to put the second

  • one of them down.

  • So I have a place called t, which is a register just for

  • temporary, t, with a button on it.

  • And then I'll take the result of that, since I have to take

  • that and put into b, over here, we'll take the result of

  • that and go like this, and a button here.

  • So that's the datapaths of a GCD machine.

  • Now what's the controller?

  • Controller's a very simple thing, too.

  • The machine has a state.

  • The way I like to visualize that is that I've got a maze.

  • And the maze has a bunch of places

  • connected by directed arrows.

  • And what I have is a marble, which represents the state of

  • the controller.

  • The marble rolls around in the maze.

  • Of course, this analogy breaks down for energy reasons.

  • I sometimes have to pump the marble up to the top, because

  • it's going to otherwise be a perpetual motion machine.

  • But not worrying about that, this is

  • not a physical analogy.

  • This marble rolls around.

  • And every time it rolls around certain bumpers, like in a

  • pinball machine, it pushes one of these buttons.

  • And every so often, it comes to a place, which is a

  • division, where it has to make a choice.

  • And there's a flap, which is controlled by this.

  • So that's a really mechanical way of thinking about it.

  • Of course, controllers these days, are not built that way

  • in real computers.

  • They're built with a little bit of

  • ROM and a state register.

  • But there was a time, like the DEC PDP-6, where that's how

  • you built the controller of a machine.

  • There was a bit that ran around the delay line, and it

  • triggered things as it went by.

  • And it would come back to the beginning and

  • get fed round again.

  • And of course, there were all sorts of great bugs you could

  • have like two bits going around, two marbles.

  • And then the machine has lost its marbles.

  • That happens, too.

  • Oh, well.

  • So anyway, for this machine, what I have

  • to do is the following.

  • I'm going to start my maze here.

  • And the first thing I've got to do, in a notation which

  • many of you are familiar with, is b equal to zero, a test.

  • And there's a possibility, either yes, in

  • which case I'm done.

  • Otherwise, if no, then I'm going have to

  • roll over some bumpers.

  • I'm going to do it in the following order.

  • I want to do this interchange game.

  • Now first, since I need both a and b, but then the first--

  • and this is not necessary--

  • I want to collect this.

  • This is the thing that's going to go into b.

  • So I'm going to say, take this, which depends upon both

  • a and b, and put the remainder into here.

  • So I'm going to push this button first. Then, I'm going

  • to transfer b to a, push that button, and then I transfer

  • the temporary into b, push that button.

  • So a very sequential machine, it's very inefficient.

  • But that's fine right now.

  • We're going to name the buttons, t gets remainder.

  • a gets b.

  • And b gets t.

  • And then I'm going to go around here and it's to go

  • back to start.

  • And if you look, what are we seeing here?

  • We're seeing the various--

  • what I really have is some sort of mechanical connection,

  • where t gets r controls this thing.

  • And I have here that a gets b controls this fellow over

  • here, and this fellow over here.

  • Boy, that's absolutely pessimal,

  • the inverse of optimal.

  • Every line heads across every other line the way I drew it.

  • I suppose this goes here, b gets t.

  • Now I'd like to run this machine.

  • But before I run the machine, I want to write down a

  • description of this controller, just so you can

  • see that these things, of course, as usual, can be

  • written down in some nice language, so that we don't

  • have to always draw these diagrams. One of the problems

  • with diagrams is that they take up a lot of space.

  • And for a machine this small, it takes two blackboards.

  • For a machine that's the evaluator machine, I have

  • trouble putting it into this room, even though

  • it isn't very big.

  • So I'm going to make a little language for this that's just

  • a description of that, saying define a

  • machine we'll call GCD.

  • Of course, once we have something like this, we have a

  • simulator for it.

  • And the reason why we want to build a language in this form,

  • is because all of a sudden we can manipulate these

  • expressions that I'm writing down.

  • And then of course I can write things that can algebraically

  • manipulate these things, simulate them, all that sort

  • of things that I might want to do, perhaps transform them as

  • a layout, who knows.

  • Once I have a nice representation of registers,

  • it has certain registers, which we can call A, B, and T.

  • And there's a controller.

  • Actually, a better language, which would be more explicit,

  • would be one which named every button also and

  • said what it did.

  • Like, this button causes the contents of T to go to the

  • contents of B. Well I don't want to do that, because it's

  • actually harder to read to do that, and it

  • takes up more space.

  • So I'm going to have that in the instructions written in

  • the controller.

  • It's going to be implicit what the operations are.

  • They can be deduced by reading these and collecting together

  • all the different things that can be done.

  • Well, let's just look at what these things are.

  • There's a little loop that we go around which says branch,

  • this is the representation of the little flap that decides

  • which way you go here, if 0 fetch of B, the contents of B,

  • and if the contents of B is 0, then go to a

  • place called done.

  • Now, one thing you're seeing here, this looks very much

  • like a traditional computer language.

  • And what you're seeing here is things like labels that

  • represent places in a sequence written down as a sequence.

  • The reason why they're needed is because over here, I've

  • written something with loops.

  • But if I'm writing English text, or something like that,

  • it's hard to refer to a place.

  • I don't have arrows.

  • Arrows are represented by giving names to the places

  • where the arrows terminate, and then referring to them by

  • those names.

  • Now this is just an encoding.

  • There's nothing magical about things like that.

  • Next thing we're going to do is we're going to say, how do

  • we do T gets R?

  • Oh, that's easy enough, assign.

  • We assign to T the remainder.

  • Assign is the name of the button.

  • That's the button-pusher.

  • Assign to T the remainder, and here's the representation of

  • the operation, when we divide the fetch of A by the fetch of

  • B.

  • And we're also going to assign to A the fetch of B, assign to

  • B the result of getting the contents of T. And now I have

  • to refer to the beginning here.

  • I see, why don't I call that loop like I have here?

  • So that's that reference to that arrow.

  • And when we're done, we're done.

  • We go to here, which is the end of the thing.

  • So here's just a written representation of this

  • fragment of machinery that we've drawn here.

  • Now the next thing I'd like to do is run this.

  • I want us to feel it running.

  • Never done this before, you got to do it once.

  • So let's take a particular problem.

  • Suppose we want to compute the GCD of a equals 30

  • and b equals 42.

  • I have no idea what that is right now.

  • But a 30 and b is 42.

  • So that's how I start this thing up.

  • Well, what's the first thing I do?

  • I say is B equal to 0, no.

  • Then assign to T the remainder of the fetch of A and the

  • fetch of B. Well the remainder of 30 when divided by

  • 42 is itself 30.

  • Push that button.

  • Now the marble has rolled to here.

  • A gets B. That pushes this button.

  • So 42 moves into here.

  • B gets C. Push that button.

  • The 30 goes here.

  • Let met just interchange them.

  • Now let's see, go back to the beginning.

  • B 0, no.

  • T gets the remainder.

  • I suppose the remainder when dividing 42 by 30 is 12.

  • I push that one.

  • Next thing I do is allow the 30 to go to here, push this

  • one, allow the 12 to go to here.

  • Go around this thing.

  • Is that done?

  • No.

  • How about--

  • so now I have to find out the remainder of 30 divided by 12.

  • And I believe that's 6.

  • So 6 goes here on this button push.

  • Then the next thing I push is this one, which the

  • 12 goes into here.

  • Then I push this button.

  • The 6 gets into here.

  • Is 6 equal to 0?

  • No.

  • OK.

  • So then at that point, the next thing to do is divide it.

  • Ooh, this has got a remainder of 0.

  • Looks like we're almost done.

  • Move the 6 over here next.

  • 0 over here.

  • Is the answer 0?

  • Yes.

  • B is 0, therefore the answer is in A.

  • The answer is 6.

  • And indeed that's right, because if we look at the

  • original problem, what we have is 30 is 2 times 3 times 5,

  • and 42 is 2 times 3 times 7.

  • So the greatest common divisor is 2 times 3, which is 6.

  • Now normally, we write one other little line here, just

  • to make it a little bit clearer, which is that we

  • leave in a connection saying that this light is the guy

  • that that flap looks at.

  • Of course, any real machine has a lot more complicated

  • things in it than what I've just shown you.

  • Let's look for a second at the first still store.

  • Wow.

  • Well you see, for example, one thing we might want to do is

  • worry about the operations that are of IO form.

  • And we may have to collect something from the outside.

  • So a state machine that we might have, the controller may

  • have to, for example, get a value from something and put

  • register a to load it up.

  • I have to master load up register b with another value.

  • And then later, when I'm done, I might want to print the

  • answer out.

  • And of course, that might be either simple or complicated.

  • I'm writing, assuming print is very simple, and

  • read is very simple.

  • But in fact, in the real world, those are very

  • complicated operations, usually much, much larger and

  • more complicated than the thing you're doing as your

  • problem you're trying to solve.

  • On the other hand, I can remember a time when, I

  • remember using IBM 7090 computer of sorts, where

  • things like read and write of a single object, a single

  • number, a number, is a primitive operation of the IO

  • controller.

  • OK?

  • And so we have that kind of thing in there.

  • And in such a machine, well, what are we really doing?

  • We're just saying that there's a source over here called

  • "read," which is an operation which always has a value.

  • We have to think about this as always having a value which

  • can be gated into either register a or b.

  • And print is some sort of thing which when you gate it

  • appropriately, when you push the button on it, will cause a

  • print of the value that's currently in register a.

  • Nothing very exciting.

  • So that's one sort of thing you might want to have. But

  • these are also other things that are

  • a little bit worrisome.

  • Like I've used here some complicated mechanisms.

  • What you see here is remainder.

  • What is that?

  • That may not be so obvious how to compute.

  • It may be something which when you open it up, you get a

  • whole machine.

  • OK?

  • In fact, that's true.

  • For example, if I write down the program for remainder, the

  • simplest program for it is by repeated subtraction.

  • Because of course, division can be done by repeated

  • subtraction of numbers, of integers.

  • So the remainder of N divided by D is nothing more than if N

  • is less than D, then the result is N. Otherwise, it's

  • the remainder when we subtract D from N with respect to D,

  • when divided by D. Gee, this looks just

  • like the GCD program.

  • Of course, it's not a very nice way to do remainders.

  • You'd really want to use something like binary notation

  • and shift and things like that in a practical computer.

  • But the point of that is that if I open this thing up, I

  • might find inside of it a computer.

  • Oh, we know how to do that.

  • We just made one.

  • And it could be another thing just like this.

  • On the other hand, we might want to make a more efficient

  • or better-structured machine, or maybe make use of some of

  • the registers more than once, or some horrible mess like

  • that that hardware designers like to do, and

  • for very good reasons.

  • So for example, here's a machine that you see, which

  • you're not supposed to be able to read.

  • It's a little bit complicated.

  • But what it is is the integration of the remainder

  • into the GCD machine.

  • And it takes, in fact, no more registers.

  • There are three registers in the datapaths.

  • OK?

  • But now there's a subtractor.

  • There are two things that are tested.

  • Is b equal to 0, or is t less than b?

  • And then the controller, which you see over here, is not much

  • more complicated.

  • But it has two loops in it, one of which is the main one

  • for doing the GCD, and one of which is the subtraction loop

  • for doing the remainder sub-operation.

  • And there are ways, of course, of, if you think about it,

  • taking the remainder program.

  • If I take remainder, as you see over there, as a lambda

  • expression, substitute it in for remainder over here in the

  • GCD program, then do some simplification by substituting

  • a and b for remainder in there, then I

  • can unwind this loop.

  • And I can get this piece of machinery by basically, a

  • little bit of algebraic simplification on the lambda

  • expressions.

  • So I suppose you've seen your first very

  • simple machines now.

  • Are there any questions?

  • Good.

  • This looks easy, doesn't it?

  • Thank you.

  • I suppose, take a break.

  • [MUSIC PLAYING - "JESU, JOY OF MAN'S DESIRING" BY JOHANN

  • SEBASTIAN BACH]

  • PROFESSOR: Well, let's see.

  • Now you know how to make an iterative procedure, or a

  • procedure that yields an iterative

  • process, turn into a machine.

  • I suppose the next thing we want to do is worry about

  • things that reveal recursive processes.

  • So let's play with a simple factorial procedure.

  • We define factorial of N to be if n is 1, the result is 1,

  • using 1 right now to decrease the amount of work I have to

  • do to simulate it, else it's times N factorial N minus 1.

  • And what's different with this program, as you know, is that

  • after I've computed factorial of N minus 1 here, I have to

  • do something to the result.

  • I have to multiply it by N.

  • So the only way I can visualize what this machine is

  • doing, because of the fact--

  • think of it this way, that I have a machine out here which

  • somehow needs a factorial machine in order to compute

  • its answer.

  • But this machine, the outer machine, has to exist before

  • and after the factorial machine, which is inside.

  • Whereas in the iterative case, the outer machine doesn't need

  • to exist after the inner machine is running, because

  • you never need to go back to the outer

  • machine to do anything.

  • So here we have a problem where we have a machine which

  • has the same machine inside of it, an

  • infinitely large machine.

  • And it's got other things inside of it, like a

  • multiplier, which takes some inputs, and there's a minus 1

  • box, and things like that.

  • You can imagine that's what it looks like.

  • But the important thing is that here I have something

  • that happens before and after, in the outer machine, the

  • execution of the inner machine.

  • So this machine has to have a life.

  • It has to exist on both times sides of this machine.

  • So somehow, I have to have a place to store the things that

  • this thing needs to run.

  • Infinite objects don't exist in the real world.

  • What we have to do is arrange an illusion that we have an

  • infinite object, we have an infinite

  • amount of hardware somewhere.

  • Now of course, illusion's all that really matters.

  • If we can arrange that every time you look at some infinite

  • object, the part of it that you look at is there, then

  • it's as infinite as you need it to be.

  • And of course, one of the things we might want to do,

  • just look at this thing over here, is the organization that

  • we've had so far involves having a part of the machine,

  • which is the controller, which sits right over here, which is

  • perfectly finite and very simple.

  • We have some datapaths, which consist of

  • registers and operators.

  • And what I propose to do here is decompose the machine into

  • two parts, such that there is a part which is fundamentally

  • finite, and some part where a certain amount of infinite

  • stuff can be kept.

  • On the other hand this is very simple and really isn't

  • infinite, but it's just very large.

  • But it's so simple that it could be cheaply reproduced in

  • such large amounts, we call it memory, that we can make a

  • structure called a stack out of it which will allow us to,

  • in fact, simulate the existence of an infinite

  • machine which is made out of a recursive

  • nest of many machines.

  • And the way it's going to work is that we're going to store

  • in this place called the stack the information required after

  • the inner machine runs to resume the operation of the

  • outer machine.

  • So it will remember the important things about the

  • life of the outer machine that will be needed for this

  • computation.

  • Since, of course, these machines are nested in a

  • recursive manner, then in fact the stack will only be

  • accessed in a manner which is the last thing that goes in is

  • the first thing that comes out.

  • So we'll only need to access some little part

  • of this stack memory.

  • OK, well, let's do it.

  • I'm going to build you a datapath now, and I'm going to

  • write the controller.

  • And then we're going to execute this to

  • see how you do it.

  • So the factorial machine isn't so bad.

  • It's going to have a register called the value, where the

  • answer is going to be stored, and a registered called N,

  • which is where the number I'm taking factorial will be

  • stored, factorial of.

  • And it will be necessary in some instances to connect VAL

  • to N.

  • In fact, one nice case of this is if I just said over here,

  • N, because that would be right for N equal 1N.

  • And I could just move the answer over

  • there if that's important.

  • I'm not worried about that right now.

  • And there are things I have to be able to do.

  • Like I have to be able to, as we see here, multiply N by

  • something in VAL, because VAL is the result

  • of computing factorial.

  • And I have to put the result back into VAL.

  • So here we can see that the result of computing a

  • factorial is N times the result

  • of computing a factorial.

  • VAL will be the representation of the answer

  • of the inner factorial.

  • And so I'm going to have to have a multiplier here, which

  • is going to sample the value of N and the value of VAL and

  • put the result back into VAL like that.

  • I'm also going to have to be able to see if N is 1.

  • So I need a light bulb.

  • And I suppose the other thing I'm going to need to have is a

  • way of decrementing N. So I'm going to have a decrementer,

  • which takes N and is going to put back the result into N.

  • That's pretty much what I need in my machine.

  • Now, there's a little bit else I need.

  • It's a little bit more complicated, because I'm also

  • going to need a way to store, to save away, the things that

  • are going to be needed for resuming the computation of a

  • factorial after I've done a sub-factorial.

  • What's that?

  • One thing I need is N.

  • So I'm going to build here a thing called a stack.

  • The stack is a bunch of stuff that I'm going to write in

  • sequentially.

  • I don't how long it is.

  • The longer it is, the better my illusion of infinity.

  • And I'm going to have to have a way of getting stuff out of

  • N and into the stack and vice versa.

  • So I'm going to need a connection like this, which is

  • two-way, whereby I can save the value of N and then

  • restore it some other time through that connection.

  • This is the stack.

  • I also need a way of remembering where I was in the

  • computation of factorial in the outer program.

  • Now in the case of this machine, it

  • isn't very much a problem.

  • Factorial always returns, has to go back to the place where

  • we multiply by N, except for the last time, when it has to

  • return to whatever needs the factorial or

  • go to done or stop.

  • However, in general, I'm going to have to remember where I

  • have been, because I might have computed factorial from

  • somewhere else.

  • I have to go back to that place and continue there.

  • So I'm going to have to have some way of taking the place

  • where the marble is in the finite state controller, the

  • state of the controller, and storing that in

  • the stack as well.

  • And I'm going to have to have ways of restoring that back to

  • the state of the-- the marble.

  • So I have to have something that moves the marble to the

  • right place.

  • Well, we're going to have a place which is the marble now.

  • And it's called the continue register, called continue,

  • which is the place to put the marble next

  • time I go to continue.

  • That's what that's for.

  • And so there's got to be some path from that into the

  • controller.

  • I also have to have some way of saving that on the stack.

  • And I have to have some way of setting that up to have

  • various constants, a certain fixed number of constants.

  • And that's very easy to arrange.

  • So let's have some constants here.

  • We'll call this one after-fact.

  • And that's a constant which we'll get into the continue

  • register, and also another one called fact-done.

  • So this is the machine I want to build.

  • That's its datapaths, at least. And it mixes a little

  • with the controller here, because of the fact that I

  • have to remember where I was and restore

  • myself to that place.

  • But let's write the program now which represents the

  • controller.

  • I'm not going to write the define machine thing and the

  • register list, because that's not very interesting.

  • I'm just going to write down the sequence of instructions

  • that constitute the controller.

  • So we have assign, to set up, continue to done.

  • We have a loop which says branch if equal 1 fetch N, if

  • N is 1, then go to the base step of the induction, the

  • simple case.

  • Otherwise, I have to remember the things that are necessary

  • to perform a sub-factorial.

  • I'm going to go over here, and I have to perform a

  • sub-factorial.

  • So I have to remember what's needed after I will

  • be done with that.

  • See, I'm about to do something terrible.

  • I'm about to change the value of N. But this guy has to know

  • the old value of N. But in order to make the

  • sub-factorial work, I have to change the value of N. So I

  • have to remember the old value.

  • And I also have to remember where I've been.

  • So I save up continue.

  • And this is an instruction that says, put

  • something in the stack.

  • Save the contents of the continuation register, which

  • in this case is done, because later I'm going to change

  • that, too, because I need to go back to

  • after-fact, as well.

  • We'll see that.

  • We save N, because I'm going to need that for later.

  • Assign to N the decrement of fetch N. Assign continue,

  • we're going to look at this now, to after, we'll call it.

  • That's a good name for this, a little bit easier and shorter,

  • and fits in here.

  • Now look what I'm doing here.

  • I'm saying, if the answer is 1, I'm done.

  • I'm going to have to just get the answer.

  • Otherwise, I'm going to save the continuation, save N, make

  • N one less than N, remember I'm going to come back to

  • someplace else, and go back and start

  • doing another factorial.

  • However, I've got a different machine [? in me ?] now.

  • N is 1, and continue is something else.

  • N is N minus 1.

  • Now after I'm done with that, I can go there.

  • I will restore the old value of N, which is the opposite of

  • this save over here.

  • I will restore the continuation.

  • I will then go to here.

  • I will assign to the VAL register the product

  • of N and fetch VAL.

  • VAL fetch product assign.

  • And then I will be done.

  • I will have my answer to the sub-factorial in VAL.

  • At that point, I'm going to return by going to the place

  • where the continuation is pointing.

  • That says, go to fetch continue.

  • And then I have finally a base step, which is

  • the immediate answer.

  • Assign to VAL fetch N, and go to fetch continue.

  • And then I'm done.

  • Now let's see how this executes on a very simple

  • case, because then we'll see the use of this stack to do

  • the job we need.

  • This is statically what it's doing, but we have look

  • dynamically at this.

  • So let's see.

  • First thing we do is continue gets done.

  • The way that happened is I pushed this.

  • Let's call that done the way I have it.

  • I push that button.

  • Done goes into there.

  • Now I also have to set this thing up to

  • have an initial value.

  • Let's consider a factorial of three, a simple case.

  • And we're going to start out with our stack

  • growing over here.

  • Stacks have their own little internal state saying where

  • they are, where the next place I'm going to write is.

  • So now we say, is N 1?

  • The answer is no.

  • So now I'm going to save continue, bang.

  • Now that done goes in here.

  • And this moves to here, the next place I'm going to write.

  • Save N 3.

  • OK?

  • Assign to N the decrement of N. That means

  • I've pushed this button.

  • This becomes 2.

  • Assign to continue aft.

  • So I've pushed that button.

  • Aft goes in here.

  • OK, now go to loop, bang, so up to here.

  • Is N 1?

  • No.

  • So I have to save continue.

  • What's continue?

  • Continue is aft.

  • Push this button.

  • So this moves to here.

  • I have to save N. N is over here.

  • I got to 2.

  • Push that button.

  • So a 2 gets written there.

  • And then this thing moves down here.

  • OK, save N. Assign N to the decrement of N.

  • This becomes a 1.

  • Assign continue to aft.

  • A-F-T gets written there again.

  • Go to loop.

  • Is N equal to 1?

  • Oh, yes, the answer is 1.

  • OK, go to base step.

  • Assign to VAL fetch of N. Bang, 1 gets put in there.

  • Go to fetch continue.

  • So we look in continue.

  • Basically, I'm pushing a button over here that goes to

  • the controller.

  • The continue becomes aft, and all of a sudden, the program's

  • running here.

  • I now have to restore the outer version of factorial.

  • So we go here.

  • We say, restore N. So restore N means take the contents

  • that's here.

  • Push this button, and it goes into here, 2, and the

  • pointer moves up.

  • Restore continue, pretty easy.

  • Go push this button.

  • And then aft gets written in here again.

  • That means this thing moves up.

  • I've gotten rid of something else on my stack.

  • Right, then I go to here, which says, assign to VAL the

  • product of N an VAL.

  • So I push this button over here, bang.

  • 2 times 1 gives me a 2, get written there.

  • Go to fetch continue.

  • Continue is aft.

  • I go to aft.

  • Aft says restore N. Do your restore N, means I take the

  • value over here, which is 3, push this up to here, and move

  • it into here, N. Now it's pushing that button.

  • The next thing I do is restore continue.

  • Continue is now going to become done.

  • So this moves up here when I push this button.

  • Done may or may be there anymore, I'm not interested,

  • but it certainly is here.

  • Next thing I do is assign to VAL the product of the fetch

  • of N and the fetch of VAL.

  • That's pushing this button over here, bang.

  • 2 times 3 is 6.

  • So I get a 6 over here.

  • And go to fetch continue, whoops, I go to

  • done, and I'm done.

  • And my answer is 6, as you can see in the VAL register.

  • And in fact, the stack is in the state it

  • originally was in.

  • Now there's a bit of discipline in using these

  • things like stacks that we have to be careful of.

  • And we'll see that in the next segment.

  • But first I want to ask if there are any

  • questions for this.

  • Are there any questions?

  • Yes, Ron.

  • AUDIENCE: What happens when you roll off the end of the

  • stack with--

  • PROFESSOR: What do you mean, roll off of?

  • AUDIENCE: Well, the largest number--

  • a larger starting point of N requires more memory, correct?

  • PROFESSOR: Oh, yes.

  • Well, I need to have a long enough stack.

  • You say, what if I violate my illusion?

  • AUDIENCE: Yes.

  • PROFESSOR: Well, then the magic doesn't work.

  • The truth of the matter is that every machine is finite.

  • And for a procedure like this, there's a limit to the number

  • of sub-factorials I could have.

  • Remember when we were doing the y-operator a while ago, we

  • pointed out that there was a sequence of exponentiation

  • procedures, each of which was a little better than the

  • previous one.

  • Well, we're now seeing how we implement that

  • mathematical idea.

  • The limiting process is only so good as as far as

  • you take the limit.

  • If you think about it, what am I using here?

  • I'm using about two pieces of memory for every recursion of

  • this process.

  • If we try to compute factorial of 10,000, that's

  • not a lot of memory.

  • On the other hand, it's an awful big number.

  • So the question is, is that a valuable thing in this case.

  • But it really turns out not to be a terrible limit, because

  • memory is el cheapo, and people are pretty expensive.

  • OK, thank you, let's take a break.

  • [MUSIC PLAYING - "JESU, JOY OF MAN'S DESIRING" BY JOHANN

  • SEBASTIAN BACH]

  • PROFESSOR: Well, let's see.

  • What I've shown you now is how to do a simple iterative

  • process and a simple recursive process.

  • I just want to summarize the design of simple machines for

  • specific applications by showing you a little bit more

  • complicated design, that of a thing that does doubly

  • recursive Fibonacci, because it will indicate to us, and

  • we'll understand, a bit about the conventions required for

  • making stacks operate correctly.

  • So let's see.

  • I'm just going to write down, first of all, the program I'm

  • going to translate.

  • I need a Fibonacci procedure, it's very simple, which says,

  • if N is less than 2, the result is N, otherwise it's

  • the sum of Fib of N minus 1 and Fib of N minus 2.

  • That's the plan I have here.

  • And we're just going to write down the

  • controller for such a machine.

  • We're going to assume that there are registers, N, which

  • holds the number we're taking Fibonacci of, VAL, which is

  • where the answer is going to get put, and continue, which

  • is the thing that's linked to the controller, like before.

  • But I'm not going to draw another physical datapath,

  • because it's pretty much the same as the

  • last one you've seen.

  • And of course, one of the most amazing things about

  • computation is that after a while, you build up a little

  • more features and a few more features, and all of the

  • sudden, you've got everything you need.

  • So it's remarkable that it just gets there so fast. I

  • don't need much more to make a universal computer.

  • But in any case, let's look at the controller for the

  • Fibonacci thing.

  • First thing I want to do is start the thing up by assign

  • to continue a place called done, called Fib-done here.

  • So that means that somewhere over here, I'm going to have a

  • label, Fib-done, which is the place where I go when I want

  • the machine to stop.

  • That's what that is.

  • And I'm going to make up a loop.

  • It's a place I'm going to go to in order to start up

  • computing a Fib.

  • Whatever is in N at this point, Fibonacci will be

  • computed of, and we will return to the place specified

  • by continue.

  • So what you're going to see here at this place, what I

  • want here is the contract that says, I'm going to write this

  • with a comment syntax, the contract is N contains arg,

  • the argument.

  • Continue is the recipient.

  • And that's where it is.

  • At this point, if I ever go to this place, I'm expecting this

  • to be true, the argument for computing the Fibonacci.

  • Now the next thing I want to do is to branch.

  • And if N is less than 2--

  • by the way, I'm using what looks like Lisp syntax.

  • This is not Lisp.

  • This does not run.

  • What I'm writing here does not run as a simple Lisp program.

  • This is a representation of another language.

  • The reason I'm using the syntax of parentheses and so

  • on is because I tend to use a Lisp system to write an

  • interpreter for this which allows me to simulate the

  • machine I'm trying to build.

  • I don't want to confuse this to think that

  • this is Lisp code.

  • It's just I'm using a lot of the pieces of Lisp.

  • I'm embedding a language in Lisp, using Lisp as pieces to

  • make my process of making my simulator easy.

  • So I'm inheriting from Lisp all of its properties.

  • Fetch of N 2, I want to go to a place

  • called immediate answer.

  • It's the base step.

  • Now, that's somewhere over here, just above done.

  • And we'll see it later.

  • Now, in the general case, which is the part I'm going to

  • write down now, let's just do it.

  • Well, first of all, I'm going to have to

  • call Fibonacci twice.

  • In each case--

  • well, in one case at least, I'm going to have to know what

  • to do to come back and do the next one.

  • I have to remember, have I done the first Fib, or have I

  • done the second one?

  • Do I have to come back to the place where I do the second

  • Fib, or do I have to come back to the place

  • where I do the add?

  • In the first case, over the first Fibonacci, I'm going to

  • need the value of N for computing for the second one.

  • So I have to store some of these things up.

  • So first I'm going to save continue.

  • That's who needs the answer.

  • And the reason I'm doing that is because I'm about to assign

  • continue to the place which is the place I

  • want to go to after.

  • Let's call it Fib-N-minus-1, big long name,

  • classic Lisp name.

  • Because I'm going to compute the first Fib of N minus 1,

  • and then after that, I want to come back and

  • do something else.

  • That's the place I want to go to after I've done the first

  • Fibonacci calculation.

  • And I want to do a save of N, because I'm going to need it

  • later, after that.

  • Now I'm going to, at this point, get ready to do the

  • Fibonacci of N minus 1.

  • So assign to N the difference of the fetch of N and 1.

  • Now I'm ready to go back to doing the Fib loop.

  • Have I satisfied my contract?

  • And the answer is yes.

  • N contains N minus 1, which is what I need.

  • Continue contains a place I want to go to when I'm done

  • with calculating N minus 1.

  • So I've satisfied the contract.

  • And therefore, I can write down here a label,

  • after-Fib-N-minus-1.

  • Now what am I going to do here?

  • Here's a place where I now have to get ready to do

  • Fib of N minus 2.

  • But in order to do a Fib of N minus 2, look, I don't know.

  • I've clobbered my N over here.

  • And presumably my N is counted down all the way to 1 or 0 or

  • something at this point.

  • So I don't know what the value of N in the N register is.

  • I want the value of N that was on the stack that I saved over

  • here so that could restore it over here.

  • I saved up the value of N, which is this value of N at

  • this point, so that I could restore it after computing Fib

  • of N minus 1, so that I could count that down to N minus 2

  • and then compute Fib of N minus 2.

  • So let's restore that.

  • Restore of N.

  • Now I'm about to do something which is superstitious, and we

  • will remove it shortly.

  • I am about to finish the sequence of doing the

  • subroutine call, if you will.

  • I'm going to say, well, I also saved up the continuation,

  • since I'm going to restore it now.

  • But actually, I don't have to, because I'm not

  • going to need it.

  • We'll fix that in a second.

  • So we'll do a restore of continue, which is what I

  • would in general need to do.

  • And we're just going to see what you would call in the

  • compiler world a peephole optimization, which says,

  • whoops, you didn't have to do that.

  • OK, so the next thing I see here is that I have to get

  • ready now to do Fibonacci of N minus 2.

  • But I don't have to save N anymore.

  • The reason why I don't have to save N anymore is because I

  • don't need N after I've done Fib of N minus 2, because the

  • next thing I do is add.

  • So I'm just going to set up my N that way.

  • Assign N minus difference of fetch N and 2.

  • Now I have to finish the setup for calling

  • Fibonacci of N minus 2.

  • Well, I have to save up continue and assign continue,

  • continue, to the place which is after Fib N 2, that place

  • over here somewhere.

  • However, I've got to be very careful.

  • The old value, the value of Fib of N minus 1, I'm going to

  • need later.

  • The value of Fibonacci of N minus 1, I'm going to need.

  • And I can't clobber it, because I'm going to have to

  • add it to the value of Fib of N minus 2.

  • That's in the value register, so I'm going to save it.

  • So I have to save this right now, save up VAL.

  • And now I can go off to my subroutine, go to Fib loop.

  • Now before I go any further and finish this program, I

  • just want to look at this segment so far and see, oh

  • yes, there's a sequence of instructions here, if you

  • will, that I can do something about.

  • Here I have a restore of continue, a save of continue,

  • and then an assign of continue, with no other

  • references to continue in between.

  • The restore followed by the save

  • leaves the stack unchanged.

  • The only difference is that I set the continue register to a

  • value, which is the value that was on the stack.

  • Since I now clobber that value, as in it was never

  • referenced, these instructions are unnecessary.

  • So we will remove these.

  • But I couldn't have seen that unless I had

  • written them down.

  • Was that really true?

  • Well, I don't know.

  • OK, so we've now gone off to compute

  • Fibonacci of N minus 2.

  • So after that, what are we going to do?

  • Well, I suppose the first thing we have to do--

  • we've got two things.

  • We've got a thing in the value register

  • which is now valuable.

  • We also have a thing on the stack that can be restored

  • into the value register.

  • And what I have to be careful with now is I want to shuffle

  • this right so I can do the multiply.

  • Now there are various conventions I might use, but

  • I'm going to be very picky and say, I'm only going to restore

  • into a register I've saved from.

  • If that's the case, I have to do a shuffle here.

  • It's the same problem with how many hands I have. So I'm

  • going to assign to N, because I'm not going to need N

  • anymore, N is useless, the current value of VAL, which

  • was the value of Fib of N minus 2.

  • And I'm going to restore the value register now.

  • This restore matches this save. And if you're very

  • careful and examine very carefully what goes on,

  • restores and saves are always matched.

  • Now there's an outstanding save, of course, that we have

  • to get rid of soon.

  • And so I restored the value register.

  • Now I restore the continue one, which matches this one,

  • dot, dot, dot, dot, dot, dot, dot, down to here, restoring

  • that continuation.

  • That continuation is a continuation of Fib of N,

  • which is the problem I was trying to solve, a major

  • problem I'm trying to solve.

  • So that's the guy I have to go back to who wants Fib of N. I

  • saved them all the way up here when I realized N was

  • not less than 2.

  • And so I had to do a complicated operation.

  • Now I've got everything I need to do it.

  • So I'm going to restore that, assign to VAL the sum of fetch

  • VAL and fetch of N, and go to continue.

  • So now I've returned from computing Fibonacci of N, the

  • general case.

  • Now what's left is we have to fix up a few details, like

  • there's the base case of this induction, immediate answer,

  • which is nothing more than assign to VAL fetch of N,

  • because N was less than 2, and therefore, the answer is N in

  • our original program, and return continue--

  • bobble, bobble almost--

  • and finally Fib done.

  • So that's a fairly complicated program.

  • And the reason I wanted you see to that is because I want

  • you to see the particular flavors of stack discipline

  • that I was obeying.

  • It was first of all, I don't want to take anything that I'm

  • not going to need later.

  • I was being very careful.

  • And it's very important.

  • And there are all sorts of other disciplines people make

  • with frames and things like that of some sort, where you

  • save all sorts of junk you're not going to need later and

  • restore it because, in some sense, it's easier to do that.

  • That's going to lead to various disasters, which we'll

  • see a little later.

  • It's crucial to say exactly what you're

  • going to need later.

  • It's an important idea.

  • And the responsibility of that is whoever saves something is

  • the guy who restores it, because he needs it.

  • And in such discipline, you can see what things are

  • unnecessary, operations that are unimportant.

  • Now, one other thing I want to tell you about that's very

  • simple is that, of course, the picture you see is not the

  • whole picture.

  • Supposing I had systems that had things like other

  • operations, CAR, CDR, cons, building a vector and

  • referencing the nth element of it, or things like that.

  • Well, at this level of detail, whatever it is, we can

  • conceptualize those as primitive

  • operations in the datapath.

  • In other words, we could say that some machine that, for

  • example, has the append machine, which has to do cons

  • of the CAR of x with the append of the CDR of x and y,

  • well, gee, that's exactly the same as

  • the factorial structure.

  • Well, it's got about the same structure.

  • And what do we have?

  • We have some sort of things in it which may be registers, x

  • and y, and then x has to somehow move to y sometimes, x

  • has to get the value of y.

  • And then we may have to be able to do

  • something which is a cons.

  • I don't remember if I need to like this is in this system,

  • but cons is sort of like subtract or add or something.

  • It combines two things, producing a thing which is the

  • cons, which we may then think goes into there.

  • And then maybe a thing called the CAR, which will produce--

  • I can get the CAR or something.

  • And maybe I can get the CDR of something, and so on.

  • But we shouldn't be too afraid of saying things this way,

  • because the worst that could happen is if we open up cons,

  • what we're going to find is some machine.

  • And cons may in fact overlap with CAR and CDR, and it

  • always does, in the same way that plus and minus overlap,

  • and really the same business.

  • Cons, CAR, and CDR are going to overlap, and we're going to

  • find a little controller, a little datapath, which may

  • have some registers in it, some stuff like that.

  • And maybe inside it, there may also be an infinite part, a

  • part that's semi-infinite or something, which is a lot of

  • very uniform stuff, which we'll call memory.

  • And I wouldn't be so horrified if that were the way it works.

  • In fact, it does, and we'll talk about that later.

  • So are there any questions?

  • Gee, what an unquestioning audience.

  • Suppose I tell you a horrible pile of lies.

  • OK.

  • Well, thank you.

  • Let's take our break.

  • [MUSIC PLAYING - "JESU, JOY OF MAN'S DESIRING" BY JOHANN

  • SEBASTIAN BACH]

[MUSIC PLAYING - "JESU, JOY OF MAN'S DESIRING" BY JOHANN

Subtitles and vocabulary

Click the word to look it up Click the word to find further inforamtion about it