IMPLEMENTATION OF FORTH WITH FLOATING POINT

CAPABILITIES ON AN 8085 SYSTEM /

A Thesis Presented to

The Faculty of the College of Engineering and Technology

Ohio University

In Partial Fulfillment

of the Requirements for the Degree

Master of Science

Douglas R. -Graham, March, 1985

OHIO CDNlVERSLTY LIBRARY TABLE OF CONTENTS

PAGE CHAPTER 1 INTRODUCTION 1

CHAPTER 2 INTRODUCTION TO FORTH

CHAPTER 3 SYSTEM HARDWARE 3.1) The Basic SDK-85 Kit Configuration 3.2) Expanding the Memory 3.3) 82318 Arithmetic Processing Unit 3.4) Zenith Terminal 3.5) System Memory Map 3.6) SDK-85 Kit Monitor Routines 3.6.1) Keypad Operation of the SDK-85 Kit 3.6.2) Terminal Operation of the SDK-85 Kit

CHAPTER 4 THREADED INTERPRETERS 4.1) Dic t ionary Space 4.2) Different Types of Threaded Code 4.2.1) Threaded Code 4.2.2) Direct-Threaded Code 4.2.3) Indirect-Threaded Code 4.2.4) Token Threaded Code 4.3) Inner 4.4) Outer Interpreter

CHAPTER 5 OUTER INTERPRETER ROUTINES 5.1) ?SEARCH 5.2) ?NUMBER 5.3) ?EXECUTE 5.4) Other Outer Interpreter Routines 5.4.1) START/RESTART 5.4.2) TYPE 5.4.3) INLINE 5.4.4) ASPACE 5.4.5) TOKEN 5.4.6) QUESTION 5.4.7) $PATCH 5.4.8) *STACK

CHAPTER 6 USER INTERACTION WITH FPFORTH 6.1) Number Porma t s 6.2) Log ica 1 Flags 6.3) Constants 6.4) Variables 6.5) Character Strings 6.6) Arrays 6.7) System Variables

CHAPTER 7 STACK AND MEMORY REFERENCE OPERATORS 7.1) Data Stack Operators 7.2) Interstack Operators 7.3) Memory Reference Operators PAGE CHAPTER 8 INPUT AND OUTPUT ROUTINES 85 8.1) Single Precision 110 Operators 86 8.2) Double Precision I/O Routines 87 8.3) Floating Point Output Routine 88

CHAPTER 9 ARITHMETIC OPERATORS 9.1) Single Precision Operators 9.2) Double Precision Operators 9.3) Floating Point Operators 9.4) Conversion Routines

CHAPTER 10 LOGICAL AND RELATIONAL OPERATORS 10.1) Logical Opera tors 10.2) Single Precision Relational Operators 10.3) Double Precision Relational Operators 10.4) Floating Point Relational Operators

CHAPTER 11 LOOP AND BRANCH KEYWORDS 11.1) BEGIN and END Keywords 11.2) IF ELSE THEN Structure 11.3) WHILE Statement 11.4) DO LOOPS

CHAPTER 12 DEFINING WORDS AND SYSTEM KEYWORDS 12.1) Defining Words 12.2) Sys tem Keywords

CHAPTER 13 FPFORTH PROGRAMMING EXAMPLE

CHAPTER 14 CONCLUSION

Bibliography

Appendix A Chip Pin Outs

Appendix SDK-85 Kit Schematic

~~~endixC Memory Expansion Buffers Schematic

Appendix D Added Memory Schematic

Appendix E Schematic of 82318 Interface to 80858

Appendix F zenith Terminal Interface

Appendix G Outer Interpreter Routines

Appendix H FPFORTH Keyword Dictionary LIST OF FIGURES

PAGE Fig. APU COMMAND FORMAT 24 Fig. Structure of the APU Stack 26 Fig. Ope rand Formats 26 Fig. APU STATUS REGISTER 28 Fig. Basic SDK-85 KIT MEMORY MAP 3 1 Fig. EXPANDED MEMORY SPACE 3 2 Fig. Dictionary Structure 37 Fig. Primitive and Secondary Code Structures 40 Fig. Subroutine Threaded Code 42 Fig. Direct-Threaded Code 44 Fig. Indirect-Threaded Code 46 Fig. Token Threaded Code 4 7 Fig. Dictionary Bodies of a Primitive and a Secondary 44 Fig. Flowchart of Inner Interpreter 50 Fig. Flowchart of Outer Interpreter 5 5 Fig. RAM Memory Layout 58 CHAPTER 1

INTRODUCTION

The FPFORTH system is an extension of the normal FORTH language.

FORTH is a threaded interpretive language originally developed by

Charles Moore. The language has been used by several organizations as

a higher level language that can be used to control real time processes. FORTH has not gained widespread acceptance but has its place in microprocessor systems. Ron Loeliger developed his own version of FORTH in the early seventies. He later wrote a book entitled Threaded Interpretive Languages. The FORTH language described

in this book is the basis for the FPFORTH. FPFORTH has expanded

Loeliger's language to give it a much more powerful arithmetic

capability.

Loeliger's FORTH language was extended in the FPFORTH system by adding floating point arithmetic operations. The FP stands for the

added feature of floating point arithmetic. The FPFORTH language uses

Loeliger's concept of how a threaded interpretive language should work. The main idea behind a threaded code language is how control is passed along through the code. FORTH languages control how the

threading of addresses is done through what is called the inner

interpreter. The inner interpreter is usually a few very short machine

code routines. The FPFORTH language uses an indirect-threaded code

form of inner interpreter. The idea in an indirect-threaded code interpreter is that the routines consist of a list of addresses for routines.

The FPFORTH language is a complete computer language that allows the user to write complex programs. The advantage of FPFORTH over other FORTH like languages is its floating point capabilities. The floating point operations are made possible through the implementation of the 8231A arithmetic processing unit. The 8231A can perform single precision, double precision, and floating point operations. The APU also provides increased speed for all the arithmetic operations. The increase in speed arises from the fact that the arithmetic processing unit performs the math operations in hardware instead of a long software routine. The added features of the 82318 to the FPFORTH system make it possible to write programs that perform much number crunching. The FPFORTH system lends itself very well to performing scientific programming.

Programming with the FPFORTH system is extremely different than programming in languages such as Fortran or Basic. There are a couple of reasons for this difference. One reason is the use of a stack to hold the operands used in an operation. The use of a stack gives the language the flavor of reverse polish notation. For users not used to reverse polish notation, it will take time to get used to performing the expressions in this format. The other reason that ~rogrammingon the FPFORTH system is different is that programs are formed from a bottom up procedure instead of the normal top down approach. The reason programs are written in this way is because the FPFORTH language builds on itself. All new instructions written in the language are made up of already existing instructions. The user must therefore start at the lowest level of new instructions to be compiled and work

his way up. The user can do this method of stepping his way up until

the point is reached when one instruction will perform the operation of

a whole program.

The FPFORTH System is almost a complete personal computer system.

The FPFORTH system lacks two features that keep it from being a

complete system. The biggest feature lacking in the system is some

form of secondary storage that would allow a user to store already

developed instructions or programs permanently. The other feature

lacking in the FPFORTH system is an editor. The creation of new

instructions is performed interactively with the system. If a user

wants to change a previously defined instruction, he will have to

reenter the whole definition of the instruction again. Without an

editor it is impossible to change part of an instruction already

defined. The two missing features just mentioned can be added to the

system with the addition of a little hardware and by extending the

FPFORTH language.

You as the user of the FPFORTH system may feel that the system

lacks some of the capabilities you desire. The FPFORTH system is very

strong in this point because the system can be expanded to do almost

anything. The system is very flexible because of the way the core

language can be expanded by a user. The FPFORTH system can have many wide and varied applications. The user can decide on the application

and build the FPFORTH language toward meeting the needs of this

application. The FPFORTH system is limited only by the imagination of

the user in most cases.

The FPFORTH system is presented in the following chapters with an emphasis on what the user is able to make the system do. A general

FORTH language is described in Chapter 2 to give the reader an idea of

how the system works. The reader will also see how the FORTH language

was expanded into a more sophisticated arithmetic processing language.

The hardware for the FPFORTH system is presented in Chapter 3 before

the software is presented because much of the software was written to

take advantage of the hardwar2 features. The hardware being described

early will give a better view of how the software uses the system hardware. The FPFORTH language is then detailed, starting with the

inner workings of the interpreter. After the interpreter is described

the FPFORTH instructions are briefly described in chapters broken down

according to different types of instruction sets. The different

FPFORTH instructions will often be referred to as keywords or routines

throughout the text. A keyword is merely an instruction that is

available to the user. Chapter 13 shows some examples of the FPFORTH

language capabilities and demonstrates some of the keywords that were

added to expand the normal FORTH language.

The text should give the reader a fairly good understanding of the

FPFORTH system, but a prior knowledge of FORTH is very helpful. This

text does not try to present the entire use and operation of a FORTH

language but give the reader with a knowledge of FORTH the advantages

that are available on the FPFORTH system. CHAPTER 2

INTRODUCTION TO FORTH

FORTH is an interactive interpretive language that was first developed by Charles Moore. The language is very unique in that operations are performed on numbers taken from a stack. All operations

involve use of what is termed the data stack to store values or to use values from the data stack to perform an operation. There is another stack in the system called the return stack which holds addresses used by the inner interpreter to control code execution. The original version of FORTH worked only with 15-bit integers, which gave constant data size for storage in the stacks. The use of word length integers limited the arithmetic capabilities that can be performed by FORTH.

Later versions have added longword operators to allow a wider range of

integer numbers. Longwords are 32-bit operands used in an instruction. Some versions of FORTH have an intermediate length operand of 24-bits. The addition of various length operands requires various length data values to be stored on the data stack. The user must keep track of the operands on the data stack to ~erformthe correct data length operation. Performing a word length operation on a longword operand will obviously return a wrong result. The word length operator will use the upper and lower words of the longword to perform

the operation. The addition of the longword operation has increased the capabilities of the system but has incurred the cost of a more complicated system from the user's point of view.

The FORTH language is an interactive language because it responds

to a user command immediately. The user enters a command and the computer system then responds to this command. The user and the system take turns responding to each other and this is what is termed interactive. The user terminates the entering of a command by entering a carriage return and the system will then execute the commands. The system designates the end of the command's execution by an OK message on the display if the command was executed without an error, otherwise it terminates with an error message. The language is also interpretive because FORTH translates an instruction into machine code instructions and executes the instruction before reading the next instruction.

These two features are accomplished by reading the instruction and searching through the system dictionary to find the instruct ion. Once the instruction is found, the code that defines the instruction is executed. The list of instructions is called a dictionary because the instruction is listed followed by the code that defines the instruction, similar to an english dictionary.

Now that a brief description of FORTH is given, a more enlightening view of how the FORTH language works can be given through some very simple examples. All FORTH language responses will be underlined to differentiate them from user entries. The first thing that needs to be shown is how data is placed on the data stack.

200 -OK

The user has typed the number 200 and the number is placed on top of the data stack. The OK message shows that the number has successfully been placed on the data stack.

.200 OK

The . takes the top number on the data stack and displays it on the

terminal. The question may be raised as to why a period was used to display the top stack entry. FORTH was written to be concise, therefore, many of the keywords are only a couple of characters and are not very descriptive of the operation to be performed. The user is now able to enter and remove data from the data stack. The two steps could be combined on one line as shown:

50 . 50 OK

The user will want to do operations on numbers placed on the stack to perform a desired function. One keyword that is often used in the

FORTH language is DUP which duplicates the top stack entry.

5 DUP -OK . .55OK

The number 5 was placed on the data stack and then duplicated on the stack using DUP. The top two stack entries were then displayed to show the DUP performed the desired operat ion.

5 DUP J; . 25 OK

The * keyword multiplies the top two stack entries together and places the result on the data stack. The . displays the top stack entry on the terminal. The data stack is empty after these operations as the * keyword removes the top two stack entries and places the product on the stack. This step also demonstrated the reverse polish notation flavor of the FORTH language. The two operands are already on the stack and the operation then uses these operands.

The operation of squaring a number is a feature that is often used in many applications. The FORTH language does not offer this keyword but does allow the user to define new keywords. The keyword defined by the user is added to the dictionary and is treated as though it was part of the original FORTH language. A new keyword will be defined called SQUARE that will square the top number on the stack.

: SQUARE DUP * '-. OK

The : keyword tells the system a new keyword is being defined and places the system into what is called the compile mode. The system

takes the word following : as the name of the new keyword being defined. The keywords following SQUARE and before ; are added to a

list in SQUARE'S definition. This list represents addresses of the keywords to be excuted when SQUARE is excuted. The interpreter executes SQUARE by getting the first address from the SQUARE definition and executing the keyword at that location. After the keyword in the list is executed, the next address is taken from the list and the corresponding keyword is executed. This procedure continues until all keywords in SQUARE'S definition have been executed. The ; keyword terminated the new keyword definition and adds the inner interpreter routine to terminate SQUARE 'S execution.

10 SQUARE . 100 OK

The FORTH language has now been expanded by one new keyword. The keyword was added to the FORTH dictionary when it was defined and can be used just like other keywords that were part of the original FORTH

language. Let a new keyword CUBE be defined by using SQUARE.

: CUBE DUP SQUARE * . '- OK

CUBE has now been defined using SQUARE. The keyword duplicates the

top data stack entry and takes the SQUARE of the number. The original number is then multiplied by its square and the result is then dis- played. The language has again been expanded to offer a new

instruction. Many times an operation is to be performed several times.

FORTH allows an operation to be performed many times by using its loop and branch instructions. This can be demonstrated by finding the cubes of the numbers 1 through 10. The control structure that would best perform this repetitive operation is the DO LOOP.

: lOCUBES 10 1 DO I> CUBE CRET . LOOP '-. OK The definition of the keyword lOCUBES is similar to the other keywords'defined as it starts with : and ends with ;. The 10 is the ending argument in the looping process, while 1 is the starting index.

The starting index is incremented by 1 each time the loop is executed.

The loop being executed starts with DO and ends with LOOP. All instructions between DO and LOOP are executed every time the loop is executed. The looping process ends when the starting index equals the ending argument. The I> keyword takes the loop index and places it on the data stack. The CRET keyword issues a carriage re.turn line feed to the terminal. The lOCUBES keyword will now be executed to demonstrate its action. lOCUBES -1 -8 -2 7 -64 -125

The results are shown, but the last solution is not shown. 10 cubed was not obtained because the loop ends at ten and does not execute at this value. The ending argument needs to be changed to 11.

To change the ending argument the keyword must be completely reentered.

By entering the keyword again the search process will find the new definition and ignore the old keyword entry. The FORTH language finds a keyword by searching through the dictionary starting with the latest entry and continuing to the first dictionary entry. The search ends as soon as the keyword to be executed is found in the dictionary. The redefined keyword will be found first and executed. This procedure of redefining the keyword leaves the old definition in the dictionary taking up space but not accessible to the user. The keyword can be deleted by the FORGET keyword. This is the best method 'to use when no other keywords will be lost by delinking the keyword from the dictionary list. Other keywords added after the keyword to be for- gotten will be lost also as they are linked to the rest of the dictionary through the keyword being deleted.

FORGET lOCUBE lOCUBE ?

lOCUBE was not found in the dictionary because it does not exist and FORTH responded by displaying the unidentified token followed by a question mark. When an error like this occurs, the instruction is aborted and the user can correct his mistake.

FORGET lOCUBES -OK ' : lOCUBES 11 1 DO I> CUBE CRET . LOOP 1- OK lOCUBES

So far, the examples presented have not used more than one number

to perform a function. The operations have only had, at most, two

numbers on the stack at once. A little more complex problem will now

be solved. Suppose it is desired to plot the function Y = 4XA2 + 6X +

4 on the XY plane from X = -5 to X = 5. A keyword could be written to evaluate the function at the value on top of the data stack. First the keyword will be defined as the function is written from left to right.

The keyword will then be redefined by nesting the equation. The function will be written out first in reverse polish notation as that is how it will be evaluated in FORTH.

Y=XA24*X6*4++

:EQ1 DUP SQUARE 4 * SWAP 6 * 4 + + : OK

The EQ1 keyword duplicates the number on the stack so the value of

X will be available for the second term in the equation. SWAP interchanges the top stack entry and second stack entry on the stack, which will place X on top of the stack and the value of 4X-2 in the second stack word. A better way to solve this equation is now given.

Y=X4*6+X*4+

:EQ1 DUP 4 * 6 + * 4 + ; OK

The nesting method of solving the equation is much more concise and will execute faster than the first definition of EQ1. The keyword to solve the equation for X = -5 to X = 5 will now be defined.

: FN1 11 0 DO I> 5 - EQ1 CRET . LOOP .9- OK

The loop index must be positive, so the starting index is set equal to zero and the ending index is set to 11. Inside the loop 5 is sub- tracted from the loop index to evaluate the function at the proper value of X. The rest of the keywords should be self-explanatory as they were defined previously. The output would be much nicer if the values of X were output beside the values of Y. The output would also be easier to understand if the values were labeled. Labels will first be defined to distinguish be tween the X and Y values.

: XMSG [ X = OK 1 9-

The XMSG and YMSG keywords ar? defined to display the messages contained in the brackets. The brackets mark the beginning and end of the string to be displayed when the keyword is executed. A new keyword will now be defined to compute the value of the function and display the X and Y values along with labels.

: FN2 11 0 DO CRET I> 5 - DUP EQ1 SWAP XMSG . YMSG . 9-' OK FN2 X=-5Y =74 X=-4Y =44

The desired results are output in a nice labeled format and easy to read. The examples presented have not used any variables. Variables are not often needed in the FORTH language because with good program- ming all data can usually be accessed from the stack. FORTH does allow both constants and variables to be defined by the user. Below is an example of a constant being defined.

12 CONSTANT DOZEN OK

The CONSTANT keyword makes a dictionary entry with the word fol- lowing CONSTANT as the name of the keyword entry. The top stack entry is the value given to the constant defined. In the case of the example, the keyword DOZEN places 12 on top of the stack when it is executed.

DOZEN. 12 OK

A variable declaration is similar to a constant declaration. The top stack value is the initial value given to the variable. When the

VARIABLE keyword is executed it places an address on the stack. This address is the location where the value of the variable is stored.

Below is an example of a variable.

144 VARIABLE GROSS OK GROSS @ . 144 OK

GROSS places the address of the variable on the top of the data stack. The keyword @ replaces the address with contents of the memory location at that address. The . then displays the top of stack value.

The CONSTANT and VARIABLE keywords add a new entry to the diction- ary when they are used. A constant or variable is seen to be similar to an instruction in the FORTH language. The constant or variable keyword execute by placing a value on the stack. A constant places a data value on the stack while a variable places an address on the stack when executed.

The examples presented here show just a few of the FORTH keywords, but it is hoped that an understanding of how the FORTH language can be changed and expanded by the user is seen. One feature that is not at all specifically shown is that a keyword definition replacing several keyword instructions will perform much quicker than the individual commands on the line. The increase in speed is due to the fact that the new keyword has all the addresses of the keywords to be executed.

The dictionary does not have to be searched for each keyword used in the definition. However, when individual keywords appear on a line, the dictionary must be searched for each keyword that is to be executed. New definitions that perform several previously defined keywords in their execution saves the need for the dictionary searches for the keywords.

The FORTH language was designed to be expanded by the user to meet the individual needs of the task to be performed. The language is very flexible and able to be adapted to perform almost any task. The user simply needs to expand the dictionary with new keywords to perform new operations. The examples shown, though very rudimentary, were meant to show this feature. The examples were also meant to demonstrate how the

FORTH language actually builds upon itself. In other words, how new keywords are built from existing keywords. The new keywords can then be used to define even newer keywords. This process of building the language can be done until a single keyword will be defined that will actually perform an entire program. CHAPTER 3

SYSTEM HARDWARE

The hardware in this system is centered around two powerful pro- cessing units that give the system a very large arithmetic capability.

The 80858 is the of the FPFORTH system and controls the system processing. The other processing unit is the 8231A

Arithmetic Processing Unit that is capable of performing single precision, double precision, and floating point operations. The system was built up from the Intel SDK-85 board, which provided a good base for the prototype FPFORTH system. The SDK-85 kit is an introductory system for microprocessors, but can be expanded into a much larger microprocessor based system. The kit was used for its expandability and because it supported some on board development with its monitor routines. The final hardware system contains the basic SDK-85 kit with the 8231A handling the arithmetic operat ions, expanded memory space for the FPFORTH language, and communication with a Zenith terminal through a standard RS232 bus. The complete hardwar? system for the FPFORTH system is described in the following sections.

3.1) The Basic SDK-85 Kit Configuration

The Intel SDK-85 kit has the Intel 80858 as the central processing unit for the FPFORTH system. The basic kit contains an 8279 Keyboard/

Display Controller, an 8355 ROM/IO, and an 8155 RAM/IO/TIMER. The kit also contains the rest of the circuitry to make the system operational, which includes the 3205 decoder used for chip selection, the keypad circuitry, and the 7-segment displays with its current driving circuitry for the LEDS. The kit can be expanded as desired to a wide variety of configurations and is one reason this system was chosen to develop the FPFORTH system on. The SDK-85 kit also has some software routines in its monitor that make program development possible. These routines are very rudimentary, but make it possible to test code seg- ments as they are written directly on the FPFORTH system. Therefore, the SDK-85 kit is a very logical choice from which to build the FPFORTH system.

The central ~rocessingunit of the FPFORTH system is the 80858.

The 80858 is an 8-bit microprocessor packaged in a 40 pin dip. The pin configuration of the 80858 is shown in appendix A. The 80858 operates at a 3-MHz clock rate on the SDK-85 kit, which corresponds to a 1.3 microsecond instruction cycle. This clock rate is about top speed for the 80858 and is more than fast enough to give good response to the user on the FPFORTH system. The 80858 has a number of internal registers which makes it a good choice in a FORTH system where tem- porary address storage is necessary. The 80858 has an 8-bit accumulator, six 8-bit registers, that can be grouped to form three

16-bit registers, a 16-bit stack register, and a 16-bit . Thus, there are enough registers to make address manipu- lations fairly easy.

The 8279 Keyboard/Display Controller does just what the name implies. It scans the keypad for key closures and decodes it for use by the 80858. It also controls the display of the six 7-segment displays. The 8279 scans through the 7-segment displays and sends out the appropriate bit pattern to turn on the corresponding LEDS. Also the 8279 has an internal buffer that is 16 bytes long to store the patterns for display which leaves the 80856 free to do other processing while the display is still operable. The same is true of the keypad, where scanning of the keys is continuous and the 80856 can read the status of the 8279 to detect key closures. The 8279 is a 40 pin chip and the pinout is given in appendix A.

The 8155 is a 256 X 8-bit RAM with three input!output ports. The full 256 bytes are not usable in the SDK-85 kit because some of these locations are used by the kits own monitor system. The user can employ approximately 200 bytes of this memory without the kit's monitor interfering with it. The three input/output ports are user program- mable to be either input or output. Two of the ports are 8-bits wide while the third port is only 6-bits wide and its lines can be used as control lines to perform handshaking. All ports are initially brought up as input ports on start up or, reset, the 8155 is a 40 pin chip with its pinout given in appendix A.

The 8355 is a 2K X 8-bit ROM with two input/output ports. The ROM of the 8355 holds the complete SDK-85 kit monitor program. The 8355 is also a 40 pin chip and its in out is given in appendix A. The two ports of the 8355 are 8-bit ports that can be programmed by the user as either input or output. These ports differ from the 8155 ports in that each line of a port can be specified as either input or output as desired. This is accomplished by loading the Data Direction Register with the proper bit pattern where A=O for input and A=l for output.

Like the 8155 ports, the 8355 ports are initially input ports on start up or reset.

The chips mentioned above are the main components of the SDK-85 kit. The rest of the kit consists of the circuit board, keypad, clock crystal, and display. A schematic diagram of the kit layout is given in appendix B. The diagram shows that the systern has space for another

8355 and 8155 to expand the basic system. These were not added on this system as they were not needed and offe.red no advantage for having them there. The chip selection is done by means of a 3205 High-Speed 1 Out

Of 8 Binary Decoder and uses address lines A15 and A14 to enable the

3205. Address lines A13, A12, and All are then used to select the proper chip. The 8355 monitor ROM is located at addresses 0000-OFFF.

The 8355 expansion ROM is located directly above this ROM at

0800-OFFF. The 8279 KeyboardIDisplay Controller is located at

1800-1FFF where the address line A8 is being accessed. The 8155 RAM110 is located at 2000-20FF with foldback of these EUM locations running from 2100-27FF. The foldback occurs because the system does not use full decoding. The 3205 breaks the memory space down into 2 Kbyte blocks running from 0000-3FFF. This means that 'he 8155 occupies a 2

Kbyte block when it only needs 256 bytes and makes it possible to address the RAM locations with eight different addresses. As can be seen, there is not an address latch to demultipli~xthe low order byte of the address bus from the data bus. It was no: necessary for this system to demultiplex the busses because the 835, and 8155 do it internally. The 8279 does not need to latch the address bus because it does not use the low order byte of the address blls at all. The remainder of the SDK-85 kit is very basic and should be self- explanatory from the schematics .

3.2) Expanding The Memory

The SDK-85 kit makes it possible for the user to expand the memory by 32 Kbytes at locations 8000-FFFF. The kit provides locations on the circuit board for buffering the address bus, data bus, and control bus. The buffering is required because the 8085A can only drive 10 TTL loads and will not be able to drive much else besides the initial

SDK-85 kit. The schematic for the expansion buffers is shown in appendix . The expansion requires three 8216's to buffer all control lines. Two additional 8216's are used to demultiplex the low order byte of the address bus from the data bus. The a~ddresslines are then run into two 8212 octal latches that latch the address lines when the

ALE signal goes from high to low. Access is available to all lines that are coming from the 80858 with the address and data bus demultiplexed. All of these were added to the system in order to add memory to support the FPFORTH core language in EE'ROM and to add enough

RAM to support the stacks, line buffer, and additional dictionary space.

The amount of memory needed for FPFORTH is a little over 6 Kbytes and therefore 8 Kbytes of EPROM were added to the system. 2716's were chosen because of their availability and ease of use. They are very compatible with the Intel 80858 and only require circuitry for chip selection to work in this system. The specific c:hips used have an access speed of 450 ns, which is within the limits of the access time allowed for the 8085A running at 3-MHz. The four 2716's are located at memory locations 8000-9FFF. This is a total of 8 Kbytes of ROM which is more than adequate for the FPFORTH language. This provides room for the addition of new routines to the basic core language in the future.

2 Kbytes of RAM were then added to take care of stacks, the input line buffer, and dictionary space. The four 2114's used have an access time of 450 ns. The 2114 chip is a 1K x 4-bit random access memory,

therefore it is necessary for two 2114's to be used in parallel to obtain a 1K x 8-bit memory space. One of the RAM pairs is used for the data stack, return address stack, and input line buffer. The other pair of RAMS is used for dictionary space which is used by the FPFORTH system as a user dynamically creates routines to add to the basic

FPFORTH language. These memory chips are selected by decoding performed by a 3205 High Speed 1 Out Of 8 Decoder. The 3205 is enabled by address lines A15 high, A14 low, and IO/M low which gives a memory range of 8000-BFFF as being selected through the 3205. The EPROM memory is located as previously stated in locations 8000-9FFF and RAM memory is located at 8400-B7FF and BCOO-BFFF. The RAM has rollover space at R000-B3FF and B800-BBFF respectively. The rollover occurs because lines A13, A12, and All are used for decoding and selecting the appropriate memory chip which breaks memory into blocks of 2 Kbytes and the RAM chips are only 1 Kbyte. The complete schematic of the entire memory system is given in appendix D. A memory map of the entire system is given in section 3.5.

3.3) 82318 Arithmetic Processing Unit

The 82318 APU is an arithmetic processing unit that performs several arithmetic operations in single-precision (16-bit), double- precision (32-bit) and floating point (32-bits) formats. The chip is also referred to as the AM9511 APU, but is exactly the same chip. The addition of this chip to the system gives the FPFORTH language the ability to perform several math functions that are very lengthy routines if implemented in software. The 82318 is also very compatible with the Intel 80858, as it was designed for use with the 8085 and 8080

CPUs. The 82318 is treated as an I/O port in this system with PORT 80 as the Data Stack Register and PORT 81 as the Command Register. The hardware interface between the 8231A and the 80858 is shown in appendix

To provide I/O mapped addressing of the 8231A, address line A15 is

NANDed with the IO/M to generate the chip select line. The reason for using A15 is that the port address is duplicated on the high order address byte besides the low order address byte. The upper address byte does not have to be latched and therefore does not suffer the delay that the low order byte does. The A8 address line is tied directly to the 82318 to address the Data Stack (A8=0) or the Command

Register (A8=1). The signals RD and WR are tied directly to the 82318 to determine what kind of 110 operation is occurring. If A8=1 and the

RD line is low, the 82318 Status Register is read. If A8=1 and the WR line is low, a command word is written into the APU Command Register.

If A8=0 then either a byte is being written onto the top of the 82318

Data Stack or a byte is being read from the top of the Data Stack. The

82318 cannot always respond as fast as the 8085 desires and requires some way to stop the 8085 until the 82318 is ready. This is accom- plished by a PAUSE signal coming from the 82318 which is tied directly to the READY line of the 8085. This line is high in its normal state and is pulled low by the APU under the following conditions:

1) A previously initiated operation of the APU is in progress and a command entry is attempted.

2) A previously initiated operation of the APU is in progress and stack access has been attempted.

3) The APU is not busy and data removal or data entry has been requested. READY line stays low until the data transfer is completed . The 8231A APU performs operations based on commands supplied by the

8085 through the Command Register. Each command represents a single

8-bit word that specifies the operation, data format, and service request. The format of the command is shown in figure 3.1. Below is a summary of all the commands of the 8231A APU with bit 7=0 and where TOS

= Top of stack and NOS = next on stack.

32-BIT FLOATING POINT INSTRUCTIONS

INSTRUCTION DESCRIPTION HEX CODE

ACOS Inverse Cosine of TOS AS IN Inverse Sine of TOS AT AN Inverse Tangent of TOS CHSF Sign change of TOS cos Cosine of TOS (radians) EXP EATOS Function FADD Add Tos and Nos E'D IV Divide NOS by TOS FL TD 32-bit integer to floating point conv. FLTS 16-bit integer to floating point conv. FMUL Multiply TOS and NOS FSUB Subtract TOS from NOS LOG Common Logarithm (base 10) of TOS LN Natural Logarithm of TOS POPF Stack Pop PTOF Stack Push PUP I Push PI onto stack PWR NOS-TOS power function SIN Sine of TOS (Radians) SQRT Square root of TOS TAN Tangent of TOS (radians) XCH F Exchange TOS and NOS on stack

32-BIT INTEGER INSTRUCTIONS

INSTRUCTION DESCRIPTION HEX CODE

CHSD Change sign of TOS DADD Add TOS and NOS DDIV Divide NOS by TOS DMUL Multiply TOS and N~S(~esultlower 32-bits) DMUU Multiply TOS and NOS(~esu1t upper 32-bits) DSUB Subtract TOS from NOS FIXD Floating point to 32-bit integer POPD Stack pop PTOD Stack push XCH D Exchange TOS and NOS 16-BIT INTEGER INSTRUCTIONS

INSTRUCTION DESCRIPTION HEX CODE

CHSS Change sign of TOS FIXS Floating point to 16-bit integer POPS Stack pop PTDS Stack push SADD Add TOS and NOS SDIV Divide NOS by TOS SMUL Multiply TOS and NOS(resu1t lower 16-bits) SMUU Multiply TOS and NoS(resu1t upper 16-bits) SSUB Subtract TOS from NOS XCHS Exchange TOS and NOS NO P No operation OPERi1TION CODE SVEQ SINGU FIXED

Bits 0-LC select the operatlm of the APU Sits 5-6 select the date format for the o;?eration. Bit 5' selects floatins point format(bit 5x0) or fixed-point data(bit 53). If bit 6=0 then double-precision(32-bits) is selected, bit 6=1 selects ~in~le-~recision(l6-bits) . Bit 7 indicates whether a service request is to be issued after the command is executed, lf bit 7=1 a service request will be generated; If bit 7=0 a service request 1,vill not be generated.

Figure 3 ,I) APU C O?IF\.lAND FORIilAT The 80858 has an 8-bit data bus, so the 82318 is limited to this

bus size even though it performs arithmetic operations on 16-bit and

32-bit operands. The 82318 was designed to be a stack oriented

processor so that it could store information beyond the needed operands

for a single command execution. The 82318 has an eight level, 16-bit

wide data stack. Single precision fixed-point operands are 16 bits

wide which means eight operands can be stored in the stack (figure

3.2a). When the data are double precision fixed-point or floating

point operands, only four operands may be stored in the stack (figure

3.2b). Data is written into the stack byte after byte, in the order

shown (B1 ,B2, B3,. . .) . Data is removed from the stack in reverse order

(B8,B7,B6, ...).

The format of each of the three operand formats is given in figure

3.3. Single precision uses 16-bit words with the most significant bit

being the sign bit. The numbers are represented in two's complement notation which gives the number range for single precision of -32,768

to +32,767. Double precision is the same as single precision except it

uses 32-bit operands which gives a number range of -2,147,483,648 to

+2,147,483,647. Floating point is shown in figure 3.3 and it can be

seen that it has 24-bits to represent the mantissa, 6 bits for the

exponent, 1 bit for the exponent sign, and 1 bit for the mantissa

sign. The mantissa and exponent are represented in sign magnitude

notation unlike that used for single and double precision operands.

The magnitude range of the mantissa is a value between 0.5 and 1. The magnitude of the exponent ranges in value from 0 to 64. The range of

values for floating point numbers is -9.2 x 10 18 to -2.7 x loA-20 and

+2.7^-20 to +9.2 x 10-18. Floating point gives a much larger range of TOS NOS 8 BYTES

It-- 16 BITS -4 Fig. 3.2 a) Structure of APU stack with 16-bit operands,

TOS -+ B B B El -T NOS j-B B B 3 ' 4 3YTES :L It-- 32 BITS --A Fig. 3.2 b) Structure of APU stack with 32-bit operands.

15 10 SINGLE P~CISION

DOUBLE PRZCISION

IMSI ES I EXP I MbNTI3SA 31 30 29 21: 23 0 F5OATING POIITT Fig, 3.3) Operand formats numbers than in either of the other two formats, but a loss in significant digits also occurs. Floating point is accurate to only 7 significant digits as compared to 10 with double precis'ion arithmetic.

The 82318 APU has a Status Register that gives information about

the top number on the stack according to the last operation performed.

The format of the Status Register is shown in figure 3.4. The BUSY bit

indicates whether the 8231A is currently executing a command (BUSY = 1) or is presently idle (BUSY = 0). The SIGN bit indicates that the value on the top of the APU stack is positive (SIGN = 0) or negative (SIGN =

1). The ZERO bit indicates whether the top value on the stack is equal to Zero (ZERO = 1) or not equal to zero (ZERO = 0). The ERROR CODE field contains an indication of the validity of the result of the last operation. The error codes are:

0000 - No error 1000 - Divide by zero 0100 - Square root or log of negative number 1100 - Argument of inverse sine, cosine, or EAX too large XXlO - Stack underflow XXOl - Stack overflow

The CARRY bit indicates whether the previous operation resulted in a carry or a borrow from the most significant bit (0 = No Carry/No

Borrow, 1 = Carry/Borrow). If the BUSY bit = 1, the other status bits are undefined until the current operation is completed.

The time required for the 82318 APU to execute a command ranges

from 1.3 us to 4 ms. This is quite a wide variation in execution time, but this is due to the wide variety of instructions by the

APU. The table below gives the instruction and a range for the number of clock cycles needed to execute the command. Multiplying the number of clock cycles by the time for one clock cycle (333 ns) will give the command execution time. 4 I I I 3USY SIGN ZERO ERROR CODE CARRY I I

Fig. 3.4) APU STATUS REGISTZR COMMAND EXECUTION TIMES

COMMAND CLOCK COMMAND CLOCK COMMAND CLOCK MNEMONIC CYCLES MNEMONIC CYCLES MNEMONIC CYCLES

SADD FSUB NOP SSUB FMUL CHS S SMUL FD IV CHSD SMUU SQRT CHSF SDIV SIN PTOS DADD cos PTOD DSUB TAN PTOF DMUL AS IN POPS DMUU ACOS POPD DDIV ATAN POPF FIXS LOG XCHS FIXD LN XCHD FLTS EXP XCHF FADD PWR PUP I

3.4) Zenith Terminal

Interfacing the user to the FPFORTH system is performed through a

Zenith terminal. The zenith terminal is connected to the FPFORTH hardware system through the standard RS232 bus. The transfer of data is performed at 110 baud, which is extremely slow, but was used because the monitor of the SDK-85 kit has routines that communicate to a terminal at 110 baud. The SDK-85 kit normally talks to the keypad on the circuit board, but can talk through the RS232 hookup with the flip of the toggle switch on the right side of the board. The system can therefore be run from the keypad or a keyboard, but the FPFORTH language is only executable through the terminal. The system can run at a considerably faster baud rate if the monitor routines are not used to start the FPFORTH program execution.

The serial data transfer is carried out by the 8085 CPU itself with- out any peripheral chip doing the data transfer. There are no hand- shaking signals used in this setup and the only lines needed are a serial out line, serial in line, and a system ground line. The Zenith

terminal is connected as shown in appendix F. The two chips, MC1488

and MC1489, are used to change the voltage levels from, TTL to RS232

levels and vice versa. These are the only extra chips needed to

perform the data transfer since the 8085 has its own serial data lines

SID (Serial In Data) and SOD (Serial Out Data).

The Zenith terminal has all the hardware necessary to take care of

the keyboard and display. The terminal takes care of keyboard scanning

and decoding. When a key closure occurs the terminal sends the

character in ASCII code to the FPFORTH system at 110 baud. No parity

is used because this would complicate coding of the software routine

that handles the reading of a character. The display is also driven by

the Zenith system so that the refresh drive and RAM storage of the

display are all taken care of and require no help from the host

system. The zenith terminal takes all work off the system it is

connected to and the host system only has to handle data transfers.

3.5) System Memory Map

The preceding sections have given a brief look at the hardware used

in the FPFORTH system. The diagrams in figures 3.5a and 3.5b give a complete look at the memory layout. These should give a better view of

the system configuration.

Locations 4000-7FFF are not usable on the SDK-85 kit, because the expansion memory buffers are only active when address line A15 is high which means there is no way to connect any memory at locations 4000-

7FFF without changing the circuit board. Locations C000-FFFF are left

open and no chip decoding logic is now implemented on the system. The CHIP ENABLZ LImS

MONITOR ROM (2x1

-- -- EXPANSION ROM (2K) OFFF 1000 OPEN (2K)

EXPANSION RAM (256 SYTES)

ZXPANSION RAN (FOLD B4CK)

OPEN (2K) CS,

OPEN (2K)

Fig. 3.5 a) BASIC SDK-85KIT METdORY IUP CHIP ENABLE LIhZS FROM ADD^ 3205 - .. 2716 EPROM 1.

FPFORTH DICTIONARY

2716 EPROh4 2

FPFORTH DICTIONARY

2716 EPRORI 3 FPFORTH DICTIOK4RY

2716 EPROM 4 FPFORTH DICTIONARY

OPZN (2K)

OPEN (2K)

FOLD BACK FLU9

B7FF 2114 FUJI! (1K) B800 BBFF FOLD BACK RAl~I BCOO 2114 RAPII (2K) BFFF 3

Fig. 3 .,5 b) EXPANDED IIIEldORY SPACE reason being that at this time there is no need for more memory space on the system.

3.6) SDK-85 Monitor Routines

The SDK-85 kit has its own monitor that allows the user to interact with the system via a keypad and six 7-segment displays or to a remote terminal or teletype. These routines allow the user to do the following things :

1) Examine the contents of all memory and register locations. 2) Deposit program steps or data in RAM or register locations. 3) Execute programs or upon command. 4) Interrupt and start operation at a location you specify.

Upon command these operations may be performed through the keypad or the Zenith terminal. The choice is made between the two by a toggle switch located on the right side of the system board. The two modes of operation are described separately in the following sections.

3.6.1) Keypad Operation of the SDK-85 Kit

The SDK kit has 24 keys on its keypad with 16 of the keys being used for the hex digits 0-F. The other 8 keys are command keys that cause an operation to be performed. The following gives a brief description of the function of the 8 command keys.

RESET This key causes a hardware reset of the whole system. This key is functional in both the keypad and terminal mode of operation.

SUBSTITUTE MEMORY The substitute memory command allows you to read the contents of ROM memory and to examine and modify the contents of RAM memory locations.

EXAMINE REGISTERS The examine command allows you to display and modify the contents of the 8085 CPU registers.

Pressing the GO key causes the contents of the program counter to be displayed in the address field. The program counter can be changed to any desired location. Program execution will start at the location that is shown when the execute key is depressed.

SINGLE STEP Pressing the single step key causes the contents of the program counter to be displayed in the address field of the display. The program counter can now be changed to any desired address. Pressing the NEXT key will cause the program to execute the next instruct ion and stop. The EXECUTE key exits the single step mode and memory locations or CPU registers can be examined.

VECTOR INTERRUPT The vector interrupt key is similar to the GO key in the respect it takes control away from the monitor and gives it to a program. The interrupt key causes immediate recognition of the RST 7.5 interrupt and control passes to location 3C in the monitor. There is an unconditional branch to location 20CE in the basic RAM. This key is also functional in the terminal mode of operation.

3.6.2) Terminal Operation of the SDK-85 Kit

The terminal portion of the monitor has the same basic routines as the keypad which gives the user the ability to work from the terminal just like the keypad. The terminal has the advantage in that it can display the contents of a whole block of memory locations. The terminal mode of operation also has a move command that allows blocks of data to be moved in memory. The terminal mode does, however, lose the use of the single step mode of operation. The following gives a brief description of the function of the terminal monitor routines.

DISPLAY PfEMORY COMMAND, D: D,

Selectable areas of addressable memory may be accessed and displayed by the D command.

PROGRAM EXECUTE COMMAND, G: G

INSERT INSTRUCTIONS into RAM, I: I

Data or instructions can be entered into RAM locations with the I command. I followed by the starting address of locations for the data to be entered at. The data is then specified by 2 digits that form a byte, separating the bytes with spaces, commas, or carriage returns. ESC terminates the insert mode.

MOVE MEMORY COMMAND, M: Mclow address>,,

The M command moves the contents of memory between .

SUBSTITUTE MEMORY COMMAND, S: S

()

S command allows you to examine and optionally modify memory locat ions individually. Type S followed by the starting address of the data to be examined and optionally modified. The contents of locations are displayed and can be changed by typing in a new data byte value. The next memory location contents are now displayed. If no change is desired the user can type a space and the next memory location will be displayed. The S command can be terminated by a carriage re turn.

EXAYINE /MODIFY CPU REGISTERS COMMAND, X: X

Display and modification of the CPU registers is accomplished by the X command. The identifier following the X command specifies the register to be displayed. The register can be modified by typing in the new value after the register's displayed contents. If no change is desired a space, comma, or carriage return will terminate the command. CHAPTER 4

THREADED INTERPRETERS

The user of a threaded interpretive language such as FPFORTH has a rather short-sighted view of the inner workings of the system. The operator of the system communicates interactively with the FPFORTH system. The system functions on the basis of two basic control structures. The inner interpreter which controls execution by keeping track of addresses of program routines as they are executed. The outer interpreter controls the system at the user level by taking care of

110 and execution of user commands. The outer interpreter is no more than a FPFORTH routine running indefinitely on its own language base.

The core of the language is called the dictionary and contains all the routines that are used by the system and are accessible for user use.

4.1) Dictionary Space

The dictionary entries comprise approximately 90% of the threaded interpretive language. A dictionary entry is a keyword followed by the code to actually execute the instruction. The dictionary entries consist of a header and a body located consecutively in memory. The header is optional and is only required if the routine is to be user accessible. The header is used by the search routine to locate the address of the first word in the body of a specific keyword. This address is called the word address of the keyword, the headers form a linear linked list of all routines with headers to make it possible to locate the word address or a keyword quickly. The basic structure of the dictionary is shown in figure 4.1. C E 7 HEilDER FOR X. E END OF LIST 0000

BODY

t--j 4 S HEADZR FOR W A S'JAP -, LINK

BODY

1 @

-- LINK .

Fig. 4.1) Dictionary Structure The header consists of 6 bytes that identify the keyword and link it to the next keyword in the list. The first byte contains the number of characters in the keyword. The next three bytes contain the first three characters of the keyword in ASCII code. If the keyword is less than three characters long, the ASCII space code fills out the byte or bytes. This means that all words are not uniquely identified. In other words, the following would be trated as the same keyword, SWAP and SWAT, but the keywords SWAP and SWAPIT would be different because of the difference in length. Following the first four bytes is the two byte address that links the current keyword with the next keyword in the list. The link address allows the SEARCH routine to step to the next header if the current header does not match the token read in from the input buffer. The link address of the last dictionary entry has an address of zero. This makes it easy to test for the end of the linked list and indicates the search has terminated unsuccessfully.

Some dictionary entries do not have headers. A good example of this is the literal handlers for numbers. The system knows the word address of the dictionary entries, but the user cannot directly access them. If a number is input to a keyword being defined in the compile mode, the system will automatically load the word address of the literal handler to the threaded code listing and then the number. The operator has no reason to know the word address of the literal handler. Therefore, header bytes would be redundant because the only one who has to know their addresses is the system.

The body of the dictionary entry contains the execution steps of the keyword. The first word of the code body specifies the type of code body to follow. This word is called the code address of the keyword and always points to executable machine code. There are two

types of code body to follow the code address: primaries which consist of executable machine code, and secondaries which consist of addresses of other primary or secondary routines. Embedded in the secondaries can be literal handler word addresses followed by data, or program control directive addresses followed by jump addresses. The jump addresses allow for the execution of loop and branch instructions.

The code address and return address of the code bodies control the

tree structured nature of the FPFORTH language through the inner

interpreter. The code address of a primitive points to the first byte of the code body of the primitive. The return address of the primitive transfers control to an inner interpreter routine which gets the next word address of the current secondary. A diagram of a primitive and secondary are given in figure 4.2.

A primitive is similar to a subroutine with a return to the inner

interpreter terminating the machine code that implements the keyword action. The code address of a secondary points to an inner interpreter routine which saves the address of the next word address of the current secondary on the return stack and makes the first word address of the new secondary the current word address. This, in effect, nests the system down one level in execution and looks for another primary or secondary to execute. The return address of a secondary points to an inner interpreter routine which pops the word address off the return stack and makes it current and in effect, it de-nests one level.

This form of threaded interpreter is referred to as an Indirect-

Threaded Interpreter and is described in the following sections as are some other forms of inner interpreters. r HEADm ':II ORD ADDR3 SS CODE ADDmSS EXECUTABLE T6A CI1I ITE CODE

ma' TO INm IIITERPRETER

HEADB WORD ADDRESS CODE ADDRESS m;?oRD ADDRESSES

, RETURN ADDRESS

Fig. 11.2) Primitive and Secondary Code Structures 4.2) Different Types of Threaded Code

There are various types of threaded code implementations with the differences being the way the inner interpreters work. The four basic types of threaded code are subroutine, direct, indirect, and token.

The four methods differ in complexity, the amount of machine inde- pendence, and execution speed. Each offers some advantages over the other, but the choice of which to use greatly depends on the hardware system used and the application. Indirect threaded code was used as the interpretation method for the FPFORTH system. This method seemed to be the best choice for the system from the four threaded

interpreters. It gives fairly good execution speed and makes it easy to move the coded routines around in memory without affecting the machine code of the routines.

4.2.1) Subroutine Threaded Code

Subroutine threaded code is the simplest threaded interpreter imple- mentation. Subroutine threaded code is a sequence of subroutine calls with no other instructions used in the intermediate language. Each subroutine call can be considered as one intermediate language operation or instruction. The intermediate language operation is a subroutine that performs the desired operation by a series of machine code instructions. The subroutine threaded code's execution speed is the fastest of the four interpretation methods because its interpre- tation is done by hardware instead of a sequence of machine code

instructions. A diagram of subroutine threaded code organization is shown in figure 4.3. TlACHIIT3 PROGMi? COUNER

i JSR A JSR B 4

INT~RMEDIJ~TE L4NCXddGE ROUTI T,TZ I~ACIIII~CODE I JSR A1 PiNcHINE LANGUAGE JSR A2 ROUTI N3

RTS RTS C -

Fig, 4.3 ) Subroutine Threaded Code 4.2.2) Direct-Threaded Code

Direct-threaded code is similar to subroutine threaded code, but is implemented at a higher level of execution. Direct-threaded code is done by performing subroutine calls with the call and return instruc- tions performed by software instead of hardware. With software performing the execution, the call up code can be removed, which leaves only a list of addresses. A short machine language program reads the addresses and transfers control to the next address in the list. The heart of the system is three machine language routines NEXT, COLON, AND

SEMI.

The NEXT operation causes the execution of the routine pointed to by the instruction register, and terminates all primary routines. The instruction register is an address register that keeps track of where the program is in the list of addresses. The routine that is then executed can be another secondary or a primary. If it is another secondary, this will put program execution at a lower level of nesting, by execution of the COLON routine. To return from this lower level of nesting the secondary routine terminates by executing SEMI. SEMI recovers the previous value of the instruction register to continue execution of the secondary routine that was executing before the program nested to a lower level. A diagram of direct-threaded code is shown in figure 4.4.

4.2.3) Indirect-Threaded Code

Indirect-threaded code is similar to direct-threaded code except that the interpreter must go through another level of indirection.

Indirect-threaded code consists of a list of addresses which then 5 LIST POINTER . . +A +B

Inm>;EDI-km LANGUAGE CODE lfL;1CHIlE CODE Machine Code B: 1':lachine Code for COLON Routine +A1 -A2 . l9achine Code for NEXT * SE711 .

Fi~o4.4) Direct-Threaded Code points to the primary routine. Indirect-threaded secondary routines do not contain machine code for the COLON operation, instead they contain the address of the routine. This gives it the advantage of only having to produce pointers to execute code. The indirect-threaded code secondaries do not need to have COLON and NEXT with each routine, but as machine code subroutines that only need to be coded once and be called as needed. This extra level of indirection allows the to be independent of the computer system it operates on. The big disadvantage of indirect-threaded code is that the interpreter has the extra overhead of another level of indirect addressing. Figure 4.5 shows a diagram of indirect-threaded code.

4.2.4) Token Threaded Code

Token threaded code is the most machine independent method of the four. The other three methods used pointers that were addresses of the subroutines in memory. Using memory addresses for routine selection wastes storage because the number of routines is far smaller than the number of memory locations. A savings in space can be achieved by using tokens to identify the routines to be executed. Token threaded code is usually implemented by using the current token to index into a table of subroutine addresses. This implementation of token threaded code is shown in figure 4.6.

4.3) Inner Interpreter

The inner interpreter is the heart of any threaded interpreter.

The inner interpreter controls execution of the code that makes the system work. The inner interpreter is composed of three short machine -. LIST POINTER . . . ---). a --+B - S~JI: I -.+ semi

SECONDARY --+ COLON w I Machine Code I

Fig. 4.5) Indirect-Threaded Code I Token A I I Token B 1 Token A: Token B:

Token B1:

Token COLON: Machine Code 7 Token SEMI: Machine Code Routine INTERPIIEDIATE LAMGUAGE CODE 7 1,Jac'nine Code for NEXT Token A1 I Token A2 I

Token SET'dII

Fig, 4.6) Token Threaded Code code routines with one routine having three entrances. These routines control the execution of all primary and secondary routines. The dictionary bodies of a primitive and secondary are shown in figure 4.7.

All keywords have a code address and a return address, except the outer interpreter which does not need one because it is an infinite loop. The code body of the outer interpreters is a list of addresses of already defined keywords. The inner interpreter maintains a register called the instruction register, which is similar to a program counter only on a higher level. This register contains the address of the next secondary. There is always a next secondary to be executed.

The inner interpreter routine that will execute the next secondary is called NEXT.

The NEXT routine gets the address of the next secondary routine pointed to by the instruction register and places it in the word address register. It then increments the instruction register by two to point to the next secondary. Using the figures given for a secondary and a primary, if the instruction register contained N+2, the routine NEXT would extract address1 and leave the instruction register containing N+4. It is desired to run the routine ADDRESS1 which is now the current instruction. ADDRESS1 is the word address of the routine to be executed. ADDRESS1 may point to either a primitive or secondary. A flowchart of how the inner interpreter works is shown in figure 4.8.

When the NEXT routine completes, machine code execution continues with the routine called RUN. The routine RUN extracts the code address pointed to by the word address register, increments the word address register by two, and loads the code address into the program counter of , Code Address

Machine Code Code Bodg

Jump NEXT A Return Address

SECOlTD-4RY b n COLON Code Address

n+2 b ADDRESS1 nt4 ,4DDRESS2 n+6 ADDRZSS 3 . Code Body a

n+2x SEMI Return Address i

Fig. 4.7) Dictionary Bodies of a Primitive and a Secondary 12 - Instruction Reg, RA - li:?ord Address Reg. CA - Code Address Reg. RS - Return Stack PC - Program Counter - -- rr I~ WA +Address pointed to by IR

! I CA t-- Address I!

1 COLON I I NO 1 I I Push IR I YES I onto RS --- + .--- L I r 1 1 I I SEMI I Pops top RS I IR 6'JA I andplaces Execute I in IR I L----A Machine Code I J \I / \

Fig, 4.8 ) Flowchart of Inner Interpreter the CPU. The next machine code instruction to be executed will be the

contents of the word address of routine ADDRESSl. The code address of

both primitives and secondaries must point to executable code.

If the word address was for a primitive, the code address obtained

by RUN ~ointsto the first instruction in the code body of the

primitive. This allows the execution of the primitive's machine code.

The return address of the primitive is an unconditional jump to the

NEXT routine. The instruction register now contains n+4 and so the

next secondary to be executed is ADDRESS2.

If the word address was that of a secondary, the code address

obtained by RUN is that of the inner interpreter routine COLON. The

routine COLON pushes the instruction register onto the return stack and moves the contents of the word address register to the instruction

register. Again using the figures, n+4 would be placed on the return

stack and ADDRESS1 + 2 would be placed in the instruction register.

The COLON routine then performs an unconditional jump to NEXT. It is

apparent that COLON effectively nests one level deeper to begin

execution of the secondary ADDRESSl.

The return address of a secondary is a primitive called SEMI. SEMI

does the opposite of COLON by de-nesting one level to begin execution

of the next instruction on the top of the return stack. SEMI pops the

top address off the return stack and loads the address into the

instruction register. SEMI exists to NEXT so that if ADDRESS1 was a

secondary we are returning from, ADDRESS2 will be the next secondary

instruction.

The only machine codes that actually run are the ~rimitivesand the

inner interpreter routines. Secondaries may call secondaries to any level being limited only by the depth of the return stack, but the bottom of the chain is always a primitive which actually executes.

EXECUTE is a primitive that actually causes the execution of a keyword. Execution is performed through the outer interpreter which searches through the keyword list and pushes its address onto the data stack. If the routine is to be executed, the routine ?EXECUTE calls the primitive EXECUTE. EXECUTE pops the address off the stack and loads it into the word address register and jumps to RUN. It is to be noted that after execution of the token, control goes to the outer interpreter because the instruction register contains the address of the routine following EXECUTE.

EXECUTE is the only inner interpreter routine with a header. The word address of COLON and SEMI are known by the other routines of the language as are the code addresses of NEXT and RUN. The routines SEMI,

NEXT, and RUN are actually one routine with three entrances, but COLON and EXECUTE are separate routines.

It is important to keep in mind that the instruction register is the effective program counter for the interpreter. The addresses it contains must be preserved or flow of execution is lost as if the value in the CPU program counter is randomly changed. The same is true of values place on the return stack. When SEMI pops the top entry from the return stack, it had better be the address of the previous level's next instruction and not some temporary value. This leads us into laying out the exact location of these registers in the CPU registers.

To implement an inner interpreter it is necessary to first lay out where the instruction register, word address register, and code address register are going to be located. Besides these register locations, space is needed to store the data stack pointer and return stack pointer. The 8085 has the following registers for use in storage. It has a 16-bit stack pointer register and three 16-bit register pairs.

The choice was made to use the stack pointer register for the data stack because the data stack is the most active of the two stacks in the system. The most important address to keep track of is the . instruction register, so this should be maintained in the CPU registers as much as possible. The register pair BC was used because this register pair has less flexibility in use than the other registers in the 8085. The code address is the address of the machine code to be executed, so it was placed in register pair HL because HL can be loaded directly into the program counter. This left little choice for the placement of the word address register and return stack pointer. The placement of the registers is shown below.

Instruction Register - BC Register Pair Word Address Register - DE Register Pair Code Address Register - HL Register Pair Data Stack Pointer - SP Register Return Stack Pointer - RAM Memory Location 2000

There are other ways of choosing which registers to store the FPFORTH working registers in, but this seems to have been the best choice.

Below is the actual inner interpreter of the system.

LOCATION CONTENTS LABEL OP CODE COMMENTS

SEMI : *+2 ;Code address LHLD RS ;Put return stack pointer in HL MOV C,M ;Move top address on return INX H ;Stack to instruction register - BC

MOV B,M 2

INX H Y SHLD RS ;Replace updated return stack pointer NEXT: LDAX B ;Get the address pointed to by the MOVE E,A ;Instruction register INX B ;BC and place in word address LDAX B ;Register - DE. also increment MOV D,A ;Instruct ion register to next INX B ;Instruction address LOCATION CONTENTS LABEL OP CODE COMMENTS

RUN: XCHG ; Exchange DE with HL MOV E,M ;Get the code address pointed to INX H ;By the word address register MOV D,M ;Word address register incremented INX H ;By 2 to point to next word address XCHG ;Exchange HL and DE back PCHL ;Put code address into program ctr COLON :LHLD RS ;Get return stack pointer DCX H ;Push the contents of the MOV M,B ;Instruction register onto the DCX H ;Return stack

MOV M,C 2 SHLD RS ;Replace updated re turn stack MOV B,D ;Place word address into MOV C,E ;Instruction register JMP NEXT

8064 07455845 DATA 7,EXE ;Header 8068 0000 0000 ;Link address 806A 6080 *+2 ;Code address 806C Dl POP D ;Put address of instruction into WA 806D C34E80 JMP RUN ;Go execute

The implementation of the inner interpreter is essential as the speed at which it executes is very important. These routines are the most important factor in response time of threaded interpretive code.

These routines must be carried out everytime any other routine is executed. This provides the possibility for quite a bit of overhead, but this overhead can be kept to a minimum with the proper use of CPU registers .

4.4) Outer Interpreter

The outer interpreter is the interface of the FPFORTH system to the user. The outer interpreter controls how the system reacts to user requests and actions. The outer interpreter is no more than a secondary that is in an infinite loop. A flowchart is shown for the outer interpreter in figure 4.9. This chart shows how control is maintained in the FPFORTH sys tem. Fig. 4.9) Flowchart of Outer Interpreter The FPFORTH system begins execution in a STARTIRESTARTroutine that

initializes the system variables and initiates execution of the outer interpreter secondary. The outer interpreter code is shown below. The code is somewhat deceptive in that it appears so simple, but buried in this code are several secondaries and large primitives needed to perform outer interpreter tasks.

OUTER INTERPRETER

LABEL KEYWORD COMMENTS

PRINT : TYPE ; Output Message INLINE ; Read in input line from terminal NEXT : ASPACE ; Place space code on stack TOKEN ; Get token from input line ?SEARCH ; Search dictionary for token *IF FOUND ; If found GOT0 FOUND ?NUMBER ; Otherwise convert to a number *END NEXT ; If valid number GOT0 NEXT QUESTION ; Otherwise output error message *WHILE PRINT ; GOT0 PRINT FOUND : ?EXECUTE ; Execute keyword *WHILE NEXT ; GOT0 NEXT

The outer interpreter controls the FPFORTH system execution where there are two modes of operation: a compile mode and an execution mode. In the execute mode, each token scanned from the input line causes the execution of one of the following processes.

1) If it is a keyword, it is executed. 2) If it is a valid number, it is pushed onto the data stack. 3) If it is not recognized, it is echoed to the display followed by ?.

Once a line is completed without an unrecognized token, an OK message is displayed at the end of the line.

The compile mode is slightly more complex and allows the building of a new dictionary entry which may have branches or literal data embedded in the threaded code. In the compile mode, each token scanned from the input line causes the following steps to occur.

1) If it is found and is an immediate keyword, it is executed. 2) If it is found and it is not an immediate keyword, its word address is added to the threaded list of the keyword being defined. 3) If it is a valid number, the number literal handler is added to the word being defined followed by the number. 4) If it is not recognized, it is echoed to the operator followed by ? and the partially completed keyword being defined is deleted.

Once a line is completed without an unrecognized token an OK message is printed and control is returned to get the next input line. The compile mode is terminated by an immediate keyword that concludes the new keyword definition. The dictionary space for the system is in RAM at location B400-B7FF. The input line buffer and stack areas are also in RAM memory. The layout of this memory space is shown in figure

The Dictionary Pointer points to the next available memory area where language extensions can be added. As definitions are added to the keyword list, the language grows upward towards higher memory. As each token is scanned in the input buffer, its length plus all of its characters are moved to the dictionary space. The use of the dictionary space makes it easy to enclose the characters to form a dictionary header.

The data stack areas build downward towards lower memory and the language builds upward toward higher memory. If the two meet the system crashes because stack overflows will over write the language or addresses. Addresses lost off the return stack will cause execution to continue in an undesired location. The system will be lost and control can only be regained through a cold start of the system. I .

DICTION.4RY SPACE

?OLD SACIZ - BCOO IITPUT LI'E BUFFEfi

mmPT STACK ma BDFF BE00 DATA STACK *-A BFFF

Fig. 4.10) RUT Memory Layout CHAPTER 5

OUTER INTERPRETER ROUTINES

The outer interpreter seems very simple in design but has some routines in it that make it fairly complex. The three secondaries

?SEARCH, ?NUMBER, and ?EXECUTE do most of the work once the FPFORTH reads in a line from the user through the terminal. The other primitives take care of 110 and system clean up if errors occur.

Together these routines take up approximately 1 Kbyte of memory.

5.1 ) ?SEARCH

?SEARCH is a secondary that searches the vocabulary to try and find a match for the input token. A token is a character or group of characters that are separated from other tokens by a space or spaces.

The token can be a keyword or a number. ?SEARCH first searches the

CONTEXT vocabulary and tries to find a keyword whose header matches the token which has just been moved to the dictionary space by the TOKEN routine. The CONTEXT vocabulary is the vocabulary that is curr2ntly being used on the system. The user can define more than one vocabulary and CONTEXT contains a pointer to the vocabulary now in use. If the search is successful, the word address of the keyword is pushed onto the stack as the second stack entry and a false flag is pushed as the top stack entry. If the CONTEXT vocabulary search is unsuccessful the system MODE flag is tested. If the MODE flag is false (execute mode), a true flag is pushed onto the stack and ?SEARCH exits to the next routine. If the MODE flag is true (compile mode), the COMPILER vocabulary is searched. If the search through the COMPILER vocabulary is successful, the word address of the keyword and a false flag are pushed onto the stack as before. After this occurs, the system STATE flag is set true. If the search of the COMPILER vocabulary is unsuccessful, a true flag is pushed onto the stack. A flag is always the top stack entry when ?SEARCH completes. If the flag is false, the second stack' entry contains the word address of the keyword as a parameter for ?EXECUTE. The ?SEARCH routine is shown below.

?SEARCH

LABEL KEYWORD COMMENTS

COLON CONTEXT ; Get address of CONTEXT vocabulary @

@ Y SEARCH ; Search CONTEXT vocabulary for token DUP ; Duplicate flag on stack *IF false EXIT ; If flag false GOT0 EXIT MODE ; Otherwise read system MODE

c @ 9 *IF false EXIT ; If false execute mode, GOT0 EXIT DROP ; Discard flag COMPILER. ; Get address of COMPILER @ ; vocabulary SEARCH ; Search COMPILER vocabulary DUP ; Duplicate flag on stack *IF false SETl ; If false GOT0 SETl 0 ; Else place false flag on stack *ELSE skip ; GOT0 SKIP SET1 : 1 ; Place true flag on stack SKIP : STATE ; Store 1 in STATE variable

C ! 9 EXIT : SEMI ; Exit

SEARCH is the primitive that actually searches the vocabularies for a keyword match. SEARCH was coded as a primitive to make it possible to find keywords as fast as possible because this routine is executed at least once for every token input. SEARCH is shown in appendix G with the rest of the outer interpreter primitives. SEARCH has a header so that it can be used and compiled into other keywords. SEARCH is called with the address of the first keyword header in the linked list to be searched as the top stack entry. The token being searched for is

located in the free dictionary space. The SEARCH routine tests the

length and up to three characters of the keyword to detect a match.

The first mismatch causes the next header to become the header to be matched with. This procedure continues until a match is found or the end of the linked list is reached. If a match is found, the word

address is pushed to the stack followed by a false flag. If no match

is found, SEARCH exits with a true flag on top of the stack.

5.2) ?NUMBER

This secondary attempts to convert the token located in the free dictionary space to a binary number. The number can be converted to a

single precision, double precision, or floating point representation.

Single precision numbers are stored internally as 16-bit numbers or word length data. Double precision and floating point numbers are

stored internally in 32-bits or as longword data. If the conversion is unsuccessful, a true flag is pushed onto the stack. This could occur due to the end of line terminator or an unidentifiable token. If a

successful conversion occurs, the system MODE flag is checked. If the

MODE flag is true (compile mode) a literal handler plus the number must be added to the dictionary. The number used in compile mode must be

single precision for the correct result to occur. If the number is within the byte number range, the word address of the byte number

literal handler *C# is added to the dictionary followed by the byte number. If the number is not in the byte range the word address of the word number literal handler *# is added to the dictionary followed by

the number. After this occurs a false flag is pushed to the stack to indicate a successful conversion. If the conversion was successful and

the system MODE flag is false, the number is pushed to the stack,

followed by a false flag. Shown below is the threaded code for the

secondary.

NUMBER

LA BE L KEYWORD COMMENTS

COLON 3 NUMBER ; Convert number to binary number *IF false INVAL ; If false invalid token GOT0 INVAL MODE ; Read system mode

c Ca 9 *IF false EXEC ; If false, execute mode, go execute SINGLE ; See if number is byte length *IF false BYTE ; If false GOT0 BYTE *?I */I ; Place address of */I in keyword ; Definition and enclose it

Y ; Enclose number in keyword definition *ELSE EXEC ; GOT0 EXEC BYTE : *# *C# ; Place address of *?I in keyword , ; Definition and enclose it C , ; Enclose number in keyword definition EXEC : 0 ; Place 0 on stack *ELSE EXIT ; GOT0 EXIT INVAL : 1 ; Place 1 on stack EXIT : SEMI ; Exit

The primitive NUMBER does the actual conversion of the token to a binary number. NUMBER is the most complicated primitive in the FPFORTH

language and is shown in appendix G. This routine is not accessible to

the user because it has no header. The header is absent because there

is no reason for the user to be able to access it. As stated previ- ously, there are three formats for the tokens to be converted into.

The basis for classifying an input number is done on format and range.

If the number contains a decimal point, it is converted to floating point format. If no decimal point is present, it is converted to

single precision or double precision, depending on its range. If the number lies between -32767 and +32767 it is converted to single prec is ion format, otherwise it is converted to double precis ion format.

The system allows numbers to be entered in any number base between

2 and 36 inclusive. This is accomplished by the'system variable BASE which contains the value of the current system number base. The NUMBER primitive uses the formula shown to convert numbers.

accumulator = accumulator * BASE + nextdigit

The formula is used even with floating point numbers and a counter is used to keep track of digits to the right of the radix point. This counter is then used to adjust the decimal point of the floating point number. Number bases greater than ten use the alphabetic characters to continue the sequence A-Z. Using the alphabet allows the user to work in a number base of his choice, a choice few systems offer. The actual advantage of being able to use various number bases is really dependent on the user. There are few applications where another number base besides decimal will be desired, but the feature is available for use.

The sign flag, decimal point flag, exponent sign flag, and exponent flag are initially set false. If the leading character is a minus sign in the token, the sign flag is set true and at the end of conversion the number is made negative. The decimal point flag is used to determine if the number is to be in floating point or fixed point representation. The exponent sign flag is then used to determine if the exponent is negative or positive. The exponent flag indicates if there was an exponent input. The following shows the correct format for numbers to be in for successful conversions.

SINGLE PRECISION DOUBLE PRECIS ION FLOATING POINT If the conversion of the token is successful the number is pushed onto the stack followed by a false flag. If the token is not a valid number, it only places a true flag on the stack.

5.3) ?EXECUTE

The ?EXECUTE secondary determines how the keyword should be handled. It decides if the keyword should be compiled or executed.

The routine tests the states of the system MODE flag and the system

STATE flag. If the flag states are equal, the word address of the stack entry is executed by the EXECUTE routine. This is one of the inner interpreter routines that causes the execution of a user command. If STATE and MODE are not equal the word address is enclosed in the dictionary. By enclosing an address in the dictionary, the address is added to the dictionary and the dictionary pointer is incremented to the first free byte following the added address. The system STATE flag is always set false before execution or compilation of a keyword. After each execution a test for stack underflow is made. If underflow occurs the address of the stack underflow message is loaded into the DE register pair and a jump to the $PATCH routine occurs. The threaded code for the ?EXECUTE routine is shown below.

KEYWORD COMMENTS

COLON > STATE ; Read the system STATE fl'ag C@ , STATE , COSET ; Set the system STATE to 0 MODE ; Read the system MODE flag c @ > - ; Does MODE =' STATE *IF false COMPIL ; If flags not equal GOT0 COMPIL EXECUTE ; Otherwise execute keyword *STACK ; Check for stack underflow LABE L KEYWORD COMMENTS

*ELSE EXIT ; GOT0 NEXT COMP IL : Y ; Enclose address of keyword in dictionary EXIT SEMI ; Exit

5.4) Other Outer Interpreter Routines

The following briefly describes the other routines in the outer interpreter, start up routine, and system error routines. These routines take care of the interface of the system to the user. This

includes the inputting of a line from the terminal and parsing the line

for tokens. These routines are also responsible for responses to the user from the system such as the OK message. The system error routines take care of stack underflow and unidentified tokens. The code for these routines is in appendix G.

5.4.1) STARTIRESTART

The START/RESTART routine has two entrances, one that initializes the entire system on start up and one that does a partial system initialization after an error message has been set up. The routine when entered on start up initializes all system variables to their appropriate values. It also loads the FPFORTH start message into the

DE register pair and then pushes the address to the data stack after the entrance from an error condition. On partial initialization the stack pointers are reset to their top of stack addresses and the MODE and STATE flags are set to 0. The routine then sets up the instruction register to start execution of the outer interpreter. It then exits to the inner interpreter routine NEXT. 5.4.2) TYPE

The TYPE routine is a ~rimitivewithout a header. It takes the

address on top of the data stack as the pointer to the message to be

displayed. TYPE outputs the message to the display and exits. When

TYPE exits it leaves no new values on the data stack.

5.4.3) INLINE

The INLINE routine is a headerless primitive that controls the

inputting of a line from the terminal. This routine first sends out a

carriage-return line feed to the terminal to get the cursor at the

start of a new line. It then clears the line buffer by loading it with the ASCII space code. The routine recognizes a backspace command

from the keyboard and a new character can replace the old one. This

allows the user to make changes if he mistypes part of a line. If the

cursor is in the leftmost position, the backspace is ignored. The

routine also recognizes the ESC key as a line deletion or abortion.

This means the cursor advances to the next line and the previous line

is completely ignored. INLINE recognizes the carriage-return key as

the termination of a line and outputs the carriage-return key as a

space to the terminal. It then sets the system line buffer pointer to

the first address of the line buffer and exits the routine by a jump to

the inner interpreter. All other characters are echo displayed and moved to the input line buffer. When the cursor reaches the end of a

line, the cursor no longer advances, but simply replaces the last

character on the display line and in the line buffer. 5.4.4) ASPACE

This is a primary routine with a header that simply pushes an ASCII space code to the low order byte of the stack. This is used as the separator for the TOKEN routine when parsing the input line. This gives TOKEN the ability to be flexible and use other characters beside a space to serve as a separator.

5.4.5) TOKEN

The TOKEN routine is a primitive with a header. This routine extracts tokens from an input line. The routine expects a separator character to be on top of the data stack. It pops the separator and gets the address of where token extraction begins from the Line Buffer

Pointer (LBP) system variable. If the separator is a space, all spaces are ignored and the address pointer is advanced until the first non- space character is found. This pointer value is saved and a byte count is kept until the next separator or the end of line terminator is reached. This byte count is placed in the first location of the free dictionary space followed by all characters in the token. The system

Dictionary Pointer (DP) variable points to the srart of the free dictionary space, but its value is not altered in the TOKEN routine.

The LBP is advanced to point to the separator following the token.

TOKEN exits to the inner interpreter routine NEXT.

5.4.6) QUESTION

QUESTION determines if the execution of the current line has been completed correctly or if an error has occurred. This routine checks the high order bit of the second byte in the free dictionary space. If the bit is a 1, the input buffer has been completely scanned and the OK message is pushed to the stack and exit occurs to the inner interpreter

NEXT routine. If the bit is 0, an unidentified token has been scanned and a carriage-r2turn line feed is issued. The unidentified token is

then displayed on the terminal. The address of ?MESSAGE is placed in

the DE register pair and a jump to $PATCH occurs.

5.4.7) $PATCH

$PATCH is a machine code routine that returns system variables to

their value before the error occurred during compile mode. $PATCH resets the dictionary pointer and CURRENT vocabulary link to the values

that existed before the attempt to compile the aborted definition.

This also allows an easy way to abort a partially completed definition by typing in an unknown token. The system variables DP and CURRENT are not affected if not in the compile mode, so in this case nothing is done to replace the addresses prior to the error. $PATCH exits to the

STARTIRESTART routine to reinitialize the stack pointers.

5.4.8) *STACK

The *STACK routine is a primitive without a header. *STACK has one entrance and two exits, depending on the result of the stack underflow

test. The stack pointer is checked to see if stack underflow has occurred and if it has, the stack pointer is reset to the correct top of stack address, $C000. The routine then puts the stack underflow error message address in the DE register pair and jumps to $PATCH. If

stack underflow did not occur the routine does a normal exit to NEXT. CHAPTER 6

USER INTERACTION WITH FPFORTH

The FPFORTH system interacts very simply with the user. The user's

input to the system consists of input lines, composed of 1 to as many

as 80 characters. The user can enter characters at the point indicated

by the cursor, a blinking box. When input is occurring, the system

waits for characters to be typed in and places them in the input line

buffer. A line is terminated by a carriage-return. The system will

Leave the input mode and attempt to execute the current line.

During the entering of a line, a mistake may be made and may need

to be corrected. There are two ways to correct a mistake. The first

method is to use the backspace key and the cursor will move to the left

one space at a time. The input line buffer puts spaces in all

positions that are backspaced over. Once the error has been backspaced

over, more characters can be entered or a carriage-return can terminate

the line. If the mistake was large and it would be easier to

completely retype the line, the ESC key will cause the line to be

aborted and the cursor to advance to the next line. In this manner, a

whole line does not have to be backspaced over for a mistake at the

very beginning the Line.

All characters but carriage-return, escape, and backspace are

displayed on the terminal at the current cursor position and the cursor moves one position to the right. The character is also moved to the

input line buffer for complete duplication of the input line. The

exception to this is lower case alphabetic characters which are stored

as upper case characters for comparison to keywords. When the end of a line is reached on the display, the cursor no longer moves to the right and any additional characters simply replace the last character on the line. The cursor will not advance and only a carriage-return will terminate the input mode.

If the system is in the input mode and execute mode of operation, a carriage-return of a blank line will cause the following message:

OK

The cursor will then advance to the first position of the next line.

If there was something on the line, it will be executed and if successful OK will be displayed and the cursor is advanced to a new line. The following illustrates this by reading in a number in base 10 and outputting it to the display in octal.

DECIMAL 10 OCTAL . 12 OK

The keyword DECIMAL makes the system number base 10. The 10 will then be read and since it is not a keyword, it will be converted to a number and pushed onto the data stack. OCTAL is similar to DECIMAL except that it makes the number base 8. The period is now scanned and it is the keyword for displaying the top stack entry in the present number base. This causes the output of 12 to be displayed. The line was executed without errors so the OK message is printed and the cursor moved to the next line.

The system detects only two types of errors, a stack underflow condition and an unidentifiable token. If a stack underflow is detected by the system, the cursor will advance one line and print

"STACK UNDERFLOW". It will then reset the stack pointer and system variables to start in the execute mode and enters the input mode. If the user enters a keyword which is not known and not a valid number in the current number base, the system will advance one line and echo the token followed by a question mark. The following shows an example of the user misspelling DUP.

5 DUO DUO ?

An error like this in the compile mode causes the partially compiled keyword to be deleted from the dictionary. This indicates that a user cannot use a keyword in a definition until the keyword has been defined.

The information presented above was a brief outline of how the user interacts with the system. The following sections describe the number formats, flags, constants, variables, and other data types in the system.

6.1) Number Formats

There are three types of numbers in the FPFORTH system. They are: single precision fixed point (16-bit), double precision fixed point

(32-bit), and floating point (32-bit). Single precision fixed point numbers are stored in what will be referred to as words, while double precision fixed point numbers will be stored as what will be referred to as longwords. The user can use any of these formats as desired but must remember what type of number is located where. This must be done or mixing of number formats in operations will cause erroneous results.

The numbers are initially placed in a data type by the way they are entered from the terminal and the range of the number. If the number entered contains a radix point, it is assumed to be floating point and stored that way. If it does not have a radix point it is assumed to be fixed point. If the number. is in the range -32767 to +32767, it is stored in single precision fixed point format. If the number was outside this range it is stored as double precision. Double precision has the range:

Floating point has the range :

-9.2 X 10-18 to -2.7 X 10"-20 and +2.7 X 10"-20 to +9.2 X 10-18

Floating point gives a much larger range than fixed point, but it also loses some precision. Floating point is accurate to 7 significant digits and double precision is accurate to 9 significant digits. The following shows some valid and invalid numbers. The system number BASE is base 10 for the examples.

SINGLE PRECIS ION FIXED POINT

25 OK -213 OK +255 OK 143- 10 143-10 ? Cannot use an exponent with fixed point numbers

DOUBLE PRECISION FIXED POINT

+300567 OK 001456789 OK Ignores leading zeroes -567832 OK 256,789 256,789 ? Commas are not allowed in the number strings

FLOATING PO INT

2.5 OK +89.67-11 OK -314.7-+2 OK 41.63^-1 OK 2."5 OK 3. OK 2-5 2-5 ? Cannot have exponent with what appears to be fixed point

You may ask what if a number is entered in one format but is desired in another? This is no problem in the FPFORTH system as there are six conversion keywords that allow conversion from one format to the other. The system allows the data to be entered in any desired number base from

2 to 36. To do this you must load the system variable BASE with the value of the number base you wish to work in. You can now run the system as in the decimal system but with the new number base.

6.2)LogicalFlags .

A logical flag is a 16-bit parameter that can have two states, true or false. In this system, true is recognized as any nonzero value and false is always zero. This allows the use of any constant or variable to be treated as a flag. The flags are stored in memory as variables.

6.3) Constants

Constants are passive keywords that push the integer value of the constant to the stack when called. Constant values may be stored as bytes, words, or longwords. Constants are defined with the following three commands: CCONSTANT, CONSTANT, and 2CONSTANT on the execute mode. The form of these commands is shown below as :

A CCONSTANT name B CONSTANT name C 2CONSTANT name where : -128 <= A <= 127 -32768 <= B <= 32767 -2,147,483,648 <= C <= 2,147,483,647 and where name is any valid token. Care must be taken when using

2CONSTANT because if the magnitude of C is not large enough, it will be placed on the stack as single precision. 2CONSTANT will take off this number plus the number below it on the stack and combine them to form a longword. This action will cause an erroroneous value to be stored as the constant's value. Below is an example using CONSTANT. 1000 CONSTANT KILO OK KILO . 1000 -OK

6.4) Variables

Variables are passive keywords that push the address of the

variable to the stack when called. Variables may be stored as bytes,

words, or longwords. Variables are defined with the following three

commands : CVARIABLE, VARIABLE, or 2VARIABLE. The form of the variable

commands are shown below.

D CVARIABLE name E VARIABLE name F 2VARIABL.E name

The same limits exist for D,E, and F as used for A,B, and C respec-

tively. The numbers D,E, and F are the initial values stored in the

variables. Below is an example of variable.

50 VARIABLE LOOPCOUNT OK LOOPCOUNT @ . 50 OK

6.5) Character Strings

The FPFORTH system has only rudimentary string handling routines.

Strings of ASCII data may be embedded within a word being compiled as a

literal. The system keyword that performs this action is the immediate keyword [. This keyword adds the ASCII literal handler word address to

the dictionary and encloses it in the definition being compiled. It

then changes the token separator to ] from the normal space and scans

the next token from the input buffer. Then it encloses the scanned

token in the dictionary space for the word being defined. This

procedure encloses the ASCII literal handler, the length of the string,

and all the characters until the occurrence of ] in the word being defined. When the literal handler is executed, the instruction register points to the length of the string. The literal handler will display the string and leave the instruction register pointing to the next instruction in the threaded code. Below is an example of the character string handler.

: MESSAGE [ HELLO ] 9- OK

WHEN EVOKED

MESSAGE HELLO OK

6.6) Arrays

Arrays are passive keywords that allocate blocks of dictionary memory for data types following a dictionary header. The creation of an array is done by creating a variable and then leaving as much space as required by the array. An array can be created as shown below:

DECIMAL 0 CVARIABLE ARRAY 49 DP +! OK

This will reserve 50 bytes of memory under the keyword array. In the example, the keywords up through array just create a byte variable and initialize it to zero. The sequence 49 DP +! advances the dictionary pointer by 49 so that 50 bytes following the header are reserved. The user will still have to create his own keywords to manipulate the data stored here, because no array handling keywords are defined on the

FPFORTH system. Some languages offer array handling instructions but most languages such as Fortran need to have array handling procedures written by the user. One advantage to FPFORTH is that these procedures only need to be written once and can be added permanently to the

FPFORTH language by the user. 6.7) System Variables

There are several variables needed by the system to operate. These variables contain critical system data and if overrun would cause seri- ous system errors and even crash the system. Some of these are stored

in CPU registers, but most are stored in RAM at locations 2000-2018.

The system variables are listed below with a description and where they are stored.

IR - The instruction register contains the address of the next keyword in the current secondary keyword that the inner interpreter will execute. BC register pair, temporary storage at 2002.

WA - The word address variable contains the address of the current keyword to be executed before the keyword code address is extracted by the inner interpreter. DE register pair, temporary storage at 2004.

SP - Data stack pointer. Stack pointer register of CPU.

RS - Return stack pointer contains the address of the top of the return stack. Memory location 2000.

MODE - The MODE variable is a flag with false equal to the execute mode and true equal to the compile mode. Memory location 2011.

STATE - The STATE variable is a flag used to control execution of immediate keywords. In the compile mode, the COMPILER vocabulary is searched and STATE is set true if the keyword is found in the vocabulary. Keywords are executed by the outer interpreter routine ?EXECUTE only if MODE and STATE are equal. Memory location 2010.

The system MODE and STATE parameters have the following states.

MODE STATE 0 0 Execute keyword 0 1 Not a1lowed 1 0 Compile keyword 1 1 Execute immediate keyword

DP - The dictionary pointer contains the address of the next free location in the dictionary space. Memory location 2006.

CONTEXT - The variable CONTEXT contains the address of the vocabu- lary which will be searched to find the keyword word address. Memory location 200C. CURRENT - The variable CURRENT contains the address of the vocabu- lary to which new keyword definitions will be linked. Memory location 200A.

LBP - The line buffer pointer is a variable containing the address where token scanning will begin. When the inputting of a line completes, LBP will point to the first location of the line buffer. As each token is scanned, LBP is set to the location following the token separator of the token scanned. Memory location 2008.

BASE - The variable RASE contains the current number base for the system. Memory location 2012.

SIGN - The variable SIGN is used by number as a flag to tell if the number is to be negative. Memory location 2013.

DECPT - The variable DECPT is a flag used to tell if a decimal point was found in the number. Memory location 2014.

ESIGN - The variable ESIGN is used by number as a flag to tell if the exponent of the number is negative. Memory location 2015.

EXP - The variable EXP is a flag used to tell if the exponent

symbol A has been read. Memory location 2016. CHAPTER 7

STACK AND MEMORY REFERENCE OPERATIONS

The stack and memory reference operators are very important in a stack oriented system. The FPFORTH language would be impossible to use without some way of manipulating the data on the data stack. There are two classes of stack operators: the strictly data stack operators and the interstack operators. The interstack operators pass values between the data stack and the return stack. The memory reference operators allow the storage and retrieval of data from memory locations. They also allow the user to change data in memory locations without putting it on the data stack. These routines, as a whole, allow the user to manipulate the data by letting the data be placed in a specific order as desired by the user.

7.1) Data Stack Operators

The data stack operators always manipulate data stack entries.

These entries can be words or longwords. These routines will be the most frequently used in the FPFORTH vocabulary. The manipulation of data on the stack allows the user to perform many operations on the stack without having to use temporary memory locations. The following briefly describes the stack operators and gives an example of how each operator works. The examples show how the stack looked before and after execution of the keyword. The top of the stack is on the left and the stack proceeds downward moving to the right. Displaying the stack contents in this format gives a clear view of what the keyword actually does . DUP - Duplicates the top word stack entry and pushes it to the stack.

stack before 12 34 stack after 12 34 12 34

2DUP - Similar to DUP except it duplicates the top longword on the data stack.

stack before 12 34 56 78 stack after 12 34 56 78 12 34 56 78

SWAP - Interchanges the order of the top word stack entries.

stack before 12 34 56 78 90 stack after 56 78 12 34 90

2SWAP - Interchanges the order of the top two longword stack entries.

stackbefore 11 22 33 44 55 66 77 88 stack after 55 66 77 88 11 22 33 44

OVER - Duplicates the second word stack entry and places it on top of the data stack.

stack before 12 34 56 78 90 stack after 56 78 12 34 56 78 90

Duplicates the second longword stack entry and places it on top of the data stack.

stack before 11 22 33 44 55 66 77 88 99 stack after 55 66 7'7 88 11 22 33 44 55 66 77 88 99

DROP - Pops the top word stack entry off the stack and discards it.

stack before 11 22 33 44 stack after 33 44

2DROP - Pops the top longword entry off the stack and discards it.

stack before 11 22 33 44 55 66 77 88 stack after 55 66 77 88

LROT - Rotates the top three stack words so that the third stack entry becomes the top and the top becomes the second.

stack before 11 22 33 44 55 66 77 stack after 55 66 11 22 33 44 77

RROT - Rotates the top three stack words so that the second stack entry becomes the top and the top becomes the third.

stack before 11 22 33 44 55 66 77 stack after 33 44 55 66 11 22 77 7.2) Interstack Operators

The data stack is usually used to store parameters, and the return

stack usually stores return addresses. The return stack is also used

to store loop parameters and may also be used to store temporary data.

Extreme care must be taken when using the return stack to store data values, and it is best to store data somewhere else. If the values are put on the return stack, they must be removed in the same keyword definition. Primary and secondary calls can occur between storage and removal from the return stack, but the return stack must be back where

it was when the keyword definition began. If the return stack is not as it was when the keyword definition began, the inner interpreter routine SEMI will not be able to get the correct return address. The system will continue execution at an unknown location destroying all work done up to this point. The user should keep this in mind whenever using interstack operators. The following ten keywords make up the

interstack operator set. An example of the action is shown with each keyword and description.

Data Return Before 11 22 33 44 80 00 After 33 44 11 22 80 00

R> - Pops the top return stack word and pushes it to the data stack.

Data Return Before 22 33 44 56 32 40 After 44 56 22 33 32 40

C

Data Return Before 12 43 68 32 48 51 After 68 32 12 48 51

CR> - Pops the return stack byte and pushes it to the data stack sign extended. Data Return Before 25 42 12 48 51 After 12 00 25 42 $8 51

I> - Pushes to the data stack the loop index for the innermost word length loop which is the top return stack word.

Data Return Before 56 87 31 75 43 54 After 31 75 56 87 31 75 43 54

CI> - Pushes to the data stack the loop index for the innermost byte length loop which is the top return stack word. The loop index is sign extended for the data stack.

Data Return Before 03 36 11 98 56 43 After 11 00 03 36 11 98 56 43

J> - Pushes to the data stack the loop index for the second innermos t word length loop.

Data Re turn Before 23 51 21 35 98 00 13 Af ter 98 00 23 51 21 35 98 00 13

CJ> - Pushes to the data stack the loop index for the second innermost byte length loop with the byte sign extended.

Data Return Before 88 62 25 A3 85 13 28 After A3 FF 88 62 25 A3 85 13 28

K> - Pushes to the data stack the loop index for the third innermost word length loop.

Data Return Before 01 32 12 43 76 95 42 54 After 42 54 01 32 12 43 76 95 42 54

CK> - Pushes to the data stack the loop index for the third innermost byte length loop with the byte sign extended.

Data Return Before 09 76 82 94 17 19 01 After 17 00 09 76 82 94 17 19 01

Care must be taken when using the last four operators because they assume that the loop indexes they are skipping on the return stack are the same siz'e as the loop index being retrieved. In other words CK> expects to skip over 2 bytes to get to the third byte length loop index. If the other loop indexes were word length the CK> keyword would retrieve the low order byte of the second word length index.

Care must be taken whenever the user plays with index counters of loops so that these values are not changed by mistake.

7.3) Memory Reference Operators

The memory reference operators allow the user to read or write data to memory locations. The memory reference operators all assume the top word on the data stack is the address of the memory location to be accessed. Most of the operators are good for RAM memory only because they write values to the memory location. The commands @,c@, and 2@ can be used for read only memory as they read the contents of the memory location, but do not try to modify its contents. The memory reference operators are listed below with a brief description of their operation.

! - Stores the second word length data stack entry at the address at the top of the stack.

Stack Before 00 20 21 43 10 After 10

C! - Stores the second stack entries low order byte at the address at the top of the data stack.

Stack Before 30 84 25 00 12 8430 - XX 8431 - XX After 12 8430 - 25 8431 - XX

2! - Stores the longword just below the top stack entry at the location specified by the top stack entry.

Stack Before 50 40 01 02 03 04 55 4050 - XX 4051 - XX 4052 - XX 4053 - XX After 55 4050 - 02 4051 - 01 4052 - 04 4053 - 03

+! - Adds the second word on the stack to the contents of the memory location pointed to by the top stack word. Stack Before 20 60 78 01 37 6020 - 88 6021 - 00 A£ ter 37 6020 - 00 6021 - 02

C+! - Adds the low order byte of the second stack entry to the byte whose address is the top stack entry.

Stack Before 89 73 18 59 80 7389 - 23 After 59 80 7389 - 3B

@ - Replaces the address at the top of the stack with the word stored at that address.

Stack Before 78 96 32 43 21 After 01 20 32 43 21

C@ - Replaces the address at the top of the stack with the byte located at that address with the high byte being sign extended.

Stack Before 80 80 23 AA D4 8080 - 84 After 84 FF 23 AA I34 8080 - 84

2@ - Replaces the address at the top of the stack with the longword located at that address.

Stack Be£ore 60 55 12 5560 - 01 5561 - 02 5562 - 03 5563 - 04 After 01 02 03 04 12 5560 - 01 5563 - 02 5562 - 03 5563 - 04

OSET - Sets the word to 0 whose address is specified by the top stack entry.

Stack Before 55 37 23 DC 3755 - XX 3756 - XX After 23 DC 3755 - 00 3756 - 00 lSET - Sets the word to 1 whose address is specified by the top stack entry.

Stack Before 00 AD 32 67 A000 - XX A001 - XX After 32 67 A000 - 01 A001 - 00

COSET Sets the byte whose address is on top of the stack to zero.

Stack Before 67 75 90 76 After 90 76 ClSET Sets the byte whose address is on top of the stack to one.

Stack Before 77 85 3C 5A After 3C 5A CHAPTER 8

INPUT AND OUTPUT ROUTINES

The FPFORTH language contains only the most basic 1/0 operations.

These routines allow the system to interface with the Zenith terminal.

The 110 keywords permit the display of the stack and memory contents.

The stack can contain, as you recall, single precision, double precision, and floating point numbers. An output routine is required for each number format to display the numbers. The bulk of the routines deal with single precision numbers being displayed or the display of bytes from memory locations. The single precision routines will be described first because they are basic operators needed for any

FORTH system. Some of these operators are also used by the double precision output routines to perform some simple operations. Double precision operators are really an extension of the single precision operators. Lastly there is the floating point operator which outputs to the display the floating point number at the top of the data stack.

The number is displayed in floating point format that is easy to read.

The I/O operators are mostly secondary routines that allow a top down structure in the coding of the routines. The numbers are always displayed in the current number base when output to the terminal. This is true for all the routines except the memory dump routines which display the contents of memory in hex or as ASCII characters. The .F routine even displays the number in the current number BASE. The exponent raises the current number BASE to a power specified by the magnitude of the exponent. This allows the user to do some unique calculations in floating point arithmetic. The complete set of I/O 86 keywords that are user accessible are now given in three sections, single recision, double precis ion and floating point.

8.1) Single Precision I/O Routines

The word length 1/0 routines take care of all byte or word length output routines and byte length 'input routines. There is only one input routine key which reads in a character from the keyboard. The output routines allow the display of memory contents, the top stack word, and other special keyword characters. The word length operators are given below with the idea that more complex I/o operations can be built up to meet the user's needs.

KEY Pushes to the stack in the low-order byte position the ASCII code of the next key closure on the keypad.

ECHO Pops the top data stack word and outputs the low order byte to the display.

This keyword pops the top data stack entry, converts the signed value to a sequence of ASCII characters representing the number. The characters are then displayed on the terminal.

.R This keyword expects a print Eield width as the top stack entry and a signed number as the second stack entry. It converts the number to a sequence of ASCII characters just as with the keyword . . If fewer characters are used than the top stack entry number, additional spaces are output before the number to make the proper field width. If more characters are generated than the field size width then all the ASCII characters are displayed without any added spaces.

Pops the top data stack entry and divides the unsigned number by the system variable BASE. Converts the remainder to an equivalent ASCII code in the set (0-9,A-Z) and pushes the result to the stack. The quotient is then pushed to the data stack.

!/ > This keyword pops the top return stack entry, discards it and displays the character string on the stack. i/ S This keyword executes the i/ routine until a zero is at the top of the data stack. The number on top of the stack is the quotient of the division performed in the i/ routine. It will execute #/ at least once to generate at least one ASCII character. < # This keyword prepares the stack for number conversion by pushing to the stack an ASCII space code with the high order bit set (AO). It also copies the high byte of the number to the return stack.

This keyword pops the top data stack entry, gets the word length number at the address specified by the top stack entry and outputs it to the display.

C? This keyword pops the top stack entry, gets the byte length number at the address specified by the top stack entry and outputs it to the display.

ASCII The keyword expects a positive binary number between 0 and 36 as the top stack entry. The number is converted to the equivalent ASCII number code 0-9, A-Z and left in the low order byte of the top stack entry.

CLEAR This keyword clears the display screen and moves the cursor to the upper left, i.e. homes the cursor.

DISPLAY This keyword expects a series of ASCII code characters on the stack in the low order byte positions. The last character in the sequence will have the high order bit set to one. The keyword pops successive entries from the stack, outputs the low order byte, and repeats until the character with the high order bit set has been output.

PAD This keyword is used by the .R routine to add spaces before the number is displayed. This allows a number to be displayed in a specified field width.

SIGN The keyword pushes an ASCII minus sign to the stack if the top byte on the return stack is negative.

SPACE Outputs a space to the display.

DUMP This keyword expects two addresses on the stack. The second stack entry is the starting address and the top entry is the ending address of a memory area. The contents of this block of memory are displayed in hexadecimal.

PART Used by DUMP to display 8 Bytes starting at the address sign.

ADUMP Similar to DUMP but the characters are displayed as the ASCII equivalent character corresponding to the lower 7 bits of each location. All control characters are output as spaces.

APART Used by ADUMP to display 8 bytes as ASCII characters starting at the address given.

8.2) Double Word 1/0 Routines

The double word 1/0 routines display the contents of the longword number on top of the data stack. These routines perform the same oper- ations as the . routine except they are for 32-bit fixed point numbers.

The routines are described below and you will see a sharp similarity between these routines and corresponding word length routines.

D. - This keyword pops the top longword data stack entry and converts the signed value to a sequence of ASCII characters representing the number. The characters are then displayed to the user.

D#S - This routine executes the Dib routine until a zero is the top stack entry. It will execute D# at least once.

Dl) - Pops the top 32-bit stack entry, divides the number by the system number base to compute the quotient and the remainder. The routine then converts the remainder to an equivalent ASCII code in the set (0-9,A-Z) and pushes the result to the stack. The quotient is then pushed to the data stack so that it is on top when the routine ends.

D.R - This routine displays the second stack entry in a field width determined by the top word on the data stack.

8.3) Floating Point Output Routine

This routine is the most complicated keyword routine in the FPFORTH system. The routine .F outputs the top floating point number on the data stack to the display. The format for the output is always the same and is shown below.

S - Represents the sign and is only ouput for negative numbers. X - Represents the digits where the number of digits to the right of the radix point varies. The same is true for the digits for the exponent.

The procedure for converting the number to ASCII characters is accomplished by positioning the radix point so that there is only one digit to the left of the radix point. Once the number is positioned by either dividing or multiplying the number by the current number base, the number is converted to an integer. This effectively truncates the fraction and leaves the most significant digit. An exponent counter is kept to keep track of where the decimal point is. If a division is per- formed, the exponent is incremented. If a multiplication is performed, the exponent is decremented. This gives the correct exponent for the number once it has been justified with one digit to the left of the decimal point. After the floating point number is converted to an integer to get the most significant digit, -it is converted to an ASCII character. The digit is subtracted from the floating point number to reduce the number by the value of the most significant digit. The floating point number is then multiplied by the current number base to get another digit to the right of the decimal point. The process continues by getting the next digit until the floating number is approx- imately zero. This is done because of the representation problem of storing decimal numbers in binary, which means that zero may never be reached exactly. Once the number is completely displayed the exponent is converted to a string of ASCII characters like the mantissa was. If the exponent is originally zero no exponent is displayed.

This output routine requires a lot of multiplying and dividing to find the digits individually for conversion. This is where the APU is again very useful in that it can do these calculations very swiftly and not cause a lot of delay in response time. The routine is able to dis- play the characters in a steady stream with no noticeable delay. The

.F routine takes full advantage of the APU's speed to quickly convert a number to ASCII characters. The actual code of .F is listed in appendix H along with the other 110 operators. CHAPTER 9

ARITHMETIC OPERATORS

The arithmetic operators are divided into three classes, single

precision, double precision, and floating point. Single precision

operators perform math operat ions on word length operands. Double

precision operators perform math operations on longword operands.

Lastly, floating point operators perform several functions besides the

normal math operations on floating point operands. The floating point

operations are made possible by the use of the 82318 Arithmetic

Processing Unit. The APU makes it possible to calculate several

trigonometric funct ions and logarithmic functions besides the basic

addition, subtraction, multiplication, and division operations. The

APU is very powerful and gives the system more power than is capable on most 8-bit machines. These routines would be very difficult to carry

out in software, but the APU can handle them quite easily. The APU

allows complex math functions to be carried out in a few bytes of code compared to several hundred bytes if the functions were coded in soft- ware. It may also be desired to convert a number from one data format

to another. Six conversion routines make it possible to convert from any data format to either of the other two data formats. The arith- metic operators are presented in three sections according to the type of operands used and the last section presents the conversion routines.

The actual code for the routines is listed in appendix H.

9.1) Single Precision Operators

The single precision operators include some fairly common types and some rather unusual types. These operators assume the numbers on the data stack are 16-bit 2's complement numbers. The simpler operators' actions are carried out directly on the 8085, but the more complex operators such as / use the 8231A APU. The APU makes the execution of these keywords much simpler than would be the case if they were coded completely in software. The APU also increases the speed at which these keywords can be executed. The keywords for all single precision operations are now listed with a brief description. No examples are given, because the operators are not difficult to understand.

+ - Adds the second stack entry to the top stack entry and replaces both entries with the 2's complement sum as the top stack entry.

- - Subtracts the top stack entry from the second stack entry and replaces both with the 2's complement result as the top stack entry.

* - Signed multiplication of the top stack entry by the second stack entry with the 16-bit result placed on the stack.

/ - Performs a signed division of the second stack entry by the top stack entry. The integer quotient is placed on top of the stack and the two operands are removed from the stack.

* - Performs a signed multiplication of the third stack word by the second stack word and a signed divide of the product by the top stack word. The 16-bit result is placed on top of the data stack and the three operands are removed from the stack.

MOD - This operator divides the second stack entry by the top stack entry, replacing the top two stack entries with the 16-bit remainder. This operation is only valid when the two operands are positive .

/MOD - This operator combines the / and MOD operators to return both a quotient and a remainder. The operator leaves the quotient as the second entry and the remainder as the top stack entry. This operation is only valid for positive operands.

ABS - This keyword finds the absolute value of the top stack entry and returns it to the top of the data stack.

MINUS - This keyword takes the 2's complement of the top number on the data stack and places the result on the data stack. In gen- eral it changes the sign of the number on top of the data stack. 1+ - Adds one to the top data stack entry.

1- - Subtracts one from the top data stack entry.

2+ - Adds two to the top data stack entry.

2- - Subtracts two from the top data stack entry.

9.2) Double Precision Operators

The double precision operators are equivalent to the single preci- sion operators except that they perform their operations on 32-bit operands. The 8085 cannot handle 32-bit operat ions very easily because of its 8-bit structure. The 8085 is just inadequate to perform 32-bit operat ions without complex software programming. The use of the 82318

APU made the implementation of these operators quite simple. The 8085 is now required to do what it does best, handle 8-bit data. The 8085 takes care of transferring the data to and from the APU. It also issues the commands to the APU to perform the correct operation. Most of the double precision operators require less than 40 bytes of code to implement. If the operations were done completely on the 8085, these operations would have required well over a hundred bytes each. The speed is also increased by the use of the APU which performs these operations in hardware instead of software. The APU can carry out the operations as 32-bit operations instead of concatenating 8-bit operations together to form the 32-bit operations. The APU is given a command from the 8085, it executes the command and the result is waiting for the 8085 on the APU stack. The eight double precision operators all expect 32-bit operands on top of the data stack and return 32-bit results back to the top of the data stack. The double precis ion arithmetic operators are an extension of Loeliger' s FORTH language and were made easy to implement with the use of the APU. The double precision operators allow for a wider number range and more significant digits than offered with single precision operators. The increase in significant digits is very important for scientific calculations that would require more than 4 significant digits. The calculations can be set up so that the decimal point is assumed to be in a set position. The double precision operators will allow calcula- tions up to 8 significant digits. The double precision arithmetic operators are now presented with a brief description.

D + - Adds the second data stack longword to the top data stack longword and replaces both entries with the longword sum as the top stack entry.

D- - Subtracts the top data stack longword from the second data stack longword and places the result on top of the data stack.

D* - Multiplies the'second data stack longword by the top data stack longword and places the product on top of the data stack.

D / - Divides the second data stack longword by the top data stack longword and places the integer quotient on top of the data stack.

DMOD - Divides the second data stack longword by the top data stack longword and places the remainder on top of the data stack. This operat ion is only valid for positive operands.

D/MOD - This routine combines the D/ and DMOD routine to return both the 32-bit quotient and the 32-bit remainder. The remainder is the top data stack entry and the quotient is the second stack entry after execution is completed. This operation is only valid for positive operands.

DABS - This keyword takes the absolute value of the top longword on the data stack.

DMINUS - This keyword changes the sign of the top longword on the data stack.

9.3) Floating Point Operators

The floating point operators perform several arithmetic functions by using the 82318 APU. Floating point operations are not offered on most FORTH systems as they require much extra software to implement.

The floating point operators were added to the FPFORTH language to greatly expand the arithmetic abilities of the language. The floating point operators were able to take full advantage of the arithmetic processing unit. The APU makes it possible to save a lot of software coding and give the system a much expanded arithmetic instruction set.

The APU performs all the floating point operations that are in the

FPFORTH language. Several of the functions are impossible to carry out in software without a ROM lookup table to find the result. This is true of the six trigonometric operators.. These include the cosine, sine, tangent, and the inverse of these functions. These functions are not defined in other FORTH languages because they are usually built on strictly 8-bit CPUs. It is almost impossible to carry out these functions on an 8-bit CPU, but by using the 82318 APU it is quite easy to do. The floating point operators are now presented with a brief description. The actual machine code for these routines is presented in appendix H.

F+ - Adds the top floating point number on the data stack to the second floating point number on the data stack and places the sum back on top of the stack.

F- - Subtracts the top floating point number from the second float- ing point number and places the result on top of the data stack.

F* - Multiplies the top floating point number by the second floating point number and places the product on top of the data stack.

F / - Divides the second floating point number by the top floating number and places the quotient on top of the data stack.

MINUS - This routine' changes the sign of the floating point number on top of the data stack. SQRT - This routine expects a positive floating point number on top of the data stack. It takes the square root of the number and places the result back on the data stack.

PWR - This routine takes the top floating point number on the data stack and raises it to the power of the second floating point number on the data stack. The result is placed on top of the data stack.

LOG - This keyword takes the common logarithm base 10 of the floating point number on top of the data stack and places the result on top of the data stack. This operation is valid for positive numbers only.

- This keyword takes the natural logarithm of the floating point number on top of the data stack and places the result on top of the data stack. This operation is valid for positive numbers only.

EXP - This routine raises e to the power of the floating point number on top of the data stack and places the result on top of the stack.

PI - Pushes PI onto the top of the data stack. cos - This routine expects a floating point number in radians on top of the data stack. The cosine of this number is taken and placed on top of the data stack.

SIN - This routine expects a floating point number in radians on top of the data stack. The sine of this number is taken and placed on top of the data stack.

TAN - This routine expects a floating point number in radians on top of the data stack. The tangent of this number is taken and placed on top of the data stack.

ACOS - This routine takes the arc cosine of the number on top of the data stack and returns the result in radians to the data stack.

- This routine takes the arc sine of the number on top of the data stack and returns the result in radians to the data stack.

- This routine takes the arc tangent of the number on top of the data stack and returns the result in radians to the data stack.

9.4) Conversion Routines

The conversion routines are very simple in nature and are carried out by the arithmetic processing unit. The conversion routine takes the top data stack entry in the data format specified and converts the number to the new data format specified by the keyword. These conver- sion keywords are presented below.

S-D - Converts the top number on the data stack from single precision to double precis ion.

S-F- Converts the top number on the data stack from single precision to floating point format.

D-S - Converts the top number on the data stack from double precis ion to single precis ion.

D-F - Converts the top number on the data stack fromdouble precis ion to floating point format.

F-S - Converts the top number on the data stack from floating point format to single precision.

F-D- Converts the top number on the data stack from floating point format to double precision. CHAPTER 10

LOGICAL AND RELATIONAL OPERATORS

The keywords of this group are used to test values on the data

stack. The logical operators are all word length operators and perform

just a few operations. These logical operators are the AND, OR, XOR,

and NOT functions. The relational operators are broken into three

groups according to the three data formats. Most of these operators

replace the top one or two stack entries with a flag. These flags can

then be used by the loop and branch control structures. The true value

is a non-zero number and a false flag is zero. The relational operators

that do not return a flag will leave one of the top two stack entries on

the stack.

10.1) Logical Operators

These operators are quite simple and perform the four basic logical

operations. These keywords expect two word length entries on the data

stack, except for NOT which expects one word length entry. The operands

are discarded after the operation and the result is placed on top of the

data stack. These operations can be used to perform on flags as well as

other data types specified by the user.

AND - Logically ANDs the top two entries on the data stack and places the result on top of the stack.

OR - Logically ORs the top two words on the data stack and places the result on top of the stack.

XOR - Logically EXCLUSIVE ORs the top two words on the data stack and places the result on top of the stack.

NOT - hi's keyword takes the 1's complement of the word on top of the stack and places the result back on the stack. This inverts the value of each bit in the word. 10.2) Single Precision Relational Operators

The single precision relational operators are unary or binary operators which return a logical flag for most of the keywords. The two keywords that do not return a flag as the result are MIN and MAX.

MIN returns the smaller of the top two numbers on the stack, which MAX returns the larger number. The relational operators expect the numbers on the stack to be 16-bit numbers. The single precision relational operators are given below. - - This relational operator returns a true if the top two stack entries are equal, otherwise it returns a false. The two numbers that were on the stack are discarded.

<> - The <> keyword returns a true if the top two stack entries are not equal, otherwise it returns a false. The two numbers that were on the stack are discarded.

< - The

> - The > operator returns a true if the top stack number is greater than the second stack number, otherwise it returns a false. The two numbers that were on the stack are discarded.

U< - The U< keyword performs an unsigned comparison of the top two stack entries. It returns a true if the absolute value of the top stack entry is less than the absolute value of the second stack entry, otherwise it returns a false. The two numbers that were on the stack are discarded. o= - This operator returns a true if the top number on the stack is zero, otherwise it returns a false. This operator can be used to complement a flag on the top of the data stack. The orginal number on the data stack is discarded.

O< - This keyword returns a true if the top number on the stack is less than zero, otherwise it returns a false. The number that was on the data stack is discarded.

O> - This keyword returns a true if the top number on the stack is greater than zero, otherwise it returns a false. The number that was on the data stack is discarded.

MIN - This operator pops the top two numbers off the data stack and places the smaller number back on the data stack. MAX - This operator pops the top two numbers off the data stack and places the larger number back on the data stack.

10.3) Double Precision Relational Operators

The double precision operators act the same as the single precision

relational operators, but the operands are 32-bit instead of 16-bit.

The double precision operator set has a few less keywords than the

single precision instruction. The operators that are left out of the

double precision set cause no real loss because they can be implemented

from the other relational operators and a few other keywords. The

double precision relational operators discard the one or two operands

used and place a 16-bit flag on the data stack. The double precision

operators are described below.

D= - This operator returns a true if the top two longwords on the data stack are equal, otherwise it returns a false. The two longwords are popped off and discarded.

DO= - This keyword returns a true if the top st.ack entry is equal to zero, otherwise it returns a false. The top longword is popped off and discarded.

D< - The D< operator returns a true if the top longword on the stack is less than the second longword on the stack, otherwise it returns a false. The two longwords are removed from the stack and discarded.

D> - The D> operator returns a true if the top longword on the stack is greater than the second longword on the stack, otherwise it returns a false. The two longwords are removed from the stack and discarded.

DU< - The DU< keyword performs an unsigned comparison of the two longwords on the data stack. A true flag is put on the data stack if the absolute value of the top entry is less than the absolute value of the second stack entry, otherwise a false flag is placed on the stack. The top two longwords are popped off the stack and discarded.

DMIN - This keyword pops the top two longwords off the stack and places the smaller longword back on the data stack.

DMAX - This keyword pops the top two longwords off the stack and places the larger longword back on the data stack. 10.4) Floating Point Relational Operators

The floating point relational operators are all binary operators in that they require two operands. These operators take the two floating point operands off the data stack, perform the comparison and return a

16-bit flag. The floating point relational operators were a necessary addition to the FPFORTH language to allow complete use of the arith- metic operations. A little warning about the results of these operators, the internal storage of a number in floating point format is not exact and can cause numbers that should be equal to generate a flag that is not what is expected. This can be especially true of F=, because it is possible that two numbers that should be equal but are not because of roundoff errors. These statements are not meant to state that these operators are useless, but must be used with a little consideration of the internal number storage. The operator set for floating point comparisons is smaller, but will perform the basic relational operations of equals, less than, and greater than. The floating point relational operators are described below.

F= - This keyword returns a true if the top stack entry equals the second stack entry, otherwise it returns a false. The top two stack entries are removed from the stack and discarded.

F< - The F< keyword returns a true 'if the top stack entry is less than the second stack entry, otherwise it returns a false. The top two stack entries are removed from the stack and discarded.

FMIN - This operator pops off the two floating point numbers on top of the stack and places the smaller number on the stack.

FMAX - This operator pops off the two floating point numbers on top of the stack and places the larger number on the stack. CHAPTER 11

LOOP AND BRANCH KEYWORDS

The loop and branch keywords are designed to allow program control structures. The loop and branch keywords are system directives that are applicable only in the compile mode. These keywords are immediate keywords that exist in the COMPILER vocabulary. Most of the keywords load the word addresses of the program control directives and branch addresses to the threaded list of the keyword being compiled. All of the loop and branch program control directives are primitives to insure fast execution. All of the loop and branch keywords are secondaries for compactness and ease of coding. The loop and branch keywords are described in the following sections.

11.1) BEGIN and END Structure

The simplest and most primitive loop construct is the BEGIN-END loop. The keyword BEGIN marks the beginning of the loop and END marks the loop end. Below is a diagram of the BEGIN-END loop structure.

: . . . .BEGIN . . . . Flag END . . . ; OK tu FALSE TRUE

The flag just before END is used to determine if the looping process is to be terminated or continued. All code between BEGIN and END will be repeated until the flag goes true during execution. Endless loops can be created by pushing a zero to the data stack just before the END keyword. There are two levels of action when using these keywords. The action that occurs during the compilation of a new keyword and the action that occurs when the keyword is executed. First consider the action of BEGIN and END during compilation. BEGIN pushes the address of the next free dictionary space to the data stack. This is the address where the word address of the next token that follows BEGIN will be placed. END adds the word address of the program control directive ;kEND to the threaded list and encloses it in the dictionary. It then pops the address placed on the data stack by

BEGIN and encloses the address in the dictionary as the jump address.

This action requires that the immediate keywords between BEGIN and END must not leave values on the data stack.

When the definition wliich contains the BEGIN-END loop is executed, the threaded code will be executed until the *END word address is encountered. When *END is executed, it takes the top value on the data stack and tests it for zero. Since this value on the stack is usually a flag, it is testing to see if the flag is true or false. If the flag is false, the routine jumps back to the address pointed to by the instruction register. This address is the address of the token follow- ing BEGIN in the keyword definition. If the flag is true, the instruction register is advanced two bytes to skip over the jump address and program execution continues. The BEGIN-END loops can be nested to any level as the process uses none of the system resources, such as the stacks. An example of the BEGIN-END structure is now given.

: DOWN BEGIN DUP . 1- DUP O= END 9- . OK

5DOWN54321 OK 11 -2) IF ELSE THEN Structure

The IF ELSE THEN construct allows the conditional execution of code. This construct is similar to the traditional IF THEN ELSE statement but is implemented in a different order due to the FORTH architecture of compilation. The control structure can be used to do the same kind of things the regular IF THEN ELSE statement can do.

Shown below is the correct syntax for the IF ELSE THEN statement.

: ... Flag IF ... THEN u Li**; OK TRUEu UNCONDITIONAL FALSE

TRUE UNCOMD ITIONAL IT rn : . . . Flag IF . . . ELSE THEN ; OK I**LA*' FALSE UNCONDITIONAL

During compilation the if keyword adds the word address of the program control directive *IF to the threaded code of the keyword being defined. IF then pushes the address of the next free dictionary space to the stack and advances the dictionary pointer by two bytes to leave space for a jump address.

The immediate keyword ELSE adds the word address of the program control directive "ELSE to the threaded code list of the keyword being defined. ELSE then pushes the address of the next free dictionary space to the stack and advances the dictionary pointer by two bytes to leave space for a jump address. The top two entries on the data stack are removed and the top entry is placed back on the data stack right away. The next free dictionary location address is stored at the

address of the second stack entry which was just removed from the

stack. By storing this address at the location immediately following

the *IF program control directive, a jump to the code following *ELSE

can be executed. This procedure effectively puts the address of the code following *ELSE right after *IF as the jump address. ELSE also

leaves two bytes following *ELSE empty for a jump address to skip the code between ELSE and THEN. The address of this memory location is currently on top of the data stack.

The THEN statement ends all control structures starting with IF.

The THEN immediate keyword will load the jump address which is the

location following THEN into the space reserved by IF or ELSE. Then pops the address on top of the stack and stores the current free dic t ionary address at the memory locat ion following *THEN.

This procedure effectively compiles the conditional statement and when executed causes the following action to occur. When the *IF control directive is encountered, it tests the value of the flag on top of the data stack. If the flag is true (<> 01, the code after IF will be executed until either ELSE or THEN is reached. If ELSE is encoun- tered an unconditional jump is made to the code immediately following

THEN. If THEN is encountered the code after THEN is executed. If the

flag before IF was false (= 0), then a jump is made to the code

following ELSE or THEN if there was no ELSE statement. If ELSE is present, the code between ELSE and THEN is executed and when THEN is reached, it jumps to the code after THEN and continues execution. No matter what value the flag has, code execution will continue following

the THEN statement once the conditional statement concludes. An example of the IF ELSE THEN statement is shown below. The example

prints out the larger number of the top two stack entries.

: LARGER OVER OVER > IF DROP . ELSE . DROP '- OK

10 6 LARGER 10 OK

1 7 LARGER 7 OK

The OVER keyword places a copy of the second stack entry on top of

the stack. Using the OVER keyword twice, effectively duplicates the

top two stack entries. The two stack entries are then comparzd using

. If the second stack entry is greater than the top stack entry, the

code between IF and ELSE is executed, otherwise the code between ELSE

and THEN is executed.

11.3) WHILE Statement

The basic loop and branch may be combined with the keyword WHILE to

create a more complicated conditional looping process. WHILE is used

differently in FPFORTH than in other languages as it ends the program

control structure instead of starting it. The syntax for the WHILE

statement is shown below.

UNCONDITIONAL

. - : . . . BEGIN .. . flag IFu .. . WHILE . . . ; OK FALSE TRUE UNCONDITIONAL r-3 BEGIN ... flag IF ELSErn WHILE .. . I"' f FALSE UNCOND LTIONAL

As can be seen from the syntax diagrams, WHILE is used with keywords already described and they perform exactly as stated in the previous sections. When WHILE is compiled it expects two addresses on top of the data stack. The first thing WHILE does is add the word address of the program control directive *WHILE to the threaded list.

The second stack entry is removed from the stack and added to the threaded list following the address of *WHILE as the jump address to the code following begin. It then removes the top address on the data stack and stores the next free dictionary location at this address.

This is the jump address required by IF or ELSE to jump out of the loop once the termination condition is met.

When WHILE is executed it will cause the unconditional jump back to the beginning of the loop. The only way to exit the loop is through the IF structure. The IF structure can cause a conditional jump out of the loop when the flag reaches the proper value.

11.4) DO LOOPS

The DO LOOPS structure allows a group of instructions to be exe- cuted a specific number of times. The DO LOOP structure can be generated using the BEGIN-END structure, but requires extra program coding. There are four DO LOOP structures and these are shown below. count < end m . . . end count DO . . . LOOP .. . ; OK I3 count > = end

count < end m ... end count CDO ... CLOOP 6..; OK count >= end

count < end 7 : ... end count DO ... Increment +LOOP

count >= end

: ... end count CDO ... increment C+LOOP

count >= end

The difference between DO and CDO is that DO uses word length indices and CDO uses byte length indices. The same is true for the other keywords that have a C preceeding a similar keyword name. The end shown in the syntax statements is the ending argument and count is originally the starting index argument. The starting index is incremented until it is equal to or greater than the ending argument and the loop is terminated when this occurs. The code between DO and

LOOP is always executed at least once no matter what the starting and ending arguments are. The starting index is incremented by 1 in the

LOOP and CLOOP structures. While in the +LOOP and C+LOOP structures the starting index is incremented by the value on top of the data stack which is shown as increment in the syntax diagrams.

During compilation DO (CDO) causes the word address of the program control directive *DO (ACDO) to be added to the threaded code list of the keyword being defined. *DO (*CDO) also pushes the address of the next free dictionary space onto the data stack. When LOOP (CLOOP,

+LOOP, C+LOOP) is compiled the word address of the program control' directive *LOOP (*CLOOP, *+LOOP, *C+LOOP) is added to the threaded code

list. The top stack address is popped off the stack and placed in the next free dictionary space as the jump address to the beginning of the

LOOP. The DO LOOP structure is now complete and ready for execution.

When the DO LOOP is executed, the *DO (*CDO) routine expects two

16-bit words on the data stack which will be the starting and ending arguments. The two numbers are moved to the stack as 16(8)-bit numbers

in the same order that they appeared on the data stack. The top return stack entry is the index value which is initially the starting argu- ment. The *LOOP and *CLOOP routines increment the loop index by 1.

While *+LOOP and *C+LOOP routines increment the loop index by the top value on the data stack. The loop index is then subtracted from the ending argument to test for loop termination. If the result is greater than zero, the jump is taken to the beginning of the loop to execute the loop again. If the result is less than or equal to zero, the loop is terminated and execution continues at the code following LOOP

(CLOOP, +LOOP, C+LOOP). The DO LOOP structure may be nested to any level by the user.

The user may wish to leave a DO LOOP because of some special condition. The keyword LEAVE (CLEAVE) allows this to be accomplished.

LEAVE (CLEAVE) causes the loop to be terminated when they are executed.

The keyword LEAVE (CLEAVE)adds the word address of *LEAVE (*CLEAVE) to the threaded code list of the keyword being defined. The *LEAVE

(*CLEAVE) program control directive will cause the loop index to be set equal to the ending argument when it is executed. The LEAVE (CLEAVE) keyword is usually used with an IF structure so that it is only executed when a certain condition is met.

This chapter presents the complete set of control structures of the

FPFORTH system. These control structures allow the user to implement any kind of loop or branch program structure. There is no GOT0 statement but the GOT0 statement is considered a poor programming structure anyway, and its use should be avoided. The control struc- tures allow the FPFORTH programs to follow a nonsequential execution.

The loop and branch keywords greatly expand the usability of the system as a problem solver. CHAPTER 12

DEFINING WORDS AND SYSTEM KEYWORDS

This chapter presents the keywords that would not be found in the other non-FORTH languages. The defining words are instructions necessary to allow the compilation of new keywords. This set of instruct ions includes the keywords that define constants and variables. The defining words are the only keywords that allow for a new entry to be added to the FPFORTH dictionary. The system keywords perform many of the chores required by the FPFORTH language. These keywords can operate with the system's vital functions. The system keywords can read the data stack pointer and return stack pointer addresses, make adjustments in dictionary space, and change the system number base. The system keywords allow the user to manipulate the

FPFORTH system variables and internal functions. The keywords in this chapter can control the very structure and operation of the FPFORTH language.

12.1) Defining Words

The defining word operators allow the user to add new keywords to the dictionary. Most of the defining words have been presented earlier when discussing constants and variables. These keywords allowed the user to create variables and constants to be accessed by a keyword name. The new variables and constants created with the defining words were added to the FPFORTH dictionary. The FPFORTH system also allows the user to create new keywords that perform a desired action. The keywords used in creating new keyword instructions are :, ;, and :CODE.

The defining words were part of Loeliger's FORTH language and were included in the FPFORTH language. The defining words are very important

to a FORTH language to allow the system to be more than just a large

pocket calculator. The FPFORTH language is able to be expanded by the

user through the use of these keywords. The syntax for creating new keywords is shown below.

:

:

The is the name given to the instruction being defined. The (already defined keywords) are keywords that are already part of the FPFORTH dictionary. The new keyword is defined from already existing keywords that are currently in the dictionary. Keywords that are not in the dictionary will result in an error if used in the definition of a new keyword. The compilation of the new keyword will be aborted and an error message containing the unknown will be displayed.

The following ;CODE will be 8085 machine code.

- This defining word causes the context vocabulary to become the current vocabularyy allowing new keywords to be added to the current vocabulary to be found during keyword searches. The token following : is scanned and a dictionary header is generated using CREATE. The code address is then formed by adding the address of the inner interpreter COLON routine to the dictionary entry. At the end of the : routine the system MODE variable is set TRUE to indicate the compile mode.

y - The ; immediate keyword encloses the word address of the inner interpreter routine SEMI in the dictionary. ; then sets the system MODE flag to FALSE to terminate the compile mode and establish the execution mode.

;CODE - The ;CODE immediate keyword encloses the word address of SCODE in the dictionary. ;CODE then sets the system MODE flag to FALSE to terminate the compile mode.

Two types of keywords can be created using the three keywords just described. Keywords defined using ; execute, the FPFORTH instructions

that were compiled in the keyword definition. The keywords defined using ;CODE execute the FPFORTH instructions that were compiled when it was created and has generic machine code that is executed when a new keyword is de'fined using the keyword compiled with ;CODE. The generic machine code is entered directly into the dictionary using , or C, to enclose the bytes in the dictionary at the end of the keyword following the word address of SCODE. The generic machine code always ends with a jump to the inner interpreter NEXT routine.

Examples of keywords using ;CODE are the keywords CONSTANT,

VARIABLE, and their variations. These keywords all use this construct because they will cause two levels of action. When a defining word such as CONSTANT is used to create a new dictionary entry, it creates a dictionary header and leaves space for the constant to be stored in.

CONSTANT then adds an unconditional jump to the generic machine code at the end of its own definition. When the new keyword is executed it goes to the generic machine code of CONSTANT and executes the code.

This action is to take the value stored in the keyword as a constant and place it on the data stack. The rest of the constant and variable defining words perform similar actions.

12.2) Sys tem Keywords

The system keywords are a collection of routines that allow the user access to the inner workings of the FPFORTH system. Many of the keywords are required for the system to operate and have been made user accessible because of their usefulness. The system keywords do a wide variety of things in the FPFORTH language. The keywords in the system keyword set are presented below and the actual code for these routines is listed in appendix H. (Tick) - The ' keyword scans the next token in the input buffer following it and searches the CONTEXT and CURRENT vocabu- laries for the keyword corresponding to the token. If the keyword is found, its word address is pushed onto the stack. If the token is not found, the token is echo displayed followed by a question mark.

+ SP - The +SP keyword adds the current data stack pointer to the number on top of the data stack and places the sum on the data stack.

- SP - The -SP keyword subtracts the current data stack pointer from the number on top of the data stack and places the difference on the data stack.

,(Comma) - The , routine pops the top data stack word and stores it at the dictionary pointer address. The dictionary pointer is then incremented by 2 to enclose the word in the dictionary. c Y - The C, routine is exactly like , except that C, stores the low order byte of the top data stack entry in the dictionary and increments the dictionary pointer by 1.

? SP - The ?SP keyword pushes the current data stack pointer address to the top of the data stack.

? RS - The ?RS keyword pushes the current return stack pointer address to the top of the data stack.

ABORT - ABORT performs an unconditional jump to the STARTIRESTART routine to reinitialize the system and the stacks.

ASPACE - ASPACE pushes an ASCII space code onto the data stack. This routine is usually used just before the TOKEN routine to put the separator on the data stack.

BINARY - BINARY sets the system number base to two or the binary radix . CA! - The CA! routine stores the address at the top of the stack in the word address location of the latest entry in the CURRENT vocabulary. This process effectively adds a routine's code address to the keyword currently being defined.

CORE - The CORE keyword sets the CONTEXT system variable to the address of the first header in the permanent vocabulary.

DECIMAL - This keyword sets the system number base to ten.

Do, - The DO, keyword stores the program control directive at the ' top of the data stack in the dictionary and returns the address of the next free dictionary location on top of the data stack. END, - The END, routine encloses the address of the program control directive on the data stack in the dictionary. END, then encloses the jump address that was the second entry on the data stack in the dictionary.

ENTRY - The ENTRY keyword places the address of the first header byte of the latest entry in the CURRENT vocabulary on the data stack.

EXECUTE - The EXECUTE keyword will cause the execution of another keyword. EXECUTE pops the top data stack entry and places it in the word address register. Control is then passed to the interpreter RUN routine which will cause execution of the keyword.

FORGET - FORGET sets the CONTEXT to CURRENT and searches the CONTEXT vocabulary for the token following FORGET in the input buffer. If the token is found in the vocabulary, it is delinked from the CURRENT vocabulary and the DP is set to point to the first header byte of the located keyword. If the token is not found in the vocabulary it is echo displayed followed by a question mark.

HE RE - HERE causes the address of the next free dictionary space to be placed on top of the data stack,

HE X - The HEX keyword sets the system number base to base 16 or hexadecimal .

IMMEDIATE- IMMEDIATE delinks the latest keyword in the CURRENT vocabulary and links it to the COMPILER vocabulary.

OCTAL - The OCTAL keywords sets the system number base to base 8 or octal.

SINGLE - The SINGLE routine determines if the top data stack entry is a byte length number (i.e. if its high order byte is all ones or zeroes). A false flag is placed on the stack if it is a byte length number, otherwise a true flag is placed on the stack.

VOCABULARY- Creates a vocabulary keyword dictionary entry whose name is the token following VOCABULARY, with a link to the last entry in the CURRENT vocabulary. CHAPTER 13

FPFORTH PROGRAMMING EXAMPLE

This chapter will try to present a programming example to demonstrate how the FPFORTH system works. The example that will be shown will be fairly simple to give the user a feel for how easy the system is to use. The example will show how the user can add to the

FPFORTH language to develop new keywords. These new keywords can then be used to create new more powerful keywords. This structure of building a user's own language will be demonstrated in the example. In the example the FPFORTH system's responses will be underlined to distinguish it from the user's input.

Many times it is desired to find the roots of anequation. To solve a problem such as this, floating point arithmetic must be used.

The FPFORTH system will be able to solve this type of problem quite easily, where a standard version of FORTH would require the equation be nodified to allow an integer solution. There are several methods to find the roots of an equation, but the Newton-Raphson method lends itself quite well to finding the roots of an equation on a computer.

The iterative equation used is Xn+, = Xn - F(xn )/F' (Xn). The Newton-Raphson method is iterative and the user must set a limit as to when the correct result has been achieved. For this example let the limit be when F(x,)/F~(x,) < 0.00001. The limit we have decided on will be defined as a constant to allow for easier interpretation of the keyword definition for the solution to this problem.

1.OA-5 2CONSTANT LIMIT -OK LIMIT . ln-5 OK Let the equation that we will try to find a root of be

Floating point numbers cannot be used in a keyword defintion because the literal handlers only work with byte and word length data.

This problem can be avoided by defining the numbers needed as constants. The constants for the coefficients will be defined as shown. 3.0 2CONSTANT THREE -OK 4.0 2CONSTANT FOUR -OK 5.0 2CONSTANT FIVE -OK 6.0 2CONSTANT SIX OK

The keywords will now be defined to evaluate the equations F(X ) n and F' (X,) , where:

:F(X) 2DUP 2DUP THREE - * FOUR + * FIVE - 9- OK :Ff(X) 2DUP THREE * SIX - * FOUR + 9- • OK

One other keyword is needed to find the root of the equation. The other keyword needed is a floating point absolute value operator to make sure the number is positive to test against the limit.

:FABS 2DUP 0 S-F F> IF MINUS THEN . OK 9- 9-

The FABS keyword is accomplished with the conditional IF THEN state- ment. The longword on top of the data stack is duplicated with the

2DUP keyword. A zero is then placed on the stack and converted to a floating point number. A comparison is then done to see if the top stack entry is less than zero. If the number is less than zero, FMINUS is executed and the absolute value will be placed on the stack, other- wise no action is taken. The FABS keyword will be tested to see if it works properly.

-2.1 FABS .F 2.1 OK 3.0 FABS .F 3 OK The FABS keyword was seen to work correctly and all keywords are now in the FPFORTH dictionary that are required to define the keyword

ROOT. ROOT will be the name given to the keyword that will evaluate

the root of the equation.

:ROOT BEGIN 2DUP 2DUP F(X) 2SWAP F1(X) F/ 2DUP FABS LIMIT F> OK IF - WHILE 2DROP CRET .F a-- OK

The keyword has been defined above on two separate input lines.

The FPFORTH language allows the user to continue a keyword definition

from one line to another. Each line will be compiled as it is entered and the system will respond with an OK message if no errors occur. The

ROOT keyword is defined to find the root of F(x) closest to the value on top of the data stack. The data stack will be assumed to hold the

first approximation to the root of the equation. The iterative procedure is accomplished through the use of the BEGIN IF WHILE structure . The loop will continue to execute until the change in the value of the approximated root is less than the limit. When this condition occurs, control is passed to the keyword after 'WHILE and the keyword ROOT will be able to terminate execution. The loop starts out by evaluating F(X) and F1(X) at the current approximation of the root.

F(X) is then divided by F1(X) to get the change in X. This change in X

is then duplicated, with the duplicated value being made positive. If the change in X is greater than the limit, the change in X is subtracted from the old approximation of the root to produce a new approximation. The loop will then be executed again by returning to the BEGIN statement. If the change in X is less than the limit, a jump occurs to the code after WHILE. The change in X is discarded off the stack and the root of the equation is displayed. One root of the equation will now be found to demonstrate that the keyword works. The first approximation to the root will be 2.

2.0 ROOT 2.21341228485107421875 OK

The result displayed has 20 digits to the right of the decimal point because of internal rounding errors that occur in the output routine .F. The floating point routine .F is unable to truncate the extra digits, so the extra digits are displayed. The .F output routine is accurate to six significant digits. All digits after the first six should be ignored as they arise due to the way the floating point number is converted to a sequence of ASCII character representing the number.

The example shown should demonstrate how the FPFORTH language and system can be used. The example is not complex but gives a view of the system's capabilities. The FPFORTH example tried to demonstrate how the user can build onto the already existing FPFORTH language and develop more useful keywords to solve the user's problem. The ability of the user to build onto the language is very important because it allows the creation of code that will eventually be either a part of a program or the program itself. CHAPTER 14

CONCLUSION

The complete FPFORTH system has now been presented. The system was developed around the Intel SDK-85 kit. The capabilities of the kit were greatly expanded by the addition of the 8231A Arithmetic

Processing Unit. The system now possesses the capabilities to perform floating point arithmetic. The memory space of the system was expanded to hold the FPFORTH language. This expansion included 8 Kbytes of ROM memory and 8 Kbytes of RAM memory. The hardware system was interfaced to a Zenith terminal to allow the user to interact with the system through the terminal instead of the SDK-85 kit's keypad. With the hard- ware system designed, an expanded version of FORTH was created for the system. The FPFORTH language was the result of expanding Loeliger's version of FORTH with floating point routines. The FPFORTH language was designed to take full advantage of the APU used in the system. The

SDK-85 kit has been expended from a development system into a small computer system running under an expanded version of FORTH.

The FPFORTH system possesses some interesting features that make the system operate extremely fast when performing arithmetic operations. The use of an extended FORTH language allows the develop- ment of complex programs in a higher level language without the great expense in execution time caused by most higher level languages.

FORTH's speed of execution has always been one of its strong points.

FORTH allows the user to write code that is so streamlined that it executes only slightly slower than code written in .

The FPFORTH language also allows the user to control the most detailed parts of the system almost as well as assembly language code. The

FPFORTH language gives the user the ability to develop fairly complex code at a much smaller expense than offered by other high level languages and still maintain control of the system at the chip level.

The other interesting feature of the FPFORTH system is the use of the 8231A Arithmetic Processing Unit. The use of an APU to do math functions on an 8-bit system is definitiely not a new idea, but it is an idea that has not been used much. The APU increases the speed at which the FPFORTH system can perform most arithmetic operations. The

APU also reduces the length of several of the keywords' codes. These two facts about using the 82318 APU in the FPFORTH system make it very advantageous to the system.

The FPFORTH system is now capable of executing code nearly as fast as machine code but without the time involved for such detailed coding requir2d by assembly language programming. The speed increase is especially noticed in the complex arithmetic routines where the APU also increases the speed of the system. The system was designed without a particular application in mind but the system is definitely oriented toward performing many math calculations.

The FPFORTH system is a prototype system in many ways. The system was developed on the SDK-85 kit and has most of the system's memory space inaccessible for the FPFORTH system because of the kit's design.

The start up of the FPFORTH system is done through the SDK-85 kit' s monitor program. The FPFORTH system no longer needs the support features of the SDK-85 kit that were needed in developing the system and is ready to be placed on a separate board to run completely by itself. The FPFORTH system also lacks any secondary memory space for storage of programs and other files. Another feature that needs to be

added to the system is an editor program. The editor program would

allow the programmer to make changes and create programs in a more

forgiving manner than in the interactive mode that is now the only method of creating programs.

Once these features are added to the FPFORTH system the system will

go from being a prototype system to a small personal computer system.

This statement may seem a little bold, but the system has the basic

tools needed to build a very powerful computer system for performing a wide variety of problem solving. The speed and math capabilities of

the system will exceed most other 8-bit systems in executing programs

that are mostly arithmetic in nature. The system also is much more

flexible than other FORTH systems because it offers the floating point

capabilities through the use of the 8231A Arithmetic Processing Unit.

Other FORTH systems must use fixed point arithmetic and are unable to

perform any of the trigonometric or exponential functions offered by

the FPFORTH language. The FPFORTH language can be used to do anything

other FORTH systems can do plus several applications that require the

floating point capabilities that FPFORTH offers.

The FPFORTH system can be the basis for a powerful scientific

problem solver. The system has the basic tools to solve most mathe- matical problems. The system is not restricted too much in this field

except by the precision that may be required in some calculations. The

FPFORTH system can be used to solve most of the computer oriented

problems confronted by scientists and mathematicians . BIBLIOGRAPHY

Brodie, L. Starting FORTH, Prentice-Hall, Englewood Cliffs, N.J., 1981.

Furht, Borivoje and Lee, P. "An Efficient Software Driver for AM9511 Arithmetic Processor Implementation ,I1 Proceedings of the IEEE, June 1984, pp. 7-19.

Hashizume, Burt "Floating Point Arithmetic ,I1 Byte, Vol. 2, No. 11, Nov. 1977, pp. 76-77, 180-188.

Loeliger, R. G. Threaded Interpretive Languages, Byte Books, Peterborough, N.H., 1981.

Ritter, T., and Walker, G. "varieties of Threaded Code for Language Implementation," Byte, Vol. 5, No. 9, Sept. 1980, 206-227. APPENDIX A

CHIP PIN OUTS VCC PC 2 TIMER PC;IN, 1 3

Lo bvcc SET OUT 3 38 HLDA SID 15 RST TRAP7.5 4 7

30 ALE 29 k so

vss 420 21ba,

RL~4 1 40 b vcc

SHIFT SL3

1 SLI 2 SLo J OUT Bo J OUT B, 2 OUT B2 3 OUT B3 2 OUT A. 3 OUT A1 3 OUT A2 J OUT Ag JBD- 2 CS = A0 vss C 1 24 XEND vcc C2 23 3 CLK EACKC 3 22 IRESET SVACKL 4 21 JA SVREQC 5 20 '3~3 Do NOT USE E 6 19 ~lim ( TI3 LOW) !I7 18 7cs DBO C8 17 3 8ZADY 16 JVDD 1.5 3~B7 14 7~~6 13 pB5

PIN OUT FOR 8231~

ARITHTiETIC P2OCESSING UNIT APPENDIX B

SDK-85 KIT SCHEMATIC

APPENDIX C

MEMORY EXPANSION BUFFERS SCHEMATIC

APPENDIX D

ADDED MEMORY SCHEMATIC ADGfiESS DATA BUS 6US APPENDIX E

SCHEMATIC OF 82318 INTERFACE TO 80856 --. --. +5V

CLK.-- CLK Vcc RD RD SVACK rn. WR V,, -+12V Do D0, EdD- Dl SVREQ- . Dz 9B2 8085A : !8,: 8231A 2:- 6 DS D B5 . 7 I I D6 D86 -- Dt . DB, EACK Ae RESET- - 7LcS REAa't RESET READYLI APPENDIX F

ZENITH TERMINAL INTERFACE T * V

I DqTP J'JT 2 3. - )ye$ ==330pC

GND 7 *I

f) ---

ZENITH TERTlINAL INTERFACX 'I7 2. 3 5,68 -J RS-232 to ZENITH Terminal APPENDIX G

OUTER INTERPRETER ROUTINES START /RE START 60 bytes 8000

This routine has two entrances, one that initializes the entire system and calls up the start message. The other one does partial system initialization and presumes an error message has already been set up.

Code Labe 1 Op Code Comments

116D99 START : LXI D, STRIMSG ;Set up start message LXI H OOBO ;Initialize dictionary SHLD DP ; pointer LXI H,2017 SHLD CURRENT :Initialize Current LXI H,2017 SHLD CONTEXT ;Initialize Context LXI H,9959 SHLD COMPILER ;Initialize Compiler MVI A OA ;Initialize Base to STA BASE ; base 10 ABORT : LXI SP,COOO ;Initialize Data Stack Pointer LXI H,BEOO ;Initialize Return SHLD RS ; Stack Pointer LXI A,OO ;Initialize State=O SHLD STATE ;Mode=O LXI H,8080 ;Store terminaters at SHLD BC 80 ; end of line buffer LXI B ,OUTER INTERPRETER ;Set up interpreter PSH D ;Push message address onto JMP NEXT ;stack

TYPE 14 Bytes 8620

Pops the top stack entry which points to a string consisting of a length argument followed by that many ASCII characters, outputs the string characters to the display.

Code Labe 1 Op Code Comments

Data 4,"TYP1' Link Address Code Address CALL $TYPE JMP NEXT Complete Memory Layout of

All FPFORTH Routines and Codes

Routine Bytes Address Routine Bytes Address

BINARY c, INNER INTERPRETER CA ! DE C IMAL SEMI 12 803C Do, NEXT 6 8048 END, RUN 7 804E ENTRY COLON 15 8055 HE RE EXECUTE 12 8064 HEX OCTAL OUTER INTERPRETER SINGLE TYPE Interpreter 3 2 ?SEARCH 52 ?NUMBER 52 SYSTEM VARIABLE ROUTINES ?EXECUTE 32 INLINE 99 BAS E $PATCH 36 COMPILER *STACK 25 CONTEXT 4 CURRENT SEARCH 84 DP TOKEN 7 4 L BP QUESTION 38 MODE NUMBER 41 1 STATE 0 9 1 9 PROGRAM CONTROL DIRECTIVES

SUBROUTINES *+LOOP *C+LOOP $CRLF 11 843 8 *DO $ECHO 40 8443 *CDO $KEY 46 846 B *LOOP $ DELAY 7 8499 *CLOOP $PSHAPU 10 84A0 *IF $POPAPU 10 84AA *ELSE *END SYSTEM ROUTINES WHILE *LEAVE *CLEAVE DOES > +SP NEXT , (Comma) -SP SCODE ?RS ? SP ABORT ASPACE Routine Bytes Address Routine Bytes Address

I/O ROUTINES CONVERSION ROUTINES

S-D S-F D- S D-F F-S F-D

LOGICAL OPERATORS ASCII DISPLAY AND DUMP OR ECHO XOR KEY NOT PART SIGN SINGLE PREC ISION SPACE RELATIONAL OPERATORS PAD CRET - ADUMP <> APART < u> DELAY > CNTRL o= D. 0 < MIN MAX

DOUBLE PRECIS ION RELATIONAL OPERATORS

DEFINING WORD ROUTINES DO= 37 D= 43 D < 43 43 CREATE DMIN 6 1 CVAR IABLE DMAX 6 1 VAR IABLE VOCABULARY FLOATING POINT 2CONSTANT RELATIONAL OPERATORS 2VAR IABLE F= LITERAL HANDLERS I?< F > FM IN MAX Routine Bytes Address Routine Bytes Address

SINGLE PRECIS ION LNTERSTACK OPERATORS ARITHMETIC OPERATORS

-Ir * / + - 1 /MOD 1+ 1- 2"x 2 + 2- STACK OPERATORS 2 / ABS DUP MOD 2DUP MINUS SWAP DROP DOUBLE PRECIS ION OVER ARITHMETIC OPERATORS 20VER LROT D* RROT D+ 2SWAP D- 2DROP D / DMOD MEMORY REFERENCE OPERATORS D/MOD DMINUS ! DABS C! +! FLOATING POINT C+! AYITHMETIC OPERATORS @ C@ F* 2! F + 2@ F - OSET F / 1SET ' SQRT COSET PWR ClSET LOG LN COMPILER DIRECTIVES EXP PI +LOOP cos BEGIN SIN C+LOOP TAN CDO ACOS CLEAVE AS IN CLOOP ATAN DO FMINUS ELSE END IF Routine Bytes Address

COMPILER DIRECTIVES (continued)

LEAVE 16 98E 1 LOOP 16 98F1 THEN 16 9901 WH I LE 20 9911 [ 32 9925

COMPILE MODE TERMINATORS

3 20 9945 ;CODE 20 99 59

MESSAGES

Startup 10 99 6 D Stack Error 18 99 7 7 OK Message 4 99 89 ? Message 3 998d

ODDS AND ENDS

$TYPE 14 9990 $ADD. S 2 1 999E NUMO 44 99 B3

VOCABULARY ROUTINES

IMMEDIATE 41 99 DF FORGET 44 9A08 CORE 12 9834 SUBROUTINES

11 bytes

Issues a carriage'return-line feed sequence to the display to scroll the display if required, clear the next display line and leave the cursor at left end of the current blank line.

Code La be 1 Op Code Comments

3E OD $CRLF : MVI A,"CRU ;Get carriage return code CALL $ECHO ;Issue to display MVI A, "LF" ;Get line feed code CALL $ECHO ;Issue to display RE T

$ECHO 40 bytes 8443

Issues character in accumulator to display. 110 BAUD.

Code Label Op Code Comments

$ECHO DI ;Disable interrupts PUSH B ;Save BC PUSH D ;Save DE MOV C,A ;Make copy of character MVI A, CO ;Start bit mask MVI B,7 ;Set up counter NXTB IT : SIM ;Send a bit LXI D,04BC ;Set up delay time CALL $DELAY ;Execute delay MOV A,C ;Pick up bits left to send RAR ;Low order bit to carry MOV C,A ;Put rest back into C MVI A, 80 ;Shifted enable bit RAR ;Shift carry bit into accum. XRI 80 ;Complement data bit DCR B ;Decrement counter JP NXTBIT ;If ~ositivego send next bit MVI A, 40 ;Set up stop bits SIM ;Send stop bit LXI D, 1230 ;Delay time CALL $DELAY ;Execute delay POP D ;Restore saved POP B ;Registers E I ;Enable interrupts RE T $KEY 46 bytes 846 B

Reads in a character from the keyboard and leaves it in the

accumulator.

Code La be 1 Op Code Comments

$KEY : D I ;Disable interrupts PUSH B ;Save BC PUSH D ;Save DE WAIT : RIM ;Get input bit RAL ;Shift into carry bit JC WAIT ;Wait until start bit LXI D, 0246 ;Wait until middle CALL $DELAY ; of start bit LXI B, 07 ;Clear B and set C as counter NXTBIT : LXI D, 048C ;Wait until middle CALL $DELAY ; of next bit RIM ;Read in bit RAL ;Shift into carry MOV A,B ;Get partial result RAR ;Shift in next bit MOV B,A ;Store partial result DCR C ;Decrement counter JNZ NXTBIT ;If more bits loop UI D, 0918 ;Wait out stop bits CALL $DELAY MOV A,B ;Get result into accumulator STC ; Set carry CMC ;Complement carry RAR ;Put most significant bit = 0 POP D ;Restore registers POP B E I ;Enable interrupts RE T

$ DELAY 7 bytes

Decrements the DE register pair to zero then returns to calling program.

Code La be 1 Op Code Comments

$DELAY : DCX D ;Decrement counter MOV A,D ORA E ;Is counter zero? JNZ $DELAY ;Yes, jump back RET ;Else return SPSHAPU 10 bytes

Transfers top stack word onto top of 8231 stack.

Code Labe 1 Op Code Comments

SPSHAPU: POP D ;Save return address POP H ;Get top stack word MOV A,L OUT 80 ;Send out low byte MOV A,H OUT 80 ;Send out high byte PUSH D ;Restore re turn address RE T

SPOPAPU 10 bytes

Transfers top word on 8231 stack on top of data stack.

Code Op Code Comments

SPOPAPU: POP D ;Save return address IN 80 ;Get high byte MOV H,A IN 80 ;Get low byte MOV L,A PUSH H ;Put word on top of stack PUSH D ;Restore re turn address RE T SYSTEM

(TICK) 36 bytes 9A40

Scans the token following the ' (TICK) in the input buffer and

searches the CURRENT and CONTEXT vocabularies for the keyword

corresponding to the token. Returns the word address of the keyword as

the top stack entry if the keyword is located. If the keyword is not

found, the token is echoed to the operator followed by a question mark.

Code Labe 1 Op Code Comments

Data 1,"' 'I Link Address COLON ASPACE ;Get the sparator TOKEN ;Scan the next token CONTEXT ;Context address @ ;Contains address of @ ;Context vocabulary SEARCH ;Search the vocabulary *IF ;If false found, otherwise 20 ENTRY ;Get latest entry SEARCH ;Search current vocabulary *IF ;If false found, otherwise 2 0 QUESTION ;Echo token end? SEMI ;Word address on the stack

-+SP 14 bytes Adds the current stack pointer to the number at the top of the data

stack.

-Code Labe 1 Op Code Comments Data 3, "+SP1' Link address Code address POP H ;Get number on top of stack DAD SP ;Add stack pointer PUSH H ;Push onto stack JMP NEXT 22 bytes

Pops the top stack entry word and encloses it in the free dictionary space.

Code Label Op Code Comments

Data I,", " Link Address Code Address POP D ;Get top of stack word LHLD DP ;Get dictionary pointer addr. MOV M,E ;Store low bytes INX H ;Bump pointer MOV M,D ;Store high byte INX H ;Bump pointer SHLD DP ;Update DP pointer JMP NEXT

25 bytes 84FA

Subtracts the current stack pointer from the number at the top of the stack.

Code Label Op Code Comments

Data 3,"-SP" Link Address Code Address LXI H,OO ;Clear HL register pair DAD SP ;Load stack pointer into HL by adding MOV A,L ;Take the 2 ' s CMA ; complement of MOV L,A ; the HL register MOV A,H ; contents CMA MOV H,A INX H POP D ;Get top number on stack DAD D ;Add this to 2's comp. of SP PUSH H ;Push this result onto stack JMP NEXT

15 bytes 8513

Pushes to the stack the current return stack pointer. -?RS (continued)

Code La be 1 Op Code Comments

Data 3,"?RS1' Link Address Code Address LHLD RS ;Load HL with val of return SP PUSH H ;Push onto stack JMP NEXT

39 bytes 85 22

Pushes to the stack the address of the top stack entry prior to the execution ?SP. If underflow occurs, the stack is reset prior to the push.

Code La be 1 Op Code Comments

Data 3 ," ?SP" Link Address Code Address LXI H,00 ;Get data stack DAD SP ;Pointer address XCHG LXI H, STACK ;Get end of stack address MOV A,D ;Get 2's complement CMA ; of current stack MOV D,A ; pointer value MOV A,E CMA MOV E,A INX H DAD D ;Add 2's comp. SP to stack end JC OKAY ; address. If carry stack okay IXI H, STACK ;Otherwise initialize SPHL ; SP to beginning address OKAY : LXI H,OO DAD SP PUSH H JMP NEXT

ABORT 11 bytes 8544

Does an unconditional jump to the STARTIRESTARTroutine to re-

initialize the system and the stacks. ABORT (cont inued)

Code La be 1 Op Code Comments

Data 5 ,"ABO" Link Address Code Address JMP START ;TO START/EESTART

ASPACE (with TYPE in Outer Interpreter Routines Set)

BINARY 16 bytes

Sets the system number base to 2 decimal or the binary radix.

Code La be 1 Op Code Comments

Data 6 ,"BINt1 Link Address Code Address MVI A,02 ;Get 2 decimal STA BASE ;Store as new number base JMP NEXT

20 bytes 857 3

Pops the top stack word and encloses the low order byte in the dictionary .

Code Label Op Code Comments

Data 2,"C, " Link Address Code Address POP D ;Get top stack byte LHLD DP ;Get Dictionary Pointer MOV M,E ;Store byte INX H ;Bump pointer SHLD DP ;Update pointer JMP NEXT

-CA! 19 bytes 85 87

Stores the address at the top of the stack in the word address location of the latest entry in the CURRENT vocabulary, i.e.: the top stack entry is the code address of the keyword currently in the process of being defined. -CA! (continued)

Code La be 1 Op Code Comments

Data 3,"CA!" Link Address

COLON Y ENTRY ;Address of latest leader *C# 6 ;Literal 6 + ;Header + 6 equals word address ! ;Store code address SEMI

DE C IMAL 16 bytes

Sets the system variable BASE to 10 decimal to evoke decimal I/O.

Code Labe 1 Op Code Comments

Data 7 ,"DEC1' Link Address Code Address MVI A,OA ;Get 10 decimal STA BASE ;Store it as new number base JMP NEXT

DO, 14 bytes 85AA

Stores the program control directive at the top of the stack in the dictionary and returns the address of the next free dictionary location on the stack.

Code La be 1 Op Code Comments

Data 3,"DOY1' Link Address COLON , ;Store directive HE RE ;Push free address SEMI

END, 14 bytes 85 B8

Encloses the address of the program control directive at the top of the stack in the dictionary, stores the jump address in the dictionary. END, (continued)

Code Label Op Code Comments

Data 4 ,"END" Link Address COLON ;Store directive word address

9 ;Store jump address SEMI

ENTRY 16 bytes 85C6

Pushes to the stack the address of the first header byte of the latest entry in the CURRENT vocabulary.

Code Label Op Code Comments

Data 5"ENT" Link Address COLON CURRENT ;Current address G! ;Vocabulary address G! ;Header address SEMI

EXECUTE 12 bytes 8064

Pops the top stack entry to the word address register and jumps to the inner interpreter RUN routine to cause the execution of a keyword.

Code Label Op Code Comments

Data 7, "EXE" Link Address Code Address POP H ;Get keyword address JMP RUN ;Execute it

HE RE 15 bytes

Pushes the address of the next free dictionary location to the stack.

Code Label Op Code Comments

Data 4, "HER" Link Address Code Address LHLD DP ;Get dictionary pointer address PUSH H ;Put free location on stack JMP NEXT -HEX 16 bytes 85E5

Sets the system variable BASE to 16 decimal to evoke hexadecimal

Code Label Op Code Comments

Data 3 ,"HEX" Link Address Code Address MVI A, 10 ;Get 16 decimal STA BASE ;Store it as number base JMP NEXT

OCTAL 16 bytes 85F5

Sets the system variable BASE to 8 decimal to evoke octal 110.

Code La be 1 Op Code Comments

Data 5 ,"OCT1' Link Address Code Address MVI A,08 ;Get 8 decimal STA BASE ;Store it as number base JMP NEXT

SINGLE 27 bytes 8605

If the top stack entry is a valid 8-bit number (the high-order byte

is all zeroes or all ones), a False Flag is pushed to the stack.

Otherwise a True Flag is pushed to the stack.

Code Labe 1 Op Code Comments

Data 6 ,"SIN" Link Address Code Address POP H ;Get word PUSH H ;Restore word LXI D,OO ;Set flag false MOV A,H ;Move high byte ORA A ; to A to test JZ OVER ;Is high byte zero INR A ;No increment 57, OVER ;To see if FF INR E ;If not set flag true OVER : PUSH D ;Push flag JMP NEXT SYSTEM VARIABLE S

BASE 15 bytes

Pushes to the stack the address of the number base variable.

Code Op Code Commen t s

Data 4,"BAS" Link Address Code Address LXI H,2012 ;Get BASE address PUSH H ;Push onto stack JMP NEXT

COMPILER 15 bytes

Pushes to the stack the address of the compiler variable which points to the last entry in the COMPILER vocabulary.

Code Labe 1 Op Code Comments

Data 8,"COM" Link Address Code Address LXI H, 200E ;Get COMPILER address PUSH H ;Push onto stack JMP NEXT

CONTEXT 15 bytes

Pushes to the stack the address of the system context variable.

Code Label Op Code Commen t s

Data 7 ,I' CON" Link Address Code Address LXI H,200C ;Get CONTEXT address PUSH H ;Push to stack JMP NEXT

CURRENT 15 bytes 8662

Pushes to the stack the address of the current vocabulary variable. CURRENT (continued)

Code La be 1 Op Code Comments

07435552 Data 7 ,"CURt' Link Address Code Address LXI H,200A ;Get CURRENT address PUSH H ;Push to stack JMP NEXT

15 bytes 8671

Pushes to the stack the address of the dictionary pointer variable.

Code La be 1 Op Code Comments

Data 2 ,"DP I' Link Address Code Address LXI H,2006 ;Get DP address PUSH H ;Push to stack JMP NEXT

-LBP 15 bytes 8680 Pushes to the stack the address of the line buffer pointer variable.

Code Label Op Code Comments

Data 3 ,"LBPt' Link Address Code Address LXI H,2008 ;Get LBP address PUSH H ;Push to stack JMP NEXT

MODE 15 bytes

Pushes to the stack the address of the system mode variable.

Code Labe 1 Op Code Comments

044D4F44 Data 4,"MOD" Link Address Code Address LXI H,2011 ;Get MODE address PUSH H ;Push to stack JMP NEXT STATE 15 bytes 869E

Pushes to the stack the address of the system state variable.

Code Labe 1 Op Code Comments

Data 5 ," STA" Link Address Code Address LXI H, 2010 ;Get STATE address PUSH H ;Push to stack JMP NEXT PROGRAM CONTROL DIRECT I VE S

8 bytes

Gets the return stack pointer, pops the index byte from the stack, and then transfers to the "LOOP code to mechanize a non-unity indexed loop.

Code La be 1 Op Code Comments

"+ 2 LHLD RS ;Get return stack address POP D ;Get increment byte MOV A,E ; to the A register JMP $LOOP ;Jump to *LOOP code

10 bytes 86B7

Pops the top stack entry and increments the top return stack byte by the low-order byte from the stack. Control is then transferred to the %LOOP code to mechanize a non-unity byte indexed loop.

Code Label Op Code Comments

"+ 2 LHLD RS ;Get return stack address POP D ;Get increment byte MOV A,M ;Get loop count ADD E ;Add increment MOV M,A ;Restore loop count JMP $CLOOP ;Jump to SCLOOP code

-"DO 25 bytes Moves the top stack entry word (the loop start index) and the second stack entry word (loop terminating argument) to the return stack with the start index on top and the terminator as the second entry.

Op Code Comments

*+ 2 LHLD RS ;Get address of return stack LXI D ,FFFC ;Decrement stack DAD D ; ~ointerby 4 POP D ;Get start index MOV M,E ;Move to return INX H ; stack as top entry -*DO (continued)

Code Label Op Code Comments

MOV M,D INX H POP D ;Get terminator MOV M,E ;Move to return INX H ; stack as second entry MOV M,D XX H ;Reset return DCX H ; stack pointer DCX H SHLD RS ;Store return stack JMP NEXT

*CDO 17 bytes

Moves the low-order byte of the top stack entry (the loop start index) and the low-order byte of the second stack entry (the loop terminator argument) to the return stack with the start index as the top stack entry and the terminator as the second entry.

Code Label Op Code Comments

*+2 LHLD RS ;Get return stack DCX H ;Adjust return DCX H ; stack pointer POP D MOV M,E ;Get start index to return INX ; top POP D ;Get terminator to 2nd MOV M,E ; place on return stack DCX H ;Reset re turn stack SHLD RS ;Store return stack JMP NEXT

*LOOP 30 bytes

Increments the top return stack word by 1 and compares it to the second return stack word entry. If the second word is larger than the first, a jump to the $WHILE code occurs to implement a relative back- wards jump. Otherwise the top return stack entries are dropped and the instruction register is incremented by 2 to step past the jump address. *LOOP (continued)

Code Op Code Comments

*+2 LHLD RS ;Get return stack pointer MVI A,01 ;Get increment $LOOP : ADD M ;Increment index low MOV M,A ;Restore low index INX H ;Bump to index high JNC NOCAR ;Carry ? INR M ;Yes increment high byte NOCAR : MOV D,M ;Get high byte index INX H ;Bump to term low SUB M ;Index - term MOV A,D ;Index high to A INX H ;Bump to term high SBB M ;Index - term - carry JC $WHILE ;If carry = 1, jump back INX H ;Else drop index & terminator SHLD RS ; from return stack INX B ;Increment instruct ion INX B ; register past address JMP NEXT

*CLOOP 19 bytes

Increments the top return stack byte by 1 and compares it to the

second return stack byte entry. If the second byte is larger than the

first, a jump to the "xWHILE code occurs to implement a jump backwards.

Otherwise the top two return stack entries are dropped and the

instruction register is incremented by 2 to step past the jump address.

Controls byte loop termination.

Code Label Op Code Comments

*+2 LHLD RS ;Get return stack pointer INR M ;Increment index $CLOOP : MOV A,M ;Get index INX H ; pointer to terminator SUB M ;Index - terminator JC $WHILE ;If carry = 1, jump back INX H ;Else drop index SHLD RS ; and terminator INX B ;Increment IR past INX B ;jump address JMP NEXT -*IF 11 bytes If the top stack entry is 0, the instruction register is

incremented by the value whose address is in the instruction register

to implement a relative forward jump. Otherwise the instruction

register is incremented by 2 to step past the jump address.

Code Label Op Code Commen t s

*+2 POP H :Get the flag MOV A,L ;Is the flag ORA H ; set FALSE JZ $ELSE ;If 0 jump INX B ;Else bump IR INX B ; past jump address JMP NEXT

*ELSE 9 bytes

Performs a jump forward to the address whose address is in the

instruction register.

Code La be 1 Op Code Comments

*+2 $ELSE : LDAX R ;Get jump address MOV L,A ; pointed to by INX B ; instruction register LDAX B MOV B,A MOV C,L JMP NEXT

*END 11 bytes

If the stack entry is 0, the instruction register is loaded with an

address to perform a jump backwards. Otherwise the instruction

register is incremented by 2 to step past the jump address.

Code La be 1 Op Code Comments

*+2 POP H ;Get the flag MOV A,L ;Is the flag ORA H ; set FALSE JZ $WHILE ;If 0 jump *END (con t inued)

Code La be 1 Op Code Comments

INX B ;Else bump IR INX B ; past jump address JMP NEXT

*WHILE 9 bytes 874B

Loads the instruction register with the address whose address is in the current instruction register to implement a relative jump backwards.

Code Labe 1 Op Code Comments

4D87 *+2 0 A $WHILE : LDAX B ;Get jump address 6 F MOV L,A ; pointed to by 03 INX B ; instruction register 0 A LDAX R 47 MOV B,A 4D MOV C,L C34880 JMP NEXT ;Re t urn

17 bytes

Replaces top return stack word (the word loop index) with the second return stack word (the terminating argument) to force loop exit on the next word loop test.

Code Op Code Comments

*+2 LHLD RS ;Get return stack pointer LXI D,03 ;Adjust to point to DAD D ; the terminating argument MOV E,M ;Get byte EXH MOV D,M ;Get byte DCX H MOV M,E ;Store bytes where DCX H ; loop index was MOV M,D ; to force loop termination JMP NEXT 10 bytes 8769

Replaces the top return stack byte (the byte loop index) with the second return stack byte (the terminating argument) to force loop exit on the next byte loop test.

Code Labe 1 Op Code Comments

*+2 LHLD RS ;Get return stack pointer INX H MOV A,M ;Get terminator DCX H ;Store terminator MOV M,A ; in loop index JMP NEXT

DOES > 40 bytes

Replaces the first word in the code body of the latest entry in the

CURRENT vocabulary with the top return stack word and then replaces its code address with the second return stack entry.

Code Label Op Code Comments

Data 5,"WE" Link Address COLON R> ;Get top return address ENTRY ;Latest header address *C1/ 8 ;Plus 8 to point + ; to code body ! ;Store return to code body SCODE ;Replace code address & return LHLD RS ;Get return stack pointer DCX H ;Adjust RSP MOV M, B ;IR register to be DCX H ; pushed onto MOV M,C ; the return stack SHLD RS ;Store adjusted RSP XCHG ;Put word address into HL MOV C,M ;Get new instruction INX H ; register address MOV B,M ; at word address location INX H PUSH H ;Push pointer JMP NEXT NEXT 14 bytes

Encloses a jump to the inner interpreter NEXT routine in the dictionary .

Code Labe 1 Op Code Comments

Data 4,"NEX" Link Address COLON *#

Y SEMI

SCODE 8 bytes

Resets the code address of the latest keyword in the CURRENT vocabulary to the address at the top of the return stack.

Code Label Op Code Comments

COLON R> ;Get return address CA ! ;Store it as code address SEMI 110 ROUTINES

20 bytes

Pops the top stack entry, computes the quotient and remainder

relative to the system number base, converts the remainder to an ASCII

character, pushes the character, then pushes the quotient.

Code Op Code Comments

01232020 Data 1 ,"# " 9D87 Link Address 5580 COLON 3B86 BASE ;Get number base address C09 7 C@ ;Get number base 859 1 /MOD ;Remainder then quotient 6888 ASCII ;Remainder converted to ASCII DD96 SWAP ; character; quotient to top, 3C 80 SEMI ; ASCII character 2nd

-> 18 bytes 87CE

Pops the sign byte left on the return stack by <#, discards it and

then displays the string on the stack using the DISPLAY format

convention.

Code Label Op Code Comments

Data 2,"#> I' Link Address Code Address LHLD RS ;Drop top return INX H ;stack byte SHLD RS JMP DISPLAY ;Go display string

-{IS 22 bytes 87ED

Converts the top stack entry to a sequence of ASCII characters

equivalent to the entry given the current system number base.

Sequentially pushes the number characters with the most significant

character to the top stack entry. -#S (continued)

Code La be 1 Op Code Commen t s

Data 2,"IkS " Link Address COLON C087 NEXT : ik ;Convert 1 character BE 96 DUP ;Duplicate quotient 45 8E 0= ;Is it zero? --3E87 E887 *END NEXT ;If not next character EB96 DROP ;Drop 0 quotient 3C 80 SEMI

24 bytes 87F6

Pops the top stack entry, pushes an ASCII space code with its high order bit set to the stack, restores the top stack entry and copies the high-order byte to the re turn stack.

Code Label Op Code Comments

Data 2,"<1 " Link Address Code Address POP D ;Get number MVI L, A0 ;Space code with high bet set PUSH H ;Push string stop PUSH D ;Restore number LHLD RS ;Get return stack pointer DCX H ;Decrement pointer MOV M,D ;Store hi byte on return stack SHLD RS ;Restore return stack pointer JMP NEXT

20 bytes

Displays the top stack entry number to the operator in the current number base followed by a space. Destroys the top stack entry.

Code Op Code Comments

Data 1,". " Link Address COLON ;Display result SEMI -.R 36 bytes 8822

Displays the second stack number to the operator in the current

number base in a field width determined by the top stack entry. The

number is right adjusted in the field and followed by a space. The

field width is the minimum field width.

Code Label Op Code Comments

Data 2 ,If .R " Link Address COLON 2* ;Double character count - SP ;Subtract current stack pointer ;Get sign from temporary DROP ;Drop it R> ;Get temporary + SP ;Add current stack pointer PAD ;Add spaces if necessary DISPLAY ;Display result SEMI

14 bytes 8846

Displays to the operator in the current number base the word whose

address is the top stack entry.

Code Labe 1 Op Code Comments

Data I,"? " Link Address COLON @ ;Get the number ;Display it SEMI

14 bytes 8859

Displays to the operator in the current number base the byte whose

word address is the top stack entry. -C? (continued)

Code Label Op Code Comments

Data 2,"C? " Link Address COLON C@ ;Get the byte ;Display it SEMI

ASCII 24 bytes

Converts the low order byte of the top stack entry from a binary number to an ASCII code in the set 0 thru 9, A thru Z.

Code Label Op Code

Data 5 ,"ASC" Link Address Code Address $ASCII : POP H ;Get binary number MVI A, 30 ;Add 30 to make ASCII code ADD L CPI 3A ;Is it greater than ASCII 9? JC DIGIT AD1 7 ;Yes, add 7 to make it a char. DIGIT : MOV L,A ;Back to L PUSH H ;Store on stack JMP NEXT

DISPLAY 20 bytes

Outputs to the display the low-order byte of successive top stack entries until a non-ASCII code is output (a character with the sign bit set).

Code Op Code Comments

Data 7,"DIS" Link Address Code Address DISPLAY: POP H ;Get top stack word MOV A,L ; low byte CALL $ECHO ;Display it ORA L ;Test sign bit JP DISPLAY ;If positive loop JMP NEXT ;Else exit

-KEY (cont inued)

Code Labe 1 Op Code Comments

Data 3,"KEY" Link Address Code Address CALL $KEY ;Read entered key MOV L,A ;Move ASCII code to L XRA A ;Clear A MOV H,A ;Move to H PUSH H ;Push code to stack JMP NEXT

PART 35 bytes

Pops an address from the stack and displays eight numbers to the

operator from the 8 bytes following the initial address. The address

pointer is left on the stack.

Code Labe 1 Op Code Comments

Data 4 ,I' PAR" Link Address COLON SPACE ;Issue space to display *C1/ 8 ;Loop ending index *C# 0 ;Loop starting index %DO ;Initialize loop DUP ;Duplicate pointer c@ ;Get memory byte *C# 3 ;Set to display 3 characters .R ;Display numbers 1+ ;Increment memory pointer *CLOOP 5 ;Loop until done SEMI

SIGN 23 bytes

Pushes the ASCII code for a minus sign to the stack if the top byte

on the return stack is negative.

Code Labe 1 Op Code Comments

Data 4,"SIG" Link Address Code Address LHLD RS ;Get return stack address MOV A,M ;Get top word on return stack ANA A ;Set sign flag SIGN (continued)

Code Label Op Code Comments

F20B89 JP PLUS ;Is it positive? 21 2D00 LXI H,2D ;Load minus sign in HL E5 PUSH H ;Push onto stack C34880 PLUS : JMP NEXT

SPACE 16 bytes

Displays a space to the display.

Code Label Op Code Comments

Data 5, " SPA" Link Address Code Address MVI A, 20 ;Get ASCII space code CALL $ECHO ;Echo display it JMP NEXT

-PAD 30 bytes

Add spaces to fill out display field for formatted number fields.

Code Op Code Comments

03504144 Data 3," PAD" OE 89 Link Address 2689 Code Address E1 POP H ;Get bytes left in field 7 C LOOP : MOV A,H ;Move high byte to A A 7 ANA A ;Check to see if FA39 89 JM OUT ; negative. If negative jump B5 ORA L ;Else check to see if JZ OUT ; zero. If zero jump LXI D,20 ;Else push space D5 PUSH D ;Code onto stack 2 B DCX H ;Decrement byte counter 2B DCX H ; by 2 C32789 JMP LOOP ;Go loop again c 34880 OUT : JMP NEXT

CRE T 14 bytes 89 3C

Issues a carriage return and line feed to the display by calling

the $CRLF subroutine. CRET (continued)

Code La be 1 Op Code Comments

Data 4,"CREtt Link Address Code Address CALL $CRLF JMP NEXT

ADUMP 36 bytes 89 48

Does a memory dump taking the second stack entry as the starting

address and the top entry as the ending address. Displays a line

consisting of the address, eight ASCII characters, a space and eight more ASCII characters.

Code Labe 1 Op Code

Data 5 ,If ADUt' Link Address COLON OVER ;Put copy of start addr. on TOS *DO ;Initialize do loop BACK : CRE T ;Issue carriage re t .-line feed DUP ;Duplicate starting address *C# 4 ;Push 4 onto stack .R ;Display line address APART ;Issue first 8 characters APART ;Issue second 8 characters *Cf 10 ;Number of characters for index *+LOOP BACK ;Loop until reach ending addr. DROP ;Drop address pointer SEMI

APART 26 bytes 89 6E

Displays eight ASCII characters using the top stack word as a

pointer. The pointer is incremented with each character access.

Control characters are replaced with spaces.

Code Label Op Code Comments

Data 5 ,"APAtt Link Address COLON SPACE *C# 8 ;End of loop value APART (continued)

Code Op Code Comments

*C# 0 ;Initial loop counter value * CDO ;Initialize do loop BACK : DUP ;Duplicate pointer C@ ;Get memory byte CNTRL ;Make control char. displayable ECHO ;Display ASCII character SPACE ;Issue space to display 1+ ;Increment address pointer *CLOOP BACK ;Loop until done SEMI

CNTRL 26 bytes 89 BO

Takes top number from stack and sees if it is a control character.

If it is, it makes the character a space, otherwise it leaves it alone.

Code La be 1 Op Code Comments

Data 5 ,"CNTt' Link Address Code Address POP H ;Get character on top of stack MOV A,L ;Move character to accumulator CPI 20 ;Is it less than 20 DAC 489 JC CNTCHR ;Jump if it is FE 7E CPI 7E ;Else is it less than 79 DAC 6 89 JC PRNT ;Jump if it is 2E 20 CNTCHR : MVI L, 20 ;Make character a space PRNT : PUSH H ;Put character back on stack JMP NEXT

DELAY 17 bytes 899F

This routine performs a 1 second delay using the $DELAY subroutine.

Code La be 1 Op Code Comments

Data 5,"DEL" Link Address Code Address LXI D, 7D32 ;Set up counter CALL $DELAY ;Call delay subroutine JMP NEXT DOUBLE-WORD I/O ROUTINES (32-bit )

-D. 20 bytes 89 CA

Displays the top stack entry number to the operator in the correct number base followed by a space. Destroys the top stack entry.

Code Label Op Code Comments

Data 2,"D. I' Link Address COLON ;Display result SEMI

26 bytes 89 DE

Pops the top 32-bit stack entry, pushes an ASCII space code with its high order bit set to the stack. Restores the top stack entry and copies the high order byte to the return stack.

Code La be 1 Op Code Comments

033C4423 Data 3 ,I1

Converts the top stack entry to a sequence of ASCII characters equivalent to the entry given the current system number base, sequentially pushes the number characters with the most significant characters to the top stack entry.

Code La be 1 Op Code Comments

Data 3 ,"D#S1' Link Address COLON NEXT : D ;Convert one character 2DUP ;Duplicate quotient DO= ;Is it zero ? *END NEXT ;If not next character DROP ;Drop 0 quotient SEMI

-D# 27 bytes 8Al l

Pops the top 32-bit stack entry, computes the quotient and remainder to the system number base, converts the remainder to an ASCII character, pushes the character, then pushes the quotient.

Code Labe 1 Op Code Comments

Data 2,"DfI 'I Link Address COLON *CSI 0 ;Put zero on stack BASE ;Get number base address C@ ;Get number base D/MOD ;Divide number by number base ASC I I ;Convert remainder to ASCII SWAP ;Swap character and zero DROP ;Drop zero RROT ;Put quotient on top of stack SEMI -D.R 36 bytes

Displays the second stack entry (32-bit) to the operator in the

current number base in a field width determined by the top stack entry

(16-bit). The number is right adjusted in the field and followed by a

space. The field width is the minimum field width.

Code Label Op Code Comments

Data 3 ,"D.R1' Link Address COLON 2* ;Double character count -SP ;Subtract current stack pointer ;Get sign from temporary DROP ;Drop it R> ;Get temporary +SP ;Add current stack pointer PAD ;Add spaces if necessary DISPLAY ;Display result SEMI 328 bytes 8A50

This routine takes the floating point number at the top of the stack and displays it. The current number base is used in displaying the number. The number is displayed with one digit to the left of the decimal point and as many digits as needed to the right of the decimal

point. This is followed by an ^ and the exponent. The mantissa and exponent may have leading minus signs if necessary.

Code Label Op Code Comments

Data 2 ," .F I' Link Address Code Address CALL SPSHAPU ;Get number from data stack CALL SPSHAPU ; and put on APU stack IN 81 AN1 20 ;Is the number zero? JZ NONZER ;Jump if not zero MVI A, 30 ;Else output a zero CALL $ECHO ; to display JMP END.F ;Jump to end of routine NON ZER : MOV A,H RAL ;Is the number negative? JNC POS ;Jump if positive MVI A,2D ;Else output minus CALL $ECHO ; sign to display MVI A,15 ;Make floating point OUT 81 ; number positive POS : MVI E, 00 ;Initialize expon. counter to 0 SMALL : MVI A, 17 ;Duplicate number in APU OUT 81 ; stack MVI A,01 ;Push 1 onto the APU stack OUT 80 XRA A OUT 80 MVI A, ID ;Convert the 1 to floating OUT 81 ; point MVI A,11 ;Subtract 1 from copy OUT 81 ; of number BUSY 1 : IN 81 ;Read status register RAL ;Is busy flag set ? JC BUSY1 ;Jump if it is AN1 80 ;Else mask out sign flag JZ BIG ;Jump if number > 1 MVI A, 18 ;Else pop results off APU OUT 81 ; stack LDA BASE ;Push current number -.F (continued)

Code La be 1 OD Code Comments

D380 OUT 80 ; base onto the 8231 AF XRA A ; stack D380 OUT 80 3E 1D MVI A, 1D ;Convert to floating point D38 1 OUT 81 3E 12 MVI A, 12 ;Multiply number by base D38 1 OUT 81 1D DCR E ;Decrement exponent counter C37D8A JMP SMALL ;Loop back to see if > 1.0 3E 18 BIG : MVI A, 18 ;Pop results of comparison D38 1 OUT 81 ; off APU stack 3E17 LARGE : MVI A, 17 ;Duplicate number on D38 1 OUT 81 ; the APU stack LDA BASE ;Load the current OUT 80 ; number base onto AF XRA A ; the APU stack D380 OUT 80 3E1D MVI A, 1D ;Convert to floating point D38 1 OUT 81 3E 11 MVI A, 11 ;Subtract base from number D381 OUT 81 DB81 BUSY 2 : IN 81 ;Read APU status register 17 RAL ;Is busy flag set ? DAC B 80 JC BUSY2 ;Jump if it is E 680 AN1 80 ;Else mask out sign flag JNZ OKAY ;Jump if number < BASE MVI A, 18 ;Pop result of comparison OUT 81 ; off APU stack LDA BASE ;Load current number OUT 80 ; base onto APU stack XRA A OUT 80 MVI A, 1D ;Convert to floating point OUT 81 MVI A, 13 ;Divide number by the OUT 81 ; current base LNR E ;Increment exponent counter C3B78A JMP LARGE ;Loop back to see if < base 3E18 OKAY : MVI A, 18 ;Pop result of comparison OUT 81 ; off the APU stack CALL GETDIG ;Get next digit and display it CALL NUMO ;See if number is zero ANA A ;Set flags JNZ EXP ;Jump if number is zero MVI A, 2E ;Output decimal point

. - CALL $ECHO ; to display 341220 DISPLAY : LDA BASE ;Load current number ~380 OUT 80 ; base onto APU stack XRA A OUT 80 MVI A, 10 ;Convert to floating point -.F (continued)

Code Labe 1 Op Code Comments

OUT 81 MVI A, 12 ;Multiply number by base OUT 81 CALL GETDIG ;Get next digit and display it CALL NUMO ;See if number is zero ANA A ;Set flags CA018B JZ DISPLAY ;Jump if number is not zero 7B EXP : MOV A,E ;Move expon. counter to accum. A 7 ANA A ;Set flags JZ END.F ;Jump if exponent is zero MVI A, 5E ;Otherwise output CALL $ECHO ; to display MOV A,E ;Move exponent to accumulator RAL ;Shift sign bit into carry JNC POS ;Jump if exponent positive MVI A, 2D ;Otherwise output CALT, $ECHO ; minus sign to display MOV A,E ;Move exponent to accumulator 2 F CMA ;Take 2's complement 3C INR A 5F MOV E,A ;Place exponent back in E 7B POS : MOV A,E ;Get exponent and D380 OUT 80 ; place it on AF XRA A ; the APU stack D380 OUT 80 3E 1D MVI A, 1D ;Convert to floating point D38 1 OUT 81 3E17 ADJ: MVI A, 17 ;Duplicate exponent on D38 1 OUT 81 ; the APU stack 3E01 MVI A,01 ;Put 1 on APU stack D380 OUT 80 AF XRA A D380 OUT 80 3E1D MVI A, 1D ;Convert to floating point D38 1 OUT 81 3E 11 MVI A, 11 ;Subtract 1 from exponent D38 1 OUT 81 DB81 BUSY 3: IN 81 ;Read status register 17 RAL ;Shift busy flag into carry JC BUSY3 ;Jump if busy AN1 80 ;Else mask out sign flag JNZ DONE ;Jump if result negative MVI A, 18 ;Otherwise pop off result OUT 81 ; of comparison LDA BASE ;Place current number OUT 80 ; base on APU stack XRAA OUT 80 MVI A, 1D ;Convert to floating point OUT 81 MVI A, 13 ;Divide exponent by -.F (continued)

Code Label Op Code Comments

D38 1 OUT 81 ; number base C33D8B JMP ADJ ;Go back, see if exponent < 1 3E 18 DONE : MVI A, 18 ;Pop result of comparison D381 OUT 81 ; off the APU stack 3A1220 NXTEXP : LDA BASE ;Get current number D380 OUT 80 ; base and place AF XRA A ; on APU stack D380 OUT 80 3E1D MVI A, 1D ;Convert to floating point D38 1 OUT 81 3E12 MVI A, 12 ;Multiply exponent D381 OUT 81 ; by number base CALL GETDZG ;Get digit and display it CALL NUMO ;See if number is zero ANA A ;Set flags JZ NXTEXP ;Jump if not zero MVI A, 20 ;Output a space CALL $ECHO ; to the display JMP NEXT ;Exit

$ADDAD J 21 bytes

Adds 0.0000001 to number on top of APU stack.

Code Labe 1 Op Code Comments

3ED0 ADD. J MVI A, DO ;Move 0.0000001 D380 OUT 80 ;Stack 3EBF MVI A, BF D380 OUT 80 3ED6 MVI A, D6 D380 OUT 80 3E 69 MVI A, 69 D380 OUT 80 3E 10 MVI A, 10 ;Add to data stack value D38 1 OUT 81 C 9 RE T

GE TD IG 40 bytes 8B98

This routine takes the number on APU and converts it to an integer. This number is converted to corresponding ASCII code and output to the display. The integer is then subtracted from the original number to set up for next time. GETD IG (continued)

Code Label Op Code

GE TD IG : MVI A,17 ;Duplicate number on OUT 81 ; the APU stack CALL ADD. 5 ;Add 0.5 to number MVI A, 1F ;Convert number to OUT 81 ; 16-bit integer MVI A, 77 ;Duplicate integer on OUT 81 ; the APU stack IN 80 ;Read top byte and discard IN 80 ;Read digit AD1 30 ;Convert to ASCII digit code CPI 3A ;Is it < "9" JC OUTPUT ;Jump iT it is AD1 7 ;Else convert to alphab. char. OUTPUT : CALL $ECHO ;Output digit to display MVI A, 1D ;Convert integer to OUT 81 ; floating point MVI A, 11 ;Subtract it from number OUT 81 RE T

NUMO 44 bytes

This routine determines if the number on the APU is small enough to - 6 be considered zero. This point occurs when the number is -< 2.0 x 10 .

If the number is zero a true flag is left in the accumulator, else a false flag is left in the accumulator.

Code Label Op Code Comments

MVI D,OO ;Set flag false MVI A,17 ;Duplicate number OUT 81 ;on APU stack MVI A, CA ;Push 0.000002 to OUT 80 ;the APU stack MVI A,37 OUT 80 MVI A, 86 OUT 80 MVI A, 6E OUT 80 MVI A,11 ;Subtract it from number OUT 81 BUSY : IN 81 ;Read status register RAL ;Shift busy into carry JC BUSY ;Jump if busy AN1 80 ;Else mask out sign flag JZ OVER ;Jump if flag not set NUMO (continued)

Code Label Op Code Comments

14 INR D ;Else set flag true 3E18 OVER : MVI A, 18 ;Pop result of subtraction D38 1 OUT 81 ; off APU stack 7A MOV A,D ;Move flag to accumulator C 9 RE T DEFINING WORDS

: (Colon) 30 bytes

Sets the CONTEXT vocabulary equal to the CURRENT vocabulary, creates a secondary header for the token following ":I' in the input buffer and links it to the CURRENT vocabulary and sets the mode to the compile mode.

Code La be 1 Op Code Comments

Data 1,": " Link Address COLON CURRENT ;Current address @ ;Contains vocabulary address CONTEXT ;Context address ! ;Lead current into context CREATE ;Create primitive header *# ;Address of COLON ;Colon routine CA! ;Replace code address MODE ;Mode address ClSET ;Set compile mode {Mode = 1) SEMI

Creates a CONSTANT keyword definition with an initial value of 0.

The keyword name is the next token available in the input buffer when

Code tabe 1 Op Code Comments

Data 7 ,"

CCONSTANT 27 bytes 8BF5

Creates a byte constant keyword dictionary entry whose name is the token following CCONSTANT and whose value equals the low-order byte of the top stack entry. CCONSTANT (continued)

Code La be 1 Op Code Comments

Data 9 "CCO" Link Address COLON CREATE ;Create primitive c, ;Store byte to body SCODE ;Replace code address LDAX D ;Get byte in code body MOV L,A ; to L register MVI H, 00 ;Assume positve UL ;Test sign bit JNC POS ;If zero positive DCR H ;Else make negative E 5 POS : PUSH H ;Push word onto stack C34880 JMP NEXT ;Jump next routine

22 bytes

Creates a word-length constant keyword dictionary entry whose name is the token following CONSTANT and whose value equals the top stack entry.

Code Op Code Comments

Data 8 ,"CON" Link Address COLON CREATE ;Create primitive header

9 ;Store number in code body SCODE ;Replace code address XCHG ;Word address to HL MOV E,M ;Get low byte in code body INX H ;Bump pointer MOV D,M ;Get high byte in code body PUSH D ;Push number onto stack JMP NEXT

CREATE 39 bytes

Creates a dictionary header for a primitive keyword whose name is the token following CREATE and links it to the CURRENT vocabulary.

Code Label Op Code Comments

06435245 Data 6,"CRE" 10 8C Link Address 5580 COLON CREATE (continued)

Code Labe 1 Op Code Comments

ENTRY ;Pointer to latest header ASPACE ;Set the separator TOKEN ;Token to dictionary space HE RE ;Points to the token CURRENT ;Address of current token @ ;Vocabulary link ! ;Update link to new token *Ci/ 04 ;4 identifier characters DP ;Dictionary pointer +! ; enclose 4 characters > ;Add link address to new header HERE ;Word address of new header 2 + ;Points to code body > ;Store at word address SEMI

CVAR IABLE 16 bytes 8C 4D

Creates a byte variable keyword dictionary entry whose name is the token following CVARIABLE and whose initial value is the low-order byte of the entry popped from the stack. -Code Label Op Code Comments Data 9 ,"CVAtt Link Address COLON CCONSTANT ;Create header and initialize SCODE ;Replace code address and exit PUSH D ;Push word address JMP NEXT ;Jump to next routine

VARIABLE 16 bytes 8C5D

Creates a word-length variable dictionary keyword entry whose name is the token following VARIABLE and whose initial value is the value popped from the stack. -Code Label Op Code Comments Data 8 , VAR" Link Address COLON CONSTANT ;Create header and SCODE ;Initialize VARIABLE ( con t inued )

Code Labe 1 Op Code Comments

PUSH D ;Push word address JMP NEXT ;Jump to NEXT routine

VOCABULARY 22 bytes 8C6D

Creates a vocabulary keyword dictionary entry whose name is the token following VOCABULARY with an initial link to the latest entry in the CURRENT vocabulary and which, when the vocabulary name is executed, sets the system variable CONTEXT to the link address.

Code Labe 1 Op Code Comments

Data 10 ,"VOCtt Link Address COLON

9 ;Store in body DOES > ;Reset code address,body & exit CONTEXT ;Get context address ! ;Store link to context SEMI

2CONSTANT 30 bytes

Creates a double-length constant keyword dictionary entry whose name is the token following CONSTANT and whose value equals the top 2 stack entries.

Code La be 1 Op Code Comments

Data 9,"2C01' Link Address COLON CREATE ;Create primitive ;Store lo order !I in code body , ;Store hi order /I in code body SCODE ;Replace code address XCHG ;Word address to HL MOV E,M ;Get low-order INX H ; word in code MOV D,M ; body INX H 2CONSTANT (continued)

Code Label Op Code Comments

MOV A,M ;Get high-order INX H ; word in code NOV H,M ; body MOV L,A PUSH H ;Push high-order to stack PUSH D ;Push low-order to stack JMP NEXT

2 VAR IABLE 16 bytes 8CA 1

Creates a double-length word variable keyword dictionary entry whose name is the token following VARIABLE and whose initial value is the value popped from the stack. -Code La be 1 Op Code Comments Data 9,"2VAW Link Address COLON 2CONSTANT ;Create header and initialize SCODE ;Replace code address PUSH D ;Push word address JMP NEXT ;Jump to NEXT routine LITERAL HANDLERS

12 bytes 8CB 1

Pushes to the stack the word whose address is in the instruction register and increments the instruction register by 2.

Op Code Comments

*/I : 000 2 ;Code Address LDAX B ;Get byte at IR MOU L,A ;Move it to L INX B ;Bump IR pointer LDAX B ;Get byte at IR MOV H,A ;Move it to H INX B ;Bump IR pointer PUSH H ;Push word onto stack JMP NEXT

16 bytes 8CBD

Pushes to the stack the byte whose address is in the instruction register and increments the instruction register one past the byte.

Code Labe 1 Op Code Comments

"CSk : 0002 ;Code Address LDAX B ;Get byte at IR INX B ;Bump IR pointer MOV L,A ;Move byte to L MVI H, 00 ;Set high byte to zero RAL ;Is the byte a JNC POS ; negative number ? DCR H ;Yes, set high byte negative POS : PUSH H ;Push onto stack JMP NEXT

17 bytes 8CCD

Uses the instruction register as a pointer to a string embedded in the threaded code. Extracts the string length from the first byte pointed to by the IR and outputs that many characters to the display.

Leaves the IR pointing to the first byte past the string. -*[ (continued)

Code Labe 1 Op Code Comment s

CF 8C *[: 0002 ;Code Address 0A LDAX B ;Get string length 5 F MOV E,A ;Put length into reg. E 0 3 LOOP : INX B ;Bump IR pointer 0A LDAX B ;Get character CD4384 CALL $ECHO ;Echo character to display 1D DCR E ;Decrement length JNZ LOOP ;Loop until length is zero INX B ;Adjust instruction register JMP NEXT CONVERSION ROUTINES

-S-D 23 bytes 8CDE

Takes the top 16-bit number on the stack and sign extends it to

32-bits and replaces it back on the stack.

Code Labe 1 Op Code Comments

Data 3 ,"S-D" Link Address Code Address LXI D,OO ;Sign extend assume positive POP H ;Get top word on stack MOV A,H ;Get high byte of word RAL ;Shift sign bit into carry JNC POS ;Jump if positive DCX D ;Change to sign extend negative POS : PUSH D ;Push sign extend word PUSH H ;Push original word JMP NEXT

-S-F 24 bytes Takes the top 16-bit number on the stack and converts it to

floating point representation.

Code Labe 1 Op Code Comments

03532D46 Data 3 ,"S-F" DE 8C Link Address FD 8C Code Address CDA084 CALL SPSHAPU ;Get top 8231 stack word stack 3E 1D MVI A, 1D ;Convert 16-bit number n381 OUT 81 ; to floating point CDAA 84 CALL $POPAPU ;Put floating point number CDAA84 CALL SPOPAPU ; back on top of data stack C34880 JMP NEXT

-D-S 14 bytes Takes the top 32-bit number on the stack and converts it to 16-bits

by dropping the high order word.

Code Op Code Comments

Data 3,"D-S" ink Address Code Address POP H ;Get low order word -D-S (con t inued )

Code La be 1 Op Code Comments

POP D ;Get high order word PUSH H ;Push low order word JMP NEXT

27 bytes

Takes the top 32-bit number on the stack and converts it to floating point representation and replaces it on top of the stack.

Code Labe 1 Op Code Comments

03442D46 Data 3 ," D-F" OD8D Link Address 238D Code Address CDA084 CALL SPSHAPU ;Get top word from data stack CDA084 CALL SPSHAPU ;and place on top of 8231 stack 3E 1C MVI A, 1C ;Convert to floating point D38 1 OUT 81 CDAA84 CALL SPOPAPU ;Push floating point number CDAA 84 CALL SPOPAPU ;onto top of data stack C34880 JMP NEXT

-F-S 24 bytes 8D36

Takes the top floating point number on the stack and converts it to a 16-bit integer. This routine truncates the fractional part of the floating point number. It puts the 16-bit integer on top of data stack.

Code Labe 1 Op Code Comments

03462D53 Data 3 ,"F-S" 1B8D Link Address 3E 8D Code Address CDA084 CALL SPSHAPU ;Get floating point number CDA084 CALL SPSHAPU ; and put on 8231 stack 3E1F MVI A, 1F ;Convert to 16-bit integer D381 OUT 81 C DAA 84 CALL SPOPAPU ;Push integer to top of stack C34880 JMP NEXT -F-D 27 bytes 8D4E Takes the top floating point number on the stack and converts it to a 32-bit integer. This routine truncates the fractional part of the floating point number. It puts the 32-bit integer on top of the data stack.

Code Labe 1 Op Code Comment s

03462D44 Data 3 ,"F-Dl' -368D Link Address 568D Code Address CDA084 CALL $PSHAPU ;Get floating point number CDA084 CALL SPSHAPU ; and put on 8231 stack 3E 1E MVI A, 1E ;Convert to 32-bit integer D38 1 OUT 81 C DAA 84 CALL $POPAPU ;Push 32-bit integer on CDAA~~ CALL SPOPAPU ; top of data stack C34880 JMP NEXT LOGICAL OPERATORS

AND 20 bytes

Logical AND of top 2 words on stack. Result placed on stack.

Code Op Code Comments

Data 3 ,"AND1' Link Address Code Address POP H ;Get top word POP D ;Get 2nd word MOV A,H ;AND high bytes ANA D MOV H,A ;Back to H MOV A,L ;AND low bytes ANA E MOV L,A ;Back to L PUSH H ;Push result to stack JMP NEXT

20 bytes 8D7D

Logical OR of top 2 words on stack. Result placed on stack.

Code Label Op Code Comments

Data 2,"OR I' Link Address Code Address POP H ;Get top word POP D ;Get 2nd word MOV A,H ;OR high bytes ORA D MOV H,A ;Back to H MOV A,L ;OR low bytes ORA E MOV L,A ;Back to L PUSH H ;Push result to stack JMP NEXT

20 bytes 8D9 1

Logical Exclusive OR of top two words on stack. Result placed on

the stack.

Code Label Op Code Comments

Data 3 ,"XOR" Link Address Code Address -XOR (continued)

Code La be 1 Op Code

POP H ;Get top word POP D ;Get 2nd word MOV A,H ;XOR high bytes XRA D MOV H,A ;Back to H MOV A,L ;XOR low bytes XRA E MOV L,A ;Back to L PUSH H ;Push result to stack JMP NEXT

-NOT 19 bytes

Takes 1's complement of top word on stack.

Code Label Op Code Comments

Data 3 ,"NOTv' Link Address Code Address POP H ;Get top word MOV A,H ;Complement CMA ; high byte MOV H,A ;Back to H MOV A,L ;Complement CMA ; low byte MOV L,A ;Back to L PUSH H ;Push result to stack JMP NEXT COMPARISON OPERATORS (S ingle-~ength)

27 bytes 8DB8

Returns a true "1" if the top 2 stack entries are equal, returns a

false 0 otherwise. Top 2 entries are popped off and lost.

Code Labe 1 Op Code Comments

Data I,"= " Link Address Code Address POP H ;Get TOS POP D ;Get NOS MOV A,L ;Subtract low byte of SUB E ;NOS from TOS MOV L,A ;Store result MOV A,H ;Subtract high byte of SBB D ;NOS from TOS ORA L ;OR results of subtractions LXI H, 00 ;Set flag false JNZ NON ;Jump if result nonzero INX H ;Otherwise set flag true NON : PUSH H ;Push flag on stack JMP NEXT

22 bytes 8DD3

Returns a true "1" if the top 2 stack entries are not equal,

returns a false 0 otherwise. Top 2 entries are popped off and lost.

Code Labe 1 Op Code Comments

Data 2,"<> " Link Address Code Address POP H ;Get TOS POP D ;Get NOS MOV A,E ;Take 2's complement CMA ; of NOS MOV E,A MOV A,D CMA MOV D,A INX D DAD D ;Subtract NOS from TOS PUSH H ;Push result as flag JMP NEXT 25 bytes

Returns a true "1" if the top stack entry is less than the 2nd entry, returns a false "0" otherwise. Top 2 entries are popped off and

lost.

Code Op Code Comments

Data I,"< " Link Address Code Address POP H ;Get top number POP D ;Get 2nd number MOV A,L ;Subtract 2nd SUB E ; number from MOV A,H ; top number SBB D LXI H,OO ;Set Flag false JP N< ;Is top < 2nd INR L ;Yes, set flag true PUSH H ;Push flag to stack JMP NEXT

25 bytes

Returns a true "1" if the top stack entry is greater than the 2nd entry, returns a false "0" otherwise. Top 2 entries popped off and

lost.

Code Label Op Code Comments

Data 1,"> " Link Address Code Address POP H ;Get top number POP D ;Get 2nd numbe r MOV A,E ;Subract top SUB L ; number from MOV A,D ; 2nd number

SBB H Y LXI H,OO ;Set flag false JP N> ;Is top > 2nd TNR L ;Yes, set flag true N>: PUSH H ;Push flag to stack JMP NEXT 20 bytes 8E03

Unsigned comparison of top 2 stack entries. Returns a True "1" if top is less than the 2nd, otherwise returns a False "0".

Code Label Op Code Comments

Data 2 ,"U< " Link Address COLON ABS ;Absolute value of NOS SWAP ;Swap TOS and NOS ABS ;Absolute value of TOS SWAP ;Swap TOS and NOS stack < ;Is TOS < NOS SEMI

22 bytes 8E 3F

If top stack entry is 0 a true flag is pushed on stack, otherwise a

false flag is put on stack. Can be used to complement flag already on stack.

Code Label Op Code Comments

02303D20 Data 2,"0= I' 268E Link Address 47 8E Code Address E1 POP H ;Get top number 7C MOV A,H ;Set Z flag B 5 ORA L ;if number is zero 23 INX H ;If zero sets flag true 0+1=1 CA518E JZ ZERO ;Is top of stack zero? 210000 LXI H,00 ;No, set flag false E5 ZERO : PUSH H ;Push flag to stack C34880 JMP NEXT

-0 < 22 bytes 8E55

Returns a true "1" if the top stack number is negative, otherwise

returns a false "0". Top number is popped off and lost.

Code Labe 1 Op Code Comments

Data 2,"0< " Link Address Code Address POP H ;Get top word MOV A,H ;Rotate sign -O< (continued)

Code Label Op Code Comments

RAL ; into carry LXI H,OO ;Set flag false JNC POS ;Is top < 0 INR L ;Yes, set flag true E 5 POS : PUSH H ;Push flag to stack C34880 JMP NEXT

-0> 22 bytes 8E6B

Returns a true "1" if the top number is positive, otherwise returns a false "0". Top number is popped off and lost.

Code Label Op Code Comments

Data 2 ,"0> " Link Address Code Address POP H ;Get top word MOV A,H ;Rotate sign RAL ; into carry LXI H, 00 ;Set flag false DA7D8E INR L ;Yes, set flag true E 5 NEG : PUSH H ;Push flag to stack JMP NEXT

-MIN 23 bytes 8E 81

Pops the top 2 numbers off the stack and places the minimum number back on the stack.

Code Labe 1 Op Code Comments

Data 3 ,"MIN" Link Address Code Address POP H ;Get top POP D ;Get 2nd PUSH H ;Assume top smaller MOV A,E ; subtract top SUB L ; number from MOV A,D ; 2nd number SBB H JP EXIT ;2nd number smaller? POP H ;Yes, drop top D5 PUSH D ;Push 2nd C34880 EXIT : JMP NEXT -MAX 23 bytes Pops the top 2 numbers off the stack and places the maximum number back on the stack.

Code Labe 1 Op Code Comments

Data 3 ,"MAX" Link Address Code Address POP H ;Get top POP D ;Get 2nd PUSH H ;Assume top greater MOV A,L ; subtract 2nd SUB E ; number from MOV A,H ; top number SBB D JP EXIT ;2nd number larger? POP H ;Yes, pop top PUSH D ;Push 2nd EXIT : JMP NEXT COMPARISON OPERATORS (Double -~ength )

37 bytes

Returns a True "1" if the 32-bit number on top of the stack is zero, otherwise returns a False "0".

Code Labe 1 Op Code Comments

Data 3 ,"DO=" Link Address Code Address CALL SPSHAPU ;Get top 32-bit number CALL SPSHAPU ; and place on top 8231 stack LXI D,OO ;Set flag to false 3E37 MVI A, 37 ;Push top of 8231 stack D38 1 OUT 81 ; to set flags DB 8 1 WAIT : IN 81 ;Read status register 17 RAL ;Shift busy flag into carry DAC 48E JC WAIT ;While busy jump E640 AN1 40 ;Else mask out zero flag CAD08E JZ NONZER ;Is zero flag set? 1C IN'R E ;Yes, set flag true D5 NON ZER : PUSH D ;Push flag to top of stack C34880 JMP NEXT

-D= 43 bytes Returns a True "1" if the top 2 double-words on the stack are equal, returns a False "0" otherwise. The 2 entries are dropped and the flag is placed on top of stack.

Code Labe 1 Op Code Comments

02443D20 Data 2,"D= " Link Address Code Address CALL $PSHAPU ;Get top number and CALL $PSHAPU ;Put on 8231 stack CALL $PSHAPU ;Get 2nd number and CALL $PSHAPU ; put on 8231 stack 3E 2D MVI A, 2D ;Subtract 2nd number from D38 1 OUT 81 ; top of stack 110000 LXI D,OO ;Set flag false DB81 WAIT : IN 81 ;Read status register 17 RAL ;Shift busy into carry DAE F8E JC WAIT ;While busy jump E 640 AN1 40 ;Else mask out zero flag CAF B 8E 57, NEQL ;Is zero flag set? 1C INR E ;Yes, set flag true D 5 NEQL : PUSH D ;Push flag to data stack C34880 JMP NEXT 43 bytes 8EFF

Returns a True "1" if the top stack double-word is less than the

2nd stack double-word, otherwise returns a False "0".

Code La be 1 Op Code Comments

Data 2,"D< " Link Address Code Address CALL $PSHAPU ;Get top 32-bit number and CALL $PSHAPU ; place on top of 8231 stack CALL SPSHAPU ;Get 2nd 32-bit number and CALL $PSHAPU ; place on top of 8231 stack MVI A, 2D ;Subtract 2nd number from OUT 81 ; top of stack LXI D,OO ;Set flag false WAIT : IN 81 ;Read status register RAL ;Shift busy into carry JC WAIT ;While busy jump ANI 80 ;Else mask out sign flag JZ OUT ;Is sign flag set INR E ;Yes set flag true OUT : PUSH D ;Push flag to data stack JMP NEXT

43 bytes 8F5D

Returns a True "1" if the top stack double-word is greater than the

2nd stack double-word, otherwise returns a False "0".

Code La be 1 Op Code Comments

Data 2,"D> " Link Address Code Address CALL $PSHAPU ;Get top number and CALL SPSHAPU ; put on 8231 stack CALL $PSHAPU ;Get 2nd number and CALL $PSHAPU ; put on 8231 stack MVI A, 2D ;Subtract 2nd from top OUT 81 LXI D,OO ;Set flag to false WAIT : IN 81 ;Read 8231 status register RAL ;Shift busy bit into carry JC WAIT ;Is 8231 Busy? AN1 CO ;No, mask out sign & zero bits JNZ OUT ;Is top > 2nd? INR E ;Yes set flag true OUT : PUSH D ;Push flag to stack JMP NEXT 20 bytes

Unsigned comparison of top 2 stack entries. Returns ,a True "1" if top is less than 2nd, otherwise returns a False "0".

Code Label Op Code Comments

03445536 Data 3, "DU<" Link Address COLON DABS ;Take absolute value TOS 2SWAP ;Swap TOS and NOS DABS ;Take absolute value NOS 2SWAP ;Swap TOS and NOS back D < ;TOS less than NOS SEMI

DMIN 61 bytes

Pops the top 2 double-words off the stack and places the smaller number back on the stack.

Code Labe 1 Op Code Comments

Data 4,"DMI" Link Address Code Address CALL $PSHAPU ;Get top number and CALL $PSHAPU ; put on top of 8231 stack MVI A,37 ;Make a copy of it OUT 81 CALL $PSHAPU ;Put 2nd number on POP D ; top of 8231 stack MOV A,E ; and keep a copy OUT 80 ; in CPU register MOV A,D ;DE HL OUT 80 MVI A, 2D ;Subtract 2nd from top OUT 81 WAIT : IN 81 ;Read 8231 status register RAL ;Shift busy into carry JC WAIT ;Is 8231 busy? AN1 co ;No, mask out sign & zero flag JZ OUT ;Is top < 2nd MVI A, 38 ;Pop result off 8231 OUT 81 ; stack CDAA84 CALL SPOPAPU ;Yes, put top back on CDAA 84 CALL SPOPAPU ; the top of the stack c 34880 JMP NEXT ;EXIT OUT : PUSH D ;No, put 2nd back on PUSH H ; the top of the stack JMP NEXT ;EXIT DMAX 61 bytes 8FC 5

Pops the top 2 double-words off the stack and places the larger number back on the stack.

Code Labe 1 Op Code Comments

Data 4," DMA" Link Address Code Address CALL SPSHAPU ;Get top number and CALL SPSHAPU ; put on top of 8231 stack MVI A,37 ;Make a copy of it OUT 81 CALL $PSHAPU ;Put 2nd number on POP D ; top of 8231 stack 7 B MOV A,E ; and keep a copy D380 OUT 80 ; in CPU registers 7A MOV A,D ; DE HL D380 OUT 80 3E 2D MVI A, 2D ;Subtract 2nd from top D381 OUT 81 DB81 WAIT: IN 81 ;Read 8231 status register 17 RAL ;Shift busy into carry DAE 58F JC WAIT ;Is 8231 busy? E6CO AN1 CO ;No, mask out sign & zero flag JNZ OUT ;Is top > 2nd MVI A, 38 ;Pop result off 8231 D381 OUT 81 ; stack CDAA 84 CALL SPOPAPU ;Put top back on CDAA 84 CALL SPOPAPU ; data stack C34880 JMP NEXT ;Exit D 5 OUT : PUSH D ;No, put 2nd back on E 5 PUSH H ; the stack C34880 JMP NEXT ;Exit COMPARISON OPERATORS (Floating point)

43 bytes 900 2

Returns a True "1" if the top 2 floating point numbers on the stack are equal, returns a False "0" otherwise. The 2 entries are dropped and the flag is placed on top of the stack.

Code Labe 1 Op Code Comments

Data 2, "F= Link Address Code Address CALL SPSHAPU ;Get top number and CALL SPSHAPU ; put on 8231 stack CALL SPSHAPU ;Get 2nd number and CALL SPSHAPU ; put on 8231 stack MVI A,11 ;Subtract 2nd from top OUT 81 LXI D,OO ;Set flag false WAIT : IN 81 ;Read 8231 status register RAL ;Shift busy into carry JC WAIT ;While busy jump AN1 40 ;Else mask out zero flag JZ NEQL ;Is zero flag set? INR E ;Yes, set flag true NEQL : PUSH D ;Push flag to data stack JMP NEXT

-F< 43 bytes 902D

Returns a True "1" if the top stack floating point number is less than the 2nd stack floating point number, otherwise returns a False "0".

Code Labe 1 Op Code Comments

Data 2, "F< " Link Address Code Address CALL SPSHAPU ;Get top number CALL $PSHAPU ; and put on 8231 stack CALL $PSHAPU ;Get 2nd number CALL SPSHAPU ; and put on 8231 stack MVI A,11 ;Subtract 2nd number D38 1 OUT 81 ; from top number 11 0000 LXI D,OO ;Set flag false DB81 WAIT IN 81 ;Read status register 17 RAL ;Shift busy into carry DA4890 JC WAIT ;While busy jump ~.680 AN1 80 ;Else mask out sign flag CA5490 JZ OUT ;Is sign flag set F< (continued)

Code Op Code Comments

1C INR E ;Yes set flag true D5 OUT : PUSH D ;Push flag to data stack C34880 JMP NEXT

43 bytes 90 5 8

Returns a True "1" if the top stack floating point number is

greater than the 2nd stack floating point number, otherwise returns a

False "0".

Code Label Op Code Comments

Data 02 ,"F> I' Link Address Code Address CALL SPSHAPU ;Get top number and CALL SPSHAPU ; put on 8231 stack CALL SPSHAPU ;Get 2nd number and CALL SPSHAPU ; put on 8231 stack MVI A,11 ;Subtract 2nd number OUT 81 ; from top number LXI D, 00 ;Set flag false WAIT : IN 81 ;Read 8231 status register RAL ;Shift busy into carry JC WAIT ;While busy wait AN1 co ;Else mask out sign, zero flags JNZ OUT ;Is top > 2nd ? INR E ;Yes, set flag true OUT: PUSH D ;Push flag to stack JMP NEXT

FM IN 61 bytes

Pops the top 2 floating point numbers off the stack and places the

smaller number back on the stack.

Code Label Op Code Comments

Data 4, "MI" Link Address Code Address CALL SPSHAPU ;Get top number and CALL SPSHAPU ; put on 8231 stack MVI A, 17 ;Make a copy of it OUT 81 CALL SPSHAPU ;Put 2nd number on FMIN (continued)

Code Label Op Code Comments

D 1 POP D ; 8231 stack and 7B MOV A,E ; keep a copy D380 OUT 80 ; in CPU registers 7A MOV A,D ; DE, HL D380 OUT 80 3Ell MVI A, 11 ;Subtract 2nd from top D381 OUT 81 DB81 WAIT : IN 81 ;Read 8231 status register 17 RAL ;Shift busy into carry DAA 39 0 JC WAIT ;Is 8231 busy? E6CO AN1 CO ;No,mask out sign & zero flags CABB90 JZ 2ND ;Is top < 2nd 3E18 MVI A,18 ;Pop result off 8231 stack D381 OUT 81 CDAA84 CALL SPOPAPU ;Put top back on top CALL SPOPAPU ; of data stck JMP NEXT ;EXIT 2ND : PUSH D ;Put 2nd back on top PUSH H ; of data stack JMP NEXT ;EXIT

FMAX 61 bytes

Pops the top 2 floating point numbers off the stack and places the

larger number back on the stack.

Code Label Op Code Comments

Data 4,"FMA" Link Address Code Address CDA084 CALL SPSHAPU ;Get top number and ~~A084 CALL SPSHAPU ; put on top of 8231 stack 3E17 MVI A, 17 ;Make a copy of it D381 OUT 81 CDA084 CALL $PSHAPU ;Put 2nd number on D 1 POP D ; top of 8231 stack and 7B MOV A,E ; keep a copy of it D380 OUT 80 ; in the CPU registers 7A MOV A, D ; DE, HL D380 OUT 80 3Ell MVI A, 11 ;Subtract 2nd from top D381 OUT 81 DB81 WAIT: IN 81 ;Read 8231 status register 17 RAL ;Shift busy into carry DAE 09 0 JC WAIT ;Is 8231 busy? AN1 co ;No, mask out sign & zero flags JNZ 2ND ;Is top > 2nd FMAX (con t inued)

Code Label Op Code Comments

MVI A, 18 ;Yes, pop result off 8231 OUT 81 ; stack and CALL $POPAPU ;Put top back on top CALL $POPAPU ; of data stack JMP NEXT ;EXIT PUSH D ;Put 2nd back on PUSH H ; top of data stack JMP NEXT ;EXIT ARITHMETIC OPERATORS

-1- - 24 bytes

Does a signed multiply of the second stack word by the word that is the top stack entry and replaces both entries by the 16-bit product.

Code Labe 1 Op Code Comments

01262020 Data 2, "* " C090 Link Address 059 1 Code Address CDA084 CALL SPSHAPU ;Put top word on 8231 stack CDA084 CALL SPSHAPU ;Put 2nd word on 8231 stack 3E 6E MVI A, 6E ; Multiply 2 D38 1 OUT 81 ; Numbers CDAA 84 CALL SPOPAPU ;Top word off 8231, onto stack C34880 JMP NEXT

35 bytes 9115

Does a signed multiply of the third stack word by the second stack word and a signed divide of the product by the top stack word.

Code Labe 1 Op Code Comments

Data 2,"*/ " Link Address Code Address CALL $PSHAPU ;Put top word on 8231 stack CALL $PSHAPU ;Put 2nd word on 8231 stack ~~A084 CALL $PSHAPU ;Put 3rd word on 8231 stack 3E 6E MVI A, 6E ; Multiply 3rd by 2nd D38 1 OUT 81 ; word 3E 79 MVI A,79 ; Jump top two D381 OUT 81 ;Numbers in 8231 3E 6F MVI A, 6F ; Divide top number D38 1 OUT 81 ; into product CDAA 84 CALL SPOPAPU ;Take word off 8231 and C34880 JMP NEXT ; put on data stack 15 bytes 9138

Adds the second stack entry and the top stack entry and replaces

both with the single two's complement sum as the top stack entry.

Code Labe 1 Op Code Comments

Data 1 ,I1+ " Link Address Code Address POP H ;Get top stack entry POP D ;Get 2nd stack entry DAD D ; Add PUSH H ;Push result JMP NEXT

28 bytes 9147

Subtracts the top stack entry from the top stack entry and replaces

both with the single two's complement result as the top stack entry.

Code Label Op Code Comments

01 2D2020 Data 1"- " 389 1 Link Address 4F9 1 Code Address CDA084 CALL SPSHAPU ;Get 1st word on 8231 stack ~~A084 CALL SPSHAPU ;Get 2nd word on 8231 stack 3E 79 MVI A, 79 ;Swap 1st and 2nd words D38 1 OUT 81 3E 6D MVI A,6D ;Subtract 1st from 2nd D38 1 OUT 81 CDAA 84 CALL SPOPAPU ;Put result on data stack C34880 JMP NEXT

-1 28 bytes 9163 Does a signed divide of the second stack entry by the top stack

entry. Replaces both entries with the quotient.

Code La be 1 Op Code Comments

Data I,"/ I' Link Address Code Address CALL SPSHAPU ;lst stack word onto 8231 stack CALL SPSHAPU ;2nd stack word onto 8231 stack MVI A, 79 ;Swap 1st and 2nd words OUT 81 MVI A, 6F ;Divide 2nd word by top / (continued)

Code Label Op Code Comments

OUT 81 CALL$POPAPU ;Put result ondatastack JMP NEXT

/MOD 57 bytes 917F

Does a divide of the second stack entry by the top stack entry.

Replaces both entries with the quotient as the second stack entry and the remainder as the top stack entry.

Code Labe 1 Op Code Comments

Data 4,"/MOH Link Address Code Address CALL SPSHAPU ;Put TOS on APU stack CALL $PSHAPU ;Put NOS on APU stack MVI A, 79 ;Swap TOS and NOS OUT 81 ; on APU stack MVI A,77 ;Push another copy of OUT 81 ;TOS on APU stack MOV A,L ;Push copy of NOS OUT 80 ;from HL register MOV A,H OUT 80 MVI A, 79 ;Swap NOS and TOS OUT 81 ; on APU stack MVI A, 6F ;Divide NOS by TOS OUT 81 MVI A, 77 ;Duplicate quotient on OUT 81 ; APU stack CALL SPOPAPU ;Put quotient on data stack MVI A, 6E ;Multiply quotient by OUT 81 ; TOS MVI A,6D ;Subtract result from NOS OUT 81 ; to get remainder CALL SPOPAPU ;Push remainder on top data JMP NEXT ; stack

14 bytes 91R9

Increments the top stack entry by 1. -1+ (continued)

Code Labe 1 Op Code

Data 2,"1+ " Link Address Code Address POP H ;Get top stack word INX H ;Increment it by 1 PUSH H ;Restore it JMP NEXT

14 bytes

Decrements the top stack entry by 1.

Code Op Code Comments

Data 2,"l- " Link Address Code Address POP H ;Get top stack word DCX H ;Decrement it by 1 PUSH H ;Restore it JMP NEXT

14 bytes

Multiplies the top stack word by 2. -Code Labe 1 Op Code Comments

Data 2,"2* " Link Address Code Address POP H ;Get top stack word DAD H ;Double word PUSH H ;Restore it to stack JMP NEXT

15 bytes

Adds 2 to the top stack entry.

Code Labe 1 Op Code Comments

Data 2 ,"2+ " Link Address Code Address POP H ;Get top stack word INX H ; Add 1 INX H ; Add 1 -2+ (continued)

Code La be 1 Op Code Comments

PUSH H ;Restore it to stack JMP NEXT

-2- 15 bytes

Subtracts 2 from the top stack entry.

Code Labe 1 Op Code Comments

Data 2,"2- " Link Address Code Address POP H ;Get top stack word DCX H ; Subtract 1 DCX H ; Subtract 1 PUSH H ;Restore it to stack JMP NEXT

-2I 28 bytes

Divides the top stack word by 2.

Code Labe 1 Op Code Comments

02322F20 Data 2 ,"2/ " Link Address Code Address CALL SPSHAPU ;Top stack word onto 8231 stack MVI A,02 ;Output the number 2 OUT 80 ; to 8231 XRA A OUT 80 MVI A,6F ;Divide top stack word OUT 81 ; by 2 CDAA 84 CALL SPOPAPU ;Put result on data stack C34880- JMP NEXT

-ABS 25 bytes 921 D Find the absolute value of the 16-bit number on top of the stack.

Code Labe 1 Op Code Comme n t s

03414253 Data 3, "ABS" Link Address Code Address POP H ;Get TOS number MOV A,H ;Check sign bit -ABS (continued)

Code La be 1 Op Code Comments

EUL JNC POS ;If positive jump out XRA A ;Else complement SUB L ;Number by subtracting MOV L,A ; it from 0 MVI A, 00 SBB H MOV H,A POS : PUSH H ;Push number onto stack JMP NEXT

-MOD 50 bytes 9239 Divides the second stack entry by top stack entry, replacing both entries with the remainder.

Code Labe 1 Op Code Comments

Data 3,"MODr' Link Address Code Address CALL $PSHAPU ;Top stack word onto 8231 stack MVT: A,77 ;Duplicate it on 8231 OUT 81 ; stack CALL SPSHAPU ;2nd stack word onto 8231 stack MVI A, 79 ;Swap top and 2nd OUT 81 MVI A, 6F ;Divide 2nd by top OUT 81 MVI A, 6E ;Multiply quotient by top OUT 81 MOV A,L ;Move 2nd word OUT 80 ; to 8231 stack MOV A,H OUT 80 MVI A, 79 ;Swap new result and OUT 81 ; 2nd word MVI A, 6D ;Subtract new result OUT 81 ; from 2nd word CALL $POPAPU ;Put remainder on data stack JMP NEXT MINUS 21 bytes

Replaces the top stack entry with its two's complement.

Code Labe 1 Op Code Comments

054D494E Data 4,"MIN" 3992 Link Address 739 2 Code Address CDA084 CALL SPSHAPU ;Top stack word onto 8231 stack 3~74 MVI A,74 ;Change sign of D38 1 OUT 81 ; number CDAA 84 CALL SPOPAPU ;Put result on data stack c 34880 JMP NEXT DOUBLE PRECISION ARITHMETIC OPERATORS (32-bit)

-D* 33 bytes 9280

This routine takes the top 32-bit number on the data stack and multiplies it by the secon'd 32-bit number and replaces both numbers by the 32-bit product.

Code Label Op Code Comments

Data 2, "D* " Link Address Code Address CALL $PSHAPU ;Put top 32-bit number CALL $PSHAPU ; on 8231 stack CALL $PSHAPU ;Put 2nd 32-bit number CDA084 CALL $PSHAPU ; on 8231 stack 3E 2E MVI A, 2E ;Get code for 32-bit mult'n. D38 1 OUT 81 ;Output to 8331 command reg. CDAA 84 CALL $POPAPU ;Place 32-bit result CALL $POPAPU ; on top of data stack JMP NEXT

D+ 33 bytes 92A1

This routine takes the top 32-bit number on the data stack and adds it to the second 32-bit number and replaces both by the 32-bit sum.

Code Labe 1 Op Code Comments

Data 2,"D+ " Link Address Code Address CALL SPSHAPU ;Put top 32-bit number CALL $PSHAPU ; on 8231 stack CALL $PSHAPU ;Put 2nd 32-bit number CALL $PSHAPU ; on 8231 stack MVI A, 2C ;Get code for 32-bit addition D381 OUT 81 ;Output to 8331 command reg. CDAA 84 CALL $POPAPU ;Place 32-bit sum CALL SPOPAPU ; on top of data stack JMP NEXT

-D- 37 bytes 92C2

This routine takes the top 32-bit number on the data stack and subtracts it from the second 32-bit number and replaces both by the

32-bit result. -D- (con t inued )

Code Label Op Code Comments

02442D20 Data 2,"D- " A192 Link Address CA92 Code Address CDA084 CALL SPSHAPU ;Put top 32-bit number ~~A084 CALL $PSHAPU ; on 8231 stack CDA084 CALL $PSHAPU ;Put 2nd 32-bit number ~~A084 CALL $PSHAPU ; on 8231 stack 3E 39 MVI A,39 ;Swap numbers on D38 1 OUT 81 ; 8231 stack 3E 2D MVI A, 2D ;Output subtract command D38 1 OUT 81 ; to 8231 CDAA 84 CALL $POPAPU ;Place 32-bit result CD~ CALL $POPAPU ; on top of data stack c 34880 JMP NEXT

-D / 37 bytes This routine takes the top 32-bit number on the data stack and divides it by the second 32-bit number and replaces both by the 32-bit quotient .

Code Labe 1 Op Code Comments

Data 2,"D/ " Link Address Code Address CALL $PSHAPU ;Put top 32-bit number CALL $PSHAPU ; on 8231 stack CALL SPSHAPU ;Put 2nd 32-bit number CALL $PSHAPU ; on 8231 stack MVI A, 39 ;Swap numbers on D38 1 OUT 81 ; 8231 stack 3E 2F MVI A, 2F ;Output divide command D381 OUT 81 ; to 8231 CDAA 84 CALL $POPAPU ;Place 32-bit quotient CDAA 84 CALL $POPAPU ; on top of data stack c 34880 JMP NEXT

69 bytes 930C

This routine takes the top 32-bit number and divides it into the second 32-bit number and puts the remainder on the stack. DMOD (continued)

Code Op Code Comments

Data 4 ,"DMO1' Link Address Code Address CALL $PSHAPU ;Put 32-bit number CALL $PSHAPU ; on APU stack MVI A, 17 ;Make a copy OUT 81 ; of number CALL $PSHAPU ;Get 2nd 32-bit POP D ; on APU stack MOV A,E ;Keep copy in CPU OUT 80 ; registers MOV A,D OUT 80 MVI A, 39 ;Swap top 2 numbers OUT 81 ; on APU stack MVI A, 2F ;Divide 2nd by top OUT 81 ; to get quotient MV1 A, 2E ;Multiply quotient by OUT 81 ; top MOV A,L ;Put 2nd 32-bit OUT 80 ; number on APU MOV A,H ; stack again OUT 80 MOV A,E OUT 80 MOV A,D OUT 80 MVI A, 39 ;Swap numbers OUT 81 ; on stack MVI A, 2D ;Subtract computed number OUT 81 ; from 2nd to get remainder CALL $POPAPU ;Put result on top CALL $POPAPU ; of data stack JMP NEXT

DIMOD 79 bytes

This routine takes the top 32-bit number and divides it into the

2nd 32-bit number and puts the integer quotient as 2nd on the stack and the remainder on top of the stack.

Code Labe 1 Op Code Comments

05442F4D Data 5 ,"D/Mtl Link Address Code Address CALL SPSHAPU ;Put TOS on APU stack CALL $PSHAPU D/MoD (continued)

Code Labe 1 Op Code Comments

CALL $PSHAPU ;Put NOS on APU stack POP D ; and keep a copy MOV A,E ; in DE and HL OUT 80 MOV A,D OUT 80 MVI A, 39 ;Swap TOS and NOS OUT 81 ; on APU stack MVI A,37 ;Push another copy of OUT 81 ; TOS MOV A,L ;Push another copy of OUT 80 ;NOS from the CPU MOV A,H ;Register DE and HL OUT 80 MOV A,E OUT 80 MOV A,D OUT 80 MVI A, 39 ;Swap last two entries OUT 81 ; of TOS and NOS MVI A, 2F ;Divide NOS by TOS OUT 81 MVI A, 37 ;Duplicate quotient OUT 81 ; on APU stack CDAA 84 CALL SPOPAPU ;Pop quotient off APU stack cDAA84 CALL SPOPAPU ; and place on data stack 3E 2E MVI A, 2E ;Multiply quotient by OUT 81 ; TOS MVI A, 2D ;Subtract result from OUT 81 ;NOS to get remainder CALL SPOPAPU ;Put remainder on CALL SPOPAPU ;Top of data stack JMP NEXT

DMINUS 27 bytes 9366

This routine changes the sign on 32-bit number on the top of the stack.

Code Labe 1 Op Code Comments

06444D49 Data 6 ,"DMI" Link Address Code Address CALL SPSHAPU ;Get 32-bit number on top of CALL SPSHAPU ; stack and put on 8231 stack MVI A, 34 ; change sign of number OUT 81 CALLSPOPAPU ;Put result backon DMINUS (continued)

Code Labe 1 Op Code Comments

CDAA 84 CALL $POPAPU ; data stack C34880 JMP NEXT

DABS 32 bytes

Takes the absolute value of the top 32-bit number on the stack.

Code Labe 1 Op Code Comments

04444142 Data 4 ,"DAB" A69 3 Link Address C993 Code Address CDA084 CALL $PSHAPU ;Put top 32-bit number CDA084 CALL $PSHAPU ; on 8231 stack 7C MOV A,H ;Move high byte to accumulator 17 RAL ;Shift sign bit into carry D2D89 3 JNC POS ;Jump if positive 3E 34 MVI A, 34 ;Else change sign D38 1 OUT 81 ; of number CDAA 84 POS : CALL $POPAPU ;Restore number CDAA~~ CALL SPOPAPU ; to data stack c 34880 JMP NEXT FLOATING POINT ARITHMETIC OPERATORS

F* - 33 bytes 93E 7 This routine takes the top floating point number on the stack and multiplies it by the second floating point number on the stack and places the product on the stack.

Code La be 1 Op Code Comments

02462620 Data 2,"F* " Link Address Code Address CALL SPSHAPU ;Get top number onto CALL SPSHAPU ; the 8231 stack CALL SPSHAPU :Get 2nd number onto CDA084- CALL SPSHAPU ; the 8231 stack 3E12 MVI A,12 ;Multiply two numbers D38 1 OUT 81 CDAA- 84 CALL SPOPAPU ;Put product on top CDAA84 CALL SPOPAPU ; of data stack C34880 JMP NEXT

-F+ 33 bytes 9402 This routine takes the top floating point number on the stack and adds it to the second floating point number on the stack and places the sum on the stack.

Code Labe 1 Op Code Comments

02462820 Data 2,"F+ " -E 19 3 Link Address -OA94 Code Address CDA084 CALL SPSHAPU ;Get top number onto CDA084 CALL SPSHAPU ; the 8231 stack CDA084 CALL SPSHAPU ;Get 2nd number onto CDA084 CALL SPSHAPU ; the 8231 stack 3E 10 MVI A, 10 ;Add the two numbers D381 OUT 81 CDAA 84 CALL $POPAPU ;Put sum on top CDAA~~ CALL SPOPAPU ; of data stack C34880 JMP NEXT 37 bytes 9423

This routine takes the top floating point number on the stack and subtracts it from the second floating point number on the stack and places the difference on the stack.

Code La be 1 Op Code Comments

Data 2,"F- " Link Address Code Address CALL $PSHAPU ;Get top floating point number CDA084 CALL SPSHAPU ;Put on 8231 stack CDA084 CALL SPSHAPU ;Get 2nd floating point number CDA084 CALL SPSHAPU ;Put on 8231 stack 3~19 MVI A, 19 ;Swap two numbers D38 1 OUT 81 ; on 8231 stack 3Ell MVI A, 11 ;Subtract top from 2nd D38 1 OUT 81 CDAA 84 CALL SPOPAPU ;Put result on top CDAA 84 CALL $POPAPU ; of data stack c 34880 JMP NEXT

37 bytes 9448

This routine divides the 2nd floating point number on the stack by the top floating point number on the stack and places the quotient on top of the data stack.

Code Op Code Comments

02462F20 Data 2,I1F/ " 2394 Link Address 5094 Code Address CDA084 CALL SPSHAPU ;Get top floating point number CDA084 CALL SPSHAPU ;Put on 8231 stack ~~A084 CALL SPSHAPU ;Get 2nd floating point number ~~A084 CALL $PSHAPU ;Put on 8231 stack 3E 19 MVI A, 19 ;Swap two numbers D38 1 OUT 81 ; on 8231 stack 3E13 MVI A, 13 ;Divide 2nd by top D38 1 OUT 81 CDAA 84 CALL SPOPAPU ;Put quotient on top CDAA 84 CALL $POPAPU ; of data stack c 34880 JMP NEXT SQRT 27 bytes

This routine takes the square root of the top floating point number

on the data stack and puts the result back on the stack.

Code Labe 1 Op Code Comments

Data 4,"SQR" Link Address Code Address CALL SPSHAPU ;Get top floating point number CALLSPSHAPU ;Put on 8231stack MVI A,01 ;Take square root of OUT 81 ; number CALL SPOPAPU ;Put result back CALL SPOPAPU ; on data stack JMP NEXT

-PWR 37 bytes 9488 This routine takes the 2nd number on the data stack and raises it

to the power of the number on top of the data stack. It places the

result back on top of the data stack.

Code La be 1 Op Code Comments

Data 3, "PWR" Link Address Code Address CALL SPSHAPU ;Get top floating point number CALL $PSHAPU ;Put onto 8231 stack CALL SPSHAPU ;Get 2nd floating point number CALL $.PSHAPU ;Put onto 8231 stack MVI A, 19 ;Swap top 2 numbers D381 OUT 81 ; on 8231 stack 3E 0B MVI A, OB ;Raise 2nd number to power D38 1 OUT 81 ; of top number CDAA 84 CALL SPOPAPU ;Put result on top CDAA 84 CALL SPOPAPU ; of data stack C34880 JMP NEXT

-LOG 27 bytes 9 4AD

This routine takes the common log base 10 of the top floating point

number on the data stack and puts the result back on top of the stack. -LOG (continued )

Code Labe 1 Op Code Comments

Data 3 ,"LOG" Link Address Code Address CALL SPSHAPU ;Get top number on stack CALL SPSHAPU ;Place it on 8231 stack MVI A, 08 ; take the log base 10 OUT 81 ; of the number CALL SPOPAPU ;Put result back on CALL SPOPAPU ; top of data stack JMP NEXT

27 bytes 94C 8

This routine takes the natural logarithm of the top floating point number on the data stack and puts the result back on top of the stack.

Code Labe 1 Op Code Comments

024C4E20 Data 2 ,"LN I' AD94 Link Address DO9 4 Code Address CDA084 CALL SPSHAPU ;Get top number on stack ~~A084 CALL SPSHAPU ;Place it on 8231 stack 3~09 MVI A, 09 ; take the natural log D381 OUT 81 ; of the number CDAA 84 CALL SPOPAPU ;Place result on top CDAA~~ CALL SPOPAPU ; of data stack C34880 JMP NEXT

-EXP 27 bytes 94E 3

This routine raises e to the power of the top floating point number on the stack and places the result on the top of the stack.

Code Label Op Code Comments

Data 3,"EXP" Link Address Code Address CALL SPSHAPU ;Get top number on stack CDA084 CALL SPSHAPU ;Place it on 8231 stack 3E OA MVI A, OA ;Raise e to power of number D381 OUT 81 CDAA 84 CALL SPOPAPU ;Put result on top of CDAA~~ CALL SPOPAPU ; data stack c 34880 JMP NEXT 21 bytes 94FE

This routine puts 3 in floating point formation on top of data stack.

Code Labe 1 Op Code Comments

Data 2,"PI I' Link Address Code Address MVI A,lA ;Push PI onto 8231 stack OUT 81 CALL SPOPAPU ;Put PI on top of CALL $POPAPU ; data stack JMP NEXT

27 bytes 95 13

This routine takes the cosine of the top floating point number on the stack. ad ians )

Code Labe 1 Op Code Comments

03434F53 Data 3 ,"COSIt Link Address Code Address CALL SPSHAPU ;Get topnumber on stack CALL SPSHAPU ; onto 8231 stack MVI A,03 ;Take cosine of number D381 OUT 81 CDAA 84 CALL SPOPAPU ;Put result back CALL SPOPAPU ; on data stack JMP NEXT

-SIN 27 bytes 9526 This routine takes the sine of the top floating point number on the stack. The result in radians is placed back on top of the stack.

Code Labe 1 Op Code Comments

0353494E Data 3 ,"SINtt 1395 Link Address -369 5 Code Address CDA084 CALL SPSHAPU ;Get top number on stack ~DA084 CALL $PSHAPU ; onto stack 3E 02 MVI A,02 ;Take sine of number D381 OUT 81 CDAA- 84 CALL SPOPAPU ;Put result back CDAA 84 CALL SPOPAPU ; on data stack c 34880- JMP NEXT -TAN 27 bytes

This routine takes the tangent of the top floating point number on the stack. It places the result in radians on top ,of the data stack.

Code Op Code Comments

Data 3, " TAN" Link Address Code Address CALL SPSHAPU ;Get top number on stack CALL SPSHAPU ;Place on 8231 stack MVI A, 04 ;Take tangent of number OUT 81 CALL SPOPAPU ;Put result on data CALL SPOPAPU ; stack JMP NEXT

ACOS 27 bytes

This routine takes the arc cosine of the top floating point number on the stack. It places the result back on top of the 8231 data stack.

Code Label Op Code Comments

0441 434F Data 4, "ACO" 4995 Link Address 6C95 Code Address CDA084 CALL SPSHAPU ;Get top number on stack CDA084 CALL SPSHAPU ;Place on 8231 stack 3E 06 MVI A, 06 ;Take arc cosine of number D38 1 OUT 81 CDAA 84 CALL SPOPAPU ;Put result on data stack CDAA84 CALL SPOPAPU C34880 JMP NEXT

AS IN 27 bytes

This routine takes the Arc sine of the top floating point number on the stack. It then places the result on top of the data stack.

Code Labe 1 Op Code Comments

04415349 Data 4 ,"ASI1' 649 5 Link Address 879 5 Code Address CDA084 CALL SPSHAPU ;Get top number from stack CDA084 CALL SPSHAPU ;Place on top of 8231 stack 3E05 MVI A,05 ;Take arc sine of number D381 OUT 81 ASIN (continued)

Code Labe 1 Op Code Comments

CALL SPOPAPU ;Put result back on top CALL SPOPAPU ; of data stack JMP NEXT

ATAN 27 bytes 959A

This routine takes the ARC tangent of the top floating point number on the stack. It then places the result on top of the data stack.

Code La be 1 Op Code Comments

0441 5441 Data 4, "ATA" 7F95 Link Address A29 5 Code Address CDA084 CALL SPSHAPU ;Get top number from stack CDA084 CALL $PS.HAPU ;Place on top of 8231 stack 3E07 MVI A,07 ;Take arc tangent of number D38 1 OUT 81 CDAA-. 84 CALL SPOPAPU ;Put result back on top CDAA84 CALL SPOPAPU ; of data stack C34880 JMP NEXT

FMINUS 27 bytes 95 B5

This routine changes the sign of the floating point number on the top of the stack.

Code Labe 1 Op Code Comments

06464D49 Data 6,"FMIt1 Link Address Code Address CALL SPSHAPU ;Get top number on stack ~~A084 CALL $PSHAPU ;Place number on 8231 stack 3E15 MVI A, 15 ;Change sign of number D381 OUT 81 CDAA 84 CALL SPOPAPU ;Put result back on stack CDAA 84 CALL SPOPAPU C34880 JMP NEXT INTERSTACK O~erators

-

Pops top stack word and pushes it to the return stack.

Code Labe 1 Op Code Comments

023C5220 Data 2,"

-R> 22 bytes 95E6

Pops the word at the top of the return stack and pushes it to the stack.

Code Op Code Comment s

Data 2,"R) " Link Address Code Address LHLD RS ;Get address of return stack MOV E,M ;Load register pair INX H ; DE with word MOV D,M ; at top of return stack INX H SHLD RS ;Store address of return stack PUSH D ;Put DE on top of data stack JMP NEXT

-C

Code Labe 1 Op Code Comments

Data 3 ,"C

Code Op Code Comments

DCX H ;Store low-order byte MOV M,E ; on return stack SHLD RS ;Store, address of return stack JMP NEXT

28 bytes 96 10

Pops the byte at the top of the return stack and pushes it to the stack in sign-extended format.

Code La be 1 Op Code Comments

Data 3,"CR>" Link Address Code Address LHLD RS ;Get address of return stack MOV E,M ;Get top byte off of INX H ; return stack SHLD RS MVI D,OO ;Assume positive byte MOV A,E RAL ;Shift sign bit into carry JNC POS ;Is byte positive? DCR D ;No, make negative POS : PUSH D ;Push sign-extended word JMP NEXT

-I > 18 bytes

Pushes to the stack the loop index for the innermost word-length loop which is the top return stack word.

Code Labe 1 Op Code Comments

Data 2,"I> " Link Address Code Address LHLD RS ;Get address of return stack MOV E,M ;Get a copy of top word INX H ; on the return stack MOV D,M PUSH D ;Push onto data stack JMP NEXT -CI> 24 bytes 963E

Pushes to the stack the loop index for the innermost byte-length loop which is the top return stack byte.

Code Labe 1 Op Code Comments

Data 3,"CI>" Link Address Code Address LHLD RS ;Get address of return stack MOV E,M ;Get byte on top of RS MVI D,OO ; Sign-extended positive MOV AYE RAL ;Shift sign bit into carry D25296 JNC POS ;Is the byte positive? 15 DCR D ;No, set to negative D5 POS : PUSH D ;Push onto data stack C34880 JMP NEXT

22 bytes

Pushes to the stack the loop index for the second innermost word- length loop.

Code La be 1 Op Code Comments

Data 2,I1J> I' Link Address Code Address LHLD RS ;Get address of return stack LXI D,04 ;Adjust pointer to DAD D ;2nd-loop index MOV E,M ;Get a copy INX H ; of 2nd-loop MOV D,M ; index PUSH D ;Push on to data stack JMP NEXT

-CJ> 26 bytes

Pushes to the stack the loop index for the second innermost byte- length loop.

Code Labe 1 Op Code Comments

Data 3 ,"CJ>" Link Address Code Address LHLD RS ;Get address of return stack -CJ> (continued)

Code Labe 1 Op Code Comments

INX H ;Adjust pointer INX H ;2nd byte loop index HOV E,M ;Get byte MVI D ,OO ;Sign-extended positive MOV AYE ;Shift sign bit into RAL ;Carry to test JNC POS ;Is the byte positive? DCR D ;No, set to negative POS : PUSH D ;Push onto data stack JMP NEXT

-K> 22 bytes 9686

Pushes to the stack the loop index for the third innermost word- length loop.

Code La be 1 Op Code Comments

Data 2 ,"K> " Link Address Code Address LHLD RS ;Get address of return stack LXI D,08 ;Adjust pointer to third DAD D ;loop index MOV E,M ;Get a copy ZNX H ; of third-loop MOV D,M ; index PUSH D ;Push onto data stack JMP NEXT

-CK > 28 bytes Pushes to the stack the loop index for the third innermost byte- length loop.

Code Labe 1 Op Code Comments

Data 3,"CK>I1 Link Address Code Address LHLD RS LXI D,04 DAD D MOV E,M MVI D,OO MOV A,E RAL -CK> (con t inued)

Code Labe 1 Op Code Comments

D2 B49 6 JNC POS 15 DCR D D5 POS : PUSH D C34880 JMP NEXT STACK OPERATIONS

-DUP 14 bytes Duplicates the top stack entry and pushes it to the stack.

Code Labe 1 Op Code Comments

Data 3 ,"DUP1' Link Address C~deAddress POP H ;Get top word PUSH H ;Restore top PUSH H ; and copy it again JMP NEXT

2DUP 17 bytes

Duplicates the top double length word on the stack

Code Op Code Comments

Data 4,"2DU1' Link Address Code Address POP H ;Get top word POP D ;Get 2nd word PUSH D ;Restore 2nd word PUSH H ;Restore top word PUSH D ;Duplicate old 2nd word PUSH H ;Duplicate old top word JMP NEXT

SWAP 14 bytes

Interchanges the order of the top two stack entries.

Code Label Op Code Comments

Data 4,"SWA" Link Address Code Address POP H ;Get top word XTHL ;Top to second PUSH H ;Second to top JMP NEXT

2SWAP 31 bytes

Interchanges the top double word with the next double word. 2SWAP (continued)

Code Op Code Comments

Data 5,"2SW1' Link Address Code Address LXI H, IR ;Get address temporary IR MOV M,C ;Store IR at INX H ; temporary locat ion MOV M,B POP D ;Pop top of stack POP H ;Pop next word POP B ;Pop 2nd double-word low word XTHL ;Exchange high order words PUSH D ;Push original top word PUSH H ;Push 2nd-double word PUSH B LXI H, IR ;Get address of IR MOV C,M ;Restore IR INX H ; back to BC MOV B,M ; register pair JMP NEXT

OVER 16 bytes

Duplicates the second stack entry and pushes it to the top of the stack. ABC BABC

Code Labe 1 Op Code Comments

044F5645 Data 4 ,"OVE" E 59 6 Link Address F99 6 Code Address E 1 POP H ;Get top D 1 POP D ;Get 2nd D 5 PUSH D ;Restore 2nd E 5 PUSH H ;Restore top D5 PUSH D ;Push 2nd to top C34880 JMP NEXT

24 bytes 9701

Duplicates the second stack double-word over the top double-word .

Code Labe 1 Op Code Comments

Data 5 ,I' 20V" Link Address Code Address 20VER (continued)

Code Labe 1 Op Code Comments

LXI H,07 ;Get address of stack pointer DAD SP ;Adjust pointer to 2nd dbl-word MOV D,M ;Move high byte to D DCX H ;Decrement pointer MOV E,M ;Move high-middle byte to E DCX H ;Decrement pointer PUSH D ;Push word MOV D,M ;Move low-middle byte to D DCX H ;Decrement pointer MOV E,M ;Move low byte to E PUSH D ;Push word JMP NEXT

16 bytes

Rotates the top three stack entries left in an infix cyclic sense.

Start: A B C X X Finish: B C A X X

Code Label Op Code Comments

Data 4,"RRO" Link Address Code Address POP H ;Get top POP D ;Get 2nd XTHL ;Exchange top and 3rd PUSH H ;Push old 3rd PUSH D ;Push old 2nd JMP NEXT

LROT 16 bytes

Code Labe 1 Op Code Comments

Data 4,"LROt' Link Address Code Address POP D ;Get top POP H ;Get 2nd XTHL ;Exchange 2nd and 3rd PUSH D ;Push old top PUSH H ;Push old 3rd JMP NEXT DROP 12 bytes

Pops the top stack entry and 'discards it.

Code Labe 1 Op Code Comments

Data 4 ,I1 DRO" Link Address Code Address POP H ;Discard top of stack JMP NEXT

2DROP 13 bytes

Pops the top two stack entries and discards them.

Code Label Op Code Comments

Data 5 ,"2DR1' Link Address Code Address POP H ;Discard top of stack POP H ;Discard 2nd stack item JMP NEXT MEMORY REFERENCE

-! 16 bytes 9768

Stores second stack entry at the address at the top of the stack entry, removing both from the stack.

Code Labe 1 Op Code Cornmen t s

Data l,"! " Link Address Code Address POP H ;Get top of stack address POP D ;Get word to be stored MOV M,E ;Store low order byte INX H ;Increment address MOV M,D ;Store high order byte JMP NEXT

14 bytes 9778

Stores second stack entry byte at the address at the top of the top stack entry.

Code La be 1 Op Code Comments

02432120 Data 2 ,"C! " Link Address Code Address POP H ;Get top of stack address POP D ;Get byte to be stored MOV M,E ;Store low order byte JMP NEXT

-+! 20 bytes Pops two stack entries and adds the word at the second entry to the word whose address is the top entry.

Code Label Op Code Comments

022B2120 Data 2,It+! " Link ~ddress Code Address POP H ;Pop top of stack address POP D ;Pop 2nd byte to be added MOV A,E ;Move low order byte to A ADD M ;Add low order bytes MOV M,A ;Store new low order bytes INX H ;Move pointer -+! (continued) Code Labe 1 Op Code

MOV A,D ;Move high order byte to A

ADC , M ;Add hi order bytes with carry MOV M,A ;Store new high order byte JMP NEXT

-C+! 16 bytes 979A Pops two stack entries and adds the byte at the second entry to the byte whose address is the top entry. -Code Labe 1 Op Code Comments Data 3,11C+!" Link Address Code Address POP H ;Pop top of stack address POP D ;Pop 2nd byte to be added MOV AYE ;Move low order byte to A ADD M ;Add low order bytes MOV M,A ;Store result JMP NEXT

-@ 16 bytes 97AA

Replaces the address at the top of the stack with the word at the address.

Code Op Code Comments

Data 1 ,"@ l1 Link Address Code Address POP H ;Get address MOV E,M ;Low byte at address INX H ;Bump address MOV D,M ;~ighbyte at address PUSH D ;Push contents to stack JMP NEXT

-C@ 22 bytes

Replaces the address at the top of stack with the byte at the address with the high byte sign extended. -C@ (continued)

Code Labe 1 Op Code Comments

Data 2 ,"C@ " Link Address Code Address POP H ;Get address MOV E,M ;Low byte at address MVI D,OO ;Set high byte to zero XRA A ;Clear accumulator ORA E ;Set sign flag JP POS ;Is byte positive DCR D ;No, set D to all 1's POS : PUSH D ;Store on stack JMP NEXT

21 bytes 97DO

Stores double length word in 2nd and 3rd positions on stack in the address specified on top of stack.

Code Label Op Code Comments

Data 2,"2! " Link Address Code Address POP H ;Get address POP D ;Get low order word MOV M,E ;Store low order byte INX H ;Bump pointer MOV M,D ;Store low middle byte INX H ;Bump pointer POP D ;Get high order word MOV M,E ;Store high middle byte INX H ;Bump pointer MOV M,D ;Store high order byte JMP NEXT

22 bytes 9 7 E 5

Replaces the address at the top of the stack with the double word starting at that address.

Code Labe 1 Op Code Comments

Data 2 ,"2@ It Link Address Code Address POP H ;Get address MOV E,M ;Get low order byte -2@ (continued)

Code Labe 1 Op Code Comments

INX H ;Bump pointer MOV D,M ;Get low-middle byte INX H ;Bump pointer MOV A,M ;Get high middle byte INX H ;Bump pointer MOV H,M ;Get high order byte MOV L,A ;Move high middle byte PUSH H ;Push high order word PUSH D ;Push low order word JMP NEXT

OSET 16 bytes

Sets word to zero that is pointed to by address on top of stack.

Code Labe 1 Op Code Comments

04305345 Data 4,"OSE" E597 Link Address -0398 Code Address E 1 POP H ;Get address AF XRA A ;Clear accumulator 7 7 MOV M,A ;Store zeroes in low byte 23 ZNX H ;Bump pointer 7 7 MOV M,A ;Store zeroes in high byte C34880 JMP NEXT

1SET 17 bytes

Sets word to 1 that is pointed to by address on top of stack.

Code Labe 1 Op Code Comments

Data 4,"1SEtt Link Address Code Address POP H ;Get address XRA A ;Clear accumulator MOV M,A ;Store in memory INR M ;Increment memory INX H ;Increment pointer MOV M,A ;Move 0 to memory JMP NEXT COSET 14 bytes

Sets byte to zero that is pointed to by address on top of the stack.

Code Op Code Comments

Data 5,"COStt Link Address Code Address POP H ;Get address XRA A ;Clear accumulator MOV M,A ;Store zeroes JMP NEXT

ClSET 15 bytes 982A

Sets byte to 1 that is pointed to by address on top of stack.

Code Labe 1 Op Code Comments

Data 5 ,"CISt' Link Address Code Address POP H ;Get address MVI A,01 ;Load A with 01 hex MOV M,A ;Store in memory JMP NEXT COMPILER DIRECTIVES

16 bytes 9839

Adds the word address of the program control directive *+LOOP to the dictionary, then encloses the jump address at the top of the stack in the dictionary.

Code Labe 1 Op Code Comme nt s

Data 5 ,"+LO" Link Address COLON *!I ;Put address of *+LOOP ;*+LOOP on stack END, ;Enclose jump address SEMI

BEGIN 12 bytes

Pushes to the stack the address of the next available free dictionary location.

Code Labe 1 Op Code Comments

Data 5,"BEG" Link Address COLON HERE ;Get value in DP SEMI

C+LOOP 16 bytes

Adds the word address of the program control directive *C+LOOP to the dictionary, and encloses the address at top of the stack as the jump address.

Code Label Op Code Comments

Data 6,"C+L1' Link Address COLON *!I ;Put address of *C+LOOP ;*C+LOOP on stack END. ;Enclose jump address 3C 80 SEMI -CDO 16 bytes 9865

Encloses the word address of the program control directive *CDO in the dictionary and then pushes the address of the next free dictionary location to the stack.

Code Labe 1 Op Code Comments

0343444F Data 3 ," CDO" 5598 Link Address 5580 COLON B18C *f ;Put address of DE 86 *CDO ;*C+LOOP on stack B085 Do, ;Stores directive and pushes 3C 80 SEMI ; next free DP location

CLEAVE 16 bytes 9875

Encloses the word address of the program control directive *CLEAVE in the dictionary. Compiles a command to cause an immediate exit from a byte loop construct at execution time. Used within a conditional branch construct.

Code Labe 1 Op Code Comments

Data 6 ,"CLE" Link Address COLON *# ;Put address of *CLEAVE *CLEAVE ; on the stack; enclose

j ; it in the dictionary SEMI

CLOOP 16 bytes 9865

Adds the word address of the program control directive *CLOOP to the dictionary, then encloses the address at the top of the stack as the jump address in the dictionary.

Code Label Op Code Comments

05434C4F Data 5,"CLO" Link Address COLON *# ;Put address of *CLOOP ; *CLOOP on the stack CLOO? (continued)

Code Labe 1 Op Code Comments

END, ; in dictionary and jump SEMI ; address is enclosed also

16 bytes 989 5

Encloses the word address of the program control directive *DO in the dictionary and then pushes to the stack the address of the next free dictionary location.

Code Labe 1 Op Code Comments

Data 2 ,"DO " Link Address COLON *# ;Put address of *DO *DO ; on top of stack DO, ;Store address in dictionary SEMI ;Put next free address on stack

-ELSE 24 bytes Encloses the word address of the program control directive *ELSE in the dictionary, saves the address of the next free dictionary location on the stack, reserves 2 bytes in the dictionary, swaps the top 2 stack entries and stores the new top address as the jump address. -Code Labe 1 Op Code Comments Data 4 ,"ELSt' Link Address COLON */I ;Word address of *ELSE ;*ELSE Do, ;Store address and get pointer 0 ;Get zero

Y ;Reserve word for a jump addr. SWAP ;Swap top two addresses THEN ;Execute then code SEMI -END 16 bytes

Encloses the word address of the program control directive *END in the dictionary, pops the top stack address and encloses this address in the dictionary as the jump address.

Code Labe 1 Op Code Comments

Data 3 ,"END1' Link Address COLON *!I ;Word address *END ; of *END END, ;Store address and jump SEMI ; address

-IF 20 bytes 98CD

Encloses the word address of the program control directive *IF in the dictionary , pushes the address of the next free dictionary locat ion to the stack and reserves two bytes in the dictionary for a jump address.

Code Labe 1 Op Code Comments

Data 2 ,"IF I' Link Address COLON ;Word address of ;*IF to store ; in dictionary and save pointer ;Get a zero

Y ;Reserve a word SEMI

LEAVE 16 bytes 98E 1

Encloses the word address of the program control directive *LEAVE

in the dictionary . -Code Labe 1 Op Code Comments Data 5 ,"LEA" Link Address COLON *!I ;Word address *LEAVE ; of *LEAVE LEAVE (con t inued )

Code Labe 1 Op Code Comments

3 ; enclosed in dictionary SEMI

16 bytes 98F1

Encloses the word address of the program control directive *LOOP in the dictionary, then pops the stack to enclose this address as the jump address in the dictionary.

Code Labe 1 Op Code Comments

Data 4,"LOO" Link Address COLON *# ;Word address *LOOP ; of *LOOP END, ;Store address and jump SEMI ; address in dictionary

THEN 16 bytes

Pops the address from the stack, and stores the address of the current free dictionary location as the jump address.

Code Labe 1 Op Code Comments

Data 4,"THE" Link Address COLON HE RE ;Get free address SWAP ! ;Store as jump address SEMI

WHILE 20 bytes 9911

Encloses the word address of the program control directive "WHILE in the dictionary. The top two stack entries are swapped and the top stack entry is enclosed in the dictionary as the jump address in the current free dictionary location. The function also takes the current free WHILE (continued)

dictionary location and stores it at the new top of stack address for

the jump address.

Code Labe 1 Op Code Comments

Data 5 ,"WHIt' Link Address COLON SWAP ;Change stack order *# ;Word address WHILE ; of *WHILE END, ;Store addr., jump addr. to begin THEN ;Store jump addr. for IF or ELSE SEMI

- 32 bytes 9925

Encloses the literal handler *[ in the dictionary , changes the

token seperator to ] and scans the next token from the input buffer and

then encloses the token in extended header format in the dictionary.

Code Label Op Code Comments

Data I,"[ " Link Address COLON ;Word address *t ; of *[ , ;Enclose it in the dictionary *Cl 5D ;Get the seperator 1 TOKEN ;Move token to the dictionary HERE ;Get start of token C@ ;Token length 1+ ;Address of length of token DP ;Dictionary address +! ;Enclose token in dictionary SEMI COMPILE MODE TERMINATORS

20 bytes 9945

Encloses the word address of the inner interpreter SEMI routine in the dictionary and sets the system mode to the execute mode.

Code Labe 1 Op Code Comments

013B2020 Data 1,"; It Link Address COLON *# ;Address of SEMI ; SEMI routine

3 ;Enclose it in dictionary MODE ;Mode address COSET ;Set execute mode {MODE=O) SEMI

;CODE 20 bytes 99 59

Encloses the word address of the keyword SCODE in the dictionary and sets the system mode to execute.

Code Labe 1 Op Code Comments

Data 5 ,";COtl Link Address COLON */I ;Word address SCODE ; of SCODE > ;Enclose it in dictionary MODE ;Mode address COSET ;Set execute mode {MODE=O) SEMI Messages

FPFORTH 09

Stack Error Message 99 7 7

Stack Underf low 11

OK Message

? Message VOCABULARY -CORE 10 bytes 9A34 Sets the CONTEXT system variable to the address of the first header

in the permanent vocabulary.

Code Labe 1 Op Code Comments

Data 4,"COR" Link Address DOES > VOCABULARY

FORGET 44 bytes 9A08

Searches the current vocabulary for the token following FORGET. If

located, the current link address is set to the address of the link in

the keyword located and the dictionary pointer is reset to the start of

the header of the located keyword. If not located, the token is echo displayed and followed by a "?". -Code Labe 1 Op Code Comments 06464F52 Data 6 ,"FOR" -DF99 Link Address 5580 COLON CURRENT ;Get CURRENT address @ ;Points to latest current entry CONTEXT ;Get context address ! ; set to search current 1 ;Search for token DUP ;Need word address twice *Ct 2 ;Subtract 2 to get - ; link address @ ;The link address CURRENT ;Get current address @ ;Points to the link ! ;Reset link to token link *C# 6 ;Word address - 6 points - ; to first leader byte DP ;Get free dictionary space addr ! ;Reset dictionary pointer SEMI IMMEDIATE 41 bytes

Delinks the latest entry from the current vocabulary and links it

to the compiler vocabulary. The previous second entry in the current vocabulary becomes the latest entry.

Code Labe 1 Op Code Comments

09494D4D Data 9 ,"IMM1' Link Address COLON ENTRY ;Pnts to latest current keyword DUP ;Save it for compiler link *C# 4 ;Adjust pointer to point + ; to latest keywords link DUP ;Save as new link address @ ;Get the link CURRENT ;Points to current @ ;Points to vocabulary ! ;Update current to 2nd entry COMPILER ; address @ ;Points to last compiler entry SWAP ;Address then link ! ;Store link in previous current COMPILER ;Compile address ! ;Previous current top of SEMI ; compiler