blob: cf3a726a60018180dbfab37b7cc3bb5bbc13229c [file] [log] [blame]
;;; Assembly definitions of built-in Forth words
;;; Assume this is included after all the relevant macros
;;; INPUT & OUTPUT ROUTINES ;;;
;; 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.
;;
;; Actually, that could be implemented in Forth for simplicity.
DEFWORD_RAW KEY, 'KEY'
CALL READ_KEY
PUSH AX
NEXT
;; This routine returns the key in AL, but Forth wants it on the
;; stack, so we have a helper function.
READ_KEY:
;; Check if line buffer is empty
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
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
;; ( flags *start len -- handle )
DEFWORD_RAW OPEN_FILE_NAMED, 'OPEN-FILE-NAMED'
POP CX ; Length
POP BX ; Start
MOV DX, BX
ADD BX, CX
MOV BYTE [BX], 0
POP CX ; Flags
CREATF
MOV AX, CX ; 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: