Why was the stored program concept developed?

The IBM 650 executed code directly off rotating drum storage. but the gotcha, as I’m sure you know, was that that drum was the machine’s moral equivalent of RAM, not the moral equivalent of what we now call mass storage = disk, etc. storage.

I could imagine someone knowing it executed off a drum, but not realizing that the drum was the “RAM” of the machine.

Thanks for the explanation. Just a couple of minor asides. These aren’t quibbles with anything you said, just some additional points.

Modifying the address of an ADD instruction or some other instruction as you describe, for the purpose of processing array elements, is a cute trick, but I wouldn’t consider it “bad programming practice” in the context of what I said earlier about self-modifying code. Given the limitations of the computer architecture, it’s actually rather good, efficient programming practice. I was thinking of self-modifying code in the broader sense of whole swaths of instructions being modified or generated on the fly. Modifying the address of a memory reference instruction is logically equivalent to using an index register to offset an address, and such programs should be no more or less difficult to debug than any other.

My other aside is that, although some simple computer architectures lacked index registers, I’ve never encountered a machine that lacked indirect addressing, and that can be used in the same way as an index register, obviating the need for an index register. Though of course, incrementing or poking values into a memory location used for indirect addressing requires stored-program capability.

Just for fun and general interest, I checked out a few old computer architectures to see if they supported my claim that I have yet to see a computer that didn’t have indirect addressing (or index registers, or both). At first it appeared that the IBM 701 (announced in 1952) did not, but on closer examination it did, in a primitive way, and interestingly, it took exactly the form you just described, of modifying the address of another memory reference instruction. It took the form of a variant of the “store accumulator” instruction called an “address-modify instruction”.

The first computer produced by DEC, the PDP-1, not only had indirect addressing, it had multi-step indirect addressing. It was an 18-bit word machine of which 12 bits were the address, and one was the indirect bit. If the indirect bit in an instruction was “1”, the memory address was taken to mean the address of the location containing the address. If the second location also had the indirect bit set, the referenced location was once again redirected. This could continue indefinitely. I’m sure you could write some really obscure code that way! :grinning:

As I mentioned, the IBM 650 was a full-fledged stored-program computer that could have been a full-fledged von Neumann machine, the drum memory being just a property of implementation, not of basic architecture, but IBM chose to segregate instructions and data so it wasn’t a true von Neumann architecture. The IBM 701, introduced a year earlier as a machine targeted at the science market, was.

You really need to scroll down further in Turing’s biography. Note the sentence “He presented a paper on 19 February 1946, which was the first detailed design of a stored-program computer.”

I feel like my original answer was in fact the correct one. Although I didn’t guess explicitly that it was the lack of an index register at play, writing out instructions so that you can read from arbitrary addresses is well within the scope of self-modifying code and a perfectly legitimate use.

In fact I’ve used the technique semi-recently (last 15 years, say) for run-time loop unrolling. We generated code on demand for fast 3D vertex processing–this was very branchy code, but the branches are all the same across millions of structures. In some cases there were loops that read from several adjacent elements. Unrolling those and hard-coding the address offset in the instruction is basically just an advanced version of what Voyager described. It wasn’t any harder to debug than any other x86 assembly.

I know that “self-modifying code” brings to mind all sorts of super tricky techniques, but there are very straightforward and commonplace applications as well.

Another example is if you want to hook your own function into another function call. You can just patch in a new address.

That doesn’t distinguish von Neumann from Harvard architectures, though. They both have read/write RAM.

I agree, and I clarified that later on.

It was the tricky, obscure stuff that I was thinking of – the kind of code that only the original author could love debug, and after a few months, nobody at all. Address modification is a good example of a valid use of dynamic code modification, but only because it really just mimics the operation of an index register. I’m not convinced there are many other examples.

I’m not quite sure why you would want to do this, but it sounds to me like having a function call that’s not the function call some future programmer might think it is is potentially inviting a world of trouble.

That’s a valid point, although the distinction between the two is getting pretty blurred in modern processors that might have separate caches and pathways for instructions vs data.

I think it’s fair to say, though, that though programmatically modifying memory addresses via index registers should be possible in a machine that was not von Neumann compliant, indirect addressing would not be (nor would poking the address parts of instructions directly, either, of course). So a machine not von Neumann compliant would need help from hardware to do certain operations involving sequentially incrementing memory addresses, like when processing arrays. That’s the interesting point that @Voyager raised.

I’m sure we all realized the general benefits of modifiable code storage but this was a specific case where implementation of code storage was immediately beneficial. I am assuming that was easier to implement than adding an index register and the rest of the architecture to take advantage of it. I’m also sure there will be conflicting accounts of who did it first, but @Voyager is saying this was an actual implementation and not just CS theory.

There are of course an infinite number of malicious possibilities here.

But a non-malicious example that I implemented was hooking the “Present” call that Direct3D games make. This is called once per frame to indicate that the frame is done and should be “presented” to the monitor. We wanted to capture the data, encode it, and then send it to a remote system. We hooked the call so it called our function first, we grabbed the data we needed, and then we made the original call so that the app was none the wiser.

The functions all had perfectly well-defined interfaces that we just had to match. It’ll work fine forever, or at least until Microsoft does some security lockdown.

Microsoft themselves has a package called Detours that does just this sort of thing, though we rolled our own:

Disciplined address modification, such as mimicking an index register across a single clearly-identified array is all well and good.

Undisicplined address modification such as making a subroutine use what amounts to different sets of parameters by pointing it to different global blobs is evil today and was darn close to evil then.

Altering branching instructions’ targets or altering the sense of test / comparison instructions is pure evil and always has been.

Self-modification is the near-ultimate example of coding non-local side effects both in space and in time. Like goto, a teeny weeny little bit goes a long way. And you’re already fully pregnant when you do even that.


ETA: Ninja’ed by @Dr.Strangelove

The function hooking is usually into somebody else’s code. Such as the TSRs’ common in DOS. You want to interpose your code is somebody else’s processing pipeline. So you find a handy seam in their pipeline, diddle in a branch there to your code, then end your code by putting any relevant state back how you found it, executing the same instruction opcode you overwrote with your branch, then branching to the instruction that follows the original overwritten one.

The LGP21 did also - there was even a timing wheel so you could optimize placement of data in memory so that the next piece of data would be under the read head when processing the last piece was completed.
I think both memories can be classified as RAM, as opposed to memory on a tape which had to be accessed in sequence.

I’m not gonna say it should be your first choice when you have several possible options. But clearly it’s necessary in certain cases. Ideally the modification part would be nicely abstracted into a library, like Detours or whatever. But back in 1956… you did what you had to do.

Everyone’s browser runs Javascript that’s been compiled into machine code on demand. That’s a much more sophisticated version of the vertex thing I described earlier. And totally necessary for decent performance (probably by a factor of 100).

It does make me wonder how much the notion of self-modifying code being bad is just an artifact of the paradigms we have accepted for modern computing, and what modern computers would be like if we had gone the other way.

Would it have just been a hopeless, insecure, unstable mess? Or might there have been a way for computing to have evolved to the same level, but just differently?

I’ve never used punchcards, but my impression is that each card was numbered. If you gave the cards non-consecutive numbers like 10; 20; 30; 40 etc. then you could insert or remove cards (= lines of code) later on. Which is essentially the same thing we did in BASIC in the 1980s.

A teacher told me the keypunch folks had a sorting machine that could, well, sort cards in the right order if they were numbered. I don’t know how widespread that was.

The IBM 650 did the optimization in its assembler, which was called SOAP (Symbolic Optimizing Assembly Program). Apparently back then the assembler was also the loader, and SOAP was responsible for placing instructions optimally on the drum so the next instruction would come flying by the read head just as the previous one finished executing.

I may be old but this stuff was way before even my time! It’s just stuff I picked up from reading old historic manuals. I did write a few programs for an IBM 7040 and 7044 as a kid, but those things were being hauled to the junkyard even before I entered college. They were replaced by DECsystem10s by university computing centers who knew what they were doing, and by System/360s by those in the thrall (and grift) of IBM. :wink:

This is fine, and even ingenious, for things like gaming. But in the context of enterprise software or any other mission-critical application, this would be called “hacking” – not in the malicious sense, but in the original sense of “clever trick by a smart programmer” – but one that violates all standards and puts the integrity of the whole system at risk.

The press conference revealing ENIAC to the world was 15 February, 1946 (it was top secret before that) so they still win. Though, as I said, the Turing machine is stored program. Colossus wasn’t. Eckert and Mauchly had a class on the design of ENIAC, and other work on computers can be traced to people attending that class or at least hearing about it. The first run of a stored program computer, by the way, was by Maurice Wilkes on May 6, 1949. Wilkes is dear to my heart, having invented microprogramming.

Interesting. The first IBM instruction set I know is the 1620. I’m not at all surprised that the people at General Precision copied their instruction from IBM.
I’d not call this method an index register. Think about what happens if you move code into a ROM or into a region of memory protected from modification. Index register techniques work fine, Y instruction techniques break.

I was looking for something more specific than allowing self modifying code - thought that was more correct than my guess.
And it’s definitely a legitimate use, as seen by the machines that included this capability in their instruction sets. In these types of machines - single user, single program stream, you can’t hurt anyone but yourself. Yet the dangers are obvious. When I TAed assembly language in 1974 (PDP-11) we sure didn’t let anyone use these techniques. We hammered structured programming (which was new) into their head so they’d have some chance of getting their programs to work.

Only numbered if you keyed in the number of each in the field on the right of the card which the Fortran compiler ignored. And you soon got out of sequence if you had to insert a card, so you’d need to number by 5s. Never knew anyone who did that in school. And the sorter I mentioned before would do the sort - if you put in the numbers.

An enterprise-friendly example would be hot-patching. Got an OS that you want to keep running for years at a time without a reboot, and yet still be able to fix OS-level bugs? Then you can bring the new code into memory, and then patch the old function to start calling the new one. Totally seamless. It requires great care to get right, though.

I spent more years of my life than I care to remember doing IT consulting for one of the country’s largest banks. Anyone who pulled that shit in anything other than a “critical emergency response team” situation would be fired on the spot.

Maybe so, but the latest Windows Server and even Oracle DBs support it.

This is obvious not some rando engineer patching things by hand. It’s tested just as much (or more) as any other software patch.

When has Microsoft ever been held up as a paragon of software excellence, of software reliability, or, Og help us, as a paragon of software development methodology?

And yet banks and others still use them.

And anyway, Linux supports it as well. IBM has a whole page on it here: