Forum: Mikrocontroller und Digitale Elektronik Retro Fieber: Z80 oder 68000 ?


von Christian J. (Gast)


Lesenswert?

Ist jetzt kein Witz aber manchmal glaube ich, dass seit meiner 
Kopfoperation Ende 2009, wo ein Tumor entfernt wurde
einiges "weg" ist. Hatte zwar ne Reha 1/2 Jahr aber auch 5 Jahre
später sind immer noch Lücken da...

von Georg G. (df2au)


Angehängte Dateien:

Lesenswert?

Christian J. schrieb:
> ein IDE Interface auf eine CF abzubilden

Wesentlich primitiver als CF -> IDE -> Z80 Bus geht es kaum noch. Wenn 
du einen 8255 spendierst, hast du schon 90% der Miete. Das mit den 
Glitches ist bekannt und es gibt eine Lösung dafür. Der Text kommt als 
Anlage.

Die Software, einen Sektor zu lesen / zu schreiben ist trivial, Sektor, 
Track, Head in das passende Register, Lesebefehl und dann Daten in aller 
Ruhe holen (es geht gut ohne DMA und der Z80 kann wunderbar Blöcke per 
IO transferieren).

Wenn dir FAT zu umständlich ist, bietet sich ein Disk-Allocation-Vector 
Prinzip wie bei CP/M an. Auch dafür gibt es erprobte Software. Oder du 
nimmst gleich CP/M (was auch keine Großtat ist).

von Christian J. (Gast)


Lesenswert?

Georg G. schrieb:
> Wenn dir FAT zu umständlich ist, bietet sich ein Disk-Allocation-Vector
> Prinzip wie bei CP/M an. Auch dafür gibt es erprobte Software. Oder du
> nimmst gleich CP/M (was auch keine Großtat ist).

Doch, wenn man absolut keine Ahnung davon hat muss man sich in das ganze 
Thema einarbeiten, nicht nur einen Teil und CP/M ist nicht mein Ziel. es 
gibt ne Reihe Lösungen die an das BDOS des CPM angepasst sind, weiss 
ich. Den Text oben kenne ich auch schon. Ich belasse es bei einer SPI, 
da es eh nur individuell ist.

von Holm T. (Gast)


Lesenswert?

A. K. schrieb:
> Holm Tiffe schrieb:
>> PIC ollte ich mir nicht antun und andere µC mit
>> BUS fähigen Latches sind dünn gesäht.
>
> Hab ein paar alte Z8594 (Z8-UPC) rumliegen, mitsame 2K RAMs huckepack.
> Die haben mehr als bloss ein Latch, die haben ein mustergültiges
> Slave-Interface mit Dualport-Registern, Blocktransfer usw.

Nice, kannste bei mir abkippen :-)

Ich habe nur irgend einen µPD75C-irgendwas 4 Bit Controlletti von NEC 
mit solch einem Gehäuse, den gabs wohl vor Urzeiten mal bei Polin? Mir 
ist es nie gelungen eine ordentliche Doku dafür zu finden, aber einen 
Assembler hatte ich schon mal angepaßt vor langer Zeit.

Gruß,

Holm

von Christian J. (Gast)


Lesenswert?

Mannomann ;-(

Seit Stunden versuche ich den TX Buffer ans Laufen zu kriegen aber es 
kommt nur Zeichenmüll dabei raus. Einzelzeichen mit Pause laufen aber 
das schnelle Vollschreiben erzeugt nur Müll als Ausgabe.

Das sieht alles absolut richtig aus aber es klappt nicht.

Was funktioniert ist:

- ISR wird aufgerufen, Int klappt also
- Einzelzeichenausgabe

Testsequenz:
1. String als Ganzes
2. Einzelzeichen


Defintion
1
// RX Empfangspuffer
2
struct rx_type{
3
    int8_t  buf[BUF_SIZE+1];
4
    uint8_t rd_ptr;
5
    uint8_t wr_ptr;
6
    uint8_t mask;
7
    uint8_t f_buf_full;           // flag:Es sind Zeichen da
8
    uint8_t f_buf_overflow;       // flag:Overflow: WR Zeiger hat RD überholt: Buffer wertlos
9
};
10
11
// TX Sendepuffer
12
struct tx_type{
13
    int8_t  buf[BUF_SIZE+1];
14
    uint8_t rd_ptr;
15
    uint8_t wr_ptr;
16
    uint8_t mask;
17
    uint8_t f_idle;               // TX Hardware ist inaktiv
18
    uint8_t f_buf_full;           // TX Buffer ist komplett voll
19
};

putchar....
1
void putchar(char c)
2
{
3
    static counter1 = 0;
4
5
    // Warte bis TX frei für neues Zeichen ist
6
    //while ((STI_TSR & 0x80) == 0);
7
    //STI_UDR = c;
8
9
    if (tx.f_idle) {
10
        // TX ist leer und abgeschaltet
11
        tx.f_idle = false;
12
        STI_UDR = c;
13
        SetDigit(counter1++);
14
        // Int Enable
15
        // CLI;
16
        // STI_IMRA  = STI_IMRA | TX_INT_MASK;  // Int Mask
17
        // STI_PVR   = STI_IERA;
18
        // STI_IDR   = STI_IDR  | TX_INT_MASK;
19
        // STI_PVR   = MODE2_VECTOR;
20
        // EI;
21
    } else
22
    {
23
        SetLedByte(counter1++);
24
        // TX sendet noch.....
25
        //  Zeichen  einschreiben, wenn Buffer nicht voll
26
        if (!tx.f_buf_full)
27
        {
28
            tx.buf[tx.wr_ptr] = c;
29
            // wr+1 am Bufferende auf 0 zurücksetzen
30
            tx.wr_ptr = (tx.wr_ptr +1) & tx.mask;
31
            // Wurde Position ISR RD Zeiger erreicht? Dann voll.
32
            tx.f_buf_full = (tx.wr_ptr == tx.rd_ptr) ? true : false;
33
        }
34
    }
35
36
}

Interrupt Buffer Empty
1
void int_sti_transmit_buffer_empty(void)  __interrupt
2
{
3
    static counter1 = 0;
4
    static counter2 = 0;
5
6
    // Sind noch Zeichen im TX Buffer?
7
    if (tx.rd_ptr != tx.wr_ptr)  {               // ja.....
8
        //SetLedByte(counter1++);
9
        tx.f_buf_full = false;                  // Buffer mindestens 1 Zeichen frei
10
        tx.f_idle = false;                      // TX aus
11
        STI_UDR   = tx.buf[tx.rd_ptr];          // Byte einschreiben
12
        tx.rd_ptr = (tx.rd_ptr +1) & tx.mask;   // Read Pointer rollen
13
    }
14
    else
15
    {                                           // nein, letztes zeichen ging grad raus
16
        //SetDigit(counter2++);
17
        tx.f_idle = true;                       // TX aus
18
        tx.f_buf_full = false;                  // Buffer mindestens 1 Zeichen frei
19
        // TX Interrupt aus
20
        // STI_PVR   = STI_IERA;
21
        // STI_IDR   = STI_IDR  & ~TX_INT_MASK;    // Int TX aus
22
        // STI_PVR   = MODE2_VECTOR;               // Vektor auf Mode 2 Tabelle
23
    }
24
25
    EI;
26
27
}

von (prx) A. K. (prx)


Lesenswert?

(1) Race condition bei
    if (tx.f_idle) {
        // TX ist leer und abgeschaltet
        tx.f_idle = false;
Da muss eine Interrupt-Sperre rein.

(2) In putchar bei Pufferüberlauf Daten wegwerfen ist selten eine 
sinnvolle Strategie. Da macht man üblicherweise nur im 
Receiver-Interrupt, weil alternativlos.

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

if (!tx.f_buf_full)
        {
            tx.buf[tx.wr_ptr] = c;
            // wr+1 am Bufferende auf 0 zurücksetzen
            tx.wr_ptr = (tx.wr_ptr +1) & tx.mask;
            // Wurde Position ISR RD Zeiger erreicht? Dann voll.
            tx.f_buf_full = (tx.wr_ptr == tx.rd_ptr) ? true : false;
        }
        else
            while (tx.f_buf_full);



Läuft trotzdem nicht.

von (prx) A. K. (prx)


Lesenswert?

(3) Ich denke nicht, dass das Register so richtig bedient wird:
        // STI_PVR   = STI_IERA;
        // STI_PVR   = MODE2_VECTOR;
Auf diese Art schaltest du zwischendurch den IM2 Vektorbereich ab. Das 
wird meistens gut gehen - aber eben nur meistens.

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

A. K. schrieb:
> Ich denke nicht, dass das Register so richtig bedient wird:
>         // STI_PVR   = STI_IERA;
>         // STI_PVR   = MODE2_VECTOR;
> Auf diese Art schaltest du zwischendurch den IM2 Vektorbereich ab. Das
> wird meistens gut gehen - aber eben nur meistens.

Geht nicht anders, da PVR benutzt wird für indirekte Adressierung und 
zum Schluss wieder auf Tabelle gesetzt wird. Ist aber auskommentiert, 
man muss keinen Int abschalten, Ergebnis mit und ohne gleich.
1
// Schaltet die RX Line ab oder an
2
void rx_int_enable(char stat)
3
{
4
     CLI;
5
    if (stat) {                               // RX Buffer Interrupt ein
6
        STI_RSR   = STI_RSR  | 0x01;          // RX Hardware ein
7
        STI_IMRA  = STI_IMRA | RX_INT_MASK;
8
        STI_PVR   = STI_IERA;
9
        STI_IDR   = STI_IDR  | RX_INT_MASK;
10
11
    } else {                                  // RX Buffer Interrupt aus
12
        STI_RSR   = STI_RSR  & ~0x01;         // RX Hardware aus
13
        STI_IMRA  = STI_IMRA & ~RX_INT_MASK;  // Maske aus
14
        STI_PVR   = STI_IERA;
15
        STI_IDR   = STI_IDR  & ~RX_INT_MASK;  // Enable aus
16
        STI_IPRA  = STI_IPRA  & ~RX_INT_MASK; // Pending Ints löschen
17
    }
18
19
    STI_PVR   = MODE2_VECTOR;               // Vektor auf Mode 2 Tabelle
20
    EI;
21
}
22
23
////////////////////////////////////////////////////////
24
// Schaltet die TX Line ab oder an
25
void tx_int_enable(char stat)
26
{
27
    CLI;
28
    if (stat == true)
29
    {                               // RX Buffer Interrupt ein
30
        STI_TSR   = STI_TSR  | 0x01;           // TX Hardware ein
31
        STI_IMRA  = STI_IMRA | TX_INT_MASK;   // Int Mask
32
        STI_PVR   = STI_IERA;
33
        STI_IDR   = STI_IDR  | TX_INT_MASK;   // Int Enable
34
    } else
35
    {                                         // TX Interrupt aus
36
        STI_TSR   = STI_TSR & ~0x01;
37
        STI_IMRA  = STI_IMRA & ~TX_INT_MASK;  // Maske aus
38
        STI_PVR   = STI_IERA;
39
        STI_IDR   = STI_IDR  & ~TX_INT_MASK;  // Enable aus
40
        STI_IPRA  = STI_IPRA  & ~TX_INT_MASK; // Pending Ints löschen
41
    }
42
43
    STI_PVR   = MODE2_VECTOR;                  // Vektor auf Mode 2 Tabelle
44
    EI;
45
}

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Läuft trotzdem nicht.

Wundert mich nicht.

(4) Vorher warten ob voll, dann reinschreiben. Nicht nachher warten und 
trotzdem wegwerfen,

(5) Race condition rund um tx.f_buf_full. Dieses Flag ist ohnehin 
unnötig. Wenn man mit einem N+1 Puffer arbeitet, dann kann man diese 
Bedingung ohne Abschaltung von Interrupts feststellen, indem man die 
Indizes vergleicht, ohne ein Flag verwenden zu müssen.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Geht nicht anders,

Das geht sehr wohl anders:
    STI_PVR = MODE2_VECTOR | STI_IERA;
    STI_IDR = STI_IDR  | RX_INT_MASK;

von Christian J. (Gast)


Lesenswert?

A. K. schrieb:
> (4) Vorher warten ob voll, dann reinschreiben. Nicht nachher warten und
> trotzdem wegwerfen,

Weisser Mann sprechen in Rätseln....

von Christian J. (Gast)


Lesenswert?

A. K. schrieb:
> (5) Race condition rund um tx.f_buf_full. Dieses Flag ist ohnehin
> unnötig. Wenn man mit einem N+1 Puffer arbeitet, dann kann man diese
> Bedingung ohne Abschaltung von Interrupts feststellen, indem man die
> Indizes vergleicht, ohne ein Flag verwenden zu müssen.

Ok....

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Weisser Mann sprechen in Rätseln....

Da ist ein "s" zu viel. ;-)

Was du machst:
  if (!full)
    reinschreiben;
  else
    while (full)
und das führt nicht dazu, dass bei vollem Puffer das Byte irgendwo 
landet.

Besser wäre das Prinzip
  while (full)
    ;
  reinschreiben;

von Christian J. (Gast)


Lesenswert?

A. K. schrieb:
> Besser wäre das Prinzip
>   while (full)
>     ;
>   reinschreiben;

Ah, Winnetou verstehen langsam was sein weißer Bruder ihm sagen will....

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Ah, Winnetou verstehen langsam was sein weißer Bruder ihm sagen will....

Na endlich. Ich habe mich schon lange gefragt, was alles passieren muss, 
bis du rot wirst. ;-)

von Christian J. (Gast)


Lesenswert?

Komt trotzdem der gleiche Müll bei raus:
1
void putchar(char c)
2
{
3
4
    if (tx.f_idle) {
5
        // TX ist leer und abgeschaltet
6
        CLI;
7
        tx.f_idle = false;
8
        STI_UDR = c;
9
        EI;
10
    } else
11
    {
12
        //  Zeichen  einschreiben, wenn Buffer nicht voll
13
        while (tx.wr_ptr != tx.rd_ptr)
14
        {
15
            tx.buf[tx.wr_ptr] = c;
16
            // wr+1 am Bufferende auf 0 zurücksetzen
17
            tx.wr_ptr = (tx.wr_ptr +1) & tx.mask;
18
        }
19
    }
20
21
}

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Komt trotzdem der gleiche Müll bei raus:

>     if (tx.f_idle) {
>         // TX ist leer und abgeschaltet
>         CLI;
>         tx.f_idle = false;
>         STI_UDR = c;
>         EI;

Immer noch (1).

von Christian J. (Gast)


Lesenswert?

1
////////////////////////////////////////////////
2
// ISR: Uart TX Sendepuffer ist leer
3
// putchar() muss dafür sorgen, dass er wieder voll wird
4
// ISR sendet solange noch zeichen im Puffer sind
5
6
void int_sti_transmit_buffer_empty(void)  __interrupt
7
{
8
9
    // Sind noch Zeichen im TX Buffer?
10
    if (tx.rd_ptr != tx.wr_ptr)  {               // ja.....
11
        tx.f_idle = false;                      // TX aus
12
        STI_UDR   = tx.buf[tx.rd_ptr];          // Byte einschreiben
13
        tx.rd_ptr = (tx.rd_ptr +1) & tx.mask;   // Read Pointer rollen
14
    }
15
    else
16
    {                                           // nein, letztes zeichen ging grad raus
17
        tx.f_idle = true;                       // TX aus
18
    }
19
20
    EI;
21
22
}

von Christian J. (Gast)


Lesenswert?

#define CLI             __asm di __endasm               // Interrupts 
sperren/frei
#define EI              __asm ei __endasm

Wieso? Ints sind aus.... auß0erdem verliert der auch so Zeichen, wenn 
Puffer voll mit der while Schleife.

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
>             tx.wr_ptr = (tx.wr_ptr +1) & tx.mask;

Kann man so machen, wenn BUFSIZE+1 eine Zweierpotenz ist. Sicherer ist 
aber
   wr = (wr <= BUFSIZE-1) ? wr + 1 : 0;
weil davon unabhängig und kaum aufwändiger.

von (prx) A. K. (prx)


Lesenswert?

A. K. schrieb:
>>     if (tx.f_idle) {
>>         // TX ist leer und abgeschaltet
>>         CLI;
>>         tx.f_idle = false;
>>         STI_UDR = c;
>>         EI;
>
> Immer noch (1).

Wenn Interrupt zwischen if() und CLI und darin das Flag gesetzt wird.

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Ok, behebt das problem aber immer noch nicht, da sporadisch...

von Christian J. (Gast)


Lesenswert?

So besser?
1
void putchar(char c)
2
{
3
    CLI;
4
    if (tx.f_idle) {
5
        // TX ist leer und abgeschaltet
6
        tx.f_idle = false;
7
        STI_UDR = c;
8
        EI;
9
10
    } else
11
    {
12
        EI;
13
        SetLedByte(counter1++);
14
        // TX sendet noch. Warte bis Buffer frei.....
15
        while (tx.wr_ptr == tx.rd_ptr);
16
        tx.buf[tx.wr_ptr] = c;
17
        // wr+1 am Bufferende auf 0 zurücksetzen
18
        tx.wr_ptr = (tx.wr_ptr +1) & tx.mask;
19
20
    }
21
22
}

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> So besser?

Fast. Die race condition ist weg, ...

>         while (tx.wr_ptr == tx.rd_ptr);

... aber die Abfrage ist falsch. Diese Bedingung kann nicht gleichzeitig 
für voll und für leer stehen.

von Christian J. (Gast)


Lesenswert?

Leer braucht man nicht, alles was nicht voll ist ist garantiert "leer" 
bzw noch 1 Zeichen Platz.

Er hängt sich aber auch genau da auf und bleibt ewig drin. Egal wie ich 
es auch drehe und wende, es kommt nur Müll bei raus und ich werde mir 
debug Funktionen schreiben müssen, damit ich weiss was Sache ist.

wr und rd können ihre Positionen tauschen, je nachdem wer schnell läuft.
rd kann aber wr nie "überholen", muss bei gleichheit stehen bleiben.

 Daher kann es nur die Abfrage == geben.

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Leer braucht man nicht, alles was nicht voll ist ist garantiert "leer"

Meinst du nicht, dass sich da was beisst?

    // Sind noch Zeichen im TX Buffer?
    if (tx.rd_ptr != tx.wr_ptr)  {

    // TX sendet noch. Warte bis Buffer frei.....
    while (tx.wr_ptr == tx.rd_ptr);

> bzw noch 1 Zeichen Platz.

Eben. Voll ist bei ((wr + 1) % N == rd).

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Hmm......

ISR guckt, ob Zeiger unterschiedlich sind. Wenj ja, wird Zeichen 
eingeschrieben und Zeiger +1. Beim nächsten Durchlauf sind Zeiger 
entwederr gleich oder es wurde schon was Neues eingeschrieben.

Soo auf Anhieb sehe ich das jetzt nicht aber das ist meist so wenn man 
schon einige Stunden damit verbracht hat.

von (prx) A. K. (prx)


Lesenswert?

Entweder steht rd==wr für Puffer voll, oder es steht für Puffer leer. 
Aber es kann nicht sein, dass es für beides steht.

Theoretischer Hintergrund: Das Prinzip besteht darin, dass man in einen 
Puffer von N Bytes nie mehr als N-1 Bytes reinschreibt. Andernfalls 
hätte man N+1 Füllungsgrade (0..N). Die Differenz der Indizes kann aber 
nur N Zustände darstellen. Also muss einer dran glauben, und das kann 
nur der Füllungsgrad N sein.

von Christian J. (Gast)


Lesenswert?

Ist mir klar aber

#define BUF_SIZE        128

// RX Empfangspuffer
struct rx_type{
    int8_t  buf[BUF_SIZE+1];
    uint8_t rd_ptr;
    uint8_t wr_ptr;
    uint8_t mask;
    uint8_t f_buf_full;
    uint8_t f_buf_overflow;
};

Puffer ist 1 groesser als der Index, ein Reservebyte.

Weiss jetzt aber auch nicht weiter .....

von Christian J. (Gast)


Lesenswert?

Ich kann das problem jetzt nur so einkreisen, dass ich definierte
Füllzustände erzeuge.

zb 10 Zeichen in Puffer schreiben
TX anstossen durch Direkteinschreiben des 1sten zeichens
Warten ob ISR dann alle 10 Zeichen richtig ausgibt.

ISR als Ok abhaken, weiter bei putchar() machen.-


Ich werde mir nochmwas was durhclesen wie man diese beiden Fälle 
abfängt.

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Ist mir klar

Offensichtlich nicht.

> #define BUF_SIZE        128
>     int8_t  buf[BUF_SIZE+1];

Es reicht nicht, den Puffer eins grösser zu definieren. Du musst die 
"Puffer voll" Abfrage passend gestalten.

Ausserdem bringt es nichts, den Puffer 129 Bytes gross zu machen, wenn 
du dank einer Maske von 0x7F grundsätzlich das letzte Bytes unbenutzt 
lässt.

Entweder
  Du suchst im Internet nach diesem Verfahren und schreibst es
  passend ab, ohne unverstandendenen Code kreativ zu optimieren.
Oder
  Du versuchst den Kram zu verstehen.

Ja, man muss bei diesem Verfahren etwas nachdenken. Die intellektuell 
anspruchslosere Methode ist eine Variable, die angibt, wieviele Bytes im 
Puffer gefüllt sind. Die taugt an beiden Enden, also n==0 für "leer" und 
n==N für "voll". Ist aber nicht frei von race conditions, muss also mit 
Sperre arbeiten. Ausserdem gibts eine kleine Falle bei N==256.

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

A. K. schrieb:
> Die intellektuell
> anspruchslosere Methode ist eine Variable, die angibt, wieviele Bytes im
> Puffer gefüllt sind

Das werde ich mal einbauen, da recht einfach.

Der RX Buffer klappt übrigens einwandfrei, direkt von Anfang an. Aber 
der wird auch immer schneller gelesen als er beschrieben werden kann.
1
char getchar()
2
{
3
    register char ch;
4
5
    // Bei Überlauf alles resetten, Buffer wertlos
6
    if (rx.f_buf_overflow) {
7
        init_buffer();
8
        return (0);
9
    }
10
11
    // Warte solange bis ein Zeichen im Buffer ist, auch ewig.
12
    while (!rx.f_buf_full);
13
    // Zeichen auslesen
14
    ch = rx.buf[rx.rd_ptr];
15
   // Read Pointer am Array Ende zurück setzen
16
    rx.rd_ptr = (rx.rd_ptr +1) & rx.mask;
17
     // Alle Zeichen gelesen?
18
    rx.f_buf_full = (rx.rd_ptr == rx.wr_ptr) ? false : true;
19
20
    return(ch);
21
}
void int_sti_receive_buffer_full(void)  __interrupt  // geprüft = ok
{
    register uint8_t zeichen;

    zeichen = STI_UDR;                              // Zeichen abholen

    if (!rx.f_buf_overflow) {
        SetLedByte(zeichen);                        // Zeichen auf LED
        rx.buf[rx.wr_ptr] = zeichen;
        rx.wr_ptr = (rx.wr_ptr +1) & rx.mask;       // Rotieren
        rx.f_buf_overflow = (rx.wr_ptr == rx.rd_ptr) ? true : false;
        rx.f_buf_full  = true;                      // Meldung
    }

    EI;
}
[code]

von Christian J. (Gast)


Lesenswert?

So klappt es einwandfrei im ersten Schuss
1
void putchar(char c)
2
{
3
    static counter1 = 0;
4
5
    // Warte bis TX frei für neues Zeichen ist
6
    //while ((STI_TSR & 0x80) == 0);
7
    //STI_UDR = c;
8
9
    CLI;
10
    // TX Hardware ist leer
11
    if (tx.f_idle) {
12
        // Byte direkt einschreiben
13
        tx.f_idle = false;
14
        STI_UDR = c;
15
        EI;
16
    } else
17
    {
18
        // Byte muss in den Puffer rein...
19
        EI;
20
        SetLedByte(counter1++);
21
        while (tx.bytes_to_send >= (BUF_SIZE-1)); // TX sendet noch. Warte bis Buffer frei.....
22
        CLI;
23
        tx.buf[tx.wr_ptr] = c;
24
        tx.wr_ptr = (tx.wr_ptr +1) & tx.mask;   // wr+1 am Bufferende auf 0 zurücksetzen
25
        tx.bytes_to_send++;
26
    }
27
28
29
30
}
1
void int_sti_transmit_buffer_empty(void)  __interrupt
2
{
3
    static counter1 = 0;
4
    static counter2 = 0;
5
6
    // Sind noch Zeichen im TX Buffer?
7
    if (tx.bytes_to_send-- != 0)  {                     // ja.....
8
        SetLedByte(counter1++);
9
        STI_UDR   = tx.buf[tx.rd_ptr];          // Byte einschreiben
10
        tx.f_idle = false;                      // TX aus
11
        tx.rd_ptr = (tx.rd_ptr +1) & tx.mask;   // Read Pointer rollen
12
    }
13
    else
14
    {                                           // nein, letztes zeichen ging grad raus
15
        SetDigit(counter2++);
16
        tx.f_idle = true;                       // TX aus
17
    }
18
19
    EI;
20
21
}

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
>     {
>         // Byte muss in den Puffer rein...
>         EI;
...
>         CLI;
>         tx.buf[tx.wr_ptr] = c;
>         tx.wr_ptr = (tx.wr_ptr +1) & tx.mask;   // wr+1 am Bufferende
> auf 0 zurücksetzen
>         tx.bytes_to_send++;
>     }

Fehlt da nicht was?

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Habe grad andere Probleme, Empfangen geht jetzt nicht mehr......

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
>     if (tx.bytes_to_send-- != 0)  {                     // ja.....
...
>     }
>     else
>     {                                           // nein, letztes zeichen
> ging grad raus
>         SetDigit(counter2++);
>         tx.f_idle = true;                       // TX aus
>     }

Welchen Wert hat nun tx.bytes_to_send, wenn er vorher 0 war?

von Christian J. (Gast)


Lesenswert?

Du, hier ist grad alles im Ar...... ich kümmere mich da später drum.
Bevor ich mir jetzt aber den Wolf suche werde ich die Version von 
gestern wieder einspielen aus dem Backup und das Neue draufsetzen.

von Christian J. (Gast)


Lesenswert?

Hi,

nach so ca 2h lief es dann...... nachdem ich etwas gefunden hatte, was 
das Indianerauge übersah.... mich erst eine Debugausgabe der Variablen 
auf den Pfad brachte....wer findet den Unterschied?

1
// Löscht den TX Buffer und setzt Zeiger auf 0
2
void init_buffer()
3
{
4
    // RX....
5
    rx.wr_ptr           = 0;
6
    rx.rd_ptr           = 0;
7
    rx.bytes_to_read    = 0;
8
    rx.f_buf_overflow   = false;
9
    rx.mask             = (BUF_SIZE-1) & 0xff;
10
11
    // TX.....
12
    tx.wr_ptr           = 0;
13
    tx.rd_ptr           = 0;
14
    tx.bytes_to_send    = 0;
15
    tx.f_idle           = true;
16
    tx.mask             = (BUF_SIZE-1) & 0xff;
17
}
1
// Löscht den TX Buffer und setzt Zeiger auf 0
2
void init_buffer()
3
{
4
    // RX....
5
    rx.wr_ptr           = 0;
6
    rx.rd_ptr           = 0;
7
    rx.bytes_to_read    = 0;
8
    rx.f_buf_overflow   = false;
9
    rx.mask             = (BUF_SIZE-1) & 0xff;
10
11
    // TX.....
12
    tx.wr_ptr           = 0;
13
    tx.rd_ptr           = 0;
14
    tx.bytes_to_send    = 0;
15
    tx.f_idle           = true;
16
    rx.mask             = (BUF_SIZE-1) & 0xff;
17
}

Wenn man die bytes_to_send auf die LED Zeile abbildet kann man sehr 
schön sehen wieder Buffer beim Schreiben voll wird und sich durch den 
Interupt danach wieder leert.
1
void int_sti_transmit_buffer_empty(void)  __interrupt
2
{
3
    // Sind noch Zeichen im TX Buffer?
4
    if (tx.bytes_to_send > 0)  {                // ja.....
5
        STI_UDR   = tx.buf[tx.rd_ptr];          // Byte einschreiben
6
        tx.rd_ptr = (tx.rd_ptr +1) & tx.mask;   // Read Pointer rollen
7
        tx.bytes_to_send--;
8
        SetLedByte(tx.bytes_to_send);
9
        tx.f_idle = false;
10
    }
11
    else
12
        // nein, letztes Zeichen ging grad raus
13
        tx.f_idle = true;
14
15
    EI;
16
17
}

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

@A.K.:

Alles fertig .....Monitor komplett, EPROM randvoll mit mcurses und 
hexedit, weil der sdcc ja keinen dead code raus wirft. Andererseits muss 
der Monitor wirklich nicht gross sein, glaube meiner ist schon etwas 
überladen....

von Holm T. (Gast)


Lesenswert?

...na also geht doch.
Unterschied ist rx.mask..

Wenn man den Basic Interpreter weg läßt hast Du einen durchaus üblichen 
Funktionsumfang.
Die Frage ist, was dieser Hex-Monitor umfaßt.

Gruß,

Holm

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Der Hex Dump stammt ja von Frank Mk hier und ist ganz nützlich wenn man 
mal schauen will wie das geladene Programm so liegt, wo der Stack und 
die Variablen etc. Volle Funktion eines Hex Editors eben. Den Basic 
Interpreter kriege ich natürlich nicht mehr rein, dann platzt das EPROM, 
was bis auf 300 Bytes jetzt auch voll ist.

Der Monitor kann von einem Skript aus gesteuert werden wenn man echo -n- 
-e "...\r" verwendet. Das nutze ich um die Load Sequenz zu vereinfachen, 
die auch nicht unbedingt in Intel Hex vorliegen muss. Dateilaenge 
übertragen, Datei, "start" Befehl dazu und schon rennt es los. Ein druck 
auf die NMI Taste und er resettet sich wieder in den Monitor, sonst 
bewirkt Reset Neustart des RAM Programmes .... ja, ein bisschen Z80 
Assembler habe ich ja gelernt :-)
1
    ld  sp,#stack   ;; Stack an Spitze des RAM legen
2
3
    ; Teste ob Userprogramm im RAM vorhanden (nur bei EPROM Version)
4
    ;ld a, (ram_prog)   ; Lade Start des RAM 0x2000
5
    ;cp #0xC3           ; vergleiche mit "C3" = JP Befehl
6
    ;jp Z, ram_prog     ; Ja, dann springe direkt ins Ram hinein
7
8
    ; Ram ausnullen (auskommentieren, wenn von PC Upload erfolgt)
9
    ;xor     a             ; clear a and carry
10
    ;ld      bc,#0xdfff    ; ram size left
11
    ;ld      hl,#0x2000    ; starting from 2000
12
    ;ld      de,#0x2001
13
    ;ld      (hl),a        ; 0 -> (2000)
14
    ;ldir                  ; (HL) -> (DE)

Nettes Spielzeug das Ding und mit dem sdcc Compiler und einer kleinen 
HAL drüber lässt sich ne Menge machen.

Übrigens ist der KC85 angekommen, mit Robotron Datasette und RAM 
Erweiterung..... gib mal Deine Adresse, dann geht er direkt durch....

von Holm T. (Gast)


Lesenswert?

Du hast ne Mail.

Gruß,

Holm

von Christian J. (Gast)


Lesenswert?

Ein paar Worte zum Datenspeicher:

Nachdem ich mir im Netz einiges über das XT/AT Computer IDE Interface 
durchgelesen habe stand für mich fest, dass das Thema sich erledigt hat. 
"Alles ganz easy...."

>> Wesentlich primitiver als CF -> IDE -> Z80 Bus geht es kaum noch

Wer mal einen Z80 Minirechner nachbaut sollte sich davon nicht einlullen 
lassen. Die IDE Register am Bus sind eine Sache und recht einfach zu 
mappen aber das IDE Protokoll an sich ist genug Lesestoff für eine Woche 
und dann ist noch immer keine Low Level Verwaltung für das Laufwerk 
geschrieben und erst recht keine High Level. Dann ist man nämlich recht 
schnell hier:

http://www.retroleum.co.uk/z80-filesystem.html

wo sich jemand aus lauter Not, weil FAT zum komplex ist ein eigenes 
Filesystem gestrickt hat. Wozu wenn es das alles schon als fertige 
Bausteine gibt?

Ein in knapp einer Woche an einigen Abenden machbares Dateninterface
um eine beliebige Reihe Binärdaten weg zu schreiben und wieder zurück zu 
lesen sieht bei mir jetzt so aus:

- Atmega328 Controller
- Arduino SD Library von Ardafruit mit 10 Basis Funktionen,ca 12 KB
- SD Card Adapter mit CD4050 Level Shifter an SPI Bus des Atmega328
- Arduino IDE zur Codentwicklung auf einen nackten Atmega328 dessen ISP 
heraus geführt wurde.
- RX/TX des 328er als Debug Interface

Wenn man noch mehr Luxus haben will nimmt man einen Arduino Nano und 
steckt den wie ein IC in die Platine. Dann hat man gleich eine Debug 
Schnittstelle mit dabei und ein ISP.

Anbindung an Z80 PIO über eine Software SPI (SDO, SDI, SCK, SS, WAIT). 
Da die Hardware SPI des Atmega bereits für die SD karte benutzt ist wird 
hier in Richtung Z80 PIO die Bitbang-Funktion "shiftin()" und shiftout() 
benutzt, zusammen mit dem SS Signal und einer extra WAIT Leitung für den 
Z80 SPI Master, da es aufwendiger ist eine Wait Funktion in Software zu 
machen.

Die eigentliche Aufgabe besteht "nur" darin, die SD Library Funktionen 
welche man benötigt und das sind nur open, close, write, read, delete 
auf ein SPI Protokoll zu matchen. Dateinamen sind 1,2,3,4,5....viele.

Ein Masterkommando vom Z80 sähe dann so aus:

Master: <Write Command> <No of Data Bytes> <Data>.....<Data> <CRC>
Master: <Read Command> <Filename> ........
Slave:                               <WAIT> ... <Data> <Data> .....
Master <get File Info> <File Name>
Slave:                               <WAIT> ... <Data> <Data> .....

usw.

Der AVR hat genug RAM Speicher (2K), um einen ganzen Datensatz vom Z80 
in einem Rutsch zu übernehmen, um ihn dann als Ganzes wegzuschreiben, 
während er die Wait Leitung zieht. Eine Kenntnis des Atmega328 von innen 
ist nicht nötig, da die Arduino Lib die Hardware völlig abstrahiert, das 
Ganze ist nur durch Nutzug der High Level Funktionen machbar.


Fangen wir mal an :-)

von Holm T. (Gast)


Lesenswert?

Das Standard OS das gerne Massenspeicher wie IDE Disks am Z80 hatte war 
CP/M und das realisierte das CP/M typische Dateisystem für logische 
Disks bis 8Mbyte, davon waren dann mehrere auf einer Platte möglich.

Ich verstehe nicht, warum Du Dich ausgerechnet in der DOS Ecke 
herumtreibst und dort nach einem passenden FS suchst. Für CP/M gibts das 
Alles fertig und getestet (GIDE).

Gru0,

Holm

von Soul E. (Gast)


Lesenswert?

Christian J. schrieb:

> (...) Die IDE Register am Bus sind eine Sache und recht einfach zu
> mappen aber das IDE Protokoll an sich ist genug Lesestoff für eine Woche

Was für ein Protokoll? Du beschreibst vier Register mit der Nummer des 
gewünschten Blocks, dann eins mit dem Kommando für lesen oder schreiben, 
und dann holst Du Dir 512 bytes Daten ab bzw schickst sie hin.

Das ist wesentlich einfacher als das Gefummel mit einer SD-Karte oder 
einem NAND-Flash.

von Christian J. (Gast)


Lesenswert?

Zuerst habe ich mal ein Compilkerproblem gefunden, dass er seinen 
eigenen Code übeschreibt....
1
//////////////////////////////////////////////
2
// Ein Byte senden und empfangen
3
uint8_t spi_transmit(uint8_t data)
4
{
5
    uint8_t mask,inbyte;
6
    uint8_t i;
7
8
    STI_GPIP = STI_GPIP | SCLK;     // Clock = HIGH
9
    STI_GPIP = STI_GPIP & ~SS;      // Slave Select LOW
10
    //delay8(50);
11
    
12
    mask     = 0b00000001;          // Bit Maske, LSB zuerst
13
    inbyte   = 0;
14
   ......

Kommentiert man das delay8(50) nämlich aus, was einwandfrei funktioniert 
passieren die tollsten Sachen, er killt entweder Daten oder 
programmcode.
1
// Verzögerung
2
static void delay8(uint8_t cnt) {
3
    volatile uint8_t k;
4
    for (k=0;k<cnt;k++);
5
}

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Tja,

also manchmal ist der sdcc etwas "seltsam". Stellt man dann etwas um, 
auch wenn es logisch das Gleiche wie vorher ist klappt es wieder. Schon 
einige Male solche "unerklärlichen Phänonomene" erlebt. In diesem Fall 
half es in die Warteschleife ein NOP in den Rumpf zu nehmen.

Sieht schon ganz nett aus mit CPOL=1, CPHA=1 und 22 khz maximaler Rate.

von Holm T. (Gast)


Lesenswert?

Solche Fehler kenne ich auch von anderen Compilern und die haben i.A. 
nichts mit dem Compiler zu tun, sondern das der Stack nicht mehr stimmt 
weil Du irgendwas nicht aufgeräumt hast. Unter Unix gibts dann eine 
Speicherschutzverletzung..

Gruß,

Holm

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Moin,

aktuell "Heavy Working on Project". Leider passt der Monitor nicht mehr 
in 8kb EPROM rein, mcurses haut sich leider zu viel weg und auch der 
Hexeditor frisst 1,8kb. Eine Kommunikation mit der SD karte haut nicht 
mehr hin, um von dort Anwenderprogramme zu laden.

Abhilfe: 16KB EPROM und nur noch 48KB RAM. 48KB reicht auch finde ich.

Schaltplan: Leitung A13 abklemmen. Input Pin 9 auf GND legen.
            27128 EPROM, Leitung A13 zusätzlich an CPU

Nur zur Sicherheit: Das wars doch?

Wegen der Fädetechnik kann nichts umverdrahtet werden, nur abschneiden 
und neu legen. Sonst würde ich das ganze Gatter für A13, A14 
herausnehmen und A14 und A15 auf das andere legen.

von Holm T. (Gast)


Lesenswert?

Ic würde mit an Deiner Stelle Gedanken um eine Urlademimik machen, die 
den gesamten ROM nach dem Reset in den RAM umlädt und diesen dann 
abschaltet.
Damit ermöglichst Du Anwenderprogrammen den RAM der mit Monitorcode 
belegt ist nach Bedarf zu überschreiben, wenn das Programm beendet wird, 
ist nichts weiter nötig als den Urlademechanismus erneut anzustoßen und 
den Zustand vor
dem Überschreiben des RAMs wieder her zu stellen.

Auf diese Art und Weise funktionieren üblicherweise die meisten 
CP/M-fähigen Z80 Rechner, da die Anwenderprogramme auf eine Startadresse 
von 0x100 gebunden sind. der CCP und das BDOS sind nachladbare 
Programmteile und ganz oben im RAM residiert da BIOS und Systemvariablen 
wie Stack usw.

Im Prinzip läßt sich das verwirklichen in dem nach Reset beim Lesen das 
ROM, beim Schreiben aber das RAM selektiert wird, nach erfolgtem Umladen 
des ROM Inhaltes wird dann ein Flipflop gekippt, das den ROM vollständig 
deselektiert
und den RAM fest einschaltet. Ein Reset kippt die Mimik zurück.

Ich weiß nicht ob Du das in Deinem Fitz noch unterbringen kannst, aber 
das hatte ich wohl schon mal "zeitig genug" erzählt..

RAM ist durch Nichts zu ersetzten, außer durch noch mehr RAM.

Gruß,

Holm

von Steffen S. (bitmaster)


Lesenswert?

Das TDL-Basic gibt es auch als TDLBAS.REL. D.h. man kann es auf eine 
bestimmte Adresse relozieren. Ich habe mir einen "Reolzierer" in vb.net 
geschrieben.
Das scheint (bin mal vorsichtig) zu funktionieren. Mein Basic liegt ab
4000h im ROM, RAM ist wie original (ab 100h).

[[http://www.mikrocontroller.net/articles/ZAtmel:_ein_minimalistisches_Z80-System]]

von Christian J. (Gast)


Lesenswert?

Hi,

da lässt sich nix mehr umverdrahten, das ist endgültig bei dem Brett. 
Nächster Wuirf vielleicht. Habe jetzt 16kb und das klappt ganz gut, mehr 
Platz für das OS.

von Christian J. (Gast)


Lesenswert?

Ein kompletter CPM rechner in dem fast 2 Jahre Arbeit stecken laut Autor 
ist hier dokumentiert:

http://searle.hostei.com/grant/cpm/index.html

Eine Unmenge an Anpassungen mussten da gemacht werden an das original 
BDOS.

Wäre mal eine Sache den eingfach nur stur nachzubauen, 1-2 Wochen 
Einarbeitung müssten sicher reichen..

von (prx) A. K. (prx)


Lesenswert?

Da findest du in U7.C+D die mehrfach erwähnte ROM/RAM-Umschaltung. 
Allerdings komplizierter als nötig, die Adressdekodierung über U6.C+D 
wäre nicht nötig (ROM lesen / RAM schreiben reicht aus).

Edit: Die Dekodierung ist sogar ungünstig. Wenn man sie weglässt, im 
ROM sofort unabhängig von der ROM-Grösse die gesamten(!) 64KB auf sich 
selbst kopiert und dann auf RAM umschaltet, dann landet das BIOS direkt 
aus dem ROM an passender Stelle im RAM und kann angesprungen werden.

: Bearbeitet durch User
von michael_ (Gast)


Lesenswert?

Steffen S. schrieb:
> Das TDL-Basic gibt es auch als TDLBAS.REL. D.h. man kann es auf eine
> bestimmte Adresse relozieren. Ich habe mir einen "Reolzierer" in vb.net
> geschrieben.

Den Begriff hab ich noch nie gehört. Beim aussprechen bricht man sich ja 
die Zunge. Erklär mal näher.

Steffen S. schrieb:
> Das scheint (bin mal vorsichtig) zu funktionieren. Mein Basic liegt ab
> 4000h im ROM, RAM ist wie original (ab 100h).

Das ist gleich ob ROM oder RAM. Wichtig ist die CP/M Sprungtabelle ab 
100H.

von Christian J. (Gast)


Lesenswert?

Moin,

nur kurz zum Verstehen:

1. Rechner startet aus dem Bios. Da Z80 immer bei 0x0000 anfängt muss 
bei 0x0000 ROM zu finden sein

2. Urlader zieht sich das OS rein ins RAM und springt dorthin. Er sieht 
entweder ROM oder RAM, d h. ein Kopiervorgang ins drunter liegende RAM 
ist so nicht mögllich, es sei denn dass jeder Schreibzugriff umgeleitet 
wird auf RAM indem die WR Leitung mit decodiert wird. Dann würde eine 
Kopierroutine das ROM ins RAM kopieren können, danach umschalten und 
fertig.

Ok?

Übrigens: Hat jemand eine Kauf Eagle Version? Ich würde den Z80 Rechner 
für CPM gerne auf richtiger Platine machen.

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> nur kurz zum Verstehen:

Das geht so:

Startphase, Flipflop ausgelöst durch Cold Reset:
  Memory Read  => ROM, adressunabhängig,
  Memory Write => RAM, adressunabhängig.

Betriebsphase, Flipflop umgeschaltet per I/O Zugriff:
  Memory Read  => RAM, adressunabhängig,
  Memory Write => RAM, adressunabhängig.

Reset:
  Kopiere 64KB von Adresse 0 nach Adresse 0,
  damit wird also beim Cold Reset das ROM exakt 1:1 ins RAM kopiert
  und beim Warm Reset das RAM exakt 1:1 auf sich selbst.
  Schalte danach per IO-Zugriff auf Betriebsphase.

Da im RAM die 1:1 Kopie vom ROM steht, ggf. mit ein paar gespiegelten 
Kopien, geht es im Code nahtlos weiter, nur kommt der nächste Befehl 
dann aus dem RAM statt aus dem ROM.

Für CP/M setzt das voraus, dass 64KB RAM vorhanden sind.

CP/M erwartet sein BIOS am oberen Ende des Adressraums. Dort liegt 
unabhängig von der Grösse des ROMs garantiert immer das hintere Ende vom 
ROM-Inhalt, sofern du nicht den Fehler machst, das ROM adressmässig 
einzuschränken. Wenn das BIOS also fertig entwickelt ist und ins ROM 
passt, dann ist nach diesem Blocktransfer nur noch ein Sprung an die 
entsprechende BIOS-Adresse nötig.

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Ok, verstanden.

Die Decodierung hälst du also für unnötig? Irgendwie muss man ja aber 
umschalten. Ich habe mir diesen Rechner angeschaut, was mir etwas fehlt 
ist die peripherie, also Ports, Timer und andere Spielsachen. So ist das 
ja nur ein Rechner der Software verarbeiten kann aber nichts steuern. 
CPM kann damit sicher nichts anfangen. Es stellt sich dann auch die 
Frage wie man eigene programme entwickelt denn mit dem sdcc wird das 
wohl kaum mehr gehen. Die müssen ja "für CPM sein".

Ist das also nur ein Spielzeug um mal wieder CPM Software zu fahren?

Diese RAM Umschaltung hätte ich bei meinem auch machen sollen vorher. 
Wäre easy gewesen:

1: Monitor lädt Programm vom PC
2. Monitor kopiert Programm "unter sich" ab 0x0000
3. Monitor schaltet um um löst Soft reset für 0x0000 aus
4. User programm rennt los und hat 64k für sich.

von Jens (Gast)


Lesenswert?

Christian J. schrieb:
> 1. Rechner startet aus dem Bios. Da Z80 immer bei 0x0000 anfängt muss
> bei 0x0000 ROM zu finden sein
Nicht zwingend.

Beim KC85/3 und Konsorten, gibt es eine Bootstrap-Schaltung. Dort liegt 
das BIOS bei 0xE000. Bei Reset wird für ein paar M1-Zyklen dieser 
Bereich nach 0x0000 gemappt. Als erstes steht ein Jump in den 
'richtigen' ROM. Das Mapping wird nach diesem Jump deaktiviert.

Also ja und nein. Es wird bei 0x0000 angefangen, aber es muß nicht 
zwingend ein ROM dort sitzen. Es reicht, wenn die ersten Memread auf dem 
Bus 0xC3 0xll 0xhh vorfinden, um von einer anderen Adresse zu starten.


> 2. Urlader zieht sich das OS rein ins RAM und springt dorthin.
[...]
Genau.


> Übrigens: Hat jemand eine Kauf Eagle Version?
Wenn es größer als 80 x 100 mm wird, verwende ich (privat) KiCad. Es ist 
zwar noch etwas kryptischer als Eagle zu bedienen, aber alles in allem 
recht brauchbar und nicht irgendwie limitiert.

Jens

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Die Decodierung hälst du also für unnötig? Irgendwie muss man ja aber
> umschalten.

Durch einen passenden IO-Zugriff. Das kann auch ein Zugriff auf den ADC 
sein, wenn dein IO-Dekoder nichts mehr frei haben sollte.

> 2. Monitor kopiert Programm "unter sich" ab 0x0000

Das setzt voraus, dass alle Schreibzugriffe im RAM landen. Auch die 
auf dem Adressbereich des ROM. Technisch kein Problem, Umbau ist 
einfach.

von Jens (Gast)


Lesenswert?

Christian J. schrieb:
> So ist das
> ja nur ein Rechner der Software verarbeiten kann aber nichts steuern.
> CPM kann damit sicher nichts anfangen.
Das hängt von den CP/M-Programmen ab. Es gibt relativ viele, nur das 
Minimalsystem (Console-IO und Disk-IO) brauchen: TurboPascal, WordStar, 
Microsoft Basic, dBase, Zork etc. pp.

> Es stellt sich dann auch die
> Frage wie man eigene programme entwickelt denn mit dem sdcc wird das
> wohl kaum mehr gehen. Die müssen ja "für CPM sein".
Richtig. Reichen Dir die Entwicklungswerkzeuge, die hier unter 
Programming aufgeführt sind?:
http://pcsauro.altervista.org/CPM.PHP

Jens

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Frage wie man eigene programme entwickelt denn mit dem sdcc wird das
> wohl kaum mehr gehen.

Kannst es mit PL/I versuchen. ;-)
War ein recht bemerkenswertes Produkt.

> Die müssen ja "für CPM sein".

Nur wenn du auf dem Zielsystem entwickeln willst. Cross-Development auf 
dem PC mit sdcc geht natürlich immer noch.

von Christian J. (Gast)


Lesenswert?

Jens schrieb:
> Beim KC85/3 und Konsorten, gibt es eine Bootstrap-Schaltung. Dort liegt
> das BIOS bei 0xE000. Bei Reset wird für ein paar M1-Zyklen dieser
> Bereich nach 0x0000 gemappt. Als erstes steht ein Jump in den
> 'richtigen' ROM. Das Mapping wird nach diesem Jump deaktiviert

Na, man will ja auch Software schreiben und wenn es kompliziert ist 
diese auf den Rechner zu kriegen, dann macht es keinen Spass. Ich frage 
mich auch grad wofür man CPM braucht wenn man keine Peripherie hat, 
keine Tastatur, keinen Monitor.

Als ich so 16 war fing es mit dem Apple 2 an, da war das OS ein 
blinkender Cursor, es gab ein besch.... Basic und ein paar Format 
Befehle. Nichts was man wirklich damit machen konnte. Mehr als UCSD 
Pascal hat der 2e, den wir hatten nie gemacht und auf dem "Basis 108" 
lief ein Stundenplan Planungsprogram, was die Lehrer auf die Klassen 
aufteilte. Dauerte Tage bis das fertig war.

Klar weiss ich, dass ich das alles besser und schöner mit einer IDE auf 
meinem ARM7 Board haben kann, Luxus Debugger, GCC, Crossworks IDE dabei 
und schneller, 80Mhz, mehr Ports, mehr Timer ..... aber das Z80 
Minisystem macht mehr Spass und es ist überschaubarer, grünes Terminal, 
9600 baud ..... eben Retro :-) fehlt nur noch das Fiepen für die 9600 
baud.

Zerbreche mir noch den Kopf wie ich 2 Software SPI miteinander 
verheiraten kann, die im Arduino und die auf dem Z80. Das könnte 
verdammt eng werden vom Timing her, der Slave (Ardu) muss deutlich 
schneller sein als der Z80 beim Datenempfang.

von Christian J. (Gast)


Lesenswert?

A. K. schrieb:
> Technisch kein Problem, Umbau ist
> einfach.

Bei Fädeltechnik ist nix mehr mit Umbau :-) Bin froh, dass ich den 16kb 
ROM Umbau geschafft habe, 2 Drähte abknipsen, 3 neue verlegen. Ende. 
Kein draht geht mehr raus und nur noch wenige rein.

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> keine Tastatur, keinen Monitor.

CP/M funktioniert auch über die serielle Schnittstelle, und die ist in 
seinem Teil drin (mit SIO ;-). Der PC stellt Tastatur und Bildschirm.

von Christian J. (Gast)


Lesenswert?

A. K. schrieb:
> CP/M funktioniert auch über die serielle Schnittstelle, und die ist in
> seinem Teil drin (mit SIO ;-). Der PC stellt Tastatur und Bildschirm.

habe mir grad die Softwaresammlung angechaut.....Wahnsinn. Turbo Pascal 
würde mich mal wieder reizen.

Andererseits muss der Bau Zeitraum überschaubar bleiben. Wenn es erst 
ein halbes Jahr dauert, bis das System überhaupt Piep sagt lohnt es 
nicht. Dieser Searle hatr aber alles gut dokumentiert, d.h. Nachbau 1:1 
dürfte möglich sein. Software läuft ja soweit wie ich das sehe. Er 
selbst hat 2 Jahre gebraucht wie er schrieb.

Dummerweise finde ich die Eagle Files nirgendwo, müsste also abzeichnen.

von Christian J. (Gast)


Lesenswert?

@AK:

Überlege die ganze zeit noch für eine sinnvolle SPI Übertragung wegen 
der SD Karte. Selten sowas benutzt bisher.

Wie sind den Protokolle allgemein aufgebaut? Arbeiten die auch mit Start 
Token, warten auf Antwort usw? Es gibt unzählige Möglichkeiten.

Ich tendiere fast dazu einen Header für den Datensatz in einen Struct zu 
packen, den Header dann stur zum Slave zu ballern und der sucht sich 
dann die Eckedaten heraus, wieviele Bytes kommen, Filenamen, File 
Descriptor (1,2,3....). Filenamen will ich schon haben, Catalog Funktion 
auch, Sichern und Laden.



#define CMD_WR   'W'        // Token für Daten schreiben
#define CMD_RD   'R'        // Token für Daten lesen
#define CMD_HEAD 'H'        // Token für Daten Header
#define DUMMY     0xff
#define RES_ACK  'A'        // Response für "OK"
#define RES_WAIT 'T'        // Response für "Warte!"
#define RES_ERR  'E'        // Response für "Warte!"

////////////////////////////////////////////////////////////
// sd_write_data
//
// Schreibt einen Datensatz auf die SD Karte
// Eingabe:  file      = filenummer 1..255
//           *filename = Zeiger auf Filename
//           *data     = Zeiger auf Daten
//           len       = Länge der Daten in Bytes
// Rückgabe: 1 = ok, 0 = fehler


char sd_write_data(char fileno, char* filename, char* data, uint16_t 
len)
{
    uint16_t i,cnt;
    uint8_t resp;           // Antwort des Slaves

    // Slave aktivieren
    sd_select(true);
    delay8(100);

    // Start Token senden
    spi_transmit(CMD_WR);
    delay8(100);

    // Filenummer und Länge des Datensatzes senden
    spi_transmit(CMD_HEAD);
    spi_transmit(fileno);
    spi_transmit(HIBYTE(len));
    spi_transmit(LOBYTE(len));
    delay8(100);

    // Ganzen Datensatz senden
    cnt = 0;
    for (i=0;i<len;i++)
    {
        while(spi_get_wait());            // WAIT vom Slave?
        SetLedByte(*data);
        resp = spi_transmit(*(data++));   // WAIT vom Slave?
        if (resp==RES_ERR)                // Slave ok?
            return (0);
    }

    delay8(50);
    sd_select(false);

    return (1);

}

von S. R. (svenska)


Lesenswert?

A. K. schrieb:
> Für CP/M setzt das voraus, dass 64KB RAM vorhanden sind.
> CP/M erwartet sein BIOS am oberen Ende des Adressraums.

Normalerweise definiert man die Speichergröße (MSIZE). Daraus ergeben 
sich dann das Ende des gültigen Adressraums und die Adressen von CCP, 
BDOS und BIOS. Bei mir lebt alles in nur 32 KB, die Mindestanforderung 
von CP/M 2.2 sind 20 KB.

Ein Adressraum voller RAM macht Dinge natürlich trotzdem einfacher.

Christian J. schrieb:
> Die Decodierung hälst du also für unnötig? Irgendwie muss man ja aber
> umschalten.

Das kann im Zweifelsfall der allererste I/O-Zugriff sein, egal zu 
welcher Adresse, oder ein Gerät mit einem steuerbaren Pin (z.B. die 
serielle Schnittstelle - ein 16550 hat zwei Ausgänge).

> Es stellt sich dann auch die Frage wie man eigene programme entwickelt
> denn mit dem sdcc wird das wohl kaum mehr gehen. Die müssen ja "für CPM
> sein".

Warum sollte das nicht gehen? Ein CP/M-Programm ist ein an 0x100 
gelinkter Haufen Bytes, der über eine festgelegte Schnittstelle (JMP auf 
fixe Adressen) mit dem Betriebssystem redet. Wenn du die entsprechenden 
Syscalls dem SDCC beibringst, kannst du auch mit dem arbeiten. Ansonsten 
gibt es die ganze Software von damals immernoch.

> Ist das also nur ein Spielzeug um mal wieder CPM Software zu fahren?

Davor schriebst du noch, das ginge ohne Ports oder Timer nicht ginge. 
Geht also doch. ;-)

> Diese RAM Umschaltung hätte ich bei meinem auch machen sollen vorher.

Ist ja nicht so, dass das jetzt eine neue Information wäre. Aber du 
hörst ja nie zu.

Christian J. schrieb:
> Wie sind den Protokolle allgemein aufgebaut? Arbeiten die auch mit Start
> Token, warten auf Antwort usw? Es gibt unzählige Möglichkeiten.

Das Protokoll zwischen Z80 und "SD-Karten-Controller"? Das kannst du dir 
beliebig aussuchen. Mir fallen da drei Möglichkeiten ein, wenn es um die 
SD-Karte geht:
(a) Der Arduino ist eine SPI-Bridge. Dein Protokoll muss dann "read", 
"write" und Chipselect für jedes Gerät abbilden können. Auf dem Z80 
kannst du dann jedes beliebige SPI-Gerät ansteuern, musst aber sowohl 
das SD-Protokoll als auch das Dateisystem auf dem Z80 implementieren.
(b) Der Arduino spielt Block-Device. Dein Protokoll muss dann "read", 
"write" und "seek" abbilden können. Der Z80 muss dann nur das 
Dateisystem implementieren (CP/M bringt übrigens eins mit!) und kann 
über diese Schnittstelle auch nur auf SD-Karten zugreifen. FAT gibt's 
nur, wenn du auch FAT implementierst.
(c) Der Arduino spielt File-Server. Dein Protokoll muss dann alle 
Dateisystemfunktionen abbilden können, die du möchtest (Datei oder 
Verzeichnis öffnen, schließen, lesen, schreiben, löschen, erstellen, 
...) und der Z80 braucht dann diese Funktionen nur aufzurufen und die 
ganze Arbeit findet im Arduino statt.

Für irgendwas davon musst du dich schon entscheiden. Über die 
Datenstrukturen nachzudenken bringt dich an der Stelle nicht wirklich 
weiter, solange du nicht weißt, was du brauchst und was nicht.

Warum willst du dem Z80 überhaupt SPI in Software aufbürden? Der hat 
einen wunderbar schnellen, parallelen Bus mit genialen Zugriffsbefehlen.

Übrigens wiederhole ich nochmal einen Vorschlag: Deine serielle 
Schnittstelle kann all das, was du willst, auch machen. Du musst dafür 
keine SD-Karte an dein System frickeln. Das Protokoll der Wahl dafür 
nennt sich ZMODEM.

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

S. R. schrieb:
> Du musst dafür
> keine SD-Karte an dein System frickeln. Das Protokoll der Wahl dafür
> nennt sich ZMODEM.

Hi,

weiss ich aber ich implementiere kein ZMODEM auf dem Z80 Board, bzw 
schreibe ich sicherlich nicht selbst. Es wird ein Fileserver, der Ardu 
übernimmt alles, der Z80 hat nur 4 Befehle: write, read, seek, delete.
Jede Datei hat vorne 64 Bytes "Header Infos" und danach die Daten.

Header:
- Dateityp (exe, dat)
- Länge
- Filenummer
- Filename
- File Description

SPI Datentransfer ist auch 64 Bytes Header, danach justiert sich der 
Slave darauf, legt Datei an etc und nimmt Daten entgegen oder sendet 
sie. Arduino hat eine SD FAT light die nur ein Verzeichnis kennt und nur 
Basisfunktionen. Passt in 8k rein, der atmega328 hat 32k.

Fiel mir so ein aber vielleicht gibt es ja sowas auch im "üblichen 
Rahmen".

>Warum willst du dem Z80 überhaupt SPI in Software aufbürden? Der hat
>einen wunderbar schnellen, parallelen Bus mit genialen >Zugriffsbefehlen.

Hätte er...... wenn ich damals das wüsste was ich heute weiss.... der 
hat nämlich eine IDE on board quasi. CF Card direkt dran gepaoppt.
Mein Bus ist s.o.

von Christian J. (Gast)


Lesenswert?

Habe mir das mal für zu hause geholt. Hoffe ich bereue es nicht:

http://www.ebay.de/itm/271682004029

von Jens (Gast)


Lesenswert?

Christian J. schrieb:
> Hoffe ich bereue es nicht:
Da fehlen noch mindestens 8, besser 16 digitale Kanäle.
Und selbst für analoges hab ich gern 4 statt 2....

ebay: 161313879775
oder: 181288566514

Beides zusammen gibt es sicher auch noch irgendwo.

Jens

von Jens (Gast)


Lesenswert?


von Steffen S. (bitmaster)


Lesenswert?

michael_ schrieb:
> Den Begriff hab ich noch nie gehört. Beim aussprechen bricht man sich ja
> die Zunge. Erklär mal näher.

Sorry, habe mich vertippt. Meinte "relozieren"
Macht normalerweiser ein Loader im Monitor. Da ich nur das TDL-Basic
an eine andere Adresse haben wollte (nicht 300h sondern 4000h) habe ich
ein kleines vb.net-Programm dafür geschrieben.
Grundlage dafür:
http://www.seasip.demon.co.uk/Cpm/rel.html

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Jens schrieb:
> Christian J. schrieb:
>> Hoffe ich bereue es nicht:
> Da fehlen noch mindestens 8, besser 16 digitale Kanäle.
> Und selbst für analoges hab ich gern 4 statt 2....
>
> ebay: 161313879775
> oder: 181288566514
>
> Beides zusammen gibt es sicher auch noch irgendwo.
>
> Jens

Logic Analyser habe ich schon, einen für 25 Euro :-) Tuts aber. Nette 
Dinger aber Kohle fehlt für sowas.

von Christian J. (Gast)


Lesenswert?

@Leo:

Sag mal, wie kommt es dass der sddc für eine einfache Zuweisung an eine 
Struct Variable dermassen ein Fass aufmacht? Der Compiler kennt doch die 
Adressen aller member? Structs sollen ja effizient sein aber hier eher 
das Gegenteil. Auch mit -> sieht es schlimm aus.
1
Test normale Zuweisung, schön
2
3
                        302 ;sd.c:191: resp = fileno;
4
   46AE DD 7E 04      [19]  303   ld  a,4 (ix)
5
   46B1 DD 77 B9      [19]  304   ld  -71 (ix),a
6
7
8
Zuweisung von 8 Bit an Struct, waaaaas?
9
10
                            305 ;sd.c:192: file.no = fileno;                      // Filenummer
11
   46B4 21 03 00      [10]  306   ld  hl,#0x0003
12
   46B7 39            [11]  307   add  hl,sp
13
   46B8 4D            [ 4]  308   ld  c,l
14
   46B9 44            [ 4]  309   ld  b,h
15
   46BA 21 0A 00      [10]  310   ld  hl,#0x000A
16
   46BD 09            [11]  311   add  hl,bc
17
   46BE DD 56 04      [19]  312   ld  d,4 (ix)
18
   46C1 DD 7E 04      [19]  313   ld  a,4 (ix)
19
   46C4 17            [ 4]  314   rla
20
   46C5 9F            [ 4]  315   sbc  a, a
21
   46C6 5F            [ 4]  316   ld  e,a
22
   46C7 72            [ 7]  317   ld  (hl),d
23
   46C8 23            [ 6]  318   inc  hl
24
   46C9 73            [ 7]  319   ld  (hl),e
25
                            320 ;sd.c:193: file.len = len;                            // Datenlänge
26
   46CA 21 06 00      [10]  321   ld  hl,#0x0006
27
   46CD 09            [11]  322   add  hl,bc
28
   46CE DD 7E 0B      [19]  323   ld  a,11 (ix)
29
   46D1 DD 77 FC      [19]  324   ld  -4 (ix),a
30
   46D4 DD 7E 0C      [19]  325   ld  a,12 (ix)
31
   46D7 DD 77 FD      [19]  326   ld  -3 (ix),a
32
   46DA DD 36 FE 00   [19]  327   ld  -2 (ix),#0x00
33
   46DE DD 36 FF 00   [19]  328   ld  -1 (ix),#0x00
34
   46E2 C5            [11]  329   push  bc
35
   46E3 EB            [ 4]  330   ex  de,hl
36
   46E4 21 45 00      [10]  331   ld  hl, #0x0045
37
   46E7 39            [11]  332   add  hl, sp
38
   46E8 01 04 00      [10]  333   ld  bc, #0x0004
39
   46EB ED B0         [21]  334   ldir
40
   46ED C1            [10]  335   pop  bc

von Christian J. (Gast)


Lesenswert?

Und noch eine Sache:

Es ist leider so, dass gleiche Routinen sich sowohl im ROM als auch im 
Userprogramm finden, zb die Hardware Inits, Kommunikation usw. Gibt es 
mit einem C Compiler irgendeine Möglichkeit aus einem RAM Programm 
heraus Routinen im ROM anzuspringen? Der Variablenbereich des ROM ist 
unangetastet, der liegt unter der Decke bei  B0000, 2K sind reserviert.

Sowas wie

function lala (char.....) @ ROM-Adresse

Die Einsprünge kenne ich ja alle aus den .rst Files.

von (prx) A. K. (prx)


Lesenswert?

#define lala ((int(*)(char))0x1234)

von Christian J. (Gast)


Lesenswert?

Wenn ich das verstehen würde.... C kann schrecklich sein :-(

von (prx) A. K. (prx)


Lesenswert?

typedef int (*PtrToFunctionTakingCharReturningInt)(char);
#define lala ((PtrToFunctionTakingCharReturningInt)0x1234)

von Leo C. (rapid)


Lesenswert?

> Gibt es mit einem C Compiler irgendeine Möglichkeit aus einem RAM
> Programm heraus Routinen im ROM anzuspringen?

Der Linker hat einen Schalter, mit dem das möglich sein sollte:
1
          -o   Linked file/library object output enable (default)
2
          -v   Linked file/library object output disable
3
...
4
        The linker commands are explained in some more detail:  
5
...
6
             5.  -o/-v     Specifies      that     subsequent     linked
7
                 files/libraries will generate object  output  (default)
8
                 or  suppress  object  output.  (if option -i, -s, or -t
9
                 was specified)

Also erst das ROM-Programm normal compilieren und linken.
Dann die Funktionen für RAM-Progamm compilieren, und beim Linken nach 
dem Schalter -v die Object-Dateien der referenzierten ROM-Teile listen.

von Christian J. (Gast)


Lesenswert?

Hi,

klingt schwierig. ROM und RAM Programm sind zwei unterschiedliche 
Projekte, die nichts miteinander zu tun haben bisher. In Assembler wäre 
das kein Thema, wäre nur Register laden und JP dahin.

Das RAM Programm müsste ja genau wissen wie es Parameter an eine ROM 
Funktion übergeben müsste, die es ja nicht kennt.

zb:

Monitor Programm hat:

void delay(zeit)
{
.....

}

Adresse ist bekannt und fix im EPROM, bzw lässt sich herausfinden. zb 
0x1234

Das RAM Programm will nun diese Funktion nutzen.... und da scheiden sich 
die Geister.

von Christian J. (Gast)


Lesenswert?

@Leo:

Ich lege das problem oben erstmal beiseite. Fällt Dir eine Mögliochkeit 
ein, wie man zur Laufzeit feststellen kann, wie gross ein Programm in 
Bytes ist?

Oder wie man eine Kennung hinten am Code anbringt, so dass man aus dem 
laufenden Programm feststellen kann, wieviele Bytes man auf die SD 
sichern muss?

Leider hat der Z80 keinen internen E2PROM wie die AVR, PIC, ARM usw :-(

von Leo C. (rapid)


Lesenswert?

> Fällt Dir eine Mögliochkeit
> ein, wie man zur Laufzeit feststellen kann, wie gross ein Programm in
> Bytes ist?

Der Compiler generiert für jede Funktion Symbole in der Form
_functionname_start und _functionname_end

Also vielleicht sowas?
1
#include <stdio.h>
2
3
extern main_start(), main_end();
4
5
int main(void)
6
{
7
    printf("main start: %04x\n"
8
           "main end:   %04x\n"
9
           "main size:  %04x\n", 
10
           main, main_end, (void *) main_end - (void *) main);
11
    
12
    return 0;
13
}

main_start braucht man nicht, da es ja das gleiche wie main ist.
Ich kanns nicht laufen lassen, aber das Ergebnis sieht ganz gut aus:
1
                             48 ; Function main
2
                             49 ; ---------------------------------
3
   0113                      50 _main_start::
4
   0113                      51 _main:
5
                             52 ;hello.c:11: main, main_end , (void *) main_end - (void *) main);
6
   0113 21 34 01      [10]   53   ld  hl,#_main_end
7
   0116 11 13 01      [10]   54   ld  de,#_main
8
   0119 BF            [ 4]   55   cp  a, a
9
   011A ED 52         [15]   56   sbc  hl, de
10
                             57 ;hello.c:10: "main size:  %04x\n", 
11
   011C 11 34 01      [10]   58   ld  de,#___str_0+0
12
   011F E5            [11]   59   push  hl
13
   0120 21 34 01      [10]   60   ld  hl,#_main_end
14
   0123 E5            [11]   61   push  hl
15
   0124 21 13 01      [10]   62   ld  hl,#_main
16
   0127 E5            [11]   63   push  hl
17
   0128 D5            [11]   64   push  de
18
   0129 CD 94 01      [17]   65   call  _printf
19
   012C F1            [10]   66   pop  af
20
   012D F1            [10]   67   pop  af
21
   012E F1            [10]   68   pop  af
22
   012F F1            [10]   69   pop  af
23
                             70 ;hello.c:13: return 0;
24
   0130 21 00 00      [10]   71   ld  hl,#0x0000
25
   0133 C9            [10]   72   ret
26
   0134                      73 _main_end::

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Einfacher in der crt0.s :-) Muss man erstmal drauf kommen.

gsinit:

    ; Initialisiere globale Variablen im RAM
    ld  bc, #l__INITIALIZER
    ld  a, b
    or  a, c
    jr  Z, weiter
    ld  de, #s__INITIALIZED
    ld  hl, #s__INITIALIZER
    ldir

weiter:

    ; // Hier muss ein ret rein
    .area _GSFINAL
      ret
     ;Markiert das Ende des Programmes
     .ascii "FINAL"


Das Wort FINAL steht direkt nach dem CODE Segment. Beim Start des 
Programmes suche ich danach.

Jetzt würde nur noch ein Assemblerbefehl fehlen, der ein Alignment auf 
zb 16 Bytes vornimmt, damit ich den Suchlauf schneller machen kann, 
nämlich nur auf das erste von jeweils 16 Bytes auf F testen und dann 
gleich +16 Bytes weiter.

SPI etc läuift schon im Trockendock..... tolles Maschinchen :-) Mit dem 
sdcc macht es erst richtig Spass.

von Leo C. (rapid)


Lesenswert?

> Einfacher :-)

Na, Du wirst doch nicht behaupten, daß eine Suchschleife einfacher als 
die Subtraktion zweier Symbole ist.

Und wenn die Code-size einer einzelnen Funktion nicht das Gewünschte 
ist, dann suchst Du dir halt die passenden Symbole aus der Mapfile raus.

> Das Wort FINAL steht direkt nach dem CODE Segment. Beim Start des
> Programmes suche ich danach.

Aber nur, wenn Du es extra hinschreibst. Das braucht auch zusätzlichen 
Platz. Stattdessen kanns Du auch das Symbol "s__GSFINAL" nehmen.

> Jetzt würde nur noch ein Assemblerbefehl fehlen, der ein Alignment auf
> zb 16 Bytes vornimmt, damit ich den Suchlauf schneller machen kann,

Dann brauchst Du Dir über sowas auch nicht den Kopf zu zerbrechen. Du 
hast sowieso andere Probleme, als die Geschwindigkeit so einer kleinen 
Schleife. :-)

von Christian J. (Gast)


Lesenswert?

Leo C. schrieb:
> Na, Du wirst doch nicht behaupten, daß eine Suchschleife einfacher als
> die Subtraktion zweier Symbole ist

Ich weiss nicht was du da vorhast aber wo die Module stehen hängt nur 
davon ab in welcher Reihenfolge man sie linkt. Und die genannten Symbole 
erzeugen nur "kennt er nicht" Fehlermeldungen. ich brauche das in sd.c, 
da ist _main nicht bekannt.

von Georg G. (df2au)


Lesenswert?

Christian J. schrieb:
> Alignment auf zb 16 Bytes

Den SDCC Assembler kenne ich nicht. Meiner macht das mit

    org    (($ & 0xfff0) + 0x10)

von Christian J. (Gast)


Lesenswert?

Hallo Leo,

deine Methoden da funktionieren alle nicht. Die Symbole sind nicht 
bekannt in den einzelnen Modulen. Ob mit oder ohne Unterstrich.

Nur das hier funktioniert einwandfrei:
1
// Suche das Codeende
2
__at (RAM_START) uint8_t ram[RAM_SIZE];          // Bereich des RAM
3
uint16_t search_code_end()
4
{
5
    _Bool hit;
6
    uint16_t i;
7
8
    i = 0;
9
    hit = false;
10
    do {
11
        if (ram[i]=='$')
12
          if (ram[i+1]=='E')
13
            if (ram[i+2]=='O')
14
              if (ram[i+3]=='C')
15
                    hit = true;
16
      i++;
17
    } while (!hit || (i > 0xF000));
18
19
    return(i+3+RAM_START);
20
21
}

von Leo C. (rapid)


Lesenswert?

> org    (($ & 0xfff0) + 0x10)

Das funktioniert selten in relozierbaren Segmenten, und beim 
SDCC-Assembler ist org in REL-Segmenten gleich ganz verboten.

von Leo C. (rapid)


Lesenswert?

Christian J. schrieb:
> deine Methoden da funktionieren alle nicht. Die Symbole sind nicht
> bekannt in den einzelnen Modulen. Ob mit oder ohne Unterstrich.

Ich hatte ein vollständiges, auf Minimum reduziertes Beispiel gepostet, 
daß das Gegenteil beweist. Offensichtlich hast Du es nicht angesehen, 
zumindest die Zeile 3 nicht.

von Leo C. (rapid)


Angehängte Dateien:

Lesenswert?

Leo C. schrieb:
> Stattdessen kanns Du auch das Symbol "s__GSFINAL" nehmen.

Ok, da habe ich leider übersehen, daß diesen Symbolen das fürhrende "_" 
fehlt. In C kann man die nicht referenzieren.

Aber Statt einer Marke, die zusätzlich RAM belegt, und die Du suchen 
mußt, kannst Du Dir auch selber Symbole in den passenden Segmenten 
definieren (in crt0.s).

crt0.s:
1
  .area   _CODE
2
  .globl _code_segment_start
3
_code_segment_start:
4
  jp  init
5
6
;...
7
  .area   _GSFINAL
8
  ret
9
  .globl  _bis_hier_und_nicht_weiter
10
_bis_hier_und_nicht_weiter:

C:
1
#include <stdio.h>
2
3
extern char code_segment_start[];
4
extern char bis_hier_und_nicht_weiter[];
5
6
int main(void)
7
{
8
    printf("CODE start: %04x\n"
9
           "End of GSFINAL: %04x\n"
10
           "Length: %04x\n", 
11
             code_segment_start, bis_hier_und_nicht_weiter, 
12
             bis_hier_und_nicht_weiter - code_segment_start);
13
14
    return 0;
15
}

output:
1
   0113                      49 _main_start::
2
   0113                      50 _main:
3
                             51 ;hello.c:13: bis_hier_und_nicht_weiter - code_segment_start);
4
   0113 11 79 0C      [10]   52   ld  de,#_bis_hier_und_nicht_weiter+0
5
   0116 7B            [ 4]   53   ld  a,e
6
   0117 D6 00         [ 7]   54   sub  a, #<(_code_segment_start)
7
   0119 4F            [ 4]   55   ld  c,a
8
   011A 7A            [ 4]   56   ld  a,d
9
   011B DE 01         [ 7]   57   sbc  a, #>(_code_segment_start)
10
   011D 47            [ 4]   58   ld  b,a
11
                             59 ;hello.c:12: code_segment_start, bis_hier_und_nicht_weiter, 
12
                             60 ;hello.c:11: "Length: %04x\n", 
13
   011E 21 33 01      [10]   61   ld  hl,#___str_0
14
   0121 C5            [11]   62   push  bc
15
   0122 D5            [11]   63   push  de
16
   0123 01 00 01      [10]   64   ld  bc,#_code_segment_start
17
   0126 C5            [11]   65   push  bc
18
   0127 E5            [11]   66   push  hl
19
   0128 CD 93 01      [17]   67   call  _printf
20
   012B F1            [10]   68   pop  af
21
   012C F1            [10]   69   pop  af
22
   012D F1            [10]   70   pop  af
23
   012E F1            [10]   71   pop  af
24
                             72 ;hello.c:15: return 0;
25
   012F 21 00 00      [10]   73   ld  hl,#0x0000
26
   0132 C9            [10]   74   ret
27
   0133                      75 _main_end::

Nachtrag:
Im Anhang die vollständigen Dateien, incl. Map-Datei

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Leo C. schrieb:

> extern char code_segment_start[];
> extern char bis_hier_und_nicht_weiter[];

Warum so etwas? Arrays?

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Warum so etwas? Arrays?

Erspart den & Operator.

von Christian J. (Gast)


Lesenswert?

Läuft nicht. Sobald ich das einbaue kriege ich jede Menge Fehler an 
anderen Stellen die vorher liefen.

ASlink Warning_Undefined-Global "_main" refrence by module

usw.

ne ganze Latte.

Vergessen wir es. Das hatte ich schonmal.

PS: Nach realclean kompiliert es wieder. Mal sehen wie lange.

von Christian J. (Gast)


Lesenswert?

Geht nicht. Nach der nächsten Codeänderung völlig woanders
kommen wieder reihenweise die asmlink Fehler....

Jetzt doch wieder.... nach "realclean".... au weia..... bloss nicht mehr 
anfassen den Konstrukt. Aber Ergebnis mit printf ist richtig.

von Christian J. (Gast)


Lesenswert?

Die Geheimnisse dieses Compilers ind unergründlich :-)

Zuweisung der absoluten Werte und Umwandlung in Integerer

code_start = (uint16_t)erste_code_adresse;
code_end   = (uint16_t)letzte_code_adresse;

Das ham wa .... 4000 und 705D. Passt laut crts0.rst file
1
                         158 ;main.c:80: code_start = (uint16_t)erste_code_adresse;
2
   415E 3A 00 40      [13]  159   ld  a,(#_erste_code_adresse + 0)
3
   4161 32 5D 70      [13]  160   ld  (#_code_start + 0),a
4
   4164 3A 00 40      [13]  161   ld  a,(#_erste_code_adresse + 0)
5
   4167 17            [ 4]  162   rla
6
   4168 9F            [ 4]  163   sbc  a, a
7
   4169 32 5E 70      [13]  164   ld  (#_code_start + 1),a
8
                            165 ;main.c:81: code_end   = (uint16_t)letzte_code_adresse;
9
   416C 3A 5D 70      [13]  166   ld  a,(#_letzte_code_adresse + 0)
10
   416F 32 5F 70      [13]  167   ld  (#_code_end + 0),a
11
   4172 3A 5D 70      [13]  168   ld  a,(#_letzte_code_adresse + 0)
12
   4175 17            [ 4]  169   rla
13
   4176 9F            [ 4]  170   sbc  a, a
14
   4177 32 60 70      [13]  171   ld  (#_code_end + 1),a

Und mit xprintf und printf gleichermassen gibt er aus

Erste Codeadresse = ffc3
Letzte Codeadresse = ffc3

Verschieben wir die Zuweisung oben einfach etwas weiter nach oben im 
Main File, was eigentlich völlig egal ist erhalten wir:

Erste Codeadresse = ffc3
Letzte Codeadresse = 7e5D  <<<=== Hurra richtig!

Fazit:

Ich bin froh den sdcc zu haben (weil es nichts anderes Gescheites gibt) 
aber müsste ich damit beruflich arbeiten (solche Sachen habe ich mit dem 
Keil nie ...) würde ich das Ding zurückschicken und mir das Geld 
auszahlen lassen. Im industriellen Umfeld wäre der sdcc nicht 
"qualifizierbar" und in meiner Branche wo Source SIL 3 erfüllen muss 
auch "lebensgefährlich."

von Leo C. (rapid)


Lesenswert?

> code_start = (uint16_t)erste_code_adresse;
> code_end   = (uint16_t)letzte_code_adresse;

Wie diese 4 Variablen deklariert sind, verrätst Du uns (natürlich mal 
wieder) nicht.

Im Assembler-Code sieht man, daß "erste_code_adresse" und 
"letzte_code_adresse" statische oder globale, vorzeichenbehaftete 8-Bit 
Variablen sind.

>                          158 ;main.c:80: code_start =
> (uint16_t)erste_code_adresse;
>    415E 3A 00 40      [13]  159   ld  a,(#_erste_code_adresse + 0)

Lade Akku mit Inhalt von erste_code_adresse

>    4161 32 5D 70      [13]  160   ld  (#_code_start + 0),a

Speichern in low(code_start)

>    4164 3A 00 40      [13]  161   ld  a,(#_erste_code_adresse + 0)

Nochmal: (hier könnte optimiert werden)
Lade Akku mit Inhalt von erste_code_adresse

>    4167 17            [ 4]  162   rla
>    4168 9F            [ 4]  163   sbc  a, a

Vorzeichenerweiterung

>    4169 32 5E 70      [13]  164   ld  (#_code_start + 1),a

Speichern in high(code_start)


> Und mit xprintf und printf gleichermassen gibt er aus

Garbage in garbage out.

von Leo C. (rapid)


Lesenswert?

A. K. schrieb:
> Christian J. schrieb:
>> Warum so etwas? Arrays?

Wei Pointerarithmetik nur mit sinnvollen Datentypen richtig 
funktionieren kann.

> Erspart den & Operator.

Mit & hatte ich seltsame Ergebnisse. Da dürfre aber noch was anderes 
falsch deklariert gewesen sein.

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Leo C. schrieb:

> Mit & hatte ich seltsame Ergebnisse.

Mit & geht es auch aber umständlicher. Ich musste a auf void* casten 
damit ich überhaupt kompilieren kann. Ich habe jetzt einfach mal wild 
rumprobiert, bis ich den Wert der Symbole hatte und nicht deren Inhalt.

extern char erste_code_adresse[];         // Zeiger auf SDCC Globals
extern char letzte_code_adresse[];

// Adressen für Start und Ende des Programms
uint16_t code_start, code_end;

// Sichere die Start- und Endadresse des Codes
code_start = (uint16_t)erste_code_adresse;
code_end   = (uint16_t)letzte_code_adresse;
1
410C                      98 _main:
2
                             99 ;main.c:52: code_start = (uint16_t)erste_code_adresse;
3
   410C 21 00 40      [10]  100   ld  hl,#_erste_code_adresse+0
4
   410F 22 2E 65      [16]  101   ld  (_code_start),hl
5
                            102 ;main.c:53: code_end   = (uint16_t)letzte_code_adresse;
6
   4112 21 2E 65      [10]  103   ld  hl,#_letzte_code_adresse+0
7
   4115 22 30 65      [16]  104   ld  (_code_end),hl

Möchte man unbedingt ein Kaufmanns UND verwenden geht es so:

extern char erste_code_adresse;         // Zeiger auf SDCC Globals
extern char letzte_code_adresse;

// Adressen für Start und Ende des Programms
uint16_t *code_start, *code_end;

// Sichere die Start- und Endadresse des Codes
code_start = (void*)&erste_code_adresse;
code_end   = (void*)&letzte_code_adresse;

ergibt:

                          99 ;main.c:52: code_start = 
(void*)&erste_code_adresse;
   410C 21 00 40      [10]  100   ld  hl,#_erste_code_adresse+0
   410F 22 36 65      [16]  101   ld  (_code_start),hl
                            102 ;main.c:53: code_end   = 
(void*)&letzte_code_adresse;
   4112 21 36 65      [10]  103   ld  hl,#_letzte_code_adresse+0
   4115 22 38 65      [16]  104   ld  (_code_end),hl



Das passiert alles meist dann, wenn man das normale Fahrwasser eines 
Compilers verlässt und seine Spezialfunktionen benutzt. Denn hier greift 
man ja schon tief in die Kiste der Tricks.

von Leo C. (rapid)


Lesenswert?

Christian J. schrieb:
> // Adressen für Start und Ende des Programms
> uint16_t *code_start, *code_end;

Rechne hier mal die Differenz aus, und lass sie Dir ausgeben.

von Christian J. (Gast)


Lesenswert?

Leo C. schrieb:
> Rechne hier mal die Differenz aus, und lass sie Dir ausgeben.

Siehe Bild: sptr->len.
1
 // Header Infos zusammenbauen
2
    sptr->cmd = CMD_WR;
3
    sptr->type = TYPE_PROG;                         // Datentyp Programm
4
    sptr->crc  = crc;                               // Prüfsumme
5
6
    sptr->start_adress = code_start;                // Startadresse
7
    sptr->end_adress = code_end;                    // Endadresse
8
    sptr->len = code_end-code_start;                // Datenlänge
9
    strcpy(sptr->name,filename);                    // Dateiname
10
    strcpy(sptr->descr,filedescr);                  // Dateibeschreibung

Passt, ist ja auch identischer Asm Code.

von Leo C. (rapid)


Lesenswert?

> Siehe Bild: sptr->len.

Das ist mit ziemlicher Sicherheit etwas anderes.

von Christian J. (Gast)


Lesenswert?

Nee, da kam das Gleiche bei raus. Siehste doch an dem Asm Code. Ausgabe 
mit xprintf. Ich vermeide call by ref sowieso wo es nur geht und auch 
nur selten Pointer, außer eben beim Heap.

Du kannst dich damit ja gern weiter befassen, ich bin froh dass es mit 
[] läuft und fasse das Modul nicht mehr an. Allein schon die ganzen Asm 
Warnungen des Linkers vorher.

ps: sehe grad dass ich im modul sd.c bei dem "extern uint16_t 
code_start" die * vergessen habe. kann sein dass da ein ungewollter typ 
cast über die Module hinweg stattgefunden hat.

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Die nächste Zeit ist auch Arduino angesagt... da auf Z80 "blind" 
programmiert, ohne Testmöglichkeit, nur nach Logic Analyzer.

Es wächst nämlich grad noch ein Platinchen heran.... mit 32 Bit Counter, 
SD, programmierbarem Taktteiler (4020 und 74151), Display und noch ein 
paar Ausgängen für die nächste Stufe...

Es werden immer mehr :-)

von S. R. (svenska)


Lesenswert?

Hallo,

> Ich bin froh den sdcc zu haben (weil es nichts anderes Gescheites gibt)
> aber müsste ich damit beruflich arbeiten (solche Sachen habe ich mit dem
> Keil nie ...) würde ich das Ding zurückschicken und mir das Geld
> auszahlen lassen.

Die null Dollar, die du dafür bezahlt hast, kann sogar ich dir 
überweisen. Es gibt (gab) übrigens auch einen Keil-Compiler für den Z80, 
den ich allerdings nie gefunden habe. Der liefert aber vergleichbare bis 
bessere Ergebnisse als SDCC und dürfte auch zertifiziert sein. 
Alternativ gibt es auch noch den z88dk als C-Compiler, aber ob der 
bugfreier ist, weiß ich nicht.

> Im industriellen Umfeld wäre der sdcc nicht "qualifizierbar" und in
> meiner Branche wo Source SIL 3 erfüllen muss auch "lebensgefährlich."

Dafür ist er auch nicht gebaut. Siehe Keil.

> Die nächste Zeit ist auch Arduino angesagt... da auf Z80 "blind"
> programmiert, ohne Testmöglichkeit, nur nach Logic Analyzer.

Verstehe ich nicht. Ich hatte auf meinem System eine Turnaround-Zeit von 
vielleicht 15 Sekunden: "make all", Reset-Knopf drücken, Sende-Befehl 
des Terminals, Dateiname eingeben, paar Sekunden warten, Ergebnis 
gucken.

Aber ich bin positiv überrascht, mit wieviel Wucht du immer wieder mit 
dem Kopf gegen die Wände rennst (Türen ignorierend) - und trotzdem 
durchkommst.

Gruß,
Svenska

von Christian J. (Gast)


Lesenswert?

S. R. schrieb:
> Verstehe ich nicht. Ich hatte auf meinem System eine Turnaround-Zeit von
> vielleicht 15 Sekunden: "make all", Reset-Knopf drücken, Sende-Befehl
> des Terminals, Dateiname eingeben, paar Sekunden warten, Ergebnis
> gucken.

Glaubst du bei mir läuft das anders? 10s wenn es hoch kommt. NMI Reset 
für Laden, ./load Script aufrufen und das Ding rennt los. Aber das 
Extension Board ist noch nicht verdrahtet.

https://www.youtube.com/watch?v=gSVBFNCqYeQ

und

https://www.youtube.com/watch?v=PpTISqDCT0c

Das nächste Projekt steht schon in den Löchern.... Z80 mit Bildschirm 
und Tastatur, S100 Bus und CP/M :-) Aber keines dieser Kunstrgiff 
Systeme wie Atmega Emulator mit Z80 als Slave. ne eine richtige schöne 
Platine mit fetten Chips drauf und viel RAM :-)

Wäre aber auch mal was, ne CPU ohne CPU:

https://www.youtube.com/watch?v=GeSSkvwFDHs

Bin in dem Zaks Buch inzwischen schon weiter ..... frage mich dennoch 
wie Menschen die Nerven hatten ganze OS damit zu schreiben. Allerdings 
hat der Jäger damals auch Stunde damit verbracht seine Pfeilspitzen zu 
schärfen, damit sie dann, wenn das Wild auftaucht scharf sind :-)

Mal ne allgemeine Frage: Ich habe mir den Asm des sdcc ja nun oft genug 
angeschaut. Er ist clever genug aus einem strcpy oder memcpy eine LDIR 
Schleife zu bauen. Also die mächtigen Befehle zu nutzen, die mit 
Mikrocode abgearbeitet werden.

Wie ist denn überhaupt die Nutzung des gesamten Befehlsvorrates? Gefühlt 
nutzt er nicht mehr als 20 Befehle. Ich vermute man könnte in Asm locker 
30% Code einsparen.

von Holm T. (Gast)


Lesenswert?

Christian J. schrieb:
[..]
> Wie ist denn überhaupt die Nutzung des gesamten Befehlsvorrates? Gefühlt
> nutzt er nicht mehr als 20 Befehle. Ich vermute man könnte in Asm locker
> 30% Code einsparen.

Du bist auf dem besten Wege herauszufinden warum RISC CPUs erfunden 
wurden.

Ich denke nicht das das auf den Z80 zutrifft aber bei der VAX gab es 
komplexe Maschinenbefehle deren Ausführungszeiten länger waren als ihr 
Ersatz durch Befehlsfolgen einfacherer Befehle.

Gruß,

Holm

von (prx) A. K. (prx)


Lesenswert?

Holm Tiffe schrieb:
> Du bist auf dem besten Wege herauszufinden warum RISC CPUs erfunden
> wurden.

http://www.cis.upenn.edu/~milom/cis501-Fall08/papers/cocke-RISC.pdf

"There even were instances of code which, when compiled for the 801 and 
simulated on a System/370 Model 168, ran faster in real time than the 
same program run directly on a Model 168 when compiled by the PL/I 
compiler."

: Bearbeitet durch User
von Erich (Gast)


Lesenswert?

Christian J. schrieb:
> Im industriellen Umfeld wäre der sdcc nicht
> "qualifizierbar" und in meiner Branche wo Source SIL 3 erfüllen muss
> auch "lebensgefährlich."

Sorry daß ich mich hier mal einmische und eurer Trialog störe.
Aber obige Aussage von Christian, daß er beruflich was mit "SIL 3" zu 
tun hätte, und das in Verbindung mit seiner Arbeitsweise was hier auf 
inzwischen 1329 Beiträgen dokumentiert ist ...

Das macht mir richtig Angst.

Gruss

von Holm T. (Gast)


Lesenswert?

A. K. schrieb:
> Holm Tiffe schrieb:
>> Du bist auf dem besten Wege herauszufinden warum RISC CPUs erfunden
>> wurden.
>
> http://www.cis.upenn.edu/~milom/cis501-Fall08/papers/cocke-RISC.pdf
>
> "There even were instances of code which, when compiled for the 801 and
> simulated on a System/370 Model 168, ran faster in real time than the
> same program run directly on a Model 168 when compiled by the PL/I
> compiler."

...das ist ja schräg... allerdings lief emuliertes Linux bzw. Linux 
Programme auf einem FreeBSD eine Zeit lang auch schneller als auf Linux.
Da war der Emulator aber mehr oder weniger ein system call wrapper und 
dadurch war das Ganze auch erklärbar.

Bei dem VAX Beispiel, das ich von cctalk@classiccmp.org habe, frage ich 
mich allerdings was die Designer des Microcodes geraucht haben wenn sie 
es hinkriegen das eine Makrobefehlsfolge schneller ist als der 
spezialisierte Mikrocode auf der selben Maschine..

Ich weiß nicht viel über PL/1 aber IMHO sagt da schon was über die 
Qualität und Implementation des Compilers aus.

Die DDR hat mal eine wenig kompatible und als langsamste geltende PDP11 
gebaut, den Robotron K1630 mit asynchroner CPU. Für das Ding gabs 
Einschubplatinen um einen KRS4200 emulieren zu können um alte Programme 
ablaufen zu lassen. Wenn dieser Emulator lief, war der Rest der Maschine 
nicht für anderes verwendbar. Der Emulator aber seinerseits war deutlich 
schneller als der eigentliche KRS4200 (Honeywell 9000).

Gruß,

Holm

von (prx) A. K. (prx)


Lesenswert?

Holm Tiffe schrieb:
> Bei dem VAX Beispiel, das ich von cctalk@classiccmp.org habe, frage ich
> mich allerdings was die Designer des Microcodes geraucht haben wenn sie
> es hinkriegen das eine Makrobefehlsfolge schneller ist als der
> spezialisierte Mikrocode auf der selben Maschine..

Bekannt war diese Verhalten von den CALLx/RET Befehlen. Die waren als 
eierlegende Wollmilchsau so komplex geraten, dass viele auf die 
einfacheren und schnelleren JSB/RSB auswichen.

Der Microcode eines komplexen Befehls muss alle möglichen Optionen des 
Befehls zur Laufzeit einzeln durchprobieren. Nur um meist festzustellen, 
dass es nichts zu tun gibt. Das kostet Zeit. Ein Compiler hingegen 
erzeugt einfach nur die Befehle, die wirklich benötigt werden.

: Bearbeitet durch User
von Holm T. (Gast)


Lesenswert?

A. K. schrieb:
[..]
> Der Microcode eines komplexen Befehls muss alle möglichen Optionen des
> Befehls zur Laufzeit einzeln durchprobieren. Nur um meist festzustellen,
> dass es nichts zu tun gibt. Das kostet Zeit. Ein Compiler hingegen
> erzeugt einfach nur die Befehle, die wirklich benötigt werden.

..nein, im konkreten Falle war das nicht der Call, ich glaube das war 
irgend ein String Befehl, wobei ich aber auch weiß das der Call auf VAX 
so ziemlich "Alles" macht..

Klar ist das die CPU immer ein "Interpreter" für die Maschinensprache 
ist
der sich durchwursteln muß.Das ist aber beim Microcode zu einem 
Maschinenbefehl auch nicht anders und auch da kann man gut oder 
beschissen programmieren.

Gruß,

Holm

von Christian J. (Gast)


Lesenswert?

Erich schrieb:
> Sorry daß ich mich hier mal einmische und eurer Trialog störe.
> Aber obige Aussage von Christian, daß er beruflich was mit "SIL 3" zu
> tun hätte, und das in Verbindung mit seiner Arbeitsweise was hier auf
> inzwischen 1329 Beiträgen dokumentiert ist ...
>
> Das macht mir richtig Angst.
>
> Gruss

Wahrscheinlich schlackerst du jetzt auch nicht mit den Ohren, wenn ich 
dir sage, dass einige Fachbeiträge über die 61508, 62061, 13849 und Co. 
in Fachzeitungen auf mein Konto gehen und ich sogar Schulungen gehalten 
habe, wie diese anzuwenden sind. Gefühlt so ca 500 Geräte mit SIL3 
wurden von mir für den Markt zugelassen, mehrere davon in 
Kernkraftwerken (Reaktorabschaltung, Sicherheitsbereich E1). Du lebst ja 
noch, also scheinen sie zu funktionieren.

Du verwechselst ARBEIT mit HOBBY! Wie viele hier leider....

Schönen Tag noch!

von Christian J. (Gast)


Lesenswert?

Holm Tiffe schrieb:

> Du bist auf dem besten Wege herauszufinden warum RISC CPUs erfunden
> wurden.

 Das frage ich mich auch, wenn ich mir den Befehlssatz einer Intel CPU 
heute anschaue..... ein PIC kommt mit 35 Befehlen aus...... für alles.

von (prx) A. K. (prx)


Lesenswert?

Holm Tiffe schrieb:
> Das ist aber beim Microcode zu einem
> Maschinenbefehl auch nicht anders und auch da kann man gut oder
> beschissen programmieren.

Auch Microcode kocht nur mit Wasser. Wenn für einen komplexen Befehl 
beispielsweise 20 Microinstructions benötigt werden, von denen die 
Hälfte nutzloser Code ist, der nicht zum Ergebnis beiträgt, dann kommt 
eine Ersatzsequenz vielleicht nur mit 10 Microinstructions aus. Und das 
liegt dann nicht daran, dass der Programmierer besoffen war, sondern 
dass der komplexe Befehl Teile enthält, die in diesem Fall nicht 
benötigt werden.

Wenn du einen Befehl alles reinsteckst, was irgendwie mal gebraucht 
werden könnte, dann ist manches davon nur selten notwendig. Dieser Teil 
des Microcodes kostet beim Komplexbefehl trotzdem Takte, nur um 
festzustellen, dass ein Wert 0 ist. Der Compiler lässt das in der 
Ersatzsequenz aber von vorneherein weg.

Als Beispiel Intels ENTER/LEAVE Befehle. Diese Befehle hatte Intel für 
Sprachen vom PASCAL Typ vorgesehen, die lokale Funktionen mit Zugriff 
auf die Variablen der umgebenden Funktion unterstützen. Dazu benötigt 
man einen viel komplexeren Stackframe als für C, weil ein Pointer-Array 
kopiert werden muss.

In C lässt sich der ENTER Befehl auch nutzen, es wird dann aber nur ein 
Teil von dessen Funktion genutzt. Einer der Parameter des Befehls ist 
dann stets 0 (nesting level). Der Microcode des Befehls muss zur 
Laufzeit überprüfen, ob der Wert 0 ist, das kostet Zeit. Die 
Ersatzsequenz enthält diesen Teil nicht, das kostet also dann keine 
Laufzeit. Folglich waren bei Intel die 2-3 Ersatzbefehle schneller als 
Intels ENTER Befehl. Der folglich von kaum einem C Compiler verwendet 
wurde.

Zweites Beispiel, wenn der Prozessor bei einem Speicherzugriffsfehler 
den Befehl abbricht, um ihn später erneut ausführen zu können. Meistens 
ist das so realisiert (m.W. auch bei den VAXen), nur die 68000 Familie 
arbeitete anders. Dann darf während der Laufzeit nichts geschehen, das 
dies verhindern könnte. Das kann Microcode deutlich komplizierter machen 
als eine exakt äquivalente Ersatzsequenz.

So kann es bei Befehlen mit mehreren möglicherweise gleichen oder gar 
überlappenden Speicheradressen nötig werden, dass der Microcode zur 
Laufzeit die Speicheradressen vergleicht, um abhängig davon verschiedene 
Pfade einzuschlagen. In einer Ersatzsequenz aus einfachen Befehlen ist 
das einerseits oft unnötig, da sie zwischendrin unterbrochen werden 
kann. Andererseits kann der Compiler eigenes Wissen über die Beziehung 
der Operanden einbringen.

Dieses Problem kann bereits bei einem scheinbar einfachen Befehl 
auftreten, der nur von Speicher zu Speicher addiert.

Komplexe Befehle erzeugen also künstlich Probleme und Laufzeit, die man 
in Ersatzsequenzen aus einfachen Befehlen überhaupt nicht hat.

: Bearbeitet durch User
von Thomas (Gast)


Lesenswert?

Christian J. schrieb:
> Gefühlt so ca 500 Geräte mit SIL3
> wurden von mir für den Markt zugelassen, mehrere davon in
> Kernkraftwerken (Reaktorabschaltung, Sicherheitsbereich E1). Du lebst ja
> noch, also scheinen sie zu funktionieren.
Offensichtlich ist in diesen Geräten keine CPU drin. Oder sie wurde 
nicht von Dir programmiert...

> Du verwechselst ARBEIT mit HOBBY!
Hmm. Warum machst Du Dir die Methoden, die Du professionell nutzt, nicht 
auch privat zu eigen?

von Christian J. (Gast)


Lesenswert?

A. K. schrieb:
> Komplexe Befehle erzeugen also künstlich Probleme und Laufzeit, die man
> in Ersatzsequenzen aus einfachen Befehlen überhaupt nicht hat.

Beeindruckend deine Kenntnisse über die Architektur! Enter und Leave 
habe ich damals auch nie gebraucht ca 1995 als ich viel Intel Asm 
machte, und auch viele andere nicht.  Gehört zwar nicht hierhin aber als 
1996 die PIC auftauchten war das wie eine Erlösung. Gestehe, dass ich 
niemals auch nur einen in Asm programmierte. Erst denr extrem buggy Byte 
Craft Compiler benutzt, dann den CCS ab 2000.

Die Compiler waren allerdings limitiert und auch heute kennt der CCS als 
bekanntester Vertreter wohl noch kein char** ptr. Indizierte 
Adressierung kannten die PIC nicht, nur ein Destination Bit, was angab 
wo etwas zu speichern ist, W oder Speicherzelle. Da ist sicherlich viel 
Grips reingeflossen den absolut minimalen einen Befehlssatz zu 
erarbeiten, der ohne Microcode auskommt und daher sehr schnell ist, 
nämlich nur 1 Takt pro Befehl. Da die Programmierung eh "maschinennah" 
auch in C erfolgt ist der Code sehr kompakt
1
....................       ////////////////////////////////////// 
2
....................       // Die Bedeutung des Bytes rec_byte auswerten 
3
....................       ////////////////////////////////////// 
4
....................  
5
....................       // --- Byte 1: Startsignatur 
6
....................       if (byte_counter == 1)  { 
7
0027E:  DECFSZ xE5,W
8
00280:  BRA    028E
9
....................         if (rec_byte != IRID) { 
10
00282:  MOVF   xE3,W
11
00284:  SUBLW  83
12
00286:  BZ    028C
13
....................           ir_state = 0; 
14
00288:  CLRF   xE0
15
....................           goto ende;   
16
0028A:  BRA    0308
17
....................       }}   
18
....................  
19
....................       // --- Byte 2: Anzahl folgender Bytes 
20
....................       else if (byte_counter == 2) {         
21
0028C:  BRA    0306
22
0028E:  MOVF   xE5,W
23
00290:  SUBLW  02
24
00292:  BNZ   029C
25
....................         no_bytes = rec_byte;  // Empfangsgrenze setzen 
26
00294:  MOVFF  E3,E6
27
....................         dat_cnt  = 0;      // Datenbytezähler rücksetzen 
28
00298:  CLRF   xE7
29
....................       }   
30
....................  
31
....................       // --- Byte 3: N Datenbytes empfangen 
32
....................       else if ((byte_counter >=3 ) && (dat_cnt < no_bytes))    
33
0029A:  BRA    0306
34
0029C:  MOVF   xE5,W
35
0029E:  SUBLW  02
36
002A0:  BC    02C0
37
002A2:  MOVF   xE6,W
38
002A4:  SUBWF  xE7,W
39
002A6:  BC    02C0
40
....................       { 
41
....................         ir_rx_data[ir_rx_pointer++] = rec_byte;    // Byte in Datenfeld schieben 
42
002A8:  MOVF   xDD,W
43
002AA:  INCF   xDD,F
44
002AC:  CLRF   03
45
002AE:  ADDLW  8C
46
002B0:  MOVWF  FE9
47
002B2:  MOVLW  00
48
002B4:  ADDWFC 03,W
49
002B6:  MOVWF  FEA
50
002B8:  MOVFF  E3,FEF
51
....................         dat_cnt++;

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Beeindruckend deine Kenntnisse über die Architektur! Enter und Leave
> habe ich damals auch nie gebraucht ca 1995 als ich viel Intel Asm
> machte, und auch viele andere nicht.  Gehört zwar nicht hierhin aber als
> 1996 die PIC auftauchten war das wie eine Erlösung.

Intels x86 Architektur war zwar nie der Brüller, aber dass es wahrhaftig 
jemanden gibt, der die 8-Bit PICs als Erlösung gegenüber 386 betrachtet, 
das hätte ich nicht gedacht. ;-)

von Christian J. (Gast)


Lesenswert?

Das hasse ich an diesen Forum.... dieses "in den Mund legen" :-( PIC 
waren fürs Hobby wie eine Erlösung, nach dem 8051 Krampf, 8048 usw.
Als der 16F84 rauskam habe ich Tage damit verbracht den auszureizen.

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Das hasse ich an diesen Forum.... dieses "in den Mund legen" :-(

Sachte. Ich dachte mir zwar, dass du 8051 meintest, aber ENTER/LEAVE 
sind nun einmal x86 Befehle. So eine Vorlage darf man sich nicht 
entgehen lassen. ;-)

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Und es ist nach wie vor "faszinierend", dass der kleine 14-Pinner in der 
Mitte zusammen mit einem Schieberegister in der Lage ist Sensoren 
auszulesen, Temperatur, Feuchte anzuzeigen, nebenbei noch eine Anzeige 
zu multiplexen ohne dass diese flackert (ok, ich lese im Umschaltmoment 
aus, da fällt es nicht auf :-) und alles was er macht auch noch über 
eine Uart auszugeben , Min Max im internen EEPROM speichert usw. usw. 
Mag sein das verwöhnte Bengels das heute alles als "normal" bezeichnen. 
Von sowas hätte ich als 18-20 Jähriger nur geträumt damals.

Ne ähnliche Schaltung, die ich 1996 mit dem 8051 machte (ext. EPROM, NTC 
Widerstand) undderen Hartpapier Platte heute so schwarz braun statt 
hellbraun aussieht läuft aber heute auch noch, EPROMs schaffen also 
mindestens 20 Jahre :-)

von greg (Gast)


Lesenswert?

So krampfig ist der 8051 doch auch nicht. Jedenfalls nicht schlimmer als 
diese krude PIC12-Architektur. :o

von (prx) A. K. (prx)


Lesenswert?

greg schrieb:
> So krampfig ist der 8051 doch auch nicht. Jedenfalls nicht schlimmer als
> diese krude PIC12-Architektur. :o

Vielleicht meinte er auch 8048. Die war deutlich krampfiger als 8051. 
PIC12 ist ok, wenn man in Assembler programmiert und das Programm 
maximal 256 Worte umfasst. Für grössere Programme war diese Architektur 
- die übrigens die älteste noch genutzte µC Architektur ist - nie 
gedacht. Und als Ziel von Compilern schon garnicht.

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

greg schrieb:
> So krampfig ist der 8051 doch auch nicht. Jedenfalls nicht schlimmer als
> diese krude PIC12-Architektur. :o

Soso, 256 Byte Stack limiert.... ok, Keil Compiler damals gehabt. Immer 
ein EPROM mit dran was Pins kostete. Und 100 DM für einen 87C51 
ausgegeben, der nach 3 Mal Brennen kaputt war :-(


Ich warte noch auf die hier für Z80:

http://en.wikipedia.org/wiki/MCU_8051_IDE#mediaviewer/File:MCU_8051_IDE_-_I.png

von Christian J. (Gast)


Lesenswert?

A. K. schrieb:
> PIC12 ist ok, wenn man in Assembler programmiert und das Programm
> maximal 256 Worte umfasst

Das sind die im SOT-23 Gehäuse, 6 Pins, winzige Dinger, die aber reichen 
um zb eine IR Diode zu modulieren, TSOP zu decodieren usw. Nie gehabt, 
nur Asm, kein Debug Interface usw.

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Soso, 256 Byte Stack limiert.... ok,

Nix Stack. Der Code. Notfalls 512 Worte, wenn man die Unterprogramme in 
die erste Hälfte davon legt. Bis dahin geht es ohne Banking ab. Mehr 
hatte der PIC1650A auch nicht. Dass dessen Architektur auch 4 Jahrzehnte 
später noch lebt hatte GI sicher nicht auf der Rechnung.

von Christian J. (Gast)


Lesenswert?

A. K. schrieb:
> Mehr hatte der PIC1650A auch nicht.

Der ist ja auch Steinzeit.... kein Mensch benutzt den mehr.

von Christian J. (Gast)


Lesenswert?

@Frank Meyer:

Wäre es ohne weiteres für Dich möglich eine nützliche Option in den 
Minieditor getstrng einzubauen, nämlich dass er den Buffer in das 
Eingabefeld ausdruckt? Dieser müsste also leer sein wenn man ein leeres 
Feld haben will aber es wäre sehr nützlich um fehlerjafte Eingaben zu 
korrigieren ohne alles neu tippen zu müssen. Habe den Parser 
geschrieben, der die Kommandozeile zerlegt und da es jede Menge gibt, 
was ein User (auch wenn nur ich es bin) da verbrechen kann wäre eine 
"Back" Funktion sehr nützlich.
1
do {
2
     getnstr(cl.inbuf,CMDBUFSIZE-1);
3
         while (strlen(cl.inbuf)==0);
4
5
     // String splitten
6
     res = split_command(&cl)
7
 } while (res != 1);
8
9
......
10
11
12
char split_command(struct cl_type* ptr)
13
{
14
    char cbuf[CMDBUFSIZE];               // Kopie Eingabepuffer
15
    char* cptr;                          // Zeiger auf Eingabepuffer
16
    char j,i;
17
18
    // Stringzerlegung (ok!)
19
    strcpy(cbuf,ptr->inbuf);    // String Kopie erzeugen
20
21
    j = 0;
22
    cptr = strtok(cbuf," ");             // Zeichentrenner und erster Abschnitt
23
    strcpy(ptr->cmd,cptr);
24
    while(cptr != 0 && (j < MAX_PARAMS))
25
    {
26
        cptr = strtok(NULL," ");
27
        if (cptr != NULL) {
28
            if (strlen(cptr)<PARAM_LEN)
29
                strcpy(ptr->param[j++],cptr);
30
            else
31
                return (2);
32
        }
33
    }
34
35
    ptr->no_params = j;
36
37
    #ifdef DEBUG_COMMAND
38
    // Testweise Ausgabe der Zerlegung
39
    xprintf("\rcmd: %s",ptr->cmd);
40
    xprintf("\rParams: %d",j);
41
    for (i=0;i < ptr->no_params;i++)
42
        xprintf("\rParam %d : %s",i,ptr->param[i]);
43
    #endif
44
45
    // 1 = ok
46
    return (1);
47
}

von Christian J. (Gast)


Lesenswert?

S. R. schrieb:

> Übrigens wiederhole ich nochmal einen Vorschlag: Deine serielle
> Schnittstelle kann all das, was du willst, auch machen. Du musst dafür
> keine SD-Karte an dein System frickeln. Das Protokoll der Wahl dafür
> nennt sich ZMODEM.

Ich habe Dich "erhört" :-) Nur ist es nicht ZMODEM geworden, sondern 
wegen der kleineren Codegröße XMODEM. 10 Minuten einfügen, etwas 
umstellen, CRC mit rein nehmen, getchar und putchar einbauen, 
Kompilieren ohne Fehler und der Test war dann schnell gemacht, dass 
minicom Dateien entgegen nimmt und abspeichert und auch der Z80 sie von 
dort laden kann.

Wie war das noch mit Boris Becker? "Bin ich schon drin?" :-)

Schön, neben der SD Karte eine weitere Möglichkeit Programme 
einzuspielen und zu sichern. Damit haben  wir

a) Intel Hex Upload
b) SD karte
c) XMODEM

und die 16kb sind leider auch bald voll :-((

http://www.menie.org/georges/embedded/xmodem.c

von S. R. (svenska)


Lesenswert?

Christian J. schrieb:
>> Das Protokoll der Wahl dafür nennt sich ZMODEM.
> Ich habe Dich "erhört" :-) Nur ist es nicht ZMODEM geworden, sondern
> wegen der kleineren Codegröße XMODEM.

Naja, immerhin. Das ZMODEM hatte übrigens auch einen konkreten 
Hintergrund, denn es bietet dir drei relevante Vorteile gegenüber 
XMODEM:
- ZMODEM überträgt Dateinamen
- ZMODEM überträgt byte-exakte Dateigrößen (XMODEM paddet auf 128 Byte)
- ZMODEM kann automatische Übertragungen (in beide Richtungen, wenn ich 
mich recht entsinne) anstoßen.

Damit kannst du dir schon fast einen richtigen Fileserver bauen. Wenn 
dein Terminal gut genug ist, dann braucht der Z80 nur noch "gib mir 
TEST.TXT" sagen und bekommt die Datei, ohne dass du in deinem Terminal 
den Namen nochmal angeben müsstest.

Gruß,
Sebastian

von Christian J. (Gast)


Lesenswert?

Hi,

naja, es klappt ja. Aber es soll ja eigentlich von ner Karte kommen. Der 
Z80 wird später mal nur an einen Raspi kommen, mit Monitor und Keyboard. 
PC als Stromfresser soll nur zur Entwicklung sein.

Leider habe ich mir mit dem Intel Hex Decoder auch die doppelte Upload 
Zeit eingefangen wie vorher auf Binärebene. Aber es klappt auch und ist 
sehr einfach, wenn man vieles weglässt und auch keine Fehlerprüfungen 
mehr als die Prüfsumme einbaut.

Interessieren würde mich mal woher die Z80er und Co. auf ebay etc 
derzeit noch kommen. Ist das NOS Ware? Oder wird der immer noch 
hergestellt? Habe einen von 1981 und andere von 1994 hier.

Für den ders nachbauen will hier der Source. Lässt man die Prüfsumme 
auch noch weg wird es noch um 30% kürzer.
1
/////////////////////////////////////////////////////////////////////
2
// 1 Zeichen Hex -> Int konvertieren
3
// Eingabe: "C" = 0x43
4
// Ausgabe: 0x0C
5
uint8_t xtoc (uint8_t ch)
6
{
7
    uint8_t val;
8
9
    // Grossbuchstaben
10
    if (ch >= 'A' && ch <= 'F')
11
        val = (ch - 'A') + 10;
12
    //Kleinbuchstaben
13
    else if (ch >= 'a' && ch <= 'f')
14
        val = (ch - 'a') + 10;
15
    else
16
        val = (ch - '0');
17
18
    return val;
19
}
20
21
// Intel Hex Decoder
22
23
__at (ROM_START) unsigned char mem[0xffff];         // Kompletter Speicher
24
25
char ihx_decode()
26
{
27
    #define DATA_RECORD     0x00
28
    #define END_RECORD      0x01
29
30
   struct ihx_data {
31
       uint8_t len;
32
       uint16_t adress;
33
       uint8_t record;
34
       uint8_t csum;
35
   } ihx;
36
37
   uint8_t ch,i,lo,hi,k,data;
38
   uint8_t check;
39
   uint8_t p[4];
40
41
42
   k = 0;
43
   do {
44
        // warte auf Startzeichen
45
        do ch = getchar();
46
        while (ch!=':');
47
48
        ihx.csum = 0;
49
50
        // Hole Anzahl Zeichen des Satzes
51
        hi = xtoc(getchar());
52
        lo = xtoc(getchar());
53
        ihx.len = (hi <<4 )+lo;
54
        ihx.csum += ihx.len;
55
56
        // Hole Zieladresse des Satzes
57
        for (i=0;i<4;i++)
58
            p[3-i] = xtoc(getchar());
59
60
        // Setze Adresse zusammen
61
        ihx.adress = (p[3]<<12) | (p[2]<<8) | (p[1]<<4) | p[0];
62
        ihx.csum += (p[1]<<4) | p[0];
63
        ihx.csum += (p[3]<<4) | p[2];
64
65
        // Hole Record Type
66
        hi = xtoc(getchar());
67
        lo = xtoc(getchar());
68
        ihx.record = (hi<<4)+lo;
69
        ihx.csum += ihx.record;
70
71
        // Record 0 = Daten
72
        if (ihx.record == DATA_RECORD)  {
73
            // Hole Datenbytes ab
74
            for (i=0;i<ihx.len;i++)
75
            {
76
                hi = xtoc(getchar());
77
                lo = xtoc(getchar());
78
                data = (hi <<4 )+lo;
79
                mem[ihx.adress+i] = data;
80
                ihx.csum += data;
81
                SetLedByte((hi<<4)+lo);
82
            }
83
84
            ihx.csum = (~ihx.csum) +1;
85
86
            // Prüfsumme holen
87
            hi = xtoc(getchar());
88
            lo = xtoc(getchar());
89
            check = (hi<<4)+lo;
90
91
            // Prüfsumme überprüfen
92
            if (check != ihx.csum)
93
                return (0);
94
95
            SetDigit(k++);
96
        }
97
98
    } while (ihx.record != END_RECORD);
99
100
    // Dateiende abwarten
101
    do
102
        ch = getchar();
103
    while (ch!=0x0a);
104
105
    return (1);
106
107
}

von Erich (Gast)


Lesenswert?

Der Programmcode 08.12.2014 12:02 erscheint mir wiederum sehr 
hobbymäßig, eher sogar mäßig bis ...mäßig. Jedenfalls nicht SIL3.
Früher ---als es noch keine SIL Stufen gab--- da hat man schon erst das 
Zeugs dekodiert und erst in den Speicher geschrieben wenn alles ok war.
Aber das war früher...
Gruss

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Erich schrieb:

> Der Programmcode 08.12.2014 12:02 erscheint mir wiederum sehr
> hobbymäßig, eher sogar mäßig bis ...mäßig. Jedenfalls nicht SIL3.

Rede keinen Stuss und melde Dich erstmal an, bevor Du hier rumnörgelst. 
Code muss funktionieren und das tut er. Schönheitspreise braucht er 
nicht zu bekommen. SIL3 bei Z80? Gehts noch? Und bevor ich nicht von Dir 
etwas hier gesehen habe halt dich bitte mit Kommentaren zurück.

Jedenfalls kam er heute an, gebraucht für 149 Ocken. Sehr nettes 
Teilchen!

von (prx) A. K. (prx)


Lesenswert?

Das Zeug daneben finde ich interessanter: Den nicht sehr erdbebenfest 
wirkenden und schon windschiefen Plattenstapel auf Platte. ;-)
Der Rest ist leider nicht zu erkennen. Freiluft-Server?

: Bearbeitet durch User
von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

A. K. schrieb:
> Freiluft-Server?

Yep, ein 24/7 Server mit 6 TB, kleine Cubietruck "Fabrik"..... da drauf 
wird ja auch alles Z80 Zeug entwickelt, kompiliert, hochgeladen usw.

von Christian J. (Gast)


Lesenswert?

AK?

Kann es sein, dass die 8254 Timer schweineheiss werden? Den hier kann 
man kaum länger anfassen. Pendelt sich bei 41 Grad ein. 1W Pv laut 
Datenblatt aber ich frage mich wo der die lässt. Gibt es die auch als 
CMOS? Oder den 8253? Der Stromverbrauch nähert sich den 500mA.....

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> 1W Pv laut Datenblatt

170mA max, sagt mir das Datasheet. Yep, darf also warm werden.

> Der Stromverbrauch nähert sich den 500mA.....

Nur vom 8254? Das wären 2,5W und das wäre mehr als warm, mehr als 
schlappe 41°C würde ich meinen.

CMOS wäre 82C54. Ganz dem Intel'schen Schema entsprechend.

: Bearbeitet durch User
von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

A. K. schrieb:
> CMOS wäre 82C54. Ganz dem Intel'schen Schema entsprechend.

500mA alles zusammen inzwischen, beide Platinen. Der 8254 zieht sich 
135ma aktuell rein.

Und schon 82C54 "bestellt" in der Bucht..... geht nicht dass der hier 
die Bude aufheizt. Die 7805 sind auch an ihrer Grenze.

Anbei die Peripherie, noch nicht ganz fertig aber frag bitte keiner nach 
dem "Sinn", es ist einfach nur etwas Elektronik, die sich konfigurieren 
lässt, mehr nicht. Das ist das Problem bei solchen Sachen.... eine echte 
Anwendung haben sie nicht :-(

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Es wächst und wächst und falls mir noch was einfällt ist die nächste 
Leiste schon zur 3.ten Platine fertig :-)

Bisher:

3 x 16 Bit Timer verkettet zu 32 Bit + 1 Interrupt Ausgang für Z80
1: 4 bis 1:14 Teilbarer 1Mhz Takgeber per Multiplexer
SD Karten Interface mit Atmega 328
8 zusätzliche Inputs seriell
8 zusätzliches Output seriell
1 x Display mit Controller
1 x 8255 zur "Verwaltung" des Ganzen.

Blöd, dass der 8255 sich nicht on the fly umschalten lässt an seinen 
Ports. Jede Änderung des Config Wortes resettet die anderen Register 
auch.

Und wenn mal Langeweile ist, das ist der Original Schaltplan des Spiels 
PONG von Atari mit TTL Gattern....

von Holm T. (Gast)


Lesenswert?

Christian J. schrieb:
> Es wächst und wächst und falls mir noch was einfällt ist die nächste
> Leiste schon zur 3.ten Platine fertig :-)
>
> Bisher:
>
> 3 x 16 Bit Timer verkettet zu 32 Bit + 1 Interrupt Ausgang für Z80
> 1: 4 bis 1:14 Teilbarer 1Mhz Takgeber per Multiplexer
> SD Karten Interface mit Atmega 328
> 8 zusätzliche Inputs seriell
> 8 zusätzliches Output seriell
> 1 x Display mit Controller
> 1 x 8255 zur "Verwaltung" des Ganzen.
>
> Blöd, dass der 8255 sich nicht on the fly umschalten lässt an seinen
> Ports. Jede Änderung des Config Wortes resettet die anderen Register
> auch.

Das wurde Dir doch aber vorher gesagt. Warum baust Du auch die 
schrottigen Intel Teile ein die es schon gab bevor der Z80 mit seiner 
Peripherie entwickelt wurde?

Stelle Dein Oszi woanders hin sonst hast Du bei Zeiten Zinn Spritzer auf 
dem Display.

Das Paket ist angekommen..THX.

Gruß,

Holm

von Christian J. (Gast)


Lesenswert?

Leo?

Hast Du ne Erklärung dafür?

Definiere ich das hier global

struct ihx_data {
       uint8_t len;
       uint16_t datalength;
       uint16_t recordsets;
       uint16_t adress;
       uint8_t record;
       uint8_t csum;
} ihx;

kommt das bei raus:


  195 ;tools.c:79: ihx.datalength = 0;
   00A1 21 00 00      [10]  196   ld  hl,#0x0000
   00A4 22r01r00      [16]  197   ld  ((_ihx + 0x0001)), hl

Definere ich es aber in der Funktion, also "dynamisch" explodiert das zu
1
                        192 ;tools.c:80: ihx.datalength = 0;
2
   00A3 21 00 00      [10]  193   ld  hl,#0x0000
3
   00A6 39            [11]  194   add  hl,sp
4
   00A7 DD 75 EF      [19]  195   ld  -17 (ix),l
5
   00AA DD 74 F0      [19]  196   ld  -16 (ix),h
6
   00AD DD 7E EF      [19]  197   ld  a,-17 (ix)
7
   00B0 C6 01         [ 7]  198   add  a, #0x01
8
   00B2 DD 77 F9      [19]  199   ld  -7 (ix),a
9
   00B5 DD 7E F0      [19]  200   ld  a,-16 (ix)
10
   00B8 CE 00         [ 7]  201   adc  a, #0x00
11
   00BA DD 77 FA      [19]  202   ld  -6 (ix),a
12
   00BD DD 6E F9      [19]  203   ld  l,-7 (ix)
13
   00C0 DD 66 FA      [19]  204   ld  h,-6 (ix)
14
   00C3 AF            [ 4]  205   xor  a, a
15
   00C4 77            [ 7]  206   ld  (hl), a
16
   00C5 23            [ 6]  207   inc  hl
17
   00C6 77            [ 7]  208   ld  (hl), a

Die ganze Routine betreffend, wo der struct zigmal angefasst wird
sind das 500 (!!!) Bytes. Absolut ineffzienter Code.

Solte man da nicht lieber fast alle global machen, bzw. quasi 
Universalvariablen einführen, statt dynamische zu verwenden?

Lege ich den struct in den Heap sieht es ähnlich aus wie unten. :-(

Irre!

von Leo C. (rapid)


Lesenswert?

1
;f.c:3: void f(void)
2
;       ---------------------------------
3
; Function f
4
; ---------------------------------
5
_f_start::
6
_f:
7
        ld      hl,#-9
8
        add     hl,sp
9
        ld      sp,hl
10
;f.c:14: ihx.datalength = 0;
11
        ld      hl,#0x0001
12
        add     hl,sp
13
        xor     a, a
14
        ld      (hl), a
15
        inc     hl
16
        ld      (hl), a
17
        ld      hl,#9
18
        add     hl,sp
19
        ld      sp,hl
20
        ret
21
_f_end::
22
        .area _CODE
23
        .area _INITIALIZER
24
        .area _CABS (ABS)

von Christian J. (Gast)


Lesenswert?

Ja, bitte?

von (prx) A. K. (prx)


Lesenswert?

Übersetzung: Er kann es nicht reproduzieren. Bei ihm kommt optimaler 
Code raus.

: Bearbeitet durch User
von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Ah, der Dolmetscher...

naja, ich kann es umso besser reproduzieren, da ich das an mehren 
Stellen ausprobiert habe. Globale Vars erzeugen erheblich weniger Code 
als dynamische. Ob die Knapserei mit dem RAM also Sinn macht sei mal 
dahingestellt. Der Heap ist sonst gern meine Rettung aber auch der wird 
über Zeiger angesprochen.

Nochmal deutlich: Definiert man einen Struct IN einer Funktion, so dass 
er dynamisch angelegt wird, so explodiert die Codemenge. Legt man ihn 
nach außen, samt seiner Variablen, so ist es prima.

von Leo C. (rapid)


Angehängte Dateien:

Lesenswert?

Christian J. schrieb:
> Ja, bitte?

Du meinst, wir hätten bessere Glaskugeln als Du?
Mit den Codefetzen, die Du hier immer abwirfst, kann man jedenfalls auch 
nicht mehr anfangen.

A. K. schrieb:
> Übersetzung: Er kann es nicht reproduzieren. Bei ihm kommt optimaler
> Code raus.

Genau. Dabei habe ich alle Informationen, die Christian geliefert hat, 
für das Beispiel verwendet.

Christian J. schrieb:
> Nochmal deutlich: Definiert man einen Struct IN einer Funktion, so dass
> er dynamisch angelegt wird, so explodiert die Codemenge.

Das ist mal wieder so eine falsche Behauptung die Du nicht bewiesen 
hast. Mein Beispiel zeigt, daß das Gegenteil richtig ist. Jedenfalls in 
diesem Einzelfall.

Inzwischen habe ich mir mal Deinen Kot-Brocken (tools.c) angeschaut. 
Zuerst habe ich alles auskommentiert, was ich nicht kompilieren kann. 
Die Funktion, um die es Dir offensichtlich geht, ihx_decode(), ist aber 
größtenteils noch drin. Kompiliert mit Standard-Optimierung (sdcc -mz80 
--opt-code-size -c) zu 0x1fe Bytes.
Dann habe ich etwas umorganisiert und das umständliche hin- und 
herkopieren zwischen Hilfsvariablen beseitigt. --> Größe ist auf 0x107 
Bytes, also fast nur noch die Hälfte, implodiert. Man könnte auch sagen, 
die Codemenge explodiert durch ineffiziente Programmierung.

Ich vermute, Sinn der "struct ihx_data" soll sein, daß die gesammelten 
Hex-Record-Daten an andere Programmteile weitergereicht werden können. 
Deshalb habe ich die struct nicht in der Funktion deklariert (wäre ja 
sinnlos), sondern ihr einen Pointer übergeben. Codegröße jetzt 0x223 
Bytes, also mehr als verdoppelt. Aber der Compiler kann ja noch 
optimieren, in dem man dem Register Allocator mehr Zeit läßt. Und hier 
bringt das besonders viel. Mit "--max-allocs-per-node 70000" kommt 
0x1bf raus.

von Christian J. (Gast)


Lesenswert?

Leo C. schrieb:
> Das ist mal wieder so eine falsche Behauptung die Du nicht bewiesen
> hast. Mein Beispiel zeigt, daß das Gegenteil richtig ist. Jedenfalls in
> diesem Einzelfall.

Ich lass es. Ich bin blind, ich habe keine Ahnung und ich schreibe 
Blödsinn, den ich nicht beweisen kann. Der Codefetzen da oben ist auch 
sicherlich nur ausgedacht.

Die Zusammenfassung der Mehrfachaufrufe und die Prüfsummenauslagerung 
habe ich inzwischen auch gemacht, allerdings etwas anders. Die erste 
Lösung muss funktionieren, optimieren kann man immer noch. Trotzdem gute 
Ideen dabei, ich lerne was dazu.

Optimierung ist aus, die hat mir nur Murks mal erzeugt. Code der ohne 
Opt läuft und mit nicht mehr kann ja nicht so richtig sein.

Danke für die Mühe!

Mit --max-allocs-per-node 70000 kompiliert er inzwischen seit 13 
Minuten.....

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Und noch weiter tot optimiert, da der struct nicht mehr extern gebraucht 
wird sieht es dann so aus, was dem profi hoffentlich auch gefällt :-)

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Optimierung ist aus, die hat mir nur Murks mal erzeugt.

Hältst du das nicht für ziemlich frech? Optimierung abschalten und dann 
über unoptimierten Code meckern?

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

Großer Dank an Holm für seine "Wundertüte" !!!

Und man glaubt es nicht...... "Sowjetiski 8255 brrrauchen mehrrr Strom, 
Towarisch aber wenn sie erstmal warm sind dann laufen sie auch". Gehäuse 
scheint entweder aus gepresstem Schwarzbrot zu bestehen oder Bakellit.

Und auch DDR "Klon" im 2,5er Raster spielt (nachdem der 
Kessel-Dampfdruck hoch genug war) hatte wie gewünscht trotz Probleme mit 
der Beinfreiheit im Sockel .... allerdings liegt die Stromaufnahme 
gesamt inzwischen bei dramatischen 550mA für die vielen NMOS Chips und 
der 7805 gerät langsam ins Schwitzen ohne Kühlkörper.

von IGOR (Gast)


Lesenswert?

Christian J. schrieb:

> Und man glaubt es nicht...... "Sowjetiski 8255 brrrauchen mehrrr Strom,
> Towarisch aber wenn sie erstmal warm sind dann laufen sie auch". Gehäuse
> scheint entweder aus gepresstem Schwarzbrot zu bestehen oder Bakellit.


Diese ICs waren doch teilweise lichtdurchlässig, was zur folge hatte daß 
sie im prallen Sonnenlicht nicht mehr zuverlässig funktionieren und 
resetten auch mit dem Blitzlicht (richtiges, nicht LED...) möglich ist. 
:-) Schon mal getestet???

Glaube der Effekt war seinerzeit beim ostzonalen Taschenrechner 
"Konkret600" öfter zu beobachten. Reperatur nur an dunklen 
Arbeitsplätzen möglich...

von Holm T. (Gast)


Lesenswert?

@Christian: ich habe dir extra die braunen 8255A geschickt weil die 
"besser" aussahen. Mit der Stromaufnahme von NMOS ICs mußt Du freilich 
klar kommen,
wobei diese Teile da noch relativ genügsam sind, weitaus mehr Strom 
wurde damals (TM) in deren Umgebung durch TTL ICs verbraten. Z80 wuren 
in der DDR auch später als CMOS Versionen gebaut, bin mir aber nicht 
sicher ob ich da was da habe..

Über die seltsame Farbe der russischen Vergußmassen wird "ostzonal" 
(danke IGOR für diesen Begriff, Du mußt wohl westzonal sein, Zone aber 
auf jeden Fall) schon öfter gerätselt und ich habe den Verdacht das das 
Zeug auch mehr Wasser aufnimmt als die gewöhnliche schwarze Vergußmasse 
die auch in der DDR benutzt wurde. Die Farbe ging von kackbraun bis zu 
spinatfahl.. Was fürs Auge sind russische Keramik-ICs in 
Schweinchenrosa/Gold..

Hier mal ein Bild (1,1Mb!) einer russischen LSI11/03 CPU, die rosa Teile 
sind 4kx1 DRAMs kompatibel zu I2107:

http://www.tiffe.de/Robotron/PDP-VAX/E60/E60-01.jpg

Einen lichtempfindlichen Taschenrechner hatte ich auch schon, allerdings 
war der komplett russisch, ein Konkret 600 dürfte so selten gewesen sein 
das kaum Jemand das Vergnügen gehabt haben dürfte das Teil reparieren zu 
dürfen.

Gruß,

Holm

von Christian J. (Gast)


Lesenswert?

Hi,

hauptsache sie spielen und das tun sie. Bevor ich wieder teuer bestellen 
muss aus China überlege ich mir, ob der MK3801-04 auch wohl 5Mhz an 
seinem Timer und Clock aushält oder ob ich doch auf den -06 wechseln 
muss, der sehr selten ist? Ich brauche höhere Baudraten und eine 
schnellere CPU und da wären 19600 die nächste durch Taktverdoppelung. 
-06 bisher nur in China gefundenm, das dauert wieder 6 Wochen bis der 
hier ist.

Da ich an dem "Homebrew Z80" immer noch Spass habe, jedenfalls mehr als 
an einem fertigen ARM, AVR usw, wo eh alles funktioniert wächst der 
Monitor inzwischen beachtlich an. Kompletter Kommandoparser ist fertig 
und wird grad " Size optimiert", wobei die 16Kb Grenze das Limit des 
Machbaren darstellt.

Dass der sdcc bei --max-allocs-per-node 50000 auch locker mal 1kB bei 
13kb Code einspart war mir auch neu, nur dauert das eben locker 10min zu 
kompilieren, so dass es nur für den "relaese" in Frage kommt. Fuer Frank 
haette ich noch nette Funktionen, die ich mir zu mcurses dazugeschrieben 
habe aber er scheint ja weg zu sein.....

von (prx) A. K. (prx)


Lesenswert?

Holm Tiffe schrieb:
> Hier mal ein Bild (1,1Mb!) einer russischen LSI11/03 CPU, die rosa Teile
> sind 4kx1 DRAMs kompatibel zu I2107:

Ist das Ding mal mit der Axt repariert worden?

von Holm T. (Gast)


Lesenswert?

A. K. schrieb:
> Holm Tiffe schrieb:
>> Hier mal ein Bild (1,1Mb!) einer russischen LSI11/03 CPU, die rosa Teile
>> sind 4kx1 DRAMs kompatibel zu I2107:
>
> Ist das Ding mal mit der Axt repariert worden?

Nee, aber wahrscheinlich mies transportiert (bei der Verschrottung).

Der RAM funzt aber noch (also kein Grund zum tauschen ,,hätte aber 
Ersatz da), die ganze Platine bzw. der gesamte Rechner geht wieder und 
man kann darauf das Tetris-Original oder auch SpaceInvaders spielen.
Der Rechner ist zwar groß, aber das dazugehörige Doppelfloppy ist 
größer.

http://www.tiffe.de/Robotron/PDP-VAX/E60/CPU-oben.jpg

Gruß,

Holm

von Christian J. (Gast)


Lesenswert?

Holm Tiffe schrieb:

> Nee, aber wahrscheinlich mies transportiert (bei der Verschrottung).

Die Block-C's sehen aber auch schon schön "knusprig" aus .... gut 
gelagert sicher. Und die TTL sind wohl mit dem Hammer in Position 
gebracht worden?

Ich frage mich grad so wie es in deinem Keller aussieht oder der Garage? 
Ist da noch Platz fur ein Auto? Und hat deine Frau noch keinen Wutanfall 
bekommen  "SCHMEISS DEN MÜLL ENDLICH MAL WEG!"

Jedenfalls merke ich grad schmerzlich, dass es besser gewesen wäre den 
ROM/RAM Switch einzubauen, damit alles RAm ist nach dem Booten :-( Fast 
alle fertige Software ist so konstruiert, dass sie nur aus einem RAM 
laufen kann.

von Joe G. (feinmechaniker) Benutzerseite


Lesenswert?

IGOR schrieb:
> Diese ICs waren doch teilweise lichtdurchlässig, was zur folge hatte daß
> sie im prallen Sonnenlicht nicht mehr zuverlässig funktionieren

Leider nicht nur Sonnenlicht oder intensives Blitzlicht. Eine normale 
Schreibtischlampe bei der Fehlersuche reichte aus um absolut irreguläres 
Verhalten zu erzeugen :-(

von Holm T. (Gast)


Lesenswert?

Christian J. schrieb:
> Holm Tiffe schrieb:
>
>> Nee, aber wahrscheinlich mies transportiert (bei der Verschrottung).
>
> Die Block-C's sehen aber auch schon schön "knusprig" aus .... gut
> gelagert sicher.

Die sind nur oberflächlich angekratzt, kein Grund zur Aufregung.

>
> Ich frage mich grad so wie es in deinem Keller aussieht oder der Garage?
> Ist da noch Platz fur ein Auto? Und hat deine Frau noch keinen Wutanfall
> bekommen  "SCHMEISS DEN MÜLL ENDLICH MAL WEG!"

Die habe ich nach 14 Jahren wilder Ehe erst in diesem September 
geheiratet und das hätte ich kaum getan wenn sie ein Problem mit dieser 
Art Hobby gehabt hätte.
War halt sorgfältiger Testbetrieb... :-)


>
> Jedenfalls merke ich grad schmerzlich, dass es besser gewesen wäre den
> ROM/RAM Switch einzubauen, damit alles RAm ist nach dem Booten :-( Fast
> alle fertige Software ist so konstruiert, dass sie nur aus einem RAM
> laufen kann.

Loriot: Ach?

Lernen durch Schmerz ist ein bewährtes Verfahren Christian..

Schaue mal Deinen Thread hier durch wie viele Leute Dir das anempfohlen 
haben.

Gruß,

Holm

von Christian J. (Gast)


Lesenswert?

Holm Tiffe schrieb:
> Lernen durch Schmerz ist ein bewährtes Verfahren Christian..

Ja... ich müsste so ca 150 Drähte wieder lösen aus den Fädelkämmen, die 
proppevoll sind,  2 ICs rausholen, eines noch dazu bauen .... und wenn 
ich fertig wäre, würde nichts mehr funktionieren und das Ding in hohem 
Bogen über Nachbars Zaun gefeuert werden.....

von Christian J. (Gast)


Lesenswert?

Aber nur mal theoretisch.....

... 0          0x4000                    0xffff
ROM -----------
RAM ###########|##########################

Wenn man es so verschaltet, dass jeder Schreib Zugriff aufs RAM geht 
würde der Monitor das Userprogramm direkt ab 0x0000 laden können. Seine 
eigenen Variablen würden dann oberhalb liegen, zb ab 0xF000

Dann kommt der Moment des Umschaltens ...... dann müsste quasi 
glöeichzeitig noch ein Sprung auf 0x0000 ausgeführt werden, was aber 
nicht geht, da der erste Portzugriff sofort umschalten würde. Der JP 
start würde nicht mehr erreicht werden.

Mit dem SDCC ist es nicht möglich Code zu erzeugen, der beliebig liegen 
kann, daher kann sich der Monitor nicht selbst hochkopieren und von dort 
weitermachen, das ginge nur mit Asm.

Alternativ wäre das ROM oben ab 0xC000 und den Umschaltbefehl im 
Userprogramm liegen zu haben. Dann würde nur der obere Teil wegklappen 
und RAM frei werden. geht aber nicht weil bei 0x0000 ein JP liegen muss 
bei Coldstart.

Wie wird es nun wirklich gemacht?

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Wenn man es so verschaltet, dass jeder Schreib Zugriff aufs RAM geht

Yep. Wie ich oben schon schrieb.

> würde der Monitor das Userprogramm direkt ab 0x0000 laden können.

Wie ich oben auch schon schrieb: Kopier als erste Aktion den Monitor 
über sich selber und schalte einfach um. Von dem Moment an läuft der 
Monitor aus dem RAM, ohne dass die CPU einen Unterschied sieht, und du 
kannst dann tun und lassen was du willst.

von Christian J. (Gast)


Lesenswert?

A. K. schrieb:
> Wie ich oben auch schon schrieb: Kopier als erste Aktion den Monitor
> über sich selber und schalte einfach um. Von dem Moment an läuft der
> Monitor aus dem RAM, ohne dass die CPU einen Unterschied sieht, und du
> kannst dann tun und lassen was du willst.

Ok, kein Problem.... die Umschalteschaltung habe ich schon. Einfach 
einen freien I/O Decoderausgang mit einem Flip Flop aus freien NANDs (2 
sind eh übrig) verheiraten und die WR Leitung mit in den CS für das RAM 
mischen.

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> und die WR Leitung mit in den CS für das RAM
> mischen.

Nicht ganz. Du musst das ROM abschalten, nicht das RAM.

von Christian J. (Gast)


Lesenswert?

A. K. schrieb:
> Von dem Moment an läuft der
> Monitor aus dem RAM, ohne dass die CPU einen Unterschied sieht, und du
> kannst dann tun und lassen was du willst.

>Nicht ganz. Du musst das ROM abschalten, nicht das RAM.
Klar, über den OE des ROMs...

Naja.....

Es müsste erstmal das userprogramm geladen werden und das wurde auf 
einen fixe Adresse 0x0000 kompiliert. Der IHx8 Loader könnte es beim 
Laden auf einen 0x4000 Offset setzen, so dass es im RAM über dem Monitor 
liegt.

Dann muss das Userprogramm aber dahin wo der Monitor liegt und das geht 
nicht, weil der ja läuft....

von Georg G. (df2au)


Lesenswert?

Christian J. schrieb:
> ROM/RAM Switch einzubauen

Wenn schon Retro, dann nimm ein GAL zwecks Umschaltung. das sortiert dir 
die /CE für RAM oben und unten und das Eprom. Und das Flipflop ist auch 
gleich drin. Wenn du möchtest, macht es dir (später) auch die 
Bankumschaltung für mehr als 64k RAM.

Und noch ein Tipp: Nimm ein Flash Eprom als Boot Medium. Dann kannst du 
dir sogar deinen Monitor noch bequem ändern.

von Holm T. (Gast)


Lesenswert?

Es gibt mehrere Varianten, ROM lesen und RAM schreiben bis zum umkippen 
eines Flipflops (durch I/O Request) ist eine Variante. Sowas habe ich 
mal in einem GAL gemacht als ich einen Z80 Emuf aufgebohrt hatte.


title    EMUF Decoder
pattern  IODEC
revision C
author   Tiffe
date     22.01.2011

CHIP IODEC GAL16V8


;pin  1    2     3     4     5     6     7     8     9     10
     RESET MREQ IORQ  RD    A15   A7    A6     A5    A4    GND
;
;pin 11   12    13    14    15    16    17    18     19    20
     GND P0CS  P1CS  CTCS   SICS  Q0  ROMCS  RA0CS  RA1CS  VCC



EQUATIONS
                /ROMCS  = /A15 * /MREQ  * /RD * /Q0

                /RA0CS  = /A15 * /MREQ * Q0

                /RA1CS  = A15  * /MREQ


                /P0CS   = /A7 * /A6 * /A5 * /A4 * /IORQ

                /P1CS   = /A7 * /A6 * /A5  A4  /IORQ

                /CTCS   = /A7 * /A6  A5  /A4 * /IORQ

                /SICS   = /A7 * /A6  A5  A4 * /IORQ

;               /EXCS   = /A7  A6  /A5 * /A4 * /IORQ

                Q0      =  /RESET * Q0 + /RESET * /A7  A6  A5  A4  
/IORQ


; end of IODEC

Gruß,

Holm

von (prx) A. K. (prx)


Lesenswert?

Also:

FF = Flipflop:
Boot: Lesen:ROM, Schreiben:RAM
RAM:  Lesen/Schreiben:RAM
In ROM/RAM Dekodierung ist kein Adressbezug drin.

Sinngemäss, unoptimiert:

RAM:/CS1 = CPU:/MREQ
RAM:CS2  = high
RAM:/OE  = FF=Boot ? high : CPU:/RD
RAM:/WE  = CPU:/WR

ROM:/CS  = FF=Boot ? high : CPU:/MREQ
ROM:/OE  = CPU:/RD

von Christian J. (Gast)


Lesenswert?

Ich erinnere mich ganz schwach an meine 486er ASM Zeiten.... da war es 
so dass ein EXE File eine Tabelle mitführte, wo im Code die absoluten 
Sprünge sind. Der DOS Lader patschte dann einzelne jede Adresse nach dem 
Laden des Code, damit dieser relokatibel wurde.

von Christian J. (Gast)


Lesenswert?

>> Wenn schon Retro, dann nimm ein GAL zwecks Umschaltung.

GAL programm habe ich, aber keine Ahnung von den Dingern und keine 
Software die Gleichungen einzugeben. Wäre aber noch besorgbar, wenn es 
nicht DOS ist, da ich kein Windows mehr habe.

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Dann muss das Userprogramm aber dahin wo der Monitor liegt und das geht
> nicht, weil der ja läuft....

Dann kompilier den Monitor auf die Adresse 0xC000.

Nur die ersten Paar Bytes vom Resetcode werden ab Adresse 0 ausgeführt, 
kopieren 0x0000:3FFF nach 0xC000, schalten auf RAM um und springen in 
den entsprechenden Entrypoint in den Monitor an 0xCxxx. Dann ist 0 frei.

von Holm T. (Gast)


Lesenswert?

Der Gal Code oben ist kaputt, ich hätte ein paar Code Tags drum herum 
bauen müssen..., naja denke sowieso nicht das Du mit einem GAL hantieren 
willst.

Gruß,

Holm

von Christian J. (Gast)


Lesenswert?

A. K. schrieb:
> FF = Flipflop:
> Boot: Lesen:ROM, Schreiben:RAM
> RAM:  Lesen/Schreiben:RAM
> In ROM/RAM Dekodierung ist kein Adressbezug drin.

http://searle.hostei.com/grant/cpm/CPMSchematic1.2.gif

Das erklärt immer noch nicht wie das Userprogramm da hinkommen soll wo 
noch der Monitor läuft.....

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> http://searle.hostei.com/grant/cpm/CPMSchematic1.2.gif

Yep, das hatten wir auch schon mal, irgendwo in einer CP/M Diskussion. 
UC6:C/D sind unnötig wenn die Kopier/Umschaltaktion sofort nach dem 
Reset erfolgt. Solange gibts zwar kein lesbares RAM, aber dafür brauchst 
du auch keines.

von Christian J. (Gast)


Lesenswert?

A. K. schrieb:
> Nur die ersten Paar Bytes vom Resetcode werden ab Adresse 0 ausgeführt,
> kopieren 0x0000:3FFF nach 0xC000, schalten auf RAM um und springen in
> den entsprechenden Entrypoint in den Monitor an 0xCxxx. Dann ist 0 frei.

Das geht deshalb nicht weil der Monitor ein Startup Programm enthält und 
bei der Erzeugung des IHX8 Files dann alles zwischen 0x0000 und 0xC0000 
mit Nullen gefüllt würde und das geht dann nicht mehr in EPROM rein. 
Denn  der programmer erwartet, dass die Ladeadressen sich mit denen des 
Chip Raumes decken.

Erstmal Kaffee machen....... kopf raucht.....

von (prx) A. K. (prx)


Lesenswert?

Wenn es dir nicht gelingt, aus dem Monitor einen Binärklumpen von der 
Grösse des EPROMs zu bauen und den ins EPROM zu brennen, unabhängig 
davon was in dem Binärklumpen steht, dann ist Hopfen und Malz verloren.

von Christian J. (Gast)


Lesenswert?

A. K. schrieb:
> Wenn es dir nicht gelingt, aus dem Monitor einen Binärklumpen von der
> Grösse des EPROMs zu bauen und den ins EPROM zu brennen, unabhängig
> davon was in dem Binärklumpen steht, dann ist Hopfen und Malz verloren.

Das mag sein .... aber um das in ein Projekt einzubetten müsste ein 
weiteres programm geschrieben werden was den Code patscht und das will 
ich vermeiden, da das aufwendig ist. Der SDCC kann keinen relokatiblen 
Code erzeugen der während der Laufzeit die Positon ändert und fertig. 
Und ohne Startup geht es auch nicht. Und genau aus einem ähnlichen Grund 
musste Microsoft damals den Umweg über die EXE Patch Tabelle gehen, was 
die bestimmt nicht gemacht hätten wenn das alles so einfach wäre.

von Georg G. (df2au)


Lesenswert?

Christian J. schrieb:
> Software die Gleichungen einzugeben.

PLANII gibt es (noch), ist Freeware und gut.

Kann dein SDCC keine Assembler Ausgabe? Wenn ja: Der M80 ist nach wie 
vor frei zu haben und kennt ".PHASE". Ruckzuck ist ein REL-File gemacht, 
das du deinem Linker dann vorwerfen kannst. Musst dich nur von dem 
Gedanken an einen monolithischen Monitor frei machen.

Deine Probleme haben andere schon vor Jahrzehnten gelöst. Und hier im 
Thread sind dir Lösungen x-mal angeboten worden.

von Christian J. (Gast)


Lesenswert?

Doch es geht!

Objectcopy erzeugt ein BIN File aus dem HEX waas keine Adressbezüge mehr 
enthält. Der Brenner kann sich das BIN an beliebiger Stelle reinholen, 
er lädt es per default an 0x0000 aber das kann ich ändern.....

Ok...

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Der SDCC kann keinen relokatiblen
> Code erzeugen der während der Laufzeit die Positon ändert und fertig.

Muss er dafür auch nicht. Es reicht, wenn er in der Lage ist, Code für 
den festen Adressbereich 0xE000 (ich war vorhin von 16K ausgegangen) zu 
erzeugen und du in der Lage bist, diese 8KB ins EPROM zu programmieren.

> Und ohne Startup geht es auch nicht.

Muss es auch nicht. Nur dass der Startup dann ab 0xE0xx läuft.

Sinngemäss (8K ROM), übersetzt für Adressraum E000-FFFF, unter der 
Annahme, dass die RST Befehle vom Monitor nicht verwendet werden:

reset:   ld bc, 0x2000    ;8KB
         ld hp, 0x0000    ;von Quelle
         ld de, 0xE000    ;nach Ziel
         ldir             ;kopieren
         out (flipflop),a ;FF umschalten
         jp startup       ;absoluter Sprung nach 0xExxx
startup: ..hier fängt dein bisheriger Startup Code an..

Der Reset führt das ROM zwar ab 0x0000 aus, aber die paar Bytes bis zum 
JP sind adressunabhängig.

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Georg G. schrieb:

> Deine Probleme haben andere schon vor Jahrzehnten gelöst. Und hier im
> Thread sind dir Lösungen x-mal angeboten worden.

Nur zum Verständis... niemand hier im Thread kennt das System was ich 
aufgebaut habe, damit von einer Codeänderung zum Test alles schnell 
geht.
Das geht vom Compiler über eine Umwandlung des Codes bis zum Laden in 
den Programmer und auch noch ein Ladeskript.

Ebenso kennst du nicht die Werkzeuge die ich verwende. Einen M80 
Assembler mit rein zu bringen wäre unsinn. Der SDCC hat einen drin, der 
gibt auch Asm aus aber der ist nicht kompatibel mit anderen, hat seine 
eigene Syntax.

Die von AK vorgeschlagene Lösung ist ok, das Monitorprojekt muss auf 
0xc000 kompiliert werden, dann kann ich es in adressloses BIN umwandeln 
und dieses kann gebrannt und direkt kopiert werden, die Kopierroutine 
bringt es im Startup Code mit.

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

A. K. schrieb:
> Muss es auch nicht. Nur dass der Startup dann ab 0xE0xx läuft.

Genau so !!!!!

Anbei mal das projekt...... das ist schon etwas gwachsen und kann daher 
nicht ohne weiteres in was anderers portiert werden.

von Christian J. (Gast)


Lesenswert?

A. K. schrieb:
> Muss er dafür auch nicht. Es reicht, wenn er in der Lage ist, Code für
> den festen Adressbereich 0xE000 (ich war vorhin von 16K ausgegangen) zu
> erzeugen und du in der Lage bist, diese 8KB ins EPROM zu programmieren

Das geht aufgrund des Aufbaus eines IHX8 Files eben nur über den Umweg 
es zu einem BIN File (COM Datei aus DOS) zu machen, da dann jeder 
Adressbezug weg ist für den Brenner. IHX8 will er da sehen wo es hin 
soll, zumindest meine China Möhre hier.....

von (prx) A. K. (prx)


Lesenswert?

A. K. schrieb:

Korrektur:
reset:   ld bc, 0xFFFF    ;64KB
         ld hp, 0x0001    ;von Quelle
         ld de, 0x0001    ;nach Ziel
         ldir             ;kopieren
         out (flipflop),a ;FF umschalten
         jp startup       ;absoluter Sprung nach 0xExxx
startup: ..hier fängt dein bisheriger Startup Code an..

Setzt voraus, dass im ROM keine Adressdekodierung drin ist. Der JP muss 
aus dem RAM laufen können, also wird das ROM einfach 8x hintereinander 
in den Adressraum kopiert.

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Es würden nur kopiert: 0x0000-0x4fff nach 0xC000-0xcfff
Darüber muss Platz bleiben  für Stack, Vars und Heap.
D.h. ein userprogramm ist auch begrenzt und kann nur 48k gross sein.

von (prx) A. K. (prx)


Lesenswert?

Christian J. schrieb:
> Es würden nur kopiert: 0x0000-0x4fff nach 0xC000-0xcfff
> Darüber muss Platz bleiben  für Stack, Vars und Heap.
> D.h. ein userprogramm ist auch begrenzt und kann nur 48k gross sein.

Oder du platzierst dieses RAM unterhalb des Monitors. Zumindest jenen 
Kram davon, den du bei laufendem Programm nicht mehr benötigst.

von Christian J. (Gast)


Lesenswert?

Ich sags mal so....

Die Entwicklung des Monitors macht man eh im RAM, habe ich bisher auch 
so gemacht. Danach kommt er ins ROM rein, man ändert einige Codebezüge 
vorher im Quelltext und für den letzten Schritt kann man auch mal im Hex 
Code des Brenners etwas rumpatchen, da es nur einmal ist und einen 
Sprung anpassen ist echt nicht die Welt. Der SDCC ist ein starkes 
Werkzeug aber er schafft auch Probleme, die man ohne ihn nicht hätte, 
denn Sonderlösungen sind in Asm einfacher.

von Winfried J. (Firma: Nisch-Aufzüge) (winne) Benutzerseite


Lesenswert?

Hallo,

Willst du volle 64 k für deine Userprogramme zur Verfügung haben, so 
benötigst du ein spezielles Bankswiching incl. HW, welches 64K Ram 
dadurch ermöglicht das minimal 1 page für die Kopierroutine und die 
notwendigen Mapgingroutienen zum laden des Programmes in andere als die 
Ramadressen zur Laufzeit laden und später ummapen, verschieben 
(selbstentpacken) kann.
 Als letzen Akt muss dieses Programm einen Zyklus anstoßen welcher die 
64 KB linear adressiert und einen NMI auslöst um dann das nun im RAM 
befindliche Userprogramm zu starten, welches komplett ohne OS auskommen 
muss. Über die Sinnhaftigkeit eine solchen Vorgehens darf nachgedacht 
werden.

Ich hatte damals meinen Monitor von Hand readressiert(an diese Adresse 
kopiert dissasmbliert und alle Adressen umgeschrieben AXXX-->> 4XXX und 
Bxxx-->>5xxx), so dass der Monitor sowohl ab A000 als auch ab 4000 im 
Ram laufen konnte.
So konnte ich den Monitor je nach Notwendigkeit von verschiedenen 
Speicheradressen laufen lassen. Zusätzlich habe ich in Page4 
(Kasettenpuffer des Atari) Eine Datenschaufel und Mappingroutine 
untergebracht, mit welcher ich vor dem Start jeden Programmes beliebige 
Rammanipulationen vornehmen konnte.

So konnte ich Bootdisketten nach dem Booten  nur durch Manipulation des 
Einsprungvectors (Biosdefiniert) auf der Diskette (Diskeditor) da zu 
veranlssen nach dem Bootvorgang  nicht zu starten sondern viel mehr 
meinen Monitor nach zu laden.

Dabei wurde freilich ein Teil des Ram überschrieben aber ich habe 
alternierend beide Monitorversionen aufgerufen und konnte so das 
komplette binär Image des jeweiligen Programmes in ein Dosformat 
umwandeln. Egal wo im RAM es residierte.

Namaste

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

hallo,

auch nicth schlecht. das der sdcc aber kein Mapping unterstützt müsste 
das auch wieder Sache des Monitors sein, wo dann endgültig die Frage 
nach Asm entstehen würde.

und dann kommen wir auch zu meinem anderen hobby.... wenn ich einen 1.8l 
VW Motor auf 200PS bringen will, dann kann ich tausende dafür ausgegen 
seine Teile zu verändern, scharfe Nockenwelle, Kopf planen, Verdichtung 
erhöhen, Ladedruck ändern, Kanäle schleifen, Abgasdruck niedriger machen 
usw.

Oder ich lass den Shice bau einfach einen G60 Motor ein mit 200PS...

Sprich: Dann nehme ich keinen Z80 mehr sondern ARM...

von Winfried J. (Firma: Nisch-Aufzüge) (winne) Benutzerseite


Lesenswert?

Christian J. schrieb:

> Oder ich lass den Shice bau einfach einen G60 Motor ein mit 200PS...
> Sprich: Dann nehme ich keinen Z80 mehr sondern ARM...

Dabei lernst du nur wie man den richtigen Chip / Motor auswählt. Aber 
nicht wie ein Motor/ Prozessor funktioniert.

ASM ist Maschinenorientiertes Programmieren, nicht modern aber sehr 
lehrreich und der imho wesentliche Aspekt sich mit derart alter Technik 
zu befassen. egal ob Oltimerautomobil oder Oltimercomputer.
Und über Geld und Zeit sollte man dabei schon gar nicht nachdenken. Es 
kann nur der persönlichen Kunst zugerechnet werden und die kommt von 
Können sonst hieße sie "Wunst".

PS. Sehr langsam gewinnt auch mein Retroprojekt an Form UB8830D +128 KB 
SRAM (4*32)+2 *8 KB ROM. Ich weiss SRAM ist nicht wirklich Retro und 
auch das geplante GLCD/PS2 ist es nicht. Aber eine gepollte Tastatur, 
das habe ich schon mal gebastelt und ein (F)BAS Grafikausgang ... grr 
will ich nicht. Also lass ich dass.

Namaste

: Bearbeitet durch User
von Christian J. (Gast)


Lesenswert?

Ok, fnde ich gut. Und da Du ja sicher ein guter lehrer bist
kanst du mir ja zum Üben mal diese Routine für die ich so
ca 25 Minuten gebraucht habe in Asm codieren :-)

Tick....tack.......Stoppuhr läuft...... ! Los !!!
1
// Konvertiere Ints nach Hex Zeichen
2
void xtoa(uint16_t val, uint8_t *buf)
3
{
4
  char *p;
5
  char *first;
6
  char temp;
7
  unsigned dig;
8
9
  p = buf;
10
11
  firstdig = p;
12
  do 
13
  {
14
    dig = (unsigned) (val % 16);
15
    val /= 16;
16
    if (digl > 9)
17
      *p++ = (char) (dig - 10 + 'a');
18
    else
19
      *p++ = (char) (dig + '0');
20
  } while (val > 0);
21
22
  *p-- = '\0';
23
  do 
24
  {
25
    temp = *p;
26
    *p = *first;
27
    *first = temp;
28
    p--;
29
    first++;
30
  } while (first < p);
31
}

von Holm T. (Gast)


Lesenswert?

1
conv8   push    af                      ; convert 8 bit in A to
2
        and     0fh                     ; 2 ascii numbers in DE
3
        add     a,90h
4
        daa
5
        adc     a,40h
6
        daa
7
        ld      e,a
8
        pop     af
9
        rrc     a
10
        rrc     a
11
        rrc     a
12
        rrc     a
13
        and     0fh
14
        add     a,90h
15
        daa
16
        adc     a,40h
17
        daa
18
        ld      d,a
19
        ret

..hilft das wirtschaften?
Solche Routinen finden sich fertig in allerlei Literatur..

Gruß,

Holm

von Winfried J. (Firma: Nisch-Aufzüge) (winne) Benutzerseite


Lesenswert?

@ Holm,
 perfect

@Christian,

von der und Grundstruktur wäre mein Ansatz der selbe gewesen, lediglich 
die Anwendung der Dezimalkorrektur kannte ich von der 6502 so nicht, was 
bei mir einen  bedingten Sprung erfordert hätte falls nibble >09 
ausfällt. eine step by step übersetzung von C  ist hier freilich mit 
Spatzen auf Kanonen gesch.... ach nein andersrum.  Fehlt noch eine 
Zählschleife und ein Ein- und ein Ausgabepuffer um auch größere 
(mehrstellige) Hexen zu konvertieren. Aber das kannst du ja schon 
selbst? ;)

von Christian J. (Gast)


Lesenswert?

:-)

Habe ganz andere Sorgen derzeit nach .....moment.....10h Programmieren 
heute ohne Pausen.

Der Monitor "DeLuxe" ist im ROM, gefühlt 50 EPROMs gelöscht und neu 
gebrannt heute, bzw war der Löscher nie aus und wurde immer wieder neu 
bestückt, da das im RAM nicht zu testen ging.

Damit das geladene File nicht verloren geht, habe ich 2 Resets 
eingebaut, einen über NMI, der zurück in den Monitor führt aber das 
Programm jederzeit neu startbar ist und einen Normalreset der es sofort 
wieder startet.

Allerdings bemerkt, dass ein Nicht-Nullen des RAMs vor dem Laden zu 
einem Programmabsturz des geladenen Programms führt. Intel Hex füllt ja 
nur dort auf, wo was ist. Ich vermute mal, dass es mit nicht 
initialisierten Vars zu tun hat.

Je größer das Ganze wird umso heikler bei Änderungen, dass plötzlich 
auch was anderes nicht mehr funktioniert......

Theoretisch kann ich jetzt mehrere Programme laden und sie einzeln 
starten und wieder beenden. Leider gibt es keine virtuellen Memory 
Manager und eine MMU auf dem System, das würde einiges vereinfachen.

von Winfried J. (Firma: Nisch-Aufzüge) (winne) Benutzerseite


Lesenswert?

...Weshalb ich in meiner HW 2*8 KB EEPROM (statt EPRROM) beschreibbar 
sowie mit Dipschaltersreibschutz vorgesehen habe. Dazu (beliebig) 
mapbare 2* 2*32 KB RAM. So kann ich sowohl vom ROM als auch vom RAM je 
ein Backup halten.


o.T.
@ Holm

Das "BM 200" befindet sich bereits in einem dieser EEPROMs.
Nur bin ich noch an der MMU. Der TFT Maximite hat mir das BinFile in 
Pages geteilt an einen ATMEGA32 auf dem Brotbrett geschickt und dieser 
hat es Byteweise ins 28C256 kaligrafiert, inclusive Verifycation.

Der TFT-Maximite wird mir auch als Serialterminal dienen.

von Christian J. (Gast)


Lesenswert?

Du hast mich da auf eine Idee gebracht..... lese mir grad den Abschnitt 
über den "Z80 DMA Controller" durch..... damit wäre es ohne weiteres 
möglich eine MMU aufzubauen, mehrere Pages zu haben usw.

Allerdings würde das Ganze dann auch größenordnungen annehmen, die mit 
"Hobby" nicht mehr viel zu tun haben sondern mehr Richtung 
"Vollzeitbeschäftigung" gehen. Und Z80 mit S100 Bus gibt es ja schon in 
Form des N8VEM.

von michael_ (Gast)


Lesenswert?

Holm Tiffe schrieb:
> Einen lichtempfindlichen Taschenrechner hatte ich auch schon, allerdings
> war der komplett russisch, ein Konkret 600 dürfte so selten gewesen sein
> das kaum Jemand das Vergnügen gehabt haben dürfte das Teil reparieren zu
> dürfen.

Wir haben damals russische Taschenrechner in der Fa. gestellt bekommen.
Solche mit Solarzelle.
Ja, die Dinger waren lichtempfindlich.
Kam jemand ins Zimmer, sagte "Warum habt ihr das Licht an, spart Strom!" 
und schaltete das Licht aus.
In der Folge war das Ergebnis seitenlang eingetippter Listen weg.
Wir haben da oft geflucht.

von Christian J. (Gast)


Angehängte Dateien:

Lesenswert?

@Leo:

Du hast auch keine Erklärung dafür, dass sich Bytes bei einem Programm 
während der Laufzeit überschreiben, je dichter das Datensegment am Code 
Segment liegt? Näher als 2kb, dann geht es los, dass sich "wahlos" Bytes 
überschreiben und den Absturz verursachten bzw chaotische Ausgaben auf 
dem Schirm?. Habe das sohar mit Checksummen überprüft nach dem Laden des 
programms und nach dem Starten. Keine Erklärung, Code siege gut aus aber 
je kleiner ich ihn mache (unbenutzte Teile mit einkompilieren), desto 
besser wird es. Unter 12kb ist es ganz weg und wenn man die Daten ab 
0xc000 legt, wenn der code bei 0x8600 endet.

Das sind s9 Dinge die dann Stunden dauern....... weil sie nicht 
erklärbar sind ohne genauen Debugger. Aber aufgetren ist es schon öfter 
mal, nur habe habe ich es am "Haken"

von Holm T. (Gast)


Lesenswert?

Christian so lange Du nicht sicher bist das Dein Code hinsichtlich 
memory allocation und deallocation alles richtig macht, würde ich sehr 
leise nachfragen..

Gruß,

Holm

von Christian J. (Gast)


Lesenswert?

Holm Tiffe schrieb:
> Christian so lange Du nicht sicher bist das Dein Code hinsichtlich
> memory allocation und deallocation alles richtig macht, würde ich sehr
> leise nachfragen.

Ich habe derzeit keine dynamische Speicherallokation drin... aber die 
Interruptts mit ihren Ringpuffern.......

PS: Es IST der TX Ringpuffer oder der TX Interrupt.... aber nur 
sporadisch. Da scheinen "kritische Zustände" zu entstehen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.