Die Suche ergab 233 Treffer

von bbock
22.12.2023, 13:59
Forum: Programmierung
Thema: BASICODE in Java
Antworten: 57
Zugriffe: 15642

Re: BASICODE in Java

Damit das Programm auch von Laien leichter zu bedienen ist, wäre eine Menüzeile mit mind. einem File / Open sehr wünschenswert um die BasiCode-Datei in einer File Selection Box auszuwählen. Dann müsste man nicht mit der Kommandozeile herumhantieren, sondern könnte die JAR-Datei einfach per Doppelklick starten (ein installiertes Java 21 vorausgesetzt).

Derzeit muss man eine Shell bemühen (z.B. Windows cmd) und etwa

Code: Alles auswählen

<Pfad zu Java 21>\java -jar <Pfad zu basicode.jar>\basicode.jar <Pfad zur BasiCode-Datei>\<Dateiname>.bas
eintippen. Natürlich kann man den <Pfad zu Java 21> weglassen, wenn die Umgebungsvariablen korrekt gesetzt sind. Ebenso kann man <Pfad zur BasiCode-Datei> weglassen, wenn man sich bereits in dem Verzeichnis befindet, in dem sich die Datei basicode.jar befindet. Sollte dort auch <Dateiname>.bas zu finden sein, dann kann man <Pfad zur BasiCode-Datei> auch noch weglassen. Der kürzeste Aufruf sieht dann wohl so aus:

Code: Alles auswählen

java -jar basicode.jar <Dateiname>.bas
Ich habe mal eine erste Version von basicode.jar zum Ausprobieren angehängt:
basicode.jar
(138.08 KiB) 189-mal heruntergeladen
von bbock
26.11.2023, 15:25
Forum: Programmierung
Thema: BASICODE in Java
Antworten: 57
Zugriffe: 15642

Re: BASICODE in Java

Wie ist denn der aktuelle Stand? Sind mittlerweile alle gültigen BASICODE-Befehle abgedeckt?

Natürlich erwarte ich nicht, dass schon alles fehlerfrei funktioniert. Ich habe die beiden simplen Testprogramme hello.bas und scroll.bas erfolgreich ausführen können. Dann habe ich mich an etwas Größeres gewagt, nämlich das Programm zur Darstellung von Peano-Kurven, das Thomas hier eingestellt hat. Es bricht mit folgendem Fehler ab:
Exception in thread "main" java.lang.IllegalStateException: line not found: 20
at de.haupz.basicode.ast.ProgramNode.resolveJump(ProgramNode.java:57)
at de.haupz.basicode.ast.ProgramNode.run(ProgramNode.java:37)
at de.haupz.basicode.Main.run(Main.java:33)
at de.haupz.basicode.Main.main(Main.java:49)
von bbock
03.09.2023, 17:35
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 38684

Re: Fragen und Antworten zum C-Kurs

Da hat wohl die Implementierung der printf-Funktion ein Problem mit Strings, die länger als 32.767 Zeichen sind. Das ist wohl ein Bug in der library.
Wenn du den Code folgendermaßen änderst, dann geht's:

Code: Alles auswählen

...
  char *p;
 
  printf("Wieviele Sterne? ");
  scanf("%u", &i);
 
  buffer = (char*) malloc(i + 1);  // ein Byte extra für das String-Ende-Zeichen
  if (buffer != NULL) {            // sonst haben wir keinen Speicher bekommen
    buffer[i] = '\0';              // String-Ende-Zeichen setzen

    while(i--) {
      buffer[i] = '*';             // String mit Sternchen füllen
    }

//    printf("%s\n", buffer);
    p = buffer;
    while (*p != '\0') {
        putchar(*p++);
    }
    putchar('\n');

    free(buffer);                  // Wichtig! Speicher wieder freigeben
...
von bbock
03.09.2023, 16:24
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 38684

Re: Fragen und Antworten zum C-Kurs

Paul hat geschrieben: 03.09.2023, 09:33 Und gleich noch eine Frage hinterher:
beim zweiten Beispiel hast die Variable ch global definiert.
Ich wollte sie natürlich gleich lokal definieren was der compiler nicht mag.
Um so größer war meine Verwunderung das der Compiler die unveränderte Zeile als globale Variable akzeptiert.
..,
Wenn du eine Variable lokal anlegst, also innerhalb einer Funktion, dann legt sie der Compiler auf dem Stack an (es sei denn, du deklarierst sie als static). Damit bekommt jeder Funktionsaufruf seine eigenen lokalen Variablen, wodurch z.B. Rekursion ermöglicht wird. Der Speicherplatz wird zur Laufzeit beim Funktionsaufruf durch Ändern des Stack Pointers (dem Z80-Register SP) reserviert und am Ende der Funktion wieder freigegeben (abermals durch Ändern des Stack Pointers). Man spricht auch von "automatischen Variablen". Sie haben naturgemäß keine festen Adressen, da sie vom Inhalt des Stack Pointers abhängen. Ergo macht die Zuordnung einer festen Speicheradresse keinen Sinn und der Compiler quittiert den Versuch mit einer Fehlermeldung.
Globale Variablen werden vom Compiler zur Compile-Zeit festen Speicheradressen zugeordnet (dasselbe gilt für static-Variablen). Während des Programmlaufs ändern diese sich nicht mehr. Hier macht es also Sinn, dem Compiler eine feste Adresse mit '@* vorzugeben. Diese verwendet er dann anstelle der Adresse, die er ohne '@'-Klausel automatisch ermittelt hätte.
von bbock
03.09.2023, 16:13
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 38684

Re: Fragen und Antworten zum C-Kurs

Paul hat geschrieben: 03.09.2023, 09:11 Kann es sei das du dich vertippt hast Bernd?
bbock hat geschrieben: 18.08.2023, 10:10

Code: Alles auswählen

zcc +cpm -subtype=pcw80 -compiler=sccz80 -pragma-define:REGISTER_SP=0xF3FF -create-app testprg1.c -o testprg1.com
Die pragma-define-Option stellt sicher, dass der Stackpointer bei FFF0 beginnt. Damit bleibt der Speicherbereich ab F400 sicher vor Überschreibung durch den Stack.
Du wolltest wahrscheinlich schreiben das der Stackpointer bei F3FF nicht bei FFF0?
Richtig erkannt; ich habe den Fehler korrigiert. Danke für den Hinweis.
von bbock
03.09.2023, 16:10
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 38684

Re: Fragen und Antworten zum C-Kurs

Paul hat geschrieben: 03.09.2023, 08:44 ...
Gibt es eine Liste von sinnvollen Speicheradressen für die Joyce wie z.B. Systemvariablen aus denen man sinnvolles auslesen oder die man sinnvoll manipulieren kann so wie bei den ZX-Maschinen?
Die ZX-Computer von Sinclair haben ein BASIC im ROM, das gleichzeitig das Betriebssystem ist. Der PCW hat aber kein Betriebssystem im ROM; wenn man eines benötigt, dann wird es wird beim Einschalten von Diskette gebootet. Meist ist das CP/M Plus. Man kann aber auch Programme ohne Betriebssystem booten, wie z.B. LocoScript. Daher gibt es auch keine Systemvariablen wie bei den BASIC-Rechnern. Schon möglich, dass es ein paar interessante Speicheradressen gibt, z.B. für CP/M Plus, aber da fällt mir jetzt nichts ein...
von bbock
30.08.2023, 18:23
Forum: Programmierung
Thema: Programmieren in C mit dem z88dk
Antworten: 20
Zugriffe: 11140

19. Dynamische Speicherverwaltung

Werden in C Variablen deklariert, dann werden sie auf unterschiedliche Weise im Speicher angelegt. Im Falle von globalen Variablen werden sie zum Zeitpunkt der Kompilierung mit festen Speicheradressen versehen. Für lokale Variablen von Funktionen oder auch deren Parameter wird zur Laufzeit Speicher auf dem Stack reserviert, der beim Beenden der jeweiligen Funktion wieder freigegeben wird.

Allen ist gemeinsam, dass die Größe des zur Verfügung gestellten Speicherplatzes von vornherein festgelegt ist. Oft benötigt man aber Speicherplatz, dessen Größe nicht bereits zur Compile-Zeit bekannt ist, weil er z.B. von Benutzereingaben abhängt. Für diese Fälle braucht man eine dynamische Speicherzuweisung (engl.: dynamic memory allocation).

C bietet dafür Funktionen in der Standardbibliothek, die man mit #include <stdlib.h> in das eigene Programm einbindet. Dort finden wir u.a. die beiden wichtigsten Funktionen malloc() und free(). malloc() hat einen Parameter: die Größe des zu reservierenden Speichers. Die Funktion liefert einen void-Pointer zurück, der auf den Anfang des reservierten Speichers zeigt. Ein void-Pointer ist im Grunde eine Speicheradresse. Von welchem Typ die Daten im reservierten Speicher sein sollen, davon weiß malloc() nichts. Das Ergebnis wird einfach per type cast in den gewünschten Datentyp umgewandelt; dazu schreibt man den Datentyp einfach in Klammern vor den Funktionsaufruf:

Code: Alles auswählen

char *characters = (char *) malloc(200);
Hier wird Platz für 200 Zeichen reserviert; der Pointer characters zeigt anschließend auf das erste der 200 Zeichen. Sollte nicht mehr genügend Speicher verfügbar sein, dann liefert malloc() NULL zurück. Man sollte das Ergebnis immer auf NULL prüfen, sonst sind Abstürze vorprogrammiert.

Sobald der Speicher nicht mehr benötigt wird, muss er mit der Funktion free() wieder freigegeben werden. Tut man das nicht, dann enstehen sog. Speicherlecks, also Speicher, der dauerhaft reserviert bleibt und nicht mehr für andere sinnvolle Aufgaben verwendet werden kann. free() ist komplementär zu malloc(), d.h. zu jedem malloc()-Aufruf muss es einen korrespondierenden free()-Aufruf geben. Zum Freigeben des zuvor angeforderten Speichers muss man free() den Pointer übergeben, den malloc() zurückgeliefert hat. Man muss des Ergebnis des malloc()-Aufrufs also immer in einer Pointer-Variablen speichern, damit man später free() aufrufen kann.

Im folgenden kleinen Beispiel wollen wir eine Zeichenkette mit einer Anzahl Sternen aufbauen, die der Benutzer vorgibt. Wir speichern die Datei als testmalloc.c.

Code: Alles auswählen

#include <stdio.h>
#include <stdlib.h>
 
int main(void) {
  int i, n, result;
  char *buffer;
 
  printf("Wieviele Sterne? ");
  scanf("%d", &i);
 
  buffer = (char*) malloc(i + 1);  // ein Byte extra für das String-Ende-Zeichen
  if (buffer != NULL) {            // sonst haben wir keinen Speicher bekommen
    buffer[i] = '\0';              // String-Ende-Zeichen setzen

    while(i--) {
      buffer[i] = '*';             // String mit Sternchen füllen
    }

    printf("%s\n", buffer);
    free(buffer);                  // Wichtig! Speicher wieder freigeben

    result = EXIT_SUCCESS;
  }
  else {
    result = EXIT_FAILURE;
  }
  
  return result;
}
Wir kompilieren mit

Code: Alles auswählen

zcc +cpm -subtype=pcw80 -compiler=sccz80 -DAMALLOC -create-app testmalloc.c -o malloc.com
Die Präprozessor-Option -DAMALLOC macht die Verwendung von malloc besonders einfach; sie stellt den automatischen Modus der Speicherverwaltung ein.

Wird die ausführbare Datei mit malloc gestartet, dann fragt das Programm nach der gewünschten Anzahl von Sternen. Gibt man z.B. 15 ein, dann wird Speicher für 16 Bytes reserviert, ein String mit 15 Sternen aufgebaut und anschließend per printf() ausgegeben.

Hier binden wir die Header-Datei stdlib.h ein, die ihrerseits malloc.h inkludiert. In diesem Beispiel hätte es genügt, nur malloc.h einzubinden, allerdings hätten dann die Konstanten EXIT_SUCCESS und EXIT_FAILURE nicht zur Verfügung gestanden.

Im Zusammenhang mit malloc() wird oft der Operator sizeof verwendet. Er dient dazu, den Speicherbedarf eines Datentyps zur Compile-Zeit zu bestimmen. Das funktioniert mit jedem in C deklarierten Datentyp, also auch mit komplexen Datentypen wie structs. Möchte man Speicher für zehn float-Variablen reservieren, dann geht das folgendermaßen:

Code: Alles auswählen

float *floatVars = (float*) malloc(20 * sizeof(float));
Der Compiler bestimmt also die Größe einer float-Variablen und durch Multiplikation mit 20 bekommen wir den Speicherplatz für 20 float-Variablen. Wie viel Speicher das tatsächlich ist, hängt vom System ab, auf dem das Programm kompiliert wird.

Für den Zugriff auf die einzelnen float-Variablen kann man auch die Array-Schreibweise verwenden. Möchte man die dritte float-Variable, also die mit dem Index 2, auf den Wert 5.3 setzen, dann geht das so:

Code: Alles auswählen

floatVars[2] = 5.5;
In der Standardbibliothek findet man weitere Funktionen zur dynamischen Speicherverwaltung. Darunter calloc(), bei der man die Anzahl der gewünschten Elemente und die Elementgröße als Parameter übergibt, und realloc() zur Änderung der Größe eines zuvor allozierten Speicherblocks.
von bbock
18.08.2023, 10:10
Forum: Programmierung
Thema: Programmieren in C mit dem z88dk
Antworten: 20
Zugriffe: 11140

18. Absolute Adressen

Manchmal ist es erforderlich auf bestimmte Speicheradressen zuzugreifen. Hierfür bietet das z88dk verschiedene Möglichkeiten. Eine Möglichkeit ist die Verwendung von Pointer-Variablen. So kann man z.B. den Wert 23 folgendermaßen in die Speicherzelle FF40 schreiben:

Code: Alles auswählen

unsigned char *p;
p = (unsigned char *) 0xFF40;
*p = 23;
Um den Wert wieder auszulesen, dereferenzieren wir den Pointer:

Code: Alles auswählen

printf("%d\n", *p);
Das komplette Testprogramm könnte so aussehen:

Code: Alles auswählen

#include <stdio.h>

int main(void) {
    unsigned char *p;
    p = (unsigned char *) 0xF400;
    *p = 23;

    printf("%d\n", *p);

    return 0;
}
Wir kompilieren es mit

Code: Alles auswählen

zcc +cpm -subtype=pcw80 -compiler=sccz80 -pragma-define:REGISTER_SP=0xF3FF -create-app testprg1.c -o testprg1.com
Die pragma-define-Option stellt sicher, dass der Stackpointer bei F3FF beginnt. Damit bleibt der Speicherbereich ab F400 sicher vor Überschreibung durch den Stack.

Es gibt noch einen einfacheren Weg um auf Werte an absoluten Adressen zuzugreifen: Variablen können so deklariert werden, dass sie an einer bestimmten Adresse im Speicher liegen.

Code: Alles auswählen

unsigned char ch @ 0xF400;
Das obige Testprogramm kann also wie folgt geändert werden:

Code: Alles auswählen

#include <stdio.h>

unsigned char ch @ 0xF400;

int main(void) {
    ch = 23;
    printf("%d\n", ch);
    printf("%d\n", *((unsigned char *) 0xF400));

    return 0;
}
Das zweite printf dient zur Überprüfung, dass der Wert tatsächlich in der Speicherzelle F400 angekommen ist.

Hat man Maschinencode an einer bestimmten Speicheradresse abgelegt, dann kann man die Einsprungstellen als C-Funktionen zugänglich machen. Die Syntax ist wie folgt:

Code: Alles auswählen

void *gx_scrAcc(void) @ 0xF494;
Ruft man im C-Programm die Funktion mit gx_scrAcc() auf, dann erzeugt der Compiler einen CALL-Befehl zur Speicheradresse F494. Für weitere Details zur Zusammenarbeit von C mit Maschinencode sei auf die z88dk-Dokumentation unter "Mixing C and Z80 Assembler" verwiesen. Dort werden Details zur Aufrufkonvention, die Parameter-Übergabe, das Erstellen von Bibliotheken und die Verwendung von Inline Assembler erläutert.
von bbock
15.08.2023, 20:46
Forum: Programmierung
Thema: HP-GL Interpreter
Antworten: 48
Zugriffe: 102559

HP-GL Interpreter C-Version

Es war eine Herausforderung, den Maschinencode-Anteil im z88dk-C zum Laufen zu bringen, aber schließlich hat es funktioniert. Der Maschinencode wird beim HPGL-Interpreter und bei pbmread für das Lesen und Schreiben von PBM-Dateien benötigt, genauer gesagt für den Zugriff auf den Bildspeicher im "Roller RAM".

Damit der Maschinencode in Sicherheit ist, muss dem C-Compiler mitgeteilt werden, dass der Stack unterhalb des gesicherten Speicherbereichs beginnen soll. Das wird mit der Option -pragma-define:REGISTER_SP=0xF3FF erreicht (siehe make.cmd).

Die Quellcode-Dateien und die ausführbaren COM-Dateien sind wie üblich im Download-Bereich zu finden.