corrected example
[rrq/rrqforth.git] / machine.asm
index ddfb9711fe18abd89b514d3ccffd2989a18140c8..86b1e7f1aac47f5276c6a3019dcb917b02f9febf 100644 (file)
 ;;; clobbers rdi rsi rdx rcx r8 r9 r11
 ;;; rax = syscall id
 ;;;
-;;; function calling
 
+;;; ######################################################################
 
+;;; ============================================================
+;;; FORTH machine model
+;;; rsp = data stack pointer
+;;; rbp = return stack pointer
+;;; rsi = instruction pointer
+
+;;; ========================================
+;;; The pushr macro pushes x onto the return stack
+;;; The popr macro pops x from the return stack
+macro pushr x {
+       sub rbp, 8
+       mov [rbp], x
+}
+
+macro popr x {
+       mov x, [rbp]
+       add rbp, 8
+}
+
+;;; ========================================
+;;; The next macro "moves" execution to the next FORTH instruction,
+;;; using rsi as instruction pointer. It points to the doer field of a
+;;; word, which points to the assembly code that implements the
+;;; execution effect of the word. That doer code is entered with rsi
+;;; referring to the subsequent address in the colling word, and rax
+;;; referring to the doer field of the called word.
+
+macro next {
+       lodsq                   ; mov rax, [rsi] + add rsi,8
+       call p_calltrace_DFA
+       jmp qword [rax]         ; goto code of that FORTH word (64 bit jump)
+}
+
+;;; ========================================
+;;; The FORTH macro transitions to inline FORTH execution.
+macro FORTH {
+       local forthcode
+       mov rsi,forthcode
+       next
+       ;; align 8
+forthcode:
+}
+
+;;; ========================================
+;;; The ENDFORTH macro transitions back to inline assembler after FORTH
+
+macro ENDFORTH {
+       dq inline_code
+}
+
+;;; ========================================
+;;; The DOFORTH lays out a single FORTH call
+
+macro DOFORTH [label] {
+common
+       FORTH
+forward
+       dq label
+common
+       ENDFORTH
+}
+
+;;; ========================================
+;;; Macro WORD starts a FORTH word definition in this code.
+;;; The layout of a word is as follows:
+;;; TFA: [8 bytes] pointer to previous word in the word list
+;;;      [8 bytes] pointer to the word's CFA
+;;;      [8 bytes] a flags field
+;;;      [8 bytes] the length of the word's pname
+;;;      [varying] the word's pname
+;;;      [1 byte]  NUL -- making an asciiz of the pname
+;;;      ;;[? bytes] 0-7 bytes for address alignment to [disabled]
+;;;      [8 bytes] pointer to the word's TFA
+;;; OFF: [8 bytes] the DOES offset for the word
+;;; CFA: [8 bytes] pointer to the word's "doer" code
+;;; DFA: [? bytes] the word's data field
+
+IMMEDIATE = 1          ; optional flag (symbol)
+
+macro WORD label, name, doer, flags, previous, offset {
+       local pname
+       ;; align 8
+label#_TFA:
+       ;; TFA
+       if previous eq
+           dq previous_word
+       else
+           dq previous
+       end if
+       previous_word = label#_TFA
+       ;; PFA
+label#_pCFA:
+       dq label#_CFA           ; link to CFA of word
+       dq flags + 0
+label#_PFA:
+       dq pname - $ - 8
+       db name
+pname:  db 0                   ; extra NUL byte
+       ;; align 8
+label#_pTFA:
+       dq label#_TFA           ; link to TFA of word
+label#_OFF:
+       dq offset + 0           ; The DOES offset. Defaults to 0.
+       ;; also CFA = pointer to "doer"
+label#_CFA:
+label:
+       if doer eq
+           dq doforth
+       else
+           if doer in <fasm>
+               dq dofasm ; label#_DFA
+           else
+               dq doer
+           end if
+       end if
+       ;; DFA
+label#_DFA:
+}
+
+macro tfa2cfa reg {
+       mov reg,qword [reg+8]
+}
+macro tfa2does reg {
+       tfa2cfa reg
+       sub reg,8
+}
+macro tfa2dfa reg {
+       tfa2cfa reg
+       add reg,8
+}
+macro tfa2flags reg {
+       add reg,16
+}
+macro tfa2pfa reg {
+       add reg,24
+}
+macro tfa2pname reg {
+       add reg,32
+}
+macro cfa2tfa reg {
+       sub reg,16
+       mov reg,qword [reg]
+}
+macro cfa2dfa reg {
+       add reg,8
+}
+macro dfa2cfa reg {
+       sub reg,8
+}
+macro dfa2tfa reg {
+       sub reg,24
+       mov reg,qword [reg]
+}
+;;; Code snippet to push a pname string with address and 64-bit length field.
+;;; The register is advanced to point at the text part.
+macro pushpname reg {
+       add reg,8
+       push reg
+       push qword [reg-8]
+}
+;;; ========================================
+;;; The BLOCK macro lays out the length for a subsequent block to the
+;;; given label.
+macro BLOCK endlabel {
+       local datastart
+       dq endlabel - datastart
+datastart:
+       }
+
+;;; ========================================
+;;; The STRING macro lays out length cell and data for several string
+;;; components.
+macro STRING [data] {
+common
+       local datastart, dataend
+       dq dataend - datastart
+datastart:
+forward
+       db data
+common
+dataend:
+       }
+
+;;; ========================================
+;;; The BRANCH macro lays out FORTH words BRANCH and 0BRANCH with offset
+macro BRANCH zero,label {
+       if zero in <0>
+           dq p_zero_branch
+       else
+           dq p_branch
+       end if
+       dq label - $ - 8
+}
+
+;;; ========================================
+;;; The STREAM macro starts an in-core FORTH STREAM area. See WORD
+;;; STREAM for details.
+       macro STREAM endlabel {
+       local datastart
+       dq $+32
+       dq -1
+       dq endlabel - datastart
+       dq 0
+datastart:
+       }