4 * (C) Copyright 2001-2002 Ullrich von Bassewitz (uz@cc65.org)
6 * This is the basic layer for all scanf type functions. It should get
7 * rewritten in assembler at some time in the future, so most of the code
8 * is not as elegant as it could be.
24 /*****************************************************************************/
25 /* SetJmp return codes */
26 /*****************************************************************************/
30 #define RC_OK 0 /* Regular call */
31 #define RC_EOF 1 /* EOF reached */
32 #define RC_NOCONV 2 /* No conversion possible */
36 /*****************************************************************************/
38 /*****************************************************************************/
42 static struct indesc* D; /* Copy of function argument */
43 static va_list ap; /* Copy of function argument */
44 static jmp_buf JumpBuf; /* Label that is used in case of EOF */
45 static char C; /* Character from input */
46 static unsigned Width; /* Maximum field width */
47 static long IntVal; /* Converted int value */
48 static unsigned Conversions; /* Number of conversions */
51 static unsigned char Positive; /* Flag for positive value */
52 static unsigned char NoAssign; /* Supppress assigment */
53 static unsigned char IsShort; /* Short type */
54 static unsigned char IsLong; /* Long type */
55 static unsigned char Invert; /* Do we need to invert the charset? */
56 static unsigned char CharSet[32]; /* 32 * 8 bits = 256 bits */
57 static const unsigned char Bits[8] = {
58 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
63 /*****************************************************************************/
65 /*****************************************************************************/
69 static void AddCharToSet (unsigned char C)
70 /* Set the given bit in the character set */
81 asm ("lda %v,y", Bits);
82 asm ("ora %v,x", CharSet);
83 asm ("sta %v,x", CharSet);
88 static unsigned char IsCharInSet (unsigned char C)
89 /* Check if the given char is part of the character set */
100 asm ("lda %v,y", Bits);
101 asm ("and %v,x", CharSet);
108 static void InvertCharSet (void)
109 /* Invert the character set */
111 asm ("ldy #%b", sizeof (CharSet) - 1);
113 asm ("lda %v,y", CharSet);
115 asm ("sta %v,y", CharSet);
122 /*****************************************************************************/
124 /*****************************************************************************/
128 static void ReadChar (void)
129 /* Get an input character, count characters */
137 static void SkipWhite (void)
138 /* Skip white space in the input and return the first non white character */
140 while (isspace (C)) {
147 static void ReadSign (void)
148 /* Read an optional sign and skip it. Store 1 in Positive if the value is
149 * positive, store 0 otherwise.
167 static unsigned char HexVal (char C)
168 /* Convert a digit to a value */
174 return C - toupper (C) + ('A' + 10);
180 static void ReadInt (unsigned char Base)
181 /* Read an integer and store it into IntVal */
183 /* Value must start with a digit */
185 longjmp (JumpBuf, RC_NOCONV);
190 while (isxdigit (C) && Width-- > 0) {
191 IntVal = IntVal * Base + HexVal (C);
195 /* One more conversion */
201 static void AssignInt (void)
202 /* Assign the integer value in Val to the next argument. The function makes
203 * several non portable assumptions to reduce code size:
204 * - int and unsigned types have the same representation
205 * - short and int have the same representation.
206 * - all pointer types have the same representation.
211 /* Get the next argument pointer */
212 __AX__ = (unsigned) va_arg (ap, void*);
214 /* Store the argument pointer into ptr1 */
218 /* Get the number of bytes-1 to copy */
220 asm ("lda %v", IsLong);
224 /* Assign the integer value */
225 asm ("L1: lda %v,y", IntVal);
226 asm ("sta (ptr1),y");
235 int _scanf (struct indesc* D_, register const char* format, va_list ap_)
236 /* This is the routine used to do the actual work. It is called from several
237 * types of wrappers to implement the actual ISO xxscanf functions.
240 char F; /* Character from format string */
241 unsigned char Result; /* setjmp result */
243 unsigned char Base; /* Integer base in %i */
244 unsigned char HaveWidth; /* True if a width was given */
245 char Start; /* Start of range */
247 /* Place copies of the arguments into global variables. This is not very
248 * nice, but on a 6502 platform it gives better code, since the values
249 * do not have to be passed as parameters.
254 /* Initialize variables */
258 /* Set up the jump label. The get() routine will use this label when EOF
261 Result = setjmp (JumpBuf);
262 if (Result == RC_OK) {
265 /* Get the next input character */
268 /* Walk over the format string */
269 while (F = *format++) {
271 /* Check for a conversion */
272 if (F != '%' || *format == '%') {
274 /* %% or any char other than % */
279 /* Check for a match */
282 /* Special white space handling: Any whitespace matches
283 * any amount of whitespace including none(!). So this
284 * match will never fail.
291 /* A mismatch. We will stop scanning the input and return
292 * the number of conversions.
298 /* A match. Read the next input character and start over */
305 /* A conversion. Skip the percent sign. */
308 /* Initialize variables */
315 /* Check for flags. */
321 /* ### Non portable ### */
322 Width = Width * 10 + (F & 0x0F);
324 } while (isdigit (F));
327 case '*': NoAssign = 1; break;
328 case 'h': IsShort = 1; break;
330 case 'L': IsLong = 1; break;
331 default: goto FlagsDone;
338 /* Check for the actual conversion character */
344 /* Optionally signed decimal integer */
355 /* Optionally signed integer with a base */
380 /* Unsigned octal integer */
387 /* Unsigned decimal integer */
395 /* Unsigned hexadecimal integer */
405 /* Optionally signed float */
406 longjmp (JumpBuf, RC_NOCONV);
410 /* Whitespace terminated string */
413 S = va_arg (ap, char*);
415 while (!isspace (C) && Width--) {
421 /* Terminate the string just read */
429 /* Fixed length string, NOT zero terminated */
431 /* No width given, default is 1 */
435 S = va_arg (ap, char*);
441 /* Just skip as many chars as given */
450 /* String using characters from a set */
453 memset (CharSet, 0, sizeof (CharSet));
463 /* Read the characters that are part of the set */
464 while (F != ']' && F != '\0') {
465 if (*format == '-') {
466 /* A range. Get start and end, skip the '-' */
471 /* '-' as last char means: include '-' */
472 AddCharToSet (Start);
474 } else if (F != '\0') {
475 /* Include all chars in the range */
477 AddCharToSet (Start);
483 /* Get next char after range */
487 /* Just a character */
494 /* Invert the set if requested */
499 /* We have the set in CharSet. Read characters and
500 * store them into a string while they are part of
504 S = va_arg (ap, char*);
505 while (IsCharInSet (C) && Width--) {
511 while (IsCharInSet (C) && Width--) {
519 /* Pointer, format is 0xABCD */
522 longjmp (JumpBuf, RC_NOCONV);
525 if (C != 'x' && C != 'X') {
526 longjmp (JumpBuf, RC_NOCONV);
534 /* Store characters consumed so far */
540 /* Invalid conversion */
541 longjmp (JumpBuf, RC_NOCONV);
546 /* Skip the format char */
553 } else if (Result == RC_EOF) {
555 /* Jump via JumpBuf means EOF on input */
556 if (D->ccount == 0) {
557 /* Special case: error */
563 /* Return the number of conversions */