CPU Without Infinite Loop

classic Classic list List threaded Threaded
17 messages Options
Reply | Threaded
Open this post in threaded view
|

CPU Without Infinite Loop

Rodolfo
Hello there!

Quick curious questions. Page 77 of the book shows two programs as examples. It explains that the recommended way to terminate a Hack program is to enter an infinite loop at the end. Without it, it may lead to [...] potentially hazardous consequences.

I understand that it will just try to execute whatever instructions are in the memory after that, but why would it be hazardous?

Again, just a curious question. Thanks!
Reply | Threaded
Open this post in threaded view
|

Re: CPU Without Infinite Loop

rleininger
If you are running programs merely for educational purposes there is probably no concern with just letting the PC continue counting after the program you are executing finishes.  This should be considered poor practice though.

Since the Hack assembly language includes no "Halt" instruction, the suggested mechanism provides a way to trap execution at a single point.  Some kinds of programs may need to be terminated at various points in the code, so "jumping to END" from these points provides a means to do this in a consistent manner.

If your Hack computer is managing the reactor control rods in a nuclear power plant, you might be advised to maintain control at all times.  Hazardous? ... Maybe.
Reply | Threaded
Open this post in threaded view
|

Re: CPU Without Infinite Loop

WBahn
Administrator
In reply to this post by Rodolfo
You don't want your computer wandering through memory executing whatever junk it finds there, since you have no idea what will happen. If your computer is controlling things in the real world, the consequences could be disastrous. Even if it's not, you risk messing things up -- interpreting the contents of memory to be a command to change the contents of the screen, corrupting the image your program just created, or initiating a low-level format of your hard drive. You want your computer to always be well-behaved, and that means always doing what you want and expect it to do. If you can't stop the clock or otherwise put it to sleep, it will insist on executing instructions, so give it something benign to do with its time, like run an infinite loop.
Reply | Threaded
Open this post in threaded view
|

Re: CPU Without Infinite Loop

Rodolfo
In reply to this post by rleininger
Clear now. Thank you!
Reply | Threaded
Open this post in threaded view
|

Re: CPU Without Infinite Loop

Rodolfo
In reply to this post by WBahn
Thank you for the explanation!
Reply | Threaded
Open this post in threaded view
|

Re: CPU Without Infinite Loop

rleininger
I have given this question a bit (no pun intended) more thought and offer the following additional observations.

Although the Hack computer may be generally regarded as an instance of the von Neumann architecture, its memory and bus paradigm follows the so-called the Harvard model.  The memory for program instructions (read-only) and program data (read-write) are managed in physically separate memory units and are transmitted on physically separate buses.  If the Hack computer overruns the end of the desired program and executes spurious instructions, it's the programmer's fault because he's the one who put them there.  The program memory cannot change as a result of execution.

If all ROM memory locations after the program are set to 0x0000, the computer will execute them as @0, a completely benign instruction.  When execution gets to the end of ROM, the emulator actually traps  the error and provides the message: "At line 32767: Can't continue past last line".  I guess you could put the instructions:
    @32767
    0;JMP
at the end of your program and let the emulator halt execution as an error trap...ugly though.

In a pure von Neumann design, where the instructions and data are contained in the same memory address space and share the same bus, the situation is a somewhat different.  Because the memory where the program instructions reside is read-write, programmer has no complete control over what might be written as data as program execution proceeds, and in some cases may not know exactly where in memory the data will be stored.  If the program is not somehow terminated from further execution after it "completes" and continues to read data as instructions, it is quite probable an error will occur.  In the early days of microprocessor-based computers, this was quite common, but was almost always the result of bad programming.  Modern microprocessors have logic and operating system safeguards to prevent this.

Nevertheless, it is good practice to insure that a program terminates safely.  
Reply | Threaded
Open this post in threaded view
|

Re: CPU Without Infinite Loop

WBahn
Administrator
Even with a Harvard architecture machine it's not so clear cut. Furthermore, what an emulator does is not always the same as what the hardware does. If someone were to physically implement the Hack CPU in hardware (and it's been done in FPGAs, for instance, though you need to expand on it to make it actually useful), the program memory may or may not be all zeroes outside the program space. That will depend on the technology at play. In and FPGA implementation, the ROM will likely consist of RAM cells within the FPGA's resources, so the programmer would have to explicitly establish them or they will likely be random garbage.

Even if a true ROM were used to store the program, many ROMs start with everything set to 1 and the programmer clears those bits that are supposed to be zero. If the software that takes the .hack file, which only defines the actual program, not the entire program instruction space, doesn't take the added step of clearing all of the rest of the bits, then they will be the all 1 instruction, which will be seen as a C-type instruction that does an unconditional JMP to whatever address happens to currently be stored in the A-register.

Even if it is all zeroes, most implementations would still wrap around back to ROM[0] after reaching the end of the program memory, regardless of what a particular emulator does. You couldn't count on this type of behavior unless it was in the hardware specification for the CPU. This emulator does several things that the actual hardware would not, and doesn't do several things that it would.

Reply | Threaded
Open this post in threaded view
|

Re: CPU Without Infinite Loop

rleininger
If hardware cannot be built that behaves exactly like the emulator, what's the point of using software tools to design computers?  As far as I'm concerned, the CPU emulator provided by the team that devised the Hack architecture and its associated ISA defines a Hack computer.  Any actual hardware computer or emulator that does not behave exactly as the "official" Hack emulator is not a Hack computer; it's merely Hack-like.  In this regard the Hack computer does not  wrap around to ROM[0], it traps the attempted continued execution past the end of ROM as an error. (At least the version I'm running does.)

Doesn't the specification for the Hack program counter call for a 16 bit register?  After the PC reaches the end of defined ROM at address 32767, wouldn't it increment to 32768, not wrap to 0?  I suspect this is how the emulator program traps the overrun condition, but  I haven't looked at the Java code.  

Regarding the ROM, my point was that in programming it, the programmer is responsible for the entire ROM address space, including those registers that do not contain any program code.  Those registers should be set by the programmer to benign values.  Once the ROM is programmed, none of these registers can be changed.

Of course, what we have with the Hack emulator for program memory is not true ROM.  It is simulated ROM that may be re-programmed before execution begins.  Good practice would be to clear the current program with the button provided in the interface before loading a new program.  Actually, I'm not sure what "value" the clear action puts in all ROM locations because the Hack emulator ROM is actually designed to contain strings of binary digits not actual binary values.  No value is shown in the interface, so I guess it's a null string. But execution of a cleared ROM register seems to have absolutely no effect on the operation of the CPU.  It is definitely not an @0 instruction as I previously supposed.  It is, however, seemingly benign.  In a real ROM device, you'd have to set registers to a binary value.  Burning each unused register to 0x000 (@0 instruction) would be benign for the Hack computer.

Don't get me wrong,  I completely and without reservation agree with you that all Hack programs should be terminated with the suggested code sequence to avoid any as yet undiscovered "hazards".  I never meant to suggest otherwise.  Nevertheless, the programmer is ultimately responsible for correct and "safe" operation of the computer.
Reply | Threaded
Open this post in threaded view
|

Re: CPU Without Infinite Loop

WBahn
Administrator
This post was updated on .
An emulator does NOT define a hardware specification (not unless it very clearly states that to be the case, which is not the case here). In the case of the Hack, the authors acknowledge that the software tools behave differently than nearly any implementation would in several ways. Some of these are deliberate, such as trapping things that are almost certainly logic errors in an attempt to help students identify and correct those errors. This is especially evident in the VM emulator where dereferencing the THIS or THAT pointers to access memory outside of the heap or screen get trapped as errors even though there is nothing in the specification that even hints that this is required behavior. Furthermore, the CPU emulator certainly doesn't enforce such behavior, so the CPU emulator and the VM emulator are inconsistent in their behaviors, so which one would define the hardware specification?

With regards to the CPU emulator specifically, there are 28 defined C-type instructions. Yet a program could use any of the 128 possible bit patterns available. The CPU specification makes it quite clear what the intended effect of each of the seven control bits should be. But the emulator does not conform to this for a large fraction of the patterns not in the 28 instructions that are formally part of the ISA. This is because the emulator only cares about behaving correctly for those 28 patterns and lets whatever happens for the remaining 100 possibilities happen. Does that really mean that someone that wanted to build a Hack in hardware has to map out what the emulator happens to do and then design the hardware to exactly match it in order to have a true Hack computer (and therefore ignore what the actual ALU and CPU specifications state)?

For another example, consider the initial state of the RAM content. All of the tools assume that the RAM always starts off with all zeroes in it, but this is not how actual RAM works. So does that mean that because that is how the emulator behaves, that a true Hack implementation must include the behavior of initializing the entire RAM content to zero upon a reset?

Most professional tools are not realistic on this point. Digital simulators (and I'm talking about quarter-million-dollar-per-license type simulators) generally let you configure memory elements to either start out as all zeros, all ones, or as "unknown". The latter sounds great, but doesn't match reality in a very significant way, which I'll illustrate with a real example. Many of the chips I helped design had gray code counters on them that, upon power up, could be running either forward or backward depending on the state the registers came up in. But there was a simple two-gate logic circuit that would detect that it was running in the wrong direction the first time it wrapped around and reverse it if necessary. This not only obviated the need for special power-on reset circuitry, but also meant that it would automatically recover from register upset events resulting from the high radiation environment some of the chips operated in. But we couldn't validate the design using the "unknown" power up mode of the simulators because if you clock a counter that is in an "unknown" state, it remains in an "unknown" state. In the actual hardware, if you clock it enough times, it WILL overflow and the direction get properly defined, but the simulators don't behave that way. Thus we could not faithfully emulate the explicitly specified behavior of the chip. Although I've been out of that game for quite some time, I'm still not aware of a single circuit or logic simulator that implements a "random" power-up mode to properly validate these types of defined behavior (though I and others requested it enough times that maybe -- hopefully -- someone has done it by now). A similar situation exists for charge-storage nodes in analog simulators.

What is being discussed here is classic undefined behavior. The various specifications at all levels, be it ALU, CPU, VM, or Jack, are incomplete (as is every real specification to one degree or another) and anything that they do not specify is undefined and any implementation is free to implement any behavior at all in those situations. It can trap, it can cause a reset, it can start a global thermonuclear war, or it can do exactly what you would hope and think it should -- and any of those (and more) are equally valid behaviors; there is no expectation that any two implementations do it the same way, and neither implementation can make a claim to being more "right" than the other. This is even the case with those 100 instructions. You can make a very strong argument that the ALU's specification fully defines the behavior, at least implicitly, for all 64 ALU commands (because of the way it is represented in terms of the descriptions of each control signal as opposed to just defining the mapping between eighteen seemingly arbitrary 6-bit commands and the corresponding operations (and many ISA specifications are essentially just that). But the CPU specification only requires that the 18 members of the ISA (plus ten additional ones that result from the 'a' bit's influence) be implemented. So one Hack could implement it the "obvious" way and use the ALU and let the other 100 instructions do what you would expect, or it could implement the ALU in the CPU using a look-up table that only implements the 28 ISA instructions and produces garbage for the rest (which is what the CPU emulator does), or it could detect non-ISA instructions and cause a hardware exception of some sort. All are equally valid.

We definitely agree that it is the programmer's responsibility, to the degree possible (or at least reasonable) to ensure that the computer behaves properly. But a big part of this is avoiding undefined behavior in the first place, and not relying on how their particular environment happens to behave in such circumstances. One place where this is particularly common is programs written in the C programming language, which very intentionally left many things either undefined or implementation defined. But lots of programmers have developed coding habits that have relied on simply observing how those things worked using a certain compiler and then assuming that that was how the language itself behaves because they are often completely unaware of the very notion of undefined behavior.
Reply | Threaded
Open this post in threaded view
|

Re: CPU Without Infinite Loop

rleininger
First of all, thanks for continuing this discussion.  Since I retired about 5 years ago, I am no longer around anyone who shares my interest in computers, so discussion opportunities are few and far between.  My background is in system analysis and software development (Fortran -> C -> C++ -> Java -> Python) and not in hardware at all, so a lot of this is new to me.  Consequently, I have a lot of questions.

Regarding the question you raised about the CPU emulator versus the VM emulator defining the hardware:  Isn't the VM completely a software layer on the hardware platform.  It's description is in the section on software in the second edition of the book.  Why would the VM emulator then be expected to have any impact of the hardware implementation.  It's after the fact.  If the VM emulator and CPU emulator are inconsistent in their operation, this would seem to be a problem with the emulator programs themselves. Now if you added a hardware stack pointer register to the CPU hardware for facilitating the implementation of the VM, well that would be altogether different.

You comments in the last two paragraphs place us in complete agreement.  I have looked at a few other sources that treat building a hardware computer based on a prescribed ISA.  See, for example, the SAP-1, -2, and -3 computers described in Digital Computer Electronics (Malvino and Brown).  Clearly the Hack computer is not described in Elements of Computing Systems  in sufficient non-ambiguous detail to allow for the construction of a computer without probable and inconsistent undefined behaviors between various independent implementations.

I can relate to your comments about things  being left undefined or implementation defined with C, the first language I did any useful work with back in the mid-1980s.  Defensive programming was a way of life.

Thanks again.
Reply | Threaded
Open this post in threaded view
|

Re: CPU Without Infinite Loop

WBahn
Administrator
Like you, I enjoy these free-wheeling discussions that delve into off-the-beaten-path topics. There's always new things to learn.

Whether the behavior expressed by the VM emulator is implemented in hardware or software is not the point I was trying to make, so I apologize if I generalized it to the point of making it seem that way. It's the notion that an emulator's behavior defines the corresponding specification that I'm taking issue with. Both the CPU emulator and the VM emulator exhibit behaviors that are beyond the specifications given. That does not mean that they extend and further define the specification, but rather only that the people that implemented them made decisions about what behavior they wanted to have (or, in the case of the non-ISA instructions, behaviors they didn't care about) in their particular implementations regarding things that were not defined by the specification.

Since the Hack was not intended to be used outside of the course, the specification given does not address things that are not required to get through the course projects. To the degree that the behavior of a program once it finishes is dealt with, it is done so by having the student enter an infinite loop that is benign and this is considered sufficient for the purposes of the course. Thus, the behavior of the hardware upon reaching the end of the program memory is left undefined and any emulator is free to deal with it any way they choose to. Because it is undefined, there is no right or wrong answer and different choices have different pros and cons. Relying on ANY of them as the basis for how any other implementation will behave is inviting disaster. Even relying on a particular implementation to consistently behave the way it has been observed to behave up to that point is risky. Not only could an update change that behavior, but it's also possible that the observed behavior is incomplete -- what if the behavior upon reaching the end of the ROM space turned out to be dependent on the current contents of either the A or the D register or the value in RAM that is currently being accessed? If the behavior is incidental, and not planned, then these kinds of strange interactions are quite possible (I've seen them in real hardware). In the case of the behavior of the CPU emulator, that behavior appears to be very deliberate and so it is highly likely that it always exhibit that behavior (not guaranteed, but that's certainly were I would place my bet). But the next version may not, since it is being written by someone completely different than the current version and they may make very different choices.


I'm not quite sure what you mean when you say that executing the non-ISA instructions in the CPU emulator results in entirely predictable results. I found it anything but predictable. There are several instructions that could be very useful (for instance, the ALU, using the signal behaviors given, implements 14 of the 16 bitwise boolean functions (XOR and XNOR being the two exceptions) and some of these are very useful for reducing instruction count when doing bit masking operations. But most of them do not behave this way in the CPU emulator and if you explore them a bit you discover that reversing out what some of them actually do is a bit easier said than done. It's possible (though I'm assuming unlikely) that they aren't even self-consistent or even deterministic (they may involve uninitialized variables, for instance). I'd have to dig into the Java code to figure that out.

Shifting gears, you had mentioned in an earlier post the fact that the ROM program space is 15 bits while the Program Counter is 16 bits. This is reflective merely of the use of the previously defined 16-bit register and adder parts in the construction of the Program Counter and, I'm pretty sure, nothing more. The project specification for the CPU is silent on the behavior of the CPU should the msb become set. Certainly it would be possible to use that bit to trap things and halt the machine until a hardware reset is issued. But it is equally reasonable to just ignore that bit so that the ROM contents are simply mirrored into the upper half of the ROM space (and this is what the CPU specification strongly implies). Doing so leaves open the option to extend the usable ROM size to 64k instructions. There are a few reasonable ways to make those instructions accessible to programs, ranging from using one of the two reserved bits in the C-type instructions to using one of the RAM locations as a paging register. So even if the Hack was intended to be specified for actual production, this behavior probably would have been left undefined to allow manufacturers to offer which ever behavior made sense for their target market.
Reply | Threaded
Open this post in threaded view
|

Re: CPU Without Infinite Loop

rleininger
I guess the only other thing I have to say about the Hack emulators is that they were created (or at least sanctioned) by the same people who created the hardware “specification” for the Hack computer.  Doesn’t it seem reasonable, then, that the CPU emulator (at least) accurately reflects their thinking about how the computer is supposed to behave?  It represents the “standard” or "reference" implementation.  When the specification is silent or ambiguous, test on the reference emulator if possible.  This is exactly the paradigm used by K&R for the pre-ANSI C language that was described, but not really formally specified, in Appendix A (1. Introduction) of the white book.  They had a C compiler implemented on 4 different computer hardware architectures, but they basically identified the one on the DEC PDP/11 as the reference for those writing compilers for other architectures.

You are, of course, correct that in some cases (perhaps many cases) a test result you get may not be very useful, as it depends on the complete machine state at the time of the test, including all registers and data memory values.  Of course, testing all possible initial conditions for a particular comp code is not practically possible, as there are well over a billion initial conditions foe each test.

But for me, the fact that the computer emulator halts when it reaches the end of ROM, which doesn't depend on any other register state, indicates that this was the intent of the designers, and that this behavior should be implemented for any version of the Hack computer.  Then again, I’d really have to look at the emulator code to verify this.

I am hopeful we will get a new version of the CPU emulator to go along with a new and improved version of the course.  This may be happening.  Have you noticed that the slide decks on the project page have been significantly revised and extended for the first six chapters?  If a new emulator appears, I will consider it to be the reference implementation of the Hack platform.

So I probably didn’t express myself very well regarding my comment about the predictability of the non-ISA instructions.    The ALU is a relativity simple finite state device.  Given a set of 6 control codes, it will always compute something from its current input operands.  I’ve looked at Robert Woodhead’s paper on examining the arithmetic or logical result all 64 of the possible control codes based on their “microcode instructions”, and it appears that in some cases, as you say, sometimes a useful result is produced from a non-ISA sanctioned code.  Many other code combinations apparently just produce duplicate results of the defined ISA instructions.  The idea I meant to convey was that, given a pair of 16-bit binary values as operands and a 6-bit op code, the ALU will always provide a determinable result, irrespective of its relevance regarding the Hack computer architecture.  Obviously, the operation of the ALU alone does not define the Hack computer.

If you are programming in Hack assembly language and not entering machine code directly (which the emulator allows you to do), is it possible to specify a non-ISA sanctioned instruction?  The assembler I wrote flags them as errors and fails to produce a .hack file.  Does the assembler provided by the authors do syntax checking?  The one built into the front end of the emulator seems to.

Gear shift.  You are completely right.  I entirely ignored the fact that the instruction address bus is only 15 bits wide, so that if the PC overruns the ROM address space (PC MSB==1), the address seen on the bus reverts to 0x0000.  You are correct that this is implied by the description of the computer, but the reference implementation indicates to me that the authors intent was something else.

Bank switching the ROM is an interesting idea.  Since the @ instruction can only specify a 15 bit address, how would you perform jumps from low bank to the high bank?  You suggest additional programming I think.  No thanks.
Reply | Threaded
Open this post in threaded view
|

Re: CPU Without Infinite Loop

WBahn
Administrator
A couple of points.

Let's consider, for a moment, the reference implementation for the original K&R C compiler that you referred to. Despite the fact that it was put forth as the reference implementation, that did NOT mean that anyone writing a conforming C compiler had to do exactly what the reference implementation did. In point of fact, the C language, including K&R C, has LOTS of undefined and implementation defined behaviors. But that compiler did something in each of those cases. So to claim that the reference implementation somehow fills in any gaps and defines what the specification "really intended" is to say that C has no undefined or implementation-defined behavior. Take the classic example of

i = i++ + ++i;

To this day this is undefined behavior -- there's no requirement that a compiler even state what it does. Run it on several different compilers and you will get several different answers. But that PDP-11 reference implementation had to do something with that statement. I have no idea what it did and, if I were writing a C compiler, I would not care. Because my compiler can do ANYTHING, including trap it as an error, issue a warning, do one of the many computations that result from the possible paths through the side-effects, or simply return a constant result of 42. The behavior for that statement is simply undefined and the fact that some reference implementation handled it a particular way does not make its behavior any more or any less "correct" than any other behavior.

Another example is the result of x / y when x and y are both integers and either x or y is negative. The older versions of the standard require only that the compiler specify whether it rounds toward zero or toward negative infinity (an example of implementation-specified behavior). But, again, that PDP-11 reference implementation chose one or the other. Doesn't matter. That says nothing about the intent of the language. That particular implementation merely made a choice about what to do in a situation in which the language specification gave latitude. It wasn't until a very recent version of the standard that this became language-specified and now rounding toward zero is required in new conforming compilers (which, to my mind, is a shame because this means that the remainder operator does not perform the commonly accepted modulo operation when negative numbers are involved).

In the case of the Hack CPU Emulator, something has to happen when the PC gets to the end of the ROM memory space. The specification is silent, but every implementation HAS to do something. So the grad student that wrote the emulator made a decision. It might have been a conscious decision (I suspect it was, in this case), or it might have been simply ignored and what we see is just what their program happened to do. In either case, maybe they consulted with the authors and maybe they didn't. Doesn't matter. That person's choice does not become required behavior; assuming that it reveals something about the intent of the authors is a risky assumption that can come back and bite you.

With regards to the non-ISA ALU commands, consider the following example:

a = 0
zx = 0
nx = 1
zy = 0
ny = 1
f = 0
no = 0

This then performs:

(!D) & (!A)

which is logically the same as

!(D|A)

In other words, the bitwise NOR of D and A (a potentially very useful instruction).

Now write the following assembly program:

@10
D=A
@12
D=D|A
@R0
M=D

Assemble it and go into the .hack file and make the no bit for ROM[3] equal to zero instead of one.

Now load it and run the program.

If the CPU Emulator implemented the ALU per its specification, then the result should be

10 = b1010
12 = b1100
01 = b0001 (NOR of 10 and 12)

But the emulator yields 12.

If you swap the @10 and @12 instructions, the result is 10. So the CPU Emulator does not even treat this as a symmetric instruction.

It seems a huge stretch to claim that this behavior somehow reveals the authors intent regarding how the CPU was meant to behave. But if we are going to assume that the behavior of the CPU Emulator when it reaches the end of ROM reveals the authors intent for no other reason than the authors "sanctioned" the emulator, then by that reasoning this must also be their intent.

Just because an implementation happens to do what we think makes sense does NOT mean that it is either correct or intended behavior. The behavior is still undefined and all we know is that that particular implementation does something that we like.

But I know for a fact that, in this case, it wasn't what the authors would have intended -- just that they didn't give it any thought. The authors were completely unaware of this behavior (and the behavior of the emulator for the other non-ISA commands). When I requested that the new emulator faithfully implement the hardware architecture presented in Chapter 5 (because I require my Compiler Design students to support the full instruction set for pedagogical reasons), they were a bit surprised to learn that it didn't and agreed that it should. But since the intent of the course doesn't involve anyone running code that uses these instructions, it's not surprising that they were never aware of it and I suspect that the person that wrote the emulator didn't even consider the behavior. They almost certainly used a dictionary that looked up the 28 defined bit patterns and chose to "do something other than crash" (which, in and of itself is a non-specified behavior) if the 7-bit opcode wasn't in the dictionary. I thought I had reversed out what they did, but the more I looked the more I realized that the behavior isn't as simple as I thought it was at first. As you noted, there are four billion possible combinations and if the "reference emulator" somehow defined the behavior, then I would have to examine ALL of them for each of the 100 non-ISA instructions to determine what the "required" behavior was and then design the hardware accordingly. That would be insane.

Another example, in both the Assembler and the CPU emulator, has to do with the commutability of the symmetric operations. For instance, the defined mnemonic to add the contents of the D and the A registers is "D+A", but the supplied tools also accept "A+D". This is NOT part of the defined instruction set and the fact that the supplied assembler accepts it does NOT mean that the authors intent was for a Hack assembler to have to accept both. The impact of this is that people can (and do) write assembly programs that the supplied tools will assemble and run, doing just what the person thinks they should. But those same programs won't run on a strictly conforming assembler that they write for Project 6. Furthermore, by accepting both, it blurs the distinction between an expression and a mnemonic instruction. "D+A" is NOT an expression, it is a string of characters representing one of 28 defined instructions. But because the tools accept both "D+A" and "A+D" students lose the opportunity to have this point driven home early on. So it's not surprising that they fail to grasp that "D+42" is not allowed since "D+1" is and why should the value of the number being added matter?

In fact, it was very much NOT the intent of the authors that the tools accept both -- that was a decision that the person writing the tools made on their own -- and the new assembler will NOT accept the latter (that requirement is being written into the specification for the new provided tools, I don't know if it will be part of the public specification for the tools the students write -- I suspect not in an effort to keep the specs as simple and clean as possible).
 





Reply | Threaded
Open this post in threaded view
|

Re: CPU Without Infinite Loop

rleininger
Again, thanks for the enlightening discussion.  I really appreciate the time you have taken to help me.

Of course, the PDP-11 argument is now moot, as there is now an official standard for the C language which does identify undefined behaviors.  The ANSI edition of the white book (although my copy was published before the spec was adopted) makes no reference to any specific reference implementation in the Appendix.

Interestingly, I wrote programs (in Fortran, not C) to support my employer's CAD system in the early 80's on a PDP-11.  The O/S was RSX-11, not Unix (Ultrix), so there was no C compiler.  When we switched to the VAX architecture (VMS, still my favorite O/S) in 1985, I finally persuaded them in about 1990 to buy the DEC C complier.  I had learned C (Turbo C) from the first edition of K&R on (God help us all, an MS-DOS PC).

Your example of getting the emulator to execute a non-ISA instruction by bit-fiddling seems a bit sketchy from a programming perspective, but nevertheless clever.  The idea opens new ideas for experimentation.

The D+A / A+D thing you cite is really interesting.  When an assembly program containing the line D=A+D in source is read into the CPU emulator, it's actually rendered in the interface (ASM mode) as D=D+A.  Shame on the programmer that did this.  It's clearly wrong with respect to the Hack assembly language description.  (I still hesitate to user the word "specification" because it's so lacking in sufficient detail.  Although maybe this was intended because of the requirement for conciseness.)

I used to write programs from "customer" supplied specs, and I learned early on, that if you implement based on your interpretation of an ambiguous or insufficient spec without clarification by the customer, you won't get paid (without a re-write).

I think I finally see your point about considering the CPU emulator as a reference implementation.  My takeaway here is the the public description of the Hack computer hardware and software is, at best, incomplete.  As a pedagogical tool, the Hack computer hits a good median between LMC and SAP-1, -2, -3.

I think we have finally beat this topic to death.  

Reply | Threaded
Open this post in threaded view
|

Re: CPU Without Infinite Loop

WBahn
Administrator
I agree that getting the current tools to execute a non-ISA instruction involves some sketchy stuff, it's just the cleanest way that I know of.

When I have my students write their assembler, I give them an extended spec that includes the 128 Xnn mnemonics where nn is replaced with the hex representation of the control bits. It was when writing some test programs that the nonsensical behavior of the provided CPU Emulator became evident.

I fully agree with you regarding customer specs. One thing that I hammer over and over is that an engineer should never simply "fill in the gaps" in a customer specification unless it is absolutely necessary (which is seldom the case). Communicate with the customer and get on the same page. This can be a lengthy and painful process for both parties because few customers really know what they need or want -- they just think they do.  
Reply | Threaded
Open this post in threaded view
|

Re: CPU Without Infinite Loop

rleininger
I see that you have dealt with a lot of customers.
Reply | Threaded
Open this post in threaded view
|

Re: CPU Without Infinite Loop

WBahn
Administrator
rleininger wrote
I see that you have dealt with a lot of customers.
I was the senior engineer at a company that designed full-custom ASICs (Application Specific Integrate Circuits) and we specialized in what I called "lunatic fringe" projects that other design houses had said were impossible (or infeasible) to do. So we had to deal with lots of off-the-wall projects and correspondingly off-the-wall customers. But we managed a very impressive first-silicon success rate and that was in large part due to having the engineer designing the circuit also be the primary (and often sole) technical point of contact with the customer. That engineer was also the primary person for laying out the circuit, doing the fab submission, designing and building the test system, and testing it when it came back. So we were truly motivated to keep that entire chain in mind when we were devising a design.

But even without that, I tell students that an engineer is, first and foremost, a problem solver. If your customer knew how to solve their own problem, they wouldn't be paying you to do it for them. Since the hardest part of solving a problem is usually understanding what the heck the problem even is (and the second hardest is often figuring out how to determine if a proposed solution actually is an acceptable one), this makes for lots of interesting and productive interaction early on. The alternative is, as you alluded to, very costly and contentious action much later.