blob: 81313bb23278799849c49458d48e7b0dc01ea3ef [file] [log] [blame]
;;; Assembly definitions of built-in Forth words
;;; Assume this is included after all the relevant macros
;;; INPUT & OUTPUT ROUTINES ;;;
;; Stack of input file pointers
DEFVAR INP_S0, 'INP-S0'
KEY_INP_STACK:
TIMES 32 DW 0
;; Top of the stack
DEFVAR INP_SP, 'INP-SP'
KEY_INP_STACKP:
DW KEY_INP_STACK
;; ( handle -- )
DEFWORD_RAW SEEK_START, 'SEEK-START'
XOR AL, AL
POP BX
XOR CX, CX
XOR DX, DX
SEEKF
NEXT
;; ( *str len -- )
;; Opens the file specified by *str and len for reading and adds it
;; to the input stack.
DEFWORD_THREADED INCLUDED, 'INCLUDED'
DW TO_RET, TO_RET ; ( ) [ len *str ]
DW LITERAL, F_READ ; ( flags )
DW FROM_RET, FROM_RET ; ( flags *str len ) [ ]
DW OPEN_FILE_NAMED ; ( handle )
DW DUP, SEEK_START ; ( handle )
DW INP_SP ; ( handle *inp-sp )
DW GET, ADD2 ; ( handle inp-sp+2 )
DW SWAP, OVER ; ( inp-sp+2 handle inp-sp+2 )
DW SET ; ( inp-sp+2 )
DW INP_SP ; ( inp-sp+2 *inp-sp )
DW SET, EXIT ; ( )
DEFWORD_THREADED INCLUDE, 'INCLUDE'
DW _WORD, INCLUDED, EXIT
KEY_INP_BUF:
DW 0
;; Read a key from the input. If STDIN is blank wait for a key
;; press.
;;
;; TODO: Keep an internal buffer until RETURN is pressed, allow
;; some line editing.
DEFWORD_RAW KEY, 'KEY'
CALL READ_KEY
PUSH AX
NEXT
KEY_ERR_MSG DOS_STRING 'CF set'
KEY_END_MSG DOS_STRING 'EOF'
KEY_NO_ERR_MSG DOS_STRING 'No error'
KEY_GOT_CHAR_MSG DOS_STRING ' Read'
;; This routine returns the key in AL, but Forth wants it on the
;; stack, so we have a helper function.
;;
;; Clobbers: BX, CX
;; Return: AX
READ_KEY:
MOV BX, [KEY_INP_STACKP] ; Address of current input file handle
MOV BX, [BX]
TEST BX, BX
JZ .READ_STDIN ; If the file handle is 0
MOV CX, 1 ; We're reading 1 byte from a file
MOV DX, KEY_INP_BUF ; Write to our temporary buffer
READF
JC .READ_ERR ; CF - general read error
TEST AX, AX
JZ .READ_ERR ; AX=0 - at EOF
MOV AX, [KEY_INP_BUF]
RET
.READ_ERR:
MOV BX, [KEY_INP_STACKP]
CLOSEF ; Close the input stream
MOV [BX], WORD 0 ; Reset **inp-sp 0
MOV BX, [KEY_INP_STACKP]
SUB BX, 2 ; Pop off input stack
MOV [KEY_INP_STACKP], BX
JMP READ_KEY ; Re-try reading the key
.READ_STDIN:
READCIN
XOR AH, AH ; We don't care about the scan code
RET
%MACRO WHITESPACE 2
CMP %1, ' '
JE %2
CMP %1, 09h ; \t
JE %2
CMP %1, 0Ah ; \n
JE %2
CMP %1, 0Dh ; \r
JE %2
%ENDMACRO
;; Read a word from the input, max 32 bytes. WORD is reserved in
;; NASM sadly.
DEFWORD_RAW _WORD, 'WORD'
READ_WORD:
MOV DI, WORD_BUFFER
.START:
;; First skip whitespace
CALL READ_KEY ; Char in AL
WHITESPACE AL, .START
CMP AL, '\'
JE .COMMENT
.LOOP:
CMP AL, 'a'
JL .STORE
CMP AL, 'z'
JG .STORE
SUB AL, ('a' - 'A') ; To upper case
.STORE:
STOSB ; Buffer char
CALL READ_KEY
WHITESPACE AL, .DONE
JMP .LOOP
.COMMENT:
CALL READ_KEY
CMP AL, ASCII_NEWLINE
JE .START
CMP AL, ASCII_RETURN
JE .START
JMP .COMMENT
.DONE:
SUB DI, WORD_BUFFER ; Length
PUSH WORD_BUFFER
PUSH DI
NEXT
DEFWORD_RAW_IMMEDIATE LPAREN, '('
.LOOP:
CALL READ_KEY
CMP AL, ')'
JNE .LOOP
NEXT
;; ( string len -- num unparsed )
DEFWORD_RAW NUMBER, 'NUMBER'
POP DX ; Length
POP BX ; Index
ADD DX, BX ; End pointer
XOR AX, AX ; The number
XOR CX, CX ; CL - used for char
.LOOP:
MOV CL, BYTE [BX]
CMP CL, '0'
JL .DONE
CMP CL, '9'
JG .DONE
SUB CL, '0'
MOV CH, 10 ; This needs to be reset each time
; which is annoying
IMUL CH ; 8-bit IMUL operand means that the
; result is just in AX, not extended
; by DX. Perfect
XOR CH, CH
ADD AX, CX
INC BX
CMP BX, DX
JL .LOOP
.DONE:
SUB DX, BX ; Number of chars unread
PUSH AX
PUSH DX
NEXT
;; Emit a char from the stack
DEFWORD_RAW EMIT, 'EMIT'
POP DX
WRITECOUT
NEXT
DEFWORD_RAW CR, 'CR'
MOV DX, CRLF_MSG
WRITESOUT
NEXT
DEFWORD_THREADED SPACE, 'SPACE'
DW LITERAL, ' ', EMIT, EXIT
DEFWORD_RAW TYPE, 'TYPE'
TYPE_STRING:
POP CX ; Length
POP BX ; Index
ADD CX, BX ; End pointer
.LOOP:
MOV DL, BYTE [BX]
WRITECOUT
INC BX
CMP BX, CX
JNE .LOOP
.DONE:
NEXT
;; ( n -- )
DEFWORD_RAW DOT, '.'
POP AX ; The number
CALL DOT_INT
NEXT
;; AX - number to print
;; Clobbers: DX, BX, CX
DOT_INT:
TEST AX, AX
JNZ .START
MOV DX, '0'
WRITECOUT
RET
.START:
MOV BX, 10 ; The base
;; TODO: BUG: Depending on this value there is a maximum number
;; that this routine will format, which is weird. For the value of
;; 7 it is 1280.
MOV CX, 7
.LOOP:
XOR DX, DX
DIV BX ; AX = quotient; DX = remainder
PUSH DX
LOOP .LOOP
MOV CX, 7
XOR BX, BX ; At start
.REVERSE:
POP DX
OR BL, DL
JZ .END
ADD DL, '0'
WRITECOUT
.END:
LOOP .REVERSE
RET
;; Write a string to the PAD and 0-terminate it. For use with DOS
;; I/O words that require ASCIZ strings.
;;
;; CX - string length
;; BX - start of string
;; Clobbers: none
;; Returns: BX - address of temporary string
MAKE_STRING_ASCIZ:
PUSH SI
PUSH DI
PUSH CX
MOV SI, BX
GET_PAD DI
PUSH DI ; Save start of temp string
REP MOVSB ; Copy bytes
MOV BYTE [DI], 0 ; 0-terminate
POP BX ; Return start in BX
POP CX
POP DI
POP SI
RET
;; ( flags *start len -- )
DEFWORD_RAW CREATE_FILE_NAMED, 'CREATE-FILE-NAMED'
POP CX ; Len
POP BX ; Start
CALL MAKE_STRING_ASCIZ
POP CX ; Flags
MOV DX, BX
CREATF
NEXT
;; ( flags -- ) CREATE-FILE <file-name>
DEFWORD_THREADED CREATE_FILE, 'CREATE-FILE'
DW _WORD, CREATE_FILE_NAMED, EXIT
;; ( flags *start len -- handle )
DEFWORD_RAW OPEN_FILE_NAMED, 'OPEN-FILE-NAMED'
POP CX ; Length
POP BX ; Start
CALL MAKE_STRING_ASCIZ
MOV DX, BX ; ASCIZ string in DX
POP AX ; Flags
OPENF
JC FILE_WRITE_ERROR
PUSH AX
NEXT
FILE_WRITE_ERROR:
MOV DX, MSG_OPENF_FAILED
WRITESOUT
PUSH AX
NEXT
;; ( flags -- handle )
DEFWORD_THREADED OPEN_FILE, 'OPEN-FILE'
DW _WORD ; ( flags *str len )
DW OPEN_FILE_NAMED
DW EXIT
DEFWORD_RAW CLOSE_FILE, 'CLOSE-FILE'
POP BX
CLOSEF
NEXT
;; Write word to file
;; ( cell handle -- )
DEFWORD_RAW FILE_COMMA, 'F,'
POP BX ; Handle
POP DX ; Data
MOV WORD [FILE_WRITE_BUFFER], DX
MOV DX, FILE_WRITE_BUFFER ; Address
MOV CX, 2 ; Length
WRITEF
JC FILE_WRITE_ERROR
NEXT
;; ( byte handle -- )
DEFWORD_RAW FILE_CHAR_COMMA, 'FC,'
POP BX
POP DX
MOV BYTE [FILE_WRITE_BUFFER], DL
MOV DX, FILE_WRITE_BUFFER
MOV CX, 1
WRITEF
JC FILE_WRITE_ERROR
NEXT
;; ( *start *end handle -- )
DEFWORD_RAW FILE_WRITE_RANGE, 'FWRITE-RANGE'
POP BX
POP CX ; End
POP DX
SUB CX, DX ; Get difference
WRITEF
NEXT
;;; DATA ;;;
CRLF_MSG DB ASCII_RETURN, ASCII_NEWLINE, '$'
MSG_OPENF_FAILED DB 'File error', ASCII_RETURN, ASCII_NEWLINE, '$'
WORD_BUFFER TIMES 33 DB 0
FILE_WRITE_BUFFER DW 0
WORD_BUFFER_END: