home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.wwiv.com
/
ftp.wwiv.com.zip
/
ftp.wwiv.com
/
pub
/
BBS
/
515DISK2.ZIP
/
DOC515.ZIP
/
WA8V21B.DOC
< prev
next >
Wrap
Text File
|
1988-01-20
|
21KB
|
494 lines
"NORD><LINK schlaegt wieder zu"
Betrifft: Neue "alte" Hostmode-Firmware fuer den TNC2.
Nachdem die Weihnachtsferien und der Jahreswechsel fuer ein ergiebiges Stueck
Arbeit auf dem Gebiet der PR-Software genutzt wurden, sind erste Ergebnisse
dabei herausgekommen, die vielleicht auch fuer andere interessant sind :
Die WA8DED-Firmware 2.1 fuer den TNC2 ist (genau wie NET/ROM) bis auf wenige
zeitkritische Routinen komplett in C geschrieben. Der komplette Source
einschliesslich der wenigen Assemblerteile konnte nun vollstaendig
rekonstruiert u n d auch komplett verifiziert werden (Uebereinstimmung bis
auf das letzte Bit nach Neuuebersetzen/Linken).
Beim Durchgucken des Sources konnten durch einfache Aenderungen einige Fehler
beseitigt und die Antwortgeschwindigkeit im Hostmode sowie die allgemeine
interne Verarbeitungsgeschwindigkeit drastisch erhoeht werden. Da allgemeines
Interesse an der genaueren Erklaerung der Aenderungen besteht, werde ich diese
spaeter ausfuehrlich in weiteren Files erlaeutern.
Im einzelnen wurde folgendes geaendert :
1. Die Firmware wurde wie die Originalversion mit einem Optimizer bearbeitet,
im Gegensatz zum Original aber wurden nur auch die Geschwindigkeit
erhoehende Optimierungen ausgefuehrt, spezielle die Geschwindigkeit
stark herabsetzende Laengenoptimierungen wurden weggelassen (die Firmware
passt trotzdem noch komplett in 32kB - im Gegensatz zu NET/ROM, wo man
die speziellen Laengenoptimierungen unbedingt machen muss, um es in 32kB
unterzubringen). Durch das Weglassen der stark verlangsamenden
Optimierungen konnte die interne Verarbeitungsgeschwindigkeit um einiges
erhoeht werden.
2. Nach einer Aussendung wird das Abschalten der PTT um "ein Byte" verzoegert
(bei 1200 Baud sind das 6,7ms). Dies loest das Problem der zu fruehen
PTT-Abschaltung, die sich bei einigen Konfigurationen bemerkbar gemacht
hat (Abschaltzeit kann ggf. auch noch weiter verlaengert werden).
3. Das L-Kommando im Hostmode gibt auch beim Kaltstart mit nicht
initialisiertem RAM keine "Hausnummern" beim ersten Ansprechen mehr aus.
4. Ein Fehler im Hostmode-Kommando G wurde beseitigt. In der Originalversion
wurde nach Ausgabe eines Kopfes eines Monitor-UI/I-Paketes grundsaetzlich
der Rumpf (die Daten) beim naechsten G/G0/G1 fuer Kanal 0 ausgegeben,
obwohl laut Beschreibung bei G1 nur Statusmeldungen kommen duerfen.
Dies fuehrte bei einigen Programmen fuer den TNC1-Hostmode (z.B. TINA) zu
Schwierigkeiten. Dieser Fehler ist nun beseitigt, das G-Kommando im
Hostmode ist nun vollstaendig TNC1-kompatibel.
5. Mit "ESC @K" versetzt man den TNC in den KISS-Mode. KISS wurde aus dem
bekannten TCP/IP-Paket uebernommen. Die Parameter der Hostmode-Firmware
werden n i c h t veraendert (KISS benutzt eigene andere Parameter und
ueberschreibt nicht die Firmware-Variablen), nach einem Reset sind alle
Parameter unveraendert (KISS wurde in ein linkfaehiges Codesegement
umgeschrieben).
6. Die Auswertung von Hostmodekommandos wurde geringfuegig geaendert,
hierdurch wurde die Antwortgeschwindigkeit im Hostmode drastisch um
ein Mehrfaches erhoeht. Der TNC2 antwortet nun aehnlich schnell wie
der TNC1, lange Datenpakete in den TNC werden nun ohne Verzoegerung
angenommen.
Weiterhin besteht auch die Moeglichkeit, Versionen mit mehr als 4 Kanaelen
zu erstellen fuer spezielle Mailboxen. Durch diese Moeglichkeit ist die
DK0MAV-Mailbox neuerdings in der Lage, fuer Store-and-Forward mit
anderen Mailboxen grundsaetzlich (auch waehrend laufendem S+F-Versuch) nicht
busy zu sein. Dies kann bei schlechten Strecken und Betrieb mit W0RLI/WA7MBL
aehnlichen Mailboxen, die nur stuendlich S+F initiieren koennen und nach einem
busy erst wieder eine Stunde spaeter neu probieren koennen, wichtig sein.
Ich werde die "neue" alte Firmware einigen bekannten OM's zum Testen geben.
Stellen sich keine schwerwiegenden Fehler heraus, wird die Firmware sofort
freigegeben, dann kann sie sich durch Weitergeben von OM zu OM sehr schnell
verteilen. Das geht sicherlich schneller, als wenn sich alle Interessenten bei
mir persoenlich melden. Die aktuelle Version hat die Versionsnummer "2.1b".
73, Michael, DC4OX @ DK0MAV.
... Einsichten und Ansichten 1
Wie versprochen, will ich an dieser Stelle etwas ausfuehrlicher die
Hintergruende einiger Aenderungen an der Hostmode-Software fuer den TNC-2
von WA8DED kundtun. Ich hoffe damit dann auf einmal etliche an mich
herangetragende Fragen erschoepfend zu beantworten.
Zunaechst eine Vorbemerkung. Eigentlich gehoert es sich, dass, wenn man Fehler
an einer Software entdeckt hat oder Verbesserungen vorgenommen hat, diese
zuallererst dem Entwickler dieser Software zukommen laesst, bevor man die
Dinge in aller Oeffentlichkeit "breittritt". Leider hat sich WA8DED jeglicher
Zusammenarbeit verschlossen, die seine exzellenten Programme betreffen.
Wir hatten direkten Kontakt mit WA8DED - auf Sourcecodefehlerangaben fuer
NET/ROM in einem Brief reagierte er aber relativ merkwuerdig. "Die Fehler
seien ja gar keine Fehler", oder "Deutsch koenne er nicht lesen". Dabei haben
wir natuerlich jegliche Briefe in Englisch abgefasst und die beschriebenen
allerdings nicht uebermaessig gravierenden Fehler (teilweise sehr versteckt,
von aussen nahezu nicht feststellbar) wurden alle in s e i n e n neueren
Versionen behoben ...
Soweit dazu. Nun zu den Aenderungen. NET/ROM und auch die TNC-2-Firmware sind
beide fast ausschliesslich in C geschrieben. Uebersetztes C beansprucht mehr
Platz als reiner Assemblercode und so passt das normal uebersetzte NET/ROM
auf gar keinen Fall mehr in ein 32kB-Eprom. Es gibt zu dem benutzten Compiler
einen Assembly-Code-Optimizer, dieser optimiert aber nur JP... in JR...
ueberall wo es moeglich ist. Das bringt so ca. an 1 kB an Codeersparnis
insgesamt, das reicht fuer NET/ROM aber bei weitem noch nicht. Es werden
weitere Optimierungen vorgenommen, unter einigen anderen auch einige, die
die Geschwindigkeit ziemlich herabsetzen. All diese Optimierungen wurden
auch auf die Hostmode-Firmware angewandt - das aber, obwohl die Firmware
auch komplett ohne die verlangsamenden Optimierungen in ein 32k-Eprom gepasst
haette.
Fuer die Insider beschreibe ich diese Optimierungen im folgenden einmal
genauer :
Zunaechst werden 4 der am haeufigsten benutzten Runtime-Modul-Funktionen
durch Z80 RST-Befehle ersetzt. Ein CALL-Befehl braucht 3 Byte,
ein RST nur ein Byte, pro Aufruf einer dieser 4 Routinen werden also 2
Byte gespart. Ein CALL braucht beim Z80 17 Maschinenzyklen, ein RST
braucht 11 Zyklen. Allerdings sitzt an den RST-Vektoren je ein JP an die
jeweilige Routine mit 10 Zyklen. Jeder Aufruf ueber einen RST braucht also
4 Zyklen laenger - das ist im Vergleich zu den 17 des CALL sicherlich
nicht so viel, dass es sehr ins Gewicht fallen wuerde.
Die naechsten Laengenoptimierungen sehen in punkto Geschwindigkeit schon
anders aus. Wenn eine Funktion lokale nicht-static Variable braucht oder
der Funktion Parameter uebergeben werden, so wird im Maschinencode auf
diese Variablen/Parameter indiziert ueber das IX-Register zugegriffen. Der
Compiler ruft am Anfang der Funktion eine Library-Routine auf, die das
IX-Register ("Stackframepointer") entsprechend setzt. Der Wert des
BC-Registers erhaelt denselben Wert. Ein Zugriff auf 16 Bit sieht dann
folgendermassen aus :
Lesen : Schreiben : Loeschen :
LD L,(IX+n) 19 LD (IX+n),L 19 LD (IX+n),0 19
LD H,(IX+n+1) 19 LD (IX+n+1),H 19 LD (IX+n+1),0 19
------------------- ------------------- -------------------
38 38 38
Jeder solcher Zugriff ist 6 Byte lang und braucht 38 Zyklen. WA8DED benutzt
nun eine spezielle Library-Routine, so dass die meisten dieser Zugriffe
durch ein RST (ueber den dann diese Routine aufgerufen wird) mit einem
1-Byte-Parameter ersetzt werden koennen. Die obersten beiden Bit dieses
Bytes geben an, ob es ein Lesen/Schreiben/16-Bit-Loeschen/8-Bit-Loeschen
ist, die restlichen 6 Bit geben den Offset auf IX (bzw. BC) an. Alle Zugriffe
mit einem Offset < 64 (also die meisten) koennen mit diesem RST optimiert
werden, bei jedem Zugriff werden somit 4 Byte gespart. Da Parameter und lokale
Variablen ueberaus haeufig bei nahezu jedem C-Statement auftreten (16 Bit
lang sind alle Pointer, integer, unsigned), ist die erreichte Ersparnis ganz
enorm. Aber wie sieht es mit der Laufzeit aus ?
Dazu einen Codeauszug, wie im einzelnen bei einem solchen RST der Ablauf
ist, im Beispiel das Setzen einer Variable :
RST 38 11
DB n or 01000000b
.
.
RST38: JP LOCAL 10
.
.
LOCAL: EX (SP),HL 19
LD A,(HL) 7
INC HL 6
EX (SP),HL 19
BIT 7,A 8
JR NZ,... 7
BIT 6,A 8
JR NZ,LOC1 12
.
.
LOC1: PUSH DE 11
EX DE,HL 4
AND 00111111b 7
LD L,A 4
LD H,0 7
ADD HL,BC 11
LD (HL),E 7
INC HL 6
LD (HL),D 7
EX DE,HL 4
POP DE 10
RET 10
--------------------------------
195
195 - 38 = 157 Zyklen mehr bei jedem lokalen Zugriff (!)
Bei den anderen Moeglichkeiten dieser Optimierung werden gebraucht :
LD HL,(IX+n) 158
LD (IX+n),0000h 193
LD (IX+n),00h 182
Anders ausgedrueckt : Jeder so laengenoptimierte Zugriff braucht
ca. 5-mal solange wie nicht optimiert.
Das macht dann doch schon einiges aus.
Eine weitere sehr verkuerzende, aber verlangsamende Optimierung betrifft
Zugriffe in Strukturen ueber Pointer. Der benutzte Compiler laed den
Pointer in HL, laed den Offset in die Struktur in DE und addiert DE auf HL :
LD DE,n 10
ADD HL,DE 11
--------------------------------
21
Der Zugriff auf diese Art wird immer bei Offsets > 8 benutzt, wie schon bei
den Zugriffen auf einfache Variable benutzt WA8DED einen RST mit einem Byte
Parameter. Der Parameter gibt den Offset an, bis 255 ist diese Art der
Optimierung also moeglich (da keine so grossen Strukturen im Source benutzt
werden also immer) :
RST 30 11
db n
RST30: JP ADDHL 10
ADDHL: EX (SP),HL 19
LD E,(HL) 7
LD D,0 7
INC HL 6
EX (SP),HL 19
ADD HL,DE 11
RET 10
--------------------------------
100
Hier werden bei jedem solchen Zugriff 2 Byte gespart, aber der optimierte
Zugriff ist wiederum ca. 5-mal langsamer.
Es werden auch noch einige andere Optimierungen gemacht und spezielle Library-
Routinen benutzt, alles in allem erhaelt man eine Codeersparnis von ueber
einem Drittel (!) - das ist dann doch schon eine ganze Menge. Ein NET/ROM
von ca. 31kB waere also ohne Optimierungen ca. 45kB lang (!!!).
Anderseits geht die interne Verarbeitungsgeschwindigkeit merklich herunter
bei den Optimierungen - es ist also wenig verstaendlich, wenn solche
Optimierungen auch dann gemacht werden (Hostmode-Firmware), wenn sie gar nicht
erforderlich sind, weil das Programm auch ohne ins EPROM passt.
Wie die Intentionen der Entwickler auch gewesen sein moegen - ich habe nach
dem Motto "Die guten ins Toepfchen, die schlechten auf den Muell" nur die
Optimierungen ausgefuehrt, die auch gleichzeitig eine Verschnellerung
bedeuten. Somit wurde immer noch genug gespart, dass der KISS-Code des
TCP/IP-Paketes mit in das EPROM passte.
73, Michael, DC4OX @ DK0MAV.
... Einsichten und Ansichten 2
Bisher habe ich nur (nicht) gemachte Laengenoptimierungen besprochen, aber
noch keine von mir vorgenommenen Aenderungen am Source.
Diese will ich jetzt erklaeren :
1. L-Kommando bei nicht initialisiertem RAM im Hostmode
Im Gegensatz zum Terminalmode konnte es im Hostmode passieren, dass
Parameter aus einem Linkblock ausgegeben wurden, die noch nicht
initialisiert waren. Durch Aufruf einer (vorhandenen) weiteren Routine
waehrend der Initialisierung nach Kaltstart/Reset werden jetzt alle
in Frage kommenden Parameter initialisiert.
2. G1 im Hostmode ohne Fehler
Auszug aus dem Original :
<G-Parameter holen> /* G: par = MBALL, G0: MBINFO, G1: MBSTATUS */
if (!actch) /* wenn actch 0, d.h. wenn Monitorkanal, und */
if (mifmbp != NULL) /* wenn noch Frame am Monitorrestausgabezeiger */
{ /* haengt, dann gebe Rest dieses Frames aus */
<gebe Monitorframerumpf aus>
}
else ...
Wie aus dem Source ersichtlich, falls noch ein Monitorframerumpf
(I/UI-Paket) auf Abholung wartet, wird dieser ausgegeben, wenn das naechste
G-Kommando auf Kanal 0 ausgefuehrt wird. Und zwar auch dann, wenn mit einem
G1 ausschliesslich ein Status abgeholt werden soll.
Durch die simple Aenderung
"if (mifmbp != NULL)" -> "if (par != MBSTATUS && mifmbp != NULL)"
wurde diese "Unschoenheit" korrigiert.
3. ESC @K -> KISS
Der aus dem TCP/IP-Paket stammende KISS-Source wurde so umgeschrieben,
dass er uebersetzt zum Firmware-Objekt gelinkt werden konnte :
- cseg-Anpassung
- Interrupt-Vektor im RAM, dadurch frei linkbar
- Freispeicher so, dass keine Firmware-Variablen betroffen werden
Der Aufruf von KISS erfolgt mit "ESC @K", oder auch ohne ESC aus dem
Hostmode heraus. Durch einen Reset (z.B. Power-On) gelangt man wieder
zur Hostmode-Firmware. Hostmode-Firmware-Variable werden durch einen
KISS-Lauf nicht veraendert.
4. PTT-Abfall verzoegert
Die Feststellung, wann ein Paket komplett einschliesslich CRC und Flag
die SIO verlassen hat, geschieht mit einem "Trick". Sowohl Z80 SIO als
auch Z8530SCC haben weder ein Flag noch einen speziellen Interrupt, der
anzeigt, wann ein Frame einschliesslich CRC und einem Flag den Baustein
komplett verlassen hat (Wie ein Entwickler des SCC mal auf Anfrage
mitteilte, wurde das nie implementiert, weil an eine Anwendung bei
Halbduplex-Nicht-Standleitungen niemals jemand gedacht hat ... ).
Wird nun bei PR zu frueh die PTT abgeschaltet, dann kann es je nach
Schnelligkeit der PTT-Umschaltung (oder immer, falls das Modem
mitabgeschaltet wird) dazu kommen, dass das letzte Frame einer Aussendung
nicht korrekt abgeschlossen wird. Fuer die Feststellung des richtigen
Zeitpunktes der Abschaltung (des Zeitpunktes, zu dem Frame + CRC + Flag
den Baustein komplett verlassen haben), gibt es mehrere Moeglichkeiten.
Einmal kann man eine feste "TailTime" verzoegern, nachdem die SIO einem
mitgeteilt hat, dass das Frame zuende ist (mitten im CRC). Diese TailTime
ist aber hochgradig von der Baudrate abhaengig. Das andere Verfahren ist,
dass man einfach ein "Dummyframe" beginnt, und in den jeweiligen TX-
Interrupts einen Zaehler herunterzaehlt, bis man sicher ist, dass das
vorherige (Nutz-)Frame komplett heraus ist. Dann wird die PTT
abgeschaltet und das Dummy-Frame mit Abort beendet. Die letztere Methode
hat auch WA8DED verwendet - aber er zaehlt dem Anschein nach ein Byte
zuwenig herunter, so dass sich daraus ein ziemlich haariger Millisekunden-
Grenzfall ergibt. Ich habe das so geaendert, dass nun ein Byte laenger
gewartet wird - allerdings muessen genaue Tests (die ich hier nicht
durchfuehren kann, meine PTT war auch vorher schon langsam genug) erst
noch zeigen, obs das denn auch wirklich komplett brachte.
5. Drastische Reduzierung der Hostmode-Antwortzeit
Die interessanteste, einfachste Aenderung, mit der groessten Wirkung, am
Schluss ...
Es fiel einem ziemlich negativ auf, dass die Antwortzeit im Hostmode
ziemlich bescheiden war beim TNC2. Genaue Tests brachten sogar hervor,
dass beim Beschicken des TNC mit langen Frames dieser es nicht einmal
zuwege brachte, mehr als drei oder vier hintereinander in eine Aussendung
zu packen. Da dieser Effekt genauso auch im Terminalmode auftrat, dachte
man zunaechst daran, dass der TNC mit seinem lahmen, in einer Hochsprache
programmierten Z80 halt einfach zu langsam ist beim Paketepacken.
Dem ist aber erstaunlicherweise nicht (ganz) so, wie folgende Betrachtungen
zeigen.
Zunaechst, wie sieht das Hauptprogramm aus :
main()
{
<Initialisierungen>
LOOP
{
l2(); /* Level 2 ausfuehren */
l3(); /* Level 3 ausfuehren (Firmware: L3-Frames wegwerfen) */
lx(); /* Level x ausfuehren (Firmware: Userein/ausgaben */
}
}
l2()
{
l2tx(); /* Level 2, Sender */
l2rx(); /* Level 2, Empfaenger */
l2rest(); /* Level 2, Rest (Busy-Condition etc.) */
}
Man sieht, dass von einem Aufruf von lx() bis zum naechsten Aufruf eine
Menge Zeit vergehen kann. Ausserdem ist es nicht erforderlich, dass
irgendwelche Routinen innerhalb einer bestimmten Zeitspanne aufgerufen
werden muessen. Denn einerseits laufen alle HDLC-Ein/Ausgaben
interruptgesteuert unter Zugriff auf voll dynamische Speicherverwaltung
(nicht feste FIFOs) ab, es ist nicht erforderlich dass zu bestimmten Zeiten
irgendwo etwas "abgeholt" wird. Andererseits gibt es auch keine
"Timerketten", die alle soundsoviel Millisekunden geservt werden muessen,
sondern die Timer werden durch Uebergabe der vergangenen "Ticks" berechnet.
Wie laeuft nun der interessierende lx() ab ?
lx()
{
<vergangene Zeit in Ticks nach letzem Aufruf berechnen>
l2timr(ticks); /* Level 2, Timerservice */
<CON- und STA-Led-Service>
if (!ishmod) /* wenn kein Hostmode eingeschaltet ... */
{
<Terminalmode>
}
else /* wenn Hostmode eingeschaltet */
{
<selektiere Monitorframes aus>
if (RS232-FIFO nicht leer)
{
<hole ein Zeichen aus RS232-FIFO>
<verarbeite dieses eine Zeichen>
if (Hostmodekommando abgeschlossen)
{
<Fuehre Hostmodekommando aus>
}
}
} /* Ende else von if (!ishmod) */
} /* Ende lx() */
Bei dieser Uebersicht faellt etwas Entscheidendes sofort ins Auge :
der Level lx() in dieser Form verarbeitet pro Aufruf immer nur
hoechstens ein einziges Zeichen !
Das heisst, wenn ich ich ein 256-Byte Frame im Hostmode in den
TNC schiebe, dass das einzelbyteweise abgearbeitet wird. Also
256 + Hostmodekommandobytes mal in der main-Hauptkommandoschleife
herum und alle einzelnen Routinen abarbeiten, alle Linkblocks und
Timer serven, etc. pp. Und d a s ist nicht notwendig, da, wie
beschrieben, ja ein Herumlaufen in der main-LOOP n i c h t alle
soundoviel Millisekunden erfolgen muss. Ausserdem, nach Beendigung einer
Hostmodekommandoeingabe wird ja ein komplettes Kommando abgearbeitet, was
dann ja auch sehr lange dauern kann, mit Sicherheit aber laenger als das
simple Holen einiger Bytes aus dem RS232-FIFO.
Die ganze Aenderung besteht nun im Ersetzen eines "if" durch "while"
und Einfuegen eines "return" :
while (RS232-FIFO nicht leer)
{
<hole ein Zeichen aus RS232-FIFO>
<verarbeite dieses eine Zeichen>
if (Hostmodekommando abgeschlossen)
{
<Fuehre Hostmodekommando aus>
return;
}
}
Im uebrigen laeuft der Terminalmode genauso ab - auch dort koennte man
eine entsprechende Aenderung anbringen. Ich habe das aber nicht gemacht,
weil der Terminalmode ausschliesslich fuer Eingaben von Hand gedacht ist
und dafuer der Ablauf wie vorhanden voellig ausreichend ist.
Bei der einen oder anderen Aenderung oder Erklaerung kann ich mich natuerlich
auch irren - falls jemand irgendwozu eine andere Meinung hat, dann bitte
sofort melden.
73, Michael, DC4OX @ DK0MAV.
-----------------------------------