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 Parameter Stack (data stack):
76 SSTKBND EQU (RSTKLIM-ALLOGAP)
77 SSTKINI EQU (SSTKBND) ; Also pre-dec, but on address word boundary.
78 SSTKSZ EQU (126*ADRWDSZ) ; Size: CELL addressing, small stacks.
79 SSTKLIM EQU (SSTKBND-SSTKSZ)
81 * The paramater stack and heap at opposite ends of the same region
84 * The initial per-user allocation heap:
85 UPGBND EQU (SSTKLIM-ALLOGAP)
86 UPGSZ EQU 64*ADRWDSZ ; This will need adjusting in many cases.
87 UPGBASE EQU (UPGBND-UPGSZ)
91 * ORG directives in older assemblers can get tangled up
92 * if they are convoluted. Keep the ORGs in assending order.
98 * Internal registers --
99 * (When switching contexts, these must be saved and restored.):
102 * A7/SP == RP, the return/flow-of-control stack on 68000
105 * A6 == PSP, the parameter/data stack pointer on 68000
107 * A5 == UP, the pointer to the per-task heap on 68000
109 * A4 == LSP, optional local stack pointer on 68000
111 * all temps not in registers are allocated locally
112 * -- on RP/A7, PSP/A6, UP/A5, or possibly LSP/A4
114 * general counter allocated in any free Dn
116 * destination index pointer as any free An (A0 .. A3)
118 * source index pointer as any free An (A0 .. A3)
121 *** D0 to D3, A0, and A1 are throwaway or return values
122 * and should be saved by the caller before calls when necessary;
123 * maybe be used freely in called routines.
125 *** D4 to D7, A2, A3, and maybe A4 are persistent or parameters
126 * and should be saved before being used in called routines.
136 ****** Working in here to make it go both ways.
137 ****** Also need to check multiply and divide.
138 ****** And need to convert the stuff past multiply and divide to 68000
145 * not_LOWORD_ Move 2^16-1 bytes or less:
146 * source in A2, destination in A3, count in D4:
147 * Overlaps only work if source is higher than dest.
152 * _WORD_ Move up to 32K (2^15) bytes ( src dest count --- ):
153 * Copies zero when count > 2^15. (Limited for safety.)
154 * Compare CMOVE in Forth.
156 MOVE.L (2*ADRWDSZ,A6),A2 ; src
157 MOVE.L (ADRWDSZ,A6),A3 ; dest
159 CMP.L #$8000 ; Pre-test, do nothing if too big,
160 BLS.B BMOVEE ; or if zero.
165 DBF D4,SMOVEL ; Catches zero count here and stops.
166 BMOVEX LEAU (3*ADRWDSZ,A6),A6
169 * _WORD_ Take logical AND of top two on stack ( n1 n2 --- n3 ):
171 MOVE.L (ADRWDSZ,A6),D3
176 * _WORD_ Take logical OR of top two on stack ( n1 n2 --- n3 ):
178 MOVE.L (ADRWDSZ,A6),D3
183 * _WORD_ Take logical OR of top two on stack ( n1 n2 --- n3 ):
185 MOVE.L (ADRWDSZ,A6),D3
190 * _WORD_ Add top two cells on stack ( n1 n2 --- sum ):
192 MOVE.L (ADRWDSZ,A6),D3
197 * _WORD_ Subtract top cell on stack from second ( n1 n2 --- difference(n1-n2) ):
199 MOVE.L (ADRWDSZ,A6),D3
204 * _WORD_ Fetch byte only pointed to by top cell on stack ( adr --- b(at adr) ):
205 * (Refer to Forth's C@, but byte is not character!)
213 * _WORD_ Store low byte of cell at 2nd at address on top of stack, deallocate both ( b adr --- ):
214 * (Refer to Forth's C!, but byte is not character!)
217 MOVE.L (A6)+,D3 ; Get whole cell instead of pre-clearing.
218 MOVE.B D3,(A1) ; Store only byte, do not clear low bytes!
221 * _WORD_ Fetch half-cell only pointed to by top cell on stack ( adr --- h(at adr) ):
222 * adr must be even address aligned on many 68K.
230 * _WORD_ Store half-cell at 2nd at address on top of stack, deallocate both ( h adr --- ):
231 * adr must be even address aligned on many 68K.
234 MOVE.L (A6)+,D3 ; Get whole cell from stack instead of pre-clearing.
235 MOVE.W D3,(A1) ; Store only half-cell, do not clear low half!
238 * _WORD_ Fetch cell pointed to by top cell on stack ( adr --- n(at adr) ):
239 * adr must be even address aligned on many 68K.
246 * _WORD_ Store cell at 2nd at address on top of stack, deallocate both ( n adr --- ):
247 * adr must be even address aligned on many 68K.
251 MOVE.L D3,(A1) ; Store only byte, do not clear high byte!
254 * u1 h1:l1 ADRWDSZ:HALFSZ+ADRWDSZ
257 * _WORD_ Unsigned multiply of top two on stack ( u1 u2 --- udproduct(n1*n2) ):
259 MOVEQ #CELLBSZ,D0 ; bits/cell
260 MOVEQ #0,D2 ; Clears carry, not extend
261 MOVE.L (ADRWDSZ,A6),D3 ; multiplicand
262 MOVE.L (A6),D1 ; multiplier
265 DBF D0,USTART ; done? hits both carry and extend!
269 ADD.L (ADRWDSZ,A6),D2
270 USTARNA RORX.L D2 ; shift result in
272 USTARX MOVEM.L D2/D3,(A6) ; Store result.
275 * u1 h1:l1 ADRWDSZ:HALFSZ+ADRWDSZ
278 * _WORD_ Unsigned multiply of top two on stack ( u1 u2 --- udproduct(n1*n2) )
279 * Using 6809's MUL for speed -- more code less time:
281 MOVEQ #0 D1 ; Scratch area for inner products
284 MOVE.W (HALFSZ+ADRWDSZ,A6),D3 ; low halves
288 MOVE.W (ADRWDSZ,A6),D2 ; inner1: u1 high
289 MULU (HALFSZ,A6),D2 ; u2 low
292 MOVE.W D2,D1 ; lower half of inner1
293 ADD.L D1,D3 ; No carry possible yet.
294 * bound: $FFFE0001+$0000FFFF=$FFFF0000
297 MOVE.W D2,D1 ; higher half of inner1, hold it.
299 MOVE.W (HALFSZ+ADRWDSZ,A6),D2 ; inner2: u1 low
300 MULU (A6),D2 ; u2 high
303 MOVE.W D2,D0 ; lower half of inner2
304 ADD.L D0,D3 ; Still no carry possible.
305 * bound: $FFFF0000+$0000FFFF=$FFFFFFFF
307 MOVE.W D2,D0 ; higher half of inner2
308 ADD.L D0,D1 ; add to inner1 higher half
309 * bound: $0000FFFF+$0000FFFF=$0001FFFE
310 MOVE.W (ADRWDSZ,A6),D2 ; high halves
314 * bound: $FFFE0001+$0001FFFE=$FFFFFFFF
315 * Done, result in D2:D3
319 * _WORD_ swap top two cells on stack ( n1 n2 --- n2 n1 ):
326 * MOVE.L D2,(ADRWDSZ,A6)
328 * Which will be smaller, faster, less bus activity?
331 * _WORD_ Unsigned divide of unsigned double dividend in 2nd:3rd on stack by top unsigned divisor
332 * ( ud u --- uremainder uquotient )
333 * Dividend should be range of product of 16 by 16 bit unsigned multiply,
334 * divisor should be the multiplier or multiplicand:
336 LDA #1+CELLBSZ bit ct
338 LDD ADRWDSZ,U dividend
339 USLDIV CMPD ,U divisor
345 USLBIT ROL 1+2*ADRWDSZ,U save it
360 **** gotta look at ,S references, work around the PC!
362 * _WORD_ Save top cell on stack to return stack ( n --- ) { --- n }:
369 * _WORD_ Pop top of return stack to parameter stack ( --- n ) { n --- }:
375 * _WORD_ Retrieve, do not pop top of return stack to parameter stack ( --- n ) { n --- n }:
381 * Should true be set as 1 or -1?
382 * _WORD_ Test top cell on stack for zero ( n --- f(top==0) ):
395 * _LOWORD_ Duplicate (count in B) bytes on stack:
402 * _WORD_ Execute the address on the stack:
409 JSR 0,X ; For debugging and flattening, no early optimizations.