Beiträge durchsuchen

WPF: Retro-Display mit Memory-Bitmap darstellen

Vor einiger Zeit wollte ich einen „altmodischen“ Retro-Bildschirm darstellen, um meinem 6502 Emulator ein wenig mehr Authentizität zu verleihen. Damit es keine Darstellungsprobleme auf 4k-Displays gibt, sollte außerdem die „Resolution independence“ eingehalten werden, um eine zeitgemäße Wiedergabe der Anzeige zu ermöglichen.

Die Wahl fiel daher auf das „Windows Presentation Framework“ (WPF) und damit auch auf „C#“ als Programmiersprache. WPF arbeitet direkt mit „Direct-X“ zusammen, und hat sich u. A. zum Ziel gesetzt, auf allen Arten von Displays das beste visuelle Ergebnis zu erzielen.

Der Retro-Bildschirm sollte in jeden Fall mit einer „Memory-Bitmap“ dargestellt werden, da die früheren Computer allesamt keine Grafikkarte im heutigen Sinn hatten. Etwas RAM wurde für die Darstellung abgezweigt, und eine clevere digital/analoge Schaltung hat den RAM-Inhalt dann entweder nur als Text oder als Pixelgrafik auf die Bildröhre gebracht.

Für eine halbwegs realistische Simulation sollten außerdem „Scan-Lines“ zu sehen sein. Früher war die Auflösung des Computerbildes nicht so hoch, das der Elektronenstrahl die Bildfläche vollständig überstrichen hat, es blieben kleine Abstände zwischen den Zeilen.

(Quelle: Wikipedia, „Raster scan“)

Diesen Effekt habe ich durch freilassen jeder zweiten Pixelzeile in der Bitmap erreicht.

Weiterhin wollte ich eine 40/80 Zeichen-Umschaltung realisieren, da es ab Mitte der 80er Jahre einige Rechner (z.B. vom Commodore und Apple) gab, die diese Möglichkeit geboten haben. IBM´s PCs lasse ich jetzt mal außen vor, das waren eigentlich keine „Heim-Computer“ mehr.

Jetzt ging es an die Programmierung des Displays.

Die Memory-Bitmap soll zunächst nur eine monochrome Darstellung bringen (wirklich „retro“), was nicht zwangsweise „schwarz/weiß“ bedeutet. „Schwarz/Grün“ oder „Grau/Bernstein“ sind auch mit einem Bit pro Pixel darstellbar, es muss nur jeweils die Farbe für „0“ und „1“ definiert werden. Damit steht fest, dass die Bitmap als „int“-Array eingerichtet wird, wobei ein Byte jeweils 8 Pixel darstellt.

Der Zeichensatz wurde „damals“ bei Apple ][ und Commodore CBM/PET ausnahmslos mit acht Bytes pro Zeichen realisiert, also eine 8×8 Pixel Matrix. In unserem Beispiel verwenden wir den „Zeichengenerator“ von Commodore, wie er in den ersten PET/CBM Maschinen eingesetzt wurde.

Der Zeichensatz hatte damals nicht unbedingt eine Anordnung wie im ASCII-Code, vielmehr waren die Zeichen (Alphabet/Grafiksymbole/Kleinbuchstaben) mehr oder weniger gemischt vorhanden. Hier hilft uns später ein „Software-Mapping“ von erwünschten Zeichen auf Adressen von Bitmustern im Zeichengenerator.

Zur Darstellung der Bitmap noch ein paar Worte: Es gibt unter allen Grafiksystemen ein kleines „Problem“, das betrifft Direct-X genau wie OpenGL. Wenn wir die Bitmap mit dem Filter „Bilinear“ oder „Trilinear“ darstellen („Bilinear“ ist unter WPF Standard), dann sieht die eigentliche Fläche der Bitmap schön geglättet aus aber der Rand ist leider nicht geglättet. Das sieht man bei vielen Grafikprogrammen, die auf Direct-X oder OpenGL basieren. Die Kanten sehen sehr unschön aus, obwohl die restlichen Flächen mit Filtern geglättet sind. Hier hilft ein ganz einfacher Trick: Die Bitmap wird etwas größer gewählt (also mit „Rand“) und dieser Rand wird einfach leer (oder transparent) gelassen. Kostet etwas mehr Speicher, aber dafür sieht es viel besser aus 🙂

Bevor wir nun zur Tat schreiten, noch ein Hinweis: Die Bedienung von Visual Studio wird als bekannt vorausgesetzt.

Jetzt kann es losgehen:

Mit Visual-Studio (2015 in meinem Fall) erstellen wir unter „C#/Windows“ eine neue WPF-Anwendung und speichern das Projekt unter einem Namen unserer Wahl an geeigneter Stelle.

Das Retro-Display soll praktischerweise als Benutzer-Steuerelement ausgeführt werden, damit wir es später noch in anderen Projekten einsetzen können. Also noch ein WPF-Benutzer-Steuerelement (WPF) hinzugefügt, ich habe es „CbmCrt“ genannt.

Im Hauptfenster setzten wir das Steuerelement mit dem Designer ein, und reduzieren den XML-Code auf das Nötigste:

<Window x:Class="CBM_Video_Screen.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CBM_Video_Screen"
        mc:Ignorable="d"
        Title="Retro-Display" 
        WindowStartupLocation="CenterScreen" 
        Background="Black" Height="540" Width="800">
    <local:CbmCrt x:Name="display" ScanLines="True"/>
</Window>

Das Augenmerk liegt hier auf der Zeile

<local:CbmCrt x:Name="display"/>

die das Display-Objekt definiert.

Damit sind wir mit dem Hauptfenster schon fertig.

Wenden wir uns nun der Datei „CbmCrt.xml“ zu. Der Einfachheit halber besteht unser Benutzer-Steuerelement nur aus einer eingebetteten Image-Klasse:

<UserControl x:Class="CBM_Video_Screen.CbmCrt"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:CBM_Video_Screen"
             mc:Ignorable="d" 
             d:DesignWidth="640" d:DesignHeight="400">
    <Image x:Name="image"/>
</UserControl>

Praktisch: Wenn wir im WPF keine Abstände oder sonstige Positionsangaben machen, werden die Elemente jeweils in voller Größe im Eltern-Element aufgezogen. Das ist in unserem Beispiel erwünscht.

Der Code in der Datei „CbmCrt.cs“ sieht wie so aus:

using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace CBM_Video_Screen
{
    public partial class CbmCrt : UserControl
    {
        private const ushort INVERSE = 0x100;

        private int charsWidth, charsHeight, charsAdvance;
        private int bitmapStride, bitmapWidth, bitmapHeight, bitmapStart;
        private int cursorX, cursorY;

        private bool wide, scan;
        private BitmapPalette palette;
        private Color[] colors;
        private byte[] pixels;
        private ushort[] text, screen;
        private ushort clearValue;

        public bool Wide
        {
            get { return wide; }
            set { wide = value; configure(); }
        }

        public bool ScanLines
        {
            get { return scan; }
            set { scan = value; configure(); }
        }

        private Color OffColor
        {
            get { return colors[0]; }
            set { colors[0] = value; palette = null; }
        }

        private Color OnColor
        {
            get { return colors[1]; }
            set { colors[1] = value; palette = null; }
        }

        public CbmCrt()
        {
            wide = scan = false;
            colors = new Color[2];
            colors[0] = Colors.Black;
            colors[1] = Colors.LightGreen;

            if (DesignerProperties.GetIsInDesignMode(this))
            {
                clearValue = 0;
            }
            else
            {
                clearValue = 32;
            }

            InitializeComponent();
            configure();
        }

        public void gotoXY(int x, int y)
        {
            cursorX = x;
            cursorY = y;
        }

        public void print(char ch, bool wrap, bool inverse)
        {
            ushort u = ch;
            if (inverse)
            {
                u |= INVERSE;
            }
            drawChar(u, cursorX++, cursorY);
            if (wrap)
            {
                if (cursorX >= charsWidth)
                {
                    cursorX = 0;
                    if (++cursorY >= charsHeight)
                    {
                        int n = charsWidth * (charsHeight - 1);
                        int s = charsWidth, d = 0;
                        while (n-- > 0)
                        {
                            text[d++] = text[s++];
                        }
                        n = charsWidth;
                        while (n-- > 0)
                        {
                            text[d++] = 32;
                        }
                        cursorY--;
                    }
                }
            }
        }

        public void clearScreen()
        {
            int n = text.Length;
            while (n-- > 0)
            {
                text[n] = clearValue;
            }
        }

        private void configure()
        {
            charsHeight = 25;
            charsWidth = wide ? 80 : 40;
            charsAdvance = scan ? 16 : 8;

            bitmapStride = charsWidth + (wide ? 4 : 2);
            bitmapWidth = bitmapStride * 8;
            bitmapHeight = (charsHeight + 2) * charsAdvance;
            bitmapStart = bitmapStride * charsAdvance + (wide ? 2 : 1);

            text = new ushort[charsWidth * charsHeight];
            screen = new ushort[charsWidth * charsHeight];
            pixels = new byte[bitmapStride * bitmapHeight];

            clearBitmap();
            clearScreen();
            update();
            gotoXY(0,0);
        }

        private void clearScreenBuffer()
        {
            int n = screen.Length;
            while (n-- > 0)
            {
                screen[n] = ushort.MaxValue;
            }
        }

        private void clearBitmap()
        {
            int n = pixels.Length;
            while (n-- > 0)
            {
                pixels[n] = 0;
            }
            clearScreenBuffer();
        }

        private void drawChar(ushort ch, int x, int y)
        {
            if (x < 0 || x >= charsWidth) return;
            if (y < 0 || y >= charsHeight) return;

            int i = y * charsWidth + x;
            text[i] = ch;
        }

        public void update()
        {
            if (palette == null)
            {
                palette = new BitmapPalette(colors);
                clearScreenBuffer();
            }
            int n = text.Length;
            while (n-- > 0)
            {
                if (text[n] != screen[n])
                {
                    ushort u = text[n];

                    int x = n % charsWidth;
                    int y = n / charsWidth;

                    int s = (u & 0xFF) * 8;
                    int d = bitmapStart + y * bitmapStride * charsAdvance + x;
                    int mask = (u & INVERSE) > 0 ? 0xFF : 0;
                    int b = 8;
                    while (b-- > 0)
                    {
                        pixels[d] = (byte)(charGenData[s++] ^ mask); d += bitmapStride;
                        if(scan)
                        {
                            pixels[d] = 0; d += bitmapStride;
                        }
                    }

                    screen[n] = text[n];
                }
            }
            image.Source = BitmapSource.Create(bitmapWidth, bitmapHeight, 
                                               wide ? 144 : 72, scan ? 144 : 72, 
                                               PixelFormats.Indexed1, palette, 
                                               pixels, bitmapStride);
        }

        private static byte[] charGenData =
        {
            0x1C, 0x22, 0x4A, 0x56, 0x4C, 0x20, 0x1E, 0x00, 0x18, 0x24, 0x42, 0x7E,
            0x42, 0x42, 0x42, 0x00, 0x7C, 0x22, 0x22, 0x3C, 0x22, 0x22, 0x7C, 0x00,
            0x1C, 0x22, 0x40, 0x40, 0x40, 0x22, 0x1C, 0x00, 0x78, 0x24, 0x22, 0x22,
            0x22, 0x24, 0x78, 0x00, 0x7E, 0x40, 0x40, 0x78, 0x40, 0x40, 0x7E, 0x00,
            0x7E, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x00, 0x1C, 0x22, 0x40, 0x4E,
            0x42, 0x22, 0x1C, 0x00, 0x42, 0x42, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x00,
            0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x0E, 0x04, 0x04, 0x04,
            0x04, 0x44, 0x38, 0x00, 0x42, 0x44, 0x48, 0x70, 0x48, 0x44, 0x42, 0x00,
            0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7E, 0x00, 0x42, 0x66, 0x5A, 0x5A,
            0x42, 0x42, 0x42, 0x00, 0x42, 0x62, 0x52, 0x4A, 0x46, 0x42, 0x42, 0x00,
            0x18, 0x24, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00, 0x7C, 0x42, 0x42, 0x7C,
            0x40, 0x40, 0x40, 0x00, 0x18, 0x24, 0x42, 0x42, 0x4A, 0x24, 0x1A, 0x00,
            0x7C, 0x42, 0x42, 0x7C, 0x48, 0x44, 0x42, 0x00, 0x3C, 0x42, 0x40, 0x3C,
            0x02, 0x42, 0x3C, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x00, 0x42, 0x42, 0x42, 0x24,
            0x24, 0x18, 0x18, 0x00, 0x42, 0x42, 0x42, 0x5A, 0x5A, 0x66, 0x42, 0x00,
            0x42, 0x42, 0x24, 0x18, 0x24, 0x42, 0x42, 0x00, 0x22, 0x22, 0x22, 0x1C,
            0x08, 0x08, 0x08, 0x00, 0x7E, 0x02, 0x04, 0x18, 0x20, 0x40, 0x7E, 0x00,
            0x3C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x00, 0x00, 0x40, 0x20, 0x10,
            0x08, 0x04, 0x02, 0x00, 0x3C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x3C, 0x00,
            0x00, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x10, 0x20,
            0x7F, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x08, 0x00, 0x24, 0x24, 0x24, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x7E, 0x24, 0x7E, 0x24, 0x24, 0x00,
            0x08, 0x1E, 0x28, 0x1C, 0x0A, 0x3C, 0x08, 0x00, 0x00, 0x62, 0x64, 0x08,
            0x10, 0x26, 0x46, 0x00, 0x30, 0x48, 0x48, 0x30, 0x4A, 0x44, 0x3A, 0x00,
            0x04, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x10, 0x10,
            0x10, 0x08, 0x04, 0x00, 0x20, 0x10, 0x08, 0x08, 0x08, 0x10, 0x20, 0x00,
            0x08, 0x2A, 0x1C, 0x3E, 0x1C, 0x2A, 0x08, 0x00, 0x00, 0x08, 0x08, 0x3E,
            0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10,
            0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x18, 0x18, 0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00,
            0x3C, 0x42, 0x46, 0x5A, 0x62, 0x42, 0x3C, 0x00, 0x08, 0x18, 0x28, 0x08,
            0x08, 0x08, 0x3E, 0x00, 0x3C, 0x42, 0x02, 0x0C, 0x30, 0x40, 0x7E, 0x00,
            0x3C, 0x42, 0x02, 0x1C, 0x02, 0x42, 0x3C, 0x00, 0x04, 0x0C, 0x14, 0x24,
            0x7E, 0x04, 0x04, 0x00, 0x7E, 0x40, 0x78, 0x04, 0x02, 0x44, 0x38, 0x00,
            0x1C, 0x20, 0x40, 0x7C, 0x42, 0x42, 0x3C, 0x00, 0x7E, 0x42, 0x04, 0x08,
            0x10, 0x10, 0x10, 0x00, 0x3C, 0x42, 0x42, 0x3C, 0x42, 0x42, 0x3C, 0x00,
            0x3C, 0x42, 0x42, 0x3E, 0x02, 0x04, 0x38, 0x00, 0x00, 0x00, 0x08, 0x00,
            0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10,
            0x0E, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0E, 0x00, 0x00, 0x00, 0x7E, 0x00,
            0x7E, 0x00, 0x00, 0x00, 0x70, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x70, 0x00,
            0x3C, 0x42, 0x02, 0x0C, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
            0xFF, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x3E, 0x7F, 0x7F, 0x1C, 0x3E, 0x00,
            0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0xFF,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0xFF, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
            0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00,
            0xE0, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x04, 0x03, 0x00, 0x00, 0x00,
            0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80,
            0x80, 0x80, 0x80, 0xFF, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01,
            0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0xFF, 0x80, 0x80, 0x80,
            0x80, 0x80, 0x80, 0x80, 0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
            0x00, 0x3C, 0x7E, 0x7E, 0x7E, 0x7E, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0xFF, 0x00, 0x36, 0x7F, 0x7F, 0x7F, 0x3E, 0x1C, 0x08, 0x00,
            0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00,
            0x03, 0x04, 0x08, 0x08, 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81,
            0x00, 0x3C, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x00, 0x08, 0x1C, 0x2A, 0x77,
            0x2A, 0x08, 0x08, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
            0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x1C, 0x08, 0x00, 0x08, 0x08, 0x08, 0x08,
            0xFF, 0x08, 0x08, 0x08, 0xA0, 0x50, 0xA0, 0x50, 0xA0, 0x50, 0xA0, 0x50,
            0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x01, 0x3E,
            0x54, 0x14, 0x14, 0x00, 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0,
            0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
            0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
            0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0x01, 0x01, 0x01, 0x01,
            0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x55, 0xAA, 0x55,
            0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, 0x03, 0x03, 0x03, 0x03,
            0x03, 0x03, 0x03, 0x03, 0x08, 0x08, 0x08, 0x08, 0x0F, 0x08, 0x08, 0x08,
            0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x08, 0x08, 0x08, 0x08,
            0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x08, 0x08, 0x08,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
            0x0F, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xFF, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0xFF, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
            0xF8, 0x08, 0x08, 0x08, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
            0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0x07, 0x07, 0x07, 0x07,
            0x07, 0x07, 0x07, 0x07, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0xFF, 0xFF, 0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF,
            0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F,
            0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0xF8, 0x00, 0x00, 0x00,
            0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0,
            0x0F, 0x0F, 0x0F, 0x0F, 0x1C, 0x22, 0x4A, 0x56, 0x4C, 0x20, 0x1E, 0x00,
            0x00, 0x00, 0x38, 0x04, 0x3C, 0x44, 0x3A, 0x00, 0x40, 0x40, 0x5C, 0x62,
            0x42, 0x62, 0x5C, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x40, 0x42, 0x3C, 0x00,
            0x02, 0x02, 0x3A, 0x46, 0x42, 0x46, 0x3A, 0x00, 0x00, 0x00, 0x3C, 0x42,
            0x7E, 0x40, 0x3C, 0x00, 0x0C, 0x12, 0x10, 0x7C, 0x10, 0x10, 0x10, 0x00,
            0x00, 0x00, 0x3A, 0x46, 0x46, 0x3A, 0x02, 0x3C, 0x40, 0x40, 0x5C, 0x62,
            0x42, 0x42, 0x42, 0x00, 0x08, 0x00, 0x18, 0x08, 0x08, 0x08, 0x1C, 0x00,
            0x04, 0x00, 0x0C, 0x04, 0x04, 0x04, 0x44, 0x38, 0x40, 0x40, 0x44, 0x48,
            0x50, 0x68, 0x44, 0x00, 0x18, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00,
            0x00, 0x00, 0x76, 0x49, 0x49, 0x49, 0x49, 0x00, 0x00, 0x00, 0x5C, 0x62,
            0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x42, 0x42, 0x3C, 0x00,
            0x00, 0x00, 0x5C, 0x62, 0x62, 0x5C, 0x40, 0x40, 0x00, 0x00, 0x3A, 0x46,
            0x46, 0x3A, 0x02, 0x02, 0x00, 0x00, 0x5C, 0x62, 0x40, 0x40, 0x40, 0x00,
            0x00, 0x00, 0x3E, 0x40, 0x3C, 0x02, 0x7C, 0x00, 0x10, 0x10, 0x7C, 0x10,
            0x10, 0x12, 0x0C, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x46, 0x3A, 0x00,
            0x00, 0x00, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00, 0x00, 0x00, 0x41, 0x49,
            0x49, 0x49, 0x36, 0x00, 0x00, 0x00, 0x42, 0x24, 0x18, 0x24, 0x42, 0x00,
            0x00, 0x00, 0x42, 0x42, 0x46, 0x3A, 0x02, 0x3C, 0x00, 0x00, 0x7E, 0x04,
            0x18, 0x20, 0x7E, 0x00, 0x3C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x00,
            0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x3C, 0x04, 0x04, 0x04,
            0x04, 0x04, 0x3C, 0x00, 0x00, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x08, 0x08,
            0x00, 0x00, 0x10, 0x20, 0x7F, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x08, 0x00,
            0x24, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x7E, 0x24,
            0x7E, 0x24, 0x24, 0x00, 0x08, 0x1E, 0x28, 0x1C, 0x0A, 0x3C, 0x08, 0x00,
            0x00, 0x62, 0x64, 0x08, 0x10, 0x26, 0x46, 0x00, 0x30, 0x48, 0x48, 0x30,
            0x4A, 0x44, 0x3A, 0x00, 0x04, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x04, 0x08, 0x10, 0x10, 0x10, 0x08, 0x04, 0x00, 0x20, 0x10, 0x08, 0x08,
            0x08, 0x10, 0x20, 0x00, 0x08, 0x2A, 0x1C, 0x3E, 0x1C, 0x2A, 0x08, 0x00,
            0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x02, 0x04, 0x08,
            0x10, 0x20, 0x40, 0x00, 0x3C, 0x42, 0x46, 0x5A, 0x62, 0x42, 0x3C, 0x00,
            0x08, 0x18, 0x28, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x3C, 0x42, 0x02, 0x0C,
            0x30, 0x40, 0x7E, 0x00, 0x3C, 0x42, 0x02, 0x1C, 0x02, 0x42, 0x3C, 0x00,
            0x04, 0x0C, 0x14, 0x24, 0x7E, 0x04, 0x04, 0x00, 0x7E, 0x40, 0x78, 0x04,
            0x02, 0x44, 0x38, 0x00, 0x1C, 0x20, 0x40, 0x7C, 0x42, 0x42, 0x3C, 0x00,
            0x7E, 0x42, 0x04, 0x08, 0x10, 0x10, 0x10, 0x00, 0x3C, 0x42, 0x42, 0x3C,
            0x42, 0x42, 0x3C, 0x00, 0x3C, 0x42, 0x42, 0x3E, 0x02, 0x04, 0x38, 0x00,
            0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
            0x00, 0x08, 0x08, 0x10, 0x0E, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0E, 0x00,
            0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x70, 0x18, 0x0C, 0x06,
            0x0C, 0x18, 0x70, 0x00, 0x3C, 0x42, 0x02, 0x0C, 0x10, 0x00, 0x10, 0x00,
            0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x18, 0x24, 0x42, 0x7E,
            0x42, 0x42, 0x42, 0x00, 0x7C, 0x22, 0x22, 0x3C, 0x22, 0x22, 0x7C, 0x00,
            0x1C, 0x22, 0x40, 0x40, 0x40, 0x22, 0x1C, 0x00, 0x78, 0x24, 0x22, 0x22,
            0x22, 0x24, 0x78, 0x00, 0x7E, 0x40, 0x40, 0x78, 0x40, 0x40, 0x7E, 0x00,
            0x7E, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x00, 0x1C, 0x22, 0x40, 0x4E,
            0x42, 0x22, 0x1C, 0x00, 0x42, 0x42, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x00,
            0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, 0x0E, 0x04, 0x04, 0x04,
            0x04, 0x44, 0x38, 0x00, 0x42, 0x44, 0x48, 0x70, 0x48, 0x44, 0x42, 0x00,
            0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7E, 0x00, 0x42, 0x66, 0x5A, 0x5A,
            0x42, 0x42, 0x42, 0x00, 0x42, 0x62, 0x52, 0x4A, 0x46, 0x42, 0x42, 0x00,
            0x18, 0x24, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00, 0x7C, 0x42, 0x42, 0x7C,
            0x40, 0x40, 0x40, 0x00, 0x18, 0x24, 0x42, 0x42, 0x4A, 0x24, 0x1A, 0x00,
            0x7C, 0x42, 0x42, 0x7C, 0x48, 0x44, 0x42, 0x00, 0x3C, 0x42, 0x40, 0x3C,
            0x02, 0x42, 0x3C, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00,
            0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x00, 0x42, 0x42, 0x42, 0x24,
            0x24, 0x18, 0x18, 0x00, 0x42, 0x42, 0x42, 0x5A, 0x5A, 0x66, 0x42, 0x00,
            0x42, 0x42, 0x24, 0x18, 0x24, 0x42, 0x42, 0x00, 0x22, 0x22, 0x22, 0x1C,
            0x08, 0x08, 0x08, 0x00, 0x7E, 0x02, 0x04, 0x18, 0x20, 0x40, 0x7E, 0x00,
            0x08, 0x08, 0x08, 0x08, 0xFF, 0x08, 0x08, 0x08, 0xA0, 0x50, 0xA0, 0x50,
            0xA0, 0x50, 0xA0, 0x50, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
            0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0x66, 0x33, 0x99,
            0xCC, 0x66, 0x33, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00,
            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x80, 0x80, 0x80,
            0x80, 0x80, 0x80, 0x80, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55,
            0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
            0xAA, 0x55, 0xAA, 0x55, 0x99, 0x33, 0x66, 0xCC, 0x99, 0x33, 0x66, 0xCC,
            0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x08, 0x08, 0x08, 0x08,
            0x0F, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F,
            0x08, 0x08, 0x08, 0x08, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0xF8, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
            0x00, 0x00, 0x00, 0x00, 0x0F, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
            0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x08, 0x08, 0x08,
            0x08, 0x08, 0x08, 0x08, 0xF8, 0x08, 0x08, 0x08, 0xC0, 0xC0, 0xC0, 0xC0,
            0xC0, 0xC0, 0xC0, 0xC0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0,
            0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xFF, 0xFF, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x01, 0x02, 0x44, 0x48,
            0x50, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0,
            0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08,
            0xF8, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00,
            0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F
        };
    }
}

Viel Spaß!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.