Forth language concise tutorial
Zhao Yu Zhang Wencui compiled
Original author Richard E. Haskell
Dept. of computer science and engineering rochester,
Michigan 48309
Original title "The Forth Course"
Foreword
What is FORTH? Why do we learn Forth?
Forth is such a programming language ...
• The early invention by Charles Moore in the 1970s;
• It is scalable;
• Save all definitions in the dictionary;
• extremely tightened;
• Support recursive;
• You can run in both types of RAM and ROMs;
• Structured;
• Use the stack and suffix representation;
• The modularity is extremely high;
• Support interactive development and operation;
• Special easy to debug;
• Very easy to access machine hardware;
• The running speed is very fast, and includes a assembler;
• Language systems are portable (developing environment and compiler size minimal);
• Fully understand;
• Can be executed on the Forth processor supported by hardware;
• Implement almost every universal and unreal microprocessor;
• If you have used it, you cannot give up;
• Differences with other programming languages are very different;
You now read is the Forth programming language tutorial, which consists of 11 parts, each part of a lesson, the total goal is to simplify the process of learning Forth.
The material of this tutorial comes from the authous language teaching practice in the autonomy, as part of Oakland University in Rochester, Michigan embedded software design and computer system science, as part of college students and graduate students, as computer engineering, electronic engineering, computer The main course of science and engineering, system engineering and computer science.
At the beginning of the course, no one knows Forth, and most people have never heard of it. When the course is over, some people (usually a computer science class) said no longer want to see this language! On the other hand, other people (most of them are engineering students) found that Forth is really a tool needed, used to solve some real-world problems, and "one but there, don't have no begging".
Now you will learn this course. We assume that you know some other programming languages (such as Pascal, Fortran or Basic), if you understand 8088/8086 assembly language is very convenient, but this is not necessary, if you want to learn assembly language, now there is countless 8088 / 8086 assembly language tutorial.
Our courses will adopt F-PC 3.5 Forth System, which is a huge, fully functional, independent Forth system, developed due to Tom Zimmer's efforts. This version contains many features you think should be included in an excellent programming language, and there are many features you have never thought, such as an integrated hypertext system. Of course, many of the functions of the F-PC system you may not be used, and some can learn again when needed.
Through this course, you will get a lot of background knowledge and valuable information in the F-PC system, hoping that this will open a new, powerful programming road.
Original author R. E. Haskell, August 1990
Translator Note: The software used in this tutorial can be downloaded online. See Forth Simplified Chinese Network resources, here is FPC 3.6 local download, ZIP files can be extracted using universal Zip tools or WinRAR tools. table of Contents
First lesson FORTH language profile
Second class uses F-PC
How to work in the third lesson FORTH
Fourth lesson FORTH judgment
The number of fifth lesson forth
Sixth class string
Seventh lesson CODE word and DOS I / O
Eight lessons definition words
Ninth lesson compilation word
Tenth lesson FORTH data structure
Eleventh lesson using an interrupted terminal program
First lesson FORTH language profile
1.1 Introduction Forth
The few words listed below are very simple, but it fully describes the Forth programming language:
• Everything in Forth is a word (Word);
• The Forth word must be separated by space;
• Forth word is stored in the dictionary;
• The Forth word can be interpreted or compiled;
• In the interpreted mode, a Forth word is executed;
• In compilation mode, a Forth word is stored in a dictionary;
• You can form Forth phrases by combining a string of Forth words;
• If you have entered a Forth word and followed by
• If you have entered a number (such as 6) and press
• Forth is widely used in the stack to pass parameters inter-line. This means that the Forth application will be significantly reduced to the use of variables;
• You can define a new word in Forth (using the Forth Word you have defined first), which will become part of the Forth Dictionary, which can be used like other Forth words.
1.2 FORTH arithmetic
FORTH uses stacks and suffix representations to arithmetic operations.
Many Forth systems including F-PCs, their stacks store 16-bit values. 32-bit Forth systems such as Macforth store 32-bit values on the stack. In the 16-bit Forth system, the value on the stack will account for 2 bytes, in the 32-bit Forth system, the value on the stack will account for 4 bytes.
When you have a number, it is put on the stack. You can use any base to enter the number, we will see how to change the number. The default number is a decimal. Therefore, if you are in 35, 16-based 23h (the suffix H is indicated by a 16-en-number) will be stored on the stack at the following format:
If you enter 2 numbers, the middle is separated in space, which will be stored on the stack.
For example, you enter:
127 (space) 256
Two 16-in-1 FH and 100H will be stored on the stack as follows:
Insert .s (or .s, for most Forth systems, no relationships are not required) Non-destructive display stack content, the order is the content of the bottom of the stack, then print the top of the stack.
127 256 OK
Here, OK is the forth's prompt, and the value is stored on the stack in the complement mode.
The 16-bit FortH system can store the value on the stack is -32, 768 to 32, 767, and 32-bit systems can store the value on the stack is -2, 147, 483, 648 to 2, 147, 483, 647.1.3 FORTH arithmetic operation
Forth word. (Reading point or DOT) can print the value of the top elements
79. will print 9 and 7
Enter usually is ignored by Forth or treated as a space, it can make the program easier to read. With the "vertical" style, we can easily use a backslash / to illustrate the "stack structure", and after any content of the backslash until any content of this row is ignored.
For example, in order to explain the stack of each step of the above example, we can write:
7/7
9/7 9
. / 7
. /
Note that point from the stack to a value.
Forth (plus) add two values of the top of the stack and put the result on the stack, for example:
7 9 . Put 16
7/7
9/7 9
/ 16
. /
Forth word - (reduced) will subtract the secondary top element with a stack of top elements and put the result difference to the top of the stack.
8 5 -. Will print 3
8/8
5/8 5
- / 3
. /
Forth Word * (Multiplication) multiplies two values of the top of the stack, reserved on the stack.
4 7 *. Take 28
4/4
7/4 7
* / 28
. /
FortH / (division) implementation, the top element of the stack is divided, the secondary stack top element is divided, and the merchant is in the stack.
8 3 /.
8/8
3/8 3
/ / 2
.
1.4 stack management word
Stack of stacks usually use this format (Before - After), where
Before = This word is executed before the top of the stack;
After = Top element after this word is executed
DUP (N - N N)
Copy the top element, such as 5 dup.. Print 5 5
5/5
DUP / 5 5
. / 5
. /
SWAP (N1 N2 - N2 N1)
Switch two elements on the stack, such as 37 swap.. Print 3 7
3/3
7/3 7
SWAP / 7 3
. / 7
. /
Drop (n -)
Remove the top elements of the stack, such as 6 2 Drop. Print 6
6/6
2/6 2
DROP / 6
.
OVER (N1 N2 - N1 N2 N1)
Copy the top elements such as 6 1 over...........
6/6
1/6 1
Over / 6 1 6
. / 6 1
. / 6
.
TUCK (N1 N2 - N2 N1 N2)
Copy the top of the stack to the top element, this operation is equivalent to Swap over
Such as 6 1 tuck... Will print 1 6 1
6/6
1/6 1
TUCK / 1 6 1
. / 1 6
. / 1
.
ROT (N1 N2 N3 - N2 N3 N1)
Three elements on the rotating stack, the original third element becomes the first element
Such as 3 5 7 Rot...
3/3
5/3 5
7/3 5 7
Rot / 5 7 3
. / 5 7
. / 5
.
-ROT (N1 N2 N3 - N3 N1 N2)
In reverse three elements in the top of the rotary stack, the top element is rotated to the second position
Such as 3 5 7 -Rot............
3/3
5/3 5
7/3 5 7
-ROT / 7 3 5
. / 7 3
. / 7
.
NIP (N1 N2 - N2)
Remove the second element from the stack, this operation is equivalent to Swap DROP, such as 6 2 NIP. Print 2
6/6
2/6 2
NIP / 2
.
2DUP (N1 N2 - N1 N2 N1 N2)
Copy the top two elements of the stack, such as 2 4 2 dup .s will print 2 4 2 4
2SWAP (N1 N2 N3 N4 - N3 N4 N1 N2)
Exchange the two elements of the stack with the third and fourth elements, such as 2 4 6 8 2SWAP .S will print 6 8 2 4
2Drop (n1 n2 -)
Move from the stack to the top two elements
PICK (N1 - N2)
The N1 position (does not include N1) from the top of the stroke, copy the value of this position to the top, and the top of the stack corresponds to the N1 position.
0 PICK is equivalent to DUP
1 PICK is equivalent to OVER
2 4 6 8 2 Pick .S will print 2 4 6 8 4
Roll (n -)
The rotational position N (does not include N) to the top, n must be greater than 0.
1 ROLL is equivalent to SWAP
2 roll equivalent to ROT
2 4 6 8 3 roll .s will print 4 6 8 2
1.5 more Forth word
MOD (N1 N2 - N3)
N1 divided by N2 and left the remaining N3 on the stack.
8 3 MOD. Print 2
/ MOD (N1 N2 - N3 N4)
N1 divided by N2 and put the commercial N4 in the top of the stack, the remainder N3 as the top of the stack.
10 3 / MOD .S will print 1 3
MIN (N1 N2 - N3)
Put the smallest one among N1 and N2 in the top of the stack.
8 3 min.
MAX (N1 N2 - N3)
Put the largest in N1 and N2 in the top of the stack.
8 3 Max. Print 8
Negate (n1 - n2)
Change the symbol of N1.
8 Negate. Print -8
ABS (N1 - N2)
Put the absolute value of N1 in the top of the stack.
-8 ABS. Print 8
2 * (N1 - N2)
Take the N1 by performing an arithmetic shift
8 2 *.
This operation is equivalent to 8 2 * but is executed faster.
2 / (n1 - n2)
The N1 is divided by performing an arithmetic right shift N1.
8 2 /.
This operation is equivalent to 8 2 / but faster.
U2 / (n1 - n2)
Perform 16-bit logic right movement. .
40000 U2 /. Will print 2000
But 40000 2 /. Will print -12768
8 * (N1 - N2)
The N1 multiplication is achieved by performing 3-bit arithmetic shift.
7 8 *. Print 56
This is equivalent to 7 8 * but faster
1 (N1 - N2)
Increase the top elements of the stack 1
1- (N1 - N2)
Minute the top elements of the stack of 1
2 (N1 - N2)
Put the top element plus 2.
2- (N1 - N2)
Reduce the top elements of the stack of 2.
U / 16 (U - U / 16)
u is an unsigned 16-bit integer that implements u divided by 16.
1.6 colon definition
You can refer to other Forth words to define your own Forth word, the method is to use the Forth word: (colon), just like this:
:
Among them: Start a definition,
Here are some examples of definitions:
If you don't like to print the value of the top of the stack, you can define it into = = two equal numbers because an equal number is already a Forth word.
Note: The upper left bracket '(' is a Forth word, which starts from it until the content between the right brackets as a comment. Therefore, they must be separated by spaces, and there must be a space after '(').
: = = (n -) / Print the top value
.;
Impress this colon definition, try again to enter 5 7 = =
: Squared (n - n * * 2) / calculates the square of N, the method is a multiplication
Dup *;
I can try to enter 5 Squared ==
3 Squared = =
7 square ==
: CUBED (N - n ** 3) / Calculator N
DUP / N N
Squared / N ** 2
*; / n ** 3
Here are two useful Forth words
Cr (-) (read as a carriage return)
Enter and wraps on the screen
"(-) (reading point quotation mark)
Print string until close to "
We can also define the following words
: bar (-) / Print a bar
CR. "*****";
: Post (-) / Print a location
Cr. "*"
CR. "*";
: C (-) / Print a C
Bar Post Post Bar;
: F (-) / Print an e
Bar post bar post;
We see that the new Forth word is defined with the previous Forth word. This is the way for Forth. New, more powerful words are constantly defined, when you complete all the programs, the program is still a word.
Your defined words are stored in the Forth Dictionary as the predefined Forth word. The Forth interpreter does not know the difference between the Forth word and the words predefined words. This means that every Forth application will actually be a special language, which is designed to solve your own, special problems.
1.7 practice
A square can be defined by its left upper corner (T L) and the lower right corner coordinates. Let X coordinates increase from left to right, Y coordinates increase from top down. Define three Forth words: Area, Circum, and Center, they will calculate the area, circumference, and center according to the top, left, bottom, right, right.
Area (T L B r - area)
Circum (T L B R - Circum)
Center (T L B R - XC YC)
Test your defined words with the given value below:
Top: 31 10
Left: 16 27
Bottom: 94 215
Right: 69 230
Second class uses F-PC
2.1 Edit the file using the SED
Full-screen editor SED is used to write programs and save them as disk files. For example, in order to write a program to solve the problem of the practice 1.1, we enter the F-PC, enter the OK prompt.
Newfile HW1
This will create a new order file hw1.seq. All F-PC source files are used for .SEQ extension.
The first line of your program should use a / start and write the name of your program or file. When you print a list of programs, the first line of each page will be printed, you can implement it by entering the following command:
FPRINT HW1.
In the second line of the file, you enter one / and one tab, then enter Alt-O P. This will paste the date and time. Now you can enter the full program: / Homework # 1
/ 07/02/89 08: 25: 20.35
/ EXERCISE 1.1 - Lesson 1
/ Find Area, Circumference and Center of Rectangle
: SIDES (T L B R - R-L B-T)
Rot / T B R L
- / T B R-L
-ROT / R-L T B
Swap -; / R-L B-T
: Area (T L B r - area)
Sides *;
: Circum (T L B R - Circum)
SIDES 2 *;
: Center (T L B R - XC YC)
Rot / T B R L
/ T B R L
2 / / t b xc
-ROT / XC T
2 /; / XC YC
Note that a middle word SIDES definition is left on the stack, then the word SIDES is used to define Area and Circum.
F-PC is unsatisfactory, this is, you can use your uppercase or lowercase letters arbitrarily. In this course, we usually do this:
The word defined by your own use of lowercase letters, the F-PC used in our definition uses uppercase letters.
This makes it easy to identify which of the definition words are the definition word of the F-PC, which is what we are defined.
In addition, it is important to note that in the SIDES and CENTER definitions, we use the way to write stack annotations on each row, you will see that this method becomes useful as the stack process continues.
The SED editor has full-featured editing capabilities, which is described in Chapter 5 of the F-PC User Manual, which is included in the F-PC package.
After entering the program, you can exit the SED via the F10 function key. At this time, you can also edit additional files, just enter the file name, if you don't need to edit other files, press the ESC key to enter the F-PC OK prompt state. You can now load and run the program. You can also enter the ed command to enter the editor just now.
2.2 Load and run your program
In order to run the program in HW1.SEQ, you are entered
Fload hw1
The process of loading is to join all the colon definitions in the file to the dictionary, which is the same as you enter all programs in the F-PC interaction mode. Of course, differences between them are all colon definitions now save them on disk and edit them at any time.
If you use the data of the Exercise 1.1 to test them, you can get the following results:
31 16 94 69 Area. 3339
31 16 94 69 Circum. 232
31 16 94 69 CENTER.. 62 42
10 27 215 230 Area. -23921
10 27 215 230 CIRCUM. 816
10 27 215 230 Center.. 112 128
The second area is equal to -23921, there is no meaning, but this is because the value of the area has been greater than 32767, while the 16-bit number represents a negative number at 1 when Bit15 is 1. We can use the Forth word u. (u-dot) to print its true value. Word u. Put the 16-digit number of the stack as an unsigned number, which will produce the following results
10 27 215 230 Area u. 41615
2.3 Debugging your program
F-PC has several useful words to help you debug the program. Word See allows you to refraction a word, for example, after a file HW1 in Fload, you can be entered in See Area
See Side
See Circum
See Center
Note that each colon definition is displayed, which is achieved by looking up each word in the dictionary and each definition name.
Word View allows you to see which file exists in a specific definition, and displays each actual definition in a file. Entry:
View Side
It will bring you into the editor's browse mode and displays the definition of SIDES in the file. At this time, you can also browse other definitions of the file. Go to the F10 and ESC to return to the OK prompt, and you can use the ed command to edit the file. View can find the definition of any F-PC word.
F-PC word Debug is a powerful debugging tool that allows you to observe the changes in the stack while each word in a single step execution. Import after fload
Debug Area
When you execute AREA, it will pause and display the contents of the stack before each word in the definition. In addition to any keys other than q, c, n, u, x, and f, such as
10 27 215 230 Area
The word representation is displayed on the top of the screen, the following is displayed at the bottom of the screen with the three empty bobs.
10 27 215 230 Area [4] 10 27 215 230
12648 0: SIDES?> [2] 203 205
12648 2 *?> [1] 41615
12648 4 Unnest?> OK
After each definition name, the stack is performed by the space bar, the number of elements on the stack is displayed in FRT [], and the value of 4 elements on the top of the stack is displayed. Note When the two values 203 and 205 multiply, the accumulation 41615 is shown to -23921, in fact, when the single step is defined above, the value 41615 remains on the stack. If you are joined. (Dot), the value of -23921 will be displayed.
The unbug will terminate Debug, and of course Area will no longer run in debug mode. When you execute in single step, we can also entered:
Q Terminate Debug and perform UNBUG;
C will continue to execute the end of the definition or press
F will be temporarily returned to Forth (sweep
X will trigger the source code list switch and provide all the screens for debugging;
N will recursively enter the word;
U Exit the word called;
S allows you to skip (with high speed) to the next word in the word sense, you can choose to move forward (using ) and move backward (use -) until you reach a word and press
As an recursive example, you can enter Debug Area and then
10 27 215 230 Area
The n key will be recursively to the word SIDES, and the single step is defined by this definition.
2.4 practice
Create a file hw2.seq, and enter the following colon definition in the file
: StackTest (A b -?)
DUP *
SWAP DUP
* ;
Write the stack instructions after each row of this definition.
Read the file with Fload, use the debug single step by this word, you can enter
4 5 stacktest
What is the value on the stack?
The third lesson FORTH works 3.1 variables
Forth word variable is a definition word for defining a variable name if you are in
Variable my.name
Forth will create a new word, its name is My.Name. All dictionary items have a universal format that contains a head.
The header consists of different fields, including View fields, name fields, and link fields. In the F-PC, the header and the body are stored in different segments, that is, in the X86's real mode, 1 M-byte address space is divided into 64 k bytes, and a physical address is The bit address SEG and a 16-bit offset address OFF consist, the complete address form is SEG: OFF. The segment can start at the 16-byte boundary, called a page. Therefore, in order to address any memory bytes, we must specify segment addresses and offset.
The word my.name's dictionary looks like this.
The View field is a character count, which is equal to the offset starting at the colon definition in the file. When using the view command, this field can locate the colon definition in the source program text.
The link field contains a pointer, which points to the LFA of a definition word.
The name field contains the name, but the first byte is the number of characters of the name, and there is 1-31 characters later, that is, the name of the definition.
Pointer to the CFA contains a CFA offset at the code segment. The address of the code segment passes in the F-PC. CS: gives it.
The code field contains a code, which is executed when this definition is interpreted, and the direct string coding is used in the F-PC, and many Forth systems use indirect string coding, where the code field contains a pointer to the execution code. For Variable, the code field contains three bytes of instructions, that is, Call Next, NEXT is the inner interpreter of the F-PC, which will be described later. The CALL command automatically puts the address of the next instruction on the stack, in our system, which is not a real command address, but the address of the parameter field.
For different types of words, the parameter field contains different things, for the Variable word, the parameter field contains the 16-bit value of this variable. The initial value is 0.
If you are in the name of this variable, the Call next command in the code field will be executed, and its role is to leave the address of the parameter field on the stack. If you are joined
MY.NAME.
My.Name's PFA will be printed. Can try it.
3.2 More Content About Variables - Fetch and Store
Forth word:
! (n addr -) ("store")
Put the value N to the address addr, 6 my.Name! The value 6 will be stored in the PFA of My.Name.
@ (addr - n) ("fetch")
Read the value of the AddR position is put on the stack, my.name @. Will print the value of my.name.
Stack variable
The system variable SP0 contains a stack pointer with an empty stack, so
SP0 @
The address of the stack pointer when there is no content is not any content.
Forth word sp @ Returns the last element to press the address after the stack, so it is the current value of the stack pointer.
Forth word depth returns the number of elements on the stack, which is defined:
: Depth (- N)
SP @ SP0 @
SWAP - 2 /;
Note Because each element on the stack contains two bytes, the number of stack elements must be divided by 2
3.3 constant
Forth word constant is a definition word for defining a constant, for example, you enter 25 Constant Quarter
Name Quarter will perform a dictionary in the following ways:
The code field contains three bytes, which correspond to the instruction call doconstant. Doconstant pops up PFA from the stack (it is pressed by the Call instruction), then put the FPA's value on the stack, so if you enter
25 Constant Quarter
Then enter the quarter again. The value 25 is printed. Note that the number of Constant stores is 16-bit unsigned number.
3.4 Forth colonial
Forth Word: (Reading "colon") is also a definition word, which allows you to define a new Forth word if you enter:
: Squared dup *;
Coroni word: is executed, it creates a Forth word Squard in the dictionary, as shown below:
The 3 bytes included in the code field correspond to the instruction JMP Nest. The code in the NEST location is part of the inner interpreter, and we will describe later in this tutorial.
The parameter field contains the offset of the list segment, called LSO, which is added to the list segment address, which is stored in the variable XSEG. In the segment address of the result, the segment address is stored in the register ES, constitutes Squared The defined code field list stores the start address ES: 0.
Unnest is the address of another subroutine, which is also part of the Forth inner layer interpreter.
3.5 arrays
If you want to create a group with five 16-digit arrays if you enter:
Variable my.Array
Then Forth will create a dictionary entry My.Array it contains a 16-bit value in the parameter field.
Here we have not given the first, pay attention to the parameter field contains 16-bit values of 2 bytes.
The Forth Word Allot will join the n-byte will be on the code field of the dictionary, where n is the value obtained from the stack when the allot is executed, so
8 allot
8 bytes will be added or 4 words to the dictionary, the code segment part of the My.Array dictionary looks like this:
To print the value of My.Array (3), you must do this:
My.Array 3 2 * @.
3.6 Back to the Stack
You enter a number, it is placed on the parameter stack. All arithmetic operations and DUP, ROT, DROP, SWAP, OVER are on the parameter stack.
Forth has a second stack called return stack. The return stack is used by the inner interpreter of Forth to store the name of the next word when the colon definition is executed, which is also used by a specific Forth word, such as DO.
If you are very careful, then you can also use the return stack, however, you need to emphasize again: careful. You can move forward from the parameter stack out of a number to the return stack, provided that you have to keep it until the end of the colon definition, otherwise, because the correct return address is not placed in the stack, inner interpreter You cannot find the appropriate address.
The following Forth is used to return to the stack, R:
> R (n -) ("to-r")
Pop up the top level of the parameter stack and press it into the stack
For example, 3> R will move 3 to return stack, and leave the argument stack empty.
R> (- n) ("from-r")
Pop up to return the top elements and press it into the parameter stack.
R @ (- n) ("r-fetch") copies the returns stack top element onto the parameter stack.
This is a possible ROT meaning:
: Rot (N1 N2 N3 - N2 N3)
> R / n1 n2
SWAP / N2 N1
R> / n2 n1 n3
SWAP; / N2 N3 N1
3.7 CODE word
The Code word is defined using the 8086 machine language instead of using other Forth word words. Of course, regardless of what is used, the real 8086 machine code must be performed, and the internal interpreter is written with the machine code, and many of the F-PC Forthwords use the machine code to improve the efficiency of the execution. In the seventh lesson, we will discuss how to write your own Forth Code.
Since the F-PC uses direct string technology, the machine code in a CODE word is stored directly in the CFA of the code segment. There are several examples of F-PC primitives, and each word is stored in the first segment as the variables, constant, and colon definitions discussed earlier.
3.8 Forth Dictionary
The Forth Dictionary is a linked list with all the words already defined. These words can be variables, constants, colon definitions, or CODE words. The names of all these words are stored in the header and connected by link field pointers. The code field of each word is specified by the first code field pointer. The code field always contains a real executable code, so it must be at the CODE segment of 8086. In a colon definition, the CFA list of each word in the definition is stored in a separate list, and pointing by storing the PFA pointer in the code segment.
When we use a colon definition to define a new word, it includes a process of depositing this word into a dictionary. F-PC links the names you defined to a clue with 64 entry, and use a hashing mechanism to find the speed.
The next available address of the code segment in the dictionary is specified by here. So, Here is a Forth word, which returns the next available address in the dictionary space on the stack. Variable DP is called a dictionary pointer, including the next available dictionary address, word here is defined:
: Here (- N)
DP @;
(Of course, the F-PC actually uses the Code word to define the here)
After booting a Forth system and appears in the OK prompt, what you are executed is the outer interpretation. When you have entered a word and entered
3.9 table
A table is like an array of constants. You can create an array and then use it! The word is stored to populate it. Another way to create a table is to use Forth word create, its work mode is the same as variable, but is not in the parameter field. For example, if you are in:
Create Table
You can create the following dictionary item
As in the case of Varible, the code field contains three bytes corresponding to the NEXT of the instruction Call next here is the inner interpreter of the F-PC. When the word table is called, the CALL instruction will leave the address of the parameter field on the stack.
The dictionary pointer DP here is the value of the PFA of the table. Forth word, comma will store the value on the stack to the point of the dictionary pointer, that is, the next available location of the dictionary. So if you enter Create Table 5, 8, 23, will create the following dictionary item: You can now define a new word name @table
: @Table (ix - n)
2 * Table / 2 * IX PFA
@; / @ (PFA 2 * ix)
For example, 2 @table will return 23 to the top of the stack.
3.10 characters and byte data
Character (ASCII code) data can be stored in one byte. Data can be stored and read in a single-byte manner with the following Forth word.
C, (C -) ("c-comma")
Store the low valid byte (LSB) of the stack to Here (the next available location of the dictionary)
C! (C addr -) ("c-store")
Store the LSB to the ADDR position of the stack top element.
C @ (addr - c) ("c-fetch")
Read the bytes at Addr and put the LSB on the stack
You can also create byte constant tables in the following way instead of the constant table:
CREATE TABLE 5 C, 8 C, 23 C,
Then you can define a word c @ Table
: C @ Table (ix - c)
Table C @;
2 c @ Table will return 23 to the top of the stack
Note the difference between @Table and 3.9 @Table definitions
3.11 Find Dictionary Address
The following words can be used to locate and check the Forth Dictionary:
'(- CFA) ("Tick")
The statement 'table will put the table's CFA on the stack.
> Name (CFA - NFA) ("To-Name")
Convert code field address CFA (in code segment) to the name field NFA (in the first section)
> LINK (CFA - LFA) ("To-link")
Conversion code field address CFA (in code segment) to link field address LFA (in the first section)
> BODY (CFA - PFA) ("To-Body")
Convert code field address CFA (in code segment) to parameter field address PFA (in code segment)
You can also get the code field address by using the following words:
Body> (PFA - CFA) ("from-body")
Name> (NFA - CFA) ("from-name")
LINK> (LFA - CFA) ("from-link")
You can also link from your name or link to the name
N> LINK (NFA - LFA) ("Name-to-link")
L> Name (LFA - NFA) ("Link-to-Name")
The Forth Word HEx will change the number of references to the output of the output. Word Decimal will change the number of reference to 10, and you can also change the value of the variable base to any number. For example, HEX is defined in this way.
: HEX 16 Base!;
Note that in the HEX definition, the number of groups must be 10.
The Forth word U. The value of the top of the stack as the unsigned number of 0 to 65535, or if it is HEX mode, it is 0000 to FFFF. As an example, in order to print the name field of the word OVER, you can be entered: Hex 'Over> Name U. Decimal
Forth word ldump (seg off #BYTES -) can be used to get a #BYTES-byte memory image from SEG: OFF.
YSEG @ 'over> Name 20 ldump
You can see the name field of Over. .
3.12 name field
If you use a colon to define a new word, such as Test1, you will create the following name fields:
If the priority is set to 1, this word will be executed immediately. The word is discussed in the 9th lesson.
If the use is 1 is 1, this word is not visible in dictionary search. This bit is set when the colon defines.
Enter the following blank colon definition:
: Test1;
Then check the name field:
YSEG @ 'Test1> Name 10 ldump
The six 16-specific numbers of the above figure will be displayed.
Note that the highest significant bit of the first and last bytes of the name word Xi section are set to 1. The maximum number of characters actually stored in the name field is determined by the variable width. OK, for example:
10 width!
Will make the name field maximum stores 10 characters. F-PC Set the width default value of 31 - this is its maximum possible value.
3.13 F-PC inner interpreter operation
The following figure illustrates the F-PC inner interpreter operation.
NEXT
Lodsw Es: / Load Ax with CFA At Es: Si & Inc Si
JMP AX / EXECUTE The Code At The CFA in AX
Nest
XCHG BP, SP / PUSH IP = ES: SI
Push ES / On The Return STACK
Push Si
XCHG BP, SP
MOV DI, AX / AX = CFA of Word to Execute
MOV AX, 3 [Di] / GET LSO AT PFA
Add Ax, XSEG / AND Add to XSEG
Mov ES, AX / PUT THIS SUM IN ES
Sub Si, Si / Make New IP = ES: Si = ES: 0
JMP> Next / Go To> Nest
Unnest
XCHG BP, SP / POP IP = ES: Si
POP Si / from the return stack
POP ES
XCHG BP, SP
JMP> Next / Go To> Next
The inner interpreter contains three subroutines next, Nest, and Unnest. An interpretation pointer or a command pointer IP pointing to the memory location of the LIST segment, here is the next code segment address of the word to be executed. In the F-PC, this instruction pointer contains two parts, ES: SI.
Suppose is as shown in the figure above, this pointer points to the CUBED defined Squared CFA, the subroutine next put this CFA in a word register W (which is AX in the F-PC), and puts IP (Si) increment 2 It points to the current defined next word (*), then executes the code at the CFA in W.
In this case, the CFA code defined by the colon is a command that jumps to the subroutine NEST. As shown above, the Nest will put the IP (ES and SI) to return the stack so that the program can be found when the unit will be executed in the future. A method of a word.
Nest then gets the offset LSO of the LIST segment is used for the word Squared, plus the base address XSEG of the list segment and stores this value into ES, and set the Si to 0 to make the new IP value of ES: 0, It points to the first word defined by Squared, then jump to Next repeating this process, this time is the first word DUP for Squared. Since DUP is a Code word, its actual machine code is in its own CFA location, this code will execute when NEXT is executed. The last instruction defined by the DUP is another instruction that jumps to Next, but now IP will increase and point to the * CFA. This is another Code word, execute, and jump again to Next.
The last word defined by the colon is Unnest. When the semicolon in the colon character; when executed, UNNEST CFA is added to the Dictionary LIST section. Unnest's code segment contains the above machine code, which pops up IP (Si and ES) from the return stack and jumps to Next. Because this is pressed into the stack when Squared is executed, it points to the next word of the squared, this word * is the next word to be executed.
This is the work of Forth. The colon definition is stored as a list of CFAs in the LIST segment. When the CFA is to execute, the IP is incremented and pressed into the stack and changing the first CFA in the new word definition that will be executed. If the CFA is a CODE word, the actual machine of the CFA position The code is executed. This process continues to perform with actions that jump to Next at the end of each word.
3.14 practice
Define colon
: Squared dup *;
with
: Cubed Dup Squared *;
Use the F-PC word '("Tick"),> LINK ("to-link"), and LDUMP (SEG Off #Bytes -) to answer the following questions:
1) What is a code segment? CS:?
2) What is the first section YSEG?
3) What is the list XSEG?
4) What is Squared CFA?
5) What is Squared LFA?
6) What is Squared NFA?
7) What is Squared PFA?
8) What is CUBED CFA?
9) What is CUBED's LFA?
10) What is CUBED's NFA?
11) What is CUBED PFA?
12) Draw Squared's head icon and mark the 16-enabled value at all locations. What is the value of the ^ CFA location? Draw Squared CFA and PFA fields and give the List segment of the dictionary. Give all address values in the dictionary.
13) What is the dictionary of Cubed definition? What is the name of the word?
14) What is the address of Nest?
15) What is DUP CFA?
16) What is * CFA?
17) What is the address of UNNEST?
Fourth lesson FORTH judgment
4.1 Branch Directive and Circulation
All computers must have some way to generate conditional branches (if ..... "and implementation cycles, Forth uses the following" Good Definition "structure:
IF ... Else ... then
DO ... loop
Begin ... Until
Begin ... While ... Repeat
Begin ... Again
The working mode of these statements is different from them in other languages. Word IF, unsil and while runtime I wish to have a True / False flag on the stack. The value of a FALSE flag is 0, and the value of a TRUE flag is -1.f-pc definitions two constants.
-1 Constant True
0 Constant False
The logo can be generated in a variety of ways, but the usual way is to use certain conditional expressions, which leave the flag on the stack.
Let's first take a look at the FORTH condition word and give some examples of branches and loop statements:
4.2 Conditions and True / False Sign
The following FortH condition word generates a TRUE / FALSE logo:
<(N1 N2 - F) ("Less-Than")
If N1 is less than N2, the flag f is true.
> (N1 N2 - F) ("Greater-Than")
If N1 is greater than N2, the flag f is true.
= (N1 N2 - F) ("equals")
If N1 is equal to N2, the flag f is true.
<> (N1 N2 - F) ("Not-Equal")
If N1 is equal to N2, the flag f is true
<= (N1 N2 - F) ("Less-Than Or Equal")
If N1 is less than or equal to N2, the flag f is true
> = (N1 N2 - F) ("Greater-Than or Equal")
If N1 is greater than or equal to N2, the flag f is true.
0 <(n - f) ("Zero-Less")
If n is less than 0 (negative number), the flag f is true
0> (n - f) ("Zero-Greater")
If n is greater than 0 (positive), the flag f is true.
0 = (n - f) ("Zero-Equals")
If n is equal to 0, the flag f is true
0 <> (n - f) ("Zero-Not-Equal")
If n is equal to 0, the flag f is true
0 <= (n - f) ("Zero-Less-Than or Equal")
If n is less than or equal to 0, the flag f is true
0> = (n - f) ("Zero-Greater-Than or Equal")
If n is greater than or equal to 0, the flag f is true
The following conditions are compared to two unsigned numbers on the stack.
U If U1 is less than U2, the flag f is true. U> (U1 U2 - F) ("U-Greater-Than") If U1 is greater than U2, the flag f is true. U <= (u1 u2 - f) ("U-LESS-THAN OR Equal") If U1 is less than or equal to U2, the flag f is true. U> = (u1 u2 - f) ("U-Greater-Thanlet") If U1 is greater than or equal to U2, the flag f is true. 4.3 FORTH logic operation Some Forth has a word Not, which can reverse the flag value on the stack. In the F-PC system, the word NOT performs 1 complement code for the word on a stack. As long as TRUE is -1 (16 credit fff), NOT TRUE is False. You have to be careful about whether any non-zero is treated as True, and 0 (FALSE) will not be generated after the completion of the complement operation outside the 16-en-implemented FFFF. You can use comparison word 0 = to generate a logo. In addition to logical operators NOT, Forth also supports the following two-purpose logic operators: AND (N1 N2 - and) Leave n1 and n2 on the stack This is a bit bit and operation, for example if you enter 255 15 and (Mask Lower 4 BITS) Leave a value of 15 in the top of the stack OR (N1 N2 - OR) Leave N1 or N2 on the stack, which is a bit operation, for example, if you enter: 9 3 or Will leave the value on the stack 11 XOR (N1 n2 - xor) Leave N1 XOR N2 on the stack, which is a bit operation, for example if you enter 240 255 xor (HEX F0 XOR FF = 0F) Leave a value of 15 on the top of the stack 4.4 IF statement Forth's IF statement is different from other languages. A typical if ... Then ... Else statement is probably: IF Else In Forth, the IF statement is like this: Else THEN Note that when the IF word is executed, the True / False flag must be on the top of the stack. If the top is a true flag on the top, The IF word must be used in the colon definition, as an example, define the following words: : iFtest (f -) IF Cr. "True Statements" "Next statements"; : ife.else.test (f -) IF Cr. "True Statements" Else Cr. "False Statements" "Next statements"; Then you enter: TRUE IFTEST False iftest True IF.ELSE.TEST False if.else.test 4.5 DO cycle Forth's DO loop must be used in a colon definition, to illustrate how it works, define the following words: : dotest (Limit IX -) DO I. LOOP; Then you enter: 5 0 dotest Value 0 1 2 3 4 will be printed onto the screen, try it. The DO loop is working like this: word do takes two values from the top of the parameter stack and puts them on the backrest. At this time, the two values are not on the parameter stack. Word loop plus the index value and compares the results with the limit. If the index value after the increment is less than the limit, it is branched to the word below DO. If the index value after the increment is equal to the limit, the word after the loop is branched. We will carefully study how the DO loop is achieved in the ninth lesson. The Forth Word I copies the index value from the return stack to the top of the parameter stack. Therefore, the above example can be explained as follows: 5/5 0/5 0 DO I / IX (ix = 0, 1, 2, 3, 4) . Loop Note that the limits must be larger than the maximum index value you want, for example: 11 1 DO I. Loop Print value 1 2 3 4 5 6 7 8 9 10 Word LOOP FORTH's DO loop index value can be other than 1 other value, then use the word loop to replace LOOP. You can see the work in the following examples: : looptest (Limit IX -) DO I. 2 LOOP; Then you enter 5 0 looptest Value 0 2 4 will be printed. Word loop acquire the value from the top of the parameter stack and add it to the index value of the returned stack, the same as the LOOP, as long as the index value is less than the limit, it branches to the statement behind DO (if The incremental value is positive. If the value of the increment is negative, the loop is exited when the index value of the increment is less than the limit. For example, you can enter : NEGLOOPTEST (Limit IX -) DO I. -1 loop; Then enter 0 10 NEGLOOPTEST Value 10 9 8 7 6 5 4 3 2 1 0 will be printed on the screen. Nested cycle - word j Forth's cycle can be nested. At this time, there must be two pairs of index / limit to be moved to the backrest. Word I Copy the index value of the inner layer loop from the return stack to the parameter stack, and the word J copies the index value of the outer loop from the return stack to the parameter stack. As an example of a nested cycle, the following words are defined: : 1.to.9 (-) 8 1 DO CR 3 0 DO J i . Loop 3 LOOP; If you perform this word, the following content will be printed on the screen: 1 2 3 4 5 6 7 8 9 Do you understand why? Nested cycles are used in Forth than in other advanced languages. A better way is to define a small word, which contains only a DO loop and then calls this word in another loop. Word Leave The Forth word Leave can be used in the DO loop to exit cycles. It is usually used in the IF statement in the DO loop. Word Leave can immediately update the DO loop (the address of the word after loop is saved as the third word) on the returns). Is there a related word? Leave (flag -) exits the DO loop in the top of the stack, which does not use the IF statement. As an example, assume you want to define a word find.n, it looks for an index value specified in the word table (that is, this value is in the table), if you find it, return true, otherwise returning in the stack . First use the FORTH statement constructory: Create Table 50, 75, 110, 135, 150, 300, 600, Will create a table in the code segment The number of values in the table is IMAX (in our case 7). The value to find is n. These values must be in the stack when executed, the following is the definition of find.n: : Find.n (IMAX N - FF | INDEX TF) 0 SWAP ROT / 0 N IMAX 0 DO / 0 N DUP I TABLE / 0 N IX PFA SWAP 2 * / 0 n n pfa 2 * ix @ = / 0 n f IF / 0 n DROP I true / 0 ix tf Rot Leave / IX TF 0 THEN LOOP / 0 N Drop; / 0 | ix TF Research this definition has been until you understand how it works. Typically, when the stack when using the DO cycle is the same when performing DO and when performing LOOP, you often need to use DUP to replicate values in the DO loop and use DROP to remove some values when leaving the loop. Pay special attention to ROT to use the stack before Leave to make the true sign remain on the top of the stack. 4.6 Until cycle FORTH's Until loop must be used in colon definitions, and the format of the unsil loop is: Begin If The following two Forth words can detect and read the input of the keyboard. Key? (- flag) If the keyboard is pressed, return to the true flag. Key (- char) Wait to press and return the ASCII code to the top of the stack. F-PC word Emit (char -) The character corresponding to the stack top ASCII code will be printed on the screen. Define the following words : Dowrite (-) Begin Key / char DUP Emit / Print On Screen 13 = / if Equal to CR Until; / quit Executing this word will print all the characters you entered on the screen until you have entered the 4.7 While cycle Forth's While loop must be used in the colon definition, the WHILE loop format is Begin If As an example, consider the algorithm for the n-step approach: X = 1 i = 2 Do While i <= n X = x * i i = i 1 Enddo Factorial = X The following Forth word calculation : Factorial (n - n!) 1 2 Rot / x i n Begin / x i n 2DUP <= / x i n f While / x i n -ROT TUCK / N i x i * SWAP / N x i 1 Rot / x i n Repeat / x i n 2Drop; / x Note that in order to make the While cycle work normally, the stack arrangement between BeGin and Repeat must be the same. It is also important to note that although the above algorithm uses three variables x, I and n, but Forth is implemented but does not use any variables! This is the characteristics of Forth. You can find that the use variables in Forth are much less than using variables in other languages. You can enter the definition of the following content test 3 Factorial. .0 Factorial. 4.8 exercises Exercise 4.1 The Fibonacci sequence is a numerical sequence, each of which is the sum of the first two numbers it is close to it. So starting a few numbers look like this: 1 1 2 3 5 8 13 21 34 Define a Forth word FIB (n -) It will print all Fibonacci sequences less than n, and test your words by the following method: 1000 FIB Exercise 4.2 Creating a table called Weights, which contains the following value 75 135 175 115 220 235 180 167 Define a Forth word called Heaviest (PFA - Max.Value) It will print the maximum value from the table according to the value of the stack, if you enter Weights Heaviest. Value 235 will be printed Fifth lesson 5.1 double precision A double precision is two 32 digits stored in 16-bit ways on the stack, and its high halfword is at the top of the stack: Double precision stack illustrates the following method (d -) You should include a "decimal point" when you enter a double precision number through the keyboard, which can be in any location. For example, you can enter 1234.56 Integer value 123456 is equivalent to 16-based 1e240, which will be stored on the stack as follows: The variable DPL contains a position of a decimal point, in this example 2. Forth word D. You can print the value of the double precision on the screen. This way, if you enter D after 1234.56, the value 123456 will be printed on the screen. Here are some double precision words: D (D D - DSUM) Add two double precision and keep a double precision. DNEGATE (D - D) Change the symbol of the double precision S> d (n - d) Turn a single summary to double precision and perform symbol extension. DABS (D - D) Get the absolute value of the double precision D2 * (d - d * 2) 32-bit left shift, equivalent to D multi 2. D2 / (d - d / 2) 32 digits right shift, equivalent to D except 2. DMIN (D1 D2 - D3) D3 is smaller in D1 and D2. DMAX (D1 D2 - D3) D3 is greater in D1 and D2. D- (D1 D2 - D1-D2) Double precision subtraction leaves a difference in double precision. Note: D- definition is : D-DNEGATE D ; ? DNEGATE (D1 N - D2) If n <0 DNEGATE D1., Pay attention? Dnegate definition is :? DNEGATE (D1 N - D2) 0 DNGATE THEN 5.2 Dual Accuracy Comparison Operation Below is the definition of double precision comparison operations : D0 = (d - f) / flag is true if D = 0 =; : D = (D1 D2 - F) / FLAG IS TRUE IF D1 = D2 D- D0 =; : DU <(UD1 UD2 - F) / FLAG IS TRUE IF UD1 Rot Swap / UD1L UD2L UD1H UD2H 2DUP u ud1l UD2L UD1H UD2H F IF / UD1L UD2L UD1H UD2H 2Drop 2Drop True / F Else <> / ud1l ud2l f IF / UD1L UD2L 2Drop False / F Else U f THEN THEN F-PC 3.5 actually uses a Code word to define DU <, which uses two double-precision subtractions: D <(D1 D2 - f) / flag is true if D1 2 PICK / D1L D1H D2L D2H D1H OVER = / D1L D1H D2L D2H f IF / D1L D1H D2L D2H Du f Else NIP ROT DROP / D1H D2H f THEN : D> (DI D2 - F) / FLAG IS TRUE IF D1> = D2 2swap D <; 5.3 Multiplication and division Basic multiplication and division operations are as follows, all other multiplication and division operations are implemented based on these operations: Um * (UN1 UN2 - UD) The unsigned 16-digit UN1 multiplied by the unsigned 16-digit UN2, returns 32-bit product UD. This word uses 8088/8086 MUL instructions. UM / MOD (UD UN - UREM Uquot) 32-bit unsigned integer UD divided by 16-bit unsigned integer UN returns unsigned business and unsigned value UREM. This word uses 8088/8086 DIV instructions. If the high halfword of UD is greater than or equal to UN, the business cannot be placed in 16 bits. Under this condition, the 8088/8086 DIV instruction will generate an exception of "Divide By Zero". Forth word UM / MOD detects this situation, returns 16 binding FFFF as merchant and remainder when the merchants cannot put in 16 bits. The following F-PC will multiply the two 16-bit signed numbers and leave a 32-bit product. F-PC 3.5 emission This word is a code word, and uses 8088/8086 Imul instructions : * D (N1 N2 - D) 2Dup XOR> R / Save Sign Of Product ABS SWAP ABS Um * / unsigned multiply R>? DNEGATE; / FIX SIGN The following F-PC word puts a non-symbolic 32-bit number with a 16-bit unsigned integer and generates a 16-bit non-symbolic remainder and a 32-bit unsigned business. This word has no UM / MOD overflow problem. : MU / MOD (UD UN - UREM UDQUOT) > R 0 R @ / udl udh 0 un UM / Mod / Udl Remh Quoth R> / UDL Remh quoth un SWAP> R / UDL Remh Un Um / mod / recml quotl R>; / Reml quotl quoth 5.4 Down to remove the division The following is two symbol-free / MOD (N1 N2 - Rem Quot) M / MOD (D N1 - Rem Quot) They perform downward division. First click on the following example to see what will display on the screen: 26 7 / MOD. -26 7 / mod. 26-7 / mod. -26 -7 / mod. The result can be summarized as follows: Do you want this result? The second and third results may be strange, but they are correct because we ask for the divisor multiplier plus the remainder equal to the divided number. look: 3 * 7 5 = 26 -4 * 7 2 = -26 -4 * -7 - 2 = 26 3 * -7 - 5 = -26 This result is called downward division, and it is characterized by the same symbols as the divisions of the divisions, and the business is rounding the negative direction. However, this is not the unique operation method of division. In fact, the IDIV instruction of 8088/8086 does not use the downward division division, in the latter case, the remainder symbol is the same as the divided, and the sales size is always the same. In order to see this situation, define the following CODE words and use the IDIV instruction. Prefix Code? M / MOD (D N1 - Rem Quot) POP BX POP DX POP AX IDIV BX 2PUSH END-CODE Now type (note that one point is entered behind 26 to ensure that it is a double precision) 26. 7? M / mod. -26. 7? MOd. 26. -7? M / mod. -26. -7? M / mod. The new result is as follows Note that in this case, the divisor multiplied by commercial plus remains equal to the divided number. Although you might like this division without imaging the division, it can resolve the uncertain problem of rounding to zero. Enter the following Under the next 3 4 / MOD.. 0 3 -3 4 / mod.. -1 1 3 -4 / mod.. -1 -1 -3 -4 / mod.. 0 -3 Non-taking 3 4? M / mod.. 0 3 -3 4? M / mod.. 0 -3 3 -4? M / mod.. 0 3 -3 -4? M / mod.. 0 -3 We can see that the non-downward removal method cannot distinguish between 3 4? M / mod and 3 -4? M / mod, and cannot distinguish between -3 4? M / mod and -3 -4? M / mod. Here is the definition method of m / mod : M / MOD (D N - Rem Quot) DUP / RETURN D if n = 0 IF / DL DH N DUP> R / Save N 2Dup xor> r / save sign > R / DL DH DABS R @ ABS / | DL DH | | N | UM / MOD / UREM Uquot Swap r> / uquot urem n Negate / Uquot Rem (SIGN = Divisor) Swap r> / Rem uquot xor 0 < IF / Rem Uquot Negate / Rem quot OVER / Rem Quot Rem IF / Rem quot 1- / Floor Quot Toward - Infinity R @ / remote quot N Rot - / quot floor.rem = n - rad SWAP / Rem Quot THEN THEN R> Drop THEN So / MOD can be defined like this : / MOD (N1 N2 - Rem quot) > R s> d r> M / MOD; The F-PC actually defines / mod into a Code word, which uses IDIV and then makes the remainder and the business downward. 5.5 16-bit operation The following is given to how 16-bit operations are defined, and these words are arithmetic operations for 16 digits and retain 16-bit results. In fact, the F-PC also defines the equivalent Code word of these words to increase the execution speed. : * (N1 N2 - N) / Signed Multiply Um * Drop; It may also be somewhat surprises, but in fact, as long as you discard a high word that is unsigned 32-bit set, you can get the correct 16-bit results. Of course, it must be between -32768 to 32767 : / (N1 N2 - N) / Signed Division / Mod NIP; : MOD (N1 N2 - REM) / Mod drop; : * / MOD (N1 N2 N3 - Rem N1 * N2 / N3)> R / N1 N2 * D / N1 * N2 (32-bit product) R> / n1 * n2 n3 M / mod; / remote quot In supportance is equal to N1 * N2 / N3, the result of the middle volume is a 32-bit number of N1 * N2 : * / (N1 N2 N3 - N1 * N2 / N3) * / Mod NIP; If you want N1 * N2 / N3 to round into an integer, we can write N1 * n2 / n3 = q r / n3 Here Q is commercial, R is the remainder. In order to round, we add 1/2 of the fractional part of the results. N1 * n2 / n3 = q r / n3 1/2 We can write N1 * n2 / n3 = q (2 * r n3) / 2 * n3 We can then define * / r to compute N1 * N2 / N3: : * / R (N1 N2 N3 - N1 * N2 / N3 ROUNDED) DUP 2SWAP / N3 N3 N2 Rot / N3 N1 N2 N3 * / Mod / n3 r q -ROT 2 * / q n3 2 * r Over / q n3 2 * r n3 SWAP 2 * / Q 2 * R N3 2 * N3 / ; 5.6 Multiplication of Double Accuracy Sometimes we need to put a double precision (32-bit) with a 16-bit and get the double precision result. Of course, in the usual case, if you use a 32-bit number by 16 digits, you can get a maximum result of 48 bits. However, in many cases, you can know that although the final result is more than 16 bits but not more than 32 bits. Suppose A, B, C, D, E, F and G are 16-digit. We can use 32 digits A: B by the result of 16-digit C as follows A b C X ___________________________ DE G f ___________________________ PH PL In the above figure, B is given 32-bit results d: E, and a multiply by c gives 32-bit results g: f. Put these two parts according to the picture is added, and the complete 48 Position. However, we want to remove G to limit the results to 32. The low half word of this product is PL = E, and the high halfword is pH = D F. so we can define this multiplication : Dum * (UD UN - UD) DUP / B A C C Rot / B C C A * / B c f -ROT / F B C Um * / f e d Rot / E D f ; / pl pH 5.7 practice An image system uses a camera to measure the volume of a bearing ball. This system tests the diameter of the bearing ball in pixels. The maximum number of pixels corresponding to the diameter is 256. After the system is adjusted, 100 pixels correspond to 1 cm. Use this system to be measured by the bearing ball diameter from 0.25 to 2.5 cm. Write a Forth word called Volume, which calculates the volume of the bearing ball using the diameter of the pixel in the stack, rounding the nearest cubic centimeter and put the result into the stack. Note: The volume of the ball is (4/3) * pi * r ** 3 Here R is the radius, and the Pi can be approximately (decimal point 7) 355/113. Test the program with the following diameter 25 50 100 150 200 250 Sixth class string 6.1 string input If you need to receive a string from the terminal and put it in a buffer starting from the ADDR, you can use the word: Expect (AddR Len -) This word has limited editing capabilities (for example, you can use Backspace to return), and you can also store the ASCII code you connect continuously until you enter the LEN character or enter a carriage return, the number of characters entered in the variable SPAN. The address of the terminal input buffer is stored in the variable #TIB, the word Tib (- AddR) Put this address on top of the stack. Word Query Enter a string from the keyboard and store it into the terminal input buffer, which can be defined by the following method: : Query (-) TIB 80 Expect SPAN @ #tib! > IN OFF; Variable #TIB contains the number of characters in the TIB. Variable> IN is a pointer to characters in TiB, which is initialized to 0. Suppose you entered the following characters on the word query and press 3.1415, 2.789 Each ASCII code of these characters will be stored in the terminal input buffer, and its address is stored in the TIB. Value 12 will be stored in the variable #TIB. Now, suppose you want to analyze the input stream and get two numbers separated by commas, you can use this word. Word (Char - Addr) It will analyze the input stream and look for character char, then leave a "count string" in the AddR position (this is actually address here). This way, if you entered a phrase below: ASCII, Word Word ASCII will put the comma's ASCII code (HEX 2C) on the stack, then the word Word will store the following results at Here Note that the first byte is a count byte, which indicates the number of bytes contained in a string (here 6), and the other word Word will put a space after the string. At this time, the variable> IN will point to the character after comma (here 2). The next time you call the phrase: ASCII, Word The count string "2.789" will be stored to Here. Although this time did not find a comma at the end of the string, the word Word will always find the string and end. 6.2 ASCII - Binary conversion Suppose you have entered a number 3.1415. We have seen in the fifth lesson. If you are in the interpretation mode when you do this, the value 31415 will be placed on the stack as a double precision (4) ) Place the variable DPL. How do I do this in my own program? For example, you want the user to enter the number and finally put the number on the stack. Forth Number converts the ASCII string into a binary number. The stack illustrates this: Number (AddR - d) This word will be converted to a count string at Addr and put the result as a double precision number onto the stack. The string can represent a real number of current digital bases, with a number of signed numbers, and the number of numbers after the decimal point is stored in the variable DPL. If there is no decimal, the value of the DPL is -1. The number string must be ended by one space, this Also just in line with the condition of word Word. If we want to enter a number (16 digits) from the keyboard, we can define the following words: : ENTER.NUMBER (- N) Query BL Word Number Drop; In this definition, the BL is the space of the ASCII code (ASCII 20h). Thus, the word Word will analyze the input string until the end of a space or go to the string. Number converts the input string into a double-precision number, DROP discards the high word and leaves a single precision result on the stack. Note that this value must be between -32768 to 32767, such a double precision number of high words (so we can simply use the method of discarding high words). 6.3 numerical output conversion In order to print a number 1234 on the screen, we must do this: 1) Use several bases to except this number 2) Turn the remainder to the ASCII code and reverse storage as a numeric string 3) Repeat 1) and 2) until the remainder is 0 E.g : 1234/10 = 123 Rem = 4 123/10 = 12 Rem = 3 12/10 = 1 Rem = 2 1/10 = 0 rad = 1 The following Forth word is used to implement the conversion and print the result on the screen. PAD is a temporary buffer in 80 characters above Here : PAD (--- addr) HERE 80 ; HLD is a variable that indicates the last character stored in a numeric string. <# Start digital conversion and store the numeric string under the PAD : <# (-) Pad HLD! HOLD moves character char to output strings : Hold (char -) -1 HLD ! HLD @ C!;; Forth! (N addr -) adds the value of the AddR position to N. Thus, in the HOLD definition, the value of the HLD is reduced by 1, and then the ASCII code corresponding to the char is stored in the HLD byte position. F-PC has two Code words INCR (Addr -) and Decr (addr -), increment and reduction of Addr content, for example HLD DECR is equivalent to -1 HLD ! INCR is equivalent to 1 SWAP ! Word # (read "Sharp") converts the following characters by performing the above 1) and 2). The divided number must be a double precision. : # (D1 - D2) Base @ MU / MOD / REM D2 Rot 9 Over / D2 REM 9 Rem < IF / IF 9 7 / add 7 to rad THEN ASCII 0 / conv. Rem to ASCII Hold; / insert in string Word #S (read "Sharp-S") Convert other double precision and leave a 0 on the stack : #S (D - 0 0) Begin # / convert Next Digit 2Dup or 0 = / Continue Until Until; / quotient = 0 Word #> Complete the conversion, its role is: Discard the double precision 0 left by #S, calculate the length of the string, reduce the first character address with the last character (PAD) (now stored in HLD), this The length is placed on the top of the stack, and the following is the string address (in HLD). : #> (D - Addr LEN) 2DROP / DROP 0 0 HLD @ / addr Pad over / addr pad addr -; / Addr LEN Forth word Sign is used to determine the number on the stack is not negative, if yes, insert a minus sign in the output string (-) : Sign (N -) 0 < IF ASCII - HOLD THEN These words will be used in the following sections to display values on the screen. 6.4 Screen Output Word TYPE prints a string, its address and length are on the top of the stack, can be defined below: : Type (AddR Len -) 0? Do / addr Dup C @ / addr charr EMIT 1 / next.addr Loop Drop; F-PC actually uses a somewhat definition Typel, which allows you to print the string stored in any segment. Before F-PC 3.5, you must put the segment address memory in the variable Typeseg, and no longer need in F-PC 3.5 (even no longer allowed). Now you use the word Typel (seg addr len -) To print the length of Len in SEG: ADDR string. Strings usually specify one of the following three ways: (1) The first byte of the counting is included in the number of characters. This string is specified by giving a number of bytes of the address (addr -). (2) The address and length of the first byte are given (AddR Len -). (3) A ASCIIZ string specifies (addr -) by giving the first character's address. The end flag of the string is a NUL character (a byte of a value of 0). Forth word count can convert a count string (1) into an address - length string (2), word Count (Addr - Addr 1 Len) Get an address (addr) of a counting string and leave the length of the address and string of the first character (this length is obtained from the byte at the ADDR). Since the address and length of Type requires the string, you must use it for print count strings. Count Type For example: The following words can print anything you entered on the screen (note: 13 is the ASCII code of the Enter key) : echo (-) Query / Get a string 13 Word CR COUNT TYPE; The use of numerical output conversion words in Section 6.3 can be explained by the following words Word (U.) Converts an unsigned number and puts the address and length of the string obtained after the conversion into the stack. : (U.) (u - addr len) 0 <# #s #>; Word u. Print this string and one space : U. (u -) (U.) TYPE SPACE; Word Space prints a space : Space (-) BL EMIT; Here BL is Constant 32, which is a space of ASCII code. Forth word Spaces (N -) Print n space When we want to print numbers in the screen, you need to print in a field that width WID. This can be implemented by the Forth word U.R for an unsigned number. : U.r (u wid -) > R (u.) / addr len R> / addr len wid OVER - SPACES / AddR LEN TYPE; For example, 8 U.R will print an unsigned number, aligned in a field of a width of 8. In order to print a symbol number, when this number is negative, we need to insert a minus sign at the beginning of the string. Word (.) Do this: : (.) (N - AddR LEN) DUP ABS / N u 0 <# #S / n 0 0 Rot Sign #>; Point (.) Is defined as follows :. (n -) (.) TYPE SPACE; Word.R can be used to print a sign of a sign and right alignment in the width of the WID: : .R (n wid -) > R (.) / Addr LEN R> / addr len wid OVER - SPACES / AddR LEN TYPE; Similar words for printing unsigned numbers and numbers : (UD.) (UD - AddR LEN) <# #S #>; : Ud. (Ud -) (UD.) TYPE SPACE; : Ud.r (UD WID -) > R (ud.) / Addr lenr> / addr len wid OVER - SPACES / AddR LEN TYPE; : (D.) (D - AddR LEN) TUCK DABS / DH UD <# # # R r s #>; : D. (d -) (D.) TYPE SPACE; : D.R (UD WID -) > R (D.) / Addr LEN R> / addr len wid OVER - SPACES / AddR LEN TYPE; If you want to clear the screen, use the word Dark (-) Set the cursor position on the screen, you can use the word AT (COL ROW -) For example, the following EXAMPLE_6.4 will clear the screen and print information "Message Starting At COL 20, ROW 10". : EXample_6.4 (-) Dark 20 10 At "Message Starting At COL 20, ROW 10" CR; Seventh lesson CODE word and DOS I / O 7.1 CODE word When we need the largest execution speed or when you need to access your computer hardware, you can define the Forth word in assembly language. This requires the Forth word code to complete. The general format of CODE is: Code END-CODE The word code instead of the colon defined colon and establishes a header of a Forth word, and end-code instead of the definition of the Code word in the semicolon. Next JMP> Next (jumps to the inner interpreter> Next) 1PUSH PUSH AX JMP> Next The Stack and Jumps to> Next) 2PUSH PUSH DX Push AX (Pushes Dx and Ax on The Stack JMP> Next and the jumps to> next) Debug CODE words You can use 8088 Tutor Monitor, which is included in this tutorial. Tutor Monitor uses 8086 assembly language, learn 8088/8086 assembly language book can be found "IBM PC - 8088 Assembly" by Richard E. Haskell. As an example of using the Tutor Monitor disassembled and single-step, the word cmove can be provided with F-PC 3.5, which is from the address Code Cmove (Source Dest Count -) MOV BX, Si / Save Si (IP) MOV AX, DS / COPY DS for Setting ESPOP CX / CX = COUNT POP DI / DI = DESTINATION Address POP Si / Si = Source Address MOV DX, ES / SAVE ES in DX Mov ES, AX / POINT ES To Code Segment RepNZ / Repeat Until Count Is Zero Movsb / Copy DS: Si To Es: DI Mov Si, BX / Restore Si Mov ES, DX / RESTORE ES Next / DONE, JMP To> Next END-CODE When you load this code, 16 entered value 11 22 33 44 55 At the code segment of the source.addr, the actual value of the code segment is forthh word? CS: give, usage show.addrs can print Go to the screen. In the offset dest.addr address, the 5 byte space is retained. When you are playing the word show.addr, the offset address Source.addr, dest.addr, stack top element, CMOCE CFA is also printed on the screen. HEX Create Source.Addr 11 C, 22 C, 33 C, 44 C, 55 C, Create dest.addr 5 allot 5 Constant #BYTES : test (-) Source.addr dest.addr #BYTES CMOVE; : show.addrs (-) HEX CR. "Code segment ="? Cs: u. "Source Addr =" Source.Addr U. "Dest addr =" DEST.ADDR U. "TOP of stack =" SP0 @ U. "Address of cmove =" ['cmove] Litral U. Cr Decimal Words [,] and Litral will be discussed in the ninth lesson. Assuming that the value printed by "show.addrs" is as follows Code segment = 1091 Source Addr = 74E0 DEST ADDR = 74E8 TOP of stack = ffe2 Address of cmove = 477 Your value may be different. If it is different, you should use the corresponding actual value in the exercises below. TYPE Debug Test. Type HEX TYPE TEST. Single step through the top three words, they will print the following stack values: 74E0 74E8 5 Press f to go to forth. TYPE SYS TUTOR - will execute the TUTOR program Display with Tutor Memory TYPE> S1091 To Display The Code Segment. TYPLE / GS1091 to Display the data segment = code segment. TYPE / GOFEDC to DISPLAY The Stack Starting At the top of the Stack (Fee2) Minus 6 in The Data Segment Region. The Data Segment Region. The Data Segment Region.. Value 5 (05 00) Should Be On Top of the Stack, FOLLOWED BY The "Source Addr" 74E0 (E0 74) and the "dest addr" 74e8 (e8 74). Type / Go74e0 to Display The "Source Addr" in The Data Segment. Note That 11 22 33 44 55 IS Displayed. Type> o477 to go to the start of the cmove code. Press the F1 to perform the first two instructions again. Note that the value of Si is moved to BX and the value of DS is moved to AX. The next instruction is POP CX, which is assumed to play the #BYTES (5) value to CX from the top of the stack. However, Tutor's stack pointer and stack segment register does not point to these values, we actually see them at 1091: Fedc. You can change the value of SS and SP, so that the stack segments and code segments are the same, in / RSS1091. RPSFEDC makes the stack pointer equal to the top of the stack (FFE2). Then press F1 to perform POP CX, however, you will encounter a problem, this is how to return to the F-PC. When you quit Tutor, you maybe you may have a DOS, or it may also hang it. A variant is to load the appropriate value 5 to CX with manual methods. Enter / RGC5, then press the right cursor to skip the command POP CX. Use the same method to skip the command POP DI, load DEST ADDR by manual input / RID74E8. Use the same method to skip the command POP SI, and load Source Addr by manual input / RIS74E0. You can perform the following two instructions twice f1. You are now in the REP directive, press F1. Note that the value 11 is copied from the data segment address 74E0 to the extended segment (which actually matches the data segment) address 74E8, and Si and Di have increased 1. This is the work of the command MOVSB - it also only do this. At the same time, CX is reduced from 5 to 4. Press f1 again. Note 22 The data segment position ((74E1) indicated (74E1) is moved to the expanded segment position indicated by the DI, and the value of the CX is reduced to 3. Press F1 three times, the values 33 44 and 55 are moved. Note When the CX is 0, the REP loop is terminated, and the next instruction is ready to execute. Press f1 twice to perform the following two instructions. The following instructions are a JMP instruction, which jumps to> next. To quit Tutor, batch / qd. This will return to your place where you are trying to sys tutor commands in Forth. Go to Forth CMOVE> (Source Dest Count -) is similar to cmove, and the difference is byte by the opposite direction. That is to say, the byte of the highest address moves first. This feature is useful when moving the string upwards because the string may overlap, and if you use CMOVE at this time, it may be destroyed before the source string has not been moved. 7.2 CODE condition When we use Forth compilation jump instructions, Forth word if ... else ... Then, Begin ... While ... Repeat and Begin ... Until can have the following code conditions 0 = jne / jnz 0 <> JE / JZ 0 0> = js > = JL / JNGE <= JNLE / JG > JLE / JNG U U> = jb / jnae / jcu <= jnbe / ja U> JBE / JNA OV JNO CX <> 0 JCX0 As an example, consider the definition of the Forth word? DUP, which only replicates the top of 0 when the value on the stack is not 0: Code? DUP (N - N n | 0) MOV DI, SP MOV CX, 0 [DI] CX <> 0 IF PUSH CX THEN NEXT END-CODE Note When this definition is compiled, statement CX <> 0 is assembled into JCX0 placed in THEN. 7.3 long memory address word Below these long memory address words are very useful for accessing data that is not in code segment: Code @L (seg off - n) / fetch 16-bit value from seg: OFF POP BX / BX = Offset Address POP DS / DS = Segment Address MOV AX, 0 [BX] / AX = DATA AT DS: BX MOV BX, CS / Restore DS To CS Value MOV DS, BX 1PUSH / PUSH VALUE ON Stack END-CODE Code! L (N seg off -) / store 16-bit value at seg: OFF POP BX / BX = Offset Address POP DS / DS = Segment Address POP AX / AX = N MOV 0 [BX], AX / STORE N AT DS: BX MOV BX, CS / Restore DS To CS Value MOV DS, BX NEXT END-CODE Here are some useful long memory characters: C @ L (seg off - byte) / fetch 8-bit byte from seg: OFF C! L (Byte seg off -) / store 8-bit byte at seg: OFF CMOVEL (SSEG SOFF DSEG Doff Count) / Move A Block of Count Bytes from SSEG: Soff to DSEG: Doff Cmovel> (SSEG SOFF DSEG DOFF COUNT) / Move A Block of Count Bytes from SSEG: Soff to DSEG: Doff / MOVES LAST BYTE FIRST to AVOID OVERWRITING MOVED DATA 7.4 DOS word The F-PC has a large number of Forth words for processing DOS file I / O, which are defined in the source file handles.seq and seqRead.seq. This section and the following section will develop a range of file I / O words, which allow you to use and extend processing various files I / O for other DOS operations. These words can be used in combination with F-PC DOS and file I / O word. Variable items / used to record stack depth Variable Handl / File Handle Variable EOF / TRUE IF END-OF-File Was Read Create Fname 80 Allot / 80 Byte Buffer Containing ASCII FileName : {{(-) Depth items! :}} (- c) DEPTH ITEMS @ -; {{.}} Use the number of elements placed on the stack, for example: {{5 2 8}} Will leave the following value on the stack 5 2 8 3 3 on the stack is the number of elements between {{{and}}. : $> Asciiz (Addr1 - AddR2) / Change Counted String to Asciiz String DUP C @ Swap 1 TUCK 0 swap c!;;; DOS 2.0 disk I / O function 2FDOS calls the DOS INT 21H function and uses AX = AH: Al, BX, CX, and DX on the stack. It returns AX, DX and an error flag on the stack. If the error flag is true, the error code is in AX (the third element on the stack). If the error flag is false, the value of AX and DX depends on the function called. FDOS is similar to 2FDOS, but does not return the error flag, which is used to indicate the incorrect function call. Prefix HEX Code 2FDOS (AX BX CX DX - AX DX F) POP DX POP CX POP BX POP AX INT 21 / DOS FUNCTION CALL U> = IF / if carry = 0 Mov bx, # false / set error flag to false ELSE / ELSE Mov bx, # true / set error flag to TRUE THEN Push AX Push dx Push bx NEXT END-CODE Code FDOS (AX BX CX DX - AX DX) POP DX POP CX POP BX POP AX INT 21 / DOS FUNCTION CALL Push AX Push dx NEXT END-CODE Decimal 7.5 Basic file I / O These words can be used for basic file I / O operations, such as open, create, off, and delete files, and read writing from disk files. Open.file (Addr - handle ff | error.code tf) Open a file. Returns a handle under the fake log of the stack, and return an error code under true signs. AddR points to an ASCIIZ string, and the access code is set to 2 for reading and writing. HEX : Open.file (Addr - Handle FF | Error.code TF) 3D02 / ah = 3d; al = access.code = 2 0 ROT 0 SWAP / 3D02 0 0 AddR 2FDoS / DOS Function Call NIP; / NIP DX Close.file Close a file, the file handle is on the top, if it is printed if it cannot be turned off. : close.file (Handle -) 3E00 / AH = 3e SWAP 0 0 / BX = HANDLE 2fdos NIP / NIP DX IF "Close error Number". Abort THEN DROP; CREATE.FILE Create a file - the return value is the same as Open.file AddR points to an ASCIIZ string Attr is the file properties 0 - Normal File 01H - Read Only 02H - Hidden 04h - System 08H - Volume Label 10h - Subdirectory 20H - Archive : Create.File (Addr ATTR - HANDLE FF | Error.code TF) 3C00 / AH = 3C 0 2SWAP SWAP / 3C00 0 Attr Addr 2fdos NIP; / NIP DX Open / CREATE opens it in the file, create a new general file when there is no existence AddR points to an ASCIZ string, returns a handle of open files, if you cannot open an error message. : Open / CREATE (Addr - Handle) Dup Open.file IF DUP 2 = IF Drop 0 Create.File "CREATE ERROR NO.". Abort THEN Else Open error no. ". Drop Abort THEN Else NIP THEN : delete.file (Addr - AX FF | Error.code TF) 4100 0 Rot 0 SWAP 2fdos NIP; : Erase.File ($ addr -) $> asciiz Delete.file IF "Delete file error no." Else Drop THEN Read.File reads the #BYTES by the #BYTES bytes from the file handle to the buff.addr buffer, returns the read byte number #BYTES, if 0, then read the file tail, if it is not successful, the error message is printed. : read.file (Handle #BYTES BUFF.Addr - #BYTES) > R 3F00 / Handle #BYTES 3F00 -ROT R> / 3F00 Handle #BYTES ADDR 2fdos NIP / NIP DX IF Read Error No. "Abort THEN Write.file writes the '#BYTES' bytes of the buff.addr 'draw buffer to file' Handle '. If you do not succeed, you will print an error message. : write.file (Handle #BYTES BUFF.ADDR -) > R 4000 / Handle #BYTES 4000 -ROT R> / 4000 Handle #BYTES ADDR 2fdos NIP / NIP DX IF "Write Error NO." Abort Else Drop THEN Mov.ptr Mobile file handle's file read-write pointer, Doffset is a double-precision 32-bit offset, and Code is a way code, its meaning is as follows: 0 - Mobile file pointer to file start offset 1 - Use OFFSET increment pointer 2 - Mobile file pointer to file tail offset : Mov.Ptr (Handle Doffset Code - DPTR) 42 FLIP / HNDL OFFH 42CD Rot> R / HNDL OFFH 42CD -ROT R> / 42cd HNDL OFFH OFFL 2fdos IF "Move Pointer Error NO.". Abort THEN ReWind.File mobile files Handle's read-write pointer to the beginning of the file : shutind.file (Handle -) 0 0 0 mov.ptr 2drop; Get.Length Returns the 32-bit byte length of the file Handle: get.length (Handle - DLENGTH) 0 0 2 MOV.PTR; Read.file.l reads #BYTES bytes from the files that have been opened from the files that have been opened. Code read.f (Handle #BYTES SEG OFFSET - AX F) POP DX POP DS POP CX POP BX Mov Ah, # 3f Int 21 U> = IF Mov bx, # false Else Mov bx, # TRUE THEN MOV CX, CS / Restore DS MOV DS, CX Push AX Push bx NEXT END-CODE Write.file.l Write #BYTES bytes to an open file handle, the data to be written is in the extended memory seg: offset. Code Write.L (Handle #BYTES SEG OFFSET - AX F) POP DX POP DS POP CX POP BX Mov Ah, # 40 Int 21 U> = IF Mov bx, # false Else Mov bx, # TRUE THEN MOV CX, CS / Restore DS MOV DS, CX Push AX Push BX NEXT END-CODE The FindFirst.dir lookup file directory's first match, the file indicator is located in the ASCIIZ string of Addr. Code Findfirst.dir (AddR - f) / Search Directory for First Match POP DX / DX = Addr of Asciiz String Push DS / Save DS MOV AX, CS MOV DS, AX / DS = CS Mov CX, # 10 / attr inCludes Subdirectories Mov AX, # 4e00 / ah = 4e INT 21 / DOS FUNCTION CALL JC 1 $ / if no error Mov AX, # ff / flag = TRUE JMP 2 $ / ELSE 1 $: MOV AX, # 0 / flag = false 2 $: POP DS / Restore DS Push AX / PUSH FLAG ON Stack NEXT END-CODE Findnext.dir lookup file directory's next match, file description at Addr Code FindNext.dir (- f) / Search Directory for Next Match Push DS / Save DS MOV AX, CS MOV DS, AX / DS = CS MOV AX, # 4f00 / ah = 4f INT 21 / DOS FUNCTION CALL JC 1 $ / if no error Mov AX, # ff / flag = TRUE JMP 2 $ / ELSE 1 $: MOV AX, # 0 / flag = false 2 $: POP DS / Restore DS Push AX / PUSH FLAG ON Stack NEXT END-CODE Set-DTA.Dir Sets the disk transport area DTA address Code Set-DTA.Dir (Addr -) / Set Disk Transfer Area Address POP DX / DX = DTA Address Push DS / Save DSMOV AX, CS MOV DS, AX / DS = CS MOV AX, # 1a00 / ah = 1A INT 21 / DOS FUNCTION CALL POP DS / Restore DS NEXT END-CODE Decimal 7.6 read number and string The following words can be used to read bytes, numbers, and strings from disk files. Get.fn Enter a file name from the keyboard and puts the FNAME as an ASCIIZ string. : get.fn (-) Query BL Word / Addr DUP C @ 1 / addr CNT 1 2DUP / AddR Len Addr.end 0 Swap C! / Make Asciiz String SWAP 1 Swap / Addr 1 Len FName swap / from to lin CMOVE; Open.FileName Enter a file name, open this file, and store the file handle into the variable handl. : Open.FileName (-) Get.fn FName Open / CREATE Handl! EOF? If you read a file end value (EOF = true), you exit this word containing EOF? : EOF? (-) EOF @ IF 2R> 2Drop exit THEN Get.next.byte derived from disk files, file handle in HANDL, if EOF sets the variable EOF true. : get.next.byte (- Byte) HANDL @ 1 Pad Read.File IF False EOF! Pad C @ Else True EOF! THEN Get.next.val reads the value (2 bytes) of the next word from the file, and the file handle is in HANDL. If the end of the file is reached, the variable EOF is true. If the file is stored in the file is not an ASCII code, it is actually The number is very useful. : get.next.val (- n) HANDL @ 2 Pad Read.File IF False EOF! Pad @ Else True EOF! THEN Get.next.dval reads 32-bit values (4 bytes) from disk files, and file handles in HANDL. If the file is over, the EOF variable is true. If the file is stored in the file is not an ASCII code, this word is very useful. : get.next.dval (- d) HANDL @ 4 Pad Read.File IF False EOF! Pad 2 @ Else True EOF! THEN Parenchk If the stack is a '(' read file until the character ')' is read. If EOF exits. : Parenchk (Byte - Byte) Dup ASCII (= IF Drop Begin GET.NEXT.BYTE EOF? ASCII) = Until GET.NEXT.BYTE EOF? THEN Quotechk If the byte on the stack is quotation marks ("), read file until byte" is read. If you read EOF, you exit. : quotechk (Byte - Byte) Dup ascii "= IF Drop Begin GET.NEXT.BYTE EOF? ASCII "= Until GET.NEXT.BYTE EOF? Then;? Digit Check the byte on the stack is not a corresponding ASCII code corresponding to the quadrature. :? Digit (Byte - byte f) DUP BASE @ Digit NIP; Get.next.digit Get a legitimate ASCII number from disk files, if you read EOF, exit. : get.next.digit (- DIGIT) Begin GET.NEXT.BYTE EOF? PARENCHK EOF? Quotechk EOF? Digit Not While Drop REPEAT; Get.digit / minus gets a legitimate ASCII number or a minus number from the disk file. If you read EOF, you exit. : get.digit / minus (- Digit Or -) Begin GET.NEXT.BYTE EOF? PARENCHK EOF? Quotechk EOF? DUP ASCII - = Swap? Digit Rot or Not NOT While Drop REPEAT; Get.next.Number reads a sign number stored in the disk file and converts it into a 16-bit integer, if you read EOF, exit. : get.next.number (- n) {{GET.DIGIT / minus EOF? / buy {{}} to store Begin / Consotrutive Digits Get.next.byte Eof? / on the stack. PARENCHK EOF? / ignore (...) Quotechk Eof? / and "..." Digit Not Until Drop}} DUP PAD C! DUP PAD BL OVER 1 C! SWAP 0 DO / MOVE DIGITS ON Stack Swap over C! 1- / to Counted String As Pad Loop Number Drop; / Convert to Number ? Period test a byte is a decimal point. Note the logo as a secondary stack top element. :? Period (byte - f byte) Dup ASCII. = SWAP; Get.next.dnumber reads a symbol real number stored in the disk file and converts it into a symbolic double precision number to put it on the stack. The number of numbers after the decimal point is placed in the variable DPL, if reading The EOF exits it. : Get.next.dnumber (- DN) {{GET.DIGIT / minus EOF? Begin GET.NEXT.BYTE EOF? PARENCHK EOF? / Similar TO Quotechk EOF? / get.next.number ? Period / But include Period Digit Rot or Not / in Number String Until Drop}} DUP PAD C! DUP PAD BL OVER 1 C! SWAP 0 DO Swap over C! 1- Loop Number; / Convert to Double Number Get.next.String reads the string included in the quotation marks in the disk file and stores it in a counting string at the AddR address. : Get.next.String (- Addr) / Counted String Begin GET.NEXT.BYTE EOF? ASCII "= Until 0 PAD 1 BEGIN / CNT AddRget.next.byte EOF? DUP ASCII "<> While OVER C! SWAP 1 swap 1 Repeat 2Drop Pad C! PAD; 7.7 numbers and strings Send.byte Enter a byte to the open file, the handle of the file in HANDL. : send.byte (Byte -) Pad C! HANDL @ 1 Pad Write.file; Send.Number Write an Symbolic 16-digit as an ASCII string to open files, the handle of the file in HANDL. : send.number (n -) (.) 0 DO Dup C @ send.byte 1 Loop DROP; Send.Number.r Write an open file as an ASCII string as an ASCII string, which will be left to a field that is grouped in the LEN and pops with ASCII spaces. : send.number.r (n l -) > R (.) R> Over - 0 DO Bl Send.byte Loop 0 DO DUP C @ send.byte 1 Loop DROP; Send.dnumber Write an symbolic 32-bit number as an ASCII string to open files, the file's handle is determined by the content of the DPL in the handle of the DPL. : Send.dnumber (D -) / DPL = #digits instil dec. point Tuck dabs <# dpl @? Dup IF 0 do # loop ASCII. Hold THEN #S Rot Sign #> 0 DO DUP C @ send.byte 1 Loop Drop; : Send.val (N -) / Send 16-Bit Value Pad! Handl @ 2 Pad Write.file; : Send.dval (D -) / Send 32-Bit Value Pad 2! Handl @ 4 Pad Write.file; : Send.String (Addr -) / Addr of Counted String Dup C @ SWAP 1 swap 0 DO DUP i C @ Send.byte Loop DROP; : send.crlf (-) 13 Send.byte 10 seund.byte; : send.lf (-) 10 seund.byte; : send.cr (-) 13 seund.byte; : send.tab (-) 9 seund.byte; : Send. ((-) ASCII (Send.byte; : Send.) (-) ASCII) seund.byte; : send., (-) ASCII, Send.byte; : send. "(-) ASCII "seund.byte; : Send. "String" (AddR -) Send. " Send.string " Eight lessons definition words 8.1 CREATE ... DOES> Forth Create ... does> Used to define a "definition word", the so-called definition word is the word that can define a new word. The most unique thing to define words is that the runtime behavior of those new words is given by this definition word, we can explain Create ... Does> by the following definitions, before loading these programs Programs loaded with the seventh lesson). : TABLE (List N ) Create 0 DO C, Loop DOES> (ix - c) C @; This word can define a new word Junk like this. 3 15 7 2 4 Table Junk When the word table is executed, the Forth word between Create and Does> in the table is executed. This will cause the word JUNK to be added to the dictionary, and these values are stored in the PFA of Junk. JUNK's code field contains a Call instruction that will make the Forth word after DOES> in the Table definition. Since this is a CALL instruction, the PFA of Junk will place on the stack when these Forth instructions are executed. In this way, when the word JUNK is executed together with an index ix on the stack, the index will be added to the FPA, and C @ will take out the byte at that position. E.g 2 Junk. Will print 15 The work mode of Create ... does> is as follows, when the word is defined, it will produce the following dictionary structure: Note that the code field of JUNK contains a Call instruction, which calls the Call ^ DOES instruction after the Table of the PFA. This call ^ DOES directive is inserted into the Junk code segment when executed by Table (; CODE). This has two effects: First, it puts the JUNK's PFA on the stack, followed, it performs Call Dodoes, which is the Forth word that is executed by the CFA pointed by LSO2. This is exactly those words defined in Does> in Table. It is particularly important that any words defined by Table have the same behavior. This powerful feature will be referenced in the following sections to define a variety of different jump tables. 8.2 A simple jump table As an example of a simple definition word, suppose you want to create a jump table named Do.Key: This table is like this: For example, we have designed a keyboard with 5 keys. When you press a key, the top of the stack will return the corresponding key number 0-5, you want to perform Forth corresponding to the key. Words 0Word, 1WORD, ..., 4WORD CFA stored in the jump table. We want to define a word called jump.table to generate DO.KEY or other similar jumping tables. To produce Do.Key we entered 5 jump.table do.key 0word 1WORD 2Word 3word 4Word Here is the definition of jump.table: : Jump.table (n ) Create DUP, 0? DO ', Loop Does> (n pfa -) SWAP 1 Swap / N 1 PFA 2DUP @> / n 1 PFA (N 1)> Nmax IF 2DROP Else SWAP / PFA N 1 2 * / addr = pfa 2 (n 1) Performthen; In this definition, word Perform will perform the words corresponding to the CFA on the top of the stack. In the DO loop after Create, ', (Tick Comma) is used to store the CFA of the word after jump.table do.Key in the table. 8.3 Jump table with any value The jump table described above has a limit that the index value must be any integer starting from 0. The usual case is that the value in the table corresponds to the pressed ASCII code value, so a more common jump table should be a value (such as an ASCII code) and a CFA entry corresponding to the value, which can be like this. : This table can be used in an editor, where the ASCII code 8 will cause the Forth word BKSPACE to be executed, and the ASCII code 17 (control-q) will cause the word quit, the ASCII code 27 will execute the word escape, if there is no match in the table Value, the default execution word chrout, this word can be displayed on the screen. 3 is (ASCII code, CFA) pair in PFA. In order to generate this table, you can use the word make.table Make.Table DoKey 8 BKSPACE 17 quit 27 Escape -1 chrout The word make.table is defined as follows: : Make.table ( ) Create HERE 0, 0 / PFA 0 Begin BL Word Number Drop / PFA 0 N DUP 1 / PFA 0 N 1 While / PFA 0 n , ', / PFA 0 1 / PFA CNT Repeat DROP ', / PFA CNT SWAP! Does> (n pfa -) DUP 2 / N PFA PFA 2 SWAP @ / n pfa 2 CNT 0 do / n code.addr 2DUP @ = / n addr (n = code) IF / N AddR NIP 2 Leave / -> CFA THEN 4 / n addr Loop Perform; (NOTE: Default Word Has N On Stack) Note that one -1 is used to indicate the default word. DUP 1 before While will make this on -1 becomes 0 after arriving by default, and exits the Begin ... While ... Repeat loop. When do.key executes with a stack of ASCII code, the Does> part of the above-defined action or match the CFA of an ASCII code, or the default word. Note If the default word is executed, the ASCII code is still on the top, so it can be displayed on the screen. 8.4 Use the forth word jump table With the previous definition word make.table has a shortcoming, the ASCII code must be known during the establishment of the table (otherwise you must check the ASCII table). If you can use Forth word ASCII and Control to find these ASCII codes that may be more convenient. such as ASCII A The return value 65 (HEX 41) is on the stack, the same Control Q The value 17 (HEX 11) will be returned to the stack. In addition, if you can include an annotation when constructing a jump watch, it will not have this ability when using make.table. We will define a new word called Exec.Table, which will make us construct the same jump table with the front, by entering: exec.table do.key Control H | BKSPACE (Backspace Key) Control Q | Quit (Quit To DOS) HEX 2B | Escape Decimal Default | chrout The definition of the word exec.table is as follows : EXEC.TABLE ( ) Create HERE 0, / PFA Does> (n pfa -) DUP 2 / N PFA PFA 2 SWAP @ / n pfa 2 CNT 0 do / n code.addr 2DUP @ = / n addr (n = code) IF / N AddR NIP 2 Leave / -> CFA THEN 4 / n addr Loop Perform; (NOTE: Default Word Has N On Stack) Note: The DOES> section of this definition word is the same as the Make.Table definition. However, its CREATE part is simpler. It just puts a 0 PFA of the defined word (DoKey) and put this PFA on the stack, then the program returns to Forth and execute the Forth word Control H. The value of 8 will be left at this time On the stack. The value on the stack is PFA 8. Padm | is a Forth word, it is defined as follows: : | (AddR n - addr) , ', / store n and cfa in table 1 over !; / Increment Count At PFA Note the first line is, ', (comma single quota comma), the first comma writes N (ASCII code) to the created table, single quotes (') get a vertical bar | rear word CFA, the latter comma Write this value into the table. Other Forth words in the same row will be executed, such as (or Decimal. Word Default | Definitions as follows : Default | (Addr -) DROP ',; It will discard the PFA to get the CFA of the default word (chrout), write it into the table over the comma. 8.5 pop-up menu This section will use definition word exec.table to define behaviors corresponding to a pop-up menu button. The words defined here can be used to construct a very good menu-driven program. In the following processing, the ASCII code of these keys is useful. 200 Constant 'Up 208 Constant 'Down 203 Constant 'LEFT 205 Constant 'Right 199 Constant 'Home 207 Constant 'End 201 Constant 'Pg.up 209 Constant 'Pg.dn 210 Constant 'INS 211 Constant 'DEL 8 Constant 'BKSP 9 Constant 'Tab 13 Constant 'ENTER 27 Constant 'ESC 187 Constant 'F1 188 Constant 'F2 189 Constant 'F3190 Constant' F4 191 Constant 'F5 192 Constant 'F6 193 Constant 'F7 194 Constant 'F8 195 Constant 'F9 196 Constant 'F10 These variables are used for each menu: Variable Row_Start / Row # of First Menu Item Variable Col_Start / Col # of First Char in First Menu Item Variable Row_Select / Row # of successEd Item Variable No_Items / NO. Of Menu Items Prefix Read characters and properties at the current cursor Code? Char / Attr (- Attr Char) MOV BH, # 0 Mov Ah, # 8 INT 16 / Read Char / Attr MOV BL, AH MOV BH, # 0 And Ah, # 0 Push bx Push AX NEXT END-CODE Writing characters and properties at current cursors Code .CHAR / Attr (attr char -) POP AX POP BX Mov Ah, # 9 Mov CX, # 1 MOV BH, # 0 INT 16 / WRITE CHAR / ATTR NEXT END-CODE Display n (character, attribute) Code .n.chars (n attr char -) POP AX POP BX POP CX Mov Ah, # 9 MOV BH, # 0 INT 16 / WRITE N Chars NEXT END-CODE Get the current video mode Code get.vmode (- N) Mov Ah, # 15 Int 16 / Current Video State Mov Ah, # 0 Push AX NEXT END-CODE : Unused; Move cursor : Inc.curs (-) IBM-AT? SWAP 1 swap at; Anti-rationality draws a character : .char.bar (attr char -) SWAP DUP 2/2/2/2/7 and / SWAP Foreground SWAP 7 and 8 * 2 * OR / And Background Swap.Char / attr; : Togatt (-) CHAR / Attr / Toggle Attribute Of Char .char.bar; / at current cursor location : Invatt (-) / Toggle Attribute of Word Begin ? char / attr dup 32 = not While .char.bar inc.curs Repeat 2Drop; : Invline (-) / Invert Line of Text Begin Invatt / Invert Word Togatt / Invert Blank Inc.curs ? Char / attr / do unsil 2 Blanks NIP 32 = Until; : Movcur (-) / Move Cursor to SELECTED ROW / DOUBLE SPACE COL_START @ row_select @ 2 * row_start @ at; : inv.first.chars (-) NO_Items @ 0 DO I row_select! Movcur Togattloop; : select.first.Item (-) 0 row_select! Movcur inflining; : Inv.field (n -) Movcur / Invert Current Line INVLINE Row_select! / invert line n Movcur INVLINE; The up and down sponsor key will change the selected item : Down.curs (-) Movcur INVLINE Row_select @ 1 dup no_items @ = IF DROP 0 THEN Row_select! Movcur INVLINE; : Up.curs (-) Movcur INVLINE Row_select @ 1- DUP 0 < IF Drop no_items @ 1- THEN Row_select! Movcur INVLINE; Each defined cursor stores the following value in its parameter field | Upper.LEFT.col | Upper.Let.Row | Width | No.Items | The following constants are the offset of each segment: 0 constant [upper.ch.col] 2 constant [Upper.Lt.Row] 4 Constant [width] 6 constant [no.items] To define a specific size menu, you need to enter {{25 [Upper.ch.col] 15 [Upper.LOW] 20 [width] 3 [No.Items]}} Define.Menu Menu1 Definition word define.Menu as follows : Define.Menu (List N ) Create Here 8 Allot Swap / List PFA N 2/0 DO / V1 IX1 V2 IX2 V3 IX3 PFA Swap over / v1 ix1 v2 ix2 v3 PFA Addr Rot Swap! / V1 ix1 v2 ix2 PFA Loop Drop Does> (PFA - PFA) DUP [UPPER.LEFT.COL] @ 1 col_start! DUP [UPPER.LEFT.ROW] @ 1 row_start! DUP [No.Items] @ no_items! Note: This will define the word MENU1, the use of values 25, 15, 20, and 3, corresponding to the size of the menu stored in the parameter field. Recall the seventh lesson double brace {{...}} The number of projects between braces will remain in the top, so you need to load the seventh lesson, then put it into the procedure here, so double big Brackets are defined. When the word MENU1 is executed, its value of its parameter field will be the value stored as the corresponding item col_start, row_start, and no_items of this particular menu. Box & Fill preparation value, it is a F-PC word, see file boxtext.seq for the description of Box & Fill. : ul.br (PFA - UL.COL UL.ROW Br.COL Br.ROW) DUP [Upper.L.col] @ / pfa ul.col Over [upper.l.row] @ / pfa ul.col ul.row 2 pick [width] @ 1- 2 pick / pfa ul.col ul.row br.col 3 roll [No.Items] @ 2 * 2 pick ; define the main menu {{25 [Upper.ch.col] 8 [Upper.LOW] 20 [width] 3 [No.Items]}} Define.Menu main.Menu First menu {{30 [Upper.col] 10 [Upper.Row] 20 [width] 2 [no.items]}} Define.Menu first.Menu : first.Menu.display (-) 0 INV.FIELD / Invert First Item SavesCR / Save Screen First.MENU / GET New Coordinates UL.BR Box & Fill / Draw Box First Sub1 item " BCR BCR. "SECOND SUB1 Item" INV.FIRST.CHARS Select.First.Item; : first.sub1; : second.sub1; : escape.first (-) RESCR Main.Menu Drop 0 row_select! 2R> 2DROP 2R> 2DROP EXIT; : enttbl.first (n -) EXEC: First.sub1 Second.SUB1; : ENTER.FIRST (-) Row_select @ enttbl.first; Exec.Table Do.key.first 'up | Up.curs 'Down | Down.curs ASCII f | first.sub1 ASCII f | first.sub1 ASCII s | Second.Sub1 ASCII s | Second.Sub1 'ESC | escape.first Control M | ENTER.First (Enter Key - Select Item) Default | Unused : first.Item (-) First.Menu.Display Begin Key Do.key.first Again; Second menu {{30 [Upper.col] 12 [Upper.Row] 20 [width] 2 [no.items]}} Define.Menu Second.Menu : Second.Menu.display (-) 1 INV.Field / Invert Second Item SavesCR / Save Screen Second.Menu / Get New Coordinates UL.BR Box & Fill / Draw Box FIRST SUB2 ITEM BCR BCR. "SECOND SUB2 ITEM" INV.FIRST.CHARS Select.First.Item; : first.sub2; : second.sub2; : escape.second (-) RESCR Main.Menu 1 row_select! 2R> 2DROP 2R> 2DROP EXIT; : enttbl.second (n -) EXEC: First.sub2 Second.SUB2; : ENTER.SECOND (-) Row_select @ enttbl.second; Exec.Table do.key.second 'up | Up.curs 'Down | Down.cursascii f | first.sub2 ASCII F | first.sub2 ASCII s | Second.Sub2 ASCII s | Second.Sub2 'ESC | escape.second Control M | ENTER.SECOND (ENTER Key - SELECT ITEM) Default | Unused : Second.Item (-) Second.Menu.display Begin Key do.key.second Again; main menu : main.Menu.display (-) Dark Main.Menu / Get New Coordinates UL.BR Box & Fill / Draw Box First item " BCR BCR. "Second Item" BCR BCR. "quit" INV.FIRST.CHARS Select.first.Item CURSOR-OFF; : quit.main (-) CURSOR-ON DARK ABORT : enttbl.main (n -) EXEC: First.Item SECOND.ITEM Quit.main; : ENTER.MAIN (-) Row_select @ enttbl.main; Exec.Table do.key.main 'up | Up.curs 'Down | Down.curs ASCII F | first.Item ASCII F | first.Item ASCII s | Second.Item ASCII s | Second.Item ASCII Q | quit.main ASCII Q | quit.main Control M | ENTER.MAIN (ENTER Key - Select Item) Default | Unused : main (-) Main.Menu.Display Begin Key Do.key.main Again; 8.6 practice Exercise 8-1 Define a definition word named based. It will create a value output word specified by the specified number, for example 16 based. HEX. The HEX will be defined. For a word, it does not need to change Base with a value of 16-en-printed stack. such as Decimal 17 dup hex. Be printed 11 17 ok Exercise 8-2 Using the vector (that is, a jump table) The following information is printed in the Forth program: Press key information F forth is ful! C Computers CAN Compute J Jump Tables N Pressing the other keys will produce a ringing (using Control G EMIT). Ninth lesson compilation word 9.1 Compilation and explanation The compile word is an immediate word, which means that if they encounter them in a colon definition, they will be executed immediately instead of being compiled into a list segment. Immediate words have a priority in the name field. (See See Lesson 3, Section 3.12). The F-PC is in two possible states: compile or explain. It is in compilation state during compilation in a colon defined, that is, after the word "colon:" and "semicolon;" execution before execution. The system variable state has the following two possible values: True - if compiled False - if explained In order to test the current state, we consider the following two definitions: : 1state? (-) State @ IF "Compling" Else "Interpreting" THEN CR; : 1test (-) 1state?; You put this program and then beyond 1state? with 1Test In each case, it is printed "Interpreting", why? Because, when you print 1STATE? And 1Test. You are in an explanation. How can you print "compiling"? This will take 1state? In 1Test compile, it means that we must design 1State? Set to an immediate word. We can achieve this by adding a word immediate after the semicolon. Let us define : 2State? (-) State @ IF "Compling" Else "Interpreting" THEN Immediate Now it's now below the definition : 2Test 2State?; Note When you are in this definition, as long as you press 2Test Note No things are printed on the screen, because 2State? Nothing to compile into the dictionary, it is only executed immediately. Immediate words are not compiled into the dictionary unless you enforce this. You can force an immediate word to be compiled into the dictionary without performing immediately, which is implemented by the word [compile]. The following word definition 3test is the word 2State? Is compiled instead of being executed: : 3test (-) [Compile] 2State?; What do you think 3Test will print? Try a try. You can also use words [and] in colon definitions to open or close compilation. The definition is: : [(-) State off; immediate Word] Open compilation mode and enter the compilation loop, compile loop includes: DO Get the next word from the input stream, if this is an immediate word, execute this word; Otherwise compile it; If this word is not in the dictionary, turn it into a number and compile it; Until input stream ends As a last example, input : 4test [1State?]; Note When you press 9.2 Compile and [Compile] We have seen: [compile] will be compiled into the list segment on the backup letter. Its definition is: : [Compile] (-) 'X,; immediate Word "Tick" (') puts the next (immediate) CFA on the stack, the word x compiles the integers on the stack to the next available address of the list dictionary. Note [compile] itself is an immediate number, which can be executed during compilation of its word compilation. Sometimes you want to compile a word during runtime, and the word Compile will implement this feature. For example, the definition of "SEMI-COLON" is basically like this: :;;; Compile Unnest / Compile The Unnest Routine Reveal / Make the Colon Word Available [Compile] [/ go to interpreting mode; immediate / do; immediately Note; is an immediate word, being executed when it encounters it in a colon definition. It Compile The CFA of the Unnest subroutine is a list dictionary of the colon definition word, and the word REVEAL makes this colon definition word in the dictionary, then switching in interpretation mode. Although [is an immediate word, it is in the semicolon; "compiled by [Compile] in the definition. The definition of Compile is as follows, when the word contains the compile is executed, it compiles the following non-immediate CFA. : Compile (-) 2R @ / get ES: SI of Next CFA in List SEG R> 2 > R / Inc Si Past Next Word in List Seg @L / Get CFA on Next Word in List Seg , X; / & compile it at run time 9.3 constant Consider the following colon definition : Four (N - N 4) 4 ; The dictionary structure it compiles is as follows Word (LIT) is a Code word, which is defined as follows: Code (LIT) (- N) Lodsw Es: / Get Next Word At Es: Si, Si = Si 2 1PUSH / PUSH IT ON Stack END-CODE So the word (LIT) will put the number 4 into the stack, the instruction pointer ES: Si will point to the CFA. If you have a number on the stack and want to compile it as a constant to the list dictionary, you can use literal, defined as follows: : Literal (n -) Compile (LIT) / Compile (LIT) X, / Plus the value n Immediate / immediately, IMMEDIATELY A very useful feature of the word Literal is that you can calculate a constant in the definition. For example, sometimes we write 2 3 to write 5 more intuitive, you can define FIVE : : FIVE (N - N 5) [3 2 ] Literal ; Of course, if you write this, the last result is the same: : FIVE 3 2 ; However, [3 2 ] Litral has an advantage that constant 5 is calculated during compilation, just executing 5 when running. And 3 2 needs to compile a constant 3 and a constant 2 to the dictionary, and two addition operations are required at runtime. So, using the code generated by [3 2 ] Literal to execute faster and more efficient. 9.4 Conditional Compilation Branch? Branch There are two conditional compilation words BRANCH and? BRANCH being used to define various conditional branch instructions in the F-PC. Word Branch is a Code word, which is defined as follows: BRANCH is compiled into a list section, which is behind it is an offset of the unsatisfactory destination address. Word? Branch on the stack top sign as a false branch to its destination address. It is defined as follows Begin ... While ... Repeat As an example of a begin ... While ... Repeat loop, we recall the definition of "Factorial" in the 4th lesson: : Factorial (n - n!) 1 2 Rot / x i nbegin / x i n 2DUP <= / x i n f While / x i n -ROT TUCK / N i x i * SWAP / N x i 1 Rot / x i n Repeat / x i n 2Drop; / x This definition exists in the list dictionary below: Word begin leaves xher1's address on the top of the stack. Word While Compile? Branch after you put a 0 in xher2. This value 0 will replace the address xhere3 of the word 2DROP later. While also puts the value of XHERE2 on the stack and under xher1. Word REPEAT compiles BRANCH and stores the address of XHERE1 after it, then put XHERE3 in the stack and store it in address seg: xher2. IF ... Else ... then Consider the following colon definition : Test (f - f) IF True Else False THEN Store in the list dictionary Word IF compile? BRANCH follows one 0 at address xhere1. This value 0 will be replaced by the address XHERE3 of the word false later. IF also leaves XHERE1 on the stack. Word ELSE compiles BRANCH follows one 0 at address xhere2. This value 0 will be replaced by the word Unnest address XHERE4 later. Else also leaving the xhere2 value on the stack after retaining the address to the stack and stores it in address seg: xher1. Word THEN put the address xhere4 on the stack and store it in the address seg: xher2. Begin ... Again As an example of using Begin ... Again, see the pop-up menu of the 8th lesson. Its typical form is : main (-) minit Begin Key Do.Key Again; Store as follows in the list dictionary Word begin leaves XHERE1 offset address in the top of the stack, and the word again compiles BRANCH and uses the address, deposits. Begin ... Until The following is an example from Begin ... Until from the 4th lesson: : Dowrite (-) Begin Key DUP EMIT 13 = Until; It will be stored in the list dictionary as follows: Word begin leaves xher1's address on the top of the stack. Word Until compile? Branch and write XHERE1, Note Begin ... Again and Begin ... Until's only difference in agin uses unsil? Branch instead of BRANCH. DO ... loop A DO loop will produce the following list dictionary: Lord DO Compile (DO) follows one 0 at address xher1. This value 0 will later replace the address XHERE2 of the first word after the DO loop. The Do also left XHERE1 value on the top of the stack. Word loop compiling (loop) and writes to address xher1 2. Loop then put address xher2 on the stack and store it into seg: xher1. Run time word (DO) (LIMIT INDEX -) Built the following back stack Running time word (loop) plus the value of the stack 1 and jumps to XHERE1 2 when the overflow flag is not set, if the overflow flag has been set (when INDEX = Limit, the top of the stack is over 8000H), then (loop) 3 projects from the return stack and point the instruction pointer ES: Si to XHERE2. The third item puts XHERE2 in return stack is for Leave to find an exit address. Plus 2 values of the returning stack to 8000h can make the DO loop can correctly handle the LIMIT greater than 8000 hours when the execution (DO) is executed. For example, if the Limit is FFFFH, INITIAL INDEX is, the Initial value O on the returns will be -7FFFH. After this value is added, the overflow flag will not be positioned until the top of the stack is equal to 8000h, that is, the FFFFH cycle. 9.5 practice View the Dictionary structure of the three test words below with words See and LDUMP: : a.test (f -) IF "True" Else False THEN : b.test (-) 5 0 DO I. LOOP; : c.Test (-) 4 Begin DUP. 1- DUP 0 = Until DROP; Please draw a dictionary structure for each word, pointing out the actual value of all fields in the name and dictionary, pointing out the actual effects of words IF, Else, THEN, DO, LOOP, BEGIN, and Uns. Please explain the words. "In A. Test's work, numbers 5, 0 and 4 in B. Test and C. Test. Tenth lesson FORTH data structure 10.1 array Many of this lesson comes from the book of "Object-Oriented Forth" (Academic Press, 1987) in Dick Pountain. We will expand our minds and to actually use the system all memory. F-PC word alloc (#para - #para segment flag) and dealloc (segment - flag) use the DOS function to call AH = 48h and AH = 49 to assign and release the memory, through these words we can define the following words Assign and release the memory. : alloc.mem (size - segment) Paragraph Alloc / DOS Alloc Int 21h - AH = 48H 8 = Abort "Not Enough Memory To Allocate" NIP; / discard #para allocated : Release.Mem (Segment -) DEALLOC / DOS INT 21H - AH = 49H Abort "failed to deallocate segment" ; Word Alloc.MEM requires the number of memory bytes you expected on the stack and returns the segment address of the assigned block. F-PC word : Paragraph 15 U16 /; You can convert the required number of bytes to a 16-byte page number. The word release.MEM will release the memory assigned by alloc.mem. First put the segment address of the expected release block into the stack (this must be the address returned by the ALLOC.MEM call). Now suppose you want to create a certain size array in the extended memory, then use @L and! L to read and write. We can define the following definitions: : Array (SIZE ) Create 2 * Dup Alloc.Mem, / Save SEG Address , / save array size in bytes Does> @; Then you can do this: 1000 array array.name This will create a dictionary project array.name, and allocate a 1000-word memory, then place the segment address of the assigned memory and the size of the array.name, which will put this address when the array.name is called. Go to the stack. The Dictionary Item Array.Name is stored in the memory in the following ways: In order to access array elements array.name (5), you can enter: Array.name 5 @L Use this policy to access arrays in the extended memory have a problem: if you construct a stand-alone system (Turnkey), it will fail. A separate system will delete the first part of the dictionary and construct a .exe file, which contains program words and all F-PC words. When you save this system, you have already defined the code segment section of each array that will be saved, but the memory assigned to the actual array will be lost. This means that when the Turnkey program is running, it must assign the required memory in some way, and store the segment address to the PFA of the array name. We can modify the definition of Array to the TurnKey system as follows. : Array.tk (Size ) Create 0, / Fill in Seg Address Later 2 *, / save array size in bytes Does> @; Note that you should now enter 1000 array.tk array.name You can create a word model array.name and save the size 1000, but do not assign any space for the array at this moment. Memory can be assigned in the following words in the future: : Alloc.Array (CFA -) > Body Dup 2 @ / Get Size in Bytes Alloc.Mem / Allocate Memory Swap!; / Save seg At PFA : allocate.Arrays (-) ['array.name] Literal Alloc.Array; Word Allocate.Arrays should include a similar row in each array defined in your program. You should take this word as part of your initialization program, which makes your Turnkey system can also allocate a memory. You can use the following words to release allocated memory. : Release.Array (CFA -) > Body @ / get segment address Release.Mem; / And Release IT : Release.all.Arays (-) ['array.name] Literal release.Array; You can join a similar row in Release.all.Arrays, as long as you want to release these memories. 10.2 Link Picture In this section, we will write some words to create and maintain the chain table. Each node in the linked list contains 4 bytes, the first two refers to a pointer to the next node, and then two is the value of the corresponding node. When we add a value to the linked list, we first need to get a node from the big pool of the free linked list. When you need to delete a value from the linked list, you need to return this node to the free linked list. You can allocate a large memory block in the memory as a free-linked area, and then link all nodes as follows: The available nodes start from the offset address 4 of the / Variables and constants Decimal 0 Constant Nil2 Constant [FreeEList.Head] 0 Value [freelist.head] value [list.offset] Distribution memory : Release.SEGLIST (-) IF Dealloc 0 = / DOS INT 21h - AH = 49h IF 0!> Else Abort "failed to deallocate THEN THEN : Alloc.SEGLIST (Size -) Release.SEGLIST 2 * 2 * 4 / 4 bytes / node head Alloc.Mem / Allocate Memory !> Create a free linked list nodes: | PTR | VAL | : Allocate.Freelist (Size -) Dup Alloc.SEGLIST / SIZE [list.offset] 2 / next PTR Addr 2 !> [List.offset] / make next PTR Current PTR 1 Do / Do Size-1 Times [list.offset] 4 / next PTR Addr 4 !> [List.offset] / make next PTR Current PTR Loop NIL 4 !> [List.offset]; / [list.offset] -> Eolist : Freeelist (- seg offset) Node handling the word The following word will be in the address seg: Node's address to the seg: List node into one node : node.insert (seg list seg node ---) / INSERT AFTER SEG: LIST 2Over @L / s L s n @L ROT 2 PICK / S L N @L S N ! L / s l n -ROT! L; The following word moves to the node after the seg: list, and put the address of the removed node seg: node on the stack. If seg: list is a header, this word shifts to the first node in the table, if the table is returned to SEG: 0. : Node.Remove (Seg List - Seg Node) 2Dup @L / s L @L 2 Pick Swap Dup / s L s @L @L IF / s L s @L 2swap 2over @L / s @l s l @@ L -ROT! L / S N ELSE / S L s 0 2SWAP 2DROP / S 0 THEN In order to get the node you just removed from the free table, you need to use getNode. : GetNode (- Seg Node) Freelist Node.Remove; In order to put the seg: Node node back to the free list, use Freenode. : Freenode (seg node ---) Freeelist 2Swap / Seg List Seg Node Node.Insert; Word NEWLIST creates a new list in the code segment, the PFA of this header contains the offset address of the header : NewList ( ) Create NIL, / FILL in Node Addr Later Does> (- seg list) In order to create a new table named Sample.list, enter Newlist sample.list You can create a header for this table in Section The following line is included: : fill.newlists (-) GetNode DUP ['Sample.List] Literal> Body! Nil -Rot! L; This technique is used in the Turnkey system, the same as the array we said. You must allocate a memory before you can use any of these data structures: : Init.Data.structure (-) Allocate.Arrays 1200 Allocate.Freelist Fill.newlists; You can test these words now: Init.Data.structure 5 Sample.list Push Use the following push : Push (Value Seg List -) GetNode? DUP IF / V S L S n 4 Roll 2 Pick 2 PICK / S L s N v s n 2 ! L Node.Insert Else "no free space" Abort THEN You can also use the following POP in Sample.list Pop. : POP (Seg List - Value) Node.Remove? DUP IF / s n 2Dup Freenode / Put Node Back in Freeelist 2 @L / Get Value Else "EMPTY LIST" ABORT THEN In order to print the contents of the table Sample.list, you can enter sample.list .all using the following words : .all (seg list -) / Print List CONTENTS Begin / s L OVER SWAP @L? DUP / S N n While 2DUP 2 @L. / S n Repeat DROP; In order to generate all the nodes in the table Sample.list, you enter Sample.List Kill using the following words : Kill (Seg List -) / Reclaim List Space Begin / s L 2Dup node.remove? DUP / S L s N n While Freenode / S L REPEAT DROP 2DROP; The word below is used to test a special word is not in a table, for example: 5 Sample.list? In.list We can 5 to confirm that it will return a logo when you are in the table: :? In.List (Val seg list - val f) > R false -rot r> / 0 v s lbegin / 0 v s L Rot 2 Pick 2 Pick / 0 S L v s L @L? DUP / 0 S L V N n While 3 Pick Swap / 0 s L v s n 2 @l over = / 0 s L v f - True IF V '= V IF NIP NIP NIP TRUE EXIT / V TF THEN / 0 S L V -ROT over swap @L / 0 v s n Repeat NIP NIP SWAP; / V FF Word? POP can be used to return the header of the table at the table. If this table is empty, you will put a fake sign on top of the stack. If you can't determine a table is empty, don't want to quit an abnormally when you are empty, this word is useful: :? POP (Seg List - Value TF | FF) / FF if List is EMPTY Node.Remove? DUP IF / s N 2Dup Freenode / Put Node Back in Freeelist 2 @l true / get value Else Drop False THEN Word? List.empty returns a flag when it is empty :? List.empty (Seg List - f) 2DUP? POP / TRY TO POP IF / if Something in List -ROT PUSH FALSE / PUSH IT Back - Set False Else 2Drop True / Else, Set True THEN Word FindPOS / 35 Sample.list Findpos : FindPOS <(Val seg list - val seg node) Begin / V s L Rot 2 Pick 2 PICK / S L V S L @L? DUP / S L V N n While 3 Pick Swap / S L v s n 2 @l over> / s l v f - true if v '> v IF -ROT EXIT / V S L THEN / S L V -ROT over Swap @L / V S n Repeat -ROT; / V S L Word FindPos> determines the location of a node in the table so that this node is inserted, the table is constructed in the decreasing order. For example, in order to insert a value of 35 in order of decrement order, you need 35 Sample.list Findpos> Push : FINDPOS> (Val Seg List - Val Seg Node) Begin / V s L Rot 2 Pick 2 PICK / S L V S L @L? DUP / S L V N n While 3 Pick Swap / S L v s n 2 @L Over s L V f - True IF V ' IF -ROT EXIT / V S L THEN / S L V -ROT over Swap @L / V S n Repeat -ROT; / V S L The following words can find the address of the Nth node in the node. For example, in order to get the value of the 5th node in the table Sample.list, you can enter: sample.list 5 traverse.n 2 @L : traverse.n (seg list n - seg addr) / Find Address on nth node ? DUP IF / s L n 0 DO / S L Over Swap / S L @L DUP 0 = / s n f IF "Beyond List end" Abort THEN LOOP / S N Then; / s l if n = 0 The following is used to get the number of nodes in a table. E.g Sample.list get. # nodes. Number of print tables Sample.list nodes : Get. # NODES (seg list - n) 0 -ROT / 0 s L Begin / CNT S L Over Swap / CNT S L @L? DUP / CNT S @L @L | CNT S 0 While / cnt s @L Rot 1 -Rot / CNT 1 s @L Repeat DROP; / CNT 10.3 record This part of the word is used to generate a more flexible link recording system, each of which is an independent segment in the memory, which can be linked by the pointer field in the record. We can define any different records that can create any number of record instances and link them to a hierarchy system. The size of the fields in the record can be any size. We explain the use of this recorder set through a simple example of a student record system. Each student specifies the following records: Head sr.Head: 0 includes a segment address of the first student record. The first element of The third field located in [Addr.SR] contains a pointer (segment address) pointing to an address record. This record can contain separate fields for streets, cities, provinces, and postal codes. The 4th field located at the offset address [data.sr] is a pointer (segment pointer) pointing to a data record, which can also contain different fields for gender, age, team, professional and other data. This record can be created by the following words: Variable total.bytes 2 Total.bytes! Name of the declaration field : Field (N ) Create Total.Bytes @, / store offset Total.Bytes ! / Bump Offset Count Immediate DOES> (SEG PFA - SEG OFF) @ / Get Field Address State @ / if compling IF [Compile] Literal / ... Bind Early THEN Constructed a record type (internal use) : make.instance (seg off n --- seg) Dup alloc.mem / allocate fields Tuck 0! L / store instance size DUP 2SWAP! L / Store New Seg At SEG: OFF IMMEDIATE; Create a record definition word : Define-Record ( ) Create Total.Bytes @, / store instance size2 total.bytes! / reset the country DOES> (seg off - seg ') @ Make.instance; 1 Array Sr.Head : Sr.List (- seg off) sr.head 0; The following fields are the offset of the SR node 2 Field [Next.SR] / Pointer (seg addr) to next node 2 Field [name.sr] / pointer (seg addr) to student name 2 Field [Addr] / Pointer (seg addr) To Student Address Record 2 Field [data.sr] / pointer (seg addr) To Student Data Define-Record SR-REC Word Field is a definition word, define the name and the corresponding offset address in the student record Statement Define-Record SR-REC A word will be generated as SR-REC, which will be used later to create an instance of student records To accomplish this example, we can define the following student records The following fields are the offset of the student data node 2 Field [Sex.d] / SEX - 1 Char Counted String M or F 11 Field [Birth.d] / Date of Birth - m / D / YR STRING 11 Field [Enter.d] / Date of Enterance - M / D / YR STRING 2 Field [Major.d] / Major Code 2 Field [GPA.D] / GPA X 100 Define-Record Data-REC The following field is the offset of the name node 24 Field [name.fn] / student name - counted string Define-Record Name-REC The following field is the offset of the address node 16 Field [Street.ad] / Street Address 16 Field [City.ad] / City 3 Field [State.ad] / State - 2 CHAR Abbrev 11 Field [Zip.ad] / Zip Code Define-Record Addr-Rec 0 Value 0 value 0 Value 0 Value The following words are used to create and delete a student record :> End.of.sr.list (seg list - seg end.of.list.node) Begin / S / L 2DUP @L? DUP / S / L / @ L / @L While / S / L / @ L OR / S / L NIP NIP [Next.SR] / @ L / OFF REPEAT; : make.sr.record (seg off -)> End.of.sr.list SR-REC DUP!> DUP 0 SWAP [Next.SR]! L DUP [Name.SR] name-rec!> DUP [addr.sr] addr-rec!> [Data.sr] data-rec!> : ZERO. 0!> 0!> 0!> 0!> : Release1.SR (^ sr -) DUP [Name.SR] @L release.mem DUP [addr.sr] @L release.mem DUP [data.sr] @L release.mem Release.mem; : Release.all.SR (seg off -) 2DUP @L? DUP IF Begin DUP [Next.SR] @L Swap release1.sr? DUP While Repeat 0 -Rot! L THEN ZERO. In order to add a record you can enter sr.list make.sr.record You can then add data to different fields through a keyboard or disk file. E.g 345 The value 345 will be stored in Major Field. Eleventh lesson using an interrupted terminal program 11.1 8086/8088 interrupt In this lesson, we must write a terminal program based on interrupt mode to make us communicate with other computers or download Forth code to a single-chip microcomputer, such as downloaded to the MC68HC11 microcontroller containing Max-Forth. We want to communicate with 9600 baud rate, which means that the interrupts must be used to store the characters, otherwise they will be lost when scrolling on the screen. We can write an interrupt service program that is called once every word after the serial port is received, and the interrupt service program reads the character and stores them into the queue. The terminal main program constantly detects whether the keyboard is pressed and there is no character in the queue. When a key is pressed, the input character will be sent to the serial port. When there is a character in the queue (that is, the serial port receives the character), this character will be displayed on the screen, or you can choose to save to the disk. The segment address and offset of the interrupt service program must be stored in the interrupt vector table of 0 segment memory. The DOS function 25h (the Get Interrupt Vector) can be used for this purpose. And these words are more convenient to implement: Prefix HEX Code get.int.vector (int # - seg offset) POP AX Push ES Push BX / Al = Interrupt Number Mov Ah, # 35 / dos service 35h INT 21 / ES: BX = segment: offset MOV DX, ES / OF INTERRUPT HANDLER MOV AX, BX POP BX POP ES 2PUSH END-CODE Code set.int.vector (segment offset Int # -) POP AX / Al = Interrupt Number POP DX / DX = Offset Addr POP bx / bx = segment addrmov AH, # 25 / dos service 25h Push DS / Save DS MOV DS, BX / DS: DX -> INT HANDLER INT 21 / DOS INT 21H POP DS / Restore DS NEXT END-CODE / Store Interrupt Vector of Routine AT Addr : Store.int.Vector (Addr INT # -) ? Cs: -rot set.int.vector; We need the words of the 7th, 8th and 10 lessons, so we should load them first: Decimal Fload Lesson7 Fload Lesson8 Fload Lesson10 11.2 8250 ACE chip Serial communication is processed by an 8250 Asynchronous Communication Interface Device (ACE) chip, connected from the interrupt line from this chip to the priority controller (PIC) chip, COM1 connection IRQ4, COM2 connection IRQ3. The 8250 MODEM register must be set before the 8250IRQ line output buffer is enabled. HEX 300 Constant Com1 / Base Address for Com1 200 Constant Com2 / Base Address for COM2 0C Constant Int # 1 / Interrupt Number for Com1 0b Constant Int # 2 / Interrupt Number for Com2 EF Constant Enable4 / Interrupt 4 Enable Mask 10 Constant Disable4 / Interrupt 4 Disable Mask F7 constant enable3 / interrupt 3 enable mask 08 Constant Disable3 / Interrupt 3 Disable Mask Default Com1 Com1 Value COM / CURRENT COM BASE ADDRESS INT # 1 value int # / interrupt # for current COM Enable4 Value Enable / Enable Mask for Current COM Disable4 Value Disable / Disable Mask for Current COM The following values are added to the COM base address to obtain the corresponding register address. F8 Constant TXData / Transmit Data REG (Write ONLY) F8 Constant Recdat / Receive Data REG (READ ONLY) FC Constant MCR / MODEM CONTROL REG F9 Constant Ier / Interrupt Enable REG FD Constant LSR / Line Status REG 21 Constant IMASK / MASK REG IN PIC 20 Constant EOI / END OF INT Value 20 Constant OCW2 / PIC OCW2 Variable int.Vec.addr / save int Vector Offset Address Variable int.Vec.seg / save int vector segment address Decimal We use the BIOS INT 14H communication port initialization subroutine (AH = 0) to set the baud rate, which must be set before the MODEM control register interrupt bit is set because INT 14h calls will shield them. The parameters of the table below are used to control the register settings, the baud rate is 300, 1200, 2400, 4800, and 9600, no check, 8 data bits, 1 stop bit. Create Baud.Table 67, 131, 163, 195, 227, Index Baud Rate 0 300 1 1200 2 2400 3 4800 4 9600 Code Init-Com (Mask -) POP AX Mov Ah, # 0 Mov dx, # 0 INT 20 NEXT END-CODE The default baud rate is 9600, if you want to modify the baud rate, you should modify this word. : Get.baud # (- n) 4; : set.baud.rate (-) Get.baud # 2 * Baud.Table @ INIT-COM; 11.3 Queue data structure In the interrupt service subroutine, use a ring queue to store the received characters, the following pointers are used to define this queue: Variable Front / Pointer to Front of Queue (Oldest Data At Front 1) Variable Rear / Pointer to Rear of Queue (Most Recent Data At REAR) Variable Qmin / Pointer to first byte in queue Variable Qmax / Pointer to Last Byte in Queue Variable qbuff.seg / segment of queue 10000 Constant Qsize / Size of Queue in Bytes Initialization queue : initq (-) Qsize alloc.mem qbuff.seg! / allocate memory for queue 0 Front! / Front = 0 0 REAR! / REAR = 0 0 Qmin! / Qmin = 0 Qsize 1- qmax!; / qmax = qsize - 1 Check queue : Checkq (- N TF | FF) Front @REAR @ <> / if front = Rear IF / THEN EMPTY Inline CLI / DISABLE INTERRUPTS NEXT End-inline 1 Front ! / Inc Front Front @ qmax @> / if front> qmax IF Qmin @ front! / then front = qmin THEN QBuff.SEG @ Front @ c @ l / get Byte True / Set True Flag Inline STI / enable interrupts NEXT End-inline Else False / Set False Flag THEN Store the bytes in Al in the queue Label QStore Push Si Push ES MOV Si, QBuff.SEG MOV ES, SI / ES = QBuff.SEG INC REAR WORD / INC REAR Mov Si, Rear / IF Rear> Qmax CMP Si, QMAX JBE 2 $ MOV Si, Qmin / Then Rear = Qmin Mov Rear Si 2 $: CMP Si, Front / IF Front = Rearjne 4 $ / THEN FULL Dec Si / DEC REAR CMP Si, Qmin / if Rear JAE 3 $ / THEN REAR = Qmax Mov Si, QMAX Mov Rear Si 3 $: POP ES POP Si RET 4 $: MOV ES: 0 [Si], Al / Else Store At REAR POP ES POP Si RET END-CODE Interrupt service subroutine, the following programs get data from the serial port and store them into the queue Label int.srv (-) Push AX Push dx Push DS MOV AX, CS MOV DS, AX / DS = CS Mov DX, # COM / IF DATA IS Ready Add dx, # lsr IN Al, DX Test al, # 1 JE 1 $ Mov DX, # com Add dx, # recdat IN AL, DX / Read IT Call QStore 1 $: MOV Al, # EOI Mov dx, # OCW2 Out DX, Al / Clear EOI POP DS POP DX POP AX Iret END-CODE Set interrupt : INT.SETUP (-) 12 COM MCR PC! / MODEM CR OUT2 LO 1 COM IER PC! / Enable Recv Int Int # get.int.vector / save old int vector INT.VEC.ADDR! INT.VEC.SEG! INT.SRV INT # Store.int.Vector; / SET New INT Vector Terminal initialization : Init.Term (-) INITQ / INITIALIZE QUEUE INT.SETUP / SET UP INTERRUPTS iMask PC @ ENABLE AND / ENABLE IRQ4 (Com1 Default) iMask PC!; : disable.term (-) iMask PC @ Disable or / disable IRQ4 (COM1 Default) iMask PC! 0 COM MCR PC! / 0 -> MODEM Control REG INT.VEC.SEG @ / restore Original INT.VEC.Addr @ / interrupt vector Int # set.int.vector; 11.4 Output Characters to Screen and / or Disk The characters in the queue will be printed to the screen, if selected, send it to a disk file: False Value?> Disk / flag to "send to disk" 0 Value col.at / saved cursor position 0 Value Row.at Variable T_Handle / Terminal File Handle Create Edit_Buff 70 Allot / Temporary Edit Buffer : $ HCREATE (AddR - f) / Create File for Count String At Addr Seqhandle HClose Drop Seqhandle $> Handle Seqhandle hcreate; : file.open.error (-) 33 12 65 14 Box & Fill Could NOT OPEN FILE !! "key Drop; These words are used to open a window to enter a file name on the screen and open this file. The data from the serial port will be written to this file, which is called when the F1 button is pressed. : select.nil.file (-) 20 4 60 7 Box & Fill Enter a filename "" "" Edit_Buff over C @ 1 cmove 21 6 Edit_Buff 30 LineEditor IF Edit_Buff $ HCREATE IF File.Open.Error Else SeqHandle> hndle @ DUP HANDL! T_HANDLE! True!>?> Disk THEN THEN :> Term (-) T_Handle @ Handl!;; After pressing the F1 button, the data capture will be opened. : disk.on.nil (-) IBM-AT?!> Row.at!> Col.at Savescr Select.nil.file RESCR COL.AT row.at at; Press the F2 button and open the data capture : Disk.off (-) t_handle @? DUP IF Close.file 0 t_handle! THEN False!>?> Disk; Output ASCII code to serial port : XMT (ASCII -) COM / USE Base Address In COM Begin DUP LSR / WAIT FOR BIT 5 in line status PC @ 32 and / register (tdre) to be set Until TXData PC!; / send data Press CTRL P to open and close the printer :? Print (-) Printing c @ not printing c!;;; Output characters to the screen : do.emit (n -) DUP 13 = / IF CR IF Drop Cr / Do a Carriage Return Else DUP 32> = / ignore other control characters IF Emit Else Drop THEN THEN :? Emit (n -) 127 And / Mask Parity Bit DUP 13 = / ignore control char Over 10 = OR / Other Than Cr And LF Over 32> = OR IF ?> Disk / if Data Capture ON IF Dup> Term send.byte / send to disk THEN DO.EMIT / Send to Screen Else Drop THEN 11.5 download file The following words can be used to download files containing MaxForth code to MC68HC11, and MaxForth reads a line, compiles the word to the dictionary. After reading a line, it will send a wrap (ASCII 10) to the PC. Variable Wait.count Send a string, give its address and length : xmt.str (AddR CNT -) / XMT String Cr 0 DO DUP i C @ Xmt Loop Drop 13 xmt; Waiting for receiving a special character : Wait.FOR (ASCII -) 0 WAIT.COUNT! Begin Checkq / char n tf | char ff IF / char n | char DUP? Emit / char n Over = / char f 0 WAIT.COUNT! Else 1 Wait.count ! False / Char FF THEN Wait.count @ 32000 = / char f f IF Control G Emit 2Drop / Beep "NO RESPONSE ..." Key Drop 2R> 2Drop / Exit Wait.For 2R> 2Drop / EXIT file.Download EXIT / EXIT DO-KEY THEN Until DROP; Download file to MC68HC11 : File.Download (-) Getfile Dark IF $ Hopen IF File.Open.Error Else File: ".SEQHANDLE CR Begin LineRead Count 2- / AddR CNT OVER C @ 26 = not / while not eof While Xmt.Str / Send Line 10 Wait.For / Wait for LF Repeat Close THEN Else 2R> 2DROP EXIT / EXIT DO-KEY THEN 11.6 terminal main program After pressing the ESC button, you will exit the word host. : Esc.host (-) Disable.Term / Disable All Interrupts Disk.off / Close File if Necessary QBuff.SEG @ release.mem / release queue buffer Dark Abort; Jump table for all keys Exec.Table Do-Key Control P |? Print (Printer On / Off) 27 | Esc.host (escape key) 187 | Disk.on.nil (f1) 188 | Disk.off (f2) 189 | File.Download (F3) 190 | Unused (f4) 191 | Unused (f5) 192 | Unused (f6) 193 | Unused (f7) 194 | Unused (f8) 195 | Unused (f9) 196 | Unused (f10) 199 | Unused 200 | Unused (Up) 201 | Unused 203 | Unused (Left) 205 | Unused (Right) 207 | Unused (End) 208 | Unused 209 | Unused (PGDN) 210 | Unused 211 | Unused (DEL) Default | XMT : T-link (-) SET.BAUD.RATE Cursor-on False!>?> Disk Dark "4thTerm is on-line ..." CR CR Init.Term; In order to run the terminal program, Improving Host : Host T-Link Begin KEY? IF Key Do-Key THEN Checkq IF ? EMIT THEN Again;