Compact 64 column print routine

The place for codemasters or beginners to talk about programming any language for the Spectrum.
Post Reply
User avatar
djnzx48
Microbot
Posts: 195
Joined: Wed Dec 06, 2017 2:13 am
Location: New Zealand

Compact 64 column print routine

Post by djnzx48 » Sun Feb 18, 2018 8:59 pm

Hello everyone, this is my first post on these forums. :)

I started learning Z80 assembly several years ago but I didn't really do anything with it apart from a few flashy screen effects. So now I've decided to come back to it and attempt to make an actual game, starting with this text routine. It's optimized to be as small as possible (34/35 bytes), with the font using 7 bytes for every pair of characters. I thought I'd share it here in the hope that others might find it useful, so feel free to use it, modify it, whatever.

And I just wanted to say thanks for all the hard work that goes into maintaining ZXDB and this site, it's really appreciated :)

Here's a quick demo of the routine. It's not really this slow, :) I just put in a few HALTs so you can see it properly in action.

Image

Code: Select all

; Prints up to 4 lines of text using a 4x8 character font.
;
; IX = points to one beyond last character of string
; HL = screen address to print at
; C  = number of characters to print, maximum 256 (0)
;
; Notes: The left edge of the string is always aligned
; with a character block. If you want a string to start
; halfway along a block, it must begin with a space.
;
; The font is stored with two graphic rows packed into
; a byte, with each of 7 rows stored on a separate
; 256-byte page.
;
; preserves: IY, alternate registers

charloop:
    ld d, font_hi   ; construct font address
    push hl         ; save screen pos

    ld a, c         ; number of chars left to print
    dec a           ; we want to round downwards
    or a            ; clear carry for scroll
    rra             ; divide by two (64 chars to 32)
    add a, l        ; calculate new screen address
    ld l, a

    ld b, 7         ; loop 7 rows

    dec ix          ; read string backwards

if 1
    ld e, (ix+0)    ; get character
    srl e           ; check value of LSB
else
    db $DD, $CB, $00, $3B   ; ld e, srl (ix+0)
                    ; saves one byte and 4 Ts
                    ; Only use for temporary strings
                    ; (string becomes corrupted)
endif

rowloop:
    inc h           ; next screen line (skipping first row)
    ld a, (de)      ; get graphic
    rrd             ; rotate onto screen
    jr c, nextrow   ; done printing?
    rrd             ; rotate again into A
    ld (hl), a      ; display on screen
nextrow:
    inc d           ; next graphic
    djnz rowloop    ; loop for all rows

nextchar:
    pop hl          ; restore screen pos

    dec c           ; any more chars?
    jr nz, charloop ; loop back round
    ret             ; done
2 x

User avatar
Einar Saukas
Manic Miner
Posts: 522
Joined: Wed Nov 15, 2017 2:48 pm

Re: Compact 64 column print routine

Post by Einar Saukas » Sun Feb 18, 2018 10:13 pm

djnzx48 wrote:
Sun Feb 18, 2018 8:59 pm
Hello everyone, this is my first post on these forums. :)
Welcome to this forum!

djnzx48 wrote:
Sun Feb 18, 2018 8:59 pm
I started learning Z80 assembly several years ago but I didn't really do anything with it apart from a few flashy screen effects. So now I've decided to come back to it and attempt to make an actual game, starting with this text routine. It's optimized to be as small as possible (34/35 bytes), with the font using 7 bytes for every pair of characters. I thought I'd share it here in the hope that others might find it useful, so feel free to use it, modify it, whatever.
Nice!

Your idea is similar to 64#4, except you saved several bytes by avoiding those bells and whistles. :)

djnzx48 wrote:
Sun Feb 18, 2018 8:59 pm
And I just wanted to say thanks for all the hard work that goes into maintaining ZXDB and this site, it's really appreciated :)
Thank you very much! Likewise, your support is really appreciated. :)
0 x

User avatar
djnzx48
Microbot
Posts: 195
Joined: Wed Dec 06, 2017 2:13 am
Location: New Zealand

Re: Compact 64 column print routine

Post by djnzx48 » Sun Feb 18, 2018 10:51 pm

Einar Saukas wrote:
Sun Feb 18, 2018 10:13 pm
djnzx48 wrote:
Sun Feb 18, 2018 8:59 pm
I started learning Z80 assembly several years ago but I didn't really do anything with it apart from a few flashy screen effects. So now I've decided to come back to it and attempt to make an actual game, starting with this text routine. It's optimized to be as small as possible (34/35 bytes), with the font using 7 bytes for every pair of characters. I thought I'd share it here in the hope that others might find it useful, so feel free to use it, modify it, whatever.
Nice!

Your idea is similar to 64#4, except you saved several bytes by avoiding those bells and whistles. :)
Thanks for replying, your routine looks very nice :) My routine is purposely made to be simple and restrictive, as I only needed something to print single lines at a time, and I didn't need to use it from BASIC.
A major advantage of your code is that the font is all packed in one place, which is more convenient than having 'holes' in the data. It makes calculating the font address slightly more complicated though.
0 x

User avatar
utz
Berk
Posts: 49
Joined: Wed Nov 15, 2017 9:04 am
Contact:

Re: Compact 64 column print routine

Post by utz » Tue Feb 20, 2018 10:20 am

Woohoo, 35 bytes! Always great to see compact routines like this. Perhaps it could be tweaked for more efficient storage of the font though? I don't mind so much the wasted bits per character pair, but spreading out over 7 256b pages seems worth optimizing. Nevertheless, this is quite an achievement ;)
0 x

User avatar
djnzx48
Microbot
Posts: 195
Joined: Wed Dec 06, 2017 2:13 am
Location: New Zealand

Re: Compact 64 column print routine

Post by djnzx48 » Tue Feb 20, 2018 11:02 pm

utz wrote:
Tue Feb 20, 2018 10:20 am
Woohoo, 35 bytes! Always great to see compact routines like this. Perhaps it could be tweaked for more efficient storage of the font though? I don't mind so much the wasted bits per character pair, but spreading out over 7 256b pages seems worth optimizing. Nevertheless, this is quite an achievement ;)
Thanks :) It wouldn't be too difficult to modify it so that the font is all in one place, although it would need some extra code to multiply the character byte by 7 and add it to the base address. I personally don't think it's worth it at this stage; for now I'll try to fit some other code or data to fill up the gaps.

I noticed that only a few of the characters in my current font have hanging tails that extend to the bottom, mostly lowercase ones like g, j, p, q, and y. If those tails were removed the font could be made smaller - only 6 bytes per pair of characters or 3 bytes for a single character. Or it could even be made to use 7px high graphics for lowercase and 6px high for uppercase letters.
0 x

Kweepa
Microbot
Posts: 172
Joined: Sat Feb 03, 2018 6:14 pm

Re: Compact 64 column print routine

Post by Kweepa » Tue Feb 20, 2018 11:15 pm

In a proportional font routine I wrote (on the VIC - boo!) I rendered the letters with descenders lower than the others, so they are the same height in the font. Just requires a lookup table for y offset.
0 x

User avatar
djnzx48
Microbot
Posts: 195
Joined: Wed Dec 06, 2017 2:13 am
Location: New Zealand

Re: Compact 64 column print routine

Post by djnzx48 » Wed Feb 21, 2018 9:05 am

Hmm, that seems like a good idea. I don't think a lookup table would be appropriate in this case, as the extra memory needed for the code and the table itself would outweigh saving 40 bytes or so in the font data. Maybe a hardcoded test or CP instruction would work though. Or the character set could be arranged differently so that similar height characters are grouped together. I'll have a closer look at the font later and try it out.
0 x

User avatar
utz
Berk
Posts: 49
Joined: Wed Nov 15, 2017 9:04 am
Contact:

Re: Compact 64 column print routine

Post by utz » Wed Feb 21, 2018 12:46 pm

A tiny optimization: Near the top of charloop, replacing "dec a, or a" with "sub 1" will save 1 t-state.

Edit: Ah, nevermind, it will break on entering with C=0.
0 x

Kweepa
Microbot
Posts: 172
Joined: Sat Feb 03, 2018 6:14 pm

Re: Compact 64 column print routine

Post by Kweepa » Wed Feb 21, 2018 2:23 pm

Back to the descenders - you're right, a look up table is expensive.
I forgot that I dropped the descenders 2 pixels, so it would still be cheaper than adding two pixels to each character in the font.
https://www.youtube.com/watch?v=KEm8pDkFJt4
I also forgot that I just checked for a descender like this:

Code: Select all

static int hasdescender(char c)
{
   char pet = c + 'a' - 97;
   if (pet == 'g' || pet == 'p' || pet == 'q' || pet == 'y')
   {
      return 1;
   }
   return 0;
}
:oops:
0 x

User avatar
djnzx48
Microbot
Posts: 195
Joined: Wed Dec 06, 2017 2:13 am
Location: New Zealand

Re: Compact 64 column print routine

Post by djnzx48 » Wed Feb 21, 2018 10:43 pm

utz wrote:
Wed Feb 21, 2018 12:46 pm
A tiny optimization: Near the top of charloop, replacing "dec a, or a" with "sub 1" will save 1 t-state.

Edit: Ah, nevermind, it will break on entering with C=0.
Nice! The C=0 case doesn't matter for me as I'll only be printing single lines at a time, so that I can do word wrapping on them. The routine was never intended to handle multiple lines of text, the fact that it does is just a bonus ;)
Kweepa wrote:
Wed Feb 21, 2018 2:23 pm
Back to the descenders - you're right, a look up table is expensive.
I forgot that I dropped the descenders 2 pixels, so it would still be cheaper than adding two pixels to each character in the font.
https://www.youtube.com/watch?v=KEm8pDkFJt4
I also forgot that I just checked for a descender like this:

Code: Select all

static int hasdescender(char c)
{
   char pet = c + 'a' - 97;
   if (pet == 'g' || pet == 'p' || pet == 'q' || pet == 'y')
   {
      return 1;
   }
   return 0;
}
Wow, nice looking game! I didn't realise that you were the author of the VIC-20 Manic Miner port either, good work :)

Your suggestion was a good one. I found that if all characters are 6 characters in height, a lookup table can be made using 1 bit per character to indicate whether it is higher or lower. With 96 characters, the table will only take 12 bytes. The code is 21 bytes larger (although it could probably be optimised), but since 48 bytes are saved by making the font data smaller, there is a net saving of 15 bytes.

Currently the table layout suffers from the same problem as the font data; there are gaps in it as the entries are all 8 bytes apart. It could be easily fixed with three RRAs though. Also some letters in my font like Q and j are 7 pixels high, so they'll have to be made smaller somehow.

Here's the new code (untested, 56 bytes):

Code: Select all

; Prints up to 4 lines of text using a 4x8 character font.
;
; IX = points to one beyond last character of string
; HL = screen address to print at
; C  = number of characters to print, maximum 255
;
; Notes: The left edge of the string is always aligned
; with a character block. If you want a string to start
; halfway along a block, it must begin with a space.
;
; The font is stored with two graphic rows packed into
; a byte, with each of 6 rows stored on a separate
; 256-byte page.
;
; preserves: IY, alternate registers

charloop:
    ld d, font_hi   ; construct font address
    push hl         ; save screen pos
    
    dec ix          ; read string backwards
    ld e, (ix+0)    ; get character
    ld a, e
    and %00111000   ; get LSB for lookup table
    
    ld h, font_height_lookup
    ld l, a
    
    ld a, e
    and %00000111   ; get number of bit to retrieve
    ld b, a
    inc b           ; get in range 1-8
    ld a, (hl)      ; get lookup table byte
    
    pop hl          ; restore screen pos
    push hl         ; save it again
    
bitloop:
    add a, a        ; shift until we get the desired bit
    djnz bitloop
    
    jr nc, skip_inc ; check value of bit
    
    inc h           ; next screen line
    inc d           ; next font byte
    
skip_inc:

    ld a, c         ; number of chars left to print
    sub 1           ; we want to round downwards
    rra             ; divide by two (64 chars to 32)
    add a, l        ; calculate new screen address
    ld l, a

    ld b, 6         ; loop 6 rows

    srl e           ; check value of LSB

rowloop:
    inc h           ; next screen line (skipping first row)
    ld a, (de)      ; get graphic
    rrd             ; rotate onto screen
    jr c, nextrow   ; done printing?
    rrd             ; rotate again into A
    ld (hl), a      ; display on screen
nextrow:
    inc d           ; next graphic
    djnz rowloop    ; loop for all rows

nextchar:
    pop hl          ; restore screen pos

    dec c           ; any more chars?
    jr nz, charloop ; loop back round
    ret             ; done
0 x

Post Reply