目次
前
次
基本動作復習
手持ちの「応用CP/M」からスクリプトの雰囲気と
文法を一瞥しておきます。
CP/Mでは、i8080を利用したアセンブリ言語で記述された
はずですが、ここまでできるのかという印象でした。
Forthインタプリタがスタック操作であるため、メモリ
消費が少なく、実時間処理に関しては、4MHzのZ80でも
充分対応できたのでしょう。
自作したForthのインタプリタでテストした内容を
入力して、動作を確認。
1 2 + .
4 5 * .
10 1 2 - .
1 2 * 3 4 5 + .
9 2 / .
9 2 % .
100 10 inc .
100 22 dec .
1 dup . .
12 24 + . cr
10 0 < .
10 0 <= .
10 0 > .
10 0 >= .
10 10 == .
10 10 != .
10 0 < if 0 1 + . then
10 11 < if 2 3 + . then
10 0 <= if 0 1 + . else 4 3 * . then
10 0 >= if 0 1 + . else 4 1 - . then
10 0 == if 2 3 + . else 5 1 - . then
3 0 do I inc dec . loop
11 1 do I . loop
1 2 3 4 drop drop . .
3 3 == if 100 . else 0 . then
3 4 == if 100 . else 200 . then
3 4 >= if 100 . else 200 . then
: mul10 10 * ;
: mul3 3 * ;
: mulx 2 * 1 + ;
9 mul3 .
9 mulx .
1 2 3 4 drop drop . .
10 9 8 7 rot . . . .
1 << .
8 << .
15 3 & .
15 16 | .
15 3 ^ .
1 2 3 20 4 max .
10 2 3 20 4 min .
var aa 123 aa ! 321 aa !
aa @ .
0 3 2 -5 min .
0 3 2 -5 max .
ワード定義や構文の扱い方に違いがあるのかも
知れないので、次のURLでLPC1114で動作する
Forthインタプリタの文法を確認。
http://mecrisp.sourceforge.net/glossary.htm
組込みワードは、以下でした。
2dup 2drop 2swap 2nip 2over 2tuck 2rot 2-rot 2>r 2r> 2r@ 2rdrop d2/ d2*
dshr dshl dabs dnegate d- d+ s>d um* m* ud* udm* */ */mod u*/ u*/mod um/mod
m/mod ud/mod d/mod d/ f* f/ 2! 2@ du< du> d< d> d0< d0= d<> d= sp@ sp!
rp@ rp! dup drop ?dup swap nip over tuck rot -rot pick depth rdepth >r r>
r@ rdrop rpick true false and bic or xor not clz shr shl ror rol rshift
arshift lshift 0= 0<> 0< >= <= < > u>= u<= u< u> <> = min max umax umin
move fill @ ! +! h@ h! h+! c@ c! c+! bis! bic! xor! bit@ hbis!
hbic! hxor! hbit@ cbis! cbic! cxor! cbit@ cell+ cells flash-khz
16flash! eraseflash initflash hflash! flushflash + - 1- 1+ 2- 2+ negate
abs u/mod /mod mod / * 2* 2/ even base binary decimal hex hook-emit
hook-key hook-emit? hook-key? hook-pause emit key emit? key? pause
serial-emit serial-key serial-emit? serial-key? cexpect accept tib >in
current-source setsource source query compare cr bl space spaces [char]
char ( \ ." c" s" count ctype type hex. h.s u.s .s words registerliteral,
call, literal, create does> <builds ['] ' postpone inline, ret, exit
recurse state ] [ : ; execute immediate inline compileonly 0-foldable
1-foldable 2-foldable 3-foldable 4-foldable 5-foldable 6-foldable
7-foldable constant 2constant smudge setflags align aligned align4,
align16, h, , ><, string, allot compiletoram? compiletoram compiletoflash
(create) variable 2variable nvariable buffer: dictionarystart
dictionarynext skipstring find cjump, jump, here flashvar-here then else if
repeat while until again begin k j i leave unloop +loop loop do ?do case
?of of endof endcase token parse digit number .digit hold hold< sign #> f#S
f# #S # <# f. f.n ud. d. u. . evaluate interpret hook-quit quit eint?
eint dint ipsr nop unhandled reset irq-systick irq-fault irq-collection
irq-adc irq-i2c irq-uart
スタックを操作するワード、四則演算、シフト処理、論理演算等に
分けて、基本演算を確認します。
スタック操作ワード
Mecris-Stellarisのページでは、2種に分類して紹介。
Single-Jugglers:
depth ( -- +n ) Gives number of single-cell stack items.
nip ( x1 x2 -- x2 )
drop ( x -- )
rot ( x1 x2 x3 -- x2 x3 x1 )
-rot ( x1 x2 x3 -- x3 x1 x2 )
swap ( x1 x2 -- x2 x1 )
tuck ( x1 x2 -- x2 x1 x2 )
over ( x1 x2 -- x1 x2 x1 )
?dup ( x -- 0 | x x )
dup ( x -- x x )
pick ( ... xi+1 xi ... x1 x0 i -- ... x1 x0 xi )
Picks one element from deep below
>r ( x -- ) (R: -- x )
r> ( -- x ) (R: x -- )
r@ ( -- x ) (R: x -- x )
rdrop ( -- ) (R: x -- )
rdepth ( -- +n ) Gives number of return stack items.
rpick ( i -- xi ) R: ( ... xi ... x0 -- ... xi ... x0 )
Double-Jugglers:
They perform the same for double numbers.
2nip ( x1 x2 x3 x4 -- x3 x4 )
2drop ( x1 x2 -- )
2rot ( x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2 )
2-rot ( x1 x2 x3 x4 x5 x6 -- x5 x6 x1 x2 x3 x4 )
2swap ( x1 x2 x3 x4 -- x3 x4 x1 x2 )
2tuck ( x1 x2 x3 x4 -- x3 x4 x1 x2 x3 x4 )
2over ( x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2 )
2dup ( x1 x2 -- x1 x2 x1 x2 )
2>r ( x1 x2 -- ) (R: -- x1 x2 )
2r> ( -- x1 x2 ) (R: x1 x2 -- )
2r@ ( -- x1 x2 ) (R: x1 x2 -- x1 x2 )
2rdrop ( -- ) (R: x1 x2 -- )
大道芸のジャグリングを思い出すと、スタックに
積まれている数を回しているのがわかります。
数は、LPC1114が32ビットのARMプロセッサであることから
32ビットで扱われることに注意します。
Forthでは、スタック操作の前後で、スタックにある数が
どう入れ替わるのかを、左と右に書いて表現。
ワード rot であれば、以下。
rot ( x1 x2 x3 -- x2 x3 x1 )
スタックを本来の縦にした状態で表現すると、次の
動作になると、わかります。
CPUとForthで利用するスタックポインタの参照と設定も可能。
sp@ ( -- a-addr ) Fetch data stack pointer
sp! ( a-addr -- ) Store data stack pointer
rp@ ( -- a-addr ) Fetch return stack pointer
rp! ( a-addr -- ) Store return stack pointer
四則演算と関連演算
四則演算は、加減乗除ですが、剰余系も扱えます。
さらに、符号なしか符号つきかの指定も可能。
u/mod ( u1 u2 -- u3 u4 ) u1/u2 = u4 rem u3 Division
/mod ( n1 n2 -- n3 n4 ) n1 / n2 = n4 rem n3
mod ( n1 n2 -- n3 ) n1 / n2 = remainder n3
/ ( n1 n2 -- n3 ) n1 / n2 = n3
* ( u1|n1 u2|n2 -- u3|n3 ) Single Multiplication
min ( n1 n2 -- n1|n2 ) Keeps smaller of top two items
max ( n1 n2 -- n1|n2 ) Keeps greater of top two items
umin ( u1 u2 -- u1|u2 ) Keeps unsigned smaller
umax ( u1 u2 -- u1|u2 ) Keeps unsigned greater
2- ( u1|n1 -- u2|n2 ) Subtracts two, optimized
1- ( u1|n1 -- u2|n2 ) Subtracts one, optimized
2+ ( u1|n1 -- u2|n2 ) Adds two, optimized
1+ ( u1|n1 -- u2|n2 ) Adds one, optimized
even ( u1|n1 -- u2|n2 ) Makes even. Adds one if uneven.
2* ( n1 -- n2 ) Arithmetric left-shift
2/ ( n1 -- n2 ) Arithmetric right-shift
abs ( n -- u ) Absolute value
negate ( n1 -- n2 ) Negate
- ( u1|n1 u2|n2 -- u3|n3 ) Subtraction
+ ( u1|n1 u2|n2 -- u3|n3 ) Addition
um* ( u1 u2 -- ud ) u1 * u2 = ud
udm* ( ud1 ud2 -- ud3-Low ud4-High ) ud1 * ud2 = ud3-Low ud4-High
um/mod ( ud u1 -- u2 u3 ) ud / u1 = u3 remainder u2
ud/mod ( ud1 ud2 -- ud3 ud4 ) ud1/ud2 = ud4 rem ud3
m* ( n1 n2 -- d ) n1 * n2 = d
m/mod ( d n1 -- n2 n3 ) d / n1 = n3 remainder r2
*/ ( n1 n2 n3 -- n4 ) n1 * n2 / n3 = n4
u*/ ( u1 u2 u3 -- u4 ) u1 * u2 / u3 = u4
*/mod ( n1 n2 n3 -- n4 n5 ) n1 * n2 / n3 = n5 remainder n4
d2* ( d1 -- d2 ) Arithmetric left-shift
d2/ ( d1 -- d2 ) Arithmetric right-shift
dshl ( ud1 -- ud2 ) Logical left-shift, same as d2*
dshr ( ud1 -- ud2 ) Logical right-shift
dabs ( d -- ud ) Absolute value
dnegate ( d1 -- d2 ) Negate
d- ( ud1|d1 ud2|d2 -- ud3|d3 ) Subtraction
d+ ( ud1|d1 ud2|d2 -- ud3|d3 ) Addition
s>d ( n -- d ) Makes a signed single number double length
剰余系では、商と余りを一度に計算して、スタックに
積む仕様のワードがあります。また、インクリメント
デクリメントもワードとして組込み済み。
算術、論理シフトに関しては、2で割るか2を掛けることと
等価という判断で、この演算分野に入れてありました。
比較演算
比較演算は、スタックに1個だけ置いた数値で処理するか
スタックに2個数値を置いて処理するかでワードが異なり
ます。
Single-Comparisions:
u<= ( u1 u2 -- flag ) Unsigned comparisions
u>= ( u1 u2 -- flag )
u> ( u1 u2 -- flag )
u< ( u1 u2 -- flag )
<= ( n1 n2 -- flag ) Signed comparisions
>= ( n1 n2 -- flag )
> ( n1 n2 -- flag )
< ( n1 n2 -- flag )
0< ( n - flag ) Negative ?
0<> ( x -- flag )
0= ( x -- flag )
<> ( x1 x2 -- flag )
= ( x1 x2 -- flag )
Double-Comparisions:
They perform the same for double numbers.
du> ( ud1 ud2 -- flag )
du< ( ud1 ud2 -- flag )
d> ( d1 d2 -- flag )
d< ( d1 d2 -- flag )
d0< ( d -- flag )
d0= ( d -- flag )
d<> ( d1 d2 -- flag )
d= ( d1 d2 -- flag )
メモリ操作とビット処理
LPC1114には、内部SRAMが4kバイト(0.5kワード)ありますが
その中に、スタックやシステム関係の数値を確保するので、ガベージ
コレクションのような操作をしたい場合も出てきます。
そんな場合に利用するワードが「move」。
move ( c-addr1 c-addr2 u -- ) Moves u Bytes in Memory
内部SRAMに画像を展開しておくような場合、背景を黒や白としたいことが
あります。そんな場合に利用するワードが「fill」。
fill ( c-addr u c ) Fill u Bytes of Memory with value c
レジスタの数値をバイトあるいはワードで見て、指定ビットの
論理値をテストし、判定結果をスタックに入れる。
cbit@ ( mask c-addr -- flag ) Test BIts in byte-location
bit@ ( mask a-addr -- flag ) Test BIts in word-location
レジスタの数値をバイトあるいはワードで見て、排他的論理和を
求めて、結果をレジスタに代入。
cxor! ( mask c-addr -- ) Toggle bits in byte-location
xor! ( mask a-addr -- ) Toggle bits in word-location
レジスタの数値をバイトあるいはワードで見て、指定ビットを0にし
結果をレジスタに代入。
cbic! ( mask c-addr -- ) Clear BIts in byte-location
bic! ( mask a-addr -- ) Clear BIts in word-location
レジスタの数値をバイトあるいはワードで見て、指定ビットを1にし
結果をレジスタに代入。
cbis! ( mask c-addr -- ) Set BIts in byte-location
bis! ( mask a-addr -- ) Set BIts in word-location
レジスタにラベルをつける。
2constant name ( ud|d -- ) Makes a double constant.
constant name ( u|n -- ) Makes a single constant.
2variable name ( ud|d -- ) Makes an initialized double variable
variable name ( n|n -- ) Makes an initialized single variable
nvariable name ( n1*u|n n1 -- ) Makes an initialized variable with
specified size of n1 words
Maximum is 15 words
メモリエリアのエントリアドレスに、ラベルをつける。
buffer: name ( u -- ) Creates a buffer in RAM with u bytes length
メモリから2ワードの数値を取り出す。
2@ ( a-addr -- ud|d ) Fetches double number from memory
メモリへ2ワードの数値を格納。
2! ( ud|d a-addr -- ) Stores double number in memory
メモリから1ワードの数値を取り出す。
@ ( a-addr -- u|n ) Fetches single number from memory
メモリへ1ワードの数値を格納。
! ( u|n a-addr -- ) Stores single number in memory
メモリアドレスにワード単位で+1して格納。
+! ( u|n a-addr -- ) Add to memory location
メモリから1バイトの数値を取り出す。
c@ ( c-addr -- char ) Fetches byte from memory
メモリへ1バイトの数値を格納。
c! ( char c-addr ) Stores byte in memory
メモリアドレスにバイト単位で+1して格納。
c+! ( u|n a-addr -- ) Add to byte memory location
印字
Forthは端末を利用してプログラムするので、端末での表示に
関係するワードが多数用意されています。
String routines:
type ( c-addr length -- )
Prints a counted string.
s" Hello" Compiles a string and
( -- c-addr length )
gives back its address and length when executed.
." Hello" Compiles a string and
( -- )
prints it when executed.
cr ( -- ) Emits line feed
bl ( -- 32 ) ASCII code for Space
space ( -- ) Emits space
spaces ( n -- ) Emits n spaces if n is positive
compare ( caddr-1 len-1 c-addr-2 len-2 -- flag )
Compares two strings
accept ( c-addr maxlength -- length ) Read input into a string.
Counted string routines:
ctype ( cstr-addr -- )
Prints a counted string.
c" Hello" Compiles a counted string and
( -- cstr-addr )
gives back its address when executed.
cexpect ( cstr-addr maxlength -- ) Read input into a counted string.
count ( cstr-addr -- c-addr length )
Convert counted string into addr-length string
skipstring ( cstr-addr -- a-addr )
Increases the pointer to the aligned end of the string.
Pictured numerical output:
.digit ( u -- char ) Converts a digit to a char
digit ( char -- u true | false ) Converts a char to a digit
[char] * Compiles code of following char
( -- char ) when executed
char * ( -- char ) gives code of following char
hold ( char -- ) Adds character to pictured number
output buffer from the front.
sign ( n -- ) Add a minus sign to pictured number
output buffer, if n is negative
#S ( ud1|d1 -- 0 0 ) Add all remaining digits
from the double length number to output buffer
# ( ud1|d1 -- ud2|d2 ) Add one digit from the
double length number to output buffer
#> ( ud|d -- c-addr len )
Drops double-length number and finishes
pictured numeric output ready for type
<# ( -- ) Prepare pictured number output buffer
u. ( u -- ) Print unsigned single number
. ( n -- ) Print single number
ud. ( ud -- ) Print unsigned double number
d. ( d -- ) Print double number
Deep insights:
words ( -- ) Prints list of defined words.
.s ( many -- many ) Prints stack contents, signed
u.s ( many -- many ) Prints stack contents, unsigned
h.s ( many -- many ) Prints stack contents, unsigned, hex
hex. ( u -- ) Prints unsigned single number in hex base,
needs emit only.
This is independent of number subsystem.
シフト処理
算術シフト、論理シフトに加えて、回転に関係するワードがあります。
シフトでは、シフト量と方向を指定可能なワードと1ビット限定のシフト
があります。
回転は1ビットに固定。
arshift ( x1 u -- x2 ) Arithmetric right-shift of u bit-places
rshift ( x1 u -- x2 ) Logical right-shift of u bit-places
lshift ( x1 u -- x2 ) Logical left-shift of u bit-places
shr ( x1 -- x2 ) Logical right-shift of one bit-place
shl ( x1 -- x2 ) Logical left-shift of one bit-place
ror ( x1 -- x2 ) Logical right-rotation of one bit-place
rol ( x1 -- x2 ) Logical left-rotation of one bit-place
論理演算
論理演算は、否定、排他的論理和、論理和、論理積が用意
されています。否定は、32ビットすべてに適用されますが
排他的論理和、論理和、論理積では、ビットごと。
not ( x1 -- x2 ) Invert all bits
xor ( x1 x2 -- x3 ) Bitwise Exclusive-OR
or ( x1 x2 -- x3 ) Bitwise OR
and ( x1 x2 -- x3 ) Bitwise AND
制御
If文の記述フォーマット指定。
Decisions:
flag if ... then
flag if ... else ... then
then ( -- ) This is the common
else ( -- ) flag if ... [else ...] then
if ( flag -- ) structure.
Case文の記述フォーマット指定。
Case:
n case
m1 of ... endof
m2 .. ... .....
flag ?of ... endof
all others
endcase
case ( n -- n ) Begins case structure
of ( m -- ) Compares m with n, choose this if n=m
?of ( flag -- ) Flag-of, for custom comparisions
endof ( -- ) End of one possibility
endcase ( n -- ) Ends case structure, discards n
無限回の繰返しの記述フォーマット指定。
Indefinite Loops:
begin ... again
begin ... flag until
begin ... flag while ... repeat
repeat ( -- ) Finish of a middle-flag-checking loop.
while ( flag -- ) Check a flag in the middle of a loop
until ( flag -- ) begin ... flag until
loops as long flag is true
again ( -- ) begin ... again
is an endless loop
begin ( -- )
有限回の繰返しの記述フォーマット指定。
Definite Loops:
limit index do ... [one or more leave(s)] ... loop
?do ... [one or more leave(s)] ... loop
do ... [one or more leave(s)] ... n +loop
?do ... [one or more leave(s)] ... n +loop
k ( -- u|n ) Gives third loop index
j ( -- u|n ) Gives second loop index
i ( -- u|n ) Gives innermost loop index
unloop (R: old-limit old-index -- )
Drops innermost loop structure,
pops back old loop structures to loop registers
exit ( -- ) Returns from current definition.
Compiles a ret opcode.
leave ( -- ) (R: old-limit old-index -- )
Leaves current innermost loop promptly
+loop ( u|n -- )
(R: unchanged | old-limit old-index -- )
Adds number to current loop index register
and checks whether to continue or not
loop ( -- )
(R: unchanged | old-limit old-index -- )
Increments current loop index register by one
and checks whether to continue or not.
?do ( Limit Index -- )
(R: unchanged | -- old-limit old-index )
Begins a loop if limit and index are not equal
do ( Limit Index -- )
(R: -- old-limit old-index )
Begins a loop
端末操作
端末への送信キャラクタが有るか否かの問い合わせ。
emit? ( -- Flag ) Ready to send a character ?
端末から受信キャラクタが有ったか否かの問い合わせ。
key? ( -- Flag ) Checks if a key is waiting
端末から受信があるまで待ち、受信キャラクタ取得。
key ( -- Char ) Waits for and fetches the pressed key
端末への送信。
emit ( Char -- ) Emits a character.
上記4ワードのフック付き処理。
hook-emit? ( -- a-addr ) Hooks for redirecting
hook-key? ( -- a-addr ) terminal IO
hook-key ( -- a-addr ) on the fly
hook-emit ( -- a-addr )
ループ内でのemit?、key?、key、emitの処理。
serial-emit? ( -- Flag ) Serial interface
serial-key? ( -- Flag ) terminal routines
serial-key ( -- Char ) as default communications
serial-emit ( Char -- )
フックの一時停止。
hook-pause ( -- a-addr ) Hook for a multitasker
一時停止。
pause ( -- ) Task switch, none for default
その他
Fixpoint numbers are stored ( n-comma n-whole ) and can be handled
like signed double numbers.
f/ ( df1 df2 -- df3 ) Division of two fixpoint numbers
f* ( df1 df2 -- df3 ) Multiplication
hold< ( char -- )
Adds character to pictured number output buffer
from behind.
f#S ( n-comma1 -- n-comma2 )
Adds all comma-digits to number output
f# ( n-comma1 -- n-comma2 )
Adds one comma-digit to number output
f. ( df -- )
Prints a fixpoint number with all fractional digits
f.n ( df n -- )
Prints a fixpoint number with n fractional digits
number ( c-addr length -- 0 )
-- n 1 )
-- n-low n-high 2 )
Tries to convert a string to a number.
bic ( x1 x2 -- x3 ) Bit clear, identical to "not and"
false ( -- 0 ) False-Flag
true ( -- -1 ) True-Flag
clz ( x1 -- u ) Count leading zeros
ハードウエア処理
ハードウエアリセット(定義ワードを消失)。
reset ( -- ) Reset on hardware level
割込み禁止。
dint ( -- ) Disables Interrupts
割込み許可。
eint ( -- ) Enables Interrupts
割込み許可チェック。
eint? ( -- ) Are Interrupts enabled ?
No operation(フック時にハンドラなしで利用)。
nop ( -- ) No Operation. Hook for unused handlers !
目次
前
次