SuperFabius / Z80-MBC2

The Z80-MBC2 is an easy to build Z80 SBC (Single Board Computer). It is the "evolution" of the Z80-MBC, with a SD as "disk emulator" and with a 128KB banked RAM for CP/M 3 (but it can run CP/M 2.2 and QP/M 2.71 too).
https://hackaday.io/project/159973-z80-mbc2-4ics-homemade-z80-computer
GNU General Public License v3.0
149 stars 34 forks source link

Suitable for automated benchmark execution? #3

Open spth opened 3 years ago

spth commented 3 years ago

Hello, I am looking for an SBC that can be easily programmed from a host system and interface with that host system via a serial connection. This should not require any mechanic interaction (i.e. no plugging / unplugging / EPROM insertion, etc).

Basically, I want to use it as one can use modern development boards for microcontrollers.

Will this work with the Z80-MBC2? If yes, is where can I find the relevant documentation?

Background: The SDCC compiler has various backends. To track code size and performance regressions, benchmarks are compiled with new SDCC revisions and executed on STM8 and MCS-51 boards (resulting in the graphs at https://sourceforge.net/p/sdcc/code/HEAD/tree/trunk/sdcc-extra/historygraphs/).

However, currently no z80-related port is tested that way (despite Z80/GameBoy/Z180 retrocomputing being an important use case for SDCC). So I'm looking for a well-documented SBC that uses a Z80 or related to close that gap.

SuperFabius commented 3 years ago

Hi, yes it can be done with the Iload boot mode. This is documented for the previous version (Z80-MBC) here with SDCC: https://hackaday.io/project/19000-a-4-4ics-z80-homemade-computer-on-breadboard/log/53121-an-automated-c-language-toolchain

With the Z80-MBC2 you have to use the S190818_crt0.s file included in the SD image in the /src folder (https://cdn.hackaday.io/files/1599736844284832/SD-S220718-R240620-v1.zip).

In the same folder there is an utility (Vidit - S180818.c) written using SDCC. You can use this source file to test the automated toolchain.

bigjohnson commented 3 years ago

I went to https://hackaday.io/project/19000-a-4-4ics-z80-homemade-computer-on-breadboard/log/53121-an-automated-c-language-toolchain and did not find the file section with the bat files, please can tell us where download them? Thanks.

Alberto

SuperFabius commented 3 years ago

The mentioned Files section is here: https://hackaday.io/project/19000/files

The .bat files are here: https://cdn.hackaday.io/files/19000812896000/C_batch.zip

bigjohnson commented 3 years ago

Many thanks!

spth commented 3 years ago

Hmm, for benchmarking, I'd also need a timer. Any recommendations? Just use a Z80-CTC like Zilog recommended back when the Z80 was new? Are they still available?

SuperFabius commented 3 years ago

Hi, you can use a special IOS version that implements a 10Hz (100ms) sytem tick IRQ. See here: https://github.com/SuperFabius/Z80-MBC2/blob/master/DEVEL/S220718-R120519_IOS-Z80-MBC2_DEVEL3.ino You could activate the Systick IRQ generation at line 281 and write the code to use the Z80 INTR ISR to act as a timing IRQ. But please note that if you activate by default the Systick IRQ at line 281, this IRQ will be always enabled from the beginning, so all the Z80 boot modes will not work any more (Iload too) because they do not use it. So I can add a new Opcode (a virtual I/O device/register) to enable it from the Z80 side, so you can activate it from your custom program (or custom crt0) at the right moment. In this way you don't need any new HW because the timer is emulated by the Atmega32. Adding a new Opcode to do this is an easy task, so if is ok for you I can add it quickly. So let me know...

SuperFabius commented 3 years ago

...or I can just add a new Opcode to act as a start/stop timer without any IRQ, so the benchmark will not be affected by the time used to serve the Sysytick IRQ.

spth commented 3 years ago

Thanks.

I've ordered a kit, and will look into it once I've assembled it.

An interrupt would be fine, though I'm not sure a 10 Hz resolution is enough. On the other boards, I use 1 kHz resolution (just using the 16-bit timer directly on STM8, via an interrupt on C8051). I don't think the interrupt overhead would make a big difference (and after all the main purpose of the benchmarks is to compare to the same benchmark compiled using a different compiler revision, so any interrupt overhead doesn'T affect the comparison - unless we have a serious interrupt overhead regression, which is something we would want to show up in the benchmarks).

SuperFabius commented 3 years ago

Hi, I've uploaded here a custom version S220718-R120519_IOS-Z80-MBC2_DEVEL4.ino of the SW for the Atmega32 that allows to set a systick IRQ in the range of [1..255] milliseconds from the Z80 application. To compile it with the Arduino IDE you need to put in the same directory also the others source files normally included (see here). I've uploaded a source test file (assembler) to show how to use the new needed Opcodes (the virtual I/O commands to the Atmega32 firmware) to deal with the IRQs, as source and .hex executable file. For any question feel free to ask. Regards

spth commented 3 years ago

Out of curiosity:

FUZIX supports the Z80-MBC2. But as a Unix, FUZIX has to implement the clock() function. I think that would also require a timer. How is is done there?

SuperFabius commented 3 years ago

Using an IRQ (INTR signal) every 100ms. Anyway I haven't tested the last version of Fuzix on the Z80-MBC2.

spth commented 3 years ago

After some break (got Rabbit boards to work for a similar purpose, by resurrecting OpenRabbit), I want to have another look at this. I have to admit, I didn't really do anything with the Z80-MBC2 so far (assembled it, connected the serial port, booted it, saw the messages up to "IOS: SD error 2 (NOT_READY on MOUNT operation) IOS: Check SD and press a key to repeat" and that's it. Don't have experience with avr, didn't have the hardware to change the firmware in the avr part. A few days ago, I got an Atmel-ICE basic, and hope I can use it with avrdude.

So coming back to this:

SuperFabius commented 3 years ago

To use the Z80-MBC2 with SDCC you doesn't need any SD. You have to set the "iLoad boot mode" from the system menu (press RST + USER, then release RST holding USER down until you see the menu). In the SD image .zip file (/src) there is a S190818_crt0.s file ready for SDCC.

You can make a fully automated toolchain using Tera Term (or probably minicom) having a serial-USB adapter with the DTR line.

So as first step try to compile with SDCC a simple helloworld. You can give a look at "Vidit - S180818.c" (/src) too. Just to have an idea how setup the toolchain give a look here (it is referred to the previous version): https://hackaday.io/project/19000-a-4-4ics-z80-homemade-computer-on-breadboard/log/53121-an-automated-c-language-toolchain

For now you don't need to change the FW (.ino file). When you'll need the time interrupt you can use the Arduino IDE and the serial-USB adapter (with the DTR signal) as in the current FW there is already a bootloader inside the AVR MCU (but will see this later), so no need to use any external programmer.

spth commented 3 years ago

Thanks. Haven't been able to use iLoad yet, as I alwas get a checksum error on the 5th line of the Intel hex file:

Enter your choice >5  Ok
IOS: Loading boot program... Done
IOS: Z80 is running from now

iLoad - Intel-Hex Loader - S200718
Waiting input stream...
:0C00000031F0FCCD0F11CD1E02C3010237
:04020000C97618FDA6
:10110F0001000078B12808110080210F11EDB0C93E
:20020400210200397EFE0A20063E0DD3013E0AD301C9DB01FEFF28FA6FC9211B00E5213425
:2002240003E5CDB004F1210C00E3213903E5CDB004F1213C0CD541BE2600020ECB411BEC50
iLoad: Checksum error!
iLoad: Load error - System halted

The line before the Checksum error varies, but it is always the same up to F1213C0. The real .ihx file start is:

:0C00000031F0FCCD0F11CD1E02C3010237
:04020000C97618FDA6
:10110F0001000078B12808110080210F11EDB0C93E
:20020400210200397EFE0A20063E0DD3013E0AD301C9DB01FEFF28FA6FC9211B00E5213425
:2002240003E5CDB004F1210C00E3213903E5CDB004F1213C03E3CD5A04219B00E321660305

I've tried with two different USB-TTL cables, both with and without a USB hub.

My Z80-MBC2 is powered via the USB-TTL cable.

I noticed that in the post on the automated asm toolchain, you use 9600 baud, while when I bring the Z80-MBC up it apparently runs the serial interface at 115200 baud.

As terminal program I'm using gtkterm.

P.S.: Does the avr zero-initialize the board SRAM on reset?

SuperFabius commented 3 years ago

When using iLoad you must use a terminal that allows to add an (about) 90ms delay at each end of line. Tera Term and minicom allow this for sure.

I never was able to make gtkterm works as wanted to do this...

I've always used a Windows VM with Tera Term and the Z80-MBC2 (@115200 bps) to do this with a teraterm script, but if you want stay on linux you should use minicom and a little script. Probably you'll encounter some problems due the linux EOL termination (I have always used iLoad from Windows with CRLF terminated IntelHex files), but I think that can be solved using a proper utility.

You have to create a proper script to do all the needed steps. For now try to use minicom with somethings as:

            minicom -w  -S minicom.txt -D /dev/ttyUSB0

where minicom.txt contains somethings like:

sleep 1
! ascii-xfr -s -n -l 100 <output_intelHex_file>
sleep 1

BTW: on reset the AVR only "drives" the Z80 to load the boot program into the SRAM. The remaining SRAM is not touched.

spth commented 3 years ago

Thanks. Using gtkterm --delay 90 I now get to

iLoad: Starting Address: 0000

But with both my Blink.ihx anf Ansitest.ihx apparently nothing happens afterwards.

Regarding the RAM: in C global variables are default-initialized to 0 when not explicitly initialized. Your crt0 doesn't do this (it seems to be based on an old SDCC crt0 that assumed the hardware to have zeroed the RAM). I suggest to have a look at the current SDCC crt0 for z80, which does zero-initialize all global variables that are not explicitly initialized: https://sourceforge.net/p/sdcc/code/HEAD/tree/trunk/sdcc/device/lib/z80/crt0.s

P.S.: When using minicom with the script as suggested, I instead get apparently random addresses for the starting address, such as:

iLoad: Starting Address: 0A20

or

iLoad: Starting Address: 0404

But again, nothing after.

SuperFabius commented 3 years ago

I've just "resuscitated" a Z80-MBC2 board and did some test.

The first minicom.txt sleep must be 3 seconds:

sleep 3
! ascii-xfr -s -n -l 100 <output_intelHex_file>
sleep 1

It worked for me using a CRLF terminated intelHex file (made with sdcc from the Win VM)

BTW: is the zero-initialize of all global variables really really needed...?

SuperFabius commented 3 years ago

Here the windows batch file to compile and generate the .hex file (I did years ago):

@echo off rem rem CCBIN.BAT - Compile C source file to target Z80-MBC2. rem rem Two output files: OUT.BIN is a binary executable rem OUT.HEX is an Intel-hex format rem rem To load OUT.HEX with iLoad (after a successful compilation) use the rem Dos batch L without any parameters rem rem rem USAGE: CCBIN

if .%1==. goto :error2 set scfile=%1 if not exist %scfile% goto :error1 ECHO Compiling... sdcc -mz80 --data-loc 0x6000 %scfile% -o out.hex if not %ERRORLEVEL%==0 goto :error3 echo No compiler errors. copy /y out.hex load.hex > nul echo Converting to binary... hex2bin -p 00 out.hex if not %ERRORLEVEL%==0 goto :error4 echo Output binary file: OUT.BIN echo All done. Bye bye... goto :end

:error4 echo Error converting HEX to BIN! goto :end

:error3 echo Errors found in source file %scfile% during C compilation. echo No binary output file. goto :end

:error2 echo Please specify a source file! echo. echo CCBIN.BAT - (C Compile with BINary output) echo Compile C source file to target Z80-MBC2 with .bin and .hex output. echo. echo USAGE: CCBIN ^<source_file.c^> echo. echo Two output files will be generated: OUT.BIN is a binary executable echo OUT.HEX is an Intel-hex format echo. echo To load OUT.HEX with iLoad (after a successful compilation) use the echo Dos batch L.BAT without any parameters goto :end

:error1 echo Error: file %scfile% not found!

:end

spth commented 3 years ago

With the 3 - second delay, I not see the same result in minicom as I saw earlier in gtkterm. Still no user led blinking and not seing any output. Could you attach you .ihx so I can check if my .ihx file is the problem? Your .bat apparently just uses default options (except for --data-loc 0x6000, but I've tried that one now and it didn't make a change for me).

SuperFabius commented 3 years ago

This is the .hex of an ansi.c test

test.zip

I'm using:

 minicom -w  -S minicom.txt -D /dev/ttyUSB0

to load (from a shell inside the dir where the files are located).

spth commented 3 years ago

Thanks. That .ihx works for me (even if I change the line endings via dos2unix).

SuperFabius commented 3 years ago

Great. To be honest this one is the first time I use the Z80-MBC2 iLoad directly from linux with minicom...

Luckily I'm currently using minicom with a 68008 based board with the same script (with sLoad, an s-record version for the 68k).

spth commented 3 years ago

I decided to just skip a head a bit, and found that easier, as I got a step further:

int getchar (void) { return(Serial); }

- So now I see output:

iLoad: Starting Address: 0000

Hello Folk!

Just some counts...

I=0 I=1 I=2 I=3 I=4 I=5 I=6 I=7 I=8 I=9 I=10

Press USER key to continue...


- My current problem: Nothing happens when I press the User key (though for the benchmarking I want to do in the end, that is not an issue).
SuperFabius commented 3 years ago

That is not clear o me...

int putchar (int c) { Serial = 0x1; User = c; return(c); }

int getchar (void) { return(Serial); }

Where is the assembler part to set the needed opcode to IOS to tx a char to the serial...? What are Serial and User?

I mean something like this:

// Z80-MBC2 I/O ports definition sfr at 0x00 EXEC_OPCODE; sfr at 0x01 STORE_OPCODE;

spth commented 3 years ago

There is no assembler part. Your Blink.c already has

// Z80-MBC I/O ports definition
__sfr __at 0x00 User;                       // Define USER led / USER Key port
__sfr __at 0x01 Serial;                     // Define Serial Tx/Rx port
SuperFabius commented 3 years ago

ahhh... you are using this old demo... Blink.c.zip

SuperFabius commented 3 years ago

...but this one was for the previous Z80-MBC board, not for the Z80-MBC2 board... This explains why user key doesn't work for you... Now is late... I'll do an updated version for the Z80-MBC2...

spth commented 3 years ago

Thanks. So essentially everything is working as it should, and that User key thing was just MBC vs. MBC2.

SuperFabius commented 3 years ago

Yes.. I have to check but i think this is the cause... it was more than 4 yrs ago...

spth commented 3 years ago

So far it works, well, e.g. Drhystone self-check passes, so the next step would be to properly implement clock() to actually measure passing time:

iLoad: Starting Address: 0000

Dhrystone Benchmark, Version 2.1 (Language: C)

Program compiled with 'register' attribute

Please give the number of runs through the benchmark: 
Execution starts, 25000 runs through Dhrystone
Execution ends

Final values of the variables used in the benchmark:

Int_Glob:            5
        should be:   5
Bool_Glob:           1
        should be:   1
Ch_1_Glob:           A
        should be:   A
Ch_2_Glob:           B
        should be:   B
Arr_1_Glob[8]:       7
        should be:   7
Arr_2_Glob[8][7]:    25010
        should be:   Number_Of_Runs + 10
Ptr_Glob->
  Ptr_Comp:          -27638
        should be:   (implementation-dependent)
  Discr:             0
        should be:   0
  Enum_Comp:         2
        should be:   2
  Int_Comp:          17
        should be:   17
  Str_Comp:          DHRYSTONE PROGRAM, SOME STRING
        should be:   DHRYSTONE PROGRAM, SOME STRING
Next_Ptr_Glob->
  Ptr_Comp:          -27638
        should be:   (implementation-dependent), same as above
  Discr:             0
        should be:   0
  Enum_Comp:         1
        should be:   1
  Int_Comp:          18
        should be:   18
  Str_Comp:          DHRYSTONE PROGRAM, SOME STRING
        should be:   DHRYSTONE PROGRAM, SOME STRING
Int_1_Loc:           5
        should be:   5
Int_2_Loc:           13
        should be:   13
Int_3_Loc:           7
        should be:   7
Enum_Loc:            1
        should be:   1
Str_1_Loc:           DHRYSTONE PROGRAM, 1'ST STRING
        should be:   DHRYSTONE PROGRAM, 1'ST STRING
Str_2_Loc:           DHRYSTONE PROGRAM, 2'ND STRING
        should be:   DHRYSTONE PROGRAM, 2'ND STRING

Measured time too small to obtain meaningful results
Please increase number of runs
SuperFabius commented 3 years ago

Glad that is running as expected... I've attached here the Blink demo working for the Z80-MBC2.

Blink_MBC2.zip

Next step is flashing the new FW to allow the systick IRQ generation.

First you have to install Arduino IDE and then inside Arduino IDE install the "core" for the Atmega32A.

To install the needed core (MightyCore) follow the steps here: https://github.com/MCUdude/MightyCore#how-to-install

When all done you will be ready to flash the new FW.

BTW: I've tried to make an ISR using SDCC, but in resulting generated code (checked with a disassembler) there isn't any trace of the JP _ISR at the wanted address (vector table) 0x0038 (or any other similar address) that should correspond to the vector 7 (it is a my "guess" as in the SDCC manual there isn't absolutely nothing explaining the relation between the number inside the __interrupt (x) parameter and the real address), so the final result is pretty useless.

The SDCC manual says at par. 3.8.1: .... _The optional number following the "_interrupt(x)" keyword is the interrupt number this routine will service. When present, the compiler will insert a call to this routine in the interrupt vector table for the interrupt number specified...

But there isn't any JP ISR at the expected address (vector table) and changing the vector number doesn't seem to have any effect.

Am I missing something...?

SuperFabius commented 3 years ago

Just found the answer myself at par. 3.8.4:

The Z80 uses several different methods for determining the correct interrupt vector depending on the hardware implementation. Therefore, SDCC does not attempt to generate an interrupt vector table.

So probably I've to tweak the crt0.S for that...

spth commented 3 years ago

So I did actually install the Arduino IDE and that MightyCore thing, and at Tools -> Board selected "ATmega32", but there is apparently a header missing:

Arduino: 1.8.13 (Linux), Board: "ATmega32, Yes (UART0), EEPROM retained, Standard pinout, BOD 2.7V, LTO disabled, External 16 MHz"

Warning: platform.txt from core 'MightyCore' contains deprecated compiler.path={runtime.tools.avr-gcc.path}/bin/, automatically converted to compiler.path=/usr/bin/. Consider upgrading this core.
S220718-R120519_IOS-Z80-MBC2_DEVEL4:192:97: fatal error: PetitFS.h: No such file or directory
compilation terminated.
exit status 1
PetitFS.h: No such file or directory

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.
bigjohnson commented 3 years ago

Did you install https://github.com/greiman/PetitFS?

SuperFabius commented 3 years ago

I've attached all the needed files. Unzip it retaining the folder name and double click the .ino inside (do not use the "vanilla" PetitFS repository as I've made changes on the configuration of PetitFS).

S220718-R120519_IOS-Z80-MBC2_DEVEL4.zip

Here the setting for Arduino IDE:

20210522_213413

Here a test program (assembler src + hex) to check that IRQ is running:

IRQ_test.zip

NOTE: For your purpose the shared IRQ scheme used in the assembler test program is an overkill (and it requires that all the IOS interactions must be treated as an atomic operation. See the assembler test program). To simplify I suggest to enable only the Systick IRQ:

           ;
           ; IOS S220718-R120519_DEVEL4: Set Systick time (IOS)
           ;
           LD       A,SETTICK_opc   ; A = SETTICK Opcode
           OUT      (StorOpc_port),A; Write the Opcode to IOS
           LD       A, TICKTIME     ; Set the Sysytick time
           OUT      (ExecWr_port),A ; Write it to the IOS EXECUTE_WRITE_OPCODE port
           ;
           ; IOS S220718-R120519_DEVEL4: Enable the Systick IRQ only (IOS)
           ;
           LD       A,SETIRQ_opc    ; A = SETIRQ Opcode
           OUT      (StorOpc_port),A; Write the Opcode to IOS
           LD       A, 0x02         ; Enable the Sysytick IRQ only
           OUT      (ExecWr_port),A ; Write it to the IOS EXECUTE_WRITE_OPCODE port
           ;
           ; Enable IRQ on the Z80 CPU (Z80 CPU)
           ;
           IM        1              ; Enable Z80 IRQ mode 1
           EI                       ; Systick IRQ is enabled and active from now!

This way every TICKTIME milliseconds ([1..255]) an IRQ to address 0x0038 will be generated, and inside your ISR you have just to increment a long long unsigned counter and then return from the ISR (do not place any IOS interaction inside the ISR or will interfere with IOS interactions inside the normal program, unless you treat all them as an atomic operation).

Of course when reading the value from inside the program you have to treat that operation as atomic (disabling IRQ during the read/copy), but you already know that...

This way you will have something similar to the Arduino's millis() function.

IMPORTANT!!!: Remember that iLoad takes the first address of the Intel Hex stream as the starting address for the execution.

SuperFabius commented 3 years ago

Regarding the RAM: in C global variables are default-initialized to 0 when not explicitly initialized. Your crt0 doesn't do this (it seems to be based on an old SDCC crt0 that assumed the hardware to have zeroed the RAM). I suggest to have a look at the current SDCC crt0 for z80, which does zero-initialize all global variables that are not explicitly initialized: https://sourceforge.net/p/sdcc/code/HEAD/tree/trunk/sdcc/device/lib/z80/crt0.s

BTW: That file (crt0.s) cannot be compiled as the .globl definitions are all missing... This is the same issue I found more the 4yrs ago...

spth commented 3 years ago

Regarding the RAM: in C global variables are default-initialized to 0 when not explicitly initialized. Your crt0 doesn't do this (it seems to be based on an old SDCC crt0 that assumed the hardware to have zeroed the RAM). I suggest to have a look at the current SDCC crt0 for z80, which does zero-initialize all global variables that are not explicitly initialized: https://sourceforge.net/p/sdcc/code/HEAD/tree/trunk/sdcc/device/lib/z80/crt0.s

BTW: That file (crt0.s) cannot be compiled as the .globl definitions are all missing... This is the same issue I found more the 4yrs ago...

sdasz80 -plosgff should assemble it (as it makes some things .globl by default).

spth commented 3 years ago

I've attached all the needed files. Unzip it retaining the folder name and double click the .ino inside (do not use the "vanilla" PetitFS repository as I've made changes on the configuration of PetitFS).

Thanks. That seems to get me a step further, as I now see two hex files having been generated: S220718-R120519_IOS-Z80-MBC2_DEVEL4.ino_atmega32_16000000L.hex and S220718-R120519_IOS-Z80-MBC2_DEVEL4.ino.with_bootloader_atmega32_16000000L.hex

So I guess the next step is to get one of them somehow onto that avr on the board. I've tried with an Atmel-ICE basic connected to J3, and the board connected to +5V/GND via J2. When I try

avrdude -p atmega32 -c atmelice

I see some activity on the LEDs (maybe a reboot?), but get this output from avrdude:

avrdude: bad response to AVR sign-on command: 0xa0
avrdude: retrying with external reset applied
avrdude: bad response to AVR sign-on command: 0xa0
avrdude: retrying with external reset applied
avrdude: JTAGEN fuse disabled?
avrdude: initialization failed, rc=-1
         Double check connections and try again, or use -F to override
         this check.

avrdude done.  Thank you.
SuperFabius commented 3 years ago

I suggest you to use Arduino IDE to upload the compiled "sketch" as it is the easiest way to do that.

Using a "normal" programmer you have to use the S220718-R120519_IOS-Z80-MBC2_DEVEL4.ino.with_bootloader_atmega32_16000000L.hex file (to retain the bootloader) and manually specify the needed Fuse bits into your programming SW (Fuse bits: High Byte 0xD6, Low Byte 0xAF, Lock Byte 0xCF).

BTW: as far as I know it looks like you are trying to program the MCU with JTAG instead with ICP (JTAG is already disabled in your preprogrammed MCU as it conflicts with other functions).

spth commented 3 years ago

I was just about to reply to you "I've already tried that, I just get the same error text in the arduino error output window as when I invoke avrdude manually". The I noticed there is a "verbose output during upload" option in preferences, and thought I just might try it to see if I get any useful information out of it (possibly to post it here). But when I tried to upload with verbose output enabled, it just worked!

SuperFabius commented 3 years ago

Good! May be a bad contact somewhere or some type of conflict on the serial port...

Now the step is try to run the IRQ_test.zip (inside the zip there is a ready to run .HEX file to use with iLoad).

spth commented 3 years ago

Thanks. The IRQ_test.zip works for me, but even the very simple attached C program simpletest.tar.gz does not.

In particular, the user led never lights up. I.e. it hangs from the moment I try to enable interrupts. When I remove the "ei", the LED does light up.

P.S.: I now wonder if maybe the

                LD       A,SYSIRQ_opc    ; A = SYSIRQ Opcode
                OUT      (StorOpc_port),A; Write the Opcode to IOS
                IN       A,(ExecRd_port) ; Read the SYSIRQ status byte in A

in the IRQ_test.zip example is required to signal to IOS that the IRQ is being handled (i.e. not just to know the reason for the IRQ)?

Some progress: Simple example apparently works if I put ei / di around the code that turn on the user led. Wonder why that would make a difference even when the interrupt routine does not do any I/O. P.P.S.: But it only work for switching on the usee LED. When I try to output something to the serial port after switching on interrupts, I don't receive it (even with putchar() having ei / di around the I/O). P.P.P.S.: The difference between the LED and serial stuff was timing: After enabling the interrupts, I can get a quick putchar('X') through, but apparently not a second. Maybe there is a bit of time until the first interrupt hits, but once it does something goes wrong? Even when I replace the interrupt handler by just a simple ei/reti directly in crt0.s. P.P.P.P.S.: Using di/ei around all i/o outside of interrupt handler plus also having ld a, #0x89 out (0x01), a in a, (0x00) in the handler, I now do get further. P.P.P.P.P.S: Dhrystone, compiled with current SDCC, default options: 539 Dhrystones / s. Looks quite good for a 80 MHz Z80 to me. For comparison, a 44.2 MHz Rabbit 3000A gets about 4700 Dhrystones / s.

SuperFabius commented 3 years ago

Hi, I've just checked what the SYSIRQ Opcode does on the source (S220718-R120519_IOS-Z80-MBC2_DEVEL4.ino) and found this interesting note I wrote (@ line 1641):

  // NOTE 3: The INT_ signal is always reset (set to HIGH) after this I/O operation, so you have to call it
             always from inside the ISR (on the Z80 side) before to re-enable the Z80 IRQ again.

I completely forgot that...

So you have to call this Opcode inside the ISR also if you don't need to check which one IRQ was triggered (as in your case using only the systick), because calling SYSIRQ the INT signal is cleared.

If you don't do that the INT signal remains fixed to active (low level) and the ISR is permanently called (as you know the INT signal is "level triggered" and not "edge triggered" as NMI).

Given that, at this point you must "surround" any I/O sequence with "di/ei" to avoid that a triggered IRQ could "break" a pending "Opcode_Store/Opcode_Execute" sequence.

BTW: If you can, just for curiosity, I'd like to see your crt0 and Dhrystone.c implementation, as up to now I never used IRQ with SDCC.

spth commented 3 years ago

Dhrystone including the crt0 is here: https://sourceforge.net/p/sdcc/code/HEAD/tree/trunk/sdcc-extra/historygraphs/dhrystone-z80/

SuperFabius commented 3 years ago

Thanks!!

spth commented 3 years ago

I now have some nice looking code size graphs for stdcbench, Dhrystone and Whetstone, that corresond to benchmark versions that actually run on the Z80-MBC2 (I can use gtkterm to send the file and look at the result, can even toggle DTR to reboot before trying the next benchmark).

To generate similar graphs for the score, I'd need to automate the sending of the .ihx file and getting the result. I guess I could write a C program to interface with the serial port. But maybe there is some simple way to do it with some bash script or so.

SuperFabius commented 3 years ago

May be using a bash script + a minicom macro (not sure if gtkterm can handle macros). This way you can automate the upload for sure, and may be the output capture too.

BTW: Inside portme.c you did on the clock():

 unsigned long clock(void)
 {
   long ret;
   do
       ret = ticks;
   while (ret != ticks);
   return(ret);
 }

while I was expecting something as:

 unsigned long clock(void)
 {
   long ret;

   __asm
      di
   __endasm;

   ret = ticks;

   __asm
      ei
   __endasm;

   return(ret);
 }

Just curious if there is any particular reason to prefer the first one...

spth commented 3 years ago

Just curious if there is any particular reason to prefer the first one...

Not really. It is just the style I'm used to since the first Z80 platform I wrote software for was the ColecoVision video game system. There, the non-maskable interrupt is used for the timer.

spth commented 2 years ago

I don't think I can reset the system from a minicom script (I can reset the system from minicom using the hangup command, but see no way if issuing minicom commands from minicom scripts). That leaves other options: a bash script that resets the system some other way before invoking the minicom script. Or just write a C program instead of trying to use scripts.

SuperFabius commented 2 years ago

I normally use a bash script to call minicom and execute the loading with a minicom script (see the test.zip file I posted here some posts ago). When you call minicom from the bash script the Z80-MBC2 board is automatically reset by the DTR signal. Then the minicom script waits a while just letting the Z80-MBC2 to complete the boot into iLoad, and after the hex file is transmitted.