Es ist wirklich viele Jahre her, dass ich ernsthaft C programmiert hab. Ich hab mich die letzten 10 Jahre fast nur um das Design von größeren Applikationen gekümmert und den ganzen Optimierungskram vergessen. Ausserdem bin ich eh ein furchtbarer Lehrer, weil ich nix erklären kann.
Beiträge von daybyter im Thema „Real-Zahlen und Funktionen im cc65 nutzen...zur Umsetzung“
-
-
Das sieht nur so verwirrend aus, weil halt die float-Operationen im cc65 fehlen. Deshalb diese fmul, fdiv usw. Methoden. Mit * / usw würde das viel einfacher aussehen...
-
Keine Ahnung...das müsste ja theoretisch der TGI-Treiber machen...was er vermutlich nicht macht...und das wäre dann der Fehler....ich glaube, Du hast das Problem gelöst...Danke!!!
-
Danke für den Tipp mit dem Optimieren! Da wird es schon ein ganzes Stück schneller. Funktionieren tut es dummerweise immer noch nicht. Ich schreib mal paar Testfunktionen...
Ciao und Danke nochmal,
AndreasEdit: das sta (regbank+4),y ist ja anscheinend das Speichern in den Bildschirmspeicher. Was 1 Byte wäre. Wo werden nur die schwarzen Pixel geschrieben? Ich versuch mal direkt in den Speicher zu schreiben, was dann passiert...
Edit2: Interessant...mit folgender Schleife bekomm ich schon Müll in den Bildschirmspeicher geschrieben...das kann doch nur ein Compilterfehler sein, oder?
-
Diese Linienfunktion soll ja quasi den Hintergrund auslöschen, und schreibt daher in der Hintergrund-Farbe. Evtl. fass ich die plot und Line-Routine noch zusammen, damit die Adresse nur einmal berechnet werden muss. Würde nochmal bischen Zeit sparen.
Den Code hab ich mir ja schon angesehen, steig aber noch nicht wirklich durch, weil halt soviele Unterroutinen aufgerufen werden, deren Code ich teilweise auch noch nicht ergoogeln konnte. Und auch ohne register-Schlüsselwort müsste der Code doch korrekt funktionieren, auch wenn er vielleicht langsamer ist?
Muss ich nochmal suchen und evtl. paar Testpogramme schreiben.
Ciao,
Andreas -
Ich bin so langsam am grübeln, ob der cc65 falschen Code erzeugt?
ist mein einziger Schreibbefehl, d.h. es dürften auf keinen Fall neue Bits gesetzt werden. Trotzdem passiert das...?
Das macht der Compiler draus:
Code
Alles anzeigen00019Er 1 ; 00019Er 1 ; *byteAddr &= bitmask; 00019Er 1 ; 00019Er 1 A0 04 ldy #$04 0001A0r 1 20 rr rr jsr ldaxysp 0001A3r 1 20 rr rr jsr pushax 0001A6r 1 A0 00 ldy #$00 0001A8r 1 20 rr rr jsr ldauidx 0001ABr 1 20 rr rr jsr pushax 0001AEr 1 A0 04 ldy #$04 0001B0r 1 A2 00 ldx #$00 0001B2r 1 B1 rr lda (sp),y 0001B4r 1 20 rr rr jsr tosandax 0001B7r 1 A0 00 ldy #$00 0001B9r 1 20 rr rr jsr staspidxWerden hier evtl. 16 bit, statt 8 geschrieben? Sind mir eigentlich nicht danach aus...*grübel*
Ciao,
Andreas -
Hallo!
Danke für Deinen Kommentar. Also es soll ja eine weisse Linie gezeichnet werden. Die Bits sollen also gelöscht werden. Insofern denke ich, dass das & schon richtig ist. Mit der Bitmaske hast Du völlig recht. Ich hab das jetzt mal auf
unsigned char bitmask = ~( (unsigned char)0x80 >> (x & 7));
geändert, so dass alle Bits, bis auf das bei (x mod 8 ) 1 sein sollten. Das muss doch 0 sein, damit es dann bei dem & gelöscht wird. Klappt aber immer noch nicht. Ich glaub, ich hab da auch einen Offset zu der Bildschirmspeicher-Adresse drin. Dann bin ich noch unsicher, ob die Schleife mit dem Mitführen von y so geschickt ist. Schöner wäre es eigentlich, wenn man gleich zu Anfang die letzte zu ändernde Adresse berechnet, und dann in der Schleife nur noch an die Adresse schreibt, sie erhöht und dann vergleicht, ob man schon die Endadresse erreicht hat. Ich bin mir da nur unsicher, weil es ja für einen 8-Bit-Prozessor ist, und der y-Vergleich in einem Byte zu machen ist. Der Adress-Pointer sind ja 2 Byte, die man vergleichen muss, was ja für nen 6510 mehr Aufwand ist.
Ciao,
Andreas -
Hab jetzt mal als Ablenkung von den Floats ne Routine für verkale Linien angefangen. Ist noch ein Bug drin, aber die Idee ist hoffentlich erkennbar. Nach dem Setzen eines Bit soll die nächste, zu setzende, Adresse durch eine einfache Addition gefunden werden. Ich hoffe mal, in dieser Richtung noch ne Ecke Zeit sparen zu können...
Code
Alles anzeigenvoid c64_vertiLine( int x, unsigned char y, unsigned char y2) { unsigned char *byteAddr; unsigned int lineOffset; unsigned char bitmask = (unsigned char)0x7f >> (x & 7); if( y2 < y) { char swap = y; y = y2; y2 = swap; } /* Start of a 1-Byte line is (y / 8) * 320 = (y & ~7) * 40 = (y & ~7) * 8 + (y & ~7) * 32 */ lineOffset = y & 0xf8; lineOffset <<= 3; byteAddr = (unsigned char *)lineOffset; byteAddr += (lineOffset << 2); /* Column is (x / 8) * 8 = x & ~7 */ byteAddr += (x & 0xfff8); /* Byte is y & 7 */ byteAddr += ( y & 7); /* Add the base address of the video memory. */ byteAddr += 0xe000; while( y <= y2) { *byteAddr &= bitmask; byteAddr += (++y & 7) == 0 ? 313 : 1; } }Ciao,
Andreas -
Hallo!
xfMax = fsqr( _fsub( _fmul( yfMin, yfMin), _fmul( yf, yf)));
Ich bin verblüfft...ich hab die Zeile jetzt minimal geändert:
, weil man doch eine Integer will. Also Deinen Ausdruck einfach gerundet, und er liefert immer 0. Läuft Deine Schleife jetzt mit floats?
Ich bin jetzt dran, und versuch mal Teile von SoftFloat ans Laufen zu bekommen. Das sind single precision floats mit 32 bit. Wenn man es hinbekäme, dass man 2 Zahlen in den beiden long Akkus des cc65 addiert, _ohne_ dass man sie jn den Funktionen überschreibt, müsste der Einbau eigentlich machbar sein. Aber da muss ich noch ein bischen probieren.
Ciao,
Andreas -
Ich habe sowas mal für den H8/300 in Assembler implementiert.
Bitte melde dich an, um diesen Link zu sehen.
Hat bei + - * / Faktor 10 gegenüber der C-Lösung (das war der Default in der "libm") gebracht.
sqrt und sin / cos / atan hab ich mir dann auch noch gegeben, da war dann nochmal Faktor 50 drin.
Aber schön ist der Code nicht gerade ...
Und kurz auch nicht, also wahrscheinlich bring das auf dem C64 nix ...Wo würdest Du die grössten Vorteile vom Assembler-Code sehen? Meine erste Idee wäre z.B. das Checken von Flags, wie Übertrag bei Addition etc., was in C schlecht (nicht effizient) zu machen ist. Oder z.B. Rollen durchs Carry-Bit, und dann in Abhängigkeit davon zu springen. Also denke ich schon, dass man am Ende Teile in Assembler brauchen wird. Am sogar dann würde ich es wohl vorziehen, wenn man das in einen Rahmen von C-Code einfügt, der die Algorithmen beschreibt.
Ciao,
Andreas -
was willst du da gross optimieren? eine derartige library müsste in erster linie eins sein: möglichst kurz. schnell wird sie sowieso nie werden. nene. falsche reihenfolge. optimierung kommt grundsätzlich immer ganz zum schluss, alles andere ist sinnfrei.
Ich wollte eigentlich das Zeichenprogrämmchen optimieren, damit man mal sieht, welcher Anteil da nun float ist, und wo z.B. nur gemalt wird. Also z.B. die Linien-Routine beschleunigen etc.
Zitatwenn du etwas über floats lernen willst ist das sicher eine gute idee - wenn dabei aber etwas in der praxis sinnvoll nutzbares rauskommen soll gibt es keine alternative zu den basic roms. und in C will man solche funktionen schon garnicht schreiben, die sind so schon langsam genug

Aber die Basic-Geschichte wird Uz niemals in den cc65 aufnehmen, weil sie auf den anderen Rechnern ja nicht läuft. Und ehrlich gesagt, glaub ich auch nicht, dass Commodore nun sooo einen Wahnsinns-Job bei den Matheroutinen gemacht hat. Da kann man bestimmt noch was optimieren...
Wie stellst Du Dir die weitere Entwicklung vor? Willst Du Uz überzeugen, dass er die Basic-Roms unterstützt?
Ciao,
Andreas -
äh ganz ehrlich? bevor die nicht in einem compiler zur anwendung kommt fasse ich die nicht an =PAch komm...es ist doch nur eine Kleinigkeit!
Die _fcmp Funktion muss benutzbar werden und die Wurzel müsste gehen...dann noch schnell ne Zeitmessung rein, und wir könnten anfangen systematisch zu optimieren. Wir erwähnen das auf der cc65-Mailing-Liste, Uz ist beeindruckt und startet vielleicht den float-Einbau in cc65. Ich würde ja auch selbst damit anfangen, aber bis jetzt hat sich ja noch niemand geäussert, dass er daran interessiert ist...Zitat
nach "aussen" sind das 32bit ieee floats. intern wird natürlich in das krude format umgerechnet das das basicrom benutzt, sonst geht ja nix
Ich hätte gesagt, dass wir das Basic-Rom mal vergessen und vielleicht folgende Routinen selbst implementieren:
+,-,*,/,cast auf long und andere Richtung, <,>,==. Damit könnte man doch schon einen guten Teil der Math-Lib implementieren. Ich würde, wie schon gesagt, gerne möglichst viel in C machen, weil ich das besser zu lesen finde, besser wartbar finde usw. Wie man das aus dem cc65 aufruft, weiss ich allerdings zugegebenermassen auch noch nicht genau.Ciao,
Andreas -
Ich versuch zu wurzeln, aber es kommt irgendwie immer 0 raus...
Code
Alles anzeigenfloat sqrt_heron(float a) { float x = a; /* Startwert = a */ int i; if( _fsgn( a) == 0) { return _itof( 0); } for(i = 1; i <= 8; ++i) { x = _fdiv( _fadd( x, fdiv( a, x)), _itof(2)); } return x; }Oben muss ich den Fall arg == 0 abtesten, damit ich keine Division durch 0 bekomme. Da _fcmp nicht tut, wollte ich auf fsgn ausweichen, was aber wohl auch nicht geht. Jedenfalls wurzelt da nix...muss man wohl warten bis Stefan (sauhund) ne neue Release macht...
Ciao,
AndreasPS: polluks: mit der tgi-Doku hast Du offensichtlich recht, aber logisch find ich es immer noch nicht...
-
polluks: Ich hab jetzt Deine Lösung zum Löschen genommen und sie ist wirklich viel schneller. Aber es passiert was ulkiges: tgi_setcolor arbeitet jetzt umgekehrt. D.h. wenn ich tgi_setcolor(COLOR_WHITE) nehme, mal er schwarz und umgekehrt. Da werden vermutlich einfach nur Paletteneinträge indiziert und setcolor bekommt nicht mit, dass diese Einträge nun vertauscht wurden. Wenn man also COLOR_WHITE mit COLOR_BLACK vertauscht, läuft das Programm wieder. Aber so arg schön ist es ja nicht...
enthusi: also man kann an dieser Stelle was über den 64er lernen (wie man z.B. effektiv für den 6510 coded), man lernt was über maschinendarstellung von floats (ieee-Darstellung z.B.), man kann was über Mathe lernen uvm. Und Spass macht es mir eigentlich auch. Es ist doch gerade der Reiz mit beschränkten Mitteln viel zu erreichen. Deshalb gibt es ja z.B. auch 4k-Coding Wettbewerbe u.ä. Natürlich könnte ich nativ auf meine AMD Quadcore hier die Funktionen locker in Echtzeit berechnen. Aber auf einem Brotkästchen find ich es halt spannender...

Ciao,
Andreas -
Hallo!
Ich dachte sauhund heisst Stefan? Deshalb dieses @stefan vor dem Kommentar.
Das mit dem schnellen Löschen sieht gut aus. Bau ich gleich mal ein.
Die 144 war tatsächlich nur ein Workaround. Im Original (in dem Data Becker Buch mit Tipps und Tricks zum 64er ist diese Wurzel-Geschichte verwendet worden, damit die ganze Grafik eine runde Grundfläche hat. Nimmst Du die 144 mal raus und ersetzt sie durch die Originalzeile, dann wird für jeden yf-Wert nur 1 xf-Wert geplottet, d.h. Du bekommst quasi einen Querschnitt der Grafik, die Du siehst.
Mir ist gestern als Workaround eine Konstruktion dieser Art eingefallen: xfMax = 144 * sin( acos( yf / 144); wobei die 144 halt yfMax sind. Problem: acos ist noch nicht implementiert. Man könnte ihn durch eine Formel mit atan ersetzen, braucht dann aber wieder eine Wurzel dazu. Die ja anscheinend nicht geht. Also hab ich gestern mal nach sqrtf Implementierungen geguckt, um sowas mal provisorisch einzubauen. Aber alle schnellen Implementierungen stützen sich auf das Binärformat der floats, rechnen also die Wurzel Bit für Bit aus. Das wollte ich ehrlich gesagt nicht unbedingt machen, weil dieses 6-Byte Format aus meiner Sicht keine grosse Zukunft hat, und vermutlich eh durch ein 32-Bit IEEE-Format ersetzt wird. Und dann würde ich das lieber in C schreiben, weil ich es leichter lesbar und wartbar finde.
Ciao,
Andreas -
Bei mir scheint die Wurzel zu gehen ...
Aber das sieht doch auch nicht wirklich rund aus? Hast Du die xfMax-Zeile geändert? Vom Bild her würde ich sagen, sieht das aus, wie xfMax = itof( 144);
@stefan: Könntest Du ggf. eine neue Release Deiner Lib machen, wo fcmp ein unsigned zurückgibt? Ich hab nur kurz in die Sources geguckt, aber Du verwendest wirklich so ein 6-Byte Format mit 4 Byte Mantisse, 1 Byte Exponent und 1 Byte Vorzeichen? Ich befürchte, dass man das nicht in cc65 integriert bekommt. 4 Byte scheinen mir von der Performance her besser zu sein, und ich bin mir nicht sicher, ob Double mit 8 Byte soooviel langsamer wie Dein Typ wäre?
Ciao,
Andreas -
Wieder da...
Nächstes Problem:
liefert -1.27605888e+38 als Wurzel von 144?
Ciao,
AndreasPS: folgender Code malt mal was:
C
Alles anzeigen#include <stdio.h> #include <stdlib.h> #include <conio.h> #include <modload.h> #include <tgi.h> #include "float.h" #include "math.h" int main (void) { unsigned char Border; int XRes, YRes; float xf, xfMin, xfMax, xfDelta, yf, yfMin, yfMax, yfDelta, radiusf, zf, zScale; int x,y; /* Load and initialize the driver */ tgi_load (TGI_MODE_320_200_2); /* CheckError ("tgi_load"); */ tgi_init (); /* CheckError ("tgi_init"); */ /* Get stuff from the driver */ XRes = tgi_getxres(); YRes = tgi_getyres(); /* Set the palette, set the border color */ Border = bordercolor(COLOR_BLACK); /* Clear the background. */ tgi_setcolor( COLOR_WHITE); tgi_bar( 0, 0, XRes - 1, YRes - 1); tgi_setcolor( COLOR_BLACK); /* Compute and draw a 3d function. */ yfMin = itof( -144); yfMax = itof( 144); yfDelta = _strtof( "2.25"); zScale = _itof( 20); for( yf = yfMin; _ftoi( _fsgn( _fsub( yfMax , yf))) >= 0; yf = _fadd( yf, yfDelta)) { /* xfMax = _ftoi( _fadd( _strtof( "0.5"), ( fsqr( _fsub( _fmul( yfMax, yfMax), _fmul( yf, yf)))))); */ xfMax = itof( 144); xfMin = _fneg( xfMax); xfDelta = _itof( 1); for( xf = xfMin; _ftoi( _fsgn( _fsub( xfMax, xf))) >= 0; xf = _fadd( xf, xfDelta)) { /* tgi_outtextxy( 10, 20, debugOutput); */ /* tgi_outtextxy( 30, 20, debugOutput); */ radiusf = _fmul( _strtof( ".0327"), _fsqr( _fadd( _fmul( xf, xf), _fmul( yf, yf)))); zf = _fmul( zScale, _fadd( _fadd( _fcos( radiusf), _fcos( _fmul( radiusf, _itof( 2)))), _fcos( _fmul( radiusf, _itof(5))))); /* Scale to screen coordinates */ x = _ftoi( _fmul( _fadd( _fadd( xf, _fdiv( yf, yfDelta)), _itof( 160)), _strtof( ".85"))); y = _ftoi( _fmul( _fsub( _itof( 199), _fadd( _fsub( zf, _fdiv( yf, yfDelta)), _itof( 90))), _strtof( ".9"))); if( y > 0 && y < 200) { tgi_setpixel( x ,y); /* Clear horizon under y. */ if( y < 199) { tgi_setcolor( COLOR_WHITE); tgi_line( x, y + 1, x, 199); tgi_setcolor( COLOR_BLACK); } } } } while (!kbhit()); /* Unload the driver */ tgi_unload (); /* Reset the border */ bordercolor (Border); /* Done */ printf ("Done\n"); return EXIT_SUCCESS; }Sieht noch nicht so gut aus, weil die xfMax-Zeile mit der Wurzel nicht tut.
Was tgi wohl fehlt:
- Hintergrundfarbe setzen und dann schnelles Löschen (mit memset, oder so).
- Spezielle Funktionen zum Zeichnen von senkrechten und waagrechten Linien. Die kann man auf den meisten Systemen gut optimieren.
- Ich will noch die Rechenzeit ausgeben, aber bisher klappt das nicht. Muss man dazu immer einen Font laden? Auch in dem alten tgi? -
polluks: Danke für den Patch! Muss jetzt leider weg, aber schau es mir später oder Morgen mal an.
Was ich schon ne Weile überlege: muss das wirklich Assembler sein? Sogar im cc65 müsste es doch möglich sein, sogar die grundlegenden Operationen in C zu schreiben, indem man natürlich nur int-Typen verwenden könnte. Wenn man diese Funktionen so schreibt, dass sie ohne Argumente aufgerufen werden, sondern einfach nur die Akkus (die an festen Adressen sind) benutzen, müsste der Compiler ja nur die Argumente in die Akkus bringen und dann ein jsr <operation> machen, oder?
Ciao,
Andreas -
-
@stefan: also im Original C gibt ja anscheinend diese fcmp Funktion nicht, weil es dort ja die Operatoren <, == und > gibt. Also hab ich mich mal an die strcmp Funktion gehalten:
Bitte melde dich an, um diesen Link zu sehen.
Danach müsste sie nur <0, 0 oder eine Zahl > 0 zurückgeben, um das Verhältnis der beiden Argumente zu bestimmen. Ich hatte das irgendwie so im Kopf, dass sie -1, 0 oder 1 zurückgibt. Allerdings hab ich mich zum letzten Mal unter Zorland C so richtig mit den Funktionen beschäftigt. Was also eeeewig her ist.
Wenn Du die Funktion wirklich nur als Gleichheits-Test nutzen willst, bräuchte man noch eine Methode, um auch kleiner und grösser testen zu können...
Sollen wir jetzt mal versuchen, den cc65 zu patchen? Einen Einstiegspunkt hätten wir ja schonmal.... Oder erst Uz anhauen?
Ciao,
Andreas