Inhaltsverzeichnis

Programmierbeispiele

Diese Beispiele sind in 4e4th05a „For Education Forth“ auf dem TI LaunchPad erstellt und ausprobiert worden.

Benutze das vollständige Glossar zum 4e4th (PDF) um dich zu orientieren.

Wenn du mit Forth noch nicht vertraut bist, mache die Übungen aus der Online Edition des Starting Forth von Leo Brodie. Das komplette Buch kann auch als PDF runtergeladen werden von http://www.exemark.com/FORTH.htm Und ist damit lokal speicherbar und zum Ausdrucken geeignet.

Dort heißt es: „Code examples run on iForth and SwiftForth“. Keine Sorge, alle Übungen sind ANS Forth, und 4e4th ist ein ANS Forth. Doch es gibt auch Einschränkungen. Denn 4e4th läuft auf einer kleinen MCU und nicht auf einem PC. Es kommt mit 1K RAM und 8K user flash aus. Und ist selbst nur 8K groß.

Die Einschränkungen sind:
Ein Zelle (CELL) ist 16 bit breit.
-1 HEX U. FFFF ok

Der integer Zahlenumfang ist daher
HEX 7FFF DECIMAL . 32767 ok
HEX 8000 DECIMAL . -32768 ok

Viel Vergnügen.

LEDs blinken lassen

DECIMAL
: blink ( -- )  
    BEGIN 
      red csetb green cclrb 100 MS
      red cclrb green csetb 100 MS
    KEY? UNTIL KEY DROP ;
    

Das Forth Wort MS nimmt eine Zahl vom Stack. Es ist eine einfache leere Schleife. 1000 MS ergeben ungefähr eine Sekunde.

Spannungspegel einstellen

Voltmeter in DCV Einstellung bringen um Gleichspannung zu messen. Schwarze Messleitung an Masse (GND) und Rote an den gewünschten Portpin anschießen. Schaltung: Portpinx ←—-Voltmeter auf DCV—-GND

Das Voltmeter integriert den Pegel über die Zeit. Eine rasche Pulsfolge wird daher in eine entsprechende Spannung umgesetzt. Pulse im Tastverhältniss 50/50 ergeben die halbe Portspannung. Bei einem Pegel von high=3,54V an den Pins ergibt das 1,77V. Probiere verschiedene Teiler aus! Welche pins sind hier aktiv?

VARIABLE teiler    100 teiler !

: teile  ( -- )
  16 P1 cset 32 P1 cclr
  BEGIN
    48 P1 ctoggle
    100 teiler @ / DROP 1 MS  ( siehe * )
    48 P1 ctoggle
    100 teiler @ / MS
  KEY? UNTIL key drop
;  ( Fritz )

Abfrage des Schalters S2

Schalter S2 ist an Port1 angeschlossen.

P1.3 --->---o_o--- GND

Das Forth Wort S2? liefert seinen Zustand auf den Stack. Mit diesem kleinen Test kannst du überprüfen ob der Schalter funktioniert.

: s2test ( -- ) BEGIN S2? . KEY? UNTIL ;

Die Phrase KEY? UNTIL in der BEGIN UNTIL Schleife sorgt dafür das du den Test auch wieder verlassen kannst.

Ich hatte ein LaunchPad auf dem S2 defekt war! Und mich lange gewundert warum all meine Programmversuche fehl schlugen. Verwende ein Voltmeter oder Oscilloskop um deine Eingabe an S2 zu überprüfen falls was nicht so wie erwartet geht.

S2? testet das Bit 3 vom Port1 ohne die anderen Bits zu beeinflussen.

Morse Code per LEDs

Erste Umsetzung des amForth Morse-Programmes, Version 1 (naive Implementierung).

MARKER --base--

: blink ( cycles -- ) 
  red cset 
  0 DO 
    3 MS 
  LOOP 
  red cclr ;

: gap ( cycles -- )
  0 DO 
    2 MS 
  LOOP ;

: kurz 50 blink 50 gap ; ( kurzes Signal )
: lang 150 blink 50 gap ; ( langes Signal )

: Zend   100 gap ; ( Zeichenende )
: Wend   300 gap ; ( Wortende )

MARKER --morse--

: _A  kurz lang Zend ;
: _B  lang kurz kurz kurz Zend ;
: _C  lang kurz lang kurz Zend ;
: _D  lang kurz kurz Zend ;
: _E  kurz Zend ;
: _F  kurz kurz lang kurz Zend ;
: _G  lang lang kurz Zend ;
: _H  kurz kurz kurz kurz Zend ;
: _I  kurz kurz Zend ;
: _J  kurz lang lang lang Zend ;
: _K  lang kurz lang Zend ;
: _L  kurz lang kurz kurz Zend ;
: _M  lang lang Zend ;
: _N  lang kurz Zend ;
: _O  lang lang lang Zend ;
: _P  kurz lang lang kurz Zend ;
: _Q  lang lang kurz lang Zend ;
: _R  kurz lang kurz Zend ;
: _S  kurz kurz kurz Zend ;
: _T  lang Zend ;
: _U  kurz kurz lang Zend ;
: _V  kurz kurz kurz lang Zend ;
: _W  kurz lang lang Zend ;
: _X  lang kurz kurz lang Zend ;
: _Y  lang kurz lang lang Zend ;
: _Z  lang lang kurz kurz Zend ;

: SOS _S _O _S ;

Morse Code per Lautsprecher

Eine Version, die Töne macht, ist auch nicht schwer. morsen2.rtf.zip

So ein einfache Schaltung mit einem kleinen 8 Ohm 0,2 Watt Lautschrecher reicht dazu schon:

    
 Schaltung:   P1.4 --->------Lautsprecher-----<---P1.5 
 

Damit kann man schon recht schön morsen. Man hört das „did did dah“ und sieht die LED dazu blinken.

Watchdog Timer

Beispiel für die TI MSP430G2553 MCU.

hex 
0120 Constant WDTCTL
: wdt! ( val -- )   5A00 ( password ) or WDTCTL ! ;
: +wdt ( -- )  08 wdt! ;
: -wdt ( -- )  80 wdt! ;
decimal 
: wdt-test ( n -- ) 1 DO  I  DUP . +wdt  0 DO LOOP -wdt LOOP   ." done"  ;
save

Der wdt-test zählt bis ca. 2700 und macht dann einen Watchdog-Reset, was man am Bit 0 von COR (cause of reset) sehen kann.

BIN COR @ . DECIMAL

Der COR Wert wird auch am Ende der Satrtmeldung angezeigt. Die Einsen und Nullen rechts vom senkrechten Strich sind der COR Wert, binär dargestellt.

Der Wert in COR ist eine Kopie des IFR1 der MCU (interupt flag register1), siehe User Manual.

Conway's Game of Life

Eine Implementierung von Conway's Game of Life, optimiert für Mikrocontroller. Eignet sich um kleine Bitmap-Animationen zu generieren um Muster auf ein Display zu zaubern. Das aktuelle Muster steht immer in PAD, eine Zelle pro Zeile, 16 Zeilen hintereinander, also 16×16 bits = 32 bytes. Das Muster wird mit NEXTGEN auf die nächste Generation aktualisiert. Einen temporären Puffer gibt es nicht, lediglich die letzten 2 Zeilen des alten Musters werden während des Updates auf dem Stack gehalten. Geometrie des Universums entspricht einem Torus, links/rechts, oben/unten gehen jeweils ineinander über. Eingabe von LIFE startet eine kontinierliche Animation ins Terminal, Abbruch mit <q>. Vordefinierte Muster können mit VOID GLIDER, VOID LWSS etc. geladen werden

DECIMAL
16 CONSTANT #lines
PAD CONSTANT universe
8 CELLS CONSTANT bits/cell  \ = number of columns

: line  ( n -- a-addr ) CELLS universe + ;
: lrot  ( x1 -- x2 )  \ rotate left by one
   DUP 2* SWAP 0< - ;
: lrot3  ( x1 x2 x3 --  x4 x5 x6 )
   lrot ROT  lrot ROT  lrot ROT ;

IHERE  0 IC, 1 IC, 1 IC, 2 IC,  1 IC, 2 IC, 2 IC, 3 IC,  CONSTANT #bits
: countbits  ( x -- n )  \ count number of bits=1 in bit0..2
   7 AND #bits + C@ ;

: alive  ( x1 x2 x3 -- flag )
   \ return whether cell at bit1 in line1 is alive in next generation
   OVER 2 AND 0= INVERT >R
   countbits
   SWAP countbits +  \ note: cell itself is counted, too. correction below.
   SWAP countbits +
   DUP 3 =  SWAP 4 = R> AND OR ;
: 3dup  ( x1 x2 x3 -- x1 x2 x3 x1 x2 x3 )
   DUP 2OVER ROT ;
: doline  ( x1 x2 x3 -- x1 x2 x3 x4 )
   0  bits/cell 0 DO 
      >R   3dup alive 2 AND R> OR lrot >R
      lrot3   R>
   LOOP ;
: nextgen  ( -- )
   0 line @   #lines 1- line @  OVER   ( s: line0   x1 x2 )
   #lines 1- 0 DO
      I 1+ line @               ( s: line0  x1 x2 x3 )
      doline  I line !
      ROT DROP                  ( s: line0  x2 x3 )
   LOOP  
   ROT doline  #lines 1- line !   \ special treatment for last line
   2DROP DROP ;
: .line  ( x -- )
   bits/cell 0 DO
      DUP 0< IF [char] @  ELSE [char] . THEN EMIT
      lrot
   LOOP  DROP ;
: .universe  ( -- )  \ print current life state to console 
   #lines 0 DO   CR I line @ .line  LOOP ;
: life  ( -- ) \ run life with output to console, until key <q> pressed
   BEGIN
      PAGE .universe     nextgen  20 MS 
   KEY? IF KEY [char] q = ELSE 0 THEN UNTIL ;

: void  ( -- )  universe #lines CELLS 0 FILL ;
: seed  ( x1 .. xn n -- )   0 DO i line ! LOOP ;
HEX   \ some wellknown patterns:
: glider  ( -- )  7 1 2  3 seed ;
: fpent  ( -- )  4 0C 6  3 seed ;
: lwss  ( -- )  0F 11 1 12  4 seed ;
: diehard  47 0C0 2  3 seed ;
: acorn  67 8 20  3 seed ;
: demo   glider   700 3 line ! ;
DECIMAL

void demo life  \ Start der Animation, Abbruch mit <q>

Disassembler

Ein kleiner Disassembler für den MSP430 um Codestückchen prüfen zu können.

\ Albert Nijhof, Willem Ouwerkerk
\ an    -- 05mei12 -- 430 disassembler
\ wo    -- 09mei12 -- working on Launchpad about 1,2 kByte
\ wo    -- 12mei12 -- Debugged version, working ok, still about 1,2 kByte
\ an&wo -- 13mei12 -- Tested, 1,1 kbytes!
\ wo    -- 14mei12 -- Print real address with jumping & keep das save

\ The disassembler output corresponds with our assembler notation:
\ MSP assembly     disassembles to
\ ------------     ------------      --------------
\ PC R8            pc r8             Register names
\ PC@              pc )              Indirect addressing
\ PC@+             pc )+             Indirect with autoincrement
\ 430              xx pc i)          xx + pc = 430 (Symbolic mode)
\ 2(R8)            2 r8 i)           Indexed
\ #430             430 #             constant 430 assembled as pc )+
\ &430             430 &             Absolute using SR
\ #4 #8            #4 #8             Constants using RS
\ #0 #1 #2 #-1     #0 #1 #2 #-1      Constants using CG

\ pc-relative jmp  goto +3           Always jump 3 words forward
\ pc-relative jnc  jcc -4            On carry clear jump 4 words backward

hex  \ until the end
: 4.r   0 <# # # # # #> type ;
: i"    [char] " word count rot swap d->i ;
: icreate <builds does> ;

hex
icreate one-ops  8 4 * iallot
   one-ops i" RRC SWPBRRA SXT PUSHCALLRETI7?  "
icreate jumpings 8 4 * iallot
   jumpings i" J0<>J0= JCC JCS J0< J>= J<  GOTO"
icreate two-ops 8 2* 4 * iallot
   two-ops i" 0?  1?  2?  3?  MOV ADD ADDCSUBC"
   two-ops 20 + i" SUB CMP DADDBIT BIC BIS XOR AND "
icreate regs 8 2* 3 * iallot
   regs i" pc rp sr cg sp ip w  tosr8 r9 r10r11r12r13r14r15"

variable dasadr  ihere dasadr !
: dasadr@+ ( -- dascode )    dasadr @ @  2 dasadr +! ;
: .reg ( nr -- ) 3 * regs +   dup 2 + c@ bl = 3 + type space ;

: .dst ( a r -- )
  over 1 = if dasadr@+ .  dup 2 = if  2drop ." & " exit then then
  .reg
  dup 1 = if ." i" then
  dup     if ." )" then
  dup 3 = if ." +" then
  if space then ;

: .src ( a reg -- )
  dup 3 = if drop dup 3 = if 4 - then  ." #" . exit then      \ cg #-1 #0 #1 #2
  over 2 and over 2 = and if ." #" 1- swap lshift . exit then \ sr #4 #8
  over 3 = over 0= and if 2drop dasadr@+ u. ." # " exit then  .dst ;

: b/w     ( dascode -- )  40 and if ." .b " then ;
: .mnemo  ( +n adr -- )   swap 2* 2* + 4 type space ;

: one-op ( dascode 1 )   drop >r
  r@ ( dascode )  7 rshift 7 and   dup 6 <>  \ not reti?
  if    r@ 4 rshift 3 and   r@ 0f and .src   r@ b/w
  then  r> drop  one-ops .mnemo ;

: two-op ( dascode 4..F )   swap
  ( src a,r ) dup 4 rshift 3 and   over 8 rshift 0F and   .src
  ( dst a,r ) dup 7 rshift 1 and   over          0F and   .dst  b/w
  ( mnemo )   two-ops .mnemo ;

: jumping ( dascode 2..3 )   drop
  ( mnemo )    dup 0A rshift 7 and   jumpings .mnemo
  ( distance ) 03FF and 0200 over and    \ Negative?
   if FC00 or then 2*  dasadr @ +  4.r space ;  \ Calculate real address

\ Decode 1 instruction, addr has to be in dasadr
: das+ ( -- ) \ disassemble next instruction
   cr dasadr @ 4.r ." :" space  \ Print address
   dasadr@+   dup 4.r space space  \ Print opcode
   dup 0C rshift    ( dascode  n )
   dup 0=  if 2drop ." ?" exit then  \ Invalid opcode
   dup 1 = if ( dascode 1 )    one-op exit then
   dup 4 < if ( dascode 2..3 ) jumping exit then
              ( dascode 4..F ) two-op ;

\ ----- User words
: cdas ( -- )     base @ >r hex  begin  das+  key bl <> until  r> base ! ;
: mdas ( adr -- ) dasadr !  cdas ;
: das  ( ccc -- ) ' dup @  over cell+ = if  cell+ mdas exit then  drop ;

: keep ( -- )  50 ms  green cclr 50 ms red cclr ;  \ leds off
' keep app !  save  \ Remember disassembler

decimal
\ End

Ansteuerung 8x8 LED-Matrix

Das 8×8 LED-Matrix Display aus Ausgabe 1993-1 der Vierten Dimension geht auch am Launchpad. Dank an Rolf Kretzschmar, der Display und Kabel zur Verfügung stellte. Die Verdrahtung ist wie folg:

Flachbandkabel Launchpad Steckerleiste
FB:2 (MODE) J1:6 (P1.4)
FB:3 (I0) J1:7 (P1.5)
FB:5 (I1) J1:8 (P2.0)
FB:7 (I2) J1:9 (P2.1)
FB:9 (I3) J1:10 (P2.2)
FB:11 (I4) J2:11 (P2.3)
FB:13 (I5) J2:12 (P2.4)
FB:15 (I6) J2:13 (P2.5)
FB:17 (I7) J2:14 (P1.6)
FB:19 (WRITE) J2:15 (P1.7)
FB:21 (GND) J2:20 (GND)

Damit das MODE-Signal auf dem Flachbandkabel, Pin 2 landet, muss auf der Rückseite des Boards eine kleine Lötbrücke gesetzt werden.
Zusätzliche Infos, siehe auch die Dokumentation zum Treiber—IC ICM7218.

\ Everything below is in HEX!!!
HEX

22 CONSTANT p1dir
2A CONSTANT p2dir

: databus!  ( x -- ) \ set data lines i0..i7
   60 P1 CCLR  3F P2 CCLR    \ clear P1.5,P1.6, P2.0..P2.5
   DUP 1 RSHIFT 3F AND P2 CSET   \ set bits I1..I6 as P2.0..P2.5
   DUP 1 AND 5 LSHIFT
   SWAP 80 AND 1 RSHIFT OR P1 CSET ;  \ set bits I0,I7 as P1.5,P1.6
: write  ( -- ) \ pull write signal low one cycle
   80 P1 2DUP CTOGGLE  CTOGGLE ;  \ toggle P1.7=WRITE
: data!  ( x -- ) \ data write
   databus!  write ;
: pixels! ( x -- )  \ when writing raw LED pixel data, ICM7218A inverts bit7!
   80 XOR data! ;
: ctrl!  ( x -- ) \ write control byte (i.e. write with mode=high)
   10 P1 2DUP CTOGGLE   \ toggle P1.4=MODE
   ROT data!  CTOGGLE ;

: ledinit  ( -- )  \ initialize LED pins
   80 P1 CSET  10 P1 CCLR  \ inactive state is WRITE=1 MODE=0
   0F0 p1dir CSET  3F p2dir CSET ;  \ P1.4..P1.7, P2.0..P2.5 as output

: framestart  ( -- ) \ start transmission of new frame,
   \ LEDs stay off, until 8 new data-bytes transferred!
   0F0 ctrl! ;
: bounds  ( c-addr1 n  -- c-addr2 c-addr1 )  OVER + SWAP ;
: frame  ( c-addr -- )  \ write 8 bytes starting at c-addr to LED
   \ note: inverting bit8, as ICM7218 treats that specially
   framestart  8 bounds DO I C@ pixels! LOOP ;
: pattern  ( x -- )  PAD 8 ROT FILL  PAD frame ;

Um Conways Game of Life (Beispile weiter oben) auf das Display zu bekommen, braucht man zusätzlich noch folgende Zeilen:

HEX
: display  ( -- )  \ write (part of) Life universe to display
   framestart 0A 2 DO I line @ 8 RSHIFT pixels! LOOP ;
: ledlife  ( -- ) \ run life with output to LED display until key <q> pressed
   BEGIN
      display     nextgen  80 MS 
   KEY? IF KEY [char] q = ELSE 0 THEN UNTIL ;

Ein 8x8 Bitmap-Font

Dieser Font kostet etwa 1.5kB Flash und liefert 8×8 Bitmap Daten für alle ASCII und Latin-1 Zeichen (also inklusive Umlaute etc.). Das Wort GLYPH gibt einen Zeiger auf die Daten eines Zeichens. Jedes Zeichen hat 8 Bytes, jedes Byte ist dabei eine Zeile, Reihenfolge oben nach unten, Bit7 ist der linke, Bit0 der rechte Rand.

\ Actual font data taken from Allegro 4 (alleg.sf.net), copyright (c) the
\ allegro authors, License: gift-ware.
\
\ This font occupies 1.5 k of flash memory.  You may want to remove the
\ latin-1 part and only keep the ASCII half to strip it down to 768 bytes.
HEX 

: glyph,  ( "num"x8 -- )  \ avoid searching dictionary when parsing data below
   8 0 DO 0 0 BL WORD COUNT >NUMBER 2DROP DROP IC, LOOP ;

IHERE  ( s: font-addr)
glyph, 00 00 00 00 00 00 00 00  \ char 0x20
glyph, 18 3C 3C 18 18 00 18 00
glyph, 6C 6C 6C 00 00 00 00 00
glyph, 6C 6C FE 6C FE 6C 6C 00
glyph, 18 7E C0 7C 06 FC 18 00
glyph, 00 C6 CC 18 30 66 C6 00
glyph, 38 6C 38 76 DC CC 76 00
glyph, 30 30 60 00 00 00 00 00
glyph, 18 30 60 60 60 30 18 00
glyph, 60 30 18 18 18 30 60 00
glyph, 00 66 3C FF 3C 66 00 00
glyph, 00 18 18 7E 18 18 00 00
glyph, 00 00 00 00 00 18 18 30
glyph, 00 00 00 7E 00 00 00 00
glyph, 00 00 00 00 00 18 18 00
glyph, 06 0C 18 30 60 C0 80 00
glyph, 7C CE DE F6 E6 C6 7C 00
glyph, 30 70 30 30 30 30 FC 00
glyph, 78 CC 0C 38 60 CC FC 00
glyph, 78 CC 0C 38 0C CC 78 00
glyph, 1C 3C 6C CC FE 0C 1E 00
glyph, FC C0 F8 0C 0C CC 78 00
glyph, 38 60 C0 F8 CC CC 78 00
glyph, FC CC 0C 18 30 30 30 00
glyph, 78 CC CC 78 CC CC 78 00
glyph, 78 CC CC 7C 0C 18 70 00
glyph, 00 18 18 00 00 18 18 00
glyph, 00 18 18 00 00 18 18 30
glyph, 18 30 60 C0 60 30 18 00
glyph, 00 00 7E 00 7E 00 00 00
glyph, 60 30 18 0C 18 30 60 00
glyph, 3C 66 0C 18 18 00 18 00
glyph, 7C C6 DE DE DC C0 7C 00
glyph, 30 78 CC CC FC CC CC 00
glyph, FC 66 66 7C 66 66 FC 00
glyph, 3C 66 C0 C0 C0 66 3C 00
glyph, F8 6C 66 66 66 6C F8 00
glyph, FE 62 68 78 68 62 FE 00
glyph, FE 62 68 78 68 60 F0 00
glyph, 3C 66 C0 C0 CE 66 3A 00
glyph, CC CC CC FC CC CC CC 00
glyph, 78 30 30 30 30 30 78 00
glyph, 1E 0C 0C 0C CC CC 78 00
glyph, E6 66 6C 78 6C 66 E6 00
glyph, F0 60 60 60 62 66 FE 00
glyph, C6 EE FE FE D6 C6 C6 00
glyph, C6 E6 F6 DE CE C6 C6 00
glyph, 38 6C C6 C6 C6 6C 38 00
glyph, FC 66 66 7C 60 60 F0 00
glyph, 7C C6 C6 C6 D6 7C 0E 00
glyph, FC 66 66 7C 6C 66 E6 00
glyph, 7C C6 E0 78 0E C6 7C 00
glyph, FC B4 30 30 30 30 78 00
glyph, CC CC CC CC CC CC FC 00
glyph, CC CC CC CC CC 78 30 00
glyph, C6 C6 C6 C6 D6 FE 6C 00
glyph, C6 C6 6C 38 6C C6 C6 00
glyph, CC CC CC 78 30 30 78 00
glyph, FE C6 8C 18 32 66 FE 00
glyph, 78 60 60 60 60 60 78 00
glyph, C0 60 30 18 0C 06 02 00
glyph, 78 18 18 18 18 18 78 00
glyph, 10 38 6C C6 00 00 00 00
glyph, 00 00 00 00 00 00 00 FF
glyph, 30 30 18 00 00 00 00 00
glyph, 00 00 78 0C 7C CC 76 00
glyph, E0 60 60 7C 66 66 DC 00
glyph, 00 00 78 CC C0 CC 78 00
glyph, 1C 0C 0C 7C CC CC 76 00
glyph, 00 00 78 CC FC C0 78 00
glyph, 38 6C 64 F0 60 60 F0 00
glyph, 00 00 76 CC CC 7C 0C F8
glyph, E0 60 6C 76 66 66 E6 00
glyph, 30 00 70 30 30 30 78 00
glyph, 0C 00 1C 0C 0C CC CC 78
glyph, E0 60 66 6C 78 6C E6 00
glyph, 70 30 30 30 30 30 78 00
glyph, 00 00 CC FE FE D6 D6 00
glyph, 00 00 B8 CC CC CC CC 00
glyph, 00 00 78 CC CC CC 78 00
glyph, 00 00 DC 66 66 7C 60 F0
glyph, 00 00 76 CC CC 7C 0C 1E
glyph, 00 00 DC 76 62 60 F0 00
glyph, 00 00 7C C0 70 1C F8 00
glyph, 10 30 FC 30 30 34 18 00 
glyph, 00 00 CC CC CC CC 76 00
glyph, 00 00 CC CC CC 78 30 00
glyph, 00 00 C6 C6 D6 FE 6C 00
glyph, 00 00 C6 6C 38 6C C6 00
glyph, 00 00 CC CC CC 7C 0C F8
glyph, 00 00 FC 98 30 64 FC 00
glyph, 1C 30 30 E0 30 30 1C 00
glyph, 18 18 18 00 18 18 18 00
glyph, E0 30 30 1C 30 30 E0 00
glyph, 76 DC 00 00 00 00 00 00 
glyph, 00 10 38 6C C6 C6 FE 00 \ char 0x7F
\ Mind the gap!
glyph, 18 18 00 18 18 18 18 00 \ char 0xA1 (NOT 0xA0!)
glyph, 18 18 7E C0 C0 7E 18 18
glyph, 38 6C 64 F0 60 E6 FC 00
glyph, 00 C6 7C C6 C6 7C C6 00
glyph, CC CC 78 FC 30 FC 30 30
glyph, 18 18 18 00 18 18 18 00
glyph, 3E 61 3C 66 66 3C 86 7C
glyph, 00 C6 00 00 00 00 00 00
glyph, 7E 81 9D A1 A1 9D 81 7E
glyph, 3C 6C 6C 3E 00 7E 00 00
glyph, 00 33 66 CC 66 33 00 00
glyph, 00 00 00 FC 0C 0C 00 00
glyph, 00 00 00 7E 00 00 00 00
glyph, 7E 81 B9 A5 B9 A5 81 7E
glyph, FF 00 00 00 00 00 00 00
glyph, 38 6C 6C 38 00 00 00 00
glyph, 30 30 FC 30 30 00 FC 00
glyph, 70 18 30 60 78 00 00 00
glyph, 78 0C 38 0C 78 00 00 00
glyph, 0C 18 30 00 00 00 00 00
glyph, 00 00 33 33 66 7E C0 80
glyph, 7F DB DB 7B 1B 1B 1B 00
glyph, 00 00 00 18 18 00 00 00
glyph, 00 00 00 00 00 18 0C 38
glyph, 18 38 18 18 3C 00 00 00
glyph, 38 6C 6C 38 00 7C 00 00
glyph, 00 CC 66 33 66 CC 00 00
glyph, C3 C6 CC DB 37 6F CF 03
glyph, C3 C6 CC DE 33 66 CC 0F
glyph, E1 32 E4 3A F6 2A 5F 86
glyph, 30 00 30 60 C0 CC 78 00
glyph, 18 0C 38 6C C6 FE C6 00
glyph, 30 60 38 6C C6 FE C6 00
glyph, 7C 82 38 6C C6 FE C6 00
glyph, 76 DC 38 6C C6 FE C6 00
glyph, C6 00 38 6C C6 FE C6 00
glyph, 10 28 38 6C C6 FE C6 00
glyph, 3E 6C CC FE CC CC CE 00
glyph, 78 CC C0 CC 78 18 0C 78
glyph, 30 18 FE C0 FC C0 FE 00
glyph, 0C 18 FE C0 FC C0 FE 00
glyph, 7C 82 FE C0 FC C0 FE 00
glyph, C6 00 FE C0 FC C0 FE 00
glyph, 30 18 3C 18 18 18 3C 00
glyph, 0C 18 3C 18 18 18 3C 00
glyph, 3C 42 3C 18 18 18 3C 00
glyph, 66 00 3C 18 18 18 3C 00
glyph, F8 6C 66 F6 66 6C F8 00
glyph, FC 00 CC EC FC DC CC 00
glyph, 30 18 7C C6 C6 C6 7C 00
glyph, 18 30 7C C6 C6 C6 7C 00
glyph, 7C 82 7C C6 C6 C6 7C 00
glyph, 76 DC 7C C6 C6 C6 7C 00
glyph, C6 00 7C C6 C6 C6 7C 00
glyph, 00 C6 6C 38 6C C6 00 00
glyph, 3A 6C CE D6 E6 6C B8 00
glyph, 60 30 C6 C6 C6 C6 7C 00
glyph, 18 30 C6 C6 C6 C6 7C 00
glyph, 7C 82 00 C6 C6 C6 7C 00
glyph, C6 00 C6 C6 C6 C6 7C 00
glyph, 0C 18 66 66 3C 18 3C 00
glyph, E0 60 7C 66 66 7C 60 F0
glyph, 78 CC CC D8 CC C6 CC 00
glyph, E0 00 78 0C 7C CC 7E 00
glyph, 1C 00 78 0C 7C CC 7E 00
glyph, 7E C3 3C 06 3E 66 3F 00
glyph, 76 DC 78 0C 7C CC 7E 00
glyph, CC 00 78 0C 7C CC 7E 00
glyph, 30 30 78 0C 7C CC 7E 00
glyph, 00 00 7F 0C 7F CC 7F 00
glyph, 00 00 78 C0 C0 78 0C 38
glyph, E0 00 78 CC FC C0 78 00
glyph, 1C 00 78 CC FC C0 78 00
glyph, 7E C3 3C 66 7E 60 3C 00
glyph, CC 00 78 CC FC C0 78 00
glyph, E0 00 70 30 30 30 78 00
glyph, 38 00 70 30 30 30 78 00
glyph, 7C C6 38 18 18 18 3C 00
glyph, CC 00 70 30 30 30 78 00
glyph, 08 3C 08 7C CC CC 78 00
glyph, 00 F8 00 F8 CC CC CC 00
glyph, 00 E0 00 78 CC CC 78 00
glyph, 00 1C 00 78 CC CC 78 00
glyph, 78 CC 00 78 CC CC 78 00
glyph, 76 DC 00 78 CC CC 78 00
glyph, 00 CC 00 78 CC CC 78 00
glyph, 30 30 00 FC 00 30 30 00
glyph, 00 02 7C CE D6 E6 7C 80
glyph, 00 E0 00 CC CC CC 7E 00
glyph, 00 1C 00 CC CC CC 7E 00
glyph, 78 CC 00 CC CC CC 7E 00
glyph, 00 CC 00 CC CC CC 7E 00
glyph, 18 30 CC CC CC 7C 0C F8
glyph, F0 60 7C 66 7C 60 F0 00 
glyph, 00 CC 00 CC CC 7C 0C F8 \ char 0xFF

( s: font-addr) CONSTANT font8

: glyphidx  ( char -- n )  \ return index of glyph in font8 table
   DUP 20 80 WITHIN IF 20 - EXIT THEN     \ ascii range
   DUP 0A1 100 WITHIN IF 041 - EXIT THEN  \ latin-1 range
   20 ;  \ unknown chars mapped to white-space
: glyph  ( char -- c-addr ) \ return 8x8 glyph of char
   \ format: top to bottom, bit7=left-most, bit0=right-most bit
   glyphidx 3 LSHIFT font8 + ;

Ein Ticker via 8x8 LED-Matrix

Nachdem man den LED-Treiber und 8×8 Font aus den Beispielen oben geladen hat, kann man folgenden Ticker laden. Dieser benutzt außer dem Stack keinen RAM! Der Bildschirminhalt wird dynamisch generiert und direkt ohne Video-RAM als Zwischenspeicher an das Display geschickt.

Der 8×8 Font im Format für PC-Displays ist aus Sicht des LED-Displays gespiegelt. Als Ausgleich schicken wir die Zeilen in umgekehrter Reihenfolge, das Display muss, damit das passt, dann auf dem Kopf stehen. Beenden tut man den Ticker per Tastendruck q vom Terminal.

\ Display text ticker on 8x8 LED panel
\ Copyright (C) David Kuehling <dvdkhlng TA gmx TOD de> 2012
\
\ Created: May 2012       License: GPLv2+; NO WARRANTY
\
\ NOTE: LOAD ./leds.FS AND ./font8.fs FIRST!

: char@  ( c-addr u idx -- char )  \ get char from string, space when outside
   TUCK U> IF  + C@ EXIT THEN
   2DROP BL ;
: 3dup  ( x1 x2 x3 -- x1 x2 x3 x1 x2 x3 )
   DUP 2OVER ROT ;
: char@2  ( c-addr u idx -- char1 char2 )  \ get char and next char
   3dup 1+ char@ >R  char@ R> ;
: glyph@2  ( char1 char2 row -- x )  \ fetch row from 2 glpyhs into 16-bit
   >R glyph R@ + C@
   SWAP glyph R> + C@  8 LSHIFT OR ;

: ticker-row  ( c-addr u offset row -- x )
   OVER >R >R
   3 RSHIFT char@2  R> glyph@2	\ get glyph of two chars at idx offs/8
   8 R> 7 AND - RSHIFT ;        \ shift by offs%8 and return high part
: ticker-frame  ( c-addr u offset -- )
   framestart
   0 7 DO		\ Rolf's display is mirrored, we mirror by row 
      3dup I ticker-row pixels!
   -1 +LOOP
   2DROP DROP ;
: ticker  ( c-addr u -- )
   BEGIN
      DUP 8 * 0 DO
         2DUP I ticker-frame 28 MS
         KEY? IF KEY [CHAR] q = IF 2DROP UNLOOP EXIT THEN THEN
      LOOP
   AGAIN ;

\ Beispiel:
: msg IS"  Forth-Gesellschaft e.V.-Wir programmieren Forth  " ;
ledinit msg ticker