;
; int __fastcall__ vsnprintf (char* Buf, size_t size, const char* Format, va_list ap);
;
-; Ullrich von Bassewitz, 2009-09-26
+; 2009-09-26, Ullrich von Bassewitz
+; 2015-07-17, Greg King
;
.export _vsnprintf, vsnprintf
.import _memcpy, __printf
.importzp sp, ptr1
+ .include "errno.inc"
+
.macpack generic
.data
sta ccount+1 ; Clear ccount
; Get the size parameter and replace it by a pointer to outdesc. This is to
-; build a stack frame for the call to _printf.
-; If size is zero, there's nothing to do.
+; build a stack frame for the call to _printf. The size must not be greater
+; than INT_MAX because the return type is int. If the size is zero,
+; then nothing will be written into the buffer; but, the arguments still will
+; be formatted and counted.
ldy #2
lda (sp),y
iny
lda (sp),y
+ bmi L9 ; More than $7FFF
sta ptr1+1
- ora ptr1
- beq L9
-
lda #>outdesc
sta (sp),y
-; Write size-1 to outdesc.uns
+; Write size-1 to outdesc.uns. It will be -1 if there is no buffer.
ldy ptr1+1
ldx ptr1
sta bufptr+0
stx bufptr+1
+; There must be a buffer if its size is non-zero.
+
+ bit bufsize+1
+ bmi L5
+ ora bufptr+1
+ bze L0 ; The pointer shouldn't be NULL
+
; Restore ap and call _printf
- pla
+L5: pla
tax
pla
jsr __printf
-; Terminate the string. The last char is either at bufptr+ccount or
-; bufptr+bufsize, whichever is smaller.
+; Terminate the string if there is a buffer. The last char. is at either
+; bufptr+bufsize or bufptr+ccount, whichever is smaller.
- lda ccount+0
- ldx ccount+1
- cpx bufsize+1
+ ldx bufsize+1
+ bmi L4 ; -1 -- No buffer
+ lda bufsize+0
+ cpx ccount+1
bne L2
- cmp bufsize+0
+ cmp ccount+0
L2: bcc L3
- lda bufsize+0
- ldx bufsize+1
+ lda ccount+0
+ ldx ccount+1
clc
L3: adc bufptr+0
sta ptr1
; Return the number of bytes written and drop buf
- lda ccount+0
+L4: lda ccount+0
ldx ccount+1
jmp incsp2
-; Bail out if size is zero.
+; Bail out if size is too high.
-L9: pla
- pla ; Discard ap
- lda #0
- tax
+L9: ldy #ERANGE
+ .byte $2C ;(bit $xxxx)
+
+; NULL buffer pointers usually are invalid.
+
+L0: ldy #EINVAL
+ pla ; Drop ap
+ pla
+ tya
+ jsr __directerrno ; Return -1
jmp incsp6 ; Drop parameters
sbc ccount+0 ; Low byte of bytes already written
sta ptr1
lda bufsize+1
+ bmi @L9 ; -1 -- No buffer
sbc ccount+1
sta ptr1+1
bcs @L0 ; Branch if space left
- lda #$00
+@L9: lda #$0000
sta ptr1
sta ptr1+1 ; No space left
;
-; Oliver Schmidt, 2013-05-16
+; 2013-05-16, Oliver Schmidt
+; 2015-07-18, Greg King
;
-; extern int errno;
+; Helper functions for several high-level functions.
;
.include "errno.inc"
+; ----------------------------------------------------------------------------
+; int __fastcall__ _directerrno (unsigned char code);
+; /* Set errno to a specific error code; and, return -1. Used
+; ** by the library.
+; */
+
+__directerrno:
+ jsr __seterrno ; Save in errno
+fail: lda #$FF ; Return -1
+ tax
+ok: rts
+
+
+; ----------------------------------------------------------------------------
+;
+; extern int _errno;
+;
.bss
__errno:
C1541 = c1541
# --------------------------------------------------------------------------
-# System dependent settings
+# System-dependent settings
# The Apple machines need the start address adjusted when using TGI
LDFLAGS_mandelbrot_apple2 = --start-addr 0x4000
.PRECIOUS: %.o
.o:
- @$(LD) $(LDFLAGS_$(basename $@)_$(SYS)) -o $@ -t $(SYS) -m $@.map $^ $(CLIB)
+ @$(LD) $(LDFLAGS_$(@F)_$(SYS)) -o $@ -t $(SYS) -m $@.map $^ $(CLIB)
# --------------------------------------------------------------------------
-# List of executables. This list could be made target dependent by checking
+# List of executables. This list could be made target-dependent by checking
# $(SYS).
EXELIST = ascii \
tgidemo
# --------------------------------------------------------------------------
-# Rules how to make each one of the binaries
+# Rules to make the binaries
.PHONY: all
all: $(EXELIST)
# --------------------------------------------------------------------------
-# Rule to make a disk with all samples. Needs the c1541 program that comes
+# Overlay rules. Overlays need special ld65 configuration files. Also, the
+# overlay file-names are shortenned to fit the Atari's 8.3-character limit.
+
+multdemo: multidemo.o
+ @$(LD) -o $@ -C $(SYS)-overlay.cfg -m $@.map $^ $(CLIB)
+
+ovrldemo: overlaydemo.o
+ @$(LD) -o $@ -C $(SYS)-overlay.cfg -m $@.map $^ $(CLIB)
+
+# --------------------------------------------------------------------------
+# Rule to make a CBM disk with all samples. Needs the c1541 program that comes
# with the VICE emulator.
.PHONY: disk
done
# --------------------------------------------------------------------------
-# Cleanup rules
+# Clean-up rules
.PHONY: clean
clean:
.PHONY: zap
zap: clean
$(RM) $(EXELIST) samples.d64
+ $(RM) multdemo.? ovrldemo.?
the programs manually.
* The makefile specifies the C64 as the default target platform, because all
+ but one
of the programs run on this platform. When compiling for another platform,
you will have to change the line that specifies the target system at the
top of the makefile.
break;
}
DVal = DigitVal (Buf[I]);
- if (DVal > Base) {
+ if (DVal >= Base) {
Error ("Invalid digits in number");
CurTok.IVal = 0;
break;
--- /dev/null
+/*
+** Test a function that formats and writes characters into a string buffer.
+** This program does not test formatting. It tests some behaviors that are
+** specific to the buffer. It tests that certain conditions are handled
+** properly.
+**
+** 2015-07-17, Greg King
+*/
+
+#include <conio.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+static const char format[] = "1234567890\nabcdefghijklmnopqrstuvwxyz\n%u\n%s\n\n";
+#define FORMAT_SIZE (sizeof format - 2u - 2u - 1u)
+
+#define arg1 12345u
+#define ARG1_SIZE (5u)
+
+static const char arg2[] = "!@#$%^&*()-+";
+#define ARG2_SIZE (sizeof arg2 - 1u)
+
+#define STRING_SIZE (FORMAT_SIZE + ARG1_SIZE + ARG2_SIZE)
+
+static char buf[256];
+static int size;
+
+
+static void fillbuf(void)
+{
+ memset(buf, 0xFF, sizeof buf - 1u);
+ buf[sizeof buf - 1u] = '\0';
+}
+
+
+unsigned char main(void)
+{
+ static unsigned char failures = 0;
+
+ /* Show what sprintf() should create. */
+
+ if ((size = printf(format, arg1, arg2)) != STRING_SIZE) {
+ ++failures;
+ printf("printf() gave the wrong size: %d.\n", size);
+ }
+
+ /* Test the normal behavior of sprintf(). */
+
+ fillbuf();
+ size = sprintf(buf, format, arg1, arg2);
+ fputs(buf, stdout);
+ if (size != STRING_SIZE) {
+ ++failures;
+ printf("sprintf() gave the wrong size: %d.\n", size);
+ }
+
+ /* Test the normal behavior of snprintf(). */
+
+ fillbuf();
+ size = snprintf(buf, sizeof buf, format, arg1, arg2);
+ fputs(buf, stdout);
+ if (size != STRING_SIZE) {
+ ++failures;
+ printf("snprintf(sizeof buf) gave the wrong size:\n %d.\n", size);
+ }
+
+ /* Does snprintf() return the full-formatted size even when the buffer
+ ** is short? Does it write beyond the end of that buffer?
+ */
+
+ fillbuf();
+ size = snprintf(buf, STRING_SIZE - 5u, format, arg1, arg2);
+ if (size != STRING_SIZE) {
+ ++failures;
+ printf("snprintf(STRING_SIZE-5) gave the wrong size:\n %d.\n", size);
+ }
+ if (buf[STRING_SIZE - 5u - 1u] != '\0' || buf[STRING_SIZE - 5u] != 0xFF) {
+ ++failures;
+ printf("snprintf(STRING_SIZE-5) wrote beyond\n the end of the buffer.\n");
+ }
+
+ /* Does snprintf() detect a buffer size that is too big? */
+
+ fillbuf();
+ errno = 0;
+ size = snprintf(buf, 0x8000, format, arg1, arg2);
+ if (size >= 0) {
+ ++failures;
+ printf("snprintf(0x8000) didn't give an error:\n %d; errno=%d.\n", size, errno);
+ } else {
+ printf("snprintf(0x8000) did give an error:\n errno=%d.\n", errno);
+ }
+ if (buf[0] != 0xFF) {
+ ++failures;
+ printf("snprintf(0x8000) wrote into the buffer.\n");
+ }
+
+ /* snprintf() must measure the length of the formatted output even when the
+ ** buffer size is zero. But, it must not touch the buffer.
+ */
+
+ fillbuf();
+ size = snprintf(buf, 0, format, arg1, arg2);
+ if (size != STRING_SIZE) {
+ ++failures;
+ printf("snprintf(0) gave the wrong size:\n %d.\n", size);
+ }
+ if (buf[0] != 0xFF) {
+ ++failures;
+ printf("snprintf(0) wrote into the buffer.\n");
+ }
+
+ /* Does sprintf() detect a zero buffer-pointer? */
+
+ errno = 0;
+ size = sprintf(NULL, format, arg1, arg2);
+ if (size >= 0) {
+ ++failures;
+ printf("sprintf(NULL) didn't give an error:\n %d; errno=%d.\n", size, errno);
+ } else {
+ printf("sprintf(NULL) did give an error:\n errno=%d.\n", errno);
+ }
+
+ /* snprintf() must measure the length of the formatted output even when the
+ ** buffer size is zero. A zero pointer is not an error, in that case.
+ */
+
+ size = snprintf(NULL, 0, format, arg1, arg2);
+ if (size != STRING_SIZE) {
+ ++failures;
+ printf("snprintf(NULL,0) gave the wrong size:\n %d.\n", size);
+ }
+
+ if (failures != 0) {
+ printf("There were %u", failures);
+ } else {
+ printf("There were no");
+ }
+ printf(" failures.\nTap a key. ");
+ cgetc();
+
+ return failures;
+}