Menu

#1979 x128 gets stuck when opening borders with Z80

v3.x
open
None
x128
2024-04-25
2024-01-13
No

I experimented with opening borders with Z80. The program z80openborders.prg works fine with a real PAL C128 and Z64K. VICE x128, however, gets stuck.

After a moment the program should return to the READY prompt. With VICE x128 this does not happen. This appears to be related to interrupts or HALT instruction. The counter in register BC is not decreased.

Code listing below.

main    .org   $1c01
    .byte $0c,$08,$0a,$00,$9e,$37,$31,$38,$31,$00,$00,$00
    lda $ff00 ;
    pha ; store RAM config to stack
    sei ; disable interrupts
    lda $ffee
    pha
    lda $ffef
    pha
    lda $fff0
    pha
    lda #$3e ;
    sta $ff00 ; select RAM config for Z80

    lda #$c3 ;
    sta $ffee ; store JP instruction for Z80 mode start
    lda #<z80code ;
    sta $ffef ; store lo-byte address
    lda #>z80code;
    sta $fff0 ; store hi-byte address
    lda $d505 ;
    pha ; store mode to stack
    lda #$b0 ;
    sta $d505 ; set Z80 mode - this instruction deactivates 8502 and jumps by Z80 PC to $ffee
    nop
    nop
    pla
    sta $d505
    pla
    sta $fff0
    pla
    sta $ffef
    pla
    sta $ffee
    pla
    sta $ff00
    cli
    rts
z80code     

.byte $F3;              di
.byte $01 $00 $02;      ld bc,0200h
.byte $11 $00 $38;      ld de,3800h
.byte $21;      ld hl
.byte <endcode
.byte >endcode
.byte $ED $B0;          ldir
.byte $C3 $00 $38;      jp 3800h

endcode
    .org    3800h

    ld  sp,37F0h

    ld  bc,0D012h
    ld  a,0f9h
    out (c),a
    ld  bc,0dc0eh
    in  a,(c)
    ld  (3101h),a
    ld  a,0f9h
    out (c),a
    ld  a,01h
    ld  bc,0d019h
    out (c),a
    inc c
    out (c),a
    ld  a,01bh
    ld  bc,0d011h
    out (c),a



    ld  bc,3000h
    ld  a,31h
_loop1:
    ld  (bc),a
    inc c
    jr nz,  _loop1
    inc b
    ld  (bc),a
    ld  a,0c3h
    ld  (3131h),a
    ld  bc,irq
    ld  (3132h),bc

    ld  a,30h
    ld  i,a
    im  2
    ei
    ld  bc,500

_loop3:
**  halt
    dec bc**
    ld  a,b
    cp  0ffh
    jr nz,  _loop3
    ld  bc,0dc0eh
    ld  a,(3101h)
    out (c),a
    jp  0ffe0h
irq:
    push    af
    push    bc
    ld  bc,0d019h
    ld  a,01h
    out (c),a
    ld  bc,0d020h
    in  a,(c)
    inc a
    out (c),a
    ld  bc,0d011h
    in  a,(c)
    and 0f7h
    out (c),a
    inc c
_loop2:
    in  a,(c)
    cp  00h
    jr nz,  _loop2
    dec c
    in  a,(c)
    and 7fh
    or  08h
    out (c),a
    ld  bc,0d020h
    in  a,(c)
    dec a
    out (c),a
    pop bc
    pop af
    ei
    ret


    .end
2 Attachments

Discussion

<< < 1 2 3 > >> (Page 2 of 3)
  • Jussi Ala-Könni

    r45045 is a LOT more accurate!

    I did a small demo which makes use Z80 raster interrupts. (Well, I am no democoder, but I felt I had to do something to demonstrate the use of Z80 in raster programming, attached also here).

    https://c-128.freeforums.net/thread/1237/little-128-raster-interrupt-demo

    The only fault is that border patterns waggle, which does not occur with real PAL C128. Is it a coincidence that timing for border opening is correct?

    The border interrupt part:

        nop ;for NTSC, omitted in PAL
        ex  af,af'
        inc bc;6
        ld  hl,(_colorptr2)
        ld  bc,0d020h
        ld  a,(hl)
        inc l
        exx
        ld  hl,(_colorptr1)
        ld  bc,0d016h
        ld  d,(hl)
        inc l
        out (c),0
        ld  c,20h
        out (c),d
    
        ;the repeated code for every scanline, unrolled in memory, 126/130 z80 cycles (PAL/NTSC)
    
        out (c),a
        out (c),d
        out (c),a
        out (c),d
        out (c),a
        out (c),d
        out (c),a
        out (c),d
        exx 
        ld  a,(hl)
        inc l
        exx
        ld  d,(hl)
        inc l
        nop ;for NTSC, omitted in PAL
    

    rastertimingtest.prg produces at the upper part of the pattern, but the lower part is straight, interesting why is this, since exactly the same code is run. And the issue #1927 with C64 CP/M cart, can that be fixed also?

    I will consider IN measurements a bit later.

     

    Last edit: Jussi Ala-Könni 2024-04-07
  • Jussi Ala-Könni

    I added 13 cycles at both sides (after HALTs; before and after measurement) and updated real hw results; within the measurement, there are still +0 +1 +2 + 4 z80 cycle variants of the tests. Again they are half-correct and half-wrong, Z64K also but in a different way.

    The timer is stopped when read, so IN read timing should not be an issue, but rather when start/stop commands get through to the CIA?

     

    Last edit: Jussi Ala-Könni 2024-03-29
    • Roberto Muscedere

      Try r45055. I got it working for your demo and alternative tests, but not your original ones. Still one value of 4 is off by 1 for those tests.

       
      • Jussi Ala-Könni

        The waggling just shifted to the border opening, every other being missed. So something else has to be tried.
        I made an alternate rastertimingtest, with absolute (JP) rather than relative (JR) jumps, and that produces a straight edge. So timings of relative jump group instructions (DJNZ, JR, JR Z, JR NZ, JR C, JR NC). I realized this since looped code did not run properly but unrolled code did.

        What I have noticed that timing adjustments do not work so simply over badlines, if 1 1MHz cycle is added or removed before badline, that may produce 2 cycle shift after the badline or no shift at all. I suspect that extra Z80 cycles are weeded out at badlines.

         
  • Jussi Ala-Könni

    I did a few tests, measuring the number of cycles taken in JR NZ and DJNZ loops, executed 256 times.

    Expected times are:

    JR, NZ: 255 * (4 +12) + (4 +7) = 4091 > make it half to get 1 MHz cycles > 2045,5
    in hex: 07fd - 07fe
    Real C128 and Z64K give precisely that, fluctuating between 07fd - 07fe.
    x128 is seriously off, giving 0580.

    DJNZ: 255 * 13 + 8 = 3323 > make it half to get 1 MHz cycles > 1661,5
    in hex: 067d - 067e
    Real C128 and Z64K give precisely that, fluctuating between 067d - 067e.
    x128 is seriously off, giving 0380.

    The test skeleton also included, which gives 0 (offset calculation).

     
  • Jussi Ala-Könni

    All RET instructions tested. Results:

    RET:
    257 x 10 = 2570 z80 cycles > 1285 1MHz cycles > 0505h
    real C128: 0505h
    Z64K: 0505h
    x128 r45045: 0505h

    conditional RET, condition met:
    theoretical: 257 x 11 = 2827 z80 cycles > 1413.5 1Mhz cycles > 0585-6h
    real C128: 0585h-0586h (fluctuates)
    Z64K: 0585h-0586h (fluctuates, excellent result!)
    x128 r45045: 0505h

    conditional RET, condition not met:
    theoretical: 257 x 5 = 1285 cycles > 642.5 1MHz cycles > 0282-3h
    real C128: 0282h-0283h (fluctuates)
    Z64K: 0282h-0283h (fluctuates)
    x128 r45045: 0282h

    RETN, RETI and undocumented opcodes:
    theoretical: 257 x 14 = 3598 > 1799 1MHz cycles > 0707h
    real C128: 0707h
    Z64K: 0707h
    x128 r45045: 0505h for RETN and RETI, 0000h for undocumented opcodes

    empty test:
    0000h for all

    Test files and sources attached.
    Edit: the loop executes 257 times, theoretical values corrected.

     

    Last edit: Jussi Ala-Könni 2024-03-31
  • Jussi Ala-Könni

    Corrected count to 256:

    RET:
    theoretical: 256 x 10 = 2560 z80 cycles > 1280 1MHz cycles > 0500h

    conditional RET, condition met:
    theoretical: 256 x 11 = 2816 z80 cycles > 1408 1Mhz cycles > 0580h

    conditional RET, condition not met:
    theoretical: 256 x 5 = 1280 cycles > 640 1MHz cycles > 0280h

    RETN, RETI and undocumented opcodes:
    theoretical: 256 x 14 = 3584 > 1792 1MHz cycles > 0700h

    Z64 same as above:
    x128 r45045 :

    unconditional: 500h
    conditional, condition met: 0500h
    conditional, condition met: 0280h
    RETI, RETN: 0500h

     
  • Jussi Ala-Könni

    CALL tests attached:
    theoretical value for CALL unconditional and CALL conditional when condition is met:
    256 x 17 = 4352 > 2176 = 0880h

    CALL conditional when condition is not met:
    256 x 10 = 2560 > 1280 = 0500h

    Z64K agrees with calculation.
    x128 r45045: 0880h for call and 0500h for conditional CALLs, for both met and not met condition.

     
  • Roberto Muscedere

    Hi. Thanks for all these tests, but there are a lot of them. This is a very good basis for a test suite. I'm sure you have already started creating some kind of skeleton API that does most of the work and all you have to do is put in the instructions you are testing. I would ask that you also include the debugcart function where you write to IO at $d7ff with a return value, 0 for good and >0 for fail. This value is returned by the vice executable once it is written to so the tests can be scripted. You would need to use the -debugcart switch to see this work though. Others often write a 2 (red) to $d020 for fail and 5 (green) to $d020 for pass as well just for a visual verification.
    For REAL machines, you can still write to $d7ff but obviously the CPU keeps going. So this is where you might consider loading the next test on a pass. Essentially, you can create a d64 with a series of tests, where each runs the next. So in testing automation we can run the tests individually to record the failure, and on the REAL machines, the test runs until any fail.
    You may also want to avoid using capital names in the filenames and text as you always have to switch to lower case if you are interactively running it.
    For my CPU tests, I usually put the opcode as well as the verbose instruction in the file name so you can easily figure out which code to run, but that is just what I do.
    I have run all your tests and have patched the code. See r45058.

     
  • Jussi Ala-Könni

    Well, my planned approach is to generate a .prg file for each instruction tested. I use Z80 Workbench https://www.heinpragt-software.com/z80-processor-ide/ . Some instructions are more complicated to test (like RSTs), but for most instructions it is just putting the tested instruction in the skeleton source file, assemble with a mouse click, and save the test .prg file, and this for all tested instructions. Debugcart can be added easily, but loading the next test on a pass starts to become more complicated - in principle, running the test on a real hardware needs to be done only once. So if it is enough that tests are put in a script I would take that approach. Odd z80 cycles also cause fluctuation in results (not a problem with most instructions however), so that needs also a consideration how to get an accurate result.

    I checked r45058 and it seems fine. That is quite an improvement in accuracy which I have seen a short period of time! Yet, there is still a way to go, reaching full accuracy requires coming to grips what happens in bad lines - my hypothesis is that some kind of timing regularization happens at bad lines, Z80 emerges from a bad line synced. But a scanline is not a multiple of NOPs, so when in HALT state, so from Z80 CPU's viewpoint, every other scanline starts with a half NOP offset when compared with the other ones. That produces the alternating pattern when measuring the stepping time from one scanline to the next.

     
  • Roberto Muscedere

    For this level of code generation, you may want to look into makefiles and perhaps using Linux. VICE is compiled under linux even for the windows build as the batching process is significantly simplified.
    You could probably setup an easy build environment using WSL.

     
    • compyx

      compyx - 2024-04-01

      VICE is compiled under linux even for the windows build

      That's not correct, the Windows builds are generated on Windows with msys2. We used to cross-compile our Windows builds using mingw on Debian, but since Debian doesn't provide mingw packages for Gtk, GLib and a ton of other dependencies we switched to using msys2.

      But indeed for the above mentioned code generation using Makefiles and some Linux/Unix environment would be the way to go. Msys2 provides a Unix-like shell and tools on Windows, as will WSL. WSL is probably faster since it'll avoid the horribly slow process spawning on Windows, iirc.

       
  • Jussi Ala-Könni

    This makes good sense indeed, only RSTs, RETs, CALLs, JPs, JRs, DJNZ, and also block move/compare/output (all should be accounted for) need separate treatment, for all others it is just substituting the tested instruction, also the loading the next test on a pass can be incorporated if desired, it can be generated separately (has to be 8502 code obviously) or put into a dump if it is static code.

    ;   tested code, repeated x times
                <opcode> <operand>
    

    also in the header part substitute to be displayed

    .byte "1MHZ CYCLES - <tested instruction>:"
    

    And also the real hw result to compare the result against - I think the theoretical expected value can be put in code initially and corrected where necessary.

     

    Last edit: Jussi Ala-Könni 2024-04-02
  • Jussi Ala-Könni

    I tested zasm (https://k1.spdns.de/Develop/Projects/zasm/Distributions/) and it accepted these sources straight on, producing identical executables (which are to be given .prg extensions). Seems very configurable at command line.

     
  • Roberto Muscedere

    z80asm is available in Debian, so assuming you are using WSL, you may want to try that.

     
    • Jussi Ala-Könni

      Sure; the first step however is generation of individual source files, can you please point out docs how to set up and use Makefiles, as I am not currently familiar with these?

       
      • Roberto Muscedere

        I'm working on a build environment for you, but I'm having trouble finding a z80 assembler with working macro features.

         
  • Roberto Muscedere

    Okay. Here is a beginning timing suite. You will need WSL and a debian install. Unzip this attached ZIP file into a new folder and make sure you have "xa" and "z80asm" installed as well.
    Use a terminal to execute "make" in the folder and it will assemble and build the d64 and d81 files for you too. (it only does this is the sources files change)
    The 6502 files are .ASM and the Z80 files are .Z80. The Makefile is setup to assemble the 6502 code and then add the Z80 code to the end of it. The Makefile will determine the size of the 6502 portion and add a "org " to the beginning of the Z80 code during assembly.
    I used macros here to make the files very small. Take a look at it and it should give you a lot of ideas on how you can easily build up this timing test suite. z80asm seems to have a lot of limitations with the macros, but it works.
    You can just open the ".d64" or ".d81" with vice and it should loop though all the testing programs. The order is based on what is in the Makefile.

     
  • Jussi Ala-Könni

    I got the environment otherwise running and valid .prg files are formed, but c1541 component I could not have due to missing dependencies.

     
    • Roberto Muscedere

      I forgot about c1541. I think you can just install a debian release of vice.

       
      • Jussi Ala-Könni

        It is a damn complex thing trying to fill in all the missing dependencies... it would just suffice to build c1541, but I can't figure out how to get it done.

        "The following packages have unmet dependencies:
        sdl2vice : Depends: libflac8 but it is not installable
        Depends: libgif7 but it is not installed
        Depends: libpcap0.8 but it is not installed"

         

        Last edit: Jussi Ala-Könni 2024-04-06
        • Roberto Muscedere

          Just keep adding those packages until you get it to work. I know it isn't ideal, but that is just the way these distros work. On the plus side, at least it is telling you what you need. Sometimes you have to figure it out.
          If you want to build it from source, I found that I needed these packages:

          subversion autoconf flex bison dos2unix texinfo texlive libgtk-3-dev libglew-dev libpulse-dev libasound2-dev libsdl2-dev libsdl2-image-dev texlive-plain-generic libevdev-dev

          Along with gcc, but I think that stuff is already there.

          You can install things quickly with "sudo apt install <packages>" in a terminal window.</packages>

           
          • Jussi Ala-Könni

            Thanks for help; libflac8 I could not resolve so precompiled package failed to install, but I was able to compile it myself. - Yes gcc was already there since I had to compile xa and z80asm but dependencies were not a problem there. - Now makefile runs correctly; quite a bit of a hassle just to get the tool c1541 working...

             
            • Roberto Muscedere

              It shouldn't be this bad, but maybe it is a WSL thing. I have never used WSL.
              I think the long term plan for c1541 is to make it separate in some way.

               
  • Jussi Ala-Könni

    The first batch of tests. With a real C128 results were all good, without any reds!

    Revised disks attached later in the thread.

     

    Last edit: Jussi Ala-Könni 2024-04-20
<< < 1 2 3 > >> (Page 2 of 3)

Log in to post a comment.