122 karma · joined September 19, 2013
Extensions that saved me the most agony:
1: Use of stack frame comments to actually define local variables and enforce the in - out stack transform. While at this I also added the comment string to the word definition in an additional help link. Thus:
[:] do_it ( in1 in2 -- out : return in1 * in2 * 2 ) local lv : lv_helper 2 * ; in1 @ in2 @ * lv_helper out ! [;]
> help do_it in1 in2 -- out : return in1 * in2 > 3 6 do_it . 36 >
Note the [:] and [;] redefs for compiler words, had to do this to accommodate nesting, locals, stack frames, while not compromising regular FORTH.
all variables are local in scope to the containing word. Defining variables inside ditto. Had words for listing contents of these too.
I later extended this to include nested word definitions, dynamically allocated variables, and stack frames that will unwind these on execution error. Gone were stack frame size errors, a missed dup or drop no longer fatal, indeed dup and drop not really necessary any more.
2: Structured data debugging aides: a few words and a C header parser allowed me to dump or modify readable contaents of application structs, and to call into application functions.
3: along the same lines of nested definitions, I was able to associate sub-dictionaries with graphical views along their heirarchy. This was a later addition, applied to VNOS. Lots of little things became manageable with this paradigm. In particular, allowed me to do a scoped local console (as a debug object instantated in a View), and to insert message overrides into the VNOS messaging mechanism, effectively adding what turned out to be a really powerful scripting system. Done as an object too - current version includes Perl 5 and Lua (almost ready).
And yes, concurrency is a concern, and being sure not to call non-re-entrant (errant:) functions, but hey, ypu'd likely be surprised at how good a pattern recognizer ther Human Brain is here.
Over-all, I think I used this system with a good half-dozen different CPU architectures and their OS's, also in the high level VNOS GUI (originally put my FORTH into it for debugging purposes, but that was rapidly out-grown).
C for (embedded(embedded))* audio DSP engine, with hardware specific intrinsics and occasional drops into inline ASM when those fail. When every cycle counts and Hard Real Time contracts must be met, to me it is anathema for any invisible compiler-generated code to exist - such breaks the required determinism. So no C++. Would write in pure ASM in the old days. Still want to - most times the C preamble/postamble is not needed in event handler functions. But TPTB dictate otherwise. Small talent pool and they want Jr progs to take it over.
FORTH as the most useful interactive hardware/low level software debugging tool for behavioral debugging (vs post mortem breakpoint debugging, which loses the behavioral context). Used to use hardware logic analysers for this but system complexity out-grew them. I should note I am not using a commercial FORTH, rather a freeware core greatly extended and modified by me for this purpose. Used it in several projects with different CPUs and architectures over the years. Can feed it C headers for readable dumps too (translator written in PERL). Key point: does this while the target continues executing. Caveat: do NOT use unless you know and understand every line of code in the engine and its implications for the target system. Given that, it's the sharpest double-edged handle-less debugging tool I've ever found.
* the DSP engine is embedded inside a larger embedded system. All inside the same chip. Makes for interesting times when one cannot touch it directly in main OS land.
I've been lucky, I guess.I would add to continuous learning: continuous invention. New stuff in the world, not just new to you. In my case, almost entirely unplanned drunkard's walk of a career that I don't recommend anyone emulates, but it sure has been a wild ride.
I'm 69 last month. Started 50 years ago inside discrete component technology mainframes. Done hardware logic design, firewire and Medialink FPGA, datacomms Hard Real Time embedded firmware, synchronous and asynchronous comms protocol design, real time networking design in the Music world (before anybody thought it possible, we showed em!:), created cool 4G visual programming languages, lately Audio DSP inside gaming consoles.
Moral: Keep inventing, keeps you young, moreso than just leaning new stuff you won't be using until it is obsolete. A wide T LI profile doesn't hurt either. But that is not an after-thought, it's a side effect of your lifetime of energy and obsessions.
Vaguely thinking of retiring when I'm 72 or so. Want to make more music, DSP takes too much time.
The test could also have included questions on things like packing bools and enums into structures. Again critical to get righht when sharing or transmitting structs, or anything bigger than a char.
My conclusion: there is no formula for staying relevant. Perhaps an understanding of the roots and rapid skill acquisition, but beyond that every second I spent learning a new framework just because has been a second wasted. By the timne I was somewhere it was poossibly relevant, it was already dead and replaced or I was too far ahead of the time and had to write my own.
Further, and sadly, after 50 some years in the biz, I still understand the insides of current SOC chips, and I shouldn't - no progress has been made in practical computing thoeretics at all. Lots of embellishment, lots of band-aids, nothing really different. Otherwise I would not be able to do this job.
Yes, really, brief this time. $0.01 instead of a full $.
Yes, Steve is indeed really cool. No contest.
"That which _can_ be configured _must_ be configured",
corr: "Defaults Aren't".
Inspired by my early experiences with early Windows 3.
First program: Sieve of Eratosthenes in ALGOL 60, typed onto paper tape on a Friden Flexowriter and run (ran first time too) on an Elliott 803. Year was 1963. I did get to touch the cabinet, but was not allowed to mount the tape.
First computer: 1967, Elliott 4130, from the inside out as commissioning tech employed at Elliott's in Elstree. 1000 cps paper tape readers were quite something. Especially when they failed.
Elliotts computers absorbed into ICL 1968/9. So any schools using 903's had to be using old refurnbished units. I would have thought.
1968: same time, evening degree at Birkbek College, prepared CS projects on IBM punched cards and handed the stacks to the priesthood via a little hole in a wall for running on the Atlas. 2 day turn-round. A step backwards, for me, after such intimacy wth the 4130. Fun fact: the course was not called CS in those days, not yet. So I got Mathematical Sciences with Physics on my piece of paper.
Warman's Law: That which _can_ be configured _must_ be comfigured,
Corr.: Defaults Aren't.
Impl: running any program on its defaults will get you in trouble somewhere down the line. This is no different. Configurations are there precisely to accommodate each users' individual needs, and we are not all the same. Typically defaults try to achieve useability by naive users, and usually do so in a way that is wrong for the sophisticated users. As well as often not chosen very well and really wrong for everybody. Then there is code written by naive programmers who code the naive default and do not provide any configuration because they are unaware of any other way to use the system. And the other extreme of course - Windws anyone? Multiple places to configure different aspects of a specific vehavior, and selection dialogues that "help" by not showing sub-options not used by the current settings, making it impossible to know the full set?
At least the /etc paradigm lets us get a canonical view of everything, explorable with a simple text editor. This article actually shows the advantages.
36 bits was a custom DSP architecture. Could not use C to get at the extra 4 bits, had to use ASM.
Storage units is also inconstant. I've had fun with another DSP who used 16 bit instead of 8, could not address bytes at all. No C at all so I don't know what it would have done with sizeof(int). Had to exchange stream data with a conventional architecture via DMA. Fun times.
In some ways I am a little dissappinted in the arc of our technology. It may have become constrained overly by what compiler tech can do easily and can not do so easily. I really should not be able to understand how a quad-core architecture is put together and works, not after some 50 years of development, but I do. Not because I'm a genius, but because it is essentially the same stuff I worked with in the 60's with a lot of emvblishments, short cuts, and optimizations, but not fundamental change.
My numbers referred to the native widths of the hardware words. sizeof(int) would of course return those numbers divided by 8, but machines are almost universally referred to by bit width, not byte width. Intel 64 bit chips, not Intel 8 byte chips, for example. So sizeof(int) is still a function of hardware width, and hardware width is defined in terms of bits.
Perhaps I should have been a bit more specific, instead of leaping over fully internalized transforms without explanation? Here? In a discussion about hardware? And note that 36 bits is not a multiple of 8, which actually breaks sizeof() anyway.
But there is another problem too - the free() might (should:) use a mutex while it does its work, but another thread could be woken up during the free() postamble, after the mutex has been released but before the caller resumes execution - or even between the == comparison and the second free() - call malloc(), and the implementation might be one that re-uses the most recent free(). Which would mess up the other thread wonderfully.
Neither of the above rely on any kind of mapping between the C variable p and the underlying hardware. This is deliberate. C is defined as an abstract grammar and semantics set, primarily because by that era it was painfully recognised that inventing new HLL's for each new hardware architecture was a losing proposition. There were so many of them, and those languages were not transportable. The essence beauty of C was it is simple enough to be implemented on any level from direct hardware out to as many layers of VM as one could wish for. And really only required the execution machine model to be Turing complete. Its early use was all done to the metal, hence the inherited impression that C pointers are hardware pointers, but really only when used at the metal is this likely to be true. And typically, these days, one finds architectural specific extensions, such as intrinsics, that allow metal access from C to the custom parts of the hardware.
The still painful part of C though is where its architectural independence intrudes. Such as, sizeof(int) being the native hardware size of an integer. 8, 16, 24, 32, 36, 48, 64, I've suffered from them all. Similarly, sizeof(enum) is undefined. The language only stats it must be large enough to represent all its values. I have found the compiler definition differences of bool to also be very frustrating. I grew into software from a hardware design background, and to me
a & true == a
is always true. Or should be. The language defines boolean values as 0 for false and any other value for true. Most compilers choose 1 for true - enum style. So then
((a & true) == a) && ((a == 0) || (a == 1))
Useless when trying to deal with metal and bitflag registers (clearly not the literal true, rather when somewhere else a variable has been set to true and later is used as a mask).
Some compilers have compile command options or #pragmas for forcing the issue, but there is no standard for these. Still. These indefinites can cause serious and sometimes difficult to debug problems when the binary structures containing these things have to be shared between different architectures. Not to mention endian-ness.
All told though, C is still easier than ASM, which usually _is_ specific to the hardware architecture.
Not only way ahead of its time, this kind of performance feature is still apparently unknown. Even in GHz SOC systems and modern DSPs. I've seen some really arcane and baroque interrupt status trees in my time since. All take significant code to parse and dispatch. One really egregious offender was the i960 CPU, sold specifically into the data communications market by Intel in the mid 90's. Sold into a necessarily HRT application domain. Even at a 50 MHz clock it still took 6 - 10 usecs to get started doing actual work. I was very surprised, but the hardware choice at that time was not open to me and I had to live with it.
But DF has always had this uphill battle against the traditional Control flow paradigms, which all the major development methodologies support. Procedural programmers have problems grokking it. Naive non-programmers love it, but they have no say in the industry.
One has to wonder if now MS is spending serious time looking at programming with Petri nets it might finally get some mainstream traction.