4 * Joel Matthew Rees April 2020
6 * Borrowing some concepts from fig-Forth.
7 * Purely theoretical, not tested!
9 * ------------------------------------LICENSE-------------------------------------
11 * Copyright (c) 2009, 2010, 2011 Joel Matthew Rees
13 * Permission is hereby granted, free of charge, to any person obtaining a copy
14 * of this software and associated documentation files (the "Software"), to deal
15 * in the Software without restriction, including without limitation the rights
16 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 * copies of the Software, and to permit persons to whom the Software is
18 * furnished to do so, subject to the following conditions:
20 * The above copyright notice and this permission notice shall be included in
21 * all copies or substantial portions of the Software.
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31 * --------------------------------END-OF-LICENSE----------------------------------
35 * These must be edited for target runtime:
37 * Necessary here for fake forward referencing:
43 BYTESZ EQU 8 ; bit count in byte
45 * I may want to explore non-linear addressing in the 68000
46 * (and apply it to the 6805) once the basic interpreter is done
47 * in the natural 32-bit CELL.
49 ADRWDSZ EQU 4 ; bytes per address word
50 ADRWREM EQU (ADRWDSZ-1) ; for masking in address-odd remainder bits
51 ADRWMSK EQU ((-ADRWREM)-1) ; for masking out odd parts of addresses (manual bit invert)
54 HALFBSZ EQU (HALFSZ*2) ; bit count in half-CELL
55 * If at all possible, a CELL should be able to contain an address.
56 * Otherwise, fetch and store become oddities.
58 CELLBSZ EQU (ADRWDSZ*BYTESZ) ; bit count in CELL
60 DBLBSZ EQU (DBLSZ*BYTESZ) ; bit count in DOUBLE
62 GAPCT EQU 2 ; address words for the gaps
63 ALLOGAP EQU (GAPCT*ADRWDSZ) ; For crude checks, gaps always zero.
66 * Declare initial Return Stack (flow-of-control stack):
67 RSTKBND EQU ($8000-ADRWDSZ ; Bound: one beyond, but avoiding wraparound
68 RSTKINI EQU (RSTKBND) ; Init: next available byte on 68000 -- pre-dec
69 RSTKSZ EQU (62*ADRWDSZ) ; Size: Safe for most purposes.
70 RSTKLIM EQU (RSTKBND-RSTKSZ) ; Limit: Last useable
71 * Kibitzing -- CPUs really should have automatic stack bounds checking.
72 * Don't forget gaps for CPUs that don't automatically check.
73 * Crude guard rails is better than none?
75 * Declare initial Locals Stack (temporaries stack):
76 LSTKBND EQU (RSTKLIM-ALLOGAP)
77 LSTKINI EQU (LSTKBND-CELLSZ) ; Pre-dec, but on address word boundary.
78 LSTKSZ EQU (30*CELLSZ) ; Size: CELL addressing, small stacks.
79 LSTKLIM EQU (LSTKBND-LSTKSZ)
81 * Declare initial Parameter Stack (data stack):
82 SSTKBND EQU (LSTKLIM-ALLOGAP)
83 SSTKINI EQU (SSTKBND) ; Also pre-dec, but on address word boundary.
84 SSTKSZ EQU (126*ADRWDSZ) ; Size: CELL addressing, small stacks.
85 SSTKLIM EQU (SSTKBND-SSTKSZ)
87 * The paramater stack and heap at opposite ends of the same region
90 * The initial per-user allocation heap:
91 UPGBND EQU (SSTKLIM-ALLOGAP)
92 UPGSZ EQU 30*ADRWDSZ ; This will need adjusting in many cases.
93 UPGBASE EQU (UPGBND-UPGSZ)
97 * On 6809, BRN is being inserted at the top of routines
98 * with an offset to the end, to mark what can be pulled in
99 * for blind in-lining.
101 * But I'm not sure I'm actually going to use it.
104 * ORG directives in older assemblers can get tangled up
105 * if they are convoluted.
106 * Keep the ORGs in assending order.
112 * Internal registers --
113 * (When switching contexts, these must be saved and restored.):
116 * A7/SP == RP, the return/flow-of-control stack on 68000
119 * A6 == PSP, the parameter/data stack pointer on 68000
121 * A5 == UP, the pointer to the per-task heap on 68000
123 * A4 == LSP, optional local stack pointer on 68000
125 * all temps not in registers are allocated locally
126 * -- on RP/A7, PSP/A6, UP/A5, or possibly LSP/A4
128 * general counter allocated in any free Dn
130 * destination index pointer as any free An (A0 .. A3)
132 * source index pointer as any free An (A0 .. A3)
135 *** D0 to D3, A0, and A1 are throwaway or return values
136 * and should be saved by the caller before calls when necessary;
137 * maybe be used freely in called routines.
139 *** D4 to D7, A2, A3, and maybe A4 are persistent or parameters
140 * and should be saved before being used in called routines.
150 * Definitions which are good candidates for direct substitution instead of call
151 * might be marked by something like
156 * CODE ... ; not needed in substitution
158 * CODE ... ; code which gets substituted instead of call
160 * CODE ... ; not needed in substitution
162 * to bracket the substitution for the interpreter/compiler.
164 * This would be part of the _WORD_ macro.
167 * _WORD_ Take logical AND of top two on stack ( n1 n2 --- n3 ):
169 MOVE.L ADRWDSZ(A6),D3
174 * _WORD_ Take logical OR of top two on stack ( n1 n2 --- n3 ):
176 MOVE.L ADRWDSZ(A6),D3
181 * _WORD_ Take logical OR of top two on stack ( n1 n2 --- n3 ):
183 MOVE.L ADRWDSZ(A6),D3
188 * _WORD_ + Add top two cells on stack ( n1 n2 --- sum ):
190 MOVE.L ADRWDSZ(A6),D3
195 * _WORD_ - Subtract top cell on stack from second ( n1 n2 --- difference(n1-n2) ):
197 MOVE.L ADRWDSZ(A6),D3
202 * _WORD_ B@ Fetch byte only pointed to by top cell on stack ( adr --- b(at adr) ):
203 * (Refer to Forth's C@, but byte is not character!)
211 * _WORD_ B! Store low byte of cell at 2nd at address on top of stack, deallocate both ( b adr --- ):
212 * (Refer to Forth's C!, but byte is not character!)
215 MOVE.L (A6)+,D3 ; Get whole cell instead of pre-clearing.
216 MOVE.B D3,(A1) ; Store only byte, do not clear high bytes!
219 * _WORD_ S@ Fetch half-cell only pointed to by top cell on stack ( adr --- h(at adr) ):
220 * adr must be even address aligned on many 68K.
228 * _WORD_ S! Store half-cell at 2nd at address on top of stack, deallocate both ( h adr --- ):
229 * adr must be even address aligned on many 68K.
232 MOVE.L (A6)+,D3 ; Get whole cell from stack instead of pre-clearing.
233 MOVE.W D3,(A1) ; Store only half-cell, do not clear high half!
236 * _WORD_ @ Fetch cell pointed to by top cell on stack ( adr --- n(at adr) ):
237 * adr must be even address aligned on many 68K.
244 * _WORD_ ! Store cell at 2nd at address on top of stack, deallocate both ( n adr --- ):
245 * adr must be even address aligned on many 68K.
252 * Low confidence in the multiply and divide without an emulator to check.
254 * u1 h1:l1 ADRWDSZ:HALFSZ+ADRWDSZ
257 * _WORD_ U*bit Unsigned multiply of top two on stack ( u1 u2 --- udproduct(n1*n2) ):
258 * Consider bit test instead of shift?
260 MOVEQ #CELLBSZ,D0 ; bits/cell -- maybe count is one less for DBcc?
261 MOVEQ #0,D2 ; Clears carry, not extend
262 MOVE.L ADRWDSZ(A6),D3 ; multiplicand
263 MOVE.L (A6),D1 ; multiplier
266 DBF D0,USTART ; done? hits both carry and extend!
271 USTARNA RORX.L D2 ; shift result in
273 USTARX MOVEM.L D2/D3,(A6) ; Store result.
276 * u1 h1:l1 ADRWDSZ:HALFSZ+ADRWDSZ
279 * _WORD_ U* Unsigned multiply of top two on stack ( u1 u2 --- udproduct(n1*n2) )
280 * Using 68000's MUL for speed.
281 * More code, less time, but I need to check that I'm handling the halves right:
283 MOVEQ #0 D1 ; Scratch area for inner products
286 MOVE.W HALFSZ+ADRWDSZ(A6),D3 ; low halves
290 MOVE.W ADRWDSZ(A6),D2 ; inner1: u1 high
291 MULU HALFSZ(A6),D2 ; u2 low
294 MOVE.W D2,D1 ; lower half of inner1
295 ADD.L D1,D3 ; No carry possible yet.
296 * bound: $FFFE0001+$0000FFFF=$FFFF0000
299 MOVE.W D2,D1 ; higher half of inner1, hold it.
301 MOVE.W HALFSZ+ADRWDSZ(A6),D2 ; inner2: u1 low
302 MULU (A6),D2 ; u2 high
305 MOVE.W D2,D0 ; lower half of inner2
306 ADD.L D0,D3 ; Still no carry possible.
307 * bound: $FFFF0000+$0000FFFF=$FFFFFFFF
309 MOVE.W D2,D0 ; higher half of inner2
310 ADD.L D0,D1 ; add to inner1 higher half
311 * bound: $0000FFFF+$0000FFFF=$0001FFFE
312 MOVE.W ADRWDSZ(A6),D2 ; high halves
316 * bound: $FFFE0001+$0001FFFE=$FFFFFFFF
317 * Done, result in D2:D3
321 * _WORD_ SWAP swap top two cells on stack ( n1 n2 --- n2 n1 ):
329 * MOVE.L D2,(ADRWDSZ,A6)
331 * Which will be smaller, faster, less bus activity?
334 * _WORD_ U/MODbit Unsigned divide of unsigned double dividend in 2nd:3rd on stack by top unsigned divisor
335 * ( ud u --- uremainder uquotient )
336 * Dividend should be range of product of 16 by 16 bit unsigned multiply,
337 * divisor should be the multiplier or multiplicand:
338 * Consider bit test instead of shift?
339 * Also, examine the native divide again.
340 * ** Native divide requires divide-by-zero trap code!
342 MOVEQ #1+CELLBSZ,D0 ; bit ct
343 MOVEM.L (A6),D3/D2/D1 ; D1 is divisor, D2:D3 is dividend
345 CMP.L D1,D2 ; dividend high in D2 - divisor in D1
346 BHS.B USLSUB ; *** need to look at this carefully
347 ANDI #^1,CCR ; clear carry bit
351 ORI #1,CCR ; quotient bit,
354 SUBQ #1,D0 ; more bits?
355 BEQ.B USLR ; Can DBcc be used here?
356 ROXL.L D2 ; move remainder in as we move dividend out
361 MOVE.L D3,(A6) ; quotient
362 MOVE.L D2,ADRWDSZ(A6) ; remainder
365 * _WORD_ >L Save top cell on stack to locals stack ( n --- ) { --- n }:
370 * _WORD_ >R Save top cell on stack to return stack ( n --- ) { --- n }:
380 * _WORD_ L> Pop top of locals stack to parameter stack ( --- n ) { n --- }:
385 * _WORD_ R> Pop top of return stack to parameter stack ( --- n ) { n --- }:
396 * _WORD_ L Retrieve, do not pop top of locals stack to parameter stack ( --- n ) { n --- n }:
401 * _WORD_ R Retrieve, do not pop top of return stack to parameter stack ( --- n ) { n --- n }:
403 MOVE.L ADRWDSZ(A7),-(A6)
410 * _WORD_ DUP Duplicate top cell on stack ( n --- n n ):
415 * _WORD_ DROP Remove a cell from the parameter stack ( n --- )
417 LEA CELLSZ(A7),A7 ; or PULU D and throw away
420 * _WORD_ 2DROP Remove a cell from the parameter stack ( n --- )
422 LEAU 2*CELLSZ(A7),U ; or PULU D,X and throw away
425 * Should true be set as 1 or -1?
426 * Going with all bits False (0)
427 * or all bits True (-1) as the flag to set.
428 * _WORD_ 0= Test top cell on stack for zero ( n --- f(top==0) ):
433 EXT.W D0 ; CPU32 can EXTB.L
449 * True as 1 would look like
462 * _LOWORD_ Duplicate (count in B) bytes on stack:
469 ****** Working in here to make it go both ways.
470 ****** Also need to check multiply and divide.
471 ****** And need to convert the stuff past multiply and divide to 68000
478 * not_LOWORD_ Move 2^16-1 bytes or less:
479 * source in A2, destination in A3, count in D4:
480 * Overlaps only work if source is higher than dest.
485 * _WORD_ Move up to 32K (2^15) bytes ( src dest count --- ):
486 * Copies zero when count > 2^15. (Limited for safety.)
487 * Compare CMOVE in Forth.
489 MOVE.L (2*ADRWDSZ,A6),A2 ; src
490 MOVE.L (ADRWDSZ,A6),A3 ; dest
492 CMP.L #$8000 ; Pre-test, do nothing if too big,
493 BLS.B BMOVEE ; or if zero.
498 DBF D4,SMOVEL ; Catches zero count here and stops.
499 BMOVEX LEAU (3*ADRWDSZ,A6),A6
502 * _WORD_ Execute the address on the stack:
509 JSR 0,X ; For debugging and flattening, no early optimizations.