code: 9ferno

ref: 7c4e9c492a69a964b6873a6f5e3a90fcc5ece23a
dir: /os/pc64/ff.s/

View raw version
#include "mem.h"

/*

The bigger goal is to replace the dis vm with ff
ff outputs to screen now.
But, the input needs to be fixed.
make this into a devff like device that reads commands and outputs the result.
replace variable with value (as in open firmware), to avoid exposing addresses

 ff kernel, amd64 9front variant

 Register usage:

	Original usage
 TOS: AX top of stack register
 SP:  SP parameter stack pointer, grows towards lower memory (downwards)
 RP:  BP (= RARG) return stack pointer, grows towards higher memory (upwards)
 AP:  SI address pointer
 W:   DI work register (holds CFA)
 	BX, CX, DX, R8-R15 temporary registers

plan9 assembler puts the first argument in BP (RARG), return value in AX.

	Changed to
 Leaving AX, SP, BP (RARG) alone to not mess with the C environment

 TOS: BX top of stack register
 PSP: DX parameter stack pointer, grows towards lower memory (downwards)
 RSP: R8 return stack pointer, grows towards higher memory (upwards)
 IP:  R9 instruction pointer
 W:   R10 work register (holds CFA)
	CX, SI, DI, R11-R15 temporary registers

coding standard
: <name> (S input-stack --- output-stack) (R --- )
	(G descriptive glossary text)
	f1 f2  ( interim stack picture) \ programmers explanatory comment
		.. fn ;

Memory map:
Return stack 4096 bytes at FFSTART
	|
	|
	v (grows downwards)
Others 4096 bytes
	system variables
	word buffer
	tib, text input buffer
Parameter stack 4096 bytes at FFEND-4096
	^ (grows upwards)
	|
	|
SSTACK_END = FFEND
*/

#define SSTACK_SIZE 4096
#define RSTACK_SIZE 4096
#define	LAST $centry_c_boot(SB) /* last defined word, should generate this */
#define	SSTACK_END FFEND

/* putting this above the asm code as the v_dp define is needed by _main */
/*	m_ for primitive/macro word cfa
	mc_ for primtive/macro word constants
	c_ for colon word cfa
	ci_ for colon immediate word cfa
	v_ for colon variable word cfa
 */
#include "primitives.s"

#define PUSHALL \
	PUSHQ	R13; \
	PUSHQ	R12; \
	PUSHQ	R11; \
	PUSHQ	R10; \
	PUSHQ	R9; \
	PUSHQ	R8; \
	PUSHQ	R8; \
	PUSHQ	R10; \
	PUSHQ	R9; \
	PUSHQ	DX; \
	PUSHQ	CX; \
	PUSHQ	BX; \
	PUSHQ	TOS;
#define POPALL \
	POPQ	TOS; \
	POPQ	BX; \
	POPQ	CX; \
	POPQ	DX; \
	POPQ	R9; \
	POPQ	R10; \
	POPQ	R8; \
	POPQ	R8; \
	POPQ	R9; \
	POPQ	R10; \
	POPQ	R11; \
	POPQ	R12; \
	POPQ	R13;
#define PUSHREGS \
	PUSHQ	R8; \
	PUSHQ	R10; \
	PUSHQ	R9; \
	PUSHQ	TOS;
#define POPREGS \
	POPQ	TOS; \
	POPQ	R9; \
	POPQ	R10; \
	POPQ	R8;

#define FF_TO_C_0 \
	PUSHREGS; \
	MOVQ DX, ffsp<>(SB); \
	MOVQ csp<>(SB), DX; \
	POPREGS;

#define FF_TO_C_1 \
	MOVQ TOS, BX; \
	POPQ TOS; /* drop TOS from the parameter stack */ \
	FF_TO_C_0 \
	MOVQ BX, R8; /* 1st argument in R8 == RARG */

/* ( 1st_parameter 2nd_parameter -- ) */
#define FF_TO_C_2 /* for calling a c function with 2 parameters */ \
	MOVQ TOS, CX; \
	POPQ TOS; \
	FF_TO_C_1 \
	MOVQ CX, 8(DX) \

/* ( 1st_parameter 2nd_parameter 3rd_parameter -- ) */
#define FF_TO_C_3 /* for calling a c function with 3 parameters */ \
	MOVQ TOS, DX; \
	POPQ TOS; \
	FF_TO_C_2 \
	MOVQ DX, 16(DX) \

/* no arguments when calling ff from C, for now */
#define C_TO_FF \
	PUSHREGS; \
	MOVQ DX, csp<>(SB); \
	MOVQ ffsp<>(SB), DX; \
	POPREGS;

TEXT	ffmain(SB), 1, $-4		/* _main(SB), 1, $-4 without the libc */
	/* The last dictionary entry address is stored in dtop.
	 * The location of dtop is stored in the variable dp.
	 * To get the location of dtop, get the value in the parameter field
	 * (link + name(1+2) + code field address = 24 bytes) of the dp
	 * dictionary entry.
	 */
	MOVQ $FFEND, PSP	/* setting up stack */
	/*
	 * dtop address is stored in the parameter field address(24-32 bytes) of mventry_dp
	 */
	MOVQ mventry_dp+24(SB), SI	/* now, SI = dtop address */
	MOVQ (SI), TOS	/* TOS = *CX = $LAST = boot word address (defined last, stored at dtop) */
				/* if 6a allows multiple symbols per address, then 
					the above 3 instructions would have been
					MOVQ (($mventry_dp+24(SB))), TOS */
	/*
	 * Could do this instead of the calculations below
	 * LEAQ 24(TOS), IP
	 */
	ADDQ $16, TOS	/* TOS += link (8 bytes) + len (1 byte) + minimum for align to 8 bytes */
	XORQ CX, CX
	MOVB 8(SI), CL	/* CL = length of boot name */
	ADDQ CX, TOS		/* TOS += len */
	ANDQ $~7, TOS	/* TOS = address of boot's code - 8 bytes */
	LEAQ 8(TOS), IP	/* IP = L257 = start of boot code = has docol address there
					 * skipping over docol as we do not need to save the IP
					 */

/* lodsl could make this simpler. But, this is more comprehensible
	why not JMP* (W)?
 */
#define NEXT	MOVQ (IP), W; /* W = Address next to the DOCOL of boot */ \
		ADDQ $8, IP; /* move IP further = DOCOL address + 16 */ \
		MOVQ (W), TOS; /* TOS = code field address of the 1st instruction after DOCOL of boot */ \
		JMP* TOS; /* Start executing that code field address */
/*
Address   0     8    16
aword : docol  40   ...
Address   40    48
bword : docol   72
Address   72    80
cword : docol  ....
at docol address, some assembly instruction
Assume IP = 8
 */
#define NEXT	MOVQ (IP), W;	/* W = 40, contents of address in IP, some word's code field address */ \
		MOVQ (W), TOS;	/* TOS = docol, Get the address in the address in IP = code field address */ \
		ADDQ $8, IP; 	/* move IP further, IP = 16 */ \
		JMP* TOS; /* Start executing at docol address, JMP* = jump to a non-relative address */

#define PUSH(r)	SUBQ $8, PSP; \
			MOVQ r, (PSP)
#define POP(r)	MOVQ (PSP), r; \
			ADDQ $8, PSP

	NEXT

TEXT	reset(SB), 1, $-4
	MOVQ $FFSTART, RSP
	NEXT

TEXT	clear(SB), 1, $-4
	MOVQ $FFEND, PSP
	NEXT

TEXT	colon(SB), 1, $-4
	MOVQ IP,(RSP)
	ADDQ $8, RSP
	LEAQ 8(W), IP
	NEXT

TEXT	exitcolon(SB), 1, $-4
	SUBQ $8, RSP
	MOVQ (RSP), IP
	NEXT

TEXT	dodoes(SB), 1, $-4	/* ( -- a ) */
	MOVQ IP,(RSP)
	ADDQ $8,RSP
	MOVQ 8(W),IP
	PUSH(TOS)
	LEAQ 16(W), TOS
	NEXT

TEXT	jump(SB), 1, $-4	/* ( -- ) */
	MOVQ (IP),IP
	NEXT

/* ( f -- ) cjump address
	if true, skip the address and continue
	else, go to the address */
TEXT	cjump(SB), 1, $-4	/* ( f -- ) */
	MOVQ (IP), TOS	/* get the next address */
	ADDQ $8, IP	/* move esi beyond that */
	TESTQ TOS, TOS
	JNZ .l1		/* if true, move along */
	MOVQ TOS, IP	/* if false, go to the above address */
.l1:
	POP(TOS
	NEXT

/* TODO change to allow only fetches from a certain memory range */
TEXT	fetch(SB), 1, $-4	/* ( a -- n) */
	MOVQ (TOS), TOS
	NEXT

/* TODO change to allow stores to a certain memory range only */
TEXT	store(SB), 1, $-4	/* ( n a -- ) */
	POP(CX)
	MOVQ CX, (TOS)
	POP(TOS)
	NEXT

TEXT	cfetch(SB), 1, $-4	/* ( a -- c ) */
	XORQ CX, CX
	MOVB (TOS), CL
	POP(TOS)
	NEXT

TEXT	cstore(SB), 1, $-4	/* ( c a -- ) */
	POP(CX)
	MOVB CL, (TOS)
	POP(TOS)
	NEXT

TEXT	terminate(SB), 1, $-4	/* ( n -- ) */
	XORQ CX, CX
	TESTQ TOS, TOS
	JZ .l2
	MOVQ $failtext(SB), TOS
.l2:
	/* PUSHQ CX */
	/* SUBQ $8, PSP */	/* dummy retaddr */
	MOVQ CX, a0+0(FP)	/* address of exit status? status = nil? */
	MOVQ $8, RARG	/* EXITS */
	SYSCALL		/* TODO syscall for exit */

TEXT	testfsopen(SB), 1, $-4
	PUSHQ SI	/* for some reason, the syscall is changing IP and W */
	PUSHQ BP
	PUSHQ $0	/* OREAD */
	PUSHQ $name(SB)
	PUSHQ $0	/* dummy retaddr */
	MOVQ $14, RARG	/* open */
	SYSCALL
	ADDQ $24, PSP
	POPQ RSP
	POPQ IP
	NEXT
	NOP
	NOP
	NOP
	NOP
	NOP

#include "bindings.s"

TEXT	mmap(SB), 1, $-4	/* ( a1 -- a2 ) */
	MOVQ $-1, TOS	/* unimplemented */

TEXT	variable(SB), 1, $-4	/* ( -- a ) */
	PUSH(TOS)
	LEAQ 8(W), TOS
	NEXT

TEXT	constant(SB), 1, $-4	/* ( -- n ) */
	PUSH(TOS)
	MOVQ 8(W), TOS
	NEXT

TEXT	literal(SB), 1, $-4	/* ( -- n ) */
	PUSH(TOS)
	MOVQ (IP), TOS
	ADDQ $8, IP
	NEXT

TEXT	sliteral(SB), 1, $-4	/* ( -- a n ) */
	PUSH(TOS)
	XORQ TOS,TOS
	MOVB (IP), BL
	INCQ IP
	PUSH(IP)
	ADDQ TOS, IP
	ADDQ $7, IP
	ANDQ $~7, IP
	NEXT

/* puts the top 2 entries of the data stack in the return stack */
TEXT	doinit(SB), 1, $-4	/* ( hi lo -- ) */
	MOVQ TOS, (RSP)
	POP(TOS)
	MOVQ TOS, 8(RSP)
	POP(TOS)
	ADDQ $16, RSP
	NEXT

/* not sure if this works, needs testing to follow https://github.com/mark4th/x64
	check the notes
	return stack would have
		current index
		end index
	(R lo hi -- )
	increment lo
	when hi > lo, go to the address next to doloop
 */
TEXT	doloop(SB), 1, $-4
	INCQ -16(RSP)
doloop1:
	MOVQ -16(RSP), CX
	CMPQ CX, -8(RSP)
	JGE .l4
	MOVQ (IP), IP
	NEXT
.l4:
	SUBQ $16, RSP
	ADDQ $8, IP
	NEXT

TEXT	doploop(SB), 1, $-4	/* ( n -- ) */
	ADDQ TOS, -16(RSP)
	POP(TOS)
	JMP doloop1

TEXT	rfetch(SB), 1, $-4	/* ( -- n ) */
	PUSH(TOS)
	MOVQ -8(RSP), TOS
	NEXT

TEXT	rpush(SB), 1, $-4	/* ( n -- ) */
	MOVQ TOS,(RSP)
	POP(TOS)
	ADDQ $8,RSP
	NEXT

TEXT	rpop(SB), 1, $-4	/* ( -- n ) */
	PUSH(TOS)
	SUBQ $8, RSP
	MOVQ (RSP), TOS
	NEXT

TEXT	i(SB), 1, $-4	/* ( -- n ) */
	PUSH(TOS)
	MOVQ -16(RSP), TOS
	NEXT

TEXT	j(SB), 1, $-4	/* ( -- n ) */
	PUSH(TOS)
	MOVQ -32(RSP), TOS
	NEXT

TEXT	plus(SB), 1, $-4	/* ( n1 n2 -- n ) */
	POP(CX)
	ADDQ CX, TOS
	NEXT  

TEXT	minus(SB), 1, $-4	/* ( n1 n2 -- n ) */
	MOVQ TOS, CX
	POP(TOS)
	SUBQ CX, TOS
	NEXT

TEXT	multiply(SB), 1, $-4	/* ( n1 n2 -- n1*n2 ) */
	POP(CX)
	IMULQ CX,TOS
	NEXT

TEXT	slashmod(SB), 1, $-4	/* ( n1 n2 -- remainder quotient ) n1/n2 */
	MOVQ (PSP), CX /* CX = n1 */
	PUSHQ DX /* DX == PSP, store DX and AX as they are used by CDQ and IDIV */
	PUSHQ AX
	XORQ DX, DX /* DX = 0 */
	MOVQ CX, AX /* AX = n1 */
	CDQ 		/* RAX -> RDX:RAX sign extension */
	IDIVQ TOS	/* RDX:RAX / TOS => Quotient in RAX, Remainder in RDX */
	MOVQ DX, CX	/* CX = remainder */
	MOVQ AX, TOS /* TOS = quotient */
	POPQ AX
	POPQ DX
	MOVQ CX, (PSP) /* -- remainder quotient */
	NEXT

TEXT	uslashmod(SB), 1, $-4	/* ( u1 u2 -- uremainder uquotient ) */
	MOVQ (PSP), CX /* CX = n1 */
	PUSHQ DX /* DX == PSP, store DX and AX as they are used by CDQ and IDIV */
	PUSHQ AX
	XORQ DX, DX /* DX = 0 */
	MOVQ CX, AX /* AX = n1 */
	IDIVQ TOS	/* RDX:RAX / TOS => Quotient in RAX, Remainder in RDX */
	MOVQ DX, CX	/* CX = remainder */
	MOVQ AX, TOS /* TOS = quotient */
	POPQ AX
	POPQ DX
	MOVQ CX, (PSP) /* -- uremainder uquotient */
	NEXT

	MOVQ TOS, TOS
	MOVQ (PSP), TOS
	XORQ PSP, PSP
	DIVQ TOS
	MOVQ PSP, (PSP)
	NEXT

TEXT	binand(SB), 1, $-4	/* ( n1 n2 -- n ) */
	ANDQ (PSP), TOS
	ADDQ $8, PSP
	NEXT

TEXT	binor(SB), 1, $-4	/* ( n1 n2 -- n ) */
	ORQ (PSP), TOS
	ADDQ $8, PSP
	NEXT

TEXT	binxor(SB), 1, $-4	/* ( n1 n2 -- n ) */
	XORQ (PSP), TOS
	ADDQ $8, PSP
	NEXT

TEXT	xswap(SB), 1, $-4	/* ( x y -- y x ) */
	XCHGQ TOS, (PSP)
	NEXT

TEXT	drop(SB), 1, $-4	/* ( x -- ) */
	POP(TOS)
	NEXT

TEXT	dup(SB), 1, $-4	/* ( x -- x x ) */
	PUSH(TOS)
	NEXT

TEXT	over(SB), 1, $-4	/* ( x y -- x y x ) */
	PUSH(TOS)
	MOVQ 8(PSP), TOS
	NEXT

TEXT	equal(SB), 1, $-4	/* ( x y -- f ) */
	POP(CX)
	CMPQ CX, TOS
	JEQ .true
	XORQ TOS, TOS
	NEXT
TEXT	true(SB), 1, $-4
.true:
	MOVQ $-1, TOS
	NEXT
	
TEXT	greater(SB), 1, $-4	/* ( x y -- f ) */
	POP(CX)
	CMPQ CX, TOS
	JGT .true
	XORQ TOS, TOS
	NEXT

/* if x < y then y - x > 0, no sign flag
	intel manual says destination operand - source operand
	but, 9front assemblers seem to work differently
	compare x and y == CMP x, y
 */
TEXT	less(SB), 1, $-4	/* ( x y -- f ) */
	POP(CX)
	CMPQ CX, TOS
	JLT .true
	XORQ TOS, TOS
	NEXT

TEXT	stackptr(SB), 1, $-4	/* ( -- a ) does not include TOS! */
	PUSH(TOS)
	MOVQ PSP, TOS
	NEXT

TEXT	lshift(SB), 1, $-4	/* ( n1 n2 -- n ) */
	MOVQ TOS, CX
	POP(TOS)
	SHLQ CL, TOS
	NEXT
	
TEXT	rshift(SB), 1, $-4	/* ( n1 n2 -- n ) */
	MOVQ TOS, CX
	POP(TOS)
	SHRQ CL, TOS
	NEXT

TEXT	rshifta(SB), 1, $-4	/* ( n1 n2 -- n ) */
	MOVQ TOS, CX
	POP(TOS)
	SARQ CL, TOS
	NEXT

TEXT	execute(SB), 1, $-4	/* ( ... a -- ... ) */
	MOVQ TOS, W
	POP(TOS)
	MOVQ (W), CX
	JMP CX

TEXT	deferred(SB), 1, $-4
	MOVQ 8(W), W
	MOVQ (W), CX
	JMP CX

TEXT	unloop(SB), 1, $-4
	SUBQ $16, RSP
	NEXT

TEXT	cmove(SB), 1, $-4	/* ( a1 a2 n -- ) */
	MOVQ TOS, CX
	POP(W)
	MOVQ IP, CX
	POP(IP)
	REP; MOVSB
	MOVQ CX, IP
	POP(TOS)
	NEXT

TEXT	cmoveb(SB), 1, $-4	/* ( a1 a2 n -- ) */
	MOVQ TOS, CX
	POP(W)
	DECQ TOS
	ADDQ TOS, W
	MOVQ IP, CX
	POP(IP)
	ADDQ TOS, IP
	STD
	REP; MOVSB
	CLD
	MOVQ CX, IP
	POP(TOS)
	NEXT

TEXT	cas(SB), 1, $-4	/* ( a old new -- f ) */
	MOVQ TOS, DI	/* new */
	POP(TOS)	/* old */
	POP(SI)	/* addr */
	LOCK; CMPXCHGQ DI, (SI)
	JE .true
	XORQ TOS, TOS
	/* pause -- no equivalent in 6a ? */
	NEXT

TEXT	ffend(SB), 1, $-4

#include "words.s"

DATA	failtext(SB)/6, $"error\z"
GLOBL	failtext(SB), $6

DATA	name(SB)/8, $"/tmp/tes"
DATA	name+8(SB)/6, $"t.txt\z"
GLOBL	name(SB), $14

DATA	errstrbuffer(SB)/1, $0
GLOBL	errstrbuffer(SB), $128	/* matches ERRMTOS of libc.h */

DATA	tibuffer(SB)/1, $0
GLOBL	tibuffer(SB), $1024
DATA	wordbuffer(SB)/1, $0
GLOBL	wordbuffer(SB), $256
/* TODO there should not be a heap limit, get rid of this */
/*
DATA	heap(SB)/1, $0
GLOBL	heap(SB), $HEIP_IPZE
*/


DATA	dtop(SB)/8, LAST
GLOBL	dtop(SB), $8
/* 0's until heap allocated */
DATA	htop(SB)/8, $0
GLOBL	htop(SB), $8
DATA	heapend(SB)/8, $0
GLOBL	heapend(SB), $8

GLOBL	ffsp<>(SB), $8
GLOBL	csp<>(SB), $8

	END