4 ; Joel Matthew Rees September 2020
6 ; Borrowing some concepts from fig-Forth.
9 ; ------------------------------------LICENSE-------------------------------------
11 ; Copyright (c) 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 fake forward referencing:
39 ; Watch label width, 5 significant characters on original 8080
41 BYTESZ: EQU 8 ; bit count in byte
43 ADRWDSZ: EQU 2 ; bytes per address word
45 ; If at all possible, a CELL should be able to contain an address.
46 ; Otherwise, fetch and store become oddities.
48 CELLBSZ: EQU (CELLSZ*BYTESZ) ; bit count in CELL
50 DBLBSZ: EQU (DBLSZ*BYTESZ) ; bit count in DOUBLE
52 GAPCT: EQU 2 ; address words for the gaps
53 ALLOGAP: EQU (GAPCT*CELLSZ) ; For crude checks, gaps always zero.
56 ; Declare initial Return Stack (flow-of-control stack):
57 RSTKBND: EQU 08000H ; Bound: one beyond
58 RSTKINI: EQU (RSTKBND-1) ; Init: 8080? next available byte on 6800
59 RSTKSZ: EQU (62*CELLSZ) ; Size: Safe for most purposes.
60 RSTKLIM: EQU (RSTKBND-RSTKSZ) ; Limit: Last useable
61 ; Kibitzing -- CPUs really should have automatic stack bounds checking.
62 ; Don't forget gaps for CPUs that don't automatically check.
63 ; Crude guard rails is better than none?
65 ; Declare initial Parameter Stack (data stack):
66 SSTKBND: EQU (RSTKLIM-ALLOGAP)
67 SSTKINI: EQU (SSTKBND-CELLSZ) ; 8080? post-dec on 6800, but on address word boundary.
68 SSTKSZ: EQU (126*CELLSZ) ; Size: CELL addressing, small stacks.
69 SSTKLIM: EQU (SSTKBND-SSTKSZ)
71 ; The paramater stack and heap at opposite ends of the same region
74 ; The initial per-user allocation heap:
75 UPGBND: EQU (SSTKLIM-ALLOGAP)
76 UPGSZ: EQU (64*CELLSZ) ; This will need adjusting in many cases.
77 UPGBASE: EQU (UPGBND-UPGSZ)
81 ; ORG directives in older assemblers can get tangled up
82 ; if they are convoluted.
83 ; Keep the ORGs in ascending order.
88 ; Internal registers --
89 ; (When switching contexts, these must be saved and restored.):
91 ; RP: EQU SP ; ?? ; the return/flow-of-control stack pointer is 8080 SP
92 ; PSP: EQU DE ; the parameter/data stack pointer (Forth SP)
93 ; Maybe we can put PSP in HL?
94 UP: DS ADRWDSZ ; pointer to the per-task heap -- BC?
95 ; TEMP: DS 2*CELLSZ ; for general math
96 ; GCOUNT: DS CELLSZ ; general counter
97 ; IXDEST: DS ADRWDSZ ; destination index pointer
98 ; IXSRC: DS ADRWDSZ ; source index pointer
99 ; IXTERM: DS ADRWDSZ ; terminator for moves
111 ; _LOWORD_ Subtract byte in B from cell pointed to by X:
112 ; Not really all that useful when you can CLRA and SUBCELL?
121 ; _LOWORD_ Add byte in B to cell pointed to by X:
122 ; Not really all that useful when you can CLRA and ADDCELL?
139 ; Or doing as subroutine would would add 4 bytes and about ten cycles?
140 ; How useful would it be?
143 LOGOP: MACRO OP0 ; hides a lot of cycles and object code
144 XCHG ; DE with HL, make PSP accessible to OP0
148 OP0 M ; less significant byte on 8080
154 OP0 M ; more significant byte on 8080
161 ; _WORD_ Take logical AND of top two on stack ( n1 n2 --- n3 ):
164 ; _WORD_ Take logical OR of top two on stack ( n1 n2 --- n3 ):
167 ; _WORD_ Take logical OR of top two on stack ( n1 n2 --- n3 ):
171 ; _WORD_ Add top two cells on stack ( n1 n2 --- sum ):
172 ADD: STC ; clear carry (LSB first required!)
173 CMC ; So we don't have to rewrite all of LOGOP
174 LOGOP ADC ; to use ADD first and ADC second.
176 ; _WORD_ Subtract top cell on stack from second ( n1 n2 --- difference(n1-n2) ):
177 SUB: STC ; clear carry (See ADD.)
182 ; _WORD_ Fetch byte only pointed to by top cell on stack ( adr --- b(at adr) ):
183 ; (Refer to Forth's C@, but byte is not character!)
190 ; STAX DE ; more significant byte
196 BFETCH: XCHG ; Make PSP available.
202 MOV M,C ; Clear high byte.
207 ; _WORD_ Store low byte of cell at 2nd at address on top of stack, deallocate both ( n adr --- ):
208 ; (Refer to Forth's C!, but byte is not character!)
210 MOV C,M ; address low byte
212 MOV B,M ; address high byte
214 MOV A,M ; ignore high byte.
221 ; ASSERT CELLSZ == ADRWDSZ
222 ; _WORD_ Fetch cell pointed to by top cell on stack ( adr --- n(at adr) ):
234 ; _WORD_ Store cell at 2nd at address on top of stack, deallocate both ( n adr --- ):
242 ; Rob point to deallocate 2 on stack.
252 ; _WORD_ Unsigned multiply of top two on stack ( n1 n2 --- uproduct(n1*n2) ):
255 LDAA #CELLBSZ ; bits/cell
259 USTARL: ROR CELLSZ,X ; shift multiplier
267 RORB ; shift result in
269 USTARX: STAB 1,X ; store more significant 16 bits
273 ; _WORD_ swap top two cells on stack ( n1 n2 --- n2 n1 ):
287 ; _WORD_ Unsigned divide of unsigned double dividend in 2nd:3rd on stack by top unsigned divisor
288 ; ( ud u --- uremainder uquotient )
289 ; Dividend should be range of product of 16 by 16 bit unsigned multiply,
290 ; divisor should be the multiplier or multiplicand:
293 LDAA #CELLBSZ+1 ; one more than bits/cell
295 LDAA CELLSZ,X ; dividend
297 USLLUP: CMPA 0,X ; divisor
298 BHI USLSUB ; Make sure carry from LSB would carry over
301 BCC USLSUB ; CC = HS (High or Same)
302 USLNSUB: CLC ; For shifting
307 USLNEXT: ROL 1+2*CELLSZ,X
314 BRA USLSUB ; Catch the excess.
315 USLX: INX ; Drop high cell.
318 STAA 0,X ; High cell now empty, save remainder.
319 STAB 1,X ; But remainder is now top.
320 BRA SWAPROB ; PSP in X, reverse quotient & remainder.
324 ; _WORD_ Save top cell on stack to return stack ( n --- ) { --- n }:
331 INX ; Not worth robbing code.
336 ; _WORD_ Pop top of return stack to parameter stack ( --- n ) { n --- }:
342 ; _WORD_ Retrieve, do not pop top of return stack to parameter stack ( --- n ) { n --- n }:
356 ; Should true be set as 1 or -1?
357 ; _WORD_ Test top cell on stack for zero ( n --- f(top==0) ):
370 ; _LOWORD_ Duplicate (count in B) bytes on stack:
377 ; _LOWORD_ Move 256 bytes or less going up:
378 ; 0=256, do not enter without pretesting for 0 count!
379 ; source in IXSRC, destination in IXDEST, count in B:
380 ; Overlaps only work if source is higher than dest.
396 ; _LOWORD_ Move 256 bytes or less going down:
397 ; 0=256, do not enter without pretesting for 0 count!
398 ; source in IXSRC, destination in IXDEST, count in B, A is overwritten:
399 ; Overlaps only work if source is higher than dest.
406 CLRA ; 1# 2~ -- BCC takes 2#, 4~; INC IXSRC takes 3# & 6~
412 CLRA ; 1# 2~ -- BCC takes 2#, 4~; INC IXDEST takes 3# & 6~
428 ; _LOWORD_ Set up parameters for MOVE ( src dest count --- )
429 ; Return without copying if count is zero or if 2^15 or more.
430 ; Make sure destination is left in A:B for math.
432 LDX PSP ; get paramaters for move
434 BEQ GMVPRX ; bail now if zero
435 BMI GMVPRX ; also if greater than 32K
438 LDAA CELLSZ,X ; preparing for math
444 RTS ; Back to MOVE that called us.
447 INS ; Drop return to MOVE code.
451 ; _WORD_ Move up to 32K bytes ( src dest count --- ):
452 ; Copies zero when count >= 2^15
453 ; Compare CMOVE in Forth.
456 SUBB CELLSZ+ADRWDSZ+1,X ;
457 SBCA CELLSZ+ADRWDSZ,X
462 ;***** Working in here to make it go both ways.
463 ;***** Also need to check multiply and divide.
472 LDAB GCOUNT+1 ; Get low byte for partial block
473 CLR GCOUNT+1 ; To avoid debugging confusion.
474 BMOVEL: BSR SMOVE ; partial block and full blocks
475 DEC GCOUNT ; count high byte down (blocks)
476 BPL BMOVEL ; This limits the count.
477 ; Rob point to deallocate 3 on stack, as long as CELLSZ == ADRWDSZ.
480 ADDB #(CELLSZ+2*ADRWDSZ) ; 2# 3~
483 INC PSP ; 3# 6~ Unaries have no direct page version. => 11# 20~
487 ; ADDB #(CELLSZ+2*ADRWDSZ) ; 2# 3~
490 ; ADCA #(CELLSZ+2*ADRWDSZ) ; 2# 3~
491 ; STAA PSP ; 2# 4~ => 12# 20~
494 ; LDX PSP ; 6 INXs is around the breakover point in the 6800.
495 ; INX ; 2 + 6 + 2 bytes => 11#
496 ; INX ; 4 + 24 + 5 cycles => 33~
506 ; _WORD_ Execute the address on the stack:
513 JSR 0,X ; For debugging and flattening, no early optimizations.