2019-05-17 11:15:00 +10:00
|
|
|
; I/Os in zasm
|
|
|
|
;
|
|
|
|
; As a general rule, I/O in zasm is pretty straightfoward. We receive, as a
|
|
|
|
; parameter, two blockdevs: One that we can read and seek and one that we can
|
|
|
|
; write to (we never seek into it).
|
|
|
|
;
|
2019-05-18 04:58:16 +10:00
|
|
|
; This unit also has the responsibility of counting the number of written bytes,
|
|
|
|
; maintaining IO_PC and of properly disabling output on first pass.
|
|
|
|
;
|
2019-05-28 10:52:40 +10:00
|
|
|
; On top of that, this unit has the responsibility of keeping track of the
|
|
|
|
; current lineno. Whenever GetC is called, we check if the fetched char is a
|
|
|
|
; newline. If it is, we increase our lineno. This unit is the best place to
|
|
|
|
; keep track of this because we have to handle ioRecallPos.
|
|
|
|
;
|
2019-05-17 11:15:00 +10:00
|
|
|
; zasm doesn't buffers its reads during tokenization, which simplifies its
|
|
|
|
; process. However, it also means that it needs, in certain cases, a "putback"
|
|
|
|
; mechanism, that is, a way to say "you see that character I've just read? that
|
|
|
|
; was out of my bounds. Could you make it as if I had never read it?". That
|
|
|
|
; buffer is one character big and is made with the expectation that ioPutBack
|
|
|
|
; is always called right after a ioGetC (when it's called).
|
|
|
|
;
|
|
|
|
; ioPutBack will mess up seek and tell offsets, so thath "put back" should be
|
|
|
|
; consumed before having to seek and tell.
|
|
|
|
;
|
|
|
|
; That's for the general rules.
|
|
|
|
;
|
|
|
|
; Now, let's enter includes. To simplify processing, we make include mostly
|
|
|
|
; transparent to all other units. They always read from ioGetC and a include
|
|
|
|
; directive should have the exact same effect as copy/pasting the contents of
|
|
|
|
; the included file in the caller.
|
|
|
|
;
|
|
|
|
; By the way: we don't support multiple level of inclusion. Only top level files
|
|
|
|
; can include.
|
|
|
|
;
|
|
|
|
; When we include, all we do here is open the file with fsOpen and set a flag
|
|
|
|
; indicating that we're inside an include. When that flag is on, GetC, Seek and
|
|
|
|
; Tell are transparently redirected to their fs* counterpart.
|
|
|
|
;
|
|
|
|
; When we reach EOF in an included file, we transparently unset the "in include"
|
|
|
|
; flag and continue on the general IN stream.
|
|
|
|
|
2019-05-10 05:36:03 +10:00
|
|
|
; *** Variables ***
|
2019-06-05 01:53:02 +10:00
|
|
|
.equ IO_IN_BLK IO_RAMSTART
|
|
|
|
.equ IO_OUT_BLK IO_IN_BLK+BLOCKDEV_SIZE
|
2019-05-17 11:15:00 +10:00
|
|
|
; Save pos for ioSavePos and ioRecallPos
|
2019-06-05 01:53:02 +10:00
|
|
|
.equ IO_SAVED_POS IO_OUT_BLK+BLOCKDEV_SIZE
|
2019-05-17 11:15:00 +10:00
|
|
|
; File handle for included source
|
|
|
|
.equ IO_INCLUDE_HDL IO_SAVED_POS+2
|
2019-06-05 10:45:01 +10:00
|
|
|
; blkdev for include file
|
|
|
|
.equ IO_INCLUDE_BLK IO_INCLUDE_HDL+FS_HANDLE_SIZE
|
2019-05-16 21:53:42 +10:00
|
|
|
; see ioPutBack below
|
2019-06-05 10:45:01 +10:00
|
|
|
.equ IO_PUTBACK_BUF IO_INCLUDE_BLK+BLOCKDEV_SIZE
|
2019-05-17 11:15:00 +10:00
|
|
|
.equ IO_IN_INCLUDE IO_PUTBACK_BUF+1
|
2019-05-28 10:52:40 +10:00
|
|
|
.equ IO_PC IO_IN_INCLUDE+1
|
|
|
|
; Current lineno in top-level file
|
|
|
|
.equ IO_LINENO IO_PC+2
|
2019-05-28 23:57:29 +10:00
|
|
|
; Current lineno in include file
|
|
|
|
.equ IO_INC_LINENO IO_LINENO+2
|
2019-05-28 10:52:40 +10:00
|
|
|
; Line number (can be top-level or include) when ioSavePos was last called.
|
2019-05-28 23:57:29 +10:00
|
|
|
.equ IO_SAVED_LINENO IO_INC_LINENO+2
|
2019-07-22 02:58:02 +10:00
|
|
|
; Handle for the ioSpitBin
|
|
|
|
.equ IO_BIN_HDL IO_SAVED_LINENO+2
|
|
|
|
.equ IO_RAMEND IO_BIN_HDL+FS_HANDLE_SIZE
|
2019-05-10 05:36:03 +10:00
|
|
|
|
|
|
|
; *** Code ***
|
|
|
|
|
2019-05-16 21:53:42 +10:00
|
|
|
ioInit:
|
|
|
|
xor a
|
|
|
|
ld (IO_PUTBACK_BUF), a
|
2019-05-17 11:15:00 +10:00
|
|
|
ld (IO_IN_INCLUDE), a
|
2019-06-05 10:45:01 +10:00
|
|
|
ld de, IO_INCLUDE_BLK
|
|
|
|
ld hl, _ioIncBlk
|
|
|
|
call blkSet
|
2019-05-28 10:52:40 +10:00
|
|
|
jp ioResetCounters
|
2019-05-16 21:53:42 +10:00
|
|
|
|
2019-05-10 05:36:03 +10:00
|
|
|
ioGetC:
|
2019-05-16 21:53:42 +10:00
|
|
|
ld a, (IO_PUTBACK_BUF)
|
|
|
|
or a ; cp 0
|
|
|
|
jr nz, .getback
|
2019-05-17 11:15:00 +10:00
|
|
|
call ioInInclude
|
|
|
|
jr z, .normalmode
|
|
|
|
; We're in "include mode", read from FS
|
2019-06-05 10:45:01 +10:00
|
|
|
ld ix, IO_INCLUDE_BLK
|
|
|
|
call _blkGetC
|
2019-06-16 05:50:27 +10:00
|
|
|
jr nz, .includeEOF
|
2019-05-28 23:57:29 +10:00
|
|
|
cp 0x0a ; newline
|
2019-06-16 05:50:27 +10:00
|
|
|
ret nz ; not newline? nothing to do
|
2019-05-28 23:57:29 +10:00
|
|
|
; We have newline. Increase lineno and return (the rest of the
|
|
|
|
; processing below isn't needed.
|
|
|
|
push hl
|
|
|
|
ld hl, IO_INC_LINENO
|
|
|
|
inc (hl)
|
|
|
|
pop hl
|
|
|
|
ret
|
|
|
|
|
2019-06-16 05:50:27 +10:00
|
|
|
.includeEOF:
|
2019-05-17 11:15:00 +10:00
|
|
|
; We reached EOF. What we do depends on whether we're in Local Pass
|
|
|
|
; mode. Yes, I know, a bit hackish. Normally, we *should* be
|
|
|
|
; transparently getting of include mode and avoid meddling with global
|
|
|
|
; states, but here, we need to tell main.asm that the local scope if
|
|
|
|
; over *before* we get off include mode, otherwise, our IO_SAVED_POS
|
|
|
|
; will be wrong (an include IO_SAVED_POS used in global IN stream).
|
|
|
|
call zasmIsLocalPass
|
|
|
|
ld a, 0 ; doesn't affect Z flag
|
|
|
|
ret z ; local pass? return EOF
|
|
|
|
; regular pass (first or second)? transparently get off include mode.
|
|
|
|
ld (IO_IN_INCLUDE), a ; A already 0
|
2019-06-15 10:26:39 +10:00
|
|
|
ld (IO_INC_LINENO), a
|
|
|
|
ld (IO_INC_LINENO+1), a
|
2019-05-17 11:15:00 +10:00
|
|
|
; continue on to "normal" reading. We don't want to return our zero
|
|
|
|
.normalmode:
|
|
|
|
; normal mode, read from IN stream
|
2019-06-05 01:53:02 +10:00
|
|
|
ld ix, IO_IN_BLK
|
|
|
|
call _blkGetC
|
2019-05-28 10:52:40 +10:00
|
|
|
cp 0x0a ; newline
|
|
|
|
ret nz ; not newline? return
|
|
|
|
; inc current lineno
|
|
|
|
push hl
|
|
|
|
ld hl, IO_LINENO
|
|
|
|
inc (hl)
|
|
|
|
pop hl
|
|
|
|
cp a ; ensure Z
|
|
|
|
ret
|
|
|
|
|
2019-05-16 21:53:42 +10:00
|
|
|
.getback:
|
|
|
|
push af
|
|
|
|
xor a
|
|
|
|
ld (IO_PUTBACK_BUF), a
|
|
|
|
pop af
|
|
|
|
ret
|
|
|
|
|
2019-05-28 10:52:40 +10:00
|
|
|
_callIX:
|
|
|
|
jp (ix)
|
|
|
|
ret
|
|
|
|
|
2019-05-16 21:53:42 +10:00
|
|
|
; Put back non-zero character A into the "ioGetC stack". The next ioGetC call,
|
2019-06-05 01:53:02 +10:00
|
|
|
; instead of reading from IO_IN_BLK, will return that character. That's the
|
2019-05-16 21:53:42 +10:00
|
|
|
; easiest way I found to handle the readWord/gotoNextLine problem.
|
|
|
|
ioPutBack:
|
|
|
|
ld (IO_PUTBACK_BUF), a
|
|
|
|
ret
|
2019-05-10 05:36:03 +10:00
|
|
|
|
|
|
|
ioPutC:
|
2019-05-18 04:58:16 +10:00
|
|
|
push hl
|
|
|
|
ld hl, (IO_PC)
|
|
|
|
inc hl
|
|
|
|
ld (IO_PC), hl
|
|
|
|
pop hl
|
|
|
|
push af
|
|
|
|
call zasmIsFirstPass
|
|
|
|
jr z, .skip
|
|
|
|
pop af
|
2019-06-05 01:53:02 +10:00
|
|
|
ld ix, IO_OUT_BLK
|
|
|
|
jp _blkPutC
|
2019-05-18 04:58:16 +10:00
|
|
|
.skip:
|
|
|
|
pop af
|
2019-06-16 06:48:30 +10:00
|
|
|
cp a ; ensure Z
|
2019-05-18 04:58:16 +10:00
|
|
|
ret
|
2019-05-10 05:36:03 +10:00
|
|
|
|
2019-05-17 11:15:00 +10:00
|
|
|
ioSavePos:
|
2019-05-28 10:52:40 +10:00
|
|
|
ld hl, (IO_LINENO)
|
2019-06-15 10:26:39 +10:00
|
|
|
call ioInInclude
|
|
|
|
jr z, .skip
|
|
|
|
ld hl, (IO_INC_LINENO)
|
|
|
|
.skip:
|
2019-05-28 10:52:40 +10:00
|
|
|
ld (IO_SAVED_LINENO), hl
|
2019-05-17 11:15:00 +10:00
|
|
|
call _ioTell
|
|
|
|
ld (IO_SAVED_POS), hl
|
|
|
|
ret
|
|
|
|
|
|
|
|
ioRecallPos:
|
2019-05-28 10:52:40 +10:00
|
|
|
ld hl, (IO_SAVED_LINENO)
|
2019-06-15 10:26:39 +10:00
|
|
|
call ioInInclude
|
|
|
|
jr nz, .include
|
2019-05-28 10:52:40 +10:00
|
|
|
ld (IO_LINENO), hl
|
2019-06-15 10:26:39 +10:00
|
|
|
jr .recallpos
|
|
|
|
.include:
|
|
|
|
ld (IO_INC_LINENO), hl
|
|
|
|
.recallpos:
|
2019-05-17 11:15:00 +10:00
|
|
|
ld hl, (IO_SAVED_POS)
|
|
|
|
jr _ioSeek
|
|
|
|
|
|
|
|
ioRewind:
|
2019-05-28 10:52:40 +10:00
|
|
|
call ioResetCounters ; sets HL to 0
|
2019-05-17 11:15:00 +10:00
|
|
|
jr _ioSeek
|
|
|
|
|
2019-05-28 10:52:40 +10:00
|
|
|
ioResetCounters:
|
2019-05-18 04:58:16 +10:00
|
|
|
ld hl, 0
|
|
|
|
ld (IO_PC), hl
|
2019-05-28 10:52:40 +10:00
|
|
|
ld (IO_LINENO), hl
|
|
|
|
ld (IO_SAVED_LINENO), hl
|
2019-05-18 04:58:16 +10:00
|
|
|
ret
|
|
|
|
|
2019-05-17 11:15:00 +10:00
|
|
|
; always in absolute mode (A = 0)
|
|
|
|
_ioSeek:
|
|
|
|
call ioInInclude
|
|
|
|
ld a, 0 ; don't alter flags
|
|
|
|
jr nz, .include
|
|
|
|
; normal mode, seek in IN stream
|
2019-06-05 01:53:02 +10:00
|
|
|
ld ix, IO_IN_BLK
|
|
|
|
jp _blkSeek
|
2019-05-17 11:15:00 +10:00
|
|
|
.include:
|
|
|
|
; We're in "include mode", seek in FS
|
2019-06-05 10:45:01 +10:00
|
|
|
ld ix, IO_INCLUDE_BLK
|
|
|
|
jp _blkSeek ; returns
|
2019-05-11 10:32:05 +10:00
|
|
|
|
2019-05-17 11:15:00 +10:00
|
|
|
_ioTell:
|
|
|
|
call ioInInclude
|
|
|
|
jp nz, .include
|
|
|
|
; normal mode, seek in IN stream
|
2019-06-05 01:53:02 +10:00
|
|
|
ld ix, IO_IN_BLK
|
|
|
|
jp _blkTell
|
2019-05-17 11:15:00 +10:00
|
|
|
.include:
|
|
|
|
; We're in "include mode", tell from FS
|
2019-06-05 10:45:01 +10:00
|
|
|
ld ix, IO_INCLUDE_BLK
|
|
|
|
jp _blkTell ; returns
|
2019-05-17 11:15:00 +10:00
|
|
|
|
|
|
|
; Sets Z according to whether we're inside an include
|
2019-06-15 10:26:39 +10:00
|
|
|
; Z is set when we're *not* in includes. A bit weird, I know...
|
2019-05-17 11:15:00 +10:00
|
|
|
ioInInclude:
|
|
|
|
ld a, (IO_IN_INCLUDE)
|
|
|
|
or a ; cp 0
|
|
|
|
ret
|
|
|
|
|
|
|
|
; Open include file name specified in (HL).
|
|
|
|
; Sets Z on success, unset on error.
|
|
|
|
ioOpenInclude:
|
2019-06-20 01:42:39 +10:00
|
|
|
call ioPrintLN
|
2019-05-17 23:50:11 +10:00
|
|
|
call fsFindFN
|
2019-05-17 11:15:00 +10:00
|
|
|
ret nz
|
2019-06-01 04:06:24 +10:00
|
|
|
ld ix, IO_INCLUDE_HDL
|
2019-05-17 23:50:11 +10:00
|
|
|
call fsOpen
|
2019-05-17 11:15:00 +10:00
|
|
|
ld a, 1
|
|
|
|
ld (IO_IN_INCLUDE), a
|
2019-05-28 23:57:29 +10:00
|
|
|
ld hl, 0
|
|
|
|
ld (IO_INC_LINENO), hl
|
2019-06-05 10:45:01 +10:00
|
|
|
xor a
|
|
|
|
ld ix, IO_INCLUDE_BLK
|
|
|
|
call _blkSeek
|
2019-05-17 11:15:00 +10:00
|
|
|
cp a ; ensure Z
|
|
|
|
ret
|
|
|
|
|
2019-07-22 02:58:02 +10:00
|
|
|
; Open file specified in (HL) and spit its contents through ioPutC
|
|
|
|
; Sets Z on success.
|
|
|
|
ioSpitBin:
|
|
|
|
call fsFindFN
|
|
|
|
ret nz
|
|
|
|
push hl ; --> lvl 1
|
|
|
|
ld ix, IO_BIN_HDL
|
|
|
|
call fsOpen
|
|
|
|
ld hl, 0
|
|
|
|
.loop:
|
|
|
|
ld ix, IO_BIN_HDL
|
|
|
|
call fsGetC
|
|
|
|
jr nz, .loopend
|
|
|
|
call ioPutC
|
|
|
|
inc hl
|
|
|
|
jr .loop
|
|
|
|
.loopend:
|
|
|
|
pop hl ; <-- lvl 1
|
|
|
|
cp a ; ensure Z
|
|
|
|
ret
|
|
|
|
|
2019-05-28 23:57:29 +10:00
|
|
|
; Return current lineno in HL and, if in an include, its lineno in DE.
|
|
|
|
; If not in an include, DE is set to 0
|
2019-05-28 10:52:40 +10:00
|
|
|
ioLineNo:
|
2019-05-28 23:57:29 +10:00
|
|
|
push af
|
2019-05-28 10:52:40 +10:00
|
|
|
ld hl, (IO_LINENO)
|
2019-05-28 23:57:29 +10:00
|
|
|
ld de, 0
|
|
|
|
call ioInInclude
|
|
|
|
jr z, .end
|
|
|
|
ld de, (IO_INC_LINENO)
|
|
|
|
.end:
|
|
|
|
pop af
|
2019-05-28 10:52:40 +10:00
|
|
|
ret
|
2019-05-28 23:57:29 +10:00
|
|
|
|
2019-06-05 10:45:01 +10:00
|
|
|
_ioIncGetC:
|
|
|
|
ld ix, IO_INCLUDE_HDL
|
|
|
|
jp fsGetC
|
|
|
|
|
|
|
|
_ioIncBlk:
|
|
|
|
.dw _ioIncGetC, unsetZ
|
|
|
|
|
2019-06-20 01:42:39 +10:00
|
|
|
; call printstr followed by newline
|
|
|
|
ioPrintLN:
|
|
|
|
push hl
|
|
|
|
call printstr
|
|
|
|
ld hl, .sCRLF
|
|
|
|
call printstr
|
|
|
|
pop hl
|
|
|
|
ret
|
|
|
|
.sCRLF:
|
|
|
|
.db 0x0a, 0x0d, 0
|