Added assembler include function for _scanf

git-svn-id: svn://svn.cc65.org/cc65/trunk@3304 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
cuz
2004-11-27 14:45:49 +00:00
parent 7cf5f27ec6
commit dbb003c9ac
3 changed files with 144 additions and 57 deletions

View File

@@ -12,6 +12,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <stddef.h>
#include <string.h> #include <string.h>
#include <setjmp.h> #include <setjmp.h>
#include <ctype.h> #include <ctype.h>
@@ -39,9 +40,10 @@
static struct scanfdata* D; /* Copy of function argument */ static 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 in case of EOF */
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 */
@@ -57,6 +59,15 @@ static const unsigned char Bits[8] = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
}; };
/* We need C to be 16 bits since we cannot check for EOF otherwise.
* Unfortunately, this causes the code to be quite larger, even if for most
* purposes, checking the low byte would be enough, since if C is EOF, the
* low byte will not match any useful character anyway (at least for the
* supported platforms - I know that this is not portable). So the following
* macro is used to access just the low byte of C.
*/
#define CHAR(c) (*((unsigned char*)&(c)))
/*****************************************************************************/ /*****************************************************************************/
@@ -124,13 +135,57 @@ static void InvertCharSet (void)
static void __fastcall__ Error (unsigned char Code)
/* Does a longjmp using the given code */
{
longjmp (JumpBuf, Code);
}
static void ReadChar (void) static void ReadChar (void)
/* Get an input character, count characters */ /* Get an input character, count characters */
{ {
C = D->get (D->data); /* Move D to ptr1 */
if (C != EOF) { asm ("lda %v", D_);
++D->ccount; asm ("ldx %v+1", D_);
} asm ("sta ptr1");
asm ("stx ptr1+1");
/* Copy the get vector to jmpvec */
asm ("ldy #%b", offsetof (struct scanfdata, get));
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 get routine */
asm ("jsr jmpvec");
/* Assign the result to C */
asm ("sta %v", C);
asm ("stx %v+1", C);
/* If C is not EOF, bump the character counter. */
asm ("inx");
asm ("bne %g", Done);
asm ("cmp #$FF");
asm ("bne %g", Done);
/* Must bump CharCount. */
asm ("inc %v", CharCount);
asm ("bne %g", Done);
asm ("inc %v+1", CharCount);
Done:
} }
@@ -140,7 +195,7 @@ static void ReadCharWithCheck (void)
{ {
ReadChar (); ReadChar ();
if (C == EOF) { if (C == EOF) {
longjmp (JumpBuf, RC_EOF); Error (RC_EOF);
} }
} }
@@ -161,17 +216,27 @@ static void ReadSign (void)
* positive, store 0 otherwise. * positive, store 0 otherwise.
*/ */
{ {
switch (C) { /* We can ignore the high byte of C here, since if it is EOF, the lower
case '-': * byte won't match anyway.
ReadChar (); */
Positive = 0; asm ("lda %v", C);
break; asm ("cmp #'-'");
case '+': asm ("bne %g", NotNeg);
ReadChar ();
/* FALLTHROUGH */ /* Negative value */
default: asm ("jsr %v", ReadChar);
Positive = 1; asm ("lda #$00"); /* Flag as negative */
} asm ("beq %g", Store);
/* Positive value */
NotNeg:
asm ("cmp #'+'");
asm ("bne %g", Pos);
asm ("jsr %v", ReadChar); /* Skip the + sign */
Pos:
asm ("lda #$01"); /* Flag as positive */
Store:
asm ("sta %v", Positive);
} }
@@ -210,10 +275,10 @@ static void AssignInt (void)
asm ("ldy %v", IntBytes); asm ("ldy %v", IntBytes);
/* Assign the integer value */ /* Assign the integer value */
asm ("L1: lda %v,y", IntVal); Loop: asm ("lda %v,y", IntVal);
asm ("sta (ptr1),y"); asm ("sta (ptr1),y");
asm ("dey"); asm ("dey");
asm ("bpl L1"); asm ("bpl %g", Loop);
} }
} }
@@ -238,7 +303,7 @@ static unsigned char ReadInt (unsigned char Base)
/* If we didn't convert anything, it's an error */ /* If we didn't convert anything, it's an error */
if (CharCount == 0) { if (CharCount == 0) {
longjmp (JumpBuf, RC_NOCONV); Error (RC_NOCONV);
} }
/* Return the number of characters converted */ /* Return the number of characters converted */
@@ -247,7 +312,7 @@ static unsigned char ReadInt (unsigned char Base)
static void 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.
*/ */
@@ -260,9 +325,9 @@ static void ScanInt (unsigned char Base)
/* If Base is unknown (zero), figure it out */ /* If Base is unknown (zero), figure it out */
if (Base == 0) { if (Base == 0) {
if (C == '0') { if (CHAR (C) == '0') {
ReadChar (); ReadChar ();
switch (C) { switch (CHAR (C)) {
case 'x': case 'x':
case 'X': case 'X':
Base = 16; Base = 16;
@@ -293,14 +358,15 @@ static void ScanInt (unsigned char Base)
int _scanf (struct scanfdata* D_, register const char* format, va_list ap_) int __fastcall__ _scanf (register struct scanfdata* D,
register 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.
*/ */
{ {
char F; /* Character from format string */ register char F; /* Character from format string */
unsigned char Result; /* setjmp result */ unsigned char Result; /* setjmp result */
char* S; char* S;
unsigned char HaveWidth; /* True if a width was given */ unsigned char HaveWidth; /* True if a width was given */
char Start; /* Start of range */ char Start; /* Start of range */
@@ -308,12 +374,12 @@ int _scanf (struct scanfdata* D_, register const char* format, va_list ap_)
* 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;
ap = ap_; ap = ap_;
/* Initialize variables */ /* Initialize variables */
Conversions = 0; Conversions = 0;
D->ccount = 0; CharCount = 0;
/* Set up the jump label. The get() routine will use this label when EOF /* Set up the jump label. The get() routine will use this label when EOF
* is reached. * is reached.
@@ -429,7 +495,7 @@ Again:
case 'i': case 'i':
/* Optionally signed integer with a base */ /* Optionally signed integer with a base */
ScanInt (0); ScanInt (0);
break; break;
case 'o': case 'o':
@@ -448,13 +514,13 @@ Again:
case 'f': case 'f':
case 'g': case 'g':
/* Optionally signed float */ /* Optionally signed float */
longjmp (JumpBuf, RC_NOCONV); Error (RC_NOCONV);
break; break;
case 's': case 's':
/* Whitespace terminated string */ /* Whitespace terminated string */
SkipWhite (); SkipWhite ();
if (!NoAssign) { if (!NoAssign) {
S = va_arg (ap, char*); S = va_arg (ap, char*);
} }
while (!isspace (C) && Width--) { while (!isspace (C) && Width--) {
@@ -465,34 +531,34 @@ Again:
} }
/* Terminate the string just read */ /* Terminate the string just read */
if (!NoAssign) { if (!NoAssign) {
*S = '\0'; *S = '\0';
} }
++Conversions; ++Conversions;
break; break;
case 'c': case 'c':
/* Fixed length string, NOT zero terminated */ /* Fixed length string, NOT zero terminated */
if (!HaveWidth) { if (!HaveWidth) {
/* No width given, default is 1 */ /* No width given, default is 1 */
Width = 1; Width = 1;
} }
if (!NoAssign) { if (!NoAssign) {
S = va_arg (ap, char*); S = va_arg (ap, char*);
while (Width--) { while (Width--) {
*S++ = C; *S++ = C;
ReadCharWithCheck (); ReadCharWithCheck ();
} }
} else { } else {
/* Just skip as many chars as given */ /* Just skip as many chars as given */
while (Width--) { while (Width--) {
ReadCharWithCheck (); ReadCharWithCheck ();
} }
} }
++Conversions; ++Conversions;
break; break;
case '[': case '[':
/* String using characters from a set */ /* String using characters from a set */
Invert = 0; Invert = 0;
/* Clear the set */ /* Clear the set */
memset (CharSet, 0, sizeof (CharSet)); memset (CharSet, 0, sizeof (CharSet));
@@ -563,16 +629,16 @@ Again:
case 'p': case 'p':
/* Pointer, format is 0xABCD */ /* Pointer, format is 0xABCD */
SkipWhite (); SkipWhite ();
if (C != '0') { if (CHAR (C) != '0') {
longjmp (JumpBuf, RC_NOCONV); Error (RC_NOCONV);
} }
ReadChar (); ReadChar ();
if (C != 'x' && C != 'X') { if (CHAR (C) != 'x' && CHAR (C) != 'X') {
longjmp (JumpBuf, RC_NOCONV); Error (RC_NOCONV);
} }
ReadChar (); ReadChar ();
if (ReadInt (16) != 4) { /* 4 chars expected */ if (ReadInt (16) != 4) { /* 4 chars expected */
longjmp (JumpBuf, RC_NOCONV); Error (RC_NOCONV);
} }
AssignInt (); AssignInt ();
++Conversions; ++Conversions;
@@ -580,13 +646,13 @@ Again:
case 'n': case 'n':
/* Store characters consumed so far */ /* Store characters consumed so far */
IntVal = D->ccount; IntVal = CharCount;
AssignInt (); AssignInt ();
break; break;
default: default:
/* Invalid conversion */ /* Invalid conversion */
longjmp (JumpBuf, RC_NOCONV); Error (RC_NOCONV);
break; break;
} }
@@ -604,7 +670,7 @@ Again:
* conversions, it is considered an error, otherwise the number * conversions, it is considered an error, otherwise the number
* of conversions is returned (the default behaviour). * of conversions is returned (the default behaviour).
*/ */
if (C == EOF && D->ccount == 0) { if (C == EOF && CharCount == 0) {
/* Special case: error */ /* Special case: error */
Conversions = EOF; Conversions = EOF;
} }
@@ -617,3 +683,4 @@ Again:

View File

@@ -21,13 +21,12 @@ typedef int __fastcall__ (*ungetfunc) (int c, void* data);
/* Control structure passed to the low level worker function. /* Control structure passed to the low level worker function.
* Beware: The low level functions will access the structure on the assembly * Beware: This structure is mirrored in the _scanf.inc assembler include
* level, so check this when altering the structure. * file, so check this when altering the structure.
*/ */
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 */
unsigned ccount; /* Number of chars read */
/* Fields used outside of _scanf */ /* Fields used outside of _scanf */
void* data; /* Caller data */ void* data; /* Caller data */
@@ -36,7 +35,7 @@ struct scanfdata {
/* Internal scanning routine */ /* Internal scanning routine */
int _scanf (struct scanfdata* d, const char* format, va_list ap); int __fastcall__ _scanf (struct scanfdata* d, const char* format, va_list ap);

21
libsrc/common/_scanf.inc Normal file
View File

@@ -0,0 +1,21 @@
;
; Ullrich von Bassewitz, 2004-11-27
;
;----------------------------------------------------------------------------
; Structure passed to _scanf
.struct SCANFDATA
GET .addr
UNGET .addr
DATA .addr
.endstruct
;----------------------------------------------------------------------------
; Global data
.global _scanf