HCC!Forth

HCC!forth

Forth vanaf de grond 12


Ron Minke

Hier is het twaalfde deel van het verhaal om een AVR Forth systeem te maken met als uitgangspunt “from scratch”. Dit deel gaat over de indeling van het RAM geheugen en de overwegingen daarbij.

      Testing, testing

In de vorige aflevering van ‘Forth vanaf de grond’ heb ik het gehad over de indeling van de bytes bij de stackvolgorde. Na enig heen- en weergeschuif van beslissingen is er nu een stabiele situatie ontstaan die ook aan de ANSI standaard voldoet (zie ANSI document punt 3.1.4). De indeling van diezelfde bytes in de dictionary in het RAM geheugen (weet u nog: deze Forth versie kopieert zichzelf naar RAM) is een geheel andere zaak. Tijdens het testen van de Forth kernel met een aangepaste versie van de John Hayes testsuite bleek er iets fout te gaan met de uitvoering van de woorden 2@ en 2!.

Huidige situatie

De inzichten uit deel 11 van deze serie zijn toegepast. In de Forth-kernel (in het flashgeheugen) is het woord LIT, die het ophalen van een getal uit de dictionary verzorgt, gedefinieerd als:

 0630:
 LIT:
    83          lengte van de tekst 'LIT' met msb=1
    4C          'L'
    49          'I'
    D4          'T' met msb=1
    0620        link naar vorige woord
    0200        pointer naar Code_Lit: machinecode voor LIT

De bijbehorende machinecode:

 0200:
 Code_Lit:
    Ld    R16,X+  ; get lo value from RAM dictionary
    Ld    R17,X+  ; get hi value, auto update IP to next word

    St    -Y,R16  ; store lo value on data stack
    St    -Y,R17  ; store hi value

    Next

Ergens in de dictionary op plaats 730 (in de RAM) staat een stukje Forth welke een getal met de waarde ABCD op de datastack zet:

 0730:
    .dw    xxxx
    .dw    yyyy
    .dw    LIT
    .dw    0xABCD
    .dw    zzzz

Een hexdump van dit stukje RAM laat zien:

 0730:  xx xx yy yy 30 06 CD AB zz zz .......

De machinecode van LIT haalt de waarde ABCD op uit het RAM en plaatst die op de stack.

  Code_Lit      (  ---  n )                         tmp.   data
                                                    AVR    stack
         input            output                    reg.   adres

 SP - > 0                 0                         ---    0x100
        1                 1   n lage byte = CD      R16    0x0FF
        2          SP - > 2   n hoge byte = AB      R17    0x0FE
        3                 3                                0x0FD

Tot op dit punt is er niets bijzonders aan de hand. Forth werkt zoals hij het moet doen.

Inhoud van het RAM

Als we de inhoud van het RAM eens nader bekijken, valt op dat de getalwaarde ABCD “achterstevoren” in het geheugen is terecht­gekomen, evenals de locatie van het woord LIT (=0630). Voor de werking van de Forth maakt dat natuurlijk helemaal niets uit; de bijbehorende stukjes machinecode verwerken de informatie correct. Toch blijft hierover een ontevreden gevoel achter. Een test met een “double” in de assembly sourcecode laat zien:

    .org     0x410
    .dd      0x12345678

Dit komt na vertaling door de Atmel assembler in het flash­geheugen terecht als:

 0410:
     5678
     1234

Deze waarde komt na het kopiëren naar de RAM bij een hexdump eruit te zien als:

 0640:  78 56 34 12 .......

Ook hier staat dit getal voor het gevoel “achterstevoren”. Bovendien valt op dat het “high word” van dit getal achteraan staat. En dat stemt niet overeen met wat in de ANSI-standaard staat.

Testsuite John Hayes

Het vermoeden dat er toch iets niet correct is in de Forth kernel wordt bevestigd door een stukje uit de testsuite van John Hayes. Bij de test van de woorden 2@ en 2! komt er een foutmelding dat de resultaten op de datastack niet kloppen.

De oorspronkelijke code in de Forth kernel voor het woord 2@ is (met toevoeging van de getalwaarden uit de RAM):

 ;   2@                  Replace the 16-bit address on top of
 ;                       the data stack with the 32-bit
 ;   nucleus             contents d of that memory address.
 ;
 ;                       addr   ---   d
 ;
 ;  input                    output
 ;
 ;          0                        0
 ;          1   lo byte addr = 40    1   lo byte lo word d = 78
 ;  Dsp --> 2   hi byte addr = 06    2   hi byte lo word d = 56
 ;          3                        3   lo byte hi word d = 34
 ;          4                Dsp --> 4   hi byte hi word d = 12
 ;          5                        5

 Code_TwoAt:

     Ld      ZH,Y+       ; get hi address          = 06
     Ld      ZL,Y+       ; get lo address          = 40

     Ld      R18,Z+      ; get lo value lo word    = 78
     Ld      R19,Z+      ; get hi value lo word    = 56

     Ld      R16,Z+      ; get lo value hi word    = 34
     Ld      R17,Z+      ; get hi value hi word    = 12

     St      -Y,R18      ; put on data stack
     St      -Y,R19

     St      -Y,R16
     St      -Y,R17

     Next

Als we met deze machinecode de getalwaarde op adres 640 ophalen komt dat in de correcte volgorde op de datastack te staan.

Maar . . . . in de ANSI standaard staat bij de bespreking van het woord 2@ (punt 6.1.0350) het volgende: “. . . . . . . Het woord 2@ is gelijk aan het uitvoeren van de volgende serie woorden” :

 DUP CELL+ @ SWAP @

Als we dit uitvoeren met diezelfde getalwaarde 12345678 op adres 640 in het RAM dan komt er iets geheel anders tevoorschijn. Even naspelen:

  Start             stack

   ---              0640
   DUP              0640  0640
   CELL+            0640  0642
   @                0640  1234
   SWAP             1234  0640
   @                1234  5678

Als we even in herinnering nemen dat (volgens ANSI) de waarde op de bovenste stackpositie de MSD van een getal moet zijn dan zie we dat dit hier niet het geval is. Het resultaat van de John Hayes test is dus correct: de huidige implementatie van het woord 2@ levert niet het juiste resultaat.

Hoe nu verder ?

Om een en ander te laten voldoen aan de ANSI standaard moeten er blijkbaar woorden en bytes worden verwisseld. Voor de machinecode van LIT is dat simpel: de waarde uit het RAM “andersom” ophalen. Maar daarmee hebben we de oorsprong van het probleem nog niet aangepakt. Het werkelijke probleem is de volgorde van de bytes in het flashgeheugen bij de .dw statements.

Een standaard aanroep om een getalwaarde in het flash te zetten

    .org     0x540
    .dw      0xABCD

komt, na assembleren en kopiëren naar het RAM, terecht als

0540: CD AB .......

Maar eigenlijk willen we dat deze waarde hier verschijnt als

0540: AB CD .......

De oplossing hiervoor is gelukkig eenvoudig: het definiëren en daarna toepassen van een macro die de bytevolgorde omdraait bij het assembleren. Deze macro geven we de naam defwrd.

    .macro  defwrd
    .db     high(@0), low(@0)
    .endmacro

De aanroep van het testgetal ABCD wordt dan

    .org     0x540
    defwrd   0xABCD

en na assembleren en kopiëren naar de RAM komt er te staan

 0540:  AB CD .......

Deze wijziging heeft heel veel impact op de assembly sourcecode! Gelukkig heeft de editor een “global replace” functie die dit klusje doet: alle .dw naar defwrd veranderen.

Rest ons nog het aanpassen van de machinecode voor het woord 2@. U ziet: het volgorde van registergebruik is anders.

 
 Code_TwoAt:

    Ld      ZH,Y+       ; get hi address
    Ld      ZL,Y+       ; get lo address

    Ld      R17,Z+      ; get hi value hi word
    Ld      R16,Z+      ; get lo value hi word

    Ld      R19,Z+      ; get hi value lo word
    Ld      R18,Z+      ; get lo value lo word

    St      -Y,R18      ; put on data stack
    St      -Y,R19

    St      -Y,R16
    St      -Y,R17

    Next

Niet alleen de machinecode voor het woord 2@ moet worden aan­gepakt, alle woorden die iets met RAM geheugentoegang doen moeten worden gewijzigd. Het gaat om de woorden

 LIT  EXECUTE  BRANCH  !  @  +!  2@  2!  HERE  (FIND)  RP!  SP!  DoConstant  DoUser  ((EMIT))  NEXT

Na deze veelomvattende actie heb ik de complete sourcecode door de assembler heengehaald en in de flash gezet, de voedings­spanning op de testprint aangesloten en . . . . het werkt!

Snel de John Hayes test uitgevoerd en geheel volgens ver­wachting: geen fouten!

Oef . . . heel veel werk deze keer en dat allemaal door zo’n “simpele fout”. Het is echter wel een volgende stap naar het “Forth vanaf de grond” systeem !


De John Hayes testsuite:

    * ftp://ftp.taygeta.com/pub/Forth/Applications/ANS/core.fr