diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..91e2317 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +*.bin +*.hlr +*.ihx +*.lst +*.map +*.o +*.rel +*.rst +*.seed +/65c02/preForth.asm +/65c02/preForthDemo.asm +/65c02/seedForth.asm +/65c02/__temp__.fs +/common/crc10.forth +/emu_65c02/emu_65c02 +/emu_z80/emu_z80 +/i386/preForth +/i386/preForth.asm +/i386/preForthDemo +/i386/preForthDemo.asm +/i386/seedForth +/i386/seedForth.asm +/i386/__temp__.fs +/z80/preForth.asm +/z80/preForthDemo.asm +/z80/seedForth.asm +/z80/__temp__.fs diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..0e1a669 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "emu_z80/z80"] + path = emu_z80/z80 + url = https://github.com/nickd4/z80.git +[submodule "asxv5pxx"] + path = asxv5pxx + url = https://github.com/nickd4/asxv5pxx.git +[submodule "emu_65c02/vrEmu6502"] + path = emu_65c02/vrEmu6502 + url = https://github.com/nickd4/vrEmu6502.git diff --git a/65c02/Makefile b/65c02/Makefile new file mode 100644 index 0000000..3648189 --- /dev/null +++ b/65c02/Makefile @@ -0,0 +1,203 @@ +# Makefile for preForth and seedForth +# +# make bootstrap should produce two identical files: preForth1.$(ASM) and preForth.$(ASM) + +# Set HOSTFORTH to the Forth system that generates the initial preForth +# ------------------------------------------------------------------------ +HOSTFORTH=gforth +# HOSTFORTH=sf # SwiftForth >3.7 +# ------------------------------------------------------------------------ + +AS6500=../asxv5pxx/asxmak/linux/exe/as6500 +ASLINK=../asxv5pxx/asxmak/linux/exe/aslink + +# need to install intelhex package in Python first: +# pip3 install --user intelhex +HEX2BIN=python3 $(HOME)/.local/bin/hex2bin.py + +LOAD_ADDR=0x300 + +EMU_65C02=../emu_65c02/emu_65c02 + +.PHONY: all +all: \ +preForthDemo.bin \ +preForth.bin \ +seedForth.bin \ +seedForthDemo.seed \ +seedForthBoot.seed \ +seedForthInteractive.seed + +.PHONY: test +test: runseedforthdemo runseedforthinteractive + +.PHONY: runseedforthdemo +runseedforthdemo: seedForth seedForthDemo.seed + ./seedForth seedForthDemo.seed + +.PHONY: runseedfortinteractive +runseedforthinteractive: seedForth seedForthInteractive.seed + ./seed + +UNIXFLAVOUR=$(shell uname -s) +ASM=asm + +# build preForthDemo via the host Forth first, so that it +# can be used for basic debugging of the preForth runtime +preForthDemo.$(ASM): \ +../common/preForth-bootstrap.fs \ +../common/preForth-cold.fs \ +preForth-65c02-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth-65c02-backend.pre \ +../common/preForth.pre \ +../common/preForthDemo.pre + cat \ +preForth-65c02-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +../common/preForthDemo.pre \ +|$(HOSTFORTH) \ +../common/preForth-bootstrap.fs \ +../common/preForth-rts-nonstandard.pre \ +preForth-65c02-backend.pre \ +../common/preForth.pre \ +../common/preForth-cold.fs \ +>$@ + +preForth.$(ASM): \ +../common/preForth-bootstrap.fs \ +../common/preForth-cold.fs \ +preForth-65c02-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth-65c02-backend.pre \ +../common/preForth.pre + cat \ +preForth-65c02-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth-65c02-backend.pre \ +../common/preForth.pre \ +|$(HOSTFORTH) \ +../common/preForth-bootstrap.fs \ +../common/preForth-rts-nonstandard.pre \ +preForth-65c02-backend.pre \ +../common/preForth.pre \ +../common/preForth-cold.fs \ +>$@ + +%.$(ASM): \ +%.pre \ +preForth-65c02-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth.bin + $(EMU_65C02) preForth.bin \ +preForth-65c02-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +$< \ +>$@ + +%.bin: %.$(ASM) + $(AS6500) -l -o $< + $(ASLINK) -n -m -u -i -b zpage=0 -b text=$(LOAD_ADDR) $(<:.$(ASM)=.ihx) $(<:.$(ASM)=.rel) + $(HEX2BIN) $(<:.$(ASM)=.ihx) $@ + +# run preForth on its own source code to perform a bootstrap +# should produce identical results +bootstrap: \ +preForth-65c02-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth-65c02-backend.pre \ +../common/preForth.pre \ +preForth.bin \ +preForth.$(ASM) + $(EMU_65C02) preForth.bin \ +preForth-65c02-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth-65c02-backend.pre \ +../common/preForth.pre \ +>preForth1.$(ASM) + cmp preForth.$(ASM) preForth1.$(ASM) + +# preForth connected to stdin - output to stdout +.PHONY: visible-bootstrap +visible-bootstrap: preForth.bin preForth-65c02-backend.pre ../common/preForth.pre + $(EMU_65C02) preForth.bin preForth-65c02-backend.pre ../common/preForth.pre + +# ------------------------------------------------------------------------ +# Docker support (for Linux version) +# ------------------------------------------------------------------------ +# create a linux image based on Dockerfile +.PHONY: docker-image +docker-image: Dockerfile + docker build -t preforth . + +# run the docker image +.PHONY: run +rundocker: docker-image + docker run -i -t --rm preforth /preForth/seed +# ------------------------------------------------------------------------ + +# ------------------------------------------------------------------------ +# seedForth +# ------------------------------------------------------------------------ +seedForth.$(ASM): \ +seedForth-65c02-header.pre \ +preForth-65c02-rts.pre \ +seedForth-65c02-rts.pre \ +seedForth-65c02.pre \ +../common/seedForth16bit.pre \ +../common/seedForth.pre \ +preForth.bin + $(EMU_65C02) preForth.bin \ +seedForth-65c02-header.pre \ +preForth-65c02-rts.pre \ +seedForth-65c02-rts.pre \ +seedForth-65c02.pre \ +../common/seedForth16bit.pre \ +../common/seedForth.pre \ +>seedForth.$(ASM) + +# a little ugly, because gforth insists upon echoing anything read from +# stdin, and also does not allow parsing words to cross a file boundary, +# so we will concatenate the tokenizer and source into a *.fs file first +seedForthDemo.seed: \ +../common/crc10.forth \ +../common/../common/seedForth-tokenizer.fs \ +../common/seedForthDemo.seedsource \ +seedForthDemo65c02.seedsource + cat $^ >__temp__.fs + $(HOSTFORTH) __temp__.fs -e bye >$@ + rm __temp__.fs + +seedForthBoot.seed: \ +../common/crc10.forth \ +../common/../common/seedForth-tokenizer.fs \ +../common/seedForthRuntime16bit.seedsource \ +../common/seedForthRuntime.seedsource \ +seedForthRuntime65c02.seedsource \ +../common/seedForthBoot.seedsource + cat $^ >__temp__.fs + $(HOSTFORTH) __temp__.fs -e bye >$@ + rm __temp__.fs + +seedForthInteractive.seed: \ +../common/crc10.forth \ +../common/../common/seedForth-tokenizer.fs \ +../common/seedForthRuntime16bit.seedsource \ +../common/seedForthRuntime.seedsource \ +seedForthRuntime65c02.seedsource \ +../common/seedForthInteractive.seedsource + cat $^ >__temp__.fs + $(HOSTFORTH) __temp__.fs -e bye >$@ + rm __temp__.fs + +.PHONY: clean +clean: + rm -f *.$(ASM) *.o *.seed preForthDemo preForth seedForth __temp__.fs diff --git a/65c02/preForth-65c02-backend.pre b/65c02/preForth-65c02-backend.pre new file mode 100644 index 0000000..138db4d --- /dev/null +++ b/65c02/preForth-65c02-backend.pre @@ -0,0 +1,158 @@ +\ -------------------------- +\ preForth backend for 65c02 (16 bit) as-65c02 +\ -------------------------- + +\ alter substitutes non-letter characters by upper case letters (to aid assemblers to deal with labels). +: replace ( c -- c d ) + 'A' swap 39 case? ?exit nip + 'B' swap '\' case? ?exit nip + 'C' swap ':' case? ?exit nip + 'D' swap '.' case? ?exit nip + 'E' swap '=' case? ?exit nip + 'F' swap '[' case? ?exit nip + 'G' swap '>' case? ?exit nip + 'H' swap ']' case? ?exit nip + 'I' swap '1' case? ?exit nip + 'J' swap '2' case? ?exit nip + 'K' swap '/' case? ?exit nip + 'L' swap '<' case? ?exit nip + 'M' swap '-' case? ?exit nip + 'N' swap '#' case? ?exit nip + 'O' swap '0' case? ?exit nip + 'P' swap '+' case? ?exit nip + 'Q' swap '?' case? ?exit nip + 'R' swap '"' case? ?exit nip + 'S' swap '!' case? ?exit nip + 'T' swap '*' case? ?exit nip + 'U' swap '(' case? ?exit nip + 'V' swap '|' case? ?exit nip + 'W' swap ',' case? ?exit nip + 'X' swap '@' case? ?exit nip \ may clash with 'X' suffix for machine code + 'Y' swap ')' case? ?exit nip + 'Z' swap ';' case? ?exit nip +; + +\ alter substitutes all non-letter characters by upper case letters. +: alter ( S1 -- S2 ) + '_' 1 rot ?dup 0= ?exit nip nip + \ dup 0= ?exit + swap >r 1- alter r> replace swap 1+ ; + +\ ------------ +\ output words +\ ------------ +\ Output is done by emit. +\ We define convenience words of the form ."xxx" to output xxx (maybe tabbed) + +: ."dw" ( -- ) + tab '.' emit 'd' emit 'w' emit tab ; + +: ."db" ( -- ) + tab '.' emit 'd' emit 'b' emit tab ; + +: ."jsr" ( -- ) + tab 'j' emit 's' emit 'r' emit tab ; + +: ."nest" ( -- ) + 'n' 'e' 's' 't' 4 alter show ; + +: ."unnest" ( -- ) + 'u' 'n' 'n' 'e' 's' 't' 6 alter show ; + +: ."lit" ( -- ) + 'l' 'i' 't' 3 alter show ; + +\ ------------ +\ Compiling words +\ ------------ + +\ ,string compiles the topmost string as a sequence of numeric DB values. +: ,string ( S -- ) + ?dup 0= ?exit + dup roll ."db" u. cr \ 1st char + 1- ,string ; + +\ reproduce a verbatim line +: ,line ( x1 ...cn n -- ) + show ; + +\ compile a reference to an invoked word +: ,word ( S -- ) + ."dw" alter show cr ; + +\ compile reference to nest primitive +: ,nest ( -- ) + ."jsr" ."nest" cr ; + +\ compile reference to unnest primitive +: ,unnest ( -- ) + ."dw" ."unnest" cr cr ; + +\ compile signed number +: ,n ( n -- ) + ."dw" . cr ; + +\ compile unsigned number +: ,u ( u -- ) + ."dw" u. cr ; + +\ compile literal +: ,_lit ( S -- ) + ."dw" ."lit" cr ,word ; + +\ compile literal +: ,lit ( x -- ) + ."dw" ."lit" cr ,n ; + +\ output string as comment +: ,comment ( S -- ) + tab ';' emit space show cr ; + +\ create a new symbolic label +\ if label is 6 characters or less, stay on same line for following code +: label ( S -- ) + alter dup >r show ':' emit r> 7 - 0< ?exit cr ; + +\ body calculates the name of the body from a token +: body ( S1 -- S2 ) + 'X' swap 1+ ; + +\ ,codefield compiles the code field of primitive +\ for 65c02, for asm words there is only a body, so omit the body label +\ : ,codefield ( S -- ) +\ \ body _dup ,word label ; +\ body label ; + +: ,code ( S -- ) + _dup label + ; \ ,codefield ; + +: ,end-code ( -- ) + cr ; + +\ ----------------------------------- +\ tail call optimization tail word ; -> [ ' word >body ] literal >r ; + +: bodylabel ( S -- ) + body label ; + +\ ,tail compiles a tail call +: ,tail ( S -- ) + body ,_lit + '>' 'r' 2 ,word ; + +\ : ."done" ( -- ) +\ ';' emit space 'd' emit 'o' emit 'n' emit 'e' emit ; +\ +\ : ."last:" ( -- ) +\ ';' emit space 'l' emit 'a' emit 's' emit 't' emit ':' emit space ; + +: ,end ( S -- ) + \ cr ."last:" alter show + \ cr ."done" cr + ; + +\ \ create a new header with given name S2 and flags - do nothing +\ : header ( S1 S2 flags -- S3 S2 ) +\ drop ; + diff --git a/65c02/preForth-65c02-rts.pre b/65c02/preForth-65c02-rts.pre new file mode 100644 index 0000000..60e3557 --- /dev/null +++ b/65c02/preForth-65c02-rts.pre @@ -0,0 +1,383 @@ +\ preForth runtime system - 65c02 (16 bit) dependent part +\ -------------------------- +\ +\ - registers: +\ A, X general purpose +\ Y zero +\ - zero page: +\ ip instruction pointer +\ dsp data stack pointer +\ rsp return stack pointer + +pre +;;; This is a preForth generated file using preForth-65c02-backend. +;;; Only modify it, if you know what you are doing. + +DATA_STACK_SIZE = 0x1000 +RETURN_STACK_SIZE = 0x1000 + +STDIN_DATA = 0x200 +STDOUT_DATA = 0x201 +STDERR_DATA = 0x202 +STDIN_STATUS = 0x203 +STDOUT_STATUS = 0x204 +STDERR_STATUS = 0x205 +USLEEP_LO = 0x206 +USLEEP_HI = 0x207 +SYS_EXIT = 0x208 + +EXIT_SUCCESS = 0 +EXIT_FAILURE = 1 + + .r65c02 + + ; define load order + .area zpage + .area text + .area data + .area bss + + .area zpage + .setdp ; set direct (zero) page -- makes instructions shorter + + ; instruction and stack pointers +ip: .ds 2 +dsp: .ds 2 +rsp: .ds 2 + + ; scratch +temp: .ds 3 ; was 4, but we can use x-register instead of last one + + .area text + +main: cld + ldx #0xff + txs + lda #<(data_stack + DATA_STACK_SIZE) + sta dsp + ;lda #<(return_stack + RETURN_STACK_SIZE) + sta rsp + lda #>(data_stack + DATA_STACK_SIZE) + sta dsp+1 + lda #>(return_stack + RETURN_STACK_SIZE) + sta rsp+1 + lda #main1 + sta ip+1 + ldy #0 + jmp next + +main1: .dw _cold + .dw _bye + +; + +code bye ( -- ) + lda #EXIT_SUCCESS + sta SYS_EXIT +; + +code emit ( c -- ) + lda [dsp],y + sta STDOUT_DATA + + inc dsp + inc dsp + bne 1$ ;next + inc dsp+1 +1$: jmp next ;bra next +; + +code eemit ( c -- ) + lda [dsp],y + sta STDERR_DATA + + inc dsp + inc dsp + bne 1$ ;next + inc dsp+1 +1$: jmp next ;bra next +; + +code key ( -- c ) + ldx dsp + bne 1$ + dec dsp+1 +1$: dec dsp + tya + sta [dsp],y + dec dsp + lda STDIN_DATA + sta [dsp],y + jmp next ;bra next +; + +code dup ( x -- x x ) + ldx dsp + bne 1$ + dec dsp+1 +1$: dec dsp + dec dsp + ldy #2 + lda [dsp],y + tax + iny + lda [dsp],y + ldy #1 + sta [dsp],y + dey + txa + sta [dsp],y + bra next +; + +code swap ( x y -- y x ) + lda [dsp],y + sta temp + iny + lda [dsp],y + sta temp+1 + iny + lda [dsp],y + sta temp+2 + iny + lda [dsp],y + tax ; sta temp+3 + lda temp+1 + sta [dsp],y + dey + lda temp + sta [dsp],y + dey + txa ; lda temp+3 + sta [dsp],y + dey + lda temp+2 + sta [dsp],y + bra next +; + +code drop ( x -- ) + inc dsp + inc dsp + bne next + inc dsp+1 + bra next +; + +\ put this in middle of the primitives to make it reachable by bra +code nest ( -- ) +; tya +; cmp #0 +; beq 5$ +; sta SYS_EXIT +;5$: + ldx rsp + bne 1$ + dec rsp+1 +1$: dec rsp + lda ip+1 + sta [rsp],y + dec rsp + lda ip + sta [rsp],y + + pla + sta ip + pla + sta ip+1 + inc ip + bne next + inc ip+1 + +next: +; tya +; cmp #0 +; beq 5$ +; sta SYS_EXIT +;5$: + lda [ip],y + sta 2$+1 ; self modifying code + inc ip + bne 1$ + inc ip+1 +1$: lda [ip],y + sta 2$+2 ; self modifying code + inc ip + bne 2$ + inc ip+1 +2$: jmp 0 +; + +code 0< ( x -- flag ) + iny + lda [dsp],y + bmi 1$ + + lda #0 + sta [dsp],y + dey + sta [dsp],y + bra next + +1$: lda #0xff + sta [dsp],y + dey + sta [dsp],y + bra next +; + +code ?exit ( f -- ) + lda [dsp],y + inc dsp + ora [dsp],y + bne 1$ + inc dsp + bne next + inc dsp+1 + bra next + +1$: inc dsp + bne _unnest + inc dsp+1 + ; fall into unnest +; + +code unnest ( -- ) +; tya +; cmp #0 +; beq 5$ +; sta SYS_EXIT +;5$: + lda [rsp],y + sta ip + inc rsp + lda [rsp],y + sta ip+1 + inc rsp + bne next + inc rsp+1 + bra next +; + +code >r ( x -- ) ( R -- x ) + ldx rsp + bne 1$ + dec rsp+1 +1$: dec rsp + dec rsp + + lda [dsp],y + sta [rsp],y + iny + lda [dsp],y + sta [rsp],y + dey + + inc dsp + inc dsp + bne next1 + inc dsp+1 + bra next1 +; + +code r> ( R x -- ) ( -- x ) + ldx dsp + bne 1$ + dec dsp+1 +1$: dec dsp + dec dsp + + lda [rsp],y + sta [dsp],y + iny + lda [rsp],y + sta [dsp],y + dey + + inc rsp + inc rsp + bne next1 + inc rsp+1 + bra next1 +; + +code - ( x1 x2 -- x3 ) + ldy #2 + lda [dsp],y + ldy #0 + sec + sbc [dsp],y + ldy #2 + sta [dsp],y + iny + lda [dsp],y + ldy #1 + sbc [dsp],y + inc dsp + inc dsp + bne 1$ + inc dsp+1 +1$: sta [dsp],y + dey + bra next1 +; + +code lit ( -- ) + ldx dsp + bne 1$ + dec dsp+1 +1$: dec dsp + dec dsp + + lda [ip],y + sta [dsp],y + inc ip + bne 2$ + inc ip+1 +2$: lda [ip],y + iny + sta [dsp],y + dey + inc ip + bne next1 + inc ip+1 + ;bra next1 + +next1: +; tya +; cmp #0 +; beq 5$ +; sta SYS_EXIT +;5$: + lda [ip],y + sta 2$+1 ; self modifying code + inc ip + bne 1$ + inc ip+1 +1$: lda [ip],y + sta 2$+2 ; self modifying code + inc ip + bne 2$ + inc ip+1 +2$: jmp 0 +; + +\ we want the text section to be first and bss last (for linkers that output +\ sections in definition order), so it would be good to have the bss section +\ be output by the ",end" hook, but at present we cannot call "pre" from a +\ defined word, so we make do by switching to bss and then back to text again +pre + .area bss + + .even + +return_stack: + .ds RETURN_STACK_SIZE +data_stack: + .ds DATA_STACK_SIZE + + .area text + +; diff --git a/65c02/seed b/65c02/seed new file mode 100755 index 0000000..db29ac9 --- /dev/null +++ b/65c02/seed @@ -0,0 +1,4 @@ +#!/bin/bash +stty raw -echo +../emu_65c02/emu_65c02 seedForth.bin seedForthInteractive.seed ../common/runtime.forth ../common/hi16bit.forth ../common/hi.forth - +stty sane diff --git a/65c02/seedForth-65c02-header.pre b/65c02/seedForth-65c02-header.pre new file mode 100644 index 0000000..d6cb393 --- /dev/null +++ b/65c02/seedForth-65c02-header.pre @@ -0,0 +1,22 @@ +\ seedForth: machine dependent header portion + +\ this contains only definitions we want at the top of the file +\ it is then followed by preForth-65c02-rts.pre (primitive asm words) +\ and then by seedForth-65c02.pre (additional primitive asm words) +\ and then by seedForth.pre (high level words and the interpreter) + +pre +;;; This is seedForth - a small potentially interactive Forth, that dynamically +;;; bootstraps from a minimal kernel. +;;; +;;; cat seedForth.seed - | ../65c02_emu/65c02_emu seedForth.bin +;;; +;;; .seed files are in byte-tokenized source code format. +;;; +;;; Use the seedForth tokenizer to convert human readable source code to +;;; byte-token form. + +HEAD_SIZE = 4000 +MEM_SIZE = 40000 + +; diff --git a/65c02/seedForth-65c02-rts.pre b/65c02/seedForth-65c02-rts.pre new file mode 100644 index 0000000..450b231 --- /dev/null +++ b/65c02/seedForth-65c02-rts.pre @@ -0,0 +1,617 @@ +\ seedForth: machine dependent portion + +\ interpreter and basic asm primitives are taken from preForth-65c02-rts.pre, +\ and then this file defines additional primitives (arithmetic, memory, etc) + +pre + .area zpage + + .ds 3 ; extend temp from 3 to 6 locations for mul/div + + .area text + +; + +\ aliases for the user-visible versions of some internal routines +pre +_enter = _nest +_exit = _unnest +; + +\ note: we arrive at _dodoes by a sequence of 2 calls, the return +\ address stacked by first call points to some instance data, and +\ the return address stacked by second call (to _dodoes) points to +\ high level forth code which is going to operate on that instance +\ data -- move the first pushed address to data stack and "nest" +\ the second pushed address, adding 1 because of 65c02's jsr/rts +code dodoes ( -- ) +; tya +; cmp #0 +; beq 5$ +; sta SYS_EXIT +;5$: + ldx rsp + bne 1$ + dec rsp+1 +1$: dec rsp + lda ip+1 + sta [rsp],y + dec rsp + lda ip + sta [rsp],y + + pla + sta ip + pla + sta ip+1 + inc ip + bne _dovar + inc ip+1 + ; fall into dovar +; + +\ note: arriving at _dovar, we just move one address to data stack +code dovar ( -- ) +; tya +; cmp #0 +; beq 5$ +; sta SYS_EXIT +;5$: + ldx dsp + bne 1$ + dec dsp+1 +1$: dec dsp + dec dsp + + pla + sec + adc #0 + sta [dsp],y + pla + adc #0 + iny + sta [dsp],y + dey + + bra next1 +; + +code key? ( -- f ) + ldx dsp + bne 1$ + dec dsp+1 +1$: dec dsp + lda STDIN_STATUS + beq 2$ + lda #0xff +2$: sta [dsp],y + dec dsp + lda STDIN_STATUS + sta [dsp],y + bra next2 +; + +code or ( x1 x2 -- x3 ) + lda [dsp],y + ldy #2 + ora [dsp],y + sta [dsp],y + dey + lda [dsp],y + ldy #3 + ora [dsp],y + sta [dsp],y + ldy #0 + + inc dsp + inc dsp + bne next2 + inc dsp+1 + bra next2 +; + +code and ( x1 x2 -- x3 ) + lda [dsp],y + ldy #2 + and [dsp],y + sta [dsp],y + dey + lda [dsp],y + ldy #3 + and [dsp],y + sta [dsp],y + ldy #0 + + inc dsp + inc dsp + bne next2 + inc dsp+1 + bra next2 +; + +code @ ( addr -- x ) + lda [dsp],y + sta temp + iny + lda [dsp],y + sta temp+1 + + lda [temp],y + sta [dsp],y + dey + lda [temp],y + sta [dsp],y + + bra next2 +; + +code c@ ( c-addr -- c ) + lda [dsp],y + sta temp + iny + lda [dsp],y + sta temp+1 + + lda #0 + sta [dsp],y + dey + lda [temp],y + sta [dsp],y + + ;bra next2 + +next2: +; tya +; cmp #0 +; beq 5$ +; sta SYS_EXIT +;5$: + lda [ip],y + sta 2$+1 ; self modifying code + inc ip + bne 1$ + inc ip+1 +1$: lda [ip],y + sta 2$+2 ; self modifying code + inc ip + bne 2$ + inc ip+1 +2$: jmp 0 +; + +code ! ( x addr -- ) + lda [dsp],y + sta temp + inc dsp + lda [dsp],y + sta temp+1 + inc dsp + bne 1$ + inc dsp+1 + +1$: lda [dsp],y + sta [temp],y + inc dsp + lda [dsp],y + iny + sta [temp],y + dey + inc dsp + bne next2 + inc dsp+1 + bra next2 +; + +code c! ( c c-addr -- ) + lda [dsp],y + sta temp + inc dsp + lda [dsp],y + sta temp+1 + inc dsp + bne 1$ + inc dsp+1 + +1$: lda [dsp],y + sta [temp],y + inc dsp + inc dsp + bne next2 + inc dsp+1 + bra next2 +; + +code execute ( xt -- ) + lda [dsp],y + sta 1$+1 ; self modifying code + inc dsp + lda [dsp],y + sta 1$+2 ; self modifying code + inc dsp + bne 1$ + inc dsp+1 +1$: jmp 0 +; + +code branch ( -- ) \ threaded code: r> @ >r ; + lda [ip],y + tax + iny + lda [ip],y + dey + sta ip+1 + txa + sta ip + bra next3 +; + +\ is this misleading? I would have thought ?branch means branch on nonzero +code ?branch ( f -- ) \ threaded code: ?exit r> @ >r ; + lda [dsp],y + inc dsp + ora [dsp],y + bne 1$ + inc dsp + bne _branch + inc dsp+1 + bra _branch + +1$: inc dsp + bne 2$ + inc dsp+1 +2$: inc ip + bne 3$ + inc ip+1 +3$: inc ip + bne next3 + inc ip+1 + bra next3 +; + +code depth ( -- n ) + sec + lda #<(data_stack + DATA_STACK_SIZE) + sbc dsp + sta temp + lda #>(data_stack + DATA_STACK_SIZE) + sbc dsp+1 + lsr a + ror temp + + ldx dsp + bne 1$ + dec dsp+1 +1$: dec dsp + sta [dsp],y + dec dsp + lda temp + sta [dsp],y + bra next3 +; + +code sp@ ( -- x ) + lda dsp+1 + ldx dsp + bne 1$ + dec dsp+1 +1$: dec dsp + sta [dsp],y + dec dsp + txa + sta [dsp],y + bra next3 +; + +code sp! ( x -- ) + lda [dsp],y + tax + iny + lda [dsp],y + dey + sta dsp+1 + stx dsp + ;bra next3 + +next3: +; tya +; cmp #0 +; beq 5$ +; sta SYS_EXIT +;5$: + lda [ip],y + sta 2$+1 ; self modifying code + inc ip + bne 1$ + inc ip+1 +1$: lda [ip],y + sta 2$+2 ; self modifying code + inc ip + bne 2$ + inc ip+1 +2$: jmp 0 +; + +code rp@ ( -- x ) + ldx dsp + bne 1$ + dec dsp+1 +1$: dec dsp + lda rsp+1 + sta [dsp],y + dec dsp + lda rsp + sta [dsp],y + bra next3 +; + +code rp! ( x -- ) + lda [dsp],y + sta rsp + inc dsp + lda [dsp],y + sta rsp+1 + inc dsp + bne next3 + inc dsp+1 + bra next3 +; + +code um* ( u1 u2 -- ud ) + sty temp+4 + sty temp+5 + + lda [dsp],y + sta temp + iny + lda [dsp],y + sta temp+1 + iny + lda [dsp],y + sta temp+2 +; tax + iny + lda [dsp],y + sta temp+3 +; jsr print_hexw +; lda #'* +; jsr print_char +; lda temp+1 +; ldx temp +; jsr print_hexw +; lda #'= +; jsr print_char + + ldx #16 + ; cf does not matter here (shift in a random bit that isn't used) + ror temp+3 + ror temp+2 +1$: bcc 2$ + clc + lda temp+4 + adc temp + sta temp+4 + lda temp+5 + adc temp+1 + sta temp+5 +2$: ror temp+5 + ror temp+4 + ror temp+3 + ror temp+2 + dex + bne 1$ +; lda temp+5 +; ldx temp+4 +; jsr print_hexw +; lda temp+3 +; ldx temp+2 +; jsr print_hexw +; lda #0xa +; jsr print_char + + lda temp+3 + sta [dsp],y + dey + lda temp+2 + sta [dsp],y + dey + lda temp+5 + sta [dsp],y + dey + lda temp+4 + sta [dsp],y + + ;bra next4 + +next4: +; tya +; cmp #0 +; beq 5$ +; sta SYS_EXIT +;5$: + lda [ip],y + sta 2$+1 ; self modifying code + inc ip + bne 1$ + inc ip+1 +1$: lda [ip],y + sta 2$+2 ; self modifying code + inc ip + bne 2$ + inc ip+1 +2$: jmp 0 +; + +code um/mod ( ud u1 -- u2 u3 ) + lda [dsp],y + sta temp + inc dsp + lda [dsp],y + sta temp+1 + inc dsp + bne 1$ + inc dsp+1 + +1$: lda [dsp],y + sta temp+4 +; tax + iny + lda [dsp],y + sta temp+5 +; jsr print_hexw + iny + lda [dsp],y + sta temp+2 +; tax + iny + lda [dsp],y + sta temp+3 +; jsr print_hexw +; lda #'/ +; jsr print_char +; ldx temp +; lda temp+1 +; jsr print_hexw +; lda #'= +; jsr print_char + + ldx #16 + ; cf does not matter here (shift in a random bit that isn't used) + bra 4$ +2$: ; shift left has not overflowed, try to subtract divisor + sec + lda temp+4 + sbc temp + sta temp+4 + lda temp+5 + sbc temp+1 + sta temp+5 + bcs 3$ ; went, record 1 in quotient + lda temp+4 + adc temp + sta temp+4 + lda temp+5 + adc temp+1 + sta temp+5 + clc ; did not go, record 0 in quotient +3$: dex + beq 5$ +4$: ; loop entry + rol temp+2 + rol temp+3 + rol temp+4 + rol temp+5 + bcc 2$ + ; shift left has overflowed, so we can always subtract divisor + lda temp+4 + sbc temp + sta temp+4 + lda temp+5 + sbc temp+1 + sta temp+5 + sec ; went, record 1 in quotient + dex + bne 4$ +5$: ; loop done + rol temp+2 + rol temp+3 ; record final quotient bit +; lda temp+5 +; ldx temp+4 +; jsr print_hexw +; lda #'r +; jsr print_char +; lda temp+3 +; ldx temp+2 +; jsr print_hexw +; lda #0xa +; jsr print_char + + lda temp+5 + sta [dsp],y + dey + lda temp+4 + sta [dsp],y + dey + lda temp+3 + sta [dsp],y + dey + lda temp+2 + sta [dsp],y + + bra next5 +; + +code usleep ( c -- ) + lda [dsp],y + sta USLEEP_LO + inc dsp + lda [dsp],y + sta USLEEP_HI + inc dsp + bne next5 + inc dsp+1 + ;bra next5 + +next5: +; tya +; cmp #0 +; beq 5$ +; sta SYS_EXIT +;5$: + lda [ip],y + sta 2$+1 ; self modifying code + inc ip + bne 1$ + inc ip+1 +1$: lda [ip],y + sta 2$+2 ; self modifying code + inc ip + bne 2$ + inc ip+1 +2$: jmp 0 + +; + +\ we want the text section to be first and bss last (for linkers that output +\ sections in definition order), so it would be good to have the bss section +\ be output by the ",end" hook, but at present we cannot call "pre" from a +\ defined word, so we make do by switching to bss and then back to text again +pre +;print_hexw: ; print a:x +; jsr print_hexb +; txa +;print_hexb: +; pha +; lsr a +; lsr a +; lsr a +; lsr a +; jsr print_hexn +; pla +; and #0xf +;print_hexn: +; ora #'0 +; cmp #'0 + 10 +; bcc print_char +; adc #'a - '0 - 10 - 1 +;print_char: +; sta STDERR_DATA +; rts + + .area data + + ; dictionary pointer: points to next free location in memory +_dp: .dw _mem + + .area bss + + ; head pointer: index of first unused head +__hp: .ds 2 +_head: .ds HEAD_SIZE*2 + + ; free memory starts at _mem +_mem: .ds MEM_SIZE +_memtop: + + .area text + +; diff --git a/65c02/seedForth-65c02.pre b/65c02/seedForth-65c02.pre new file mode 100644 index 0000000..ad54502 --- /dev/null +++ b/65c02/seedForth-65c02.pre @@ -0,0 +1,31 @@ +\ seedForth: less machine dependent portion +\ allows us to adjust things for direct threaded vs indirect threaded + +\ insert "jsr _dodoes" after each "does>" token +: ?does> ( xt -- xt | ) + dup lit does> - ?exit \ not does> token: exit i.e. normal compile action + , 32 c, lit dodoes , \ generate word of does> and instruction of jsr + r> drop tail compiler ; + +: compiler ( -- ) + token h@ + ?eot + ?lit + ?does> + , tail compiler ; + +\ for 65c02 dtc implementation, compile "jsr _enter" before high level code +: new ( -- xt ) + here 32 c, lit enter , ; + +\ for 65c02 dtc implementation, compile "jsr _dovar" before data field of new +\ word, the "_dovar" will be changed the address of "jsr _dodoes" if needed +: create ( -- xt ) + here 32 c, lit dovar , ; + +\ for does> we do not execute the remainder of the routine, instead we pop +\ the return stack and plug the resulting number into the word being compiled, +\ so that this word will execute the remainder of the routine when invoked +\ (and note remainder of the routine has been prefixed with a "jsr _dodoes") +: does> ( xt -- ) \ replace "_dovar" in "jsr _dovar" with return stack addr + r> swap 1 + ! ; diff --git a/65c02/seedForth-tokenizer b/65c02/seedForth-tokenizer new file mode 100755 index 0000000..b651b20 --- /dev/null +++ b/65c02/seedForth-tokenizer @@ -0,0 +1,2 @@ +#!/bin/sh +../emu_65c02/emu_65c02 seedForth.bin seedForthBoot.seed ../common/runtime.forth ../common/crc10.forth ../common/seedForth-tokenizer.fs - diff --git a/65c02/seedForthDemo65c02.seedsource b/65c02/seedForthDemo65c02.seedsource new file mode 100644 index 0000000..48d1987 --- /dev/null +++ b/65c02/seedForthDemo65c02.seedsource @@ -0,0 +1,15 @@ +\ machine dependent part of seedForthDemo.seedsource +\ allows us to adjust things for direct threaded vs indirect threaded + +\ we must index past the "jsr" instruction +: >body ( xt -- body ) 3 + ; + +: is ( xt -- ) ' >body ! ; + +Defer d1 +' ten is d1 +t{ d1 d1 d1 -> ten ten ten }t +' five is d1 +t{ d1 d1 d1 -> five five five }t + +done bye diff --git a/65c02/seedForthRuntime65c02.seedsource b/65c02/seedForthRuntime65c02.seedsource new file mode 100644 index 0000000..5d18023 --- /dev/null +++ b/65c02/seedForthRuntime65c02.seedsource @@ -0,0 +1,22 @@ +\ machine dependent part of seedForthRuntime.seedsource +\ allows us to adjust things for direct threaded vs indirect threaded + +\ we must index past the "jsr" instruction +: >body ( xt -- body ) + 3 + ; +' >body has-header >body + +: is ( xt -- ) \ only interactive + ' >body ! ; +' is has-header is + +\ these could not be done until here +' key is getkey +' noop is .status + +\ insert "jsr _dodoes" after each "does>" token +: (Does>) ( -- ) + [ ' last-xt ] Literal , + [ ' does> ] Literal , + 32 c, [ ' dodoes ] Literal , ; +' (Does>) has-header Does> immediate diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..9197608 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,16 @@ +preForth, seedForth, seedForthInteractive +Copyright 2018-2020 Ulrich Hoffman +Copyright 2022 Nick Downing + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8751a54 --- /dev/null +++ b/Makefile @@ -0,0 +1,38 @@ +.PHONY: all +all: 65c02 asxv5pxx common emu_65c02 emu_z80 i386 z80 + +.PHONY: 65c02 +65c02: emu_65c02 common + $(MAKE) $(MAKEFLAGS) -C 65c02 + +.PHONY: asxv5pxx +asxv5pxx: + $(MAKE) $(MAKEFLAGS) -C asxv5pxx/asxmak/linux/build asz80 as6500 aslink + +.PHONY: common +common: + $(MAKE) $(MAKEFLAGS) -C common + +.PHONY: emu_65c02 +emu_65c02: asxv5pxx + $(MAKE) $(MAKEFLAGS) -C emu_65c02 + +.PHONY: emu_z80 +emu_z80: asxv5pxx + $(MAKE) $(MAKEFLAGS) -C emu_z80 + +.PHONY: i386 +i386: common + $(MAKE) $(MAKEFLAGS) -C i386 + +.PHONY: z80 +z80: emu_z80 common + $(MAKE) $(MAKEFLAGS) -C z80 + +clean: + $(MAKE) $(MAKEFLAGS) -C asxv5pxx/asxmak/linux/build clean + # avoid git complaining of changes in subrepo: + touch asxv5pxx/asxmak/linux/exe/_exe + $(MAKE) $(MAKEFLAGS) -C common clean + $(MAKE) $(MAKEFLAGS) -C emu_z80 clean + $(MAKE) $(MAKEFLAGS) -C i386 clean diff --git a/README.md b/README.md index 9102921..900850f 100644 --- a/README.md +++ b/README.md @@ -83,3 +83,6 @@ Inspect sources and generated files. *Have fun. May the Forth be with you.* +# Copyright and license + +Please see the files `COPYRIGHT` and `LICENSE` in the root of this repository. diff --git a/asxv5pxx b/asxv5pxx new file mode 160000 index 0000000..6d5d121 --- /dev/null +++ b/asxv5pxx @@ -0,0 +1 @@ +Subproject commit 6d5d1219781ad4b90294d84bd2589cccc4d728f3 diff --git a/preForth/.gitignore b/common/.gitignore similarity index 100% rename from preForth/.gitignore rename to common/.gitignore diff --git a/preForth/Dockerfile b/common/Dockerfile similarity index 100% rename from preForth/Dockerfile rename to common/Dockerfile diff --git a/common/Makefile b/common/Makefile new file mode 100644 index 0000000..c75c47c --- /dev/null +++ b/common/Makefile @@ -0,0 +1,15 @@ +# Set HOSTFORTH to the Forth system that generates the initial preForth +# ------------------------------------------------------------------------ +HOSTFORTH=gforth +# HOSTFORTH=sf # SwiftForth >3.7 +# ------------------------------------------------------------------------ + +.PHONY: all +all: crc10.forth + +crc10.forth: crc10_gen.forth + $(HOSTFORTH) $^ -e bye >$@ + +.PHONY: clean +clean: + rm -f crc10.forth diff --git a/common/crc10_gen.forth b/common/crc10_gen.forth new file mode 100644 index 0000000..11b94eb --- /dev/null +++ b/common/crc10_gen.forth @@ -0,0 +1,50 @@ +\ run this as follows: +\ ./seedForth seedForthBoot.seed runtime.forth crc10_gen.forth >crc10.forth + +817 Constant poly \ CRC-10/ATM, truncated polynomial 0x233, bit reversed 0x331 + +\ we don't want MS-DOS style line endings in the generated CRC table file +: lf 10 emit ; + +: gen + ." Create crc10_tab" lf + 256 0 ?DO + I 8 0 ?DO + \ split into LSB and the remaining bits + \ in seedForth: 2 u/mod swap + \ in gForth: 2 /mod swap + dup 1 rshift swap 1 and + + IF poly xor THEN + LOOP + . ',' emit + I 7 and 7 = IF lf ELSE space THEN + LOOP + lf + ." : crc10 ( addr len crc -- crc )" lf + ." swap 0 ?DO ( addr crc )" lf + ." \ retrieve next character" lf + ." over I + c@" lf + lf + ." \ xor into low bits of crc" lf + ." xor" lf + lf + ." \ separate into table index and remaining bits" lf + ." \ 256 u/mod swap" lf + ." dup 8 rshift swap 255 and" lf + lf + ." \ look up new bits from table" lf + ." cells crc10_tab + @" lf + lf + ." \ combine with remaining (shifted right) bits" lf + ." xor" lf + ." LOOP" lf + ." nip" lf + ." ;" lf + lf + ." \ testing:" lf + ." \ Create hello 104 c, 101 c, 108 c, 108 c, 111 c," lf + ." \ hello 5 1023 crc10 . ;" lf +; + +gen diff --git a/preForth/hi.forth b/common/hi.forth similarity index 82% rename from preForth/hi.forth rename to common/hi.forth index 4cd1091..5223203 100644 --- a/preForth/hi.forth +++ b/common/hi.forth @@ -1,15 +1,5 @@ -0 echo ! -0 input-echo ! - cr .( ⓪ ) -: ( - ')' parse 2drop ; immediate - -: \ - source nip >in ! ; immediate - - \ save and empty (need HP) single wordlist only no string header reclaim \ HEAD, the start of seedForth header table is not required. \ DP is not required as it can be read via HERE and set via ALLOT (see DP!) @@ -31,71 +21,13 @@ cr .( ⓪ ) \ t{ 3 -> }t \ wrong number of results \ t{ 3 4 + -> 8 }t \ incorrect result -: AHEAD ( -- c:orig ) - postpone branch here 0 , ; immediate - -: IF ( -- c:orig ) - postpone ?branch here 0 , ; immediate - -: THEN ( c:orig -- ) - here swap ! ; immediate - -: ELSE ( c:orig1 -- c:orig2 ) - postpone AHEAD swap postpone THEN ; immediate - -: BEGIN ( -- c:dest ) - here ; immediate - -: WHILE ( c: orig -- c:dest c:orig ) - postpone IF swap ; immediate - -: AGAIN ( c:orig -- ) - postpone branch , ; immediate - -: UNTIL ( c:orig -- ) - postpone ?branch , ; immediate - -: REPEAT ( c:orig c:dest -- ) - postpone AGAIN postpone THEN ; immediate - -\ are these necessary? -\ you can use the phrase dup x = IF drop instead of x case? IF or x OF -: case? ( n1 n2 -- true | n1 false ) - over = dup IF nip THEN ; - -: OF ( n1 n2 -- n1 | ) - postpone case? postpone IF ; immediate - cr .( ① ) cr -: :noname ( -- xt ) - new ] ; - -: Variable ( ) - Create 0 , ; - -: Constant ( x -- ) - Create , Does> @ ; - -0 Constant false -false invert Constant true - - : on ( addr -- ) true swap ! ; : off ( addr -- ) false swap ! ; -: fill ( c-addr u x -- ) - >r BEGIN ( c-addr u ) - dup - WHILE ( c-addr u ) - r@ third c! - 1 /string - REPEAT ( c-addr u ) - 2drop r> drop -; - : erase ( c-addr u -- ) 0 fill ; : blank ( c-addr u -- ) bl fill ; @@ -167,18 +99,6 @@ t{ 1 2 3 4 5 6 2rot -> 3 4 5 6 1 2 }t end-tests -: lshift ( x u -- ) BEGIN ?dup WHILE swap 2* swap 1- REPEAT ; - -\ if we don't have u2/ but only 2* and 0< we need to implement u2/ with a loop. Candidate for primitive -: u2/ ( x1 -- x2 ) - 0 8 cells 1- \ for every bit - BEGIN ( x q n ) - ?dup - WHILE ( x q n ) - >r 2* over 0< IF 1+ THEN >r 2* r> r> 1- - REPEAT ( x q n ) - nip ; - begin-tests t{ -1 u2/ dup 1+ u< -> -1 }t @@ -186,8 +106,6 @@ t{ -1 u2/ 10 + dup 10 + u< -> -1 }t end-tests -: rshift ( x u -- ) BEGIN ?dup WHILE swap u2/ swap 1- REPEAT ; - : s>d ( n -- d ) dup 0< ; t{ 1 3 lshift -> 8 }t @@ -254,9 +172,6 @@ cr .( ② ) \ : test s" xlerb" evaluate ; -: * ( n1 n2 -- ) - 2dup xor 0< >r abs swap abs um* drop r> IF negate THEN ; - : fac ( n -- ) recursive dup 0= IF drop 1 exit THEN dup 1- fac * ; @@ -280,8 +195,6 @@ end-tests : sqr ( u -- u^2 ) dup * ; -: u/ ( u1 u2 -- u3 ) >r 0 r> um/mod nip ; - : sqrt ( u^2 -- u ) dup 0= ?exit dup >r dup @@ -306,7 +219,7 @@ end-tests begin-tests t{ 3 4 pyth -> 5 }t -t{ 65535 dup * sqrt -> 65535 }t +t{ test_sqr dup * sqrt -> test_sqr }t end-tests @@ -384,7 +297,7 @@ Defer %defer ' %defer >body 2 cells - @ Constant dodefer [ ' exit ] Literal >body 1 cells - r> cell+ ! ; : backpatch ( xt1 xt2 -- ) - here >r >body dp! compile, postpone exit r> dp! ; + here >r >body dp! , postpone exit r> dp! ; begin-tests @@ -398,18 +311,6 @@ begin-tests end-tests -: FOR ( n -- ) - postpone BEGIN - postpone >r ; immediate - -: NEXT ( -- ) - postpone r> - postpone 1- - postpone dup - postpone 0< - postpone UNTIL - postpone drop ; immediate - : cntdwn 65535 FOR r@ . NEXT ; : ² sqr ; @@ -452,8 +353,6 @@ Variable voc-link 0 voc-link ! ' .voc ' .wordlist backpatch -: recurse ( -- ) last @ _xt @ compile, ; immediate - : cntd ( n -- ) ?dup 0= ?exit dup . 1- recurse '.' emit ; \ division / /mod fm/mod sm/rem mod @@ -588,7 +487,7 @@ only Forth also definitions : th.prime ( u -- ) 1 BEGIN over WHILE 1+ dup prime? IF swap 1- swap THEN REPEAT nip ; -cr cr cr .( The ) 10001 dup . .( st prime is ) th.prime . +cr cr cr .( The ) test_prime dup . .( st prime is ) th.prime . \ cooperative multi tasker @@ -810,28 +709,6 @@ cr .( Interactive decompiler: Use single letter commands n d l c b s ) cr cr dump-line REPEAT 2drop ; -\ conditional compilation - -| : next-token ( -- c-addr u ) - BEGIN - parse-name dup 0= - WHILE ( c-addr u ) - 2drop refill 0= -39 and throw - REPEAT ( c-addr u ) ; - -| : ([ELSE]) ( level c-addr u -- level' ) - 2dup s" [IF]" compare 0= IF 2drop 1+ exit THEN - 2dup s" [ELSE]" compare 0= IF 2drop 1- dup IF 1+ THEN exit THEN - s" [THEN]" compare 0= IF 1- THEN ; - -: [ELSE] ( -- ) - 1 BEGIN ( level ) next-token ([ELSE]) ?dup 0= UNTIL ; immediate - -: [IF] ( f -- ) ?exit postpone [ELSE] ; immediate - -: [THEN] ; immediate - - 1 [IF] cr .( ok: if line, ) .( ok: next line) [ELSE] cr .( fail: else line, ) @@ -846,44 +723,8 @@ cr .( Interactive decompiler: Use single letter commands n d l c b s ) cr cr .( ok: afterwords ) -: abort ( -- ) -1 throw ; - -| : (abort") ( f c-addr u -- ) rot IF errormsg 2! -2 throw THEN 2drop ; - -: abort" ( f -- ) - postpone s" - postpone (abort") ; immediate - \ : abort"test ( -- ) dup abort" abort" ; -: chars ; immediate - -: char+ 1+ ; - -' exit Alias EXIT - -: bounds ( addr count -- limit addr) over + swap ; - -: DO ( to from -- ) - postpone swap - postpone BEGIN - postpone >r postpone >r ; immediate - -: LOOP ( -- ) - postpone r> - postpone 1+ - postpone r> - postpone 2dup postpone = postpone UNTIL - postpone 2drop ; immediate - -: I ( -- ) - postpone r@ ; immediate - -\ : ?DO ( to from -- ) -\ postpone 2dup -\ postpone - -\ postpone IF postpone DO ; immediate - begin-tests t{ : dotest 10 0 DO I LOOP ; dotest -> 0 1 2 3 4 5 6 7 8 9 }t diff --git a/common/hi16bit.forth b/common/hi16bit.forth new file mode 100644 index 0000000..7496c75 --- /dev/null +++ b/common/hi16bit.forth @@ -0,0 +1,2 @@ +255 Constant test_sqr +1001 Constant test_prime diff --git a/common/hi32bit.forth b/common/hi32bit.forth new file mode 100644 index 0000000..4368905 --- /dev/null +++ b/common/hi32bit.forth @@ -0,0 +1,2 @@ +65535 Constant test_sqr +10001 Constant test_prime diff --git a/common/preForth-bootstrap.fs b/common/preForth-bootstrap.fs new file mode 100644 index 0000000..129a24e --- /dev/null +++ b/common/preForth-bootstrap.fs @@ -0,0 +1,20 @@ +\ preForth runtime system - compatibility package for bootstrap + +\ since preForth is mostly a subset of standard Forth, preForth programs can +\ basically run under gForth or similar for bootstrapping, after including: +\ preForth-bootstrap.fs: adjust environment (not needed under pure preForth) +\ preForth-rts-nonstandard.fs: useful words (needed under/part of preForth) + +\ This : allows for recursion by using a word's name. +defined -smudge [IF] \ SwiftForth +: : : -smudge ; +[THEN] + +defined reveal [IF] \ gforth +: : : reveal ; +[THEN] + +\ ignore tail recursion optimization +\ the host system is assumed to have a large enough stack to handle the +\ steady growth of the stack as the compiler loops through the input file +: tail ( -- ) ; diff --git a/common/preForth-cold.fs b/common/preForth-cold.fs new file mode 100644 index 0000000..c4d2343 --- /dev/null +++ b/common/preForth-cold.fs @@ -0,0 +1,4 @@ +\ include this after the bootstrap compiler, to launch reading of stdin + +quit \ quit is the top-level interpreter loop +bye diff --git a/common/preForth-rts-nonstandard.pre b/common/preForth-rts-nonstandard.pre new file mode 100644 index 0000000..67a8ff0 --- /dev/null +++ b/common/preForth-rts-nonstandard.pre @@ -0,0 +1,64 @@ +\ preForth runtime system - machine independent part - nonstandard words + +\ since preForth is mostly a subset of standard Forth, preForth programs can +\ basically run under gForth or similar for bootstrapping, after including: +\ preForth-bootstrap.fs: adjust environment (not needed under pure preForth) +\ preForth-rts-nonstandard.fs: useful words (needed under/part of preForth) + +\ case? compares the value x to y. If they match, return true. If not keep x +\ and return false. +: case? ( x y -- tf | x ff ) + over = dup 0= ?exit 2drop -1 ; + +\ text output words +\ ----------------- + +: tab ( -- ) + 9 emit ; + + +\ number output +\ ------------- + +: 10* ( x1 -- x2 ) + dup + dup dup + dup + + ; + + +\ strings +\ ------- + +\ Strings are represented as character stack elements with a count on top +\ They can be processed conveniently using recursion. +\ Idioms: dup pick gets 1st character +\ dup gets length +\ x swap 1+ adds x to end of string +\ nip 1- removes last character +\ +\ Useful words +\ show displays trings +\ _dup duplicates topmost string +\ _drop removes topmost string +\ _swap exchanges two topmost strings + +\ show displays topmost string +: show ( S -- ) + ?dup 0= ?exit swap >r 1- show r> emit ; + +: (_dup ( S m n -- S S ) + ?dup 0= ?exit over 2 + pick rot rot 1- tail (_dup ; + +\ _dup duplicated topmost string +: _dup ( S -- S S ) + dup dup (_dup ; + +\ _drop removes topmost string +: _drop ( S -- ) + ?dup 0= ?exit nip 1- _drop ; + + +: (_swap ( S1 S2 x1 x2 -- S2 S1 x1 0 ) + dup 0= ?exit over 3 + roll rot rot 1- (_swap ; + +\ _swap exchanges two topmost strings +: _swap ( S1 S2 -- S2 S1 ) + dup >r pick r> dup >r over >r + r> r> rot rot 1+ (_swap 2drop ; diff --git a/preForth/preForth-rts.pre b/common/preForth-rts.pre similarity index 53% rename from preForth/preForth-rts.pre rename to common/preForth-rts.pre index a66b851..eb088c3 100644 --- a/preForth/preForth-rts.pre +++ b/common/preForth-rts.pre @@ -1,4 +1,4 @@ -\ preForth runtime system - machine independent part +\ preForth runtime system - machine independent part - standard words \ ------------------------------------ \ define lots of useful standard words @@ -31,10 +31,6 @@ : > ( n1 n2 -- flag ) swap < ; -\ case? compares the value x to y. If they match, return true. If not keep x and return false. -: case? ( x y -- tf | x ff ) - over = dup 0= ?exit 2drop -1 ; - \ additional stack operators \ -------------------------- @@ -67,9 +63,6 @@ : space ( -- ) bl emit ; -: tab ( -- ) - 9 emit ; - : cr ( -- ) 10 emit ; @@ -79,10 +72,7 @@ : (/mod ( n d q0 -- r d q ) >r over over < r> swap ?exit - >r swap over - swap r> 1+ (/mod ; - -: 10* ( x1 -- x2 ) - dup + dup dup + dup + + ; + >r swap over - swap r> 1+ tail (/mod ; : (10u/mod ( n q d -- r q d ) 2 pick over > 0= ?exit \ ( n q d ) @@ -107,45 +97,3 @@ \ display signed number : . ( n -- ) (. u. ; - - -\ ----------- -\ strings -\ ----------- -\ Strings are represented as character stack elements with a count on top -\ They convieniently be processed using recursion. -\ Idioms: dup pick gets 1st character -\ dup gets length -\ x swap 1+ adds x to end of string -\ nip 1- removes last character -\ -\ Useful words -\ show displays trings -\ _dup duplicates topmost string -\ _drop removes topmost string -\ _swap exchanges two topmost strings - -\ show displays topmost string -: show ( S -- ) - ?dup 0= ?exit swap >r 1- show r> emit ; - - -: (_dup ( S m n -- S S ) - ?dup 0= ?exit over 2 + pick rot rot 1- tail (_dup ; - -\ _dup duplicated topmost string -: _dup ( S -- S S ) - dup dup (_dup ; - -\ _drop removes topmost string -: _drop ( S -- ) - ?dup 0= ?exit nip 1- _drop ; - - -: (_swap ( S1 S2 x1 x2 -- S2 S1 x1 0 ) - dup 0= ?exit over 3 + roll rot rot 1- (_swap ; - -\ _swap exchanges two topmost strings -: _swap ( S1 S2 -- S2 S1 ) - dup >r pick r> dup >r over >r + r> r> rot rot 1+ (_swap 2drop ; - diff --git a/preForth/preForth.pre b/common/preForth.pre similarity index 96% rename from preForth/preForth.pre rename to common/preForth.pre index ee9f710..6418cc8 100644 --- a/preForth/preForth.pre +++ b/common/preForth.pre @@ -38,7 +38,7 @@ \ \ compiler words: \ ,line ,comment ,codefield ,end -\ ,lit ,>word ,nest ,unnest ,tail +\ ,lit ,word ,nest ,unnest ,tail \ \ header creation: \ header label bodylabel @@ -130,7 +130,7 @@ : code ( -- ) token _dup ,comment - 0 header + \ 0 header ,code line _drop pre ,end-code ; \ Colon definitions - the preForth compiler @@ -238,7 +238,7 @@ \ ?word detects and handles words by compiling them as reference. : ?word ( S -- 0 | S ) - dup 0= ?exit ,>word 0 ; + dup 0= ?exit ,word 0 ; \ Compiler loop \ ------------- @@ -262,7 +262,7 @@ : :' ( -- ) token _dup ,comment - 0 header + \ 0 header (: ; \ ----------- @@ -316,13 +316,7 @@ \ cold initializes the dictionary link and starts the interpreter. Acknowledge end on exit. : cold ( -- ) - '0' 1 \ dictionary anchor - quit _drop \ eof + \ '0' 1 \ dictionary anchor + quit \ _drop \ eof \ top of dictionary as string on stack ,end ; - -\ : is eventually defined as preForth is now complete (assuming the primitives existed). -\ In order to bootstrap. They have to be defined. -: : ( -- ) - :' ; - diff --git a/preForth/preForthDemo.pre b/common/preForthDemo.pre similarity index 100% rename from preForth/preForthDemo.pre rename to common/preForthDemo.pre diff --git a/common/runtime.forth b/common/runtime.forth new file mode 100644 index 0000000..535f4bb --- /dev/null +++ b/common/runtime.forth @@ -0,0 +1,168 @@ +: ( + ')' parse 2drop ; immediate + +: \ + source nip >in ! ; immediate + +: AHEAD ( -- c:orig ) + postpone branch here 0 , ; immediate + +: IF ( -- c:orig ) + postpone ?branch here 0 , ; immediate + +: THEN ( c:orig -- ) + here swap ! ; immediate + +: ELSE ( c:orig1 -- c:orig2 ) + postpone AHEAD swap postpone THEN ; immediate + +: BEGIN ( -- c:dest ) + here ; immediate + +: WHILE ( c: orig -- c:dest c:orig ) + postpone IF swap ; immediate + +: AGAIN ( c:orig -- ) + postpone branch , ; immediate + +: UNTIL ( c:orig -- ) + postpone ?branch , ; immediate + +: REPEAT ( c:orig c:dest -- ) + postpone AGAIN postpone THEN ; immediate + +\ are these necessary? +\ you can use the phrase dup x = IF drop instead of x case? IF or x OF +: case? ( n1 n2 -- true | n1 false ) + over = dup IF nip THEN ; + +: OF ( n1 n2 -- n1 | ) + postpone case? postpone IF ; immediate + +: :noname ( -- xt ) + new ] ; + +: Variable ( ) + Create 0 , ; + +: Constant ( x -- ) + Create , Does> @ ; + +0 Constant false +false invert Constant true + +: fill ( c-addr u x -- ) + >r BEGIN ( c-addr u ) + dup + WHILE ( c-addr u ) + r@ third c! + 1 /string + REPEAT ( c-addr u ) + 2drop r> drop +; + +: lshift ( x u -- ) BEGIN ?dup WHILE swap 2* swap 1- REPEAT ; + +\ if we don't have u2/ but only 2* and 0< we need to implement u2/ with a loop. Candidate for primitive +: u2/ ( x1 -- x2 ) + 0 8 cells 1- \ for every bit + BEGIN ( x q n ) + ?dup + WHILE ( x q n ) + >r 2* over 0< IF 1+ THEN >r 2* r> r> 1- + REPEAT ( x q n ) + nip ; + +: rshift ( x u -- ) BEGIN ?dup WHILE swap u2/ swap 1- REPEAT ; + +: * ( n1 n2 -- ) + 2dup xor 0< >r abs swap abs um* drop r> IF negate THEN ; + +: u/ ( n1 n2 -- quot ) 0 swap um/mod nip ; + +: u/mod ( n1 n2 -- rem quot ) 0 swap um/mod ; + +: umod ( n1 n2 -- rem ) 0 swap um/mod drop ; + +: FOR ( n -- ) + postpone BEGIN + postpone >r ; immediate + +: NEXT ( -- ) + postpone r> + postpone 1- + postpone dup + postpone 0< + postpone UNTIL + postpone drop ; immediate + +: recurse ( -- ) last @ _xt @ , ; immediate + +\ conditional compilation + +| : next-token ( -- c-addr u ) + BEGIN + parse-name dup 0= + WHILE ( c-addr u ) + 2drop refill 0= -39 and throw + REPEAT ( c-addr u ) ; + +| : ([ELSE]) ( level c-addr u -- level' ) + 2dup s" [IF]" compare 0= IF 2drop 1+ exit THEN + 2dup s" [ELSE]" compare 0= IF 2drop 1- dup IF 1+ THEN exit THEN + s" [THEN]" compare 0= IF 1- THEN ; + +: [ELSE] ( -- ) + 1 BEGIN ( level ) next-token ([ELSE]) ?dup 0= UNTIL ; immediate + +: [IF] ( f -- ) ?exit postpone [ELSE] ; immediate + +: [THEN] ; immediate + +: abort ( -- ) -1 throw ; + +: chars ; immediate + +: char+ 1+ ; + +' exit Alias EXIT + +: bounds ( addr count -- limit addr) over + swap ; + +| : (abort") ( f c-addr u -- ) rot IF errormsg 2! -2 throw THEN 2drop ; + +: abort" ( f -- ) + postpone s" + postpone (abort") ; immediate + +: DO ( to from -- ) + 0 \ open a dummy IF block for the backpatching test in LOOP + postpone swap + postpone BEGIN + postpone >r postpone >r ; immediate + +: ?DO ( to from -- ) + postpone 2dup + postpone - + postpone IF + postpone swap + postpone BEGIN + postpone >r postpone >r ; immediate + +: LOOP ( -- ) + postpone r> + postpone 1+ + postpone r> + postpone 2dup postpone = postpone UNTIL + postpone 2drop + \ this backpatches IF block for ?DO or does nothing for DO + ?dup IF postpone THEN THEN ; immediate + +: I ( -- ) + postpone r@ ; immediate + +\ Nick +: UNLOOP ( -- ) + postpone r> + postpone r> + postpone 2drop ; immediate diff --git a/common/seedForth-tokenizer.fs b/common/seedForth-tokenizer.fs new file mode 100644 index 0000000..b8749f0 --- /dev/null +++ b/common/seedForth-tokenizer.fs @@ -0,0 +1,420 @@ +\ Another seedForth tokenizer 2019-10-18 + +\ seedForth does not support hex so put some useful constants in decimal +255 Constant xFF +1023 Constant x3FF +1024 Constant x400 +65261 Constant xFEED + +\ exceptions +100 Constant except-hash-table-full + +\ hash table entry structure +0 Constant _hash-table-xt +1 cells Constant _hash-table-name-addr +2 cells Constant _hash-table-name-len +3 cells Constant #hash-table + +\ the sizing below accommodates up to 1K word definitions +\ (the same as the number of tokens available to seedForth) +x3FF Constant hash-table-mask +x400 Constant hash-table-size +Create hash-table +hash-table-size #hash-table * dup allot hash-table swap 0 fill + +: hash-table-index ( entry -- addr ) + #hash-table * hash-table + ; + +: hash-table-find ( name-addr name-len -- entry-addr found ) + \ calculate CRC10 of the symbol name + \ initial value is same as hash table mask (all 1s) + 2dup hash-table-mask crc10 + \ hash-table-mask and + + \ using the CRC10 as the starting entry, look circularly + \ for either a null entry (not found) or a matching entry + hash-table-size 0 ?DO ( name-addr name-len entry ) + dup >r hash-table-index >r ( name-addr name-len R: entry entry-addr ) + + \ check for null entry + r@ _hash-table-name-len + @ 0= IF + 2drop r> r> drop false UNLOOP exit + THEN + + \ check for matching entry + 2dup + r@ _hash-table-name-addr + @ + r@ _hash-table-name-len + @ + compare 0= IF + 2drop r> r> drop true UNLOOP exit + THEN + + \ go to next entry, circularly + r> drop + r> 1+ hash-table-mask and + LOOP + + \ not found, and no room for new entry + except-hash-table-full throw +; + +: token@ ( c-addr u -- x ) + \ get entry address and flag for found/empty + hash-table-find + + \ if found, return value of -xt, otherwise 0 + \ note: value of -xt can be 0 if caller has not filled it in yet + \ (e.g. occurs between a Macro call and corresponding end-macro) + IF _hash-table-xt + @ ELSE drop 0 THEN +; + +: ?token ( c-addr u -- x ) + \ get entry address and flag for found/empty + 2dup hash-table-find + + \ if empty, copy symbol name and fill in entry + 0= IF + >r + here r@ _hash-table-name-addr + ! + dup r@ _hash-table-name-len + ! + here swap dup allot cmove + r> + ELSE + nip nip + THEN + + \ return address of -xt for caller to fill in + _hash-table-xt + +; + +\ VARIABLE OUTFILE + +: emit-token ( x -- ) + dup xFF > IF dup 8 rshift emit THEN emit ; + +\ The following words "Token", "Macro", "end-macro" and "seed" are the heart of +\ the tokenizer -- either "Token" or "Macro" makes an entry in the hash table, +\ and each hash table entry points to an anonymous function which is called by +\ "seed" when that token is encountered in the input stream. If you define it +\ with "Token", you get a canned routine that simply emits the corresponding +\ token into the *.seed file, but if you define it with "Macro" you specify the +\ routine to be executed when that token is compiled. So macros allows you to +\ compile control structures and so forth. "Macro" operates similarly to ":" in +\ that it switches to compilation mode. "end-macro" operates similarly to ";" +\ in that it finishes compilation, then it writes your routine into hash table. +\ Note that a difference between "Token" and "Macro" is that "Token" generates +\ a new token number (hence assuming we will emit a "fun" token to make the +\ seedForth kernel do the same at the other side), whereas "Macro" does not. +Variable #tokens 0 #tokens ! +Variable last-xt-ptr \ will be copied to colon-xt-ptr during colon definition +: Token ( -- ) + :noname + #tokens @ postpone Literal + postpone emit-token + postpone ; + parse-name ?token dup last-xt-ptr ! ! + 1 #tokens +! ; + +: Macro ( -- ) + parse-name ?token :noname xFEED ; + +: end-macro ( 'hash colon-sys -- ) + xFEED - abort" end-macro without corresponding Macro" + postpone ; ( 'hash xt ) swap ! ; immediate + +: seed ( i*x -- j*x ) + parse-name token@ dup 0= abort" is undefined" postpone Literal postpone execute ; immediate + + +( 0 $00 ) Token bye +4 #tokens ! +( 4 $04 ) Token eot Token dup Token swap Token drop +( 8 $08 ) Token 0< Token ?exit Token >r Token r> +( 12 $0C ) Token - Token exit Token lit Token @ +( 16 $10 ) Token c@ Token ! Token c! Token execute +( 20 $14 ) Token branch Token ?branch Token negate Token + +( 24 $18 ) Token 0= Token ?dup Token cells Token +! +( 28 $1C ) Token h@ Token h, Token here Token allot +( 32 $20 ) Token , Token c, Token fun Token interpreter +( 36 $24 ) Token compiler Token create Token does> Token cold +( 40 $28 ) Token depth Token dodoes Token new Token couple +( 44 $2C ) Token and Token or Token sp@ Token sp! +( 48 $30 ) Token rp@ Token rp! Token $lit Token num +( 52 $34 ) Token um* Token um/mod Token unused Token key? +( 56 $38 ) Token token Token usleep Token hp Token key +( 60 $3C ) Token emit Token eemit + +\ generate token sequences for numbers + +: seed-byte ( c -- ) + seed key emit ; + +: seed-number ( x -- ) \ x is placed in the token file. Handle also negative and large numbers + dup 0< IF 0 seed-byte negate recurse seed - EXIT THEN + dup xFF > IF dup 8 rshift recurse xFF and seed-byte seed couple EXIT THEN + seed-byte ; + +: char-lit? ( c-addr u -- x flag ) + 3 - IF drop 0 false EXIT THEN + dup c@ ''' - IF drop 0 false EXIT THEN + dup 2 chars + c@ ''' - IF drop 0 false EXIT THEN + char+ c@ true ; + +: process-digit? ( x c -- x' flag ) + '0' - dup 10 u< IF swap 10 * + true EXIT THEN drop false ; + +: process-number? ( c-addr u -- x flag ) + dup 0= IF 2drop 0 false EXIT THEN + over c@ '-' = dup >r IF 1 /string THEN + >r >r 0 r> r> bounds + ?DO ( x ) + I c@ process-digit? 0= IF UNLOOP r> drop false EXIT THEN ( x d ) + LOOP + r> IF negate THEN true ; + +: seed-name ( c-addr u -- ) + 2dup token@ dup IF nip nip execute EXIT THEN drop + 2dup char-lit? IF nip nip seed num seed-number seed exit EXIT THEN drop + 2dup process-number? IF nip nip seed num seed-number seed exit EXIT THEN drop + cr type ." not found" abort ; + +: seed-line ( -- ) + BEGIN parse-name dup WHILE seed-name REPEAT 2drop ; + +: seed-file ( -- ) + BEGIN refill WHILE seed-line REPEAT ; + +\ eot is overloaded to either: +\ - return from compilation state to interpretive state +\ (used for compiling ; and various control flow constructs) +\ - quit the interpreter if invoked in interpretive state +\ (can overload it because control flow is not allowed here) +\ this means that if the token stream runs out and starts to return +\ EOT characters, we will first terminate any word definition that +\ was in progress, then we'll do an automatic bye (in the old way, +\ there was an automatic bye token appended to seed file, but this +\ was annoying because seedForthInteractive had to read and drop it) +Macro [ ( -- ) + seed eot +end-macro +Macro ] ( -- ) + seed compiler +end-macro + +\ the colon-xt-ptr points into the hash table entry of the symbol +\ being defined during a colon-definition, indirecting through this +\ pointer gives the routine the tokenizer executes when compiling +\ that symbol later -- initially it just outputs the symbol's token, +\ but by editing the value at the colon-xt-ptr we can implement a +\ chain of actions to execute before outputting the symbol's token +Variable colon-xt-ptr 0 colon-xt-ptr ! +Macro : ( -- ) + Token last-xt-ptr @ colon-xt-ptr ! + seed fun +end-macro +Macro ; ( -- ) + 0 colon-xt-ptr ! + seed exit + seed [ +end-macro + +\ New style defining-words (regular Forth syntax) +\ Call the Create macro inside a definer-definition, e.g. +\ : Variable Create drop 0 , ; +\ This scheme can also handle SOME more complex cases, e.g. +\ : 2Variable Create drop 0 , Create drop 0 , ; +\ But, it does not have the full generality of Forth Create, since we cannot +\ properly handle arguments to Forth words when working via tokenizer +Macro Create + colon-xt-ptr @ 0= abort" Create outside of colon-definition" + + \ tokenizer side of creating a new defining-word + \ hook the routine at colon-xt-ptr to call Token then call the old routine, + \ so each time seed source calls e.g. Variable, we'll consume the name and + \ create the corresponding token (then the new variable can be referenced) + :noname + postpone Token + colon-xt-ptr @ @ postpone Literal postpone execute + postpone ; + colon-xt-ptr @ ! + + \ seedForth side of creating a new defining-word + \ compile "create dup h," instead of "Create" in body of new defining-word, + \ makes the corresponding token in seedForth heads table to keep us in sync + seed create + seed dup + seed h, +end-macro + +\ generate token sequences for strings + +: seed-stack-string ( c-addr u -- ) + dup >r + BEGIN dup WHILE ( c-addr u ) + over c@ seed-number 1 /string + REPEAT ( c-addr u ) + 2drop + r> seed-number +; + +: seed-string ( c-addr u -- ) + dup seed-number seed c, + BEGIN dup WHILE + >r dup char+ swap c@ seed-number seed c, + r> 1- + REPEAT 2drop +; + +Macro ," ( ccc" -- ) '"' parse seed-string end-macro + +: $, ( c-addr u -- ) + seed $lit + seed [ + seed-string + seed ] +; + +Macro $name ( -- ) + parse-name seed-stack-string +end-macro + +Macro $( \ ( ccc) -- ) + ')' parse seed-stack-string +end-macro + +Macro s" ( ccc" -- ) \ only in compile mode + '"' parse $, +end-macro + + +\ Control structure macros +: forward ( -- ) + seed [ + seed here + 0 seed-number seed , + seed ] +; + +: back ( -- ) + seed [ + seed , + seed ] +; + + +Macro AHEAD ( -- addr ) + seed branch forward +end-macro + +Macro IF ( -- addr ) + seed ?branch forward +end-macro + + +Macro THEN ( addr -- ) + seed [ + seed here + seed swap + seed ! + seed ] +end-macro + +Macro ELSE ( addr1 -- addr2 ) + seed branch forward + seed [ + seed swap + seed ] + seed THEN +end-macro + +Macro BEGIN ( -- addr ) + seed [ + seed here + seed ] +end-macro + +Macro AGAIN ( addr -- ) + seed branch back +end-macro + +Macro UNTIL ( addr -- ) + seed ?branch back +end-macro + +Macro WHILE ( addr1 -- addr2 addr1 ) + seed IF + seed [ + seed swap + seed ] +end-macro + +Macro REPEAT ( addr -- ) + seed AGAIN + seed THEN +end-macro + +Macro ( ( -- ) + postpone ( +end-macro + +Macro \ ( -- ) + postpone \ +end-macro + +\ Old style defining-words (special seedForth syntax) +\ Use "Definer" instead of ":" for definition that begins with "create", e.g. +\ Definer Variable create drop 0 , ; +\ Note: above style is deprecated, please use the Create macro instead, e.g. +\ : Variable Create drop 0 , ; + +\ A Definer-definition is similar to a :-definition, see for reference: +\ Macro : ( -- ) Token seed fun end-macro +\ However, "Token" is replaced by "Macro", so that we will get control again +\ when the user invokes the Definer-definition, e.g. if user calls "Variable". +\ The "Token" routine compiles an anonymous function referring to the current +\ #token and then increments #token, and we do the same thing here -- we have +\ "# tokens @" in the Macro-body and then "1 #tokens +!" before the "seed fun". +Macro Definer ( -- ) + Macro + \ take name of e.g. Variable being defined and create a token for it + postpone Token + \ compile a call to the user's Definer-definition (original token no) + #tokens @ postpone Literal + postpone emit-token + postpone end-macro + 1 #tokens +! + seed fun + \ user's code for Definer-body will begin with a call to "create", so + \ prefix their code with the sequence "here h," to give the token that + \ they create a header, and therefore keep the token numbering in sync + seed here + seed h, +end-macro + +\ for defining Macros later in seedForth +Macro Macro ( -- ) + Macro +end-macro + +Macro end-macro + postpone end-macro +end-macro + +Macro seed ( -- ) + postpone seed +end-macro + +Macro save-#tokens + postpone #tokens + postpone @ +end-macro + +Macro restore-#tokens + postpone #tokens + postpone ! +end-macro + +seed-file +\ user code has to be concatenated here +\ it cannot be in a separate file when running via gforth +\ it cannot have a partial last line when running via seedForth diff --git a/common/seedForth.pre b/common/seedForth.pre new file mode 100644 index 0000000..974f611 --- /dev/null +++ b/common/seedForth.pre @@ -0,0 +1,146 @@ +\ seedForth: less machine dependent portion + +: negate ( n1 -- n2 ) + 0 swap - ; + +: + ( x1 x2 -- x3 ) + negate - ; + +: 0= ( x -- flag ) + 0 swap ?exit drop -1 ; + +: ?dup ( x -- x x | 0 ) + dup 0= ?exit dup ; + +: +! ( x addr -- ) + swap >r dup @ r> + swap ! ; + +: hp ( -- addr ) + lit _hp ; + +: h@ ( i -- addr ) + cells lit head + @ ; + +: h! ( x i -- ) + cells lit head + ! ; + +: h, ( x -- ) + hp @ h! 1 hp +! ; + +: here ( -- addr ) + lit dp @ ; + +: allot ( n -- ) + lit dp +! ; + +: , ( x -- ) + here 1 cells allot ! ; + +: c, ( c -- ) + here 1 allot c! ; + +\ token are in the range 0 .. 1023: +\ 0, 4 .. 255 are single byte tokens +\ 256 .. 511 are double byte tokens of the form 01 xx +\ 511 .. 767 are double byte tokens of the form 02 xx +\ 768 .. 1023 are double byte tokens of the form 03 xx +: token ( -- x ) + key dup 0= ?exit \ 0 -> single byte token + dup 4 - 0< 0= ?exit \ not 1 2 3 -> single byte token + key couple ; \ double byte token + +: interpreter ( -- ) + token h@ execute tail interpreter ; \ executing exit will leave this loop + +: num ( -- x ) + tail interpreter ; + +: ?lit ( xt -- xt | ) + dup lit num - ?exit drop \ not num token: exit i.e. normal compile action + lit lit , num , \ generate lit x num call puts x on stack + r> drop tail compiler ; + +: eot ( -- ) + bye ; \ interpretive semantics: input exhausted, automatic bye + +: ?eot ( xt -- xt | ) + dup lit eot - ?exit drop \ not eot token: exit i.e. normal compile action + r> drop ; \ compilation semantics: return to interpretive state + +: fun ( -- ) + new h, compiler ; + +: couple ( hi lo -- hilo ) + >r 2* 2* 2* 2* 2* 2* 2* 2* r> + ; + +: $lit ( -- addr u ) + r> dup 1 + dup >r swap c@ dup r> + >r ; + +: unused ( -- u ) + lit memtop here - ; + +: cold ( -- ) + \ 's' emit 'e' dup emit emit 'd' emit 10 emit + lit bye h, \ 0 00 code + 0 h, \ 1 01 prefix + 0 h, \ 2 02 prefix + 0 h, \ 3 03 prefix + lit eot h, \ 4 04 code + lit dup h, \ 5 05 code + lit swap h, \ 6 06 code + lit drop h, \ 7 07 code + lit 0< h, \ 8 08 code + lit ?exit h, \ 9 09 code + lit >r h, \ 10 0A code + lit r> h, \ 11 0B code + lit - h, \ 12 0C code + lit exit h, \ 13 0D code + lit lit h, \ 14 0E code + lit @ h, \ 15 0F code + lit c@ h, \ 16 10 code + lit ! h, \ 17 11 code + lit c! h, \ 18 12 code + lit execute h, \ 19 13 code + lit branch h, \ 20 14 code + lit ?branch h, \ 21 15 code + lit negate h, \ 22 16 + lit + h, \ 23 17 + lit 0= h, \ 24 18 + lit ?dup h, \ 25 19 + lit cells h, \ 26 1A + lit +! h, \ 27 1B + lit h@ h, \ 28 1C + lit h, h, \ 29 1D + lit here h, \ 30 1E + lit allot h, \ 31 1F + lit , h, \ 32 20 + lit c, h, \ 33 21 + lit fun h, \ 34 22 + lit interpreter h, \ 35 23 + lit compiler h, \ 36 24 + lit create h, \ 37 25 + lit does> h, \ 38 26 + lit cold h, \ 39 27 + lit depth h, \ 40 28 code + lit dodoes h, \ 41 29 + lit new h, \ 42 2A + lit couple h, \ 43 2B + lit and h, \ 44 2C code + lit or h, \ 45 2D code + lit sp@ h, \ 46 2E code + lit sp! h, \ 47 2F code + lit rp@ h, \ 48 30 code + lit rp! h, \ 49 31 code + lit $lit h, \ 50 32 + lit num h, \ 51 33 + lit um* h, \ 52 34 code + lit um/mod h, \ 53 35 code + lit unused h, \ 54 36 + lit key? h, \ 55 37 + lit token h, \ 56 38 + lit usleep h, \ 57 39 code + lit hp h, \ 58 40 + lit key h, \ 59 41 code + lit emit h, \ 60 42 code + lit eemit h, \ 61 43 code + interpreter bye ; diff --git a/common/seedForth16bit.pre b/common/seedForth16bit.pre new file mode 100644 index 0000000..22be495 --- /dev/null +++ b/common/seedForth16bit.pre @@ -0,0 +1,7 @@ +\ seedForth: less machine dependent portion + +: 2* ( x1 -- x2 ) + dup + ; + +: cells ( x1 -- x2 ) + 2* ; diff --git a/common/seedForth32bit.pre b/common/seedForth32bit.pre new file mode 100644 index 0000000..d67da51 --- /dev/null +++ b/common/seedForth32bit.pre @@ -0,0 +1,7 @@ +\ seedForth: less machine dependent portion + +: 2* ( x1 -- x2 ) + dup + ; + +: cells ( x1 -- x2 ) + 2* 2* ; diff --git a/common/seedForthBoot.seedsource b/common/seedForthBoot.seedsource new file mode 100644 index 0000000..145815c --- /dev/null +++ b/common/seedForthBoot.seedsource @@ -0,0 +1,3 @@ +\ seedForth interactive system +\ this file boots for running textual forth program (no banner, prompt, echo) +boot diff --git a/preForth/seedForthDemo.seedsource b/common/seedForthDemo.seedsource similarity index 84% rename from preForth/seedForthDemo.seedsource rename to common/seedForthDemo.seedsource index 4e2ddad..98a8dfa 100644 --- a/preForth/seedForthDemo.seedsource +++ b/common/seedForthDemo.seedsource @@ -9,10 +9,38 @@ \ cat seedForthDemo.seed | ./seedForth \ -PROGRAM seedForthDemo.seed +\ multiply debug: emits '0000' +\ 0x1234 * 0x5678 = 0x06260060 +\ 4660 22136 um* 1574 - 0= '1' + emit 96 - 0= '1' + emit +\ 0xffff * 0xffff = 0xfffe0001 +\ 65535 65535 um* 65534 - 0= '1' + emit 1 - 0= '1' + emit + +\ divide debug: emits '000000' +\ 0x06260060 / 0x5678 = 0x1234 rem 0x0000 +\ 96 1574 22136 um/mod 4660 - 0= '1' + emit 0= '1' + emit +\ 0x06260060 / 0x1234 = 0x5678 rem 0x0000 +\ 96 1574 4660 um/mod 22136 - 0= '1' + emit 0= '1' + emit +\ 0xfffe0001 / 0xffff = 0xffff rem 0x0000 +\ 1 65534 65535 um/mod 65535 - 0= '1' + emit 0= '1' + emit + +\ divide debug: emits '000000' +\ 0x062656d7 / 0x5678 = 0x1234 rem 0x5677 +\ 22231 1574 22136 um/mod 4660 - 0= '1' + emit 22135 - 0= '1' + emit +\ 0x06261293 / 0x1234 = 0x5678 rem 0x1233 +\ 4755 1574 4660 um/mod 22136 - 0= '1' + emit 4659 - 0= '1' + emit +\ 0xfffeffff / 0xffff = 0xffff rem 0xfffe +\ 65535 65534 65535 um/mod 65535 - 0= '1' + emit 65534 - 0= '1' + emit + +\ stack debug: emits '00000' +\ depth 0= '1' + emit +\ 1234 sp@ @ 1234 - 0= '1' + emit sp@ 2345 swap ! 2345 - 0= '1' + emit +\ 3456 >r rp@ @ 3456 - 0= '1' + emit rp@ 4567 swap ! r> 4567 - 0= '1' + emit Definer Variable create ( x ) drop 0 , ; +\ Variable debug: emits '0' +\ Variable dummy dummy 2 + here - 0= '1' + emit + \ Missing primitives : over ( x1 x2 -- x1 x2 x1 ) >r dup r> swap ; : /string ( x1 x2 x3 -- x4 x5 ) swap over - >r + r> ; @@ -168,23 +196,13 @@ t{ person -> 3 cells }t \ size of structure \ Deferred words -: ' ( -- x ) key ; +: ' ( -- x ) token h@ ; : uninitialized ( -- ) cr s" uninitialized execution vector" type ; ' uninitialized Constant 'uninitialized Definer Defer ( -- ) create >r 'uninitialized , r> does> @ execute ; -: >body ( xt -- body ) h@ 1 cells + ; - -: is ( xt -- ) ' >body ! ; - -Defer d1 -' ten is d1 -t{ d1 d1 d1 -> ten ten ten }t -' five is d1 -t{ d1 d1 d1 -> five five five }t - t{ 3 4 + -> 7 }t @@ -291,7 +309,7 @@ Variable #tib over dup c@ upc swap c! 1 /string REPEAT ( c-addr u ) 2drop ; -: hi ( -- ) key drop \ discard END / bye token +: hi ( -- ) \ key drop \ discard END / bye token BEGIN cr s" > " type query cr .s @@ -393,14 +411,7 @@ Definer Array ( n -- ) t{ 60 5 a ! 0 a @ 1 a @ 2 a @ 3 a @ 4 a @ 5 a @ -> 10 20 30 40 50 60 }t -: done ( -- ) cr ." done" cr ; done - -\ How to compile bye that normally exits the compile and interpret loop -\ : goodbye lit [ key bye , ] execute ; - -\ cr 'd' emit 'o' emit 'n' emit 'e' emit cr - -\ hi -END - +: done ( -- ) cr ." done" cr ; +\ seedForthDemoXXX.seedsource in XXX directory does some more tests then: +\ done bye diff --git a/common/seedForthInteractive.seedsource b/common/seedForthInteractive.seedsource new file mode 100644 index 0000000..dc983a8 --- /dev/null +++ b/common/seedForthInteractive.seedsource @@ -0,0 +1,134 @@ +\ seedForth interactive system +\ this file boots for interactive use (with banner, prompt, echo) + +\ catch and throw tests: see later when ' is defined + +\ save and empty tests +save + +: three 3 ; + +empty + +begin-tests +\ Test basics +t{ 10 '*' + -> 52 }t +t{ 0 0< -> 0 }t +t{ 1 0< -> 0 }t +t{ 2 0< -> 0 }t + +t{ 1 negate 0< -> -1 }t +t{ 2 negate 0< -> -1 }t +t{ 0 negate -> 0 }t +t{ -1 negate 0< -> 0 }t +t{ -2 negate 0< -> 0 }t + + +t{ 10 20 30 third -> 10 20 30 10 }t + +t{ 1 2 3 rot -> 2 3 1 }t +t{ 1 2 3 -rot -> 3 1 2 }t + + +t{ 3 4 max -> 4 }t +t{ 3 4 min -> 3 }t +t{ -1 4 max -> 4 }t +t{ -1 4 min -> -1 }t + +t{ 1 2 2drop -> }t +t{ 1 2 2dup -> 1 2 1 2 }t + +t{ 1 2 3 4 2swap -> 3 4 1 2 }t +t{ 1 2 3 4 2over -> 1 2 3 4 1 2 }t + +t{ 10 abs -> 10 }t +t{ -10 abs -> 10 }t + +t{ 15 10 xor -> 5 }t +t{ 21845 dup xor -> 0 }t \ $5555 +t{ 21845 dup 2* xor -> 65535 }t + +t{ minint maxint < -> -1 }t \ 32bit $80000000 $7FFFFFFF +t{ minint 0 < -> -1 }t \ 32bit $80000000 0 +t{ 0 minint < -> 0 }t \ 32bit 0 $80000000 + +\ both positive +t{ 10 10 < -> 0 }t +t{ 10 1000 < -> -1 }t +t{ 1000 10 < -> 0 }t + +\ both negative +t{ -10 -10 < -> 0 }t +t{ -10 -1000 < -> 0 }t +t{ -1000 -10 < -> -1 }t + +\ left negative +t{ -10 10 < -> -1 }t +t{ -10 1000 < -> -1 }t +t{ -1000 10 < -> -1 }t + +\ right negative +t{ 10 -10 < -> 0 }t +t{ 10 -1000 < -> 0 }t +t{ 1000 -10 < -> 0 }t + +end-tests + +\ minint and maxint tests +begin-tests + +t{ minint negate -> minint }t +t{ minint maxint < -> -1 }t +t{ maxint minint < -> 0 }t + +t{ 0 1 u< -> -1 }t +t{ 1 0 u< -> 0 }t +t{ -1 0 u< -> 0 }t +t{ 0 -1 u< -> -1 }t + +end-tests + +\ catch and throw tests +begin-tests + +t{ 10 ' dup catch -> 10 10 0 }t + +: err99 ( x -- ) dup 9 = IF 99 throw THEN 1 + ; + +t{ 1 ' err99 catch -> 2 0 }t +t{ 5 9 ' err99 catch nip -> 5 99 }t + +end-tests + +\ interactive part +2 Constant major ( -- x ) +2 Constant minor ( -- x ) +0 Constant patch ( -- x ) + +: .version ( -- ) + major .digit '.' emit + minor .digit '.' emit + patch .digit ; + +: .banner ( -- ) + cr ." seedForth/interactive " .version + cr ." ---------------------------" + cr unused . ." bytes free" cr ; + +\ ---- try colored words with embedded ESC sequences +Create colored-header here 0 c, here + 27 c, '[' c, '3' c, '1' c, 'm' c, 'r' c, 'e' c, 'd' c, 'w' c, 'o' c, 'r' c, 'd' c, + 27 c, '[' c, '3' c, '9' c, ';' c, '4' c, '9' c, 'm' c, +here swap - swap c! + + colored-header count "header dup link-header +\ -------- + + +cr +t{ -> }t + + +reveal +.banner +boot diff --git a/preForth/seedForthInteractive.seedsource b/common/seedForthRuntime.seedsource similarity index 76% rename from preForth/seedForthInteractive.seedsource rename to common/seedForthRuntime.seedsource index 089137f..5504a0b 100644 --- a/preForth/seedForthInteractive.seedsource +++ b/common/seedForthRuntime.seedsource @@ -1,20 +1,11 @@ \ seedForth interactive system -\ -\ tokenize with -\ -\ gforth seedForth-tokinzer.fs seedForthInteractive.seedsource -\ -\ then pipe into seedForth: -\ -\ cat seedForthInteractive.seed | ./seedForth -\ - -PROGRAM seedForthInteractive.seed +\ this file has routines needed to interpret or compile textual forth source \ Defining words Definer Create ( -- ) create ( x ) drop ; Definer Variable ( -- ) create ( x ) drop 0 , ; -Definer Constant ( x -- ) create ( x ) >r , r> does> @ ; +\ the following is defined in seedForthRuntimeXXbit.seedsource +\ Definer Constant ( x -- ) create ( x ) >r , r> does> @ ; Macro Literal seed lit @@ -174,8 +165,6 @@ Variable frame ( -- addr ) ?dup 0= ?exit frame @ rp! r> swap >r sp! drop r> r> frame ! ; -\ tests: see later when ' is defined - \ save and empty Create savearea 0 , 0 , \ { hp | dp } @@ -196,13 +185,6 @@ Macro empty ( -- ) restore-#tokens end-macro -save - -: three 3 ; - -empty - - \ Tester : empty-stack ( i*x -- ) BEGIN depth 0< WHILE 0 REPEAT @@ -235,89 +217,6 @@ Macro end-tests seed empty end-macro - -begin-tests -\ Test basics -t{ 10 '*' + -> 52 }t -t{ 0 0< -> 0 }t -t{ 1 0< -> 0 }t -t{ 2 0< -> 0 }t - -t{ 1 negate 0< -> -1 }t -t{ 2 negate 0< -> -1 }t -t{ 0 negate -> 0 }t -t{ -1 negate 0< -> 0 }t -t{ -2 negate 0< -> 0 }t - - -t{ 10 20 30 third -> 10 20 30 10 }t - -t{ 1 2 3 rot -> 2 3 1 }t -t{ 1 2 3 -rot -> 3 1 2 }t - - -t{ 3 4 max -> 4 }t -t{ 3 4 min -> 3 }t -t{ -1 4 max -> 4 }t -t{ -1 4 min -> -1 }t - -t{ 1 2 2drop -> }t -t{ 1 2 2dup -> 1 2 1 2 }t - -t{ 1 2 3 4 2swap -> 3 4 1 2 }t -t{ 1 2 3 4 2over -> 1 2 3 4 1 2 }t - -t{ 10 abs -> 10 }t -t{ -10 abs -> 10 }t - -t{ 15 10 xor -> 5 }t -t{ 21845 dup xor -> 0 }t \ $5555 -t{ 21845 dup 2* xor -> 65535 }t - -t{ -2147483648 2147483647 < -> -1 }t \ 32bit $80000000 $7FFFFFFF -t{ -2147483648 0 < -> -1 }t \ 32bit $80000000 0 -t{ 0 -2147483648 < -> 0 }t \ 32bit 0 $80000000 - -\ both positive -t{ 10 10 < -> 0 }t -t{ 10 1000 < -> -1 }t -t{ 1000 10 < -> 0 }t - -\ both negative -t{ -10 -10 < -> 0 }t -t{ -10 -1000 < -> 0 }t -t{ -1000 -10 < -> -1 }t - -\ left negative -t{ -10 10 < -> -1 }t -t{ -10 1000 < -> -1 }t -t{ -1000 10 < -> -1 }t - -\ right negative -t{ 10 -10 < -> 0 }t -t{ 10 -1000 < -> 0 }t -t{ 1000 -10 < -> 0 }t - -end-tests - -: minint ( -- n ) - 1 BEGIN dup 2* dup WHILE nip REPEAT drop ; - -minint 1- Constant maxint - -begin-tests - -t{ minint negate -> minint }t -t{ minint maxint < -> -1 }t -t{ maxint minint < -> 0 }t - -t{ 0 1 u< -> -1 }t -t{ 1 0 u< -> 0 }t -t{ -1 0 u< -> 0 }t -t{ 0 -1 u< -> -1 }t - -end-tests - : skip ( c-addr1 u1 c -- c-addr2 u2 ) BEGIN over @@ -353,7 +252,7 @@ end-tests \ Deferred words -: ' ( -- x ) token ; +: ' ( -- x ) token h@ ; : uninitialized ( -- ) cr s" uninitialized execution vector" type -1 throw ; @@ -361,24 +260,6 @@ end-tests Definer Defer ( -- ) create >r [ ' uninitialized ] Literal , r> does> @ execute ; -: >body ( xt -- body ) - h@ 1 cells + ; - -: is ( xt -- ) \ only interactive - ' >body ! ; - -\ catch and throw tests -begin-tests - -t{ 10 ' dup catch -> 10 10 0 }t - -: err99 ( x -- ) dup 9 = IF 99 throw THEN 1 + ; - -t{ 1 ' err99 catch -> 2 0 }t -t{ 5 9 ' err99 catch nip -> 5 99 }t - -end-tests - \ String comparison : compare ( c-addr1 u1 c-addr2 u2 -- n ) rot @@ -488,24 +369,37 @@ init \ for the input stream a struct could be defined that generally handles terminal input, evaluate, file input and others. -Create tib 80 allot +Create tib 255 allot Create 'source here 0 , tib , \ ' source is normally ^tib #tib is set to c-addr u for evaluate Constant #tib -Defer getkey ' key is getkey +Defer getkey \ ' key is getkey (done later) -Variable input-echo -1 input-echo ! +\ after loading the *.seed runtime we will need to load further runtime as +\ textual forth source before being useable, so we will default to echo off +Variable input-echo 0 input-echo ! : accept ( c-addr u1 -- u2 ) >r 0 BEGIN ( c-addr u2 ) ( R: u1 ) getkey dup 10 = over 13 = or 0= WHILE ( c-addr u2 key ) - dup 8 = over 127 = or - IF drop dup 0 > - IF 1- 8 emit bl emit 8 emit ELSE 7 emit THEN ELSE - input-echo @ IF dup emit THEN >r 2dup + r> swap c! 1+ r@ min THEN + dup 8 = over 127 = or IF + drop dup 0 > IF + 1- 8 emit bl emit 8 emit + ELSE + 7 emit + THEN + ELSE + \ only char write to buffer and echo if there is room + over r@ < IF + >r 2dup + r@ swap c! 1+ r> + input-echo @ IF dup emit THEN + THEN + \ if char was EOT, quit after maybe writing to buffer + 4 = IF r> drop nip exit THEN + THEN REPEAT ( c-addr u2 key r:u1 ) drop r> drop nip \ input-echo @ IF cr THEN @@ -513,7 +407,7 @@ Variable input-echo -1 input-echo ! ; : query ( -- ) - tib 80 accept #tib ! ; + tib 255 accept #tib ! ; @@ -654,58 +548,56 @@ Macro has-header ( -- ) end-macro -' bye has-header bye \ 0 00 -' emit has-header emit \ 1 01 -' key has-header key \ 2 02 -' dup has-header dup \ 3 03 -' swap has-header swap \ 4 04 -' drop has-header drop \ 5 05 -' 0< has-header 0< \ 6 06 -' ?exit has-header ?exit \ 7 07 -' >r has-header >r \ 8 08 -' r> has-header r> \ 9 09 -' - has-header - \ 10 0A -' exit has-header exit \ 11 0B -' lit has-header lit \ 12 0C -' @ has-header @ \ 13 0D -' c@ has-header c@ \ 14 0E -' ! has-header ! \ 15 0F -' c! has-header c! \ 16 10 -' execute has-header execute \ 17 11 -' branch has-header branch \ 18 12 -' ?branch has-header ?branch \ 19 13 -' negate has-header negate \ 20 14 -' + has-header + \ 21 15 -' 0= has-header 0= \ 22 16 -' ?dup has-header ?dup \ 23 17 -' cells has-header cells \ 24 18 -' +! has-header +! \ 25 19 -' h@ has-header h@ \ 26 1A -' h, has-header h, \ 27 1B -' here has-header here \ 28 1C -' allot has-header allot \ 29 1D -' , has-header , \ 30 1E -' c, has-header c, \ 31 1F -' fun has-header fun \ 32 20 -' interpreter has-header interpreter \ 33 21 -' compiler has-header compiler \ 34 22 -' create has-header create \ 35 23 -' does> has-header does> \ 36 24 -' cold has-header cold \ 37 25 -' depth has-header depth \ 38 26 -' compile, has-header compile, \ 39 26 -' new has-header new \ 40 28 -' couple has-header couple \ 41 29 -' and has-header and \ 42 2A -' or has-header or \ 43 2B -' catch has-header catch \ 44 2C -' throw has-header throw \ 45 2D -' sp@ has-header sp@ \ 46 2E -' sp! has-header sp! \ 47 2F -' rp@ has-header rp@ \ 48 30 -' rp! has-header rp! \ 49 31 -' $lit has-header $lit \ 50 32 -' num has-header num \ 51 33 +' bye has-header bye +' dup has-header dup +' swap has-header swap +' drop has-header drop +' 0< has-header 0< +' ?exit has-header ?exit +' >r has-header >r +' r> has-header r> +' - has-header - +' exit has-header exit +' lit has-header lit +' @ has-header @ +' c@ has-header c@ +' ! has-header ! +' c! has-header c! +' execute has-header execute +' branch has-header branch +' ?branch has-header ?branch +' negate has-header negate +' + has-header + +' 0= has-header 0= +' ?dup has-header ?dup +' cells has-header cells +' +! has-header +! +' h@ has-header h@ +' h, has-header h, +' here has-header here +' allot has-header allot +' , has-header , +' c, has-header c, +' fun has-header fun +' interpreter has-header interpreter +' compiler has-header compiler +' create has-header create +' does> has-header does> +' cold has-header cold +' depth has-header depth +' , has-header , +' new has-header new +' couple has-header couple +' and has-header and +' or has-header or +' catch has-header catch +' throw has-header throw +' sp@ has-header sp@ +' sp! has-header sp! +' rp@ has-header rp@ +' rp! has-header rp! +' $lit has-header $lit +' num has-header num ' um* has-header um* ' um/mod has-header um/mod ' unused has-header unused @@ -713,6 +605,9 @@ end-macro \ ' token has-header token ' usleep has-header usleep ' hp has-header hp +' key has-header key +' emit has-header emit +' eemit has-header eemit ' over has-header over ' rot has-header rot @@ -775,7 +670,6 @@ end-macro ' reveal has-header reveal ' hide has-header hide ' pad has-header pad -' >body has-header >body ' allocate has-header allocate ' free has-header free @@ -855,29 +749,24 @@ Variable heads -1 heads ! : last-xt ( -- xt ) last @ _xt @ ; -: (Does>) ( -- ) - [ ' last-xt ] Literal compile, - [ ' does> ] Literal compile, ; - -' (Does>) has-header Does> immediate ' last has-header last ' _xt has-header _xt ' _name has-header _name : (Literal) ( x -- ) - lit [ ' lit , ] compile, , ; + lit [ ' lit , ] , , ; ' (Literal) has-header Literal immediate : (s") ( ccc" -- ) - [ ' $lit ] Literal compile, + [ ' $lit ] Literal , '"' parse here over 1+ allot place ; ' (s") has-header s" immediate : (.") ( ccc" -- ) (s") - [ ' type ] Literal compile, ; + [ ' type ] Literal , ; ' (.") has-header ." immediate @@ -933,7 +822,7 @@ Variable heads -1 heads ! BEGIN dup WHILE ( xt wid ) - 2dup _xt @ h@ = IF nip exit THEN + 2dup _xt @ = IF nip exit THEN _link @ REPEAT ( xt wid ) 2drop 0 ; @@ -953,9 +842,9 @@ Variable heads -1 heads ! : (postpone) ( -- ) parse-name find-name dup 0= -13 and throw dup immediate? IF - _xt @ compile, + _xt @ , ELSE - [ ' lit ] Literal compile, _xt @ , [ ' compile, ] Literal compile, + [ ' lit ] Literal , _xt @ , [ ' , ] Literal , THEN ; @@ -968,7 +857,7 @@ Variable heads -1 heads ! ' tick has-header ' : ([']) ( -- xt ) - tick [ ' lit ] Literal compile, , ; + tick [ ' lit ] Literal , , ; ' ([']) has-header ['] immediate @@ -992,7 +881,7 @@ Variable heads -1 heads ! : ,# ( c-addr u -- 0 0 | c-addr u ) dup 0= ?exit ?# dup ?exit - lit [ ' lit , ] compile, rot , ; + lit [ ' lit , ] , rot , ; : ?'x' ( c-addr u -- x 0 0 | c-addr u ) dup 0= ?exit @@ -1004,7 +893,7 @@ Variable heads -1 heads ! : ,'x' ( c-addr u -- 0 0 | c-addr u ) dup 0= ?exit ?'x' dup ?exit - lit [ ' lit , ] compile, rot , ; + lit [ ' lit , ] , rot , ; : ?word ( c-addr1 u1 | i*x c-addr2 u2 ) dup 0= ?exit @@ -1024,7 +913,7 @@ Variable heads -1 heads ! dup 0= ?exit 2dup find-name ?dup IF - nip nip dup immediate? IF _xt @ execute ELSE _xt @ compile, THEN 0 0 + nip nip dup immediate? IF _xt @ execute ELSE _xt @ , THEN 0 0 THEN ; @@ -1053,7 +942,7 @@ Variable handlers interpreters @ handlers ! Header new swap _xt ! hide (]) ; : (;) ( -- ) - lit [ ' exit , ] compile, reveal ([) ; + lit [ ' exit , ] , reveal ([) ; ' (]) has-header ] ' ([) has-header [ immediate @@ -1083,13 +972,21 @@ Variable handlers interpreters @ handlers ! ' evaluate has-header evaluate : refill ( -- f ) - 'source cell+ @ tib = IF query 0 >in ! -1 exit THEN + 'source cell+ @ tib = IF + query 0 >in ! + + \ Nick: detect EOT (only works at the start of a line) + #tib @ IF tib c@ 4 = IF 0 #tib ! 0 exit THEN THEN + + -1 exit + THEN 0 ; ' refill has-header refill - -Variable echo -1 echo ! +\ after loading the *.seed runtime we will need to load further runtime as +\ textual forth source before being useable, so we will default to echo off +Variable echo 0 echo ! ' echo has-header echo ' input-echo has-header input-echo @@ -1118,12 +1015,13 @@ Variable echo -1 echo ! ' compiling? has-header compiling? -Defer .status : noop ; ' noop is .status +Defer .status : noop ; \ ' noop is .status (done later) ' noop has-header noop ' .status has-header .status +\ interactive part : prompt ( -- ) echo @ IF cr cyan bold .s normal reset-colors compiling? IF ']' ELSE '>' THEN emit space @@ -1139,28 +1037,19 @@ Defer .status : noop ; ' noop is .status tib 0 'source 2! ([) BEGIN - .status prompt query 0 >in ! interpret ?stack .ok + .status prompt + query 0 >in ! + + \ Nick: detect EOT (only works at the start of a line) + #tib @ IF tib c@ 4 = IF bye THEN THEN + + interpret ?stack .ok 0 UNTIL ; : warm ( -- ) - \ [ ' [ compile, ] + \ [ ' [ , ] empty-stack restart ; - -2 Constant major ( -- x ) -2 Constant minor ( -- x ) -0 Constant patch ( -- x ) - -: .version ( -- ) - major .digit '.' emit - minor .digit '.' emit - patch .digit ; - -: .banner ( -- ) - cr ." seedForth/interactive " .version - cr ." ---------------------------" - cr unused . ." bytes free" cr ; - Create errormsg 0 , 0 , ' errormsg has-header errormsg @@ -1178,32 +1067,13 @@ Create errormsg 0 , 0 , : .error ( n -- ) red bold .error# normal reset-colors ." 🤔 " ; - : boot ( -- ) - key drop \ skip 0 of boot program - .banner BEGIN [ ' warm ] Literal catch ?dup IF .error cr THEN AGAIN ; ' boot has-header boot -\ ---- try colored words with embedded ESC sequences -Create colored-header here 0 c, here - 27 c, '[' c, '3' c, '1' c, 'm' c, 'r' c, 'e' c, 'd' c, 'w' c, 'o' c, 'r' c, 'd' c, - 27 c, '[' c, '3' c, '9' c, ';' c, '4' c, '9' c, 'm' c, -here swap - swap c! - - colored-header count "header dup link-header -\ -------- - - -cr -t{ -> }t - - -0 echo ! -\ 0 input-echo ! -reveal -boot -END +\ at this point append either: +\ seedForthInteractive.seedsource (boot system for interactive use) +\ seedForthBoot.seedsource (boot system for running textual forth program) diff --git a/common/seedForthRuntime16bit.seedsource b/common/seedForthRuntime16bit.seedsource new file mode 100644 index 0000000..4eb6fd7 --- /dev/null +++ b/common/seedForthRuntime16bit.seedsource @@ -0,0 +1,4 @@ +Definer Constant ( x -- ) create ( x ) >r , r> does> @ ; + +-32768 Constant minint +32767 Constant maxint diff --git a/common/seedForthRuntime32bit.seedsource b/common/seedForthRuntime32bit.seedsource new file mode 100644 index 0000000..dbdfbbe --- /dev/null +++ b/common/seedForthRuntime32bit.seedsource @@ -0,0 +1,4 @@ +Definer Constant ( x -- ) create ( x ) >r , r> does> @ ; + +-2147483648 Constant minint +2147483647 Constant maxint diff --git a/emu_65c02/Makefile b/emu_65c02/Makefile new file mode 100644 index 0000000..a807161 --- /dev/null +++ b/emu_65c02/Makefile @@ -0,0 +1,30 @@ +CFLAGS=-g -Wall -O3 -DVR_6502_EMU_STATIC=1 +LDFLAGS=-g + +AS6500=../asxv5pxx/asxmak/linux/exe/as6500 +ASLINK=../asxv5pxx/asxmak/linux/exe/aslink + +# need to install intelhex package in Python first: +# pip3 install --user intelhex +HEX2BIN=python3 $(HOME)/.local/bin/hex2bin.py + +LOAD_ADDR=0x300 + +.PHONY: all +all: emu_65c02 test.bin + +emu_65c02: emu_65c02.o vrEmu6502/src/vrEmu6502.o + $(CC) $(LDFLAGS) -o $@ $^ + +test.bin: test.ihx + $(HEX2BIN) $< $@ + +test.ihx: test.rel + $(ASLINK) -n -m -u -i -b text=$(LOAD_ADDR) $@ $^ + +test.rel: test.asm + $(AS6500) -l -o $< + +.PHONY: clean +clean: + rm -f *.bin *.hlr *.ihx *.lst *.map *.o *.rel *.rst emu_65c02 z80/*.o diff --git a/emu_65c02/annotate_trace.py b/emu_65c02/annotate_trace.py new file mode 100755 index 0000000..7ebba61 --- /dev/null +++ b/emu_65c02/annotate_trace.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +import bisect +import sys + +EXIT_SUCCESS = 0 +EXIT_FAILURE = 1 + +if len(sys.argv) < 2: + print(f'usage: {sys.argv[0]:s} program.rst annotated_trace.txt') + sys.exit(EXIT_FAILURE) + +symbol_table = [] +with open(sys.argv[1]) as fin: + i = 0 # fallback sort key (use symbol defined later in file) + for line in fin: + fields = line[32:].split() + if ( + len(fields) and + fields[0][-1:] == ':' and + fields[0][:1] != ';' and + fields[0][-2:] != '$:' + ): + addr = int(line[3:7], 16) + symbol = fields[0][:-1] + symbol_table.append((addr, i, symbol)) + i += 1 +symbol_table.sort() +symbol_table = ( + [addr for addr, _, _ in symbol_table], + [symbol for _, _, symbol in symbol_table] +) + +for line in sys.stdin: + fields = line.split('=') + for i in range(1, len(fields)): + addr = int(fields[i][:4], 16) + j = bisect.bisect_right(symbol_table[0], addr) + if j: + j -= 1 + offset = addr - symbol_table[0][j] + symbol = symbol_table[1][j] + fields[i] = fields[i][:4] + f'({symbol:s}+{offset:04x})' + fields[i][4:] + sys.stdout.write('='.join(fields)) diff --git a/emu_65c02/emu_65c02.c b/emu_65c02/emu_65c02.c new file mode 100644 index 0000000..0b9add8 --- /dev/null +++ b/emu_65c02/emu_65c02.c @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include +#include +#include +#include "vrEmu6502/src/vrEmu6502.h" + +#define IO_PAGE 0x200 +#define STDIN_DATA 0x200 +#define STDOUT_DATA 0x201 +#define STDERR_DATA 0x202 +#define STDIN_STATUS 0x203 +#define STDOUT_STATUS 0x204 +#define STDERR_STATUS 0x205 +#define USLEEP_LO 0x206 +#define USLEEP_HI 0x207 +#define SYS_EXIT 0x208 + +#define LOAD_ADDRESS 0x300 +#define RESET_VECTOR 0xfffc + +#define TRACE 0 + +VrEmu6502 *cpu; + +int stdin_fd; +int g_argn = 0; +int g_argc = 1; +const char *default_argv = "-"; +const char **g_argv = &default_argv; + +#define MEMORY_SIZE 0x10000 +uint8_t memory[MEMORY_SIZE]; +uint8_t usleep_lo; +int exit_flag; + +// call with g_argn < g_argc +void open_stdin(void) { + if (strcmp(g_argv[g_argn], "-") == 0) + stdin_fd = STDIN_FILENO; + else { + stdin_fd = open(g_argv[g_argn], O_RDONLY); + if (stdin_fd == -1) { + perror(g_argv[g_argn]); + exit(EXIT_FAILURE); + } + } +} + +void close_stdin(void) { + if (stdin_fd != STDIN_FILENO) + close(stdin_fd); +} + +uint8_t mem_read(uint16_t addr, bool isDbg) { + if ((addr & 0xff00) != IO_PAGE) + return memory[addr]; + + switch (addr) { + case STDIN_DATA: + { + uint8_t data = 4; // EOT + if (g_argn < g_argc) + while (true) { + ssize_t count = read(stdin_fd, &data, 1); + if (count == -1) { + perror("read()"); + exit(EXIT_FAILURE); + } + if (count) + break; + close_stdin(); + ++g_argn; + if (g_argn >= g_argc) + break; + open_stdin(); + } + return data; + } + case STDIN_STATUS: + { + if (g_argn >= g_argc) + return 1; // if no more input, force application to read EOT + struct pollfd fd = {stdin_fd, POLLIN, 0}; + if (poll(&fd, 1, 0) == -1) { + perror("poll()"); + exit(EXIT_FAILURE); + } + return (fd.revents & POLLIN) != 0; + } + case STDOUT_STATUS: + { + struct pollfd fd = {STDOUT_FILENO, POLLOUT, 0}; + if (poll(&fd, 1, 0) == -1) { + perror("poll()"); + exit(EXIT_FAILURE); + } + return (fd.revents & POLLOUT) != 0; + } + case STDERR_STATUS: + { + struct pollfd fd = {STDERR_FILENO, POLLOUT, 0}; + if (poll(&fd, 1, 0) == -1) { + perror("poll()"); + exit(EXIT_FAILURE); + } + return (fd.revents & POLLOUT) != 0; + } + case USLEEP_LO: + return usleep_lo; + } + return 0xff; +} + +void mem_write(uint16_t addr, uint8_t val) { + if ((addr & 0xff00) != IO_PAGE) { + memory[addr] = val; + return; + } + + switch (addr) { + case STDOUT_DATA: + if (write(STDOUT_FILENO, &val, 1) == -1) { + perror("write()"); + exit(EXIT_FAILURE); + } + break; + case STDERR_DATA: + if (write(STDERR_FILENO, &val, 1) == -1) { + perror("write()"); + exit(EXIT_FAILURE); + } + break; + case USLEEP_LO: + usleep_lo = val; + break; + case USLEEP_HI: + usleep(usleep_lo | (val << 8)); + break; + case SYS_EXIT: + exit_flag = val | 0x100; + vrEmu6502Jam(cpu); + break; + } +} + +int main(int argc, char **argv) { + int argn = 1; + bool timing = false; + if (argn < argc && strcmp(argv[argn], "-t") == 0) { + timing = true; + ++argn; + } + + if (argn >= argc) { + printf("usage: %s [-t] program.bin\n", argv[0]); + exit(EXIT_FAILURE); + } + + int fd = open(argv[argn], O_RDONLY); + if (fd == -1) { + perror(argv[argn]); + exit(EXIT_FAILURE); + } + if (read(fd, memory + LOAD_ADDRESS, MEMORY_SIZE - LOAD_ADDRESS) == -1) { + perror("read()"); + exit(EXIT_FAILURE); + } + close(fd); + + // implement "cat" functionality for stdin + // if not enough arguments, supply default argument of "-" + ++argn; + if (argn < argc) { + g_argn = argn; + g_argc = argc; + g_argv = (const char **)argv; + } + open_stdin(); + + // do this before creating the CPU + memory[RESET_VECTOR] = (uint8_t)(LOAD_ADDRESS & 0xff); + memory[RESET_VECTOR + 1] = (uint8_t)(LOAD_ADDRESS >> 8); + + cpu = vrEmu6502New(CPU_65C02, mem_read, mem_write); + if (cpu == NULL) { + perror("malloc()"); + exit(EXIT_FAILURE); + } + + long nb_instructions = 0, nb_cycles = 0; + int i, j; +#if TRACE + do { + int pc = vrEmu6502GetPC(cpu); + int ip = memory[0] | (memory[1] << 8); + int dsp = memory[2] | (memory[3] << 8); + int rsp = memory[4] | (memory[5] << 8); + fprintf( + stderr, + "pc=%04x:%02x,%02x,%02x ip=%04x:%04x dsp=%04x:%04x rsp=%04x:%04x\n", + pc, + memory[pc], + memory[(pc + 1) & 0xffff], + memory[(pc + 2) & 0xffff], + ip, + memory[ip] | (memory[(ip + 1) & 0xffff] << 8), + dsp, + memory[dsp] | (memory[(dsp + 1) & 0xffff] << 8), + rsp, + memory[rsp] | (memory[(rsp + 1) & 0xffff] << 8) + ); + i = vrEmu6502Run(cpu, 1, &j); + nb_instructions += i; + nb_cycles += j; + } while (i); +#else + do { + i = vrEmu6502Run(cpu, 1000, &j); + nb_instructions += i; + nb_cycles += j; + } while (i >= 1000); +#endif + + vrEmu6502Destroy(cpu); + + if (timing) + fprintf( + stderr, + "%lu instructions executed on %lu cycles\n", + nb_instructions, + nb_cycles + ); + exit(exit_flag & 0xff); +} diff --git a/emu_65c02/test.asm b/emu_65c02/test.asm new file mode 100644 index 0000000..d345960 --- /dev/null +++ b/emu_65c02/test.asm @@ -0,0 +1,66 @@ +STDIN_DATA = 0x200 +STDOUT_DATA = 0x201 +STDERR_DATA = 0x202 +STDIN_STATUS = 0x203 +STDOUT_STATUS = 0x204 +STDERR_STATUS = 0x205 +USLEEP_LO = 0x206 +USLEEP_HI = 0x207 +SYS_EXIT = 0x208 + + .r65c02 + + .area text + + cld + + ldx #0 + ldy #message_end - message +print_message: + lda message,x + sta STDERR_DATA + inx + dey + bne print_message + +in_wait: + lda STDIN_STATUS + bne in_char + + lda #<1000 + sta USLEEP_LO + lda #>1000 + sta USLEEP_HI + bra in_wait + +in_char: + lda STDIN_DATA + clc + cmp #4 ; EOT + beq done + + tax + +out_wait: + lda STDOUT_STATUS + bne out_char + + lda #<1000 + sta USLEEP_LO + lda #>1000 + sta USLEEP_HI + bra out_wait + +out_char: + stx STDOUT_DATA + bra in_wait + +done: lda #0 + sta SYS_EXIT + + .area text + +message: + .ascii /hello, world!/ + .db 0xa +message_end: diff --git a/emu_65c02/vrEmu6502 b/emu_65c02/vrEmu6502 new file mode 160000 index 0000000..abce433 --- /dev/null +++ b/emu_65c02/vrEmu6502 @@ -0,0 +1 @@ +Subproject commit abce43336301fa2bc4675f6d8d96fd8bd879192a diff --git a/emu_z80/Makefile b/emu_z80/Makefile new file mode 100644 index 0000000..ec9cf98 --- /dev/null +++ b/emu_z80/Makefile @@ -0,0 +1,28 @@ +CFLAGS=-g -Wall -O3 +LDFLAGS=-g + +ASZ80=../asxv5pxx/asxmak/linux/exe/asz80 +ASLINK=../asxv5pxx/asxmak/linux/exe/aslink + +# need to install intelhex package in Python first: +# pip3 install --user intelhex +HEX2BIN=python3 $(HOME)/.local/bin/hex2bin.py + +.PHONY: all +all: emu_z80 test.bin + +emu_z80: emu_z80.o z80/z80.o + $(CC) $(LDFLAGS) -o $@ $^ + +test.bin: test.ihx + $(HEX2BIN) $< $@ + +test.ihx: test.rel + $(ASLINK) -n -m -u -i $@ $^ + +test.rel: test.asm + $(ASZ80) -l -o $< + +.PHONY: clean +clean: + rm -f *.bin *.hlr *.ihx *.lst *.map *.o *.rel *.rst emu_z80 z80/*.o diff --git a/emu_z80/annotate_trace.py b/emu_z80/annotate_trace.py new file mode 100755 index 0000000..7ebba61 --- /dev/null +++ b/emu_z80/annotate_trace.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +import bisect +import sys + +EXIT_SUCCESS = 0 +EXIT_FAILURE = 1 + +if len(sys.argv) < 2: + print(f'usage: {sys.argv[0]:s} program.rst annotated_trace.txt') + sys.exit(EXIT_FAILURE) + +symbol_table = [] +with open(sys.argv[1]) as fin: + i = 0 # fallback sort key (use symbol defined later in file) + for line in fin: + fields = line[32:].split() + if ( + len(fields) and + fields[0][-1:] == ':' and + fields[0][:1] != ';' and + fields[0][-2:] != '$:' + ): + addr = int(line[3:7], 16) + symbol = fields[0][:-1] + symbol_table.append((addr, i, symbol)) + i += 1 +symbol_table.sort() +symbol_table = ( + [addr for addr, _, _ in symbol_table], + [symbol for _, _, symbol in symbol_table] +) + +for line in sys.stdin: + fields = line.split('=') + for i in range(1, len(fields)): + addr = int(fields[i][:4], 16) + j = bisect.bisect_right(symbol_table[0], addr) + if j: + j -= 1 + offset = addr - symbol_table[0][j] + symbol = symbol_table[1][j] + fields[i] = fields[i][:4] + f'({symbol:s}+{offset:04x})' + fields[i][4:] + sys.stdout.write('='.join(fields)) diff --git a/emu_z80/emu_z80.c b/emu_z80/emu_z80.c new file mode 100644 index 0000000..1bba91c --- /dev/null +++ b/emu_z80/emu_z80.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include +#include +#include +#include "z80/z80.h" + +#define STDIN_DATA 0 +#define STDOUT_DATA 1 +#define STDERR_DATA 2 +#define STDIN_STATUS 3 +#define STDOUT_STATUS 4 +#define STDERR_STATUS 5 +#define USLEEP_LO 6 +#define USLEEP_HI 7 +#define SYS_EXIT 8 + +#define TRACE 0 + +z80 cpu; + +int stdin_fd; +int g_argn = 0; +int g_argc = 1; +const char *default_argv = "-"; +const char **g_argv = &default_argv; + +#define MEMORY_SIZE 0x10000 +uint8_t memory[MEMORY_SIZE]; +uint8_t usleep_lo; +int exit_flag; + +uint8_t rb(void *userdata, uint16_t addr) { + return memory[addr]; +} + +void wb(void *userdata, uint16_t addr, uint8_t val) { + memory[addr] = val; +} + +// call with g_argn < g_argc +void open_stdin(void) { + if (strcmp(g_argv[g_argn], "-") == 0) + stdin_fd = STDIN_FILENO; + else { + stdin_fd = open(g_argv[g_argn], O_RDONLY); + if (stdin_fd == -1) { + perror(g_argv[g_argn]); + exit(EXIT_FAILURE); + } + } +} + +void close_stdin(void) { + if (stdin_fd != STDIN_FILENO) + close(stdin_fd); +} + +uint8_t in(z80 *const z, uint8_t port) { + switch (port) { + case STDIN_DATA: + { + uint8_t data = 4; // EOT + if (g_argn < g_argc) + while (true) { + ssize_t count = read(stdin_fd, &data, 1); + if (count == -1) { + perror("read()"); + exit(EXIT_FAILURE); + } + if (count) + break; + close_stdin(); + ++g_argn; + if (g_argn >= g_argc) + break; + open_stdin(); + } + return data; + } + case STDIN_STATUS: + { + if (g_argn >= g_argc) + return 1; // if no more input, force application to read EOT + struct pollfd fd = {stdin_fd, POLLIN, 0}; + if (poll(&fd, 1, 0) == -1) { + perror("poll()"); + exit(EXIT_FAILURE); + } + return (fd.revents & POLLIN) != 0; + } + case STDOUT_STATUS: + { + struct pollfd fd = {STDOUT_FILENO, POLLOUT, 0}; + if (poll(&fd, 1, 0) == -1) { + perror("poll()"); + exit(EXIT_FAILURE); + } + return (fd.revents & POLLOUT) != 0; + } + case STDERR_STATUS: + { + struct pollfd fd = {STDERR_FILENO, POLLOUT, 0}; + if (poll(&fd, 1, 0) == -1) { + perror("poll()"); + exit(EXIT_FAILURE); + } + return (fd.revents & POLLOUT) != 0; + } + case USLEEP_LO: + return usleep_lo; + } + return 0xff; +} + +void out(z80 *const z, uint8_t port, uint8_t val) { + switch (port) { + case STDOUT_DATA: + if (write(STDOUT_FILENO, &val, 1) == -1) { + perror("write()"); + exit(EXIT_FAILURE); + } + break; + case STDERR_DATA: + if (write(STDERR_FILENO, &val, 1) == -1) { + perror("write()"); + exit(EXIT_FAILURE); + } + break; + case USLEEP_LO: + usleep_lo = val; + break; + case USLEEP_HI: + usleep(usleep_lo | (val << 8)); + break; + case SYS_EXIT: + exit_flag = val | 0x100; + cpu.halted = true; + break; + } +} + +int main(int argc, char **argv) { + int argn = 1; + bool timing = false; + if (argn < argc && strcmp(argv[argn], "-t") == 0) { + timing = true; + ++argn; + } + + if (argn >= argc) { + printf("usage: %s [-t] program.bin\n", argv[0]); + exit(EXIT_FAILURE); + } + + int fd = open(argv[argn], O_RDONLY); + if (fd == -1) { + perror(argv[argn]); + exit(EXIT_FAILURE); + } + if (read(fd, memory, MEMORY_SIZE) == -1) { + perror("read()"); + exit(EXIT_FAILURE); + } + close(fd); + + // implement "cat" functionality for stdin + // if not enough arguments, supply default argument of "-" + ++argn; + if (argn < argc) { + g_argn = argn; + g_argc = argc; + g_argv = (const char **)argv; + } + open_stdin(); + + z80_init(&cpu); + cpu.read_byte = rb; + cpu.write_byte = wb; + cpu.port_in = in; + cpu.port_out = out; + + long n, nb_instructions = 0; +#if TRACE + do { + int pc = cpu.pc; + int ip = cpu.c | cpu.b << 8; + int dsp = cpu.sp; + int rsp = cpu.ix; + fprintf( + stderr, + "pc=%04x:%02x,%02x,%02x,%02x ip=%04x:%04x dsp=%04x:%04x rsp=%04x:%04x\n", + pc, + memory[pc], + memory[(pc + 1) & 0xffff], + memory[(pc + 2) & 0xffff], + memory[(pc + 3) & 0xffff], + ip, + memory[ip] | (memory[(ip + 1) & 0xffff] << 8), + dsp, + memory[dsp] | (memory[(dsp + 1) & 0xffff] << 8), + rsp, + memory[rsp] | (memory[(rsp + 1) & 0xffff] << 8) + ); + + n = z80_step(&cpu, 1); + nb_instructions += n; + } while (n); +#else + do { + n = z80_step(&cpu, 1000); + nb_instructions += n; + } while (n >= 1000); +#endif + + if (timing) + fprintf( + stderr, + "%lu instructions executed on %lu cycles\n", + nb_instructions, + cpu.cyc + ); + exit(exit_flag & 0xff); +} diff --git a/emu_z80/test.asm b/emu_z80/test.asm new file mode 100644 index 0000000..cf9aa68 --- /dev/null +++ b/emu_z80/test.asm @@ -0,0 +1,63 @@ +STDIN_DATA = 0 +STDOUT_DATA = 1 +STDERR_DATA = 2 +STDIN_STATUS = 3 +STDOUT_STATUS = 4 +STDERR_STATUS = 5 +USLEEP_LO = 6 +USLEEP_HI = 7 +SYS_EXIT = 8 + + .area text + + ld hl,message + ld b,message_end - message +print_message: + ld a,(hl) + inc hl + out (STDERR_DATA),a + djnz print_message + +in_wait: + in a,(STDIN_STATUS) + or a + jr nz,in_char + + ld a,<1000 + out (USLEEP_LO),a + ld a,>1000 + out (USLEEP_HI),a + jr in_wait + +in_char: + in a,(STDIN_DATA) + cp 4 ; EOT + jr z,done + + ld e,a + +out_wait: + in a,(STDOUT_STATUS) + or a + jr nz,out_char + + ld a,<1000 + out (USLEEP_LO),a + ld a,>1000 + out (USLEEP_HI),a + jr out_wait + +out_char: + ld a,e + out (STDOUT_DATA),a + jr in_wait + +done: ld a,0 + out (SYS_EXIT),a + + .area text + +message: + .ascii /hello, world/ + .db 0xa +message_end: diff --git a/emu_z80/z80 b/emu_z80/z80 new file mode 160000 index 0000000..ae62511 --- /dev/null +++ b/emu_z80/z80 @@ -0,0 +1 @@ +Subproject commit ae625116f1f9b013fd1a69d0173f8207f8703e21 diff --git a/i386/Makefile b/i386/Makefile new file mode 100644 index 0000000..c2429db --- /dev/null +++ b/i386/Makefile @@ -0,0 +1,210 @@ +# Makefile for preForth and seedForth +# +# make bootstrap should produce two identical files: preForth1.$(ASM) and preForth.$(ASM) + +# Set HOSTFORTH to the Forth system that generates the initial preForth +# ------------------------------------------------------------------------ +HOSTFORTH=gforth +# HOSTFORTH=sf # SwiftForth >3.7 +# ------------------------------------------------------------------------ + +.PHONY: all +all: \ +preForthDemo \ +preForth \ +seedForth \ +seedForthDemo.seed \ +seedForthBoot.seed \ +seedForthInteractive.seed + +.PHONY: test +test: runseedforthdemo runseedforthinteractive + +.PHONY: runseedforthdemo +runseedforthdemo: seedForth seedForthDemo.seed + ./seedForth seedForthDemo.seed + +.PHONY: runseedfortinteractive +runseedforthinteractive: seedForth seedForthInteractive.seed + ./seed + +UNIXFLAVOUR=$(shell uname -s) +ASM=asm + +# build preForthDemo via the host Forth first, so that it +# can be used for basic debugging of the preForth runtime +preForthDemo.$(ASM): \ +../common/preForth-bootstrap.fs \ +../common/preForth-cold.fs \ +preForth-i386-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth-i386-backend.pre \ +../common/preForth.pre \ +../common/preForthDemo.pre + cat \ +preForth-i386-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +../common/preForthDemo.pre \ +|$(HOSTFORTH) \ +../common/preForth-bootstrap.fs \ +../common/preForth-rts-nonstandard.pre \ +preForth-i386-backend.pre \ +../common/preForth.pre \ +../common/preForth-cold.fs \ +>$@ + +preForth.$(ASM): \ +../common/preForth-bootstrap.fs \ +../common/preForth-cold.fs \ +preForth-i386-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth-i386-backend.pre \ +../common/preForth.pre + cat \ +preForth-i386-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth-i386-backend.pre \ +../common/preForth.pre \ +|$(HOSTFORTH) \ +../common/preForth-bootstrap.fs \ +../common/preForth-rts-nonstandard.pre \ +preForth-i386-backend.pre \ +../common/preForth.pre \ +../common/preForth-cold.fs \ +>$@ + +%.$(ASM): \ +%.pre \ +preForth-i386-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth + ./preForth \ +preForth-i386-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +$< \ +>$@ + +ifeq ($(UNIXFLAVOUR),Linux) +# assemble and link executable on linux +%: %.$(ASM) + fasm $< $@.o + LDEMULATION=elf_i386 ld -arch i386 -o $@ \ +-dynamic-linker /lib32/ld-linux.so.2 \ +/usr/lib/i386-linux-gnu/crt1.o /usr/lib/i386-linux-gnu/crti.o \ +$@.o \ +-lc /usr/lib/i386-linux-gnu/crtn.o + # rm $@.o +else +ifeq ($(UNIXFLAVOUR),Darwin) +# assemble and link executable on MacOS +%: %.$(ASM) + fasm $< $@.o + objconv -fmacho32 -nu $@.o $@_m.o + ld -arch i386 -macosx_version_min 10.6 -o $@ \ +$@_m.o /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib/crt1.o \ +/usr/lib/libc.dylib + # rm $@.o $@_m.o +endif +endif + +# run preForth on its own source code to perform a bootstrap +# should produce identical results +bootstrap: \ +preForth-i386-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth-i386-backend.pre \ +../common/preForth.pre \ +preForth \ +preForth.$(ASM) + ./preForth \ +preForth-i386-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth-i386-backend.pre \ +../common/preForth.pre \ +>preForth1.$(ASM) + cmp preForth.$(ASM) preForth1.$(ASM) + +# preForth connected to stdin - output to stdout +.PHONY: visible-bootstrap +visible-bootstrap: preForth preForth-i386-backend.pre ../common/preForth.pre + ./preForth preForth-i386-backend.pre ../common/preForth.pre + +# ------------------------------------------------------------------------ +# Docker support (for Linux version) +# ------------------------------------------------------------------------ +# create a linux image based on Dockerfile +.PHONY: docker-image +docker-image: Dockerfile + docker build -t preforth . + +# run the docker image +.PHONY: run +rundocker: docker-image + docker run -i -t --rm preforth /preForth/seed +# ------------------------------------------------------------------------ + +# ------------------------------------------------------------------------ +# seedForth +# ------------------------------------------------------------------------ +seedForth.$(ASM): \ +seedForth-i386-header.pre \ +preForth-i386-rts.pre \ +seedForth-i386-rts.pre \ +seedForth-i386.pre \ +../common/seedForth32bit.pre \ +../common/seedForth.pre \ +preForth + ./preForth \ +seedForth-i386-header.pre \ +preForth-i386-rts.pre \ +seedForth-i386-rts.pre \ +seedForth-i386.pre \ +../common/seedForth32bit.pre \ +../common/seedForth.pre \ +>seedForth.$(ASM) + +# a little ugly, because gforth insists upon echoing anything read from +# stdin, and also does not allow parsing words to cross a file boundary, +# so we will concatenate the tokenizer and source into a *.fs file first +seedForthDemo.seed: \ +../common/crc10.forth \ +../common/../common/seedForth-tokenizer.fs \ +../common/seedForthDemo.seedsource \ +seedForthDemoi386.seedsource + cat $^ >__temp__.fs + $(HOSTFORTH) __temp__.fs -e bye >$@ + rm __temp__.fs + +seedForthBoot.seed: \ +../common/crc10.forth \ +../common/../common/seedForth-tokenizer.fs \ +../common/seedForthRuntime32bit.seedsource \ +../common/seedForthRuntime.seedsource \ +seedForthRuntimei386.seedsource \ +../common/seedForthBoot.seedsource + cat $^ >__temp__.fs + $(HOSTFORTH) __temp__.fs -e bye >$@ + rm __temp__.fs + +seedForthInteractive.seed: \ +../common/crc10.forth \ +../common/../common/seedForth-tokenizer.fs \ +../common/seedForthRuntime32bit.seedsource \ +../common/seedForthRuntime.seedsource \ +seedForthRuntimei386.seedsource \ +../common/seedForthInteractive.seedsource + cat $^ >__temp__.fs + $(HOSTFORTH) __temp__.fs -e bye >$@ + rm __temp__.fs + +.PHONY: clean +clean: + rm -f *.$(ASM) *.o *.seed preForthDemo preForth seedForth __temp__.fs diff --git a/preForth/preForth-i386-backend.pre b/i386/preForth-i386-backend.pre similarity index 70% rename from preForth/preForth-i386-backend.pre rename to i386/preForth-i386-backend.pre index f218fbb..6881741 100644 --- a/preForth/preForth-i386-backend.pre +++ b/i386/preForth-i386-backend.pre @@ -23,7 +23,7 @@ 'Q' swap '?' case? ?exit nip 'R' swap '"' case? ?exit nip \ 'S' swap '!' case? ?exit nip - 'T' swap '*' case? ?exit nip + 'T' swap '*' case? ?exit nip 'U' swap '(' case? ?exit nip 'V' swap '|' case? ?exit nip 'W' swap ',' case? ?exit nip @@ -42,22 +42,13 @@ \ output words \ ------------ \ Output is done by emit. -\ We define convenience words of the form ."xxx" to output xxx and >"xxx" to output xxx on a new line indented by a tab. +\ We define convenience words of the form ."xxx" to output xxx (maybe tabbed) : ."dd" ( -- ) - 'D' emit 'D' emit space ; - -: >"dd" ( -- ) - cr tab ."dd" ; + tab 'd' emit 'd' emit tab ; : ."db" ( -- ) - 'D' emit 'B' emit space ; - -: >"db" ( -- ) - cr tab ."db" ; - -: >"ds" ( -- ) - cr tab 'D' emit 'S' emit space ; + tab 'd' emit 'b' emit tab ; : ."nest" ( -- ) 'n' 'e' 's' 't' 4 alter show ; @@ -74,55 +65,50 @@ \ ,string compiles the topmost string as a sequence of numeric DB values. : ,string ( S -- ) - \ ."ds" show ; ?dup 0= ?exit - dup roll >"db" u. \ 1st char + dup roll ."db" u. cr \ 1st char 1- ,string ; \ reproduce a verbatim line : ,line ( x1 ...cn n -- ) show ; -\ compile a reference to an invoked word +\ compile a reference to an invoked word : ,word ( S -- ) - ."dd" alter show ; - -\ compile a reference to an invoked word on a new line -: ,>word ( S -- ) - >"dd" alter show ; + ."dd" alter show cr ; \ compile reference to nest primitive : ,nest ( -- ) - ."dd" ."nest" ; + ."dd" ."nest" cr ; \ compile reference to unnest primitive : ,unnest ( -- ) - >"dd" ."unnest" - cr ; + ."dd" ."unnest" cr cr ; \ compile signed number : ,n ( n -- ) - >"dd" . ; + ."dd" . cr ; \ compile unsigned number : ,u ( u -- ) - >"dd" u. ; + ."dd" u. cr ; \ compile literal : ,_lit ( S -- ) - >"dd" ."lit" ,>word ; + ."dd" ."lit" cr ,word ; \ compile literal : ,lit ( x -- ) - >"dd" ."lit" ,n ; + ."dd" ."lit" cr ,n ; \ output string as comment : ,comment ( S -- ) - cr tab ';' emit space show ; + tab ';' emit space show cr ; \ create a new symbolic label +\ if label is 6 characters or less, stay on same line for following code : label ( S -- ) - cr alter show ':' emit tab ; + alter dup >r show ':' emit r> 7 - 0< ?exit cr ; \ body calculates the name of the body from a token : body ( S1 -- S2 ) @@ -138,7 +124,7 @@ : ,end-code ( -- ) cr ; - + \ ----------------------------------- \ tail call optimization tail word ; -> [ ' word >body ] literal >r ; @@ -148,19 +134,20 @@ \ ,tail compiles a tail call : ,tail ( S -- ) body ,_lit - '>' 'r' 2 ,>word ; - -: ."done" ( -- ) - ';' emit space 'd' emit 'o' emit 'n' emit 'e' emit ; + '>' 'r' 2 ,word ; -: ."last:" ( -- ) - ';' emit space 'l' emit 'a' emit 's' emit 't' emit ':' emit space ; +\ : ."done" ( -- ) +\ ';' emit space 'd' emit 'o' emit 'n' emit 'e' emit ; +\ +\ : ."last:" ( -- ) +\ ';' emit space 'l' emit 'a' emit 's' emit 't' emit ':' emit space ; : ,end ( S -- ) - cr ."last:" alter show - cr ."done" cr ; + \ cr ."last:" alter show + \ cr ."done" cr + ; -\ create a new header with given name S2 and flags - do nothing -: header ( S1 S2 flags -- S3 S2 ) - drop ; +\ \ create a new header with given name S2 and flags - do nothing +\ : header ( S1 S2 flags -- S3 S2 ) +\ drop ; diff --git a/i386/preForth-i386-rts.pre b/i386/preForth-i386-rts.pre new file mode 100644 index 0000000..93e3614 --- /dev/null +++ b/i386/preForth-i386-rts.pre @@ -0,0 +1,398 @@ +\ preForth runtime system - i386 (32 bit) dependent part +\ -------------------------- +\ +\ - registers: +\ EAX, EDX general purpose +\ ESI instruction pointer +\ EBP return stack pointer +\ ESP data stack pointer + +pre +;;; This is a preForth generated file using preForth-i386-backend. +;;; Only modify it, if you know what you are doing. + +DATA_STACK_SIZE = 40000 +RETURN_STACK_SIZE = 40000 + +O_RDONLY = 0 +STDIN_FILENO = 0 +STDOUT_FILENO = 1 +STDERR_FILENO = 2 + +EXIT_SUCCESS = 0 +EXIT_FAILURE = 1 + +EOT_CHAR = 4 + +format ELF + +section '.text' executable + +public main +extrn close +extrn exit +extrn open +extrn read +extrn strcmp +extrn strlen +extrn write + +macro next { + lodsd + jmp dword [eax] +} + +main: cld + + ; implement the functionality of "cat" for reading "key" input + ; files listed on command line will be read in order, "-" is stdin + ; if there are no command line arguments, supply a default of "-" + mov eax,[esp+4] ; argc + mov ecx,[esp+8] ; argv + cmp eax,2 + jb no_arguments ; if no arguments, use pre-filled defaults + mov [argc],eax + mov [argv],ecx +no_arguments: + call open_fd_in + + mov esp,data_stack + DATA_STACK_SIZE + mov ebp,return_stack + RETURN_STACK_SIZE + mov esi,main1 + next + +open_fd_in: + push esi + push ebp + mov ebp,esp + and esp,0xfffffff0 + sub esp,16 + + mov eax,[argn] + mov ecx,[argv] + mov esi,[ecx+eax*4] ; esi = argv[argn] + + mov [esp],esi + mov dword [esp+4],dash_c_str + call strcmp ; eax = strcmp(esi, "-") + test eax,eax + mov eax,STDIN_FILENO + jz open_fd_in_ok ; if "-", do not open and use STDIN_FILENO + + mov [esp],esi + mov dword [esp+4],O_RDONLY + call open ; eax = open(esi, O_RDONLY) + cmp eax,-1 + jnz open_fd_in_ok ; if open successful, use returned fd + + mov dword [esp],STDERR_FILENO + mov dword [esp+4],message_cant_open + mov dword [esp+8],message_cant_open_end - message_cant_open + call write ; write(STDERR_FILENO, "can't open: ", 12) + + mov dword [esp],esi + call strlen ; eax = strlen(esi) + + mov dword [esp],STDERR_FILENO + mov [esp+4],esi + mov [esp+8],eax + call write ; write(STDERR_FILENO, esi, strlen(esi)) + + mov dword [esp],STDERR_FILENO + mov dword [esp+4],message_newline + mov dword [esp+8],message_newline_end - message_newline + call write ; write(STDERR_FILENO, "\n", 1) + + mov dword [esp],EXIT_FAILURE + call exit ; exit(EXIT_FAILURE) + +open_fd_in_ok: + mov [fd_in],eax + + mov esp,ebp + pop ebp + pop esi + ret + +close_fd_in: + push ebp + mov ebp,esp + and esp,0xfffffff0 + sub esp,16 + + mov eax,[fd_in] + test eax,eax ; cmp eax,STDIN_FILENO + jz close_fd_in_ok + + mov [esp],eax + call close + +close_fd_in_ok: + mov esp,ebp + pop ebp + ret + +main1: dd _cold + dd _bye + +_nest: lea ebp,[ebp-4] + mov [ebp],esi + lea esi,[eax+4] + next + +; + +code bye ( -- ) + and esp,0xfffffff0 + sub esp,16 + + mov dword [esp],EXIT_SUCCESS + call exit ; exit(EXIT_SUCCESS) +; + +code emit ( c -- ) + pop eax ; eax = character to emit + + push ebp + mov ebp,esp + and esp,0xfffffff0 + sub esp,16 + + mov [esp+12],al ; char ch_out = character to emit + + mov dword [esp],STDOUT_FILENO + lea eax,[esp+12] + mov [esp+4],eax + mov dword [esp+8],1 + call write ; eax = write(STDOUT_FILENO, &ch_out, 1) + cmp eax,-1 + jnz emit_ok + + mov dword [esp],STDERR_FILENO + mov dword [esp+4],message_cant_write + mov dword [esp+8],message_cant_write_end - message_cant_write + call write ; write(STDERR_FILENO, "can't write\n", 12) + + mov dword [esp],EXIT_FAILURE + call exit ; exit(EXIT_FAILURE) + +emit_ok: + mov esp,ebp + pop ebp + next +; + +code eemit ( c -- ) + pop eax ; eax = character to emit + + push ebp + mov ebp,esp + and esp,0xfffffff0 + sub esp,16 + + mov [esp+12],al ; char ch_out = character to emit + + mov dword [esp],STDERR_FILENO + lea eax,[esp+12] + mov [esp+4],eax + mov dword [esp+8],1 + call write ; eax = write(STDERR_FILENO, &ch_out, 1) + cmp eax,-1 + jnz eemit_ok + + mov dword [esp],STDERR_FILENO + mov dword [esp+4],message_cant_write + mov dword [esp+8],message_cant_write_end - message_cant_write + call write ; write(STDERR_FILENO, "can't write\n", 12) + + mov dword [esp],EXIT_FAILURE + call exit ; exit(EXIT_FAILURE) + +eemit_ok: + mov esp,ebp + pop ebp + next +; + +code key ( -- c ) + push ebp + mov ebp,esp + and esp,0xfffffff0 + sub esp,16 + + mov dword [esp+12],EOT_CHAR ; int ch_in = EOT_CHAR (litle endian) + + ; if argn >= argc then no file is open, so return EOT_CHAR + mov eax,[argn] + cmp eax,[argc] + jae key_done + +key_read: + mov eax,[fd_in] + mov [esp],eax + lea eax,[esp+12] + mov [esp+4],eax + mov dword [esp+8],1 + call read ; eax = read(fd_in, &ch_in, 1) + cmp eax,-1 + jnz key_ok + + mov dword [esp],STDERR_FILENO + mov dword [esp+4],message_cant_read + mov dword [esp+8],message_cant_read_end - message_cant_read + call write ; write(STDERR_FILENO, "can't read\n", 11) + + mov dword [esp],EXIT_FAILURE + call exit ; exit(EXIT_FAILURE) + +key_ok: + ; if key was read successfully, send to application + test eax,eax + jnz key_done + + ; eof + call close_fd_in + + ; if arguments now exhausted, sent EOT to application + mov eax,[argn] + inc eax + mov [argn],eax + cmp eax,[argc] + jae key_done + + ; open next input file, and then re-attempt the read + call open_fd_in + jmp key_read + +key_done: + mov eax,[esp+12] ; eax = ch_in + + mov esp,ebp + pop ebp + + push eax + next +; + +code dup ( x -- x x ) + pop eax + push eax + push eax + next +; + +code swap ( x y -- y x ) + pop edx + pop eax + push edx + push eax + next +; + +code drop ( x -- ) + pop eax + next +; + +code 0< ( x -- flag ) + pop eax + sar eax,31 + push eax + next +; + +code >r ( x -- ) ( R -- x ) + pop ebx + lea ebp,[ebp-4] + mov [ebp],ebx + next +; + +code r> ( R x -- ) ( -- x ) + mov eax,[ebp] + lea ebp,[ebp+4] + push eax + next +; + +code - ( x1 x2 -- x3 ) + pop edx + pop eax + sub eax,edx + push eax + next +; + +code ?exit ( f -- ) \ high level: IF exit THEN + pop eax + or eax,eax + jz qexit1 + mov esi,[ebp] + lea ebp,[ebp+4] +qexit1: next +; + +code unnest ( -- ) + mov esi,[ebp] + lea ebp,[ebp+4] + next +; + +code lit ( -- ) + lodsd + push eax + next +; + +\ we want the text section to be first and bss last (for linkers that output +\ sections in definition order), so it would be good to have the bss section +\ be output by the ",end" hook, but at present we cannot call "pre" from a +\ defined word, so we make do by switching to bss and then back to text again +pre +section '.data' writeable align 16 + +message_cant_open: + db 'can''t open: ' +message_cant_open_end: + +message_newline: + db 0xa +message_newline_end: + +message_cant_read: + db 'can''t read',0xa +message_cant_read_end: + +message_cant_write: + db 'can''t write',0xa +message_cant_write_end: + +dash_c_str: + db '-',0 + + align 4 + +default_argv: + dd 0 ; argv[0] + dd dash_c_str +default_argv_end: + +; default command line arguments, overwritten if any passed in +argc: dd (default_argv_end - default_argv) / 4 +argv: dd default_argv + +; argument number being processed, fd_in is valid if argn < argc +argn: dd 1 + +section '.bss' writeable align 16 + +fd_in: dd 0 + +data_stack: + db DATA_STACK_SIZE dup (0) +return_stack: + db RETURN_STACK_SIZE dup (0) + +section '.text' executable + +; diff --git a/i386/seed b/i386/seed new file mode 100755 index 0000000..dc9b45d --- /dev/null +++ b/i386/seed @@ -0,0 +1,4 @@ +#!/bin/bash +stty raw -echo +./seedForth seedForthInteractive.seed ../common/runtime.forth ../common/hi32bit.forth ../common/hi.forth - +stty sane diff --git a/i386/seedForth-i386-header.pre b/i386/seedForth-i386-header.pre new file mode 100644 index 0000000..bddc8e3 --- /dev/null +++ b/i386/seedForth-i386-header.pre @@ -0,0 +1,24 @@ +\ seedForth: machine dependent header portion + +\ this contains only definitions we want at the top of the file +\ it is then followed by preForth-i386-rts.pre (primitive asm words) +\ and then by seedForth-i386.pre (additional primitive asm words) +\ and then by seedForth.pre (high level words and the interpreter) + +pre +;;; This is seedForth - a small potentially interactive Forth, that dynamically +;;; bootstraps from a minimal kernel. +;;; +;;; cat seedForth.seed - | ./seedForth +;;; +;;; .seed files are in byte-tokenized source code format. +;;; +;;; Use the seedForth tokenizer to convert human readable source code to +;;; byte-token form. + +HEAD_SIZE = 40000 +MEM_SIZE = 400000 + +POLLIN = 1 + +; diff --git a/i386/seedForth-i386-rts.pre b/i386/seedForth-i386-rts.pre new file mode 100644 index 0000000..ca1bf33 --- /dev/null +++ b/i386/seedForth-i386-rts.pre @@ -0,0 +1,226 @@ +\ seedForth: machine dependent portion + +\ interpreter and basic asm primitives are taken from preForth-i386-rts.pre, +\ and then this file defines additional primitives (arithmetic, memory, etc) + +pre +extrn poll +extrn usleep + +_enter = _nest +_exit = _unnest + +_dodoes: ; ( -- addr ) + lea ebp,[ebp-4] ; push IP + mov [ebp],esi + mov esi,[eax-4] ; set IP +_dovar: ; ( -- addr ) + lea eax,[eax+4] ; to parameter field + push eax + next +; + +code key? ( -- f ) + push ebp + mov ebp,esp + and esp,0xfffffff0 + sub esp,32 + + ; if all arguments exhausted, return ready + ; this allows application to collect the EOT character + mov eax,[argn] + cmp eax,[argc] + jae keyq_ready + + mov eax,[fd_in] + mov dword [esp+12],eax + mov dword [esp+16],POLLIN ; struct pollfd fd = {fd_in, POLLIN, 0} + + lea eax,[esp+12] + mov [esp],eax + mov dword [esp+4],1 + mov dword [esp+8],0 + call poll ; eax = poll(&fd, 1, 0) + cmp eax,-1 + jnz keyq_ok + + mov dword [esp],STDERR_FILENO + mov dword [esp+4],message_cant_poll + mov dword [esp+8],message_cant_poll_end - message_cant_poll + call write ; write(STDERR_FILENO, "can't poll\n", 11) + + mov dword [esp],EXIT_FAILURE + call exit ; exit(EXIT_FAILURE) + +keyq_ok: + sub eax,eax + test word [esp+18],POLLIN ; fd.revents & POLLIN + jz keyq_done +keyq_ready: + mov eax,-1 +keyq_done: + + mov esp,ebp + pop ebp + + push eax + next +; + +code or ( x1 x2 -- x3 ) + pop edx + pop eax + or eax,edx + push eax + next +; + +code and ( x1 x2 -- x3 ) + pop edx + pop eax + and eax,edx + push eax + next +; + +code @ ( addr -- x ) + pop eax + mov eax,[eax] + push eax + next +; + +code c@ ( c-addr -- c ) + pop edx + xor eax,eax + mov al,byte [edx] + push eax + next +; + +code ! ( x addr -- ) + pop edx + pop eax + mov dword [edx],eax + next +; + +code c! ( c c-addr -- ) + pop edx + pop eax + mov byte [edx],al + next +; + +code execute ( xt -- ) + pop eax + jmp dword [eax] +; + +code branch ( -- ) \ threaded code: r> @ >r ; + lodsd + mov esi,eax + next +; + +\ is this misleading? I would have thought ?branch means branch on nonzero +code ?branch ( f -- ) \ threaded code: ?exit r> @ >r ; + pop eax + or eax,eax + jz _branchX + lea esi,[esi+4] + next +; + +code depth ( -- n ) + mov eax,data_stack + DATA_STACK_SIZE + sub eax,esp + sar eax,2 + push eax + next +; + +code sp@ ( -- x ) + push esp + next +; + +code sp! ( x -- ) + pop esp + next +; + +code rp@ ( -- x ) + push ebp + next +; + +code rp! ( x -- ) + pop ebp + next +; + +code um* ( u1 u2 -- ud ) + pop edx + pop eax + mul edx + push eax + push edx + next +; + +code um/mod ( ud u1 -- u2 u3 ) + pop ebx + pop edx + pop eax + div ebx + push edx + push eax + next +; + +code usleep ( c -- ) + pop eax ; eax = microseconds to sleep + + push ebp + mov ebp,esp + and esp,0xfffffff0 + sub esp,16 + + mov [esp],eax + call usleep + + mov esp,ebp + pop ebp + next +; + +\ we want the text section to be first and bss last (for linkers that output +\ sections in definition order), so it would be good to have the bss section +\ be output by the ",end" hook, but at present we cannot call "pre" from a +\ defined word, so we make do by switching to bss and then back to text again +pre +section '.data' writeable align 16 + +message_cant_poll: + db 'can''t poll',0xa +message_cant_poll_end: + + align 4 + + ; dictionary pointer: points to next free location in memory +_dp: dd _mem + +section '.bss' writeable align 16 + + ; head pointer: index of first unused head +__hp: dd 0 +_head: dd HEAD_SIZE dup (0) + + ; free memory starts at _mem +_mem: db MEM_SIZE dup (0) +_memtop: + +section '.text' executable + +; diff --git a/i386/seedForth-i386.pre b/i386/seedForth-i386.pre new file mode 100644 index 0000000..ff14707 --- /dev/null +++ b/i386/seedForth-i386.pre @@ -0,0 +1,18 @@ +\ seedForth: less machine dependent portion +\ allows us to adjust things for direct threaded vs indirect threaded + +: compiler ( -- ) + token h@ + ?eot + ?lit + , tail compiler ; + +: new ( -- xt ) + here lit enter , ; + +: create ( -- xt ) + 0 , \ dummy does> field + here lit dovar , ; + +: does> ( xt -- ) \ set code field of last defined word + r> swap dup >r 1 cells - ! lit dodoes r> ! ; diff --git a/i386/seedForth-tokenizer b/i386/seedForth-tokenizer new file mode 100755 index 0000000..942d967 --- /dev/null +++ b/i386/seedForth-tokenizer @@ -0,0 +1,2 @@ +#!/bin/sh +./seedForth seedForthBoot.seed ../common/runtime.forth ../common/crc10.forth ../common/seedForth-tokenizer.fs - diff --git a/i386/seedForthDemoi386.seedsource b/i386/seedForthDemoi386.seedsource new file mode 100644 index 0000000..a9b182d --- /dev/null +++ b/i386/seedForthDemoi386.seedsource @@ -0,0 +1,14 @@ +\ machine dependent part of seedForthDemo.seedsource +\ allows us to adjust things for direct threaded vs indirect threaded + +: >body ( xt -- body ) 1 cells + ; + +: is ( xt -- ) ' >body ! ; + +Defer d1 +' ten is d1 +t{ d1 d1 d1 -> ten ten ten }t +' five is d1 +t{ d1 d1 d1 -> five five five }t + +done bye diff --git a/i386/seedForthRuntimei386.seedsource b/i386/seedForthRuntimei386.seedsource new file mode 100644 index 0000000..de5ddc4 --- /dev/null +++ b/i386/seedForthRuntimei386.seedsource @@ -0,0 +1,19 @@ +\ machine dependent part of seedForthRuntime.seedsource +\ allows us to adjust things for direct threaded vs indirect threaded + +: >body ( xt -- body ) + 1 cells + ; +' >body has-header >body + +: is ( xt -- ) \ only interactive + ' >body ! ; +' is has-header is + +\ these could not be done until here +' key is getkey +' noop is .status + +: (Does>) ( -- ) + [ ' last-xt ] Literal , + [ ' does> ] Literal , ; +' (Does>) has-header Does> immediate diff --git a/preForth/Makefile b/preForth/Makefile deleted file mode 100644 index e50cd04..0000000 --- a/preForth/Makefile +++ /dev/null @@ -1,104 +0,0 @@ -# Makefile for preForth and seedForth -# -# make bootstrap should produce two identical files: preForth1.asm and preForth.asm - -# Set HOSTFORTH to the Forth system that generates the initial preForth -# ------------------------------------------------------------------------ -HOSTFORTH=gforth -# HOSTFORTH=sf # SwiftForth >3.7 -# ------------------------------------------------------------------------ - -.PHONY=all -all: preForth seedForth seedForthDemo.seed seedForthInteractive.seed - -.PHONY=test -test: runseedforthdemo runseedforthinteractive - -.PHONY=runseedforthdemo -runseedforthdemo: seedForth seedForthDemo.seed - cat seedForthDemo.seed | ./seedForth - -.PHONY=runseedfortinteractive -runseedforthinteractive: seedForth seedForthInteractive.seed - ./seed - -UNIXFLAVOUR=$(shell uname -s) -EXT=asm - -seedForth-i386.asm: seedForth-i386.pre preForth - cat seedForth-i386.pre | ./preForth >seedForth-i386.asm - -# preForth connected to stdin - output to preForth.asm -preForth.asm: preForth.pre preForth-i386-backend.pre load-i386-preForth.fs - cat preForth-i386-rts.pre preForth-rts.pre preForth-i386-backend.pre preForth.pre \ - | $(HOSTFORTH) load-i386-preForth.fs >preForth.asm - -preForth: preForth.$(UNIXFLAVOUR) - cp preForth.$(UNIXFLAVOUR) preForth - -%.asm: %.pre preForth preForth-i386-rts.pre preForth-rts.pre - cat preForth-i386-rts.pre preForth-rts.pre $< | ./preForth >$@ - -%: %.$(UNIXFLAVOUR) - cp $< $@ - -# assemble and link executable on linux -%.Linux: %.asm - fasm $< $@.o - LDEMULATION=elf_i386 ld -arch i386 -o $@ \ - -dynamic-linker /lib32/ld-linux.so.2 \ - /usr/lib/i386-linux-gnu/crt1.o /usr/lib/i386-linux-gnu/crti.o \ - $@.o \ - -lc /usr/lib/i386-linux-gnu/crtn.o - # rm $@.o - -# assemble and link executable on MacOS -%.Darwin: %.asm - fasm $< $@.o - objconv -fmacho32 -nu $@.o $@_m.o - ld -arch i386 -macosx_version_min 10.6 -o $@ \ - $@_m.o /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib/crt1.o /usr/lib/libc.dylib - # rm $@.o $@_m.o - -# run preForth on its own source code to perform a bootstrap -# should produce identical results -bootstrap: preForth preForth-i386-backend.pre preForth.pre preForth.$(EXT) - cat preForth-i386-rts.pre preForth-rts.pre preForth-i386-backend.pre preForth.pre\ - | ./preForth >preForth1.$(EXT) - cmp preForth.$(EXT) preForth1.$(EXT) - -# preForth connected to stdin - output to stdout -.PHONY=visible-bootstrap -visible-bootstrap: preForth preForth-i386-backend.pre preForth.pre - cat preForth-i386-backend.pre preForth.pre | ./preForth - -# ------------------------------------------------------------------------ -# Docker support (for Linux version) -# ------------------------------------------------------------------------ -# create a linux image based on Dockerfile -.PHONY=docker-image -docker-image: Dockerfile - docker build -t preforth . - -# run the docker image -.PHONY=run -rundocker: docker-image - docker run -i -t --rm preforth /preForth/seed -# ------------------------------------------------------------------------ - -# ------------------------------------------------------------------------ -# seedForth -# ------------------------------------------------------------------------ -seedForth.$(EXT): seedForth-i386.pre preForth - cat seedForth-i386.pre | ./preForth >seedForth.$(EXT) - -seedForth: seedForth.$(UNIXFLAVOUR) - cp seedForth.$(UNIXFLAVOUR) seedForth - -%.seed: %.seedsource seedForth-tokenizer.fs - gforth seedForth-tokenizer.fs $< - - -.PHONY=clean -clean: - rm -f *.asm *.o *.fas *.s *.c *.Darwin *.Linux preForthdemo preForth forth seedForth seedForthDemo.seed seedForthInteractive.seed diff --git a/preForth/borrow.fs b/preForth/borrow.fs deleted file mode 100644 index b0d84e2..0000000 --- a/preForth/borrow.fs +++ /dev/null @@ -1,56 +0,0 @@ -\ Minimal Forth Workbench: main file uh 2015-10-05 - -: tick ( name -- comp-xt exec-xt flag ) - STATE @ >R - ] >IN @ >R BL WORD FIND - IF R> >IN ! - POSTPONE [ BL WORD FIND - ELSE R> DROP - DROP 0 0 false - THEN - R> IF ] ELSE POSTPONE [ THEN ; - -: immediate-alias ( comp-xt exec-xt name -- ) - CREATE , , IMMEDIATE DOES> STATE @ IF CELL+ THEN @ EXECUTE ; - -: non-immediate-alias ( comp-xt exec-xt name -- ) - CREATE , , IMMEDIATE DOES> STATE @ IF CELL+ @ COMPILE, ELSE @ EXECUTE THEN ; - -VARIABLE #primitives 0 #primitives ! -VARIABLE #words 0 #words ! - -: another-primitive ( -- ) 1 #primitives +! 1 #words +! ; - -: borrow ( ccc -- ) - get-order - >IN @ >R tick R> >IN ! NIP NIP - 0= IF - forth-wordlist 1 set-order - another-primitive - >IN @ >R tick R> >IN ! DUP 0= Abort" ?" - 0< IF non-immediate-alias ELSE immediate-alias THEN - ELSE - CR BL WORD COUNT TYPE ." is already defined." - THEN - set-order ; - -: primitive ( ccc -- ) borrow ; - -\ : later ( ccc -- ) \ word ccc uses late binding -\ \ has danger of infinite recursion if no defintion exists -\ >IN @ >R CREATE R> >IN ! -\ HERE BL WORD COUNT >R -\ HERE CHAR+ R@ MOVE R@ CHAR+ ALLOT R> SWAP C! -\ DOES> COUNT EVALUATE ; - -: later ( ccc -- ) \ word ccc uses late binding - >IN @ >R CREATE R> >IN ! - HERE BL WORD COUNT >R - HERE CHAR+ R@ MOVE R@ CHAR+ ALLOT R> SWAP C! - DOES> DUP >R - FIND 0= ABORT" ?" - DUP >BODY R@ = IF R> COUNT TYPE ." is not yet defined." ABORT THEN - R> DROP EXECUTE ; - - - diff --git a/preForth/load-i386-preForth.fs b/preForth/load-i386-preForth.fs deleted file mode 100644 index 7da3f75..0000000 --- a/preForth/load-i386-preForth.fs +++ /dev/null @@ -1,11 +0,0 @@ -\ load i386 preForth on top of a host Forth system - -include load-preForth.fs -include preForth-i386-rts.pre -include preForth-rts.pre -include preForth-i386-backend.pre -include preForth.pre - -cold - -bye diff --git a/preForth/load-preForth.fs b/preForth/load-preForth.fs deleted file mode 100644 index 13dd2c9..0000000 --- a/preForth/load-preForth.fs +++ /dev/null @@ -1,115 +0,0 @@ -\ Load preForth on GForth or SwiftForth connected to stdin and stdout. - - -defined warnings [IF] \ e.g. gforth - warnings off -[THEN] - -defined warning [IF] \ e.g. SwiftForth - warning off -[THEN] - -Variable ch - -\ key reads from stdin so it can be used with pipes and input redirection. -: key ( -- c ) - ch 1 stdin read-file throw - 1 < IF 4 ( eof ) ELSE ch c@ THEN - ; \ dup emit ; - -\ This : allows for recursion by using a word's name. -defined -smudge [IF] \ SwiftForth -: : : -smudge ; -[THEN] - -defined reveal [IF] \ gforth -: : : reveal ; -[THEN] - - -\ Define pre and code so they skip their body - -: pre ( -- ) - BEGIN refill WHILE - source s" ;" compare 0= IF POSTPONE \ EXIT THEN - REPEAT ; - -: prefix pre ; -: prelude pre ; -: preamble pre ; -: code pre ; - -: tail ; - -include borrow.fs - -wordlist Constant preForth - -preForth set-current - -: borrow borrow ; -: primitive borrow ; -: tail tail ; - -preForth 1 set-order - -borrow include -borrow : -borrow ; -borrow \ -borrow ( -borrow .s - -borrow pre -borrow prefix -borrow prelude -borrow preamble -borrow code - -borrow later -later ?dup -later 0= -later negate -later + -later 1+ -later 1- -later = -later < -later > -later case? - -later over -later rot -later nip -later 2drop -later pick -later roll - -later bl -later space -later tab -later cr -later u. -later . - -later show -later _dup -later _drop -later _swap - -primitive emit -primitive key -primitive dup -primitive swap -primitive 0< -primitive ?exit -primitive drop -primitive recurse -primitive >r -primitive r> -primitive - -\ nest -\ unnest -\ lit - -borrow bye diff --git a/preForth/preForth-i386-rts.pre b/preForth/preForth-i386-rts.pre deleted file mode 100644 index 09f5efd..0000000 --- a/preForth/preForth-i386-rts.pre +++ /dev/null @@ -1,175 +0,0 @@ -\ preForth runtime system - i386 (32 bit) dependent part -\ -------------------------- -\ -\ - registers: -\ EAX, EDX general purpose -\ ESI instruction pointer -\ EBP return stack pointer -\ ESP data stack pointer - -prelude -;;; This is a preForth generated file using preForth-i386-backend. -;;; Only modify it, if you know what you are doing. - -; - -prefix -format ELF - -section '.bss' writeable executable - - DD 10000 dup (0) -stck: DD 16 dup(0) - - DD 10000 dup(0) -rstck: DD 16 dup(0) - - -section '.text' executable writeable -public main -extrn putchar -extrn getchar -extrn fflush -extrn exit - -macro next { - lodsd - jmp dword [eax] -} - - -main: cld - mov esp, dword stck - mov ebp, dword rstck - mov esi, main1 - next - -main1: DD _cold - DD _bye - - -_nest: lea ebp, [ebp-4] - mov [ebp], esi - lea esi, [eax+4] - next - -_O = 0 - -; - -code bye ( -- ) - push ebp - mov ebp, esp - and esp, 0xfffffff0 - mov eax, 0 - mov [esp], eax - call exit -; - -code emit ( c -- ) - pop eax - - push ebp - mov ebp, esp - push eax - and esp, 0xfffffff0 - - mov dword [esp], eax - call putchar - - mov eax, 0 - mov [esp], eax - call fflush ; flush all output streams - - mov esp, ebp - pop ebp - next -; - -code key ( -- c ) - push ebp - mov ebp, esp - and esp, 0xfffffff0 - - call getchar - mov esp, ebp - pop ebp - cmp eax,-1 - jnz key1 - mov eax,4 -key1: push eax - next -; - -code dup ( x -- x x ) - pop eax - push eax - push eax - next -; - -code swap ( x y -- y x ) - pop edx - pop eax - push edx - push eax - next -; - -code drop ( x -- ) - pop eax - next -; - -code 0< ( x -- flag ) - pop eax - or eax, eax - mov eax, 0 - jns zless1 - dec eax -zless1: push eax - next -; - -code ?exit ( f -- ) - pop eax - or eax, eax - jz qexit1 - mov esi, [ebp] - lea ebp,[ebp+4] -qexit1: next -; - -code >r ( x -- ) ( R -- x ) - pop ebx - lea ebp,[ebp-4] - mov [ebp], ebx - next -; - -code r> ( R x -- ) ( -- x ) - mov eax,[ebp] - lea ebp, [ebp+4] - push eax - next -; - -code - ( x1 x2 -- x3 ) - pop edx - pop eax - sub eax, edx - push eax - next -; - -code unnest ( -- ) - mov esi,[ebp] - lea ebp,[ebp+4] - next -; - -code lit ( -- ) - lodsd - push eax - next -; diff --git a/preForth/seed b/preForth/seed deleted file mode 100755 index 52ff983..0000000 --- a/preForth/seed +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -stty raw -echo -cat seedForthInteractive.seed hi.forth - | ./seedForth -stty sane - diff --git a/preForth/seedForth-i386.pre b/preForth/seedForth-i386.pre deleted file mode 100644 index 7f603df..0000000 --- a/preForth/seedForth-i386.pre +++ /dev/null @@ -1,545 +0,0 @@ -\ seedForth - seed it, feed it, grow it - i386 (32 bit) ITC flavour uho 2018-04-13 -\ ---------------------------------------------------------------------------------- -\ -\ - registers: -\ EAX, EDX general purpose -\ ESI instruction pointer -\ EBP return stack pointer -\ ESP data stack pointer - -prelude -;;; This is seedForth - a small, potentially interactive Forth, that dynamically -;;; bootstraps from a minimal kernel. -;;; -;;; cat seedForth.seed - | ./seedForth -;;; -;;; .seed-files are in byte-tokenized source code format. -;;; -;;; Use the seedForth tokenizer to convert human readable source code to byte-token form. -; - -prefix -format ELF - -section '.bss' executable writable - - DD 10000 dup(0) -stck: DD 16 dup(0) - - DD 10000 dup(0) -rstck: DD 16 dup(0) - -_dp: DD _start ; dictionary pointer: points to next free location in memory - ; free memory starts at _start - -__hp: DD 0 ; head pointer: index of first unused head -_head: DD 10000 dup (0) - -section '.text' executable writable align 4096 - -public main -extrn putchar -extrn getchar -extrn fflush -extrn exit -extrn mprotect -extrn ioctl -extrn usleep - -macro next { - lodsd - jmp dword [eax] -} - -origin: - -main: cld - mov esp, dword stck - mov ebp, dword rstck - - ; make section writable - push ebp - mov ebp, esp - sub esp, 16 - and esp, 0xfffffff0 - mov dword [esp+8], 7 ; rwx - mov eax, _memtop - sub eax, origin - mov dword [esp+4], eax - mov dword [esp], origin - call mprotect - mov esp, ebp - pop ebp - or eax, eax ; error? - jz main0 - push ebp - mov ebp, esp - push eax - and esp, 0xfffffff0 - ; call __error ; get error code on Mac OS - ; mov eax, [eax] - ; call __errno_location ; get error on Linux - ; mov eax, [eax] - mov [esp], eax - call exit - -main0: mov esi, main1 - next - -main1: DD _cold - DD _bye - -_nest: -_enter: lea ebp, [ebp-4] - mov [ebp], esi - lea esi, [eax+4] - next - -_dodoes: ; ( -- addr ) - lea ebp, [ebp-4] ; push IP - mov [ebp], esi - mov esi,[eax-4] ; set IP -_dovar: ; ( -- addr ) - lea eax,[eax+4] ; to parameter field - push eax - next - -_O = 0 - -; - - -code bye ( -- ) - push ebp - mov ebp, esp - and esp, 0xfffffff0 - mov eax, 0 - mov [esp], eax - call exit -; - -code emit ( c -- ) - pop eax - - push ebp - mov ebp, esp - push eax - and esp, 0xfffffff0 - - mov dword [esp], eax - call putchar - - mov eax, 0 - mov [esp], eax - call fflush ; flush all output streams - - mov esp, ebp - pop ebp - next -; - -code key ( -- c ) - push ebp - mov ebp, esp - and esp, 0xfffffff0 - - call getchar - mov esp, ebp - pop ebp - cmp eax,-1 - jnz key1 - mov eax,4 ; eof: return Ctrl-D -key1: push eax - next -; - -code key? ( -- f ) - push ebp - mov ebp, esp - and esp, 0xfffffff0 - sub esp, 32 - - mov dword [esp], 0 - mov dword [esp+4], 1074030207 ; FIONREAD - lea dword eax, [esp+24] - mov dword [esp+8], eax - - call ioctl - mov dword eax, [esp+24] - - mov esp, ebp - pop ebp - - cmp eax, 0 - jz keyq1 - mov eax, -1 -keyq1: push eax - next -; - -code dup ( x -- x x ) - pop eax - push eax - push eax - next -; - -code swap ( x y -- y x ) - pop edx - pop eax - push edx - push eax - next -; - -code drop ( x -- ) - pop eax - next -; - -code 0< ( x -- flag ) - pop eax - sar eax,31 - push eax - next -; - -code ?exit ( f -- ) \ high level: IF exit THEN - pop eax - or eax, eax - jz qexit1 - mov esi, [ebp] - lea ebp,[ebp+4] -qexit1: next -; - -code >r ( x -- ) ( R -- x ) - pop ebx - lea ebp,[ebp-4] - mov [ebp], ebx - next -; - -code r> ( R x -- ) ( -- x ) - mov eax,[ebp] - lea ebp, [ebp+4] - push eax - next -; - -code - ( x1 x2 -- x3 ) - pop edx - pop eax - sub eax, edx - push eax - next -; - -code or ( x1 x2 -- x3 ) - pop edx - pop eax - or eax, edx - push eax - next -; - -code and ( x1 x2 -- x3 ) - pop edx - pop eax - and eax, edx - push eax - next -; - -pre -_unnest: -; -code exit ( -- ) - mov esi,[ebp] - lea ebp,[ebp+4] - next -; - -code lit ( -- ) - lodsd - push eax - next -; - -code @ ( addr -- x ) - pop eax - mov eax,[eax] - push eax - next -; - -code c@ ( c-addr -- c ) - pop edx - xor eax, eax - mov al,byte [edx] - push eax - next -; - -code ! ( x addr -- ) - pop edx - pop eax - mov dword [edx],eax - next -; - -code c! ( c c-addr -- ) - pop edx - pop eax - mov byte [edx], al - next -; - -\ code invoke ( addr -- ) \ native code: >r ; -code execute ( addr -- ) \ this version uses token numbers as execution tokens and finds their code address via the headers table - pop edx - mov dword eax, [_head+edx*4] - jmp dword [eax] -; - -code branch ( -- ) \ threaded code: r> @ >r ; - lodsd - mov esi,eax - next -; - -code ?branch ( f -- ) \ threaded code: ?exit r> @ >r ; - pop eax - or eax,eax - jz _branchX - lea esi,[esi+4] - next -; - -code depth ( -- n ) - mov eax, stck - sub eax, esp - sar eax,2 - push eax - next -; - -code sp@ ( -- x ) - push esp - next -; - -code sp! ( x -- ) - pop esp - next -; - -code rp@ ( -- x ) - push ebp - next -; - -code rp! ( x -- ) - pop ebp - next -; - -code um* ( u1 u2 -- ud ) - pop edx - pop eax - mul edx - push eax - push edx - next -; - -code um/mod ( ud u1 -- u2 u3 ) - pop ebx - pop edx - pop eax - div ebx - push edx - push eax - next -; - -code usleep ( c -- ) - pop eax - - push ebp - mov ebp, esp - push eax - and esp, 0xfffffff0 - - mov dword [esp], eax - call usleep - - mov esp, ebp - pop ebp - next -; - - -: negate ( n1 -- n2 ) - 0 swap - ; - -: + ( x1 x2 -- x3 ) - negate - ; - -: 0= ( x -- flag ) - 0 swap ?exit drop -1 ; - -: ?dup ( x -- x x | 0 ) - dup 0= ?exit dup ; - -: 2* ( x1 -- x2 ) - dup + ; - -: cells ( x1 -- x2 ) - 2* 2* ; - -: +! ( x addr -- ) - swap >r dup @ r> + swap ! ; - -: hp ( -- addr ) - lit _hp ; - -: h@ ( i -- addr ) - cells lit head + @ ; - -: h! ( x i -- ) - cells lit head + ! ; - -: h, ( x -- ) - hp @ h! 1 hp +! ; - -: here ( -- addr ) - lit dp @ ; - -: allot ( n -- ) - lit dp +! ; - -: , ( x -- ) - here 1 cells allot ! ; - -: c, ( c -- ) - here 1 allot c! ; - -: compile, ( x -- ) - h@ , ; - -\ token are in the range 0 .. 767: -\ 0, 3 .. 255 are single byte tokens -\ 256 .. 511 are double byte tokens of the form 01 xx -\ 511 .. 767 are double byte tokens of the form 02 xx -: token ( -- x ) - key dup 0= ?exit \ 0 -> single byte token - dup 3 - 0< 0= ?exit \ not 1 2 -> single byte token - key couple ; \ double byte token - -: interpreter ( -- ) - token execute tail interpreter ; \ executing exit will leave this loop - -: num ( -- x ) - tail interpreter ; - -: ?lit ( xt -- xt | ) - dup h@ lit num - ?exit drop \ not num token: exit i.e. normal compile action - lit lit , num , \ generate lit x num call puts x on stack - r> drop tail compiler ; - -: compiler ( -- ) - token ?dup 0= ?exit ?lit - compile, tail compiler ; - -: new ( -- xt ) - hp @ here h, lit enter , ; - -: fun ( -- ) - new drop compiler ; - -: couple ( hi lo -- hilo ) - >r 2* 2* 2* 2* 2* 2* 2* 2* r> + ; - -: $lit ( -- addr u ) - r> dup 1 + dup >r swap c@ dup r> + >r ; - -: create ( -- xt ) - 0 , \ dummy does> field - hp @ here h, lit dovar , ; - -: does> ( xt -- ) \ set code field of last defined word - r> swap h@ dup >r 1 cells - ! lit dodoes r> ! -; - -: unused ( -- u ) - lit memtop here - ; - -: cold ( -- ) - \ 's' emit 'e' dup emit emit 'd' emit 10 emit - lit bye h, \ 0 00 code - 0 h, \ 1 01 prefix - 0 h, \ 2 02 prefix - lit emit h, \ 3 03 code - lit key h, \ 4 04 code - lit dup h, \ 5 05 code - lit swap h, \ 6 06 code - lit drop h, \ 7 07 code - lit 0< h, \ 8 08 code - lit ?exit h, \ 9 09 code - lit >r h, \ 10 0A code - lit r> h, \ 11 0B code - lit - h, \ 12 0C code - lit exit h, \ 13 0D code - lit lit h, \ 14 0E code - lit @ h, \ 15 0F code - lit c@ h, \ 16 10 code - lit ! h, \ 17 11 code - lit c! h, \ 18 12 code - lit execute h, \ 19 13 code - lit branch h, \ 20 14 code - lit ?branch h, \ 21 15 code - lit negate h, \ 22 16 - lit + h, \ 23 17 - lit 0= h, \ 24 18 - lit ?dup h, \ 25 19 - lit cells h, \ 26 1A - lit +! h, \ 27 1B - lit h@ h, \ 28 1C - lit h, h, \ 29 1D - lit here h, \ 30 1E - lit allot h, \ 31 1F - lit , h, \ 32 20 - lit c, h, \ 33 21 - lit fun h, \ 34 22 - lit interpreter h, \ 35 23 - lit compiler h, \ 36 24 - lit create h, \ 37 25 - lit does> h, \ 38 26 - lit cold h, \ 39 27 - lit depth h, \ 40 28 code - lit compile, h, \ 41 29 - lit new h, \ 42 2A - lit couple h, \ 43 2B - lit and h, \ 44 2C code - lit or h, \ 45 2D code - lit sp@ h, \ 46 2E code - lit sp! h, \ 47 2F code - lit rp@ h, \ 48 30 code - lit rp! h, \ 49 31 code - lit $lit h, \ 50 32 - lit num h, \ 51 33 - lit um* h, \ 52 34 code - lit um/mod h, \ 53 35 code - lit unused h, \ 54 36 - lit key? h, \ 55 37 - lit token h, \ 56 38 - lit usleep h, \ 57 39 code - lit hp h, \ 58 40 - interpreter bye ; - -pre - _start: DB 43 - DD 100000 dup (0) - _memtop: DD 0 -; diff --git a/preForth/seedForth-tokenizer.fs b/preForth/seedForth-tokenizer.fs deleted file mode 100644 index 3cbd559..0000000 --- a/preForth/seedForth-tokenizer.fs +++ /dev/null @@ -1,280 +0,0 @@ -\ Another seedForth tokenizer 2019-10-18 - -: fnv1a ( c-addr u -- x ) - 2166136261 >r - BEGIN dup WHILE over c@ r> xor 16777619 um* drop $FFFFFFFF and >r 1 /string REPEAT 2drop r> ; - -15 Constant #hashbits -1 #hashbits lshift Constant #hashsize - -#hashbits 16 < [IF] - - #hashsize 1 - Constant tinymask - : fold ( x1 -- x2 ) dup #hashbits rshift xor tinymask and ; - -[ELSE] \ #hashbits has 16 bits or more - - #hashsize 1 - Constant mask - : fold ( x1 -- x2 ) dup #hashbits rshift swap mask and xor ; - -[THEN] - -Create tokens #hashsize cells allot tokens #hashsize cells 0 fill - -: 'token ( c-addr u -- addr ) - fnv1a fold cells tokens + ; - -: token@ ( c-addr u -- x ) 'token @ ; - -: ?token ( c-addr u -- x ) - 2dup 'token dup @ - IF - >r cr type ." collides with another token " - cr source type cr r> @ name-see abort - THEN nip nip ; - -VARIABLE OUTFILE - -: submit ( c -- ) - PAD C! PAD 1 OUTFILE @ WRITE-FILE THROW ; - -: submit-token ( x -- ) - dup 255 > IF dup 8 rshift SUBMIT THEN SUBMIT ; - -: ( -- c-addr u ) bl word count ; - -Variable #tokens 0 #tokens ! -: Token ( -- ) - :noname - #tokens @ postpone LITERAL postpone SUBMIT-TOKEN postpone ; - - cr #tokens @ 3 .r space 2dup type \ tell user about used tokens - ?token ! 1 #tokens +! ; - -: Macro ( -- ) - ?token :noname $FEED ; - -: end-macro ( 'hash colon-sys -- ) - $FEED - Abort" end-macro without corresponding Macro" - postpone ; ( 'hash xt ) swap ! ; immediate - -: seed ( i*x -- j*x ) - token@ dup 0= Abort" is undefined" postpone LITERAL postpone EXECUTE ; immediate - - -( 0 $00 ) Token bye Token prefix1 Token prefix2 Token emit -( 4 $04 ) Token key Token dup Token swap Token drop -( 8 $08 ) Token 0< Token ?exit Token >r Token r> -( 12 $0C ) Token - Token exit Token lit Token @ -( 16 $10 ) Token c@ Token ! Token c! Token execute -( 20 $14 ) Token branch Token ?branch Token negate Token + -( 24 $18 ) Token 0= Token ?dup Token cells Token +! -( 28 $1C ) Token h@ Token h, Token here Token allot -( 32 $20 ) Token , Token c, Token fun Token interpreter -( 36 $24 ) Token compiler Token create Token does> Token cold -( 40 $28 ) Token depth Token compile, Token new Token couple -( 44 $2C ) Token and Token or Token sp@ Token sp! -( 48 $30 ) Token rp@ Token rp! Token $lit Token num -( 52 $34 ) Token um* Token um/mod Token unused Token key? -( 56 $38 ) Token token Token usleep Token hp - -\ generate token sequences for numbers - -: seed-byte ( c -- ) - seed key SUBMIT ; - -: seed-number ( x -- ) \ x is placed in the token file. Handle also negative and large numbers - dup 0< IF 0 seed-byte negate recurse seed - EXIT THEN - dup $FF > IF dup 8 rshift recurse $FF and seed-byte seed couple EXIT THEN - seed-byte ; - -: char-lit? ( c-addr u -- x flag ) - 3 - IF drop 0 false EXIT THEN - dup c@ [char] ' - IF drop 0 false EXIT THEN - dup 2 chars + c@ [char] ' - IF drop 0 false EXIT THEN - char+ c@ true ; - -: process-digit? ( x c -- x' flag ) - '0' - dup 10 u< IF swap 10 * + true EXIT THEN drop false ; - -: number? ( c-addr u -- x flag ) - dup 0= IF 2drop 0 false EXIT THEN - over c@ '-' = dup >r IF 1 /string THEN - >r >r 0 r> r> bounds - ?DO ( x ) - I c@ process-digit? 0= IF unloop r> drop false EXIT THEN ( x d ) - LOOP - r> IF negate THEN true ; - -: seed-name ( c-addr u -- ) - 2dup token@ dup IF nip nip execute EXIT THEN drop - 2dup char-lit? IF nip nip seed num seed-number seed exit EXIT THEN drop - 2dup number? IF nip nip seed num seed-number seed exit EXIT THEN drop - cr type ." not found" abort ; - -: seed-line ( -- ) - BEGIN dup WHILE seed-name REPEAT 2drop ; - -: seed-file ( -- ) - BEGIN refill WHILE seed-line REPEAT ; - -: PROGRAM ( -- ) - R/W CREATE-FILE THROW OUTFILE ! - seed-file ; - -Macro END ( -- ) - .S CR 0 SUBMIT OUTFILE @ CLOSE-FILE THROW BYE end-macro - -Macro [ ( -- ) seed bye end-macro \ bye -Macro ] ( -- ) seed compiler end-macro \ compiler - -Macro : ( -- ) seed fun Token end-macro -Macro ; ( -- ) seed exit seed [ end-macro - -\ generate token sequences for strings - -: seed-stack-string ( c-addr u -- ) - dup >r - BEGIN dup WHILE ( c-addr u ) - over c@ seed-number 1 /string - REPEAT ( c-addr u ) - 2drop - r> seed-number -; - -: seed-string ( c-addr u -- ) - dup seed-number seed c, - BEGIN dup WHILE - >r dup char+ swap c@ seed-number seed c, - r> 1- - REPEAT 2drop -; - -Macro ," ( ccc" -- ) [char] " parse seed-string end-macro - -: $, ( c-addr u -- ) - seed $lit - seed [ - seed-string - seed ] -; - -Macro $name ( -- ) - seed-stack-string -end-macro - -Macro $( \ ( ccc) -- ) - [char] ) parse seed-stack-string -end-macro - -Macro s" ( ccc" -- ) \ only in compile mode - [char] " parse $, -end-macro - - -\ Control structure macros -: forward ( -- ) - seed [ - seed here - 0 seed-number seed , - seed ] -; - -: back ( -- ) - seed [ - seed , - seed ] -; - - -Macro AHEAD ( -- addr ) - seed branch forward -end-macro - -Macro IF ( -- addr ) - seed ?branch forward -end-macro - - -Macro THEN ( addr -- ) - seed [ - seed here - seed swap - seed ! - seed ] -end-macro - -Macro ELSE ( addr1 -- addr2 ) - seed branch forward - seed [ - seed swap - seed ] - seed THEN -end-macro - -Macro BEGIN ( -- addr ) - seed [ - seed here - seed ] -end-macro - -Macro AGAIN ( addr -- ) - seed branch back -end-macro - -Macro UNTIL ( addr -- ) - seed ?branch back -end-macro - -Macro WHILE ( addr1 -- addr2 addr1 ) - seed IF - seed [ - seed swap - seed ] -end-macro - -Macro REPEAT ( addr -- ) - seed AGAIN - seed THEN -end-macro - -Macro ( ( -- ) - postpone ( -end-macro - -Macro \ ( -- ) - postpone \ -end-macro - -Macro Definer ( -- ) - Macro - postpone Token - #tokens @ 1 #tokens +! - postpone Literal - postpone SUBMIT-TOKEN - seed fun - postpone end-macro -end-macro - -\ for defining Macros later in seedForth -Macro Macro ( -- ) - Macro -end-macro - -Macro end-macro - postpone end-macro -end-macro - -Macro seed ( -- ) - postpone seed -end-macro - -Macro save-#tokens - postpone #tokens - postpone @ -end-macro - -Macro restore-#tokens - postpone #tokens - postpone ! -end-macro \ No newline at end of file diff --git a/z80/Makefile b/z80/Makefile new file mode 100644 index 0000000..60831cd --- /dev/null +++ b/z80/Makefile @@ -0,0 +1,201 @@ +# Makefile for preForth and seedForth +# +# make bootstrap should produce two identical files: preForth1.$(ASM) and preForth.$(ASM) + +# Set HOSTFORTH to the Forth system that generates the initial preForth +# ------------------------------------------------------------------------ +HOSTFORTH=gforth +# HOSTFORTH=sf # SwiftForth >3.7 +# ------------------------------------------------------------------------ + +ASZ80=../asxv5pxx/asxmak/linux/exe/asz80 +ASLINK=../asxv5pxx/asxmak/linux/exe/aslink + +# need to install intelhex package in Python first: +# pip3 install --user intelhex +HEX2BIN=python3 $(HOME)/.local/bin/hex2bin.py + +EMU_Z80=../emu_z80/emu_z80 + +.PHONY: all +all: \ +preForthDemo.bin \ +preForth.bin \ +seedForth.bin \ +seedForthDemo.seed \ +seedForthBoot.seed \ +seedForthInteractive.seed + +.PHONY: test +test: runseedforthdemo runseedforthinteractive + +.PHONY: runseedforthdemo +runseedforthdemo: seedForth seedForthDemo.seed + ./seedForth seedForthDemo.seed + +.PHONY: runseedfortinteractive +runseedforthinteractive: seedForth seedForthInteractive.seed + ./seed + +UNIXFLAVOUR=$(shell uname -s) +ASM=asm + +# build preForthDemo via the host Forth first, so that it +# can be used for basic debugging of the preForth runtime +preForthDemo.$(ASM): \ +../common/preForth-bootstrap.fs \ +../common/preForth-cold.fs \ +preForth-z80-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth-z80-backend.pre \ +../common/preForth.pre \ +../common/preForthDemo.pre + cat \ +preForth-z80-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +../common/preForthDemo.pre \ +|$(HOSTFORTH) \ +../common/preForth-bootstrap.fs \ +../common/preForth-rts-nonstandard.pre \ +preForth-z80-backend.pre \ +../common/preForth.pre \ +../common/preForth-cold.fs \ +>$@ + +preForth.$(ASM): \ +../common/preForth-bootstrap.fs \ +../common/preForth-cold.fs \ +preForth-z80-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth-z80-backend.pre \ +../common/preForth.pre + cat \ +preForth-z80-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth-z80-backend.pre \ +../common/preForth.pre \ +|$(HOSTFORTH) \ +../common/preForth-bootstrap.fs \ +../common/preForth-rts-nonstandard.pre \ +preForth-z80-backend.pre \ +../common/preForth.pre \ +../common/preForth-cold.fs \ +>$@ + +%.$(ASM): \ +%.pre \ +preForth-z80-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth.bin + $(EMU_Z80) preForth.bin \ +preForth-z80-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +$< \ +>$@ + +%.bin: %.$(ASM) + $(ASZ80) -l -o $< + $(ASLINK) -n -m -u -i $(<:.$(ASM)=.ihx) $(<:.$(ASM)=.rel) + $(HEX2BIN) $(<:.$(ASM)=.ihx) $@ + +# run preForth on its own source code to perform a bootstrap +# should produce identical results +bootstrap: \ +preForth-z80-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth-z80-backend.pre \ +../common/preForth.pre \ +preForth.bin \ +preForth.$(ASM) + $(EMU_Z80) preForth.bin \ +preForth-z80-rts.pre \ +../common/preForth-rts-nonstandard.pre \ +../common/preForth-rts.pre \ +preForth-z80-backend.pre \ +../common/preForth.pre \ +>preForth1.$(ASM) + cmp preForth.$(ASM) preForth1.$(ASM) + +# preForth connected to stdin - output to stdout +.PHONY: visible-bootstrap +visible-bootstrap: preForth.bin preForth-z80-backend.pre ../common/preForth.pre + $(EMU_Z80) preForth.bin preForth-z80-backend.pre ../common/preForth.pre + +# ------------------------------------------------------------------------ +# Docker support (for Linux version) +# ------------------------------------------------------------------------ +# create a linux image based on Dockerfile +.PHONY: docker-image +docker-image: Dockerfile + docker build -t preforth . + +# run the docker image +.PHONY: run +rundocker: docker-image + docker run -i -t --rm preforth /preForth/seed +# ------------------------------------------------------------------------ + +# ------------------------------------------------------------------------ +# seedForth +# ------------------------------------------------------------------------ +seedForth.$(ASM): \ +seedForth-z80-header.pre \ +preForth-z80-rts.pre \ +seedForth-z80-rts.pre \ +seedForth-z80.pre \ +../common/seedForth16bit.pre \ +../common/seedForth.pre \ +preForth.bin + $(EMU_Z80) preForth.bin \ +seedForth-z80-header.pre \ +preForth-z80-rts.pre \ +seedForth-z80-rts.pre \ +seedForth-z80.pre \ +../common/seedForth16bit.pre \ +../common/seedForth.pre \ +>seedForth.$(ASM) + +# a little ugly, because gforth insists upon echoing anything read from +# stdin, and also does not allow parsing words to cross a file boundary, +# so we will concatenate the tokenizer and source into a *.fs file first +seedForthDemo.seed: \ +../common/crc10.forth \ +../common/../common/seedForth-tokenizer.fs \ +../common/seedForthDemo.seedsource \ +seedForthDemoz80.seedsource + cat $^ >__temp__.fs + $(HOSTFORTH) __temp__.fs -e bye >$@ + rm __temp__.fs + +seedForthBoot.seed: \ +../common/crc10.forth \ +../common/../common/seedForth-tokenizer.fs \ +../common/seedForthRuntime16bit.seedsource \ +../common/seedForthRuntime.seedsource \ +seedForthRuntimez80.seedsource \ +../common/seedForthBoot.seedsource + cat $^ >__temp__.fs + $(HOSTFORTH) __temp__.fs -e bye >$@ + rm __temp__.fs + +seedForthInteractive.seed: \ +../common/crc10.forth \ +../common/../common/seedForth-tokenizer.fs \ +../common/seedForthRuntime16bit.seedsource \ +../common/seedForthRuntime.seedsource \ +seedForthRuntimez80.seedsource \ +../common/seedForthInteractive.seedsource + cat $^ >__temp__.fs + $(HOSTFORTH) __temp__.fs -e bye >$@ + rm __temp__.fs + +.PHONY: clean +clean: + rm -f *.$(ASM) *.o *.seed preForthDemo preForth seedForth __temp__.fs diff --git a/z80/preForth-z80-backend.pre b/z80/preForth-z80-backend.pre new file mode 100644 index 0000000..4cac9a7 --- /dev/null +++ b/z80/preForth-z80-backend.pre @@ -0,0 +1,158 @@ +\ -------------------------- +\ preForth backend for z80 (16 bit) as-z80 +\ -------------------------- + +\ alter substitutes non-letter characters by upper case letters (to aid assemblers to deal with labels). +: replace ( c -- c d ) + 'A' swap 39 case? ?exit nip + 'B' swap '\' case? ?exit nip + 'C' swap ':' case? ?exit nip + 'D' swap '.' case? ?exit nip + 'E' swap '=' case? ?exit nip + 'F' swap '[' case? ?exit nip + 'G' swap '>' case? ?exit nip + 'H' swap ']' case? ?exit nip + 'I' swap '1' case? ?exit nip + 'J' swap '2' case? ?exit nip + 'K' swap '/' case? ?exit nip + 'L' swap '<' case? ?exit nip + 'M' swap '-' case? ?exit nip + 'N' swap '#' case? ?exit nip + 'O' swap '0' case? ?exit nip + 'P' swap '+' case? ?exit nip + 'Q' swap '?' case? ?exit nip + 'R' swap '"' case? ?exit nip + 'S' swap '!' case? ?exit nip + 'T' swap '*' case? ?exit nip + 'U' swap '(' case? ?exit nip + 'V' swap '|' case? ?exit nip + 'W' swap ',' case? ?exit nip + 'X' swap '@' case? ?exit nip \ may clash with 'X' suffix for machine code + 'Y' swap ')' case? ?exit nip + 'Z' swap ';' case? ?exit nip +; + +\ alter substitutes all non-letter characters by upper case letters. +: alter ( S1 -- S2 ) + '_' 1 rot ?dup 0= ?exit nip nip + \ dup 0= ?exit + swap >r 1- alter r> replace swap 1+ ; + +\ ------------ +\ output words +\ ------------ +\ Output is done by emit. +\ We define convenience words of the form ."xxx" to output xxx (maybe tabbed) + +: ."dw" ( -- ) + tab '.' emit 'd' emit 'w' emit tab ; + +: ."db" ( -- ) + tab '.' emit 'd' emit 'b' emit tab ; + +: ."call" ( -- ) + tab 'c' emit 'a' emit 'l' emit 'l' emit tab ; + +: ."nest" ( -- ) + 'n' 'e' 's' 't' 4 alter show ; + +: ."unnest" ( -- ) + 'u' 'n' 'n' 'e' 's' 't' 6 alter show ; + +: ."lit" ( -- ) + 'l' 'i' 't' 3 alter show ; + +\ ------------ +\ Compiling words +\ ------------ + +\ ,string compiles the topmost string as a sequence of numeric DB values. +: ,string ( S -- ) + ?dup 0= ?exit + dup roll ."db" u. cr \ 1st char + 1- ,string ; + +\ reproduce a verbatim line +: ,line ( x1 ...cn n -- ) + show ; + +\ compile a reference to an invoked word +: ,word ( S -- ) + ."dw" alter show cr ; + +\ compile reference to nest primitive +: ,nest ( -- ) + ."call" ."nest" cr ; + +\ compile reference to unnest primitive +: ,unnest ( -- ) + ."dw" ."unnest" cr cr ; + +\ compile signed number +: ,n ( n -- ) + ."dw" . cr ; + +\ compile unsigned number +: ,u ( u -- ) + ."dw" u. cr ; + +\ compile literal +: ,_lit ( S -- ) + ."dw" ."lit" cr ,word ; + +\ compile literal +: ,lit ( x -- ) + ."dw" ."lit" cr ,n ; + +\ output string as comment +: ,comment ( S -- ) + tab ';' emit space show cr ; + +\ create a new symbolic label +\ if label is 6 characters or less, stay on same line for following code +: label ( S -- ) + alter dup >r show ':' emit r> 7 - 0< ?exit cr ; + +\ body calculates the name of the body from a token +: body ( S1 -- S2 ) + 'X' swap 1+ ; + +\ ,codefield compiles the code field of primitive +\ for z80, for asm words there is only a body, so omit the body label +\ : ,codefield ( S -- ) +\ \ body _dup ,word label ; +\ body label ; + +: ,code ( S -- ) + _dup label + ; \ ,codefield ; + +: ,end-code ( -- ) + cr ; + +\ ----------------------------------- +\ tail call optimization tail word ; -> [ ' word >body ] literal >r ; + +: bodylabel ( S -- ) + body label ; + +\ ,tail compiles a tail call +: ,tail ( S -- ) + body ,_lit + '>' 'r' 2 ,word ; + +\ : ."done" ( -- ) +\ ';' emit space 'd' emit 'o' emit 'n' emit 'e' emit ; +\ +\ : ."last:" ( -- ) +\ ';' emit space 'l' emit 'a' emit 's' emit 't' emit ':' emit space ; + +: ,end ( S -- ) + \ cr ."last:" alter show + \ cr ."done" cr + ; + +\ \ create a new header with given name S2 and flags - do nothing +\ : header ( S1 S2 flags -- S3 S2 ) +\ drop ; + diff --git a/z80/preForth-z80-rts.pre b/z80/preForth-z80-rts.pre new file mode 100644 index 0000000..6eeb06a --- /dev/null +++ b/z80/preForth-z80-rts.pre @@ -0,0 +1,188 @@ +\ preForth runtime system - z80 (16 bit) dependent part +\ -------------------------- +\ +\ - registers: +\ HL, DE general purpose +\ BC instruction pointer +\ IX return stack pointer +\ SP data stack pointer + +pre +;;; This is a preForth generated file using preForth-z80-backend. +;;; Only modify it, if you know what you are doing. + +DATA_STACK_SIZE = 0x1000 +RETURN_STACK_SIZE = 0x1000 + +STDIN_DATA = 0 +STDOUT_DATA = 1 +STDERR_DATA = 2 +STDIN_STATUS = 3 +STDOUT_STATUS = 4 +STDERR_STATUS = 5 +USLEEP_LO = 6 +USLEEP_HI = 7 +SYS_EXIT = 8 + +EXIT_SUCCESS = 0 +EXIT_FAILURE = 1 + + ; define load order + .area text + .area data + .area bss + + .area text + +main: ld ix,return_stack + RETURN_STACK_SIZE + ld sp,data_stack + DATA_STACK_SIZE + ld bc,main1 + jp next + +main1: .dw _cold + .dw _bye + +; + +code bye ( -- ) + ld a,EXIT_SUCCESS + out (SYS_EXIT),a +; + +code emit ( c -- ) + pop hl + ld a,l + out (STDOUT_DATA),a + jr next +; + +code eemit ( c -- ) + pop hl + ld a,l + out (STDERR_DATA),a + jr next +; + +code key ( -- c ) + in a,(STDIN_DATA) + ld l,a + ld h,0 + push hl + jr next +; + +code dup ( x -- x x ) + pop hl + push hl + push hl + jr next +; + +code swap ( x y -- y x ) + pop de + pop hl + push de + push hl + jr next +; + +code drop ( x -- ) + pop hl + jr next +; + +code 0< ( x -- flag ) + pop hl + add hl,hl + ld hl,0 + jr nc,zless1 + dec hl +zless1: push hl + jr next +; + +code ?exit ( f -- ) + pop hl + ld a,l + or h + jr z,next + ; fall into unnest +; + +code unnest ( -- ) + ld c,(ix) + inc ix + ld b,(ix) + inc ix + jr next +; + +code >r ( x -- ) ( R -- x ) + pop hl + dec ix + ld (ix),h + dec ix + ld (ix),l + jr next +; + +code r> ( R x -- ) ( -- x ) + ld l,(ix) + inc ix + ld h,(ix) + inc ix + push hl + jr next +; + +code - ( x1 x2 -- x3 ) + pop de + pop hl + or a + sbc hl,de + push hl + jr next +; + +\ put this in middle of the primitives to make it reachable by jr +code nest ( -- ) + dec ix + ld (ix),b + dec ix + ld (ix),c + pop bc +next: ld a,(bc) + ld l,a + inc bc + ld a,(bc) + ld h,a + inc bc + jp (hl) +; + +code lit ( -- ) + ld a,(bc) + ld l,a + inc bc + ld a,(bc) + ld h,a + inc bc + push hl + jr next +; + +\ we want the text section to be first and bss last (for linkers that output +\ sections in definition order), so it would be good to have the bss section +\ be output by the ",end" hook, but at present we cannot call "pre" from a +\ defined word, so we make do by switching to bss and then back to text again +pre + .area bss + +return_stack: + .ds RETURN_STACK_SIZE +data_stack: + .ds DATA_STACK_SIZE + + .area text + +; diff --git a/z80/seed b/z80/seed new file mode 100755 index 0000000..95defad --- /dev/null +++ b/z80/seed @@ -0,0 +1,4 @@ +#!/bin/bash +stty raw -echo +../emu_z80/emu_z80 seedForth.bin seedForthInteractive.seed ../common/runtime.forth ../common/hi16bit.forth ../common/hi.forth - +stty sane diff --git a/z80/seedForth-tokenizer b/z80/seedForth-tokenizer new file mode 100755 index 0000000..645a590 --- /dev/null +++ b/z80/seedForth-tokenizer @@ -0,0 +1,2 @@ +#!/bin/sh +../emu_z80/emu_z80 seedForth.bin seedForthBoot.seed ../common/runtime.forth ../common/crc10.forth ../common/seedForth-tokenizer.fs - diff --git a/z80/seedForth-z80-header.pre b/z80/seedForth-z80-header.pre new file mode 100644 index 0000000..a0528ef --- /dev/null +++ b/z80/seedForth-z80-header.pre @@ -0,0 +1,22 @@ +\ seedForth: machine dependent header portion + +\ this contains only definitions we want at the top of the file +\ it is then followed by preForth-z80-rts.pre (primitive asm words) +\ and then by seedForth-z80.pre (additional primitive asm words) +\ and then by seedForth.pre (high level words and the interpreter) + +pre +;;; This is seedForth - a small potentially interactive Forth, that dynamically +;;; bootstraps from a minimal kernel. +;;; +;;; cat seedForth.seed - | ../z80_emu/z80_emu seedForth.bin +;;; +;;; .seed files are in byte-tokenized source code format. +;;; +;;; Use the seedForth tokenizer to convert human readable source code to +;;; byte-token form. + +HEAD_SIZE = 4000 +MEM_SIZE = 40000 + +; diff --git a/z80/seedForth-z80-rts.pre b/z80/seedForth-z80-rts.pre new file mode 100644 index 0000000..75b3eb0 --- /dev/null +++ b/z80/seedForth-z80-rts.pre @@ -0,0 +1,330 @@ +\ seedForth: machine dependent portion + +\ interpreter and basic asm primitives are taken from preForth-z80-rts.pre, +\ and then this file defines additional primitives (arithmetic, memory, etc) + +\ aliases for the user-visible versions of some internal routines +pre +_enter = _nest +_exit = _unnest +; + +\ note: we arrive at _dodoes by a sequence of 2 calls, the return +\ address stacked by first call points to some instance data, and +\ the return address stacked by second call (to _dodoes) points to +\ high level forth code which is going to operate on that instance +\ data -- we simply leave the instance data's address stacked for +\ the high level forth code and then "execute" the high level forth +\ code, which means that _dodoes is the same as _enter in our case +pre +_dodoes = _nest +; + +\ note: similarly, arriving at _dovar we just leave address stacked +pre +_dovar = next +; + +code key? ( -- f ) + in a,(STDIN_STATUS) + or a + jr z,1$ + ld a,0xff +1$: ld l,a + ld h,a + push hl + jr next +; + +code or ( x1 x2 -- x3 ) + pop de + pop hl + ld a,l + or e + ld l,a + ld a,h + or d + ld h,a + push hl + jr next +; + +code and ( x1 x2 -- x3 ) + pop de + pop hl + ld a,l + and e + ld l,a + ld a,h + and d + ld h,a + push hl + jr next +; + +code @ ( addr -- x ) + pop hl + ld e,(hl) + inc hl + ld d,(hl) + push de + jr next +; + +code c@ ( c-addr -- c ) + pop hl + ld e,(hl) + ld d,0 + push de + jr next +; + +code ! ( x addr -- ) + pop hl + pop de + ld (hl),e + inc hl + ld (hl),d + jr next +; + +code c! ( c c-addr -- ) + pop hl + pop de + ld (hl),e + jr next +; + +code execute ( xt -- ) + ret +; + +code branch ( -- ) \ threaded code: r> @ >r ; + ld l,c + ld h,b + ld c,(hl) + inc hl + ld b,(hl) + jr next +; + +\ is this misleading? I would have thought ?branch means branch on nonzero +code ?branch ( f -- ) \ threaded code: ?exit r> @ >r ; + pop hl + ld a,l + or h + jr z,_branch + inc bc + inc bc + jr next +; + +code depth ( -- n ) + ld hl,data_stack + DATA_STACK_SIZE + or a + sbc hl,sp ; should leave cf = 0 + rr h + rr l + push hl + jr next1 +; + +code sp@ ( -- x ) + ld hl,0 + add hl,sp + push hl + jr next1 +; + +code sp! ( x -- ) + pop hl + ld sp,hl + jr next1 +; + +code rp@ ( -- x ) + push ix + jr next1 +; + +code rp! ( x -- ) + pop ix + jr next1 +; + +code um* ( u1 u2 -- ud ) + exx ; preserve bc + + pop de ; pop u2 + pop bc ; pop u1 +; ld l,c +; ld h,b +; call print_hexw +; ld a,'* +; call print_char +; ld l,e +; ld h,d +; call print_hexw +; ld a,'= +; call print_char + + ld hl,0 + ld a,b + ld b,16 + ; cf does not matter here (shift in a random bit that isn't used) + rra + rr c +umul_loop: + jr nc,umul_skip + add hl,de +umul_skip: + rr h + rr l + rra + rr c + djnz umul_loop + ld b,a +; push hl +; call print_hexw +; ld l,c +; ld h,b +; call print_hexw +; pop hl +; ld a,0xa +; call print_char + + push bc ; push ud lo + push hl ; push ud hi + + exx + ;jr next1 + +next1: ld a,(bc) + ld l,a + inc bc + ld a,(bc) + ld h,a + inc bc + jp (hl) +; + +code um/mod ( ud u1 -- u2 u3 ) + exx ; preserve bc + + pop bc ; pop u1 + pop hl ; pop ud hi + pop de ; pop ud lo +; push hl +; call print_hexw +; ld l,e +; ld h,d +; call print_hexw +; ld a,'/ +; call print_char +; ld l,c +; ld h,b +; call print_hexw +; pop hl +; ld a,'= +; call print_char + + ld a,16 + ; cf does not matter here (shift in a random bit that isn't used) + jr udiv_loop +udiv_test: + ; shift left has not overflowed, try to subtract bc, leaves cf=0 + ; only if subtraction went, record complement of cf in quotient + sbc hl,bc + jr nc,udiv_goes + add hl,bc ; preserves cf +udiv_goes: + ccf + dec a + jr z,udiv_done +udiv_loop: + ex de,hl + adc hl,hl + ex de,hl + adc hl,hl + jr nc,udiv_test + ; shift left has overflowed, so we can always subtract bc, always + ; leaves cf=1 to indicate subtraction went, record cf in quotient + or a + sbc hl,bc + dec a + jr nz,udiv_loop +udiv_done: + ex de,hl + adc hl,hl ; record final quotient bit +; push hl +; call print_hexw +; ld a,'r +; call print_char +; ld l,e +; ld h,d +; call print_hexw +; pop hl +; ld a,0xa +; call print_char + + push de ; push u2 (remainder) + push hl ; push u1 (quotient) + + exx + jr next1 +; + +code usleep ( c -- ) + pop hl + ld a,l + out (USLEEP_LO),a + ld a,h + out (USLEEP_HI),a + jr next1 +; + +\ we want the text section to be first and bss last (for linkers that output +\ sections in definition order), so it would be good to have the bss section +\ be output by the ",end" hook, but at present we cannot call "pre" from a +\ defined word, so we make do by switching to bss and then back to text again +pre +;print_hexw: +; ld a,h +; call print_hexb +; ld a,l +;print_hexb: +; push af +; rrca +; rrca +; rrca +; rrca +; call print_hexn +; pop af +;print_hexn: +; and 0xf +; add a,'0 +; cp '0 + 10 +; jr c,print_char +; add a,'a - '0 - 10 +;print_char: +; out (STDERR_DATA),a +; ret + + .area data + + ; dictionary pointer: points to next free location in memory +_dp: .dw _mem + + .area bss + + ; head pointer: index of first unused head +__hp: .ds 2 +_head: .ds HEAD_SIZE*2 + + ; free memory starts at _mem +_mem: .ds MEM_SIZE +_memtop: + + .area text + +; diff --git a/z80/seedForth-z80.pre b/z80/seedForth-z80.pre new file mode 100644 index 0000000..3383a3b --- /dev/null +++ b/z80/seedForth-z80.pre @@ -0,0 +1,31 @@ +\ seedForth: less machine dependent portion +\ allows us to adjust things for direct threaded vs indirect threaded + +\ insert "call _dodoes" after each "does>" token +: ?does> ( xt -- xt | ) + dup lit does> - ?exit \ not does> token: exit i.e. normal compile action + , 205 c, lit dodoes , \ generate word of does> and instruction of call + r> drop tail compiler ; + +: compiler ( -- ) + token h@ + ?eot + ?lit + ?does> + , tail compiler ; + +\ for z80 dtc implementation, compile "call _enter" before high level code +: new ( -- xt ) + here 205 c, lit enter , ; + +\ for z80 dtc implementation, compile "call _dovar" before data field of new +\ word, the "_dovar" will be changed the address of "call _dodoes" if needed +: create ( -- xt ) + here 205 c, lit dovar , ; + +\ for does> we do not execute the remainder of the routine, instead we pop +\ the return stack and plug the resulting number into the word being compiled, +\ so that this word will execute the remainder of the routine when invoked +\ (and note remainder of the routine has been prefixed with a "call _dodoes") +: does> ( xt -- ) \ replace "_dovar" in "call _dovar" with return stack addr + r> swap 1 + ! ; diff --git a/z80/seedForthDemoz80.seedsource b/z80/seedForthDemoz80.seedsource new file mode 100644 index 0000000..fffe0eb --- /dev/null +++ b/z80/seedForthDemoz80.seedsource @@ -0,0 +1,15 @@ +\ machine dependent part of seedForthDemo.seedsource +\ allows us to adjust things for direct threaded vs indirect threaded + +\ we must index past the "call" instruction +: >body ( xt -- body ) 3 + ; + +: is ( xt -- ) ' >body ! ; + +Defer d1 +' ten is d1 +t{ d1 d1 d1 -> ten ten ten }t +' five is d1 +t{ d1 d1 d1 -> five five five }t + +done bye diff --git a/z80/seedForthRuntimez80.seedsource b/z80/seedForthRuntimez80.seedsource new file mode 100644 index 0000000..17f6515 --- /dev/null +++ b/z80/seedForthRuntimez80.seedsource @@ -0,0 +1,22 @@ +\ machine dependent part of seedForthRuntime.seedsource +\ allows us to adjust things for direct threaded vs indirect threaded + +\ we must index past the "call" instruction +: >body ( xt -- body ) + 3 + ; +' >body has-header >body + +: is ( xt -- ) \ only interactive + ' >body ! ; +' is has-header is + +\ these could not be done until here +' key is getkey +' noop is .status + +\ insert "call _dodoes" after each "does>" token +: (Does>) ( -- ) + [ ' last-xt ] Literal , + [ ' does> ] Literal , + 205 c, [ ' dodoes ] Literal , ; +' (Does>) has-header Does> immediate