Skip to content

Latest commit

 

History

History
567 lines (417 loc) · 23.8 KB

hybride_call_commandos.md

File metadata and controls

567 lines (417 loc) · 23.8 KB
                    - HYBRIDE -
      
                            C A L L   - 1 - 
                                             
      
      In de  serie Hybride  artikelen hebben we het deze keer over 
      CALL   commando's.  Het  CALL  commando  is  aan  MSX  BASIC 
      toegevoegd om  het mogelijk  te maken uw eigen commando's of 
      toekomstige uitbreidingen te gebruiken.
      
      De syntax luidt:
      
      CALL <naam uitbreidingsbevel> [ (parameter,parameter ...) ]
      
      In plaats van CALL mag ook het onderstrepingsteken _  worden
      gebruikt.
      
      Kennis van machinetaal is vereist voor het  zelf  maken  van
      een CALL  commando.  BASIC  programmeurs  kunnen  de  nieuwe
      commando's natuurlijk wel zonder problemen gebruiken in  hun
      eigen programma's.
      
      Voordat we ingaan op het maken van een CALL  commando  wordt
      er eerst enige achtergrondinformatie gegeven.
      
      
                  U I T B R E I D I N G S P A G I N A 
      
      Een pagina is een stuk geheugen (ROM of RAM) van 16  kB.  De
      pagina's zijn genummerd van 0 t/m 3.
      
      Page:    Adresbereik:
      0        &H0000-&H3FFF
      1        &H4000-&H7FFF
      2        &H8000-&HBFFF
      3        &HC000-&HFFFF
      
      De MSX  computer heeft  standaard pagina's met RAM, MAIN ROM 
      en  Disk ROM.  Daar kunnen  echter door de fabrikant (in ROM 
      vorm) of door de gebruiker (in het RAM) uitbreidingspagina's 
      aan worden toegevoegd.
      
      Een uitbreidingspagina  kan  een  machinetaalprogramma,  een
      apparaatuitbreiding, een  BASIC-uitbreiding  of  een  BASIC-
      programma bevatten. De informatie daarover is opgeslagen  in
      SLTATR. SLTATR is een gedeelte van  het  systeemgebied,  dat
      begint op adres &HFCC9. Er  is  ÇÇn  byte  gereserveerd  per
      mogelijke pagina. Er zijn 4 primaire sloten mogelijk die elk
      4 secundaire sloten kunnen hebben. Elk slot bestaat weer uit
      4 pages, zodat SLTATR 4*4*4=64 bytes lang is.  De  bits  van
      de bytes in SLTATR hebben de volgende betekenis:
      
      Bit 7    BASIC programma (1=ja)
      Bit 6    Apparaat uitbreiding (1=ja)
      Bit 5    Statement uitbreiding (1=ja) (CALL commando)
      Bit 4-0  Ongebruikt
      
      U kunt het adres dat bij een bepaalde pagina hoort berekenen
      met de volgende formule:
      
      ADRES = &HFCC9 + 16 * primaire gleufnummer + 4 *  secundaire
      gleufnummer + paginanummer
      
      Voorbeeld: in uw computer zit het RAM in slot 3-2 en u heeft
      een statement uitbreiding in page 1 gemaakt. Het adres wordt
      dan:
      
      &HFCC9 + 16 * 3 + 4 * 2 + 1 = &HFD02
      
      Bit 5 staat voor een statement uitbreiding, dus de waarde 32 
      (2^5) moet naar adres &HFD02 worden geschreven: POKE &HFD02, 
      32. De  computer zal nu naar page 1 in slot 3-2 springen als 
      er een CALL commando wordt gegeven.
      
      
                              H E A D E R 
      
      De eerste  16 bytes  van een uitbreidingspagina worden samen 
      ook wel de header genoemd en zijn als volgt opgebouwd:
      
      Offset:  Naam:               Bevat:
      ------------------------------------------------------------
      +0       ID                  "AB"
      +2       INIT                beginadr initialisatie
      +4       STATEMENT           beginadr statement uitbreiding
      +6       DEVICE              beginadr apparaat uitbreiding
      +8       TEXT                beginadr BASIC programma
      +10      -                   6 bytes gereserveerd
      ------------------------------------------------------------
      
      Aan het  begin van  een pagina moeten de letters "AB" (ASCII 
      &H41   en  &H42)  staan.  De  computer  herkent  daaraan  de 
      uitbreidingspagina.  Bij  een  RESET  zal  de  computer zelf 
      kijken wat  er in de pagina's staat en SLTATR overeenkomstig 
      invullen.  Als u een CALL commando wilt gebruiken zonder een 
      RESET, dan  zult u zelf in SLTATR moeten POKEn, zoals we bij 
      de uitleg van SLTATR al hebben gezien.
      
      
               S T A T E M E N T   U I T B R E I D I N G 
      
      Wij  interesseren  ons   nu   alleen   voor   de   statement
      uitbreiding. Als er geen statement uitbreiding in de  pagina
      aanwezig is, moet STATEMENT gelijk zijn aan  &H0000.  Alleen
      page 1 kan een statement  uitbreiding  bevatten.  Er  kunnen
      problemen  ontstaan  als  bij  andere  pagina's   het   veld
      STATEMENT is ingevuld.
      
      Het is overigens logisch dat  de  statement  uitbreiding  in
      page 1 moet staan. In page 0 staat de BIOS en in page 3  het
      systeemgebied, dus  die  moeten  ingeschakeld  blijven.  Het
      BASIC programma staat in page 2 (als het een groot programma
      is tot in page 3), dus page 1 is de enige  mogelijkheid  die
      overblijft.
      
      Als  de computer  bij het  uitvoeren van een BASIC programma 
      een CALL  commando tegenkomt, wordt de naam van het commando 
      (het  woord dat  achter CALL  staat) in PROCNM gezet. PROCNM 
      ligt  in  het  systeem  gebied,  vanaf  &HFD89. Alle  kleine 
      letters worden  omgezet in hoofdletters en de komma's worden 
      eruit  gehaald  (probeert  u  maar  eens:  _F,O,R,M,A,T  het 
      werkt!).  De naam  mag 15  tekens lang  zijn. Achter de naam 
      wordt een  ASCII-teken 0 gezet. Daarna gaat de computer alle 
      pagina's  langs die volgens SLTATR een statement uitbreiding 
      bevatten.
      
      Als de computer bij STATEMENT een waarde vindt die  ongelijk
      is aan &H0000, dan springt hij naar dat  adres  en  laat  de
      besturing verder aan  die  routine  over.  De  taak  van  de
      routine is nu om te controleren of het het juiste  statement
      is.
      
      HL wijst naar het eerste teken  na  de  naam  van  het  CALL
      statement in het tokenized  BASIC.  Spaties  worden  daarbij
      niet meegeteld. Bij bijvoorbeeld CALL FORMAT  wijst  HL  dus
      naar de positie NA de T van FORMAT.
      
      Als  de routine  ziet dat  het de  juiste naam  is, moet het 
      statement  worden  uitgevoerd  en  moet  de  routine  worden 
      verlaten met  de carry  gereset (gelijk  aan 0 dus). HL moet 
      naar  het eerste teken na het commando wijzen (het einde van 
      de regel of een dubbele punt). Bij een CALL statement zonder 
      parameters  is   dat  dus  de  oude  situatie  (bijvoorbeeld 
      _FORMAT),  bij _COLOR(15,0)  moet HL  wijzen naar de positie 
      achter het tweede haakje.
      
      Is het een ander commando,  dan  moet  de  routine  verlaten
      worden met de carry  geset  (hoog)  en  HL  onveranderd.  De
      andere registers mogen wel zijn veranderd.
      
      Samenvatting:
      
      - CALL statement wordt niet gevonden: verlaat routine met HL 
      onveranderd en carry hoog
      -  CALL statement  is gevonden:  voer CALL  statement uit en 
      verlaat de  routine met  HL op  de positie achter het gehele 
      CALL statement en de carry laag
      
      
                   S T A N D A A R D   R O U T I N E 
      
      Om  het  allemaal  wat  makkelijker  te  maken  heb  ik  een 
      standaardroutine geschreven, die als CALL.ASC op de diskette 
      staat.  U  kunt  deze  routine  gebruiken  voor  al  uw CALL 
      statements.  De  routine  kan  ÇÇn,  maar  ook twee  of meer 
      routines  aansturen.  Als al  het RAM  in ÇÇn  slot zit  (is 
      meestal  zo),  dan  kunt  u  maar  ÇÇn blok  CALL commando's 
      tegelijk  gebruiken.  Er  is  dan namelijk  maar ÇÇn  page 1 
      beschikbaar, en de statement uitbreiding moet altijd in page 
      1 staan.
      
      Achter de  standaardroutine staan  drie voorbeelden van CALL 
      commando's,  die  u  bij het  maken van  uw eigen  blok CALL 
      commando's  natuurlijk  kunt  weglaten.  Ik ga  deze routine 
      zometeen uitgebreid bespreken. Maar eerst even dit.
      
      
               N A A R   D I S K   A S S E M B L E R E N 
      
      Het is  het makkelijkst als het blok CALL commando's met een 
      BLOAD,R kan worden geãnitialiseerd. De CALL code moet echter 
      op  &H4000 staan,  en daar kunnen we niet laden onder BASIC. 
      We  moeten   daarom  twee   ORGs  gebruiken,  ÇÇn  voor  het 
      intialisatieprogramma  en  ÇÇn  voor  de  CALL code.  Om het 
      allemaal  toch netjes  in ÇÇn .BIN file te krijgen moeten we 
      naar disk assembleren.
      
      Ik  ga  hier  uit van  WB-ASS2. GEN80  gebruikers zullen  de 
      source  dus enigszins  moeten aanpassen.  Je kunt  met GEN80 
      trouwens alleen  maar naar disk assembleren. Bij WB-ASS2 kan 
      dit  door de vragen die het ASM statement stelt als volgt te 
      beantwoorden:
      
      Listing afdrukken? Nee          (Ja mag natuurlijk ook)
      Ascii listing naar disk? Nee
      Mcode in geheugen zetten? Nee
      Mcode naar disk? Ja
      Relocatie-tabel maken? Nee
      
      Vervolgens   moet   nog   de   filenaam   worden   ingetypt, 
      bijvoorbeeld  CALL.BIN.  WB-ASS2  gaat  aan  de slag  en als 
      resultaat  staat de  file CALL.BIN  op de  diskette, die met 
      BLOAD"CALL.BIN",R kan worden gestart.
      
      (Nvdr. De  tekst was  langer dan  16 kB,  u kunt  het tweede 
      gedeelte in het submenu vinden.)
      
      
      
                              - HYBRIDE -
      
                            C A L L   - 2 - 
                                             
      
      
      Het is nu hoog tijd voor:
      
                           D E   S O U R C E 
      
      ; CALL.ASC
      ; Standaardroutine voor het maken van CALL commando's
      ; Door Stefan Boer
      ; (c) Ectry 1993
      ; Sunrise Special #3
      ; (c) Stichting Sunrise 1993
      
      ; (Alleen) naar disk assembleren!
      ; Resultaat starten met BLOAD"...",R
      
      ; Header voor .BIN file
      
              DEFB  &HFE
              DEFW  EXEC
              DEFW  ENCALL-&H4001+STCALL
              DEFW  EXEC
      
      
      Omdat  we  rechtstreeks een  file aanmaken,  moeten we  zelf 
      zorgen voor de juiste header. Een BLOAD,R file moet beginnen 
      met een  header van  7 bytes. De eerste byte is altijd &HFE, 
      hieraan  kan een  .BIN file  worden herkend.  Vervolgens 3*2 
      bytes  beginadres,  eindadres en  startadres. ENCALL  is het 
      eindadres+1  van  de CALL  code die  vanaf &H4000  staat. De 
      lengte van  dat blok  CALL code is dus ENCALL-&H4001. Om het 
      eindadres  te berekenen  wordt daar nog het eindadres van de 
      initialisatie routine bij opgeteld.
      
      ; Dit startadres moet eventueel omlaag als CALL code lang is
      
              ORG   &HC000
      
      ; Initialisatieroutine
      ; Kopieer CALL code naar &H4000
      ; En zet bitje in SLTATR
      
      EXEC:   LD    A,(&HF342)      ; slot RAM page 1
              LD    H,&H40          ; PAGE 1
              CALL  &H24            ; BIOS routine ENASLT
              LD    HL,STCALL
              LD    DE,&H4000
              LD    BC,ENCALL-&H4000
              LDIR                  ; verplaats CALL code
              LD    A,(&HFCC0)      ; slot MAIN ROM
              LD    H,&H40
              CALL  &H24
      
      
      Hier wordt er RAM geselecteerd op adres &H4000, de CALL code 
      wordt  gekopieerd en vervolgens wordt de MAIN ROM weer terug 
      gezet op adres &H4000.
      
      
              LD    HL,&HFCCA       ; &HFCC9 + pagenummer (=1)
              LD    BC,&H10
              LD    A,(&HF342)      ; slot RAM page 1
              AND   &B00000011      ; primair slotnummer
      CLUS1:  JR    Z,CNEXT
              ADD   HL,BC           ; tel primair*16 erbij op
              DEC   A
              JR    CLUS1
      CNEXT:  LD    BC,4
              LD    A,(&HF342)      ; slot RAM page 1
              AND   A               ; zet vlaggen goed
              JP    P,CEINDE        ; is er wel secundair slot
              AND   &B00001100      ; secundaire gleuf * 4
              RRCA                  ; secundaire gleuf * 2
              RRCA                  ; secundaire gleuf
      CLUS2:  JR    Z,CEINDE
              ADD   HL,BC           ; tel secundair*4 erbij op
              DEC   A
              JR    CLUS2
      CEINDE: LD    (HL),&H20       ; zet bit 5 aan
              RET                   ; einde initialisatie
      
      
      Hier wordt het juiste adres in SLTATR uitgerekend, en op dat 
      adres wordt de waarde &H20 gezet. Hieraan kan de interpreter 
      zien dat er een statement uitbreiding in die page zit.
      
      
      STCALL:                       ; beginadres van code IN FILE
      
      
      Als de BLOAD,R file is ingeladen, staat de CALL code niet op 
      adres &H4000 maar direct achter de RET van CEINDE. We hebben 
      dat adres  nodig om de CALL code te kunnen verplaatsen en om 
      het  eindadres  uit  te kunnen  rekenen. Vandaar  dit "loze" 
      label.
      
      
      ; CALL code
      
              ORG   &H4000          ; hier begint CALL code
      
      ; CALL header
      
              DEFM  "AB"
              DEFW  0               ; geen initialisatie
              DEFW  CAL             ; startadres CALL code
              DEFW  0               ; geen device
              DEFW  0               ; geen BASIC
              DEFW  0
              DEFW  0
              DEFW  0
      
      
      ; Hier springt interpreter naar toe bij CALL commando
      
      CAL:    PUSH  HL              ; HL wijst naar teken na naam
                                      van CALL statement
      
      ; Kijk of naam van CALL statement overeenkomt met
      ; een van de namen in de tabel
      
              LD    DE,TABEL        ; begin van de tabel
      C_1:    LD    HL,&HFD89       ; PROCNM bevat naam
              LD    A,(DE)          ; lengte van naam
              AND   A               ; zet vlaggen goed
              JR    Z,C_ERR         ; einde tabel als A=0
              LD    B,A             ; lengte
              INC   DE
      
      C_2:    LD    A,(DE)          ; vergelijk naam in tabel
              CP    (HL)            ; met naam in PROCNM
              JR    NZ,NEXT1        ; naar NEXT1 als onjuist
              INC   DE              ; volgende letter
              INC   HL
              DJNZ  C_2
      
              LD    A,(HL)          ; laatste teken moet nu
              AND   A               ; gelijk zijn aan 0, anders
              JR    NZ,NEXT2        ; is CALL naam te lang
      
      
      Dit laatste stukje code is nodig om ervoor te zorgen dat het 
      verschil wordt  gezien tussen  bijvoorbeeld CALL SUN en CALL 
      SUNRISE.
      
      
      ; alles Ok, spring naar CALL routine
      
              EX    DE,HL           ; HL is plaats in tabel
              LD    E,(HL)          ; low byte sprongadres
              INC   HL
              LD    D,(HL)          ; high byte sprongadres
              EX    DE,HL
              JP    (HL)            ; spring naar dat adres
      
      ; Verkeerde naam, ga naar volgende naam in tabel
      
      NEXT1:  LD    C,B
              LD    B,0             ; BC=B
              INC   BC
              INC   BC
              EX    DE,HL
              ADD   HL,BC           ; sla rest naam + sprongadres
              EX    DE,HL           ; over
              JR    C_1             ; probeer het nog eens
      
      ; Naam te lang, ga naar volgende naam in tabel
      
      NEXT2:  INC   DE              ; sla het sprongadres
              INC   DE              ; over
              JR    C_1             ; probeer het nog eens
      
      ; Niet gevonden, einde routine met carry hoog
      ; Als in andere sloten ook geen kloppend CALL commando
      ; wordt gevonden, zal BASIC foutmelding geven
      
      C_ERR:  POP   HL
              SCF                   ; Set Carry Flag
              RET
      
      
      Tot  zover de  "CALL handler".  Het is  in de code voor CALL 
      commando's makkelijk  om een  aantal routines  uit de  BASIC 
      interpreter "bij de hand te hebben":
      
      ; Handige routines
      
      ILLEGA: LD    IX,&H475A       ; Illegal function call
              JP    &H0159
      
      SYNTAX: LD    IX,&H4055       ; Syntax error
              JP    &H0159
      
      R_BYTE: LD    IX,&H521C       ; Lees een byte van getokenized
              JP    &H0159          ; BASIC, resultaat in A
      
      R_WORD: LD    IX,&H6F0B       ; Lees een word van getokenized
              JP    &H0159          ; BASIC, resultaat in DE
      
      
      Hier  ontbreekt  nog  FRMEVL op  adres &H4C64,  waarmee elke 
      willekeurige   expressie   kan   worden  "geâvalueerd".   De 
      interpreter  zal VALTYP  (&HF663) en DAC (&HF7F6) vullen met 
      het resultaat.  De namen  van de routines die door R_BYTE en 
      R_WORD  worden  aangeroepen  zijn overigens  respectievelijk 
      GETBYT en FRMQNT.
      
      
      ; Tabel met CALL commando's
      
      TABEL:  DB    4               ; lengte CALL commando
              DM    "INFO"          ; de naam
              DW    INFO            ; het adres
      
              DB    4
              DM    "DSKI"
              DW    DSKI
      
              DB    6
              DM    "KILBUF"
              DW    KILBUF
      
              DEFB  0               ; afsluiten
      
      
      In deze  tabel moeten  alle namen,  lengtes en startadressen 
      van de CALL statements in het blok CALL code worden gezet.
      
      Voor de  duidelijkheid heb  ik nog  een drietal  voorbeelden 
      toegevoegd. Op de disk staat de file CALLTEST.BIN, waarmee u 
      het zelf kunt uitproberen.
      
      
      ; Voorbeelden
      
      ; CALL INFO
      ; Zet melding op het scherm
      
      INFO:   LD    HL,TEXT         ; begin tekst
      INFO_1: LD    A,(HL)          ; ASCII teken
              AND   A
              JR    Z,INFO_2        ; klaar als 0
              CALL  &HA2            ; teken naar scherm
              INC   HL
              JR    INFO_1          ; volgende teken
      
      INFO_2: POP   HL              ; pointer achter statement
              XOR   A               ; wis carry: CALL commando Ok
              RET
      
      TEXT:   DM    "Standaard CALL routine",13,10
              DM    "(c) Ectry 1993",13,10,0
      
      ; CALL DSKI (drive,sector,aantal,adres)
      ; Lees sectoren in
      
      DSKI:   POP   HL              ; pointer achter naam
      
              LD    A,(HL)
              CP    "("             ; check haakje
              JP    NZ,SYNTAX
              INC   HL
      
              CALL  R_BYTE          ; haal drivenummer
              LD    (DRIVE),A
      
              LD    A,(HL)
              CP    ","             ; check komma
              JP    NZ,SYNTAX
              INC   HL
      
              CALL  R_WORD          ; haal start sector
              LD    (SECTOR),DE
      
              LD    A,(HL)
              CP    ","             ; check komma
              JP    NZ,SYNTAX
              INC   HL
      
              CALL  R_BYTE          ; haal aantal sectoren
              LD    (COUNT),A
              AND   A               ; zet vlaggen goed
              JP    Z,ILLEGA        ; Illegal function call als
                                    ; 0 sectoren
      
              LD    A,(HL)
              CP    ","
              JP    NZ,SYNTAX
              INC   HL
      
              CALL  R_WORD          ; haal adres (komt in DE)
      
              LD    A,(HL)
              CP    ")"
              JP    NZ,SYNTAX
              INC   HL
      
              PUSH  HL              ; bewaar pointer
      
              LD    C,&H1A          ; BDOS call SETDMA
              CALL  &HF37D          ; Zet DMA adres op DE
      
              LD    DE,(SECTOR)     ; DE=startsector
              LD    HL,(DRIVE)      ; H=#sectoren, L=drive
              LD    C,&H2F          ; BDOS call Absolute Disk Read
              CALL  &HF37D          ; Lees sectoren
      
              POP   HL              ; pointer achter statement
              XOR   A               ; wis carry: CALL commando Ok
              RET
      
      ; data voor DSKI routine
      
      DRIVE:  DB    0
      COUNT:  DB    0
      SECTOR: DW    0
      
      ; CALL KILBUF
      ; Wis toetsenbordbuffer
      
      KILBUF: CALL  &H0156          ; BIOS routine
              POP   HL              ; pointer achter statement
              XOR   A               ; wis carry: CALL commando Ok
              RET
      
      ENCALL: END
      
      
                    Z E L F   A A N   D E   S L A G 
      
      Het zelf  maken van  een CALL commando is nu heel eenvoudig. 
      Schrijf  een machinetaalroutine die u door een CALL commando 
      wilt  laten  aanroepen.  U  mag daarin  de routines  ILLEGA, 
      SYNTAX,  R_BYTE  en  R_WORD  gebruiken.  Zorg  dat  bij  het 
      verlaten van  de routine  HL wijst  naar de  positie na  het 
      commando  en dat  de C vlag gewist is. Denk er verder om dat 
      de stack niet verandert ten opzichte van de situatie voordat 
      uw routine werd aangeroepen.
      
      Zet  de  standaardroutine erbij  en zet  het commando  in de 
      tabel. De  standaardroutine zorgt  ervoor dat  er alleen bij 
      het   juiste  CALL  commando  naar  uw  routine  zal  worden 
      gesprongen.
      
      Let erop  dat het totale blok CALL commando's nooit meer kan 
      zijn  dan 16  kB. Verlaag indien nodig de ORG &HC000. &H9000 
      is in ieder geval altijd laag genoeg. U mag de BIOS routines 
      in  page  0  en  het  systeem  gebied in  page 3  natuurlijk 
      gebruiken.
      
      Veel succes met het maken van uw eigen CALL commando's!
      
                                                      Stefan Boer