-
Notifications
You must be signed in to change notification settings - Fork 0
/
IOWORDS.ASM
397 lines (305 loc) · 6.53 KB
/
IOWORDS.ASM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
;;; Assembly definitions of built-in Forth words
;;; Assume this is included after all the relevant macros
;;; INPUT & OUTPUT ROUTINES ;;;
;; Stack of input file pointers
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
;; 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:
;; BX already holds the file handle
CLOSEF ; Close the input stream
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: