
**********************************************************************
files: Printing_Text.TXT, Printing_Text.TAP
       PadNum.BAS, PadNum3.ASM, Substr.ASM, PrinText.ASM
       http://www.users.globalnet.co.uk/~jg27paw4/pourri/wordwrapv4.tap
       http://www.users.globalnet.co.uk/~jg27paw4/pourri/propwrap.zip
       http://www.users.globalnet.co.uk/~jg27paw4/pourri/chars_plot.asm
**********************************************************************


** Number Padding Function
   =======================
PadNum
Positive pad lengths use zeroes, negative use spaces. Numbers wider than the pad parameters print in full, as shown in some of the examples. It works on the +128k and +2A as well. The examples below should make it clear how to use it. There's an interesting bit at the OUTPUT label, which shows a method of manipulating the machine stack to get USR to return a string rather than a number.

[code]
     DEF FN p$(n,l,r)=STR$ USR address: REM n=number,l=leftpad,r=rightpad
     ... examples ...
     FN p$(1.2,0,0)        := "1.2"
     FN p$(1.2,3,3)        := "001.200"
     FN p$(1.2,-3,-3)      := "  1.2  "
     FN p$(123.456,0,0)    := "123.456"
     FN p$(123.456,-6,4)   := "   123.4560"
     FN p$(-123.456,-6,4)  := "-  123.4560"
     FN p$(123.456,-2,2)   := "123.456"
     FN p$(1,0,0)          := "1"
     FN p$(1,0,1)          := "1.0"
     FN p$(1,-5,3)         := "    1.000"
     FN p$(1,-5,0)         := "    1"
     FN p$(1.2,-5,0)       := "    1.2"
     FN p$(1.2,-5,1)       := "    1.2"
     FN p$(1.2,-5,2)       := "    1.20"
     FN p$(0.12,0,0)       := ".12"
     FN p$(0.12,1,1)       := "0.12"
     FN p$(0.12,3,3)       := "000.120"
     FN p$(-0.12,3,3)      := "-00.120"
     FN p$(0,0,0)          := ""
     FN p$(1.2345679E+8,any,any) := "1.2345679E+8"
[/code]

PadNum3.ASM [code]

;    DEF FN p$(n,l,r)=STR$ USR padnum
;    00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23
;    n  0e .. .. .. .. .. ,  l  0e .. .. nn .. .. ,  r  0e .. .. nn .. .. )

PADNUM: ld   hl,(23563)      ;HL=(DEFADD)
        push hl
        inc  hl
        inc  hl
        call $33b4           ;STACK_NUM
        rst  $28             ;FP_CALC
        defb $2e             ;str$
        defb $38             ;end_calc
        call $2bf1           ;STK_FETCH
        pop  ix              ;IX=(DEFADD)
        ld   a,"E"
        call FINDA
        jr   z,OUTPUT

        ld   a,"."
        call FINDA
        jr   z,GOTDP         ;got decimal point
        ld   (hl),a          ;add a "." if there isn't one
        inc  c

GOTDP:  push de
        pop  hl
        add  hl,bc
        ld   (hl),$80        ;end marker
        inc  hl
        ex   de,hl

        ld   a,(hl)
        cp   "-"
        jr   nz,NOMINUS
        ld   (de),a
        inc  hl
        inc  de
        inc  b

NOMINUS:ld   a,(hl)
        cp   "0"
        jr   nz,NEXTINT
        inc  hl              ;skip a leading zero

NEXTINT:ld   c,"."
        push hl
        push de
        call COPYDIG         ;count integer
        pop  de
        ld   a,(ix+12)       ;A =maximum integer padding (signed)
        call PADSUB
        pop  hl
        ld   c,"."
        call COPYDIG         ;copy integer

NEXTDEC:ld   c,$80
        ld   b,0
        call COPYDIG         ;copy decimal
        dec  de
        ld   a,(ix+20)       ;A =maximum decimal padding (signed)
        call PADSUB

FINISH: ld   (23651),de      ;STKBOT
        ld   (23653),de      ;STKEND
        dec  de
        ld   a,(de)
        ex   de,hl           ;DE=start address
        sbc  hl,de
        inc  hl
        ld   c,l             ;BC=length
        cp   "."
        jr   nz,OUTPUT
        dec  c               ;discard a trailing "."

OUTPUT: call $2ab1           ;STK_ST_0

        ;reorganise the machine stack so that this string
        ;becomes the output from the function instead of BC
        pop  bc              ;BC=$2d2b (discard)
        pop  bc              ;BC=$3365
        pop  de              ;DE=$0000
        inc  sp
        inc  sp
        pop  hl              ;HL=$000D
        ex   (sp),hl         ;HL=$106E
        push hl
        push de
        push bc
        ld   de,(23653)      ;DE=(STKEND) (for RE_ENTRY)
        ret                  ;return via $3365 RE_ENTRY

;-------

;search from DE for character A
FINDA:  push bc
        push de
        pop  hl
        cpir
        pop  bc
        ret
;copy digits up to character C to new number string
COPYDIG:ld   a,(hl)
        ld   (de),a
        inc  hl
        inc  de
        inc  b
        cp   c
        jr   nz,COPYDIG
        dec  b               ;B =bytes (including minus)
        ret
;determine required padding character & length
PADSUB: ld   c,"0"
        bit  7,a
        jr   z,ZEROPAD
        ld   c," "
        neg
ZEROPAD:sub  b               ;A =maximum padding - byte count
        jr  nc,PADLR
        xor  a
;copy B pad characters to new number string
PADLR:  ld   b,a
        ld   a,c
        ret  z
PADNEXT:ld   (de),a
        inc  de
        djnz PADNEXT
        ret
[/code]


**********************************************************************


** Procrustean Printing
   ====================

Here's a short routine to manage printing of text which includes ZX BASIC command tokens, to avoid messing up the screen when they are expanded. It just prints as many characters as it can, expanding command tokens in situ, until it reaches the requested limit. It can be used on its own or embedded in another PRINT statement thus:

PRINT "stuff";USR stack,string_expression,max_length,substr;"morestuff"

Substr.ASM [code]
;PRINT USR stack,string_expression,max_length,substr;
;expand string_expression and print up to max_length characters
;stack function parameters passed from BASIC by:
;PRINT USR stack,arg1[,arg2...,argn],address;
SCANNG: equ  $24fb           ;the SCANNING subroutine
FETCHS: equ  $2bf1           ;from calculator stack: AEDCB
FPTOBC: equ  $2da2           ;from calculator stack: BC
FPTOA:  equ  $2dd5           ;from calculator stack: A

STACK:  ld   hl,14
        add  hl,sp
        ld   sp,hl           ;undo USR
        ld   ix,0
GETARG: rst  $20
        call SCANNG
        inc  ix
        rst  $18
        cp   ","
        jr   z,GETARG
        call FPTOBC          ;BC=address
        ld   h,b
        ld   l,c
        push ix
        pop  bc
        dec  bc
        ld   a,c             ;A =number of parameters
        jp   (hl)            ;JP address

;******

SUBSTR: call FPTOA
        push af
        call FETCHS
        pop  af
        ex   de,hl           ;HL=string pointer
        inc  c               ;C =string length + 1
        ld   b,a             ;B =max_length
        ld   ix,TOKENDATA
        ld   (ix+0),0        ;reset flags
        push hl

SSNEXT1:pop  hl
        ld   a,(hl)
        inc  hl
        dec  c
        jr   z,NOMORE        ;end of string
        push hl
        cp   (ix+1)          ;check if in token range
        jr   nc,TOKEN
        res  0,(ix+0)        ;flag "leading space" for next token
        cp   $20
        jr   nc,PRCHAR
        inc  b               ;don't count print controls
PRCHAR: rst  $10             ;print a character
SSDECB: djnz SSNEXT1         ;printed max_length?
SSEND:  pop  hl
        ret

NOMORE: ld   a,$20           ;pad to end of max_length
        rst  $10
        djnz NOMORE
        ret

        ;expand this token
TOKEN:  exx                  ;save counters
        ld   hl,EXLIST
        ld   b,0
        ld   c,(ix+3)        ;C =length of exclusion list
        cpir                 ;search for A=token in list
        exx
        jr   nz,CBOUND
        set  1,(ix+0)        ;flag "excluded"
CBOUND: cp   (ix+2)          ;check upper boundary
        jr   nc,SEARCH
        set  2,(ix+0)        ;flag "in lower group"
SEARCH: sub  (ix+1)
        ld   de,$0095
        call $0c41 ;PO_SEARCH;sets DE=token table entry address

        ;check if a leading space is needed
        ld   a,%110
        and  (ix+0)          ;excluded or not in upper group?
        jr   nz,SSNEXT2      ;yes, so don't print a space
        bit  0,(ix+0)        ;space already printed?
        jr   nz,SSNEXT2      ;yes, so don't print a space
        ld   a,$20           ;no, so print a leading space
        rst  $10
        dec  b
        jr   z,SSEND         ;printed max_length?

SSNEXT2:ld   a,(de)
        push af
        and  $7f             ;reset bit 7
        rst  $10             ;print a character
        pop  af              ;A =last token char printed
        inc  de
        dec  b
        jr   z,SSEND         ;printed max_length?
        bit  7,a
        jr   z,SSNEXT2       ;end of token?
        bit  1,(ix+0)        ;yes; excluded?
        ld   (ix+0),0        ;reset flags
        jr   nz,SSNEXT1
DOSPACE:ld   a,$20           ;print a trailing space
        rst  $10
        set  0,(ix+0)        ;flag "no leading space"
        jr   SSDECB

TOKENDATA:                   ;leading/trailing spaces info
;tokens <  boundary need only a trailing space
;tokens >= boundary need both leading/trailing spaces
;excluding those in the table below,
;and conditional on a space not having just been printed previously
        defb $00             ;bit 0 set if leading space not needed
                             ;bit 1 set if token is in exclusion list
                             ;bit 2 set if token is in lower group
        defb $a5             ;start of token codes
        defb $c5             ;leading/trailing boundary token
        defb $06             ;length of exclusion list
;table of tokens which require no leading/trailing spaces
EXLIST: defb $a5,$a6,$a7     ;RND,INKEY$,PI
        defb $c7,$c8,$c9     ;<=,>=,<>
[/code]

The initial Stack section is incidental to the main Substring section; it's just the method by which I'm passing multiple parameters to the m/code routine itself. So Stack isn't involved with the printing at all, even though it's called via a PRINT statement.

The advantage of using the "PRINT USR to calculator stack" method of passing parameters is that it will accept any number of parameters of any type. It then uses Scanning to evaluate each expression on the command line and put them on the calculator stack. So the general function call:
PRINT USR stack,arg1[,arg2...,argn],address;
will print nothing at all (as long as there's a ";" at the end, otherwise it will print a carriage return). In this case it's the Substring routine which does all the printing.

As regards my reason for writing Substring, I was working on a program which held expressions in an array, selected and then run using VAL; but when the editor printed a function; eg. "FN d(2,0)<=FN i(47)", the characters printed would be more than those in the string (eg. "FN " is held as a single token character but prints as three characters), so the screen got messed up as characters were running over onto the next line. So I wrote this to control the length of the output regardless of how embedded command tokens might be expanded.

The general idea could be used for any application which uses the token technique for storing data; ie. space is conserved by storing strings as single characters which are expanded on output; it would just be necessary to change the "ld de,$0095" line to point to the address of the token table being used, or else the address could be passed as an extra parameter. I've made it easier to alter for other token tables by including exceptions in a little table at the end.

To clarify how the Sinclair tokens work (in ascending order):
[code]
RND, INKEY$, PI - no spaces either side
FN to BIN       - trailing space
OR, AND         - space both sides
<=, >=, <>      - no spaces either side
LINE to COPY    - space both sides
[/code]
Always qualified by the proviso that if a token printed a trailing space then a consecutive one won't print a leading space.


**********************************************************************


** Printing Text with Condensed Print Controls
   ===========================================

Here's a routine which just uses one byte for each print control instead of three, combining the print column with a "next row" flag.

PrinText.ASM [code]
    ld   a,2
    call 5633           ;open print channel
    ld   bc,9
    ld   de,text_start
    call $203c          ;PR_STRING print a string length BC from DE
    ex   de,hl

print_text:
    ld   a,(hl)
    cp   $ff
    ret  z
    cp   " "
    jr   c,print_control
    cp   128
    jr   c,print_char
print_control:
    ld   bc,(23688)     ;(S_POSN)
    bit  7,a
    jr   z,same_row
    dec  b              ;next row
    and  %01111111
same_row:
    neg
    add  a,33
    ld   c,a            ;set column
    push hl
    call $0dd9          ;CL_SET
    pop  hl
    jr   next_char
print_char:
    rst  16
next_char:
    inc  hl
    jr   print_text
    ret

text_start:
    defb 22,1,20,16,1,17,7,19,1    ;   AT 1,20; INK 1; PAPER 7; BRIGHT 1
text_to_print:
    defb 20,"LINE 1 TXT"
    defb 128+22,"LINE 2 TXT"
    defb 128+22,"LINE 3 TXT"
    defb 128+22,"LINE 4 TXT"
    defb 128+00
    defb 128+20,"LINE 5 TXT"
    defb 128+22,"LINE 6 TXT"
    defb 128+22,"LINE 7 TXT"
    defb $ff
[/code]


**********************************************************************


** Printing 64 Column Text from BASIC
   ==================================

Some compressed text routines which support PRINT AT are:
[url=http://www.users.globalnet.co.uk/~jg27paw4/yr20/propsys.zip]Proportional Printing[/url] (Your Spectrum), [url=http://www.users.globalnet.co.uk/~jg27paw4/type-ins/zx-comp/zxcoll84.zip]Print 64[/url] (ZX Computing), [url=http://www.users.globalnet.co.uk/~jg27paw4/type-ins/yourcomp/yc8410.zip]PrintProg[/url] (Your Computer)
There are others, but they usually need the text & control codes to be in a string variable or POKEd into memory, so they're not so easy to use.

   ==================================

That reminds me that ages ago I wrote a BASIC 64-column font routine which generates the character set from scratch without DATA statements by comparing adjacent bits in the pattern for each standard character and setting a half-size pattern based on if adjacent bits reinforce each other. It prints compressed by having left-hand & right-hand charsets and printing consecutive pairs of characters from the alternate charsets in the same position with OVER.

I wrote it ten years ago and now I have no idea why, but the programs are in MINIGEN2.TAP in [url=http://www.users.globalnet.co.uk/~jg27paw4/pourri/newtype.zip]this ZIP file[/url] for reference. The generation program can be skipped as the charsets are already on there. There's a little demo program at the end.

   ==================================

Have a look at my [url=http://www.users.globalnet.co.uk/~jg27paw4/pourri/browser.zip]VU-R Browser[/url] files and read the section "Notes on the machine code routines in BROWSER.Z80" in BROWSER.TXT . The ZIP file includes enhanced versions of three routines from the mags which only take effect at the time they're called to print something. They all require the characters and control codes to be either built up in a string variable or POKEd into memory, so they're a bit more fiddly to use.


**********************************************************************


** A neat little BASIC trick for TALL letters
   ==========================================

(from a type-in by Robert Burgess)
This one line of code completely bamboozled me for a while, as it seemed impossible for it to produce the result shown on the screen:
[center][img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tfa.gif[/img][/center]
particularly as the manual says that the system variable byte at 23681 is "not used" - but, looking at the use of PR_CC at 23680 and deducing from that what might have been the intended use of 23681, it eventually dawned on me what was happening. (Doesn't work on 128k models in 128 mode.)


**********************************************************************


** Proportional Print Routine
   ==========================

The Your Sinclair routine needs an alternative compressed character set for best results, which you can find included with my modified version amongst my [url=http://www.users.globalnet.co.uk/~jg27paw4/pourri/browser.zip]VU-R[/url] browser files. Read sections (b) & (c) in "Notes on the machine code routines" in BROWSER.TXT . Up to 48 characters fit on a line (depending upon the mix of characters). The green text in this screen is an example:

[center][img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/yr20_25.gif[/img][/center]

The Your Spectrum routine already includes a modified character set. There's an example of the text at the end of the [url=http://www.users.globalnet.co.uk/~jg27paw4/yr20/yr20_46.htm]Out Of All Proportion[/url] article; there's a download link on the page as well.

   ==========================

The demo of the Your Spectrum routine doesn't automatically load the CODE block, so you have to CLEAR 62199 then LOAD "PS-demo" then LOAD "" CODE then RUN.

The two routines, including 768 bytes for their alternate character sets, take 953 bytes and 1150 bytes respectively. The Your Spectrum routine is larger, but easier to use as it just uses the LPRINT command; whereas the Your Sinclair routine uses POKEs to specify print positions and the string location (although it does have my much nicer character set :) ).

   ==========================

Although PrintPR and PropSys both use a 768 byte character set, the routines work differently so the character set definition rules are different. PrintPR uses a normal definition, whereas PropSys expects the first byte of each character to specify the width and the remaining seven bytes to hold a left-justified character definition.

The PrintPR set could be transferred to PropSys, but in between it would have to be altered as per the rules explained in the "Out Of All Proportion" article. A short routine would be needed to left-justify the character definitions and calculate the header byte values for compatibility with PropSys. The screen below shows the original PropSys character definitions from the article displayed as plain text, to show the header byte structure.

[center][img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/propsys2.gif[/img][/center]

   ==========================

width = 8-(LN (byte+1)/LN 2)

   ==========================

Here's a shorter version of the printing subroutine which does line wrap rather than word wrap. Even so, it only reduces the time to display the page from 30 to 22 seconds. Removing the LPRINTs it still takes 20 seconds, so that little bit of BASIC that's left in the loop is taking up most of the time.

[code]
 920 LET beg=1: LET wi=0: FOR i=1 TO LEN a$
 930 IF a$(i)=" " THEN LET end=i-1: LET wo=wi
 935 LET wi=wi+PEEK (64072+CODE a$(i)):
     IF wi>255 THEN LPRINT AT 0,y;a$(beg TO end):
     LET y=y+8: LET beg=end+2: LET wi=wi-wo
 940 NEXT i:
     LPRINT AT 0,y;a$(beg TO ): LET y=y+8: RETURN
[/code]

   ==========================

There's a call at $FE7B to the ROM routine PIXEL-ADD at $22AA to calculate the display file address and this routine limits the Y co-ord to 175. Even if the PropSys code is changed to allow the extra 16 pixel rows the call to PIXEL-ADD will give an "Integer out of range" error. That ROM call can't be used if you want to print across the full length of the screen.

   ==========================

Here's another mod for na_th_an's program which uses a bit of machine code to do the line wrapping calculations.

[code]
  10 CLEAR 62999: GO TO 1000
     ...
 920 LET beg=1: POKE 63007,0: POKE 63005,1: POKE 63006,0
 935 LET end=USR 63009: LPRINT AT 0,y;a$(beg TO end):
     LET y=y+8: LET beg=end+2: IF end<LEN a$ THEN GO TO 935
 940 RETURN

----

        org  63000

table:  equ  64072
ch_add: equ  23645
char:   defm "a$:"            ;63000
sadd:   defw $0000            ;63003(IX+0) absolute a$ start address
posn:   defw $0001            ;63005(IX+2) relative a$ current pointer
wi:     defb $00              ;63007(IX+4) current line width
wo:     defb $00              ;63008(IX+5) last full word line width

find:   ld   bc,(ch_add)
        push bc
        ld   bc,char
        ld   (ch_add),bc
        call $28b2            ;LOOK-VARS
        pop  bc
        ld   (ch_add),bc
        ld   ix,sadd
        inc  hl
        ld   e,(hl)
        inc  hl
        ld   d,(hl)           ;DE=LEN a$
        ex   de,hl            ;HL=LEN a$; DE=start a$
        ld   (ix+0),e
        ld   (ix+1),d
        add  hl,de            ;HL=end a$
        push hl               ;save it
        ex   de,hl            ;HL=start a$
        ld   e,(ix+2)
        ld   d,(ix+3)         ;DE=current pointer
        add  hl,de            ;HL=current a$

nextch: pop  de               ;DE=end a$
        push de
        ex   de,hl
        and  a
        sbc  hl,de            ;end a$ - current a$
        ex   de,hl
        jr   nc,nextok
        push hl
        pop  bc
        dec  bc               ;BC=end address of last char in a$
        jr   print

nextok: ld   a,(hl)
        cp   32
        jr   nz,notspc
        push hl
        pop  bc
        dec  bc               ;BC=end address of last word on line
        ld   e,(ix+4)         ;E=wi
        ld   (ix+5),e         ;wo=E=wi

notspc: ld   e,a
        ld   d,0
        push hl
        ld   hl,table         ;HL=start of width table
        add  hl,de            ;HL=character position in table
        ld   a,(hl)           ;A =character width
        add  a,(ix+4)         ;calculate new line width
        ld   (ix+4),a         ;save it
        pop  hl               ;HL=current a$
        inc  hl
        jr   nc,nextch        ;checks result of "add  a,(ix+4)"

print:  pop  de               ;DE=end a$
        ld   e,(ix+0)
        ld   d,(ix+1)         ;DE=start a$
        and  a
        sbc  hl,de            ;current a$ - start a$
        ld   (ix+2),l
        ld   (ix+3),h         ;save current pointer
        push bc
        pop  hl
        sbc  hl,de            ;end of segment - start a$
        push hl
        pop  bc               ;BC="end" variable for BASIC
        ld   a,(ix+4)
        sub  (ix+5)
        ld   (ix+4),a
        ret
[/code]

   ==========================

Here's a patch to print into the lower screen, plus some evidence. It works either if the text wraps around into the lower screen or if printed directly to the lower screen (eg. LPRINT AT 0,183;"something").

[code]
OLD                                         NEW
FDE8 26A8     0050 INITI LD   H,168    |    26B8     0050 INITI LD   H,184
FE2B 3EA8     0450       LD   A,168    |    3EB8     0450       LD   A,184
FE6F FEA9     0770       CP   169      |    FEB9     0770       CP   185
FE7B CDAA22   0810       CALL PIXAD    |    CDE2FD   0810       CALL PATCH
FEDC A8       1290 YPOS  DEFB 168      |    B8       1290 YPOS  DEFB 184

FDE2                                        3EBF          PATCH LD   A,191
FDE4                                        CDAC22              CALL PIXAD+2
FDE7                                        C9                  RET
[/code]

[center][img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/wordwrapv4.gif[/img][/center]

   ==========================

Here's [url=http://www.users.globalnet.co.uk/~jg27paw4/pourri/wordwrapv4.tap]a TAP file[/url] with na_th_an's demo updated to include the machine code wrapping routine and the lower screen patch. There's about 1k of unused space between the two routines, as I just started "linewrap" at a round number, so it could be reassembled higher up to save space.

   ==========================

It would be neater if the lower screen mod was inserted at the correct point, rather than being patched on to the end. As this would affect JP and CALL addresses within the PropSys routine it would be easiest to extract the assembler listing from the article, alter that and reassemble it.

At the same time I might as well move up the linewrap code so that the two routines plus the new font and width data are in one continuous chunk and do a new listing for the whole lot. I'll include three sets of font & width data as well: for the original, plus the new one, and I'll alter the one which I did for the other PrintPR routine to work with PropSys.

I'd better add some credits as well; I think that it was Paddy who did the new font & width data, Nathan did the word wrap demo, I did the linewrap routine, and of course there's Tony Samuels who did the original routine all those years ago.

   ==========================

I've consolidated the two routines together with the lower screen mod and the combined assembler listing and TAP file is [url=http://www.users.globalnet.co.uk/~jg27paw4/pourri/propwrap.zip]here[/url]. I've included the three fonts, although Paddy's is the best one as it's the easiest to read even though it's the most compact.

I've included a little BASIC program called "width" which takes a normal character set and converts it into a PropSys form with each character left justified and the appropriate header byte. Plus there's a line 9100 at the end of the demo program which will construct a 96-byte width table from a PropSys character set so that linewrap will work with it.

   ==========================

The POKEs initialise some of the variables defined at the start of the linewrap routine:
[code]
table:  equ  64072            ;address-32 of character widths table
ch_add: equ  23645
char:   defm "a$:"            ;63970
sadd:   defw $0000            ;63973(IX+0) absolute a$ start address
posn:   defw $0001            ;63975(IX+2) relative a$ current pointer
wi:     defb $00              ;63977(IX+4) current line width
wo:     defb $00              ;63978(IX+5) last full word line width
[/code]
Namely 'posn' to 1 and 'wi' to 0. When printing a string linewrap would be called several times in the loop at line 935, but 'posn' & 'wi' only need to be reset the first time, so it was just easier to do that externally.

The 'end' value returned by linewrap is the pointer for the last character to print from a$ on the current line of wrapped text (ie. the end of the last word which will fit on the line).

The PropSys-format character set (ie. each character left justified with its top byte as a width indicator) stays at the same place as in the original article; ie. 64200. That's all that's needed for using PropSys on its own. If using linewrap then the extra width data table is needed as well, which is at the same place as Nathan put it in his original BASIC demo; ie. 96 bytes down from the character set, at 64104.

   ==========================

A small change which I haven't uploaded.

[center][img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tnog_old.gif[/img]    [img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tnog_new.gif[/img][/center]

The difference - apart from it using the Tir Na Nog font - is that I've padded the lines to an extent. If there's more pixel space at the end of the line than there are words in the line then linewrap temporarily adjusts the width marker for "space" so that when PropSys prints the line it gets padded out.

If there's less than 8 pixels spare then it gets left at the end of the line, which is why I haven't uploaded it, as I really wanted it to be full justification between both margins, but I can't see an easy way to get PropSys to take up the slack.

   ==========================

I noticed that there's a few bytes that can be saved in PropSys thus:

* The "JP Z,PUTIT" can be changed to a JR.
* The "JP Z,PST" can be changed to a JR.
* If the ERR5 routine is moved up before "XPOS: DEFB 0" then the "JP NC,ERR5" further up can be changed to a JR.
* The "JR FCHR" immediately before the FCHR label can be removed.

I've worked out a fairly simple way of passing padding info from linewrap to PropSys so that wrapped text can be aligned to both margins. On each call to linewrap it saves a byte with the trailing blank pixel count, and then PropSys temporarily expands the width of a space by one pixel until it's used up all the trailing pixels.

   ==========================

Well, I got the right justification working reasonably well.

[center][img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tnog_old.gif[/img]    [img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tnog_newer.gif[/img][/center]

I've included the various small changes which have been suggested. There was a convenient 15 bytes spare between the end of the character set and the start of PropSys, so I was able to save another 14 bytes by moving the PropSys workspace into that gap, leaving about 10 bytes again between the end of the code and the start of the UDGs. The [url=http://www.users.globalnet.co.uk/~jg27paw4/pourri/propwrap.zip]propwrap.zip[/url] file now contains both the old and new versions.

To disable or re-enable the right justification in the new version, enter the relevant set of five POKEs below:

[code]
Disable
        XOR A                    ; 64059 175
        LD (IX+6), A             ; 64060 221 119 6
        RET                      ; 64063 201
Enable
        LD HL, 64200             ; 64059 33  200 250
        LD A, 255                ; 64062 62  255
[/code]

   ==========================

OK, having thought of it I had to do it, so here's a mod to linewrap which will take an optional parameter to toggle right justification off/on. This needs RAMTOP dropped by 50 and makes the linewrap CODE block 246 bytes. Options in the demo line 935 are:

[code]
 935 LET end=USR 63850 ...    no change
 935 LET end=0+USR 63850 ...  right justification off
 935 LET end=1+USR 63850 ...  right justification on
 935 LET end=flag+USR 63850 ...  right justification depends on 'flag'
[/code]

Any numeric variable can be used as the parameter, and any non-zero parameter will set right justification on.

[code]
lnwrap: LD BC, (STKBOT)  ; 63850 237 75  99  92
        LD HL, (STKEND)  ; 63854 42  101 92
        XOR A            ; 63857 175
        SBC HL, BC       ; 63858 237 66          check if parameter on stack
        PUSH AF          ; 63860 245             save flags
        JR Z, npatch     ; 63861 40  18          no change if no parameter
        CALL FPTOA       ; 63863 205 213 45
        OR A             ; 63866 183
        LD BC, 5         ; 63867 1   5   0
        LD DE, padchk    ; 63870 17  59  250
        LD HL, justno    ; 63873 33  139 249
        JR Z, cpatch     ; 63876 40  1           disable if parameter=0
        ADD HL, BC       ; 63878 9               enable otherwise
cpatch: LDIR             ; 63879 237 176
npatch: JR gowrap        ; 63881 24  27
justno: XOR A            ; 63883 175             Disable right justification
        JP nonspc        ; 63884 195 80  250
        NOP              ; 63887 0
juston: LD HL, 64200     ; 63888 33  200 250     Enable  right justification
        LD A, 255        ; 63891 62  255
...
        POP AF           ; 64083 241             get parameter flags
        RET Z            ; 64084 200             "end" in BC if stack empty
        CALL STAKBC      ; 64085 205 43  45      otherwise "end" on stack
        LD BC, 0         ; 64088 1   0   0       and reset BC
        RET              ; 64091 201
padspc: SRL (HL)         ; 64092 203 62          widen "space" by one pixel
        JR chkspc        ; 64094 24  232
[/code]

   ==========================

OK, I've got it now. The last version of [url=http://www.users.globalnet.co.uk/~jg27paw4/pourri/propwrap.zip]propwrap.zip[/url] which I uploaded a while ago included Timmy's change relating to this, but there are two factors to take into account.

1) That version is integrated with the linewrap code. The linewrap code, character data and PropSys code are in separate files, so you can:
LOAD "PropSys3" CODE 64969,388
on its own, and then POKE 65112 & 65113 & 65114,0 and POKE 65292,24 then this will give a version which doesn't make reference to the separate linewrap code and that can be SAVEd on its own.

2) Timmy's change only fixes it when the overlap is exactly on a 1/3rd boundary; ie. where the Y co-ord is 56 or 120 or 184; compare the results with Y=48 or 56 or 57. If the text spans two character rows (eg. where Y=57) then two rows of attributes will be affected, and there's nothing to be done about that. I'll have a look at the overlap into the next character column, as it should be possible to avoid that.

   ==========================

I've added the new version as PropSysATT in PropWrap.TAP in [url=http://www.users.globalnet.co.uk/~jg27paw4/pourri/propwrap.zip]propwrap.zip[/url]. This includes the attribute mod shown below, plus the various space saving changes which were later added to PropSys3. It will work with the original version of linewrap in the same TAP file, but is independent of it. The routine is CODE 64969,379, leaving 20 bytes spare before the UDGs.

I haven't included the character definitions. Just load the old version with its character set then load PropSysATT over the top and save back characters and code as CODE 64104,1244. This version of the routine is longer than the original but takes up less space when saved with the character definitions because, as well as some coding optimisation, the workspace was moved into an unused area between the character set and code.

Note that I removed the DEC DE just before label PST as DE isn't used any further in the loop and is re-assigned at the top. Also, PUSH/POP BC isn't needed as that's already been done elsewhere in the larger loop. I've tried numerous print tests at various key points and they all display correctly with no superfluous characters.

[code]
        ...
PUTIT:  LD   DE,(DFADD)
        LD   A,(DE)
        XOR  L
        LD   (DE),A
        CALL COLAD
        LD   A,(PIXPOS)
        AND  A
        JR   Z,PST

        ;attribute patch starts
        inc  e
        ld   a,e
        and  31
        jr   z,pst
	    ld   a, (XPOS)
	    or   248
	    ld   b, a
	    ld   a, (WIDTH)
        dec  a
        dec  a
	    add  a,b
	    jr   nc, pst
        ;attribute patch ends

        LD   A,(DE)
        XOR  H
        LD   (DE),A
        CALL COLAD
PST:    LD   HL,(DFADD)
        ...
[/code]


**********************************************************************


** Printing a Listing
   ==================

I've never had any problems listing programs from Fuse. Having checked "emulate printers" in peripheral options, LLIST/LPRINT output will be sent to files printout.txt & printout.pbm in 48k mode, or just printout.txt in 128k mode. The files will normally be created in the current directory (ie. the one most recently used to open or save a file), or appended if they already exist. Otherwise, they should be put in the startup directory specified in the shortcut created by the installer (in my case this is "Fuse/roms").

Remember that if using ZX printer emulation in 48k mode the print width will only be 32 characters, matching the screen LLIST format. In 128k mode the print width is 80 columns and double spaced (it prints 2 CR & 1 LF at the end of each line). Alternatively, BASin prints each numbered line of BASIC as one unbroken line.

Fuse is provided with a comprehensive (100kb) HTML manual; the Printer Emulation section is unsurprisingly relevant to this question. Perhaps the only thing which the manual doesn't explain is the name & location of the configuration file, which I discovered to be "fuse.cfg" in the "Documents and Settings" sub-folder for the installing user.

Another option for MSWindows is M.D.Wynne's SpecPrint, which will print directly (ie. to a real printer) BASIC listings from Z80, SNA, SP, ZX and P files. Options can be selected for character font & size, print width, keyword highlighting, vertical spacing and separating multi-statement lines.

   ==================

As regards the problem with LLIST in EightyOne, as well as selecting ZX Printer Output in Tools, the ZX Printer box in Hardware: Interfaces needs to be checked. This won't work in 128k mode, of course.

In the ZX Printer options panel, Carriage Speed needs to be boosted to at least 75 otherwise it will take ages to print anything. Even then, the emulated printer output can only be saved as a BMP file (as is also the case with ZXSpin) which isn't a lot of use except maybe for printing a paper copy from it.

This bit-mapped output is also what goes into Fuse's printout.pbm file, although in the case of that program "simulated text output is generated at the same time using a crude sort of OCR based on the current character set".


**********************************************************************


** Printing text at any position/size
   ==================================

Here's that logo plotting routine which I promised - a first draft, anyway -with a little test program to play around with the parameters. It recognizes 96 characters, 16 block graphics, 112 UDGs. To use more than 21 UDGs just define them after 'U' in the UDG data table and use CHR$ 165-255 to refer to them. The "RANDOMIZE USR 60212" is to set up the BLOCK pointer in 23680/1 as the routine encodes the block graphics rather than generating them ad hoc (the method employed by the ROM). This effectively provides another 16 UDGs as the BlockData table can have anything in it; it just starts off with the 16 block graphic definitions.

The parameters are fairly straightforward; the x,y coords locate the bottom left corner of the first character. The full screen is addressed, so 0,0 is at the extreme bottom left. Density is an inverse quantity, as it is the number of pixels shaved off each character, so the bigger the number the less chunky each character becomes. Scale specifies the x,y dimensions of the characters; an x,y scale of 40,30 is about the biggest that will fit (one character) on the screen.

The code is pretty robust. Some parameter combinations give -ve results (eg. if density is bigger than scale); these just won't print anything. If the characters come out too big to fit on the screen then they will be truncated vertically and wrap around horizontally. A scale of 1 and a density of 0 gives normal size (if chunky) characters. As x,y have separate scales the characters can be stretched along either axis independently. Text can be printed horizontally, vertically or at angles in between by including suitable steps on ox,oy in the "FOR a ..." loop.
[code]
 100 RANDOMIZE USR 60212
 180 INPUT "text "'c$
 181 INPUT "coord:x ";ox,"coord:y ";oy'
           "scale:x ";sx,"scale:y ";sy'
           "density ";ds
 185 FOR a=1 TO LEN c$:
       LET c=CODE c$(a):
       LET bc=ox=(oy=(sx=(sy=(ds=(c+USR 60000))))):
       LET ox=ox+sx+7:
     NEXT a
 189 BORDER 4: PAUSE 0: CLS : BORDER 1: GO TO 180
[/code]

http://www.users.globalnet.co.uk/~jg27paw4/pourri/chars_plot.asm


**********************************************************************
