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
|
; Altirra BASIC - READ/INPUT data module
; Copyright (C) 2014 Avery Lee, All Rights Reserved.
;
; Copying and distribution of this file, with or without modification,
; are permitted in any medium without royalty provided the copyright
; notice and this notice are preserved. This file is offered as-is,
; without any warranty.
?read_start = *
;===========================================================================
; INPUT [#aexp{,|;}] var [,var...]
;
; Reads input from the console or an I/O channel.
;
; If the IOCB is #0, either implicitly or explicitly, a question mark is
; printed to the console first. Whether the IOCB number is followed by
; a comma or semicolon doesn't matter.
;
; Leading spaces are included for string input and skipped for numeric
; input. Numeric input is considered invalid if the immediately following
; character is not a comma or a string of spaces (spaces followed by
; a comma is _not_ accepted). Numeric inputs may not be empty -- either
; a blank line or no input after a comma will cause a numeric input to
; fail.
;
; When multiple variables are supplied, subsequent variables are read in
; from the same line if more values are available as a comma separated
; list. If not, a new line is read in. String reads eat the comma as part
; of the string and always force a new line.
;
; The default line buffer is always used (255 bytes), even if a larger
; string buffer is supplied. The string is silently truncated as necessary.
; EOLs are not placed into string arrays. If the string array is not
; dimensioned, error 9 is issued, but only when that string array is
; reached and after input has been read.
;
; End of file gives an EOF error.
;
;==========================================================================
; READ var [,var...]
;
; Reads input from DATA lines in a program.
;
; Quirks:
; - Atari BASIC caches the pointer and offset within the DATA line and
; does not update them when tables are adjusted. This means that
; editing the current program, even just by adding new variables,
; will cause corrupted READs from immediate mode unless a RESTORE is
; done.
;
; Entry (stDataRead):
; IOCBIDX = IOCB to use for INPUT, or -1 for READ
;
.nowarn .proc stDataRead
need_data_line:
;check if we are at line 32768
;#ASSERT dw(dataptr) >= dw(stmtab) && dw(dataptr) < dw(starp)
ldy #1
lda (dataptr),y
bpl data_line_valid
jmp errorOutOfData
.def :stInput = *
;parse optional #iocb and set iocbidx
jsr evaluateHashIOCBOpt
bcs read_loop
;eat following comma or semicolon
inc exLineOffset
bne read_loop
.def :stRead = *
lda #$ff
sta iocbidx
sta iocbidx2
read_loop:
;check if IOCB #0 was specified and we're in INPUT mode, and if so,
;print a question mark. note that this happens even if the IOCB was
;specified, i.e. INPUT #0,A$, and that INPUT #16;A$ should not print
;a prompt.
ldx iocbidx2
bne skip_prompt
;print ? prompt
lda #'?'
jsr IoPutCharDirect
skip_prompt:
;reset read pointer
lda #0
sta cix
;check if we are doing READ or INPUT
ldx iocbidx
bpl read_line_input
;we're doing READ -- check if we have a valid cached line
lda dataptr+1
bne have_data_line
;call into exec to get next line
ldx dataln
lda dataln+1
jsr exFindLineInt
sta dataptr
sty dataptr+1
;reset starting index
have_data_line_2:
mva #0 dataoff
;cache off line length
ldy #2
mva (dataptr),y dataLnEnd
have_data_line:
;check if we have a valid index into a DATA statement already
ldy dataoff
beq need_data_line
cpy dataLnEnd
bcc have_data
data_line_end:
;jump to next line
lda dataLnEnd
ldx #dataptr
jsr VarAdvancePtrX
;##TRACE "Data: Advancing to line %u ($%04X)" dw(dw(dataptr)) dw(dataptr)
;stash off line number
ldy #1
mva (0,x) dataln ;!! X = dataptr
mva (dataptr),y dataln+1
bpl have_data_line_2 ;!! - unconditional
data_line_valid:
;##TRACE "Data: Scanning for DATA token on line %u ($%04X)" dw(dw(dataptr)) dw(dataptr)
ldy #3
data_line_scan:
;scan the line to find the next DATA statement
cpy dataLnEnd
beq data_line_end
;fetch next statement token
iny
lda (dataptr),y
;is it the DATA token?
cmp #TOK_DATA
beq have_data_stmt
;no... jump to next statement
dey
lda (dataptr),y
tay
bne data_line_scan ;!! - unconditional
have_data_stmt:
iny
have_data:
sty cix
lda dataptr
ldy dataptr+1
jsr IoSetInbuffYA
;##TRACE "Beginning READ with data: $%04X+$%02X [%.*s]" dw(dataptr) db(cix) db(dataLnEnd)-db(cix) dw(dataptr)+db(cix)
bne parse_loop ;!! - unconditional
is_eol:
;force end of DATA statement, if we are reading one
ldy dataLnEnd
is_comma:
;check if we are processing a DATA statement -- if so, stash the current
;offset.
bit iocbidx
spl:sty dataoff
;check if we have more vars to read
;read current token and check for comma
jsr ExecGetComma
bne xit
;read new line if line is empty, else keep parsing
txa
bne parse_loop
beq read_loop ;!! - unconditional
read_line_input:
;read line to LBUFF
jsr IoSetupReadLineLDBUFA_X
jsr ioChecked
parse_loop:
;get next variable
jsr evaluateVar
;check type of variable
lda expType
bpl is_numeric
;we have a string... compute the remaining length
;
;READ statements will stop string reads at a comma; INPUT
;statements will consume the comma. Note that we must NOT actually
;consume the comma here, as the end of read routine needs to see it
;in case we have this case:
;
; DATA ABC,
;
;Eating the comma here would cause the end of read code to jump to
;the next line, preventing the empty trailing string from being read.
;
ldx #fr1
jsr zf1
ldy cix
sty fr1 ;set src offset
len_loop:
lda (inbuff),y
cmp #$9b
beq len_loop_end
bit iocbidx
bpl no_comma_stop
cmp #','
beq len_loop_end
no_comma_stop:
iny
inc fr1+2
bne len_loop
len_loop_end:
;warp to end of the input
sty cix
;jump through string assignment code to set string array
ldx #fr1
stx expAsnCtx ;set assignment mode to force length update
ldy #inbuff
jsr IntAdd
jsr funAssignStr._read_entry
advance:
;advance to next input, checking for EOL or a comma -- we must
;do this before we store the parsed value and even if there are no
;other values to retrieve
ldy cix
lda (inbuff),y
;check if we had an EOL (and stash EOL flag in X)
eor #$9b
tax
beq is_eol
;not an EOL -- better be comma or it's an error
iny
sty cix
cmp #[','^$9b]
beq is_comma
parse_error:
jmp errorInputStmt
xit:
rts
is_numeric:
;attempt to parse out a number
jsr MathParseFP
bcs parse_error
;store numeric value to variable
;##TRACE "READ -> %g" fr0
jsr VarStoreFR0
beq advance ;!! - unconditional
.endp
;============================================================================
.echo "- READ/INPUT module length: ",*-?read_start
|