🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

5 total messages Started by Marten Feldtmann Tue, 19 Feb 1991 22:58
TC 2.0, ST-80V2.3 und Primitives
#453
Author: Marten Feldtmann
Date: Tue, 19 Feb 1991 22:58
748 lines
27443 bytes
        Low-Level C-Funktionen (Primitives) unter Smalltalk-80 V2.3 
                 auf einem Atari-ST mit Turbo-C

Achtung:
         Text geht sehr auf ST80, C und Assembler runter...
             
Smalltalk-80 verfuegt seit der Version 2.3 ueber die Faehigkeit,
Routinen, die in einer anderen Sprache geschrieben worden sind,
anzusprechen.  Diese andere Sprache ist momentan 'C'.  Diese Routinen
nennt man 'User Primitives'. 

                   Smalltalk-80 unter dem Atari-ST 

Fuer die Familie der Atari-Rechner gibt es momentan nur die Version 2.3,
obwohl man inzwischen die Version 2.5, teilweise sogar schon die voellig
neue Objectworks-Release 4 als aktuell ansehen kann.  Doch leider hat
man unter dem ST von Atari nur 4MB RAM zur Verfuegung, so dass man den
Sprung zu 2.5 nicht mitmachen konnte. 

ST-80 wurde auf dem ST mit dem voellig veralterten C-Compiler von
Digital Research implementiert.  So muss man also Spezialitaeten dieses
Compilers bei der Anpassung eines anderen Compilers beruecksichtigen. 

Jeder ST-80 Besitzer bekommt mit den 6 Disketten des ST-80 Paketes auch
Beispiele und Unterlagen ueber die Einbindung sogenannter Primitives
mitgeliefert.  Alles fast passend fuer den DR-C Compiler.  Die weiteren
schriftlichen Unterlagen sind knapp, wie man dies von ST-80 halt
gewoehnt ist.  Dies ist meiner Ansicht nach Ursache von der voellig
falschen Annahme, dass man OO-Programme ohne weiter Dokumentation lesen
kann.  Wenn man sieht, wo die Doku schon fruehzeitig bei der Version 2.3
aufhoert.  Aber auch die Handbuecher von V2.5 waeren noch ausfuehrlicher
zu machen, oder schon mal die Dokumentation zu den sogenannten
VICK-Tools (,die super sind..) gesehen? Laecherlich!

Auf einer dieser Disketten erhaelt man die vorgelinkte Version der
virtuellen Smalltalk-80-Maschine fuer den ST.  Wie es sich gehoert: im
DR-C Format.  Des weiteren liegen noch zwei Include-Files auf der
Diskette herum: 'userprim.h' und 'shorten.h'.  Dies sind die drei
Dateien, an denen Anpassungen gemacht werden muessen. 

Ich besitze alleine 2 verschiedene Versionen der virt.  Maschine. 
Erkennbar sind sie an den unterschiedlichen Programmlaengen.Die
groessere Version unterstuetzt allerdings unter ST-80 auch den
Nummernblock auf der Atari- Tastatur.  Dies zur Unterscheidung. 

          a)  st80.o                 297410 Bytes
          b)  st80.o                 304246 Bytes

                Besonderheiten des DR-C Compilers

Das DR-C Entwicklungssystem hat einige Eigenheiten, die man bei der
Erstellung von C-Programmen beruecksichtigen muss:

 a) Der Linker unterscheidet Symbole nur in den ersten 7 Buchstaben.
    Daher wird die Datei 'shorten.h' benoetigt, damit man mittels Define
    eindeutig definierte Funktionsnamen hat.

 b) Allen globalen Symbolen (als z.B. Funktionen) wird ein '_' im Symbol-
    name vorangestellt. So wuerde aus der gobalen Funktion 'vier()' eine
    Funktion '_vier()' werden!!!
    
 Nach diesen beiden Punkten halten wir mal kurz inne und schauen uns den
Turbo-C Compiler an.  Der Linker unterscheidet Symbole in den ersten 32
Buchstaben.  Ausserdem erzeugt der Compiler KEIN '_' fuer den Linker. 
Zwar hat der Compiler dafuer eine Option, doch man laeuft dabei in einen
Fehler des Turbo-C Linkers, der solche von Turbo-C erzeugten '.o'-Files
nicht mehr lesen kann und weist diese mit 'falschen Format' zurueck. 
Die von dem DR-C System '.o'-Files kann das System dagegen sehr wohl
lesen und verarbeiten.  Aber man sieht hier einige Probleme:
 
 'st80.ttp' ruft z.B. bei dem Start eine Routine auf, die vom Benutzer
definiert sein muss: 'UPinstall()' hiess diese im DR-System.  Das
abgelegte Symbol fuer diesen Aufruf lautet '_UPinsta'.  In Turbo-C muss
man diese Routine dann auch so kryptisch benennen, damit der Linker
diesen Aufruf aufloesen kann (erfolgt uebrigens in 'shorten.h')!!

 c) Die Uebergabe der Parameter erfolgt immer ueber den Programmstack. 
    Turbo-C uebergibt viel lieber in Datenregistern, aber auch dieses
    Verhalten kann man TC abgewoehnen mittels Deklaration der Funktion
    mit dem Schluesselwort 'cdecl'.
    
 d) Jede Primitive muss mit dem Befehl 'UPbegin(...)' beginnen. Man er-
    kennt schnell in 'userprim.h', dass es sich dabei um ein C-Makro 
    handelt, dass das setjmp/longjmp-Konstrukt benutzt. Bedeutung dieser
    Einrichtung kann man in jedem guten C-Buch nachlesen. Bei einem setjmp
    speichert der Compiler die notwendigen internen Daten ab, die man bei 
    einem longjmp benoetigt. Alles etwas vereinfacht...
    Mit 'UPbegin' haben wir also ein setjmp programmiert, aber wo sind die
    dazugehoerigen longjmp-Aufrufe?? Genau, in 'st80.o', dem Teil, welches 
    vom DR-C Compiler ausgespuckt wurde. Unser setjmp stammt aber aus meiner
    neugeschriebenen Primitive aus dem Turbo-C Compiler. Wenn man nun hofft, 
    dass die abgespeicherten internen Daten identisch ist.....falsch!
    
    Turbo-C reizt die internen Register des 68000er so aus, dass er wesent-
    lich mehr abspeichern muss! Boese Inkompatibilitaeten sind hier vorher-
    sehbar!!!!
    
 e) Man beachte die Uebergabe von Float-Zahlen an eine Funktion! Erfolgt
    eine Konvertierung nach double, oder werden lediglich 32-Bit 
    uebergeben?
    
Wie ich diese Probleme nacheinander loesen, werde ich im folgenden etwas
genauer beschreiben. 

Beim Start der ST-80 Maschine wird eine Routine gestartet, die die
Anmeldung der Primitives uebernimmt.  Sie lautet 'UPinstall' und MUSS
vom Benutzer geschreiben werden in der folgenden Art:

#include <math.h>
#include <setjmp.h>
#include <string.h>

#include "userprim.h"

/* 
  Diese Routine berechnet einen Cosinus
*/
upHandle cdecl cosinus( receiver)
 long receiver;
{  	
}

char * cdecl UPinstall()
{	

 /*
   Besondere Funktion, die weitere Initialisierungen vornimmt. Nicht
   notwendig bei anderen Systemen..	 
 */
 install();
	
 /*
   Anmeldung der Funktion 'cosinus', unter der Nummer -1 und ohne einen
   weiteren Paramter! Man beachte die long-Parameter!
 */
 UPaddPrimitive( -1L, cosinus, 0L);

 /*
   Rueckgabe als Zeichen der erfolgreichen Anmeldung
 */
 return( "Eigene trig. Funktionen installiert");
}

Wurden die Routinen erfolgreich angemeldet, dann bekommt man zur
Belohnung noch die Zeichenkette "Eigene trig.  Funktionen installiert"
auf dem Bildschirm ausgegeben und man weiss, dass man es mit einer
besonderen ST-80 Maschine zu tun hat.  Unter ST80V2.3 kann man mit zu
255 verschiedene Primitives (-1 ...  -255) durch den Benutzer
definieren.  Unter ST80V2.5 koennen dies erheblich mehr sein. 

Wenn man eine neue ST80-Maschine zusammenlinkt, dann benoetigt man NICHT
den Startup-Code des TC-Compilers.  In 'st80.o' steckt schon einer drin,
den man auch benutzen muss und der auch funktioniert.  Eventuelle
Initialisierungen muessen halt in einer eigenen Routine gemacht werden. 
Ich habe dies in die Funktion 'install()' gepackt, die aus einem
Assemblerfile besteht. 

Wie ich oben bereits angedeutet habe, laufen wir sicherlich in ein
setjmp/ longjmp Problem.  Man loest dies dadurch, dass man die
entsprechende Routine '_longjmp' in 'st80.o' mittels Programm abaendert!
Wir haben es also mit selbstmodifizierten Code zu tun, aber anders
loesen wir das Problem nicht.  Das Symbol '_longjmp' ist netterweise
noch in 'st80.o' vorhanden, so dass wir im Assemblercode dies sehr gut
aendern koennen.  Durch Zeigervergleiche koennte man aber auch dies in
den Griff bekommen. 

longjmp und setjmp bekommen unter anderem einen Adresszeiger auf eine
Struktur 'jmp_buf' als Parameter mit, wo man die internen Daten
abspeichern muss.  Die Struktur 'jmp_buf', die unter DR-C 9-Long-Werte
Platz gibt, ist unter TC 16 Longwerte gross. 

In 'st80.o' ist aber dieser Adressparameter hard kodiert und man kann
ihn nicht aendern, da er nahezu in allen 30-40 Routinen des C-Interfaces
vorkommt.  Nun gut, dann schreibe man halt ein Interface (durch unseren
modifizierten Code erhaelt die urspruengliche longjmp-Routine nur noch
einen Sprung auf dieses Interface), dass den Parameter wegwirft und
unseren eigenen Speicherzeiger immer laedt und dann weiter in die
longjmp-Routine des TC verzweigt..  Dieses Vorgehen findet man unter
'newlongjmp' im Assemblerfile.  Diese Loesung haette natuerlich nicht
funktioniert, falls ein einziger setjmp-Aufruf noch in st80.o irgendwo
auftaucht, doch dies ist gluecklicherweise nicht der Fall. 

Ich will noch von einem kleinen Problem reden: in MEINER groesseren
'st80.o' Maschine waren zwei Routinen falsch zusammengelinkt.  Beide
Routinen sorgten fuer den Zugriff auf ST-80-Float-Objekte:
'UPSTtoCfloat()' und 'UPCtoSTfloat()' waren fehlerhaft.  Mit einem
Disassembler betrachtet, konnte man Line-F Code sehen und ich wollte G. 
Heeg schon paar Zentimeter hoeher haengen, doch als ich meine kleinere
'st80.o' Version zu Rate zog, konnte ich erkennen, dass hier fehlerhaft
gelinkt worden ist! Es ist nicht einfach ein Byte umgekippt, nein
richtig fehlerhaft gelinkt! So wird man in der Assemblerroutine auch
Code finden, die diese Stellen reparieren! Sonst lasse man den
entsprechenden Block halt weg - dieser Code ist SEHR von der jeweiligen
'st80.o' Datei abhaengig!!!!!!!!

Kommen wir also zum Assemblercode: 'setjmp.s'


*      	ST80V2.3-Anpassung an Turbo-C V2.0
*>>>>>> Export references <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

        .EXPORT  install, errno, newsetjmp, newlongjmp

*>>>>>> Import references <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

        .IMPORT  setjmp, longjmp,  _setjmp, _longjmp
        .IMPORT	 _UP58TCp, _UP58pri, _UP29cop, _UP56nil
        .IMPORT	 _fpuinit
        .IMPORT  _UP03Cto, _UP07STt
*>>>>>> Data structures <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

        .DATA
errno:
        .DC.W   0
field:
        .dc.l   0
counters:
        .dc.l   0
counterl:
        .dc.l   0
*>>>>>>> Code segment <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

;       Im folgenden sollen die setjmp und longjmp-Routinen des Turbo-C+
;       abgefangen und auf kompat. DR-Research-Routinen umgeleitet werden:

	.CODE

;       Zuerst die Codesequenz durch Assembler/Linker erstellen lassen,
;       die spaeter an die Programmstellen kopiert werden

setcode:
        JMP  newsetjmp
longcode:
        JMP  newlongjmp

;       Fangen wir mit der setjmp()-Routine an. Im Gegensatz zu dem 
;       DR-C muessen wir hier weitere zusaetzliche Register retten.
;       Sonst sind diese Routinen sehr aehnlich

;       Zwar ist diese Routine implementiert, doch scheint sie nirgends
;       durch das vorhandene DR-C Interface benutzt zu werden. Also
;       auf Absturz programmieren.
newsetjmp:
        JMP  $0
				
;       ganz sicher wird aber diese Routine benutzt, sie ist aehnlich der
;       DR-C Routine aufgebaut
newlongjmp:
        lea.l   _UP58TCp,a0         ; holt NEUE jmp_buf Adresse
        JMP     longjmp	            ; longjmp von TC

install:
        movem.l	d0-d7/a0-a6,-(a7)				
        JSR     _fpuinit            ; fpu initialisieren

        lea.l	_UP56nil-$1A, A1    ; _UP03CtoSTfloat patchen
        lea.l	tab0301, A0
        move.w	(a0)+,(a1)+				
        move.w	(a0)+,(a1)+				
        move.w	(a0)+,(a1)+				
        move.w	(a0)+,(a1)+				
        move.w	(a0)+,(a1)+				
        move.w	(a0)+,(a1)+				
        move.w	(a0)+,(a1)+				
        move.w	(a0)+,(a1)+				
        move.w	(a0)+,(a1)+				
        move.w	(a0)+,(a1)+				
        lea.l	_UP29cop-$20, A1    ; _UP07STtoCfloat patchen
	lea.l	tab0701, A0
	move.w	(a0)+,(a1)+				
	move.w	(a0)+,(a1)+				
	move.w	(a0)+,(a1)+				
	move.w	(a0)+,(a1)+				
	lea.l	_UP29cop-$0a, A1    ; _UP07STtoCfloat patchen
	lea.l	tab0702, A0
	move.w	(a0)+,(a1)+				
	move.w	(a0)+,(a1)+				
	move.w	(a0)+,(a1)+				
	move.w	(a0)+,(a1)+				

	lea.l	setcode, A0         ; _setjmp von DRI umlenken
	lea.l	_setjmp, A1
	move.w	(a0)+,(a1)+				
	move.l	(a0)+,(a1)+
	lea.l	longcode, A0        ; _longjmp von DRI umlenken
	lea.l	_longjmp, A1
	move.w	(a0)+,(a1)+
	move.l	(a0)+,(a1)+
	movem.l	(a7)+,d0-d7/a0-a6
	RTS
		
	.data
tab0301:	
        dc.w	$2d40               ; Patchdaten fuer einige Routine
	dc.w	$ffe2
	dc.w	$206e
        dc.w	$ffe2
	dc.w	$20ae
	dc.w	$0008
	dc.w	$202e
	dc.w	$ffde
	dc.w	$4e5e
	dc.w	$4e75
tab0701:	
        dc.w	$203c
	dc.w	$8000
	dc.w	$00c1
	dc.w	$6012
tab0702:
	dc.w	$2040
	dc.w	$2010
	dc.w	$4e5e
	dc.w	$4e75
******* Module end *****************************************************

Kommen wir zum letzten Problem: die Uebergabe der Float-Zahlen.  Wie die
dadurch zu resultierende Handhabung aussehen muss, zeige ich also in der
vollen cosinus()-Funktion:

/* 
        Diese Routine berechnet einen Cosinus
*/
upHandle cdecl cosinus( receiver)
  long receiver;
{  float  wert1;
   long	  dummy;
	
    UPbegin( receiver, 0L );

    /*
      Ermittelt den IEEE-Wert und gibt ihn korrekt in D0 zurueck und legt
      ihn auch ordentlich in dummy ab.
    */
    dummy = UPSTtoCfloat( receiver);
	
    /*
      Kopieren des Speicherplatzes von dummy nacheiner als Float-Var.
      deklarierten Variable. Mit dieser rechne man weiter!!!
    */
    memcpy( &wert1, &dummy, 4);
	
    wert1 = cos( wert1);
	
    /*
      Falls man zum Ende kommt: Kopieren wieder nach dummy und diese
      Variable als Parameter uebergeben.
    */
    memcpy( &dummy, &wert1, 4);
    UPreturnHandle( UPCtoSTfloat( dummy));
}

( aufgerufen wird dies dann mittels '<primitive: -2>' als erste Code-
  sequenz in einer Methode 'cos' in der Klasse 'Float')

Mit DR-C koennte man dies uebrigens nicht machen, da DR-C keine interne
IEEE Darstellung benutzt! TC dagegen speichert die Variablen in IEEE ab. 
Warum muss man aber den Float Wert aus einer Integervariablen in eine
Floatvariable kopieren?

 Schauen wir uns mal den Prototype von UPSTtoCfloat() an:
 
 a) float cdecl UPSTtoCfloat( upHandle)
 
Das waere toll, funktioniert aber aus mehreren Punkten nicht mit TC. 
Zum einen glaubt TC trotz float-Deklaration an eine double-Variable (in
Wirklichkeit sogar extended...) als Rueckgabewert und fuegt noch im
Compilercode eine Konvertierung extended -> single ein! Leider nicht
brauchbar, da die DR-C Routine eine richtige Float-Zahl in D0
zurueckgibt.  Uebrigens stimmt auch nicht die Parameteruebergabe fuer
die entsprechende DR-C Routine, denn die DR-C Routine erwartet das
Ergebnis direkt auf dem Stack, doch TC packt da noch einen Zeiger auf
einen Speicherplatz zwischen, der halt die extended-Darstellung dieser
Floatzahl erlaubt!
 
 b) float UPSTtoCfloat( upHandle)
 
Aha, TC uebergibt jetzt die Floatzahl in D0, nicht mehr ueber den Stack,
doch ist da immer noch diese dumme Anpassungscode von extended ->
single. 
 
 c) long cdecl UPSTtoCfloat( upHandle)
 
Da haben wir es, jetzt klappt dies ohne Probleme! Uebgergabe ueber den
Stack und Rueckgabe in D0.  Allerdings ist jetzt die Funktion als long
deklariert, was also einen ordnungsgemaesen Umgang mit einem
ANSI-Compiler nicht moeglich macht.  Es sollte klar sein, dass die
folgende Zeile das Problem NICHT loest, da hier im erzeugten
Compilercode noch eine Umwandlung long -> float eingebaut wird. Das ist
gaaaanz schlecht. 
 
  	           wert1 = (float) UPSTtoCfloat( arg);
  	           
Damit sollte auch dieser komplizierte Umgang mit Flieskommazahlen
klar sein.

Uebrigens gibt es aequivalentes Problem mit der Uebergabe von Bytes und
Zeichen! Man beachte bei allen Parameteruebergaben: der Parameter muss
32 Bit lang sein, egal ob ein Character nur 8 Bit braucht. DR legt immer
32 Bit auf den Stackab, TC optimiert dagegen und fuegt lediglich ein
'move.b' (move Byte) in den Code ein. 'move.l' waere besser gewesen.

                      Was bringt das alles?

Tja, diese Frage ist berechtigt.  Man sollte sich nicht zuviel erwarten. 
Alleine durch die Ersetzung einiger trig.  Primitives bekommt man noch
kein Fortran.  ST-80 ist halt nicht fuer grosse numerische Loesungen
gedacht.  Der Ersatz der trig.  Funktionen bringen in diesem Teil eine
Verdreifachung der Arbeitsgeschwindigkeit, aber in welchen
ST80-Programmen tauchen diese Teile schon auf?

Als Abschreckung sei gleich gesagt, dass es sich nicht lohnt, die Grund-
rechenarten durch die des TC zu ersetzen.  Durch die Konvertierung in
den extended-Type bringt es trotz meiner eingebauten FPU nichts!!!!! Der
Overhead ist einfach zu gross!

So hab ich zum Beispiel probiert, die Summe eines Array von Integer in
einer Primitiven und ueber ST80 zu berechnen.  Ergebnis war, dass der
reine ST80-Code doppelt so schnell war!

                  Weiterer Umgang mit 'Primitives'
                  
Noch ein paar erklaerende Worte ueber den Umgang mit dem C-Interface im
Bezug auf komplexere Objekte:

                        *** Arrayobjekte ***
 
Man betrachte das Objekt: (Array new: 100 withAll: 12) und schreibe in
die Klasse Array eine Methode 'test', die in etwa so aussieht:

test

 <primitive: #>
 
mit # als die gewuenschte negative Nummer, die man in UPinstall() fuer
diese Primitive ausgesucht hat. 

Um jetzt die Summe der einzelnen Zahlen zu berechnen, sei der folgende
C-Code angegeben.  Man beachte hier die Benutzung der beiden notwendigen
Funktionen 'UPindexVarSize()' und 'UPbasicAt()':
  
upHandle cdecl calcSumme( receiver)
  long receiver;
{ long	max, index, summe;

   UPbegin( receiver, 0L);

   summe = 0L;

   /*
     Falls das Element aus einem Array besteht ermittelt man erst
     einmal die Laenge des Arrays
   */
   max = UPindexVarSize( receiver);

   /*
     In einer normalen Schleife (angefangen bei 1 !) wird auf jedes
     Arrayelement zugegriffen...
   */
   for (index = 1; index <= max; index++)	
     summe += UPSTtoCint( UPbasicAt( receiver, index));

   UPreturnHandle( UPCtoSTint( summe));	
}

      *** Zugriff auf Instanzvariablen 'instance variables' ***

Jedes Objekt kann lokale Variable haben, die sogenannten 'instance
variables'.  Manchmal ist der Zugriff auch auf diese Variablen
wuenschenswert. 

Schauen wir uns mal die Definition einer Klasse an, die pro Objekt zwei
lokale Float-Zahlen hat: die moegliche Definition der Klasse der
komplexen Zahlen:

Float subclass: #complex
instanceVariableNames: 'real imag'	
classVariableNames: ''	
poolDictionaries: ''	
category: 'Numbers'!

Waehrend man im obigen C-Beispiel, den 'Wert' eines Objektes aus der
Variable 'receiver' entnehmen konnte (wie dies bei unary messages
oftmals der Fall ist..), kann man hier nur sinnvoll auf dem Objekt
arbeiten, wenn man auch auf die Instanzvariablen zugreifen kann. 

Wie man dies machen koennte, sei kurz in der folgenden C-Routine
aufgezeigt (man bedenke die Notwendigkeit des memcpy() aus dem bereits
oben erwaehnte Problem mit Float-Zahlen..):

upHandle cdecl complexDemo( receiver)
  long receiver;
{ long	anzahlInstanzVariables, dummy;
  float   real, imag;
	
  /*
    Holt die Anzahl der Instanzvariablen des Objektes. Benoetigen
    wir in diesem Beispiel zwar nicht, aber was solls..... 
  */	
  anzahlInstanzVariables = UPinstVarSize( receiver);

  /*
    Zugriff auf die erste Instanzvar.: real
  */
  dummy = UPinstVarAt( receiver, 1L);
  memcpy( &real, &dummy, 4);

  /*
    Zugriff auf die erste Instanzvar.: imag
  */
  dummy = UPinstVarAt( receiver, 2L);
  memcpy( &imag, &dummy, 4);

  /*
    Nun rechnen wir mal ein wenig rum....
  */
  [....]
    
  /*
    Und packen die veraenderten Werte wieder in die Instanzvar.
    zurueck:
  */
  memcpy( &dummy, &real, 4);
  UP36instVarAtPut(receiver, 1L, UPCtoSTfloat( dummy));
  memcpy( &dummy, &imag, 4);
  UP36instVarAtPut(receiver, 2L, UPCtoSTfloat( dummy));
        
  /*
    Und geben das veraenderte Objekt wieder zurueck! Die ST80
    Routine verhaelt sich also wie '^self'
  */
  UPreturnHandle( receiver);	
}

Die vom C-Interface vorgegebenen Routinen sind sehr sicher geschrieben. 
Sie fuehren z.B.  eigene Tests durch, ob die Zahl auch dem gewuenschten
Typ entspricht (z.B.  UPCtoSTfloat() ).  Man kann hier also auf eigene
Tests weitesgehend verzichten! Bei Fehlerfall wird die eigene Primitive
beendet und eine Fehlercode von -1 bis -13 zurueckgegeben.  Naehere Er-
laeuterungen findet man dazu in den 'userprim.h'. 


             *** Events von C nach Smalltalk-80 schicken... ***
   
Dann gibt es da noch eine Routine, die 'Eventcodes' nach ST80 senden:

                    UPpostEvent( code)
					 
Benutzerevents (Long-Werte!) liegen zwischen 0x0000 und 0x0fff.  Daraus
wird in ST80 noch eine 0x8000 draufaddiert, sodass man also zwischen
0x8000 und 0x8fff zu liegen kommt. 

Wenn man also diesen Event aus der Primitive abschickt, dann sollte es
doch bitte moeglich sein, in ST80 diesen Event abzuarbeiten!? Ist es
auch und zwar in (ST-80 V2.3) der

 Klasse:    'InputState', 
 Protokoll: 'userprimitive'
 Methode:   'userEvent:'
 
Diese Methode macht (in der Voreinstellung) nichts.  Die Methode hat
also einen leeren Coderumpf in ST80.  Hier muss man seinen eigenen ST80-
Code einbinden und neu uebersetzen lassen. 

Diese Methode wird uebrigens durch ST80 geforkt, sie wird also parallel
im Hintergrund abgearbeitet.  So kann man unabhaengig vom Hauptprogramm
Events abarbeiten, ohne Pollen zu muessen.  Sehr einfach kann man diese
durch eine einfache Routine demonstrieren: bei jedem Event wird
ein String in das Transcript-Fenster geschrieben:

ST-Routine:

 userEvent: anEvent
 
 "Einfache Abarbeitung eines UserEvents"
 
 Transcript show: 'Dies ist ein UserEvent' ; cr

C-Routine:
 
upHandle cdecl doEvent( receiver)
  long receiver;
{
  UPbegin( receiver, 0L);
  UPpostEvent( 0x8200L);
  UPreturnHandle( receiver);	
}

Hier werden Anwendung deutlich, die meiner Meinung nach sehr elegant
sind.  Nehmen wir mal an, dass wir eine C-Routine schreiben wollen, die
einen digitalen Eingabeport periodisch ueberwachen soll. Bei Aenderungen
dieses Eingabeports soll ein Signal an das ST80 gesandt werden, welches
entsprechend auf diese Zustandsaenderung reagiert. Man koennte also eine
eine Art Visualisierung eines Real-Prozesses damit aufbauen.

Wir binden dazu die Routine so in das Betriebssystem ein, dass es durch
das Betriebssystem regelmaessig aufgerufen wird.  Dazu wird man sich
eines Timers bedienen, welcher in den meisten kleineren Rechnern immer
zur Verfuegung steht. 

Unter dem Atari koennte man dazu die Vertical-Blank Routine aufrufen,
die zwischen 50 und 72 mal in der Sekunde aufgerufen wird (je nach
angeschl.  Monitor). 

Beispielcode in C:

#include <math.h>
#include <setjmp.h>
#include <stdio.h>
#include <tos.h>

#include "userprim.h"

#define TRUE 1
#define FALSE 0

extern intRoutine();

int     zaehler;
long    eventNummer;

char * cdecl UPinstall()
{  int  nvbls;
   long *_vblqueue;
   long Stack;

   int index = 0;
   int	abbruch = FALSE;
   int installiert = FALSE;
	
   /*
     TC spezifischer Code 
   */
   install();
	
   /*
     InterruptRoutine einbinden in die Vert.Blank Schleife. Gilt
     fuer den Atari-ST.....
   */
   Stack = Super( 0L);

    nvbls = *(int *)0x454;
    _vblqueue = *(long *)0x456;

    while( ! abbruch) {
      index += 1;
      if( *_vblqueue == 0L && index <= 8) {
        *_vblqueue = (long) intRoutine;
        abbruch = TRUE;
        installiert = TRUE;
      }
      else {
        _vblqueue++;
        if (index > 8) {
          abbruch = TRUE;
        }
      }
    };
    Super( (void *)Stack);
	
    if (installiert)
      puts("Interruptroutine IST installiert");
    else
      puts("Interruptroutine NICHT installiert");
    /*
      Ende des systemabhaengigen Codes...
    */

    /* 
      glob. Variabeln vorbesetzen
    */
    zaehler = 0;
    eventNummer = 0L;	

    return("Event-Beispiel eingebunden");
}

und die dazugehoerige Assemblerroutine:

;   Interruptroutine, um etwas mit UPuserEvent zu
;   spielen
;
;   Die folgende Assemblerroutine entspricht dem folgenden
;   C-Code
;   int	 zaehler;
;   long eventNummer;
;
;    void interruptRoutine()
;    {	
;      if (zaehler++ >= 72)	{
;         zaehler = 0;
;         if (eventNummer++ > 0xfffL)
;           eventNummer = 0L;
;         else
;           UPpostEvent( eventNummer);
;      }
;    }			
;
;
    .IMPORT   _UP57pos, zaehler, eventNummer
;
     .EXPORT  intRoutine
;
intRoutine:
    movem.l a0,-(a7)                 ; Register retten
    lea.l   zaehler, a0              ; holt Adr. von zaehler
    addi.w  #$1,(a0)                 ; zaehler += 1
    cmpi.w  #72,(a0)                 ; < 72
    blt     back                     ; ja -> return
    clr.w   (a0)                     ; zaehler = 0
    lea.l   eventNummer,a0           ; holt Adr. von eventNummer
    addi.l  #$1,(a0)                 ; eventNummer += 1L
    cmpi.l  #$fff,(a0)               ; <= $ffff
    ble     postEvent                ; ja -> Event an ST80 senden
    clr.l   (a0)                     ; eventNummer = 0L
    bra     back                     ; zurueck
postEvent:
    move.l  (a0), -(a7)              ; Parameter auf Stack 
    jsr	    _UP57pos                 ; Event an ST80 senden
    addq.l  #4,a7                    ; Stack korrigieren
back:                                ; Routine beenden
    movem.l (a7)+,a0                 ; Register restaurieren
    rts		

 
Nun muesste fast jede Sekunde ein Event an das ST80 System geschickt
werden und die 'userEvent:'-Methode abgearbeitet werden.  Nun ja, man
kann schon merken, wie das ST-80 System runtergezogen wird..... 

             *** Debuggen der Primitives ***

 Leider nicht so leicht, wie man es sich wuenschen moechte. ST80 
installiert sich in fast alle Ausnahmevektoren eines 68000er System
rein und daher funktionieren etliche Debugger nicht. So z.B. der 
Source-Level-Debugger des TC-Paketes. Pech....

  ===================================================================

Zum System gehoeren noch an TC angepasste 'userprim.h' und 'shorten.h'. 
Ich schicke die entsprechenden Files gerne den Interessenten zu. 
Momentan habe ich 'userprim.h' noch nicht vollstaendig angepasst. 

 Marten

--
Marten Feldtmann, Kieler Str. 29, W-2300 Kiel 14, Germany, Tel.: +49 431 731916
             DNET/EUNET/USENET/SUBNET: marten@tpki.toppoint.de
         Please keep your replies short - I have to pay for them
Re: TC 2.0, ST-80V2.3 und Primitives
#454
Author: Andreas Toenne
Date: Fri, 22 Feb 1991 10:13
22 lines
1962 bytes
>  ST-80 wurde auf dem ST mit dem voellig veralterten C-Compiler von
>  Digital Research implementiert.  So muss man also Spezialitaeten dieses
>  Compilers bei der Anpassung eines anderen Compilers beruecksichtigen. 

Das ist nicht richtig. Das ST80 wurde cross-compiliert von einer PCS Cadmus
Maschine. Es wurde dann lediglich das DRI Objektformat verwendet, da es vollstaendig
dokumentiert war. Bedenke bitte, dass diese Portierung vor ueber 4 Jahren (damals
war der Atari eine Top-Aktuelle Maschine) stattfand. Eine Portierung von R4
auf den ST/TT ist zur Zeit in der Vorbereitung.

Ich persoenlich habe stets den Mark Williams Compiler verwendent und bin gut
damit gefahren. Man musste dann lediglich die Includefiles aenderen, da die
Binaries nur 8 Zeichen Symbole enthielten, die Includes aber mehr.


Im uebrigen finde ich deinen Artikel sehr gut und sicherlich fuer viele Deutsche
informativ (wieviele setzen hier ST80 auf dem Atari  ein ? na ?). Ich habe zur
Zeit in viel lustigeres Problem. Wie bindet man an R4 auf Musdos unter Windows 3.0
Interrupthandler an ? (Grusel) 

	Andreas Toenne
	atoenne@unido.uucp
Re: TC 2.0, ST-80V2.3 und Primitives
#456
Author: Marten Feldtmann
Date: Sat, 23 Feb 1991 08:00
11 lines
1173 bytes
>informativ (wieviele setzen hier ST80 auf dem Atari  ein ? na ?). Ich habe zur

 Nach meiner Seriennummer muessten es wenigstens 400 sein, oder fing die
Zaehlerei aus Marketinggruenden erst ab 400 an? :-)))))

 Marten

--
Marten Feldtmann, Kieler Str. 29, W-2300 Kiel 14, Germany, Tel.: +49 431 731916
             DNET/EUNET/USENET/SUBNET: marten@tpki.toppoint.de
         Please keep your replies short - I have to pay for them
Re: TC 2.0, ST-80V2.3 und Primitives
#457
Author: Dirk Rode
Date: Mon, 25 Feb 1991 19:34
18 lines
1615 bytes
marten@feki.toppoint.de (Marten Feldtmann) writes:

>>informativ (wieviele setzen hier ST80 auf dem Atari  ein ? na ?). Ich habe zur

> Nach meiner Seriennummer muessten es wenigstens 400 sein, oder fing die
>Zaehlerei aus Marketinggruenden erst ab 400 an? :-)))))

Meine Seriennummer ist 264 (wenn ich mich nicht irre) meine Registriernummer
ist 8906-300.

mfG Diro
-- 
*  Dirk Rode              * dirk.rode@arbi.informatik.uni-oldenburg.de *
*  Zwischenahner Str. 64  * Bitnet:   077481@Doluni1.Bitnet            *
*  2910 Howiek            * Privat: diro@edison.phone.north.de         *
*  Arbeitsgruppe Rechnerbetrieb Informatik der                         *
*  Universitaet Oldenburg (FB 10 - ARBI)  * Nickname: Waldi            *
*  Telefon: +49 441 798 2198                                           *
Re: TC 2.0, ST-80V2.3 und Primitives
#462
Author: Andreas Toenne
Date: Fri, 01 Mar 1991 10:24
18 lines
1304 bytes
In article <4869@uniol.UUCP>, Dirk.Rode@arbi.informatik.uni-oldenburg.de (Dirk Rode) writes:
|> marten@feki.toppoint.de (Marten Feldtmann) writes:
|> 
|> >>informativ (wieviele setzen hier ST80 auf dem Atari  ein ? na ?). Ich habe zur
|> 
|> > Nach meiner Seriennummer muessten es wenigstens 400 sein, oder fing die
|> >Zaehlerei aus Marketinggruenden erst ab 400 an? :-)))))

Nicht alle wurden verkauft.

|> 
|> Meine Seriennummer ist 264 (wenn ich mich nicht irre) meine Registriernummer
|> ist 8906-300.

Ich schaetze ich habe die kleinste Nummer: 8711-05

	Andreas Toenne
	atoenne@Unido.uucp
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads