Forth language concise tutorial

xiaoxiao2021-03-06  15

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 , it will be executed (explained mode);

• If you have entered a number (such as 6) and press , this number will be stored as a 16-bit unsigned number on the stack;

• 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, is your name to define the name of the Forth word, --- is the specific content of this definition word, and the semicolon; end this 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 can return to single-step trial);

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 , you can Use ESC to terminate this process.

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 , the outer interpreter looks out with the word you entered. If you find this word, it executes the code field. If it doesn't find this word, call a word called Number to turn the input string to a number. If the conversion is successful, press this number into the stack, otherwise it shows a message <- what? Tells you that it doesn't understand the words you entered. See 3.13 for a detailed discussion of the inner interpreter.

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 then

Else

In Forth, the IF statement is like this:

if

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, is executed if it is a fake sign on the top of the stack, and is executed. After or is executed, the statement behind the word kil is executed. Else clause is optional

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 Until

If is a fake, the word branch is branched to Begin. If is true, the program executes the word after the Until.

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 key (ASCII code = 13). Note that unsil will move from the stack.

4.7 While cycle

Forth's While loop must be used in the colon definition, the WHILE loop format is

Begin while repeat

If is true, the word between the words while and the repeat is executed, and then bifurcated to the word behind Begin. If is a fake, the word branch behind the REPEAT is branched.

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

IF / UD1L UD2L UD1H UD2H

2Drop 2Drop True / F

Else

<> / ud1l ud2l f

IF / UD1L UD2L

2Drop False / F

Else

U

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

Else

NIP ROT DROP / D1H D2H

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.

You can also write it with a POSTFIX (suffix). We recommend using the Prefix prefix format, so assembly language is similar to the standard 8086/8088 assembly language, so you need to give the word prefix before Code compile.

can be any of these instructions below:

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 mobile bytes to address , and assume the status register The direction flag is 0 (by executing the CLD instruction), the string protording MOVSB ​​will automatically increment SI and DI.

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 Return to the debug mode, and you can return to Forth.

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 , Compiling will print it. That is, 2State? Is executed immediately, don't wait for you to enter 2Test. Now print

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 , "interpreting" is printed, why?

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 segment, then assigned by the multiple of 4, the head pointer of the free linked list is at the address : 2. In : 0 The value at the point is not used, the following words will create a free-list:

/ Variables and constants

Decimal

0 Constant Nil2 Constant [FreeEList.Head]

0 Value

[freelist.head] value [list.offset]

Distribution memory

: Release.SEGLIST (-)

? DUP

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

!> ; / = Base Segment Address

Create a free linked list nodes: | PTR | VAL |

: Allocate.Freelist (Size -)

Dup Alloc.SEGLIST / SIZE

[list.offset] 2 / next PTR Addr

[list.offset]! l / store at current PTR

2 !> [List.offset] / make next PTR Current PTR

1 Do / Do Size-1 Times

[list.offset] 4 / next PTR Addr

[list.offset]! l / store at current PTR

4 !> [List.offset] / make next PTR Current PTR

Loop

NIL [list.offset]! l / make Last PTR NIL

4 !> [List.offset]; / [list.offset] -> Eolist

: Freeelist (- seg offset)

[freeelist.head];

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)

swap @;

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 method is in the word Fill.Newlists.

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

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 contains the number of fields currently recorded. In the first field of offset address [next.sr], a pointer is contained to the next student record. The second field in address [name.sr] contains the name of the student.

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 . When these words are created, the value of the variable Total.bytes is written into the PFA created, and the value of Total.bytes is incremented by the stack of this field call. (TOTAL.BYTES initial value starts from 2). This technique can generate the correct offset address for fields of different widths. Fields can also be added or decreased as needed without having to care about its offset address.

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 / SR Node SEG Address

0 value / name node seg address

0 Value / address node seg address

0 Value / SR Data Node SEG Address

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 [major.d]! L

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;

转载请注明原文地址:https://www.9cbs.com/read-49787.html

New Post(0)