|
- ; I/Os in zasm
- ;
- ; As a general rule, I/O in zasm is pretty straightforward. 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).
- ;
- ; This unit also has the responsibility of counting the number of written bytes,
- ; maintaining IO_PC and of properly disabling output on first pass.
- ;
- ; On top of that, this unit has the responsibility of keeping track of the
- ; current lineno. Whenever GetB is called, we check if the fetched byte 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.
- ;
- ; 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 ioGetB (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 ioGetB 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, GetB, 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.
-
- ; *** Variables ***
- .equ IO_IN_BLK IO_RAMSTART
- .equ IO_OUT_BLK IO_IN_BLK+BLOCKDEV_SIZE
- ; Save pos for ioSavePos and ioRecallPos
- .equ IO_SAVED_POS IO_OUT_BLK+BLOCKDEV_SIZE
- ; File handle for included source
- .equ IO_INCLUDE_HDL IO_SAVED_POS+2
- ; blkdev for include file
- .equ IO_INCLUDE_BLK IO_INCLUDE_HDL+FS_HANDLE_SIZE
- ; see ioPutBack below
- .equ IO_PUTBACK_BUF IO_INCLUDE_BLK+BLOCKDEV_SIZE
- .equ IO_IN_INCLUDE IO_PUTBACK_BUF+1
- .equ IO_PC IO_IN_INCLUDE+1
- ; Current lineno in top-level file
- .equ IO_LINENO IO_PC+2
- ; Current lineno in include file
- .equ IO_INC_LINENO IO_LINENO+2
- ; Line number (can be top-level or include) when ioSavePos was last called.
- .equ IO_SAVED_LINENO IO_INC_LINENO+2
- ; Handle for the ioSpitBin
- .equ IO_BIN_HDL IO_SAVED_LINENO+2
- .equ IO_RAMEND IO_BIN_HDL+FS_HANDLE_SIZE
-
- ; *** Code ***
-
- ioInit:
- xor a
- ld (IO_PUTBACK_BUF), a
- ld (IO_IN_INCLUDE), a
- ld de, IO_INCLUDE_BLK
- ld hl, _ioIncBlk
- call blkSet
- jp ioResetCounters
-
- ioGetB:
- ld a, (IO_PUTBACK_BUF)
- or a ; cp 0
- jr nz, .getback
- call ioInInclude
- jr z, .normalmode
- ; We're in "include mode", read from FS
- ld ix, IO_INCLUDE_BLK
- call _blkGetB
- jr nz, .includeEOF
- cp 0x0a ; newline
- ret nz ; not newline? nothing to do
- ; 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
- ld (IO_INC_LINENO), hl
- pop hl
- ret
-
- .includeEOF:
- ; 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
- ld (IO_INC_LINENO), a
- ld (IO_INC_LINENO+1), a
- ; continue on to "normal" reading. We don't want to return our zero
- .normalmode:
- ; normal mode, read from IN stream
- ld ix, IO_IN_BLK
- call _blkGetB
- 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
-
- .getback:
- push af
- xor a
- ld (IO_PUTBACK_BUF), a
- pop af
- ret
-
- _callIX:
- jp (ix)
- ret
-
- ; Put back non-zero character A into the "ioGetB stack". The next ioGetB call,
- ; instead of reading from IO_IN_BLK, will return that character. That's the
- ; easiest way I found to handle the readWord/gotoNextLine problem.
- ioPutBack:
- ld (IO_PUTBACK_BUF), a
- ret
-
- ioPutB:
- push hl ; --> lvl 1
- ld hl, (IO_PC)
- inc hl
- ld (IO_PC), hl
- pop hl ; <-- lvl 1
- push af ; --> lvl 1
- call zasmIsFirstPass
- jr z, .skip
- pop af ; <-- lvl 1
- push ix ; --> lvl 1
- ld ix, IO_OUT_BLK
- call _blkPutB
- pop ix ; <-- lvl 1
- ret
- .skip:
- pop af ; <-- lvl 1
- cp a ; ensure Z
- ret
-
- ioSavePos:
- ld hl, (IO_LINENO)
- call ioInInclude
- jr z, .skip
- ld hl, (IO_INC_LINENO)
- .skip:
- ld (IO_SAVED_LINENO), hl
- call _ioTell
- ld (IO_SAVED_POS), hl
- ret
-
- ioRecallPos:
- ld hl, (IO_SAVED_LINENO)
- call ioInInclude
- jr nz, .include
- ld (IO_LINENO), hl
- jr .recallpos
- .include:
- ld (IO_INC_LINENO), hl
- .recallpos:
- ld hl, (IO_SAVED_POS)
- jr _ioSeek
-
- ioRewind:
- call ioResetCounters ; sets HL to 0
- jr _ioSeek
-
- ioResetCounters:
- ld hl, 0
- ld (IO_PC), hl
- ld (IO_LINENO), hl
- ld (IO_SAVED_LINENO), hl
- ret
-
- ; 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
- ld ix, IO_IN_BLK
- jp _blkSeek
- .include:
- ; We're in "include mode", seek in FS
- ld ix, IO_INCLUDE_BLK
- jp _blkSeek ; returns
-
- _ioTell:
- call ioInInclude
- jp nz, .include
- ; normal mode, seek in IN stream
- ld ix, IO_IN_BLK
- jp _blkTell
- .include:
- ; We're in "include mode", tell from FS
- ld ix, IO_INCLUDE_BLK
- jp _blkTell ; returns
-
- ; Sets Z according to whether we're inside an include
- ; Z is set when we're *not* in includes. A bit weird, I know...
- 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:
- call ioPrintLN
- call fsFindFN
- ret nz
- ld ix, IO_INCLUDE_HDL
- call fsOpen
- ld a, 1
- ld (IO_IN_INCLUDE), a
- ld hl, 0
- ld (IO_INC_LINENO), hl
- xor a
- ld ix, IO_INCLUDE_BLK
- call _blkSeek
- cp a ; ensure Z
- ret
-
- ; Open file specified in (HL) and spit its contents through ioPutB
- ; 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 fsGetB
- jr nz, .loopend
- call ioPutB
- inc hl
- jr .loop
- .loopend:
- pop hl ; <-- lvl 1
- cp a ; ensure Z
- ret
-
- ; Return current lineno in HL and, if in an include, its lineno in DE.
- ; If not in an include, DE is set to 0
- ioLineNo:
- push af
- ld hl, (IO_LINENO)
- ld de, 0
- call ioInInclude
- jr z, .end
- ld de, (IO_INC_LINENO)
- .end:
- pop af
- ret
-
- _ioIncGetB:
- ld ix, IO_INCLUDE_HDL
- jp fsGetB
-
- _ioIncBlk:
- .dw _ioIncGetB, unsetZ
-
- ; call printstr followed by newline
- ioPrintLN:
- push hl
- call printstr
- ld hl, .sCRLF
- call printstr
- pop hl
- ret
- .sCRLF:
- .db 0x0a, 0x0d, 0
|