% Content-encoding: UTF-8
\documentclass[ngerman]{article}
\usepackage[utf8]{inputenc}
\usepackage{multicol,babel}
\setcounter{secnumdepth}{0}
\setcounter{tocdepth}{0}

\begin{document}

\title{Assembler in amforth}
\author{Matthias Trute}
\maketitle

\section{Zusammenfassung}

Amforth verfügt über einen in Forth geschriebenen Assembler, der von Lubos Pekny 
programmiert und der Community bereitgestellt wurde. Um seine Arbeit zu 
unterstützen, erhielt das Kernsystem die Worte \verb|code| und \verb|end-code|.
Dieser Artikel soll Lust auf mehr machen, ohne alle Details zu beleuchten.

\begin{multicols}{2}

Jedes Forth braucht einen Assembler. Heißt es. Bei amforth ist
der Assembler ein optionales und nachladbares Modul und ist selbst in
Forth geschrieben.

Das amforth--Kernsystem ist um zwei Worte erweitert worden, die für die Erzeugung
der Codeworte und deren Einbindung in den inneren Interpreter zuständig sind: 
\verb|code| und \verb|end-code|. Jedes dieser Worte benutzt Informationen, 
die nur zur Übersetzungszeit des Quellcodes portabel verfügbar sind: die 
Einsprungadresse des inneren  Interpreters \verb|NEXT| und das in verschiedenen 
Definitionsworten genutzte \verb|(DOCREATE)|, mit dem sich der Rumpf eines 
Dictionaryeintrags erstellen lässt.

Alle weiteren Assemblerbefehle sind als Forth--Code umgesetzt. Die 
Syntax lehnt sich dabei sowohl an die Konventionen des AVR--Assemblers 
als auch an Forth an. Dieser Artikel beruht auf der Version 1.1 des 
Assemblers.

Hier auch gleich eine Warnung: Der Einsatz des Assemblers erfordert etwas mehr als 
nur grundlegende Kenntnisse der Mikrocontroller und der Interna von amforth. Der 
Programmtext ist nicht portabel, etwa zu PC--Forths, und, wenn man nicht aufpasst,
noch nicht einmal zwischen verschiedenen AVR--Controllertypen.

\section{AVR Atmega}

Die AVR--Atmega--Mikrocontroller der Firma Atmel sind kleine System--On--A--Chip--Bausteine
die aus einem Kernsystem und einer Anzahl von Hardwaremodulen bestehen. 
Das Kernsystem umfasst den Registersatz, die Instruktionslogik, RAM, EEPROM und 
Flashspeicher. Je nach Typ sind unterschiedliche Speichervolumina 
vorgesehen, wobei RAM und EEPROM eher knapp bemessen sind (wenige KB), Flash dagegen
vergleichsweise viel vorhanden sein kann (derzeit bis zu 256KB).

Die Architektur hat zudem die Besonderheit, dass direkt ausführbarer Maschinencode ausschließlich
im Flash sein kann. Andere Speichertypen können dafür nicht genutzt werden. 

Der Registersatz besteht aus 32 8--bit--Registern, von denen einige als 
16--bit--Registerpaare genutzt werden können. In amforth werden 8 der 32 Register ausdrücklich
für den Einsatz in Code--Wörtern bereitgehalten (Im Quellcode als temp1 bis temp8 bezeichnet). 
Diese können problemlos innerhalb eines Wortes benutzt werden. Alle anderen Register (und 
dazu gehören auch die 4 Registerpaare) müssen am Ende des Assemblercodes wiederhergestellt 
werden, sollten sie genutzt werden. Wirklich ungenutzt sind derzeit nur 4 Register, was sich 
aber im Zuge der bei der Euroforth2008 diskutierten Erweiterungen der Forth--VM ändern kann\footnote{Derzeit sind die A/B--Register implementiert, die X/Y harren noch der weiteren Entwicklung.}.

Der Befehlssatz orientiert auf Register/Register--Operationen. Dabei sind
die meisten Befehle für alle oder viele Register einsetzbar. Einige wenige
Befehle benutzen feststehende Register, etwa die Hardwaremultiplikation
oder der Zugriff auf den Flash.

An dieses Kernsystem hat Atmel je nach Typ weitere Module angebaut. Dabei sind
einige Module bei allen Controllern vorhanden (z.~B.\ serielle Schnittstelle
oder A/D--Wandler), andere Module sind nur bei einigen Typen vorhanden (CAN-- oder USB--Schnittstellen, LCD--Ansteuerung).

Die Module werden über definierte (leider bei jedem Typ andere) Adressen
für die Konfiguration und die Daten angesprochen. Einige Module bieten die Möglichkeit, 
Interrupts auszulösen, die beim Eintreffen der Interruptbedingung aktiviert
werden. Genau diese Interrupts sind neben Geschwindigkeitsoptimierungen der Grund,
den Assembler einzusetzen: Nicht alle Interrupts lassen sich mit der in amforth
enthaltenen generischen Interrupt--Service--Routine behandeln, da sie spezielle
Aktionen ausführen müssen, die der Hardware signalisieren, dass der Interrupt
bearbeitet wurde. Unterlässt man diese Signalisierung, löst die Hardware umgehend
einen neuen Interrupt aus, was das System faktisch lahmlegt.

\section{Syntax}

Der Assembler nutzt die Postfix--Notation, die für Forth so typisch
ist. Zuerst werden die Operanden angegeben und danach die Kommandos.
Die Namen selbst entstammen den AVR--Definitionen. Die Kommandoworte
haben ein Komma als Suffix, um zu signalisieren: Hier wird in das 
Dictionary geschrieben.

\begin{center}
\begin{tabular}{|c|c|}
\hline
   AVR--Standard    &     amforth  \\
   \hline\hline
    ldi R16, 10  &      R16 10 ldi, \\
    subi R8, 1   &      R8 1 subi, \\
    ld R17, Y+   &      R17 Y+ ld, \\
\hline
\end{tabular}
\end{center}

Der Assembler schreibt das Ergebnis seiner Arbeit direkt in das Dictionary
und setzt den \verb|HERE|--Pointer entsprechend weiter. Bei Sprüngen wird das
Sprungziel ggf.\ nachträglich eingearbeitet, die erforderlichen Angaben werden
auf dem Datenstack verwaltet.

\section{Codeworte}

Codewörter hießen früher Primitive und stellen in Maschinencode
geschriebene und für den Forthinterpreter nutzbare 
Worte dar. Ein einfaches Codewort ist das folgende:

\begin{quote}
\begin{verbatim}
code dup
  -Y R23 st,
  -Y R22 st,
end-code
\end{verbatim}
\end{quote}

Im Beispiel wird ein Dictionaryeintrag \verb|dup| erzeugt, dessen Execution Token
auf den eigenen (noch leeren)  Datenbereich zeigt. Im Unterschied zu Definitionswort
\verb|:| (COLON) wird nicht in den Compile-Modus umgeschaltet, die Assemblerworte werden
ganz normal interpretiert. Dabei werden auf dem Datenstack die Operanden zusammengestellt, die
die Assemblerworte dann passend in den Flash compilieren. 

Die Register R22/R23 bilden das Top--Of--Stack--Element des Datenstacks,
das Registerpaar Y ist der Datenstackpointer. Die Mnemonic \verb|st| bezeichnet
die Storeoperation (Speichern von Registerinhalten in RAM). \verb|-Y| 
signalisiert einen Auto-Decrement des Y--Registerpaars.

Abgeschlossen wird eine code--Definition mit dem Wort \verb|end-code|, das den obligatorischen 
Sprung in den inneren Interpreter compiliert.

\section{Interrupts}

Etwas interessanter wird es, wenn man Interruptroutinen im Assembler
umsetzen will oder muss. Das kann, wie eingangs erwähnt, erforderlich sein, 
um innerhalb der Interruptbearbeitung gewisse Hardwareflags zurückzusetzen, was
bei in Forth geschriebenen Interruptroutinen nicht möglich ist.

Da Interruptroutinen nicht direkt vom Forth-Interpreter aus aufrufbar 
sein sollen, legt man am einfachsten einen headerlosen Eintrag im 
Dictionary an, wobei die Startadresse gespeichert werden sollte.

\begin{quote}
\begin{verbatim}
  here
  R18 push,
  R18 3F in,

  ...

  3F R18 out,
  R18 pop,
  reti,

  940c 6 i!
       7 i!
\end{verbatim}
\end{quote}

Zuerst wird die Startadresse ermittelt (\verb|here|). Anschließend wird der 
Assemblercode in das Dictionary compiliert und am Ende die eingangs 
gespeicherte Startadresse als Ziel eines Sprungelements in die 
Interrupttabelle des Controllers geschrieben.

940c ist die AVR--Codierung für den absoluten Sprungbefehl, die Adresse 6 ist für 
den Interrupt 3 zuständig. Bei den meisten Atmega--Typen werden zwei Flashzellen 
für jeden Interruptvektor benötigt: Eine für den Befehlscode (940c) und eine für 
die Adresse. Bei kleineren Typen (ATmega8) ist nur eine Flashzelle pro Vektor
vorgesehen, dann muss man einen relativen Sprung codieren, der in einer Flashzelle
sowohl den Befehlscode als auch die Sprungdistanz enthält.

\section{Wer nutzt es?}

Lubos Pekny selbst hat mit diesem Assembler einen kleinen
Stand--Alone--Rechner mit einem 4--zeiligem Text--LCD und einer 
PS/2--Standard--Tastatur gebaut. Details und ein Video sind im Internet über
\url{www.forth.cz/download.html} zu finden.

Michael Kalus hat sich die Mühe gemacht, den Assembler systematisch auf Herz 
und Nieren zu prüfen; Ergebnis: alles funktioniert korrekt.

\end{multicols}
\end{document}