Chars (PI5): Unterschied zwischen den Versionen
KKeine Bearbeitungszusammenfassung |
KKeine Bearbeitungszusammenfassung |
||
| Zeile 121: | Zeile 121: | ||
Mein Beispiel für den Font habe ich als "ms8x8font.fon" im Includeverzeichnis abgelegt. | Mein Beispiel für den Font habe ich als "ms8x8font.fon" im Includeverzeichnis abgelegt. | ||
=== Font-Anzeigen lassen === | === Font-Anzeigen lassen === | ||
| Zeile 245: | Zeile 194: | ||
Nun müssen wir jedes Bit dieser Font-Matrix auswerten. Dazu verwenden wir folgende Formel: (Zeile >> (7 - BitPos)) & 1; | Nun müssen wir jedes Bit dieser Font-Matrix auswerten. Dazu verwenden wir folgende Formel: (Zeile >> (7 - BitPos)) & 1; | ||
Wir lesen zunächst das Byte heraus, was wir mit w11 (Zeile) definiert haben. Dies machen wir mit dem Befehl ldrb w13,[x10,x11]. Dies bedeutet das wir aus dem Speicher das Byte lesen, welches im Speicher an x10 steht und addieren dort den Offset von x11 dazu. Den Wert schreiben wir nach w13. Da wir das Ergebnis falsch herum | Wir lesen zunächst das Byte heraus, was wir mit w11 (Zeile) definiert haben. Dies machen wir mit dem Befehl ldrb w13,[x10,x11]. Dies bedeutet das wir aus dem Speicher das Byte lesen, welches im Speicher an x10 steht und addieren dort den Offset von x11 dazu. Den Wert schreiben wir nach w13. Da wir das Ergebnis falsch herum bekommen, ziehen wir von der 7 (1. Bit = 0) die BitPos ab, die wir in der Schleife mit w12 definiert haben. Verschieben damit dann die Bits im Byte um die Positionen mit lsr nach rechts und mit "and" löschen wir alle höherwertigen Bits, bis auf das erste. | ||
Nun können wir das Ergebnis auswerten. Wenn der Wert gleich Null ist, dann zeichne ein Pixel in der Hintergrundfarbe, ansonsten in der Vordergrundfarbe. | |||
Hier der gesamte Code für die DrawChar-Funktion: | |||
<syntaxhighlight lang="asm"> | <syntaxhighlight lang="asm"> | ||
//void DrawChar(char c, u32 x0, u32 y0) | |||
//Zeichnet einen Char an die Position x0, y0 auf den Screen | |||
.globl DrawChar | |||
DrawChar: | |||
stp x29, x30, [sp, -16]! | |||
stp x20, x21, [sp, -16]! | |||
stp x10, x11, [sp, -16]! | |||
stp x12, x13, [sp, -16]! | |||
stp x14, x15, [sp, -16]! | |||
mov x29, sp | |||
Sichere zunächst die Position | |||
mov w20,w1 // x0 | |||
mov w21,w2 // y0 | |||
//Berechne den Zeiger auf das richtige Zeichen | |||
ldr x10,=font | |||
lsl w0,w0,#3 | |||
add x10,x10,x0 | |||
//for (w11 = 0; w11 < FONT_HEIGHT; w11++) | |||
mov w11,#0 | |||
1: | |||
cmp w11,#FONT_HEIGHT | |||
bge 2f | |||
< | //for (w12 = 0; w12 < FONT_WIDTH; w12++) | ||
mov w12,#0 | |||
3: | |||
cmp w12,#FONT_WIDTH | |||
bge 4f | |||
//Überprüfe das Bit nach folgender Formel: | |||
//(Zeile >> (7 - BitPos)) & 1; | |||
ldrb w13,[x10,x11] | |||
mov w14,#7 | |||
sub w14,w14,w12 | |||
lsr w13,w13,w14 | |||
and w13,w13,#1 | |||
//Wenn Ergebniss 0 ist, dann Hintergrund zeichnen | |||
cbz w13,5f | |||
//Ansonsten in der Vordergrundfarbe | |||
ldr x13,=FrontColor | |||
ldr w13,[x13] | |||
ldr x14,=DrawColor | |||
str w13,[x14] | |||
b 6f | |||
5: | |||
ldr x13,=BackColor | |||
ldr w13,[x13] | |||
ldr x14,=DrawColor | |||
str w13,[x14] | |||
6: | |||
//Zeichne den Pixel an die Position | |||
mov w0,w12 | |||
add w0,w0,w20 | |||
mov w1,w11 | |||
add w1,w1,w21 | |||
bl DrawPixel | |||
//end_for w12 | |||
add w12,w12,#1 | |||
b 3b | |||
4: | |||
//end_for w11 | |||
add w11,w11,#1 | |||
b 1b | |||
2: | |||
ldp x14, x15, [sp], 16 | |||
ldp x12, x13, [sp], 16 | |||
ldp x10, x11, [sp], 16 | |||
ldp x20, x21, [sp], 16 | |||
ldp x29, x30, [sp], 16 | |||
ret | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Um das nun zu testen, ob es geht, müssen wir unser main-Programm ändern: | |||
<syntaxhighlight lang="asm"> | <syntaxhighlight lang="asm"> | ||
// | |||
// kernel.S | |||
// | |||
.section .text | |||
.globl main | |||
main: | |||
bl LED_off | |||
bl Init_Screen | |||
mov w10,#33 | |||
mov w11,#100 | |||
2: | |||
cmp w10,#90 | |||
bge 1f | |||
mov w0,w10 | |||
mov w1,#100 | |||
mov w2,w11 | |||
bl DrawChar | |||
add w11,w11,#10 | |||
add w10,w10,#1 | |||
b 2b | |||
1: | |||
mov w0,#2 | |||
bl LED_Error | |||
999: | |||
b 999b | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Wie zuvor schon, schalten wir wieder unsere LED aus, um einen Ausgangszustand zu haben. Initialiesieren unseren Screen und erzeugen dann eine Schleife, die von 33 bis 90 zählt. Diesen Wert übergeben wir der DrawChar-Funktion in w0. Zusätzlich geben wir ihr die Koordinaten für den "Char" in w1 (immer 100) und w2 (jeweils um 10 erhöht) mit. Am ende unseres Programms gehen wir wieder in den Fehlercode 2, damit wir sehen, dass er alles abgearbeitet hat. | |||
Version vom 13. August 2024, 09:40 Uhr
DrawChar auf dem Raspberry Pi
Wir haben nun eine Pixel-Funktion geschrieben, aber möchten wir nicht auch, dass der Raspberry Pi Informationen direkt auf dem Bildschirm anzeigen kann? Das wollen wir jetzt versuchen. In der Regel kommuniziert der Computer mittels Schriftzeichen. Dies geschieht nicht einfach so; ähnlich wie ein Mensch muss der Computer zuerst lernen, wie jeder einzelne Buchstabe aussieht. Außerdem muss er wissen, wann er das entsprechende Zeichen verwenden soll. Bestimmte Bytewerte stehen für bestimmte Zeichen oder Steuercodes. Es gibt einen Standard, der diese Kommunikation festlegt. Dieser Code nennt sich "ANSI-Zeichencode".
Der ANSI-Zeichencode
Der ANSI-Zeichencode, den wir hier verwenden, ist eine Erweiterung des ASCII-Codes, bei dem von 7-Bit pro Zeichen auf 8-Bit pro Zeichen umgestellt wurde, was einem Byte entspricht. Der ANSI-Zeichencode wurde eingeführt, als Computeranwender über Netzwerke kommunizieren wollten. Dafür war es notwendig, dass beide Computer mit dem gleichen Code "sprechen".
Innerhalb des ASCII-Codes sind die Zahlenwerte für die Zeichen in der Regel identisch, aber in der Erweiterung zu 8-Bit nicht unbedingt gleich. Jede Computerfirma hat eigene Codepages entwickelt und veröffentlicht. Gerade in der Anfangszeit dominierten IBM-Rechner mit dem Betriebssystem MS-DOS. Wir werden hier genau diesen Zeichencode verwenden, der dort eingesetzt wurde und auch unter Windows in der Konsole verwendet wird. Dieser Code wird als "Codepage 437" bezeichnet.
| 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0A | 0B | 0C | 0D | 0E | 0F | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0x0 | NULL | ☺ | ☻ | ♥ | ♦ | ♣ | ♠ | • | ◘ | ○ | ◙ | ♂ | ♀ | ♪ | ♫ | ☼ |
| 0x1 | ► | ◄ | ↕ | ‼ | ¶ | § | ▬ | ↨ | ↑ | ↓ | → | ← | ∟ | ↔ | ▲ | ▼ |
| 0x2 | Space | ! | " | # | $ | % | & | ' | ( | ) | * | + | , | - | . | / |
| 0x3 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | : | ; | < | = | > | ? |
| 0x4 | @ | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O |
| 0x5 | P | Q | R | S | T | U | V | W | X | Y | Z | [ | \ | ] | ^ | _ |
| 0x6 | ` | a | b | c | d | e | f | g | h | i | j | k | l | m | n | o |
| 0x7 | p | q | r | s | t | u | v | w | x | y | z | { | } | ~ | DEL | |
| 0x8 | Ç | ü | é | â | ä | à | å | ç | ê | ë | è | ï | î | ì | Ä | Å |
| 0x9 | É | æ | Æ | ô | ö | ò | û | ù | ÿ | Ö | Ü | ¢ | £ | ¥ | ₧ | ƒ |
| 0xA | á | í | ó | ú | ñ | Ñ | ª | º | ¿ | ⌐ | ¬ | ½ | ¼ | ¡ | « | » |
| 0xB | ░ | ▒ | ▓ | │ | ┤ | ╡ | ╢ | ╖ | ╕ | ╣ | ║ | ╗ | ╝ | ╜ | ╛ | ┐ |
| 0xC | └ | ┴ | ┬ | ├ | ─ | ┼ | ╞ | ╟ | ╚ | ╔ | ╩ | ╦ | ╠ | ═ | ╬ | ╧ |
| 0xD | ╨ | ╤ | ╥ | ╙ | ╘ | ╒ | ╓ | ╪ | ┘ | ┌ | █ | ▄ | ▌ | ▐ | ▀ | |
| 0xE | α | ß | Γ | π | Σ | σ | µ | τ | Φ | Θ | Ω | δ | ∞ | φ | ε | ∩ |
| 0xF | ≡ | ± | ≥ | ≤ | ⌠ | ⌡ | ÷ | ≈ | ° | • | · | √ | ⁿ | ² | ■ |
Font-Erstellen
Nun kümmern wir uns darum, dem Computer beizubringen, wie die einzelnen Zeichen aussehen. Dazu erstellen wir eine Art Datenbank, in der jedes Zeichen beschrieben wird. Zur Einfachheit verwenden wir eine 8x8 Matrix, um die Zeichen darzustellen.
Dazu erstellen wir eine Datei, die folgendermaßen aussieht:
- Dimension
- INT X
- INT Y
- Data
- Binärcode
Zunächst geben wir die Größe der Zeichen in X und Y an. Daraus können wir zur Berechnung der einzelnen Zeichen den entsprechenden Binärcode ableiten. Hier steht jedes Bit, das gesetzt ist, für ein Pixel, welches gezeichnet werden soll. Umgesetzt in Code sieht es dann so aus:
/* 8x8 FONT by Satyria */
@ Dimension
.int 8 @ x
.int 8 @ y
@ Data
@ 0: NULL
.byte 0b00000000
.byte 0b00000000
.byte 0b00000000
.byte 0b00000000
.byte 0b00000000
.byte 0b00000000
.byte 0b00000000
.byte 0b00000000
...
@ 65: "A"
.byte 0b00011000
.byte 0b00111100
.byte 0b00111100
.byte 0b01100110
.byte 0b01111110
.byte 0b11000011
.byte 0b11000011
.byte 0b00000000
@ 66: "B"
.byte 0b11111100
.byte 0b01100110
.byte 0b01100110
.byte 0b01111100
.byte 0b01100110
.byte 0b01100110
.byte 0b11111100
.byte 0b00000000
...
Dieses Beispiel zeigt, wie wir die Datenbank für die Zeichendarstellung aufbauen können. Jeder Buchstabe wird als 8x8 Pixel großes Muster beschrieben, wobei jedes Byte eine Zeile des Musters darstellt. Das Bitmuster gibt an, welche Pixel gesetzt werden und somit sichtbar sind.
Mein Beispiel für den Font habe ich als "ms8x8font.fon" im Includeverzeichnis abgelegt.
Font-Anzeigen lassen
Nachdem wir nun einen Font beschrieben haben, also definiert haben, wie ein Zeichen aussieht, müssen wir diesen noch auf den Bildschirm zeichnen.
Bisher können wir nicht auf externe Medien, während unser Programm läuft, zugreifen. Da dies bisher nicht geht, müssen wir den Font in unseren Sourcecode integrieren. Dazu verwenden wir die Include-Funktion des Compilers.
.section .data
font:
.include "ms8x8font.fon"
Unserer Funktion werden wir den Namen "DrawChar" geben. Ihr wird das Zeichen selbst, welches angezeigt werden soll und die Position (x,y) übergeben.
Zunächst sichern wir diese Angaben, damit diese nicht gelöscht werden, wenn wir eine andere Funktion verwenden.
//void DrawChar(char c, u32 x0, u32 y0)
//Zeichnet einen Char an die Position x0, y0 auf den Screen
.globl DrawChar
DrawChar:
stp x29, x30, [sp, -16]!
stp x20, x21, [sp, -16]!
stp x10, x11, [sp, -16]!
stp x12, x13, [sp, -16]!
stp x14, x15, [sp, -16]!
mov x29, sp
Sichere zunächst die Position
mov w20,w1 // x0
mov w21,w2 // y0
Unseren Font haben wir im Datenbereich abgelegt und ihm das Label "font" gegeben. Diesen Zeiger legen wir in x10 ab.
//Berechne den Zeiger auf das richtige Zeichen
ldr x10,=font
lsl w0,w0,#3
add x10,x10,x0
Da wir die Dimension des Fonts kennen, überspringen wir diese Angabe. Danach multiplizieren wir die Übergabe des Zeichen, welche wir in w0 übergeben bekommen haben, mit acht. Ein Shift um 3 Positionen nach links, entspricht einer Multiplikation von acht. Und addieren das Ergebnis auf den Zeiger. Damit haben wir die Position des entsprechenden Fonts, den wir anzeigen lassen wollen.
Da wir wissen, wie der Font aufgebaut ist, also 8x8 Pixel, verwenden wir zwei ineinander verschachtelte Schleifen:
//for (w11 = 0; w11 < FONT_HEIGHT; w11++)
mov w11,#0
1:
cmp w11,#FONT_HEIGHT
bge 2f
//for (w12 = 0; w12 < FONT_WIDTH; w12++)
mov w12,#0
3:
cmp w12,#FONT_WIDTH
bge 4f
...
//end_for w12
add w12,w12,#1
b 3b
4:
//end_for w11
add w11,w11,#1
b 1b
Nun müssen wir jedes Bit dieser Font-Matrix auswerten. Dazu verwenden wir folgende Formel: (Zeile >> (7 - BitPos)) & 1;
Wir lesen zunächst das Byte heraus, was wir mit w11 (Zeile) definiert haben. Dies machen wir mit dem Befehl ldrb w13,[x10,x11]. Dies bedeutet das wir aus dem Speicher das Byte lesen, welches im Speicher an x10 steht und addieren dort den Offset von x11 dazu. Den Wert schreiben wir nach w13. Da wir das Ergebnis falsch herum bekommen, ziehen wir von der 7 (1. Bit = 0) die BitPos ab, die wir in der Schleife mit w12 definiert haben. Verschieben damit dann die Bits im Byte um die Positionen mit lsr nach rechts und mit "and" löschen wir alle höherwertigen Bits, bis auf das erste. Nun können wir das Ergebnis auswerten. Wenn der Wert gleich Null ist, dann zeichne ein Pixel in der Hintergrundfarbe, ansonsten in der Vordergrundfarbe.
Hier der gesamte Code für die DrawChar-Funktion:
//void DrawChar(char c, u32 x0, u32 y0)
//Zeichnet einen Char an die Position x0, y0 auf den Screen
.globl DrawChar
DrawChar:
stp x29, x30, [sp, -16]!
stp x20, x21, [sp, -16]!
stp x10, x11, [sp, -16]!
stp x12, x13, [sp, -16]!
stp x14, x15, [sp, -16]!
mov x29, sp
Sichere zunächst die Position
mov w20,w1 // x0
mov w21,w2 // y0
//Berechne den Zeiger auf das richtige Zeichen
ldr x10,=font
lsl w0,w0,#3
add x10,x10,x0
//for (w11 = 0; w11 < FONT_HEIGHT; w11++)
mov w11,#0
1:
cmp w11,#FONT_HEIGHT
bge 2f
//for (w12 = 0; w12 < FONT_WIDTH; w12++)
mov w12,#0
3:
cmp w12,#FONT_WIDTH
bge 4f
//Überprüfe das Bit nach folgender Formel:
//(Zeile >> (7 - BitPos)) & 1;
ldrb w13,[x10,x11]
mov w14,#7
sub w14,w14,w12
lsr w13,w13,w14
and w13,w13,#1
//Wenn Ergebniss 0 ist, dann Hintergrund zeichnen
cbz w13,5f
//Ansonsten in der Vordergrundfarbe
ldr x13,=FrontColor
ldr w13,[x13]
ldr x14,=DrawColor
str w13,[x14]
b 6f
5:
ldr x13,=BackColor
ldr w13,[x13]
ldr x14,=DrawColor
str w13,[x14]
6:
//Zeichne den Pixel an die Position
mov w0,w12
add w0,w0,w20
mov w1,w11
add w1,w1,w21
bl DrawPixel
//end_for w12
add w12,w12,#1
b 3b
4:
//end_for w11
add w11,w11,#1
b 1b
2:
ldp x14, x15, [sp], 16
ldp x12, x13, [sp], 16
ldp x10, x11, [sp], 16
ldp x20, x21, [sp], 16
ldp x29, x30, [sp], 16
ret
Um das nun zu testen, ob es geht, müssen wir unser main-Programm ändern:
//
// kernel.S
//
.section .text
.globl main
main:
bl LED_off
bl Init_Screen
mov w10,#33
mov w11,#100
2:
cmp w10,#90
bge 1f
mov w0,w10
mov w1,#100
mov w2,w11
bl DrawChar
add w11,w11,#10
add w10,w10,#1
b 2b
1:
mov w0,#2
bl LED_Error
999:
b 999b
Wie zuvor schon, schalten wir wieder unsere LED aus, um einen Ausgangszustand zu haben. Initialiesieren unseren Screen und erzeugen dann eine Schleife, die von 33 bis 90 zählt. Diesen Wert übergeben wir der DrawChar-Funktion in w0. Zusätzlich geben wir ihr die Koordinaten für den "Char" in w1 (immer 100) und w2 (jeweils um 10 erhöht) mit. Am ende unseres Programms gehen wir wieder in den Fehlercode 2, damit wir sehen, dass er alles abgearbeitet hat.