• Welcome to the new COTI server. We've moved the Citizens to a new server. Please let us know in the COTI Website issue forum if you find any problems.

Vilani Programming Languages

robject

SOC-14 10K
Admin Award
Marquis
My ORIGINAL attempt at this (Z*PL): http://www.travellerrpg.com/CotI/Discuss/showthread.php?p=480076#post480076


I wondered what a Vilani programming language might look like.

I'd thought about column-specific fields and punch cards, and COBOLesque verbose grammars, but also cool things like quantum bits and inference engines.

I haven't come up with a satisfying, consistent language, yet, but my current effort is to show a mix of strong conservatism, strong type safety, verbosity, but also power.

I think a "more fun" language would have VERY strong ties to columns, but for now I'll stick to a marriage between COBOL and Smalltalk-80. And I'd like to add in some stuff from Quantum INTERCAL just for amusement and shininess. And maybe just a hint of JavaScript. Oh, the horror!

Working example.

Code:
IDENTIFICATION DIVISION.
	PROGRAM-ID. VILANI PROGRAMMING LANGUAGE EXAMPLE

* column 1 carries flags:
*   '*' = comment line
*   '%' = quantum line
*   '.' = end of block (?)
*   '@' = loop indicator (?)
* Smalltalk-80 messaging structure and COBOL verbosity
* structured modules (program title. variables. methods.)
* quantum computing (e.g. sort of multithreaded.)
* PLEASE and MAYBE statement (and even COME FROM) - INTERCAL

FIELD DIVISION.
	LET x BE INT OF LENGTH 3.
	LET w BE STR OF LENGTH 16.
	LET v BE RAT.
	LET u BE NUM OF LENGTH 10.
	LET t BE BIT OF LENGTH 4.

METHOD DIVISION.
	DEFINE METHOD exampleWithNumber
	WITH PARAMETER x
	AS
		LET VARIABLE y BE INT OF LENGTH 5.
		TRUE AND FALSE NOT AND (NIL IS NIL) IF FALSE SELF HALT.
		SELF SIZE PLUS SUPER SIZE -> y.
		DEFINE ARRAY tempArray
			AS 
			$a #a "a" 1 1.0
			ITERATE WITH EACH
				<- EACH CLASS NAME
				<- ' '
			END ITERATE
		END DEFINE ARRAY
	RETURNS 
		x GREATER THAN y
	END DEFINE METHOD

MAIN DIVISION.
	<- "Hello, world!"
 
Last edited:
It would probably be a functional programming design - can't change variables, have to create new ones.

It would be extremely strongly-typed. With a LOT more types than any programming language we use.

It would rarely have revisions once it was stable - version 2.1 being only 120 years old. The steering committee requires 100% agreement before changes can be added.

It could even be something akin to RPG - columns themselves were very specific in what you could do in them (but that language could really create great reports in very few lines of code). Edit: as you actually mentioned in your post.
 
It would probably be a functional programming design - can't change variables, have to create new ones.

It would be extremely strongly-typed. With a LOT more types than any programming language we use.

I do like the immutability of -- well, everything. Yeah. That could also make it a very concurrentizable language.

And yes on the very strong typing. I'm not as worried about the number of types, as a strict specification on the SIZES of the types. Sort of like I remember Pascal doing.

So you don't declare an INT. You declare an INT of an exact size.
 
I do like the immutability of -- well, everything. Yeah. That could also make it a very concurrentizable language.

And yes on the very strong typing. I'm not as worried about the number of types, as a strict specification on the SIZES of the types. Sort of like I remember Pascal doing.

So you don't declare an INT. You declare an INT of an exact size.

And probably multiple character types.... with specific sizes... and a flag at start of chunk for which language base it accepts.

And, given advanced memory mapping, I wouldn't be surprised to see ints and chars spread across words in half-word chunks... So not just Char8, but Char8Galanglic, Char12Bilandin, Char16Japanese, Char12Aslan, Char20Chinese. And, of course, a standard defined character set ID bloating individual chars by probably 12 bits.

Of course, strings grow from chars... and a chartype becomes part of the specific length (or arbitrary length) definition... so the array actually steps in character width increments, but has only one type ID thus reducing overhead...
Declare X StringFixed(Char8Galanglic;1024);
Declare X StringFixed(Char12Bilandin, 256);

And, of course, programmers choosing to use strings with random access in place of individual chars for reduced overhead.
 
Another disturbing idea - they could be using a 5 data bit (32 character) with 3 bit command word structure, and do something similar to BF....

000x null; data only
001x jump to x (if x=0 the reboot.)
010x if stack == x then execute next else skip next.
011x rotate stack down x steps
100x push data of x address to stack
101x pop stack to x address
110x add x to value in stack
111x add –x to value in stack

just enough to do a reasonably

Or even use it on larger words...
Give the command set one more bit, and a much more useful command set comes to mind.

15 commands (4 bits, plus a null)...
  1. push data at x to stack face
  2. pop data on stack face to x
  3. write data on stack face to x
  4. add data at x to stack face
  5. subtract data at x to stack face
  6. multiply stack face by data at x
  7. jump to x
  8. skip next unless stack < x
  9. skip next unless stack = x
  10. AND stack face with data at x
  11. OR stack face with data at x
  12. XOR stack face with data at x
  13. NOR stack face with data at x
  14. shiftleft stack face data x steps
  15. looping shiftleft stack face data x steps


Headache? I'd expect a bunch of vilani coding at the low level might be similarly wide data but narrow opcode, so smush them together.

Envision this kind of thing on a 64bit risk processor... sure, some code inefficiencies, but really tight command set...
 
I could see the Vilani being quite big on formal methods and belt-and-braces programming styles, so Ada-style strong typing (e.g. specifying legal ranges of variables) and Eiffel-style pre and post condition checking might have been quite big, q.v. Spark.

Formailisms such as CCS (communicating concurrent systems) sit behind Erlang, so you might find similar languages based on a formal logical calculus that can be reasoned about.

From that perspective, duck typing would probably not be flavour of the month so your langauge might be more Java than Smalltalk.
 
I could see the Vilani being quite big on formal methods and belt-and-braces programming styles, so Ada-style strong typing (e.g. specifying legal ranges of variables) and Eiffel-style pre and post condition checking might have been quite big, q.v. Spark.

I tend to agree. The Vilani will not fool around with a few broad mushy data types. No! When you create a variable, you strongly define its type, its acceptable scope.

As far as Wil's RISC instruction set: assembly language (or perhaps just machine language) appeals to the Vilani in me as well.

And both support a type of "concurrency", though it is probably not the way we think of it. Maybe quantum computing.



Since BUREAUCRACY and HIERARCHY also appeals to the Vilani in me, I can easily see that they would be happy with a virtual machine which runs a RISC set, and a very strongly-typed language which compiles down to this VM.

Let's pretend they have One True Hardware Platform for starships, and therefore they can burn that VM onto chips instead of emulation software.

I suppose that would be the Models/1 through /9 (or however high they go).
 
Instead of "One True Hardware Platform," you can go the Ada way:

The spec requires a compiler that can add a runtime environment to the executable for whatever platform the compiler targets.

Really, as nobby-w suggests, Ada already seems like it was designed by Vilani, since its requirements were defined by committee and implemented by a competition of contractors, then selected by committee and then passed through a conformance process that vendors had a hard time passing.

For half the 90's, Ada was legally mandated for all DoD software.
 
I tend to agree. The Vilani will not fool around with a few broad mushy data types. No! When you create a variable, you strongly define its type, its acceptable scope.

As far as Wil's RISC instruction set: assembly language (or perhaps just machine language) appeals to the Vilani in me as well.

And both support a type of "concurrency", though it is probably not the way we think of it. Maybe quantum computing.
[ . . . ]
You could have one true instruction set, and there's even precenent for that (MIL-1750A, for example). Folks have done hardware stack based systems before (e.g. hardware FORTH or Java VMs) but they were never really that successful. The top of the stack becomes a hot spot and doesn't play nicely with the type of optimisations you need to get superscalar execution.

I think a register based family of ISAs that could scale down to a small embedded CPU or up to a large server processor - in much the same way that ARM scales down to 16 bit thumb (which can be implemented in about 11,000 gates) through to a single-issue RISC architecture (25-30,000 gates) through to high performance 64 bit server or desktop CPUs. The number of bits doesn't necessarily have to be a power of 2 - for example you could have a 56 bit CPU with a 48 bit virtual address space. Your Vlada compiler could target any of those architectures.

I think native compilation would be the order of the day for aerospace - you can't really use just-in-time compilation for hard realtime systems.

Quantum computing is a different critter - and will always be hosted by a conventional cpu to handle I/O and care and feeding of the quantum processor - you program those by 'wiring' the qubits together through quantum entanglement. The effect is sort of like a probabilistic verilog. However, the notion of controlling a jump requiring a quantum algorithm - and therefore a large, cryogenically cooled processor to run it - makes a good mcguffin to explain the size of starship computers.
 
You could have one true instruction set, and there's even precenent for that (MIL-1750A, for example). Folks have done hardware stack based systems before (e.g. hardware FORTH or Java VMs) but they were never really that successful. The top of the stack becomes a hot spot and doesn't play nicely with the type of optimisations you need to get superscalar execution.

A few that are little known exist as ASICS, using a register pointing to one of the stack registers.. Rolling the stack isn't moving data, it's changing that pointer. Values beyond the depth of that stack pointer get bitwise truncated... And it's essentially L1 data cache.

Actual operations are done by the usual process, except the copying from the stack into the math register. It's an extra partial cycle.

I've had friends who'd worked in TI's and HP's chip fab programs in the late 70's and early 80's. Lots of wonky stuff was tried.

The big slowdown, from what I've read, is in the depth of instruction choices. Keep those down, increase speed.

We are, however, not at a point quite yet where improvements in performance require reducing the instruction set further. The Vilani, however, probably are.

Another issue is that modern RISC systems still store instructions in a multi-word format. Very few instructions are single-word, and most of those can be functionally done by a two-word instruction. Many instructions that are 3 word can be done by two 2 word instructions using a register.
 
[ . . . ]
I've had friends who'd worked in TI's and HP's chip fab programs in the late 70's and early 80's. Lots of wonky stuff was tried.
Seriously, that is pre-historic. HP did make stack-based machines back in the late Jurassic, but they lost out to RISC architectures by the latter part of the 1980s. Stack based machines were interesting in the 1970s because the instructions were compact but that stopped being a big deal (in most cases) in the 1980s.

[ . . . ]
Another issue is that modern RISC systems still store instructions in a multi-word format. Very few instructions are single-word, and most of those can be functionally done by a two-word instruction. Many instructions that are 3 word can be done by two 2 word instructions using a register.
That is not how a RISC architecture works. All instructions in (for example) the SPARC v8 or ARM2/3 instruction sets1 are a single-word format. The description you just gave sounds more like a modern implementation of a CISC x86 ISA with a front-end chopping the CISC instructions into micro-instructions executed by a RISC core.

The principal obstacle to optimising a stack architecture is that the stack becomes a hot spot that every execution dispatch becomes dependent on. In order to use it then you have to find a way to interpret various instruction streams without any hints from the compiler by way of spreading variable usage across registers. It's much easier to optimise a superscalar2 engine based on a register architecture. With a register based architecture you can have multiple data flows between registers that can be dispatched across multiple execution units. The fetch-decode pipeline can do graph optimisations on the fly with the register dependencies spread across multiple registers. No single register becomes a hotspot in the way that the stack does.

Even Java JIT compilation systems break the stack based architecture out into registers for optimisation, but this is a more complex process that has to be done in software as it requires a fair amount of static code analysis. The principal benefit of a stack architecture is not performance but code size. An instruction for a stack based machine can (by and large) assume that it's reading its operands from the stack and dropping the result back on the stack, so it doesn't have to waste bits specifying the source and destination of its operands. This makes compiled java bytecode relatively compact and was one of the reasons that a stack machine was used.

As a real-world example of this, A friend of mine described a project to me that he had occasion to do back in the 1970s. This was based on a RCA 1802, which is one of the ISAs that had the ability to swizzle the stack pointer and program counter between registers.3 This machine had 2k of ROM and 256 bytes of RAM - the whole architecture was CMOS, so it could run off a battery.

He implemented a FORTH interpreter in about 30 bytes and wrote the rest of a 256 channel data logger in FORTH. This included the drivers for the packet radio system that it used to transmit the water levels back home. It was possible to do this because of the relatively compact stack-based FORTH code. FORTH was also used in early video games a bit and was quite popular in embedded systems during the 1970s and fisrt half of the '80s.

A few that are little known exist as ASICS, [ . . . ]
SOC applications are one field where simple implementation and compact code size might be of interest, so I could see folks using stack-based systems there. 6502 cores are quite popular in SOC circles for much the same reason - you can implement one in about 3,500 gates.
_____________________________________
1 To pick two that I've actually had occasion to program in assembly language.

2 By superscalar, I mean any architecture that tries to dispatch a single instruction stream across more than one execution unit. This is easier to do with register based ISAs as you can very quickly do dependency graphs by register and dispatch instructions with no register interdependencies across multiple execution units. Doing this with a stack is much harder and requires static code analysis that's not quick enough to perform on the fly during the decode operations.

3 These were one with the (in)famous SEP and SEX instructions. Rad-hardened 1802s were also quite popular on satellites at one point.
 
Last edited:
I'm guessing also with the modern practice of out-of-order execution that register machines are much easier to optimize that way than a stack machine. One could argue that a stack machine could be just a machine with a large register array, and still optimized that way, but not sure if that's enough of a real benefit.

Burroughs did a lot of work and commercialized stack machines in the late 60's.
 
I'm guessing also with the modern practice of out-of-order execution that register machines are much easier to optimize that way than a stack machine.
Yes, they are. Register dependencies can be calculated very quickly in the decode stage, allowing an instruction stream to be dynamically dispatched across multiple execution units.

One could argue that a stack machine could be just a machine with a large register array, and still optimized that way, but not sure if that's enough of a real benefit.
You can't argue that, due to the nature of the stack - FIFO with a single hot point. Optimising that requires code analysis to make sense of the usage of
the variables, to see what uses the items on the stack. You can't do that efficiently enough to do dynamic instruction dispatch on the fly. With registers, you have multiple data flows that allow dependencies to be analysed quickly with relatively straightforward graph traversal algorithms based on the input and output registers.

Just-in-time compilers for JVMs (which are nominally a stack machine) have to do this analysis and convert it into a virtual register machine to produce code that can be optimised by the CPU.
Burroughs did a lot of work and commercialized stack machines in the late 60's.
They're not the only ones - HP did stack based 16 and 32 bit minis in the '70s and '80s, and there have been various other stack-based architectures. It was popular at one point due to the compact size of the instructions (also one of the reasons that FORTH got popular in embedded systems during the '70s) - you don't have to use bits to specify source and destination registers as the source and destination are implicitly the top of the stack. One of the reasons that the JVM went with a stack model is so that the bytecode was compact to download over a network (also the original design predates the wide availability of superscalar RISC cores).

However, register-to-register operations are quicker than register to memory operations (although there are various approaches for caching the top of the stack), and having multiple registers to allow compiler and pipeline optimisation for multiple dispatch instructions make register-based architectures much faster and much more amenable to optimisation. Thus, register based architectures won out for all but specialised applications.
 
Last edited:
You can't argue that, due to the nature of the stack - FIFO with a single hot point. Optimising that requires code analysis to make sense of the usage of
the variables, to see what uses the items on the stack. You can't do that efficiently enough to do dynamic instruction dispatch on the fly. With registers, you have multiple data flows that allow dependencies to be analysed quickly with relatively straightforward graph traversal algorithms based on the input and output registers.
Eh, most of the time, particularly in modern languages, the stack sets up the call frame, but once invoked, the code uses static offsets in to it with vary little actual active stack manipulation going on.

In contrast to something like Forth where the routines perform active stack gymnastics on the thesis that these specific stack manipulations are cheaper that constantly performing math to get to offsets in to the stack frame. Which is where register rich architecture approach the problem by loading a working set of registers from the stack frame (via offsets), performing all of the work in them, and then copying them back (perhaps) to the stack frame on return.

But also consider CPU's like the 6502, which highlight access to "Zero Page" (that portion of memory from $0000 to $00FF). The 6502 considers Zero Page as "almost registers". 6502 is register poor because it can fall back on the Zero Page.

However, on 6502 Forths, the parameter stack is implemented IN Zero Page -- so here we have, effectively, a register array used as a stack. So, I don't know if that concept can be made manifest in hardware to where you can have code that says: MOV R1, R2, but there's some mechanism of mapping the registers "once" at routine entry on to the stack frame.

On the 6502, LDA 0,X (which is how Forth addresses the parameter stack, X is an index register representing the stack pointer) takes the address $00, adds X and loads the accumulator. So, there's a bit of math involved each time. If there was some mapping technique to where you could do, instead, LDA R0, where all of those offsets are calculated once vs for every operation. Maybe there's a net benefit.
 

It's sounds amazing, but it's not.

The "Forth Interpreter" is actually the inner interpreter that is used to jump from one high level operation to the next. It's a very simple operation.

It does not include the parser, the compiler, or any of the runtime components of the system (such as stack manipulation, branching, etc.).

That, of course, the basic Forth runtime is quite small. The core of it can readily fit in 2-3K of RAM.
 
The difference between large register and L0 stack is simply one of access, and that can be handled by hardware

PuSA x x is first addend by address
PuSA y y is second addend by address
AdSW z z is target memory by address where answer will be stored. It will also decrement the stack pointer and store that in the stack register x was loaded in

can be handled by having a set of stack registers, and a pointer register, plus the accumulator.

I read (some time ago) an old whitepaper about implementing RPN calculators in single IC. The expense was in the complexity of that much memory on a single chip. Now, modern chips have more complexity than is needed to implement an HP38 equivalent as hardware-on-single-IC. But now, they don't need the speed improvements such would bring; general purpose CPUs implement fast enough to not need it.

(I'll note that HP is selling RPN calculators still. The Prime is RPN and Algebraic... prgrammable... and graphing. And, much like the 1980's, still in the $150 range.)
 
Back
Top