home *** CD-ROM | disk | FTP | other *** search
/ Turbo Toolbox / Turbo_Toolbox.iso / qc25 / beispiel / life.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-07-25  |  34.3 KB  |  1,185 lines

  1. /* LIFE.C - Demonstrationsprogramm für das Spiel LIFE
  2.  *
  3.  * Sofern GRAPHICS.LIB und PGCHART.LIB bei der Installation nicht
  4.  * in die Laufzeit-Bibliothek eingefügt wurden, müssen die beiden
  5.  * Bibliotheken in die Programmliste von GRDEMO.MAK aufgenommen 
  6.  * oder auf der Befehlszeile angegeben werden. 
  7.  *
  8.  * Das Spiel Life wurde 1970 vom Mathematiker John Horton Conway erfunden.
  9.  * Ziel ist, Lebensformen zu schaffen und zu untersuchen, die sich aus Mustern
  10.  * ergeben, die auf einem 'Spielbrett' eingegeben wurden (normalerweise der
  11.  * Bildschirm, man kann es aber auch mit Papier und Bleistift spielen).
  12.  *
  13.  * Das Spiel Life basiert auf folgenden Gesetzen:
  14.  *
  15.  *     1. Das Gesetz des Überlebens - Wenn eine lebende Zelle entweder
  16.  *        zwei oder drei Nachbarzellen hat, überlebt sie.
  17.  *
  18.  *     2. Das Gesetz des Absterbens - Eine lebende Zelle mit mehr als drei
  19.  *        Nachbarn stirbt wegen 'Überbevölkerung', eine mit weniger als
  20.  *        zwei Nachbarzellen stirbt an 'Einsamkeit'.
  21.  *
  22.  *     3. Das Gesetz der Geburt - In der nächsten Generation wird eine
  23.  *        tote Zelle mit genau drei Nachbarzellen geboren.
  24.  *
  25.  * Aus diesen einfachen Gesetzen ergeben sich komplexe Gegenwirkungen.
  26.  * Versuchen Sie beispielsweise einmal folgende Muster einzugeben:
  27.  *
  28.  *      ■■       ■             ■        ■■                  ■    ■
  29.  *     ■ ■         ■            ■      ■■       ■■■■■     ■■ ■■■■ ■■
  30.  *       ■      ■■  ■■■     ■   ■       ■       ■   ■       ■    ■
  31.  *                          ■■■■
  32.  */
  33.  
  34. #include <string.h>
  35. #include <stdlib.h>
  36. #include <stdio.h>
  37. #include <conio.h>
  38. #include <ctype.h>
  39. #include <time.h>
  40. #include <graph.h>
  41. #include "tools.h"
  42.  
  43. /* Dimensionen der Bevölkerungsmatrix für den größtmöglichen Bildschirm --
  44.  * VGA 80 * 50).
  45.  */
  46. #define MAXREIH 46
  47. #define MAXSPLT 78
  48. #define MAXBILD ((MAXREIH + 2) * (MAXSPLT + 2))
  49.  
  50. #define BREIH    (cfg.mzeile + 2)         /* Bildschirm-Zeilen             */
  51. #define BSPLT    (cfg.msplt + 2)          /* Bildschirm-Spalten            */
  52. #define BBUFSIZE (BREIH * BSPLT)          /* Größe Bildschirm-Zwspeicher   */
  53. #define MBUFSIZE (cfg.mzeile * cfg.msplt) /* Größe Matrix-Zwischenspeicher */
  54.  
  55. #define SIGNAL 7
  56.  
  57. /* Aktion für status_zelle */
  58. enum ZELLENSTATUS { AUS, EIN, UMGESCH };
  59.  
  60. /* Der Zellentyp bestimmt Symbol und Attribute einer Zelle.
  61.  * Leben und Tod, die zwei häufigsten Zellen, werden initialisiert.
  62.  */
  63. typedef struct ZELLENTYP
  64. {
  65.    unsigned char  symbol;
  66.    unsigned char  atrib;
  67. } ZELLE;
  68. ZELLE leben =
  69. {   '■',
  70.    SETATRIB( _TBRIGHTWHITE, _TBLACK )
  71. };
  72. ZELLE tod =
  73. {   ' ',
  74.    SETATRIB( _TBRIGHTWHITE, _TBLACK )
  75. };
  76.  
  77. /* Struktur für Gesamtattribute von LIFE, für Mono initialisiert   */
  78. struct LIFECONFIG
  79. {
  80.    float dichte;          /* Prozent Zufallsverteilung        */
  81.    int   rescan;          /* Abtastwiederholung für CGA       */
  82.    int   far *videomem;   /* Adresse des Videospeichers       */
  83.    char  boxatrib;        /* Attribut für Rahmen              */
  84.    char  hlfatrib;        /* Attribut für Dialogzeile         */
  85.    int   mzeile;          /* Matrix-Zeilen                    */
  86.    int   msplt;           /* Matrix-Spalten                   */
  87.    unsigned cursor;       /* Anfangs- u. Endzeilen für Cursor */
  88. } cfg =
  89. {
  90.    0.40,
  91.    FALSCH,
  92.    (int far *)0xb0000000,
  93.    SETATRIB( _TWHITE, _TBLACK ),
  94.    SETATRIB( _TBLACK, _TWHITE ),
  95.    21,
  96.    78
  97. };
  98.  
  99. /* Globalvariablen */
  100. char   mat1[MAXREIH][MAXSPLT]; /* Matrix 1: speichert ggw. 'Bevölkerung'     */
  101. char   mat2[MAXREIH][MAXSPLT]; /* Matrix 2: speichert Überbevölkerungszahlen */
  102. int    zelle;                  /* Zellenzeichen                              */
  103. char   attrib;                 /* Videoattribut einer jeden Position         */
  104. int    endlos;                 /* Unbegrenzte Anzahl von Generationen?       */
  105. long   grenze;                 /* Höchstanzahl auszuführender Generationen   */
  106. ZELLE  bildpfr[MAXBILD];       /* Bildschirm-Zwischenspeicherbereich         */
  107.  
  108. /* Tastencodes */
  109. #define HM      0x0147
  110. #define UA      0x0148
  111. #define PU      0x0149
  112. #define RA      0x014d
  113. #define PD      0x0151
  114. #define DA      0x0150
  115. #define ED      0x014f
  116. #define LA      0x014b
  117. #define SH_HM   0x0247
  118. #define SH_UA   0x0248
  119. #define SH_PU   0x0249
  120. #define SH_RA   0x024d
  121. #define SH_PD   0x0251
  122. #define SH_DA   0x0250
  123. #define SH_ED   0x024f
  124. #define SH_LA   0x024b
  125. #define EINFG   0x0152
  126. #define ENTF    0x0153
  127. #define EINGABE   13
  128. #define ESC       27
  129.  
  130. /* Zeichenfolgen-Dialoghinweise */
  131. char run_str[] =
  132. "AUSFÜHREN:  S=Schneller  L=Langsamer  O=Optionen  E=Ende";
  133.  
  134. char edit_str[] =
  135. "BEARBEITEN:  RICHTUNGSTASTEN=Bewegen  UMSCHALT+RICHTUNGS=Bewegen/Umschalten\n             LEERTASTE=Umschalten  X=Löschen  EINGABE=Fertig";
  136.  
  137. char pause_str[] =
  138. "PAUSE:  S=Start  X=Löschen  Z=Einzelschritt  B=Bearbeiten  E=Ende\n        N=Neue Zufallsverteilung  I=Einlesen  R=Schreiben";
  139.  
  140. char file_str[]  = "Dateinamen eingeben: ";
  141.  
  142. char ferr_str[]  = "Dateizugriff versagt - weiter mit beliebiger Taste ...";
  143.  
  144. char dense_str[] = "Ungültige Dichte - weiter mit beliebiger Taste ...";
  145.  
  146. /* Funktionsprototypen */
  147. int  main( int argc, char **argv );
  148. void run_mode( void );
  149. int  pause_mode( void );
  150. void edit_mode( void );
  151. void init_life( void );
  152. void init_pfr( void );
  153. void zeichne_box( void );
  154. void init_mats( void );
  155. void zufall_vt( float chance );
  156. void generation( void );
  157. void pass2( void );
  158. void status_zelle( int aktion, int zle, int spl );
  159. int  lese_life( void );
  160. int  schrbe_life( void );
  161. int  zeige_prompt( char *prompt, char antwort[] );
  162. void auffrischen( ZELLE einpuffer[], int far *auspuffer );
  163.  
  164. /* main - Führt das Spiel Life aus.
  165.  *
  166.  * Parameter: argc - Anzahl der Befehlszeilenargumente
  167.  *            argv - Datenfeld aus Befehlszeilen-Zeichenfolgen
  168.  *
  169.  * Ergibt:    0
  170.  *
  171.  * Benutzt:   grenze
  172.  */
  173. int main( int argc, char **argv )
  174. {
  175.  
  176.    /* Video und Matrix initialisieren. Rahmen zeichnen.  */
  177.    init_life();
  178.    init_pfr();
  179.    zeichne_box();
  180.    init_mats();
  181.  
  182.    /* Gibt es kein Befehlszeilenargument, endlos weiterfahren, sonst so
  183.     * oft, wie auf Befehlszeile bestimmt. 0 auf der Befehlszeile bedeutet,
  184.     * daß mit Bearbeiten statt Zufallszahlgeneration angefangen werden soll.
  185.     */
  186.    if( argc > 1 )
  187.    {
  188.       grenze = atol( argv[1] );
  189.       endlos = FALSCH;
  190.    }
  191.    else
  192.    {
  193.       grenze = WAHR;
  194.       endlos = WAHR;
  195.    }
  196.    if ( !grenze )
  197.    {
  198.       endlos = WAHR;
  199.       edit_mode();
  200.    }
  201.    else
  202.       zufall_vt( cfg.dichte );
  203.  
  204.    /* Life ausführen. */
  205.    run_mode();
  206.  
  207.    /* Rücksetzen und beenden. */
  208.    _settextcursor( cfg.cursor );
  209.    _displaycursor( _GCURSORON );
  210.    _setvideomode( _DEFAULTMODE );
  211.     exit( FALSCH );
  212. }
  213.  
  214. /* run_mode - Führt Life aus, prüft auf Tastatureingaben zwischen Genera-
  215.  * tionen. Bei Tastatureingabe den entsprechenden Vorgang ausführen.
  216.  *
  217.  * Parameter: Keine
  218.  *
  219.  * Ergebnis:  Keines
  220.  *
  221.  * Benutzt:   grenze, endlos, run_str
  222.  */
  223. void run_mode()
  224. {
  225.    char taste;
  226.    static clock_t speed = 100; /* Verspätung in Mikrosekonden (unter 1000) */
  227.  
  228.    /* Befehlsaufforderung anzeigen und auf Tastatureingabe prüfen. */
  229.    zeige_prompt( run_str, "" );
  230.    while( endlos || grenze-- )
  231.    {
  232.       delay( speed );
  233.       generation();
  234.       if( taste = getkey( NO_WAIT ) )
  235.       {
  236.      taste = toupper( taste );
  237.      switch( taste )
  238.          {
  239.         case 'O':   /* auf Pausenmodus */
  240.                if( !pause_mode() )
  241.                   return;
  242.                break;
  243.         case 'S':   /* Schneller */
  244.                if( speed )
  245.                   speed -= 100;
  246.                break;
  247.         case 'L':   /* Langsamer */
  248.                if( speed < 1000 )
  249.                   speed += 100;
  250.                break;
  251.         case 'E':   /* Ende */
  252.             case ESC:
  253.                return;
  254.          }
  255.       }
  256.    }
  257. }
  258.  
  259. /* pause_mode - Übernimmt eine Pausenmodus-Tastatureingabe und nimmt die
  260.  *              entsprechende Maßnahme.
  261.  *
  262.  * Parameter: Keine
  263.  *
  264.  * Ergibt: bei <E>nde FALSCH, sonst WAHR
  265.  *
  266.  * Benutzt:   cfg und diverse Meldungs-Zeichenfolgen
  267.  */
  268. int pause_mode()
  269. {
  270.    int i, pause = WAHR;
  271.    char tmp[80];
  272.    char taste;
  273.  
  274.    zeige_prompt( pause_str, "" );
  275.    while( pause )
  276.    {
  277.       taste = getkey( WAIT );
  278.       switch( toupper( taste ) )
  279.       {
  280.  
  281.      case 'X':         /* Life-Arena löschen             */
  282.         init_pfr();
  283.         zeichne_box();
  284.             init_mats();
  285.             break;
  286.      case 'S':          /* Start - Life neu starten             */
  287.             pause = FALSCH;
  288.             break;
  289.      case 'B':          /* Bearbeiten des gegenwärtigen Musters */
  290.             edit_mode();
  291.             break;
  292.      case 'E':          /* Ende - das Spiel beenden             */
  293.          case ESC:
  294.             return FALSCH;
  295.      case 'Z':          /* Einzelschritt - nur eine Generation  */
  296.             generation();
  297.         grenze--;
  298.             break;
  299.      case 'N':          /* Neue Zufallswerte erstellen          */
  300.             sprintf( tmp, "Gegenwärtige Dichte:  %.f  Neue Dichte: ",
  301.              cfg.dichte * 100 );
  302.         zeige_prompt( tmp, tmp );
  303.             i = atoi( tmp );
  304.             if ( (i < 1) || (i > 100) )
  305.             {
  306.            zeige_prompt( dense_str, "" );
  307.                putch( SIGNAL );
  308.                getch();
  309.            zeige_prompt( pause_str, "" );
  310.                break;
  311.             }
  312.             /* Bildschirm löschen und neu einrichten. */
  313.         init_pfr();
  314.         zeichne_box();
  315.             init_mats();
  316.         zeige_prompt( pause_str, "" );
  317.         zufall_vt( cfg.dichte = (float)(i / 100.0) );
  318.             break;
  319.      case 'I':              /* Neues Muster von Datei einlesen   */
  320.         if( !lese_life() )
  321.             {
  322.            zeige_prompt( ferr_str, "" );
  323.                putch( SIGNAL );
  324.                getch();
  325.             }
  326.         zeige_prompt( pause_str, "" );
  327.             break;
  328.      case 'R':             /* Gegenwärtiges Muster zu Datei schreiben */
  329.         if( !schrbe_life() )
  330.             {
  331.            zeige_prompt( ferr_str, "" );
  332.                putch( SIGNAL );
  333.                getch();
  334.             }
  335.         zeige_prompt( pause_str, "" );
  336.             break;
  337.       }
  338.    }
  339.    /* prompt wiederherstellen. */
  340.    zeige_prompt( run_str, "" );
  341.    return WAHR;
  342. }
  343.  
  344. /* edit_mode - Übernimmt mehrfache Bearbeitungs-Tastatureingabe und nimmt die
  345.  *             entsprechende Maßnahme.
  346.  *
  347.  * Parameter:  Keine
  348.  *
  349.  * Ergebnis:   Keines
  350.  *
  351.  * Benutzt:    grenze, cfg, edit_str, pause_str
  352.  */
  353. void edit_mode()
  354. {
  355.    int mehr = WAHR;
  356.    unsigned taste;
  357.    int curs_zle = cfg.mzeile / 2, curs_spl = cfg.msplt / 2;
  358.  
  359.    /* Dialoghinweis erneuern, Cursor anzeigen und mittigstellen. */
  360.    zeige_prompt( edit_str, "" );
  361.    _displaycursor ( _GCURSORON );
  362.    _settextposition( curs_zle + 2, curs_spl + 2 );
  363.  
  364.    do {
  365.       taste = getkey( WAIT );
  366.       switch( taste )
  367.       {
  368.          case SH_HM:         /* Nach links oben ('Nord-West') */
  369.          case HM:
  370.         if( (curs_spl > 0) && (curs_zle > 0) )
  371.             {
  372.            curs_spl--;
  373.            curs_zle--;
  374.             }
  375.             break;
  376.          case SH_UA:         /* Gerade nach oben ('Norden') */
  377.          case UA:
  378.          case 'k':
  379.         if( curs_zle > 0 )
  380.            curs_zle--;
  381.             break;
  382.          case SH_PU:         /* Nach rechts oben ('Nord-Osten') */
  383.          case PU:
  384.         if( (curs_spl < cfg.msplt - 1) && (curs_zle > 0) )
  385.             {
  386.            curs_spl++;
  387.            curs_zle--;
  388.             }
  389.             break;
  390.          case SH_RA:         /* Nach rechst ('Osten') */
  391.          case RA:
  392.          case 'l':
  393.         if( curs_spl < cfg.msplt - 1)
  394.            curs_spl++;
  395.             break;
  396.          case SH_PD:         /* Nach rechts-unten ('Süd-Osten') */
  397.          case PD:
  398.         if( (curs_spl < cfg.msplt - 1) && (curs_zle < cfg.mzeile - 1) )
  399.             {
  400.            curs_spl++;
  401.            curs_zle++;
  402.             }
  403.             break;
  404.          case SH_DA:         /* Direkt nach unten ('Süden') */
  405.          case DA:
  406.          case 'j':
  407.         if( curs_zle < cfg.mzeile - 1)
  408.            curs_zle++;
  409.             break;
  410.      case SH_ED:         /* Nach links-unten ('Süd-West') */
  411.          case ED:
  412.         if( (curs_spl > 0 ) && (curs_zle < cfg.mzeile - 1) )
  413.             {
  414.            curs_spl--;
  415.            curs_zle++;
  416.             }
  417.             break;
  418.          case SH_LA:         /* Nach links ('Westen') */
  419.          case LA:
  420.          case 'h':
  421.         if( curs_spl > 0 )
  422.            curs_spl--;
  423.             break;
  424.          case ' ':           /* Gegenwärtige Zelle umschalten */
  425.         status_zelle( UMGESCH, curs_zle, curs_spl );
  426.             break;
  427.      case EINFG:         /* Gegenwärtige Zelle einschalten */
  428.         status_zelle( EIN, curs_zle, curs_spl );
  429.             break;
  430.      case ENTF:          /* Gegenwärtige Zelle ausschalten */
  431.         status_zelle( AUS, curs_zle, curs_spl );
  432.             break;
  433.      case 'X':        /* Zellen löschen */
  434.      case 'x':
  435.         init_pfr();
  436.         zeichne_box();
  437.             init_mats();
  438.             break;
  439.          case 'F':           /* Fertig - Änderungen annehmen */
  440.      case 'f':
  441.      case EINGABE:
  442.         mehr = FALSCH;
  443.             break;
  444.      default:            /* Unbekannte Tasten ignorieren */
  445.             break;
  446.       }
  447.       /* Wenn UMSCHALTTASTE gedrückt, Taste umschalten. */
  448.       if( (taste >> 8) == 2 )
  449.      status_zelle( UMGESCH, curs_zle, curs_spl );
  450.  
  451.       /* Cursorposition neu einrichten. */
  452.       _settextposition( curs_zle + 2, curs_spl + 2 );
  453.  
  454.    } while( mehr );
  455.  
  456.    /* Cursor ausschalten und Pausen-Dialoghinweis wiederherstellen. */
  457.    _displaycursor (_GCURSOROFF );
  458.    zeige_prompt( pause_str, "" );
  459. }
  460.  
  461. /* init_life - Initialisiert Bildschirmmodus, Zeilen- und Cursor-Status.
  462.  *             Bestimmt Global-Variablen für Bildschirm, Konfiguration
  463.  *             und Life.
  464.  *
  465.  * Parameter:  Keine
  466.  *
  467.  * Ergebnis:   Keines
  468.  *
  469.  * Benutzt:    Richtet folgende Werte ein:
  470.  *             cfg.rescan - Kennung für CGA-Handhabung
  471.  *                .cursor - Cusorform
  472.  *                .mzeile - Höchstanzahl der Zeilen
  473.  *              .videomem - Zeiger zum Bildschirm-Zwischenspeicher
  474.  *              .boxatrib - Vordergrund- u. Hintergundfarben des Rahmens
  475.  *              .hlfatrib - Farben der Hilfezeile
  476.  *            leben.atrib - Farben der lebendigen Zellen
  477.  *              tod.atrib - Farben der toten Zellen
  478.  */
  479. void init_life()
  480. {
  481.    struct videoconfig vc;
  482.  
  483.    /* Anfangs-Cursorposition speichern, und auf Blockform einrichten, dann
  484.     * Cursor ausschalten.
  485.     */
  486.    cfg.cursor = _settextcursor( SETCURSOR( 0, 7 ) );
  487.    _displaycursor( _GCURSOROFF );
  488.  
  489.    /* Konfiguration holen, und Variablen dem Adapter entsprechend einrichten. */
  490.    _getvideoconfig( &vc );
  491.    switch( vc.adapter )
  492.    {
  493.       case _CGA:
  494.          cfg.rescan = WAHR;
  495.          break;
  496.       case _EGA:
  497.       case _OEGA:
  498.      cfg.mzeile = 39;           /* 43-zeilig - 4 Zeilen */
  499.          break;
  500.       case _VGA:
  501.       case _OVGA:
  502.      cfg.mzeile = 46;           /* 50-zeilig - 4 Zeilen */
  503.          break;
  504.       default:
  505.      cfg.mzeile = 21;           /* 25-zeilig - 4 Zeilen */
  506.          break;
  507.    }
  508.  
  509.    /* Variablen dem Vidomodus entsprechend einrichten. */
  510.    switch( vc.mode )
  511.    {
  512.  
  513.       case _HERCMONO:
  514.       case _ERESNOCOLOR:
  515.       case _TEXTMONO:
  516.      _setvideomoderows( _TEXTMONO, cfg.mzeile + 4 );
  517.          break;
  518.  
  519.       case _TEXTBW40:
  520.       case _TEXTBW80:
  521.          cfg.videomem = (int far *)0xb8000000;
  522.      _setvideomoderows( _TEXTBW80, cfg.mzeile + 4 );
  523.          break;
  524.  
  525.       default:
  526.          cfg.videomem = (int far *)0xb8000000;
  527.          cfg.boxatrib = SETATRIB( _TBRIGHTWHITE, _TBLUE );
  528.          leben.atrib = tod.atrib = SETATRIB( _TWHITE, _TBLUE );
  529.      cfg.hlfatrib = SETATRIB( _TWHITE, _TBLACK );
  530.      _setvideomoderows( _TEXTC80, cfg.mzeile + 4 );
  531.          break;
  532.    }
  533. }
  534.  
  535. /* init_pfr - Tote Zellen des Bildschirm-Zwischenspeichers initialisieren.
  536.  *
  537.  * Parameter: Keine
  538.  *
  539.  * Ergebnis:  Keines
  540.  *
  541.  * Benutzt:   bildpfr, cfg
  542.  */
  543. void init_pfr()
  544. {
  545.    register ZELLE *p = bildpfr;
  546.  
  547.    while( p < bildpfr + BBUFSIZE )
  548.       *p++ = tod;
  549. }
  550.  
  551.  
  552. /* zeichne_box - ASCII-Grafikzeichen um den Rahmen des Bildschirm-Zwischen-
  553.  *      speichers zu zeichnen, dann den geänderten Zwischenspeicher zum
  554.  *      Bildschirm schreiben.
  555.  *
  556.  * Parameter: Keine
  557.  *
  558.  * Ergebnis:  Keines
  559.  *
  560.  * Benutzt:   bildpfr, cfg
  561.  */
  562. void zeichne_box()
  563. {
  564.    register unsigned char *p = (char *)bildpfr;  /* Zeiger zum Zw-Speicher */
  565.    int i, incr;
  566.  
  567.    /* Oberen Rahmen zeichnen. */
  568.    *p = '┌';
  569.    p +=2;
  570.    for( i = 0; i < cfg.msplt; p += 2, i++ )
  571.       *p = '─';
  572.    *p = '┐';
  573.    p += 2;
  574.  
  575.    /* Seitenrahmen zeichnen. */
  576.    incr = (BSPLT - 1) * 2;
  577.    for( i = 0; i < cfg.mzeile; p += (BSPLT * 2), i++ )
  578.    {
  579.       *p = '│';
  580.       *(p + incr) = '│';
  581.    }
  582.  
  583.    /* Untere Rahmenlinie zeichnen.  */
  584.    *p = '└';
  585.    p += 2;
  586.    for( i = 0; i < cfg.msplt; p += 2, i++)
  587.       *p = '─';
  588.    *p = '┘';
  589.  
  590.    /* Geänderten Bildschirm-Zwischenspeicher zum Videospeicher kopieren. */
  591.    auffrischen( bildpfr, cfg.videomem );
  592. }
  593.  
  594. /* init_mats - Initialisiert Matrizen von Life. Löscht Matrix 1 und 2,
  595.  *                   initialisiert dann alle Zonen (1-9) von Matrix 1.
  596.  *
  597.  * Die "Zonen" dienen dem LIFE-Algorithmus beim Bestimmen der Methode zum
  598.  * Berechnen der Nachbarn. Zonen sind wichtig für Ränder u. Ecken:
  599.  *
  600.  *   +-+--------------+-+
  601.  *   |6|      2       |7|
  602.  *   +-+--------------+-+
  603.  *   | |              | |
  604.  *   |4|      1       |5|
  605.  *   | |              | |
  606.  *   +-+--------------+-+
  607.  *   |8|      3       |9|
  608.  *   +-+--------------+-+
  609.  *
  610.  * Zur leichteren Berechnung sind Zonen in Matrix 1 aufgezeichnet. Lebt eine
  611.  * Zelle, wird ihre Existenz durch Hinzufügen von 100 gekennzeichnet.
  612.  *
  613.  * Parameter: Keine
  614.  *
  615.  * Ergebnis:  Keines
  616.  *
  617.  * Benutzt:   bildpfr, cfg
  618.  */
  619. void init_mats()
  620. {
  621.    int i, j;                  /* Schleifenzähler         */
  622.    char *p = (char *)mat1;    /* Zeiger zu Matrix 1 */
  623.  
  624.    /* Zonen Matrix 1 mit 0 initialisieren. */
  625.    memset( mat1, 0, cfg.mzeile * cfg.msplt );
  626.    memset( mat2, 0, cfg.mzeile * cfg.msplt );
  627.  
  628.    /* Reihe 1 zu Zonen 6, 2 und 7 initialisieren. */
  629.    *p++ = 6;
  630.    for( i = 0; i < (cfg.msplt - 2); i++)
  631.       *p++ = 2;
  632.    *p++ = 7;
  633.  
  634.    /* Mittelzeilen 1 zu Zonen 4, 1 und 5 initialisieren. */
  635.    for( j = 0; j < (cfg.mzeile - 2); j++ )
  636.    {
  637.       *p++ = 4;
  638.       for( i = 0; i < (cfg.msplt - 2); i++ )
  639.          *p++ = 1;
  640.       *p++ = 5;
  641.    }
  642.  
  643.    /* Letzte Zeilen zu Zonen 8, 3 und 9 initialisieren. */
  644.    *p++ = 8;
  645.    for( i = 0; i < (cfg.msplt - 2); i++ )
  646.       *p++ = 3;
  647.    *p++ = 9;
  648. }
  649.  
  650. /* zufall_vt - Initialisiert die Zufallsverteilung der Zellen. Die Zellen
  651.  * werden in Matrix 1 wie auch im Bildschirm-Zwischenspeicher mitgeschrieben.
  652.  * Hat eine Zelle einen Zufallswert größer als die berechnete Verteilung, wird
  653.  * ihrem Wert in Matrix 1 100 hinzugefügt, und sie wird zum Bildschirm-
  654.  * Zwischenspeicher geschrieben.
  655.  *
  656.  * Parameter:    chance - Prozent an Zufallseigenschaft
  657.  *
  658.  * Ergebniswert: Keiner
  659.  *
  660.  * Benutzt:      bildpfr, cfg
  661.  */
  662. void zufall_vt( float chance )
  663. {
  664.    char   *p = (char *)mat1;     /* Zeiger zu Matrix 1      */
  665.    register ZELLE *bp = bildpfr; /* Zeiger zu Bildschirm-Zwischenspeicher */
  666.    int    i, j;                  /* Schleifenzähler            */
  667.    int    amt, rnd;
  668.  
  669.    amt = (int)(chance * 32768);     /* Mindestwert, den Lebend-Zelle
  670.                      * überschreiten muß.
  671.                      */
  672.    srand( (unsigned)time( NULL ) ); /* Anfangswert zu Zufallswerten machen   */
  673.    bp += BSPLT + 1;                 /* Start an erster Zelle, die nicht im Rahmen liegt */
  674.  
  675.    /* Jeder Zellen Status 'lebend' oder 'tot' zuweisen. */
  676.    for( i = 0; i < cfg.mzeile; i++, bp += 2 )
  677.    {
  678.       for( j = 0; j < cfg.msplt; j++, p++, bp++ )
  679.       {
  680.          rnd = rand();
  681.          if( rnd < amt )
  682.          {
  683.             *p += 100;
  684.             *bp = leben;
  685.          }
  686.       }
  687.    }
  688.  
  689.    /* Ergebnis am Bildschirm ausgeben. */
  690.    auffrischen( bildpfr, cfg.videomem );
  691. }
  692.  
  693. #define NW  (-1-cfg.msplt)      /*  Richtungskonstanten innerhalb Matrix 2. */
  694. #define N   (-cfg.msplt)        /*  Beispiel: NW (Nord-West) bedeutet den */
  695. #define NO  (1-cfg.msplt)       /*  links darüberliegenden Nachbarn. */
  696. #define O   (1)
  697. #define SO  (1+cfg.msplt)
  698. #define S   (cfg.msplt)
  699. #define SW  (-1+cfg.msplt)
  700. #define W   (-1)
  701.  
  702. /* generation - Eine Generation von Life durchführen. Zuerst wird Matrix 2
  703.  * gelöscht, dann Matrix 1 abgetastet. Wird auf lebendige Zelle gestoßen,
  704.  * werden die ENTSPRECHENDEN NACHBARZELLEN IN MATRIX 2 um 1 erhöht, die
  705.  * entsprechende Zelle darin selbst um 100. Bei toder Zelle nichts unter-
  706.  * nehemen. Dies ist eine schnelle Art der 'Nachbarzählung', die in Matrix 2
  707.  * mitverfolgt wird.
  708.  *
  709.  * Die "Zone" einer jeden Zelle wird geprüft und als Richtline zur Nachbar-
  710.  * Bestimmung benutzt. Nachbarn der obersten Reihe liegen in der untersten
  711.  * Reihe, d.h., Bildschirmgrenzen sind überwunden, indem eine jede Grenze
  712.  * gleichzeitig zum Anfang auf der gegenüberliegenden Seite wird.
  713.  *
  714.  * Durchlauf 2 wird aufgerufen, um zu bestimmen, welche Zellen tatsächlich
  715.  * leben oder sterben, je nach der Anzahl ihrer Nachbarn.
  716.  *
  717.  * Parameter: Keine
  718.  *
  719.  * Ergebnis:  Keines
  720.  *
  721.  * Benutzt:   bildpfr, cfg
  722.  */
  723. void generation()
  724. {
  725.    register  char *p1;   /* Zeiger zu Matrix 1 und 2            */
  726.    register  char *p2;
  727.    int    diff;          /* Bytes zwischen Matrix 1 und 2       */
  728.    int    zone;          /* Zone einer jeden Zelle              */
  729.    int    mgroesse = MBUFSIZE;
  730.  
  731.    /* Matrix 2 löschen, und Abstand zwischen Zone 1 und 2 berechnen. */
  732.    memset( mat2, 0, mgroesse );
  733.    diff = (char *)mat2 - (char *)mat1;
  734.  
  735.    /* Für jede Zelle ... */
  736.    for( p1 = (char *)mat1; p1 < (char *)mat1 + mgroesse; p1++ )
  737.    {
  738.       /* Wenn Zelle in Matrix 1 lebendig ist ... */
  739.       if( *p1 > 100 )
  740.       {
  741.          /* Zu Zelle in Matrix 2 deuten und sie mit neuem Wert versehen. */
  742.          p2 = p1 + diff;
  743.          *p2 += 100;
  744.  
  745.          /* Zone holen und Nachbarn entsprechend fortschreiben. */
  746.          zone = (*p1 - 100);
  747.          switch( zone )
  748.          {
  749.             case 1:
  750.                ++*(p2 + NW);
  751.                ++*(p2 + N);
  752.            ++*(p2 + NO);
  753.            ++*(p2 + O);
  754.            ++*(p2 + SO);
  755.                ++*(p2 + S);
  756.                ++*(p2 + SW);
  757.                ++*(p2 + W);
  758.                break;
  759.             case 2:
  760.                ++*(p2 + NW + mgroesse);
  761.                ++*(p2 + N + mgroesse);
  762.            ++*(p2 + NO + mgroesse);
  763.            ++*(p2 + O);
  764.            ++*(p2 + SO);
  765.                ++*(p2 + S);
  766.                ++*(p2 + SW);
  767.                ++*(p2 + W);
  768.                break;
  769.             case 3:
  770.                ++*(p2 + NW);
  771.                ++*(p2 + N);
  772.            ++*(p2 + NO);
  773.            ++*(p2 + O);
  774.            ++*(p2 + SO - mgroesse);
  775.                ++*(p2 + S - mgroesse);
  776.                ++*(p2 + SW - mgroesse);
  777.                ++*(p2 + W);
  778.                break;
  779.             case 4:
  780.            ++*(p2 + NW + cfg.msplt);
  781.                ++*(p2 + N);
  782.            ++*(p2 + NO);
  783.            ++*(p2 + O);
  784.            ++*(p2 + SO);
  785.                ++*(p2 + S);
  786.            ++*(p2 + SW + cfg.msplt);
  787.            ++*(p2 + W + cfg.msplt);
  788.                break;
  789.             case 5:
  790.                ++*(p2 + NW);
  791.                ++*(p2 + N);
  792.            ++*(p2 + NO - cfg.msplt);
  793.            ++*(p2 + O - cfg.msplt);
  794.            ++*(p2 + SO - cfg.msplt);
  795.                ++*(p2 + S);
  796.                ++*(p2 + SW);
  797.                ++*(p2 + W);
  798.                break;
  799.             case 6:
  800.            ++*(p2 + NW + mgroesse + cfg.msplt);
  801.                ++*(p2 + N + mgroesse);
  802.            ++*(p2 + NO + mgroesse);
  803.            ++*(p2 + O);
  804.            ++*(p2 + SO);
  805.                ++*(p2 + S);
  806.            ++*(p2 + SW + cfg.msplt);
  807.            ++*(p2 + W + cfg.msplt);
  808.                break;
  809.             case 7:
  810.                ++*(p2 + NW + mgroesse);
  811.                ++*(p2 + N + mgroesse);
  812.            ++*(p2 + NO + mgroesse - cfg.msplt);
  813.            ++*(p2 + O - cfg.msplt);
  814.            ++*(p2 + SO - cfg.msplt);
  815.                ++*(p2 + S);
  816.                ++*(p2 + SW);
  817.                ++*(p2 + W);
  818.                break;
  819.             case 8:
  820.            ++*(p2 + NW + cfg.msplt);
  821.                ++*(p2 + N);
  822.            ++*(p2 + NO);
  823.            ++*(p2 + O);
  824.            ++*(p2 + SO - mgroesse);
  825.                ++*(p2 + S - mgroesse);
  826.            ++*(p2 + SW + cfg.msplt - mgroesse);
  827.            ++*(p2 + W + cfg.msplt);
  828.                break;
  829.             case 9:
  830.                ++*(p2 + NW);
  831.                ++*(p2 + N);
  832.            ++*(p2 + NO - cfg.msplt);
  833.            ++*(p2 + O - cfg.msplt);
  834.            ++*(p2 + SO - mgroesse - cfg.msplt);
  835.                ++*(p2 + S - mgroesse);
  836.                ++*(p2 + SW - mgroesse);
  837.                ++*(p2 + W);
  838.                break;
  839.             default:
  840.                break;
  841.          }
  842.       } /* Ende if */
  843.    } /* Ende for */
  844.  
  845.   /* pass2 zum Berechnen von 'Geburt' bzw. 'Tod' einer jeden Zellen aufrufen. */
  846.    pass2();
  847. }
  848.  
  849. /* pass2 - Matrix 2 absuchen, und Matrix 1 nach folgenden Kriterien anpassen:
  850.  *
  851.  *   Wert Matrix 2        Ergebnis Matrix 1
  852.  *   --------------       ----------------------
  853.  *        3               Tote Zelle wird lebendig
  854.  *        102, 103        Keine Änderung
  855.  *        other > 100     Lebende Zelle 'stirbt'
  856.  *        other < 100     Keine Änderung
  857.  *
  858.  * Parameter: Keine
  859.  *
  860.  * Ergebnis:  Keines
  861.  *
  862.  * Benutzt:   bildpfr, cfg
  863.  */
  864. void pass2()
  865. {
  866.    register int i, j;               /* Schleifen-Variablen                */
  867.    register char *p2= (char *)mat2; /* Zeiger zu Matrix 2                 */
  868.    ZELLE    *bp = bildpfr;          /* Zeiger Bildschirm-Zwischenspeicher */
  869.    int      diff;                   /* Abstand zwischen Matrix 1 und 2    */
  870.  
  871.    /* Rahmen zur ersten Zelle überspringen und Abstand zwischen
  872.     * Matrix 1 und 2 berechnen
  873.     */
  874.    bp += BSPLT + 1;
  875.    diff = (char *)mat2 - (char *)mat1;
  876.  
  877.    /* Äußere Schleife zählt Zeilen. */
  878.    for( i = 0; i < cfg.mzeile; i++, bp += 2 )
  879.    {
  880.       /* Nächste Schleife zählt Spalten. */
  881.       for( j = 0; j < cfg.msplt; j++, p2++, bp++ )
  882.       {
  883.          /* Wenn 3, lebende Zelle schreiben. */
  884.          if( *p2 < 100 )
  885.          {
  886.             if( *p2 == 3 )
  887.             {
  888.                *(p2 - diff) += 100;
  889.                *bp = leben;
  890.             }
  891.          }
  892.          else
  893.          /* Tote Zelle, wenn über 100 - außer 102 oder 103. */
  894.          {
  895.             if( (*p2 < 102) || (*p2 > 103) )
  896.             {
  897.                *(p2 - diff) -= 100;
  898.                *bp = tod;
  899.             }
  900.          }
  901.       }
  902.    }
  903.  
  904.    /* Ergebnis zum Bildschirm ausgeben. */
  905.    auffrischen( bildpfr, cfg.videomem );
  906. }
  907.  
  908. /* status_zelle - Status einer bestimmten Zelle einrichten. Die Zelle kann
  909.  * ein-, aus- oder umgeschaltet werden. Der Status wird in Matrix 1 und im
  910.  * Bildschirm-Zwischenspeicher mitverändert.
  911.  *
  912.  * Parameter: aktion - AUS, EIN oder UMGESCH
  913.  *            zle
  914.  *            spl
  915.  *
  916.  * Ergebnis:  Keines
  917.  *
  918.  * Benutzt:   bildpfr, cfg
  919.  */
  920. void status_zelle( int aktion, int zle, int spl )
  921. {
  922.    register ZELLE *sp = bildpfr;
  923.  
  924.    /* Rahmen überspringen zur ersten Zelle. */
  925.    sp += BSPLT + 1;
  926.    sp += zle * BSPLT;
  927.    sp += spl;
  928.  
  929.    /* Zellenstatus einrichten. */
  930.    switch( aktion )
  931.    {
  932.       case AUS:
  933.      mat1[zle][spl] -= 100;
  934.          *sp = tod;
  935.          break;
  936.       case EIN:
  937.      mat1[zle][spl] += 100;
  938.          *sp = leben;
  939.          break;
  940.       case UMGESCH:
  941.      if( mat1[zle][spl] > 100 )
  942.          {
  943.         mat1[zle][spl] -= 100;
  944.             *sp = tod;
  945.          }
  946.          else
  947.          {
  948.         mat1[zle][spl] += 100;
  949.             *sp = leben;
  950.          }
  951.          break;
  952.    }
  953.  
  954.    /* Ergebnis am Bildschirm ausgeben. */
  955.    auffrischen( bildpfr, cfg.videomem );
  956. }
  957.  
  958. /* zeige_prompt - Ausgabe eines Dialoghinweises unten auf dem Bildschirm.
  959.  * Wird ein Zwischenspeicher ungleich Null als Antwort übergeben, wird darauf
  960.  * gewartet, daß der Benutzer eine Zeichenfolge eingibt.
  961.  *
  962.  * Parameter: prompt - Dialoghinweis oder Hilfe-Zeichenfolge
  963.  *            antwort - Zwischenspeicher für Antwort-Zeichenfolge
  964.  *                       (NULL = keine Antwort)
  965.  *
  966.  * Ergibt:    WAHR, wenn keine Antwort erwartet oder richtige Antwort
  967.  *                       eingegangen ist; bei ungültiger Antwort FALSCH.
  968.  */
  969. int zeige_prompt( char *prompt, char antwort[] )
  970. {
  971.    char tmp[159];
  972.  
  973.    /* Alten Dialoghinweis löschen und neuen schreiben. */
  974.    memset( tmp, ' ', 158 );
  975.    tmp[158] = 0;
  976.    _settextcolor( cfg.hlfatrib & 0x00ff );
  977.    _setbkcolor( (long)(cfg.hlfatrib >> 4) );
  978.    _settextposition( BREIH + 1, 1 );
  979.    _outtext( tmp );
  980.    _settextposition( BREIH + 1, 1 );
  981.    _outtext( prompt );
  982.  
  983.    /* Wurde Antwort-Zwischenspeicher übergeben, Antwort dafür holen. */
  984.    if ( *antwort )
  985.    {
  986.       _displaycursor( _GCURSORON );
  987.       tmp[0] = 158;
  988.       cgets( tmp );
  989.       strcpy( antwort, tmp + 2 );
  990.  
  991.       _displaycursor( _GCURSOROFF );
  992.       if( *antwort )
  993.          return WAHR;
  994.       else
  995.          return FALSCH;
  996.    }
  997.    else
  998.       return WAHR;
  999. }
  1000.  
  1001. /* lese_life - Liest ein Life-Muster von Datei ein. Das Muster besteht aus
  1002.  * Bitcodes: Bit geladen = Leben, Bit gelöscht = Tod. Enthält die Datei mehr
  1003.  * Zellen, als der Bildschirm, werden die Extra-Zellen ignoriert. Dies kann
  1004.  * vorkommen, wenn ein Muster in 25-Zeilenmodus gespeichert wurde, aber in
  1005.  * 43-Zeilenmodus eingelesen wird.
  1006.  *
  1007.  * Parameter: Keine
  1008.  *
  1009.  * Ergibt:    WAHR wenn erfolgreich, sonst FALSCH
  1010.  *
  1011.  * Benutzt:   bildpfr, cfg
  1012.  */
  1013. int lese_life()
  1014. {
  1015.    ZELLE *scrnp = bildpfr;     /* Zeiger zum Bildschirm-Zwischenspeicher   */
  1016.    char *matp = (char *)mat1;  /* Zeiger zu Matrix 1                       */
  1017.    unsigned char mbyte, fbyte; /* Masken-Byte und Lese-Byte                */
  1018.    int  i, j, k;               /* Schleifenzähler                          */
  1019.    int  mehr = WAHR;           /* Fortsetzungs-Kennung                     */
  1020.    FILE *filep;                /* Zeiger zu file struct                    */
  1021.    char fname[81];             /* Dateinamen-Zwischenspeicher              */
  1022.  
  1023.    /* FALSCH ergeben, wenn Dialoghinweis oder Öffnen der Datei fehlschlägt. */
  1024.    if( !zeige_prompt( file_str, fname ) )
  1025.       return FALSCH;
  1026.    if( !(filep = fopen( fname, "rb" )) )
  1027.       return FALSCH;
  1028.  
  1029.    /* Zwischenspeicher, Bildschirm und Zeiger initialisieren. */
  1030.    init_pfr();
  1031.    init_mats();
  1032.    zeichne_box();
  1033.    scrnp += BSPLT + 1;
  1034.  
  1035.    /* Masken-Byte initialisieren und erstes Byte lesen. */
  1036.    mbyte = 0x80;
  1037.    fread( (void *)&fbyte, 1, 1, filep );
  1038.  
  1039.    /* Zeilen zählen ... */
  1040.    for( i = 0, k = 0; (i < cfg.mzeile) && mehr; i++, scrnp += 2 )
  1041.    {
  1042.       /* Spalten zählen ... */
  1043.       for( j = 0; (j < cfg.msplt) && mehr; j++, scrnp++, matp++)
  1044.       {
  1045.          /* Ist Bit 'ein', die Zelle als lebendig kennzeichnen. */
  1046.          if( mbyte & fbyte )
  1047.          {
  1048.             *matp += 100;
  1049.             *scrnp = leben;
  1050.          }
  1051.  
  1052.          /* Maske anpassen und nötigenfalls weiteres Byte einlesen. */
  1053.          mbyte >>= 1;
  1054.          if( ++k > 7 )
  1055.          {
  1056.             k = 0;
  1057.             mbyte = 0x80;
  1058.  
  1059.             /* Beenden, wenn Datei keine weiteren Bytes enthält. */
  1060.             if( !fread( (void *)&fbyte, 1, 1, filep ) )
  1061.            mehr = FALSCH;
  1062.          }
  1063.       }
  1064.    }
  1065.  
  1066.    /* Am Bildschirm anzeigen, und Datei schließen. */
  1067.    auffrischen( bildpfr, cfg.videomem );
  1068.    fclose( filep );
  1069.    return WAHR;
  1070. }
  1071.  
  1072. /* schrbe_life - Schreibt ein Life-Muster zur Datei. Das Muster besteht aus
  1073.  * Bitcodes: Bit geladen = Leben, Bit gelöscht = Tod.
  1074.  *
  1075.  * Parameter: Keine
  1076.  *
  1077.  * Ergibt:    WAHR wenn erfolgreich, sonst FALSCH
  1078.  *
  1079.  * Benutzt:   bildpfr, cfg
  1080.  */
  1081. int schrbe_life()
  1082. {
  1083.    char *matp = (char *)mat1;  /* Zeiger zu Matrix 1                       */
  1084.    unsigned char mbyte, fbyte; /* Masken-Byte und Lese-Byte                */
  1085.    int  i, j, k;               /* Schleifenzähler                          */
  1086.    FILE *filep;                /* Zeiger zu file struct                    */
  1087.    char fname[81];             /* Dateinamen-Zwischenspeicher              */
  1088.  
  1089.    /* FALSCH ergeben, wenn Dialoghinweis oder Öffnen der Datei fehlschlägt. */
  1090.    if( !zeige_prompt( file_str, fname ) )
  1091.       return FALSCH;
  1092.    if( !(filep = fopen( fname, "wb" )) )
  1093.       return FALSCH;
  1094.  
  1095.    /* Masken-Byte initialisieren und erstes Byte lesen. */
  1096.    mbyte = 0x80;
  1097.    fbyte = k = 0;
  1098.  
  1099.    /* Zeilen zählen ... */
  1100.    for( i = 0; i < cfg.mzeile; i++)
  1101.    {
  1102.       /* Spalten zählen ... */
  1103.       for( j = 0; j < cfg.msplt; j++, matp++)
  1104.       {
  1105.          /* Ist Zelle lebendig, Bit einschalten. */
  1106.          if( *matp > 100 )
  1107.             fbyte += mbyte;
  1108.  
  1109.      /* Maske anpassen, und nötigenfalls weiteres Byte schreiben. */
  1110.          mbyte >>= 1;
  1111.          if( ++k > 7 )
  1112.          {
  1113.             fwrite( (void *)&fbyte, 1, 1, filep );
  1114.             fbyte = k = 0;
  1115.             mbyte = 0x80;
  1116.          }
  1117.       }
  1118.    }
  1119.  
  1120.    /* Sicherstellen, daß alle bis einschl. des letzten Bytes geschrieben
  1121.     * sind, dann Datei schließen.
  1122.     */
  1123.    if( k > 0 )
  1124.       fwrite( (void *)&fbyte, 1, 1, filep );
  1125.    fclose( filep );
  1126.    return WAHR;
  1127. }
  1128.  
  1129. /* auffrischen - Schreibt Zwischenspeicher mit den Zellen zum eigentlichen
  1130.  *      Video-Zwischenspeicher. Für CGA beim Kopieren auf rescan anpassen,
  1131.  *      sonst direkt kopieren. Die CGA-Variante funktioniert nur in Assembler
  1132.  *      schnell genug.
  1133.  *
  1134.  * Parameter: einpuffer - interner Zwischenspeicher mit den Zellen
  1135.  *            auspuffer - Zeiger zum Hardware-Videospeicher
  1136.  *
  1137.  * Ergebnis:  Keines
  1138.  *
  1139.  * Benutzt:   cfg
  1140.  */
  1141. void auffrischen( ZELLE einpuffer[], int far *auspuffer )
  1142. {
  1143.    int bgroesse = BBUFSIZE;
  1144.  
  1145.    _asm \
  1146.    {
  1147.       mov  si, einpuffer  ; Quelle laden = Bildschirmspeicher
  1148.       les  di, auspuffer  ; Ziel laden   = Videospeicher
  1149.       mov  cx, bgroesse   ; Zeilen * Spalten
  1150.       cld                 ; DF = 0 (Richtungskennung)
  1151.  
  1152.       cmp  cfg.rescan, FALSCH  ; Rescan nur prüfen, falls CGA
  1153.       je   nichtcga
  1154.  
  1155.       mov  dx, 03DAh
  1156. warten0:
  1157.       sti
  1158.       nop
  1159.       cli
  1160.       lodsw               ; Zeichen laden und in BX speichern
  1161.       mov  bx, ax
  1162.  
  1163. warten1:  in   al, dx     ; Warten, bis Horizontale aktiv ist
  1164.       shr  al, 1
  1165.       jc   warten1
  1166.       cli
  1167.  
  1168. warten2:  in   al, dx     ; Warten, bis Horizontale nicht aktiv ist (retrace)
  1169.       shr  al, 1
  1170.       jnc  warten2
  1171.  
  1172.       mov  ax, bx         ; Zeichen wiederherstellen und
  1173.       stosw               ;   zu Videospeicher verlegen
  1174.       sti
  1175.  
  1176.       loop warten0        ; Nächstes
  1177.       jmp SHORT getout    ; Fertig mit CGA
  1178.  
  1179. nichtcga:                 ; Wenn nicht CGA,
  1180.       rep  movsw          ; ganzen Bildschirm ohne Warten kopieren
  1181. getout:
  1182.  
  1183.    }
  1184. }
  1185.