Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.sys.apple2.programmer > #2219 > unrolled thread

printf for 65c02

Started bywssimms@gmail.com
First post2016-02-13 20:29 -0800
Last post2016-02-18 13:37 -0800
Articles 18 on this page of 38 — 7 participants

Back to article view | Back to comp.sys.apple2.programmer


Contents

  printf for 65c02 wssimms@gmail.com - 2016-02-13 20:29 -0800
    Re: printf for 65c02 wssimms@gmail.com - 2016-02-13 20:31 -0800
      Re: printf for 65c02 wssimms@gmail.com - 2016-02-13 20:36 -0800
        Re: printf for 65c02 Antoine Vignau <antoine.vignau@laposte.net> - 2016-02-13 23:22 -0800
          Re: printf for 65c02 Michael Barry <barrym95838@yahoo.com> - 2016-02-15 22:57 -0800
            Re: printf for 65c02 wssimms@gmail.com - 2016-02-16 01:54 -0800
              Re: printf for 65c02 "Bill Garber" <willy46pa@comcast.net> - 2016-02-16 21:11 -0500
                Re: printf for 65c02 Michael Pohoreski <michael.pohoreski@gmail.com> - 2016-02-18 16:03 -0800
                  Re: printf for 65c02 wssimms@gmail.com - 2016-02-18 18:07 -0800
                  Re: printf for 65c02 Michael J. Mahon <mjmahon@aol.com> - 2016-02-19 03:01 -0600
                    Re: printf for 65c02 Michael Pohoreski <michael.pohoreski@gmail.com> - 2016-02-19 08:04 -0800
                      Re: printf for 65c02 qkumba <peter.ferrie@gmail.com> - 2016-02-19 10:05 -0800
                        Re: printf for 65c02 Michael Pohoreski <michael.pohoreski@gmail.com> - 2016-02-20 00:37 -0800
                          Re: printf for 65c02 qkumba <peter.ferrie@gmail.com> - 2016-02-20 09:46 -0800
                            Re: printf for 65c02 Michael Pohoreski <michael.pohoreski@gmail.com> - 2016-02-20 11:35 -0800
                        Re: printf for 65c02 wssimms@gmail.com - 2016-02-20 04:40 -0800
                          Re: printf for 65c02 Michael Pohoreski <michael.pohoreski@gmail.com> - 2016-02-20 08:14 -0800
                            Re: printf for 65c02 Michael J. Mahon <mjmahon@aol.com> - 2016-02-22 14:13 -0600
                          Re: printf for 65c02 Michael Pohoreski <michael.pohoreski@gmail.com> - 2016-02-20 10:52 -0800
                          Re: printf for 65c02 Michael Pohoreski <michael.pohoreski@gmail.com> - 2016-02-21 00:53 -0800
                            Re: printf for 65c02 wssimms@gmail.com - 2016-02-25 09:26 -0800
                              Re: printf for 65c02 Michael Pohoreski <michael.pohoreski@gmail.com> - 2016-02-25 12:36 -0800
          Re: printf for 65c02 Michael Pohoreski <michael.pohoreski@gmail.com> - 2016-02-18 07:38 -0800
    Re: printf for 65c02 Michael Pohoreski <michael.pohoreski@gmail.com> - 2016-02-16 15:27 -0800
      Re: printf for 65c02 wssimms@gmail.com - 2016-02-16 18:48 -0800
        Re: printf for 65c02 Michael Pohoreski <michael.pohoreski@gmail.com> - 2016-02-16 19:13 -0800
          Re: printf for 65c02 wssimms@gmail.com - 2016-02-17 07:49 -0800
          Re: printf for 65c02 wssimms@gmail.com - 2016-02-17 07:55 -0800
      Re: printf for 65c02 wssimms@gmail.com - 2016-02-16 18:55 -0800
        Re: printf for 65c02 wssimms@gmail.com - 2016-02-16 18:58 -0800
        Re: printf for 65c02 Michael Pohoreski <michael.pohoreski@gmail.com> - 2016-02-18 13:14 -0800
          Re: printf for 65c02 Michael Pohoreski <michael.pohoreski@gmail.com> - 2016-02-18 13:27 -0800
            Re: printf for 65c02 Michael Pohoreski <michael.pohoreski@gmail.com> - 2016-02-18 15:09 -0800
              Re: printf for 65c02 wssimms@gmail.com - 2016-02-18 17:58 -0800
                Re: printf for 65c02 Michael Barry <barrym95838@yahoo.com> - 2016-02-19 17:52 -0800
                  Re: printf for 65c02 wssimms@gmail.com - 2016-02-19 18:09 -0800
                    Re: printf for 65c02 Michael J. Mahon <mjmahon@aol.com> - 2016-02-20 00:27 -0600
    Re: printf for 65c02 Michael Pohoreski <michael.pohoreski@gmail.com> - 2016-02-18 13:37 -0800

Page 2 of 2 — ← Prev page 1 [2]


#2376

Fromwssimms@gmail.com
Date2016-02-25 09:26 -0800
Message-ID<adc59a74-cc23-4a58-8418-02359a0d37c9@googlegroups.com>
In reply to#2334
Am Sonntag, 21. Februar 2016 17:53:47 UTC+9 schrieb Michael Pohoreski:
> On Saturday, February 20, 2016 at 4:40:38 AM UTC-8, wss...@gmail.com wrote:
> > Thanks. I've incorporated all of your changes into my source.
> > I wrote this code probably 8 or 9 months ago and I remember
> > thinking I wanted the loops unrolled at the time, but when I
> > now think of how I use this routine, the few cycles saved by
> > unrolling are completely inconsequential.
> 
> This may be of interest:  
> 
> I optimized PrintDec so that it supports a variable width output AND doesn't need to
> do a "reverse print" using the bcd buffer.

It took me a while to look at this and, frankly, that code is really hard to read with all
the conditional assembly stuff, but I think i finally figured out that you are using
a different format character for each width. Seems reasonable, and if it saves space,
useful too.

Personally, at the moment I'm really not that concerned with space optimization. I
follow some personal guidelines to keep things somewhat tight, but if my code is
10-15% bigger, so be it for now. I'm hoping to make a preliminary release of my
Apple II project sometime in the summer, but unfortunately I don't have much time to
work on it and I have a very large amount of code left to be written, so I have to be a
bit lax on optimization.

[toc] | [prev] | [next] | [standalone]


#2378

FromMichael Pohoreski <michael.pohoreski@gmail.com>
Date2016-02-25 12:36 -0800
Message-ID<741560b8-90ab-487a-a965-b8f0b05efeef@googlegroups.com>
In reply to#2376
On Thursday, February 25, 2016 at 10:26:22 AM UTC-7, wss...@gmail.com wrote:
> It took me a while to look at this and, frankly, that code is really hard to read with all
> the conditional assembly stuff, 

That's one of the down-sides.

> but I think i finally figured out that you are using
> a different format character for each width. Seems reasonable, and if it saves space,
> useful too.

That's the benefit.  If you don't use one of the formatting characters, none of the relevant code is included. i.e. Why waste space with Print Octal code if you never use octal ?


> Personally, at the moment I'm really not that concerned with space optimization. I
> follow some personal guidelines to keep things somewhat tight, but if my code is
> 10-15% bigger, so be it for now. I'm hoping to make a preliminary release of my
> Apple II project sometime in the summer, but unfortunately I don't have much time to
> work on it and I have a very large amount of code left to be written, so I have to be a
> bit lax on optimization.

Looking forward to when you release this.  Keep up posted!

[toc] | [prev] | [next] | [standalone]


#2262

FromMichael Pohoreski <michael.pohoreski@gmail.com>
Date2016-02-18 07:38 -0800
Message-ID<ddb593b4-f0dc-43e2-81e8-24be8c766c96@googlegroups.com>
In reply to#2222
On Saturday, February 13, 2016 at 11:22:26 PM UTC-8, Antoine Vignau wrote:
> Oh yeah, where are the comments?

I've started adding comments ... since that `PrintDec` is absolutely brilliant!


PrintDec
        STX val+0
        STY val+1

        STZ bcd+0
        STZ bcd+1
        STZ bcd+2

Dec2BCD
        LDX   #16
        SED
_Dec2BCD
        ASL val+0
        ROl val+1

        LDA bcd+0
        ADC bcd+0
        STA bcd+0

        LDA bcd+1
        ADC bcd+1
        STA bcd+1

        LDA bcd+2
        ADC bcd+2
        STA _cd+2

        DEX
        BNE _Dec2BCD
        CLD

BCD2Char
        LDX #2
        LDY #5
_BCD2Char           ; Pass 1    Pass 2  Pass 3
        LDA bcd,X   ; ab?000    a?_0XX  ?_YYXX
        LSR
        LSR
        LSR
        LSR
        CLC
        ADC #'0' + $80
        STA bcd,Y   ; ab?00X    a?_YXX  ?ZYYXX
        DEY

        LDA bcd,X   ; ab?00X    a?_YXX  ?ZYYXX
        AND #$F
        CLC
        ADC #'0' + $80
        STA bcd,Y   ; ab?0XX    a?YYXX  ZZYYXX
        DEY
        DEX
        BPL _BCD2Char

[toc] | [prev] | [next] | [standalone]


#2247

FromMichael Pohoreski <michael.pohoreski@gmail.com>
Date2016-02-16 15:27 -0800
Message-ID<5af5b88b-ed0e-457d-bab8-28f6a4f531a4@googlegroups.com>
In reply to#2219
On Saturday, February 13, 2016 at 8:29:36 PM UTC-8, wss...@gmail.com wrote:
> I just posted 65c02 code on comp.sys.apple2 that calls printf.
> I thought that might pique someone's interest, so here is my printf
> for 65c02.

Personally I think printf() is a little overkill / bloated on the 6502, but this is not bad, not bad at all !  Nice job!  This is pretty cool, especially the printd !


> If someone actually has an interest in reading and understanding it, and finds themselves stumped, I'll be happy to answer any questions. 

0. I wish you had used case to distinguish between Funcs and Vars. :-/  Would of helped with the readability.

1. I assume "scien" is short for Scientific Notation ? Not everyone may be familiar with the abbreviation(s).

Everything is pretty straightforward, except for these two:

2 a) What does "Logical" do? Looks like it an alias for printf Long ?
; %l  Print Logical ??

logical 
               jsr   nxtarg 
logic1 
               jsr   printd 


2 b) What does "Remote" do ? Looks like it prints a Pointer ?
; %r  Print Remote


I'll second Antoine's Request For Comments --  maybe document the input registers? :-)

i.e.
; Get Argument Stack Pointer -> XY
getsp

; Set Argument Stack Pointer: XY -> 
setsp

; Print 16-bit XY as Decimal
printd

; Print XY as hex
printx 

; Get Pointer to next argument -> XY
nxtarg

; Increment argument pointer, return argument in A
nxtargb

; %d  Print decimal
; %o  Print octal
; %x  Print hex
; %f  Print float
; %e  Print float in Scientific Notation
; %c  Print Char
; %s  Print string
; %l  Print Long
; %r  Print Pointer
printf


Assuming my math is correct, one can save 3 bytes by factoring out the double "jsr nxtarg" :-)

nxtarg2        jsr   nxtarg  ; intentional fall into
nxtarg         jsr   nxtargb 

pfloat 
               jsr   nxtarg2   ; OLD: jsr nxtarg jsr nxtarg
pscien 
               jsr   nxtarg2   ; OLD: jsr nxtarg jsr nxtarg

And save another 1 byte:

float 
               ldx   ndigit 
               lda   ndfnd 
               jsr   pfloat 
               bra  scien2 ; OLD: jmp   prbuf

scien 
               ldx   ndigit 
               lda   ndfnd 
               jsr   pscien 
scien2
               jmp   prbuf 

Nice 650C02 usage. :-)


> I ripped this code out of a larger project for posting

Yeah, I'm working on m own mini-version taking advantage of Apple'ism since I don't need the useless octal or less needed floats.  I really should get that sub-project done. :-)

[toc] | [prev] | [next] | [standalone]


#2249

Fromwssimms@gmail.com
Date2016-02-16 18:48 -0800
Message-ID<3b4f20f7-f5d2-4147-a62b-27a3fa244853@googlegroups.com>
In reply to#2247
Am Mittwoch, 17. Februar 2016 08:27:17 UTC+9 schrieb Michael Pohoreski:
> Everything is pretty straightforward, except for these two:
> 
> 2 a) What does "Logical" do? Looks like it an alias for printf Long ?
> ; %l  Print Logical ??
> 
> logical 
>                jsr   nxtarg 
> logic1 
>                jsr   printd 

logical prints a 16-bit integer as an unsigned value. This printf doesn't
know about long values. If you look at decimal, you can see that all it
does is test the signedness of the value retrieved from the argument
stack and, if negative, does a manual negation by XORing #$FFFF, etc.,
prints '-', and jumps to logic1

logical, on the other hand, calls printd to generate the decimal repre-
sentation in the BCD buffer. The rest of logical is concerned with the
formatting of the field width and printing number of digits requested,
including leading zeros, if necessary.

> 
> 2 b) What does "Remote" do ? Looks like it prints a Pointer ?
> ; %r  Print Remote

It allows you to insert another set of arguments to printf. 

char *greeting = "Hello";
char *title = "Mr.";
char *nameargs[] = { "%s/%s", "Smith", "Jones" };
printf("%s %s %r!", greeting, title, nameargs);

yielding:

Hello Mr. Smith/Jones!

This was a feature of early printfs that was removed for portability
and type-safety reasons as Unix and C developed.

> Assuming my math is correct, one can save 3 bytes by factoring out the double "jsr nxtarg" :-)

No doubt there are savings to be had. I haven't put much if any
effort into optimizing code size.

[toc] | [prev] | [next] | [standalone]


#2254

FromMichael Pohoreski <michael.pohoreski@gmail.com>
Date2016-02-16 19:13 -0800
Message-ID<c2de8e12-e0f5-4cf7-ba1d-5d5d8c747c6e@googlegroups.com>
In reply to#2249
On Tuesday, February 16, 2016 at 6:48:43 PM UTC-8, wss...@gmail.com wrote:
> > 2 b) What does "Remote" do ? Looks like it prints a Pointer ?
> > ; %r  Print Remote
> 
> It allows you to insert another set of arguments to printf. 
> 
> char *greeting = "Hello";
> char *title = "Mr.";
> char *nameargs[] = { "%s/%s", "Smith", "Jones" };
> printf("%s %s %r!", greeting, title, nameargs);
> 
> yielding:
> 
> Hello Mr. Smith/Jones!
> 
> This was a feature of early printfs that was removed for portability
> and type-safety reasons as Unix and C developed.

Ah, thanks for the clarification Sheldon.
That "should" make sense but I'm missing something ... :-(
I guess it is has been too long of a day ... :-/

Everything but the %r works in this cc65 demo program.

; ca65
.feature c_comments
.feature labels_without_colons
.feature leading_dot_in_identifiers
; 65C02
.PC02

.macro asc text
    .byte text
.endmacro

.macro db val
    .byte val
.endmacro

.macro dw val
    .word val
.endmacro

.macro ds bytes
    .res bytes
.endmacro

              __MAIN= $6000
        .word __MAIN         ; 2 byte BLOAD address
        .word __END - __MAIN ; 2 byte BLOAD size
        .org  __MAIN         ; .org must come after header else offsets are wrong

                HOME = $FC58

                JSR HOME
                LDX #<DATA
                LDY #>DATA
                JSR SetArgStackPtr
                JSR Printf
                RTS

TEXT
                asc "Dec = %d, Hex = %x, Oct = %o, Chr = %c"
                db  $8D
                asc "String = %-14s, Long = %7l"
                db  $8D
                asc "Float = %f, Sci = %e"
                db  $8D
                asc "Remote = %r"
                db  $8D
                db  0
DATA
                dw  TEXT    ; printf( text, ... );
                dw  123     ; %d 123
                dw  123     ; %x  7B
                dw  123     ; %o 173
                dw  123     ; %c   {
                dw  HELLO   ; %s Hello World
                dw  $FF69   ; %l 65385 == -151 == $FF69
                dw  $3F80   ; %f // not implemented
                dw  $3F80   ; %e // not implemented
                dw  ARG2    ; %r

HELLO
                asc "Hello World!"
                db  0

APPLE
                asc "Apple //e"
                db  0

DONE
                asc "Done."
                db  0

ARG2
                dw  DATA2
                asc "%s/%s"
                db  0

DATA2
                dw  APPLE
                dw  DONE



ArgStack        dw  0

; Main entry point is Printf
;               xc
;               rel
;               use   sys
;               dsk   printf.l
;
;getsp          ext
;setsp          ext
;putchar        ext

; Addr -> XY
GetArgStackPtr
                ldx ArgStack+0
                ldy ArgStack+1
                rts

; XY -> Addr
SetArgStackPtr
                stx ArgStack+0
                sty ArgStack+1
                rts

PutChar
                ORA #$80
                JMP $FDED

bcd            ds    6  ; 999999
nval           dw    0

PrintDec
               stx   nval
               sty   nval+1
               stz   bcd
               stz   bcd+1
               stz   bcd+2
               ldx   #16
               sed
@_a
               asl   nval
               rol   nval+1
               lda   bcd
               adc   bcd
               sta   bcd
               lda   bcd+1
               adc   bcd+1
               sta   bcd+1
               lda   bcd+2
               adc   bcd+2
               sta   bcd+2
               dex
               bne   @_a
               cld
               ldx   #2
               ldy   #5
@_b            lda   bcd,x
               lsr
               lsr
               lsr
               lsr
               clc
               adc   #'0'
               sta   bcd,y
               dey
               lda   bcd,x
               and   #$0F
               clc
               adc   #'0'
               sta   bcd,y
               dey
               dex
               bpl   @_b
               rts


; Print XY in Hex
PrintHex
               stx   nval
               sty   nval+1
               ldx   #0
@_1            lda   nval
               and   #15
               cmp   #10    ; $0..$9
               bcc   @_2
               adc   #6     ; $A + (C=1) -> $10
@_2            adc   #'0'
               sta   bcd,x
               lsr   nval+1
               ror   nval
               lsr   nval+1
               ror   nval
               lsr   nval+1
               ror   nval
               lsr   nval+1
               ror   nval
               inx
               cpx   #6     ; Print 5 hex digits
               bne   @_1
               rts

; Print XY in Octal
PrintOct
               stx   nval
               sty   nval+1
               ldx   #0
@_1            lda   nval
               and   #7
               clc
               adc   #'0'
               sta   bcd,x
               lsr   nval+1
               ror   nval
               lsr   nval+1
               ror   nval
               lsr   nval+1
               ror   nval
               inx
               cpx   #6
               bne   @_1
               rts

rjust          db    0
ndigit         db    0
width          db    0

Printf         ;ent
               jsr   GetArgStackPtr
               stx   argptr+1
               sty   argptr+2
               stz   argidx
               jsr   NxtArg
               stx   GetFormat+1    ; NOTE: self-modifying
               sty   GetFormat+2    ; NOTE: self-modifying
loop
               jsr   FormatChar
               cmp   #0         ; ASCIIZ
               beq   @_1a
               cmp   #'%'       ; have escape char?
               beq   @_2a
@_3
               jsr   PutChar
               bra   loop
@_1a                            ; end of string
               jsr   FixArgStack
               rts
@_2a
               stz   rjust
               stz   ndigit
               jsr   GetFormat
               cmp   #'-'       ; have width?
               bne   @_2b
               jsr   FormatChar
               inc   rjust
@_2b
               jsr   GetNum
               ldx   nval
               stx   width
               stz   ndfnd
               cmp   #'.'
               bne   @_1b
               jsr   GetNum
               ldx   nval
               stx   ndigit
@_1b
               sta   @_1d+1     ; NOTE: Self-modifying
               stz   fbidx
               ldx   #0
@_1c
               lda   PrintSpecifier,x
               beq   @_1e
               inx
@_1d           cmp   #0         ; NOTE: Self-modifying: width
               bne   @_1c
               dex
               txa
               asl
               tax
               jmp   (PrintFunction,x)
@_1e           lda   @_1d+1
               bra   @_3    ; always

PrintSpecifier
               db    'd'
               db    'o'
               db    'x'
               db    'f'
               db    'e'
               db    'c'
               db    's'
               db    'l'
               db    'r'
               db    0  ; end of table

PrintFunction
               dw    DoDecimal
               dw    DoOctal
               dw    DoHex
               dw    DoFloatStandard
               dw    DoFloatScientific
               dw    DoCharac
               dw    DoString
               dw    DoLong
               dw    DoRemote

DoDecimal
               jsr   NxtArg
               tya
               bpl   _DoLong1
               eor   #$FF
               sta   nval+1
               txa
               eor   #$FF
               inc
               sta   nval
               bne   @_0
               inc   nval+1
@_0            lda   #'-'
               ldy   fbidx
               inc   fbidx
               sta   fbuf,y
               ldx   nval
               ldy   nval+1
               bra   _DoLong1

; logical 
DoLong
               jsr   NxtArg
;logic1
_DoLong1
               jsr   PrintDec
nm2bf          ldx   #5     ; 16-bit max 5 decimal digits
               ldy   fbidx
@_1a           lda   bcd,x
               cpx   ndigit
               bcc   @_1d
               dex
               bmi   @_1c
               cmp   #'0'
               bne   @_1b
               bra   @_1a    ; always
@_1d           dex
@_1b           sta   fbuf,y
               iny
               lda   bcd,x
               dex
               bpl   @_1b
@_1c           sta   fbuf,y
               iny
               sty   fbidx
               jmp   PrintBuf

DoCharac
               jsr   NxtArgB
               cmp   #0
               beq   @_1a
               ldy   fbidx
               sta   fbuf,y
               inc   fbidx
@_1a           jsr   NxtArgB
               cmp   #0
               beq   @_1b
               ldy   fbidx
               sta   fbuf,y
               inc   fbidx
@_1b           jmp   PrintBuf

DoString
               jsr   NxtArg
               stx   @_1a+1
               sty   @_1a+2
               ldx   ndigit
               ldy   #0
@_1a
               lda   $FFFF,y
               beq   @_1b
               iny
               dex
               bne   @_1a
@_1b
               tya
               ldx   @_1a+1
               ldy   @_1a+2
               jmp   PrintStr

DoHex
               jsr   NxtArg
               jsr   PrintHex
               jmp   nm2bf

DoOctal
               jsr   NxtArg
               jsr   PrintOct
               jmp   nm2bf

; Print Floating-Point number in standard notation
DoFloatStandard
               ldx   ndigit
               lda   ndfnd
               jsr   PrintFloatStandard
               jmp   PrintBuf

; Print Floating-Point number in scientific notation
DoFloatScientific
               ldx   ndigit
               lda   ndfnd
               jsr   PrintFloatScientific
               jmp   PrintBuf

DoRemote
               jsr   NxtArg
               stx   @_1+1
               sty   @_2+1
               jsr   FixArgStack
@_1            ldx   #0         ; NOTE: Self-modifying
@_2            ldy   #0         ; NOTE: Self-modifying
               stx   argptr+1
               sty   argptr+2
               stz   argidx
               jsr   NxtArg
               stx   GetFormat+1
               sty   GetFormat+2
_GoLoop
               jmp   loop

; Print Float Buffer
PrintBuf
               lda   fbidx
               ldx   #<fbuf
               ldy   #>fbuf
PrintStr
               stx   @_sptr+1
               sty   @_sptr+2
               sta   nchar
               lda   width
               sec
               sbc   nchar
               sta   nxtra
               bcc   @_1a
               beq   @_1a
               ldx   rjust
               bne   @_1a
@_2a
               lda   #' '
               jsr   PutChar
               dec   nxtra
               bne   @_2a
@_1a
               lda   nchar
               beq   @_2b
@_1b
@_sptr         lda   $FFFF      ; NOTE: self-modifying
               inc   @_sptr+1
               bne   @_sp1
               inc   @_sptr+2
@_sp1
               jsr   PutChar
               dec   nchar
               bne   @_sptr
@_2b
               lda   nxtra
               bmi   @_1c
               beq   @_1c
@_2c
               lda   #' '
               jsr   PutChar
               dec   nxtra
               bne   @_2c
@_1c
;               jmp   loop  ; OLD
               bra   _GoLoop


nxtra          db    0
nchar          db    0
fbidx          db    0      ; Float Buffer # chars
fbuf           ds    128    ; Float Buffer

;Return Num in A
GetNum
               stz   ndfnd
               stz   nval
               stz   nval+1
@_1
               jsr   FormatChar
               sec
               sbc   #'0'
               cmp   #$100+'*'-'0'   ; ca65 bug: 0x2A - 0x30 = 0xFA = -6
               bne   @_2
               jsr   NxtArg
               stx   dval
               bra   @_3
@_2
               cmp   #10
               bcs   @_4
               sta   dval
@_3
               inc   ndfnd
; x10
               lda   nval
               sta   ntmp
               asl
               asl
               clc
               adc   ntmp
               asl
               clc
               adc   dval
               sta   nval
               bra   @_1    ; always
@_4
               adc   #'0'-1
               rts


dval           db    0
ntmp           db    0
ndfnd          db    0
argidx         db    0

;pkfmt
GetFormat      lda   $FFFF
               rts

FormatChar     jsr   GetFormat
               inc   GetFormat+1
               bne   @_x        ; Doesn't cross page boundary
               inc   GetFormat+2
@_x            rts

NxtArg2        jsr   NxtArg     ; intentional fall into
NxtArg         jsr   NxtArgB
               tax
               jsr   NxtArgB
               tay
               rts

; Inc next arg, return in A
NxtArgB        ldy   argidx
argptr         lda   $FFFF,y    ; NOTE: Self-Modifying!
               inc   argidx
               bne   @_0
               inc   argptr+2
@_0            rts

; Move to next Argument by skipping 'argidx' bytes
; Return Argument Stack -> XY
FixArgStack
               clc
               lda   argptr+1
               adc   argidx
               tax
               lda   argptr+2
               adc   #0
               tay
               jsr   SetArgStackPtr
               rts

; Print Floating-Point Number
; Not implemented
PrintFloatStandard
;               jsr   NxtArg
;               jsr   NxtArg
               jsr   NxtArg2
               ldx   #$FF
@_0            inx
               lda   flts,x
               sta   fbuf,x
               bne   @_0
               stx   fbidx
               rts

; Print Floating-Point in Scientific Notation
; Not implemented
PrintFloatScientific
;               jsr   NxtArg
;               jsr   NxtArg
               jsr   NxtArg2
               ldx   #$FF
@_0            inx
               lda   scis,x
               sta   fbuf,x
               bne   @_0
               stx   fbidx
               rts


flts           asc   "<float>"  ; Standard Notation
               db    0
scis           asc   "<scien>"  ; Scientific Notation
               db    0

__END:

[toc] | [prev] | [next] | [standalone]


#2255

Fromwssimms@gmail.com
Date2016-02-17 07:49 -0800
Message-ID<b32baf15-18f5-4a9a-bb76-9580786aeb07@googlegroups.com>
In reply to#2254
Am Mittwoch, 17. Februar 2016 12:13:13 UTC+9 schrieb Michael Pohoreski:
> On Tuesday, February 16, 2016 at 6:48:43 PM UTC-8, wss...@gmail.com wrote:
> > > 2 b) What does "Remote" do ? Looks like it prints a Pointer ?
> > > ; %r  Print Remote
> > 
> > It allows you to insert another set of arguments to printf. 
> > 
> > char *greeting = "Hello";
> > char *title = "Mr.";
> > char *nameargs[] = { "%s/%s", "Smith", "Jones" };
> > printf("%s %s %r!", greeting, title, nameargs);
> > 
> > yielding:
> > 
> > Hello Mr. Smith/Jones!
> > 
> > This was a feature of early printfs that was removed for portability
> > and type-safety reasons as Unix and C developed.
> 
> Ah, thanks for the clarification Sheldon.
> That "should" make sense but I'm missing something ... :-(
> I guess it is has been too long of a day ... :-/
> 
> Everything but the %r works in this cc65 demo program.

I did make a mistake in my previous comment, I think, in that I implied
that printf will come back from the 'remote' argument list to continue
the original format string after the remote list is exhausted. However
that is not the case.

Here's a short exercise for %r that Works For Me (TM):

               xc
               rel
               dsk   rtst.l

printf         ext
spushw         ext

               ldx   #<args
               ldy   #>args
               jsr   spushw
               ldx   #$A5
               ldy   #$C3
               jsr   spushw
               ldx   #<pfs
               ldy   #>pfs
               jsr   spushw
               jsr   printf
               rts

pfs            asc   'N=0x%x, more:%r',00

args           dw    a1
               dw    12345
               dw    a2

a1             asc   '(%d : %s)',0A00
a2             asc   'ok?',00

[toc] | [prev] | [next] | [standalone]


#2256

Fromwssimms@gmail.com
Date2016-02-17 07:55 -0800
Message-ID<b2b0fb1c-b098-4951-ab5b-1c15e75243e8@googlegroups.com>
In reply to#2254
Am Mittwoch, 17. Februar 2016 12:13:13 UTC+9 schrieb Michael Pohoreski:
> Everything but the %r works in this cc65 demo program.

Ok I'm kind of a dummy -- always posting before I have thoroughly
digested the post I am responding to. I think your problem isn't with
%r, per se, but rather the problem is that you are providing only a single
word (16 bits) each for %f and %e, but they are expecting 32 bits:

Your code is this:

> DATA
>                 dw  TEXT    ; printf( text, ... );
>                 dw  123     ; %d 123
>                 dw  123     ; %x  7B
>                 dw  123     ; %o 173
>                 dw  123     ; %c   {
>                 dw  HELLO   ; %s Hello World
>                 dw  $FF69   ; %l 65385 == -151 == $FF69
>                 dw  $3F80   ; %f // not implemented
>                 dw  $3F80   ; %e // not implemented
>                 dw  ARG2    ; %r

But the float handling code pops two words (16 bits args):

> PrintFloatStandard
> ;               jsr   NxtArg
> ;               jsr   NxtArg
>                jsr   NxtArg2
>                ldx   #$FF
> @_0            inx
>                lda   flts,x
>                sta   fbuf,x
>                bne   @_0
>                stx   fbidx
>                rts

[toc] | [prev] | [next] | [standalone]


#2250

Fromwssimms@gmail.com
Date2016-02-16 18:55 -0800
Message-ID<d01691e7-4440-4a73-a3fb-cb3d6fcb24e1@googlegroups.com>
In reply to#2247
Am Mittwoch, 17. Februar 2016 08:27:17 UTC+9 schrieb Michael Pohoreski:
> Yeah, I'm working on m own mini-version taking advantage of Apple'ism
> since I don't need the useless octal or less needed floats.  I really should
> get that sub-project done. :-)

Here's a smaller version, tprintf (tiny-printf) that only does decimal, hex, and
strings. Sorry, also no comments.

Instead of the getsp/setsp routines and an internal pointer, it uses only a
spopw routine to pop a word off of the arg stack into X/Y.

This assembles to 282 bytes.

               xc
               rel
               dsk  tprintf.l

spopw          ext
putchar        ext

bcd            ds    6
nval           dw    0

printd
               stx   nval
               sty   nval+1
               stz   bcd
               stz   bcd+1
               stz   bcd+2
               ldx   #16
               sed
:0a
               asl   nval
               rol   nval+1
               lda   bcd
               adc   bcd
               sta   bcd
               lda   bcd+1
               adc   bcd+1
               sta   bcd+1
               lda   bcd+2
               adc   bcd+2
               sta   bcd+2
               dex
               bne   :0a
               cld
               ldx   #2
               ldy   #5
:0b            lda   bcd,x
               lsr
               lsr
               lsr
               lsr
               clc
               adc   #'0'
               sta   bcd,y
               dey
               lda   bcd,x
               and   #$0F
               clc
               adc   #'0'
               sta   bcd,y
               dey
               dex
               bpl   :0b
               rts

printx
               stx   nval
               sty   nval+1
               ldx   #0
:1             lda   nval
               and   #15
               cmp   #10
               bcc   :2
               adc   #6
:2             adc   #'0'
               sta   bcd,x
               lsr   nval+1
               ror   nval
               lsr   nval+1
               ror   nval
               lsr   nval+1
               ror   nval
               lsr   nval+1
               ror   nval
               inx
               cpx   #6
               bne   :1
               rts

prbf           ldx   #5
:1a            lda   bcd,x
               dex
               bmi   :1c
               cmp   #'0'
               beq   :1a
:1b            jsr   putchar
               lda   bcd,x
               dex
               bpl   :1b
:1c            jsr   putchar
               jmp   loop

tprintf        ent
               jsr   spopw
               stx   fmtc+1
               sty   fmtc+2
loop           jsr   fmtc
               cmp   #0
               beq   done
               and   #$7F
               cmp   #'%'
               beq   :1
               jsr   putchar
               bra   loop
:1             jsr   fmtc
               cmp   #'d'
               beq   :1F
               cmp   #'x'
               beq   :2F
               cmp   #'s'
               beq   :3F
               jsr   putchar
               bra   loop
* %d
:1F            jsr   spopw
               jsr   printd
               jmp   prbf
* %x
:2F            jsr   spopw
               jsr   printx
               jmp   prbf
* %s
:3F            jsr   spopw
               stx   :8+1
               sty   :8+2
               ldx   #0
:8             lda   $FFFF,x
               beq   loop
               jsr   putchar
               inx
               bne   :8
               inc   :8+2
               bra   :8
*
fmtc           lda   $FFFF
               inc   fmtc+1
               bne   done
               inc   fmtc+2
done           rts

[toc] | [prev] | [next] | [standalone]


#2251

Fromwssimms@gmail.com
Date2016-02-16 18:58 -0800
Message-ID<8b979266-2345-4af0-8449-a7eafbe554fe@googlegroups.com>
In reply to#2250
Am Mittwoch, 17. Februar 2016 11:55:16 UTC+9 schrieb wss...@gmail.com:

Whoops, gmail users be careful, part of the code I just posted is being
hidden as "cited text" (at least for me). You have to click on
"show cited text" to see it.

[toc] | [prev] | [next] | [standalone]


#2266

FromMichael Pohoreski <michael.pohoreski@gmail.com>
Date2016-02-18 13:14 -0800
Message-ID<1d52f16f-2651-4ae0-a8ea-6263ee20a19b@googlegroups.com>
In reply to#2250
On Tuesday, February 16, 2016 at 6:55:16 PM UTC-8, wss...@gmail.com wrote:
> Here's a smaller version, tprintf (tiny-printf) that only does decimal, hex, and
> strings. Sorry, also no comments.

Nice job on tprintf!    What I hate abour standard printf() is that it provides a useless octal but no portable and essential binary printing.  Really?!  But thanks for all your source code!  It was inspiration to get off my but and finish off my next project ...

I'll call your printf() and raise it with my printm().  A "printf()-the-Apple-way" that provides:

* Hex printing with 2 and 4 widths
* Dec printing, with 2, 3, and 5 widths
* Signed Byte printing
* Binary printing with normal, and inverse 1's.
* Pointer deref printing 2 or 4 bytes
* Strings in Apple, C, and Pascal formats.

Also, you can turn features on/off (at compile time) to reduce the memory footprint since with everything turned on its a little big at 728 bytes.

Anyways, here's the source + demo, *ahem* WITH comments. :-)
I also provide a text dump so you can copy/paste that into your favorite emulator if you're too lazy to assemble this. :-)

- - - 8< printm.s - - -

; ca65
.feature c_comments

/*
printm - a printf replacement for 6502
Michael Pohroeski


Problem:
Ideally we want to print a single line that includes literal and variables.

    .byte "X=## Y=### $=####:@@ %%%%%%%%~????????"

without having to waste marking up literals with an escape character

Why printf() on the 6502 sucks:

- is bloated by using a meta-character '%' instead of the high bit
- doesn't provide a standard way to print binary *facepalm*
- doesn't provide a standard way to print a deferenced pointer
- 2 digit, 3 digit and 5 digit decimals requiring wasting "width" characters
  e.g. %2d, %3d, %5d
  When a single character would work instead.

Solution:

Here is a micro replacement, printm()

* Literals have the high byte set (APPLE text)
* Meta characters have the high bit cleared (ASCII)

    x Hex - print 2 Byte
    $ Hex - print 4 Byte

    @ Ptr - print hex byte at 16-bit pointer
    & Ptr - print hex word at 16-bit pointer

    # Dec - Print 1 Byte in decimal (max 2 digits)
    d Dec - Print 2 Byte in decimal (max 3 digits)
    u Dec - Print 2 Byte in decimal (max 5 digits)
    b Dec - Print signed byte

    % Bin 1 Byte
    ? Bin 1 Byte but 1's are printed in inverse

    a Str - APPLE text (high bit set), last char is ASCII
    s Str - C string, zero terminated
    p Str - Pascal string, first character is string length

Note: The dummy address $C0DE is to force the assembler
to generate a 16-bit address instead of optimizing a ZP operand

To toggle features on / off:

*/

ENABLE_BIN   = 1
ENABLE_DEC   = 1
ENABLE_BYTE  = 1    ; requires ENABLE_DEC
ENABLE_HEX   = 1
ENABLE_PTR   = 1    ; requires ENABLE_HEX
ENABLE_STR   = 1


.feature labels_without_colons
.feature leading_dot_in_identifiers
; 65C02
.PC02

; Force APPLE 'text' to have high bit on
; Will display as NORMAL characters
.macro APPLE text
    .repeat .strlen(text), I
        .byte   .strat(text, I) | $80
    .endrep
.endmacro

; Force APPLE 'text' with high bit on but last character has high bit off
; Will display as NORMAL characters (last character will appear FLASHING)
; Merlin: Macro Assembler -- Dextral Character Inverted
.macro DCI text
    .repeat .strlen(text)-1, I
        .byte   .strat(text, I) | $80
    .endrep
    .byte   .strat(text, .strlen(text)-1) & $7F
.endmacro

; Force ASCII 'text' to be control chars: $00..$1F
; Will display as INVERSE characters
.macro CTRL text
    .repeat .strlen(text), I
        .byte   .strat(text, I) & $1F
    .endrep
.endmacro

; Force ASCII 'text' to be control chars: $00..$3F
; Will display as INVERSE characters
.macro INV text
    .repeat .strlen(text), I
        .byte   .strat(text, I) & $3F
    .endrep
.endmacro

.macro PASCAL text
    .byte .strlen(text)
    APPLE text
.endmacro

.macro db val
    .byte val
.endmacro

.macro dw val
    .word val
.endmacro

.macro ds bytes
    .res bytes
.endmacro

        HOME    = $FC58
        TABV    = $FB5B

; printm pointer for PrintPtr2, PrintPtr4, PrintStrA, PrintStrC, PrintStrP
        _temp   = $FE

        __MAIN = $4000
; DOS3.3 meta -- remove these 2 if running under ProDOS
        .word __MAIN         ; 2 byte BLOAD address
        .word __END - __MAIN ; 2 byte BLOAD size

/*

Output:

X=39 Y=191 $=2345:D5 11010101~10101011

Byte=-128 -001 000 001 127

Strings: 'HELLO','WORLD'

Apple: 'HOME' Pascal: 'String Len 13'

*/

        .org  __MAIN         ; .org must come after header else offsets are wrong
; Demo printm
        JSR HOME

        LDA #$D5

        LDX #$45
        LDY #$23
        STX DATA+6
        STY DATA+7
        STX DATA+8
        STY DATA+9

        STX _HgrAddr+1
        STY _HgrAddr+2
_HgrAddr
        STA $C0DE

        STA DATA+10
        JSR ReverseByte
        STA DATA+12

.if ENABLE_BIN || ENABLE_DEC || ENABLE_HEX
        LDY #0
        JSR VTABY
        LDX #<DATA  ; Low  Byte of Address
        LDY #>DATA  ; High Byte of Address
        JSR PrintM
.endif

.if ENABLE_BYTE
        LDY #2
        JSR VTABY
        LDX #<DATA2
        LDY #>DATA2
        JSR PrintM
.endif  ; ENABLE_BYTE

.if ENABLE_STR
        LDY #4
        JSR VTABY
        LDX #<DATA3
        LDY #>DATA3
        JSR PrintM

        LDY #6
        JSR VTABY
        LDX #<DATA4
        LDY #>DATA4
        JSR PrintM
.endif  ; ENABLE_STR

        LDA #8
        JMP TABV

ReverseByte
        LDX #8
        STA $FF     ; temp working byte
ReverseBit
        ASL $FF     ; temp working byte
        ROR
        DEX
        BNE ReverseBit
        RTS

VTABY
        LDA SCREEN_LO,Y
        STA PutChar+1
        LDA SCREEN_HI,Y
        ORA #$04    ; TXT page 1
        STA PutChar+2
        RTS

; Y Lookup Table for 40x24 Text Screen
SCREEN_LO
        .byte $00, $80, $00, $80
        .byte $00, $80, $00, $80

        .byte $28, $A8, $28, $A8
        .byte $28, $A8, $28, $A8

        .byte $50, $D0, $50, $D0
        .byte $50, $D0, $50, $D0
SCREEN_HI
        .byte $00, $00, $01, $01
        .byte $02, $02, $03, $03

        .byte $00, $00, $01, $01
        .byte $02, $02, $03, $03

        .byte $00, $00, $01, $01
        .byte $02, $02, $03, $03

TEXT
    ;byte "X=## Y=ddd $=xxxx:@@ %%%%%%%%~????????"
    APPLE "X="
    .byte   "#"
    APPLE    " Y="
    .byte       "d"
    APPLE        " $="
    .byte           "x"
    APPLE            ":"
    .byte             "@"
    APPLE              " "
    .byte               "%"
    APPLE                "~"
    .byte                 "?"
    .byte 0

DATA
    dw TEXT     ; aArg[ 0] text
    dw 39       ; aArg[ 1] x
    dw 191      ; aArg[ 2] y
    dw $C0DE    ; aArg[ 3] addr  ScreenAddr
    dw $C0DE    ; aArg[ 4] byte  ScreenAddr pointer
    dw $DA1A    ; aArg[ 5] bits  ScreenByte
    dw $DA1A    ; aArg[ 6] bits  ScreenByte reversed

TEXT2
    ;byte "Byte=b b b b Str=s,s"
    APPLE "Byte="
    .byte      "b"
    APPLE       " "
    .byte        "b"
    APPLE         " "
    .byte          "b"
    APPLE           " "
    .byte            "b"
    APPLE             " "
    .byte              "b"
    db 0

TEXT_HELLO
    APPLE "HELLO"
    db 0

TEXT_WORLD
    APPLE "WORLD"
    db 0

DATA2
    dw TEXT2
    dw $80      ; -128
    dw $FF      ; -1
    dw $00      ;  0
    dw $01      ; +1
    dw $7F      ; +127

TEXT3
    APPLE "Strings: '"
    .byte           "s"
    APPLE            "','"
    .byte               "s"
    APPLE                "'"
    db 0

DATA3
    dw TEXT3
    dw TEXT_HELLO
    dw TEXT_WORLD

TEXT_DCI
    DCI "HOME"

TEXT_PASCAL
    PASCAL "String Len 13"

TEXT4
    APPLE "Apple: '"
    .byte         "a"
    APPLE          "'  Pascal: '"
    .byte                      "p"
    APPLE                       "'"
    db 0

DATA4
    dw TEXT4
    dw TEXT_DCI
    dw TEXT_PASCAL

; Pad until end of page so PrintM starts on new page
    ds 256 - <*


; self-modifying variable aliases

        _pScreen     = PutChar   +1
        _pFormat     = GetFormat +1
        _iArg        = NxtArgByte+1
        _pArg        = IncArg    +1
.if ENABLE_HEX
        _nHexWidth   = HexWidth  +1
.endif
.if ENABLE_DEC
        _nDecWidth   = DecWidth  +1
.endif ; ENABLE_DEC

; printm( format, args, ... )
; ======================================================================
PrintM
        STX _pArg+0
        STY _pArg+1
        STZ _iArg

NextArg
        JSR NxtArgYX
        STX _pFormat+0  ; lo
        STY _pFormat+1  ; hi
        BRA GetFormat   ; always

.if ENABLE_HEX

; x Hex 2 Byte
; $ Hex 4 Byte
; ======================================================================
PrintHex4
        LDA #4
        BNE _PrintHex
PrintHex2
        LDA #2
_PrintHex
        STA _nHexWidth
        JSR NxtArgYX

; Print 16-bit Y,X in hex
; Uses _nHexWidth to limit output width
PrintHexYX
        STX _val+0      ; may be tempting to move this to NxtArgYX
        STY _val+1      ; as XYtoVal but others call us

        LDX #0
_HexDigit
        LDA _val+0
        AND #$F
        CMP #$A         ; n < 10 ?
        BCC _Hex2Asc
        ADC #6          ; n += 6    $A -> +6 + (C=1) = $11
_Hex2Asc
        ADC #'0' + $80  ; inverse=remove #$80
        STA _bcd, X     ; NOTE: Digits are reversed!

        LSR _val+1      ; 16-bit SHR nibble
        ROR _val+0

        LSR _val+1
        ROR _val+0

        LSR _val+1
        ROR _val+0

        LSR _val+1
        ROR _val+0

        INX
HexWidth
        CPX #0          ; _nHexWidth NOTE: self-modifying!
        BNE _HexDigit
                        ; Intentional fall into reverse BCD

; On Entry: X number of chars to print in buffer _bcd
; ======================================================================
PrintReverseBCD
        DEX
        BMI NextFormat
        LDA _bcd, X
        JSR PutChar
        BRA PrintReverseBCD


    .if ENABLE_PTR
; @ Ptr 2 Byte
; & Ptr 4 Byte
; ======================================================================
PrintPtr4
        LDA #4
        BNE _PrintPtr
PrintPtr2
        LDA #2
_PrintPtr
        STA _nHexWidth
        JSR NxtArgToTemp

;       JSR NxtArgYX
; 13 bytes: zero page version
;       STX _temp+0     ; zero-page for (ZP),Y
;       STY _temp+1
        LDY #$0
        LDA (_temp),Y
        TAX
        INY
        LDA (_temp),Y
        TAY

; 20 bytes: self-modifying code version if zero-page not available
;        STX PtrVal+1
;        STY PtrVal+2
;        LDY #0          ; 0: A->X
;PrtVal
;        TAX             ; 1: A->Y
;        LDA $C0DE, Y
;        INY
;        CPY #2
;        BEQ _JumpPrintHexXY
;        BNE _PtrVal
;_JumpPrintHexXY
;        TAY

        BRA PrintHexYX  ; needs XYtoVal setup
    .endif  ; ENABLE_PTR
.endif  ; ENABLE_HEX


; ======================================================================
Print
        JSR PutChar

; Adjust pointer to next char in format
NextFormat
        INC _pFormat+0
        BNE GetFormat
        INC _pFormat+1
GetFormat
        LDA $C0DE       ; _pFormat NOTE: self-modifying!
        BEQ _Done
        BMI Print       ; neg = literal
; NOTE: If all features are turned off, will get a ca65 Range Error
        LDX #NumMeta-1  ; pos = meta
FindMeta
        CMP MetaChar,X
        BEQ CallMeta
        DEX
        BPL FindMeta
        BMI NextFormat  ; always = invalid meta; ignore
CallMeta
        TXA
        ASL
        TAX
        JMP (MetaFunc,X)
_Done
        RTS

; === Meta Ops ===

.if ENABLE_DEC
; # Dec 1 Byte (max 2 digits)
; d Dec 2 Byte (max 3 digits)
; u Dec 2 Byte (max 5 digits)
; ======================================================================
PrintDec5
        LDA #5
        BNE _PrintDec   ; always
PrintDec3
        LDA #3
        BNE _PrintDec   ; always
PrintDec2
        LDA #2          ; 2 digits
_PrintDec
        STA _nDecWidth
        JSR NxtArgYX

PrintDecYX
        STX _val+0      ; may be tempting to move this to NxtArgYX
        STY _val+1      ; as XYtoVal but others call us

        STZ _bcd+0
        STZ _bcd+1
        STZ _bcd+2

Dec2BCD
        LDX   #16
        SED
_Dec2BCD
        ASL _val+0
        ROl _val+1

        LDA _bcd+0
        ADC _bcd+0
        STA _bcd+0

        LDA _bcd+1
        ADC _bcd+1
        STA _bcd+1

        LDA _bcd+2
        ADC _bcd+2
        STA _bcd+2

        DEX
        BNE _Dec2BCD
        CLD

BCD2Char
        LDX #2
        LDY #5
_BCD2Char
        LDA _bcd,X  ; __c???    _b_?XX  a_YYXX
        LSR
        LSR
        LSR
        LSR
        CLC
        ADC #'0'+$80
        STA _bcd,Y  ; __c??X    _b_YXX  aZYYXX
        DEY

        LDA _bcd,X  ; __c??X    _b_YXX  aZYYXX
        AND #$F
        CLC
        ADC #'0'+$80
        STA _bcd,Y  ; __c?XX    _bYYXX  ZZYYXX
        DEY
        DEX
        BPL _BCD2Char

DecWidth
        LDX #0      ; _nDecDigits NOTE: self-modifying!
        JMP PrintReverseBCD
.endif  ; ENABLE_DEC


.if ENABLE_BIN
; % Bin 1 Byte normal  one's and zeros
; ? Bin 1 Byte inverse one's, normal zeroes
; ======================================================================
PrintBinInv
        LDA #$81
        BNE _PrintBin
PrintBinAsc
        LDA #$01
_PrintBin
        STA _PrintBit+1
        JSR NxtArgYX    ; X = low byte

        LDY #8          ; print 8 bits
_Bit2Asc
        TXA
        CMP #$80        ; C= A>=$80
        ROL             ; C<-76543210<-C
        TAX
        AND #$01        ; 0 -> B0
        BEQ _FlipBit
_PrintBit
        LDA #$81        ; 1 -> 31 NOTE: self-modifying!
_FlipBit
        EOR #$B0
        JSR PutChar
        DEY
        BNE _Bit2Asc
.endif  ; ENABLE_BIN

_JumpNextFormat
;       BRA NextFormat  ; always
        JMP NextFormat  ; JMP :-(

; b Print a signed byte in decimal
; ======================================================================
.if ENABLE_DEC
    .if ENABLE_BYTE
PrintByte
        JSR NxtArgYX    ; X = low byte
        TXA
        BPL PrintBytePos
        LDA #'-' + $80  ; X >= $80 --> $80 (-128) .. $FF (-1)
        JSR PutChar
        TXA
        EOR #$FF        ; 2's complement
        AND #$7F
        CLC
        ADC #$01
PrintBytePos
        TAX

        LDY #00         ; 00XX
        LDA #3          ; 3 digits max
        STA _nDecWidth
        JMP PrintDecYX  ; needs XYtoVal setup
    .endif ; ENABLE_BYTE
.endif  ; ENABLE_DEC


.if ENABLE_STR
; a String (APPLE text, last byte ASCII)
; See: DCI
; ======================================================================
PrintStrA
        JSR NxtArgToTemp

        LDY #$0
_PrintStrA
        LDA (_temp),Y
        BPL @_LastChar
        JSR PutChar
        INY
        BNE _PrintStrA
        INC _temp+1
        BRA _PrintStrA
@_LastChar
        LDX #1
        ORA #$80
        BRA _PrintCharA

; s String (C,ASCIIZ)
; ======================================================================
PrintStrC
        JSR NxtArgToTemp

        LDY #$0
@_NextByte
        LDA (_temp),Y
        BEQ _JumpNextFormat
        JSR PutChar
        INY
        BNE @_NextByte
        INC _temp+1
        BRA @_NextByte

; p String (Pascal)
; ======================================================================
PrintStrP
        JSR NxtArgToTemp

        LDY #$0
        LDA (_temp),Y
        BEQ _JumpNextFormat
        TAX
_PrintStrP
        INY
        LDA (_temp),Y
_PrintCharA
        JSR PutChar
        DEX
        BNE _PrintStrP
        BEQ _JumpNextFormat ; always
.endif  ; ENABLE_STR

; __________ Utility __________

; ======================================================================
PutChar
        STA $C0DE       ; _pScreen NOTE: self-modifying!
        INC PutChar+1   ; inc lo
        RTS

; ======================================================================
; @return next arg as 16-bit arg value in Y,X
NxtArgToTemp
NxtArgYX
        JSR NxtArgByte
        TAX

; @return _Arg[ _Num ]
NxtArgByte
        LDY #00         ; _iArg NOTE: self-modifying!
IncArg
        LDA $C0DE,Y     ; _pArg NOTE: self-modifying!
        INC _iArg       ;
        BNE @_SamePage
        INC _pArg+1     ;
@_SamePage
        TAY

; Callers of NxtToArgYX don't use _temp
_NxtArgToTemp
        STX _temp+0     ; zero-page for (ZP),Y
        STY _temp+1

;XYtoVal
;        STX _val+0      ; may be tempting to move this to NxtArgYX
;        STY _val+1      ;

        RTS

;
; ======================================================================

_bcd    ds  6   ; 6 chars for printing dec
_val    dw  0   ; PrintHex2 PrintHex4 temp

MetaChar

.if ENABLE_BIN
        db '?'  ; PrintBinInv   NOTE: 1's printed in inverse
        db '%'  ; PrintBinAsc
.endif
.if ENABLE_DEC
    .if ENABLE_BYTE
        db 'b'  ; PrintByte     NOTE: Signed -128 .. +127
    .endif
        db 'u'  ; PrintDec5
        db 'd'  ; PrintDec3
        db '#'  ; PrintDec2
.endif
.if ENABLE_HEX
        db 'x'  ; PrintHex4
        db '$'  ; PrintHex2
    .if ENABLE_PTR
        db '&'  ; PrintPtr4
        db '@'  ; PrintPtr2
    .endif
.endif
.if ENABLE_STR
        db 'p'  ; PrintStrP     NOTE: Pascal string; C printf 'p' is pointer!
        db 's'  ; PrintStrC     NOTE: C string, zero terminated
        db 'a'  ; PrintStrA     NOTE: Last byte is ASCII
.endif

_MetaCharEnd
NumMeta = _MetaCharEnd - MetaChar

MetaFunc

.if ENABLE_BIN
        dw PrintBinInv
        dw PrintBinAsc
.endif
.if ENABLE_DEC
    .if ENABLE_BYTE
        dw PrintByte
    .endif
        dw PrintDec5
        dw PrintDec3
        dw PrintDec2
.endif
.if ENABLE_HEX
        dw PrintHex4
        dw PrintHex2
    .if ENABLE_PTR
        dw PrintPtr4
        dw PrintPtr2
    .endif
.endif
.if ENABLE_STR
        dw PrintStrP
        dw PrintStrC
        dw PrintStrA
.endif

__END


And if you want to try it out:

4000:20 58 FC A9 D5 A2 45 A0
4008:23 8E BE 40 8C BF 40 8E
4010:C0 40 8C C1 40 8E 1C 40
4018:8C 1D 40 8D DE C0 8D C2
4020:40 20 5C 40 8D C4 40 A0
4028:00 20 67 40 A2 B8 A0 40
4030:20 00 42 A0 02 20 67 40
4038:A2 E1 A0 40 20 00 42 A0
4040:04 20 67 40 A2 FE A0 40
4048:20 00 42 A0 06 20 67 40
4050:A2 2E A0 41 20 00 42 A9
4058:08 4C 5B FB A2 08 85 FF
4060:06 FF 6A CA D0 FA 60 B9
4068:76 40 8D 8D 43 B9 8E 40
4070:09 04 8D 8E 43 60 00 80
4078:00 80 00 80 00 80 28 A8
4080:28 A8 28 A8 28 A8 50 D0
4088:50 D0 50 D0 50 D0 00 00
4090:01 01 02 02 03 03 00 00
4098:01 01 02 02 03 03 00 00
40A0:01 01 02 02 03 03 D8 BD
40A8:23 A0 D9 BD 64 A0 A4 BD
40B0:78 BA 40 A0 25 FE 3F 00
40B8:A6 40 27 00 BF 00 DE C0
40C0:DE C0 1A DA 1A DA C2 F9
40C8:F4 E5 BD 62 A0 62 A0 62
40D0:A0 62 A0 62 00 C8 C5 CC
40D8:CC CF 00 D7 CF D2 CC C4
40E0:00 C6 40 80 00 FF 00 00
40E8:00 01 00 7F 00 D3 F4 F2
40F0:E9 EE E7 F3 BA A0 A7 73
40F8:A7 AC A7 73 A7 00 ED 40
4100:D5 40 DB 40 C8 CF CD 45
4108:0D D3 F4 F2 E9 EE E7 A0
4110:CC E5 EE A0 B1 B3 C1 F0
4118:F0 EC E5 BA A0 A7 61 A7
4120:A0 A0 D0 E1 F3 E3 E1 EC
4128:BA A0 A7 70 A7 00 16 41
4130:04 41 08 41 00 00 00 00
4138:00 00 00 00 00 00 00 00
4140:00 00 00 00 00 00 00 00
4148:00 00 00 00 00 00 00 00
4150:00 00 00 00 00 00 00 00
4158:00 00 00 00 00 00 00 00
4160:00 00 00 00 00 00 00 00
4168:00 00 00 00 00 00 00 00
4170:00 00 00 00 00 00 00 00
4178:00 00 00 00 00 00 00 00
4180:00 00 00 00 00 00 00 00
4188:00 00 00 00 00 00 00 00
4190:00 00 00 00 00 00 00 00
4198:00 00 00 00 00 00 00 00
41A0:00 00 00 00 00 00 00 00
41A8:00 00 00 00 00 00 00 00
41B0:00 00 00 00 00 00 00 00
41B8:00 00 00 00 00 00 00 00
41C0:00 00 00 00 00 00 00 00
41C8:00 00 00 00 00 00 00 00
41D0:00 00 00 00 00 00 00 00
41D8:00 00 00 00 00 00 00 00
41E0:00 00 00 00 00 00 00 00
41E8:00 00 00 00 00 00 00 00
41F0:00 00 00 00 00 00 00 00
41F8:00 00 00 00 00 00 00 00
4200:8E 9A 43 8C 9B 43 9C 98
4208:43 20 93 43 8E 83 42 8C
4210:84 42 80 6E A9 04 D0 02
4218:A9 02 8D 52 42 20 93 43
4220:8E B0 43 8C B1 43 A2 00
4228:AD B0 43 29 0F C9 0A 90
4230:02 69 06 69 B0 9D AA 43
4238:4E B1 43 6E B0 43 4E B1
4240:43 6E B0 43 4E B1 43 6E
4248:B0 43 4E B1 43 6E B0 43
4250:E8 E0 00 D0 D3 CA 30 22
4258:BD AA 43 20 8C 43 80 F5
4260:A9 04 D0 02 A9 02 8D 52
4268:42 20 93 43 A0 00 B1 FE
4270:AA C8 B1 FE A8 80 A9 20
4278:8C 43 EE 83 42 D0 03 EE
4280:84 42 AD DE C0 F0 14 30
4288:EE A2 0C DD B2 43 F0 05
4290:CA 10 F8 30 E5 8A 0A AA
4298:7C BF 43 60 A9 05 D0 06
42A0:A9 03 D0 02 A9 02 8D 05
42A8:43 20 93 43 8E B0 43 8C
42B0:B1 43 9C AA 43 9C AB 43
42B8:9C AC 43 A2 10 F8 0E B0
42C0:43 2E B1 43 AD AA 43 6D
42C8:AA 43 8D AA 43 AD AB 43
42D0:6D AB 43 8D AB 43 AD AC
42D8:43 6D AC 43 8D AC 43 CA
42E0:D0 DC D8 A2 02 A0 05 BD
42E8:AA 43 4A 4A 4A 4A 18 69
42F0:B0 99 AA 43 88 BD AA 43
42F8:29 0F 18 69 B0 99 AA 43
4300:88 CA 10 E3 A2 00 4C 55
4308:42 A9 81 D0 02 A9 01 8D
4310:21 43 20 93 43 A0 08 8A
4318:C9 80 2A AA 29 01 F0 02
4320:A9 81 49 B0 20 8C 43 88
4328:D0 ED 4C 7A 42 20 93 43
4330:8A 10 0D A9 AD 20 8C 43
4338:8A 49 FF 29 7F 18 69 01
4340:AA A0 00 A9 03 8D 05 43
4348:4C AC 42 20 93 43 A0 00
4350:B1 FE 10 0A 20 8C 43 C8
4358:D0 F6 E6 FF 80 F2 A2 01
4360:09 80 80 20 20 93 43 A0
4368:00 B1 FE F0 BD 20 8C 43
4370:C8 D0 F6 E6 FF 80 F2 20
4378:93 43 A0 00 B1 FE F0 AA
4380:AA C8 B1 FE 20 8C 43 CA
4388:D0 F7 F0 9E 8D DE C0 EE
4390:8D 43 60 20 97 43 AA A0
4398:00 B9 DE C0 EE 98 43 D0
43A0:03 EE 9B 43 A8 86 FE 84
43A8:FF 60 00 00 00 00 00 00
43B0:00 00 3F 25 62 75 64 23
43B8:78 24 26 40 70 73 61 09
43C0:43 0D 43 2D 43 9C 42 A0
43C8:42 A4 42 14 42 18 42 60
43D0:42 64 42 77 43 64 43 4B
43D8:43

[toc] | [prev] | [next] | [standalone]


#2269

FromMichael Pohoreski <michael.pohoreski@gmail.com>
Date2016-02-18 13:27 -0800
Message-ID<516443d6-2b52-40ab-9b51-370b7167c025@googlegroups.com>
In reply to#2266
On Thursday, February 18, 2016 at 1:14:15 PM UTC-8, Michael Pohoreski wrote:
> Also, you can turn features on/off (at compile time) to reduce the memory footprint since with everything turned on its a little big at 728 bytes.

Whoops, that should be 0x1D9 = 473 bytes!

I didn't realize the demo spilled over another page.
(I didn't think it was THAT BIG)


>  it uses only a spopw routine to pop a word off of the arg stack into X/Y. 
> This assembles to 282 bytes. 

/sarcasm "That's cheating" :-)

Seriously, a complete demo so we can get a "actual final" byte usage would be, ahem, nice. :-)

[toc] | [prev] | [next] | [standalone]


#2273

FromMichael Pohoreski <michael.pohoreski@gmail.com>
Date2016-02-18 15:09 -0800
Message-ID<a0383c9f-78a6-4755-b8bd-8fa1a1b23e6f@googlegroups.com>
In reply to#2269
I've cleanup that hideous requirement of interleaving ASCII + APPLE text using a much cleaner
PRINTM macro now.

; ca65
.feature c_comments

/* Version 15
printm - a printf replacement for 65C02
Michael Pohoreski


Problem:

Ideally we want to print a single line that includes literal and variables
in MIXED ASCII case -- high bit characters would be output "as is"
and ASCII characters would be interpreted as a variable output.

While originally this gives us a nice 1:1 mapping for input:output ...

    .byte "X=## Y=### $=####:@@ %%%%%%%%~????????"

... it has 2 problems:

a) it has to be constructed in pieces
b) and it is bloated.

Is there we can use a more compact printf-style format string
where we don't waste storing the escape character, and
toggle the high bit on characters on/off as needed?

Yes, if we use a macro!

     PRINTM "X=%# Y=%d $=%x:%@ %%~%?"

Thisis why printf() on the 6502 sucks:

- is bloated by using a meta-character '%' instead of the high bit
- doesn't provide a standard way to print binary *facepalm*
- doesn't provide a standard way to print a deferenced pointer
- 2 digit, 3 digit and 5 digit decimals requiring wasting "width" characters
  e.g. %2d, %3d, %5d
  When a single character would work instead.

Solution:

Here is a micro replacement, printm()

* Literals have the high byte set (APPLE text)
* Meta characters have the high bit cleared (ASCII)

    x Hex - print 2 Byte
    $ Hex - print 4 Byte

    @ Ptr - print hex byte at 16-bit pointer
    & Ptr - print hex word at 16-bit pointer

    # Dec - Print 1 Byte in decimal (max 2 digits)
    d Dec - Print 2 Byte in decimal (max 3 digits)
    u Dec - Print 2 Byte in decimal (max 5 digits)
    b Dec - Print signed byte

    % Bin 1 Byte
    ? Bin 1 Byte but 1's are printed in inverse

    a Str - APPLE text (high bit set), last char is ASCII
    s Str - C string, zero terminated
    p Str - Pascal string, first character is string length

Note: The dummy address $C0DE is to force the assembler
to generate a 16-bit address instead of optimizing a ZP operand

The printm with everything enabled takes up 472 bytes

Demo + Library text dump:

4000:20 58 FC A9 D5 A2 45 A0
4008:23 8E BE 40 8C BF 40 8E
4010:C0 40 8C C1 40 8E 1C 40
4018:8C 1D 40 8D DE C0 8D C2
4020:40 20 5C 40 8D C4 40 A0
4028:00 20 67 40 A2 B8 A0 40
4030:20 00 42 A0 02 20 67 40
4038:A2 E1 A0 40 20 00 42 A0
4040:04 20 67 40 A2 FE A0 40
4048:20 00 42 A0 06 20 67 40
4050:A2 2E A0 41 20 00 42 A9
4058:08 4C 5B FB A2 08 85 FF
4060:06 FF 6A CA D0 FA 60 B9
4068:76 40 8D 8C 43 B9 8E 40
4070:09 04 8D 8D 43 60 00 80
4078:00 80 00 80 00 80 28 A8
4080:28 A8 28 A8 28 A8 50 D0
4088:50 D0 50 D0 50 D0 00 00
4090:01 01 02 02 03 03 00 00
4098:01 01 02 02 03 03 00 00
40A0:01 01 02 02 03 03 D8 BD
40A8:23 A0 D9 BD 64 A0 A4 BD
40B0:78 BA 40 A0 25 FE 3F 00
40B8:A6 40 27 00 BF 00 DE C0
40C0:DE C0 1A DA 1A DA C2 F9
40C8:F4 E5 BD 62 A0 62 A0 62
40D0:A0 62 A0 62 00 C8 C5 CC
40D8:CC CF 00 D7 CF D2 CC C4
40E0:00 C6 40 80 00 FF 00 00
40E8:00 01 00 7F 00 D3 F4 F2
40F0:E9 EE E7 F3 BA A0 A7 73
40F8:A7 AC A7 73 A7 00 ED 40
4100:D5 40 DB 40 C8 CF CD 45
4108:0D D3 F4 F2 E9 EE E7 A0
4110:CC E5 EE A0 B1 B3 C1 F0
4118:F0 EC E5 BA A0 A7 61 A7
4120:A0 A0 D0 E1 F3 E3 E1 EC
4128:BA A0 A7 70 A7 00 16 41
4130:04 41 08 41 00 00 00 00
4138:00 00 00 00 00 00 00 00
4140:00 00 00 00 00 00 00 00
4148:00 00 00 00 00 00 00 00
4150:00 00 00 00 00 00 00 00
4158:00 00 00 00 00 00 00 00
4160:00 00 00 00 00 00 00 00
4168:00 00 00 00 00 00 00 00
4170:00 00 00 00 00 00 00 00
4178:00 00 00 00 00 00 00 00
4180:00 00 00 00 00 00 00 00
4188:00 00 00 00 00 00 00 00
4190:00 00 00 00 00 00 00 00
4198:00 00 00 00 00 00 00 00
41A0:00 00 00 00 00 00 00 00
41A8:00 00 00 00 00 00 00 00
41B0:00 00 00 00 00 00 00 00
41B8:00 00 00 00 00 00 00 00
41C0:00 00 00 00 00 00 00 00
41C8:00 00 00 00 00 00 00 00
41D0:00 00 00 00 00 00 00 00
41D8:00 00 00 00 00 00 00 00
41E0:00 00 00 00 00 00 00 00
41E8:00 00 00 00 00 00 00 00
41F0:00 00 00 00 00 00 00 00
41F8:00 00 00 00 00 00 00 00
4200:8E 99 43 8C 9A 43 9C 97
4208:43 20 92 43 8E 83 42 8C
4210:84 42 80 6E A9 04 D0 02
4218:A9 02 8D 52 42 20 92 43
4220:8E AF 43 8C B0 43 A2 00
4228:AD AF 43 29 0F C9 0A 90
4230:02 69 06 69 B0 9D A9 43
4238:4E B0 43 6E AF 43 4E B0
4240:43 6E AF 43 4E B0 43 6E
4248:AF 43 4E B0 43 6E AF 43
4250:E8 E0 00 D0 D3 CA 30 22
4258:BD A9 43 20 8B 43 80 F5
4260:A9 04 D0 02 A9 02 8D 52
4268:42 20 92 43 A0 00 B1 FE
4270:AA C8 B1 FE A8 80 A9 20
4278:8B 43 EE 83 42 D0 03 EE
4280:84 42 AD DE C0 F0 14 30
4288:EE A2 0C DD B1 43 F0 05
4290:CA 10 F8 30 E5 8A 0A AA
4298:7C BE 43 60 A9 05 D0 06
42A0:A9 03 D0 02 A9 02 8D 05
42A8:43 20 92 43 8E AF 43 8C
42B0:B0 43 9C A9 43 9C AA 43
42B8:9C AB 43 A2 10 F8 0E AF
42C0:43 2E B0 43 AD A9 43 6D
42C8:A9 43 8D A9 43 AD AA 43
42D0:6D AA 43 8D AA 43 AD AB
42D8:43 6D AB 43 8D AB 43 CA
42E0:D0 DC D8 A2 02 A0 05 BD
42E8:A9 43 4A 4A 4A 4A 18 69
42F0:B0 99 A9 43 88 BD A9 43
42F8:29 0F 18 69 B0 99 A9 43
4300:88 CA 10 E3 A2 00 4C 55
4308:42 A9 81 D0 02 A9 01 8D
4310:21 43 20 92 43 A0 08 8A
4318:C9 80 2A AA 29 01 F0 02
4320:A9 81 49 B0 20 8B 43 88
4328:D0 ED 4C 7A 42 20 92 43
4330:8A 10 0D A9 AD 20 8B 43
4338:8A 49 FF 29 7F 18 69 01
4340:AA A0 00 A9 03 8D 05 43
4348:4C AC 42 20 92 43 A0 00
4350:B1 FE 10 0A 20 8B 43 C8
4358:D0 F6 E6 FF 80 F2 09 80
4360:4C 77 42 20 92 43 A0 00
4368:B1 FE F0 BE 20 8B 43 C8
4370:D0 F6 E6 FF 80 F2 20 92
4378:43 A0 00 B1 FE F0 AB AA
4380:C8 B1 FE 20 8B 43 CA D0
4388:F7 F0 9F 8D DE C0 EE 8C
4390:43 60 20 96 43 AA A0 00
4398:B9 DE C0 EE 97 43 D0 03
43A0:EE 9A 43 A8 86 FE 84 FF
43A8:60 00 00 00 00 00 00 00
43B0:00 3F 25 62 75 64 23 78
43B8:24 26 40 70 73 61 09 43
43C0:0D 43 2D 43 9C 42 A0 42
43C8:A4 42 14 42 18 42 60 42
43D0:64 42 76 43 63 43 4B 43

To toggle features on / off:

*/

ENABLE_BIN   = 1
ENABLE_DEC   = 1
ENABLE_BYTE  = 1    ; requires ENABLE_DEC
ENABLE_HEX   = 1
ENABLE_PTR   = 1    ; requires ENABLE_HEX
ENABLE_STR   = 1

; more ca65
.feature labels_without_colons
.feature leading_dot_in_identifiers
; 65C02
.PC02

; This will take a printf-style string and compact it
; % is the escape character to output the next byte in ASCII (high bit clear)
; othersise the remaining chars will default to have their high bit set
.macro PRINTM text
    .local h
    h .set $80

    .repeat .strlen(text), I
        .if (.strat(text , I) = '%')
            ; handle special case of last char was %
            .if( h = $00 )
                .byte .strat(text, I) | h
                h .set $80
            .else
                h .set $00
            .endif
        .else
            .byte .strat(text, I) | h
            h .set $80
        .endif
    .endrep
    .byte 0
.endmacro

; Force APPLE 'text' to have high bit on
; Will display as NORMAL characters
.macro APPLE text
    .repeat .strlen(text), I
        .byte   .strat(text, I) | $80
    .endrep
.endmacro

; Force APPLE 'text' with high bit on but last character has high bit off
; Will display as NORMAL characters (last character will appear FLASHING)
; Merlin: Macro Assembler -- Dextral Character Inverted
.macro DCI text
    .repeat .strlen(text)-1, I
        .byte   .strat(text, I) | $80
    .endrep
    .byte   .strat(text, .strlen(text)-1) & $7F
.endmacro

; Force ASCII 'text' to be control chars: $00..$1F
; Will display as INVERSE characters
.macro CTRL text
    .repeat .strlen(text), I
        .byte   .strat(text, I) & $1F
    .endrep
.endmacro

; Force ASCII 'text' to be control chars: $00..$3F
; Will display as INVERSE characters
.macro INV text
    .repeat .strlen(text), I
        .byte   .strat(text, I) & $3F
    .endrep
.endmacro

.macro PASCAL text
    .byte .strlen(text)
    APPLE text
.endmacro

.macro db val
    .byte val
.endmacro

.macro dw val
    .word val
.endmacro

.macro ds bytes
    .res bytes
.endmacro

        HOME    = $FC58
        TABV    = $FB5B

; printm pointer for PrintPtr2, PrintPtr4, PrintStrA, PrintStrC, PrintStrP
        _temp   = $FE

        __MAIN = $4000
; DOS3.3 meta -- remove these 2 if running under ProDOS
        .word __MAIN         ; 2 byte BLOAD address
        .word __END - __MAIN ; 2 byte BLOAD size

/*

Output:

X=39 Y=191 $=2345:D5 11010101~10101011

Byte=-128 -001 000 001 127

Strings: 'HELLO','WORLD'

Apple: 'HOME' Pascal: 'String Len 13'

*/

        .org  __MAIN         ; .org must come after header else offsets are wrong
; Demo printm
        JSR HOME

        LDA #$D5

        LDX #$45
        LDY #$23
        STX DATA+6
        STY DATA+7
        STX DATA+8
        STY DATA+9

        STX _HgrAddr+1
        STY _HgrAddr+2
_HgrAddr
        STA $C0DE

        STA DATA+10
        JSR ReverseByte
        STA DATA+12

.if ENABLE_BIN || ENABLE_DEC || ENABLE_HEX
        LDY #0
        JSR VTABY
        LDX #<DATA  ; Low  Byte of Address
        LDY #>DATA  ; High Byte of Address
        JSR PrintM
.endif

.if ENABLE_BYTE
        LDY #2
        JSR VTABY
        LDX #<DATA2
        LDY #>DATA2
        JSR PrintM
.endif  ; ENABLE_BYTE

.if ENABLE_STR
        LDY #4
        JSR VTABY
        LDX #<DATA3
        LDY #>DATA3
        JSR PrintM

        LDY #6
        JSR VTABY
        LDX #<DATA4
        LDY #>DATA4
        JSR PrintM
.endif  ; ENABLE_STR

        LDA #8
        JMP TABV

ReverseByte
        LDX #8
        STA $FF     ; temp working byte
ReverseBit
        ASL $FF     ; temp working byte
        ROR
        DEX
        BNE ReverseBit
        RTS

VTABY
        LDA SCREEN_LO,Y
        STA PutChar+1
        LDA SCREEN_HI,Y
        ORA #$04    ; TXT page 1
        STA PutChar+2
        RTS

; Y Lookup Table for 40x24 Text Screen
SCREEN_LO
        .byte $00, $80, $00, $80
        .byte $00, $80, $00, $80

        .byte $28, $A8, $28, $A8
        .byte $28, $A8, $28, $A8

        .byte $50, $D0, $50, $D0
        .byte $50, $D0, $50, $D0
SCREEN_HI
        .byte $00, $00, $01, $01
        .byte $02, $02, $03, $03

        .byte $00, $00, $01, $01
        .byte $02, $02, $03, $03

        .byte $00, $00, $01, $01
        .byte $02, $02, $03, $03

TEXT
    ;byte "X=## Y=ddd $=xxxx:@@ %%%%%%%%~????????"
    PRINTM "X=%# Y=%d $=%x:%@ %%~%?"

DATA
    dw TEXT     ; aArg[ 0] text
    dw 39       ; aArg[ 1] x
    dw 191      ; aArg[ 2] y
    dw $C0DE    ; aArg[ 3] addr  ScreenAddr
    dw $C0DE    ; aArg[ 4] byte  ScreenAddr pointer
    dw $DA1A    ; aArg[ 5] bits  ScreenByte
    dw $DA1A    ; aArg[ 6] bits  ScreenByte reversed

TEXT2
    PRINTM "Byte=%b %b %b %b %b"

TEXT_HELLO
    APPLE "HELLO"
    db 0

TEXT_WORLD
    APPLE "WORLD"
    db 0

DATA2
    dw TEXT2
    dw $80      ; -128
    dw $FF      ; -1
    dw $00      ;  0
    dw $01      ; +1
    dw $7F      ; +127

TEXT3
    PRINTM "Strings: '%s','%s'"

DATA3
    dw TEXT3
    dw TEXT_HELLO
    dw TEXT_WORLD

TEXT_DCI
    DCI "HOME"

TEXT_PASCAL
    PASCAL "String Len 13"

TEXT4
    PRINTM "Apple: '%a'  Pascal: '%p'"

DATA4
    dw TEXT4
    dw TEXT_DCI
    dw TEXT_PASCAL

; Pad until end of page so PrintM starts on new page
    ds 256 - <*


; self-modifying variable aliases

        _pScreen     = PutChar   +1
        _pFormat     = GetFormat +1
        _iArg        = NxtArgByte+1
        _pArg        = IncArg    +1
.if ENABLE_HEX
        _nHexWidth   = HexWidth  +1
.endif
.if ENABLE_DEC
        _nDecWidth   = DecWidth  +1
.endif ; ENABLE_DEC

; ======================================================================
; printm( format, args, ... )
; ======================================================================
PrintM
        STX _pArg+0
        STY _pArg+1
        STZ _iArg

NextArg
        JSR NxtArgYX
        STX _pFormat+0  ; lo
        STY _pFormat+1  ; hi
        BRA GetFormat   ; always

.if ENABLE_HEX

; x Hex 2 Byte
; $ Hex 4 Byte
; ======================================================================
PrintHex4
        LDA #4
        BNE _PrintHex
PrintHex2
        LDA #2
_PrintHex
        STA _nHexWidth
        JSR NxtArgYX

; Print 16-bit Y,X in hex
; Uses _nHexWidth to limit output width
PrintHexYX
        STX _val+0      ; may be tempting to move this to NxtArgYX
        STY _val+1      ; as XYtoVal but others call us

        LDX #0
_HexDigit
        LDA _val+0
        AND #$F
        CMP #$A         ; n < 10 ?
        BCC _Hex2Asc
        ADC #6          ; n += 6    $A -> +6 + (C=1) = $11
_Hex2Asc
        ADC #'0' + $80  ; inverse=remove #$80
        STA _bcd, X     ; NOTE: Digits are reversed!

        LSR _val+1      ; 16-bit SHR nibble
        ROR _val+0

        LSR _val+1
        ROR _val+0

        LSR _val+1
        ROR _val+0

        LSR _val+1
        ROR _val+0

        INX
HexWidth
        CPX #0          ; _nHexWidth NOTE: self-modifying!
        BNE _HexDigit
                        ; Intentional fall into reverse BCD

; On Entry: X number of chars to print in buffer _bcd
; ======================================================================
PrintReverseBCD
        DEX
        BMI NextFormat
        LDA _bcd, X
        JSR PutChar
        BRA PrintReverseBCD


    .if ENABLE_PTR
; @ Ptr 2 Byte
; & Ptr 4 Byte
; ======================================================================
PrintPtr4
        LDA #4
        BNE _PrintPtr
PrintPtr2
        LDA #2
_PrintPtr
        STA _nHexWidth
        JSR NxtArgToTemp

;       JSR NxtArgYX
; 13 bytes: zero page version
;       STX _temp+0     ; zero-page for (ZP),Y
;       STY _temp+1
        LDY #$0
        LDA (_temp),Y
        TAX
        INY
        LDA (_temp),Y
        TAY

; 20 bytes: self-modifying code version if zero-page not available
;        STX PtrVal+1
;        STY PtrVal+2
;        LDY #0          ; 0: A->X
;PrtVal
;        TAX             ; 1: A->Y
;        LDA $C0DE, Y
;        INY
;        CPY #2
;        BEQ _JumpPrintHexXY
;        BNE _PtrVal
;_JumpPrintHexXY
;        TAY

        BRA PrintHexYX  ; needs XYtoVal setup
    .endif  ; ENABLE_PTR
.endif  ; ENABLE_HEX


; ======================================================================
Print
        JSR PutChar
NextFormat              ; Adjust pointer to next char in format
        INC _pFormat+0
        BNE GetFormat
        INC _pFormat+1
GetFormat
        LDA $C0DE       ; _pFormat NOTE: self-modifying!
        BEQ _Done       ; zero-terminated
        BMI Print       ; neg = literal
; NOTE: If all features are turned off, will get a ca65 Range Error
        LDX #NumMeta-1  ; pos = meta
FindMeta
        CMP MetaChar,X
        BEQ CallMeta
        DEX
        BPL FindMeta
        BMI NextFormat  ; always = invalid meta; ignore
CallMeta
        TXA
        ASL
        TAX
        JMP (MetaFunc,X)
_Done
        RTS

; === Meta Ops ===

.if ENABLE_DEC
; # Dec 1 Byte (max 2 digits)
; d Dec 2 Byte (max 3 digits)
; u Dec 2 Byte (max 5 digits)
; ======================================================================
PrintDec5
        LDA #5
        BNE _PrintDec   ; always
PrintDec3
        LDA #3
        BNE _PrintDec   ; always
PrintDec2
        LDA #2          ; 2 digits
_PrintDec
        STA _nDecWidth
        JSR NxtArgYX

PrintDecYX
        STX _val+0      ; may be tempting to move this to NxtArgYX
        STY _val+1      ; as XYtoVal but others call us

        STZ _bcd+0
        STZ _bcd+1
        STZ _bcd+2

Dec2BCD
        LDX   #16
        SED
_Dec2BCD
        ASL _val+0
        ROl _val+1

        LDA _bcd+0
        ADC _bcd+0
        STA _bcd+0

        LDA _bcd+1
        ADC _bcd+1
        STA _bcd+1

        LDA _bcd+2
        ADC _bcd+2
        STA _bcd+2

        DEX
        BNE _Dec2BCD
        CLD

BCD2Char
        LDX #2
        LDY #5
_BCD2Char
        LDA _bcd,X  ; __c???    _b_?XX  a_YYXX
        LSR
        LSR
        LSR
        LSR
        CLC
        ADC #'0'+$80
        STA _bcd,Y  ; __c??X    _b_YXX  aZYYXX
        DEY

        LDA _bcd,X  ; __c??X    _b_YXX  aZYYXX
        AND #$F
        CLC
        ADC #'0'+$80
        STA _bcd,Y  ; __c?XX    _bYYXX  ZZYYXX
        DEY
        DEX
        BPL _BCD2Char

DecWidth
        LDX #0      ; _nDecDigits NOTE: self-modifying!
        JMP PrintReverseBCD
.endif  ; ENABLE_DEC


.if ENABLE_BIN
; % Bin 1 Byte normal  one's and zeros
; ? Bin 1 Byte inverse one's, normal zeroes
; ======================================================================
PrintBinInv
        LDA #$81
        BNE _PrintBin
PrintBinAsc
        LDA #$01
_PrintBin
        STA _PrintBit+1
        JSR NxtArgYX    ; X = low byte

        LDY #8          ; print 8 bits
_Bit2Asc
        TXA
        CMP #$80        ; C= A>=$80
        ROL             ; C<-76543210<-C
        TAX
        AND #$01        ; 0 -> B0
        BEQ _FlipBit
_PrintBit
        LDA #$81        ; 1 -> 31 NOTE: self-modifying!
_FlipBit
        EOR #$B0
        JSR PutChar
        DEY
        BNE _Bit2Asc
.endif  ; ENABLE_BIN

_JumpNextFormat
;       BRA NextFormat  ; always
        JMP NextFormat  ; JMP :-(

; b Print a signed byte in decimal
; ======================================================================
.if ENABLE_DEC
    .if ENABLE_BYTE
PrintByte
        JSR NxtArgYX    ; X = low byte
        TXA
        BPL PrintBytePos
        LDA #'-' + $80  ; X >= $80 --> $80 (-128) .. $FF (-1)
        JSR PutChar
        TXA
        EOR #$FF        ; 2's complement
        AND #$7F
        CLC
        ADC #$01
PrintBytePos
        TAX

        LDY #00         ; 00XX
        LDA #3          ; 3 digits max
        STA _nDecWidth
        JMP PrintDecYX  ; needs XYtoVal setup
    .endif ; ENABLE_BYTE
.endif  ; ENABLE_DEC


.if ENABLE_STR
; a String (APPLE text, last byte ASCII)
; See: DCI
; ======================================================================
PrintStrA
        JSR NxtArgToTemp

        LDY #$0
_PrintStrA
        LDA (_temp),Y
        BPL @_LastChar
        JSR PutChar
        INY
        BNE _PrintStrA
        INC _temp+1
        BRA _PrintStrA
@_LastChar
; 6 byte:
;        LDX #1
;        ORA #$80
;        BRA _PrintCharA
; 5 byte:
        ORA #$80
        JMP Print

; s String (C,ASCIIZ)
; ======================================================================
PrintStrC
        JSR NxtArgToTemp

        LDY #$0
@_NextByte
        LDA (_temp),Y
        BEQ _JumpNextFormat
        JSR PutChar
        INY
        BNE @_NextByte
        INC _temp+1
        BRA @_NextByte

; p String (Pascal)
; ======================================================================
PrintStrP
        JSR NxtArgToTemp

        LDY #$0
        LDA (_temp),Y
        BEQ _JumpNextFormat
        TAX
_PrintStrP
        INY
        LDA (_temp),Y
_PrintCharA
        JSR PutChar
        DEX
        BNE _PrintStrP
        BEQ _JumpNextFormat ; always
.endif  ; ENABLE_STR

; __________ Utility __________

; ======================================================================
PutChar
        STA $C0DE       ; _pScreen NOTE: self-modifying!
        INC PutChar+1   ; inc lo
        RTS

; ======================================================================
; @return next arg as 16-bit arg value in Y,X
NxtArgToTemp
NxtArgYX
        JSR NxtArgByte
        TAX

; @return _Arg[ _Num ]
NxtArgByte
        LDY #00         ; _iArg NOTE: self-modifying!
IncArg
        LDA $C0DE,Y     ; _pArg NOTE: self-modifying!
        INC _iArg       ;
        BNE @_SamePage
        INC _pArg+1     ;
@_SamePage
        TAY

; Callers of NxtToArgYX don't use _temp
_NxtArgToTemp
        STX _temp+0     ; zero-page for (ZP),Y
        STY _temp+1

;XYtoVal
;        STX _val+0      ; may be tempting to move this to NxtArgYX
;        STY _val+1      ;

        RTS

;
; ======================================================================

_bcd    ds  6   ; 6 chars for printing dec
_val    dw  0   ; PrintHex2 PrintHex4 temp

MetaChar

.if ENABLE_BIN
        db '?'  ; PrintBinInv   NOTE: 1's printed in inverse
        db '%'  ; PrintBinAsc
.endif
.if ENABLE_DEC
    .if ENABLE_BYTE
        db 'b'  ; PrintByte     NOTE: Signed -128 .. +127
    .endif
        db 'u'  ; PrintDec5
        db 'd'  ; PrintDec3
        db '#'  ; PrintDec2
.endif
.if ENABLE_HEX
        db 'x'  ; PrintHex4
        db '$'  ; PrintHex2
    .if ENABLE_PTR
        db '&'  ; PrintPtr4
        db '@'  ; PrintPtr2
    .endif
.endif
.if ENABLE_STR
        db 'p'  ; PrintStrP     NOTE: Pascal string; C printf 'p' is pointer!
        db 's'  ; PrintStrC     NOTE: C string, zero terminated
        db 'a'  ; PrintStrA     NOTE: Last byte is ASCII
.endif

_MetaCharEnd
NumMeta = _MetaCharEnd - MetaChar

MetaFunc

.if ENABLE_BIN
        dw PrintBinInv
        dw PrintBinAsc
.endif
.if ENABLE_DEC
    .if ENABLE_BYTE
        dw PrintByte
    .endif
        dw PrintDec5
        dw PrintDec3
        dw PrintDec2
.endif
.if ENABLE_HEX
        dw PrintHex4
        dw PrintHex2
    .if ENABLE_PTR
        dw PrintPtr4
        dw PrintPtr2
    .endif
.endif
.if ENABLE_STR
        dw PrintStrP
        dw PrintStrC
        dw PrintStrA
.endif

__END

[toc] | [prev] | [next] | [standalone]


#2285

Fromwssimms@gmail.com
Date2016-02-18 17:58 -0800
Message-ID<b56a3f9a-d08b-43e2-b543-816572c69a85@googlegroups.com>
In reply to#2273
Am Freitag, 19. Februar 2016 08:09:39 UTC+9 schrieb Michael Pohoreski:
> I've cleanup that hideous requirement of interleaving ASCII + APPLE text using a much cleaner
> PRINTM macro now.
> 
>      PRINTM "X=%# Y=%d $=%x:%@ %%~%?"

I like this version much better. This makes it much easier to use.

Also I agree with you on the octal. It's mystifying to me how those guys
(like Ken Thompson, Dennis Ritchie, etc.) put up with using octal for
everything on a 16-bit computer in the 60s and 70s. Of course I can
always justify it by telling myself that they are/were way smarter than I am.

[toc] | [prev] | [next] | [standalone]


#2300

FromMichael Barry <barrym95838@yahoo.com>
Date2016-02-19 17:52 -0800
Message-ID<ac829b2c-6460-4ddb-b09d-c610774130b3@googlegroups.com>
In reply to#2285
On Thursday, February 18, 2016 at 5:58:02 PM UTC-8, wss...@gmail.com wrote:
> ...
> Also I agree with you on the octal. It's mystifying to me how those
> guys (like Ken Thompson, Dennis Ritchie, etc.) put up with using octal
> for everything on a 16-bit computer in the 60s and 70s. Of course I
> can always justify it by telling myself that they are/were way smarter
> than I am.

Octal was a natural fit for all of the 12-bit and 18-bit word machines
 DEC cranked out in the 60s, but it takes an examination of the pdp-11
 instruction field format to understand why octal was also "natural" for
 that 16-bit word machine.

Mike B.

[toc] | [prev] | [next] | [standalone]


#2301

Fromwssimms@gmail.com
Date2016-02-19 18:09 -0800
Message-ID<d221c69a-fb99-4ac8-9946-e308758269ed@googlegroups.com>
In reply to#2300
Am Samstag, 20. Februar 2016 10:52:06 UTC+9 schrieb Michael Barry:
> On Thursday, February 18, 2016 at 5:58:02 PM UTC-8, wss...@gmail.com wrote:
> > ...
> > Also I agree with you on the octal. It's mystifying to me how those
> > guys (like Ken Thompson, Dennis Ritchie, etc.) put up with using octal
> > for everything on a 16-bit computer in the 60s and 70s. Of course I
> > can always justify it by telling myself that they are/were way smarter
> > than I am.
> 
> Octal was a natural fit for all of the 12-bit and 18-bit word machines
>  DEC cranked out in the 60s, but it takes an examination of the pdp-11
>  instruction field format to understand why octal was also "natural" for
>  that 16-bit word machine.
> 
> Mike B.

Granted, it was marginally useful for dealing with three bit fields in
the instruction word. But in practice that was done approximately
never. The assembler took care of that nonsense.

Really, it was mainly familiarity. Once you're comfortable with octal,
there's no problem, and no obvious need to use anything else.

[toc] | [prev] | [next] | [standalone]


#2305

FromMichael J. Mahon <mjmahon@aol.com>
Date2016-02-20 00:27 -0600
Message-ID<JYCdnRZWUq36mlXLnZ2dnUU7-RmdnZ2d@giganews.com>
In reply to#2301
<wssimms@gmail.com> wrote:
> Am Samstag, 20. Februar 2016 10:52:06 UTC+9 schrieb Michael Barry:
>> On Thursday, February 18, 2016 at 5:58:02 PM UTC-8, wss...@gmail.com wrote:
>>> ...
>>> Also I agree with you on the octal. It's mystifying to me how those
>>> guys (like Ken Thompson, Dennis Ritchie, etc.) put up with using octal
>>> for everything on a 16-bit computer in the 60s and 70s. Of course I
>>> can always justify it by telling myself that they are/were way smarter
>>> than I am.
>> 
>> Octal was a natural fit for all of the 12-bit and 18-bit word machines
>> DEC cranked out in the 60s, but it takes an examination of the pdp-11
>> instruction field format to understand why octal was also "natural" for
>> that 16-bit word machine.
>> 
>> Mike B.
> 
> Granted, it was marginally useful for dealing with three bit fields in
> the instruction word. But in practice that was done approximately
> never. The assembler took care of that nonsense.
> 
> Really, it was mainly familiarity. Once you're comfortable with octal,
> there's no problem, and no obvious need to use anything else.
> 

Exactly. 

The pre-360 IBM binary machines had 36-bit words and two 3-bit instruction
fields, and octal seemed natural for those machines. 

Also, there was no standard for hex digits greater than 9 at first, and
non-numeric digits seemed strange--until they became commonplace. In the
US, that happened with the 32-bit 360 series. 

After you've become familiar with hex, it seems quite natural--just like
your favorite text editor. ;-)
-- 
-michael - NadaNet 3.1 and AppleCrate II:  http://michaeljmahon.com

[toc] | [prev] | [next] | [standalone]


#2270

FromMichael Pohoreski <michael.pohoreski@gmail.com>
Date2016-02-18 13:37 -0800
Message-ID<3605e58c-b6ff-4f72-9e73-5592afe66d8e@googlegroups.com>
In reply to#2219
Just wanted to send a public shoutout "Thanks!" to

* Sheldon,
* Antoine,
* anyone else who posts assembly

for sharing all their 6502 (assembly) code & write-ups.  

Just when one thinks one understand 6502 assembly you guys come along and teach an old dog new tricks. ;-)  Thanks for keeping this (ancient) hobby exciting!

I've finally "relented" and started using 65C02 instructions. Hey, better late, then never, right. :-)
If only we had a CAX (compare A with X), and CAY (compare A with Y) instructions ...

Sheldon, I like your self-modifying code tricks. :-)

[toc] | [prev] | [standalone]


Page 2 of 2 — ← Prev page 1 [2]

Back to top | Article view | comp.sys.apple2.programmer


csiph-web