atarionline.pl Stałe tekstowe w BASICu - Forum Atarum

    Jeśli chcesz wziąć udział w dyskusjach na forum - zaloguj się. Jeżeli nie masz loginu - poproś o członkostwo.

    • :
    • :

    Vanilla 1.1.4 jest produktem Lussumo. Więcej informacji: Dokumentacja, Forum.

      • 1: CommentAuthormono
      • CommentTime10 Feb 2010 02:02 zmieniony
       
      Ponieważ od czasu do czasu bawię się Atari BASIC'em, nasunęły mi się pewne spostrzeżenia, którymi się podzielę.

      Stałe tekstowe w BASICu podlegają kilku ograniczeniom:
      1. Maksymalna długość 255 znaków - w praktyce jest to dużo mniej, bo uwarunkowane jest możliwym rozmiarem wprowadzanej linii oraz długością stokenizowanej linii (która nie może przekraczać właśnie 255 znaków).
      2. Niemożność użycia znaków cudzysłowu (34 ASC) oraz końca wiersza (155 ASC).

      Ograniczenie 1

      Pozornie można je ominąć za pomocą deklaracji zmiennej tekstowej o żądanym rozmiarze i zainicjalizowaniu jej stałą. Jest to jednak marnotrawstwo pamięci, ponieważ ten sam tekst znajduje się wtedy w pamięci DWA RAZY! Raz w kodzie programu, jako stała, drugi raz w pamięci danych (konkretnie w obszarze zmiennych indeksowanych) jako zmienna.
      Jedyny więc sposób na pozbycie się ograniczenia na rozmiar jest zadeklarowanie zmiennej tekstowej i załadowanie jej zawartości z zewnętrznego nośnika danych np tak:
      10 DIM A$(1000): A=USR(ADR("kod maszynowy"),1,ADR("D1:A.CNS"),8,ADR(A$),1000)

      gdzie w stałej tekstowej funkcji USR umieszczamy krótką procedurę w kodzie maszynowym ładującą plik z urządzenia zewnętrznego (zwolennicy TBXL mogą zamiast niej użyć BGET):
      bget:
      pla
      ; pobieramy nr kanalu CIO
      pla
      sta fr0+1
      pla
      asl @
      asl @
      asl @
      asl @
      tax
      ; pobieramy adres i dlugosc nazwy pliku i otwieramy kanal
      pla
      sta icbufa+1,x
      pla
      sta icbufa,x
      pla
      sta icbufl+1,x
      pla
      sta icbufl,x
      lda #3 ;OPEN
      sta iccmnd,x
      lda #4 ;READ
      sta icaux1,x
      jsr jciov
      sty fr0
      bmi err
      ; pobieramy adres i rozmiar danych ladowanych
      pla
      sta icbufa+1,x
      pla
      sta icbufa,x
      pla
      sta icbufl+1,x
      pla
      sta icbufl,x
      lda #7 ;GETBYTES
      sta iccmnd,x
      jsr jciov
      sty fr0
      lda #12 ;CLOSE
      sta iccmnd,x
      jmp jciov
      err:
      pla
      pla
      pla
      pla
      rts

      Kolejne parametry procedury USR to:
      a) adres stałej tekstowej zawierającej procedurę maszynową,
      b) numer wykorzystywanego kanału CIO (bezpiecznie można założyć wartości 1..5, ale jest to uzależnione od otwartych za pomocą OPEN kanałów I/O),
      c) adres nazwy pliku do załadowania,
      d) długosć nazwy pliku,
      e) adres zmiennej tekstowej,
      f) długość zmiennej tekstowej.
      Jako wynik funkcja zwraca kod statusu operacji I/O:
      1 - operacja przebiegła poprawnie,
      inny - systemowy kod błędu operacji I/O.
      Żeby było ciekawiej można w ten sposób z jednego pliku załadować od razu zawartość wszystkich zmiennych indeksowanych (tablice numeryczne i ciągi tekstowe - zwykłe zmienne przechowywane są w innym miejscu) zadeklarowanych w programie! Warunek jest jeden: muszą one zostać zadeklarowane w takiej kolejności, w jakiej zostaną umieszczone w pliku zewnętrznym. Ładowanie odbywa się bajt po bajcie a zmienne nie są separowane żadnymi dodatkowymi znakami. Tak więc zawartość pliku zawierającego zmienne deklarowane w programie następująco:
      10 DIM A$(11),B$(26)

      powinna wyglądać tak:
      ALA MA KOTAchrzaszcz brzmi w trzcinie

      Takie rozwiązanie powoduje, że tekst zmiennej występuje w pamięci tylko raz - jako zawartość zmiennej tekstowej, a nie występuje nigdzie w kodzie programu, jako stała.
      Ponieważ BASIC zarządza zmiennymi bardzo niewydajnie i potrafi do wynikowego programu zapisać zmienną (i jej wartość!) zadeklarowaną tymczasowo w trybie bezpośrednim, to gorąco zalecane jest zapisanie programu za pomocą LIST, wykonanie NEW po czym ENTER. Teraz można zapisać gotowy program instrukcją SAVE/CSAVE mając pewność, że w tablicach nie zachowały się żadne śmieci.

      Ograniczenie 2

      Jest to ograniczenie pozornie nie do przejścia, bo i jak tu wprowadzać znaki zabronione, skoro wpisanie cudzysłowia, jak i zakończenie wiersza RETURNem prowadzi natychmiastowo do zakończenia ciągu tekstowego w trybie bezpośrednim.
      BASIC sprawdza składnię wyrażenia w trakcie wprowadzania go do pamięci w postaci tekstowej - albo w trybie bezpośrednim, albo za pomocą polecenia ENTER. NIE SPRAWDZA jednak poprawności składni programu już stokenizowanego! Aby więc w treści stałej tekstowej użyć znaku cudzysłowu, lub znaku końca wiersza wystarczy:
      a) dowiedzieć się gdzie w pamięci znajduje się zawartość stałej tekstowej i
      b) zapisać na jej odpowiedniej pozycji żądany znak.
      Można tego dokonać prostą sztuczką:
      10 A=ADR(".program..maszynowy")
      RUN
      POKE A,34: POKE A+8,34: POKE A+9,155

      Pierwsza linia zawiera w stałej tekstowej program w kodzie maszynowym. Po wykonaniu tej linii w zmiennej A znajdzie się adres stałej tekstowej, której odpowiednie bajty modyfikujemy w kolejnej linii wykonanej w trybie bezpośrednim. Po wylistowaniu takiego programu można zobaczyć efekt.
      Dzięki temu unikamy niepotrzebnego deklarowania zmiennej, przepisywania stałej do zmiennej i uzupełniania zmiennej, które to rzeczy dzieją się przecież w trakcie wykonania programu (ang. runtime).
      Rozwiązanie ma jednak mankamenty, mianowicie:
      a) nie pozwala na reedycję zmodyfikowanej linii programu w BASICu, jak również na odczyt zapisanego programu za pomocą ENTER,
      b) nadaje się do uzupełniania kodu procedur maszynowych, ale nie do uzupełniania np. stałych wyświetlanych instrukcją PRINT.

      Aby uzupełnić PRINTa trzeba zlokalizować punkt w programie, po czym wykorzystać wiedzę o budowie linii programu w BASICu.
      10 A=ADR("@")
      20 ? "Pierwsza linia.A TO druga!"

      Każda linia w BASICu składa się z:
      a) dwóch bajtów nru linii,
      b) bajtu długości linii (łącznie z nrem linii i długością linii),
      c) stokenizowanych poleceń.
      Każde stokenizowane polecenie składa się z:
      a) bajtu adresu kolejnego polecenia (względem początku linii),
      b) tokenów słów kluczowych i funkcji, stałych (tekstowych i liczbowych), indeksów zmiennych (tekstowych, liczbowych i tablicowych).
      Linie programu będą w pamięci wyglądać więc tak:
      0A 00 0E 0E 36 80 2D 43 3A 0F 01 40 2C 16
      14 00 22 22 28 0F 1A ... 16

      W miejscu ... znajdują się bajty tekstu "Pierwsza linia.A to druga!". Ponieważ ADR("@") wskaże adres naszego znaku @ (kod $40 w pierwszej linii), to wystarczy do niego dodać offset lokalizujący położenie szukanej stałej (10 bajtów) i już w trybie bezpośrednim za pomocą POKE wstawić znak, jaki nam pasuje np tak:
      RUN
      POKE A+10+14,155

      Po wylistowaniu kodu programu otrzymamy pożądany efekt :)
      Co najfajniejsze - linię 10 można śmiało usunąć. BASIC bez trudności poradzi sobie z reorganizacją takiego kodu!
      Funkcja ADR może zawierać ciąg pusty - wstawiłem tam "@" tylko po to, żeby ułatwić lokalizację punktu odniesienia w kodzie programu.
      Oczywiście jeśli linia 20 ma wyglądać inaczej trzeba zapoznać się najpierw z jej budową (warto przeczytać pierwszy rozdział książki "Procedury interpretera BASICa" Wojciecha Zientary).

      Edit: Poprawki w asm.
      • 2:
         
        CommentAuthorKaz
      • CommentTime10 Feb 2010 10:02
       
      Piekne!

      mono:

      1. Maksymalna długość 255 znaków - w praktyce jest to dużo mniej, bo uwarunkowane jest możliwym rozmiarem wprowadzanej linii


      Przypomne tylko czytelnikom, ze mozna obejsc ten problem takze zewnetrznym edytorem:

      ->link<-
      • 3: CommentAuthormono
      • CommentTime10 Feb 2010 12:02 zmieniony
       
      Można, ale tylko jeśli chodzi o długość linii wprowadzanej w trybie edycji - postać stokenizowana wiersza dalej podlega ograniczeniu 255 bajtów, a ponieważ stała tekstowa zapisywana jest bezpośrednio w takim stokenizowanym wierszu, to rozmiar stałej tekstowej pozostaje wewnętrznie ograniczony przez sam BASIC do mniej niż 255 znaków. Dla przykładu:
      10 A=ADR("stała tekstowa o maksymalnej długości")

      zostanie stokenizowany do postaci ciągu bajtów:
      0A 00 FF FF 36 80 2D 43 3A 0F F2 ... 2C 16

      (w miejscu ... zapisana jest stała), co realnie ogranicza długość stałej do max 255-13=242 znaków.

      Jeśli zaś chodzi o zapis kompletu zmiennych indeksowanych (ciągi tekstowe i tablice numeryczne) to można to zrobić jeszcze prościej korzystając z lokacji STARP ($8C) i RUNSTK ($8E):
      10 B=PEEK(140)+256*PEEK(141): A=USR(ADR("kod maszynowy"),1,ADR("D1:TABLICE.CNS"),14,B,PEEK(142)+256*PEEK(143)-B)

      Istotne jest to, żeby wszystkie zmienne tablicowe były podczas wywołania procedury maszynowej już zadeklarowane instrukcją DIM/COM.

      Edit: Zapomniałem o ważnej sprawie. Przy wczytywaniu zawartości zmiennych tekstowych oraz tablic numerycznych z urządzenia zewnętrznego należy jeszcze ustawić aktualny rozmiar ciągom tekstowym na odpowiadającą nam wartość (np. maksymalną zadeklarowaną) za pomocą:
      100 A$(1000)=""

      bo po samej deklaracji wszystkie ciągi mają długość 0 i wyświetlając je PRINTem nie zobaczymy nic mimo, że dane fizycznie są w pamięci.