Page 1 of 2

Trying Steven McDonald zx81 Pseudo Hires routines

Posted: Thu Feb 27, 2020 3:04 am
by zx81ultra
Hello,

I tried to implement Steven McDonald zx81 Pseudo Hires routines (http://www.pictureviewerpro.com/hosting ... ighres.txt, my assembly knowledge is very basic and maybe I have mistakes somewhere ?

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <input.h>
#include <zx81.h>

int hr_d_file @26431; // 0x673f 

void SetPseudoHiRes(void)
{
#asm

hires:                halt
                        ld a,(04034h)
                        ld c,a
sync1:          ld a,(04034h)
                        cp c            
                        jr z, sync1
                        ld a,c
                        ld i,a
                        ld ix,hresgen
                        ret

hresgen:        ld hl,0e71eh
                        ld de,0021
                        di
                        ld c,0feh
                        ld b,16
sync3:                djnz sync3
                        ld b,0c0h
genline:        in a,(c)
                        out (0ffh),a
                        add hl,de
                        call ulaout
                        dec b
                        jp nz, genline
                        call 0292
                        call 0220
                        ld ix,hresgen

                        jp 02a4h
ulaout:                jp (hl)

#endasm
}

void SetLoRes(void)
{
#asm

lores:                halt
                        ld a,(04034h)
                        ld c,a
sync2:          ld a,(04034h)
                        cp c
                        jr z, sync2
                        ld a,01eh
                        ld i,a
                        ld ix,0281
                        ret
#endasm
}

main()
{
int i,j;
char *Ptr = (char *) hr_d_file;

SetPseudoHiRes(); // a bizarre image pattern is displayed, I guess it's in hires

for(i=0; i <= 31;++i)
        for(j=0; j <= 191;++j)
                *(Ptr + i + j*33) = 0; // nothing happens on the display

in_WaitForKey();
SetLoRes();
}
I get this image after calling the hires routine:

https://drive.google.com/open?id=1iIxh_ ... exlhCP6790

The new pseudo hires d_file should be located at 0x673f but nothing happens when I write into the hires display file.

Thank you !

Posted: Thu Feb 27, 2020 9:50 pm
by stefano
uh ..you're going to be in a real jam !
First of all let's explain the ix register: on the ZX81 it points to the video subroutine (as for your routine). It is reached with a call located in ROM, so it must be IX and can't be changed.
Sadly it is also the mostly used index register for z88dk, so to permit a reasonable compatibility I chose to add an option to Z80ASM and, once everything has been built, to swap IX and IY everywhere.. this means that to really use IX in your code you must write IY.

Also IY can't be used easily, it is even used by the display driver itself to quickly reach a system variable, so all the existing display drivers, including the one used to generate normal text in slow mode are replaced by something not touching IY. The sync timing counters were corrected to deal with the different code length: you could have to do the same.

Then you have Sync2, jumping to the normal ROM display subroutine, which should be replaced with a jump to the modified one:
https://github.com/z88dk/z88dk/blob/mas ... t_core.asm
.. this piece of code in z88dk must be modified to make the address of label L281 public, to be reached by your driver. Note the entries already exposed for the G007 interface and the reference to IY, meaning IX after the swap operated by the compiler.

you are calling other ROM locations, the should be checked too

... I'm sure I'm forgetting something, but I think is it a good picture to put you in the right direction. You can look at the e existing drivers in the lib/zx81/classic folder, and/or put yours in the same position. The crt0 file includes the driver files depending on the value given to the "startup=" compiler variable

Posted: Thu Feb 27, 2020 10:37 pm
by zx81ultra
Thank you Stefano !

Definitely, I'm already in deep trouble ! I think I got too ambitious and it is clear that my assembler capabilities and knowledge of the zx81 video system and the crt are too limited for now.

I just finished coding a game for the zx81 16k with z88dk in text mode and I wanted to see how difficult it would be to port it to pseudo hires before releasing it to the community but I couldn't find "ready to use" routines and then I tried to almost "copy&paste" Steven McDonald and Wilf code... shortcuts are a dangerous way of life :)

I'll keep trying.

Thanks again.

Posted: Thu Feb 27, 2020 11:24 pm
by stefano
honestly, I like the code you pasted and assembled
being a simplification shows clearly what needs to be done to start porting that driver.

that one needs also tricks to build the actual picture, though

Posted: Fri Feb 28, 2020 4:47 pm
by stefano
I'm willing to try and fix this routine.
A missing thing here is the video page data structure rules:
.Number of rows and their length
.Row termination code used (halt, ret, nothing?)
.heading and tail data (I see 21h bytes on top?)

I think the display data can be relocated, but it could be necessary to keep it in the first 16k ram range

Posted: Fri Feb 28, 2020 4:51 pm
by stefano
Found. It is described in the link you posted (blush).
I'm off for the weekend, will continue the next week.

Posted: Fri Feb 28, 2020 7:23 pm
by zx81ultra

Posted: Mon Mar 02, 2020 11:54 am
by stefano
Here is my rework of your proof of concept ;)

You need to expand the display data area by repeating 48 times the 4 rows I left in "_hr_d_file", then compile it as a normal non-graphics application.

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <input.h>
#include <zx81.h>

//int hr_d_file @26431; // 0x673f


extern char *hr_d_file;

#asm
_hr_d_file:
        
        defb        11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,201
        defb        11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,201
        defb        11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,201
        defb        11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,201

    ; (repeat 48 times)


#endasm

void SetPseudoHiRes(void)
{
        // "zx_slow" is not strictly necessary, by default we're already in "z88dk slow mode".
        //zx_slow();
        
#asm

                EXTERN                L0229
                EXTERN                L0292

hires:      halt
            ld a,(04034h)
            ld c,a
sync1:      ld a,(04034h)
            cp c
            jr z, sync1
            
                        ;ld a,c                        ; ?? I don't know why it initialized I in this way...
                        
                        ld        a,12                ; FONT ptr in ROM at address: 12 * 256 = 3072
            ld i,a
            ld iy,hresgen
            ret

hresgen:
            ;; ld hl,0e71eh
                        ld hl,_hr_d_file+$8000-33
            ld de,33
            di
            ld c,0feh
                        
            ;ld b,$16        ; ORIGINAL timing
            
                        nop                        ;  z88dk ADJUSTED timing
                        nop
                        nop
                        ld b,$15
                        
sync3:        djnz sync3

            ld b,192
genline:    in a,(c)                ; trick the ULA and reset the..
            out (0ffh),a        ; ..character ROW counter
            add hl,de
                        call ulaout
            dec b
            jp nz, genline        ; display 192 rows
            call L0292
            call L0220
            ld iy,hresgen        ; re-set IX to point to hresgen
            jp 02a4h

ulaout:        jp (hl)                ; 'execute' the display file at its shadowed position (the given row + $8000)

L0220:                
        PUSH    AF              ;
        PUSH    BC              ;
        PUSH    DE              ;
        PUSH    HL              ;
        JP      L0229           ; to DISPLAY-1   (was JR, equivalent T states, I think)

#endasm
}


int SetLoRes()
{
  zx_slow();                // Restore text mode subroutine
#asm
  ld a,$1e                        ; Standard ROM font
  ld i,a
#endasm
}


int main()
{
int i,j;
//char *Ptr = (char *) hr_d_file;

SetPseudoHiRes(); // pre-loaded picture, but hr_d_file is an array which can be edited ! 

in_WaitForKey();

SetLoRes();

}
I adjusted the timing experimentally on EightyOne, this progam MUST be tested on real ZX81 computers connected to different TV sets before being confident of the TV frame stability !!

I think it is possible to work at a reduced size version, which could leave more spare time for the CPU and save memory, but setting the correct timing will require a relevant work of tuning on the real hardware.
If you think it could be useful, we must first agree on which is the most interesting display size (96 rows?).

Posted: Mon Mar 02, 2020 11:58 am
by stefano
I forgot to say that you need to update the file in {z88dk}/lib/target/zx81/classic/zx81_altint_core.asm

I had to export one more entry point (I already pushed it z88dk, it will be available after the next 'nightly' build).

PUBLIC L0229

Posted: Mon Mar 02, 2020 3:26 pm
by zx81ultra
Great ! Can't wait to test it, I'll report how it goes.

Posted: Mon Mar 02, 2020 3:57 pm
by stefano
Slightly corrected. I must have left some dirt in the comments, it didn't compile anymore !

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <input.h>
#include <zx81.h>

//int hr_d_file @26431; // 0x673f


extern char *hr_d_file;

#asm
_hr_d_file:
        
        defb        11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,201
        defb        11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,11,11+128,201
        defb        11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,201
        defb        11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,201


    ; (must be repeated 48 times)


#endasm

void SetPseudoHiRes(void)
{
        // "zx_slow" is not strictly necessary, by default we're already in "z88dk slow mode".
        //zx_slow();
        
#asm

                EXTERN                L0229
                EXTERN                L0292

hires:      halt
            ld a,(04034h)
            ld c,a
sync1:      ld a,(04034h)
            cp c
            jr z, sync1
            
                        ;ld a,c                        ;  the article by Steve Mc Donald suggests another value
                        
                        ld        a,12                ; FONT ptr in ROM at address: 12 * 256 = 3072
            ld i,a
            ld iy,hresgen
            ret

hresgen:
            ;; ld hl,0e71eh
                        ld hl,_hr_d_file+$8000-33
            ld de,33
            di
            ld c,0feh
                        
            ;ld b,$16        ; ORIGINAL timing
            
                        nop                        ;  z88dk ADJUSTED timing
                        nop
                        nop
                        ld b,$15
                        
sync3:        djnz sync3

            ld b,192
genline:    in a,(c)                ; trick the ULA and reset the..
            out (0ffh),a        ; ..character ROW counter
            add hl,de
                        call ulaout
            dec b
            jp nz, genline        ; display 192 rows
            call L0292
            call L0220
            ld iy,hresgen        ; re-set IX to point to hresgen
            jp 02a4h

ulaout:        jp (hl)                ; 'execute' the display file at its shadowed position (the given row + $8000)

L0220:                
        PUSH    AF              ;
        PUSH    BC              ;
        PUSH    DE              ;
        PUSH    HL              ;
        JP      L0229           ; to DISPLAY-1   (was JR, equivalent T states, I think)

#endasm
}


void SetLoRes(void)
{
  zx_slow();                // Restore text mode subroutine

#asm
  ld a,$1e                        ; Standard ROM font
  ld i,a
#endasm

}


int main(void)
{
//char *Ptr = (char *) hr_d_file;

SetPseudoHiRes(); // pre-loaded picture, but hr_d_file is an array which can be edited ! 

in_WaitForKey();

SetLoRes();

}

Posted: Mon Mar 02, 2020 5:37 pm
by zx81ultra
It works fine ! I did a quick test, I'll dig deeper tonight.

https://drive.google.com/open?id=1iIbgM ... eQqsdbG_lu

Thanks !

Posted: Mon Mar 02, 2020 7:22 pm
by stefano
You've been warned, I tuned it on EightyOne,
A real TV set might behave differently.

Just in case, try also by commenting out the lines an the timing set to $15 and try also $16, no extra NOPs.
I saw a misalignment on the first row, but perhaps a real zx81 likes that sync.

Posted: Tue Mar 03, 2020 3:04 pm
by siggi
stefano wrote:You've been warned, I tuned it on EightyOne,
A real TV set might behave differently.
And a real ZX81 might also behave differently!

Don't forget, that the region in ROM, pointed by I, might contain different patterns, because nowadays there are a lot of new (BIGBANG) or patched (for LOAD/SAVE from USB or SD) roms in use (instead of the Sinclair rom). So these ZX81 might show a different or distorted picture ....

Siggi

Posted: Tue Mar 03, 2020 3:58 pm
by stefano
Yup, Siggi did a LOT of experiments to help me testing the WRX engine for z88dk on different TV configuration.
Thank you again and again for that!

now.. for those feeling adventurous, the following variant accepts a dynamic number of ROWS ! It accepts a parameter at compile time, in theory biasing the bottom display delay for the range 1..192 (e.g. -DHRROWS=96).
Needless to say that is should be extensively tested on real HW stuff, ideally both PAL and NTSC (the difference is managed, but it wasn't tested).

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <input.h>
#include <zx81.h>

//int hr_d_file @26431; // 0x673f


extern char *hr_d_file;

#asm
_hr_d_file:
    
    defb   (see above)


#endasm


void SetPseudoHiRes(void)
{
    // "zx_slow" is not strictly necessary, by default we're already in "z88dk slow mode".
    //zx_slow();
    
#asm

        EXTERN        L0229
        EXTERN        L0292
                EXTERN        MTCH_P3

hires:      halt
            ld a,(04034h)
            ld c,a
sync1:      ld a,(04034h)
            cp c
            jr z, sync1
            
            ;ld a,c            ;  the article by Steve Mc Donald suggests another value
            
            ld    a,12        ; FONT ptr in ROM at address: 12 * 256 = 3072
            ld i,a
            ld iy,hresgen


IF (HRROWS=192)
                        ; 0
ELSE

;IF (HRROWS=64)
;            ld        a,70                ; 64
;ELSE
;            ld        a,60                ; 96
;ENDIF
            ld  a,90-(HRROWS*5/16)
            ld        (MTCH_P3+1),a        ; patch also our custom interrupt handler for 64 rows

ENDIF

            ret

hresgen:
            ;; ld hl,0e71eh
            ld hl,_hr_d_file+$8000-33
            ld de,33
            di
            ld c,0feh
            
            ;ld b,$16    ; ORIGINAL timing
            
            nop            ;  z88dk ADJUSTED timing
            nop
            nop
            ld b,$15
            
sync3:        djnz sync3

            ld b,HRROWS
genline:    in a,(c)        ; trick the ULA and reset the..
            out (0ffh),a    ; ..character ROW counter
            add hl,de
            call ulaout
            dec b           ; row count
            jp nz, genline  ; loop
            call L0292
            call L0220
            ld iy,hresgen    ; re-set IX to point to hresgen
            jp 02a4h



ulaout:        jp (hl)        ; 'execute' the display file at its shadowed position (the given row + $8000)

L0220:        
        PUSH    AF              ;
        PUSH    BC              ;
        PUSH    DE              ;
        PUSH    HL              ;
        JP      L0229           ; to DISPLAY-1   (was JR, equivalent T states, I think)

#endasm
}


void SetLoRes(void)
{
  zx_slow();        // Restore text mode subroutine

#asm
  ld a,$1e            ; Standard ROM font
  ld i,a
#endasm

}


int main(void)
{
//char *Ptr = (char *) hr_d_file;

SetPseudoHiRes(); // pre-loaded picture, but hr_d_file is an array which can be edited ! 

in_WaitForKey();

SetLoRes();
}

Posted: Tue Mar 03, 2020 5:07 pm
by stefano
Better timing expression, on EO it now works on a range between 1 and 200 :)
Emulated TV Sync looks more effective when the videomode switch happens


IF (HRROWS>191)
; 0
ELSE
ld a,96-(HRROWS*6/12)
ld (MTCH_P3+1),a ; patch also our custom interrupt handler for 64 rows
ENDIF

Posted: Tue Mar 03, 2020 5:08 pm
by stefano
Note that I'm reusing the entry point I've setup for the Memotech HRG ;)

Posted: Tue Mar 03, 2020 8:38 pm
by zx81ultra
Impressive work, thank you all !

In order to start doing some tests, I've compiled a table with all valid codes for the HR D_FILE when pointing to the ROM font 3072 address, maybe this exists already (I hope not) but I couldn't find anything, there are 84 unique codes available:

Code: Select all

HR D_FILE
Valid Values        Pixels                 Dec        Hex
0                                01001111        79        4F
1                                00001110        14        E
2                                00100010        34        22
3                                00001001        9        9
4                                01000000        64        40
5                                00001010        10        A
6                                01011111        95        5F
7                                01000101        69        45
8                                01001100        76        4C
9                                00000001        1        1
10                                11011110        222        DE
11                                00000000        0        0
12                                00000110        6        6
13                                00101110        46        2E
15                                11000011        195        C3
18                                01111100        124        7C
22                                00001111        15        F
23                                00101100        44        2C
25                                11111111        255        FF
26                                11111110        254        FE
27                                00001101        13        D
29                                11100001        225        E1
30                                01001110        78        4E
31                                00100011        35        23
34                                11011111        223        DF
35                                01010011        83        53
36                                11000000        192        C0
38                                00011101        29        1D
39                                01000110        70        46
40                                00110110        54        36
41                                11001110        206        CE
42                                11111101        253        FD
43                                10100110        166        A6
44                                10110110        182        B6
45                                00010010        18        12
46                                11001101        205        CD
47                                01010110        86        56
48                                01111010        122        7A
49                                11110101        245        F5
50                                00011000        24        18
51                                01110110        118        76
54                                11101111        239        EF
55                                00001100        12        C
58                                11100000        224        E0
62                                11101011        235        EB
128                                10110000        176        B0
129                                11110001        241        F1
130                                11011101        221        DD
131                                11110110        246        F6
132                                10111111        191        BF
134                                10100000        160        A0
135                                10111010        186        BA
136                                10110011        179        B3
138                                00100001        33        21
140                                11111001        249        F9
141                                11010001        209        D1
143                                00111100        60        3C
146                                10000001        129        81
150                                11110000        240        F0
151                                11010011        211        D3
155                                11110010        242        F2
157                                00011110        30        1E
158                                10110001        177        B1
159                                11011100        220        DC
162                                00100000        32        20
163                                10101100        172        AC
164                                00111111        63        3F
166                                11100010        226        E2
167                                10111001        185        B9
168                                11001001        201        C9
169                                00110001        49        31
170                                00000010        2        2
171                                01011001        89        59
172                                01001001        73        49
173                                11101101        237        ED
174                                00110010        50        32
175                                10101001        169        A9
176                                10000101        133        85
178                                11100111        231        E7
179                                10001001        137        89
182                                00010000        16        10
183                                11110011        243        F3
186                                00011111        31        1F
190                                00010100        20        14

Posted: Tue Mar 03, 2020 9:42 pm
by stefano
wonderful, I was trying to get something similar

Posted: Tue Mar 03, 2020 10:07 pm
by stefano
could you exlore other ranges, e.g. for I=0 or others?

Posted: Tue Mar 03, 2020 10:20 pm
by zx81ultra
stefano wrote:could you exlore other ranges, e.g. for I=0 or others?
What other ranges could be interesting/valid ?

Code: Select all

ld    a,12        ; FONT ptr in ROM at address: 12 * 256 = 3072

Posted: Tue Mar 03, 2020 10:39 pm
by stefano
the original sample code used zero (ld a,c)

Posted: Wed Mar 04, 2020 9:45 am
by stefano
Duh.. for the record, the display does not need to be defined in #asm !


unsigned char hr_d_file[]={
11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,201,
11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,201,
(....)


Then, to access to the video memory:
hr_d_file[10]=11+128;

All, remember that every row must be closed with RET (201) and that values with bit #6 enabled can't be used, the table by 'zx81ultra' lists the only valid values and is of great help.

At this point I'd love to have feedback about this new engine and whether the real hardware is able to actually display something. I can be reached at <my name>.bodrato, hotmail.com and will gladly provide a precompiled test program in case you are not able to build your own.
Different TV sets should be checked.

Posted: Wed Mar 04, 2020 12:32 pm
by zx81ultra
I have now the patterns for ROM font pointers at 0, 4, 8, 12 and 16 compiled in a Google Sheets file:

https://docs.google.com/spreadsheets/d/ ... sp=sharing

As for testing in the real thing, sadly my TS1000 stopped working some time ago, living in the emulators now.

I'll check the other version with dynamic number of rows and practical applications, I still have to figure out how to map a specific x,y hr point with this.

Posted: Wed Mar 04, 2020 4:41 pm
by stefano
Very nice :)
For what I could see only three of those table are interesting for common applications: a blank byte is a key requirement.
I prepared a list of 16 values (4 bit) for I=12, which can be used in a translation table to get 128 different bits on the x axis.
To me the interesting resolution in rows could be 64,80,96,128, or 192. The lower the faster (and cheaper in terms of memory usage).
Since the horizontal resolution is differs from the other implementations there's no point to match the existing values for Y on other hrg modes.
Just, 192 is the most reliable (expected size, but must be tested as well), 64 saves a lot of space. Opinions?