Skip to content

Latest commit

 

History

History
346 lines (261 loc) · 15 KB

werken_met_ascii_c_deel_2.md

File metadata and controls

346 lines (261 loc) · 15 KB
              H E T   W E R K E N   M E T   A S C I I   C 
                                                           
      
      
                              D E E L   2 
      
                     C   M E T   A S S E M B L E R 
      
      Hoewel ASCII  C behoorlijk  goede machinecode  genereert, is 
      het  soms toch  handig om  bepaalde routines in assembler te 
      schrijven. Zo is het bijvoorbeeld niet mogelijk om direct de 
      BIOS aan  te roepen,  hier is  toch weer een library functie 
      voor  nodig. Ook  VDP I/O  kan het beste in assembler worden 
      gedaan, omdat dit toch een stuk sneller gaat.
      
      Gelukkig is  dit met  ASCII C  geen enkel  probleem, het  is 
      namelijk  mogelijk om  een library in assembler te schrijven 
      en  de  functies die  in deze  library staan,  vervolgens te 
      gebruiken vanuit een C programma.
      
      Als de  library goed  wordt opgezet is het zelfs mogelijk om 
      uit  1  enkele  source  file  zowel  de  .TCO  file  voor de 
      parameter checker te maken, als de .REL file voor de linker.
      
      In  dit  deel  van  de  cursus  zullen  achtereenvolgens  de 
      volgende onderwerpen worden besproken:
      
      - Het doorgeven van variabelen aan de functies.
      - Het opzetten van de source zodat er zowel een .TCO als 
        een .REL file van gemaakt kan worden.
      - Een voorbeeld library.
      
      
                V A R I A B E L E N   D O O R G E V E N 
      
      ASCII-C kent 2 typen functies:
      
      - Fixed parameter functies: de functie heeft een vast aantal 
        parameters.
      - Variabele parameter functies: de functie heeft een 
        variabel aantal parameters.
      
      Normaal gaat  de C-compiler  er uit  dat een functie van het 
      fixed  parameter type  is. Een  variabele parameter  functie 
      moet eerst als volgt gedeclareerd worden:
      
                      int func(.);
      
      Bij  een   fixed  parameter   functie  worden  de  eerste  3 
      variabelen  in de  Z80 registers  doorgegeven, de rest wordt 
      allemaal  op  de  stack  gezet.  Hierbij  wordt de  volgende 
      conventie gebruikt:
      
      parameter  | char   | niet-char 
      -----------+--------+--------------- 
      1e         | A      | HL 
      2e         | E      | DE 
      3e         | C      | BC 
      4e         | (SP+2) | (SP+2, SP+3) 
      5e         | (SP+4) | (SP+4, SP+5) 
      etc.
      
      Bij  een   variabele  parameter  functie  wordt  het  aantal 
      variabelen  in  HL  gezet,  en  de  rest  wordt op  de stack 
      gepushed.  De volgorde is hierbij weer hetzelfde als bij een 
      fixed parameterfunctie,  dus de  variabelen worden als volgt 
      doorgegeven:
      
      parameter  | char   | niet-char
      -----------+--------+---------------
      aantal vars| HL     | HL
      1e         | (SP+2) | (SP+2, SP+3)
      2e         | (SP+4) | (SP+4, SP+5) etc.
      
      Als  de functie  een bepaalde waarde berekent, dan moet deze 
      ook weer worden teruggegeven aan de aanroepende functie, dit 
      gaat altijd  via de registers. Als het functie type char is, 
      dan  wordt de  waarde teruggegeven  in de A en in het andere 
      geval komt ze terug in HL.
      
      Een functie mag in principe alle registers veranderen.
      
      Voorbeeld:
      
      Stel je  wilt een functie schrijven die aan karakter afdrukt 
      via  de MSX1  main rom  (CHPUT: adres 00A2H), en een functie 
      die een  karakter van  het toetsenbord  leest (CHGET:  adres 
      009FH). Je zult deze functies dan moeten declareren in het C 
      programma  dat er gebruik van gaat maken. Om dit te bereiken 
      kun je  het beste  alle declaraties  die nodig  zijn voor je 
      library  bij  elkaar  zetten  in  1 header  file die  je dan 
      include  vanuit je C programma's. In deze header file moeten 
      dan de  volgende declaraties  staan:
      
      void bchput(); /* zet karakter via bios op scherm */
      char bchget(); /* lees karakter uit via bios */
      
      In  de  assembly  source  (mlvb1.mac)  zet je  vervolgens de 
      volgende routines:
      
      bchput::        ld      ix,0a2h         ;Druk karakter in A
                      jr      domsx1          ; af via de BIOS
      
      bchget::        ld      ix,09fh         ;Lees een karakter
      domsx1:         ld      iy,(0fcc0h)     ; en zet dat in A
                      jp      01ch
      
                      end
      
      
      Deze source kun je assembleren met: 
      
                      M80 = MLVB1 /Z
      
      om het bestand MLVB1.REL te maken.
      
      In  een  C  programma  (CVB1.C)  kun  je  ze  dan  als volgt 
      gebruiken:
      
      typedef char void;
      
      void bchput(); 
      char bchget();
      
      void main() 
      { 
        char test;
      
        do { 
          test = bchget();  /* Lees karakter uit via bios */ 
          bchput(test); 
        } while (test != 13); 
      }
      
      Deze  listing kun  je vervolgens  compileren, assembleren en 
      linken met:
      
                      CF CVB1
                      CG -K CVB1
                      M80 = CVB1 /Z
                      L80 CVB1,MLVB1,CVB1/N/Y/E:MAIN@
      
      Als je  dit allemaal  hebt gedaan, dan heb je nu een (klein) 
      programma met de naam CVB1.COM, waarmee je kunt typen tot je 
      op de return toets duwt.
      
      
          Z O W E L   . T C O   A L S   . R E L   V E R S I E 
      
                      V A N   E E N   S O U R C E 
      
      Zoals  je in bovenstaand voorbeeld wel kunt zien is het niet 
      zoveel  werk  om C  te combineren  met assembler.  Het wordt 
      echter  iets  meer  werk  als  je  de  library  netter  wilt 
      opzetten.
      
      In  het  bovenstaande  voorbeeld  kun  je bijvoorbeeld  geen 
      gebruik  maken van  de functie parameter checker (FPC) om te 
      kijken of het type van de variabelen wel klopt.
      
      Ook  de linker  wordt nu niet optimaal benut. Met L80 is het 
      namelijk mogelijk  om een  library zo  samen te  stellen dat 
      alleen functies die gebruikt worden, ook worden meegelinkt.
      
      Om  dit te bereiken moet de source echter worden opgesplitst 
      in  allemaal  losse sources  (��n source  per onafhankelijke 
      functie).  Deze   sources  moeten  vervolgens  allemaal  los 
      geassembleerd  worden (zodat  er allemaal  losse .REL  files 
      ontstaan),  en  deze  .REL  files kunnen  dan met  LIB80 aan 
      elkaar worden gekoppeld tot 1 grote library.
      
      Om  nu  te  voorkomen  dat  je  disk vol  komt te  staan met 
      honderden losse library routines heeft ASCII corporation het 
      hulp programma  MX gemaakt.  Dit programma  splitst 1  grote 
      source  op in losse deel sources, maakt een batchfile aan om 
      deze sources  afzonderlijk te assembleren en weer te deleten 
      en om vervolgens de .REL files te linken tot 1 .LIB file.
      
      Om  goed gebruik  te maken  van MX moet de source echter wel 
      goed zijn  opgezet. Hierbij  wordt gebruik  gemaakt van  het 
      feit  dat M80 een macro assembler is. In je source dien je 3 
      (verder lege) macro's te defini�ren:
      
      module          macro
                      endm
      
      endmodule       macro
                      endm
      
      extrn           macro
                      endm
      
      Alles  wat   in  principe  echt  bij  elkaar  hoort  kun  je 
      vervolgens  tussen  MODULE  FILENAME  en  ENDMODULE  zetten. 
      Indien  je vanuit dit stuk machinecode ook nog routines wilt 
      aanroepen die  in een  andere module staan, dan dien je deze 
      routines te declareren met EXTRN ROUTINE.
      
      MX zal dan alles wat tussen MODULE en ENDMODULE staat in het 
      bestand  met de  naam FILENAME zetten. In dit bestand worden 
      dan  de  EXTRN  ROUTINE  declaraties omgezet  in de  normale 
      EXTERN ROUTINE declaraties die nodig zijn voor M80.
      
      Het is wel belangrijk dat je de juiste volgorde aanhoudt als 
      bepaalde routines gebruik maken van andere routines:
      
      Als een  routine uit  MODULE A gebruik maakt van een routine 
      uit  MODULE B, dan dient MODULE B VOOR MODULE A in de source 
      te staan!!
      
      Als we bij het bovenstaande voorbeeld blijven, dan kunnen we 
      de volgende source maken (met de naam MLVB2.MAC):
      
      module          macro
                      endm
      
      endmodule       macro
                      endm
      
      extrn           macro
                      endm
      
                      MODULE  DOMSX1
      domsx1::        ld      iy,(0fcc0h)
                      jp      01ch
                      ENDMODULE
      
                      MODULE  BCHPUT
                      extrn   domsx1       ;Deze routine is nodig
      bchput::        ld      ix,0a2h      ;Zet karakter op scherm
                      jp      domsx1
                      ENDMODULE
      
                      MODULE  BCHGET
                      extrn   domsx1
      bchget::        ld      ix,09fh      ;Lees karakter uit
                      jp      domsx1
                      ENDMODULE
      
                      end
      
      
      Deze nieuwe  source kun je nog altijd direct assembleren met 
      M80.  Wil je  er echter een library van maken waaruit alleen 
      de functies  worden meegelinkt  die aangeroepen  worden, dan 
      dien je de volgende stappen te ondernemen:
      
      - Zorg ervoor dat AREL.BAT op je disk staat
      - Zorg ervoor dat MX.COM, M80.COM en LIB80.COM op je disk 
        staan.
      - Typ in:
        MX -L MLVB2 > TEMP.BAT TEMP REN MLVB2.LIB MLVB2.REL
      
      Deze nieuwe library kun je vervolgens meelinken met:
      
                   L80 CVB1,MLVB2/S,CVB1/N/Y/E:MAIN@
      
      De /s  achter mlvb2 betekent dat L80 alleen die modules moet 
      meelinken waar routines in zitten die worden aangeroepen.
      
      In het  eerste C  voorbeeld worden  zowel bchput  als bchget 
      gebruikt.  Ze  zullen  in dit  geval dan  ook beiden  worden 
      meegelinkt  en er  is nu  dus geen echt voordeel behaald. In 
      het volgende  voorbeeld is  het gebruik  van de  (lastigere) 
      methode via MX wel al voordeliger (CVB2.C):
      
      typedef char void; 
      void bchput();
      
      void bputstr(string) 
      char *string; 
      { 
        while (*string) 
          bchput(*string++); 
      }
      
      void main() 
      { 
        bputstr("Dit is C voorbeeld 2"); 
      }
      
      Deze  listing kun  je vervolgens  compileren, assembleren en 
      linken met:
                      CF CVB2
                      CG -K CVB2
                      M80 = CVB2 /Z
                      L80 CVB2,MLVB2/S,CVB2/N/Y/E:MAIN@
      
      CVB2.COM zal nu 58 bytes groot zijn. Indien je echter met de 
      oude     library      had     gelinked      (middels     l80 
      cvb2,mlvb1,cvb2/n/y/e:main@),  dan  was  CVB2.COM echter  61 
      bytes  groot geweest  omdat dan ook de overbodige bchget was 
      meegelinked.
      
      Om  ook nog gebruik te kunnen maken van de functie parameter 
      checker  (fpc),  dient  de  source  nog  iets uitgebreid  te 
      worden. Om  FPC te  gebruiken, moeten namelijk lege C body's 
      worden  opgenomen zodat  FPC weet  hoeveel en  wat voor type 
      parameters  gebruikt  worden door  de functie.  Aangezien de 
      source hiervoor  echter door  CF.COM en door M80.COM gehaald 
      moet kunnen worden, is er een klein probleem (de programma's 
      verwachten namelijk een compleet verschillende syntax). Door 
      nu echter van een truuk gebruik te maken, kunnen evengoed de 
      lege  C routine body's en de echte ML routines in ��n enkele 
      source worden  gebruikt. De  truuk bestaat  eruit dat ; voor 
      M80  betekent dat  er commentaar begint (en dat alles wat er 
      achter staat  dus niet  van belang  is), terwijl het voor CF 
      een scheidings teken tussen te statements is, dus CF negeert 
      een losse ;.
      
      De source  kan dan  beginnen met "; /*". Alles wat daarna op 
      dezelfde  regel staat,  is dan  zowel voor  M80 als  voor CF 
      commentaar. Door  hierna goed  af te wisselenen met /* en */ 
      (voor  CF) en  .comment #  en # voor M80, is het mogelijk om 
      alles in 1 source te houden.
      
      
               E E N   V O O R B E E L D   L I B R A R Y 
      
      De  library XS-CLIB1.MAC  is de  volledig ontwikkelde versie 
      van  de  eerste  voorbeelden.  Er  zitten  ook nog  een paar 
      functies bij om de VDP aan te sturen. De volledig afgewerkte 
      library  staat  tevens  op  de  disk  (XS-CLIB1.REL). Om  er 
      gebruik  van  te  maken,  dient de  parameter XS-CLIB1/S  te 
      worden  opgenomen  achter  L80  in in  de batchfile  waar je 
      normaal  je  C  programma's  mee  linkt.  Om de  functies te 
      gebruiken, dien  je ook  nog de header file XS-CLIB1.H op te 
      nemen in je source.
      
      Met  CF.COM kan  uit deze  source nu  de .TCO  file voor  de 
      function parameter  checker worden  gehaald. De library zelf 
      kan   worden  aangemaakt   met  MX.COM.   Het  aanmaken  van 
      XS-CLIB1.TCO en XS-CLIB1.REL gaat nu als volgt:
      
      - Zorg ervoor dat CF.COM, MX.COM, M80.COM, LIB80.COM en 
        AREL.BAT op de disk staan.
      - Typ in: cf xs-clib1.mac om de .TCO file te maken.
      - En typ vervolgens in:
              MX -L XS-CLIB1 > TEMP.BAT TEMP
              REN XS-LIB1.LIB XS-LIB1.REL
        om XS-CLIB1.REL te maken.
      
      Het  bestand XS-VB.C  is een  voorbeeld van  het gebruik van 
      deze library. Dit bestand is te compileren met MK-XSVB.BAT.
      
                                                       Alex Wulms