Welcome, Guest. Please login or register.


Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - Alcoholics Anonymous

Pages: [1]
1
Programozás / Re: C programozás (Aztec C / Hisoft C)
« on: 2016.January.07. 02:43:24 »
Ez már nem egészen fair összehasonlítás, mert a gyorsabb verziók az SDCC-vel készültek (azzal teszteltem, hogy az egyes változtatások javítanak-e a kódon), ezért bizonyos mértékben arra a fordítóra optimalizáltak. De más tesztekben is általában az SDCC jobb kódot generál - legalábbis a C runtime használata nélkül, egész számos aritmetikával. A runtime függvények és a float típus megvalósítása viszont a z88dk-ban tűnnek jobbnak, azzal jobban is futott a mandel.c és a tunnel.c.

This translation is going to be terrible so I put the english into code blocks.  Sorry :(



Próbálta már a legújabb z88dk amely egyesíti sdcc és z88dk együtt?

A különbség z88dk és sdcc ez:

sdcc könyvtára minimális, és azt végrehajtani C. Egy maroknyi funkciókat valósították ASM és ezek közé tartozik a 16 bites aritmetikai, memcpy, strcpy, strncpy, strchr, és memset. sdcc a fordító megpróbálja generálni optimalizált fordította C. Az ötlet mögött sdcc könyvtára, hogy C-ben írt, hogy a hordozható annak minden cél CPU-k.

z88dk könyvtár célja a C-szabvány követelményei és meg van írva assembly nyelven. z88dk a fordító megpróbálja létrehozni a kis lefordított C - ezért látod sok hívásokat. Az ötlet mögött z88dk könyvtára, hogy a legtöbb végrehajtási időt fordítanak a könyvtári rutinok így a sebesség szállítják a könyvtár és a kis kód mérete kézbesíti a fordító.

Ha összevetjük a kis C töredékek, amelyek nem élnek könyvtári funkciókat, sdcc akkor mindig gyorsabb kódot, mint z88dk. Azonban a reálisabb programok, ahol könyvtári funkciók fontosak, z88dk gyorsabb lesz, és kisebb. Láttad eltúlzott esetében ez az, amikor összeállítása a float funkciókat. sdcc float könyvtár 32 bites és teljesen C nyelven írt z88dk a 48 bites és teljesen assembly nyelven. z88dk float végrehajtása majdnem 3-szor kisebb, és majdnem 3-szer gyorsabb, mint a sdcc ellenére nagyobb úszós. Hasonló eredményt a 32 bites egész matek, de mind z88dk és sdcc is hasonló teljesítményt nyújt 16 bites egész matematikai mivel mindkettő végre asm.

Láthatjuk néhány benchmark eredmények itt:
http://www.z88dk.org/wiki/doku.php?id=temp:front#benchmarks

Code: [Select]
Have you tried the latest z88dk which combines sdcc and z88dk together?

The difference between z88dk and sdcc is this:

sdcc's library is minimal and it is implemented in C.  A handful of functions are implemented in asm and these include 16-bit arithmetic,  memcpy, strcpy, strncpy, strchr, and memset.  sdcc's compiler tries to generate optimized translated C.  The idea behind sdcc's library is that, written in C, it is portable to all of its target CPUs.

z88dk's library aims for C standard compliance and it is written in assembly language.  z88dk's compiler tries to generate small translated C - that's why you see many calls.  The idea behind z88dk's library is that most execution time will be spent in library routines so the speed is delivered by the library and small code size is delivered by the compiler.

When you compare small C fragments that don't make use of library functions, sdcc will always generate faster code than z88dk.  However, in more realistic programs where library functions are important, z88dk will normally be faster and smaller.   You saw an exaggerated case of this when you compiled using float functions.  sdcc's float library is 32-bit and entirely written in C.  z88dk's is 48-bit and entirely in assembly language.  z88dk's float implementation is almost 3 times smaller and almost 3 times faster than sdcc's despite the larger float type.  Similar results come from 32-bit integer math but both z88dk and sdcc will have similar performance for 16-bit integer math since both are implemented in asm.

You can see some benchmark results here:
http://www.z88dk.org/wiki/doku.php?id=temp:front#benchmarks

sdcc és z88dk is módosult az elmúlt évben el kell végezni kompatibilisek egymással. Az új verzió lehetővé teszi z88dk sdcc kell használni, mint a C fordítót, mialatt a CRT-k és a C könyvtár és javítása sdcc kimenet. Ezek együtt eredményezi kód mérete és sebessége, hogy nagyon jó, mint a kereskedelmi összeállítói.

Ha az új verzió a z88dk telepítve, és egy foltozott sdcc végrehajtható, akkor össze programokat, mint ez:

zcc +embedded -vn -SO3 -startup=0 -clib=sdcc_iy --max-allocs-per-node200000 test.c -o test

Lesz, aki pragma a csúcsra "test.c" megváltoztatni a org címre:

#pragma output CRT_ORG_CODE = 32768

A kimenet lesz "test_CODE.bin", amely ORGed címen 32.768 ebben az esetben.

Code: [Select]
sdcc and z88dk have been modified in the past year to be made compatible with each other.  The new version of z88dk allows sdcc to be used as C compiler while supplying the crts and C library and improving sdcc's output.  This combination produces results in code size and speed that are very good compared to commercial compilers.

If you have the new version of z88dk installed and have a [url=http://www.z88dk.org/wiki/doku.php?id=temp:front#sdcc1]patched sdcc executable,[/url] you can compile programs like this:

zcc +embedded -vn -SO3 -startup=0 -clib=sdcc_iy --max-allocs-per-node200000 test.c -o test

You will have to add a pragma to the top of "test.c" to change the org address:

#pragma output CRT_ORG_CODE = 32768

The output will be "test_CODE.bin" which is ORGed at address 32768 in this case.

Mert ez a beágyazott célpont nincs stdio stdout, stderr így printf kellene helyettesíteni sprintf vagy sprintf. % f is le van tiltva alapértelmezésben úgy, hogy lehetővé tegye bármely% aefg a könyvtár újra kell építeni. Ez a fajta dolog leírt wiki.

Hogy egyszerűen csak nézd meg a lefordított C nincs, hogy létrehoz egy bináris és tudod használni bármilyen kívánt funkciókat:

zcc +embedded -vn -a -SO3 -clib=sdcc_iy --max-allocs-per-node200000 test.c

Code: [Select]
Because it's the embedded target there is no stdio, stdout, stderr so printf would have to be replaced by sprintf or snprintf.  %f is also disabled by default so to enable any of %aefg the library has to be rebuilt.  That's the sort of thing described in the wiki.

To simply look at the translated C you don't have to generate a binary and you can use whatever functions you want:

zcc +embedded -vn -a -SO3 -clib=sdcc_iy --max-allocs-per-node200000 test.c

Ez a kombináció a z88dk / sdcc mindig gyorsabb és kisebb, mint akár sdcc önmagában vagy z88dk egyedül (kivéve, ha a program tartalmaz sok long), és ez nagyon jó betartását C szabvány. sdcc szállítja a fordító megfelelés és z88dk ellátja a könyvtár betartását.

Sajnos már csak három célokat jelenleg definiált erre a pályára: +cpm +embedded +zx. +embedded az általános cél, és fel lehet használni összeállításához bármilyen Z80 gép, de természetesen a dolgok, mint stdin, stdout, stderr, grafika, hang, stb nem lesz elérhető, mint a célhardver határozatlan.

Code: [Select]
This combination of z88dk/sdcc is always faster and smaller than either sdcc alone or z88dk alone (except when the program contains lots of longs) and it has very good compliance with the C standard.  sdcc supplies the compiler compliance and z88dk supplies the library compliance.

Unfortunately there are only three targets currently defined for this combination:  +cpm +embedded and +zx.  +embedded is the general target and can be used to compile for any z80 machine but of course things like stdin, stdout, stderr, graphics, sound, etc will not be available as the target hardware is undefined.

2
Thanks for the information on the mouse -- I think there is enough information there.  One thing I am wondering about is if the mouse ISR will always run on VBI interrupt?  Ie- you plug in the hardware and then its ROM plugs itself into the VBI interrupt at initialization.  The reason why I ask is under a C program you may want to take control of interrupts yourself, just as you might want to do in an assembly program.  I had anticipated that the clib would supply the driver software (this is how it's done for other machines) but if the mouse firmware runs at 50Hz, it's going to be reading and clearing the mouse state itself.  If that's going to be the case then there would have to be one mouse routine that reads the built-in driver's updated variables when interrupts are owned by the EP and another that reads the mouse hw directly when the interrupts are owned by the program.

About your C code examples, FILE* stuffs, etc: well, as far as I can see, my views on this topic are somewhat different. Your approach is about the correct ("C standard") implementation which of course is the better one. I usually have the habit to "ignore" this (well ...) since I often feel it gives too much complexity for a 8 bit system with limited resources. I must admit, my viewpoint is not so nice at all :) I should not comment these topics further, since it seems you are quite good in it already :)

Well I think people don't even try to do sophisticated things in z80 or at least the number of examples is small.  EXOS is one example :)  In z88dk I'm pretty confident we could fit an OS with standard C library, FAT filesystems and all that in under 24k.  There are no shortcuts being taken -- the parts of the C library that are implemented are complete.

Something like this which I used to test the error detection of strtol:

Code: [Select]
#include <stdlib.h>
#include <errno.h>

main()
{
   char *buf = 0;
   size_t sz;
  
   while (1)
   {
      printf("\n> ");
      
      fflush(stdin);
      getline(&buf, &sz, stdin);
      
      printf("= %ld\n", strtol(buf, 0, 10));

      perror(0);
      errno = 0;
   }
}

works as expected.  There's a lot of hidden complexity there, the most obvious are the steps taken to make printf efficiently communicate with the stream.  But getline() is another big one -- it needs to efficiently gather input from the stream up to a '\n' and not only that but dynamically grow a buffer to accommodate the line being read.  getline() here is actually built on a vector<char> within the z88dk lib and growth is via realloc().  realloc() is written to be very fast -- if the allocated memory must be moved, it is moved into the largest hole in the heap so another move will not occur.  If the buffer handed to getline is 0 (NULL), realloc will begin by allocating the largest available block in the heap.

So yeah, things like vector<char> are in the library and are exposed to the user as container types modelled on C++ STL.  This is all in z80 assembly language and it's all efficiently implemented.  We never lose sight of performance, which is really what you are concerned with I think so we do both non-standard and standard C things with the library.

Quote
Maybe this can help?
http://ep.homeserver.hu/Dokumentacio/Konyvek/EXOS_2.1_technikal_information/exos/keyboard/Ch2.html

I don't know how missed that :P

3
Thanks gflorez I can see how it works.

The boxsoft mouse replaces one of the external joysticks and the entermouse communicates on a different bit.

I'm not sure what this does:

Code: [Select]
      call   sub_C3A6   ; this is the "corrections and drawing" routine where the "velocity" 1.1 modification was made

Are you compensating for low precision legacy mice?


Code: [Select]
      ld   a, 2      ; RTS low
      out   (0B7h),   a
      ld   b, 8      ; long delay
      call   WAIT
      call   READ_4BIT   ; read four higher bits

I assume this first long delay resets the mouse logic so if the subroutine does not read all nibbles for whatever reason, this will make the mouse start again at the first nibble?

Are these delays specified precisely in seconds or z80 clock cycles?  I saw mention in other threads of faster z80 substitutions so if it's measured in seconds I think it's best to compute a cycle delay depending on the cpu clock rate.  There is a config option in the compiler that sets the target's clock rate for situations like this.

If you're unaware of them, there are z80 subroutines around that can busy-wait a precise number of z80 clock cycles.  This is one of them by Jan Bobrowski:

http://z88dk.cvs.sourceforge.net/viewvc/z88dk/z88dk/libsrc/_DEVELOPMENT/z80/z80/asm_z80_delay_tstate.asm?revision=1.4&content-type=text%2Fplain

You use it like this:

Code: [Select]
ld hl,500  ; at least 141
call asm_z80_delay_tstate
; here exactly 500 cycles have passed assuming no 'clock stretching'

4
I'm not sure what you mean here, where did you find this information and in what kind of context?

I was glancing through http://ep.homeserver.hu/Dokumentacio/Konyvek/EXDOS/EXDOSeng.htm but I can see now it is mentioned that the disk channel is only opened and closed if you're loading / saving without first opening a channel.  There's no mention of seeking or random access of files.  It's just a simple reference for basic so there isn't a lot of meaty information there.  I found a reference to ISDOS on the same site (in Hungarian) which seems to have a lot more information and makes it look like ISDOS has a lot in common with cpm.  But of course ISDOS is an extension / replacement of EXDOS.

Code: [Select]
while ((c = fgetc(in)) != EOF)
   fputc(process(c), out);

Quote
Well, that  code is not so nice anyway, since you need to issue "system calls" (or whatever we want to name them) all the time character by character, even on a modern OS on decent hardware it will be much slower than doing more bytes on the I/O by your code already. Just to mention that I had to write a log analyzer/"converter" software in C on UNIX and it was much slower using the scheme above. Anyway, that's true that still some may want to try to optimize these somewhat at least on the compiler/OS level.

There are a lot of programs out there doing things this way because sometimes the top performance is not the main concern.  On modern OSes buffering is done at the FILE* level so that the c library is maintaining a 256/512/..4k block of memory for each open FILE so that i/o goes to this user-level buffer and only spills to the OS when the buffer is exhausted.  That way the very slow OS calls are avoided and are traded for a small (on big machines!) memory penalty.  That memory penalty is too high in a 64k memory space so z88dk has changed things somewhat by giving the responsibility to the driver to manage buffers for all storage access.  This way it's not one buffer per open FILE, it's a small number of buffers shared by all storage devices.  But the device driver has to be able to manage such a cache and if it can't z88dk is supposed to supply that code and disk devices will sit on top of that.  So the question is does EXDOS on its own manage such a buffer so that disk thrashing is avoided (read one byte from one file, write one byte to another -- does that mean entire sectors are read and written to access that one byte?)

Note that fuzix, an 8-bit project to bring a unix-like OS to the z80/6809/6502 is doing things like a modern machine as described above and is currently impractical on a 64k machine partly because of this.

In the sample program above the performance issue is very clear but a little more hidden is the exact same issue when using scanf for files:

Code: [Select]
fscanf(in, " %*[A-Z] %d %f", &i, &num);

This just an illustrative example and isn't really a meaningful program fragment.

The first specifier "%*[A-Z]" is supposed to throw away any group of chars from the stream in the set [A-Z].  Then it's supposed to skip whitespace, read an integer, skip whitespace and read a float.

To do any of those specifiers, the c library itself must read one char at a time from the file and decide whether to accept it or not.  That's not any different from the fgetc() case.

People who are concerned with performance normally do this in a line-oriented manner:

Code: [Select]
getline(&line, &len, in);
sscanf(line, " %*[A-Z] %d %f", &i, &num);

But getline() must *still* read chars one at a time because it needs to identify the end of the line on the stream.  So the improvement is only marginal.  (This is a much better way to do things for a different reason:  error handling and having the file pointer at a known position).

Anyway the unique thing that z88dk does is it pushes a state machine to the driver so that the driver is able to determine what is accepted by stdio as it reads chars from the device/cache.

Quote
In my opinion the code above can be done with opening two (file) channels with EXOS one for reading and one for writing. The "read character from channel" EXOS call (function 5) can be used to read a character, and "write character to channel" EXOS call (function 7) to write, to replace fgetc() and fputc(). What I am not sure about: the EOF check.

If EXOS can do it, it's no problem to do it that way.  EOF is one thing but also C allows files to be opened for updating.  That means there are two file pointers in a single file:  one where the reads are coming from and one used to append to the end of a file.  I'm not aware of any z80 disk system that is able to handle this well since it requires the system to maintain two buffers for a single file in order to avoid thrashing.  But if it's a problem, open for update can be disallowed too.

Quote
seems to be OK. But consider the situation that you want to open a file "deeply" in a directory structure like A:\LONGDIR.1\LONGDIR.2\.....\FINAL.TXT so the file name (with full path I mean, what you pass to the EXOS open channel function) can be longer than 28 characters then, which is a no-no then. If I remember correctly I've already asked this on this forum, and the response was something similar that: "it's not so common to use absolute long paths on the EP anyway, just change directory first and access files there without the path information then".

Another way to solve is to traverse the path in a loop and only change one directory at a time.  So it should be ok.

Quote
Reading the external joysticks also uses the $B5 port (like the keyboard) to select what you want to scan, but after the selection is made, you must read port $B6 (unlike the keyboard, where you can read the state on $B5), and bit 0 will carry the state of the selected line. The "selection" itself (numbers are the values written to $B5), and if I recall correctly: 0 = fire, 1 = up, 2 = down, 3 = left, 4 = right, for external joy-1, and for kbd sel 5-9 the same series of assignments for external joy-2. Or something similar.

Thanks I also found a reference through the links you gave and it gives all the info needed:
http://enterprise.iko.hu/technical/Converting_Spectrum_programs.pdf

Joystick scanning is separated from key scanning so that it can be faster.  Admittedly in this case the EP needs to scan all rows anyway but the logic to make sure only one key is pressed and applying shifts can be skipped.

Quote
Another interesting thing, that mouse also works like this (well, the "official" EP mouse is said to be "terrible" - I don't know it personally -

Ok, it's probably a joystick mouse or a mouse that needs to be polled constantly in a loop.  Yeah they aren't too useful.

Quote
so I talk about the "common" solution instead, often named as "boxsoft mouse". It also uses control port lines to read mouse data (originally an MSX-mouse like protocol serialized to one bit transfer with a "shift" signal produced on a line on the serial port .... or something like that). It has some problem though that there is a conflict between external joystick and a mouse then. There is an on-going project (also can be found on the forum) to create a hardware solution for more modern (PS/2?) mouses with some modification to avoid the mentioned conflict.

Ok if I can find something I can add it too.


Another question about the keyboard:  is there a list of standard ascii codes for keypresses returned by the EP firmware somewhere?  I'm not sure what to do about returning ascii codes for ALT keys and some special keys.  I realize the function keys are macros on the EP but in the c lib it's lower level so there is no macro expansion and key codes would have to be returned.

5
You can do this, but please note that EXOS usually refers vertical resolution in units of 9 pixels instead of 8. Of course if you plan to have your own routines to set up video modes (so: defining LPT) that it's your business to use 8 pixel tail characters, etc, whatever.

For the hardware modes, the native character set can be used at 9 pixels tall.  The screen size is still measured in characters.

For the other modes, the difficulty is the max common vertical screen resolution does not divide into 9.  Eg, p4 is typically max 320x200.  200 pixels is 25 chars tall if the chars are 8 pixels tall.  But if they are 9 pixels, it is 200/9 = 22.2 chars tall.  You can reduce the max vertical res to 9*22 = 198 pixels so it's not really a big deal.  The other things that are making me think 8 pixels is 8 is a power of 2 (add a register to itself three times to multiply by 8) and, most importantly, other z80 targets use height 8 pix for their fonts so doing it like this lets the EP benefit from fonts and code on other machines.

There's also a proportional font driver where fonts can be any height so the 9-pixel height specialness of the EP does not have to be lost.

Quote
Well, in case of EP it is not needed and probably you can't do it either (because of the different used hw solutions, I've already written about). Internally EXDOS uses FAT12 btw, but you don't need to know that too much, if you want to use only the "file level" functions. It's interesting to hear what z88dk plans, FAT driver etc, huh. I mean it's almost an OS then, and not only a compiler with its standard library too much :)

Yes it's meant to provide a complete system on z80 embedded systems so there are a lot of extras that other targets can select from as appropriate.

The user-level documentation I found on EXDOS mentioned that a disk channel was automatically closed after a read or write operation, so it seemed like EXDOS would not be able to support streamed i/o.  I was reading user-level documentation which was concerned with loadng and saving entire files rather than random access so it could be I was unlucky to look at the wrong place.

Would something like this be able to be efficiently implemented using just EXDOS?

Code: [Select]
while ((c = fgetc(in)) != EOF)
   fputc(process(c), out);

For this kind of common code to be efficient, the implementation has to cache several disk sectors in memory so that individual reads and writes go to memory rather than disk.  Most z80 disk implementations have been as simple as possible and perform very badly under C without assistance which is why z88dk is trying to supply disk i.o in a layered manner so that these simple systems can sit on top of a caching mechanism.


Quote
for example - to be able to exploit the target machine's abilities using the C language which may need its own "standard" suitable for the purpose. So it's really hard to judge, what would be better, at least it's hard for me ...

We don't replace the underlying system, only add to it.  So eg, a cpm system can still do file i/o by calling the bdos directly but the c standard i/o is also available to use.

Quote
The _internal joystick_ is just like other keys on the keyboard, even the same membrane is used. The only difference, that other keys uses buttons to be able to be operated, while up/down/left/right keys use mechanically different solution and the designers used a joystick handle to be able to use them instead of buttons. So, yes, the internal joystick can be interpreted as other keys, keyboard matrix contains them. For the _external_ joysticks, the situation is different, they are handled differently, as you mentioned with the $b6 port, they are not part of the keyboard matrix. That also mean that a software want to use internal _and_ external joystick they must use separated routine for that purposes.

Ok, is there any documentation on what is returned from a port read of $b6?  The only doc I've found so far is this:

http://gafz.enterpriseforever.com/Dokumentacio/Konyvek/EXOS_2.1_technikal_information/exos/keyboard/Ch3.html#3.7
which is using the EXOS keyboard driver to read the joystick.  The bits are obviously moved because when you read the internal joystick those bits do not correspond to the keys read from $b5.

Quote
But btw the keyboard matrix: do you plan to use your own keyboard scanning routine instead of EXOS' one? Well, it can cause some problems at least since there is eg UK and BRD (and HUN) layouts defined in software, and layout can be changed. Also, there were some projects to interface with an external keyboard without the hardware level compatibility (though to be honest, it can cause problems with many EP software as well, since many of them doing direct hw access), and only on software level the compatibility is provided. I'm really unsure here what can be the best solution, in some situation there can be a significant overhead to allow EXOS interrupt, keyboard scan, than doing EXOS calls to check keyboard etc ...

Yes, here's the untested and first pass at the code:

Code: [Select]
; ===============================================================
; Aug 2015
; ===============================================================
;
; int in_inkey(void)
;
; Read instantaneous state of the keyboard and return ascii code
; if only one key is pressed.
;
; ===============================================================

SECTION code_input

PUBLIC asm_in_inkey

EXTERN in_key_translation_table, error_znc, error_zc

asm_in_inkey:

   ; exit : if one key is pressed
   ;
   ;           hl = ascii code
   ;           carry reset
   ;
   ;         if no keys are pressed
   ;
   ;            hl = 0
   ;            carry reset
   ;
   ;         if more than one key is pressed
   ;
   ;            hl = 0
   ;            carry set
   ;
   ; uses : af, bc, de, hl

   ;EP keyboard matrix:
   ;        b7    b6    b5    b4    b3    b2    b1    b0
   ;Row    80H   40H   20H   10H   08H   04H   02H   01H
   ; 0   L.SH.     Z     X     V     C     B     \     N
   ; 1    CTRL     A     S     F     D     G  LOCK     H
   ; 2     TAB     W     E     T     R     Y     Q     U
   ; 3     ESC     2     3     5     4     6     1     7
   ; 4      F1    F2    F7    F5    F6    F3    F8    F4
   ; 5         ERASE     ^     0     -     9           8
   ; 6             ]     :     L     ;     K           J
   ; 7     ALT ENTER   LEFT  HOLD   UP   RIGHT DOWN  STOP
   ; 8     INS SPACE R.SH.     .     /     ,   DEL     M
   ; 9                   [     P     @     0           I
 
   ld bc,$09b5
   ld d,$ff
   ld hl,shift_table + 9

hit_loop:

   out (c),b                   ; select key row
   in a,(c)                    ; read key state active low
  
   or (hl)                     ; ignore shift keys in this row
   dec hl
  
   cp d
   jr nz, key_hit_0            ; if at least one key pressed in row
  
   dec b
   jp p, hit_loop              ; repeat for rows 9 to 0
  
   jp error_znc                ; if no keys pressed

key_hit_0:

   ; at least one key row is active

   ;  a = active low key result
   ;  b = row containing key press
   ;  c = $b5
   ;  d = $ff
   ; hl = shift table address corresponding to key press - 1

   ; find bit position corresponding to key press

   ld e,d

bit_loop:

   inc e
   rrca
   jr c, bit_loop
  
   cp $7f
   jp nz, error_zc             ; if multiple keys pressed

   ; make sure no other rows are active
  
   ;  e = key bit position 0-7
   ;  b = row containing key press
   ;  c = $b5
   ;  d = $ff
   ; hl = shift table address corresponding to key press - 1
  
   ld h,l
   push hl
  
miss_loop:

   dec b
   jp m, key_hit_1             ; if done checking other key rows
  
   out (c),b                   ; select key row
   in a,(c)                    ; read key state active low
  
   or (hl)                     ; ignore shift keys in this row
   dec hl
  
   cp d
   jr z, miss_loop             ; if no keys pressed in row
  
   jp error_zc - 1             ; if multiple keys pressed

key_hit_1:

   ; exactly one key is pressed
  
   ;     c = $b5
   ;     e = key bit position 0-7
   ;     d = $ff
   ; stack = LSB shift table address corresponding to key press - 1

   pop af                      ; a = LSB of shift table address for key press
  
   inc a
   sub shift_table & 0xff
   add a,a
   add a,a
   add a,a                     ; row offset into table in bytes
  
   add a,e                     ; add key bit offset into table
  
   ld e,a
   inc d                       ; de = key offset into table
  
   ; check for shift modifiers

   ; de = key offset into table 0-79
   ; hl = & key translation table
  
check_LSHIFT:

   xor a
   out ($b5),a                 ; select row 0

   in a,($b5)
   and $80                     ; LSHIFT
  
   ld hl,in_key_translation_table + 80  
   jr z, ascii                 ; if pressed

check_RSHIFT:

   ld a,8
   out ($b5),a                 ; select row 8
  
   in a,($b5)
   and $20                     ; RSHIFT
   jr z, ascii                 ; if pressed

check_CTRL:

   ld a,1
   out ($b5),a                 ; select row 1
  
   in a,($b5)
   and $80                     ; CTRL
  
   ld hl,in_key_translation_table + 160
   jr z, ascii                 ; if pressed

check_ALT:

   ld a,7
   out ($b5),a                 ; select row 7
  
   in a,($b5)
   and $80                     ; ALT
  
   ld hl,in_key_translation_table + 240
   jr z, ascii                 ; if pressed
  
   ; no shift modifiers
  
   ld hl,in_key_translation_table

ascii:

   add hl,de
  
   ld l,(hl)
   ld h,0
  
   ret

shift_table:

   defb $80 ; row 0 ignore LSHIFT
   defb $80 ; row 1 ignore CTRL
   defb $00 ; row 2
   defb $00 ; row 3
   defb $00 ; row 4
   defb $80 ; row 5 ignore unconnected
   defb $80 ; row 6 ignore unconnected
   defb $80 ; row 7 ignore ALT
   defb $20 ; row 8 ignore RSHIFT
   defb $c2 ; row 9 ignore unconnected

in_inkey() returns the ascii char corresponding to exactly one key pressed.  It processes SHIFT, CTRL and ALT and looks up ascii codes in a 320-byte table.  I didn't quite like the size of that but it can be looked at again later.  This subroutine would be used by the terminals to read input.  Related code generates a 16-bit scancode corresponding to an ascii keypress and another subroutine takes this scancode to see if the key is pressed.  This gives very fast keyboard scanning in games, eg.

We can accommodate different language keyboards easily by specifying a keyboard type in the config file and just choosing a different ascii table depending on the keyboard type.

Quote
Btw, Dave supports 6 bit D/A too. And "of course" it has got oscillators with some modulation/distortion/etc settings which are more like digital implementation (unlike with C64 SID, where there are analogue filters for example). Dave is stereo btw.

EXOS here is quite elegant btw, it again uses the channel notion, so you can open a channel (with "file name" of SOUND:). You can write quite "complex" sound definition "control codes" there, and EXOS within the EXOS interrupt will "play it" in the "background". Or something like that, honestly I have never used these ...

Yes I don't want to take away from that - I'm just looking for a way to bring what is in the z88dk library now to the EP.  The D/A can probably give volume-controlled 1-bit sound effects and music.

Quote
But in general, you can see how advanced EXOS is  I was really surprised that a "8 bit micro" uses an OS like EXOS (biut . I don't want to say that you "must" use EXOS for SOUND at any price, just I mentioned it here for the completeness.

Yes it is - that's why I'm sort of staggering around to figure out the best way to do this   I'm sure the best way will evolve over time.

Quote
And feel free to write me "you're too much with those novels", I know I can write just too much  Hopefully my long posts are not only for enlarge the database of the forum

I don't mind.. the more info the better.   Once I have something preliminary ready I'm sure it will be easier to point out how to improve things.
[/code][/code]

6
This sounds good for me. There is a more complex scenario but I don't think sdcc can support it: ie the "far pointer" scenario where a segment number and offset is also stored within a pointer (in contrast of "near pointer" which is simply a 16 bit value directly accessible for the Z80 without extra ops) and an access would cause to alter the segment mapping. But it's complex, slow, and requires extra code in the core compiler code, I think. If I am right ...

sdcc is beginning to support C Embedded, which is a C language extension for embedded systems.  C Embedded specifies how to deal with bankswitched memory and sdcc is starting to do that but I haven't looked in detail at it yet.  sccz80 does have 3-byte far pointers but again it's preliminary.  This is on my to-do list so once I've studied it I will include it in the crts.  I don't think it needs more than providing a subroutine in page-0 (the 'HOME' bank) to perform bankswitching and maybe provide some trampoline code.

So for now, it's left to the user program to handle bankswitched memory intelligently.  What we can do with the crt, for the ep, is make sure all the video-related stuff, some key functions like memcpy, and interrupt related handlers are in page-0 so that user code can be free to place two 16k pages in page-2/3 to copy or operate on data in different banks.  The ep will be the first target that needs a special memory map for this sort of thing :)

Quote
As far as I can remember, EXOS files with type-5 header can be max of 48K (well, minus 256 bytes, since they are loaded to $100 - the 16 bytes long header is not loaded though), and the memory used by the loaded program is allocated, and segments are mapped to be able to access them by the CPU directly. However, there is some problem if you have large BSS area. What I mean here, that your program (let's say) would fit in 16K after loaded, but with the BSS part it's over of 16K. Then you have some extra work to allocate and then map a segment as page 1 ($4000-$7FFF) so you can use it for the "tail of the BSS area". Some people are lazy :)

Heh yeah :)  That's what I want to do:  tell EXOS that the program is CODE+DATA+BSS bytes in size and have it allocate the pages but only load CODE+DATA bytes.  But because of the 48k limit it might be better just to do the BSS allocation at runtime so that it is possible to go beyond 48k.

Quote
since in C, I think nobody says that it's a good idea to access uninitialized variables before some value assignment ...).

The BSS segment has to be zero according to the C standard but the crt takes care of all initialization details so the loader doesn't have to worry about it.

Quote
Well, that's a hard question. As I will mention later :) in this post, some may want to use "EXOS centric" simple stream like console I/O which can be integrated into std EXOS I/O for some reason. Without even using any (console) control codes other than maybe newline or such. It's more like a C program can also use only putchar() (or some "frontend" on top of it like printf()), but others may expect a conio-level interface for some _other_ purpose. Both of them can be important and useful in some cases, hard to replace with the other!

I like the idea to create an API which can provide most of the features what EP can do. Split screen with different resolution (thanks to Nick and its LPT feature) is one of that. But not only hardware is what someone should relay on, but also the software. Eg EXOS has the channel notion to open files. For screen/display stuff I would vote for direct hardware access because of the speed. However for file I/O it's better to use it trough the EXOS (and EXDOS) of course, especially because of the great spectrum of possible "modern" storage solutions (just two examples: SD card cartridge, and the IDE HDD interface) which are totally incompatible at hardware level, but there is a common ground (implemented in software), in the form of EXDOS. For example Bruce (one of the creator of Enterprise) has the nice EPNET project to build ethernet connectivity for the EP with the help of "hardware tcp/ip chip" from Wiznet (W5300). If I know well enough the details, it's also considered to provide EXOS compatibility later, that is, you can open a channel (like a file channel ...) to open - for example - a TCP connection to a remote server on the Internet. Just supporting this channel notion through the planned C API/SDK would make it possible to support even future projects more or less.

I agree with all the above.  I've read quite a bit more and I've started to write some of the library code (I'll mention what's missing below).

I've given the ep's different video modes my own names (I don't know if something better already exists) and I'm measuring characters as 8x8 pixels:

* p2 (pixel 2 colour mode) 80x25 characters full screen
* p4 (pixel 4 colour mode) 40x25 characters full screen
* p16 (pixel 16 colour mode) 20x25 characters full screen
* a8 (attr 8 scan lines tall) 40x25 characters full screen, 40x25 attributes
* c128 (hardware characters)

a8 corresponds to the zx spectrum, a1 corresponds to the ts2068 (1 attribute byte per 8 pixels) and of course there are other aN and cN.

The crt will be able to statically declare up to one display surface per mode.  The size of each display surface would be specified in characters and could be anything from very small to very large.  Memory for these surfaces would be allocated at startup from video memory and how they are displayed would be up to the LPT.  The LPT may only display a portion or all of each surface.  The video memory address and dimensions of each display surface would be stored in global variables accessible by the C library.

The C library will operate on the video memory corresponding to each display surface directly.  The library will have to supply video primitives for each display surface type.

I'm going to write the p4 primitives first and possibly the p2, c128 and a8 modes later.  Ultimately all of them should be there but I think it will be up to the EP community if they are interested in it.  I'm picking on the a8 mode specifically because this is compatible with the zx spectrum and the zx spectrum has a sprite library that has been used by many games which could easily use this mode on the EP.  That sprite library is intended to support many video modes so it could be modified for other of the EP's modes but that is also a lot of work.

Does this sound like a good method to deal with the EP's flexibility?

For file i/o, the clib in z88dk has not finalized it yet -- all the code is present for file systems but the drivers have not been written to do file i/o yet.  The design is object oriented and operates like a stack where you would inject the target's native system at the level it can support.  If the target does not supply any disk device the idea is we will supply the entire stack including a fat driver.  But that's a long time for now.

For the first pass, I'll ignore file i/o (and don't forget you can always call EXOS directly from within C) and then second pass maybe look at connecting stdio to any sort of streaming that EXOS can support.  At the lowest level, stdio generates about a dozen messages for i/o and if those messages can be satisfied by EXOS, it can be plugged in.  I think things like the printer, serial i/o, built-in EXOS video drivers can probably plug into stdio but EXDOS (disk i/o) doesn't seem to be a candidate.  It looks like it performs one whole read.write operation and then closes the channel.  So in order to use it like a streaming device, the z88dk driver would have to cache disk blocks in ram for stdio to access.

Quote
... But that requires great amount of work, of course. Also, static linking the full feature set would result in too large binaries.

Sorry, I do not know enough about the features of sdcc and its standard library/libraries, maybe my ideas are simply odd or overkill.

z80asm is a linking assembler so it will only pull in code if the program uses it.  You can add all sorts of things to the library without worrying about it being attached to the binary all the time.

sdcc's libraries are minimal but z88dk's are fairly substantial.  The two shouldn't be operated separately :)

The EP-specific code that needs to be written includes:

* direct access to keyboard and joystick hw.   There is also an api for mice but this is the minimum.  I found details on port $b5 for reading the keyboard directly but I got a little confused with the built in joystick.

Key matrix:

Code: [Select]
;EP keyboard matrix:
 ;        b7    b6    b5    b4    b3    b2    b1    b0
 ;Row    80H   40H   20H   10H   08H   04H   02H   01H
 ; 0   L.SH.     Z     X     V     C     B     \     N
 ; 1    CTRL     A     S     F     D     G  LOCK     H
 ; 2     TAB     W     E     T     R     Y     Q     U
 ; 3     ESC     2     3     5     4     6     1     7
 ; 4      F1    F2    F7    F5    F6    F3    F8    F4
 ; 5         ERASE     ^     0     -     9           8
 ; 6             ]     :     L     ;     K           J
 ; 7     ALT ENTER   LEFT  HOLD   UP   RIGHT DOWN  STOP
 ; 8     INS SPACE R.SH.     .     /     ,   DEL     M
 ; 9                   [     P     @     0           I

I thought left, right, up, down (and space for fire) were the built in joystick.

But then I saw mention of port $b6 in connections with the joystick... how do I read the joystick directly?  There's also mention of external joysticks in the EP tech docs -- is there a standard way to directly read that hw?

* display primitives that can compute pixel addresses for different display modes given the surface declared by the crt.  The C library will use these to print and draw.

* fzx draw character primitive for each video mode.  This is the putchar for proportional fonts.

* sound.  The sound library is 1-bit oriented but it contains some really good music synthesis routines.  It should be compatible with Dave.

* drivers for fixed width and proportional font terminals.  This is mostly implemented through code inheritance.

The rest is common code.

7
Well, sorry in advance, I type this in hurry, hopefully it's understandable even with my English :)

Ha, your English is better than most Englishmen's.  Example:

Quote
it's a "machine code user app"

You know the difference between "it's" and "its".  This is a 'tell' for people educated in the UK - a very large number have difficulty with correct usage of apostrophes.  I've spent some time on UK-dominated forums and, sadly, it's beginning to rub off on me too when I write quickly.  Maybe some Hungarian English will set things right :)

Quote
Quote
1. What sort of executables should be produced..   Cartridge?  Com?  Binary loaded into ram?
It depends what you want to use :) But in general, I think the most useful on EP (for a program compiled from C source) is maybe the loadable program (into RAM). I am not sure if it's important that someone want to write a C program can be complied into ROMable code ...

I'd like to support all types even if people only use the RAM executable.  It's not difficult to do since the tools are already there to produce ROMs.

Quote
What people usually does, is the type-5 EXOS header applications (EXOS have the notion of file headers which is basically makes possible that EXOS will "know" what to do with a file ... it's a "machine code user app", an IS-BASIC program, or a system extension or even some gfx information with some special EXOS extensions, and so on). So you don't need "BASIC loader" what many 8 bit micros, for machine code programs.
http://ep.homeserver.hu/Dokumentacio/Konyvek/EXOS_2.1_technikal_information/exos/kernel/Ch10.html
(see: 10.4 New Applications Programs)

Thanks for the link - I'm still working through all the documentation on that site.

EP devices are not interrupt driven then, other than what dave & nick are doing?  For example, disk / network / serial devices are not depending on an interrupt to do i/o or turn off motors or anything like that?  And the interrupters (including new peripherals) are not im2 devices?

Quote
In general, EP is quite flexible on memory configurations, it has 4Mbyte (22 bit) address space and address bus width on the motherboard, and it's not uncommon nowdays to have EPs with 512K or even more RAM installed. On the software side, I think original EP64 had older EXOS version.

What I'm thinking about doing initially is a 64k target with 48kish ram at the bottom and the top 16kish containing the display data and being pageable for extra ram.  This fits with the simple model many other machines are currently using for games where a 16k window is used to copy level data (eg) into the main ram as needed.  The EP is more flexible than that but this would provide a simple starting point familiar to everyone where we don't have to worry about code being generated into individual 16k pages.

The -ish part depends on how much ram EXOS is using on the top segment on EP64 machines.

Quote
EXOS has the function 24 - allocate segment which can be used by programs to allocate RAM segment, it always gives the lowest numbered segment. Thus it only gives video segment ("slower") if "other" RAM segments are all used. Segment 0xFF (which is the top segment in VRAM too) is used by EXOS as the "system segment".

Ok so you always get fast ram if it's available when you load in.  Executables output by the compiler consist of CODE/DATA/BSS sections in order and on other targets, BSS is sometimes left off to improve loading times.  The EXOS header for New Application Programs (bytes 2&3) contain a length which I assume corresponds to how many pages will be allocated for the program by the loader.  Does that length have to correspond to the length of the file on disk or tape?  What I want to do is chop off BSS so that only CODE+DATA sections are loaded but the length parameter in the header must be CODE+DATA+BSS size to reserve the necessary number of pages.

Quote
(display)
Complex topic. Nick is quite flexible, no "global video mode" as with most video chips, but only the starting address of the LPT (Line Parameter Table) is set, and even for every scan lines you can have entries (LPB - Line Parameter Block) in LPT with the video mode, colour mode, margin parameters, and so on.

Yes it is.. that flexibility also makes it difficult to settle on a good way to do things.  I think what I will do is take over the machine and access hw directly except for file / printer / serial i/o.  I'm guessing that EXOS can't be used to implement the sort of terminal io expected from C but we can put that decision off until much later.

For display, I think there should be a way to declare surfaces of variable size for different modes.  The data for the surface would be directly written by the terminal / graphics / sprite engines and then the LPT is under program control so that you can pick out where and how much of these surface are displayed.  I think we can define LPTs declaratively in the crt so you could split a screen into hi-res top, text bottom, eg without too much trouble and we can do the same for display surfaces.  This would be an extension of how we statically instantiate drivers on stdin/stdout/stderr in crts now and would get some good defaults for users and at the same time leave it open to more advanced users to make their own crts with their own custom resolutions.

8
Honestly I can't remember what was the details about my problems with sdcc, but surely one of was the "stored twice" stuff (the bug report). Another issue was (but again: I can't remember too much) that sdcc generated CODE to initialize data ...

Yes that problem is gone now too.

Quote
Well I am not sure how experienced EP user I am, but since I wrote (well, "am writing") two EP emulators, maybe I have enough knowledge on the EP side, but not on the sdcc :(  I would be interested, but I have not so much time ...

Well I can do the compiler side; it's the EP side that I would need the help with :)  This would be under z88dk but sdcc can be used with z88dk too.   When used with z88dk, sdcc gains access to the much larger set of libraries in z88dk which are all written in assembly language.

I will see what I can learn tonight but mainly I'm looking at:

1. What sort of executables should be produced..   Cartridge?  Com?  Binary loaded into ram?

2. Is EP64 and EP128 different enough that they should be two different targets?

3. What does the memory map look like.  Is there any 'slow' ram to be aware of?

4. Who owns the interrupts and does the program field them?

5. The display file is a difficult one.. it's programmable so I'd need to know what sort of resolutions should be supported and then pick one to start with.  Ideally it's a bitmapped screen.

6. For stdin/stdout/stderr, z88dk can supply its own terminals.  These terminals support non-overlapping windows and proportional fonts so we usually want to implement terminals using z88dk's drivers for every target and then supply another set of terminals as supported by the target (eg EXOS).  These will occupy less space in the binary but the native terminals can't always do the type of terminal expected by C, particularly the line-oriented editing.  I'd need to figure out if EXOS can be used for these lightweight terminals.

7. The initial main block of asm code we look for from the target is a set of display file primitives that can compute screen addresses from pixel and character coordinates, scroll a rectangle upward, clear a rectangle so that the terminal drivers can be easily implemented.  If these are missing we have to write them.  As an example, here is a set for the spectrum: http://z88dk.cvs.sourceforge.net/viewvc/z88dk/z88dk/libsrc/_DEVELOPMENT/arch/zx/display/z80/  Again we've tended to implement them separately from the existing OS code (EXOS in EP's case) because they tend to be quicker and it gives the tools the ability to generate code that doesn't rely on the firmware.  Sometimes cartridge targets cannot access system firmware.

9
Quote
I did this project some time ago, so I can't remember everything. Unfortunately sdcc was (or "is" still?) a moving target: its internals was changed all the time, but I couldn't stick with a given version (which would be a better strategy) since it had another annoying bugs (fixed later, but also introduced changes affected my tries as well). So this was a reason I lost my interest. Maybe it's time now to re-check the situation now :)

It looks like you got involved while the z80 port was being rewritten.  Things are stable now :)

Quote
I also had an "official" bugreport (about _one_ bug only) to the sdcc team:
https://sourceforge.net/p/sdcc/bugs/2089/
About 3 years, and the bug report is still have status of "open" ... :(

To be fair, they do have eight pages of bug reports -- enough that they probably pick the easiest and/or most interesting (to them) to fix first.

That bug report I know about (I'm a dev at z88dk) and it also affects another major project (fuzix).  Fuzix has resolved it like you did -- they post process the source and remove the redundant space.  The solution is not as straightforward to fix with postprocessing as it might seem at first and I'm not sure if fuzix's solution considers the following.

If you change your code example a little bit:

Code: [Select]
char *test = "Hello world, this is not const!";
char welcome[] = "Hello world, this is not const!";

The generated output (with z88dk tools but the output comes from sdcc):

Code: [Select]
SECTION code_compiler

___str_0:
DEFM "Hello world, this is not const!"
DEFB 0x00

SECTION data_compiler

_test:
DEFW ___str_0
_welcome:
DEFM "Hello world, this is not const!"
DEFB 0x00

The third extra bit of space is eliminated by z88dk but there is still the string constant in section code_compiler which is not required.  However it's not safe to blindly remove it :- you can see that that the same string "Hello world, this is not const!" is reused for assignment to variable test.  Without test there, ___str_0 is redundant and can be removed.

Anyway I do agree with you that this bug is annoying and probably deserves a bump to gain some present day attention.

What I was mainly wondering is if anyone had taken the EP further with C compilation.  It looks like it stalled with your efforts a few years ago :(  The EP is an especially interesting target and we're all interested in it but it's also one of the more difficult ones that would require some input from experienced EP users to do well.

10
some time ago I played with sdcc for EP: http://ep.lgb.hu/sdcc/

Has anyone gone further than this in trying C for the EP on either sdcc or z88dk?  Any crts, assembler libraries, etc?

Quote
I found the solution here, I searched for 'CRT0'
http://sdcc.sourceforge.net/mediawiki/index.php/Z80_port
To get rid of the problem I have to use the '--no-std-crt0' option. And it works perfectly.

The crt sets up the environment before calling main().  Some of the things it's supposed to do is initialize the data segment, zero the bss segment and initialize the heap.  If you eliminate it and don't initialize those segments, the global variables may not have correct initial values and malloc() may not work.


Pages: [1]