In C kann nicht in den Bildschirmspeicher geschrieben werden

Lesezeit: 7 Minuten

Benutzeravatar von Ampera
Ampera

Ich bin sehr neu in C, es ist meine zweite höhere Programmiersprache nach Java. Ich habe die meisten Grundlagen verstanden, aber aus irgendeinem Grund kann ich kein einziges Zeichen in den Bildschirmspeicher schreiben.

Dieses Programm wurde mit Turbo C für DOS auf einem Am486-DX4-100 mit 120 MHz kompiliert. Die Grafikkarte ist eine sehr standardmäßige VLB Diamond Multimedia Stealth SE mit einem Trio32-Chip.

Als Betriebssystem verwende ich PC-DOS 2000 mit geladener ISO-Codepage. Ich arbeite im Standard-Textmodus im MDA/CGA/EGA/VGA-Stil mit 80 Spalten und Farbe.

Hier ist das Programm, wie ich es geschrieben habe:

#include <stdio.h>

int main(void) {
    unsigned short int *Video = (unsigned short int *)0xB8000;
    *Video = 0x0402;
    getchar();
    return 0;
}

Wie ich bereits sagte, bin ich sehr neu in C, also entschuldige ich mich, wenn mein Fehler offensichtlich erscheint, ich konnte keine solide Quelle dafür finden, die ich verstehen könnte.

Meines Wissens beginnt der Bildschirmspeicher für den Textmodus im Realmodus auf der x86-Plattform bei 0xB8000. Jedes Zeichen wird in zwei Bytes gespeichert, eines für das Zeichen und eines für den Hintergrund/Vordergrund. Die Idee ist, den Wert 0x0402 (was ein rotes lächelndes Gesicht sein sollte) in 0xB8000 zu schreiben. Dies sollte es oben links auf dem Bildschirm platzieren.

Ich habe die Möglichkeit berücksichtigt, dass der Bildschirm scrollt und somit meinen Charakter bei der Ausführung auf zwei Arten sofort entfernt. Um dieses Problem zu lösen, habe ich versucht:

  • Schreiben Sie diesen Wert wiederholt mit einer Schleife
  • Schreib es etwas weiter unten.

Ich kann den Wert, den ich in den Speicher geschrieben habe, lesen und drucken, also ist er offensichtlich noch irgendwo im Speicher, aber aus irgendeinem Grund bekomme ich nichts auf dem Bildschirm. Ich mache offensichtlich etwas falsch, aber ich weiß nicht, woran es liegen könnte. Wenn weitere Details benötigt werden, fragen Sie bitte. Vielen Dank für jede mögliche Hilfe, die Sie geben können.

  • Wie ordnet Ihre C-Implementierung Ganzzahlen Zeigern zu? Müssen Sie irgendeine Art von verwenden far Zeiger-Schlüsselwort? Oder läuft es ein Big/Huge Unreal-Modus mit 32-Bit-Zeigern?. Wenn ein C-Zeiger nur 16 Bit breit ist, dann ist es nur ein Offset innerhalb eines Segments (ds standardmäßig die meiste Zeit) und konvertieren 0xB8000 zu einem Zeiger wird auf 16 Bit gekürzt und gibt Ihnen einen Offset von 0x8000 relativ zu ds. Die TL: DR-Segmentierung im Realmodus wird nicht sauber auf C-Zeiger abgebildet. Erwarten Sie NICHT, dass dies einfach istbesonders wenn Sie nicht sowohl C als auch x86-16 asm kennen.

    – Peter Cordes

    1. Dezember 2017 um 7:40 Uhr


  • 0xB8000 ist 20 Bit breit. (Jede Hex-Ziffer codiert vier Bits, und es gibt fünf davon.) Die Breite eines an unsigned short int ist nur 16 Bit. Die Breite eines Near-Zeigers in den meisten BCC-Speichermodellen (ich denke, nicht riesig, aber es ist Jahrzehnte her.) beträgt ebenfalls 16 Bit. Sie haben also einen Überlauf verursacht und herumgewickelt. Ich vermute, Sie haben schließlich an die Adresse geschrieben DS:8000 stattdessen.

    – Davislor

    1. Dezember 2017 um 10:08 Uhr


  • Gibt es auch einen Professor in Indien, der Compiler aus der Mitte der 90er Jahre, die auf einem 16-Bit-Betriebssystem laufen, an beginnende CS-Studenten zuweist, oder so etwas? Gibt es dafür irgendeinen Grund? Moderne Tools wie Linux, Clang und gcc sind völlig kostenlos.

    – Davislor

    1. Dezember 2017 um 10:12 Uhr

  • Sie können gerne vorbeischauen retrocomputing.stackexchange.com

    – Benutzer11153

    1. Dezember 2017 um 14:00 Uhr

  • @Davislor: Ein bisschen Asm zu kennen ist auch sehr praktisch, um von den Fehlersymptomen zu den Ursachen in C zurückzuarbeiten, daher sollte das Verständnis, wie die Dinge funktionieren, nicht unterschätzt werden. Lustiges Beispiel dafür: nicht ausgerichtet uint16_t* kann auf x86 segfault, aber x86 kann nicht ausgerichtete Lasten ausführen? Die Autovektorisierung bricht diesen “funktioniert zufällig”-Code auf interessante Weise.

    – Peter Cordes

    2. Dezember 2017 um 4:24 Uhr

  • Das funktioniert und durch all diese Erklärungen denke ich, dass ich das Problem verstanden habe. Es ist fast 3 Uhr morgens, wo ich wohne, und ich werde Zeit brauchen, um das richtig zu verstehen. Danke für die Hilfe.

    – Ampera

    1. Dezember 2017 um 7:48 Uhr


  • Casting von einer 32-Bit-Ganzzahl in a far * nicht arbeiten? Wie Stackers Antwort, die verwendet char far *Video = (char far *)0xb8000000;

    – Peter Cordes

    1. Dezember 2017 um 7:51 Uhr

  • @Ampera In meiner Antwort gibt es einen Link zu Starmans Seite bezüglich segment:offset-Adressierung im Allgemeinen. Sie brauchen eine gute Grundlage dafür, wie die segment:offset-Adressierung im 16-Bit-Realmodus funktioniert. Wenn Sie tatsächlich wach sind, schlage ich vor, die zu lesen Starman-Link . Weitere Informationen zu den Zeigertypen, die 16-Bit-Turbo-C unterstützt, finden Sie in einer anderen SO-Antwort

    – Michael Petsch

    1. Dezember 2017 um 7:51 Uhr

  • @PeterCordes Es ist eine triviale Kopie, die fertig ist (far<=>uint32_t). uint32_t (und der far-Zeiger) stellen keine lineare Adresse dar. Es ist ein 32-Bit-Wert, bei dem die oberen 16 Bits das Segment und die unteren 16 Bits der Offset sind. Es erfolgt keine Konvertierung. Sie können mit einem solchen gecasteten Zeiger Zeigerarithmetik durchführen, solange Sie nicht in die oberen 16 Bits übertragen.

    – Michael Petsch

    1. Dezember 2017 um 8:06 Uhr


  • @PeterCordes: turboCand viele frühe Compiler hatten keine stdint.h. Es gab einen Header eines Drittanbieters, den Sie verwenden konnten. Davon abgesehen war die Größe eines Zeigers vom Speichermodell abhängig (Tiny, Small, Medium waren 16-Bit-Zeiger). Sie können mit diesen Modellen nicht einfach einen far oder huge Zeiger (die immer 32-Bit sind) auf einen 16-Bit-Zeiger umwandeln. Im kompakten und großen Modell ist der Standardzeigertyp far (und uintptr_t ist Größe 4). Riesig ist Größe 4. Sie können einen riesigen Zeiger auf weit werfen, aber nicht umgekehrt, es sei denn, der ferne Zeiger wurde zuerst normalisiert.

    – Michael Petsch

    1. Dezember 2017 um 23:05 Uhr


  • Dies wäre eine bessere Antwort, wenn Sie das erklären würden 0xb8000000 ist der seg:off Vertretung der OPs 0xB8000 lineare Adresse. dh die oberen 16 Bit sind 0xB8000 >> 4und die unteren 16 Bits sind alle Null (0 & 15). Und Ihr erster Satz sollte “lineare Adresse” und nicht “Segmentadresse” lauten, da dies keine Segmentregisterwerte sind, sondern das, was Sie für den Zugriff auf die Segmentierung benötigen. (Außerdem sollte ein VGAtext-Basiszeiger möglicherweise als deklariert werden short oder eine Struktur, nicht char).

    – Peter Cordes

    2. Dezember 2017 um 3:07 Uhr


  • Wäre Huge nicht genau das, was das OP braucht, damit der Code in der Frage unverändert funktioniert? So können alle Zeiger als lineare Adressen in den physischen Real-Mode-Speicher behandelt werden, und der Compiler kann die Segmentierung für Sie “umgehen”, indem er die Segmentregister bei Bedarf neu lädt. (oops NVM, so funktioniert es nicht. Sie sind keine linearen Adressen. stackoverflow.com/questions/47588486/…)

    – Peter Cordes

    1. Dezember 2017 um 8:08 Uhr


  • Ja, ich habe den Hinweis gefunden: “Unter DOS/4GW wird das erste Megabyte des physischen Speichers als gemeinsam genutzter linearer Adressraum abgebildet. Dadurch kann Ihre Anwendung auf Video-RAM zugreifen, indem sie einen Near-Zeiger verwendet, der auf die lineare Adresse des Bildschirms gesetzt ist.“. Aber noch einmal, dann ist dies keine x86-16-Bit-Programmierung mehr.

    – jjmontes

    1. Dezember 2017 um 16:00 Uhr


  • @MichaelKjörling: Compiler-Autoren des 20. Jahrhunderts erkannten den Wert einer Sprachfamilie, die für verschiedene Plattformen optimiert wurde, aber eine gemeinsame Basis hatte. Ich denke, der Grund, warum die Autoren des C89-Standards nur zwei Arten von C-Implementierungen (gehostete und freistehende) definiert haben, war nicht, dass sie dachten, zwei seien genug, sondern dass sie sich nicht für in der Lage hielten, alles zu definieren, was dazu erforderlich wäre Implementierungen für alle Zwecke geeignet machen, für die C eingesetzt wurde. Der 8086 hatte eindeutig eine natürliche Art, einen 32-Bit-Wert als Zeiger zu lesen …

    – Superkatze

    1. Dezember 2017 um 22:16 Uhr


  • @PeterCordes: Der wirkliche Wert der Unterstützung von C für seltsame Plattformen war nie die Fähigkeit, solche Plattformen mit einer Vielzahl von Programmen nutzbar zu machen, sondern eher die Fähigkeit, solche Plattformen mit einer Vielzahl von Programmen nutzbar zu machen Programmierer. Wenn eine Plattform 12-Bit hat charzum Beispiel, Code, der für Oktett-basierte Speicherung geschrieben wurde, sollte nicht damit funktionieren, aber einem Programmierer zu sagen: „Diese Plattform ist ziemlich normal, außer dass sie 12-Bit hat char24-Bit-Big-Endian int und Zeiger und 48-Bit-Big-Endian long” sollte dieser Person genügend Informationen geben, um Code dafür zu schreiben …

    – Superkatze

    1. Dezember 2017 um 23:43 Uhr


  • … ohne einen neuen Befehlssatz, Assembler-Format usw. lernen zu müssen. Ich finde die Vorstellung, dass Programmierer danach streben sollten, jede konforme Implementierung zu unterstützen, absurd, zumal die Autoren des Standards anerkennen, dass eine Implementierung gleichzeitig aber konform sein könnte nicht zu gebrauchen.

    – Superkatze

    1. Dezember 2017 um 23:45 Uhr

1404970cookie-checkIn C kann nicht in den Bildschirmspeicher geschrieben werden

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy