Assembler-Kurs Teil 8
Der Stapel
 Der  Stapel  ist  der  Speicherbereich  von
$0100 bis $01 FF, der  direkt  von  der  CPU
 verwaltet  wird. Der  Zugriff  auf  diese
" Page 1" ist  daher  sehr  schnell.
 Ein  Stapel  wächst  von  oben  nach  unten, d. h. in  unserem  Fall, daß  zuerst  ein
 Byte  in $01 FF, das  nächste  in $01 FE  usw.
 abgelegt  wird.
 Bemerkenswert  ist  beim  Stapel, daß  er
 nach  dem  LIFO-Prinzip ( last  infirst
 out) arbeitet. Das  zuletzt  auf  den
 Stapel  gebrachte  Byte  muß  also  als
 erstef  wieder  heruntergeholt  werden, wenn  die  darunterliegenden  Bytes  gelesen
 werden  sollen.
 Wenn  Sie  Schwierigkeiten  haben, sich  das
 LIFO-Prinzip  zu  verdeutlichen, dann
 denken  Sie  sich  den  Stapel  doch  einfach
 als  einen  Stapel  von  Getränkekisten.
 Dann  wird  Ihnen  klar, daß  Sie  keinesfalls  eine  der  unteren  Kisten  herausziehen  können. Die  zuletzt  auf  den
 Stapel  gebrachte  Kiste, das  ist  die
 Kiste  ganz  oben  auf  dem  Stapel, muß  als
 erste  entfernt  werden, um  an  die
 darunterliegende  Kiste  heranzukommen.
 Aber  woher  weiß  der  Computer, welcher
 Wert  auf  dem  Stapel  der  oberste  ist?
 Dazu  benötigen  wir  einen  Zeiger
( Pointer), der  auf  die  jeweilige  Spitze
 des  Stapels  zeigt, den  sogenannten
 Stapelzeiger ( Stackpointer) . Unser
 Stapelzeiger  ist 8 Bits  breit, also
 eigentlich  um  ein  Byte  zu  klein  für  die
 Adressierung  des  Stapelbereichs ($01 FF
 bis $0100) . Sicher  fällt  Ihnen  auf, daß
 das  höherwertige  Byte ($01) über  dem
 gesamten  Stapelbereich  gleich  bleibt. Es
 ist  daher  unnötig, diesen  Wert  immer  im
 Stapelzeiger  mit  abzuspeichern. Stattdessen  begnügt  man  sich  damit, daß  der
 Stapelzeiger  das  niederwertige  Byte  der
 Spitze  des  Stapels  enthält.
 Die  effektive  Adresse, auf  die  der
 Zeiger  deutet, kann  man  sich  folglich
 mit $0100+( Inhalt  des  Stapelzeigers) selbst  errechnen.
 Gültige  Werte  sind  für  den  Stapelzeiger
 zwischen $00 und $ FF.
 So, jetzt  wissen  wir, wie  ein  Stapel
 funktioniert, aber  wozu  man  einen  Stapel
 benötigt, das  wissen  wir  noch  nicht.
 Es  gibt  drei  wichtige  Anwendungsgebiete
 für  einen  Stapel:
1 . Er  übernimmt  die  Verwaltung  der  Rücksprungadressen  bei  Unterprogrammaufrufen. Diese  Eigenschaft  wird  später
 noch  näher  erläutert  werden.
2 . Zur  Zwischenspeicherung  von  Daten  bei
 Interrupts.
3 . Kurzzeitige  Sicherung  von  Daten.
 Diesen  letzte  Punkt  wollen  wir  uns
 nun  genau  ansehen.
Die Stapeloperationen
 Im  Grunde  genommen  benötigt  man  nur  zwei
 verschiedene  Stapelbefehle, nämlich
 einen, um  etwas  auf  den  Stapel  zulegen
( PUSH) und  einen, um  einen  Wert  vom
 Stapel  zu  holen ( PULL, POP) .
Stapel        nach PUSH      nach PUSH  
vorher:       Wert1:         Wert2:     
$01FD |  |       |  |        |  |<-SP   
      |  |       |--|        |--|       
$01FE |  |       |  |<-SP    |W2|       
      |--|       |--|        |--|       
$01FF |  |<-SP   |W1|        |W1|       
      ----       ----        ----       
 Wenn  noch  keine  Stapeloperationen  durchgeführt  wurden, zeigt  der  Stapelzeiger
 SP  auf  die  Speicherstelle $01 FF. Nun
 wird  WERT1 auf  den  Stapel  gelegt, und
 zwar  an  der  Adresse, auf  die  der
 Stapelzeiger  weist. Nach  diesem  Schreib- zugriff  auf  den  Stapel  wird  der  Stapelzeiger  um 1 erniedrigt  und  zeigt  so  auf
 die  nächste  freie  Speicherstelle  des
 Stapels ($01 FE) . Eine  weiters  PUSH-Anweisung  legt  den  WERT2 auf  die  Spitze
 des  Stapels ( Top  of  Stack), was  der
 Position  des  Stapelzeigers  entspricht.
 Anschließend  erniedrigt  sich  der
 Stapelzeiger  wieder  um 1 .
 nach  PULL  Wert2 : nach  PULL  Wert1 :
$01FD |  |            |  |              
      |  |            |--|              
$01FE |  |<-SP        |  |              
      |--|            |--|              
$01FF |W1|            |  |<-SP          
      ----            ----              
 Während  bei  PUSH-Befehlen  des  Stapels  zu
 beobachten  war, daß  sich  der  Stapelzeiger  erst  nach  dem  Schreiben  in  die
 Stapelspeicherstelle  erniedrigt  hat, stellt  man  nun  fest, daß  bei  PULL- Anweisungen  der  Stapelzeiger  vor  dem
 Lesezugriff  inkrementiert  wird. Das  ist
 auch  nötig, da  der  Stapelzeiger  immer
 auf  die  nächste  FREIE  Speicherstelle  des
 Stapels  zeigt. Wird  der  Stapelzeiger
 zuvor  inkrementiert, dann  weist  er  nun
 auf  das  zuletzt  auf  den  Stapel  geschobene  Element. Dieser  Wert  kann  nun
 vom  Stapel  geholt  werden.
 Das  obige  Beispiel  verdeutlicht, daß  der
 zuerst  auf  den  Stapel  gelegte  Wert
( Wert1) erst  als  letzter  vom  Stapel
 geholt  werden  kann.
 Die  PUSH-Befehle 
PHA (PusH Accumulator)
 Mit  diesem  Befehl  wird  der  Akkuinhalt
 auf  den  Stapel  geschoben. Anschließend
 wird  der  Stapelzeiger  um 1 erniedrigt.
 Der  Akkuinhalt  bleibt  dabei, ebenso  wie
 die  Flaggen, unverändert.
PHP (PusH Processor status)
 Anstelle  des  Akkuninhaltes  wird  jetzt
 das  gesamte  Statusregister  des
 Prozessors  mit  allen  Flaggen  auf  den
 Stapel  gelegt.
 Danach  wird  der  Stapelzeiger  um 1 erniedrigt.
 Die  PULL-Befehle 
PLA (PuLl Accumulator)
 Diese  Anweisung  ist  das  Gegenstück  zu
 PHA. Der  Stapelzeiger  wird  zuerst  um 1 erhöht. Nun  wird  der  Inhalt  der
 Speicherstelle, auf  die  der  Stapelzeiger
 deutet, in  den  Akku  eingelesen. Neben
 dem  Akkuinhalt  können  sich  auch  die
 Flaggen  N  und  Z  ändern.
PLP (PuLl Processor status)
 Der  Stapelzeiger  wird  inkrementiert  und der  aktuelle  Stapelwert  wird  in  das
 Prozessor-Statusregister  übertragen.
 Dabei  ändern  sich  selbstverständlich
 alle  Flaggen.
 Zusätzlich  zu  den  Stapeloperationen  gibt
 es  auch  noch  zwei  Transferbefehle, die
 sich  direkt  auf  den  Stapelzeiger
 beziehen.
 Die  Stapel-Transferbefehle 
TSX (Transfer Stackpointer to X)
 Wie  der  Name  der  Anweisung  schon  aussagt, überträgt  der  Befehl  den  Stapelzeiger  in  das  X-Register. Somit  kann  die
 Adresse, auf  die  der  Stapelzeiger  weist, ermittelt  werden. Das  X-Register  kann
 dementsprechend  Werte  zwischen $00 und
$ FF  enthalten.
TXS (Transfer X to Stackpointer)
 Dieser  gegenteilige  Befehl  zu  TSX  ermöglicht  eine  direkte  Einflußnahme  des
 Programmierers  auf  den  Stapelzeiger, indem  der  Wert  des  X-Registers  in  den
 Stapelzeiger  übertragen  wird.
 Alle  oben  genannten  Befehle  können  nur
 impliziert  adressiert  werden, d. h. sie
 haben  keinen  Adressteil.
Der Einsatz des Stapels bei Unterprg's --------------------------------------
 Bisher  haben  wir  es  als  selbstverständlich  erachtet, daß  unser  Assembler-Programm  nach  einem  RTS  bei  der  nächsten
 Anweisung  nach  dem  JSR  im  aufrufenden
 Programm  fortfährt.
 Das  Programm  hat  sich  also  die  Stelle, von  der  aus  der  Unterprogrammsprung  erfolgte, gemerkt. Womit  wir  wieder
 beim  Stapel  wären.
 Der  Prozessor  muß  also  vor  jeder  Verzweigung  in  ein  Unterprogramm  die momentane  Adresse  auf  den  Stapel
 speichert, was  der  Adresse  die
 Instruktion  nach  dem  JSR-Befehl  entspricht. Diese  Adresse  muß  nach  einer
 speziellen  Regel " gestackt" werden, da
 unser  Stapel  bekanntlich  immer  nur  ein
 Byte  aufnehmen  kann, eine  Adresse  jedoch
 aus 2 Bytes  besteht. Zuerst  wird  das
 höherwertige  Byte  mit  einem  PUSH  auf  den
 Stapel  gelegt  und  anschließend  der
 Stapelzeiger  um 1 vermindert. Erst  jetzt
 wird  das  niederwertige  Byte  auf  den
 Stapel  gebracht. Erneut  muß  der  Stapelzeiger  dekrementiert  werden.
 Hauptprogramm  Unterprogramm
       -----------                ----- 
      |     .     |     -> $C200 |  .  |
      |     .     |    |         |  .  |
$C100 | JSR $C200 | ---          |  .  |
$C103 |     .     | <----------- | RTS |
      |     .     |               ----- 
-----------
 Der  Stapel  nach  JSR $ C200 :
      |   |<-SP                         
      |---|                             
$01FE |$03|                             
      |---|                             
$01FF |$C1|                             
      -----                             
 Nach  einem  RTS-Befehl  läuft  der  umgekehrte  Prozess  ab. Zuerst  wird  der
 Stapelzeiger  um 1 erhöht  und  das  niederwertige  Byte  vom  Stapel  geholt. Im
 Anschluß  daran  holt  man  das  höherwertige
 Byte  vom  Stapel  und  der  Stapelzeiger
 wird  wieder  erhöht. nun  kann  die
 aktuelle  Adresse  wieder  zusammengesetzt
 werden  und  das  Hauptprogramm  fährt  mit
 seiner  Abarbeitung  an  der  gewünschten
 Stelle  fort.
 Es  fällt  uns  auf, daß  diese  abgelegten
 Rücksprung-Adressen  denselben  Stapelbereich  belegen, den  auch  der  Programmierer  zur  vorübergehenden  Datenspeicherung  benutzen  kann. Da  der  Speicherbereich  des  Stapels  begrenzt  ist, müssen
 wir  auch  die  Struktur  dieser  automatisch
 ablaufenden  Prozesse  der  Unterprogramm-Verwaltung  kennen.
( rt/ wk)