How to use Timex high-resolution on Spectrum Next?

ZX80, ZX 81, ZX Spectrum, TS2068 and other clones
Post Reply
Stefan123
Member
Posts: 85
Joined: Fri Oct 21, 2016 7:57 am

How to use Timex high-resolution on Spectrum Next?

Post by Stefan123 »

I want to play with the Timex High-Resolution mode on ZX Spectrum Next. How do I enable the 64x24 characters (or 80x24 if available) font?

I have done the following configuration:

* Compiled using the zxn target and selecting crt 5.
* Enabled the Timex modes be setting bit 2 in Next register 8 (peripheral 3 settings).
* Enabled the Timex High-Resolution mode by writing 0x06 to port 0xFF.

When I run my test program and print some text, I get the 64x24 font but it looks like the Spectrum version, just in higher resolution. The characters are hard to read and I get a space character between each second character. What have I missed?

/Stefan
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

This is easy to answer - I haven't put it in yet :) I was side-tracked by the banked memory issues. The 64 column terminal in crt5 is indeed 64 columns in the spectrum 256x192 display using a 4x8 font.

There are display file manipulators for the timex modes either committed or on my hard drive but the scrolling functions still have to be completed. After that's done the terminals can go in. There is a library-build configuration that causes the display file to be accessed at 0xc000 instead of 0x4000 so that it will be possible to do a full ram compile with the program occupying address 0 - 48k. The introduction of mmu has made it possible to map the display file anywhere in memory but I think 0xc000 will be enough flexibility.

If you're working on this now, I can try to finish up the timex code sooner rather than later.
Stefan123
Member
Posts: 85
Joined: Fri Oct 21, 2016 7:57 am

Post by Stefan123 »

Hi Alvin,

Thanks for your super quick reply :) I'm dabbling with a text adventure game where I plan to use a mix of layer 2 for the images and Timex high-resolution mode for the text. If you have the time to add your Timex high-resolution support it would be great! Using a 64 or 80 columns font in high-resolution mode for a text adventure game in combination with layer 2 graphics seems like a perfect fit.

Cheers,
Stefan
Stefan123
Member
Posts: 85
Joined: Fri Oct 21, 2016 7:57 am

Post by Stefan123 »

I want to accomplish the following in my text adventure game: a one-row input terminal at the bottom of the screen, a 7 rows output terminal above the input terminal, and a layer 2 graphics area on the rest of the screen above the output terminal. Ideally, I would like the terminal area to be in Timex high resolution mode with 64 or more characters per row but for now I will use the standard Spectrum mode with a proportional font.

I found the two terminals example in z88dk and I have used that as a starting point to create my input and output terminals. I was pleasantly surprised to see z88dk has such sophisticated terminal handling :) I have the following questions:

1. What is the best way to limit the number of input characters in my one-row input terminal, i.e. I want to limit the input to exactly one row? I have found one solution and that is to set the edit buffer size in m4_zx_01_input_kbd_inkey() to, for example, 40 characters. But if you have a proportional font it is not ideal since you have to account for the widest character, e.g. 40 'x' characters may fill up one line but not 40 'i' characters.

2. In the output terminal, if the output doesn't fit in the terminal window, I would like a prompt message at the bottom row of the output terminal and when the user presses a key (any key or a certain key is fine), the rest of the output is scrolled in. Is this supported by z88dk's terminals? Does the output terminal need to be connected to stdin for this? If a scroll prompt would appear in the output terminal, which of the output terminal and input terminal has the keyboard focus in that case?

3. Is there any support for word wrapping in the output terminal? If not, I guess I can preprocess the text to be output, count characters and insert newline characters at the appropriate positions. This would work fine for a fixed-width font but not for a proportional font. Is there any support for calculating the width of a string to be output in a proportional font?

I have a basic version of the terminal and graphics part of the game running and it works fine in CSpect. However, when the output terminal scrolls in ZEsarUX, it crashes horribly. Since the two terminal example works in ZEsarUX, it could be something in my code.

/Stefan
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Stefan123 wrote:1. What is the best way to limit the number of input characters in my one-row input terminal, i.e. I want to limit the input to exactly one row? I have found one solution and that is to set the edit buffer size in m4_zx_01_input_kbd_inkey() to, for example, 40 characters. But if you have a proportional font it is not ideal since you have to account for the widest character, e.g. 40 'x' characters may fill up one line but not 40 'i' characters.
Yes the edit buffer size is one way.

There is only one way to stop the line editing right now and that's by intercepting the ITERM_MSG_INTERRUPT message in the driver ( https://github.com/z88dk/z88dk/blob/mas ... al.asm#L48 ). But the behaviour is not quite right - if you tell the driver to interrupt line editing what it does is immediately stop line editing and return the edit line as input. The is meant for ctrl-c, etc.

Being able to reject input characters is quite useful so I think I will add that to the drivers. This new message ITERM_MSG_REJECT will come and give the driver the opportunity to figure out if the character is acceptable or not. If rejected it will behave like the end of the edit buffer is reached (a beep and nothing else). So your driver would base rejection on the current x coordinate in the attached output terminal.

When your input driver runs (I'm talking about the driver because you will be subclassing it to add new behaviour) it will have this data structure pointed at by ix:
( https://github.com/z88dk/z88dk/blob/mas ... y.asm#L105 )

; offset (wrt FDSTRUCT.JP) description
;
; 8..13 mutex
; 14..15 FDSTRUCT *oterm (connected output terminal, 0 if none)
; 16 pending_char
; 17..18 read_index (index of next char to read from edit buffer)
; 19..24 b_array (manages edit buffer)
; 25 getk_state
; 26 getk_lastk
; 27 getk_debounce_ms
; 28..29 getk_repeatbegin_ms
; 30..31 getk_repeatperiod_ms

you'll grab the address of the attached output terminal's FDSTRUCT at offset 14/15 which will be the attached fzx terminal:
( https://github.com/z88dk/z88dk/blob/mas ... k.asm#L165 )

and from index 25/26 you can read the current edit x coordinate in pixels to decide if you're going to reject or not.

2. In the output terminal, if the output doesn't fit in the terminal window, I would like a prompt message at the bottom row of the output terminal and when the user presses a key (any key or a certain key is fine), the rest of the output is scrolled in. Is this supported by z88dk's terminals? Does the output terminal need to be connected to stdin for this? If a scroll prompt would appear in the output terminal, which of the output terminal and input terminal has the keyboard focus in that case?
Yes it's supported. A scroll message is generated in the output terminal driver when its screen is filled so it's independent of any input terminal. The default behaviour is to beep and wait for a keypress to continue. You can change this to do whatever you want by subclassing the driver and intercepting that message. You may have to do a little fiddling to make sure the message is generated before the bottom row so that you have someplace to print the prompt message. Or you could print the prompt message in a reserved space outside the terminal window. Or maybe you would overlap the bottom row with the prompt and then restore the text underneath after keypress.

When the fonts become vertically large the scrolling can seem jerky because the scroll happens all at once. ie if it needs to scroll 18 pixels, it scrolls by 18 pixels and not in stages. This behaviour can also be changed to make the scroll simpler. I have experimented a little with this with varying results (if the area is large, you see tearing, etc) but if the cpu is fast, it should be smooth to do one pixel scrolling.
3. Is there any support for word wrapping in the output terminal? If not, I guess I can preprocess the text to be output, count characters and insert newline characters at the appropriate positions. This would work fine for a fixed-width font but not for a proportional font. Is there any support for calculating the width of a string to be output in a proportional font?
No there is nothing for word wrapping or justification in the drivers yet. However, fzx does have subroutines to measure text size in pixels and break up strings into units that will fit a horizontal space.

#include <font/fzx.h>
( https://github.com/z88dk/z88dk/blob/mas ... font/fzx.h )

In particular ( https://github.com/z88dk/z88dk/tree/mas ... nt/fzx/z80 ) check out fzx_buffer_partition_ww().


I did write some code in this vein some time ago. This short demo shows a subclass of the fzx output driver that tries to do pixel scrolling in the window. The code has a sizable delay in it of 60000T which I think was an attempt to position the tear someplace but it should probably just be removed. If you speed up the cpu in an emulator you can see how it improves. The demo also has a word wrap function which you can check out. Maybe it can be done better.

https://drive.google.com/file/d/0B6XhJJ ... sp=sharing
I have a basic version of the terminal and graphics part of the game running and it works fine in CSpect. However, when the output terminal scrolls in ZEsarUX, it crashes horribly. Since the two terminal example works in ZEsarUX, it could be something in my code.
It could be something in zesarux too if you are doing anything with layer 2.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

The ITERM_MSG_REJECT message has been added to the input terminal. What will happen is the driver will send an ITERM_MSG_REJECT message for each character typed on the edit line. This will give you the option of rejecting it, in which case the same behaviour as trying to add to a full edit buffer happens (the default is beep & ignore).

The drivers are object oriented with the methods invoked by sending messages which are registers set to method parameters and the A register containing a message id. If you intercept messages you can change the terminal behaviour; unintercepted messages are forwarded to the base class to get the default behaviour.

So for the input terminal where you want to stop the user from entering more characters after the end of the line on screen is reached, you would intercept the ITERM_MSG_REJECT message, check the attached output terminal to see what the x coord is and then reject the character if the x coord is too big.

For the output terminal where you want to do something else when the screen fills, you would intercept the OTERM_MSG_PAUSE message in the output terminal, print your prompt, wait for key and return.

===

The input terminal you are using is zx_01_input_kbd_inkey ( https://github.com/z88dk/z88dk/blob/mas ... _inkey.asm ) which subclasses the library driver ( https://github.com/z88dk/z88dk/blob/mas ... al.asm#L48 ) which has documentation on the types of messages it sends to the driver.

To subclass zx_01_input_kbd_inkey, you write a new driver that passes unintercepted messages to zx_01_input_kbd_inkey. You can use zx_01_input_kbd_inkey.asm as a guide.

Code: Select all

( copy other information from zx_01_input_kbd_inkey.asm as it won't change)
( below is the data structure ix points to when your driver runs, you're not changing anything so it's not different)

; ;;;;;;;;;;;;;;;;;;;;;;;;;;
; BYTES RESERVED IN FDSTRUCT
; ;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; offset (wrt FDSTRUCT.JP)  description
;
;    8..13                  mutex
;   14..15                  FDSTRUCT *oterm (connected output terminal, 0 if none)
;   16                      pending_char
;   17..18                  read_index (index of next char to read from edit buffer)
;   19..24                  b_array (manages edit buffer)
;   25                      getk_state
;   26                      getk_lastk
;   27                      getk_debounce_ms
;   28..29                  getk_repeatbegin_ms
;   30..31                  getk_repeatperiod_ms

SECTION code_driver
SECTION code_driver_terminal_input

PUBLIC my_01_input_kbd_inkey

EXTERN ITERM_MSG_REJECT
EXTERN zx_01_input_kbd_inkey

my_01_input_kbd_inkey:

   cp ITERM_MSG_REJECT
   jp nz, zx_01_input_kbd_inkey   ;; let the parent class handle other messages

iterm_msg_reject:

   ; * ITERM_MSG_REJECT
   ;
   ;   Indicate whether typed character should be rejected.
   ;
   ;   enter:  c = ascii code
   ;    exit:  carry reset indicates the character should be rejected.
   ; can use:  af, bc, de, hl

   ld l,(ix+14)
   ld h,(ix+15)    ; hl = FDSTRUCT *output_terminal (an fzx terminal)

   ld a,h
   or l
   scf
   ret z          ; if there is no output terminal return indicating char is accepted

   ld de,35+3    ; current x pixel coord is at offset 35 in the fzx driver + 3 more because the oterm points at a lock
                     ; ( https://github.com/z88dk/z88dk/blob/master/libsrc/_DEVELOPMENT/target/zx/driver/terminal/zx_01_output_fzx.asm#L193 )
   add hl,de

   ld a,(hl)
   inc hl
   ld h,(hl)
   ld l,a

   ; hl = current x coord in pixels
   ; this is relative the left edge of the window, not necessarily absolute screen coord (add paper.x for that)

   ld de,500    ; x coord limit?
   sbc hl,bc    ; set carry flag if x < 500

   ret
Besides the actual driver you also need the m4 instantiator that sets up the library's data structures for the driver. Just copy the m4 file for zx_01_input_kbd_inkey ( https://github.com/z88dk/z88dk/blob/mas ... d_inkey.m4 ). You're not changing the data structure but you are changing the driver address. Change lines 90 and 112 to point at this new driver "my_01_input_kbd_inkey".

See the link I gave before that shows how a subclassed driver was added to the compile. It's similar to what you're doing with the two terminals thing but you're using the custom m4 file here to do the instantiation.

Similar changes for the output fzx driver would be done to intercept the pause message.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Instead of using an absolute coord like 500 as the x coord limit (and for spectrum screen it should be something like 240 instead), using the paper.width at offset 41 ( https://github.com/z88dk/z88dk/blob/mas ... x.asm#L215 ) to find out the width of the fzx terminal to calculate a rightmost margin would make the code window-size independent.
Stefan123
Member
Posts: 85
Joined: Fri Oct 21, 2016 7:57 am

Post by Stefan123 »

Hi Alvin,

Thanks for your help. Today, I had time to digest all your technical information. I used your example code but I had to change the "ld de,500" statement to "ld bc,240" to get it to work. I had to singlestep in CSpect to realize that the DE register pair was a typo in "ld de,500" since the following instruction is a "sbc hl,bc".

I also tried to make the driver generic and use the paper.width instead of an hard-coded x limit and it seems to work fine. My Z80 assembly knowledge is quite limited and everytime I write some Z80 assembly I make the mistake to use the registers more freely than the instructions allow... However, below is my take of the driver. Can you take a quick look and see if I do anything stupid?

Code: Select all

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; zx_01_input_kbd_inkey_custom ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This input terminal driver subclasses the zx_01_input_kbd_inkey driver.
; It is intended to be connected to an fzx output terminal and limits the
; input to one line.
;
; ;;;;;;;;;;;;;;;;;;;;
; DRIVER CLASS DIAGRAM
; ;;;;;;;;;;;;;;;;;;;;
;
; CONSOLE_01_INPUT_TERMINAL (root, abstract)
; ZX_01_INPUT_KBD_INKEY (concrete)
; ZX_01_INPUT_KBD_INKEY_CUSTOM (concrete)

SECTION code_driver
SECTION code_driver_terminal_input

PUBLIC zx_01_input_kbd_inkey_custom

EXTERN ITERM_MSG_REJECT
EXTERN zx_01_input_kbd_inkey

zx_01_input_kbd_inkey_custom:

   ; let parent class handle other messages than ITERM_MSG_REJECT
   cp ITERM_MSG_REJECT
   jp nz, zx_01_input_kbd_inkey

iterm_msg_reject:

   ; ITERM_MSG_REJECT:
   ; Indicate whether typed character should be rejected.
   ;   enter: ix = FDSTRUCT.JP *input_terminal
   ;          c = ascii code
   ;    exit: carry reset indicates the character should be rejected.
   ; can use: af, bc, de, hl

   ; hl = FDSTRUCT *output_terminal (an fzx output terminal)
   ; at offset 14 in FDSTRUCT.JP *input_terminal
   ld l,(ix+14)
   ld h,(ix+15)

   ; if there is no output terminal return indicating char is accepted
   ld a,h
   or l
   scf
   ret z

   ; paper.width is at offset 41 in the fzx driver + 3 more because
   ; the output_terminal points at a lock, see zx_01_output_fzx.asm.
   ; bc = paper.width - 8 as the x limit. The maximal width of a
   ; character is assumed to be 8 pixels.

   ld bc,41+3
   add hl,bc

   ld a,(hl)
   inc hl
   ld h,(hl)
   ld l,a

   ld bc,8
   sbc hl,bc

   ld b,h
   ld c,l

   ; hl = FDSTRUCT *output_terminal
   ld l,(ix+14)
   ld h,(ix+15)

   ; current x pixel coordinate is at offset 35 + 3 in the fzx driver
   ; hl = current x pixel coordinate (relative the left edge of the window)
   ld de,35+3
   add hl,de

   ld a,(hl)
   inc hl
   ld h,(hl)
   ld l,a

   ; set carry flag if current x pixel coordinate < paper.width
   sbc hl,bc

   ret
I have also written a word wrapping function using fzx_string_partition_ww() and strtok() that word wraps a string to fit the width of a specified window but respects any existing newlines in the string. I also had to write a function for stripping any carriage returns from text files read using esxdos_f_read(). If you read a text file using fread(), does it automatically normalize the read text to only contain newline characters?

Now I will struggle to write a customized fzx output terminal driver that prints a prompt message in the input terminal when scrolling is needed. Since the input terminal is not used while scrolling the output terminal, I thought it would be a good place to put a "<MORE>" message. I guess this driver will be a bit more complicated since I need to call fprintf() or similar in the driver to write in the input terminal. Is there any limitations on calling functions from within an output driver?
Stefan123
Member
Posts: 85
Joined: Fri Oct 21, 2016 7:57 am

Post by Stefan123 »

There is one more thing I'm not sure how to do. When the user presses a key to scroll the output terminal, I want to consume that character so that it's not written in the input terminal after the scrolling is done.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I used your example code but I had to change the "ld de,500" statement to "ld bc,240" to get it to work. I had to singlestep in CSpect to realize that the DE register pair was a typo in "ld de,500" since the following instruction is a "sbc hl,bc".
Sorry about that. I changed my mind to use de instead of bc but missed changing the subtraction. By using de, bc is not used at all so c still carries the ascii code in case that ever comes in handy if the subroutine would be changed.
There is one more thing I'm not sure how to do. When the user presses a key to scroll the output terminal, I want to consume that character so that it's not written in the input terminal after the scrolling is done.
This code will be running inside the output terminal so I would just wait for keypress by reading the keyboard directly like this:

Code: Select all

EXTERN asm_in_wait_nokey, asm_in_wait_key

call asm_in_wait_nokey
call asm_in_wait_key
call asm_in_wait_nokey
These functions are the same as the c ones in the input library and you can see the asm implementation in this directory:
https://github.com/z88dk/z88dk/tree/mas ... put/zx/z80
They only modify af.

The first call will wait until the user is not pressing a key in case he's pressing one already. Then you wait for a keypress. Then you wait for the key to be released so that it is not read as a keypress by the input driver when you return.
I also tried to make the driver generic and use the paper.width instead of an hard-coded x limit and it seems to work fine. My Z80 assembly knowledge is quite limited and everytime I write some Z80 assembly I make the mistake to use the registers more freely than the instructions allow... However, below is my take of the driver. Can you take a quick look and see if I do anything stupid?
The driver looks good!
I have also written a word wrapping function using fzx_string_partition_ww() and strtok() that word wraps a string to fit the width of a specified window but respects any existing newlines in the string.
Nice. Word wrapping and some text effects like bold, italic and underline is probably something that could be added to the standard drivers.
I also had to write a function for stripping any carriage returns from text files read using esxdos_f_read(). If you read a text file using fread(), does it automatically normalize the read text to only contain newline characters?
If it went through stdio, it would do this for you automatically. But stdio is not ready for disk io so the esxdos functions are independent and you are talking to esxdos directly. There's no text mode open so esxdos will always give exactly what's on disk and if that's CRLF that's what you will get. But you can do a dos2unix on the text files you are reading to change line endings to LF only ('\n') - this is what is expected on the code side in z88dk anyway.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I just thought of something else. You may want to unconditionally accept '\n' in your reject routine so that the user can press enter with a full text line:

Code: Select all

iterm_msg_reject:

   ; ITERM_MSG_REJECT:
   ; Indicate whether typed character should be rejected.
   ;   enter: ix = FDSTRUCT.JP *input_terminal
   ;          c = ascii code
   ;    exit: carry reset indicates the character should be rejected.
   ; can use: af, bc, de, hl

   ld a,c
   cp '\n'
   scf
   ret z        ; unconditionally accept enter

   ...
Now I will struggle to write a customized fzx output terminal driver that prints a prompt message in the input terminal when scrolling is needed. Since the input terminal is not used while scrolling the output terminal, I thought it would be a good place to put a "<MORE>" message. I guess this driver will be a bit more complicated since I need to call fprintf() or similar in the driver to write in the input terminal. Is there any limitations on calling functions from within an output driver?
You'll do the same thing as above, subclassing zx_01_output_fzx ( https://github.com/z88dk/z88dk/blob/mas ... ut_fzx.asm ) - or the tty version if you need cursor control, colours, etc - and intercepting the OTERM_MSG_PAUSE message ( https://github.com/z88dk/z88dk/blob/mas ... zx.asm#L93 ).

When your output terminal processes this message the associated input terminal is locked and your terminal is also locked so nothing can send these terminals any messages. But if you're printing "<MORE>" you're either printing in your output terminal or in another output terminal. It's safe to send a print message to another output terminal because it won't be locked. But you don't have to do it this way - you could write the "<MORE>" message directly to screen if you have a place to put it. You can save the area under the message to a buffer, print the message, wait for key, then restore the area under the message to erase it. If you have text under the message, it will be restored. If you have a dedicated area then you can just clear the area to erase the message. The functions in the library to save/restore an area on screen already - you'd just need a buffer to save the stored area into which you can declare in your driver. The "<MORE>" message itself could just be a bitmap too.

If you let me know which way you want to go, I can help with that too.
Stefan123
Member
Posts: 85
Joined: Fri Oct 21, 2016 7:57 am

Post by Stefan123 »

Hi Alvin,

Thanks for the tip on using asm_in_wait_nokey and asm_in_wait_key and always accepting enter, that completes the input terminal :)

Sending a message from the output terminal being scrolled (containing the room description in my case) to the input/output terminal at the bottom of the screen (where the user enters input) to print a "<MORE>" message in it sounds like a really nice solution. I tried to figure out myself how the message sending mechanism works but I couldn't find it in the source code. Is it the STDIO_MSG_WRIT that I should send using the l_jpix function?
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Stefan123 wrote:Sending a message from the output terminal being scrolled (containing the room description in my case) to the input/output terminal at the bottom of the screen (where the user enters input) to print a "<MORE>" message in it sounds like a really nice solution. I tried to figure out myself how the message sending mechanism works but I couldn't find it in the source code. Is it the STDIO_MSG_WRIT that I should send using the l_jpix function?
Yes that's one way to do it. The messages sent by stdio are all here:

https://github.com/z88dk/z88dk/blob/mas ... me.txt#L90

To send a string (or rather a buffer) you could use STDIO_MSG_WRIT. Sending a message entails loading up the registers as indicated but also having IX point at the FILE* or FDSTRUCT* associated with the output terminal. While you're serving the OTERM_MSG_PAUSE message you're only allowed to modify af,bc,de,hl so you'll have to save other registers around the message.

So something like:

Code: Select all

; push all registers (can also push them yourself)
; see https://github.com/z88dk/z88dk/blob/master/libsrc/_DEVELOPMENT/im2/z80/asm_im2_push_registers.asm

   EXTERN _WINDOW_FP, STDIO_MSG_WRIT, l_jpix
   EXTERN asm_im2_push_registers, asm_im2_pop_registers

   call asm_im2_push_registers  ;; push all registers

IF __SDCC_IY
   ld iy,(_WINDOW_FP)  ;; FILE * for the destination output terminal
ELSE
   ld ix,(_WINDOW_FP)  ;; FILE * for the destination output terminal
ENDIF

   ld a,STDIO_MSG_WRIT

   ld bc,buffer_len
   ld hl,buffer
   exx
   ld hl,buffer_len

   call l_jpix   ;; send the message (will be swapped to iy for SDCC_IY already because it's in the library)
   ;; or use l_call_ix / l_call_iy to use the named registers (see https://github.com/z88dk/z88dk/tree/master/libsrc/_DEVELOPMENT/l/z80 )

   call asm_im2_pop_registers  ;; pop all registers
I keep forgetting something important -- for SDCC_IY compiles, the library is using iy instead of ix. So either you use iy and compile with SDCC_IY or you put IFs in there as above. The l_jpix routine is in the library so for SDCC_IY compiles it actually jumps to iy. If this is confusing you can use the l_call_ix / l_call_iy routines instead which will always jump using the named ix or iy.

This affects the input driver as well -- all references to (ix+d) are actually (iy+d) in a SDCC_IY compile. It's annoying but the IX/IY swap is necessary for some targets and we wouldn't normally use SDCC_IY otherwise except that it leads to much better code.


You don't have to operate at this level, you can go one higher and use fprintf:
https://github.com/z88dk/z88dk/blob/mas ... tf.asm#L29

This is a vararg function so it expects its arguments on the stack just like a c call.

Code: Select all

;; fprintf(WINDOW_FP, "<MORE>")

   EXTERN _WINDOW_FP, asm_fprintf
   EXTERN asm_im2_push_registers, asm_im2_pop_registers

   call asm_im2_push_registers  ;; push all registers

IF __SDCC

   ;; sdcc is right to left

   ld hl,message   ; c string
   push hl
   ld hl,(_WINDOW_FP)  ; FILE*
   push hl

ELSE

   ;; sccz80 is left to right

   ld hl,(_WINDOW_FP)  ; FILE*
   push hl
   ld hl,message   ; c string
   push hl

ENDIF

   call asm_fprintf
   pop af
   pop af

   call asm_im2_pop_registers  ;; pop all registers
And you can go one lower using the file descriptor and asm_write:
https://github.com/z88dk/z88dk/blob/mas ... _write.asm

which you can see just sets up a STDIO_MSG_WRIT for the driver.


You may also want to clear the screen in the input area before printing the more message. If the output terminal is a tty_z88dk type, you can send 0x0c as part of the string to clear the terminal's screen (see https://github.com/z88dk/z88dk/blob/mas ... le.asm#L22 ). If it's not a tty terminal you can do the same by sending an IOCTL message.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

The printf family is using a subroutine to send a STDIO_MSG_WRITE:
https://github.com/z88dk/z88dk/blob/mas ... t.asm#L119

If you save registers, set up ix/iy, hl, bc, call __stdio_send_output_buffer, restore registers then this function can send the STDIO_MSG_WRIT message for you. Normally the number of chars output by printf are tallied in hl' but you can ignore that bit. You'll be restoring hl' to its correct value when you restore registers.
Stefan123
Member
Posts: 85
Joined: Fri Oct 21, 2016 7:57 am

Post by Stefan123 »

Hi Alvin,

My customized output terminal driver ended up looking like this:

Code: Select all

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ZX_01_OUTPUT_FZX_CUSTOM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; This output terminal driver subclasses the zx_01_output_fzx driver.
; It overrides the scroll pause handling by printing a scroll prompt
; message in another output terminal, waiting for a key press and
; clearing the scroll prompt message.
;
; ;;;;;;;;;;;;;;;;;;;;
; DRIVER CLASS DIAGRAM
; ;;;;;;;;;;;;;;;;;;;;
;
; CONSOLE_01_OUTPUT_TERMINAL (root, abstract)
; CONSOLE_01_OUTPUT_TERMINAL_FZX (abstract)
; ZX_01_OUTPUT_FZX (concrete)
; ZX_01_OUTPUT_FZX_CUSTOM (concrete)

SECTION code_driver
SECTION code_driver_terminal_output

PUBLIC zx_01_output_fzx_custom

EXTERN zx_01_output_fzx
EXTERN asm_im2_push_registers, asm_im2_pop_registers
EXTERN asm_in_wait_nokey, asm_in_wait_key
EXTERN l_jpix

EXTERN OTERM_MSG_PAUSE
EXTERN STDIO_MSG_WRIT, STDIO_MSG_ICTL
EXTERN IOCTL_OTERM_CLS
EXTERN _window_1 ; prompt message window

zx_01_output_fzx_custom:

   ; let parent class handle other messages than OTERM_MSG_PAUSE
   cp OTERM_MSG_PAUSE
   jp nz, zx_01_output_fzx

oterm_msg_pause:

   ; OTERM_MSG_PAUSE:
   ; The scroll count has reached zero so the driver should pause the output somehow.
   ;
   ; can use: af, bc, de, hl

   ; STDIO_MSG_WRIT:
   ; Write a buffer to the stream.
   ;
   ; input:
   ;  IX = FDSTRUCT *output_terminal
   ;  A  = STDIO_MSG_WRIT
   ; BC' = buffer length > 0
   ; HL' = void *buffer
   ; HL  = buffer length > 0

   ; STDIO_MSG_ICTL:
   ; IOCTL message delivery to driver.
   ;
   ; input:
   ;  IX = FDSTRUCT *output_terminal
   ;   A = STDIO_MSG_ICTL
   ;  BC = first parameter
   ;  DE = ioctl request
   ;  HL = void *arg

   call asm_im2_push_registers

   ; send message STDIO_MSG_WRIT to other output terminal to print the scroll prompt
IF __SDCC_IY
   ld iy,(_window_1)
ELSE
   ld ix,(_window_1)
ENDIF
   ld a,STDIO_MSG_WRIT
   ld bc,6
   ld hl,scroll_prompt
   exx
   ld hl,6
   call l_jpix

   ; wait for a key press
   call asm_in_wait_nokey
   call asm_in_wait_key
   call asm_in_wait_nokey

   ; send message STDIO_MSG_ICTL with request IOCTL_OTERM_CLS to other output terminal to clear it
IF __SDCC_IY
   ld iy,(_window_1)
ELSE
   ld ix,(_window_1)
ENDIF
   ld a,STDIO_MSG_ICTL
   ld bc,0
   ld de,IOCTL_OTERM_CLS
   ld hl,1
   call l_jpix

   call asm_im2_pop_registers

   ret

scroll_prompt:
   defm "<MORE>"
There is one detail I don't understand. I have to set HL to 1 (or anything else != 0) when sending the STDIO_MSG_ICTL message. I thought HL was the varargs parameter in ioctl() and NULL (0) seemed more logical.

One other detail that I don't understand are the values 0x2330 (output terminal connected to input terminal) and 0x2370 (output terminal with scrolling) for the ioctl_flags paramater in m4_zx_01_output_fzx(). I simply borrowed these values from other z88dk examples but I haven't been able to figure out the individual ioctl flags they are made up of.

Thanks to your help, I'm now done with the input/output terminal handling and I think they work really good. All in all, it was a fun experience fiddling with the terminal drivers and I really appreciate their object-oriented design.

Hopefully, I can reuse most of my code when the support for the Timex high-resolution mode is added to the zxn target.

Cheers,
Stefan
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Stefan123 wrote:One other detail that I don't understand are the values 0x2330 (output terminal connected to input terminal) and 0x2370 (output terminal with scrolling) for the ioctl_flags paramater in m4_zx_01_output_fzx(). I simply borrowed these values from other z88dk examples but I haven't been able to figure out the individual ioctl flags they are made up of.
The flag bits are described here:
https://github.com/z88dk/z88dk/pull/458 ... -337115644

0x2370 is going to set the terminal pause bit which will cause the output terminal to pause when the screen is filled. 0x2330 will just allow the terminal to continue scrolling.
There is one detail I don't understand. I have to set HL to 1 (or anything else != 0) when sending the STDIO_MSG_ICTL message. I thought HL was the varargs parameter in ioctl() and NULL (0) seemed more logical.
Yes HL is the vararg pointer into the stack for further ioctl parameters if they are present. The library is using HL=0 to distinguish a special case of ioctls - the ones that modify the ioctl_flag bits that every driver has. These include the 0x2330 and 0x2370 flag bits that you mention above. Because every driver has these bits, the library provides the code for all drivers to manipulate those bits so that every drivers' ioctl handler doesn't have to implement the bit twiddling on those bits. Only ioctls that do not twiddle those bits will have a vararg pointer.

For this message:

Code: Select all

   ld a,STDIO_MSG_ICTL
   ld bc,0
   ld de,IOCTL_OTERM_CLS
   ld hl,1
   call l_jpix
HL !=0 is going to indicate this is not the ioctl_flags being twiddled and therefore the library should not automatically handle it ("ld l,e; ld h,d" would be a byte shorter and a couple cycles faster). BC contains the first parameter for the ioctl. In the case of IOCTL_OTERM_CLS there are no parameters so you could omit the "ld bc,0".

The second:

Code: Select all

IF __SDCC_IY
   ld iy,(_window_1)
ELSE
   ld ix,(_window_1)
ENDIF
can be omitted too because ix/iy will not be changed by any message passing or the wait key functions.
Thanks to your help, I'm now done with the input/output terminal handling and I think they work really good. All in all, it was a fun experience fiddling with the terminal drivers and I really appreciate their object-oriented design.

Hopefully, I can reuse most of my code when the support for the Timex high-resolution mode is added to the zxn target.
Glad to hear. The object oriented nature builds in a lot of flexibility and when the timex hi res terminals go in, you should be able to use the same code.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

The timex hi-res proportional font driver was added on startup=24 tonight. The tty on top and dot command versions still need to be finished.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I should mention you have to enable 512x192 mode for the terminal to be properly visible on screen:

Code: Select all

//   ZXN_NEXTREG(0x08, 0x14);

        zx_border(INK_YELLOW);
        ts_vmod(TVM_HIRES | PAPER_YELLOW);
//   IO_FF = 6;
The nextreg (zxn.h) is required by the next to enable timex video modes. The new ts_vmod function (zxn.h) accepts a mode flag and for 512x192 mode, a paper colour. The hw automatically chooses to most contrasting ink colour.

cpsect only accepts a value of 6 to enable the hi-res mode which corresponds to white ink on black paper. To get cspect to display hi-res, I write to port $ff directly instead of going through ts_vmod.
Stefan123
Member
Posts: 85
Joined: Fri Oct 21, 2016 7:57 am

Post by Stefan123 »

Great news, Alvin! :-) I will try it out tomorrow.
Stefan123
Member
Posts: 85
Joined: Fri Oct 21, 2016 7:57 am

Post by Stefan123 »

I just switched to the Timex hi-res proportional font driver and it works perfect :)

There are quite many FZX fonts to choose from, which font do you recommend? I want a font that is pleasable to read in Timex hi-res mode without too much serifs. Right now, I'm using the Prefect font and it's quite alright.

It's a shame that Sinclair didn't borrow more features from the Timex Sinclar 2068 when they designed the Spectrum 128. The Timex video modes would have been really nice to have in 1986. I wonder if the cooperation between Sinclar and Timex had ended when Spectrum 128 was designed or if it was just Sinclair that lacked ambition in this case? Tasword would have been a joy to use in 512 x 192. By the way, a while a go I tried to find TS2068 games that used the Timex high-color mode but I didn't find any. Do you know of any Timex high-color games, preferably developed during the 80s?
Stefan123
Member
Posts: 85
Joined: Fri Oct 21, 2016 7:57 am

Post by Stefan123 »

I have another quick question: Is there any hidden support for making the font glyphs double width to get a better aspect ratio?

Another question just popped up: Does the input terminal drivers support line editing, i.e. use the left and right arrows to move the cursor? I realize I'm asking for a lot here but I just want to make sure that I haven't missed to enable this feature.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Stefan123 wrote:There are quite many FZX fonts to choose from, which font do you recommend? I want a font that is pleasable to read in Timex hi-res mode without too much serifs. Right now, I'm using the Prefect font and it's quite alright.
I'm not terribly familiar with them either and the quality varies as you'd expect.

There is a windows editor "fzx editor" that you can use to look at the fonts in libsrc/_DEVELOPMENT/font/fzx/fonts
http://www.zx-modules.de/

For a readable small font I've found ff_ao_Twombly is pretty good.
1986. I wonder if the cooperation between Sinclar and Timex had ended when Spectrum 128 was designed or if it was just Sinclair that lacked ambition in this case? Tasword would have been a joy to use in 512 x 192.
Probably a lack of ambition at Sinclair - they'd pretty much moved on and were thinking QL. One fellow who worked at Timex mentioned that Sinclair was totally uninterested in what they were doing. I'm not sure the 128 would have even been made if Investronica wasn't bugging them about it.

For word processing I used MSCRIPT which was a 512x192 program.
By the way, a while a go I tried to find TS2068 games that used the Timex high-color mode but I didn't find any. Do you know of any Timex high-color games, preferably developed during the 80s?
Unfortunately I'm not aware of any either. There were some art programs but that's about it. The few professional level developers most often aimed for spectrum compatibility.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Stefan123 wrote:I have another quick question: Is there any hidden support for making the font glyphs double width to get a better aspect ratio?
No but I've thought of this too.
Another question just popped up: Does the input terminal drivers support line editing, i.e. use the left and right arrows to move the cursor? I realize I'm asking for a lot here but I just want to make sure that I haven't missed to enable this feature.
Unfortunately no but it is something that would be nice to have.
Post Reply