Beiträge von Raffzahn

    Eine hab ich noch:


    Der Kanalcontroller/Interface:




    Die sichtbaren Kartenstecker gehören übrigens zu den Hartnäckigsten die ich je hatte. Fürs ziehen musste ich einen Kartenzieher basteln, weil die Greifer meines verstellbaren etwas zu dickwaren. Soweit normal. Aber fürs Reinstecken musste ich den Rechner gegen eine Wand stellen und mir eine 'Drückhilfe' bauen. Für einfach auf die Kante drücken waren meine Fingerchen zu schwach :))

    Hier noch ein paar Bilder bevor ich die P6066 wieder zumache:



    Beschriftung der Einbauplätze von Oben nach Unten:

    BASIS-I/O, 2x Floppy, 2x CPU, RAM, ROM, RAM-Erweiterung, Freie Erweiterungen, ganz unten dann I/O-Kanal



    Mit Verkabelung und Steckern. Letztere sind interessant, da sie Ober- und Unterseite vertauschen.

    Die Stecker sind die Gleichen für das CPU-Doppel und die Floppy.

    Grund ist wohl dass man so die Baugruppen bei Entwicklung und Fehlersuche nebeneinander legen und mit einem direkten Kabel verbinden konnte.



    Basis-I/O:



    Floppy (Obere Platine)



    Floppy (Untere Platine)


    forum.classic-computing.de/index.php?attachment/173156/


    CPU (Obere Platine)



    CPU (Untere Platine)



    Basis-RAM (48 KiB) mit 4116ern.

    Interessant die Verwendung des 3242 DRAM Multiplexers/Refreshcounters. Sehr praktischer Chip der komischerweise bei Micros selten aufgetaucht ist.



    ROM. Wohl hauptsächlich Microcode.



    RAM-Erweiterung (64 KiB, auch 4116)


    Fast richtig, aber es werden nur die unteren 64KiB addressiert. In den oberen 64KiB ist nur Mikrocode, auf den hat die /360-Seite keinen Zugriff (und braucht auch keine Zugriff) Deshalb sind es nur 16 gültige Bits. (Außerdem wäre es eine Verschwendung, den Mikrocode zu zwingen, für das eine Bit noch haufenweise zusätzlichen Code hinzustellen. Soviel Speicher hat das Ding nicht).

    Und welchen Sinn macht dann bitte eine Speichererweiterung über 64 KiByte RAM hinaus - so wie ich sie in wenigstens 2 Maschinen hab:

    Das würde dann auch eher zu der P6066 die ich hier stehen habe passen - die hat 112 KiB Speicher eingebaut. Wär schon arg doof wenn davon mehr als die Hälfte nicht verwendbar wär.

    Der Microcode muss also einen Weg haben darauf zuzugreifen - so wie er einen Weg haben muss auf eigene Konstanten im eigenen Code zuzugreifen. So wie sich die Tabellen in den bisher zitierteten Papieren lesen hat die Kiste 64 Ki Adressraum und adressiert damit 16 bit Speicherworte. Die Unterscheidung High/Lowbyte braucht auch nicht Haufenweise code. Da gibts eine Vielzahl von Möglichkeiten das übersichtlich zu halten.


    Wie gesagt, siehe Patente.


    Patente sind keine Pläne und kein Beleg für eine Implementierung. Sie dienen zum Schutz einer grundsätzlichen Idee, nicht um den Nachbau zu ermöglichen oder den eigenen Rechner zu verstehen. Aus Erfahrung würde ich mich da nur auf den grundsätzlichen Aufbau verlassen, aber jedes Implementationsdetail hinterfragen.

    [...], aber die "normalen" Selectrics sind rein mechanisch gekoppelt - wie ginge das dort? Viele Elektromagnete?

    Ja, wobei nicht so viele wie es tasten hat, nur so viele wie es Hebel braucht um den Druckkopf in die richtige Position zu bringen, plus je einen für Walzenvorschub und Wagenrücklauf.


    Die Geräte erkennt man an der 'Bauchbinde' die nötig ist um die zusätzlichen Aktoren und ihre Steuerung unterzubringen:



    Von der Seite sieht man recht schön wie die Mechanik auf Stelzen gesetzt wurde um Raum zu schaffen:



    Ich hab jetzt mal nur den Deckel abgenommen, weil Boden abbauen ist echte Arbeit. Man sieht aber von der Seite zumindest einige der neuen Hebel:



    Die 3 in der Mitte sind IIRC für den Druckkopf, der Eine vorne ebenfalls - macht glaub ich Shift. Alles ohne Gewähr weil lange her dass ich da reingeschaut habe.

    Was halt bedeutet dass eine adresse im /360 Code wenigstens 17 gültige Bits hat - sprich mindestens 3 Byte zum Abspeichern braucht.

    Nein, sie hat 16 gültige Bits. An die Firmware (Mikrocode) kommt sie nicht ran. Siehe die Patente.


    Ich schätz mal wir reden gerade wieder aneinander vorbei. Die /360 Seite arbeitet mit Byteadressen, nicht Wortadressen. Entsprechend braucht es 17 bit um 128 KiB zu adressieren. Und genau das sind die 17 gültigen Bits die Bedingen dass es 3 von 4 Bytes eines Adressworts belegen. Nicht? Dass der Microcode die oberen 16 dann als 16 bit Wortadresse verwendet und das niederwertigste zu Selektion des Bytes ist nicht das Problem des /360 codes - und auf der Ebene reden wir, oder nicht?


    > (Irgendwie hab ich da auf Github jetzt keine Codestücke gefunden, sonst hätt ich gleich mal Beispiele rausgesucht.)


    Da sind keine Codestücke, du musst Radere2 mit dem P6060-Plugin auf das P6SW-Extrakt anwenden. Dann hast du alles komplett. :)

    Ah. Passt. Den Spaß lass ich erstmal Dir :)) ::pc::

    > Ergo: Der Speicher ist logisch wahrscheinlich als 16 Bit (32 Bit wie vor angenommen wohl nicht) organisiert, angesprochen mit 16 bit (Halb)Wortadressen. Daher braucht das Microprogramm nur 16 bit Speicheradressen in den Befehlen. wie die beiden Bytes im Wort adressiert werden müsste man im Microprogramm sehen.


    So ist es. Wahrscheinlich habe ich mich schlecht ausgedrückt.


    Was halt bedeutet dass eine adresse im /360 Code wenigstens 17 gültige Bits hat - sprich mindestens 3 Byte zum Abspeichern braucht. Damit gelten die gleichen Regeln wie IBM: Eine Adresse hat 24 Bit und wird in einem Wort gespeichert und die Leute missbrauchen die obersten 8 bit für Flags/Marker/Locking. Dass von den 24 jetzt nur 17 gültig sind macht da keinen Unterschied.


    Und das ist genau was bei den beiden Fällen mit 93 mir aufgefallen ist.


    Deswegen muss auch in einen Start-IO das CCW aus mindestens 7 Byte bestehen. Weniger hat nicht genug Platz für alle benötigte Information. Test-IO und ähnliches kommt mit weniger daten aus.


    > und Adressen sind doch immer noch Worte in den P6060 Programmen


    Ich habe bis jetzt eigentlich immer nur relative Adressen gesehen, nie irgendwo absolute. Die Module sind verschiebbar, alle Adressen dort relativ. Referenzen auf Systemtabellen über R12 und R13, auf den Stack mit R14. Aber vielleicht stolpere ich mal über eine.


    Das (fast) alle Adressierung im Code Register-Relativ sind ist auch einer der genialen Züge die Amdahl gemacht hat (Fast weil ja auch ZP geht). Ist aber halt nur im Code. Und da jede Adressierung über ein Register geht müssen Adressen auch dorthin gelangen. Für die Adressierung im Code gibts da den Sonderfall des BALR Rx,0 der Rx mit der Adresse des nächsten Befehls läd - damit kann relativer Code die eigene Adressierung (BR 47 ..RX...) und die der statischen Datenbereiche im Code wie Münchhausen am Schopf aus dem Sumpf ziehen.


    Zwischen Modulen (oder Systemaufrufen) klappt das nicht. Man könnte zwar S-Adressen verwenden, macht man aber kaum, weil eher unhandlich zum Auflösen (braucht mehrere Befehle inkl. EX). Schau einfach mal in dem Code den Du hast nach der allbekannten 'LA Rx/ST Rx' Kombination - das ist die Ablage einer absoluten Adresse in einem Wort um die an andere Module weiterzugeben - z.B. die Adresse eines Puffers mit/für Daten. Das Gegenstück ist 'L Rx' gefolgt von Befehlen (meist MVC, CLC, etc.) die dann Rx zum Adressieren verwenden, egal ob als Quelle oder Ziel.


    (Irgendwie hab ich da auf Github jetzt keine Codestücke gefunden, sonst hätt ich gleich mal Beispiele rausgesucht.)

    Raffzahn Du kannst auch gerne selbst schauen, das Repo mit Extrakten und Radare-Plugins is hier. Syntax läßt sich auch zu Großschreibung und Hexadezimal ändern. Das alles ist WIP und teilweise etwas halbgar.


    Nett.


    > C.C. = condition code, 2 bit, used with 4 bit mask in branch?


    Ja. Jeder Befehl hinterläßte einen eindeutigen CC. Vergleiche z.B.


    0 für Gleich

    1 für Kleiner

    2 für Größer


    Integerrechnung das gleiche plus


    3 für Überlauf


    Die ganzen Vorzeichenlosen packen da dann Zero und Carry rein


    00 -> NC,Z

    01 -> NC,NZ

    10 -> C,Z

    11 -> C,NZ


    Die 4 bit in der Maske des BC(R) können den 4 möglichen werten


    1 -> 3

    2 -> 2

    4 -> 1

    8 -> 0


    Entsprechend kann man dann alle Kombinationen die es so braucht in einem Test machen, anstelle mehrere Sprünge zu brauchen wie z.B. auf einer Z80:


    47 8x -> Springen wenn Gleich

    47 Ax -> Springen wenn Gleich oder Größer

    47 6x -> Springen wenn Ungleich

    ...


    > Den Link hast du wahrscheinlich auch schon vor einem Jahr gesehen.


    Ja, und gefreut dass sich da jemand die Arbeit macht.


    Da bei Adressen von der Hardware nur die niedrigsten 24 Bit ausgewertet wurden war es bei der /360 Usus das erste Byte für Flags zu verwenden.

    Das ist bis jetzt auch nur fundierte Spekulation, aber die P6060 hat für den /360-Adressraum nur 64K, also 16 bit-Adressen, die ein Byte addressieren.


    Der Mikrocode benutzt 16-bit Adressen, die ein Wort adressieren (d.h. der /360-Adressraum erscheint in der unteren Hälfte).


    Also das widerspricht sich jetzt irgendwie. Wenn der Microcode Wortadressierung verwendet, dann sind es 256 Ki Bytes die adressiert werden können. Wieso der /360 Microcode dann nur die ersten 64 Ki davon adressieren können erschließt sich mir nicht. Kann es Sein dass es da noch eine Verwechselung zwischen Byteadressierung der Befehle und Wortorganisation des Speichers gibt? Das zu trennen war ja (einer) der genialen Dinge die die /360 gebracht hat: Die Trennung von Speicherschnittstelle und Adressierung. Aus Befehlssicht war der Speicher byteorganisiert (mit der Einschränkung dass Halbwort-, Wort- und Doppelwort-Zugriffe ausgerichtet sein mussten), auf der Speicherschnittstelle waren zugriffe aber immer 32 Bit oder mehr (die ersten kleine -30 ausgenommen, die arbeitete mit 16 bit Speicher).


    Könnte es daher nicht auch bei der P6060 so sein, dass der Speicher als Wort adressiert wird? Dann geht mit 16 bit Speicheradressen problemlos eine Adressierung von 256 KiB. Adressen die aus den niederwertigen 18 bit der effektiven Adresse der /360 Logik stammen.


    Das würde dann auch eher zu der P6066 die ich hier stehen habe passen - die hat 112 KiB Speicher eingebaut. Wär schon arg doof wenn davon mehr als die Hälfte nicht verwendbar wär.


    Siehe das verlinkte Patent für Details, sowie das STAC-4 Dokument für ein paar Adressen:

    Code
    Physical memory map: Slot/Piastra, range, ...
      6  0000-1FFF  RAM Software  (16 K)
      6  2000-7FFF  RAM Utente    (48 K max)
      7  8000-87FF  ROM 4K
      8  8800-BFFF  RAM firmware  (24 K)

    Äh, irgendwie addieren sich die werte nicht so ganz auf wenn das Byteadressen sein sollen:


    0000-1FFF sind für mich nur 8 Ki, keine 16

    2000-7FFF entsprechend auch nur 24 Ki

    8000-87FF sind 2 Ki

    und

    8800-BFFF sind 14 Ki, keine 24


    Wenn man mal annimmt dass das letzte einfach ein Tippfehler ist (hatte es schon einige) , dann schaut doch alles nach Faktor 2 aus - wie als wenn der Speicher nicht als 8 bit Worte, sondern 16 Bit Worte organisiert ist und die Adressbreite 16 bit, dann komm ich auf 64Ki Worte oder 128 Ki Byte ... was auch eher zu meiner P6066 mit 112 KiB RAM passt. Die Tabelle für den RAM Ausbau auf Seite 9 des STAC-4 weist das gleiche Phänomen auf, da wird dem Adressbereich 0000-7FFF, was 32 Ki Adressen sind beschieden 48KiB User-RAM (Utente) zu bieten, was 64 KiB physikalischem (fisici) RAM entspricht (16 KiB verwendet das System).


    Auf Seite 5 von STAC-2 gibt es eine Tabelle die 16 bit Adressen verwendet, im Kopf aber in 4 Ki Schritten von 0 bis 128 geht.


    Ergo: Der Speicher ist logisch wahrscheinlich als 16 Bit (32 Bit wie vor angenommen wohl nicht) organisiert, angesprochen mit 16 bit (Halb)Wortadressen. Daher braucht das Microprogramm nur 16 bit Speicheradressen in den Befehlen. wie die beiden Bytes im Wort adressiert werden müsste man im Microprogramm sehen.


    ---


    STAC-4 zeigt auch auf Seite 11 und 24 die physikalischen Kanalnummern. Die Nummerierung passt zu der Darstellung auf Seite 108 vom I/O fisico, spalte FISICO. Belegt Damit auch, dass jeder der UART wirklich gleich 2 Kanäle belegt. Seite 23 löst auch die Frage was das fehlende Bit in der Geräteadresse ist: Die Richtung :))


    ---


    Ach ja, das ganze hierändert natürlich nichts an der bei /360 Programmen üblichen Verwendung des ersten Bytes eines Wortes (und Adressen sind doch immer noch Worte in den P6060 Programmen (bearbeitet mit L/ST)?) um das es in der ursprünglichen Passage ging.

    > Interessant dabei dass hier ein 22 als RETEXT umgesetzt wird. Wo hast D das her?


    Geraten :) und verifiziert dadurch, dass es bis jetzt an allen Stellen passt. Wie auch ein paar andere Dinge, die ich bis jetzt nirgendwo gefunden habe.


    Ist das so ? :)) Würde es Dir was ausmachen die 'paar anderen Dinge' zu teilen? Ich würd die gerne in meine Tabelle aufnehmen.


    > Wär interessant zu sehen was auf Adresse 3BDC ff und 3BE0 ff steht. Beides Wort-Adressen.


    Mit großer Wahrscheinlichkeit 4 Bytes, die die I/O-Aktion beschreiben. Was alles nicht zu dem "i_o fisico in assembler" Dokument passt, wo es 8 Bytes sind, und anders aufgebaut. Wie gesagt, ich verstehe es noch nicht. Konkret:


    Code
                0x00003dbc      .byte 0x00
                0x00003dbd      .byte 0x2b
                0x00003dbe      .byte 0xef
                0x00003dbf      .byte 0xef
                0x00003dc0      .byte 0x01
                0x00003dc1      .byte 0xff
                0x00003dc2      .byte 0xff
                0x00003dc3      .byte 0xff


    4 Byte reichen nicht. Eine I/O Aktion braucht wenigstens eine Kanal/Geräteadresse, eine Befehl, eine Adresse und eine Länge. Längensind 16 Bit und Adressen wenigstens 24. zusammen mit Befehl und Gerät also wenigstens 7 Byte. Das was in dem i_o fisico in assembler steht macht schon sehr viel sinn.


    4 Byte hingegen, noch dazu auf Wort ausgerichtet ist mit hoher Wahrscheinlichkeit eine Adresse. Da bei Adressen von der Hardware nur die niedrigsten 24 Bit ausgewertet wurden war es bei der /360 Usus das erste Byte für Flags zu verwenden. Oft als Semaphor oder ähnliches. Werte ungleich 0 bedeuteten belegt, 0 frei.


    TS (93) ist dafür da das ununterbrechbar zu machen. Dazu wurde er ursprünglich als Funktion des Kernspeichers implementiert - Kernspeicher musste ja nach lesen wieder neu geschrieben werden. Das machte die Kernspeichersteuerung ganz alleine. Der TS griff da ein und sorge dafür dass immer FF zurückgeschrieben wurde. Da das im Lesen des Speicher ablief war es Atomar über den Speicher, das heisst weder ein anderer Prozess, noch eine andere CPU konnte das verfälschen. Als Ergebnis liefert TS zurück ob das gelesene (!) Highbit gesetzt war oder nicht.


    War es gesetzt gehörte das lock schon jemand anderem, war es nicht gesetzt, dann hatte man die Resource bekommen. In der Folge schrieb man dann oft in die unteren 7 Bit eine Kennung der Funktion oder des Prozesses um das debuggen einfacher zu machen.


    Wie gesagt, I/O haut mit nur 4 bye nicht hin. Was ich mir jedoch vorstellen könnte ist dass Olivetti daraus einen Super-TS gemacht hat der die üblichen Befehle zusammenfasst. Ähnlich wie mit dem CALEXS. Hier sogar noch sinnvoller da es eine aktive Warteschleife, bzw. Schleife mit test und einem OS-Aufruf durch einen einzigen Befehl ersetzt der dann gleich wartet bis das Lock belegt werden kann. Wäre sehr hilfreich und performancefördernd, gerade bei kleinem langsamen System. Im Microprogramm würde der durchlaufen wenn er das Lock belegen kann und einen Trap machen wenn nicht, so das das OS sehen kann was man anstelle macht.


    Nur mal so als Idee.


    37 und 36 sind wahrscheinlich Kanalnummern, wobei es auch da Umsetzungen zu geben scheint, so dass die Kanalnummern der Basic-Befehle nur bedingt helfen.


    Da ist ja eine Tabelle, die passt aber nicht zu den Nummern. Auch seh ich jetzt nur 36 in deinen Codestücken - also X'24', sowas ist meist binär. Und 24 im Gegenzug passt auch nicht zu den Befehlsnummern. Ich würde also erstmal der Semaphoridee weiter nachgehen.



    > Es muss also wo noch ein 'Systemprogrammierer'-Manual geben die ACT, CALEXS, CALEXT, RETEXT und RLSEM erklärt.


    Das wäre schön, aber ich vermute stark, da gibt's nur interne Dokumentation. CALEXT und RETEXT kommen dauernd vor, CALEXS und CALL habe ich bis jetzt nirgendwo gesehen, und ACT und RLSEM auch nicht. ACT und RLSEM sollten auch ziemlich offensichtlich sein, die Module/Segmente sind auf S. 3-11 im "Manuale generale del systema" erklärt. Im "üblichen Betrieb" werden die einfach über CALEXT angesprungen, ohne locking.

    Ich weis ja nicht wo Du genau schaust, aber CALL ist nur innerhalb kleiner (Anwendugs-)Programme sinnvoll, da die Zieladresse prinzipiell nur innerhalb des Adressbereichs des aktiven Moduls liegen kann (ja, man kann das Basisregister natürlich anders laden, aber das ist ungewöhnlich). Daher ist CALL für Userprogramme die Unterprogramme mit Parameterübergabe haben. Und das dürfe eher selten sein, da man sowas in klassischer /360 Manier eher mit globalen Datenstrukturen oder Zeigern in Registern macht.


    CALEXT hingegen sollte idR mit einem L Rx,=V'Modul' eigenleitet werden, einer Adresse die vom Linker bzw. Ladereingesetzt wird.


    Ich seh da jetzt nicht wie CALEXT die Anzuspringende Adresse aus der dynamischen Modulstruktur auf 3-11 bekommen soll. Weder in dem Code noch kann ichs mir zusammenreimen. Dazu ist die Tabelle zu dynamisch.


    Die in 3-11 aufgezeigte Modulstruktur scheint mir das zu sein was über CALEXS angesprungen wird. Dabei würde der Immediate (das 2. Byte von CALEXS) gegen den 2. Eintrag in der Tabelle gesucht. wird der gefunden, dann startet die Ausführung an der Adresse (II?) wie bei einem CALEXT (also BALR mit Parametern). Wenn kein Eintrag mit der Nummer gefunden wird, dann gibts einen Trap und das Basissystem (PICOS) kümmert sich ums nachladen.


    Erstmal nur ne Vermutung.

    Und da die stackrelevanten Opcodes ja offenbar Interesse finden, hier mal ein Beispiel wie das in existierendem Code aussieht:


    Code
                ;-- segment.s23:
    
                0x00003984      180d           lr r0, r13                  ; prolog 124
                0x00003986      41d0007c       la r13, 124
                0x0000398a      23dd           asa r13, r13
                0x0000398c      5000d000       st r0, 0(,r13)
                0x00003990      9029d004       stm r2, r9, 4(r13)
                0x00003994      0570           balr r7, r0                 ; balr.r7_3996

    Ah ja, das entspricht dem PROC Macro in der Form

    Code
            PROC R2,R9,ENDLOC=X+124,STARLOC=X


    Die "arg" sind Pseudo-Opcodes weil das dumme Radare2 mit der variablen Argumentzahl nicht klarkommt (wie es auch mit einigen anderen Dingen in diesem Befehlssatz nicht klarkommt).


    Tsts, und das von einem Unix-tool :))


    Erklärt auch warum ich so Schwierigkeiten beim lesen habe: /360 Mnemonics und Adressen in Kleinbuchstaben schaut aus wie Kauderwelsch, ich muss dauernd die Augen zusammenkneifen und im Kopf in Groß übersetzen bevor es Sinn ergibt :)) Genauso die durchgehende Verwendung von dezimalen Offsets bei Adressen.


    Code
    ----------- ; done
      └└└─└└└─> 0x00003cba      9324742a       ??93 1066(r7), 36           ; 0x3dc0
         │      0x00003cbe      5010c130       st r1, 304(,r12)
         │      0x00003cc2      9829d004       lm r2, r9, 4(r13)
         │      0x00003cc6      58d0d000       l r13, 0(,r13)
         │      0x00003cca      4100007c       la r0, 124
         │      0x00003cce      2200           fsa r0, r0
         │      0x00003cd0      2800           retext 0


    Das entspricht ab Zeile 3CC2 einem Rücksprung mit Stackframe auflösen und Register wiederherstellen.


    Code
            PRET


    Interessant dabei dass hier ein 22 als RETEXT umgesetzt wird. Wo hast D das her? Im Manual wird zwar RETEXT erwähnt, aber nicht weiter beschrieben.


    RETEXT geht wohl zusammen mit CALEXT und dürfte die BALR variante des CALL/RETS Pärchens (9A/20) sein.


    Genauso interessant die Verwendung von CALLEXS. Von der Kodierung her ist es ein SI Befehl, und die Adresse schaut ganz nach der vorher aufgestellten Parameterliste aus. Entsprechend dürfte das 1. Byte in Immediate sein, was sinn macht wenn man den als einen SVC mit Parameterliste auffasst. Packt den sonst so üblichen LA R1,par/SVC xx in einen einzigen Befehl. Spart Speicher.


    Lustig ist, dass diese Befehle zwar in der Befehlsübersicht des P6066 Manuals auftauchen, in der Beschreibung aber nicht, dafür aber in der Macroauflistung von PRET sowie bei der Beschreibung der/einiger Aufrufe in Kapitel 6, aber ohne genaue Beschreibung. Im P6066 Assembler Manual ist davon nur noch die Macroauflistung erhalten. Der Bearbeiter konnte die wohl nicht wegmachen ohne alles umzuschreiben :))


    Es muss also wo noch ein 'Systemprogrammierer'-Manual geben die ACT, CALEXS, CALEXT, RETEXT und RLSEM erklärt. ACT und RLSEM scheinen zur Verwaltung dynamisch geladener Erweiterungen zu sein. Wirklich nett was die alles ausgeknobelt haben.


    Der 93 ist übrigens einer von den undokumentierten Opcodes, vermutlich I/O.


    Kann sein. Weis nicht. 93 ist bei der /360 der TS - Test and Set - also locking. Dazu passt aber nicht unbedingt der Code der danach kommt.


    Wär interessant zu sehen was auf Adresse 3BDC ff und 3BE0 ff steht. Beides Wort-Adressen.


    In die lokalen Unterroutinen springen sie dann aber doch mit BAL oder BALR an und nicht mit CALL.


    Der Ansprung mit CALL ist nur nötig wenn Parameter abgelegt werden. Ohne kann man gleich nur den BAL verwenden :))

    Überhaupt liegen alle SxA und SxL Befehle ohne ersichtlichen Grund an anderer Stelle (02/03/04/06).

    Meine bisherige Vermutung ist, dass sie die Opcodes eine bisschen zusammengequetscht haben, um die Dekodierung im Mikrocode zu vereinfachen. Wenn man mal eine Tabelle macht, sieht man, dass von nur neun Werte von den 16 des High-Nibbles benutzt werden. Aber um das zu überprüfen müsste ich erstmal den Mikrocode verstehen.


    Noe, dazu reicht es die einfache Struktur der /360 Befehle zu sehen:


    Die ersten 2 bit des Opcodes geben Befehlslänge/Typ an


    00: 2 Byte mit RR, d.h. das 2. Byte enthält 2 Register als Operanden

    01: 4 Byte mit RX, d.h. erster Operand ist ein Register, 2. Operand ist eine indizierte Speicheradresse

    10: 4 Byte mit RS oder SI, d.h. ein Register und eine Speicheradresse oder ein Immediate und eine Speicheradresse

    11: 6 Byte mit SS, d.h. zwei Speicheradressen.


    Ein paar Ausreißer sind dann entsprechend Ihrer Struktur in einer der 4 Gruppen verpackt - z.B. der SVC mit seinem 8 bit immediate in RR (0A).


    Sinn dieser Struktur ist dass bereits das Statisising erkennen kann wie viele Halbworte zu dem Befehl gehören und welche Operanden bereitzustellen sind und wie die zu adressieren sind wenn es Speicheroperanden sind. Alles ohne komplexe Dekodierung, rein aus den ersten 2 Bit. Damit lässt sich die Pipelinesteuerung ganz einfach mit wenigen Logikgattern aufbauen. Es gibt nur einen Befehl der nicht ins Schema passt (SVC) und wenige bei denen der Speicherzugriff nicht nötig ist (LA aber auch die 4 Shifts), was aber entweder eh nicht stört (SVC) oder nur den Speicherzugriff unterdrücken muss.


    In der Praxis schaut das dann z.B. beim Oder so aus:


    16 -> 2 Byte OR - Oder Register mit Register

    56 -> 4 Byte O - Oder Register mit Speicher

    96 -> 4 Byte OI - Oder Speicherbyte mit Immediate (8 bit)

    D6 -> 6 Byte OC - Oder zwei Speicherbereiche mit 1..256 Byte Länge


    Amdahl war halt wirklich gut.


    (Richtig chaotisch wirds erst mit /390 und den Unmengen an Spezialformaten)


    Und als ich das hier gerade im Kopf formuliert habe war mir auch klar warum die 4 Shiftbefehle nach 02/etc. verlegt wurden: Olivetti hat die Behandlung von Konstante + Registerinhalt für die Stellenanzahl weggelassen und nur eine 4 bit konstante vorgesehen. Damit geht halt nur 1-16 stellen schiften, dafür auch nur 2 bytes - und dann müssen sie natürlich in den 00xxxxxx Codebereich, damit die Dekodierung einfach bleibt.


    Nach dem das geklärt ist, passt wieder alles. Die 606x ist (mit den genannten Einschränkungen) /360-Komaptibel.

    Ein weiterer Aspekt ist, das alle Unterschiede zum /360 Befehlsatz natürlich gegen evtl. Ürheberklagen geholfen hätten.

    Das war kein Thema. Weder damals noch heute. IBM hat mit seinen Patenten gearbeitet - und dabei hauptsächlich auch nur mit der Drohung dass ein Rechtsstreit _sehr_ lange und _sehr_ teuer wird und der andere das vielleicht gewinnt aber nicht überlebt. :)).

    Es gibt auch diverse I/O-Befehle, die im Handbuch nicht dokumentiert sind, und die ich noch nicht verstehe.


    Die scheinen eine vereinfachte Form der IO Struktur der /360 zu sein. So gibt es z.B. keine Verkettung und die Adresse liegt an anderer Stelle, ist dafür aber 32 Bit und Canal/Gerätenummer steht im CCW. Auch enthält der Befehl gleich die CCW Adresse, es gibt also kein CAW. Auch sind die Kanal und Gerätenummern Fix vergeben, d.h. die oberen 4 bit der Nummer ist der Kanal (die Baugruppe) und die unteren 3 das Gerät an dem Kanal. Wobei da schleuder ich noch ein bisschen, weil entweder hat Olivetti da geast und jeder seriellen Schnittstelle ZWEI Kanalnummern gegeben (anstelle Geräte) oder ich kapier die Beispiele nicht.


    SRL hat in den von mir benutzten Quellen übrigens Opcode 08, und nicht 06.


    Noch mal Nachgeschaut, hast recht, mit genug Vergrößerung wird auch auf meinem Laptop eine 8 draus. Macht auch sinn, siehe oben.

    Das führt schon mal weiter. Und in der Tat: der 6060 Assembler ist bzgl. Syntax dem /360 Assembler sehr sehr ähnlich. Und der Maschinencode ist (nach kurzer Durchsicht geschätzt) zu 75% binärkompatibel zum /360 Code. Es gibt aber auch deutliche Differenzen, die der anderen Architektur der 6060 geschuldet sind (zB Stack-Kommandos) - interessante Maschine !


    Wenn man also /360 Kompatibilität in dem Bereich messen will, dann ist die Frage nicht wieviel Anteil hat der /360-gleiche Teil an der Maschine, sondern wieviel % des nicht privilegierten /360 Befehlssatz ist 1:1 vorhanden. Ich hab jetzt nicht nochmal nachgeschaut, aber in meiner Erinnerung geht das bei der P6060 gegen 100%

    Sodala, jetzt hab ich doch nachgeschaut und muss mich entschuldigen, 100% sind es ganz sicher nicht. Selbst 70 nur mit Einschränkung darauf dass FP halt generell nicht dabei ist.


    Der Vergleich des P6066-Handbuch mit dem /360 Befehlssatz schaut aus als wenn da keine /360 sondern eher eine /120 Kompatibilität wäre:


    Von den 147 Befehlen der verschiedene /360 (-20 nicht eingerechnet) beherrscht die P6066 'nur' 66.


    Was fehlt sind

    - Alle FP Befehle

    - Alle BCD Befehle

    > Daher nur 120 der 360 Grad :)


    Und vom Integer-Befehlssatz fehlen

    - Alle Doppelregisterbefehle (LPDR, DDR, etc.)

    - Multiplikation und Division (M,MH,MR und D,DH,DR)


    Natürlich auch alle Befehle mit Bezug zur Hardware bzw. zur Trennung von OS und Anwendung

    - Hardware/Privileg (TS, SVC, SPM, SSK, etc.)

    - I/O Befehle (SOI, TCH, etc.)


    Ich kann mir meine Fehleinschätzung jetzt nur so erklären, dass fast alles davon Befehle sind die ich in 30+ Jahren /370 Assembler praktisch nie gebraucht habe - mal die BCD Befehle ausgenommen - und ich da dann an den Rest garnicht mehr gedacht habe. Sorry.


    Aber die P6066 bietet auch eine gaze Reihe richtig netter Erweiterungen die einiges ausgleichen, oder sogar besser machen - gerade für einen Desktoprechner - die /360 ist halt mit ihrer Blockorientierung zwar extrem performant, aber nur wenn man nicht Bytefummeln will.


    Erweiterungen:


    - Stack


    Der schon erwähne Stack mit Befehlen ähnlich ENTER/LEAVE beim x86, aber auch parameterzugriff (SP:BP relative Adressierung bei x86)


    - Rechnen im Speicher mit variabel langen (1..16 Byte) Integer


    Alle Rechenbefehle gibt es in einer Speicher-Speicher Version für vorzeichenbehaftete und vorzeichenlose Zahlen mit variabler Länge. Eigentlich wie die BCD Befehlen der /360 aber halt binär. Da wie diese mit 2 Längenangaben kann man problemlos zahlen verschiedener länge miteinander verknüpfen. Spart viel platz wenn man nicht jedes +1 als ein 4 byte Wort ablegen muss :))


    Die Befehle können praktisch alle weggefallenen Doppelregisterbefehle ersetzen - und sogar länger.


    - Konvertierungen


    ASCII kann in Integer oder Float und zurück gewandelt werden


    Für das Float-Format gibt es zwar dann keine Maschinenbefehle, aber es hat die interessante Eigenschaft sich die führenden Nullen merken zu können - das heißt man kann immer sehen wie große das in Textform war/sein soll. Hab ich so noch nirgends gesehen.


    - Suche in Tabellen


    Das ist ein besonderes Schmankerl. Es gibt zwei Funktionen die in einen Eintrag in einer Liste suchen können:


    SES - Sequential Search und DIS - Dictomic Search


    Beide arbeiten gegen eine Tabelle mit Zeilenläne 1..16 Byte und einem Schlüsselbegriff von 1..16 Zeichen am Anfang der Zeile. SES durchsucht die Tabelle sequentiell, wohingegen DIS mit halbierender Suche vorgeht. Letzteres richtig schnell (O(log n)), dafür muss die Tabelle natürlich gemäß dem Schlüssel sortiert sein. Bei BASIC gibts gleich mehrere Stellen wo das riesig helfen kann, z.B. Schlüsselwörter zu token wandeln, Token zu Adresse wandeln oder Variablen suchen.


    - Sonstiges


    - Z.B. ein Pärchen das ein Zeichen in einem Speicher suchen kann (ala strlen() in C) oder gleiche Zeichen überlesen.

    - Testen ob ein ASCII Zeichen ein Buchstabe, Ziffer oder Sonderzeichen ist.

    - Ausbreiten von Zeichen

    - MVC mit variabler Länge


    Das sind teilweise Sachen die dann erst die 370 auch hatte - aber halt anders.


    Es juckt einem direkt in den Fingern damit zu programmieren :))


    Ansonsten hat das Buch auch einige offensichtliche Fehler und Ungereimtheiten


    So hat SRL und BCTR den gleichen Opcode (06), as nicht sein kann

    Überhaupt liegen alle SxA und SxL Befehle ohne ersichtlichen Grund an anderer Stelle (02/03/04/06).

    Auch ist Für TRT der Opcode D0 angegeben, was bei der /360 aber DD wär, gleichzeitig liegt auf DD einer der neuen Konvertierungen. Ein dreher der ebenfalls keinen Sinn macht.


    Dazu kommen dann einige Schreibfehler in den Befehlsnamen und andere Kleinigkeiten.


    Ich würde also empfehlen die Angaben in dem Buch mit Vorsicht zu genießen :))

    Nicht vergessen dass die /360 ursprünglich für die Arbeit mit ASCII gedacht war. E

    Das ist wohl eines der vielen Märchen die man sich erzählt.

    Als ich Mainframe Assembler lernte wurde das Bit auch mal kurz angesprochen. Der Referent von IBM meinte das dieses Feature bei keinem IBM Programmprodukt verwendet würde und ihm sei auch keine Anwendungen bekannt die das verwenden.

    In einer Facebookgruppe in der ich die Frage gestellt hat kennt auch niemand Software die das verwendet.

    Wieso Märchen? Das gab es und die Hardware unterstützte es, habs selbst ausprobiert Anfang der 80er. Als die /360 entworfen wurde war ASCII noch in der Entwicklung und IBM der hauptsächlichen Treiber für den Standard. ASCII sollte eigentlich mit der 370 kommen um gleich von Anfang erst garnicht die Codeprobleme der Lochkarten- und 6-Bit-Zeit zu wiederholen. Der Standard wurde aber nicht schnell genug entschieden und EBCDIC daher als Zwischenlösung entworfen (vor der /360 verwendet IBM 6 bit Codes).


    Wie jeder weis ist Zwischenlösung nur ein Synonym für Dauerlösung :))


    Sache ist halt aber auch, dass es keinen großen Unterschied für die Software macht da damit ausschließlich die Funktion von PACK/UNPK beeinflusst wird, also ob der Zonenteil 3 oder F sein soll. Alles andere das Codes betrifft wird eh mit Befehlen gemacht in denen der Code nicht vorgegeben ist.


    Was Anwendungen anbelangt, so hat man zur Handhabung von ASCII eh immer eigene Routinen gehabt die dann so oder so einen TR verwendet haben um das Ergebnis für die Ausgabe vorzubereiten.


    Also, kein Märchen, nur ein netter Tanz der Geschichte.

    Bis jetzt habe ich nur das Standardhandbuch plus ein Handbuch für den Plotter gesehen (der Plotter selbst ist nicht mehr vorhanden). An optionalen Karten ist nichts drin außer der Karte für den Plotter (vermutlich IPSO).


    Also wohl keine große Ausbeute in dieser Richtung.

    Nun, z.B. das Plotterhandbuch haben wir nicht - ansonsten nehmen wir natürlich auch ::heilig::großzügerweise::heilig:: auch das ganze Gerät :))


    Würd aber verstehen wenn Du da lieber selbst mit speilst. Ist ein phantastisches Teil.


    Ich hoff Du kommst mal dazu das Bücher zu scannen.

    Cool, und Raffzahn mischt auch mit!

    Ach was, ich nerv Greg nur das er besser dokumentieren muss und mecker wenn er wieder ausm Ruder läuft und sich in Details verrennt.


    Und ja, ich kann das Teil empfehlen. Weniger wegen dem bunten Startschirm (den konnte ich ihm nicht ausreden), sondern weil es innen wirklich so optimal wie nur möglich programmiert ist. Da steckt inzwischen mehr als ein Jahr Arbeit drinnen, ich erzähl Storys vom Krieg und wie simpel das Leben damals war und er verbeißt sich in den Befehlen :)) Ich kenn kein XT-BIOS das gleichzeitig dermaßen kompatibel und komplett ist. Wir haben sogar die Kasettenroutienen wieder untergebracht - schneller und kürzer als beim PC :))


    Primärziel ist ein 100% kompatibles XT BIOS für 100% kompatible 8088 Rechner - was nicht heist dass nicht auch andere Kombinationen (optional) unterstützt werden.

    Sonst wäre wahrscheinlich auch Raffzahn daran interessiert.

    Auch an den Disks...


    Ja, durchaus - wobei mehr noch als an den Disks sind es Handbücher und Zubehör. Wir haben z.B. zwar den original Monitor (gruseliges Blechteil, aber im richtigen Farbton), aber nicht den 'Grafikzusatz' welcher über den I/O Bus angeschlossen war.

    Wenn das ASCII ist wundert es mich nicht dass die 3741 die Diskette nicht erkannt hat.

    Aus dem Link habe ich ja die Beschreibung, wie es aussehen soll :)


    Und ich weiß, dass es zumindest bei z/OS da auch irgendwelche ASCII-Varianten gibt; aber keine Ahnung, ob es die damals auch schon gab. Und evtl. haben sie den Bootstrap auch noch mit EBCDIC gemacht, und der wurde dann irgendwann mit der ASCII-Version ersetzt, sobald sie die Entwicklung auf die P6060 selbst verschoben haben. Aber das sind natürlich alles nur Spekulationen.


    Nicht vergessen dass die /360 ursprünglich für die Arbeit mit ASCII gedacht war. Erst mit der /390 wurde der ASCII Modus rausgeworfen. Wobei das eigentlich nur beim Packen/Entpacken eine Rolle spielt. Für alles andere ist es eh egal welchen Code man verwendet.


    Was die Entwicklung anbelangte, so erfolgte die wohl eher auf Olivetti's eigenen /360artigen Mainframes :))


    Ansonsten ist die Verwendung von ASCII Daten(strukturen) auf IBM- 8 Zoll Disketten weder was besonderes noch auf Kompatible beschränkt. EBCDIC ist nur Standard wenn die Disketten auf 3740 Data Entry Systemen beschrieben wurde, bzw. zu deren Arbeitsablauf kompatibel sein sollte. Terminalsysteme (im allgemeinen unter 3270 subsumiert) gab es sowohl in EBCDIC als auch ASCII varianten - letztere schrieben auch ASCII auf lokale Disketten

    So wie mir das Raffzahn erklärt hat, geht es mehr um die internen Peripherie Busse.


    Aber vielleicht will er hier ja Licht in unser Halbwissen werfen?


    Jaja, immer mich mit reinziehen. Ok, gut, ich bin spezialisiert in Halbwissen:))


    Und nein, es ist nicht der Peripheriebus, sondern die CPU die die P606x /360 kompatibel macht.


    Der Peripheriebus ist der wie er zuerst für dieP102 verwendet wurde. Und ja, P102, nicht P101. Die P102 war eine weniger bekannte Weiterentwicklung für die Britische Armee und andere Kunden mit viel Kleingeld. Der Bus wurde anschließend bei den P6xx verwendet, der P606x (es gab noch eine erweiterte P6066, erkennbar am grauen Gehäuse), den BCS und den Bankensystemen verwendet.


    Ich habe die gleiche Frage wie Pyewacket : trotz intensiver Suche konnte ich keinen Hinweis finden, dass der 6060 Prozessor den IBM /360 Maschinencode emuliert. Das würde doch nur Sinn machen, wenn Olivetti Softwarelizenzen von IBM für /360 Software genommen hätte.


    Warum? Auch andere können Software schreiben. An der Stelle sollte man nicht vergessen, dass die Geschichte von Olivetti extrem ... aeh, sagen wir mal vielfältig war. So gab es auch eine Zeit als Mainframehersteller. Das brachte nur die äußerst bemerkenswerte ELEA 9003 / 6001 Ende der 50er Jahre, sondern auch /360 kompatible Rechner in den 60ern und 70ern. Dazwischen die ELEA 9004 und 4001, jeweils inkompatible miteinander und vorherigen und späteren Modellen. Ich wills jetzt nicht behaupten, aber dieser Wildwuchs kann durchaus mit ein Grund gewesen sein warum Olivetti sich ende der 70er aus dem Geschäft verabschiedete.


    Wenn man aber per Google sucht und das Ergebnis auf archive.org reduziert, wird man fast schon erschlagen... klick

    Die hatte ich auch gefunden, sind aber auf italienisch.

    Beim durchblättern ist mir die Ähnlichkeit des Befehlssatzes mit IBM 360 auch aufgefallen.

    Warum das so gemacht wurde weiss ich nicht. Schaut man sich den IBM5100 ff an arbeiten die auch nach

    diesem Prinzip. Vielleicht hatte Olivetti das BASIC irgendwo dazugekauft?


    Da Olivetti bereits /360artige Mainframes im program hatte macht es auf jeden Fall 1974 viel Sinn auch den ersten Desktop auf Basis einer CPU nach /360 Art zu bauen. Und nein, nicht weil man irgendwo Software zukaufte - dass war meines Wissens alles Eigenentwicklung - sondern weil man die Hardwareentwicklung eh schon im Haus hat.


    dirkt vermutet, dass Olivetti noch "was größeres" vor hatte, was IBM /360 kompatibel sein hätte sollen.


    Fast. Olivetti hatte VORHER schon was /360 kompatibles im Program :)


    super, danke ! Das führt schon mal weiter. Und in der Tat: der 6060 Assembler ist bzgl. Syntax dem /360 Assembler sehr sehr ähnlich. Und der Maschinencode ist (nach kurzer Durchsicht geschätzt) zu 75% binärkompatibel zum /360 Code. Es gibt aber auch deutliche Differenzen, die der anderen Architektur der 6060 geschuldet sind (zB Stack-Kommandos) - interessante Maschine !


    Olivetti hat natürlich wie eigentlich alle damaligen Herstelle von /360 kompatiblen Rechnern eigene Erweiterungen. Ähnlich wie NEC in ihrer V20, oder AMD mit AMD 64 in der x86 Welt eigene Erweiterungen hatte. Siemens hatte z.B. auch einige sehr interessante Erweiterungen in ihren X-CPU. U.A. auch stackartige Befehle - aber nicht nur einen sondern gleich 2 verschiedene Ansätze. Letztendlich hat es die IBM ja auch vorgemacht. Zum einen gab es über die Jahre eine ganze reihe von /360 basierten Entwicklungen, zum anderen natürlich eine schier unglaubliche Anzahl von Erweiterungen für die eigentlichen /360 /370 /390/ etc. Wer sich mal die aktuellen Z-Series anschaut, der wird nie wieder behaupten dass ein Pentium eine CISC CPU ist :))


    Kompatibilität in dem Bereich bedeutete immer nur dass der /360 (bzw. /370) User-Mode-Befehlssatz der gleiche ist wie bei der IBM Hauptlinie. Das heist, dass Kunden die man von IBM abgeworben hat ihre User-Land Programme mit geringem Aufwand auf Siemens, Olivetti, Hitachi, Amdahl, etc. portieren konnten. Am Umgekehrten weg hatte keiner dieser Hersteller irgend ein Interesse.


    Nicht Vergessen, der Mainframemarkt bestand und besteht bis heute fast komplett aus Individual-Programmen.


    Wenn man also /360 Kompatibilität in dem Bereich messen will, dann ist die Frage nicht wieviel Anteil hat der /360-gleiche Teil an der Maschine, sondern wieviel % des nicht privilegierten /360 Befehlssatz ist 1:1 vorhanden. Ich hab jetzt nicht nochmal nachgeschaut, aber in meiner Erinnerung geht das bei der P6060 gegen 100%



    Roland_t29 , denk nochmal über das Angebot nach, Dirk ist auch dafür... Du hast ja dann gleich den Dirk, der sich drum kümmert...


    Das größte Problem ist halt etwa 1m² Platz wo sie stehen kann und was bei geschätzt 40kg nicht zusammenbricht.

    so verführerisch das ist, wir schaffen das platzmäßig nicht adäquat. Unsere IBM 5100 musste auch schon von ihrem angestammten Platz weichen, weil wir Platz brauchen


    Oh, ich wüsste da einen Platz - direkt neben unserer 5110 :))


    Ansonsten, die P6060 ist ein wirklich bemerkenswerter Rechner - und durchaus zusammen mit einer P652 darstellbar (beide verwenden die Gleiche I/O).

    SRAM MMU, sowas ist verbaut im MC-68000-Computer.

    Auszug MC11/1984: MC-68000 Teil 1_Schaltung.pdf
    diese "MMU" ist auf Seite 123 zu finden.
    Leider hab ich so noch immer nicht verstanden.
    Vermutlich müsste ich länger drüber nachdenken.

    Habs damals auch interessiert angeschaut :))


    Und jain. Die Schwierigkeit beim Verständnis dürfte darin liegen, dass es keine MMU sondern ein programmierbarer Adressdekoder ist.


    Die Aufgabe des 1Ki x 4 RAM ist nicht das 'Umschaufeln' von RAM auf andere Adressen, sondern festlegen welche Bausteine auf welche Adressen reagieren.


    Input ins RAM sind die oberen 10 Adressbits (A14..23), Output vom RAM sind 4 bit mit einem 'Selektionscode', welcher dann per stino 138er in 16 Chipselect-Leitungen dekodiert wird. Für jeden 16 Ki (14 bit) Block im Adressbereich kann man so festlegen ob sich RAM, ROM, I/O oder Slots angesprochen fühlen soll. Was diese dann wiederum mit der (ganzen) 24 Bit Adresse machen oder nicht ist deren Sache.


    Der Clou ist dass halt einige der CS Codes verwendet werden um spezielle Funktionen zu erzeugen:


    - Busfehler

    - Automatisches DTACK


    Mit Busfehler kann man Speicherbereiche gegen Zugriff schützen, mit dem automatischen DTACK kann man auch auf Adressen zugreifen die es (möglicherweise) nicht gibt - hilfreich um zu schauen of irgendwo ein Zusatz-ROM eingesteckt ist oder nicht.


    Könnte man auch alles mit einem schnellen PROM bzw. PAL machen, oder halt einem TTL-Bergwerk.


    Im Sinne einer MMU kann eigentlich nur den Speicherschutz verwenden indem man während der Laufzeit eines Tasks alles was der nicht haben darf auf gesperrt setzt. Aber 1024 transfers bei jedem Taskwechsel ist teuer.


    Was man natürlich machen könnte ist das RAM breiter machen (bis 16 bit geht problemlos) und da dann auch eine Adressübersetzung reinmachen - verändert aber das Dekodertiming. Da könnte man dann auch Adressen rumschieben und/oder sinnvolle Bits wie 'read only' und 'no Execute' einführen.


    Bei 65816 würde das auch gut gehen, wobei ich da gleich mit 64 Ki Blöcken arbeiten würde. Das ist der für die CPU am besten passende Kompromiss zwischen feiner Stückelung und wenig Overhead. Kommt auch ganz natürlich, da die 65816 den Speicher eh als 256 64 KiB Blöcke sieht.

    Ohne Raffzahn jetzt im einzelnen zu zitieren - am Ende sind wir denke ich gar nicht so weit auseinander.

    Äh jain. Ich muss an der Stelle vielleicht etwas Historie einfügen. Der Kernel basiert auf einem kooperativen multitasking/threading System das ich 1990 auf 286ern implementiert habe. Dabei gab es (fast) keine normalen user Tasks, sondern alles bestand aus asynchronen Aktivierungen die wiederum über Aktivierungen miteinander redeten. Das ganze war ein Kommunikationssystem das eine oder mehrere Hostverbindungen (zu einer /370) bediente und bis zu 32 Clientverbindungen (Modem bis 14400). Über die Modems waren mobile Systeme angebunden welche über den Knoten mit verschiedenen Services auf dem Host redeten. So klassisch client-server wie es nur geht.


    Da (nahezu) alles in dem system aus einer Vielzahl von asynchronen Aufrufen/Nachrichten zwischen den Komponenten und zum Host bzw. Client bestand gab es keinen Bedarf auf preemptive Verwaltung. Die Hauptaufgabe des Timertasks war die Verwaltung von Timerlisten. Zusätzlich wurden (im Interrupt) die Aufgaben auf Langläufer, also solche die länger brauchten als erwartet, überwacht, was auf der Konsole gemeldet wurde und bei noch mehr Überschreitung zu Systemhalt führte. Der Vergleichswert wurde dabei über eine Statistikfunktion im laufenden Betrieb ermittelt.


    Sie 6502 Version ist nun das Ganze etwas runterskaliert (nur 8 bit Handles für Kontrollblöcke, Speicherbereiche, Tasks, und so weiter) und mit einem Layer versehen der es erlaubt Prozesse zu unterbrechen - das ist der oben geschilderte neue Timer - der im Originalsystem lief nicht in den Scheduler. Der Scheduler kümmerte sich daher nur um die Aktivierungsliste und hatte mit Prioritäten nix am Hut (ok, die Hostschlange wurde immer zuerst behandelt schliesslich ging es darum den Mainframe am arbeiten zu halten, damit die Clients schnell ihre Antworten bekamen :))


    Das es den Layer gibt ist eigentlich nur dafür da dass auch Idiotenprogramme laufen können, da meine eigenen Sachen eh asynchron aufgezogen sind. Ist einfach einfacher.

    Bezgl. Lauffähiger Tasks - da Interrupts nur bedingt in den Scheduler eingreifen können (aktuell) können sie dafür Signale absetzen. Da wird dann der Kontext und Stack des wartenden/interrupteten Tasks entsprechend modifiziert, so dass beim Wiederanlauf erst die Signal-Routine aufgerufen wird, und die dann mit IIRC RTI in den ursprünglichen Code zurückspringt.


    Hihi. Meine ursprüngliche Idee für das neue System sah ganz ähnlich aus. Da waren Aktivierungen nicht einfach eine globale Lise, sondern die Blöcke waren eine verkettete Liste je Task. Im Prinzip hatte jeder Task einen TCB der Seine Daten enthielt und einen Zeiger auf einen Aktivierungsblock der im Prinzip den Programmzustand der letzten Zeitscheibe enthielt. Wenn jetzt ein asynchrones Ereignis kommt dann wird dafür ein neuer ACB angelegt und entsprechend der Unterbrechungspriorität eingehängt (der standard-ACB hat niedrigste mögliche Priorität). In der Praxis bedeutet es er wird am Anfang der Kette zwischengeschoben. Das führt dann dazu, dass bei der nächsten Zeitscheibe diese Ebene zuerst ausgeführt wird.


    Wenn die Arbeit getan ist, dann beendet sich die Routine mit TERM, was dazu führt dass der ACB ausgefügt wird und für den Rest der Zeitscheibe (oder der nächsten) die Ebene drannkommt die 'Unterbrochen' wurde. Wird der letzte ACB ausgefügt dann wird der Task beendet (*1)- das ist der Fall wenn das Hauptprogramm TERM aufruft :))


    Ich hab das ganze nur verworfen weil es einfach zu viel Verwaltungsarbeit für die keine 6502 ist - zumindest für 1 MHz. Wobei die Idee noch nicht tot ist. Dann sähe der Scheduler etwas anders aus.


    *1 - eigentlich doch nicht, weil interaktive Tasks - die die ein Terminal haben, dann in die Kommandozeile zurückfallen - aber das ist ne andere Geschichte.


    D.h. ein "waiting for signal" task gilt auch als lauffähig. "waiting for receive" (wie z.B. die Dateisystem-Tasks wenn sie nichts zu tun haben) sind nicht lauffähig, denn sie warten auf einen anderen Task.


    Jo, wobei bei mir die Tasks da keine verschiedenen Zustände haben. Sie sind einfach nicht Ablauffähig. das macht den Scheduler einfach.


    Am einfachsten ist das wohl erklärt wie ein blockierender (synchroner) Schreib oder Leseaufruf funktioniert:


    • Der Befehl wird in ein Aktivierungspacket für den zuständigen Dienst verpackt. Das enthält z.B. FCB und Pufferadresse des Datenblocks zum lesen
    • Das Paket wird in die Aktivierungsliste übernommen
    • Der Task erklärt sich für nicht ablauffähig (wie ein Yield der aber nicht zurückkommt wenn die nächste Zeitscheibe verfügbar ist)
    • Mit dem Paket wird (hoffentlich bald) das DMS (Datei-I/O) aktiviert
    • Das DMS macht was immer gefordert ist
    • Wenn DMS fertig, wird ein Paket mit der Quittung fürs Schreiben oder Lesen (letztere hat den Puffer schon gefüllt) erzeugt
    • Paket kommt in die Aktivierungsliste
    • DMS kehrt in den Scheduler zurück
    • Der Scheduler sieht das neue Paket in der Liste (hoffentlich oben an)
    • Scheduler aktiviert den Empfänger im Usertask
    • Die Aktivierung landet (bei so einem blockierenden Aufruf) in einem Default-Handler für 'DMS Fertig'
    • (Wars ein asynchroner Aufruf so hat der Task ja einen eigenen Handler installiert)
    • Der Default-Handler macht
      • aus der Meldung einen Returncode wie er im Handbuch steht (eigentlich nur ein kopieren),
      • schreibt den RC in den TCB,
      • gibt dem Task unter Umständen einen Prioritätsboost,
      • erzeugt ggf. eine Aktivierung für einen Loggingeintrag (erleichtert userside Debugging enorm),
      • setzt den Task auf Ablaufbereit (was ggf. die Priorität neu berechnet),
      • Und kehrt in den Scheduler zurück (einfacher Return)
    • Der Scheduler macht in der in der Aktivierungsliste weiter
    • Ist keine Aktivierung mehr da, dann sucht er den höchsten ablaufbereiten Task (hoffentich unserer) und startet ihn

    Klingt jetzt kompliziert, ist es aber nicht. dafür aber extrem flexibel - auch weil man alles umleiten kann. Z.B. um zwecks debugging, weil um jede Funktion dynamisch eine Mantelfunktion machen kann die z.B. Logging macht, zusätzlich parameter überprüft und so weiter. Das dauernde hin und her zwischen Sicherheits- (viel Überprüfung) und Performance-Versionen wird zum Kinderspiel und kann task- und funktionsspezifisch gesteuert werden.


    Dienste haben zwar eine Tasknummer und werden so verwaltet, sind aber eigentlich nicht selbstständig lauffähig sondern eher eine Funktionsbibliothek die indirekt aufgerufen wird. Wobei das jetzt halt wieder so nur halb stimmt, weil natürlich kann eine Aktivierung in einem dienst mehr als eine Folgeaktivierung haben - neben dem zurück an den Usertask könnte ein zweiter ACB (oder noch mehr) erzeugt werden, der z.B. einen FS-Check oder einen Defrag anstößt. So gesehen haben Dienste natürlich ein Eigenleben, aber halt nicht so wie Usertasks.


    (Die Codestückchen die da so aktiviert werden heißen bei mir Contingency Routinen - weil sie auch für jede art von Fehlerausgängen verwendet werden)

    Für IO gibt es zwei Möglichkeiten: mit char-devices via Streams zu kommunizieren, wie z.B. Konsole oder Serial. Alles andere ist asynchron. [...]


    Da der SEND/RECEIVE buffer als eines der letzten Überbleibsel noch an einer fixen Adresse steht ($0200) und die character-weise Übertragung von Dateien via Streams laaaanngggsssaaaammm ist, will ich beides über sog. Buffer und Channel neu umsetzen: https://github.com/fachat/Geck…er/GeckOS-NG-Buffers.adoc


    Wie gesagt, mein Ansatz ist eher Mainframe ohne den Wunsch Unix nachzubauen. Daher mit Records als Basis für den Datenzugriff , damit gibts das Problem nicht. Ich hab aber auch schon drüber nachgedacht - wegen 08/15 Programmen in C :))


    Dir ist sicher schon aufgefallen, dass das in Teilen einem L4 artigen Nanokernel ähnelt (war nicht beabsichtigt, hab ich erst später kennengelernt). Dort ist praktisch alles was das OS macht ein Aufruf an irgendwelche Dienste. Und neben 'normalen' Nachrichten, die meinen Aktivierungen ähneln, hat der L4 auch eine superschnelle Kurznachricht eingeführt. Das ist im Prinzip der Aufruf einer Funktion in einem Dienst mit minimalen Daten (nur 2 Registern) und ohne kompletten Prozesswechsel. Klingt doch wie gemacht für character I/O, oder?


    (Mein eigenes Konzept hatte vorgesehen solche blocking-/deblocking-Funktionen durch Systemcode innerhalb des Usertasks zu erledigen, aber das L4 Prinzip scheint mir besser)


    Was an der stelle richtig nervt ist dass dynamische Speicherverwaltung fr die 6502 echt hart ist.


    Edit: das log-file enthält im Prinzip den kompletten Boot-Prozess, ist ca. 3.6GB groß, und es hat > 2 Tage gebraucht das zu flöhen....!

    Genau deswegen ist Logging die einzige Ecke an der ich eine Ausnahme mach und hart am ansonsten asynchronen System vorbeiprogrammier.

    Prio heisst einfach, ein Task bekommt mehrere Timeslices. Insofern ist die "Scheduler" "Logik" minimal bis nicht vorhanden. Wenn man um Tasks zu schedulen in einen separaten Task springen müsste, wäre es ein starker extra Aufwand.

    Nachtrag: Das da erinnert mich extrem an das Multitasking-Etwas (nee, OS will ichs nicht nennen) das ich ca 1982 auf dem Apfel gebastelt habe.


    Ich hatte damals frisch eine Karte mit einem 6522 und das musste ausprobiert werden und Taskswitching war die erste Idee.


    • Fest n Tasks
    • 3 Listen mit je einem Wert pro Task in der ZP
      • TCB_ZS mit den Zeitscheiben
      • TCB_AS mit der aktuellen Scheibe
      • TCB_SP mit dem Stackpointer des Tasks
    • 1 Byte ZP Taskpointer
    • Feste Zuordnung von Stackbereichen

    Der Code ging ungefähr so

    Fetisch.


    Um einen Task zu erzeugen musste man

    • ein Stück Stack suchen,
    • dort
      • Flags,
      • Startadresse,
      • X und
      • A ablegen,
    • den Stackpointer in TCB_SP,* ablegen.

    Sowie man jetzt in TCB_ZS,* einen wert Ungleich 0 schrieb wurde der Task ausgeführt.


    :))

    Bei 1 MHz läuft er rund 0,25s, das ist jetzt nicht gerade kurz. 12 mal wieder angetriggert und DU hast deine 3 Sekunden mit einer reinen Softwarelösung, Null Hardware. Und nebenbei kann der auch gleich ne Echtzeituhr und das Gerüst für eine generische Timerliste liefern:))


    Klingt schon wirklich eleganter - aber leider zerreißt es das dann auch als Erstes, wenn doch mal jemand den 14MHz WDC 65C02 probieren will. Oder den - habe ich neulich mal "gelernt" - Nachbau namens 65f02 , als FPGA mit 100 MHz. (sehr interessantes Teil)

    Öh, die Systemlast bleibt die Gleiche, Nicht?


    Die CPU ist 14 mal schneller und muss dann halt 56 interrupts pro Sekunde verkraften. 3 Sekunden gehen dann immer noch gut in ein Byte :)))


    Ok, bei 100x schneller brauchts dann halt noch einen zweiten DEC und einen BNE drumherum um 16 bit zu handhaben. Wobei, hat die Emulation eigentlich auch eine 6532? Und wenn ja, warum den nicht aufbohren, weil ist ja eh nur FPGA:))

    Sagt der Mann dem 2 TTL und ein bisschen Glue zu viel sind um high speed DMA zu machen

    Hmm, hab ich zwar nie gesagt - kannst du weiter oben auch nochmals nachlesen - aber lass mal gut sein... ich bin jetzt nicht wegen anderer Meinungen bzgl. meiner Entwicklungsentscheidungen gleich auf Krawall gebürstet. :)


    Wer redet denn von Krawall?


    Ich bin einfach erstaunt über den hohen Hardwareaufwand für was was man auch ohne kann. Auch mit 6532.


    :wegmuss:

    Teil 2 - deppertes system:

    (Interrupts und Zeitscheiben) Damit das dann klappt haben Interrupts auch noch die Möglichkeit den gerade laufenden (User) Task beschleunigt zu beenden. Dazu gibt es eine Funktion die in das Byte der aktuellen Zeitscheibe den Wert 1 schreibt - dadurch wird dieser nach mindestens einem, spätestens zwei Ticks an den Scheduler gegeben (*7). Die Idee dahinter ist, dass die meisten Tasks eh nicht im Dauerbetrieb laufen und damit die Chance bekommen das fertigzustellen was sie gerade machen und damit keine Folgeaktivierungen nötig werden (jemand fertig machen lassen ist immer besser als dauernd stören) während Langläufer ausgebremst werden zwecks besserer Reaktion. Das ist insbesondere Hilfreich wenn das System mit Swapping arbeiten sollte und Deaktivierung eines Task auch Auslagerung bedeuten könnte. 6502 ist halt kein Speicherriese :))


    *7 - Ob 1 oder einer höherer Wert richtig ist kommt auf CPU und Ticklänge an. Eine der vielen Schrauben für Performance.



    Das wär das Grundkonzept. Da drüber sind dann schon die Systemfunktionen die dem Anwender zugängig sind. Das normale Zeug, aber auch die Kommunkationssachen und die Steuerung für die Unterbrechungen (Threads), was eigentlich dass gleiche ist.


    Wenn der Scheduler ausgehend vom aktuellen Task keinen lauffähigen Task mehr findet (also auch nicht den originalen) wird ein Dead system erkannt und neu gebootet.


    Das gibts bei meinem System nicht, das OS läuft immer, auch wenn aktuell kein Task da ist.


    könnte man ggf. auch mal einen "bluescreen" einführen ;)


    LOL. Klar, Bluescreen muss sein :)


    Ich hab an der Stelle den System-Halt vorgesehen. Der springt in eine Art Minimal-Monitor mit dem man im Speicher rumstochern kann.


    Ich finde immer noch dass das für einen 6502 eine ziemlich effiziente Lösung ist.


    Wie gesagt, wenn läuft, und das tuts, dann ist es eine gute Lösung.


    Als grobe Ideen sind allerdings in der Tat noch Verbesserungen geplant, wie eine Linked List der lauffähigen Tasks. Muss ich aber erst noch durchdenken ob das so viel bringt. Auch eine Monitoring-Lösung die Statistiken macht, wie viele Tasks im Schnitt lauffähig sind etc. gehört dazu.


    Da drinnen steckt das ganze Potential zur Optimierung. Ich hab das in meinem Konzept erstmal nach hinten geschoben, bzw. nur minimal angedacht, weiß aber wie wichtig das ist. Letztendlich gehts da um einen Satz and Statemachines die das kodifizieren wie auch Last die weit über das rausgeht an das wir so denken verwaltet werden kann.


    Ich freue mich auf die Diskussion :)


    DHDEDWE :))


    (Ach ja, obiges hat sicher ne halbe million Typos und Dreher, dürfen alle frei verteilt werden :))

    Nachdem in Junior Computer ][ angefangen wurde über Mulitasking-OS und GeckOS zu reden, mache ich das mal hier auf.

    Passt.

    Wahrscheinlich macht GeckOS aus euer beider Sicht Mist... ;)

    Ned wirklich, weil es greift Regel#1: Was läuft hat recht.


    Ansonsten muss ich eh vorausschicken, dass meine Anmerkungen auf einem halb fertigen Kernel, bzw. dessen Planung, für den Apfel II+ (mit 65C02) beruhen. Die Struktur beruht dabei in weiten Teilen auf meinen /370 Erfahrungen (was mangels Stack eh das beste Beispiel ist) angepasst an die Probleme der 6502 mit komplexen dynamischen Strukturen. Entsprechend sind vielleicht einige Namen ungewöhnlich. Das ganze ist ein On/Off-Projekt seit 20+ Jahren :))


    In GeckOS wird der "Scheduler" hauptsächlich im Timer-Interrupt ausgeführt, kann aber auch durch freiwillige Aufgabe eines Tasks mit YIELD aufgerufen werden. Beim Aufruf wird einfach der Kontext (also ob Aufruf z.B. durch YIELD, Interrupt oder system call wie blocking RECEIVE) gespeichert und in der (statischen) Liste nach dem nächsten lauffähigen Task gesucht. Prio heisst einfach, ein Task bekommt mehrere Timeslices. Insofern ist die "Scheduler" "Logik" minimal bis nicht vorhanden. Wenn man um Tasks zu schedulen in einen separaten Task springen müsste, wäre es ein starker extra Aufwand.


    Einen Idle Task gibt es nicht, wieso auch? Solange ein lauffähiger Task da ist kann er voll durchrennen, nur unterbrochen davon im Timer-Interrupt zu schauen, ob andere Tasks jetzt lauffähig sind.


    Ist bei mir recht verwandt, außer dass der Timer den Scheduler nur vorbereitet. Der Scheduler arbeitet nur wenn entweder der Task Zeit abgibt (yield) er eine synchrone Aktion veranlasst (was intern ähnlich yield ist), eine Nachricht absetzt oder ein vorhergehender Interrupt meint dass der Scheduler auch mal wieder was tun sollte.


    Yield ist dabei ein VPASS 0, ein Warten mit Zeit Null - weil jedes Warten eine implizite Abgabe von Rechenzeit ist, auch bei 0.


    (Ach ja, OS aufrufe sind als SVC gemacht - unter Verwendung von BRK :))


    (Zeitscheibe/Timer) Das mit den Zeitscheiben hat bei mir Ähnlichkeit, nur wird die im Timer in einer verkürzten Weise behandelt. Jeder Task hat einen Zeitscheibenbeiwert der normalerweise (also derzeit immer) einer Standardscheibe entspricht, gemessen in Ticks (*1). Wenn der Task aktiviert wird, dann wird die Zeitscheibenlänge-1 in ein ZP Byte des Timers gespeichert. Jeder Tick verringert das um 1. Ist der Wert >= 0 kehrt der Timer einfach zurück. Ist der neue Wert kleiner 0 (*2), dann wird der Scheduler aufgerufen.


    *1 - Ein Timer Interrupt kann die Uhr um mehrere Ticks weiterstellen kann, aber das ist ein anderes Thema


    *2 - Und ja, das bedeutet dass die maximale Anzahl Ticks einer Zeitscheibe 128 ist, nicht 255. Grund ist dass so ein einzelner (warum auch immer) verhunzter Test auf 0 nicht zu einer 254 Tick langen Zeitscheibe führt. Ist ein dicker, fetter Wachposten :))


    (Übergang zum Scheduler) An der Stelle kann man jetzt drüber philosophieren ob mein Scheduler jetzt im Interrupt läuft oder im normalen Kernel-Zustand. Ich seh es logisch als Letzteres, wie bei (einigen) Mainframe-OS wo der Interrupt (P3) nicht in den unterbrochenen Task (P1) sondern im OS-Zustand (P2) weitermacht.


    Der Trick ist dass der Interrupt zu einem OS-Funktionsaufruf wird. Hat der Timer erkannt, dass der Scheduler was tun sollte, macht er keinen RTI, sondern springt in einen privaten Eingang im OS-Funktions-Handler. Ab da ist es kein Timer-Interrupt mehr sondern ein "eingefügter" OS-Aufruf durch den Task. Ein Aufruf wie ein Yield, oder jeder andere OS-Aufruf der zur Deaktivierung führen kann.


    Der extra Eingang im Funktionshandler ist halt weil dieser Aufruf keine normalen Parameter hat und auch die Rückkehradresse daher nicht anpassen muss. Technisch ist der private Eingang ein kleines Stück Code das eine Funktionsnummer und Parameter hinterlegt und dann hinter der Parameteranalyse weitermacht, wenig mehr als LDA/STA/STZ/BNE. Was folgt ist der Funktionsverteiler.


    (Funktion) Die Funktion die aufgerufen wird kann man eigentlich als NOP sehen. Am ihrem Ende verzweigt sie in den Scheduler, genauso wie verschiedene andere Funktionen (z.B. alles blockierende), die nicht (oder nicht immer) direkt zurückkehren.


    (Scheduler Schritt A) Wenn der Scheduler startet, egal was vorher war, dann schaut er zuerst in die Aktivierungsliste. Das ist eine Liste mit Ereignissen (Nachrichten an Tasks) diese werden zuerst zugestellt - sprich der Task (*3) aktiviert (siehe unten). Man kann es sich auch als 'synchrone Interrupts' oder 'Superpriorität' vorstellen. Diese Aktivierungen erfolgen mit einer minimalen Zeitscheibe, abgeleitet aus Priorität und Nachricht (derzeit einfach die Standardscheibe). Weiter bei (Aktivierung)


    *3 - Das kann durchaus erstmal ein Task eines Systemdiensts sein, z.B. das File-System, welcher noch irgendwas macht und erst das Ergebnis dann an den eigentlichen Empfänger schickt (Derzeit nicht, FS code läuft noch im Usertask).


    (Scheduler Schritt B) Wenn kein Eintrag in der Aktivierungsliste ist, dann wird in die Liste der ablaufbereiten Tasks geschaut (*4). Existieren dort Tasks, so wird der mit der höchsten Priorität ausgewählt. War der Aufruf ein Yield und der Top-Eintrag ist der des immer noch aktuellen Tasks, so wird der nächste genommen.


    *4 - Die Liste ist eigentlich einfach, komplizierter ist wie ein Task da ein oder ausgetragen wird, bzw, von dort zu anderen wechselt oder zurück. Also eigentlich nicht kompliziert, nur was mit vielen verschiedenen Bedingungen - welche aber nicht der Scheduler machen mus, die laufen Andernorts ab, der Scheduler muss wirklich nur da durchrutschen. Durchlaufen deswegen weil ich mir das Umsortieren sparen will. Hier stecken dann auch die ganzen Schrauben an denen man zum Thema performance drehen kann. Welche Tasktypen will man bevorzugen, Batch, Dialog oder Services? Etc. PP. Aber wie gesagt, anderes Thema. Ach ja, es ist keine linked list, sondern ein einfacher Puffer mit 2 Werten je Task: TSN und Prio.


    (Scheduler Schritt C)

    Wurde ein ablaufbereiter Task gefunden?

    Ja -> Der Neue wird aktiviert. Siehe (Aktivierung)

    Nein ->Die CPU läuft auf einen WAI. Fällt es vom WAI runter, dann geht es weiter bei (Scheduler Schritt A)


    (Aktivierung) Ist der Neue NICHT der Gleiche wie der (noch) Aktive, so werden

    • temporär gespeicherte Werte (Register etc.) in den aktiven TCB übertragen,
    • Accounting gemacht
    • eine Prüfsumme (EOR) gebildet und gespeichert
    • die Prüfsumme des neuen TCB überprüft (Wenn Falsch -> System-Halt wegen Wildläufer)
    • Alle nötigen Werte aus dem TCB kopiert

    Und zum Schluss der Task entsprechend der Art (*5) gestartet.


    *5 - Hier wird unterschieden ob ein Task nach einer Unterbrechung/Deaktivierung wieder angestartet wird, oder ob der Task selber in einer Unterbrechungsebene läuft. Der Einfachheit halber kann man sich so eine Unterbrechungsebene wie einen temporären Thread vorstellen der innerhalb des Tasks eingerichtet und gestartet wird anstelle der Hauptebene. Unterbrechung deswegen weil die Hauptebene wie bei einem Interrupt unterbrochen wird. Die Nützlichkeit sollte auf der Hand liegen - z.B. bei asynchroner Kommunikation oder auch der Fehlerbehandlung. Das OS kann z.B. Fehlermeldungen zu Funktionen als Unterbrechung schicken, und so weiter und so fort. Zugegeben, nix was man für Hello-World braucht :)))


    (I/O) Was zum Gesamtbild jetzt noch fehlt ist die I/O (inkl. Timer). Alle I/O (*6), läuft asynchron. Entweder vom Userprogramm aus, oder weil es das OS unter der Haube so gestaltet. Z.B. beim Schreiben auf eine Datei bedeutet dass, dass der Schreibaufruf die Daten an einen ACB kettet, den in die Aktivierungsliste hängt, und dann bei (Scheduler Schritt A) weiter macht. Egal ob das vom Userprogram her ein synchrones oder asynchrones Schreiben war. Der Unterschied ist dann nur ob der Usertask selbst anschließend Ablaufbereit ist oder nicht (blockiert).


    *6 - Ausgaben auf die Systemkonsole (Debug, Logging) ist Ausnahme. Die geht zwar eigentlich durch die gleichen Wege, erfolgt aber auf dem kürzest möglichen Weg und (soweit geht) ohne Unterbrechung. Das mag das System langsam machen, aber Debugging sollte schon sein.


    Die Struktur erlaubt dann auch Treiber zu schreiben die ähnlich wie in manchen Systemen mit Strategie und Ausführung arbeiten und dabei voll dem Tasking unterliegen.


    Ob die I/O selbst dann in Interrupts läuft, oder nicht ist ein anderes Thema, ich geh aber davon aus dass in den meisten Fällen ja. Am Ende wenn die Daten geschrieben wurden hängt der Treiber halt wieder einen ACB ein der dann den Usertask weiterlaufen lässt. Da liegt wiederum ein Stückchen Schwupzidität: Jedwede I/O kommt so schnell wie möglich nach ihrem Ende beim passenden Usertask an, der dann auch reagieren kann. Egal ob auf Mausschupsen oder ein Bild von Platte lesen.



    ... zu viele Zeichen ... Teil 2 folgt ...

    Der zwar immer vorhandene Timer im RIOT ist leider ungeeignet, da er viel zu kurz läuft und auch nicht repetierend ist.


    Der Hardwareaufwand hält sich ebenfalls in Grenzen, da im verwendeten 74LS123 zwei Monoflops verbaut sind. Zusätzlich braucht man noch zwei JK Flipflops (74LS112) und nochmals ein GAL16V8, was ich aber für den Graphics Controller so oder so noch benötigt hätte.

    Bei 1 MHz läuft er rund 0,25s, das ist jetzt nicht gerade kurz. 12 mal wieder angetriggert und DU hast deine 3 Sekunden mit einer reinen Softwarelösung, Null Hardware. Und nebenbei kann der auch gleich ne Echtzeituhr und das Gerüst für eine generische Timerliste liefern:))


    Oder halt gleich umgekehrt als 4 Hz Timer selbige Warteschlange betreiben und die Floppy-routine hängt sich dort ein und wird nach12 ticks benachrichtigt. Wenige Befehle, großer fortschritt.

    Der Hardwareaufwand hält sich ebenfalls in Grenzen, da im verwendeten 74LS123 zwei Monoflops verbaut sind. Zusätzlich braucht man noch zwei JK Flipflops (74LS112) und nochmals ein GAL16V8, was ich aber für den Graphics Controller so oder so noch benötigt hätte.

    Sagt der Mann dem 2 TTL und ein bisschen Glue zu viel sind um high speed DMA zu machen :))

    Edit: Nochmal zum WAI.


    Wenn du ein Multitasking OS hast, wird der Scheduler normalerwiese als Interrupt-Routine - angestoßen durch einen Timer IRQ - implementiert. Zum Auswählen des nächsten Tasks wird also der Scheduler geweckt, der nächste Task gewählt der rechenbereit ist und die CPU an diesen Task durch setzen der Rücksprungadresse aus der IRQ Routine durch ein RTI ( ReTurn from Interrupt) übergeben. Falls es aber keinen rechenbereiten Task gibt, wird und muss der Idle-Task ausgewählt werden.

    Erzähl dass mal besser nicht den Mainframe-Leuten. Scheduler im Interrupt ist eine potentielle potentielle Fehlerquelle und eine mögliche Quelle richtig ekeliger Performanceprobleme. Interrupts sollen so kurz wie möglich sein und nur die Arbeit machen die direkt für die Hardwarebedienung nötig sind. Alles andere, ist außerhalb zu erledigen. Und das ist was der Code nach dem WAI macht. Einen Idle Task basteln ist zwar recht hübsch aus theoretischer Sicht und um gut um Studenten im ersten Semester nicht zu verwirren, in der Praxis isses aber weniger gut.

    Mag sein - und v.a., wenn man dann in stromlosen(armen) Zustand verharren kann, ist das bestimmt hin und wieder sehr sinnvoll.

    Ich fand es eher so aus "logischen" Gründen irgendwie "schräg".

    Schräg? Weis nicht, das ganze ist viel älter als alle Microprozessoren zusammen. Schon die allerersten Computer (Zuse etc.) hatten einen Halt-Befehl. Weil warum soll die CPU arbeiten und Strom fressen wenn es nix zu tun gibt? Bei der /370 z.B. gibts an der Konsole sogar ein extra Lamperl das leuchtet wenn die CPU auf dem IDLE Befehl steht und nix tut. Wenn das leuchtet dann gibts zwei Gründe: es gibt ix zu tun, oder alle Prozesse warten auf die langsamen Platten :))


    Und der IDLE funktioniert praktisch genauso wie der WAI der 65C02:


    - Die CPU geht in einen stromsparenden Wartezustand.

    - Wenn ein Interrupt kommt wird er bearbeitet

    - Wenn der fertig ist, dann gehts hinter dem IDEL Befehl weiter.

    - und das OS schaut was es so alles zu tun gibt als ergebnis von dem Interrupt.


    Und da liegt dann die ganze Eleganz: So ein Interrupt war ja in der Regel eine I/O oder ein Timer. Wars ein Timer, so muss man jetzt mal nachschauen ob die erreichte Zeit irgend einen Task aufweckt oder so. Wars eine I/O, dann will garantiert ebenfalls irgend ein Task aufgeweckt werden - sei es weil die Daten geschrieben wurden und nun die Puffer frei sind, oder weil die angeforderten Daten geliefert wurden. In beiden Fällen will das Userprogramm weiterarbeiten. Und damit das kann muss das Betriebssystem jetzt nachschauen wer das ist und ob der das darf und so weiter.


    So wein IDLE oder WAI ist der zentrale Punkt um den rum das ganze Scheduling aufgebaut wird. Alles andere ist Kinderkram.

    WAI ... Wait for Interrupt


    wer denkt sich sowas aus ... :)

    sehr skurril

    Praktisch jeder Prozessorhersteller:

    RCA 1802: IDLE (00)

    Valvo 2650: HALT ($40)

    Intel 8080: HLT (76h)

    Zilog Z80: HLT (76h)

    8086: HLT (F4h)

    68000: STOP


    Und auch der Papa der 6502, die 6800 hatte sowas, und das hies sogar auch schon WAI. War in dem Fall sogar schon ein halber interrupt der alle Register sicherte und dann erst wartete.


    Die 6502 hat das dann weggelassen und die 65C02 das wieder behoben - wobei es wurden dann gleich zwei Befehle eingebaut:


    WAI und STP. WAI wacht bei jedem Interrupt auf, STP kann man nur mit RESET beenden.