;*******************************************************************************
;* Module    : PRINT.SUB
;* Programmer: Tony Papadimitriou <tonyp@acm.org>
;* Purpose   : Provides macro(s) to print strings and numeric expressions in a
;*           ; single statement, just like in a HLL.  Example,
;*           : @print 'Hello ' sqr(2)+1 ' times!'
;*           : will print the string 'Hello 5 times!' making it simple to add
;*           : print statements containing math expressions (uses STAKMATH).
;* Language  : Motorola/Freescale/NXP HC08/9S08 Assembly Language (aspisys.com/ASM8)
;* Status    : FREEWARE Copyright (c) 2021 by Tony Papadimitriou <tonyp@acm.org>
;* Original  : http://www.aspisys.com/code/hc08/print.html
;* Note(s)   : Use: #Include print.sub
;*           : You need to supply your own 'putc' and 'puts' macros that call
;*           : the appropriate routines to print an ASCII character, or an ASCIZ
;*           : string pointed to by HX, respectively.
;* History   : 13.05.30 v1.00 Original (Started on 2013.05.30)
;*           : 13.08.22       Improvement allows [StringVar] and #CONST syntax
;*           :                If [StringVar] is size one, then it is [CharVar]
;*           :                (pointers assumed to always point to StringVar)
;*           : 13.10.16 v1.10 PrintStr optimized for single-char string constant
;*           : 13.10.22 v1.20 PrintStr optimized for string constants by FCS macro
;*           : 13.11.22 v1.30 Added PrintStr string printing routine override
;*           : 14.04.02 v1.31 Replaced @FCC/FCS macro use with actual directives
;*           : 15.05.31 v1.32 Added 1st : expression option to allow decimal dots definition
;*           : 15.06.01       Added 2nd : expression option to allow left padding
;*           : 15.06.04       Added missing #uses for AddDecimalPoint & StringPadLeft
;*           : 15.06.13 v1.33 Added optional leading $ symbol to the Print macro
;*           :                to specify that we need to protect all registers
;*           : 15.06.27 v1.34 Added optional inline subroutine call
;*           : 16.06.10 v1.35 Reverted the use of fPrint as it does not use putc
;*           : 16.06.15 v1.36 BugFix: Print macro no longer assumes # for [...]
;*           : 19.04.19       Removed MMU case warning with constant strings
;*           : 19.09.23 v1.37 Added optional leading $$ symbol to the Print macro
;*           :                to specify that we also need to protect the CCR
;*           : 20.04.01 v1.38 Replaced #Message with Msg macro in PrintStr and
;*           :                PrintNum macros to silence debugging messages
;*           :                unless SHOW_ALL_MESSAGES is defined
;*           : 21.02.18 v1.39 Guarantee FCS to be in #ROM segment under #MMU
;*           : 21.04.13 v1.40 BugFix: Strings no longer get trimmed
;*           :                BugFix: Strings including | are parsed correctly
;*           : 21.06.23 v1.41 Improved Print macro to use BSR instead of JSR when distance allows
;*******************************************************************************

#ifmain ;-----------------------------------------------------------------------
                    #ListOff
                    #Uses     mcu.inc
                    #Uses     stkmth32.sub
                    #ListOn

putc                macro
                    call      PutCharInBuffer     ;;print char in RegA
                    endm

puts                macro
                    call      PrintAsciz          ;;print ASCIZ string pointed to by HX
                    endm
#endif ;------------------------------------------------------------------------
                    #Uses     string/adddecimalpoint.sub
                    #Uses     string/padleft.sub
?_OBJECT_?
;*******************************************************************************
; Macro to print an ASCIZ string (constant or variable)
; You can override the default string printing routine by appending |subname
; after the operand, where subname is the name of the subroutine to be CALLed.

PrintStr            macro
                    mset      #'|'
                    @@Msg     ~0~: ~1~
          #ifstr ~1~
            #if :1 = 3                            ;;single character
                    lda       #~1~
                    @putc
                    mexit
            #endif
                    #push
            #ifmmu
                    #ROM
            #endif
                    bra       _$$$
                    #temp     :pc
                    fcs       ~1~                 ;;string constant
_$$$
                    #pull
                    ldhx      #[[{:temp}          ;;HX -> constant string
          #else
                    @@lea     ~1~                 ;;HX -> user string
          #endif
          #ifnb ~2~
                    call      ~2~
                    mexit
          #endif
                    @puts
                    endm

;*******************************************************************************
; Macro to print a number or expression as string

PrintNum            macro
                    mset      #
                    @@Msg     ~0~: ~1~
                    #push
                    #spauto   :sp
                    #psp
                    @@Eval    n$$$,sp = ~[:]~     ;;evaluate expression/number
                    ais       #-{::n$$$*3+3}      ;;make room for string
s$$$                equ       ::,{:ais}           ;;assign it a name (and size)
                    @@StrMath, n$$$,sp s$$$,sp    ;;convert number to ASCIZ
          #ifnb ~[:]2~
                    lda       ~[:]2~
                    @@lea     s$$$,sp
                    call      AddDecimalPoint
          #endif
          #ifnb ~[:]3~
                    lda       ~[:]3~
            #ifdef DEBUG
                    cmpa      #::s$$$             ;compare against allocated string space
                    bhs       *                   ;new string length is too big
            #endif
                    @@lea     s$$$,sp
                    call      StringPadLeft
          #endif
                    @@PrintStr s$$$,sp            ;;print it
                    ais       #:psp               ;;de-allocate all temporaries
                    #pull
                    endm

;*******************************************************************************
; Macro to print a mixed series of strings and/or expressions separated by
; spaces, regardless of the #PARMS setting.
; If you need to include spaces inside an expression, you must enclose the whole
; expression in parentheses.
; (Strings must be quoted.  String or character variables must be enclosed in
; [ ] pairs, with size one meaning character variable else string variable,
; character constants must be given as immediate mode or zero-size labels, and
; finally, any other non-quoted parts are assumed to be expressions.  Pointers
; are always assumed to point to string variables.)
; You can override the default string printing routine by appending '|subname'
; (without the quotes) after the corresponding operand, where subname is the
; name of the subroutine to be CALLed.
; You can call any routine inline by using the @Routine format.
; Important Note: To prevent ambiguity with [ ... ] between Eval macro expressions
; that use this to denote unsigned item, and Print macro that uses this to denote
; variable, you can either put an expression inside parentheses, or simply end it
; with a colon even if no formatting parameters will be used.

Print               macro     [$[$]]('String'|@Routine|[StringVar]|#CONST|Expr[:Decimals][:Width])+
                    mset      #' '                ;;re-split on space delimiter
          #ifnb ~1~ = $
                    mdel      1
                    mset      #
                    #push
                    #spauto   :sp
                    push
                    @@~0~     ~1~
                    pull
                    #pull
                    mexit
          #else ifnb ~1~ = $$
                    mdel      1
                    mset      #
                    #push
                    #spauto   :sp
                    push
                    tpa
                    psha
                    @@~0~     ~1~
                    pula
                    tap
                    pull
                    #pull
                    mexit
          #endif
                    #push
                    #spauto   :sp
                    mdo
                    mswap     1,:mloop
                    mset      0
          #ifnostr ~1~
                    mset      0,~'|'2~
                    mset      1,~'|'1~
          #endif
                    #temp
          #ifdef ~#1~                             ;;if a defined symbol
            #ifnostr ~1~                          ;;but not a string
              #ifz ::~#1~                         ;;and having size zero
                #ifb ~#~                          ;;but not immediate mode
                  #ifnoparm ~1.1.1~ = [
                    mset      1,#~#1~             ;;force it to immediate mode
                  #endif
                #endif
              #endif
            #endif
          #endif
          #ifparm ~#~                             ;;immediate mode is a char
                    #temp     1
                    @@_lda_   ~1~
                    @@putc
          #endif
          #ifstr ~1~                              ;;explicit string constant
                    #temp     2
                    @@PrintStr,, ~1~|~text~
          #endif
          #ifparm \@~1.1.1~\@ = \@.\@             ;;pointer to string variable
                    #temp     3
                    @@PrintStr ~1~|~text~
          #endif
          #ifparm ~1.1.1~~1.{:1}~ = []            ;;explicit string variable
                    #temp     4
                    mset      1,~1.2.{:1-2}~      ;;remove outer []
            #if ::~1,~ = 1                        ;;character variable
                    #temp     5
                    lda       ~1~
                    @@putc
            #endif
            #if :temp = 4
                    @@PrintStr ~1~|~text~         ;;print without outer []
            #endif
          #endif
          #ifparm \@~1.1.1~\@ = \@@\@             ;;subroutine call
                    #temp     6
            #!if :pc-~1.2~ < 127
                    bsr       ~1.2~
            #else
                    jsr       ~1.2~
            #endif
          #endif
          #ifz :temp                              ;;assume math expression
                    @@PrintNum ~1~
          #endif
                    mloop     :n                  ;;repeat for all parms
                    #pull
                    endm

;*******************************************************************************
                    #Exit
;*******************************************************************************
                    @EndStats
                    #HideMacros

ResetBuffer         macro
                    @mov.s    #buffer .ptr
                    endm

.ptr                @var      2
buffer              @var      20

;*******************************************************************************
; A dummy routine to emulate printing of a single ASCII character

                    #spauto

PutCharInBuffer     proc
                    pshhx
                    @PutNextA .ptr
                    clr       ,x                  ;make ASCIZ string
                    pulhx
                    rtc

;*******************************************************************************
; A dummy routine to emulate printing of an ASCIZ string

                    #spauto

PrintAsciz          proc
                    push
Loop@@              lda       ,x
                    beq       Done@@
                    @putc
                    aix       #1
                    bra       Loop@@
Done@@              pull
                    rtc

;*******************************************************************************

                    #spauto

Start               proc
                    lda       #2
                    psha      a@@                 ;create a local variable
          ;-------------------------------------- ;Example 1
Ex1                 @ResetBuffer
                    @print    $ 'This is the ' (3 + a@@,sp) 'th number!' CR LF
          ;-------------------------------------- ;Example 2
Ex2                 @ResetBuffer
                    @print    $ 'Hello ' sqr(a@@,sp)+1 ' times!' CR LF
          ;--------------------------------------
Done                ais       #:ais               ;de-allocate local variable(s)

Halt@@              proc
                    @cop
                    bra       Halt@@

;*******************************************************************************
                    @vector   Vreset,Start
;*******************************************************************************