4 * Joel Matthew Rees April 2020
6 * Borrowing some concepts from fig-Forth.
9 * ------------------------------------LICENSE-------------------------------------
11 * Copyright (c) 2009, 2010, 2011, 2020 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 forward referencing:
39 BYTESZ EQU 8 ; bit count in byte
41 ADRWDSZ EQU 2 ; bytes per address word
43 * If at all possible, a CELL should be able to contain an address.
44 * Otherwise, fetch and store become oddities.
46 CELLBSZ EQU (CELLSZ*BYTESZ) ; bit count in CELL
48 DBLBSZ EQU (DBLSZ*BYTESZ) ; bit count in DOUBLE
50 GAPCT EQU 2 ; address words for the gaps
51 ALLOGAP EQU (GAPCT*CELLSZ) ; For crude checks, gaps always zero.
54 * Declare initial Return Stack (flow-of-control stack):
55 RSTKBND EQU $8000 ; Bound: one beyond
56 RSTKINI EQU (RSTKBND-1) ; Init: next available byte on 6800
57 RSTKSZ EQU (62*CELLSZ) ; Size: Safe for most purposes.
58 RSTKLIM EQU (RSTKBND-RSTKSZ) ; Limit: Last useable (Ignore pre-dec.)
59 * Kibitzing -- CPUs really should have automatic stack bounds checking.
60 * Don't forget gaps for CPUs that don't automatically check.
61 * Crude guard rails is better than none?
63 * Declare initial Locals Stack (temporaries stack):
64 LSTKBND EQU (RSTKLIM-ALLOGAP)
65 LSTKINI EQU (LSTKBND-CELLSZ) ; Pre-dec, even on 6800, but on address word boundary.
66 LSTKSZ EQU (30*CELLSZ) ; Size: CELL addressing, small stacks.
67 LSTKLIM EQU (LSTKBND-LSTKSZ)
69 * Declare initial Parameter Stack (data stack):
70 SSTKBND EQU (LSTKLIM-ALLOGAP)
71 SSTKINI EQU (SSTKBND-CELLSZ) ; Pre-dec, even on 6800, but on address word boundary.
72 SSTKSZ EQU (126*CELLSZ) ; Size: CELL addressing, small stacks.
73 SSTKLIM EQU (SSTKBND-SSTKSZ)
75 * The paramater stack and heap at opposite ends of the same region
78 * The initial per-user allocation heap:
79 UPGBND EQU (SSTKLIM-ALLOGAP)
80 UPGSZ EQU (30*CELLSZ) ; This will need adjusting in many cases.
81 UPGBASE EQU (UPGBND-UPGSZ)
85 * ORG directives in older assemblers can get tangled up
86 * if they are convoluted.
87 * Keep the ORGs in ascending order.
92 * Internal registers --
93 * (When switching contexts, these must be saved and restored.):
95 * RP RMB ADRWDSZ ; the return/flow-of-control stack pointer is 6800 S
96 * A software locals stack would require thrashing X,
97 * so we'll take the unwise expedient of using the return stack.
98 LSP RMB ADRWDSZ ; the locals stack pointer (for locals, i. e., instead of RP)
99 PSP RMB ADRWDSZ ; the parameter/data stack pointer (Forth SP)
100 UP RMB ADRWDSZ ; pointer to the per-task heap
101 TEMP RMB 2*CELLSZ ; for general math
102 GCOUNT RMB CELLSZ ; general counter
103 IXDEST RMB ADRWDSZ ; destination index pointer
104 IXSRC RMB ADRWDSZ ; source index pointer
105 IXTERM RMB ADRWDSZ ; terminator for moves
117 * _LOWORD_ Subtract byte in B from cell pointed to by X:
118 * Not really all that useful when you can CLRA and SUBCELL?
127 * _LOWORD_ Add byte in B to cell pointed to by X:
128 * Not really all that useful when you can CLRA and ADDCELL?
145 * Or doing as subroutine would would add 4 bytes and about ten cycles?
146 * How useful would it be?
148 * Instead of providing such here, plan on providing funtionality later.
151 * _WORD_ AND Take logical AND of top two on stack ( n1 n2 --- n3 ):
162 * Might replace the first three instructions with
172 * But that only saves four bytes each,
173 * at a cost of some thirteen cycles
174 * in code that we assume will become a bottleneck.
176 * The number of places it would be used are less than ten, I think.
177 * Less than 40 bytes saved.
179 * Tail-stealing the STDEALL code costs enough to throw that into question as it is,
180 * but that's seven bytes saved at a cost of four cycles.
182 * We will assume that auto-in-lining for the 6800 will be not be in this version.
184 * _WORD_ OR Take logical OR of top two on stack ( n1 n2 --- n3 ):
193 * _WORD_ XOR Take logical OR of top two on stack ( n1 n2 --- n3 ):
202 * _WORD_ Add top two cells on stack ( n1 n2 --- sum ):
204 LDX PSP ; MACRO GETAB
209 * _LOWORD_ + Add cell in A:B to cell pointed to by X:
213 * Keep rob point for storing over 2nd and deallocating top.
222 * _WORD_ - Subtract top cell on stack from second ( n1 n2 --- difference(n1-n2) ):
224 LDX PSP ; MACRO GETAB
229 * !xxx _LOWORD_ Subtract cell pointed to by 2,X from cell in A:B (not useful?):
241 * ASSERT CELLSZ == ADRWDSZ
243 * _WORD_ B@ Fetch byte only pointed to by top cell on stack ( adr --- b(at adr) ):
244 * (Refer to Forth's C@, but byte is not character!)
251 STAB 1,X ; Not worth robbing.
254 * _WORD_ B! Store low byte of cell at 2nd at address on top of stack, deallocate both ( n adr --- ):
255 * (Refer to Forth's C!, but byte is not character!)
258 LDAB 1+ADRWDSZ,X ; n low byte only
260 STAB 0,X ; Store only byte, do not clear high byte!
261 BRA DEALL2 ; Rob code to deallocate, assumes CELLSZ == ADRWDSZ.
263 * _WORD_ @ Fetch cell pointed to by top cell on stack ( adr --- n(at adr) ):
274 * _WORD_ ! Store cell at 2nd at address on top of stack, deallocate both ( n adr --- ):
282 * Rob point to deallocate 2 on stack.
285 INX ; implicit dependency on CELLSZ and ADRWDSZ
292 * _WORD_ U* Unsigned multiply of top two on stack ( n1 n2 --- udproduct(n1*n2) ):
295 LDAA #CELLBSZ ; bits/cell
297 CLRA ; Clear Carry, too.
299 USTARL ROR CELLSZ,X ; shift multiplier
307 RORB ; shift result in
309 USTARX STAB 1,X ; store more significant 16 bits
313 * _WORD_ SWAP swap top two cells on stack ( n1 n2 --- n2 n1 ):
327 * _WORD_ U/MOD Unsigned divide of unsigned double dividend in 2nd:3rd on stack by top unsigned divisor
328 * ( ud u --- uremainder uquotient )
329 * Dividend should be range of product of 16 by 16 bit unsigned multiply,
330 * divisor should be the multiplier or multiplicand:
333 LDAA #1+CELLBSZ ; one more than bits/cell
335 LDAA CELLSZ,X ; dividend
337 USLLUP CMPA 0,X ; divisor
338 BHI USLSUB ; If equal, must compare low byte.
341 BCC USLSUB ; CC = HS (High or Same)
342 USLNSUB CLC ; For shifting
347 USLNEXT ROL 1+2*CELLSZ,X
354 BRA USLSUB ; Catch the excess.
355 USLX INX ; Drop high cell.
358 STAA 0,X ; High cell now empty, save remainder.
359 STAB 1,X ; But remainder is now top.
360 BRA SWAPROB ; PSP in X, reverse quotient & remainder.
363 * _WORD_ >L Save top cell on stack to locals stack ( n --- ) { --- n }:
368 INX ; Not worth robbing code.
379 * _WORD_ >R Save top cell on stack to return stack ( n --- ) { --- n }:
384 INX ; Not worth robbing code.
388 LDX 0,X ; get return address
395 * _WORD_ L> Pop top of locals stack to parameter stack ( --- n ) { n --- }:
398 LDAA 0,X top cell on locals stack
407 STAA 0,X ; save it before you leave
411 * _WORD_ R> Pop top of return stack to parameter stack ( --- n ) { n --- }:
414 LDAA ADRWDSZ,X top cell on R stack -- dodge return address
420 STAA 0,X ; save it first
423 LDX 0,X ; get return address
428 * _WORD_ L Retrieve, do not pop top of locals stack to parameter stack ( --- n ) { n --- n }:
431 LDAA 0,X top cell on locals stack
436 * _WORD_ R Retrieve, do not pop top of return stack to parameter stack ( --- n ) { n --- n }:
439 LDAA ADRWDSZ,X ; top cell on R stack -- dodge return address
444 * _WORD_ DUP Duplicate top cell on stack ( n --- n n ):
457 * _WORD_ DROP Remove a cell from the parameter stack ( n --- )
465 * _WORD_ 2DROP Remove two cells from the parameter stack ( d --- )
476 * Should true be set as 1 or -1?
477 * Going with all bits False (0)
478 * or all bits True (-1) as the flag to set.
479 * _WORD_ 0= Test top cell on stack for zero ( n --- f(top==0) ):
492 * True as 1 would look like
507 * _LOWORD_ Duplicate (count in B) bytes on stack:
514 * _LOWORD_ Move 256 bytes or less going up:
515 * 0=256, do not enter without pretesting for 0 count!
516 * source in IXSRC, destination in IXDEST, count in B:
517 * Overlaps only work if source is higher than dest.
533 * _LOWORD_ Move 256 bytes or less going down:
534 * 0=256, do not enter without pretesting for 0 count!
535 * source in IXSRC, destination in IXDEST, count in B, A is overwritten:
536 * Overlaps only work if source is higher than dest.
543 CLRA ; 1# 2~ -- BCC takes 2#, 4~; INC IXSRC takes 3# & 6~
549 CLRA ; 1# 2~ -- BCC takes 2#, 4~; INC IXDEST takes 3# & 6~
565 * _LOWORD_ Set up parameters for MOVE ( src dest count --- )
566 * Return without copying if count is zero or if 2^15 or more.
567 * Make sure destination is left in A:B for math.
569 LDX PSP ; get paramaters for move
571 BEQ GMVPRX ; bail now if zero
572 BMI GMVPRX ; also if greater than 32K
575 LDAA CELLSZ,X ; preparing for math
581 RTS ; Back to MOVE that called us.
584 INS ; Drop return to MOVE code.
588 * _WORD_ Move up to 32K bytes ( src dest count --- ):
589 * Copies zero when count >= 2^15
590 * Compare CMOVE in Forth.
593 SUBB 1+CELLSZ+ADRWDSZ,X ;
594 SBCA CELLSZ+ADRWDSZ,X
599 ****** Working in here to make it go both ways.
600 ****** Also need to check multiply and divide.
609 LDAB 1+GCOUNT ; Get low byte for partial block
610 CLR 1+GCOUNT ; To avoid debugging confusion.
611 BMOVEL BSR SMOVE ; partial block and full blocks
612 DEC GCOUNT ; count high byte down (blocks)
613 BPL BMOVEL ; This limits the count.
614 * Rob point to deallocate 3 on stack, as long as CELLSZ == ADRWDSZ.
617 ADDB #(CELLSZ+2*ADRWDSZ) ; 2# 3~
620 INC PSP ; 3# 6~ Unaries have no direct page version. => 11# 20~
624 * ADDB #(CELLSZ+2*ADRWDSZ) ; 2# 3~
627 * ADCA #(CELLSZ+2*ADRWDSZ) ; 2# 3~
628 * STAA PSP ; 2# 4~ => 12# 20~
631 * LDX PSP ; 6 INXs is around the breakover point in the 6800.
632 * INX ; 2 + 6 + 2 bytes => 11#
633 * INX ; 4 + 24 + 5 cycles => 33~
643 * _WORD_ Execute the address on the stack:
650 JSR 0,X ; For debugging and flattening, no early optimizations.