Scanf improvements by Greg King
git-svn-id: svn://svn.cc65.org/cc65/trunk@3377 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
@@ -29,7 +29,6 @@ puts.s
|
|||||||
qsort.s
|
qsort.s
|
||||||
realloc.s
|
realloc.s
|
||||||
rewind.s
|
rewind.s
|
||||||
scanf.s
|
|
||||||
sleep.s
|
sleep.s
|
||||||
strftime.s
|
strftime.s
|
||||||
strtok.s
|
strtok.s
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
# -*- makefile -*-
|
||||||
#
|
#
|
||||||
# makefile for CC65 runtime library
|
# makefile for CC65's common library
|
||||||
#
|
#
|
||||||
|
|
||||||
.SUFFIXES: .o .s .c
|
.SUFFIXES: .o .s .c
|
||||||
@@ -26,9 +27,19 @@ CFLAGS = -Osir -g -T -t $(SYS) --forget-inc-paths -I . -I ../../include
|
|||||||
%.o: %.s
|
%.o: %.s
|
||||||
@$(AS) -g -o $@ $(AFLAGS) $<
|
@$(AS) -g -o $@ $(AFLAGS) $<
|
||||||
|
|
||||||
|
#--------------------------------------------------------------------------
|
||||||
|
# Rules to help us see what code the compiler and assembler make.
|
||||||
|
|
||||||
|
%.s : %.c
|
||||||
|
@$(CC) $(CFLAGS) -S $<
|
||||||
|
|
||||||
|
%.lst : %.s
|
||||||
|
@$(AS) $(AFLAGS) -l -o /dev/null $<
|
||||||
|
|
||||||
#--------------------------------------------------------------------------
|
#--------------------------------------------------------------------------
|
||||||
# Object files
|
# Object files
|
||||||
|
|
||||||
|
# From C source-files
|
||||||
C_OBJS = _afailed.o \
|
C_OBJS = _afailed.o \
|
||||||
_aligned_malloc.o \
|
_aligned_malloc.o \
|
||||||
_hextab.o \
|
_hextab.o \
|
||||||
@@ -59,7 +70,6 @@ C_OBJS = _afailed.o \
|
|||||||
qsort.o \
|
qsort.o \
|
||||||
realloc.o \
|
realloc.o \
|
||||||
rewind.o \
|
rewind.o \
|
||||||
scanf.o \
|
|
||||||
sleep.o \
|
sleep.o \
|
||||||
strftime.o \
|
strftime.o \
|
||||||
strxfrm.o \
|
strxfrm.o \
|
||||||
@@ -67,7 +77,7 @@ C_OBJS = _afailed.o \
|
|||||||
system.o \
|
system.o \
|
||||||
timezone.o
|
timezone.o
|
||||||
|
|
||||||
|
# From assembly source-files
|
||||||
S_OBJS = _cwd.o \
|
S_OBJS = _cwd.o \
|
||||||
_fdesc.o \
|
_fdesc.o \
|
||||||
_file.o \
|
_file.o \
|
||||||
@@ -134,6 +144,7 @@ S_OBJS = _cwd.o \
|
|||||||
raise.o \
|
raise.o \
|
||||||
remove.o \
|
remove.o \
|
||||||
rename.o \
|
rename.o \
|
||||||
|
scanf.o \
|
||||||
setjmp.o \
|
setjmp.o \
|
||||||
signal.o \
|
signal.o \
|
||||||
sigtable.o \
|
sigtable.o \
|
||||||
@@ -183,10 +194,10 @@ S_OBJS = _cwd.o \
|
|||||||
all: $(C_OBJS) $(S_OBJS)
|
all: $(C_OBJS) $(S_OBJS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@rm -f *~
|
@$(RM) *~ *.lst
|
||||||
@rm -f $(C_OBJS:.o=.s)
|
@$(RM) $(C_OBJS:.o=.s)
|
||||||
@rm -f $(C_OBJS)
|
@$(RM) $(C_OBJS)
|
||||||
@rm -f $(S_OBJS)
|
@$(RM) $(S_OBJS)
|
||||||
|
|
||||||
zap: clean
|
zap: clean
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,38 @@
|
|||||||
/*
|
/*
|
||||||
* _scanf.c
|
* _scanf.c
|
||||||
*
|
*
|
||||||
* (C) Copyright 2001-2002 Ullrich von Bassewitz (uz@cc65.org)
|
* (c) Copyright 2001-2005, Ullrich von Bassewitz <uz@cc65.org>
|
||||||
|
* 2005-01-24, Greg King <gngking@erols.com>
|
||||||
*
|
*
|
||||||
* This is the basic layer for all scanf type functions. It should get
|
* This is the basic layer for all scanf-type functions. It should be
|
||||||
* rewritten in assembler at some time in the future, so most of the code
|
* rewritten in assembly, at some time in the future. So, some of the code
|
||||||
* is not as elegant as it could be.
|
* is not as elegant as it could be.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <ctype.h>
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
/* _scanf() can give EOF to these functions. But, the macroes can't
|
||||||
|
** understand it; so, they are removed.
|
||||||
|
*/
|
||||||
|
#undef isspace
|
||||||
|
#undef isxdigit
|
||||||
|
|
||||||
#include "_scanf.h"
|
#include "_scanf.h"
|
||||||
|
|
||||||
|
extern void __fastcall__ _seterrno (unsigned char code);
|
||||||
|
|
||||||
|
#pragma staticlocals(on)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@@ -28,9 +41,11 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define RC_OK 0 /* Regular call */
|
enum {
|
||||||
#define RC_EOF 1 /* EOF reached */
|
RC_OK, /* setjmp() call */
|
||||||
#define RC_NOCONV 2 /* No conversion possible */
|
RC_NOCONV, /* No conversion possible */
|
||||||
|
RC_EOF /* EOF reached */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -40,22 +55,25 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static struct scanfdata* D_; /* Copy of function argument */
|
static const char* format; /* Copy of function argument */
|
||||||
|
static const struct scanfdata* D_; /* Copy of function argument */
|
||||||
static va_list ap; /* Copy of function argument */
|
static va_list ap; /* Copy of function argument */
|
||||||
static jmp_buf JumpBuf; /* Label that is used in case of EOF */
|
static jmp_buf JumpBuf; /* "Label" that is used for failures */
|
||||||
|
static char F; /* Character from format string */
|
||||||
static unsigned CharCount; /* Characters read so far */
|
static unsigned CharCount; /* Characters read so far */
|
||||||
static int C; /* Character from input */
|
static int C; /* Character from input */
|
||||||
static unsigned Width; /* Maximum field width */
|
static unsigned Width; /* Maximum field width */
|
||||||
static long IntVal; /* Converted int value */
|
static long IntVal; /* Converted int value */
|
||||||
static unsigned Conversions; /* Number of conversions */
|
static int Assignments; /* Number of assignments */
|
||||||
static unsigned char IntBytes; /* Number of bytes-1 for int conversions */
|
static unsigned char IntBytes; /* Number of bytes-1 for int conversions */
|
||||||
|
|
||||||
/* Flags */
|
/* Flags */
|
||||||
static unsigned char Positive; /* Flag for positive value */
|
static bool Converted; /* Some object was converted */
|
||||||
static unsigned char NoAssign; /* Supppress assigment */
|
static bool Positive; /* Flag for positive value */
|
||||||
static unsigned char Invert; /* Do we need to invert the charset? */
|
static bool NoAssign; /* Suppress assignment */
|
||||||
static unsigned char CharSet[32]; /* 32 * 8 bits = 256 bits */
|
static bool Invert; /* Do we need to invert the charset? */
|
||||||
static const unsigned char Bits[8] = {
|
static unsigned char CharSet[(1+UCHAR_MAX)/CHAR_BIT];
|
||||||
|
static const unsigned char Bits[CHAR_BIT] = {
|
||||||
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
|
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -76,43 +94,62 @@ static const unsigned char Bits[8] = {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void __fastcall__ AddCharToSet (unsigned char C)
|
/* We don't want the optimizer to ruin our "perfect" ;-)
|
||||||
|
* assembly code!
|
||||||
|
*/
|
||||||
|
#pragma optimize (push, off)
|
||||||
|
|
||||||
|
static unsigned FindBit (void)
|
||||||
|
/* Locate the character's bit in the charset array.
|
||||||
|
* < .A - Argument character
|
||||||
|
* > .X - Offset of the byte in the character-set mask
|
||||||
|
* > .A - Bit-mask
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
asm ("pha");
|
||||||
|
asm ("lsr a"); /* Divide by CHAR_BIT */
|
||||||
|
asm ("lsr a");
|
||||||
|
asm ("lsr a");
|
||||||
|
asm ("tax"); /* Byte's offset */
|
||||||
|
asm ("pla");
|
||||||
|
asm ("and #%b", CHAR_BIT-1);
|
||||||
|
asm ("tay"); /* Bit's offset */
|
||||||
|
asm ("lda %v,y", Bits);
|
||||||
|
return (unsigned) __AX__;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma optimize (pop)
|
||||||
|
|
||||||
|
|
||||||
|
static void __fastcall__ AddCharToSet (unsigned char /* C */)
|
||||||
/* Set the given bit in the character set */
|
/* Set the given bit in the character set */
|
||||||
{
|
{
|
||||||
asm ("ldy #%o", C);
|
FindBit();
|
||||||
asm ("lda (sp),y");
|
|
||||||
asm ("lsr a");
|
|
||||||
asm ("lsr a");
|
|
||||||
asm ("lsr a");
|
|
||||||
asm ("tax");
|
|
||||||
asm ("lda (sp),y");
|
|
||||||
asm ("and #$07");
|
|
||||||
asm ("tay");
|
|
||||||
asm ("lda %v,y", Bits);
|
|
||||||
asm ("ora %v,x", CharSet);
|
asm ("ora %v,x", CharSet);
|
||||||
asm ("sta %v,x", CharSet);
|
asm ("sta %v,x", CharSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static unsigned char __fastcall__ IsCharInSet (unsigned char C)
|
#pragma optimize (push, off)
|
||||||
/* Check if the given char is part of the character set */
|
|
||||||
|
static unsigned char IsCharInSet (void)
|
||||||
|
/* Check if the char. is part of the character set. */
|
||||||
{
|
{
|
||||||
asm ("ldy #%o", C);
|
/* Get the character from C. */
|
||||||
asm ("lda (sp),y");
|
asm ("lda #$00");
|
||||||
asm ("lsr a");
|
asm ("ldx %v+1", C);
|
||||||
asm ("lsr a");
|
asm ("bne L1"); /* EOF never is in the set */
|
||||||
asm ("lsr a");
|
asm ("lda %v", C);
|
||||||
asm ("tax");
|
FindBit();
|
||||||
asm ("lda (sp),y");
|
|
||||||
asm ("and #$07");
|
|
||||||
asm ("tay");
|
|
||||||
asm ("lda %v,y", Bits);
|
|
||||||
asm ("and %v,x", CharSet);
|
asm ("and %v,x", CharSet);
|
||||||
|
asm ("L1:");
|
||||||
asm ("ldx #$00");
|
asm ("ldx #$00");
|
||||||
return __AX__;
|
return (unsigned char) __AX__;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma optimize (pop)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void InvertCharSet (void)
|
static void InvertCharSet (void)
|
||||||
@@ -135,10 +172,49 @@ static void InvertCharSet (void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void __fastcall__ Error (unsigned char Code)
|
static void PushBack (void)
|
||||||
/* Does a longjmp using the given code */
|
/* Push back the last (unused) character, provided it is not EOF. */
|
||||||
{
|
{
|
||||||
longjmp (JumpBuf, Code);
|
/* Get the character from C. */
|
||||||
|
/* Only the high-byte needs to be checked for EOF. */
|
||||||
|
asm ("ldx %v+1", C);
|
||||||
|
asm ("bne %g", Done);
|
||||||
|
asm ("lda %v", C);
|
||||||
|
|
||||||
|
/* Put unget()'s first argument on the stack. */
|
||||||
|
asm ("jsr pushax");
|
||||||
|
|
||||||
|
/* Copy D into the zero-page. */
|
||||||
|
(const struct scanfdata*) __AX__ = D_;
|
||||||
|
asm ("sta ptr1");
|
||||||
|
asm ("stx ptr1+1");
|
||||||
|
|
||||||
|
/* Copy the unget vector to jmpvec. */
|
||||||
|
asm ("ldy #%b", offsetof (struct scanfdata, unget));
|
||||||
|
asm ("lda (ptr1),y");
|
||||||
|
asm ("sta jmpvec+1");
|
||||||
|
asm ("iny");
|
||||||
|
asm ("lda (ptr1),y");
|
||||||
|
asm ("sta jmpvec+2");
|
||||||
|
|
||||||
|
/* Load D->data into __AX__. */
|
||||||
|
asm ("ldy #%b", offsetof (struct scanfdata, data) + 1);
|
||||||
|
asm ("lda (ptr1),y");
|
||||||
|
asm ("tax");
|
||||||
|
asm ("dey");
|
||||||
|
asm ("lda (ptr1),y");
|
||||||
|
|
||||||
|
/* Call the unget routine. */
|
||||||
|
asm ("jsr jmpvec");
|
||||||
|
|
||||||
|
/* Take back that character's count. */
|
||||||
|
asm ("lda %v", CharCount);
|
||||||
|
asm ("bne %g", Yank);
|
||||||
|
asm ("dec %v+1", CharCount);
|
||||||
|
Yank:
|
||||||
|
asm ("dec %v", CharCount);
|
||||||
|
|
||||||
|
Done:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -174,11 +250,11 @@ static void ReadChar (void)
|
|||||||
asm ("sta %v", C);
|
asm ("sta %v", C);
|
||||||
asm ("stx %v+1", C);
|
asm ("stx %v+1", C);
|
||||||
|
|
||||||
/* If C is not EOF, bump the character counter. */
|
/* If C is EOF, don't bump the character counter.
|
||||||
|
* Only the high-byte needs to be checked.
|
||||||
|
*/
|
||||||
asm ("inx");
|
asm ("inx");
|
||||||
asm ("bne %g", Done);
|
asm ("beq %g", Done);
|
||||||
asm ("cmp #$FF");
|
|
||||||
asm ("bne %g", Done);
|
|
||||||
|
|
||||||
/* Must bump CharCount. */
|
/* Must bump CharCount. */
|
||||||
asm ("inc %v", CharCount);
|
asm ("inc %v", CharCount);
|
||||||
@@ -190,13 +266,32 @@ Done:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void ReadCharWithCheck (void)
|
#pragma optimize (push, off)
|
||||||
/* Get an input char, use longjmp in case of EOF */
|
|
||||||
|
static void __fastcall__ Error (unsigned char /* Code */)
|
||||||
|
/* Does a longjmp using the given code */
|
||||||
{
|
{
|
||||||
ReadChar ();
|
asm ("pha");
|
||||||
if (C == EOF) {
|
(char*) __AX__ = JumpBuf;
|
||||||
Error (RC_EOF);
|
asm ("jsr pushax");
|
||||||
|
asm ("pla");
|
||||||
|
asm ("ldx #>0");
|
||||||
|
asm ("jmp %v", longjmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma optimize (pop)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void CheckEnd (void)
|
||||||
|
/* Stop a scan if it prematurely reaches the end of a string or a file. */
|
||||||
|
{
|
||||||
|
/* Only the high-byte needs to be checked for EOF. */
|
||||||
|
asm ("ldx %v+1", C);
|
||||||
|
asm ("beq %g", Done);
|
||||||
|
|
||||||
|
Error (RC_EOF);
|
||||||
|
Done:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -204,13 +299,15 @@ static void ReadCharWithCheck (void)
|
|||||||
static void SkipWhite (void)
|
static void SkipWhite (void)
|
||||||
/* Skip white space in the input and return the first non white character */
|
/* Skip white space in the input and return the first non white character */
|
||||||
{
|
{
|
||||||
while (isspace (C)) {
|
while ((bool) isspace (C)) {
|
||||||
ReadChar ();
|
ReadChar ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma optimize (push, off)
|
||||||
|
|
||||||
static void ReadSign (void)
|
static void ReadSign (void)
|
||||||
/* Read an optional sign and skip it. Store 1 in Positive if the value is
|
/* Read an optional sign and skip it. Store 1 in Positive if the value is
|
||||||
* positive, store 0 otherwise.
|
* positive, store 0 otherwise.
|
||||||
@@ -224,6 +321,7 @@ static void ReadSign (void)
|
|||||||
asm ("bne %g", NotNeg);
|
asm ("bne %g", NotNeg);
|
||||||
|
|
||||||
/* Negative value */
|
/* Negative value */
|
||||||
|
asm ("sta %v", Converted);
|
||||||
asm ("jsr %v", ReadChar);
|
asm ("jsr %v", ReadChar);
|
||||||
asm ("lda #$00"); /* Flag as negative */
|
asm ("lda #$00"); /* Flag as negative */
|
||||||
asm ("beq %g", Store);
|
asm ("beq %g", Store);
|
||||||
@@ -232,6 +330,7 @@ static void ReadSign (void)
|
|||||||
NotNeg:
|
NotNeg:
|
||||||
asm ("cmp #'+'");
|
asm ("cmp #'+'");
|
||||||
asm ("bne %g", Pos);
|
asm ("bne %g", Pos);
|
||||||
|
asm ("sta %v", Converted);
|
||||||
asm ("jsr %v", ReadChar); /* Skip the + sign */
|
asm ("jsr %v", ReadChar); /* Skip the + sign */
|
||||||
Pos:
|
Pos:
|
||||||
asm ("lda #$01"); /* Flag as positive */
|
asm ("lda #$01"); /* Flag as positive */
|
||||||
@@ -239,35 +338,59 @@ Store:
|
|||||||
asm ("sta %v", Positive);
|
asm ("sta %v", Positive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma optimize (pop)
|
||||||
|
|
||||||
|
|
||||||
static unsigned char HexVal (char C)
|
|
||||||
|
static unsigned char __fastcall__ HexVal (char C)
|
||||||
/* Convert a digit to a value */
|
/* Convert a digit to a value */
|
||||||
{
|
{
|
||||||
|
return (bool) isdigit (C) ?
|
||||||
if (isdigit (C)) {
|
C - '0' :
|
||||||
return C - '0';
|
(char) tolower ((int) C) - ('a' - 10);
|
||||||
} else {
|
|
||||||
return toupper (C) - ('A' - 10);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void __fastcall__ ReadInt (unsigned char Base)
|
||||||
|
/* Read an integer, and store it into IntVal. */
|
||||||
|
{
|
||||||
|
unsigned char Val, CharCount = 0;
|
||||||
|
|
||||||
|
/* Read the integer value */
|
||||||
|
IntVal = 0L;
|
||||||
|
while ((bool) isxdigit (C) && ++Width != 0
|
||||||
|
&& (Val = HexVal ((char) C)) < Base) {
|
||||||
|
++CharCount;
|
||||||
|
IntVal = IntVal * (long) Base + (long) Val;
|
||||||
|
ReadChar ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we didn't convert anything, it's a failure. */
|
||||||
|
if (CharCount == 0) {
|
||||||
|
Error (RC_NOCONV);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Another conversion */
|
||||||
|
Converted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void AssignInt (void)
|
static void AssignInt (void)
|
||||||
/* Assign the integer value in Val to the next argument. The function makes
|
/* Assign the integer value in Val to the next argument. The function makes
|
||||||
* several non portable assumptions to reduce code size:
|
* several non-portable assumptions, to reduce code size:
|
||||||
* - int and unsigned types have the same representation
|
* - signed and unsigned types have the same representation.
|
||||||
* - short and int have the same representation.
|
* - short and int have the same representation.
|
||||||
* - all pointer types have the same representation.
|
* - all pointer types have the same representation.
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
if (!NoAssign) {
|
if (NoAssign == false) {
|
||||||
|
|
||||||
/* Get the next argument pointer */
|
/* Get the next argument pointer */
|
||||||
__AX__ = (unsigned) va_arg (ap, void*);
|
(void*) __AX__ = va_arg (ap, void*);
|
||||||
|
|
||||||
/* Store the argument pointer into ptr1 */
|
/* Put the argument pointer into the zero-page. */
|
||||||
asm ("sta ptr1");
|
asm ("sta ptr1");
|
||||||
asm ("stx ptr1+1");
|
asm ("stx ptr1+1");
|
||||||
|
|
||||||
@@ -280,38 +403,16 @@ Loop: asm ("lda %v,y", IntVal);
|
|||||||
asm ("dey");
|
asm ("dey");
|
||||||
asm ("bpl %g", Loop);
|
asm ("bpl %g", Loop);
|
||||||
|
|
||||||
|
/* Another assignment */
|
||||||
|
asm ("inc %v", Assignments);
|
||||||
|
asm ("bne %g", Done);
|
||||||
|
asm ("inc %v+1", Assignments);
|
||||||
|
Done:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static unsigned char ReadInt (unsigned char Base)
|
|
||||||
/* Read an integer and store it into IntVal. Returns the number of chars
|
|
||||||
* converted. Does NOT bump Conversions.
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
unsigned char Val;
|
|
||||||
unsigned char CharCount = 0;
|
|
||||||
|
|
||||||
/* Read the integer value */
|
|
||||||
IntVal = 0;
|
|
||||||
while (isxdigit (C) && Width-- > 0 && (Val = HexVal (C)) < Base) {
|
|
||||||
++CharCount;
|
|
||||||
IntVal = IntVal * Base + Val;
|
|
||||||
ReadChar ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we didn't convert anything, it's an error */
|
|
||||||
if (CharCount == 0) {
|
|
||||||
Error (RC_NOCONV);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the number of characters converted */
|
|
||||||
return CharCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void __fastcall__ ScanInt (unsigned char Base)
|
static void __fastcall__ ScanInt (unsigned char Base)
|
||||||
/* Scan an integer including white space, sign and optional base spec,
|
/* Scan an integer including white space, sign and optional base spec,
|
||||||
* and store it into IntVal.
|
* and store it into IntVal.
|
||||||
@@ -331,10 +432,17 @@ static void __fastcall__ ScanInt (unsigned char Base)
|
|||||||
case 'x':
|
case 'x':
|
||||||
case 'X':
|
case 'X':
|
||||||
Base = 16;
|
Base = 16;
|
||||||
|
Converted = true;
|
||||||
ReadChar ();
|
ReadChar ();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Base = 8;
|
Base = 8;
|
||||||
|
|
||||||
|
/* Restart at the beginning of the number because it might
|
||||||
|
* be only a single zero digit (which already was read).
|
||||||
|
*/
|
||||||
|
PushBack ();
|
||||||
|
C = '0';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Base = 10;
|
Base = 10;
|
||||||
@@ -345,65 +453,73 @@ static void __fastcall__ ScanInt (unsigned char Base)
|
|||||||
ReadInt (Base);
|
ReadInt (Base);
|
||||||
|
|
||||||
/* Apply the sign */
|
/* Apply the sign */
|
||||||
if (!Positive) {
|
if (Positive == false) {
|
||||||
IntVal = -IntVal;
|
IntVal = -IntVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Assign the value to the next argument unless suppressed */
|
/* Assign the value to the next argument unless suppressed */
|
||||||
AssignInt ();
|
AssignInt ();
|
||||||
|
|
||||||
/* One more conversion */
|
|
||||||
++Conversions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int __fastcall__ _scanf (register struct scanfdata* D,
|
static char GetFormat (void)
|
||||||
register const char* format, va_list ap_)
|
/* Pick up the next character from the format string. */
|
||||||
|
{
|
||||||
|
/* return (F = *format++); */
|
||||||
|
(const char*) __AX__ = format;
|
||||||
|
asm ("sta regsave");
|
||||||
|
asm ("stx regsave+1");
|
||||||
|
++format;
|
||||||
|
asm ("ldy #0");
|
||||||
|
asm ("lda (regsave),y");
|
||||||
|
asm ("ldx #>0");
|
||||||
|
return (F = (char) __AX__);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int __fastcall__ _scanf (const struct scanfdata* D,
|
||||||
|
const char* format_, va_list ap_)
|
||||||
/* This is the routine used to do the actual work. It is called from several
|
/* This is the routine used to do the actual work. It is called from several
|
||||||
* types of wrappers to implement the actual ISO xxscanf functions.
|
* types of wrappers to implement the actual ISO xxscanf functions.
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
register char F; /* Character from format string */
|
register char* S;
|
||||||
unsigned char Result; /* setjmp result */
|
bool HaveWidth; /* True if a width was given */
|
||||||
char* S;
|
bool Match; /* True if a character-set has any matches */
|
||||||
unsigned char HaveWidth; /* True if a width was given */
|
char Start; /* Walks over a range */
|
||||||
char Start; /* Start of range */
|
|
||||||
|
|
||||||
/* Place copies of the arguments into global variables. This is not very
|
/* Place copies of the arguments into global variables. This is not very
|
||||||
* nice, but on a 6502 platform it gives better code, since the values
|
* nice, but on a 6502 platform it gives better code, since the values
|
||||||
* do not have to be passed as parameters.
|
* do not have to be passed as parameters.
|
||||||
*/
|
*/
|
||||||
D_ = D;
|
D_ = D;
|
||||||
|
format = format_;
|
||||||
ap = ap_;
|
ap = ap_;
|
||||||
|
|
||||||
/* Initialize variables */
|
/* Initialize variables */
|
||||||
Conversions = 0;
|
Converted = false;
|
||||||
|
Assignments = 0;
|
||||||
CharCount = 0;
|
CharCount = 0;
|
||||||
|
|
||||||
/* Set up the jump label. The get() routine will use this label when EOF
|
/* Set up the jump "label". CheckEnd() will use that label when EOF
|
||||||
* is reached.
|
* is reached. ReadInt() will use it when number-conversion fails.
|
||||||
*/
|
*/
|
||||||
Result = setjmp (JumpBuf);
|
if ((unsigned char) setjmp (JumpBuf) == RC_OK) {
|
||||||
if (Result == RC_OK) {
|
|
||||||
|
|
||||||
Again:
|
Again:
|
||||||
|
|
||||||
/* Get the next input character */
|
/* Get the next input character */
|
||||||
ReadChar ();
|
ReadChar ();
|
||||||
|
|
||||||
/* Walk over the format string */
|
/* Walk over the format string */
|
||||||
while (F = *format++) {
|
while (GetFormat ()) {
|
||||||
|
|
||||||
/* Check for a conversion */
|
/* Check for a conversion */
|
||||||
if (F != '%' || *format == '%') {
|
if (F != '%') {
|
||||||
|
|
||||||
/* %% or any char other than % */
|
|
||||||
if (F == '%') {
|
|
||||||
++format;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for a match */
|
/* Check for a match */
|
||||||
if (isspace (F)) {
|
if ((bool) isspace ((int) F)) {
|
||||||
|
|
||||||
/* Special white space handling: Any whitespace in the
|
/* Special white space handling: Any whitespace in the
|
||||||
* format string matches any amount of whitespace including
|
* format string matches any amount of whitespace including
|
||||||
@@ -411,46 +527,63 @@ Again:
|
|||||||
*/
|
*/
|
||||||
SkipWhite ();
|
SkipWhite ();
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (F == C) {
|
Percent:
|
||||||
|
/* ### Note: The opposite test (C == F)
|
||||||
|
** would be optimized into buggy code!
|
||||||
|
*/
|
||||||
|
if (C != (int) F) {
|
||||||
|
|
||||||
/* A match. Read the next input character and start over */
|
/* A mismatch -- we will stop scanning the input,
|
||||||
|
* and return the number of assigned conversions.
|
||||||
|
*/
|
||||||
|
goto NoConv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A match -- get the next input character, and continue. */
|
||||||
goto Again;
|
goto Again;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
/* A mismatch. We will stop scanning the input and return
|
/* A conversion. Skip the percent sign. */
|
||||||
* the number of conversions.
|
/* 0. Check for %% */
|
||||||
*/
|
if (GetFormat () == '%') {
|
||||||
return Conversions;
|
goto Percent;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
/* A conversion. Skip the percent sign. */
|
|
||||||
F = *format++;
|
|
||||||
|
|
||||||
/* 1. Assignment suppression */
|
/* 1. Assignment suppression */
|
||||||
if (F == '*') {
|
NoAssign = (F == '*');
|
||||||
F = *format++;
|
if (NoAssign) {
|
||||||
NoAssign = 1;
|
GetFormat ();
|
||||||
} else {
|
|
||||||
NoAssign = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 2. Maximum field width */
|
/* 2. Maximum field width */
|
||||||
Width = UINT_MAX;
|
Width = UINT_MAX;
|
||||||
HaveWidth = 0;
|
HaveWidth = (bool) isdigit (F);
|
||||||
if (isdigit (F)) {
|
if (HaveWidth) {
|
||||||
HaveWidth = 1;
|
|
||||||
Width = 0;
|
Width = 0;
|
||||||
do {
|
do {
|
||||||
/* ### Non portable ### */
|
/* ### Non portable ### */
|
||||||
Width = Width * 10 + (F & 0x0F);
|
Width = Width * 10 + (F & 0x0F);
|
||||||
F = *format++;
|
} while ((bool) isdigit (GetFormat ()));
|
||||||
} while (isdigit (F));
|
|
||||||
}
|
}
|
||||||
|
if (Width == 0) {
|
||||||
|
/* Invalid specification */
|
||||||
|
/* Note: This method of leaving the function might seem
|
||||||
|
* to be crude, but it optimizes very well because
|
||||||
|
* the four exits can share this code.
|
||||||
|
*/
|
||||||
|
_seterrno (EINVAL);
|
||||||
|
Assignments = EOF;
|
||||||
|
PushBack ();
|
||||||
|
return Assignments;
|
||||||
|
}
|
||||||
|
/* Increment-and-test makes better code than test-and-decrement
|
||||||
|
* does. So, change the width into a form that can be used in
|
||||||
|
* that way.
|
||||||
|
*/
|
||||||
|
Width = ~Width;
|
||||||
|
|
||||||
/* 3. Length modifier */
|
/* 3. Length modifier */
|
||||||
IntBytes = sizeof(int) - 1;
|
IntBytes = sizeof(int) - 1;
|
||||||
@@ -460,7 +593,7 @@ Again:
|
|||||||
IntBytes = sizeof(char) - 1;
|
IntBytes = sizeof(char) - 1;
|
||||||
++format;
|
++format;
|
||||||
}
|
}
|
||||||
F = *format++;
|
GetFormat ();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
@@ -471,21 +604,20 @@ Again:
|
|||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
case 'j': /* intmax_t */
|
case 'j': /* intmax_t */
|
||||||
IntBytes = sizeof(long) - 1;
|
IntBytes = sizeof(long) - 1;
|
||||||
F = *format++;
|
/* FALLTHROUGH */
|
||||||
break;
|
|
||||||
|
|
||||||
case 'z': /* size_t */
|
case 'z': /* size_t */
|
||||||
case 't': /* ptrdiff_t */
|
case 't': /* ptrdiff_t */
|
||||||
|
/* Same size as int */
|
||||||
|
|
||||||
case 'L': /* long double - ignore this one */
|
case 'L': /* long double - ignore this one */
|
||||||
F = *format++;
|
GetFormat ();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 4. Conversion specifier */
|
/* 4. Conversion specifier */
|
||||||
switch (F) {
|
switch (F) {
|
||||||
|
|
||||||
/* 'd' and 'u' conversions are actually the same, since the
|
/* 'd' and 'u' conversions are actually the same, since the
|
||||||
* standard says that evene the 'u' modifier allows an
|
* standard says that even the 'u' modifier allows an
|
||||||
* optionally signed integer.
|
* optionally signed integer.
|
||||||
*/
|
*/
|
||||||
case 'd': /* Optionally signed decimal integer */
|
case 'd': /* Optionally signed decimal integer */
|
||||||
@@ -509,81 +641,91 @@ Again:
|
|||||||
ScanInt (16);
|
ScanInt (16);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'E':
|
|
||||||
case 'e':
|
|
||||||
case 'f':
|
|
||||||
case 'g':
|
|
||||||
/* Optionally signed float */
|
|
||||||
Error (RC_NOCONV);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 's':
|
case 's':
|
||||||
/* Whitespace terminated string */
|
/* Whitespace-terminated string */
|
||||||
SkipWhite ();
|
SkipWhite ();
|
||||||
if (!NoAssign) {
|
CheckEnd (); /* Is it an input failure? */
|
||||||
|
Converted = true; /* No, conversion will succeed */
|
||||||
|
if (NoAssign == false) {
|
||||||
S = va_arg (ap, char*);
|
S = va_arg (ap, char*);
|
||||||
}
|
}
|
||||||
while (!isspace (C) && Width--) {
|
while (C != EOF
|
||||||
if (!NoAssign) {
|
&& (bool) isspace (C) == false
|
||||||
|
&& ++Width) {
|
||||||
|
if (NoAssign == false) {
|
||||||
*S++ = C;
|
*S++ = C;
|
||||||
}
|
}
|
||||||
ReadChar ();
|
ReadChar ();
|
||||||
}
|
}
|
||||||
/* Terminate the string just read */
|
/* Terminate the string just read */
|
||||||
if (!NoAssign) {
|
if (NoAssign == false) {
|
||||||
*S = '\0';
|
*S = '\0';
|
||||||
|
++Assignments;
|
||||||
}
|
}
|
||||||
++Conversions;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'c':
|
case 'c':
|
||||||
/* Fixed length string, NOT zero terminated */
|
/* Fixed-length string, NOT zero-terminated */
|
||||||
if (!HaveWidth) {
|
if (HaveWidth == false) {
|
||||||
/* No width given, default is 1 */
|
/* No width given, default is 1 */
|
||||||
Width = 1;
|
Width = ~1u;
|
||||||
}
|
}
|
||||||
if (!NoAssign) {
|
CheckEnd (); /* Is it an input failure? */
|
||||||
|
Converted = true; /* No, at least 1 char. available */
|
||||||
|
if (NoAssign == false) {
|
||||||
S = va_arg (ap, char*);
|
S = va_arg (ap, char*);
|
||||||
while (Width--) {
|
/* ## This loop is convenient for us, but it isn't
|
||||||
|
* standard C. The standard implies that a failure
|
||||||
|
* shouldn't put anything into the array argument.
|
||||||
|
*/
|
||||||
|
while (++Width) {
|
||||||
|
CheckEnd (); /* Is it a matching failure? */
|
||||||
*S++ = C;
|
*S++ = C;
|
||||||
ReadCharWithCheck ();
|
ReadChar ();
|
||||||
}
|
}
|
||||||
|
++Assignments;
|
||||||
} else {
|
} else {
|
||||||
/* Just skip as many chars as given */
|
/* Just skip as many chars as given */
|
||||||
while (Width--) {
|
while (++Width) {
|
||||||
ReadCharWithCheck ();
|
CheckEnd (); /* Is it a matching failure? */
|
||||||
|
ReadChar ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++Conversions;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '[':
|
case '[':
|
||||||
/* String using characters from a set */
|
/* String using characters from a set */
|
||||||
Invert = 0;
|
|
||||||
/* Clear the set */
|
/* Clear the set */
|
||||||
memset (CharSet, 0, sizeof (CharSet));
|
memset (CharSet, 0, sizeof (CharSet));
|
||||||
F = *format++;
|
/* Skip the left-bracket, and test for inversion. */
|
||||||
if (F == '^') {
|
Invert = (GetFormat () == '^');
|
||||||
Invert = 1;
|
if (Invert) {
|
||||||
F = *format++;
|
GetFormat ();
|
||||||
}
|
}
|
||||||
if (F == ']') {
|
if (F == ']') {
|
||||||
AddCharToSet (']');
|
/* Empty sets aren't allowed; so, a right-bracket
|
||||||
F = *format++;
|
* at the beginning must be a member of the set.
|
||||||
|
*/
|
||||||
|
AddCharToSet (F);
|
||||||
|
GetFormat ();
|
||||||
}
|
}
|
||||||
/* Read the characters that are part of the set */
|
/* Read the characters that are part of the set */
|
||||||
while (F != ']' && F != '\0') {
|
while (F != '\0' && F != ']') {
|
||||||
if (*format == '-') {
|
if (*format == '-') { /* Look ahead at next char. */
|
||||||
/* A range. Get start and end, skip the '-' */
|
/* A range. Get start and end, skip the '-' */
|
||||||
Start = F;
|
Start = F;
|
||||||
F = *++format;
|
|
||||||
++format;
|
++format;
|
||||||
if (F == ']') {
|
switch (GetFormat ()) {
|
||||||
|
case '\0':
|
||||||
|
case ']':
|
||||||
/* '-' as last char means: include '-' */
|
/* '-' as last char means: include '-' */
|
||||||
AddCharToSet (Start);
|
AddCharToSet (Start);
|
||||||
AddCharToSet ('-');
|
AddCharToSet ('-');
|
||||||
} else if (F != '\0') {
|
break;
|
||||||
/* Include all chars in the range */
|
default:
|
||||||
|
/* Include all characters
|
||||||
|
* that are in the range.
|
||||||
|
*/
|
||||||
while (1) {
|
while (1) {
|
||||||
AddCharToSet (Start);
|
AddCharToSet (Start);
|
||||||
if (Start == F) {
|
if (Start == F) {
|
||||||
@@ -592,15 +734,20 @@ Again:
|
|||||||
++Start;
|
++Start;
|
||||||
}
|
}
|
||||||
/* Get next char after range */
|
/* Get next char after range */
|
||||||
F = *format++;
|
GetFormat ();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Just a character */
|
/* Just a character */
|
||||||
AddCharToSet (F);
|
AddCharToSet (F);
|
||||||
/* Get next char */
|
/* Get next char */
|
||||||
F = *format++;
|
GetFormat ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* Don't go beyond the end of the format string. */
|
||||||
|
/* (Maybe, this should mean an invalid specification.) */
|
||||||
|
if (F == '\0') {
|
||||||
|
--format;
|
||||||
|
}
|
||||||
|
|
||||||
/* Invert the set if requested */
|
/* Invert the set if requested */
|
||||||
if (Invert) {
|
if (Invert) {
|
||||||
@@ -611,76 +758,110 @@ Again:
|
|||||||
* store them into a string while they are part of
|
* store them into a string while they are part of
|
||||||
* the set.
|
* the set.
|
||||||
*/
|
*/
|
||||||
if (!NoAssign) {
|
Match = false;
|
||||||
|
if (NoAssign == false) {
|
||||||
S = va_arg (ap, char*);
|
S = va_arg (ap, char*);
|
||||||
while (IsCharInSet (C) && Width--) {
|
}
|
||||||
|
while (IsCharInSet () && ++Width) {
|
||||||
|
if (NoAssign == false) {
|
||||||
*S++ = C;
|
*S++ = C;
|
||||||
|
}
|
||||||
|
Match = Converted = true;
|
||||||
ReadChar ();
|
ReadChar ();
|
||||||
}
|
}
|
||||||
|
/* At least one character must match the set. */
|
||||||
|
if (Match == false) {
|
||||||
|
goto NoConv;
|
||||||
|
}
|
||||||
|
if (NoAssign == false) {
|
||||||
*S = '\0';
|
*S = '\0';
|
||||||
} else {
|
++Assignments;
|
||||||
while (IsCharInSet (C) && Width--) {
|
|
||||||
ReadChar ();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
++Conversions;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'p':
|
case 'p':
|
||||||
/* Pointer, format is 0xABCD */
|
/* Pointer, general format is 0xABCD.
|
||||||
|
* %hhp --> zero-page pointer
|
||||||
|
* %hp --> near pointer
|
||||||
|
* %lp --> far pointer
|
||||||
|
*/
|
||||||
SkipWhite ();
|
SkipWhite ();
|
||||||
if (CHAR (C) != '0') {
|
if (CHAR (C) != '0') {
|
||||||
Error (RC_NOCONV);
|
goto NoConv;
|
||||||
|
}
|
||||||
|
Converted = true;
|
||||||
|
ReadChar ();
|
||||||
|
switch (CHAR (C)) {
|
||||||
|
case 'x':
|
||||||
|
case 'X':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto NoConv;
|
||||||
}
|
}
|
||||||
ReadChar ();
|
ReadChar ();
|
||||||
if (CHAR (C) != 'x' && CHAR (C) != 'X') {
|
ReadInt (16);
|
||||||
Error (RC_NOCONV);
|
|
||||||
}
|
|
||||||
ReadChar ();
|
|
||||||
if (ReadInt (16) != 4) { /* 4 chars expected */
|
|
||||||
Error (RC_NOCONV);
|
|
||||||
}
|
|
||||||
AssignInt ();
|
AssignInt ();
|
||||||
++Conversions;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'n':
|
case 'n':
|
||||||
/* Store characters consumed so far */
|
/* Store the number of characters consumed so far
|
||||||
IntVal = CharCount;
|
* (the read-ahead character hasn't been consumed).
|
||||||
|
*/
|
||||||
|
IntVal = (long) (CharCount - (C == EOF ? 0u : 1u));
|
||||||
AssignInt ();
|
AssignInt ();
|
||||||
|
/* Don't count it. */
|
||||||
|
if (NoAssign == false) {
|
||||||
|
--Assignments;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'S':
|
||||||
|
case 'C':
|
||||||
|
/* Wide characters */
|
||||||
|
|
||||||
|
case 'a':
|
||||||
|
case 'A':
|
||||||
|
case 'e':
|
||||||
|
case 'E':
|
||||||
|
case 'f':
|
||||||
|
case 'F':
|
||||||
|
case 'g':
|
||||||
|
case 'G':
|
||||||
|
/* Optionally signed float */
|
||||||
|
|
||||||
|
/* Those 2 groups aren't implemented. */
|
||||||
|
_seterrno (ENOSYS);
|
||||||
|
Assignments = EOF;
|
||||||
|
PushBack ();
|
||||||
|
return Assignments;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* Invalid conversion */
|
/* Invalid specification */
|
||||||
Error (RC_NOCONV);
|
_seterrno (EINVAL);
|
||||||
break;
|
Assignments = EOF;
|
||||||
|
PushBack ();
|
||||||
|
return Assignments;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Push back the last unused character, provided it is not EOF */
|
|
||||||
if (C != EOF) {
|
|
||||||
D->unget (C, D->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
NoConv:
|
||||||
|
|
||||||
/* Jump via JumpBuf means an error. If this happens at EOF with no
|
/* Coming here means a failure. If that happens at EOF, with no
|
||||||
* conversions, it is considered an error, otherwise the number
|
* conversion attempts, then it is considered an error; otherwise,
|
||||||
* of conversions is returned (the default behaviour).
|
* the number of assignments is returned (the default behaviour).
|
||||||
*/
|
*/
|
||||||
if (C == EOF && CharCount == 0) {
|
if (C == EOF && Converted == false) {
|
||||||
/* Special case: error */
|
Assignments = EOF; /* Special case: error */
|
||||||
Conversions = EOF;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
/* Put the read-ahead character back into the input stream. */
|
||||||
|
PushBack ();
|
||||||
|
|
||||||
/* Return the number of conversions */
|
/* Return the number of conversion-and-assignments. */
|
||||||
return Conversions;
|
return Assignments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* _scanf.h
|
* _scanf.h
|
||||||
*
|
*
|
||||||
* (C) Copyright 2001 Ullrich von Bassewitz (uz@cc65.org)
|
* (c) Copyright 2004, Ullrich von Bassewitz <uz@cc65.org>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -16,6 +16,8 @@
|
|||||||
* return EOF if no more data is available.
|
* return EOF if no more data is available.
|
||||||
*/
|
*/
|
||||||
typedef int __fastcall__ (*getfunc) (void* data);
|
typedef int __fastcall__ (*getfunc) (void* data);
|
||||||
|
|
||||||
|
/* Type of the function that is called to put back unused data */
|
||||||
typedef int __fastcall__ (*ungetfunc) (int c, void* data);
|
typedef int __fastcall__ (*ungetfunc) (int c, void* data);
|
||||||
|
|
||||||
|
|
||||||
@@ -27,15 +29,13 @@ typedef int __fastcall__ (*ungetfunc) (int c, void* data);
|
|||||||
struct scanfdata {
|
struct scanfdata {
|
||||||
getfunc get; /* Pointer to input routine */
|
getfunc get; /* Pointer to input routine */
|
||||||
ungetfunc unget; /* Pointer to pushback routine */
|
ungetfunc unget; /* Pointer to pushback routine */
|
||||||
|
void* data; /* Pointer to struct. used outside of _scanf() */
|
||||||
/* Fields used outside of _scanf */
|
|
||||||
void* data; /* Caller data */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Internal scanning routine */
|
/* Internal scanning routine */
|
||||||
int __fastcall__ _scanf (struct scanfdata* d, const char* format, va_list ap);
|
int __fastcall__ _scanf (const struct scanfdata* d, const char* format, va_list ap);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -44,4 +44,3 @@ int __fastcall__ _scanf (struct scanfdata* d, const char* format, va_list ap);
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
* scanf.c
|
|
||||||
*
|
|
||||||
* Ullrich von Bassewitz (uz@cc65.org), 2004-11-26
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* Code */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int scanf (const char* format, ...)
|
|
||||||
/* Standard C function */
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
/* Setup for variable arguments */
|
|
||||||
va_start (ap, format);
|
|
||||||
|
|
||||||
/* Call vfscanf(). Since we know that va_end won't do anything, we will
|
|
||||||
* save the call and return the value directly.
|
|
||||||
*/
|
|
||||||
return vfscanf (stdin, format, ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
74
libsrc/common/scanf.s
Normal file
74
libsrc/common/scanf.s
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
;
|
||||||
|
; int scanf(const char* Format, ...);
|
||||||
|
;
|
||||||
|
; 2000-12-01, Ullrich von Bassewitz
|
||||||
|
; 2004-12-31, Greg King
|
||||||
|
;
|
||||||
|
|
||||||
|
.export _scanf
|
||||||
|
|
||||||
|
.import _stdin, pushax, addysp, _vfscanf
|
||||||
|
.import sp:zp, ptr1:zp
|
||||||
|
|
||||||
|
.macpack generic
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Code
|
||||||
|
;
|
||||||
|
_scanf:
|
||||||
|
sty ArgSize ; Number of argument bytes passed in .Y
|
||||||
|
|
||||||
|
; We are using a (hopefully) clever trick here to reduce code size. On entry,
|
||||||
|
; the stack pointer points to the last pushed argument of the variable
|
||||||
|
; argument list. Adding the number of argument bytes, would result in a
|
||||||
|
; pointer that points _above_ the Format argument.
|
||||||
|
; Because we have to push stdin anyway, we will do that here, so:
|
||||||
|
;
|
||||||
|
; * we will save the subtraction of 2 (__fixargs__) later;
|
||||||
|
; * we will have the address of the Format argument which needs to
|
||||||
|
; be pushed next.
|
||||||
|
|
||||||
|
lda _stdin
|
||||||
|
ldx _stdin+1
|
||||||
|
jsr pushax
|
||||||
|
|
||||||
|
; Now, calculate the va_list pointer, which does point to Format.
|
||||||
|
|
||||||
|
lda sp
|
||||||
|
ldx sp+1
|
||||||
|
add ArgSize
|
||||||
|
bcc @L1
|
||||||
|
inx
|
||||||
|
@L1: sta ptr1
|
||||||
|
stx ptr1+1
|
||||||
|
|
||||||
|
; Push a copy of Format.
|
||||||
|
|
||||||
|
ldy #1
|
||||||
|
lda (ptr1),y
|
||||||
|
tax
|
||||||
|
dey
|
||||||
|
lda (ptr1),y
|
||||||
|
jsr pushax
|
||||||
|
|
||||||
|
; Load va_list [last and __fastcall__ argument to vfscanf()].
|
||||||
|
|
||||||
|
lda ptr1
|
||||||
|
ldx ptr1+1
|
||||||
|
|
||||||
|
; Call vfscanf().
|
||||||
|
|
||||||
|
jsr _vfscanf
|
||||||
|
|
||||||
|
; Clean up the stack. We will return what we got from vfscanf().
|
||||||
|
|
||||||
|
ldy ArgSize
|
||||||
|
jmp addysp
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------
|
||||||
|
; Data
|
||||||
|
;
|
||||||
|
.bss
|
||||||
|
ArgSize:
|
||||||
|
.res 1 ; Number of argument bytes
|
||||||
|
|
||||||
@@ -1,14 +1,19 @@
|
|||||||
;
|
;
|
||||||
; int __fastcall__ vfscanf (FILE* f, const char* format, va_list ap);
|
; int __fastcall__ vfscanf (FILE* f, const char* format, va_list ap);
|
||||||
;
|
;
|
||||||
; Ullrich von Bassewitz, 2004-11-27
|
; 2004-11-27, Ullrich von Bassewitz
|
||||||
|
; 2004-12-21, Greg King
|
||||||
;
|
;
|
||||||
|
|
||||||
.export _vfscanf
|
.export _vfscanf
|
||||||
.import _fgetc, _ungetc
|
.import _fgetc, _ungetc, _ferror
|
||||||
|
|
||||||
.include "zeropage.inc"
|
.include "zeropage.inc"
|
||||||
.include "_scanf.inc"
|
.include "_scanf.inc"
|
||||||
|
.include "stdio.inc"
|
||||||
|
|
||||||
|
|
||||||
|
count := ptr3 ; Result of scan
|
||||||
|
|
||||||
|
|
||||||
; ----------------------------------------------------------------------------
|
; ----------------------------------------------------------------------------
|
||||||
@@ -25,23 +30,27 @@ d: .addr _fgetc ; GET
|
|||||||
; int __fastcall__ vfscanf (FILE* f, const char* format, va_list ap)
|
; int __fastcall__ vfscanf (FILE* f, const char* format, va_list ap)
|
||||||
; /* Standard C function */
|
; /* Standard C function */
|
||||||
; {
|
; {
|
||||||
; struct scanfdata d;
|
|
||||||
;
|
|
||||||
; /* Initialize the data struct. We do only need the given file as user data,
|
; /* Initialize the data struct. We do only need the given file as user data,
|
||||||
; * since the get and ungetc are crafted so they match the standard fgetc
|
; * because the (getfunc) and (ungetfunc) functions are crafted so that they
|
||||||
; * and ungetc functions.
|
; * match the standard-I/O fgetc() and ungetc().
|
||||||
; */
|
; */
|
||||||
; d.get = (getfunc) fgetc,
|
; static struct scanfdata d = {
|
||||||
; d.unget = (ungetfunc) ungetc,
|
; ( getfunc) fgetc,
|
||||||
; d.data = f;
|
; (ungetfunc) ungetc
|
||||||
|
; };
|
||||||
|
; static int count;
|
||||||
;
|
;
|
||||||
; /* Call the internal function and return the result */
|
; d.data = (void*) f;
|
||||||
; return _scanf (&d, format, ap);
|
;
|
||||||
|
; /* Call the internal function */
|
||||||
|
; count = _scanf (&d, format, ap);
|
||||||
|
;
|
||||||
|
; /* And, return the result */
|
||||||
|
; return ferror (f) ? EOF : count;
|
||||||
; }
|
; }
|
||||||
;
|
;
|
||||||
; Since _scanf has the same parameter stack as vfscanf, with f replaced by &d,
|
; Because _scanf() has the same parameter stack as vfscanf(), with f replaced
|
||||||
; we will do exactly that. _scanf will then clean up the stack, so we can jump
|
; by &d, we will do exactly that. _scanf() then will clean up the stack.
|
||||||
; directly there, no need to return.
|
|
||||||
; Beware: Since ap is a fastcall parameter, we must not destroy a/x.
|
; Beware: Since ap is a fastcall parameter, we must not destroy a/x.
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -63,8 +72,27 @@ _vfscanf:
|
|||||||
lda #>d
|
lda #>d
|
||||||
sta (sp),y
|
sta (sp),y
|
||||||
|
|
||||||
; Restore the low byte of ap and jump to the _scanf function
|
; Restore the low byte of ap, and call the _scanf function
|
||||||
|
|
||||||
pla
|
pla
|
||||||
jmp __scanf
|
jsr __scanf
|
||||||
|
sta count
|
||||||
|
stx count+1
|
||||||
|
|
||||||
|
; Return -1 if there was a read error during the scan
|
||||||
|
|
||||||
|
lda d + SCANFDATA::DATA ; Get f
|
||||||
|
ldx d + SCANFDATA::DATA+1
|
||||||
|
jsr _ferror
|
||||||
|
tay
|
||||||
|
beq L1
|
||||||
|
lda #<EOF
|
||||||
|
tax
|
||||||
|
rts
|
||||||
|
|
||||||
|
; Or, return the result of the scan
|
||||||
|
|
||||||
|
L1: lda count
|
||||||
|
ldx count+1
|
||||||
|
rts
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,12 @@
|
|||||||
; int __fastcall__ vsscanf (const char* str, const char* format, va_list ap);
|
; int __fastcall__ vsscanf (const char* str, const char* format, va_list ap);
|
||||||
; /* Standard C function */
|
; /* Standard C function */
|
||||||
;
|
;
|
||||||
; Ullrich von Bassewitz, 2004-11-28
|
; 2004-11-28, Ullrich von Bassewitz
|
||||||
|
; 2004-12-21, Greg King
|
||||||
;
|
;
|
||||||
|
|
||||||
.export _vsscanf
|
.export _vsscanf
|
||||||
|
|
||||||
.import popax, __scanf
|
.import popax, __scanf
|
||||||
.importzp sp, ptr1, ptr2
|
.importzp sp, ptr1, ptr2
|
||||||
|
|
||||||
@@ -25,14 +27,13 @@
|
|||||||
; static int __fastcall__ get (struct sscanfdata* d)
|
; static int __fastcall__ get (struct sscanfdata* d)
|
||||||
; /* Read a character from the input string and return it */
|
; /* Read a character from the input string and return it */
|
||||||
; {
|
; {
|
||||||
; char C;
|
; char C = d->str[d->index];
|
||||||
; if (C = d->str[d->index]) {
|
; if (C == '\0') {
|
||||||
|
; return EOF;
|
||||||
|
; }
|
||||||
; /* Increment index only if end not reached */
|
; /* Increment index only if end not reached */
|
||||||
; ++d->index;
|
; ++d->index;
|
||||||
; return C;
|
; return C;
|
||||||
; } else {
|
|
||||||
; return EOF;
|
|
||||||
; }
|
|
||||||
; }
|
; }
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -73,17 +74,17 @@
|
|||||||
L1: tax ; Save return value
|
L1: tax ; Save return value
|
||||||
tya ; Low byte of index
|
tya ; Low byte of index
|
||||||
ldy #SSCANFDATA::INDEX
|
ldy #SSCANFDATA::INDEX
|
||||||
add #1
|
add #<1
|
||||||
sta (ptr1),y
|
sta (ptr1),y
|
||||||
iny
|
iny
|
||||||
lda (ptr1),y
|
lda (ptr1),y
|
||||||
adc #$00
|
adc #>1
|
||||||
sta (ptr1),y
|
sta (ptr1),y
|
||||||
|
|
||||||
; Return the char just read
|
; Return the char just read
|
||||||
|
|
||||||
txa
|
txa
|
||||||
ldx #$00
|
ldx #>0
|
||||||
rts
|
rts
|
||||||
|
|
||||||
.endproc
|
.endproc
|
||||||
@@ -110,11 +111,11 @@ L1: tax ; Save return value
|
|||||||
|
|
||||||
ldy #SSCANFDATA::INDEX
|
ldy #SSCANFDATA::INDEX
|
||||||
lda (ptr1),y
|
lda (ptr1),y
|
||||||
sub #1
|
sub #<1
|
||||||
sta (ptr1),y
|
sta (ptr1),y
|
||||||
iny
|
iny
|
||||||
lda (ptr1),y
|
lda (ptr1),y
|
||||||
sbc #0
|
sbc #>1
|
||||||
sta (ptr1),y
|
sta (ptr1),y
|
||||||
|
|
||||||
; Return c
|
; Return c
|
||||||
@@ -127,15 +128,16 @@ L1: tax ; Save return value
|
|||||||
; int __fastcall__ vsscanf (const char* str, const char* format, va_list ap)
|
; int __fastcall__ vsscanf (const char* str, const char* format, va_list ap)
|
||||||
; /* Standard C function */
|
; /* Standard C function */
|
||||||
; {
|
; {
|
||||||
; struct sscanfdata sd;
|
|
||||||
; struct scanfdata d;
|
|
||||||
;
|
|
||||||
; /* Initialize the data structs. The sscanfdata struct will be passed back
|
; /* Initialize the data structs. The sscanfdata struct will be passed back
|
||||||
; * to the get and unget functions by _scanf.
|
; * to the get and unget functions by _scanf().
|
||||||
; */
|
; */
|
||||||
; d.get = (getfunc) get;
|
; static struct sscanfdata sd;
|
||||||
; d.unget = (ungetfunc) unget,
|
; static const struct scanfdata d = {
|
||||||
; d.data = &sd;
|
; ( getfunc) get,
|
||||||
|
; (ungetfunc) unget,
|
||||||
|
; (void*) &sd
|
||||||
|
; };
|
||||||
|
;
|
||||||
; sd.str = str;
|
; sd.str = str;
|
||||||
; sd.index = 0;
|
; sd.index = 0;
|
||||||
;
|
;
|
||||||
@@ -144,10 +146,10 @@ L1: tax ; Save return value
|
|||||||
; }
|
; }
|
||||||
;
|
;
|
||||||
|
|
||||||
.data
|
.bss
|
||||||
|
|
||||||
sd: .tag SSCANFDATA
|
sd: .tag SSCANFDATA
|
||||||
|
|
||||||
|
.rodata
|
||||||
d: .addr get
|
d: .addr get
|
||||||
.addr unget
|
.addr unget
|
||||||
.addr sd
|
.addr sd
|
||||||
@@ -177,11 +179,10 @@ d: .addr get
|
|||||||
sta sd + SSCANFDATA::INDEX
|
sta sd + SSCANFDATA::INDEX
|
||||||
sta sd + SSCANFDATA::INDEX+1
|
sta sd + SSCANFDATA::INDEX+1
|
||||||
|
|
||||||
; Restore the low byte of ap and jump to _scanf which will cleanup the stacl
|
; Restore the low byte of ap, and jump to _scanf() which will clean up the stack
|
||||||
|
|
||||||
pla
|
pla
|
||||||
jmp __scanf
|
jmp __scanf
|
||||||
|
|
||||||
.endproc
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user