Implementing figFORTH on SH3 assembler
Revision | 9b5a66bd60ec110a45576f8b46600fa8b2a0dd70 (tree) |
---|---|
Time | 2014-03-07 14:01:56 |
Author | Joel Matthew Rees <reiisi@user...> |
Commiter | Joel Matthew Rees |
Making sure stuff will work like it should, maybe?
@@ -34,7 +34,8 @@ | ||
34 | 34 | .cpu sh3 |
35 | 35 | |
36 | 36 | NATURAL_SIZE: .equ 4 ; 4 byte word |
37 | -ALL_BITS: .equ H'FFFFFFFF | |
37 | +ALIGN_MASK: .equ ( H'FF & ~(NATURAL_SIZE - 1) ) | |
38 | +ALL_BITS8: .equ ( H'FF & -1 ) | |
38 | 39 | |
39 | 40 | |
40 | 41 | ; The SH-3 has modern dev tools, so we don't have to pay much attention |
@@ -94,12 +95,42 @@ fW: .reg r4 ; currently executing Word definition pointer | ||
94 | 95 | ; we can save a lot of time during interrupt processing. |
95 | 96 | |
96 | 97 | |
98 | +; Structure of the symbol table entry: | |
99 | +; Length byte of the symbol name (NFA) | |
100 | +; high bit set to flag the start byte | |
101 | +; next bit is MIMM | |
102 | +; next bit is MHID | |
103 | +; 5 bits of length, 0 really does mean zero. | |
104 | +; Length - 1 bytes of name, 7 bit characters. (Woe unto ye who use UTF-8, shift-JIS, etc.) | |
105 | +; Last byte, high bit set to flag the end byte. | |
106 | +; In alignment sensitive architectures, 0 to alignment - 1 padding. (SH3 has 0 to 3.) | |
107 | +; Link to the previous name in the vocabulary. (LFA) | |
108 | +; Link to the (machine language level) characteristic definition for the symbol table entry. | |
109 | +; With "low-level words", this just links to the code that follows. (CFA) | |
110 | +; Parameter field(s). (PFA) | |
111 | +; For "low-level words", this is machine code. | |
112 | +; For global constants and variables, this contains the value. | |
113 | +; For per-USER (task) constants and variables, the offset into | |
114 | +; the current per-USER table. | |
115 | +; For "high-level words", this is a list of virtual instruction pointers, | |
116 | +; in other words, pointers to the characterictics (CFA) of the symbols | |
117 | +; compiled into the definition. | |
118 | +; | |
119 | + | |
120 | + | |
97 | 121 | ; mode flags (to go with the length byte): |
98 | -MIMM .equ H'40 ; precedence is IMMEDIATE execution (in length byte -- NFA) | |
99 | -MHID .equ H'20 ; SMUDGEd definition (in length byte -- NFA) | |
122 | +CTFLAG: .equ H'80 ; Count byte in dictionary has high bit set. | |
123 | +MIMM: .equ H'40 ; precedence is IMMEDIATE execution (in length byte -- NFA) | |
124 | +MHID: .equ H'20 ; SMUDGEd definition (in length byte -- NFA) | |
125 | +CTMASK: .equ ( H'FF & ~( CTFLAG | MIMM ) ) ; MHID hides SMUDGEd definitions this way. | |
126 | +; MHID limits length to 31 | |
127 | +; | |
128 | +TAILFLAG: .equ H'80 ; Trailing character in dictionary has high bit set. | |
129 | +TAILMASK: .equ ( H'FF & ~TAILFLAG ) ; Expose the tail character. | |
130 | + | |
100 | 131 | |
101 | 132 | |
102 | -_LASTNAME: .assign 0 ; allocation/dictionary link (terminated by zero) | |
133 | +_PREVNAME: .assign 0 ; allocation/dictionary link (terminated by zero) | |
103 | 134 | |
104 | 135 | .macro HEADER name, characteristic, mode=0 |
105 | 136 | ; Symbol name length and mode (Too much stuff in one byte, really.) |
@@ -111,10 +142,10 @@ _s\characteristic: .sdata .substr("\name", 0, .len("\name")-1) | ||
111 | 142 | ; Symbol names float, yes this is a bit awkward. |
112 | 143 | .align NATURAL_SIZE |
113 | 144 | ; Link to previously defined symbol's header. |
114 | - .data.l _LASTNAME | |
145 | + .data.l _PREVNAME | |
115 | 146 | ; Use the SH-3 assembler to track the last symbol. |
116 | 147 | ; (This is not always a good idea.) |
117 | -_LASTNAME: .assign _s\characteristic | |
148 | +_PREVNAME: .assign _s\characteristic | |
118 | 149 | ; Point to the characteristic code for this Word (symbol) to execute. |
119 | 150 | \characteristic .equ $ |
120 | 151 | .data.l _p\characteristic |
@@ -143,3 +174,18 @@ exit\@: | ||
143 | 174 | .endm |
144 | 175 | |
145 | 176 | |
177 | +; Convenience macros: | |
178 | +; *** Use these instead of stealing code. | |
179 | +; Branch to address is limited to +- 4k, | |
180 | +; and stolen code is easy to lose track of. | |
181 | +; Or, maybe you can let an optimizer move the code more reliably for you! | |
182 | + | |
183 | +; See xPALIGN | |
184 | +; Immediate masks are only available for r0! | |
185 | + .macro mALIGNr0 | |
186 | + add #NATURAL_SIZE-1, r0 ; Only works where NATURAL_SIZE is a power of 2. | |
187 | + and #ALIGN_MASK, r0 ; SH3's NATURAL_SIZE is 4, or 2^2, so it works. | |
188 | + .endm | |
189 | + | |
190 | + | |
191 | + |
@@ -11,6 +11,20 @@ | ||
11 | 11 | ; |
12 | 12 | ; .section evaluator, code |
13 | 13 | |
14 | + | |
15 | +; Not in the 6800 fig model, I've just re-factored it for fun. | |
16 | +; (ALIGN) ( ptr1 --- ptr2 ) | |
17 | +; Adjust ptr1 to the nearest aligned address not lower. | |
18 | +; In other words, if ptr1 is aligned at a NATURAL_SIZE boundary, do nothing. | |
19 | +; Otherwise, adjust it up until it is aligned. | |
20 | +; | |
21 | + HEADER ALIGN, xPALIGN | |
22 | + mov.l @fSP, r0 | |
23 | + mALIGNr0 | |
24 | + rts | |
25 | + mov.l r0, @fSP | |
26 | + | |
27 | + | |
14 | 28 | |
15 | 29 | ; It's tempting to put I in a dedicated register, |
16 | 30 | ; but we don't want to optimize too early. |
@@ -23,14 +23,14 @@ | ||
23 | 23 | mov.b #"0", r1 |
24 | 24 | cmp/ge r1, r0 ; character (r0) >= "0" |
25 | 25 | bf xDIGITno |
26 | - add.l #-"0", r0 | |
26 | + add #-"0", r0 | |
27 | 27 | mov.b #9, r1 |
28 | 28 | cmp/gt r1, r0 ; digit (r0) > 9 |
29 | 29 | bf xDIGITbase |
30 | 30 | mov.b #"A"-"0", r1 |
31 | 31 | cmp/ge r1, r0 ; was it between "9" and "A"? |
32 | 32 | bf xDIGITno |
33 | - add.l #"9"-"A"+1, r0 | |
33 | + add #"9"-"A"+1, r0 | |
34 | 34 | xDIGITbase: |
35 | 35 | mov.l @fSP, r1 |
36 | 36 | cmp/ge r1, r0 ; digit (r0) >= base |
@@ -42,10 +42,113 @@ xDIGITbase: | ||
42 | 42 | ; |
43 | 43 | xDIGITno: |
44 | 44 | mov.b #0, r0 |
45 | - add.l #NATURAL_SIZE, fSP | |
45 | + add #NATURAL_SIZE, fSP | |
46 | 46 | rts |
47 | 47 | mov.l r0, @fSP ; set the flag on our way out |
48 | - | |
49 | - | |
50 | - | |
48 | + | |
49 | + | |
50 | +; ENCLOSE ( buffer c --- buffer off1 off2 off3 ) | |
51 | +; Scan buffer for a symbol delimited by c or ASCII NUL; | |
52 | +; return the offsets to the first character of the symbol, | |
53 | +; the last character of the symbol, | |
54 | +; and the next character after the symbol. | |
55 | +; | |
56 | + HEADER ENCLOSE, xENCLOSE | |
57 | + mov.l @fSP, r2 ; delimiter | |
58 | + mov.b #0, r3 ; start the count at zero | |
59 | + mov.l @(NATURAL_SIZE,fSP), r1 | |
60 | +xENCLOSEloopwhite: | |
61 | + mov.b @r1+, r0 | |
62 | + cmp/eq #0, r0 ; NUL character before symbol? | |
63 | + bt xENCLOSEnone | |
64 | + cmp/eq r2, r0 ; leading delimiter? (Usually SPACE.) | |
65 | + bt xENCLOSEloopwhite | |
66 | + add #1, r3 ; Count it as we go. | |
67 | +; | |
68 | + add #-1, r3 ; Counted too far. | |
69 | + mov.l r3, @fSP ; Save offset to symbol or NUL. | |
70 | + add #-1, r1 ; Back up to what stopped us. | |
71 | +xENCLOSEloopword: | |
72 | + cmp/eq #0, r0 ; NUL? | |
73 | + bt xENCLOSEnul | |
74 | + **** | |
75 | + add #1, r3 | |
76 | + bra xENCLOSEloopword | |
77 | + mov.l @r1+, r0 | |
78 | +; | |
79 | + | |
80 | +; found NUL before non-delimiter, therefore there is no word | |
81 | +xENCLOSEnone: | |
82 | + mov.l r3, @fSP | |
83 | + add #1, r3 | |
84 | + mov.l r3, @-fSP | |
85 | + add #-1, r3 | |
86 | + rts | |
87 | + mov.l r3, @-fSP | |
88 | +; delimited by NUL | |
89 | +xENCLOSEnul: | |
90 | + mov.l r3, @-fSP | |
91 | + rts | |
92 | + mov.l r3, @-fSP | |
93 | + | |
94 | + | |
95 | + | |
96 | +;* ######>> screen 20 << | |
97 | +;* ======>> 12 << | |
98 | +; FCB $87 | |
99 | +; FCC 6,ENCLOSE | |
100 | +; FCB $C5 | |
101 | +; FDB PFIND-9 | |
102 | +;* NOTE : | |
103 | +;* FC means offset (bytes) to First Character of next word | |
104 | +;* EW " " to End of Word | |
105 | +;* NC " " to Next Character to start next enclose at | |
106 | +;ENCLOS FDB *+2 | |
107 | +; INS | |
108 | +; PUL B now, get the low byte, for an 8-bit delimiter | |
109 | +; TSX | |
110 | +; LDX 0,X | |
111 | +; CLR N | |
112 | +;* wait for a non-delimiter or a NUL | |
113 | +;ENCL2 LDA A 0,X | |
114 | +; BEQ ENCL6 | |
115 | +; CBA CHECK FOR DELIM | |
116 | +; BNE ENCL3 | |
117 | +; INX | |
118 | +; INC N | |
119 | +; BRA ENCL2 | |
120 | +;* found first character. Push FC | |
121 | +;ENCL3 LDA A N found first char. | |
122 | +; PSH A | |
123 | +; CLR A | |
124 | +; PSH A | |
125 | +;* wait for a delimiter or a NUL | |
126 | +;ENCL4 LDA A 0,X | |
127 | +; BEQ ENCL7 | |
128 | +; CBA ckech for delim. | |
129 | +; BEQ ENCL5 | |
130 | +; INX | |
131 | +; INC N | |
132 | +; BRA ENCL4 | |
133 | +;* found EW. Push it | |
134 | +;ENCL5 LDA B N | |
135 | +; CLR A | |
136 | +; PSH B | |
137 | +; PSH A | |
138 | +;* advance and push NC | |
139 | +; INC B | |
140 | +; JMP PUSHBA | |
141 | +; | |
142 | +;ENCL6 LDA B N found NUL | |
143 | +; PSH B | |
144 | +; PSH A | |
145 | +; INC B | |
146 | +; BRA ENCL7+2 | |
147 | +;* found NUL following the word instead of SPACE | |
148 | +;ENCL7 LDA B N | |
149 | +; PSH B save EW | |
150 | +; PSH A | |
151 | +;ENCL8 LDA B N save NC | |
152 | +; JMP PUSHBA | |
153 | +; | |
51 | 154 |
@@ -25,7 +25,7 @@ next: | ||
25 | 25 | nop |
26 | 26 | bra next |
27 | 27 | nop |
28 | -; Note that, since jumps to absolute addresses have limits on constant-width instruction sets, | |
28 | +; Note that, since jumps to absolute addresses have limits in constant-width instruction sets, | |
29 | 29 | ; using the subroutine call mode for the virtual machine is not as much a penalty as it might seem. |
30 | 30 | ; It also has the advantage of being more compatible with more conventional code. |
31 | 31 | ; Ways to make an absolute jump work might include |
@@ -61,12 +61,12 @@ next: | ||
61 | 61 | |
62 | 62 | ; BRANCH ( --- ) C |
63 | 63 | ; Add the following word from the instruction stream to the |
64 | -; instruction pointer (Y++). Causes a program branch. | |
64 | +; instruction pointer (postincrement). Causes a program branch. | |
65 | 65 | ; |
66 | 66 | HEADER BRANCH, BRANCH |
67 | 67 | mov.l @fIP+, r0 |
68 | 68 | BRANCHgo: |
69 | - add.l r0, fIP | |
69 | + add r0, fIP | |
70 | 70 | rts |
71 | 71 | nop |
72 | 72 |
@@ -103,14 +103,14 @@ BRANCHgo: | ||
103 | 103 | ; |
104 | 104 | HEADER (LOOP), xLOOP |
105 | 105 | mov.l @fRP, r0 ; I (loop counter) |
106 | - add.l #1, r0 | |
106 | + add #1, r0 | |
107 | 107 | mov.l r0, @fRP ; update I |
108 | 108 | mov.l @(NATURAL_SIZE,fRP), r1 ; limit |
109 | 109 | cmp/ge r1, r0 ; r0 >= r1 ? |
110 | 110 | bf/s BRANCHgo ; not yet |
111 | 111 | mov.l @fIP+, r0 |
112 | 112 | rts |
113 | - add.l #2*NATURAL_SIZE, fRP | |
113 | + add #2*NATURAL_SIZE, fRP | |
114 | 114 | |
115 | 115 | |
116 | 116 | ; (+LOOP) ( n --- ) ( limit index *** limit index+n ) C |
@@ -124,7 +124,7 @@ BRANCHgo: | ||
124 | 124 | HEADER (+LOOP), xPLOOP |
125 | 125 | mov.l @fSP+, r1 ; increment |
126 | 126 | mov.l @fRP, r0 ; I (loop counter) |
127 | - add.l r1, r0 | |
127 | + add r1, r0 | |
128 | 128 | mov.l r0, @fRP ; update I |
129 | 129 | shal r1 ; increment negative or positive? |
130 | 130 | bt/s xPLOOPminus |
@@ -156,10 +156,10 @@ xPLOOPminus: | ||
156 | 156 | HEADER (DO), xPDO |
157 | 157 | mov.l @fSP+, r0 |
158 | 158 | mov.l @fSP+, r1 |
159 | - add.l #-2*NATURAL_SIZE, fRP | |
159 | + add #-2*NATURAL_SIZE, fRP | |
160 | 160 | mov.l r1, @(NATURAL_SIZE,fRP) |
161 | - mov.l r0, @fRP | |
162 | 161 | rts |
162 | + mov.l r0, @fRP | |
163 | 163 | |
164 | 164 | |
165 | 165 |
@@ -9,22 +9,115 @@ | ||
9 | 9 | ; .include "context.inc" |
10 | 10 | ; |
11 | 11 | ; .section evaluator, code |
12 | - | |
13 | - | |
14 | -; (FIND) ( name vocptr --- locptr f ) | |
15 | -; Search vocabulary for a symbol called name. Name is a pointer | |
16 | -; to a NUL terminated string of characters without count, vocptr | |
17 | -; is a pointer to a pointer to a definition (the length byte of a | |
18 | -; symbol table entry). Locptr is also a pointer to a pointer to a | |
19 | -; definition, such that, if the flag is false, a symbol with the | |
20 | -; name searched for may be inserted in proper order at that point. | |
21 | -; Vocptr and locptr may point to either the right or left entry of | |
22 | -; the order-parent entry in the symbol table, or to pointer to the | |
23 | -; root of a vocabulary. HIDDEN (smudged) definitions are | |
24 | -; lexically less than their name strings. Searches only the local | |
25 | -; vocabulary, from the order-parent node passed. Uses (REFIND). | |
26 | -; | |
27 | -; vocptr is a pointer to the parameter field of a vocabulary | |
28 | -; header. | |
12 | + | |
13 | + | |
14 | +; Not in the 6800 fig model, I've just re-factored it for fun. | |
15 | +; (NAME-SCAN) ( ptr1 --- ptr2 ) | |
16 | +; Scan ptr1 to a byte with the high bit set, | |
17 | +; leave ptr2 pointing to the next byte. | |
18 | +; Walks all over r0 and r1. Must leave fW untouched. | |
19 | +; | |
20 | + HEADER (NAME-SCAN), xNAMESCAN | |
21 | + mov.l @fSP, r1 | |
22 | + mov.b @r1+, r0 | |
23 | +xNAMESCANloop: | |
24 | + and #CTFLAG, r0 | |
25 | + cmp/eq #CTFLAG, r0 | |
26 | +xNAMESCANstart: | |
27 | + bf/s xNAMESCANloop | |
28 | + mov.b @r1+, r0 | |
29 | +; | |
30 | + add #-1, r1 | |
31 | + mov r1, r0 | |
32 | + mALIGNr0 | |
33 | + rts | |
34 | + mov.l r0, @fSP | |
35 | + | |
36 | + | |
37 | + | |
38 | +; Not in the 6800 fig model, I've just re-factored it for fun. | |
39 | +; (CHK-NAME) ( name nfa --- name link f ) | |
40 | +; Compare a name in a buffer to a name in the symbol table. | |
41 | +; Leave an equality flag and a pointer to the link field for the next name. | |
42 | +; Names in the dictionary are terminated with the high bit set. | |
43 | +; (Names only save 3 significant characters in some FORTHs.) | |
44 | +; Walks all over r0 - r3. Must leave fW untouched. | |
45 | +; | |
46 | + HEADER (CHK-NAME), xCHKNAME | |
47 | + sts.l pr, @-fRP ; so we can call stuff | |
48 | + mov.l @fSP, r2 ; name in dictionary | |
49 | + mov.l @(NATURAL_SIZE, fSP), r3 ; name in buffer | |
50 | + mov.b @r2+, r0 ; count byte in dictionary, plus flags | |
51 | + and #CTMASK, r0 ; Extract the actual count. | |
52 | + mov.b @r3+, r1 ; count byte in buffer | |
53 | + cmp/eq r0, r1 | |
54 | + bf xCHKNAMEno | |
55 | +xCHKNAMEloop: | |
56 | + mov.b @r2+, r0 ; character in dictionary | |
57 | + tst #TAILFLAG, r0 | |
58 | + bt xCHKNAMElast | |
59 | + mov.b @r3+, r1 ; character in buffer | |
60 | + cmp/eq r0, r1 | |
61 | + bt xCHKNAMEloop | |
62 | +; | |
63 | +xCHKNAMEno: | |
64 | + mov #0, r3 ; r3 is not touched by xNAMESCAN | |
65 | +xCHKNAMEret: | |
66 | + bsr xNAMESCAN | |
67 | + mov.l r2, @fSP ; save it as we go | |
68 | + lds.l @fRP+, pr ; Gotta have that return address! | |
69 | + rts | |
70 | + mov.l r3, @-fSP ; flag it as we go | |
71 | +; | |
72 | +xCHKNAMElast: | |
73 | + mov.b @r3+, r1 ; last character in buffer | |
74 | + and #TAILMASK, r0 | |
75 | + cmp/eq r0, r1 | |
76 | + bf xCHKNAMEno | |
77 | +; | |
78 | + bra xCHKNAMEret | |
79 | + mov #ALL_BITS8, r3 ; Set the flag as we go. | |
80 | + | |
81 | + | |
82 | +; (FIND) ( name nfa --- pfa b tf ) | |
83 | +; ( name nfa --- ff ) | |
84 | +; Search vocabulary for a symbol called name. | |
85 | +; name is a pointer to a counted string. | |
86 | +; nfa is the NFA of the last entry in the vocabulary to be searched. | |
87 | +; Walks all over r0 - r3, and fW. | |
29 | 88 | ; |
30 | 89 | HEADER (FIND), xPFIND |
90 | + sts.l pr, @-fRP ; so we can call stuff | |
91 | + mov.l @fSP, r0 | |
92 | +xPFINDloop: | |
93 | + mov.b @r0, fW ; We aren't using fW anyway, and it doesn't get walked in. | |
94 | + bsr xCHKNAME | |
95 | + mov.l @fSP+, r0 ; Did we find it? | |
96 | + cmp/eq #0, r0 | |
97 | + bf/s xPFINDfound ; Use the true flag in r0 | |
98 | + mov.l @fSP, r1 ; LFA needed either way | |
99 | +; | |
100 | + mov.l @r1, r0 | |
101 | + cmp/eq #0, r0 | |
102 | + bt xPFINDnot | |
103 | + bra xPFINDloop | |
104 | + mov.l r0, @fSP ; Store the next one to check as we go. | |
105 | +; | |
106 | +xPFINDnot: | |
107 | +; mov #0, r0 ; use the NULL pointer as a false flag | |
108 | + bra xPFINDret | |
109 | + add #2*NATURAL_SIZE, fSP ; bump as we go | |
110 | +; | |
111 | +xPFINDfound: | |
112 | + add #2*NATURAL_SIZE, r1 ; pfa | |
113 | + mov.l r1, @(NATURAL_SIZE,fSP) | |
114 | + mov.l fW, @fSP ; Store the saved count byte, with mode bits. | |
115 | +; mov #ALL_BITS8, r0 ; We can reuse the flag that sent us here. | |
116 | +xPFINDret: | |
117 | + lds.l @fRP+, pr ; Gotta have that return address! | |
118 | + rts | |
119 | + mov.l r0, @-fSP ; Flag it as we go. | |
120 | + | |
121 | + | |
122 | +; *** Sometime check whether there are extra (unused) instructions in the 6800 code about here. | |
123 | + |