XMS w Turbo BASIC XL


       Zapewne wielu początkujących koderów stanęło kiedyś przed problemem wykorzystania dodatkowej pamięci w programie napisanym w języku wysokiego poziomu, np. Pascalu, C, czy Action! O ile większość operacji wykonuje się w nich bajecznie prosto, o tyle brakuje kontroli położenia kodu w pamięci, przez co przełączając banki pamięci ryzykujemy zawieszenie komputera.

       Można wykorzystać dodatkową pamięć przy pomocy DOS-owego ramdysku. Niby jest to sposób, ale moim zdaniem nie najlepszy. Wymaga on przepisywania danych do podstawowej pamięci, a konieczność odwoływania się do procedur WEjścia/WYjścia powoduje olbrzymie straty czasu. Nie zawsze też ramdysk ze swoją plikową organizacją idzie nam na rękę.

       Jednak klony Atari BASIC posiadają ciekawą zaletę, którą jest możliwość przeniesienia dolnej granicy kodu w dowolne miejsce pamięci. Tak więc problem... z głowy! Wystarczy ustalić Lomem na $8000 i już możemy cieszyć się łatwym dostępem do całej dodatkowej pamięci. Pamięć poniżej $4000 pozostaje niewykorzystana i można do niej przenieść np. pamięć ekranu, procedury maszynowe, fonty.. W TBXL wolna pamięć zaczyna się nieco poniżej $3800.

Praktycznie realizuje się to w następujący sposób:
  1. Zapamiętujemy gdzieś dotychczasową zawartość słowa Memlo ($02e7) - aby można było wrócić do czystej postaci interpretera
  2. Wpisujemy $8000 do Memlo
  3. Wykonujemy instrukcję NEW, po której Basic automatycznie ustawia wszystkie swoje wektory począwszy od Lomem ($80) do Memtop ($90) na odpowiednie wartości, według zawartości wektora Memlo.
Najwygodniej jest stworzyć plik AUTORUN.BAS, który wykona wszystko za nas. W Turbo BASIC XL może on wyglądać tak:
   1 GRAPHICS %0:POKE 710,%0:POKE 709,%0
   2 DPOKE $CB,DPEEK($02E7)  <- 1
   3 DPOKE $02E7,$8000       <- 2
   4 ? :? :? :? "POKE 842,12:RUN ""D:AUTORUNX.BAS"""
   5 POSITION %0,%0
   6 POKE 842,13
   7 NEW                     <- 3

Cyfry po strzałkach oznaczają instrukcje, które realizują poszczególne punkty naszego opisu.

       Naturalnie możemy w takim króciutkim programie wczytać jeszcze jakieś dodatkowe dane, np.fonty czy "wstawki" maszynowe dla właściwego programu, czy też wyświetlić jakiś obrazek...

       W naszym wypadku załadowany zostanie D:AUTORUNX.BAS, ale oczywiście nazwę tę można zmienić.

       We właściwym programie konieczne jest również kilka działań dla obsługi dodatkowej pamięci. Poniższe przykłady są tak napisane, że można je umieścić w dowolnym miejscu w programie.

       Początek programu...

    GO# START


(inicjalizację zwykle najlepiej stawiać na końcu)

       A oto i inicjalizacja:

 -> 1 # START
      GRAPHICS %0
      POKE 752,%1
      POSITION 13,11:? "pliiz łejd...";
 -> 2 DIM BANKS(31)
 -> 3 EXEC CONST
 -> 4 EXEC EXTRAM
 -> 5 EXEC MAIN
 -> 6 GO# EXIT

  1. To trwa, więc przyda się jakaś informacja dla niecierpliwego.
  2. Istotna z naszego punktu widzenia tablica, zawiera kody banków pamięci. Najprostsze i najbezpieczniejsze rozwiązanie - przełączamy 5 bitów, co daje 32 kombinacje, czyli 512 kB RAM-u we wszystkich bankach $4000-$7FFF, łącznie z podstawowym.
  3. Deklaracje stałych - patrz niżej.
  4. Procedura wykrywająca banki XMS.
  5. Program główny.
  6. Wyjście.
Stałe... czyli zmienne, które się nie zmieniają ;) (nawiasem mówiąc: przydałoby się w Basicu coś takiego jak #DEFINE, nie?)
    PROC CONST
      ...
      PB=$D301:EXTM=$4000
    ENDPROC

PB - PortB - $D301
EXTM - adres pocz. banku XMS - $4000

Detekcja banków. Metoda prosta, znana i oklepana. Na wszelki wypadek opiszę jej działanie.

      PROC EXTRAM
 -> 1   FOR I=%0 TO 31
          POKE PB,$72+I*4
          POKE EXTM,I
        NEXT I
 -> 2   FOR I=%0 TO 31
          BANKS(I)=$FE
        NEXT I:M=%0
 -> 3   FOR I=%0 TO 31
          POKE PB,$72+I*4
          IF PEEK(EXTM)=I
            BANKS(M)=PEEK(PB):M=M+%1
          ENDIF
        NEXT I
 -> 4   POKE PB,$FE
      ENDPROC

  1. Pierwsza pętla - ustawia odpowiedni kod banku w Porcie B, a następnie wpisuje jego index pod adres $4000.
  2. "Zerowanie" tablicy banków - zabezpieczenie przed "nielegalnymi" numerami banków.
  3. Sprawdza zawartość pamięci pod adresem $4000. Jeżeli zapisana tam wartość jest zgodna z aktualnym indexem, to bank istnieje i "jest sobą" - jego kod jest wpisywany do tablicy, ilość dostępnych banków (M) jest zwiększana o 1.
  4. Ustawia standardową wartość Portu B (odłączone OS, Self Test, Basic; ustawiony podstawowy bank pamięci).
Główny program...
    PROC MAIN
      ...hulaj dusza...
    ENDPROC

Dostęp do dodatkowej pamięci uzyskujemy za pomocą instrukcji:
    POKE PB,BANKS(x)


gdzie x oznacza numer banku, z któregochcemy skorzystać. Podanie numeru większego od liczby dostępnych banków powoduje ustawienie banku podstawowego.

Ponieważ podstawowy bank pamięci również nie jest wykorzystany przez TBXL, więc jest on traktowany jako pamięć dodatkowa.

Pozostaje jeszcze tylko wyjście - musimy zostawić interpreter w takiej postaci, żeby nikt niczego się nie domyślił.

      # EXIT
 -> 1 POKE PB,$FE
 -> 2 DPOKE $02E7,DPEEK($CB)
 -> 3 GRAPHICS %0:NEW

  1. Ustawiamy bank podstawowy.
  2. Memlo na poprzednią wartość.
  3. Czyścimy ekran i... NEW! - TBXL resetuje się w standardowy sposób.
Można prościej:
  # EXIT
  ? USR($E477) - zimny start  lub
  ? USR($E480) - Self Test/Monitor Qmeg

       Metoda ta ma, przy wszystkich swoich zaletach, jedną niewątpliwą wadę: nie da się jej wykorzystać w programach przeznaczonych do skompilowania. Niektóre instrukcje, które są w tym wypadku niezbędne, np. NEW - po prostu nie dają się kompilować, w każdym razie nie w standardowym TBXL Compiler F. Ostrowskiego. Niestety.

       I to już chyba wszystko. Powyższej metody używałem z powodzeniem w kilku swoich programach i muszę przyznać, że nie znam opisu lepszej, chociaż chętnie bym taki zobaczył. Metoda jest tak prosta, że nie zdziwiłoby mnie, gdyby ktoś wpadł na nią wcześniej i opracował ją lepiej.

       A wszystkim tym, którzy skorzystają z powyższego opisu pozostaje mi życzyć jak najwięcej wykrytych banków... do przeczytania.

epi/Allegresse