IPS - High Level Programming of Small Systems
IPS - High Level Programming of Small Systems
IPS - High Level Programming of Small Systems
Author and publisher have used their best efforts in preparing this
book. They make no warranty of any kind, expressed or implied with
regard to programs or documentation contained in this book. The
author and publisher shall not be liable in any event for incidental or
consequential damages in connection with, or arising out of material
contained in this book.
ISBN 0-9530507-0-X
Contents iii
Contents
Prefaces vi
Introduction viii
Acknowledgements x
References x
CHAPTER I
CHAPTER II
1 General 43
2 Basic philosophy of the assemblers 44
3 The CDP 1801 assembler 46
3.1 Register allocations 46
3.2 The instruction mnemonics 47
3.3 An 1801 assembler example 47
3.4 Listing of the CDP 1801 Assembler 48
4 The 8080 assembler 49
4.1 Resource allocations 49
4.2 The instruction mnemonics 49
4.3 An 8080 assembler example 50
4.4 Assembler for the 8080; 5. 3. 78 for IPS-N 50
5 The 6800 assembler 52
5.1 Resource allocations 52
5.2 The instruction mnemonics 52
5.3 A 6800 assembler example 53
5.4 The 6800 assembler for IPS-N, 27.1.78 53
6 The 6502 assembler 56
6.1 Resource allocations 56
6.2 The instruction mnemonics 56
6.3 A 6502 assembler example 58
6.4 The 6502 Assembler for IPS-N, 23. 1. 78 58
7 The CDP 1802 assembler 61
7.1 Listing of the CDP 1802 Assembler 61
Contents v
CHAPTER III
ARE YOU
owner of a microcomputer based on the 8080, 6800, 6502 or the CDP 1801/2 ?
ARE YOU
looking for an efficient, easy and clean way to program your computer using a
high level language ?
DO YOU
DO YOU
IPS has been designed to solve these problems for the AMSAT space projects
(communication satellites for radio amateurs) and has since been found to be a
very useful tool for programming small systems. It uses an extremely modular
and structured approach to develop programs interactively. Because IPS is a high
level language, it allows the sharing of programs regardless of the processor for
which they were developed.
Preface vii
Happily, in 1996 the IPS manual was discovered to have survived on Atari
800XL (ca. 1980) computer cassette tapes, and moreover had recently been
transferred to floppy disc by Robin Gape, a prominent IPS contributor in the mid
'80s. Thus it became practical to republish the document.
In the late '70s, disc systems were uncommon. Thus the manual refers
throughout to cassette tape as its input/output medium; IPS computers usually had
two recorders. This anachronism does not detract in any way from the relevance
of IPS, and for the sake of consistency has been left in the document.
IPS development has not stood still. It has been written for the Atari ST and
in 1996 for 32-bit Acorn RISC Computers. The latter are based on the ARM
family of processors, and run IPS 100-1000x faster than the early 1802/6502/8080
systems. Hopefully re-publication of this manual will encourage versions for other
machines, notably the IBM-PC.
A significant application of IPS has been its use as the Operating System for
AMSAT's Phase III series of communications spacecraft. The Oscar-13
(Phase III C) satellite's 1802/IPS computer functioned without missing a beat for
eight years until it re-entered the atmosphere in 1996. Impressive for any
computer's OS, let alone one functioning in space.
James Miller
Cambridge, England, 1997.
viii IPS - High Level Programming of Small Systems
Introduction
One of the most significant technological innovations during the last few years is
the integrated microcomputer. This device is permeating all walks of life; it
allows solution of a large number of engineering problems that previously could
not be solved due to excessive hardware complexity. With microcomputers the
limiting factor has become the effort one is required to invest in software
engineering. There are two phases to each project.
I am certain you will find IPS a useful tool for a vast number of programming
projects. If you should discover any errors or improvements, I will be pleased to
learn of them.
Karl Meinzer
Marburg, 1978.
x IPS - High Level Programming of Small Systems
Acknowledgements
R. Gerlich wrote a first implementation of IPS for the 6502. His work made
many problems transparent particular to that processor, enabling the more
compact version in this book.
R. Dunbar helped with the English mnemonics, the polishing of this book
and ( in 10 hectic nights ) bringing up the 8080 IPS.
References
CHAPTER I
Mass storage in the IPS versions described in this book is done using
standard tape cassettes. The four systems use the AMSAT cassette standard.
( See appendix B ). From a software point of view the significant property of this
standard is the fixed length of the records; all records are 512 bytes long and may
contain arbitrary data ( character or binary ). This length allows the use of the
TV-screen as an input buffer; one record ( block ) fills exactly one half of the
TV-screen.
The AMSAT standard uses a synchronous rate of 400 bit/s and thus is
approximately twice as fast as the asynchronous 300 bit/s Kansas-City standard.
The AMSAT standard is marginally acceptable from a speed point of view. If
you do not intend to use the AMSAT recording technique, any other standard
faster than this system and capable of blocks of 512 arbitrary bytes could be used;
of course the software drivers may have to be changed.
Finally, the system requires a bootstrap capability to enter IPS initially. This
may be a hardware load feature ( COSMAC ) or a small ROM program
containing a loader. Bootstrap loader
2 Keyboard Operations
2.2.1 Numbers
All numbers used by IPS are stored internally as 16-bit integers and thus
range from -32768 to 32767. Other number types may be defined if necessary.
The internal representation of the numbers is binary, with positive numbers
bit 15 is zero; with negative numbers bit 15 is set to one. Logically they are
produced by counting backwards from zero ( -1 thus corresponds to
1111111111111111 , or in hexadecimal writing #FFFF ) binary number
Number range Number type
6 Chapter I - How to use IPS
With all numerical inputs, leading zeros may be omitted. But you must be
careful not to exceed the permissible range possible with 16 bit numbers.
Otherwise, erroneous numbers are placed on the stack. The largest permissible
numbers are: Number range Number type
-32768 to 32767
#0000 to #FFFF
B0000000000000000 to B1111111111111111 ( 16 digits after B )
The format of the stack display on the TV-screen depends on the number last
entered. For example:
15 B111 #15 results in the display:
#000F #0007 #0015 ( the last entry was in hexadecimal )
If you enter now -40 you get:
15 7 21 -40 ( the last entry was in decimal )
If the last entry was binary, the stack is displayed in hexadecimal; there is no
binary display.
If you use the above stack content ( 15, 7, 21, -40 ) and enter the "word" +
you obtain: 15 7 -19
Sect. 2 - Keyboard Operations 7
The upper two numbers of the stack were removed, added and the result was
placed on the stack.
Entering a - now results in: 15 26
Now entering a * results in: 390
So far we have seen only operations using two numbers. There are also
operations using a single number.
The word INV replaces all bits of a number by the opposite.
The word BIT takes a number from the stack and discards all bits except the
four least significant ones. This resulting number ( it may range from 0 to 15 or #0
to #F ) is taken as a number identifying a specific single bit in a 16-bit number.
( The 16 bits of a number may be identified as bits 0 through 15, bit 0 being the
least significant. ) BIT now places a number on the stack in which all bits are 0
except the one identified by the original four least significant bits.
e.g.: #7 BIT results in #0080 equal B0000000010000000
Bit-number: 15 7 0
This function is useful, if you want to test large numbers of bits in loops.
8 Chapter I - How to use IPS
You may use any string of arbitrary characters for the names of dictionary
entries - that includes special and control characters. For example $ or >>> are
perfectly valid names. Only blanks and round brackets are not permissible. ( See
Sect. 3.1 )
10 Chapter I - How to use IPS
Because the variables deliver the address on the stack, it is simple to change
their value. The word ! ( store ) expects two numbers on the stack; a number that
is to be stored and on top of it the address where it is to be stored. When
performing storage, the ! removes both numbers. store number
e.g.: LIMIT AUTO ! calls the constant LIMIT ( 510 ), calls the address
of AUTO ; then stores the 510 into AUTO. Now entering AUTO @ delivers 510
to the stack.
2.5 Fields
Often a set of numbers set required, which are stored under a single name
and which differs only by an index. Such structures are called fields. Such fields
may be defined by the use of an extension of the variable concept. In order to
understand this, let us take a closer look at how variables are stored in memory.
All entries in the dictionary ( nametable ) are very similar.
The nametable contains the "name" coded into 4 bytes. The first byte
contains the number of characters in the name ( truncated to 63, if necessary ).
The following three bytes contain a 24-bit number computed from all the ASCII
Sect. 2 - Keyboard Operations 11
characters of the name. All entries into the IPS dictionary utilize this name coding;
each name has a unique code and may thus be identified later.
In the nametable each namecode is followed by a pointer which indicates the
dictionary position containing the typecode of the entry. A variable entry in the
dictionary consists of a 2 bytes typecode for variable followed by 2 bytes
containing the actual numerical value of this particular variable. For full details
see Chapter III, Sect. 5.3.
}
Program
Type
Value
Code
VAR
IPS has a system-variable named $H, which contains a pointer to the next free
position in the dictionary. Thus IPS keeps track of where the next entry in the
dictionary is to go.
If you increment this pointer after creating a variable-entry, you make room
in the variable to contain more than two bytes. If you want to create a field for 10
numbers, for example, you must increase $H by 9 number positions ( 18 bytes
total, since you will recall that each number entry consists of two bytes. )
With the operation !FC, as with all other IPS operations expecting numbers
on the stack, the following principle is always observed: first the "source", then
the "destination" and finally additional information is expected on the stack in
this order. Operators field Field operators
To move components of a field into another field, you could accomplish this
component by component using @ and ! . With a large set of data this would be
clumsy and slow. To simplify and speed up this activity, a special word is
available.
Using the two fields GROUP and ALPHA of 20 and 8 bytes, respectively,
you might move ALPHA into GROUP say, beginning at position 10:
ALPHA GROUP 10 + 8 >>>
source destination no of bytes field transport
Graphically:
0 1 2 3 4 5 6 7
ALPHA
>>>
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
GROUP
This transport takes place directly ( not via the stack ) and thus is much
faster than transporting individual components. The transport takes place byte by
byte starting at the lower addresses. The addresses may overlap. This enables
the following useful function:
Sect. 2 - Keyboard Operations 13
0 0 0 0 0 0 0 0
ALPHA
The word >>> transports 1 - 256 bytes. If you wish to transport a larger
number of bytes, you may use the word L>>> . This word transports between 0
and 32767 bytes. If the number of bytes to be transported is negative, no transport
takes place.
The word L>>> takes slightly longer to execute than >>> . For short
transports the word >>> thus is the preferred one.
In this context note that there is also an instruction which compares two
fields, byte by byte ( length: 1 - 256 bytes ).
Write:
GROUP ALPHA 8 F-COMP
field 1 field 2 no. of bytes field comparison
This operation compares the first 8 bytes of the two fields. If these field
contents are identical, F-COMP leaves a 1 on the stack. If GROUP is numerically
smaller than ALPHA , the result is 0; if GROUP is larger, the result is 2 . The
bytes having the higher addresses are also treated as the more significant ones.
In variables and constants, the byte having the lower address is also the least
significant one. Numbers on the stack always are held and handled as two bytes -
there is no single byte access to the stack.
Often there is a longer text following the error ? . Make sure that you place
the cursor after the last word of your entry before pressing CR. IPS reads only to
the position of the cursor prior to entering CR .
There are only a few error messages beyond the ? mentioned; they are
written into the seventh line:
The error messages remain on the screen until they are acknowledged by
typing OK. This will clear the message and also re-enable cassette reading. ( An
error will stop it, see Sect. 3.3, Tape Operations )
Sect. 3 - Programming 15
3 Programming
3.1 Definitions
So far, methods have been introduced which allow the computer to perform
mathematical or logical operations. The main strength of a computer though, lies
in its ability the "remember" a sequence of actions and to perform them at a later
time as dictated by other events, e.g. at a certain time or after certain external
events. programming
To enable the computer to remember a set of actions, select a name and then
enter the actions required. To be precise, type:
: name word word ... word ;
The colon produces an entry identified by the name which follows the colon.
The following words are not executed, but stored in the entry for later execution
( coded internally as addresses ). The semicolon terminates the entry. Again:
words are separated by blanks; the semicolon is a word, too. separator
In order to make such definitions of actions more readable, you may insert
arbitrary comments - simply enclose these comments by parentheses. IPS will
ignore these comments, but later they may help you to understand your programs.
Example of a definition:
: 4NUMBERS 10 DUP ( PUTS 10 ON THE STACK TWICE )
15 DUP ( PUTS 15 ON THE STACK TWICE ) ;
If you now type 4NUMBERS , the stack will display:
10 10 15 15
You may see something new from this example beyond the creation of
definitions: within definitions numbers are also not executed ( put on the stack ),
but "compiled" into the definition in such a way that they will be put on the stack
at the time of actual execution of the definition.
The only rule which must be observed is that you can put into definitions only
items as are defined within IPS itself, or those which you have defined previously.
Of course you may put into definitions other definitions you created before; thus
you may create arbitrary hierarchies of definitions.
3.1.1 Branches
One of the most important properties of computers is their ability to make
decisions based on the result of calculations or other events. IPS offers several
possibilities towards this end. They assume that you have left a number on the
stack. This number is to govern the further actions of the computer. For the time
being, let us assume that there is a 0 or a 1 on the stack. You may then use the
following words:
16 Chapter I - How to use IPS
The word YES? removes a number from the stack and checks it. If it is a 1,
the words following the YES? are executed and then the words following the
THEN. If there was a 0 on the stack, the words following the NO: are executed
and the program continues after the THEN. branch rule
Generally there is not a 0 or 1 on the stack to base a decision on, it rather has
to be calculated or produced by a comparison.
Strictly, only the least significant bit of the number is checked. An odd
number leads to the YES? action, an even number results in the NO: action.
> )
< ) These compare two numbers on the stack and remove them.
= ) They put a 1 on the stack, if the condition is
>= ) true, else they put a 0 on the stack.
<= ) operators comparison comparison operators
=/ )
<> ) Equivalent to =/ in some versions of IPS.
>0 <0 =0
> < = >= <= =/ <>
Sect. 3 - Programming 17
In addition to these standard comparisons, you may also use the logical
operations. In this case you utilize only their effect on the least significant bit.
e.g.: A @ B @ > C @ D @ < AND
YES? ...
NO: ...
THEN ...
These words will test, if A > B and C < D . Only if both are true, the words
following YES? are executed. It is a good exercise to write down the state of the
stack after each word.
The "aligned" method of writing the YES? NO: THEN of course is not
mandatory, but it helps to improve the readability of programs and is an insurance
against trivial errors ( like forgetting the THEN ).
3.1.2 Loops
Many problems require a repetition of actions before you may continue with
other instructions. IPS has four ways of effecting repetitions. operators looping
looping operators
1. Instruction sequences requiring an initially unknown number of repetitions
are written between the words LBEGIN and LEND? . LEND? expects a number
on the stack and removes it. If it was 1, the loop terminates and the computer
proceeds with the activities following the LEND? . If the number found by
LEND? was 0, the instructions following the LBEGIN are repeated again. This
repetition continues until the LEND? finally finds a 1. ( Again, as with the YES? ,
only the least significant bit is tested; the loop terminates with an odd number. )
The following program demonstrates this concept.
This program may now be executed by typing PICTURE . It will fill line 4 of
the TV with @-characters.
Note: IPS has the constants TV0 , TV4 and TV8 containing the addresses of
the start of line 0, 4 , 8 of the TV-screen. The constant $TVE is the address of the
last character on the TV. TV0 TV4 TV8
18 Chapter I - How to use IPS
2. With the LBEGIN ... LEND? construct the loop is executed at least
once, because the termination test is at the end. If you need a loop with the test at
the beginning, use this construct:
LBEGIN words for the test
YES? words to be executed, if test delivered 1
THEN/REPEAT
This program will fill line 4 with As. operators looping looping operators
In the LBEGIN ... YES? ... THEN/REPEAT construct the word NO: is not
permissible. Logically the same result will be obtained, if the NO: action follows
the THEN/REPEAT.
The EACH takes the two parameters off the stack and puts them on the
return stack. The NOW increments the iteration counter and then checks whether
Sect. 3 - Programming 19
it exceeds the loop limit. If not, the loop ( between EACH and NOW ) repeats.
Else, the NOW discards the two parameters on the return stack and the loop is
terminated. Inside the loop the loop limit and the iteration counter are on the
return stack; the later on top. You may thus use the word I to access the iteration
counter. If the initial value of the iteration counter exceeds the limit, the loop
action is not executed at all. loop index operators looping looping operators
4. If you desire another increment than one with the loop index, you may use
the word +NOW instead of NOW. +NOW expects a number on the stack and
removes it. The iteration counter is incremented by this number. e.g.
: PATTERN TV4 TV4 63 + EACH #5F ( ASCII FOR _ )
I !B
3 +NOW ;
will write an "_" into each third position when you type PATTERN .
The return stack first belongs to IPS; if you wish to use it, you must comply
with these restrictions:
a. If you place something on the return stack within a definition you must
remove it again in the same definition. IPS uses the return stack to remember
where it is to return after executing a definition.
b. The EACH ... NOW construct also uses the return stack. If you place a
number on the return stack within the EACH ... NOW loop, you also must remove
it inside the loop. If you call a definition from within the loop, note that this
definition puts a number on the return stack. Thus you cannot call I within this
definition. If you need I within the definition, put it on the stack prior to calling
the definition. ( These rules may be circumvented by appropriate stack
manipulations. But programs become difficult to read this way and this practice is
therefore not recommended. ) loop nesting rules
Caution: If the text contains more characters than the field has bytes, IPS will
be destroyed. The number of characters of a text-string is the
number of positions between the quotation marks minus two.
You may create the field in conjunction with the text input.
" COLD " 4 FIELD CD CD !T
Instead of writing 4 you might also have written DUP, but the length of the
array would not be documented that way. text handling text length
Text strings may have a length between 1 and 256 characters. If you forget
the closing " or if the string is too long, you will get the diagnostic message
TEXT-ERROR ! and the stack indicates the length of the erroneous string. Within
a definition this also will result in a structuring error.
Outside definitions, the text string and the words connected with the further
processing of the text must be within the same input, since the screen will be
cleared after the input and thus the text will not available beyond the input.
Text stored in fields is transported to the screen using the word WRITE .
WRITE expects on the stack the address of the field containing the string and on
top the number of characters to be written. Again SP is used as pointer, and after
writing, SP points to the next free position.
CD 4 WRITE
To write numbers on the screen, you may use the word CONV . It takes a
number from the stack, converts it into an ASCII string and then writes it at SP .
SP again is updated to the next free position. The format of this writing is
identical to the stack display. number writing
The presentation ( decimal or hexadecimal ) depends on the last input. If
you want to make certain, that the display is either decimal or hexadecimal, you
must store ( prior to calling CONV ) a 10 or a 16 into the variable BASE .
e.g.: 10 BASE ! before calling CONV will guarantee a decimal display.
CONV may produce up to six characters. text handling
Text strings of course also may be transported to the screen using >>> . In
this case SP , if you use it, is not updated. Also you may produce text on the
screen by explicitly storing the numerical representation of the ASCII characters
as was demonstrated in the loop sample-programs. All techniques together give
you the choice to create the display handling best suited for your application.
Playing back your recordings will place them on the screen again starting at
the position of the cursor. Do not stop the recorder while the "block" lamp is on.
If the space following the cursor is fewer than 8 lines, the rest of the block is lost.
Most IPS-versions incorporate additional editing keys.
1. DEL moves the cursor 1 line up, it thus is the opposite of LINEFEED.
2. CTRL O ( SI ) deletes one character under the cursor, the rest of the line
moves one position left and the last character of the line becomes a blank.
3. CTRL N ( SO ) inserts a blank at the cursor. The character under the
cursor and the rest of the line moves one position to the right, the last character of
the line is lost.
4. CTRL R ( DC2 ) ( delete line ) replaces the rest of the line starting at the
cursor by the corresponding part of the next line. The following lines move one
line up, the last line is being filled by blanks.
5. CTRL Q ( DC1 ) inserts a line filled with blanks starting at the cursor; the
rest of the line following the cursor and all lines below move one line down. The
last line is lost.
6. CTRL X ( CAN ) resets the block counter to one. The block counter is
incremented with each recorded block and written after RS into the last position of
the screen. After count 5 it starts with 1 again. The purpose of this counter is to
simplify editing large amounts of text. This concept assumes that your text
material will be printed with a hard-copy device onto pages. If 5 blocks constitute
one page ( 40 lines ), block number 5 signals the last block on a page. The block
counter thus enables you to keep track of the page format.
If you have completed your text editing, you may leave the edit mode again
by pressing CTRL D ( EOT ). IPS once again expects IPS words starting at line 8.
Instead of manual entries, you may now also play back text recordings. Make
sure the cursor is at the beginning of line 8 . Since CR is not recorded, IPS will
recognize only complete blocks of 512 bytes. Entering such a block is equivalent
to the CR. IPS will stop the recorder while it is processing the block. After
completion it restarts the recorder. If there are further blocks, it will process them
one after another. tape cassette operations
Because the cassettes contain no inherent file identification, you must write
appropriate notes on the cassettes to maintain order in your recordings; there is no
automatic file search available due to the limited control the computer has over the
recorder.
During program development it is a good idea to not record too many blocks
on a cassette, modifications of text are much faster this way. You may overwrite
individual blocks on the cassette without problems; you merely have to position
the tape to the block gap before the block to be overwritten. If you listen to the
recording, the block gap easily can be identified by a momentary increase in pitch.
24 Chapter I - How to use IPS
You may also record spoken comments on the cassette between blocks; the
system will ignore this voice recording.
Later on it will be explained, how you may record data or complete
translated programs and how you may enter them again.
3.4.2 Clock
To simplify programming time-dependent problems, IPS contains a clock
supplying the time, day and 4 stopwatches. The field CLOCK supplies time in 6
bytes. ( The operating system keeps the time ).
CLOCK byte 0 contains 1/100 seconds. It is updated every
20 ms up to 98, then it is reset and there is
a carry into byte 1.
byte 1 contains seconds ( 0 - 59 )
byte 2 contains minutes ( 0 - 59 )
byte 3 contains hours ( 0 - 23 )
byte 4 and
byte 5 contain days, continuous counting without
special limit or reset ( 16-bit number )
You must set the clock after loading IPS, if you wish to use it. For this
purpose you may enter the definition:
: SET 0 CLOCK ! CLOCK 2 + DUP 2 +
EACH I !B
NOW 0 CLOCK 5 + !B ;
You then may enter 15 12 24 SET and the clock is set to date 15, time
12:24 hours.
If your program needs to check the time for a limit, it is recommended to
proceed backwards from date to hours and so on. You thus avoid problems if the
time changes during your checking. The following definition checks if a certain
time has been reached or exceeded. If not, it will deliver 0, else 1 onto the stack.
3.4.3 Stopwatches
In addition to the clock there are four stopwatches. They are fields, 4 bytes
each, and are named SW0 , SW1 , SW2 and SW3 .
e.g.:
SW1 @B YES? ... ( WATCH EXPIRED )
NO: ... ( WATCH STILL RUNNING )
THEN
In setting the stopwatches to a particular time, byte 0 should be the last byte
set; the watch will start only then. ( Or you may use >>> to set all four bytes at a
time ).
If you are interested whether a stopwatch is below a certain value, you again
may use F-COMP . The stopwatch is not updated while F-COMP is being
executed.
Sect. 3 - Programming 27
}
Com- Jump
Def: 0 Def: 1 Def: 2 Def: 3 Def: 4 Def: 5 Def: 6 Def: 7
piler Back
CHAIN
The chain has eight positions in which to put programs. A ninth position in
front of the chain positions contains the COMPILER, the program processing
keyboard and tape inputs. Position 0 contains the program displaying the stack
contents; all other positions are preset with no-ops, words resulting in no action of
the computer. You may add further programs to the chain which then are also
periodically executed. Experience shows that even with large programs it seldom
takes longer than 100 or 200 ms to run once through the complete chain. For
many technical applications this is fast enough and eliminates the need of
interrupts and their associated problems typical with real-time programming. To
put or remove programs from the chain is very easy; it may take place by keyboard
or by definitions. In particular, a definition may remove itself from the chain.
It later may be put into the chain again by another definition. With these
operations the rule always holds that a definition in the chain is always executed to
the end. Its removal from the chain means only that during the next pass through
the chain the definition no longer exists in the chain.
Before you put definitions into the chain, you have to make sure that they
leave the stack as they find it upon entry. A definition leaving a number on the
stack will leave this number upon each pass of the chain; thus the stack will
overflow or underflow resulting in a program crash. ( While executing definitions
in the chain, the stack is not checked. )
28 Chapter I - How to use IPS
To put a program into the chain, you first specify the chain position ( 0 - 7 ),
followed by the word ICHN and the name of the program to be put into the chain.
e.g.:
4 ICHN TEST puts the definition TEST into position 4 for
periodic execution.
4 DCHN removes the definition at position 4 and replaces
it by a no-op.
In particular, 0 DCHN removes the stack display from the chain. This may
be useful if you have other ideas on the use of the display. To switch it on, you
may type 0 ICHN STACKDISPLAY .
Of course you might have put the stack display into position 5 as well, the
net result would have been the same.
If you design programs in- and de-chaining each other make sure that there
are no states from which there does not exist an exit. It is a good idea to prepare
a list of the use of the chain positions and the conditions under which definitions
are put there and removed.
Although the chain is a simple tool to allow multiprogramming, there may
exist situations where this concept is not fast enough. In chapter III another
concept is presented allowing high-level interrupts ( so-called pseudo-interrupts )
to exist in parallel to the chain. Where even this is not fast enough, the assembler
of chapter II will enable you to use machine interrupts. The two later techniques
are slightly more complex to use, thus the chain is the preferred mode if the
timing is not critical. chain add item chain remove item
The following table relates the applicability and response time requirements
of the three techniques.
a. Chain: Up to a few 100 milliseconds until service
depending on rest of chain.
b. Pseudo-interrupt: Up to 18 ms until service with single user-
pseudo-interrupt. With more, add duration
of higher priority routines.
c. Machine interrupt: 50 - 250 µs until service depending on processor
and hardware.
If the stopwatch has run out, the least-significant byte becomes odd activating
the YES:CONT and thus resuming the program. We still need the definition which
initiates the waiting.
This definition expects a number on the stack; the number of minutes to wait.
By calling it, the program will continue after this number of minutes. For example
the MAINPROG in the chain is to wait for 20 minutes before continuing:
: MAINPROG ... 20 M/WAIT ... ;
While MAINPROG waits for the 20 minutes to expire, the rest of the
programs in the chain are periodically executed.
IPS has to store some addresses to enable the WAITDEF and YES:CONT.
This results in the restriction that YES:CONT may only occur in the definition that
is directly put into the chain. ( Since only one return address is stored the
YES:CONT should not be invoked by further nested definitions ).
The WAITDEF has a similar restriction, only it is to occur in a definition
which is called by the definition in the chain.
Furthermore, while invoking such a wait action, the stacks should be empty
( do not use it inside a EACH .. NOW ). Routines containing wait actions can
only be tested by putting them into the chain. Do not call them by the keyboard.
30 Chapter I - How to use IPS
The effect of this mode is to allow access to only certain definitions - those
which are required to control the application. All other words are marked by ?
and not executed. Furthermore, numbers are not recognized and are not put on
the stack. Rather they will be accepted only if they were requested by the
program and then are put into the variable N-INPUT . While the program is
waiting for a number input, no other words are recognized. Inverted ?
The words to be recognized must be the last words defined into the
dictionary. Entering the limited input mode involves two steps. The typecode
address ( ' name ) of the earliest word in the dictionary to be recognized must be
stored in the variable LIS . For instance, if OFF is the first word to be
recognized, type:
After this store a 1 into the variable LIM ( limited input mode ) .
1 LIM !
puts IPS into the limited input mode and it will then only recognize OFF
and entries defined thereafter. number requested
If you desire to request a number from the computer operator, set the
variable N-READ to 1 . Your program then needs to check if N-READ again has
become 0; in this case the requested number is available in the variable
N-INPUT . The actual input resets N-READ . Until a valid number has been
entered, no other words are recognized.
By now you have learned all important properties of IPS except how to
record and re-enter data and translated programs by tape. In principle you could
enter all programs always as source, but this requires more time and tape than
entering objects in their translated form. To understand the necessary actions, let
us have a look at the IPS-memory organization first. This is only a cursory
introduction, the details are presented in chapter III. memory organisation
The nametable and the stacks fill the memory from the other end downwards.
The nametable is located between #3400 and #3FFF. except for the 6502, the
return stack extends from #3400 downwards and the parameter stack starts #3300
32 Chapter I - How to use IPS
downwards. ( 6502: return stack #0200, parameter stack #3400 ) IPS has two
variables containing pointers to the last position in use by the stack. Thus the
system "knows" how many numbers it has on the stack; it keeps track of the
entries by decrementing or incrementing these pointers. Only 16-bit numbers are
held on the stacks. Thus the stack pointers always change in twos.
Variable
( or VAR )
With variables and constants there is a two byte parameter field containing
the actual value. entrytype
Next entry
TypeCode Field
Field Extended Parameter Field
Next entry
TypeCode Field
Definition
: First 2nd
action action
Last
action ;
DEF addr RETEX
All entries eventually must lead to executable code. These entries are called
RCODE or CODE. The entry simply consists of a pointer pointing to actual
machine code which the processor can execute. The IPS code routines are
collected on the lower pages of memory to simplify transcription of the system to
new processors. Thus the RCODE entries point to this code. CODE entries
produced by the assembler have the executable code following the entry.
The emulator requires only a few instructions and a few bytes of code; with
some processors it is faster than a standard sub-routine call and thus responsible
for the fast execution of IPS.
Now type $TUE and your program including IPS is recorded as a whole on
the cassette.
You re-enter this program into the computer simply by loading the tape just
like the IPS tape, only your load now contains IPS plus your program.
By this means you also may duplicate IPS. Note that with the program
DUMPER you can produce only one copy; by doing the copy, DUMPER is
destroyed. You thus have to re-enter DUMPER for each copy.
Sect. 6 - IPS programming hints and exercises 37
Note that you start writing a program by always defining the more general
items first ( top-down design ), but you must enter it in reverse since you can
reference only those items already defined.
It is not a good idea to start writing a program by defining details first which
you feel might be useful later. You lose only time this way because these details
never turn out the way you need them later.
If you have already programmed in FORTRAN or BASIC, try to resist the
temptation to name too many variables. As a guideline, use the rule that variables
should be named only if you must access them from more than one definition
( global variables ). If you need a variable only for in-between storage within a
definition, use the stack or the return stack.
You may remove items from the dictionary again and thus reclaim the
memory space by typing DEL/FROM name . For instance typing DEL/FROM
TEST removes the word TEST and all words defined after TEST from the
dictionary and the nametable.
When you create definitions, it is a good idea to adopt the same philosophy
with regard to the stack as IPS uses itself. This means that definitions remove all
parameters intended for it and leave only explicit results. This helps you to avoid
hard to locate stack errors ( unaccounted items on the stack ).
38 Chapter I - How to use IPS
Also make sure that both ways through a YES? ... THEN leave the stack
with an identical number of parameters on it.
If you have problems with a definition, simply test it by keying the words
constituting it individually and observe the stack. Most errors are readily located
this way. Of course, you must manually simulate the effect of branches and loops.
Do not test too many untried definitions at once. Debugging may get too
involved this way. IPS offers you the possibility to start with the low-level items
first. You thus are able to create a base of proven definitions on which you can
rely at the next higher level.
Before you put definitions into the chain, make sure beyond any doubt that
they leave the stack as they found it. If you have definitions in the chain, first
dechain them prior to eliminating them by DEL/FROM .
6.2 Exercises
The following exercises were designed to acquaint you with the use of the
more common IPS words. The solutions given are by no means the only possible
solutions and possibly not the best. Type in your solutions and give them a test.
You will learn more this way than by hours of theoretical deliberations.
0 VAR AUX
: HEXDISPLAY
0 255 EACH DUP I + @B DUP #F AND SWAP 16 / POX
NUMB NUMB #2020 SWAP !
NOW WEG
0 15 EACH I I 4 * 2 + TV0 + NUMBI ( ANNOTATE )
I I 64 * 63 + TV0 + NUMBI ( DISPLAY )
NOW
TV8 2 + $CEN 0 $P2 ! ;
: 0H 0 HEXDISPLAY ;
: 1H 256 HEXDISPLAY ;
This sample program was modelled after the StarTrek described by David
Price ( Byte Vol.2, No.3 ( March 1977), pp 106 ). The main differences are in the
command structure and in the fact, that the Klingons fight back. The original
version was written in BASIC requiring 22 Kbytes of memory. The IPS version
uses about 11 Kbytes including IPS.
[ At this point the example program should follow but, tantalisingly, has not
been found either in paper or electronic form. - Ed.]
CHAPTER II
The Assemblers
1 General
IPS is sufficient to program almost all problems without having to know the
structure or machine language of your processor.
But there are three exceptions:
a. You want to interface to some special hardware. The IPS I/O structure may
not be sufficient for this purpose, particularly if interrupts are involved.
b. You want to program an extremely time critical problem, that cannot
tolerate any overhead. The IPS emulator has an overhead of about 40 - 50
µs per executed word. Further, IPS treats all numbers as 16-bit quantities.
This results in additional overhead if you are mostly concerned with 8-bit
quantities. The combined effect results in IPS-programs running about
2 - 3 times slower than optimum machine code.
c. The mathematical precision available from 16-bit integers is not sufficient
for your problem and you want to add multiple precision or special
arithmetic operators.
All these problems are solved elegantly by being able to define words, not in
terms of other IPS words, but by machine instructions. These words then may be
used like all other IPS words, but by being defined at the machine level they give
you the facilities mentioned above. The program allowing you to add the machine
instruction definitions is called assembler. To use it you must of course be familiar
with the instruction set of your processor. assembler purpose
The IPS assemblers are intended mainly to allow you to build short interface
routines between your processor, your hardware and high-level IPS. They are not
intended for extended programs. It is not reasonable to do any extended assembly
programming with IPS because assembly programs are harder to debug, usually
require more memory and most important, prevent your programs from being run
on other computers using a different processor. It is thus a good idea to keep the
machine routines as short and simple as possible.
44 Chapter 2 - The Assemblers
The IPS assemblers create assembly language definitions by using the word
CODE followed by a name instead of using the colon. The last word is NEXT
instead of the semicolon. In contrast to the creation of high-level IPS definitions,
the assembly takes place in the keyboard mode; definition mode is not entered.
Typing a mnemonic, e.g. LDA , results in the appropriate code being deposited at
HERE and then HERE being incremented to the next free position. This has
important ramifications as we shall see later.
There are two major departures from conventional assemblers. First, the
sequence of specifying operations follows the IPS convention, i.e. first source,
then destination and then the operation. Most conventional assemblers use the
reverse order. Second, the usual branch mnemonics are not used, rather the
words Y? N: TH and BEGIN END are used similarly as in IPS. The Y? and END
are preceded by a condition ( usually one of the condition codes of the processor
status register ). Thus there are no labels or GOTOs used with the assemblers.
Just as with IPS, the stack is used to handle the jump addresses. Let us have
a closer look at this mechanism. If the word Y? is encountered, the appropriate
jump code is deposited at HERE and the address of the following position is put
on the stack; HERE is then incremented further to where the next instruction is to
be placed. Thus there is on the stack the address where later the jump destination
of the Y? is to be inserted. When the TH is encountered later, the Y? jump may
be completed; HERE is the address where the jump is to go. Thus, TH takes the
address off the stack left by the Y? and inserts there the jump destination.
The N: similarly assembles an unconditional branch leaving on the stack the
position where the jump address is to be inserted. The address left by the Y? is
removed and used to insert the position following the N: jump into the Y? .
The BEGIN is equivalent to HERE ; it simply leaves the address to which
the END must assemble a conditional branch. Because the addresses are on the
stack, you may manipulate them, e.g. by SWAP, to be able to depart from the
strict IPS nesting rules or DUP them to return from multiple points to a loop
begin. assembler principles assembler macro
The assembler works in the interpretive mode. Thus you may use any
regular IPS words in between assembly mnemonics. ( e.g. to compute numbers
used in immediate instructions ) You may also use the word NEXT as often as
you wish to create multiple exit points from a code definition.
You may collect a recurring sequence of mnemonics into a regular
definition; calling it each time deposits the whole sequence of assembly
instructions. Such definitions are called macros. Macros of course may contain
Sect. 2 - Philosophy of the assemblers 45
the structuring words ( e.g. Y? or END ) as well. Because IPS uses different
structuring words, you might use these as well to create different assemblies
depending on parameters received by the macro definition. This facility is called
conditional macro assembly.
Although IPS offers these advanced capabilities, do not get carried away by
them. Always remember that assembly routines serve a sort of crutch function and
should not be allowed to mushroom. Even in extremely time critical situations it
usually pays only to code the innermost loop in assembly language. The effect of
the high-level overhead on total execution time is negligible if outer loops are
coded in IPS.
The main function of machine code routines is to create an interface to IPS
via the stack. At the machine level the stack operations are not automatic.
Depending on the processor, the stack is maintained explicitly by a pointer and
appropriate machine code instructions. If the processor already has instructions
for stack operations, this stack is usually the parameter stack. The return stack is
simulated by another pointer. ( exception: 6502 )
The assemblers by and large use the original mnemonics of the manufacturer.
Unfortunately this approach precludes much commonality between the several
processor's assemblers. But most likely you will work only with one processor,
anyway. Thus the differences will be of little consequence.
IPS occupies certain machine resources. Make sure that you are familiar with
the registers or memory sections used by IPS, which are not to be disturbed.
Handling interrupts is a bit more involved and thus is postponed until
chapter III.
The assemblers are written in high-level IPS. Thus they may also be used to
produce code for different processors than the one on which they are running.
This cross-assembly is particular useful for transplanting IPS onto new processors.
In addition to the mnemonics for the individual processors all assemblers
allow to deposit in-line numbers by using the word , . One byte is deposited at a
time. deposit 1 byte
All assemblers may create certain error messages. These differ from
processor to processor but are self explanatory. If you encounter such a message,
generally it is not recommended to manually correct the code. Rather the entire
code word should be re-assembled because the stack may have been modified
unpredictably. If your assembly leaves numbers on the stack or results in "stack
empty", you may take this as an indication of an error in your code. Do not
execute this code, rather re-assemble it word by word to identify the problem.
The 8080 assembler is coded using English IPS words; the others use the
German set.
46 Chapter 2 - The Assemblers
The X register is not being kept pointing to any particular register. You
must set it in each code routine prior to using the first X referencing instruction.
Between code routines X may get changed by the operating system, so never
forget to set X ! EF 1 is connected to the 20 ms flip-flop, it is reset by output 5.
EF 4 is the keyboard input, it is scanned every 20 ms. If EF 4 is active, output 6
instructions are used to strobe in 7 bits of the character sequentially via EF 4,
LSB first. Outputs 5 and 7 are reserved for special functions; see chap. I, sect 3.4
Sect. 2 - CDP 1801 assembler 47
The following mnemonics are identical to the RCA mnemonics; they expect a
number ( 0 - 15 ) on the stack. You may also use the constants RS ( 2 ) or PS
( #B ) instead:
INC DEC GLO GHI PLO PHI LDA STR
The instructions IDL , RET , DIS , SAV and SHR are identical to RCA usage
and do not expect a parameter.
Control structures are created by: Y? N: TH and BEGIN END . The words
Y? and END must be preceded by the conditionals:
: NOT 8 - ;
: EF 11 + ;
: BEGIN HIER ;
: Y? #30 $AD HIER $H INCR ;
: N: #30 , HIER VERT $H INCR HIER $INADR ;
: TH HIER $INADR ;
: END #30 $AD HIER VERT $INADR $H INCR ;
: HLT #76 , ;
: INR #04 7txa ;
: DCR #05 7txa ;
: MVI #06 7txa , ;
: LXI #01 pt 2 > FVB $DEP ;
: STAX #02 pt >0 FVB ;
: INX #03 pt 2 > FVB ;
: DAD #09 pt 2 > FVB ;
: LDAX #0A pt >0 FVB ;
: DCX #0B pt 2 > FVB ;
: PUSH #C5 pt 2 = FVB ;
: NOP #00 , ; : RLC #07 , ;
: RRC #0F , ; : RAL #17 , ;
: RAR #1F , ; : DAA #27 , ;
: CMA #2F , ; : STC #37 , ;
: CMC #3F , ;
: SHLD #22 , $DEP ;
: LHLD #2A , $DEP ;
: STA #32 , $DEP ;
: LDA #3A , $DEP ;
0 CON B 0 CON NZ 100 CON NEVER
1 CON C 1 CON Z #0354 CON RS
2 CON D 2 CON NCY
3 CON E 3 CON CY
4 CON H 4 CON PO
5 CON L 5 CON PE
6 CON M 6 CON POS
7 CON A 7 CON NEG
: B,C 0 0 ; : PC B,C ;
: D,E 0 2 ;
: H,L 1 4 ;
: $SP 2 6 ;
: PSW 3 6 ;
: BEGIN HERE ;
: N: #C3 , HERE SWAP H2INC HERE SWAP ! ;
: TH HERE SWAP ! ;
: JPCODE DUP NEVER = YES? DEL #C3 ,
NO: 1 XOR #C2 7txa
THEN ;
: Y? JPCODE HERE H2INC ;
: END JPCODE $DEP ;
: NEXT #0380 NEVER END ;
TAB TBA TAP TPA TSX TXS PLA PLB PUA PUB A+B A-B INA INB INS
INX DEA DEB DES DEX ACOM BCOM ANEG BNEG DAA SLA SLB
SRA7 SRB7 SRA SRB RLA RLB RRA RRB CBA ATST BTST RTS RTI
SWI WAI NOP CLA CLB 0>C 1>C 0>OF 1>OF EI DI
LDA LDB LDS LDX STA STB STS STX +A +B +CA +CB A- B- A-C B-C
INC DEC COM NEG ANA ANB ORA ORB XRA XRB SL SR7 SR ROL
ROR ACMP BCMP CPX OST ABIT BBIT JSR JMP CLR
Sect. 2 - 6800 assembler 53
The addressing modes are identified by one of these preceding address codes:
IM P0 ABS IX
( e.g.: 5 IX LDA load A at the memory position pointed to by the index
register + 5 )
CODE /4
TSX 0 IX SR 1 IX ROR 0 IX SR 1 IX ROR NEXT
( ADDRESS-CODES )
#5500 KON IM #6610 KON P0
#9930 KON ABS #AA20 KON IX
( BRANCH-CODES ) : NOT 1 + ;
#20 KON NEVER #22 KON LS #24 KON C #26 KON Z
#28 KON OF #2A KON NEG #2C KON LZ #2E KON LEZ
( FIELDS )
16 FELD $VB " WORD COMB. ERROR " $VB !T
16 FELD $UK " ADDR-MODE ERROR! " $UK !T
16 FELD $UB " CONDITION ERROR! " $UB !T
16 FELD $ZW " BR.OUT OF RANGE! " $ZW !T
( cont. )
54 Chapter 2 - The Assemblers
( AUX.ROUTINES )
: , HIER !B $H INCR ;
: $AWR SYSLINE 32 + 16 >>> 0 IE ;
:INT CODE ENTRYSETUP JA? HIER VERT ! DANN ;
:INT RCODE ENTRYSETUP JA? ! DANN ;
: $ALTERNATIVDEP
JA? + , DUP 256 / , ,
NEIN: DUP P0 = ZWO IX = ODER ZWO IM = ODER
JA? + , ,
NEIN: WEG $VB $AWR
DANN
DANN ;
: $3ALL VERT DUP IM = ZWO ABS = ODER $ALTERNATIVDEP ;
: $2ALL VERT DUP ABS = $ALTERNATIVDEP ;
: $0ABIX ZWO IM = JA? WEG $UK $AWR RETEX
DANN $2ALL ;
: $ABIX ZWO P0 = JA? VERT WEG IM VERT
DANN $0ABIX ;
( STRUCTURING WORDS )
: BEGIN HIER ;
: Y? DUP #FFD0 UND =0 ZWO #21 =/ UND
JA? , HIER $H INCR
NEIN: $UB $AWR DANN ;
: N: #20 , HIER VERT $H INCR HIER $INSERT ;
: TH HIER $INSERT ;
: END Y? VERT $INSERT ;
Sect. 2 - 6800 assembler 55
( COMPOSITE CODES )
: LDA #86 $2ALL ; : LDB #C6 $2ALL ; : LDS #8E $3ALL ;
: LDX #CE $3ALL ; : STA #87 $0ABIX ; : STB #C7 $0ABIX ;
: STS #8F $0ABIX ; : STX #CF $0ABIX ; : +A #8B $2ALL ;
: +B #CB $2ALL ; : +CA #89 $2ALL ; : +CB #C9 $2ALL ;
: A- #80 $2ALL ; : B- #C0 $2ALL ; : A-C #82 $2ALL ;
: B-C #C2 $2ALL ; : INC #4C $ABIX ; : DEC #4A $ABIX ;
: COM #43 $ABIX ; : NEG #40 $ABIX ; : ANA #84 $2ALL ;
: ANB #C4 $2ALL ; : ORA #8A $2ALL ; : ORB #CA $2ALL ;
: XRA #88 $2ALL ; : XRB #C8 $2ALL ; : SL #48 $ABIX ;
: SR7 #47 $ABIX ; : SR #44 $ABIX ; : ROL #49 $ABIX ;
: ROR #46 $ABIX ; : ACMP #81 $2ALL ; : BCMP #C1 $2ALL ;
: CPX #8C $3ALL ; : TST #4D $ABIX ; : ABIT #85 $2ALL ;
: BBIT #C5 $2ALL ; : JSR #8D $ABIX ; : JMP #4E $ABIX ;
: CLR #4F $ABIX ;
BRK 0>C 0>D DI 0>OF DEX DEY INX INY NOP PUA PUS PLA PLS
RTI RTS 1>C 1>D EI TAX TAY TSX TXA TXS TYA
1S->$AF transfers one parameter ( 2 byte ) from the stack into $AF .
It expects Y=1 and delivers Y=0 . C and the stack
remains unchanged.
S->$AF transfers n bytes from the stack into $AF. It expects Y=n-1
and delivers Y=0 . C and stack remain unchanged.
PS-2 decrements the parameter stack pointer by 2 . It does not expect
any particular register setting and only clobbers C.
PS+2 increments the parameter stack pointer by 2 . It does not expect
any particular register setting and only clobbers C.
PC+2 increments the pseudo-PC by 2. It does not expect any particular
register setting and only clobbers C.
D-PREP expects Y=1 . It transfers the stack pointer into $AF and then
increments the stackpointer by 2. It then sets Y=0 and loads A by
performing a PS 0@Y LDA . It returns with this A , C=0 and
Y=0 . This subroutine is most useful for operations working
on two parameters on the stack and leaving a single result.
58 Chapter 2 - The Assemblers
( ADDRESS-CODES )
0 KON A 1 KON IM 2 KON P0
3 KON 0X@ 4 KON 0@Y 5 KON 0X
6 KON 0Y 7 KON ABS 8 KON AX 9 KON AY
( FIELDS )
210 FELD $CTAB
16 FELD $VB " PREC.WORD EERROR " $VB !T
16 FELD $UK " ADDR-MODE ERROR! " $UK !T
16 FELD $UB " CONDITION ERROR! " $UB !T
16 FELD $ZW " BR. OUT OF RANGE " $ZW !T
( AUX-ROUTINES )
: $AWR SYSLINE 32 + 16 >>> 0 IE ;
: , HIER !B $H INCR ;
:INT CODE ENTRYSETUP JA? HIER VERT !
DANN ;
:INT RCODE ENTRYSETUP JA? ! DANN ;
( STRUCTURING WORDS )
: @JMP #6C , $DEP ; : JSR #20 , $DEP ; : BEGIN HIER ;
: Y? DUP #4C =
JA? , HIER H2INC
NEIN: DUP #10 - #FF1F UND =0
JA? , HIER $H INCR
NEIN: $UB $AWR DANN DANN ;
( SIMPLE CODE-DEPOSITS )
: BRK #00 , ; : 0>C #18 , ; : 0>D #D8 , ;
: DI #58 , ; : 0>OF #B8 , ; : DEX #CA , ;
: DEY #88 , ; : INX #E8 , ; : INY #C8 , ;
: NOP #EA , ; : PUA #48 , ; : PUS #08 , ;
: PLA #68 , ; : PLS #28 , ; : RTI #40 , ;
: RTS #60 , ; : 1>C #38 , ; : 1>D #F8 , ;
: EI #78 , ; : TAX #AA , ; : TAY #A8 , ;
: TSX #BA , ; : TXA #8A , ; : TXS #9A , ;
: TYA #98 , ;
( ZUSAMMENGESETZTE BEFEHLE )
: STA 0 $MDEP ; : SHL 10 $MDEP ; : SHR 20 $MDEP ;
: ROL 30 $MDEP ; : ROR 40 $MDEP ; : STX 50 $MDEP ;
: STY 60 $MDEP ; : BCMP 70 $MDEP ; : INC 80 $MDEP ;
: DEC 90 $MDEP ; : CPY 100 $MDEP ; : CPX 110 $MDEP ;
: LDX 120 $MDEP ; : LDY 130 $MDEP ; : A+ 140 $MDEP ;
: ANA 150 $MDEP ; : CMP 160 $MDEP ; : XRA 170 $MDEP ;
: LDA 180 $MDEP ; : A- 190 $MDEP ; : ORA 200 $MDEP ;
( cont. )
60 Chapter 2 - The Assemblers
The RCA COSMAC CDP 1802 is used in the flight computer of all AMSAT
Phase III communications satellites.
The 1802 is similar to the 1801, with an extended instruction set. Refer to
section 3 ( CDP 1800 ) for details of resource allocations and instruction
mnemonics.
: , HIER !B $H INCR ;
:INT RCODE ENTRYSETUP JA? ! DANN ;
:INT CODE ENTRYSETUP JA? HIER VERT !
DANN ;
( ERROR MESSAGES )
16 FELD $JERROR " DESTIN. OFF PAGE " $JERROR !T
16 FELD $CERROR " PREINSTR. ERROR! " $CERROR !T
( AUX ROUTINES )
: $TERM WEG $CERROR SYSWRITE ;
( cont. )
62 Chapter 2 - The Assemblers
: MX 0 $ALUACT ;
: IM 8 $ALUACT , ;
CHAPTER III
IPS was primarily designed to allow the speedy writing of programs intended
for the control of satellites, scientific data collection and other engineering
applications. There are many programming languages claiming to be suitable for
these applications. But on closer inspection most of these require either rather
large systems and thus are not very practical for microcomputers or they have
serious limitations, like insufficient speed or no multiprogramming. Most control
oriented languages are derived from languages created for mathematical or
commercial data processing. Generally this means that the real-time part needs to
be handled by the operating system, and the power of this combination is highly
dependent on the capabilities of the operating system.
With IPS a different approach was possible, since there is no real need to
maintain compatibility with other languages and an entirely different approach
could be taken. Every programming language represents an interface between
machine and men. Thus it must comply with two requirements:
1. The language should allow the translation of programs making efficient use
of the underlying processor, both from a speed and memory economy point
of view. This is essentially an engineering problem.
2. The language should allow the expression of problems in a way matching
the human understanding and decomposition of problems; the system is to
be "user-friendly". Achieving this is not an engineering problem, but a
problem of psychology and aesthetics - a form of art. IPS general design
Let us look at the second point first. In order to be able to put the problem
into perspective it would be necessary to define the "human way of
understanding"; obviously an impossible task because it would have to take into
account the different backgrounds of all the people intending to use the system.
The second best approach would be to isolate certain general aspects of a problem
area and make sure that these are matched by the language. Along this track lots
64 Chapter 3 - The IPS System
of subjective judgements are bound to creep into the language. This is not as bad
as it implies, though. We adapt easily to a given structure and thus soon feel
comfortable with it. This adaptation, on the other hand, causes us to cling to
given structures we are used to. This makes progress in this discipline very slow
because once somebody is adapted to a language lots of energy is necessary to
learn or invent some new way.
Most programming languages initially were designed to allow mathematical
problem solving. There we have at the core of the problem the requirement to
solve algebraic expressions as given by mathematical formulas. These describe a
desired result in terms of the formulas. If the syntax of these formulas is
unambigious a program may turn this syntax into actions ( procedures )
producing the result. This general approach resulted that most programming
languages today are essentially syntax driven and the main thrust of investigation
went into the understanding of syntax. IPS general design
Although significant progress has been made in syntax driven high-level
languages, their generally somewhat static frame has prevented them from being
used extensively in systems programming and real-time engineering. In these
disciplines the tremendous power of the strictly procedural assembly language
was needed and outweighed the need to live with the sometimes quite baroque
instruction sets of the hardware. In this problem area the programmer, in
planning and thinking, wants the computer to perform certain actions. If he uses
a high level language, he has to transform these procedures into the syntax of the
programming language. The compiler in turn decomposes this syntax again into
a set of instructions.
Thus it becomes clear that two translation processes take place. A short cut
is possible if the detour via the syntax can be avoided. With IPS it was attempted
to design a language that is strictly procedural and thus offers the power of
assembly language. But the underlying instruction set was designed to be more
acceptable from a human point of view than the instruction sets defined by
hardware constraints.
One of the most powerful procedural computer architectures can be built
around the concept of the stack. This technique has been in use for a long time in
systems programming, and it became widely known by its use in the Hewlett
Packard calculators. A very powerful and comfortable instruction set can be built
around this concept, but it requires some relearning to become comfortable with
reverse polish notation ( RPN ). The acceptance of this approach for
mathematical problem solving is a good demonstration of its power. With
engineering problems it is even more appropriate, because its only disadvantage,
the reduced readability of algebraic expressions, is less serious. With these
problems the intelligence of the programs resides in the flow of control.
Expressions play a minor role.
Sect. 1 - General design considerations 65
Addresses as pseudocode
IPS pseudocode
This technique can be extended by using the pseudocode not as addresses to
executable code, but to a pointer which in turn contains the address of the
executable code. This indirect execution requires at the head of each instruction
a "descriptor" or typecode containing the pointer to executable code.
"Instruction" = Address
Program
}
Pointer
$H @
VAR value CODE @ execution code next
}
HERE
}
}
(:) (;)
+
DEFEX $H @ RETEX CODE + execution code next
}
H2INC
}
}
}
(:) (;)
1BLIT-
DEFEX HERE ERAL 2 + $H ! RETEX
4 The Executive
During the following discussion please refer to the listing of your processor
at the end of this chapter (Sect. 7). The listed program describes your compiler in
a way usable for an automated translation. There we run into the problem of
having to define a language in its own terms. In principle this is possible if the
translation process is carefully sequenced and the prohibition to redefine words is
disabled. The first IPS versions were translated this way using a previous version
to produce the new version.
Unfortunately this technique becomes very clumsy if the compiler is to be
translated for a different processor.
A much more general solution to this problem results if the language is not
defined in its own terms, but in a different language; a so-called meta language.
This meta language of course may be exactly the same as to the meaning of the
words, only different names are to be used to distinguish between the translation
operation and the object to be translated.
To aid portability of IPS, such a meta language and the compiler to go with
it ( IPS-X ) was developed. This was a very worthwhile investment; it enables a
very fast transcription of IPS to an arbitrary processor. IPS cross-compiler
The IPS-X meta language was derived from the German version of IPS
simply by using lower case letters instead of the usual upper case letters. Where
this was not applicable ( e.g. with the colon ), the meta language words were
produced by appending a lower case n .
This list should allow you to read the listings, it shows only the words
differing from the English version:
weg DEL , hier HERE , aufnahme RECORD , !fk !FC , ja? YES? ,
nein: NO: , dann THEN , je EACH , nun NOW , +nun +NOW ,
anfang LBEGIN , ende? LEND , dann/nochmal THEN/REPEAT ,
kon CON , feld FIELD , ~ " , zwo SOT .
Please try to find the COMPILER in your listing ( next page and also Sect. 7 )
for the following discussion. Compiler input Compiler description
The compiler is executed periodically and processes one input word with
each pass through it, provided an input is available. It does so by calling $NAME .
This definition scans the input and encodes the word in the standard IPS format
( see further down ) and returns a 1 if a word is available. Otherwise it returns a 0
on the stack. If after the word the end of the input buffer has been reached,
$NAME (or $CSCAN) additionally sets the variable $P2 to 1, else it clears it.
If a word is available, $SUCH tries to find it in the nametable. If it finds it, it
returns the starting address of the particular routine. If the word does not exist in
the nametable, a 0 is returned. On top of this address the position of the entry in
the nametable is returned. This is put into $V1 for possible later use.
Now the compiler checks if the system is in the limited input mode. If so, the
word RUMPELSTILZCHEN can reset this mode. If the address found by $SUCH
is lower than LIS or the system expects a number input, the necessary actions are
taken.
An input error is treated by calling IE . This word marks the input with a
question mark and terminates further processing. IE removes one item from the
stack. $SUCH description
Provided that so far everything went well, the return of $SUCH is checked for
0. If a 0 has been returned, the input may be a number ( "number processor" ). In
this case $ZAHL is called to convert the input word into a number on the stack. If
this conversion was successful, a 1 is returned on top of this number by $ZAHL ,
else a 0. Let us assume a number is available. In this case the number is simply
left on the stack in the interpretive mode. But in compile mode ( COMPILEFLAG
= 1 ) the compiler has to deposit a literal code and then the number. If the number
is between 0 and 255, the address of 1BLITERAL is deposited followed by a
single byte containing the number. Else the address of 2BLITERAL is deposited
followed by the number stored into two bytes.
If the system is in the limited input mode, the number is placed into
N-INPUT and N-READ is reset.
If the input word could not be converted into a valid number, again IE is
called.
If $SUCH returned an address ( i.e. the name is available in the nametable ),
the "found processor" is invoked. The action to be taken depends on the type of
the entry and if the system is in compile mode or not. There are four different
types of entries:
74 Chapter 3 - The IPS System
COMPILEFLAG =0 =1
Interpretive Compile
Entrytype
Code Mode Mode
#00 : (normal) execute compile
#80 :PRIOR IE execute
#40 :HPRI execute execute
#C0 :INT execute IE IE = Input Error
: COMPILER $NAME
YES? $SUCH
1 ( TO CONTINUE ) LIM @B
YES? SOT ' RUMPELSTILZCHEN
= YES? ( RUMP. ) 0 LIM !
NO: ( NOT RUMP. ) N-READ @
YES? PDEL 0 1
NO: SOT LIS @ <
YES? IE DEL 0
THEN
THEN
THEN
THEN
YES? ( CONTINUE ? ) DUP =0
YES? ( NUMBER PROCESSOR )
DEL $ZAHL
YES? COMPILEFLAG @B
YES? DUP #FF00 AND
=0 YES? ' 1BLITERAL $DEP
HERE !B $H INCR
NO: ' 2BLITERAL $DEP $DEP
THEN
NO: LIM @B YES? N-INPUT ! 0 N-READ !
THEN
THEN
NO: IE
THEN
NO: ( FOUND PROCESSOR ) DUP 6 - @B #C0 AND
COMPILEFLAG @B OR DUP 1 =
YES? DEL HERE $ML >=
YES? DEL MEMMESSAGE SYSWRITE
NO: $DEP
THEN
NO: DUP #80 = SWAP #C1 = OR
YES? IE
NO: R>S $V1 ! $TUE $V1 @ S>R
THEN
THEN
THEN
$PSHOLEN $SL > YES? $SL $PSSETZEN
STACKMESSAGE SYSWRITE DEL $F1
THEN
THEN
THEN
READYFLAG @B $P2 @B AND ( $BU ALL PROCESSED? )
YES?
#20 $BU !B $BU DUP 1 + 511 L>>> $CEN
THEN
;
76 Chapter 3 - The IPS System
Some IPS versions use as the scanning routine ( $CSCAN ) a code routine
because the compiler spends a significant fraction of the time in this scanner.
This is somewhat involved, because a scan may take more than 20 ms. Thus
provisions are needed to handle the pseudo-interrupt in between. In the interest
of portability the high-level $CSCAN is more straightforward and thus was
selected for the faster processor.
Namecode example: :INT WEG/AB encodes as #C6 #D5 #10 #17. The
first byte is #C0 ( entrytype :INT ) plus 6 ( number of bytes in the name ). The
other three bytes are produced by $NAME running "WEG/AB" through
$POLYNAME . (Sect. 5.2.1 )
Pointer or Link: Namecodes are collected into either a nametable (4+2 =
6 bytes per entry) or a linked list. In a nametable system the pointer points to the
entry's associated typecode field. In a linked list system, Link points to the
namecode field of the previous entry; the first entry has the value of Link set to
zero. (Sect. 5.2.2)
Typecode: The address of executable code that characterises an entry.
Standard typecodes are CON VAR : and FIELD ( as above ).
Parameter field: Additional data required by the executable code; for
example the actual 2-byte value of a variable ( VAR ) entry. A definition ( : ) entry
is followed by many bytes, representing the words that make up the definition.
82 Chapter 3 - The IPS System
The word ' is of type :HPRI . This means that it is executed in the interpretive
mode and in the compile mode. In compile mode ( COMPILEFLAG set ) this
word first deposits the code of 2BLITERAL and then the address. Thus the
address is pushed on the stack only at execution time of the definition.
Most of the address fetching routines use the word $GETADR . This
definition first calls $NAME to encode the name of the word for which the address
is to be fetched. Then $SUCH is used to locate the address. $GETADR returns a
1 if successful and below it the address. Otherwise it returns only a 0 and also
terminates input processing.
The words WAITDEF and YES:CONT require a storage area for storing the
original chain content and the address as to where the program is to resume. The
field IMMAGE thus has four bytes for each chain position. The word WAITDEF
stores the chain address and the resumption address in this field. When called it
first discards its own return address, then calculates the chain location it involves
by making use of the fact that the third item on the return stack is this address
plus two. It then stores the chain content of this location and the resumption
address ( item 2 on the returnstack ) at the appropriate location in the field
IMMAGE. chain description
YES:CONT reverses this process if it finds a 1 on the stack. It again
discards its own return address, again calculates the chain location, picks up from
IMMAGE plus chain position times two ( the first index ) the address to
continue, pushes it on the return stack and then picks up from IMMAGE 16 bytes
higher the original chain operator and stores it in the chain. IMMAGE contains,
in the 16 lower bytes, the resumption address and in the 16 higher bytes the
original chain operators.
$LOAD initializes a load operation by setting the load limit pointer and the
running load pointer. Then it sets LOADFLAG thus starting the load sequence
and decoupling the tape input from the TV/keyboard input.
READ starts a load sequence into the tape buffer $BU .
RECORD starts the recording action by putting #70 into the variable C/Z .
The 20 ms routine picks it up there and starts the recording. After completion, it
is reset to zero by the 20 ms routine.
The low level routines of IPS break down into the IPS words defined by
machine code ( the primitives ) and into the executive responsible for bringing
these routines to execution and doing various housekeeping tasks.
The IPS codewords are made up of two groups. The words constituting the
high level are already known from the description in chapter I. In addition there
are other words that are not directly handled by the user, but are compiled in the
right context by the compiler. ( Like the branch compiled when the compiler
encounters a YES? ).
LOOPEX the action taken with the NOW of loops. The address to the loop
entry follows LOOPEX in the program.
+LOOPEX like LOOPEX, but for the +NOW taking the increment off the
stack.
$JEEX The action going with the EACH . The address of the
corresponding LOOPEX address follows $JEEX in the program
to enable it to bypass the loop in case it is not to be executed at
all.
JUMP like BRONZ, but the jump is always taken and nothing is taken
off the stack. ( Unconditional jump ).
The following three routines define the types of IPS entries ( definitions,
constants and variables including fields ); their starting addresses are put into the
typecode fields of entries rather than into the normal program instruction stream.
Thus they do not require the header of the other code routines. For the compiler
they are represented as constants containing the starting address of the code.
The compiler places this address into the typecode field of the entry when
creating it. Upon execution of the entry, this address is found in the second step of
the emulation process and thus it must point to executable code.
90 Chapter 3 - The IPS System
6.4 Initialization
In addition to the executive described, all IPS versions require an
initialization routine setting up the various pointers and registers before the
emulator can be entered. This routine is only run through once; the memory space
can be used for other purposes later on. IPS initialisation
( DEFINITIONEN )
:n HIER $H @ ;n
:n H2INC HIER 2 + $H ! ;n
:n $DEP HIER ! H2INC ;n
:n $CEN $BU $PI ! 0 READYFLAG !B ;n
:n IE $P1 @ $PI @ 1 - je I @B #80 ODER I !B
nun 0 $P2 ! $CEN WEG ;n
:n SYSWRITE SYSLINE 16 >>> 0 IE ;n
:n L>>> anfang DUP 256 > ja? 256 - S>R PDUP 256 >>>
256 + VERT 256 + VERT R>S
dann/nochmal DUP >0 ja? >>>
nein: PWEG WEG
dann ;n
:n $NAME 0 READYFLAG @B
ja? 1 $CSCAN >0
ja? $PI @ $P1 !
2 $CSCAN PWEG #CE57 #8D
$P1 @ $PI @ ZWO - DUP 63 > ja? WEG 63
dann
DUP $ND !B 1 - ZWO +
je I @B $POLYNAME
nun $ND 3 + !B $ND 1 + ! 1
dann
dann ;n
:n COMPILER $NAME
ja? $SUCH
1 ( For continuing ) BEM @B
ja? ZWO 'n RUMPELSTILZCHEN
= ja? ( RUMP. ) 0 BEM !
nein: ( NICHT RUMP. ) Z-LESEN @
ja? PWEG 0 1
nein: ZWO BEA @ <
ja? IE WEG 0
dann
dann
dann
dann ( End limited input mode checks )
Sect. 7 - IPS Compiler 93
( COMPILER cont. )
:n !CHAR SP @ !B SP INCR ;n
Appendix A
Text Editor:
( entry by TEXT , exit by control D ( EOT ) )
( Also available during IPS-entries )
control \ ( FS ) Forwardspace
control H ( BS ) Backspace
control J ( LF ) Linefeed
( Only available in edit-mode )
control P ( DLE ) Deletes all after cursor, cursor to top
control M ( Return ) Normal function, not IPS entry indication
control ^ ( RS ) Recording start function
( Not with all IPS versions )
control O Deletes character
control N Inserts blank for character insertion
control R Deletes line
control Q Inserts empty line
control X Set blockcounter ( 1-5 ) to 1
( DEL ) Move cursor one line up
98 Appendices
1. Stack manipulations
Before operation After operation
DEL a b a
PDEL a b c a
DUP a a a
PDUP a b a b a b
SWAP a b b a
SOT a b a b a
RTU a b c c a b
RTD a b c b c a
S>R Stack to return stack )
R>S Return stack to stack ) one number
I Duplicate return stack to stack ) operators stack
operators logical operators arithmetic logical operators arithmetic operators
2. Arithmetic and logic operations
+ AND and all bits
- OR or all bits
* XOR exclusive-or all bits
/ INV complements all bits
MOD remainder BIT select one bit 0 - 15
/MOD quotient, and remainder on top
operators comparison comparison operators stack operators
3. Comparison operators
=0 >0 <0 = < > =/ >= <=
F-COMP ( expects two addresses and length in bytes. Compares two arrays
and delivers 1 if equal, 0 with smaller and 2 with larger ) >0 <0 =0
> < = >= <= =/ <>
4. Storage operations
@ Get word operators storage storage operators
@B Get byte; MS-byte =0
! Store word
!B Store byte; LS-byte only, MS-byte discarded
!T stores text from screen. Format:
" text " fieldname !T
!FC stores field components. Format:
n1 n2 n3 ... nZ fieldname Z !FC
!CHAR stores a number as character as pointed at by SP and then
increments SP
Appendix A - IPS short reference summary 99
5. Control of flow
a. YES? NO: THEN
b. EACH NOW or +NOW
c. LBEGIN LEND?
d. LBEGIN YES? THEN/REPEAT operators looping looping operators
6. Field operations
>>> from-addr to-addr num-of-bytes (1-256) >>>
L>>> same as >>> , but length 0 - 32767 bytes
!FC see Storage operations, 4.
!T see Text operations, 7.
TRANSPORT expects address, moves 512 bytes starting at this location into
tape output buffer $BU operators field field operators
7. Text operations
" arbitrary text-string not containing quote "
within definitions: text will be written at execution time starting at SP, SP
then points at first unused position.
outside definitions: leaves on stack address of first character ( on TV ) and
length of string on top. Length of string is number of characters
between quotes minus two. This may be 1-256 characters.
!T stores text strings in fields. Field must have sufficient length. Format:
" ... " fieldname !T
8. Systems operations
DEL/FROM name Deletes all entries starting at name.
READ Reads a cassette-block into field $BU .
RECORD Writes a cassette-block from $BU to tape.
OK Clears error messages, enables cassette read.
n DCHN ( n=0-7 ) dechains program at position n.
n ICHN name ( n=0-7 ) puts definition 'name' into chain at
location n .
' name WAITDEF replaces current program in chain temporarily by
definition 'name'. Only permissible at two
definition levels below chain.
n YES:CONT if n is odd, it reverses the action of WAITDEF and
resumes the program with the word following
WAITDEF. Only permissible at one definition
level below chain.
Note: WAITDEF and YES:CONT are to be used with empty stacks only.
Inside the EACH ... NOW there are items on the return stack. Use
LBEGIN ... LEND? with iteration parameters in variables instead!
The source of DUMPER is lost. Though specifically for the Atari 800XL IPS
and its 6502 processor, the following is similar. [Ed]
Appendix B
Cl
4Clv90
8Clv90
D=0 -> Cs
D=1 -> Cs
Note 1: while motor comes to stop, the recording of 50's continues; there are
no true block-gaps
Note 2: for recordings requiring exceptional data integrity, the first two bytes
of the trailer may be a CRC .
4. Tapes:
Standard audio tapes ( Fe2O3 ), C60 , C90 or C120
Two-track recording ( mono ).
5. Audio levels:
The levels comply with the DIN standards and connectors. In particular the
recording interface produces approximately 15 mV p-p at pin 1 of the DIN
connector for the recorder. The output circuit contains some harmonic suppression
to prevent beats with the bias-current of the recorder:
The reading interface expects about 2 V p-p at pin 3 of the DIN connector
from the recorder. The internal resistance should be below 20 kW.
104 Appendices
Appendix C
Remarks:
1. The most significant bit of the byte is occasionally used as parity check
bit. With IPS it serves to indicate an inversion of character and background
brightness; e.g. #D3, #D7, #C1, #D0 displays as SWAP
2. The characters of column 0 and 1, and the DEL are non-printing control
functions.
3. On teletypes ( e.g. ASR 33 ) the characters of column 6 and 7 are printed
by the characters of columns 4 and 5, i.e. upper case only.
Index 105
Definition 15, 20, 32, 74, 90 FIELD 10-13, 32, 74, 80, 95, 97
Definition very long 20 Comparison 13
DEL 6, 98 Components 12
DEL/FROM 37-38, 86, 100 Operators 12, 99
Deposit 1 byte 45 Set to zero 13
Deposit 2 bytes 74, 80 Transport 12
Dictionary 4, 10-11, 14, 31-32 FORTRAN 37
Display 3 Found processor 73-74, 93
Division 7 Front-panel 3
DMA 87
DUMPER 35 H
DUP 98 Hardware requirements 1-3
DUPLICATE NAME 14 Hash table 79
Header Pointer 33, 70
HERE 31, 100
E Hewlett Packard 64
EACH 18-19, 89, 99 Hex display 84
EDITFLAG 87-88 Hexadecimal number 6
Emulator 33-34 HIER 91
END 44, 49, 53, 57 Hints programming 37
Entry summary 81 HP 33, 70, 90
Generation 80 Storing 70
: 80
FIELD 80 I
VAR CON 80 I 9, 18-19, 98
ENTRYSETUP 80, 82, 94 I/O ports 24
Entrytype 32, 74, 77, 80-82 IBM-PC vii
Entrytype code 74 ICHN 28, 74, 85, 100
Error 17, 21, 34, 37, 45, 73-74, 80-82 IE 73-74, 80, 91
Error messages 14 IMMAGE 86
Executable code 33, 68, 70, 81, 89 INCR 99
Exercises 37-38 Index (I) 9, 18
Solutions 39 Indirect execution 69
Inner interpreter 33, 70
INPUT 24, 101
F Input buffer 2, 73-74
F-COMP 12, 16, 26, 39, 98 Input keyboard character 87
108 Index
O
OK 14, 100 R
Operators
R>S 9, 98
Arithmetic 6, 98
RAM 3, 31
Comparison 16, 98
RCODE 33, 50
Field 12, 99
READ 35, 86, 100
Logical 7, 98
READYFLAG 74, 80, 83, 87-88
Looping 17-19, 99
93, 100
Stack 8, 98
RECORD 35, 86, 100
Storage 98
Remainder 7
OR 7
Repeated execution 27
Oscar-13 vii
RETEX 82, 88
OUTPUT 24, 101
Return stack 8-9, 19, 31
Output buffer 31, 79, 99
Reverse polish notation 64
Overflow 7
ROM 2-3, 10, 86
RTD 98
RTU 98
P RUMPELSTILZCHEN 30, 73
Parameter field 20, 32, 80-82, 86 85, 100
Parameter stack 5, 8, 31
PDEL 98 S
PDP 11 3 S>R 9, 98
PDUP 98 Screen pointer 20
Permanent storage 9 Semicolon 15
PPC 33 Separator 4, 15, 76
Primitive 87 SOT 98
PRIMODIFY 82 SP 20, 22, 83-84, 101
110 Index