index

NCS Language

Stack Manipulation and Basic Math

keyword stack effect notes

Division '/' was integer, rounding toward zero. Remainder could be found with the modulo operator '&'.

The operations involving ↑ and ↓ used symbols built into the OS Challenger's character ROM and keyboard scanner. Today, we'd use UTF characters for up arrow (↑ or ↑) and down arrow (↓ or ↓).

If NEG weren't already defined, one could define it within the NCS language like this:

: NEG COM 1+ .

Accessing Memory

keyword stack effect notes

Tests & Comparisons

keyword stack effect notes

During development of NCS, I changed my mind about whether a "true" result should be integer 1 or have all sixteen bits set.

Why is <? testing B<A rather than A<B? If you're computing some value, x, then you want to test if it's within some expected range, you start with x on the stack. Duplicate it, so it will continue to exist after the test. Then you push on a limit value, for example 100, and want to ask: "is x less than 100?". If things are fine, x is still there to be tested against a lower limit, perhaps -100.

Conditional Execution

A pair of parentheses surround code to be conditionally executed. The opening '(' has an optional extra character to indicate the condition. Bare naked '(' executes only if the top stack item is "true", which is any nonzero value. The closing ')' has the same extra character appended, though the interpreter doesn't check if it matches. This is just to help the human.

Double backslash \\ may be used once inside the parentheses to indicate end of the conditional block, and the start of an "else" block.

keyword stack effect notes

Conditional Execution Example

This example tests if the top stack item is odd or even, without removing it.

DEF oddoreven? : V 1 AND ("odd" \\ "even") .

Quote marks surround text to be printed. This chunk of code duplicates the given item, tests the LSB, then prints "odd" or "even" as appropriate.

Case Statement

I was familiar with constructs like 'switch' in C, but the Pascal version. Something similar could be defined for NCS.

n case( ... \ ... \ ... \ ... \\ ... )

An integer n is pushed onto the data stack. Then the case statement holds some number of code chunks to be executed in case the integer is 0, 1, 2, ... or the 'else' chunk following the \\ if the value is out of range. I may have used 1-based counting not 0-based. This construction is limited to a few consecutive integers starting with 0 or 1.

Loops

Looping is indicated with curly brackets { ... }. The code within is repeated. One problem that bothered me at the time with looping constructs in other languages is that they didn't do a decent job separating different phases of repeated execution.

  1. Initialization
  2. Actual work to be done
  3. Computing for some termination condition
  4. Testing the termination condition
  5. Joiner work: If not terminating, some extra work to do before the "actual work to be done" for example, inserting commas in a list of numbers being printed out.

In C, there's while() {...} which lets you test first, before the first exectution of the innards, and do {...} while() which always executes first. These are fine most of the time. But sometime one needs to jump into the middle of the "actual work" because the first part of it is really joiner work. Or, one could rearrange the loop so that the joiner work is at the end, after the termination test. In that case, one must arrange to exit the loop from somewhere in the middle, not at the final "while()", 'end', or closing curly bracket.

What I came up with is: surround the repeating code with { }. Use '@' as a bulls-eye target for entering the loop. Before the initial '{', a right-arrow character → reminds us that we're leaping to the bulls-eye.

def yomp : foo → { bar @ glorp 0? } finishup .

What this does: After executing regular operation 'foo', we leap into a loop construct. 'glorp' is executed. Some result is tested. If zero, we exit the loop and go on to perform 'finishup'. Otherwise, joiner work is done by 'bar', then 'glorp' is done again. Repeat as needed.

Compare this to Postscript, which is Forth-like in being stack based and defining new words to add to a Dictionary. We have odd looking conditional code like:

x y eq {foo bar} if
and
1 1 100 { foo bar} for

The 'innards' code to be repeated goes inside { }, superficially similar to C/C++/D/Javascript, and treated as one thing. This is common in functional languages. This one thing, the code chunk, is pushed onto the data stack (really a pointer to executable code built in some temporary CODE segment), on top of some boolean value. The 'if' keyword tests the boolean, and may or may not execute the code chunk. Chunk and boolean are ultimately removed from the stack. 'For' does a little more, but is similar.

The 'if', 'repeat', or 'for' following the code looks odd to me.

NCS has '}' which could be taken as a combination of the '}' terminating the code chunk, and 'if', welded into one keyword. '{' begins the code chunk. There is no Postscript, Forth or functional language equivalent to my '@'. Well, 'goto' - but that's considered dangerous! While Postscript make an elegant separation of code chunks from the control logic, NCS has one thing for 'if' and another for looping. With a redesign effort, perhaps these could be unified. But this is early 1980s technology.

Simple Counted Repeat

Repeat a chunk of code a fixed number of time using '#{', like this:
15 #{ foo bar blarf }

Compiling and Executing

There is somewhere defined in 6502 page zero memory a pointer, the Compile Pointer. It is used for building monobyte code routines. My notes are unclear on the details. Apparently, the CP could be hijacked to write arbitrary binary data anywhere, but the main intent was for expanding the NCS interpeter's vocabulary.

keyword stack effect notes
DEF yomp : 1 2 foo . Define 'yomp', converting following text into 6502 or MBC code, and adding word to dictionary.
RED yomp : 5 6 foo bar . Redefine 'yomp', overriding previous definition. Code for words defined before this, are modified to use the new definition. Old def is kept in dictionary, just not used any more.
NEW yomp : 5 6 foo bar . Redefine 'yomp', overriding previous definition, but effective only for any words defined from now on. Words defined before this are not modified.
RESTORE yomp . Remove the redefined 'yomp', restoring the original definition. If there wasn't a previous definition,
FORGET yomp . Remove 'yomp' from the dictionary, along with all words defined following it.
LIST Print out all words currently in the Dictionary

Under the Hood

Beware - here be monkey business!

keyword stack effect notes

Example: Implementing 'conb'

The routine 'conb' is a tight little subroutine which when called, fetches a byte following the caller's JSR instruction, and pushes it onto the data stack. Think of it as working like "LOAD IMMEDIATE" but if your CPU didn't have a load immediate opcode, so you had to implement it as a subroutine. The data follows the JSR and address. The subroutine must look at the return address, use it as a pointer to fetch the data, do whatever must be done with the data (push it on the Data Stack), then adjust the return address before returning.

I call this technique "back-grabbing". I saw it first when disassembling by hand an ancient tic-tac-toe playing program written in 6800 machine (not assembly, machine) for the SWTPC 6800 microcomputer. I wonder who wrote it?

'conb' should be hand-written in super-tight machine code, but if we had to implement it in NCS, there are three ways to do it. (Note that the syntax here doesn't conform to keywords described in the above sections.)

: conb WC? V Fb 1&2 2+ GO! .
: conb WC? Fb 1 RA+ .
: conb RA? V Fb 1&2 1+ RA! .

When the 6502 jumps to a subroutine, the PC is advanced beyond the destination address. The address pushed to the machine stack is for the first byte following - the data to be backgrabbed.

Of these three implementations, the middle one is fastest. The first is slowest and buggy - it asks for the RA using a keyword which leaves the machine stack unchanged, but uses the fetched value to execute an artificial return. The SP remains incorrect. The second and third implementations either remove the original RA or adjust it in place.

Table of 'con' routines

These are subroutines for use by 6502 code.

conPush the back-grabbed 16-bit constant onto the data stack.
conbPush the back-grabbed 8-bit constant onto the data stack.
consPush multiple 16-bit constants onto the data stack. First byte is the number of 16-bit constants, then the 16-bit constants follow.
conbsPush multiple 8-bit constants, similar to 'cons'.

Writer

My notes are a bit messy on this, and being from an era before which I had any concept of "stdout", files, or many of the concepts common in software, operating systems, and systems administration today, I cannot today fathom what I was thinking. But here goes...

Recognizing that I'd often want to write characters to the screen, and also I'd often want to write characters to some chunk of memory, and in both cases maybe converting integers into base ten notation, it seemed wise to define only one mechanism for writing formatted text. I created some keywords using a diagonal arrow: ↘ which by itself wrote a byte at some pointed-to location. Dealing with a pointer which had to persist over many characters, while also manipulating the ASCII codes to make characters, seemd klutzy, so I had a specially reserved "Write Pointer" in page zero RAM. When it points into video frame buffer, it writes text onto the screen. It could just as well point to the contents of a text variable, or be used to write 6502 or MBC opcodes into a keyword definition under construction.

keyword stack effect notes
Daren Scot Wilson's personal site
linkedin