putchar and backspace

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

putchar and backspace

Post by Stefan123 »

How does z88dk handle backspace in putchar()?

On most systems, the following statements will print the string "Fox" but in z88dk it prints "Foo?x":

putchar('F');
putchar('o');
putchar('o');
putchar('\b');
putchar('x');
putchar('\n');

The reason I'm asking is that I'm experimenting with porting the Magnetic Scrolls interpreter Magnetic to Spectrum Next. The Magnetic interpreter is a little bit odd in that in can emit backspace characters '\b' to the output function in order to make small corrections to the last printed characters. This seems to be used for things like deleting the last ',' character in an enumeration, e.g. "You have a, b, and c" is corrected to "You have a, b and c".

I saw that the backspace character seems to get special treatment in the console output drivers in z88dk (e.g. console_01_output_fzx_iterm_msg_bs.asm) but I don't know exactly how this relates to putchar(). Maybe it's only handled for input and not output?
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

The input stream and output stream is separate. As you've figured out "console_01_output_fzx_iterm_msg_bs" is for backspace during line editing.

For the basic terminal the output stream is almost uninterpretted. It will deal with the bell (ctrl-G) and CRLF conversion if those flags are enabled and they are by default.
(The library's putc code is https://github.com/z88dk/z88dk/blob/mas ... g_putc.asm where the TTY message will be ignored by a basic terminal because it is unimplemented. The zx-specific putc code gets the ascii code and turns anything less than 32 into ?)

The *_tty_z88dk terminals interpret some escape sequences:
https://github.com/z88dk/z88dk/tree/mas ... _tty_z88dk
where you can see code 8 (backspace) gets turned into move left when printed.

For the fixed char width terminals you get a pair of drivers like:

zn_01_output_char_32
zx_01_output_char_32_tty_z88dk

with the first one not doing control codes and the second one doing them. This last one will do backspace properly when printed.

The fzx terminals are more difficult because the fonts are not usually fixed width. The 256x192 version of the fzx terminal does have a tty_z88dk version but the 512x192 timex hires does not yet. It doesn't take long to do; I just haven't got around to it yet.

zx_01_output_fzx
zx_01_output_fzx_tty_z88dk
tshr_01_output_fzx

But for the fzx_tty_z88dk terminal, the cursor movements are in terms of 8x8 character positions. So a backspace first computes the current x,y char position corresponding to the fzx pixel x,y position and moves left to the previous 8x8 character cell.

What you really want to do with fzx backspace is move left to overwrite the previous character printed which might be variable width. So to move left, you need to know what that character is. You can change the fzx terminal to do this if you override the backspace code but you're going to have to pass the ascii code of the char to the left of the current position to do it.
Stefan123
Member
Posts: 85
Joined: Fri Oct 21, 2016 7:57 am

Post by Stefan123 »

Thanks for your good explanation :)

It sounds a bit tricky to support backspace in the fzx tty output drivers. I guess the tty versions of the drivers are a bit larger in code size as well. It's quite a challenge to get the Magnetic interpreter to fit between 0x7800 and 0xFFFF. I'm using the bottom 16K for paging in the current part of the Magnetic story file (containing the acual game code). The Magnetic interpreter is a strange beast, it's essentially an interpreter / virtual machine for a subset of the Motorola 68K instruction set with some added custom instructions... A strange design decision in hindsight compared to the higher-level languages used by Infocom (z-code) and Level 9 (A-code).

For now, I'm thinking of doing my own buffering in the output function called by the Magnetic interpreter and handle backspace there.

By the way, is it safe to use the memory area 0x5800 - 0x6000 (between the two Times hi-res display buffers)? The C library in z88dk does not try to read/write any system variables normally located in this area?

Is it possible to use Next's raster line interrupts instead of having a big and clunky IM2 interrupt vector table in order to have code executed at the end of a frame? I have read the limited info available on raster line interrupts but I don't understand how you connect your interrupt handler code to the raster line interrupt you have specified...
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

For now, I'm thinking of doing my own buffering in the output function called by the Magnetic interpreter and handle backspace there.
That's probably the best solution.
By the way, is it safe to use the memory area 0x5800 - 0x6000 (between the two Times hi-res display buffers)? The C library in z88dk does not try to read/write any system variables normally located in this area?
Yes it's safe there. The library is completely independent of the rom (and system vars) except for startup=30 which uses rst$10 to print. For the latter, system variables will be used. esxdos calls should not affect the system variables area except for the new MP3DOS hook that calls a NextOS function from esxdos. The NextOS API does use the system variables area.

There is a library build time configuration "__USE_SYSVAR":
https://github.com/z88dk/z88dk/blob/mas ... get.m4#L43
If it is made non-zero then some banking related variables will be mapped onto system variables. Eg, GLOBAL_ZX_PORT_7FFD will be mapped on top of system variable BANKM because they serve the same purpose (they store the last out to port 7ffd).
Is it possible to use Next's raster line interrupts instead of having a big and clunky IM2 interrupt vector table in order to have code executed at the end of a frame? I have read the limited info available on raster line interrupts but I don't understand how you connect your interrupt handler code to the raster line interrupt you have specified...
Unfortunately the big clunky im2 table does not go away. The ula causes an interrupt on line 248 (see https://github.com/z88dk/z88dk/blob/mas ... er.m4#L18) and it works like on the standard spectrum by asserting /INT low for something like 35 cycles. The line interrupt works the same way. If set it will generate an interrupt at the start of line x by doing the same thing: /INT is pulled low for 35 cycles. So your interrupt routine can't know which caused the interrupt; you must poll the active video line to find out where the raster is (https://github.com/z88dk/z88dk/blob/mas ... eg.m4#L305) to know if the ula caused it.

Of course you can disable the ula interrupt and use only the line interrupt. Then your isr knows where the raster is without polling and you can place the line interrupt at a more convenient location than vsync.
Timmy
Well known member
Posts: 393
Joined: Sat Mar 10, 2012 4:18 pm

Post by Timmy »

This is part of my (very old) code for my game Any Treasure Day, the fzx'd version.

The other functions are in a different file and I can't find it quickly, so I skipped them for now. print_prop is just a simple call to the fzx function, if i remember correctly.

I did it a bit differently by just clearing the whole line and reprint it instead... Then you don't need to calculate its length and it will flicker, but still good enough for now.
Note that in this case I chose a fzx font with 8 pixel height so I don't need to deal with much scrolling... :)

Code: Select all

void clearbottomline()
// ROM scroll
{
        #asm
        ld b,2
        call 0e44h
        #endasm
}

#define INPUT_SIZE 42
char input_line[INPUT_SIZE];
char input_index;

void print_input()
{
        clearbottomline();
        printatx(6);
        printprop("> ");
        printprop(input_line);
}

void get_input()
// returns the line in the char array called input_line.
{
        uchar ch;
        uchar x;

        setattr(6);
        input_index=0;
        input_line[0]='\0';
        print_input();
        printprop("_");

        do
        {
                ch = get_ch();
                if ((ch>=32) && (input_index<INPUT_SIZE))
                {
                        input_line[input_index]=ch;
                        input_index++;
                        input_line[input_index]='\0';
                        print_input();
                        printprop("_");
                }
                else
                if ( (ch==12) && (input_index) )  // if DELETE key is pressed and the string is not empty.
                {
                        input_index--;
                        input_line[input_index]='\0';
                        print_input();
                        printprop("_");
                }

        } while ((ch!=13) && (input_index<INPUT_SIZE));

        input_index = 0;
        
        print_input(); 

        setattr(7);
        printprop("\n"); scroll();
        scroll();
}
Stefan123
Member
Posts: 85
Joined: Fri Oct 21, 2016 7:57 am

Post by Stefan123 »

Ok, good that z88dk doesn't use the system variables area (except when configured to do so). Just to be sure, z88dk doesn't use the LASTK system variable for keyboard handling either? By the way, how does keyboard/joystick/mouse input handling work in z88dk? The keyboard works fine without explicitly enabling IM2 mode. Does z88dk have its own internal interrupt handler for these input devices or does it read the input hardware directly without interrupts or does the interrupt-driven input functions in z88dk (if there are any) depend on the programmer to update certain z88dk variables in the IM2 isr?

Aha, so the Next line interrupt mechanism is connected to the IM2 isr. I couldn't find any information about that at specnext.com. I thought there were some Next register (that I had missed) where you registered the address of the isr that should be called when a line interrupt occurred. That would have been a nicer solution since it would not require the overhead of the IM2 interrupt vector table :)

Timmy, thanks for sharing your code. I have a buffered output solution working now but it's always nice to compare with other people's code. I saw a video on your adventure game on youtube, looking good :)
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Stefan123 wrote:Ok, good that z88dk doesn't use the system variables area (except when configured to do so). Just to be sure, z88dk doesn't use the LASTK system variable for keyboard handling either?
In the input terminals there is a LASTK keyboard driver available but none of the terminals use it - they all use the in_inkey driver which scans the keyboard directly and implements key repeat, etc in a busy loop:

https://github.com/z88dk/z88dk/blob/mas ... g_getc.asm

So it will work even if interrupts are disabled and it has much better keyboard response than a 50Hz scan would allow.
By the way, how does keyboard/joystick/mouse input handling work in z88dk? The keyboard works fine without explicitly enabling IM2 mode. Does z88dk have its own internal interrupt handler for these input devices or does it read the input hardware directly without interrupts or does the interrupt-driven input functions in z88dk (if there are any) depend on the programmer to update certain z88dk variables in the IM2 isr?
All the keyboard functions in input.h (in_inkey, etc) scan the instantaneous state of the keyboard directly. There's no state retained so there's no key repeat, etc. The terminals are based on in_inkey but implement there own state machine locally to do the key repeat features.

The joystick and mouse functions are also a direct hardware scan. Kempston mouse provides current pointer coord so you don't need to constantly read it to maintain a current position. The amx mouse driver installs interrupts as that device is im2 interrupt driven.

No interrupt code is set up unless you set it up, with the exception of the nirvana colour engines, etc, which must have their draw functions run first thing in an interrupt. The newlib starts programs with interrupts disabled by default in fact.
Post Reply