;; ;; Copyright (c) 2019 Marco Granati ;; ;; Permission to use, copy, modify, and distribute this software for any ;; purpose with or without fee is hereby granted, provided that the above ;; copyright notice and this permission notice appear in all copies. ;; ;; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ;; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ;; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ;; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ;; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ;; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ;; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ;; ;; project: c16 library ;; name: printf.asm ;; purpose: printf family functions ;; revision: 1.0 ;; date: 2019/03/18 .list off .include inc\mainlib.inc .include inc\fplib.inc .include inc\bios.inc ;; This code attempt to implement the "c-like" printf() family function ;; Current implementation is near fully compatible with standard C function's ;; For compatibility comparison see the table below, about format string ;; ;; cprintf(lpFmt, ..., wCount) ;; tprintf(lpFmt, ..., wCount) ;; printf(lpFmt, ..., wCount) ;; dprintf(bHandle, lpFmt, ..., wCount) ;; fprintf(lpStream, lpFmt, ..., wCount) ;; snprintf(lpBuf, wSize, lpFmt, ..., wCount) ;; ;; NOTE: as now just cprintf() and snprintf() are fully implemented ;; ;; FORMAT STRING ;; '%'[flag][width]['.'precision][modifier]type ;; ;; flags ::= '-' | '+' | ' ' | '#' | '0' | ',' | '$' ;; width ::= '*' | number (8 bit) (if * byte is passed by arg) ;; precision ::= '*' | number (8 bit) (if * byte is passed by arg) ;; modifier ::= 'H' | 'h' | 'l' | 'L' ;; type ::= 'c' | 's' | 'd' | 'i' | 'u' | 'x' | 'X' | 'p' | 'P' | ;; 'S' | 'f' | 'F' | 'e' | 'E' | 'g' | 'G' ;; ;; flag '#' - alternate format (integer conversion: see below) ;; for e, E, f, F, g, and G conversions, the result ;; will always contain a decimal point, even if no ;; digits follow it (normally, a decimal point appears ;; in the results of those conversions only if a digit ;; follows). ;; for g and G conversions, trailing zeros are not ;; removed from the result as they would otherwise be. ;; ;; flag '-' - left justify (default) ;; flag '+' - right justify ;; flag ' ' or '0' - fill field with blank or '0' (default: blank) ;; flag ',' - only for d,i,u types - group thousands with ;; ',' as seperator ;; flag '$' - for hex. conversion: prefix with '$' ;; (or with 0x/0X if alternate format selected) ;; for float: don't discriminate -0.0/+0.0 ;; TYPES ;; c - character ;; s - asciiz string (nul terminated string) ;; S - Pascal/Basic string (not standard) ;; d,i - signed decimal integer 32 bit ;; u - unsigned decimal integer 32 bit ;; x,X - unsigned hex. integer 32 bit (nocaps/caps) ;; p,P - long pointer (3 bytes) in hex (nocaps/caps, no std.) ;; f,F,e,E,g,G - decimal float point number ;; a,A,k,K - hex. floating point ;; ;; e,E The argument is rounded and converted in the style ;; [-]d.ddde+-dd where there is one digit before the ;; decimal-point character and the number of digits after ;; it is equal to the precision; if the precision is ;; missing, it is taken as 6; if the precision is zero, ;; no decimal-point character appears. An E conversion ;; uses the letter `E' (rather than `e') to introduce the ;; exponent. The exponent always contains at least two ;; digits; if the value is zero, the exponent is 00. ;; ;; f,F The argument is rounded and converted to decimal ;; notation in the style [-]ddd.ddd, where the number of ;; digits after the decimal-point character is equal to ;; the precision specification. If the precision is ;; missing, it is taken as 6; if the precision is ;; explicitly zero, no decimal-point character appears. ;; If a decimal point appears, at least one digit appears ;; before it. ;; ;; g,G The argument is converted in style f or e (or F or E ;; for G conversions). The precision specifies the number ;; of significant digits. If the precision is missing, 6 ;; digits are given; if the precision is zero, it is ;; treated as 1. Style e is used if the exponent from its ;; conversion is less than -4 or greater than or equal to ;; the precision. Trailing zeros are removed from the ;; fractional part of the result; a decimal point appears ;; only if it is followed by at least one digit. ;; ;; a,A The argument is unpacked in hexadecimal, and contain ;; 32 hexadecimal digits for mantissa (low 15 bits are ;; guard-bits, always zero) , followed by the biased ;; exponent (introduced by literal 'p' or 'P'), or-ed at ;; bit 15 with sign of the float. Hexadecimal string can ;; be prepended either by '$' (or '0x' or '0X'). ;; ;; k,K The argument is converted in hexadecimal "as-is", in ;; packed form as stored in memory, and contain 32 hex. ;; digits. Hexadecimal string can be prepended either ;; by '$' (or '0x' or '0X'). ;; ;; note: For e, E, f, F, g, and G conversions, positive and ;; negative infinity are represented as inf and -inf ;; respectively when using the lowercase conversion ;; character, and INF and -INF respectively when using ;; the uppercase conversion character. Similarly, NaN is ;; represented as nan when using the lowercase conversion, ;; and NAN when using the uppercase conversion. ;; ;; ;; MODIFIERS (allowed for types d, i, u, x, X only) ;; H - short short integer ( 8 bit) ;; h - short integer ( 16 bit) ;; l - long integer ( 64 bit) ;; L - long long integer (128 bit) ;; ;; NOTES - all functions below follow "pascal" convention for ;; passing parameters on stack: left-to-right ;; - strings are passed by reference (long pointer) ;; - all others params are passed by value ;; - in this implementation float are 128 bits ;; - all functions are fully reentrant (no static data) ;; .list on ;--------------------------------------------------------------------------- ; equates ;--------------------------------------------------------------------------- ; offset of known parameters, relative to the top of stack frame ; lpFmt .equ 0 ; lpFmt param offset bHandle .equ 3 ; bHandle offset wSize .equ 3 ; wSize offset lpBuf .equ 5 ; lpBuf offset lpStream .equ 3 ; lpStream offset pbase .equ 0 ; base ; prologue code save 10 bytes in stack (c,x,y 16 bits, status, dpr, dbr) ; plus 3 bytes saved by jsl instruction for return address ; pushed .equ 13 ; pushed bytes bufsize .equ 128 ; local output buffer ; constants for outtyp var out_cons .equ 0 ; output to console out_text .equ 1 ; output to text device out_file .equ 2 ; output to file out_strm .equ 3 ; output to stream out_buf .equ $c4 ; output to buffer (store) out_fake .equ $84 ; fake output to buffer (no store) stdout .equ 1 ; standard out handle ; spec's class _dc .equ 0 ; don't care _si .equ 1 ; sign fill +/- _af .equ 2 ; alternate form _ar .equ 3 ; format (width or precision) by argument _lj .equ 4 ; left justify _pr .equ 5 ; precision _nu .equ 6 ; numeral _lo .equ 7 ; long integer (64 bit) _sh .equ 8 ; short integer (16 bit) _hh .equ 9 ; short short integer (8 bit) _fz .equ 10 ; fill zeros _de .equ 11 ; signed decimal _un .equ 12 ; unsigned decimal _hx .equ 13 ; hexadecimal _pt .equ 14 ; long pointer _ch .equ 15 ; char _st .equ 16 ; asciiz string _ff .equ 17 ; float _tg .equ 18 ; thousand group _ll .equ 19 ; long long integer (128 bits) _ps .equ 20 ; pascal/basic string _xp .equ 21 ; flag '$': prefix hex. ; fmtflag bits f_altfmt .equ $80 ; alternate format f_group .equ $40 ; thousand group f_nz .equ $40 ; no sign discrimination for float = 0.0 f_signed .equ $20 ; signed integer f_leftj .equ $10 ; left align (default: right) f_width .equ $08 ; 'width' field was given f_prec .equ $04 ; 'precision' field was given f_pls .equ $02 ; '+' prefix flag for positive decimal f_pfx .equ $01 ; prefix hex. or positive decimal ; valid flags mask for pointer and hexadecimal fmaskx .equ f_altfmt.or.f_leftj.or.f_width.or.f_prec ; hexadecimal prefix mask pfxmask .equ f_altfmt.or.f_pfx ; valid flags mask for integer decimal type fmaskd .equ f_group.or.f_signed.or.f_leftj.or.f_width.or.f_prec ; valid flags mask for char type fmaskc .equ f_leftj.or.f_width ; valid flags mask for string type fmasks .equ f_leftj.or.f_width.or.f_prec ; valid flags mask for decimal float type fmaskf .equ f_altfmt.or.f_leftj.or.f_width.or.f_prec ; valid flags mask for hex. float type fmaskhf .equ f_altfmt.or.f_leftj.or.f_width fminprec .equ 8 ; minimum float precision (if unspecified) ; extended flags bits (xfg var) fx_fp .equ $80 ; dp of fp module was saved fx_pfx .equ $40 ; hexadecimal prefixed with '0x' or '0X' fx_mod .equ $02 ; 'H','h','l' or 'L' modifier given fx_xp .equ $01 ; hex. prefix with '$' or '0x' or '0X' ; or for float: 0.0 is always positive ; current stage while parse format flagstage .equ 0 ; [flag] stage padstage .equ 1 ; fill with '0' stage widestage .equ 2 ; [width] stage dotstage .equ 3 ; ['.'] stage precstage .equ 4 ; [precision] stage modstage .equ 5 ; [modifier] stage ; conversion function index t_chr .equ 0 ; char conversion t_str .equ (1 * 2) ; asciiz strings conversion t_pstr .equ (2 * 2) ; pascal/basic strings conversion t_int .equ (3 * 2) ; integer conversion t_float .equ (4 * 2) ; float conversion ;--------------------------------------------------------------------------- ; macros ;--------------------------------------------------------------------------- ; prolog npars prologue code ; npars = size in bytes of known parameters on stack ; prolog .macro npars clc ; clear carry: assume no error php ; save status phb ; save dbr phd ; save dpr c16c ; switch cpu to bit mode + clear carry pha ; save a phx ; save x phy ; save y tsx ; make room for local var's txa ; a = old frame stack sbc #nlocals-1 ; here cf = 0 ! tcs ; new stack pointer inc a tcd ; dpr point to base of local var's txa clc adc #pushed ; pointer to top of return address on stack sta mvfrom ; pointer to source frame stack (be moved) inc a ; skip wCount param inc a ; pointer to top + 1 of wCount param adc wcnt ; add size of variables params tax inx ; pointer to top + 1 of variables params stx pframe ; this point to the lpFmt param adc #npars ; add known params size sta mvto ; pointer to dest. frame stack (be moved) am8 ; switch a/m to 8 bits lda #0 pha plb ; dbr => bank 0 ldy .abs.lpFmt,x ; get lpFmt pointer sty pfmt lda .abs.lpFmt+2,x sta pfmt+2 .endm ; leave x/y to 16 bits size ;--------------------------------------------------------------------------- ; local vars accessed by dpr ;--------------------------------------------------------------------------- printdp: .section page0, common, ref_only, offset 0 mvfrom .dw ; source frame stack to move mvto .dw ; destination frame stack to move fpusav .dw ; stack pointer to page where save fpu d.p. pframe .dw ; pointer on top of variable params pfmt lp ; long pointer to format string pbuf lp ; long pointer to output buffer dsize .dw ; destination buffer size ncnt .dw ; bytes count on output buffer hndl .db ; file handle outtyp .db ; output type (out_xxx constants) pstrm lp ; long pointer to output stream xfg .db ; extended flags: see fx_xxx constants fmtidx .dw ; current index to format string fmtspc .dw ; index to specifier format '%' wtmp .dw ; temp. word arg .ds 3 ; argument fetched from stack fmtsgn .db ; ' ' or '+' flag fmtflag .db ; flags bits: see f_xxx constants cpad .db ; pad char (blank or '0') width .db ; width field prec .db ; precision field isize .db ; integer size in bytes cfmt .db ; format type char slen .db ; string length pfx1 .db ; prefix 1 pfx2 .db ; prefix 2 outix .db ; output function index lpad .db ; count of left-padding rpad .db ; count of right-padding nerr .db ; error code dummy .db ; unused byte pdp_size .ds 0 nlocals .equ pdp_size ; local vars size ; following var's are all register pushed on stack from prologue code ; all accessed by direct page register, because are on top of last local var ; yr .dw ; pushed 16 bit y reg. xr .dw ; pushed 16 bit x reg. ar .dw ; pushed 16 bit a reg. dpr .dw ; pushed dp reg. dbr .db ; pushed db reg. sr .db ; pushed status reg. ; follow, on top, return address pushed by the caller (jsl instruction) ; pcr .dw ; return address - 1 pbr .db ; return bank ; on top of return address lie the last pushed param by the caller ; it is the wCnt param: the size, on bytes, of all variable param's ; wcnt .dw ; size of variables params ; we cannot access others params by the dp register because we cannot know ; in advance their size, that can exced the direct page limit ; .ends ;--------------------------------------------------------------------------- ; code segment ;--------------------------------------------------------------------------- .CODE ;--------------------------------------------------------------------------- ; public library interface ;--------------------------------------------------------------------------- ; cprintf(lpFmt, ..., wCount) ; ; format string and output to console ; ; stack param's: ; ; : long pointer to format string ; <...>: variable params ; : bytes count of variable params (16 bit) ; ; return: cf set if error, y = error code ; ; note: all regs. preserved but y hold the error code if error ; ;-------------------------------------- _cprintf: public _cprintf ;-------------------------------------- .longa off .longi off ; known parameters size: lpFmt ; npars .set 3 prolog npars ; prologue code lda #out_cons ; output type bra _print ; common code ; tprintf(lpFmt, ..., wCount) ; ; format string and output to current default text output device ; ; stack param's: ; ; : long pointer to format string ; <...>: variable params ; : bytes count of variable params (16 bit) ; ; return: cf set if error, y = error code ; ; note: all regs. preserved but y hold the error code if error ; ;-------------------------------------- _tprintf: public _tprintf ;-------------------------------------- .longa off .longi off ; known parameters size: lpFmt ; npars .set 3 prolog npars ; prologue code lda #out_text ; output type bra _print ; common code ; printf(lpFmt, ..., wCount) ; ; format string and output to standard out ; ; stack param's: ; ; : long pointer to format string ; <...>: variable params ; : bytes count of variable params (16 bit) ; ; return: cf set if error, y = error code ; ; note: all regs. preserved but y hold the error code if error ; ;-------------------------------------- _printf: public _printf ;-------------------------------------- .longa off .longi off ; known parameters size: lpFmt ; npars .set 3 prolog npars ; prologue code lda #stdout ; output to standard out sta hndl ; save standard out handle lda #out_file ; output type ; common code for cprintf/tprintf/dprintf/printf/fprintf ; _print sta outtyp ; output type stz pbuf+2 ; output buffer is in the stack (bank 0) c16c ; switch cpu to 16 bit + clear carry tsc ; make room for local buffer sbc #bufsize-1 ; here cf = 0 tcs ; set stack frame inc a ; local output buffer pointer sta pbuf lda #bufsize sta dsize c8 ; switch cpu to 8 bits jsr vprinter ; call the formattation routine ; epilogue code - re-align stack frame and return to the caller ; epilogue: c16c ; switch cpu to 16 bit ldx mvfrom ; source pointer for data move ldy mvto ; destination pointer for data move lda #pushed-1 ; move bytes count - 1 mvp #0, #0 ; cleanup stack tya ; new stack pointer tcs ply ; restore registers plx pla pld plb plp rtl ; dprintf(bHandle, lpFmt, ..., wCount) ; ; format string and output to file ; ; stack param's: ; ; : file handle (8 bit) ; : long pointer to format string ; <...>: variable params ; : bytes count of variable params (16 bit) ; ; return: cf set if error, y = error code ; ; note: all regs. preserved but y hold the error code if error ; ;-------------------------------------- _dprintf: public _dprintf ;-------------------------------------- .longa off .longi off ; known parameters size: bHandle, lpFmt ; npars .set 4 prolog npars ; prologue code lda .abs.bHandle,x ; get file handle sta hndl lda #out_file ; output type bra _print ; fprintf(lpStream, lpFmt, ..., wCount) ; ; format string and output to output stream ; ; stack param's: ; ; : long pointer to output stream ; : long pointer to format string ; <...>: variable params ; : bytes count of variable params (16 bit) ; ; return: cf set if error, y = error code ; ; note: all regs. preserved but y hold the error code if error ; ;-------------------------------------- _fprintf: public _fprintf ;-------------------------------------- .longa off .longi off ; known parameters size: lpStream, lpFmt ; npars .set 6 prolog npars ; prologue code ldy .abs.lpStream,x ; get stream pointer sty pstrm lda .abs.lpStream+2,x sta pstrm+2 lda #out_strm ; output type jmp _print ; snprintf(lpBuf, wSize, lpFmt, ..., wCount) ; ; format string to destination buffer ; ; stack param's: ; ; : long pointer to destination buffer ; if lpBuf=0 this function compute the need buffer size ; : destination buffer size, not including the nul term. ; if wSize=0 this function compute the need buffer size ; : long pointer to format string ; <...>: variable params ; : bytes count of variable params (16 bit) ; ; return: cf set if error, y = error code ; c = count of bytes copied to destination buffer ; not including the nul terminator or the minimum ; size of destination buffer if wSize=0 and/or lpBuf=0 ; (not including the count for nul terminator) ; ; note: x preserved (y preserved if no error) ; ;-------------------------------------- _snprintf: public _snprintf ;-------------------------------------- .longa off .longi off ; known parameters size: lpBuf, wSize, lpFmt ; npars .set 8 prolog npars ; prologue code ldy .abs.wSize,x ; get buffer size sty dsize beq ?clr ; clear output buffer pointer ldy .abs.lpBuf,x ; get buffer pointer sty pbuf lda .abs.lpBuf+2,x sta pbuf+2 ora pbuf ; test if nul pointer ora pbuf+1 beq ?cls ; clear dsize lda #out_buf ; format to output buffer bra ?go ?clr: sty pbuf ; clear buffer pointer stz pbuf+2 bra ?fake ?cls: stz dsize ; clear output buffer size stz dsize+1 ?fake: lda #out_fake ; fake format: just compute needs size ?go: sta outtyp ; store output type c8 ; switch cpu to 8 bits jsr vprinter ; call the formattation routine jmp epilogue ; stack cleanup & return to the caller .longa off .longi off ;--------------------------------------------------------------------------- ; main formattation routine ;--------------------------------------------------------------------------- vprinter: lda outtyp ; get output function index and #$0f asl a sta outix ; out function index stz xfg ; clear extended flag stz nerr ; clear error code ldy #0 xy16 ; switch x/y to 16 bits sty ncnt ; clear bytes count sty fmtidx ; clear format string index ; main loop: get and process characters from format string ; fmtidx = format string pointer ; ?loop: xy16 ; switch x/y to 16 bits ldy fmtidx ; get index to next character ?get: lda [pfmt],y beq ?done ; end of format string: terminate cmp #'%' ; get format specifier? beq ?get2 ; yes, get next character ?put: jsr putout ; put character "as is" (y preserved) bcs ?abrt ; if error occurred, abort process iny bra ?get ; get next character ?get2: iny lda [pfmt],y ; get next character after '%' cmp #'%' ; parsed '%%' ? beq ?put ; yes: put literal '%' and continue ; begin of format specifier: parse format ; jsr prsfmt ; return cpu in 8 bit mode .longa off .longi off bcs ?err ; format error occurred ; at this point the format specifier was recognized and we assume ; that was parsed correctly and x hold the index to the right ; function that get parameter(s) from stack and convert to string. ; If the conversion function use fpu emulator we save the direct-page ; used by fp emulator to a local page in stack (for reentrancy) ; cpx #t_int ; will use fp module? bcc ?go ; no bit xfg ; fp module d.p. already saved? bmi ?go ; yes ; make 256 bytes room on stack for save fp module d.p. ; stx wtmp ; save function index am16c ; switch a/m to 16 bit and clear carry tsc sbc #255 ; really subtract 256 tcs ; set new stack inc a ; address of saved page on stack sta fpusav ; save this pointer am8 jsl fmovedp ; save fp module d.p. lda #$80 ; set xfg<7>: fp module d.p. saved tsb xfg ldx wtmp ; get back function index ; call the conversion function ; ?go: jsr (cvtfnc,x) bcs ?abrt ; abort if an error occurred bra ?loop ; continue format string parsing ; parser error handler ; we put in output the format string starting from '%' delimiter ; where the error occurred, till the end of format string. ; ?err: xy16 ; switch x/y to 16 bits ldy fmtspc ; get index to '%' ?errl: lda [pfmt],y beq ?done ; end of format string: terminate jsr putout ; put character "as is" (y preserved) bcs ?abrt ; if error occurred, abort process iny bra ?errl ; get next character ; abort handler ; we arrive here for 2 raisons: ; - the output function fail (never fail if output to console): vf = 0 ; - or the destination buffer is full: vf = 1 ; in the first case we no call the flush buffer function (output fail) ; in the last case we terminate destination buffer with a null ; ?abrt: c8 bvc ?tst ; skip flush/terminate process ; terminate process ; call putend routine that flush local buffer or ; put null terminator to user global buffer ; ?done: c8 jsr putend ; this function can raise an error ; restore fp module direct page if was moved ; ?tst: bit xfg ; will restore fp module dp ? bpl ?rep ; no am16 lda fpusav jsl frestdp ; restore fp module d.p. tsc ; re-align stack pointer clc adc #256 tcs c8 ; in the case of snprintf(...) function we should return in c the ; total count of formatted character's ; ?rep: bit outtyp ; if called function is not snprintf... bpl ?tste ; ...go to report error (if any) lda ncnt ; reprt total count to the caller (in c reg.) sta ar lda ncnt+1 sta ar+1 ; set carry if an error occurred and report on y reg. any error code ; ?tste: ldx nerr beq ?end ; no error lda #pcflag ; set carry: error flag tsb sr ; saved status register stx yr ; store error code in y reg stz yr+1 ; clear high y reg. ?end: rts ; parse format specifier after '%' ; ; entry: y = pointer to next character after '%' ; ; exit: cf = 1 if error, x = nerr = error code ; vf = 0: error code = ed_format ; vf = 1: error code = ed_block ; ; cf = 0 if no error, x = conversion function index ; fmtidx = pointer to last parsed character ; ; note: on entry, x/y size = 16 bit ; on exit, cpu switched to 8 bit ; prsfmt: dey ; point to format specifier '%' sty fmtspc ; save pointer to '%' sty fmtidx lda #$7f trb xfg ; clear xfg but not xfg<7> lda #4 sta isize ; default integer size: 32 bit lda #' ' sta cpad ; default pad character: blank lda #0 ; a = starting stage sta fmtsgn ; clear working var's sta fmtflag sta width sta prec ?nxt: xy16 ; switch x/y to 16 bit ldy fmtidx iny ; get next character sty fmtidx ; save format string index xba ; b = current stage lda [pfmt],y beq ?err ; premature ending: parsing error sta wtmp ; save char sec sbc #' ' ; scale char to 00..5F bcc ?err ; illegal char at this stage cmp #(128 - ' ') bcs ?err ; illegal char at this stage c8 ; switch cpu to 8 bit tax ; x = 00..5F lda >classch,x ; a = class character lookup asl a ; twice tax ; parsing function index lda wtmp ; a = current char xba ; b = current char, a = current stage ; zf = 1 if current stage = 0 jsr (prsfnc,x) ; call parsing function ; return cf set if error otherwise: ; vf = 0 if will continue to parse current format specifier ; vf = 1 if current parsing is complete, x = conversion function index bcs ?ret ; format parsing error bvc ?nxt ; format parsing loop am16 inc fmtidx ; bump format string pointer am8 ?ret: rts ; return with cpu in 8 bit mode ?err: c8c ; switch cpu to 8 bit + set carry clv ; clear vf ldx #ed_format ; format error stx nerr rts ;---------------------------------------------------------------------------- ; conversion routines ; ; These routines convert any param basis on formattation specifier's, and put ; result string to file or output device or to destination buffer. ; ; on exit: ; cf = 0 if putting process no cause error ; cf = 1 & vf = 0 if putting process cause error ; set error to system call specific error code ; cf = 1 & vf = 1 ; wrong arguments -> ed_block ("invalid memory block") ; destination buffer is full -> ed_data ("invalid data") ; ;---------------------------------------------------------------------------- ; conversion function for float ; fconv: lda fmtflag bit #f_prec bne ?get ; precision was given ora #f_prec ; force precision to a default sta fmtflag lda #fminprec ; set a minimum precision sta prec ?get: jsr fetchlp ; get long pointer to float bne ?nxt ; not null pointer lda #fzero sta arg+1 lda #^fzero sta arg+2 ; now we call the library function that convert float in string ; ; fp2str(lpFloat, nPrec, cFormat, nFlags) ; ; this function return: ; ; c = address of string in bank 0 ; x = y = string length ; ?nxt: lda arg+2 ; lpFloat pha pei (arg) lda prec ; nPrec pha lda cfmt ; cFormat pha lda fmtflag ; nFlags pha jsl fp2str bra cnum ; numeric formattation ; conversion function for integer ; iconv: lda isize ; integer size jsr setarg ; set arg = long pointer to integer ; now we call the library function that convert integer in string ; ; lint2str(lpInt, nSize, nPrec, cFormat, nFlags) ; ; this function return: ; ; c = address of string in bank 0 ; x = y = string length ; lda arg+2 ; lpInt pha pei (arg) lda isize ; nSize pha lda prec ; nPrec pha lda cfmt ; cFormat pha lda fmtflag ; nFlags pha jsl int2str ; common code that handle numeric conversion ; cnum: stx slen ; string length sta arg ; arg = string pointer (in bank 0) xba sta arg+1 stz arg+2 stz pfx1 ; clear both prefixes stz pfx2 jsr strip ; strip prefixes from numeric string bra putstr ; format string ; conversion function for char type ; cconv: lda #1 ; char size = 1 jsr setarg ; set arg = long pointer to char ldx #1 ; string length = 1 lda (arg) ; test char bne ?st ; char not null: store length dex ; set length = 0 ?st: stx slen ; string length bra clrpfx ; clear pfx1 & pfx2 & format string ; conversion function for pascal/basic string ; psconv: jsr fetchlp ; fetch pointer to string bne ?nxt ; not null pointer lda #nullpstr sta arg+1 lda #^nullpstr sta arg+2 ?nxt: lda [arg] ; get string length sta slen inc arg ; bump string pointer bne tstlen inc arg+1 bne tstlen inc arg+2 bra tstlen ; test length ; conversion function for asciiz string ; sconv: jsr fetchlp ; fetch pointer to string bne ?nxt ; not null pointer lda #nullstr sta arg+1 lda #^nullstr sta arg+2 ?nxt: ldy #0 ?lp: lda [arg],y ; get string length beq ?set ; end of string iny bne ?lp dey ; limit string to 255 characters ?set: sty slen ; set string length ; test string length ; [precision] field can truncate a string ; tstlen: lda #f_prec bit fmtflag beq clrpfx ; no precision given lda prec cmp slen bcs clrpfx ; precision >= length: keep original length sta slen ; precision < length: string truncation ; clear both prefixes & format string to output device/buffer ; clrpfx: stz pfx1 ; clear both prefixes stz pfx2 ; now format string and put to output device ; or file or to destination buffer ; if any error, return nerr set to error code and carry set, and: ; vf = 0 if system call fail ; vf = 1 if user global buffer overflow (ed_block) ; putstr: stz lpad ; clear pad count stz rpad sec lda width ; get pad count sbc slen bcc ?nxt ; width < len: no pad at all beq ?nxt ; width = len: no pad at all tax ; x = pad count lda #f_leftj bit fmtflag beq ?lp ; right-align so left-pad stx rpad ; left-align so right-pad bra ?nxt ?lp: stx lpad ?nxt: lda pfx1 ; should put a first prefix? beq ?nxt1 ; no jsr putchr ?nxt1: lda pfx2 ; should put a 2nd prefix? beq ?nxt2 ; no jsr putchr ; now left-pad if we will do it ; ?nxt2: ldx lpad beq ?nxt3 ; no left-pad at all lda cpad ; pad character ?put1: jsr putchr dex bne ?put1 ; put the converted string ; ?nxt3: ldx slen beq ?nxt4 ; no char's to put ldy #0 ?put2: lda [arg],y ; put string jsr putchr iny dex bne ?put2 ; now right-pad if we will do it ; ?nxt4: ldx rpad beq ?end ; no left-pad at all lda cpad ; pad character ?put3: jsr putchr dex bne ?put3 ?end: clc ; no error rts ; strip any prefixes from numeric string, if needs ; ; the converted string can start with a '$' or '0x' (or '0X') for ; hexadecimal conversion, or can start with ' ' or a sign ('+' or '-') ; for a decimal conversion. If we should left-pad the string with '0', ; we need to put any prefix before to pad with '0'. If the padding ; character is blank, or if we should right-pad (left align), we can ; use the converted string without modification. Also, if the [width] ; field is zero we not need to pad. ; strip: lda cpad ; if pad character is blank... cmp #' ' beq ?ret ; ...we use unmodified string ; if width field is zero, we can use unmodified string ; lda width tax ; x = width beq ?ret ; if the given width is less or equal than converted string length, ; we no need to pad with '0', so we can use converted string unmodified ; sec sbc slen bcc ?ret ; cf = 0 if width < string length: no left pad beq ?ret ; width = string length: no left pad ; we need to strip any prefix and put it before to left pad ; we strip first character if is: '$' or ' ' or '+' or '-' ; x = width ; ldy #0 ; y = index lda (arg) ; get first character cmp #' ' beq ?stp ; strip a beginning blank cmp #'-' beq ?stp ; strip a beginning '-' cmp #'+' beq ?stp ; strip a beginning '+' cmp #'$' beq ?stp ; strip a beginning '$' ; if converted string is hexadecimal and start with '0x' or '0X' ; we will strip two prefixes ; bit xfg bvc ?ret ; no '0x' nor '0X' prefix to strip iny ; get 2nd character xba ; b = first prefix lda (arg),y sta pfx2 ; 2nd prefix: this is 'x' or 'X' dec slen ; update residual string length dex ; update width field xba ; first prefix: '0' ?stp: iny ; bump string pointer dex ; update width field stx width sta pfx1 ; first prefix clc tya adc arg ; update string pointer sta arg bcc ?ret inc arg+1 ?ret: rts ; set arg as long pointer to a block of stack args ; ; entry: a = block size in bytes ; ; exit: arg = byte ; ; cf & vf set if error ; set error ed_block ("invalid memory block") ; ; if error this routine don't return to the caller, ; but return to the main routine ; setarg: sta wtmp ; argument size stz wtmp+1 jsr fpalign ; align frame pointer bcs eskip ; error: return to the main routine lda pframe ; set arg = pframe sta arg lda pframe+1 sta arg+1 stz arg+2 ; pointer to bank 0 rts ; return cf reset ; fetch a long pointer from stack ; ; exit: arg = long pointer ; zf set if long pointer is null ; ; cf & vf set if error ; set error ed_block ("invalid memory block") ; ; if error this routine don't return to the caller, ; but return to the main routine ; fetchlp: lda #3 sta wtmp ; argument size = 3 stz wtmp+1 jsr fpalign ; align frame pointer bcs eskip ; error: return to the main routine xy16 ldx pframe ; x = frame pointer lda .abs.pbase,x ; get argument sta arg lda .abs.pbase+1,x sta arg+1 lda .abs.pbase+2,x sta arg+2 xy8 ora arg+1 ora arg ; zf = 1 if arg = null pointer rts ; return cf reset ; discard return address and return to the main routine ; eskip: pla pla rts ; align the frame pointer to next argument that should be fetched from stack ; ; entry: wtmp = next argument size in bytes ; pframe = current frame pointer ; ; exit: pframe = frame pointer to next argument ; cf = 1 & vf = 1 if frame is misaligned ; set error ed_block ("invalid memory block") ; ; use: b, a (x & y preserved) ; fpalign: am16 ; switch a/m to 16 bit sec lda wcnt ; check param's count sbc wtmp bcc ?err ; frame misaligned sta wcnt ; update counter lda pframe sbc wtmp sta pframe ; update pframe: point to next argument am8 clc ; no error rts ?err: am8cv ; fatal error: set cf & vf lda #ed_block sta nerr rts ;---------------------------------------------------------------------------- ; putter routines ;---------------------------------------------------------------------------- ; putout - put character to output device or file or to user global buffer ; call this routine for put out character from main routine ; should be called with x/y size = 16 bit and return with x/y size = 16 bit ; ; entry: a = character ; ; exit: cf = 0 if no error ; cf = 1, vf = 0 if device/file error (system call fail) ; cf = 1, vf = 1 if no room in user buffer (ed_data) ; ; use: a, x & y preserved ; putout: sty fmtidx ; save y (16 bit) c8 ; switch cpu to 8 bit jsr ?put ; even if error return here ; xy16 ; switch back x/y to 16 bit ldy fmtidx ; restore y (16 bit) rts ; return cf & vf as putchr routine ?put: jsr putchr ; put out char rts .longa off .longi off ; putchr - put character to output device or file or to user global buffer ; ; entry: a = character ; ; exit: cf = 0 if no error ; cf = 1, vf = 0 if device/file error (system call fail) ; cf = 1, vf = 1 if no room in user buffer (ed_data) ; ; use: a, x & y preserved ; ; if error this routine don't return to the caller, ; but return to the main routine ; putchr: phy ; save y xy16 ; switch x/y to 16 bits bit outtyp bmi ?buf ; put char in user global buffer ; output to device or file is buffered on local buffer ; ldy ncnt ; get count of bytes in local buffer cpy dsize ; local buffer is full? bcc ?put ; no -- put char c8 ; switch cpu to 8 bits pha ; save a & x in stack phx jsr flush ; flush local buffer plx ; restore a & x from stak pla bcs ?err2 ; device/file error: back to the main routine ldy #0 ; restart to begin of local buffer xy16 ; switch x/y to 16 bits ?put: sta (pbuf),y ; put char on local buffer (in bank 0) iny ; update bytes counter bra ?upd ; here always cf = 0 ; put char to user global buffer ; ?buf: bvs ?inc ; no store: just increment counter ldy ncnt ; get count of bytes in global buffer cpy dsize ; global buffer is full? bcs ?err ; yes sta [pbuf],y ; store byte to global buffer ?inc: iny ; update bytes counter beq ?err ; max. count limited to $ffff ?upd: sty ncnt c8 ; switch cpu to 8 bits ply ; restore y clc ; no error rts ?err: c8cv ; set cf & vf lda #ed_data ; "invalid data" sta nerr ?err2: ply ; restore y pla ; discard return address pla rts ; terminate the put process before main routine termination ; ; return cf = 1, vf = 0 if system call fail while flush local buffer ; error code set to specific system function return ; putend: bit outtyp bmi ?buf ; terminate global user buffer with final null lda ncnt ; need to flush local buffer? ora ncnt+1 bne flush ; yes - flush local buffer clc ; no error rts ?buf: bvs ?ret ; no store null terminator xy16 ldy ncnt lda #0 sta [pbuf],y ; put string terminator c8 ?ret: clc ; no error rts ; flush local buffer to output device or file ; ; return cf = 1 & vf = 0 if any error ; flush: ldx outix ; get out function index jmp (outfnc,x) ; call specific output function ; flush buffer to console - never cause error ; outcons: ldx #0 ; bank that hold buffer c16 lda pbuf ; c = buffer address ldy ncnt ; y = buffer length sbufout ; call function that output buffer to console c8 clc ; never fail rts ; flush buffer to current default text device ; outtxt: ldx #0 ; bank that hold buffer c16 lda pbuf ; c = buffer address ldy ncnt ; y = buffer length tbufout ; call function that output buffer to ; current text defaul device c8 bcs fail ; set system call error code rts ; TODO - must be implemented ; flush buffer to file handle ; outfile: clc rts ; TODO - must be implemented ; flush buffer to output stream ; outstrm: clc rts ; if a system call fail (cf = 1), clear vf and set the error code to the ; error code returned by the specific systema call in y ; fail: clv ; clear vf sty nerr ; report system call error code rts ;---------------------------------------------------------------------------- ; specifier format parsing/checking - funtions that checks type and modifiers ; ; in - b = current parsed char, a = current stage ; zf set if current stage = 0 ; ; out - cf set if error: unknow type/modifier not allowed/misaligned stack ; set x = error = ed_format (vf=0) or ed_block (vf=1) ; cf reset & vf set: x = conversion function index (end parsing) ; cf reset & vf reset: will continue parsing ; b = current parsed char, a = next stage ; ; Note: y preserved ; ;---------------------------------------------------------------------------- .longa off .longi off ; first of all start whith parsing flags, width & precision (if any) ; ; case '.' -- only valid if current stage < precstage ; 'dot' set a precision field ; case_pr: cmp #precstage bcs case_dc ; error if current stage >= precstage stz prec ; clear prec field lda #f_prec ; set 'precision' flag tsb fmtflag ldx #precstage ; set current stage clc ; no error clv ; continue parsing rts ; case '$' -- only valid while stage = 0 (flags stage) ; for hex. conversion ('x', 'X', 'p', 'P') prefix the hex. with '$' ; (or with '0x' or '0X' if alternate form is given). ; For float this flag mean to no discriminate 0.0 sign ; default case: no prefix for hexadecimal numbers, ; sign discrimination for float = 0.0 ; case_xp: bne case_dc ; error: invalid if not in flags stage tax ; x = stage lda #fx_xp ; set extended flag '$' bit tsb xfg txa bra case_cont ; continue at this current stage ; case ' ' or '+' -- only valid while stage = 0 (flags stage) ; set a flag if will emit a sign '+' or a blank for positive numbers ; default: no sign '+' nor blank for positive numbers ; '+' override any ' ' if both are given ; case_si: bne case_dc ; error: invalid if not in flags stage ldx fmtsgn cpx #'+' ; '+' override blank beq case_cont xba ; a = current char (blank or '+') sta fmtsgn ; store sign byte xba ; a = stage bra case_cont ; continue at this current stage ; case digit '0' - meaning depend on current stage ; if stage = 0: left padding with digit '0' instead of blank ; if stage > 0 then '0' is a digit of [width] or [precision] fields ; case_fz: bne case_nu ; stage > 0 => handle width/precision tax ; x = stage lda #f_leftj bit fmtflag ; if left aligned is specified... bne ?skp ; ...always pad with blanks xba sta cpad ; set pad with '0' if right aligned inx ; set stage = padstage ; next step expect 'width' stage ?skp: txa ; a = stage bra case_cont ; continue at this current stage ; case '-' -- only valid while stage = 0 (flags stage) ; set left align (default: right align) ; padding in the right always with blanks ; '-' override '0' if both are given ; case_lj: bne case_dc ; error: invalid if not in flags stage tax ; x = stage lda #' ' sta cpad ; set pad with blank because left aligned lda #f_leftj ; set left justify bit bra set_fg ; set flag & continue at this current stage ; case '#' -- only valid while stage = 0 (flags stage) ; set alternate format flag ; case_af: bne case_dc ; error: invalid if not in flags stage tax ; x = stage lda #f_altfmt ; set alternate format flag bra set_fg ; set flag & continue at this current stage ; case ',' -- only valid while stage = 0 (flags stage) ; set group thousand flag valid for decimal integer ; case_tg: bne case_dc ; error: invalid if not in flags stage tax ; x = stage lda #f_group ; set group thousand flag ; set fmtflag with bits in a ; set_fg: tsb fmtflag ; set flags txa ; a = stage ; reset cf & vf: no error & continue parsing ; case_cont: clc ; no error clv ; continue parsing rts ; unknow character was parsed, or unknow type/modifier - error ; set error ed_format : "invalid format" ; case_dc: sec ; set cf clv ; clear vf ldx #ed_format ; set error code stx nerr rts ; case '*' -- [width] or [precision] field passed by argument ; case_ar: xba ; save stage xy16 ldx wcnt ; update param's count beq ?err ; no more param's dex stx wcnt ldx pframe ; get frame pointer dex ; update frame pointer lda .abs.pbase,x ; get byte stx pframe xy8 tax ; x = param xba ; a = stage cmp #widestage bcs ?nxt ; stage >= widestage stx width ; store width ldx #dotstage ; expected next stage: [.precision] lda #f_width bra set_fg ; set flag & continue at this current stage ?nxt: cmp #precstage bne case_dc ; discard: unexpected stage level stx prec ; store precision tax ; x = stage lda #f_prec ; set 'precision' bit bra set_fg ; set flag & continue at this current stage ?err: c8cv ; set carry and vf ldx #ed_block ; set error: "invalid memory block" stx nerr rts ; case '0'..'9' - digit that are part of [width] or [precision] ; width and prec are reset to zero before enter parsing routine, ; so any parsed digit are added to current width or prec value ; case_nu: tax ; x = stage xba ; a = char '0'..'9', b = stage sec sbc #'0' ; scale to 0..9 cpx #widestage+1 ; part of [width] field ? bcs ?prc ; no: current stage > widestage tax ; x = digit 0..9 lda width stx width beq ?z ; old width = 0: just store digit jsr ?add ; add digit to old value bcs case_dc ; overflow error sta width ?z: lda #f_width ; set valid width field flag ldx #widestage ; set current stage: 'width' bra set_fg ; set flag & continue at widestage ?prc: cpx #precstage bne case_dc ; error: unexpected stage level tax ; x = digit 0..9 lda prec stx prec beq ?z1 ; old prec = 0: just store digit jsr ?add ; add digit to old value bcs case_dc ; overflow error sta prec ?z1: lda #f_prec ; set valid precision field flag ldx #precstage ; keep current stage bra set_fg ; set flag & continue at this current stage ; add digit in x to byte in a ; ?add: stx wtmp ; wtmp = digit asl a ; width * 2 bcs ?ret ; overflow sta wtmp+1 asl a bcs ?ret ; overflow asl a ; width * 8 bcs ?ret ; overflow adc wtmp+1 ; width * 10 bcs ?ret ; overflow adc wtmp ; (value * 10) + digit ?ret: rts ; cf set if overflow ; modifier are allowed only if current stage < modstage ; these routines set stage = modstage, after just types are expected ; modifiers allowed just for integer conversion: default size 32 bits ; ; case 'H' - 'short short' modifier (8 bit integer) ; case_hh: ldx #1 ; 1 byte integer bra setmod ; case 'h' - 'short' modifier (16 bit integer) ; case_sh: ldx #2 ; 2 bytes integer bra setmod ; case 'l' - 'long' modifier (64 bit integer) ; case_lo: ldx #8 ; 8 bytes integer bra setmod ; case 'L' - 'long long' modifier (128 bit integer) ; case_ll: ldx #16 ; 16 bytes integer ; set stage = modstage: after just types are expected ; setmod: cmp #modstage bcs ?err ; error if at stage above or equal modstage stx isize ; set integer size lda #fx_mod ; set modifier bit flag (for later test) tsb xfg lda #modstage ; set current stage clc ; no error clv ; continue rts ?err: ldx #ed_format ; set error code stx nerr clv ; clear vf rts ; specifier format types are parsed at any level stage and stop parsing ; if a valid type is recognized returning cf reset and vf set and the ; function index that handle conversion for the specified type. ; Invalid types and/or invalid modifiers return carry set (error) ; ; case 'f', 'e', 'g', 'a', 'k': lower case float ; case 'F', 'E', 'G', 'A', 'K': upper case float ; modifiers not allowed ; case_ff: jsr tstmod ; no modifier allowed xba sta cfmt ; format char: 'f','F', 'e', .... and #$df ; upper case cmp #'A' beq ?hex ; hex. float conversion cmp #'K' beq ?hex ; hex. float conversion ; decimal float conversion ; ldx fmtsgn ; was given '+'/blank prefix? txa beq ?nxt ; no, not prefix (a = 0) lda #f_pfx ; set prefix bit in fmtflag cpx #'+' bne ?nxt ; prefix positive float with blank ora #f_pls ; prefix positive float with '+' ?nxt: ora #f_signed ; decimal float are signed tax lda xfg ; test bit 0 (fx_xp) lsr a ; cf set if was specified '$' flag txa bcc ?set ora #f_nz ; float = 0.0 always positive ?set: sta wtmp ; save additional flags lda fmtflag and #fmaskf ; mask off invalid dec. float flags ora wtmp ; or additional flags sta fmtflag ; format flags for conversion function ldx #t_float ; x = float function conversion sep #pvflag ; set vf: stop parsing clc ; no error rts ; float hexadecimal form: ignore sign & precision ; always pad with blanks ; ?hex: ldx #' ' ; always pad with blanks stx cpad stz prec ; clear precision lda xfg ; bit 0 is set if we will prefix hex. and #fx_xp ; '$' bit sta wtmp lda fmtflag and #fmaskhf ; mask off invalid hex. float flags ora wtmp ; or prefix (if any) sta fmtflag ; format flags for conversion function ldx #t_float ; x = float function conversion bra setpfx ; set prefix for '0x' or '0X' ; case 'x', 'X' -- hexadecimal integer ; integer default size: 32 bits ; modifiers allowed ; case_hx: xba ; format char: 'x' or 'X' sta cfmt lda xfg ; bit 0 is set if we will prefix hex. and #fx_xp ; '$' bit sta wtmp lda fmtflag and #fmaskx ; mask on valid hexadecimal flags bit #f_prec beq sethex ; no precision given ldx #' ' ; if precision is given... stx cpad ; ...always pad hex with blanks bra sethex ; case 'p', 'P' -- hexadecimal pointer ; hex. pointer always are formatted as 6 hex. digits ; case_pt: jsr tstmod ; no modifier allowed xba ; format char: 'p' or 'P' sta cfmt lda #' ' ; pad with blank (ignore '0' flag) sta cpad lda #3 ; size = 3 sta isize lda #6 ; always precision = 6 for pointer sta prec lda xfg ; bit 0 is set if will prefix hex. and #fx_xp ; '$' bit ora #f_prec ; force precision flag sta wtmp lda fmtflag and #fmaskx ; mask on valid hexadecimal flags ; entry point for hexadecimal integer type ; in: a = format flag ; sethex: ora wtmp ; or prefix + prec bits sta fmtflag ; format flags for conversion function ldx #t_int ; x = integer function conversion ; set prefix '0x' or '0X' flag if was specified in alternate format ; in: a = format flag ; x = conversion function index ; out: vf set ; setpfx: and #pfxmask ; mask hex. prefix & alternate form cmp #pfxmask bne ?vf ; no '0x' or '0X' prefix lda #fx_pfx ; set fx_pfx bit in xfg tsb xfg ?vf: sep #pvflag ; set vf: stop parsing clc ; no error rts ; case 'd', 'i' -- signed decimal integer ; integer default size: 32 bits ; modifiers allowed ; case_de: lda #f_signed tsb fmtflag ; set signed integer bit ; case 'u' -- unsigned decimal integer ; integer default size: 32 bits ; modifiers allowed ; case_un: lda #'d' ; set decimal conversion type sta cfmt ; case don't care ldx fmtsgn ; was given '+'/blank prefix? txa beq ?nxt ; no, not prefix (a = 0) lda #f_pfx ; set prefix bit in fmtflag cpx #'+' bne ?nxt ; prefix positive integer with blank ora #f_pls ; prefix positive integer with '+' ?nxt: sta wtmp ; save additional flags lda fmtflag and #fmaskd ; mask on valid hexadecimal flags bit #f_prec beq ?set ; no precision given ldx #' ' ; if precision is given... stx cpad ; ...always pad with blanks ?set: ora wtmp ; or prefix + prec bits sta fmtflag ; format flags for conversion function ldx #t_int ; x = integer function conversion bra case_ok ; case 'c' -- char ; character format requires precision = 1 ; modifiers not allowed ; case_ch: jsr tstmod ; no modifier allowed xba sta cfmt ; format char: 'c' lda #1 sta prec ; set precision = 1 lda fmtflag and #fmaskc ; mask off invalid char flags sta fmtflag ldx #t_chr bra case_ok ; case 's' -- asciiz string ; modifiers not allowed ; case_st: jsr tstmod ; no modifier allowed xba sta cfmt ; format char: 's' ldx #t_str bra strings ; case 'S' -- pascal/basic string ; modifiers not allowed ; case_ps: jsr tstmod ; no modifier allowed xba sta cfmt ; format char: 'S' ldx #t_pstr ; common code for asciiz/pascal/basic strings ; strings: lda fmtflag and #fmasks ; mask off invalid strings flags sta fmtflag ; return cf reset & vf set ; case_ok: sep #pvflag ; set vf: stop parsing clc ; no error rts ; test if a modifier was given and return cf set if yes ; tstmod: lda xfg ; test bit 1 lsr a lsr a bcc ?ret ; ok pla ; discard return address... pla ; ...and return to main routine with cf set ldx #ed_format ; set error code stx nerr clv ; clear vf ?ret: rts ;--------------------------------------------------------------------------- ; tables ;--------------------------------------------------------------------------- ; character's class lookup table (32 <= char < 128) ; classch: ; SP ! " # $ % & ' ( ) * + , - . / .db _si,_dc,_dc,_af,_xp,_dc,_dc,_dc,_dc,_dc,_ar,_si,_tg,_lj,_pr,_dc ; 0 1 2 3 4 5 6 7 8 9 : ; < = > ? .db _fz,_nu,_nu,_nu,_nu,_nu,_nu,_nu,_nu,_nu,_dc,_dc,_dc,_dc,_dc,_dc ; _ A B C D E F G H I J K L M N O .db _dc,_ff,_dc,_dc,_dc,_ff,_ff,_ff,_hh,_dc,_dc,_ff,_ll,_dc,_dc,_dc ; P Q R S T U V W X Y Z [ \ ] ^ _ .db _pt,_dc,_dc,_ps,_dc,_dc,_dc,_dc,_hx,_dc,_dc,_dc,_dc,_dc,_dc,_dc ; ` a b c d e f g h i j k l m n o .db _dc,_ff,_dc,_ch,_de,_ff,_ff,_ff,_sh,_de,_dc,_ff,_lo,_dc,_dc,_dc ; p q r s t u v w x y z { | } ~ DEL .db _pt,_dc,_dc,_st,_dc,_un,_dc,_dc,_hx,_dc,_dc,_dc,_dc,_dc,_dc,_dc ; parsing functions table ; prsfnc: .dw case_dc, case_si, case_af, case_ar .dw case_lj, case_pr, case_nu, case_lo .dw case_sh, case_hh, case_fz, case_de .dw case_un, case_hx, case_pt, case_ch .dw case_st, case_ff, case_tg, case_ll .dw case_ps, case_xp ; conversion functions table cvtfnc: .dw cconv, sconv, psconv, iconv, fconv ; output function table ; outfnc: .dw outcons, outtxt, outfile, outstrm ; pascal string 'null' ; nullpstr: .db nplen ; asciiz string 'null' ; nullstr: .db '(null)', 0 nplen .equ $ - nullstr - 1 ; float constant = 0.0 ; fzero: .db $00, $00, $00, $00, $00, $00, $00, $00 .db $00, $00, $00, $00, $00, $00, $00, $00 ;--------------------------------------------------------------------------- ; end of file ;--------------------------------------------------------------------------- .end