Turbo Pascal Assembler

Tartalomjegyzék

Bevezetés

I. Gépi kód alkalmazása a TURBO Pascalban
1. Az inline utasítás
2. A TURBO ASSEMBLER feladata

II. A TURBO ASSEMBLER által olvasott és írt állományok
1. TURBO ASSEMBLER forrásállományok
2. A TURBO ASSEMBLER áltat létrehozható állományok
3. A TURBO ASSEMBLER által elfogadott assembly nyelv

III. A TURBO ASSEMLER futtatása
1. A TURBO ASSEMBLER indítása
2. TURBO ASSEMBLER parancsfüzérek
3. A TURBO ASSEMBLER közvetlen vezérlése
4. A TURBO ASSEMBLER lista állományok formátuma
5. Hibaüzenetek

Bevezetés
Egy magasszintű programnyelv adott processzoron való implementációjának hatékonyságát jelentősen megnöveli az, ha a processzor assembly nyelvén írt programok illeszthetők a magasszintű programnyelven írt programokhoz.
A TURBO Pascal Z80-as mikroprocesszorra és CP/M-80 operációs rendszerre írt fordítói (1.0, 2.0 és 3.0 változat) közvetlenül nem támogatják az assembly nyelvű programok illesztését TURBO Pascal programokhoz. Azonban a TURBO Pascal nyelvben létezik egy inline nevű utasítás, amely lehetővé teszi közvetlen gépi kód beillesztését TURBO Pascalban írt programba. Erről részletesebben szó lesz az I. fejezetben.
Az inline utasítás létezése egyszerű lehetőséget teremt assembly programok TURBO Pascal programokhoz való illesztésére. Ennek magvalósítására készült a TURBO ASSEMBLER, amelynek részletes leírását tartalmazza ez dokumentum.
A TURBO ASSEMBLER kifejlesztése, első ellenőrzése és alkalmazása Enterprise 128 mikrogépen, IS-DOS operációs rendszer alatt történt, ezért az alábbiakban különös hangsúlyt kap a TURBO ASSEMBLER Enterprise-on való használata. Azonban már itt megemlítendő, hogy (apró módosítással) a TURBO ASSEMBLER a CP/M operációs rendszer mindazon 8-bites változata alatt működőképes, amelyen legalább a TURBO Pascal 1.0 változatú fordítója futtatható.
Az alábbiak megértéséhez nem szükséges, de a hatékony alkalmazáshoz ajánlatos ismerni az eredeti TURBO Pascal kézikönyveket:

TURBO PASCAL, Reference Manual; TURBO PASCAL Version 2.0 and 8087
Supplement, Addendum to Reference Manual; TURBO PASCAL Version 3.0,
Borland International Inc., Scotts Valley, California, l983, 1984

I. GÉPI KÓD ALKALMAZÁSA TURBO PASCALBAN

1. Az inline utasítás
A TURBO Pascal felhasználói kézikönyv(ek) rövid tájékoztatása alapján a TURBO Pascal inline utasításáról a következők mondhatók.
A TURBO Pascal (mindhárom változata) az inline utasítás alkalmazásával lehetővé teszi gépi kód közvetlen beillesztését TURBO Pascal nyelvű forrásprogramba. Egy inline utasítás a lefoglalt INLINE szóból és két zárójel között, egymástól / (slash) jellel elválasztott jelelek sorozatából áll. Ezek mindegyike vagy konstans, vagy változó azonosító, vagy LC (location counter) hivatkozás.
A konstansok akár literal konstansok, akár konstans azonosítók lehetnek és feltétlenül integer típusúaknak kell lenniük. Ha egy literal értéke a 0..255 ($00..$FF) tartományba esik, akkor egybyte-os kódot ad, egyébként kétbyte-os kódot (elől áll az alsó byte). Konstans azonosítók mindig kétbyte-os kódot generálnak.
Változó azonosító két byte-os kódot generál (elől áll az alsó byte). Ha a változó azonosító var-ral lett deklarálva, akkor az általa generált szó annak a címnek a címét tartalmazza, ahol a változó értéke van a memóriában; egyébként a szó a változó memóriabeli címét tartalmazza.
Egy helyszámláló (LC:=location counter) hivatkozás formája: *+N vagy *-N, ahol N integer konstans. Itt * jelöli a LC aktuális (egyébként a programozó számára nem ismert) értékét és +N ill. -N adja meg annak az offset-nek az értékét, amivel a pillanatnyi LC értéket el kell tolni előre, ill. hátra.
Egyedül álló * jel két byte-os kódot generál, amely az LC pillanatnyi értékével egyenlő (elől áll az alsó byte). Ha a * jelet offset érték követ, akkor a kód generálása előtt az offset érték hozzáadódik az LC aktuális értékéhez, vagy levonódik abból.
A TURBO Pascal programban inline utasítások bárhol előfordulhatnak egy blokk utasításokat tartalmazó részében. Egy inline utasításban mindegyik CPU regiszter szabadon használható, de a stack pointer (SP) értéke az inline utasítás befejeztével ugyanaz kell legyen mint belépéskor (vagyis a vermet korrekt módon kell kezelni).
Az inline utasítás alkalmazására példaként két programot mutatunk be. Az első egy karakterfüzért meghatározott standard alakra transzformál. (Az itt szereplő LinePack procedure analógja egyébként a TURBO ASSEMBLER forrásprogramjában is megtalálható.)

Program LP;
type LineType=String(.80.);
var Line:LineType;

Procedure LinePack(var ForLine: LineType);
begin
  inline($2A/FORLINE/ { ld hl,(ForLine) }
         $54/     { ld d,h }
         $5D/     { ld e,l }
         $46/     { ld b,(hl) }
         $04/     { inc b }
         $0E/$00/ { ld c,0 }
         $13/     { inc de }
         $23/     { L1 inc hl }
         $7E/     { ld a,(hl) }
         $05/     { dec b }
         $CA/*+49/ { jp z,output }
         $FE/$20/ { cp 20h }
         $CA/*-9/ { jp z,L }
         $FE/$3B/ { cp 3bh }
         $CA/*+39/ { jp z,output }
         $F5/     { push af }
         $79/     { ld a,c }
         $FE/$00/ { cp 0 }
         $CA/*+7/ { jp z,L4 }
         $3E/$20/ { ld a,20h }
         $12/     { ld (de),a }
         $0C/     { inc c }
         $13/     { inc de }
         $F1/     { L4 pop af }
         $FE/$41/ { L2 cp 41h }
         $DA/*+4/ { jp c,L3 }
         $E6/$DF/ { and 0dfh }
         $12/     { L3 ld (de),a }
         $0C/     { inc c }
         $13/     { inc de }
         $23/     { inc hl }
         $7E/     { ld a,(hl) }
         $05/     { dec b }
         $CA/*+10/ { jp z,output }
         $FE/$20/  { cp 20h }
         $C2/*-19/ { jp nz,L2 }
         $C3/*-51/ { jp L1 }
         $3E/$20/ { output ld a,20h }
         $12/     { ld (de),a }
         $0C/     { inc c }
         $13/     { inc de }
         $3E/$20/ { ld a,20h }
         $12/     { ld (de),a }
         $0C/     { inc c }
         $13/     { inc de }
         $3E/$0D/ { ld a,0dh }
         $12/     { ld (de),a }
         $13/     { inc de }
         $3E/$0A/ { ld a,0ah }
         $12/     { ld (de),a }
         $2A/FORLINE/ { ld hl,(ForLine) }
         $71      { ld (hl),c }
        )
end;

begin
  Write('Enter line: ');Readln(Line);
  LinePack(Line);
  Writeln(Line);
  repeat until KeyPressed
end.

A program például a:

Line:=' Addr   ld  ($1234),A      ; save reg. A '

karakterfüzéren a következő átalakítást hajtja végre:

Line:='ADDR LD ($1234),A'

Nyilvánvaló az ilyen feladatot elvégző, gyors rutin jelentősége egy assembly fordítóprogram számára.
A második példa egy olyan eljárást mutat be, amely EXOS-hívást tartalmaz.

Procedure Time;
begin
  inline ($C3/*+13/ { JP START }
          $00/$00/$3A/ { HOUR DEFB $00,$00,$3A }
          $00/$00/$3A/ { MIN DEFB $00,$00,$3A }
          $00/$00/  { SEC DEFW $00,$00 }
          $00/$00/  { BUFF DEFW $00,$00 }
          $00/      { BYTE DEFB $00 }
          $F7/$20/  { START EXOS $20 }
          $D5/      { PUSH DE }
          $79/      { LD A,C }
          $CD/*+74/ { CALL CONV }
          $01/$02/$00/ { LD BC,$0002 }
          $21/*-14/ { LD HL,BUFF }
          $11/*-25/ { LD DE,HOUR }
          $ED/$B0/  { LDIR }
          $D1/      { POP DE }
          $D5/      { PUSH DE }
          $7A/      { LD A,D }
          $CD/*+57/ { CALL CONV }
          $01/$02/$00/ { LD BC,$0002 }
          $21/*-31/ { LD HL,BUFF }
          $11/*-39/ { LD DE,MIN }
          $ED/$B0/  { LDIR }
          $D1/      { POP DE }
          $7B/      { LD A,E }
          $CD/*+41/ { CALL CONV }
          $01/$02/$00/ { LD BC,$0002 }
          $21/*-47/ { LD HL,BUFF }
          $11/*-52/ { LD DE,SEC }
          $ED/$B0/  { LDIR }
          $DB/$B2/  { IN A,($B2) }
          $32/*-55/ { LD (BYTE),A }
          $F3/      { DI }
          $3E/$FF/  { LD A,$FF }
          $D3/$B2/  { OUT ($B2),A }
          $11/$D2/$BE/ { LD DE,$BED2 }
          $21/*-76/ { LD HL,HOUR }
          $01/$08/$00/ { LD BC,$0008 }
          $ED/$B0/  { LDIR }
          $3A/*-74/ { LD A,(BYTE) }
          $D3/$B2/  { OUT ($B2),A }
          $FB/      { EI }
          $C9/      { RET }
          $F5/      { CONV PUSH AF }
          $E6/$F0/  { AND $F0 }
          $0F/      { RRCA }
          $0F/      { RRCA }
          $0F/      { RRCA }
          $0F/      { RRCA }
          $C6/$30/  { ADD A,$30 }
          $21/*-92/ { LD HL,BUFF }
          $32/*-95/ { LD (BUFF),A }
          $23/      { INC HL }
          $F1/      { POP AF }
          $E6/$0F/  { AND $0F }
          $C6/$30/  { ADD A,$30 }
          $77/      { LD (HL),A }
          $C9       { RET }
         )
end;

Ez az eljárás kiolvassa az EXOS óráját és az eredményt kiírja az állapotsorba. Célszerű beletenni ezt egy TIME.TPU nevű állományba és bármely olyan TURBO Pascal programba include-dal belefoglalni, amelyben valamilyen algoritmus futási idejét akarjuk mérni. Például, a következő program megmutatja, hogy a benne szereplő ciklus 24 másodpercig fut Enterprise-on (IS-BASIC-ben kb. 18 percig!)

program CYCLE;
var i,j:Integer;
{ $I Time.tpu}
begin
  Time;
  for i:=1 to 32000 do j:=i mod 2;
  Time;
  Writeln('End cycle.');
  repeat until KeyPressed
end.

2. A TURBO ASSEMBLER feladata
A mutatott példák világosan mutatják, hogy egy inline utasítás létrehozásához először assemblyben meg kell írni a szükséges programot, majd táblázatból ki kell keresni a megfelelő kódokat, végül az ugrási címeket ki kell számítani a helyes LC hivatkozások felírása céljából. (És persze az egészet még be kell gépelni egy szövegszerkesztőbe és bele kell tenni egy állományba.)
A TURBO ASSEMBLER célja az, hogy a fenti fáradságos és hosszadalmas eljárás az assembly program megírására redukálódjék; a többit egy assembly fordító automatikusan végezze el.

II. A TURBO ASSEMBLER által olvasott és írt állományok

1. TURBO ASSEMBLER forrásállományok
A TURBO ASSEMBLER forrásállományok rendszerint olyan szövegállományok, amelyekben TURBO Pascal és assembly nyelvű programrészletek vannak. Az assembly programszegmenseket meg kell megjelölni úgy, hogy mindegyik előtt és utána olyan sort kell írni, amelynek elején a * jel áll. A * után bármi lehet az adott sorban, de lényeges, hogy a * a sor legelső karaktere legyen.
Fontos az, hogy az assembly programszegmenseket nem fejezhetjük be az END álutasítással; azt a fordító fel sem ismeri.

Egy TURBO ASSEMBLER forrásállomány elvben akármekkora méretű lehet és akárhány assembly programrészletet tartalmazhat; csak a rendelkezésre álló lemezes memória nagysága szab korlátot. Ez azért lehetséges, mert a TURBO ASSEMBLER a forrásállományt soronként dolgozza fel és még pufferelten sem tárolja azt a memóriában. Természetesen ez a tény csökkenti a fordítás sebességét.

A forrásprogram nevének kötelezően az ASM toldalékot kell adni; e nélkül a TURBO ASSEMBLER nem fogadja el azt feldolgozásra.
A forrásállomány assembly nyelvű szegmenseit kis és nagy betűkkel (esetleg ezeket keverve) lehet megírni. A fordító minden betűt nagybetűsít, ezért vigyázni kell arra, hogy például a 'Start' és 'sTaRt' címkéket a rendszer azonosnak (és a 'START'-tal egyenlőnek) fogja tekinteni (ugyanúgy, mint a TURBO Pascal).
Az assembly programrészletekben tetszőlegesen elhelyezhetünk üres, vagy csak szóközöket tartalmazó sorokat, továbbá az assembly programsorok mezői között tetszőleges számú szóköz helyezhető el.
A TURBO ASSEMBLER által elfogadott assembly nyelv szintaxisáról, valamint e nyelv specialitásairól (pl. az álutasításokról és a beépített makrókról) a 3. pontban lesz szó

2. A TURBO ASSEMBLER által létrehozható állományok
A TURBO ASSEMBLER három lemezes állomány létrehozására képes. Ezek bármelyikének (esetleg az összesnek) az előállítása letiltható a másik kettőtől függetlenül. Mindhárom létrehozható állomány szövegállomány.
A legfontosabb írhat állomány abban különbözik a forrásállománytól, hogy a forrásállomány * jelekkel megjelölt assembly programrészletei helyén a megfelelő inline utasítások állnak. Az assembly szegmensek közti rész minden értelmezés nélkül, változatlan formában átmásolódik. A TURBO ASSEMBLER célja éppen ennek az állománynak a létrehozása, amit a továbbiakban INLINE formátumú állománynak nevezünk.
Az INLINE formátumú állomány a tipikus esetben már teljesen szabványos TURBO Pascal programot tartalmaz, amit a TURBO fordító le tud fordítani.
A másik két TURBO ASSEMBLER által generált állomány hibakeresési, illetve dokumentálási célokat szolgáló lista file. Az egyik részletes listáját adja a forrásállományban levő mindegyik assembly programrészlet inline transzformáltjának; a másik az assembly szegmensek internal és external szimbólumainak jegyzéke.
A TURBO ASSEMBLER által írható állományok létrehozásának módjáról, valamint a lista állományok formátumáról a III. fejezetben lesz szó.

3. A TURBO ASSEMBLER által elfogadott assembly nyelv
Ebben a pontban azt vizsgáljuk meg, hogy a TURBO ASSEMBLER forrásállományok assembly nyelvű programszegmenseinek milyen szintaktikai feltételeknek kell eleget tenniük ahhoz, hogy azokat a TURBO ASSEMBLER helyesen értelmezhesse.
Látni fogjuk, hogy a fejlett assemblerekhez képest a TURBO ASSEMBLER-ben jelentős megszorítások vannak. Ezek egy része a fordítás gyorsaságát szolgálja, más része viszont csak a fordító jelenlegi változatának hiányossága, amit a későbbi verziók majd fokozatosan kiküszöbölnek.

3.1 Az alkalmazható assembly nyelv
A TURBO ASSEMBLER kizárólag ZILOG mnemonikokkal írt, Z80 assembly nyelvű programokat fogad el; INTEL mnemonikokat egyáltalán nem ismer fel.

3.2 Az assembly programsorok szerkezete
Az assembly nyelvű programsorok általában négy mezőt tartalmazhatnak:

A mezők egyikében sem szerepelhet a szóköz karakter, de közöttük és előttük tetszőleges számú szóköz állhat.

A címkemezőben (a sor hossza által korlátozott méretben) tetszőleges hosszúságú karakterfüzér állhat. A címkékben a billentyűzet bármely nyomtatható karaktere szerepelhet (számok és speciális jelek is). A címkék számokkal is kezdődhetnek, sőt maguk a számok is lehetnek címkék. Az egyetlen megkötés az, hogy a * jel nem lehet az első karakter egy olyan címkében, amely közvetlenül a sor elején kezdődik. (Ugyanis ekkor a fordító azt fogja hinni, hogy ez az assembly programszegmenst lezáró sor.) Példák helyes címkékre

(help-7
_IdEntifier:21
34$/*
start
0

A címkék után állhat :, de akkor ez a karakter hozzátartozik a címkenévhez, tehát a címke minden előfordulása helyén ki kell tenni.

Az utasításmezőben a standard Z80-as ZILOG assembly nyelv tetszőleges operátora állhat. Ezek a szabványos ZILOG mnemonikájú utasítások (66 darab) és még három makrooperátor, az EXOS, EXDOS és ISDOS, továbbá még öt álutasítás operátor, az EQU, DEFB, DEFW, DEFS és DEFM. A standard Z80 assembly operátorok felsorolásától eltekintünk; a három beépített makrooperátorral pedig később részletesen foglalkozunk.

Az operandusmezóben az utasításmezőben álló operátor által megengedett operandusok állhatnak. Itt a standard Z80-as assembly nyelvhez képest a következő általánosítás használható: egy kivételtől eltekintve, minden olyan helyen állhat címkenév ahol az eredeti operandusban is konstans szerepelhet.
Például:

LD HL,(LINE)
LD (PORT_VALUE),A
IN (0PAGE),A

helyes operandusok. Az első kettőben 'LINE' ill. 'PORT_VALUE' lehetnek az assembly szegmensben értelmezett címkék, vagy external változó nevek, vagy EQU által értelmezett kétbyte-os konstans azonosítók; a harmadikban '0PAGE' lehet EQU által rögzített egybyte-os konstans azonosító.
A kivétel az IX és IY indexregiszterek argumentumában álló eltolás esete; itt explicit konstansnak kell állnia. A JR és DJNZ argumentumában szerepelhet címekenév.

A megjegyzés-mezőnek mindig a ; karakterrel kell kezdődnie. Ezeket a fordító figyelmen kívül hagyja, de beleteszi a nyomtatható állományba. Megengedett az, hogy egy programsor teljes egészében egyetlen megjegyzés-mezőt tartalmazzon.

3.3 Konstansok
Szigorú szintaktikai megkötés érvényes az alkalmazható konstansokra vonatkozóan.

Numerikus konstans csakis egy-, vagy kétbytos hexadecimális szám lehet, amelynek első karaktere kötelezően a $ jel és amelynek mind a két, vagy mind a négy hexadecimális számjegyét ki kell írni. Példák helyes konstansokra:

$fe
$0001
$00
$E106

Tehát például a DE regiszterpárba a LD DE,$0000 utasítással kell 0-t helyezni.

Karakter konstans a 3.2 pontban említett kivételtől eltekintve minden olyan utasítás operandusában szerepelhet, ahol egyébként egybyte-os numerikus konstans megengedett. Kilenc ilyen utasítás létezik:
ADC, ADD, AND, CP, LD, OR, SBC, SUB és XOR.

Egy karakter konstans alakja:

'Char'

ahol Char a szóban forgó karakter. Ennek helyére a fordító egyszerűen behelyettesíti a Char karakter ASCII kódját. Például a:

CP 'A'

programsor kódja $FE/$41/ lesz.
Vigyázzunk arra, hogy üres karakter konstans nem létezik, továbbá álutasításokban (a DEFM kivételével) nem szerepelhet karakter konstans.

3.4 Kifejezések
A TURBO ASSEMBLER jelenlegi változata semmilyen kifejezés kiértékelésére nincs felkészítve. Ezért például a:

LD BC,Str_Last-Str_First

utasítás operandusában álló név a hivatkozások feloldása során nem az Str_Last és Str_First cimkék konkrét értékének különbségével lesz helyettesítve, hanem az 'Str_Last-Str_First' karakterfüzért egyetlen névnek tekinti a fordító (amit persze nem fog megtalálni a szimbólum táblában), ezért ehhez az utasításhoz a következő kódot generálja:

$01/STR_LAST-STR_FIRST/

és az assembler nem jelez hibát, mert azt feltételezi, hogy az általa nem azonosítható 'STR_LAST-STR_FIRST' név external azonosító (ami az assembly programszegmensen kívül álló TURBO Pascal programban értelmezett).
A helyzetet tovább bonyolítja az, hogy az inline utasítás argumentumában szerepelhetnek kifejezések, amiket a TURBO fordító felismer. Például, ha Inter egy integer típusú változó a TURBO Pascal programban, akkor az assembly programrészletben szereplő:

ld a,Inter+7

utasításhoz tartozó TURBO ASSEMBLER kód:

$3E/INTER+7/

lesz az inline argumentumában, ami a TURBO fordító által felismerhető és az 'INTER+7' jel helyére a TURBO fordító az INTER konkrét értékeinek és 7-nek az összegét helyettesíti.
A súlyos hibalehetőségek miatt ajánlatos a kifejezések elkerülése; ez valamelyest csökkenti a TURBO ASSEMBLER hatékonyságát.

3.5 Álutasítások
A TURBO ASSEMLER öt pszeudooperátort tud azonosítani, ezek az EQU, DEFB, DEFW, DEFS és DEFM álutasítások.

3.5.1 EQU álutasítás
Az EQU álutasítást tartalmazó assembly programsor formája:

címke EQU konstans megjegyzés

ahol 'konstans' olyan numerikus konstans, amelynek a formájáról a 3.3 pontban volt szó. A 'konstans' helyén semmilyen karakter konstans nem állhat.
A 'konstans' lehet egybyte-os ($dd), vagy kétbyte-os ($dddd). A fordító a hivatkozások feloldása során az 'azonosító' minden előfordulása helyére a hozzátartozó 'konstans' értéket fogja behelyettesíteni.
Ha egy utasítás operandusában egybyte-os konstansnak kell állnia (ami nem offset jellegű, ld. 3.2),de azt olyan névvel helyettesítjük, amelynek olyan EQU álutasítással adunk értéket, amelyben az EQU után kétbyte-os konstans van, akkor a fordító a kétbyte-os konstans magasabb helyiértékű byte-ját helyettesíti be a név helyére és nem küld hibaüzenetet. Például:

ADDR EQU $F04E
...
LD B,ADDR

esetében az adatmozgatást végző utasítás kódja $06/$F0/ lesz, mintha ADDR értéke $F0 lenne.
Lényeges szabadságot jelent az, hogy postdefinit EQU álutasítások megengedettek, tehát egy azonosító értékét a programban lehet később definiálni EQU-val, mint az előfordulása(i)kor. Például az összes EQU álutasítás összegyűjthető az assembly program végén.

3.5.2 DEFB álutasítás
A DEFB álutasítást tartalmazó programsor formája:

címke DEFB arg megjegyzés

ahol 'arg' olyan karakterfüzér, amelynek alakja:

$dd,$dd,...,$dd

ahol 'd' tetszőleges hexadecimális számjegyet jelöl. Ennek hatására a TURBO ASSEMBLER fordító a LC pillanatnyi helyére elhelyezi az 'arg'-ba írt byte-okat. Az elhelyezhető byte-ok számát csak a programsor hossza korlátozza. Itt arra kell ügyelni, hogy minden hexadecimális szám megengedett formátumú legyen (ld. 3.3) és közöttük ',' álljon. Vigyázni kell továbbá, hogy szóköz ne legyen a DEFB argumentumában.
A DEFB álutasítással nincs lehetőség karakterek, vagy karakterfüzérek ASCII kódjainak elhelyezésére a memóriában (arra a DEFM álutasítás való).

3.5.3 DEFW álutasítás
A DEFW álutasítást tartalmazó programsor formája:

címke DEFW $dd,$dd megjegyzés

vagy

címke DEFW $dddd megjegyzés,

ahol 'd' mindenütt hexadecimális számjegyet jelöl.
Az első eset egyenértékű a

címke DEFB $dd,$dd megjegyzés

esetével és ekkor a két byte abban a sorrendben lesz elhelyezve a memóriában, ahogy azok a DEFW argumentumában állnak.
A második esetben a $dddd szó úgy lesz elhelyezve, hogy elől áll az alsó byte, utána a felső byte. Például a

DEFW $A0,$00

kódja $A0/$00/ lesz, azonban a

DEFW $A000

kódja: $00/$A0/.

3.5.4 DEFS álutasítás
A DEFS álutasítást tartalmazó programsor formája:

címke DEFS $dd megjegyzés

vagy

címke DEFS $dd,$ee megjegyzés

ahol 'd' és 'e' hexadecimális számjegyet jelölnek.
Az első hatására a fordító az LC pillanatnyi helyére $dd darab 0-t helyez el; a második esetben ugyanide a $ee byte-ot helyezi el $dd-szer.
A DEFS álutasítással maximum 255 darab byte helyezhető el a memóriában. Ügyeljünk arra, hogy a DEFS alkalmazása jelentősen növeli az INLINE formátumú állomány méretét.

3.5.5 DEFM álutasítás
A DEFM álutasítást tartalmazó programsor formája:

címke DEFM 'String' megjegyzés

3.5.6 Nem implementált álutasítások
A fejlett assemblerekhez képest (pl. Microsoft MACRO-80) a TURBO ASSEMBLER lényeges hiányossága, hogy hiányoznak belőle a feltételes fordítás (IF), a file kezelés (INCLUDE), az ismétléses fordítás (REPT, IRP), lista-formátum vezérlés és (legfontosabbként) a makró (MACRO) álutasítások.

3.6 Beépített makrók
A TURBO ASSEMBLER 1.4 változata nem makroassembler, tehát a felhasználó által értelmezett makrók lefordítására alkalmatlan. Létezik azonban három beépített makró, amelyek kifejezetten az Enterprise-on való alkalmazást támogatják, lehetővé téve az EXOS, EXDOS és IS-DOS operációs rendszerek közvetlen hívását assemblyből.

3.6.1 EXOS makró
Az EXOS makrót tartalmazó programsor alakja:

címke EXOS $dd megjegyzés,

ahol 'd' hexadecimális számjegyet jelöl. Ennek kódja: $F7/$dd/
tehát ekvivalens a DEFB $F7,$dd pszeudoutasítással.
M int ismeretes, ennek hatására meghívódik az EXOS $dd-sorszámú funkciója, amelynek paramétereit előzőleg az A,BC,DE,HL regiszterekben be kell állítani. A $dd konstans értéktartománya $00-tól $22-ig terjed (kivéve a $0C-$0F értékeket).

3.6.2 EXDOS makró
Az EXDOS makrót tartalmazó programsor lehetséges alakjai:

címke EXDOS $dddd megjegyzés
címke EXDOS ADDRESS megjegyzés
címke EXDOS 'String' megjegyzés

ahol $dddd konkrét kétbyte-os hexadecimális konstans és ADDRESS tetszőleges címkenév és String szabályos EXDOS parancsfüzér. Mindegyik esetben egy EXDOS parancs hajtódik végre (ld. EXDOS Felhasználói kézikönyv), azonban

Például; ha tudjuk, hogy a $4000 címen a 'DIR A:' karakterfüzér ASCII kódsorozata áll (legelöl a hosszbyte), akkor az

EXDOS $4000

utasítás hatására futtatáskor az A: lemez könyvtára kiíródik a képernyőre. Természetesen nem ez a tipikus eset, hanem amikor az assembly programon belül mi magunk helyezzük el egy ADDRESS címen a parancsfüzért; pl:

ADDRESS DEFB $06
DEFM 'DIR A:'
...
EXDOS ADDRESS

hatása ugyanaz lesz, mint az előbbi parancsé. De még közvetlenebb módon végrahajtható ugyanez a harmadik módszerrel:

EXDOS 'dir a:'

Ebben az esetben hosszbyte-ra nincs szükség; azt a fordító kiszámítja.

3.6.3 ISDOS makró
Az ISDOS makrót tartalmazó programsor alakja:

címke ISDOS $dd megjegyzés

Ennek hatása egyenértékű a:

LD C,$dd
CALL $0005

utasítások fordításával, tehát $dd a hívott IS-DOS funkció sorszáma. Természetesen a sikeres híváshoz az E regiszterben, ill. a DE és HL regiszterpárban előzőleg be kell állítani a szóban forgó IS-DOS funkció által igényelt paramétereket.

3.7 Relatív ugrások
A JR és DJNZ utasítások argumentuma vagy konkrét egybyte-os konstans:

JR $dd
JR cond,$dd
DJNZ $dd

vagy belső (tehát az assembly programszegmensben értelmezett) címke:

JR ADDR
JR cond,ADDR
DJNZ ADDR

ahol 'd' hexadecimális számjegyet jelöl, 'cond' a 'C','NC','Z' és 'NZ' jelek valamelyike és ADDR címke.
A standard Z80-as assembly nyelvben csak az első eset szerepel; ekkor a relatív ugrások argumentumában álló konstans jelentése, valamint a hozzájuk tarozó kód értéke jól ismert. A TURBO ASSEMBLER itt csak annyi megszorítást tesz, hogy a $dd konstans értékét nem lehet EQU álutasítással beállítani, tehát például a következő programrészletet a TURBO ASSEMBLER szintaktikusan hibásnak tekinti:

OFFSET EQU $FA
...
JR C,OFFSET

A második esetben a fordító kikeresi a szimbólum táblából az ADDR címke kódrelatív értékét és ebből, valamint az PC aktuális értékéből kiszámítja az eltolás értéket. Ha ez beleesik a -126..+128 tartományba, akkor előállítja az ehhez az offsethez tartozó kódot. Az ADDR értékét sem szabad EQU-val beállítani.

III. A TURBO ASSEMBLER futtatása

1. A TURBO ASSEMBLER indítása
A TURBO ASSEMBLER futtatása előtt mind a TURBOASM.COM nevű fordítónak, mind a forrásállománynak lemezen kell lennie és ezeket a lemezeket be kell helyezni a megfelelő (nem feltétlenül azonos) meghajtóba.
A forrásállomány nevének toldaléka kötelezően .ASM legyen; erre a megkötésre a forrásállomány védelmében van szükség. Ha a forrásállomány neve SRCNAME.ASM, akkor a TURBO ASSEMBLER a fordítás során az aktuális meghajtóban levő lemezen létrehozza a SRCNAME.001, SRCNAME.002 és esetleg a SRCNAME.003 nevű átmeneti állományokat (amelyeket a fordítás befejeztével le is töröl), ezért vigyázni kell arra, hogy ilyen nevű állományok ne legyenek ezen a lemezen.
Ezután a TURBO ASSEMBLER futtatása kétféleképpen történhet; IS-DOS-ból parancsfüzérrel és közvetlen parancsátadással.
A TURBO ASSEMBLER operációs rendszerből való vezérlése az alábbi IS-DOS parancs begépelésével történik:

d> Eszköznév:TURBOASM parancsfüzér <CR>

ahol

A TURBO ASSEMBLER parancsfüzér szintaxisával és értelmezésével a 2. pont foglalkozik.
Ha a TURBO ASSEMBLER elfogadja a parancsfüzért, akkor az állapotsorban megjelenik a 'TURBO PASCAL ASSEMBLER' felirat, kiíródik a verzió száma, a terminál neve, a forrásprogram neve és azonnal megkezdődik a parancsfüzérben meghatározott állományok létrehozása. A program normális lefutása, vagy bármilyen run-time hiba esetén a vezérlés visszaadódik az operációs rendszernek. A hibák kezeléséről az 5. pontban lesz szó.

A TURBO ASSEMBLER közvetlen vezérléséhez a következő IS-DOS parancsot kell kiadni:

d> Eszköznév: TURBOASM <CR>

ahol 'd' és 'Eszköznév' ugyanaz, mint fent.
Ekkor az IS-DOS a kijelölt eszközről betölti a TURBOASM.COM állományt az operatív tárba a $0100 címtől és a PC-t ráállítja a program elejére. Ezután megtörténik ugyanaz a feliratozás, amiről az előbb szó volt, de ezúttal a parancsértelmező kiír egy prompt-ot és arra választ vár. Ekkor az operátornak közvetlenül be kell gépelnie a forrásállomány és a létrehozandó állományok nevét; a részletekről a 3. pontban lesz szó. Ezután indul el a tényleges fordítás.

2. TURBO ASSEMBLER parancsfüzérek
A TURBO ASSEMBLER parancsfüzérnek tartalmaznia kell a feldolgozandó forrásállomány nevét és helyét, valamint a létrehozandó állományok nevét és helyét. A megengedett TURBO ASSEMBLER parancsfüzérek alakja hasonlít a szabványos CP/M parancsok alakjára:

parancsfüzér := 'TARGET,PRINTF=SOURCE/SYMTAB'

ahol:

Megjegyzések:

  1. A parancsfüzér szóközt nem tartalmazhat; ellenkező esetben a fordító általában nem fogja felismerni azt. A parancsfüzért megelőző és azt követő szóközök, valamint a füzért követő szóközök után álló jelek hatástalanok. Mivel a parancsfüzért az IS-DOS adja át a TURBO ASSEMBLERnek, ezért minden betű nagybetűvé alakul, tehát a parancsban ennek nincs jelentősége.
  2. A forrásállomány neve előtt álló eszköznév annak a lemezmeghajtónak a neve, amelybe azt a lemezt helyeztük, ahol a főkönyvtárban megtalálható a forrásállomány. Itt ösvény nem adható meg; tehát ha a forrásállomány valamelyik alkönyvtárban van, akkor előbb ki kell azt mozgatni (MOVE) vagy másolni (COPY) onnan a főkönyvtárba.
  3. Ha az eszköznévben lemezmeghajtó jele áll, akkor a TURBO ASSEMBLER létrehozza az adott lemezen a kettőspont után álló névvel ellátott lemezes állományt (kivéve természetesen a forrásállomány esetét). Ha valamelyik létrehozandó állomány neve előtt nincs lemezmeghajtó név megadva, akkor az illető állomány az aktuális lemezen jön létre.
  4. Állománynevet nem igénylő eszköznév (CON ill. LST) esetén a : után álló állománynév figyelmen kívül marad. Ha a 'CON:' (ill. 'LST:') eszközt specifikáljuk a parancsfüzér első, második vagy negyedik mezőjében, akkor az INLINE alakú állomány, a lista állomány vagy a szimbólum tábla tényleges lemezes állományként nem jön létre, hanem kiíródik a képernyőre (ill. sornyomtatóra). Természetesen a forrásállomány mezőjében ezek az eszköznevek nem használhatók.
  5. A 'CON:' specifikáció helyett 'TRM:' is használható; ezek hatása egyenértékű.
  6. Nem ajánlatos INLINE formátumú és nyomtatható állomány egyidejű létrehozását kérni a 'CON:' eszközre, vagyis kerülendő a (valójában helyes):
        'CON:,CON:=SOURCE/SYMTAB'
    alakú parancsfüzér. Ekkor ugyanis a képernyőn a két állomány "egymásba fűzve" jelenik meg, azaz minden inline sort lista sor követ, ezért a kép kevésbé tekinthető át.
  7. Mindegyik állománynévnek van alapértelmezés szerinti toldaléka, tehát ha toldalékot nem írunk, akkor az alapértelmezés szerintit használja a TURBO ASSEMBLER parancsértelmezője. Ez azt is jelenti, hogy ha a forrásállomány nevében nem szerepel toldalék, akkor azt a fordító ASM toldalékú állományként fogja keresni a specifikált lemezen.
  8. A forrásállományon kívül a többi nevének toldaléka lényegében szabadon választható. Az egyetlen korlátozás az, hogy az INLINE állománynak, valamint a lista állományoknak nem adható a toldalék nélküli forrásállomány név a '001' vagy '002' vagy '003' toldalékkal ellátva, mert ez feltétlenül run-time hibára vezet.
  9. A parancsfüzérnek tartalmaznia kell az = jelet, és utána a forrásállomány spacifikációját, különben 'Command error.' hibaüzenettel visszakerül a vezérlés az operációs rendszerhez.
  10. Ha az = jel előtt nincs vessző, akkor nyomtatható (PRN) állomány nem jön létre, azonban létrejön az INLINE formátumú állomány, amelynek nevét az = jel előtt megadtuk, vagy ha nincs név, (tehát pl. semmi nincs az = jel előtt) akkor ez SRCNAME.INL néven keletkezik (ha a forrásállomány neve SRCNAME.ASM).
  11. Ha az = jel előtt csak egy vessző áll, akkor sem INLINE formátumú, sem nyomtatható állomány nem jön létre. Azonban a fordító kiírja a képernyőre a talált hibákat. Új assembly program első átírási kísérleténél célszerű ilyen parancsfüzért használni, mert a fordító ekkor dolgozik a leggyorsabban és a leghamarabb kijelzi az általa felismerhetetlen assembly programsorokat.
  12. Szimbólum tábla mindenképpen létrejön a fordítás során, de ha a parancsfüzérben nem szerepel a forrásállomány specifikációja után (közvetlenül) a / jel, akkor a második menet végrehajtása után a szimbólum tábla törlődik. Ha szükség van rá, akkor egyedül a / jel is elmenti ezt az állományt SRCNAME.SYM név alatt az aktuális lemezen (ha a forrásállomány neve SRCNAME.ASM). Ha más néven, vagy más meghajtóban levő lemezen kívánjuk felvenni a szimbólum táblát, akkor meg kell adni a specifikációját. Ha csak a képernyőre akarjuk kinyomtatni, akkor a parancsfüzért a '/CON:' karaktersorozattal kell befejezni.

Az alábbi táblázat összefoglalja a megengedett TURBO ASSEMBLER parancsfüzéreket és azok hatását. A táblázatban álló 'I' vagy 'N' azt jelöli, hogy az abban az oszlopban álló állomány az adott sorban található parancsfüzér hatására létrejön vagy nem. Az 'I' után álló <.EXT> kifejezés mutatja, hogy az adott esetben a megfelelő állomány a SRCNAME.EXT nevet kapja, ha a forrásállomány neve SRCNAME.ASM.

Parancsfüzér TARGET PRINTF SYMTAB
TARGET,PRINTF=SOURCE/SYMTAB
I
I
I
TARGET,=SOURCE/SYMTAB
I
I (.PRN)
I
TARGET,=SOURCE/
I
I (.PRN)
I (.SYM)
TARGET,PRINTF=SOURCE
I
I
N
TARGET,=SOURCE
I
I (.PRN)
N
TARGET=SOURCE/SYMTAB
I
N
I
TARGET=SOURCE/
I
N
I (.SYM)
=SOURCE/SYMTAB
I (.INL)
N
I
=SOURCE/
I (.INL)
N
I (.SYM)
,PRINTF=SOURCE/SYMTAB
N
I
I
,PRINTF=SOURCE/
N
I
I (.SYM)
TARGET=SOURCE
I
N
N
=SOURCE
I (.INL)
N
N
,PRINTF=SOURCE
N
I
N
,=SOURCE/SYMTAB
N
N
I
,=SOURCE/
N
N
I (.SYM)
,=SOURCE
N
N
N

Példa:
Tegyük fel, hogy a TURBOASM.COM nevű állomány az 'A:' lemez 'TURBO3' nevű alkönyvtárában van és a 'B:' lemez főkönyvtárában található SRCNAME.ASM nevű szövegállományban levő két (vezető és bezáró soreleji csillaggal megjelölt) assembly alprogramot kívánjuk átírni INLINE alakra. Az aktuális lemezegység legyen a RAMDISK.
Első lépésben meggyőződünk arról, hogy a programokban nincs hiba, tehát kiadjuk IS-DOS-ból a következő utasítást:

E> A:\TURBO3\TURBOASM ,=B:SRCNAME <CR>

Ekkor elindul a TURBO ASSEMBLER és megtörténik az 1. pontban leírt feliratozás, majd azonnal megjelenik a

Source file name: SRCNAME.ASM
No object file.

Compiling the 1. assembly subprogram

üzenet és megkezdődik az első assembly alprogram átírásának első menete, amit hibátlan esetben azonnal követ a második menet, végül normális lefutás esetén a:

Pass 1.
Pass 2.
No error.

üzenetet látjuk a képernyőn. A fordítás közben előforduló hibák kijelződnek és ekkor a 'No error.' felirat helyett azt látjuk, hogy:

Assembly error(s) found.

és ettől függetlenül elindul a második assembly alprogram feldolgozása, amit a:

Compiling the 2. assembly subprogram

felirat vezet be.

Tegyük fel, hogy semmi hiba nincs; ekkor célszerű elkészíteni az INLINE formátumú és a nyomtatható állományt. Ha az utóbbit csak a képernyőn akarjuk megjeleníteni és az INLINE formátumú állományt még nem akarjuk felvenni, hanem csak kinyomtatni a sornyomtatóra, akkor kiadjuk a következő parancsot:

E> A:\TURBO3\TURBOASM LST:,CON:=B:SRCNAME <CR>

Ekkor ismét lejátszódik az előbbi folyamat azzal a különbséggel, hogy mindkét assembly alprogram fordításának második menetében a képernyőn megjelenik az éppen lefordított programsor listája és az üzembe helyezett printerre kinyomtatódik a megfelelő kódsorozat. Miután ismét meggyőződünk a létrejövő program helyességéről, elkészítjük az INLINE formájú állományt például a OBJNAME.INL néven mondjuk az 'A:' lemezen a következő parancs végrehajtásával:

E> A:\TURBO3\TURBOASM A:OBJNAME=B:SRCNAME/LST: <CR>

Ezúttal a nyomtatóra kerül a szimbólum tábla és az 'A:' lemez tartalmazni fogja az INLINE formátumú állományt, amely bármilyen Turbo Pascal programba beilleszthető.

3. A TURBO ASSEMBLER közvetlen vezérlése
Mivel a kipróbált forrásállományokra mindenképpen szükség van, ezért helykímélés céljából célszerű csak az assembly forrásállományokat tárolni lemezen és szükség esetén azokat egyszerre lefordítani a TURBO ASSEMBLERREL. Ezt teszi lehetővé a TURBO ASSEMBLER közvetlen vezérlési módja. Az 1. pontban már volt szó arról, hogy ekkor egyszerűen el kell indítani a TURBOASM.COM fordítóprogramot az operációs rendszerből. Ekkor a TURBO ASSEMBLER négy kérdést tesz fel egymás után:

Source file name <.ASM>:
Object file name <.INL>:
List file name <.PRN>:
Symbol Table name <.SYM>:

Az operátornak be kell gépelni az értelemszerű válaszokat.
Az eszköz- és állománynevek használatával kapcsolatban ugyanazok érvényesek, amiket a 2. pontban elmondtunk. Ha például a SRCNAME.ASM nevű forrásállomány a 'B:' lemez főkönyvtárában van és elő akarjuk állítani az 'A:' lemezen az OBJNAME.PAS nevű INLINE formátumú állományt és a lefordított program listáját ki akarjuk nyomtatni a sornyomtatóra, miközben a szimbólum táblát a képernyőn látni szeretnénk, akkor a következő utasítássorozatra van szükség:

B:SRCNAME <CR>
A:OBJNAME.PAS <CR>
LST: <CR>
CON: <CR>

Ha valamelyik output állományra nincs szükség, akkor az ENTER billentyűt kell megnyomni.
A válaszok begépelése után a TURBO ASSEMBLER ugyanúgy működik, mintha az operációs rendszerből kapta volna a megfelelő parancsfüzért. Az egyetlen különbség az, hogy a fordítás befejeztével megjelenik a:

Terminate program ? (Y/N)

kérdés, amelyre 'Y'-nal válaszolva lehetőség nyílik újabb assembly programok lefordítására a TURBO ASSEMBLER-rel, az operációs rendszerbe való visszatérés nélkül.

4. TURBO ASSEMBLER lista állományok formája
A TURBO ASSEMBLER által előállítható nyomtatható állomány olyan ASCII file, amelynek minden sora három mezőt tartalmaz. A harmadik mezőben van az eredeti assembly utasítás, a másodikban annak kódja és az elsőben a kód legalsó byte-jának kódrelatív címe, ami azt mutatja meg, hogy ez a byte hányadik az inline argumentumában álló kódsorozat első byte-jától számítva (az első byte címét 0-nak tekintve).
A TURBO ASSEMBLER által előállított szimbólum tábla olyan szövegállomány, amelynek két részből áll. Az első rész a belső (vagyis az eredeti assembly programban értelmezett) szimbólumokat tartalmazza. Ennek minden sorában szerepel a szimbólum neve és annak kódrelatív címe, ha cimkéről van szó, illetve annak hexadecimális értéke, ha EQU álutasításban szereplő szimbólumról van szó. A második rész a külső (vagyis az eredeti assembly programban nem értelmezett external) szimbólumok nevét tartalmazza.

5. Hibaüzenetek
A TURBO ASSEMBLER jelenlegi változata csak kevés hibaellenőrzést végez; a későbbi változatok majd kiküszöbölik ezt a hiányosságot.

a) A TURBO ASSEMBLER parancsértelmező hibaüzenetei:

Ha IS-DOS-ban adjuk meg a TURBO ASSEMBLER parancsfüzérét és abban nem szerepel az = jel, akkor

'Command error.'

hibaüzenetet kapunk. Ha az = jel szerepel, de az utána álló, szükségképpen .ASM kiterjesztésű állomány nem létezik a megadott meghajtóban levő lemezen, akkor

'File SRCNAME.ASM not found.'

hibaüzenet jelenik meg a képernyőn.
Ha a forrásállomány azonosítható, de sehol nincs benne soreleji *, akkor a

'No assembly subprogram.'

üzenetet kapjuk.
Ha minden rendben van de az aktuális lemezen nincs elegendő hely a fordítás közben létrehozandó állományok számára, akkor az alábbi hibaüzenet jelenik meg:

I/O error F0, PC=nnnn
Program aborted

és a vezérlés visszaadódik az operációs rendszernek (csakúgy mint a többi I/O hiba esetén). Itt nnnn értéke a TURBO ASSEMBLER adott változatától függ.
Ha a TURBO ASSEMBLER-t közvetlenül irányítjuk, akkor csak a három utóbbi esetben lesz (ugyanolyan) hibaüzenet.

b) A TURBO ASSEMBLER fordító hibaüzenetei:
Két I/O hiba lehetséges fordítás közben. Ha szimbólum tábla létrehozását kértük, akkor az a)-ban említett hiba előfordulhat, mert az external szimbólumok számára még egy állományt kell nyitni. Ha a fordító által létrehozott átmeneti állományok írása közben betelik az aktuális lemez, akkor

I/O error F1, PC=nnnn
Program aborted

hibaüzenet adódik. Ekkor az aktuális lemezen megtalálhatók a SRCNAME.001, SRCNAME.002 és esetleg SRCNAME.003 állományok (ha a forrásállomány neve SRCNAME.PAS), amelyek közül az első kettő az INLINE formátumú, ill. nyomtatható állomány és az utolsó a szimbólum tábla nem formázott töredéke.
Ha a fordító nem ismer föl egy assembly programsort, akkor a létrehozandó INLINE formátumú állományba, illetve nyomtatható állományba az:

'Syntax error'


sort írja be, feltéve, hogy ilyen állományok előállítására valóban utasítottuk a TURBO ASSEMBLER-t, továbbá a képernyőn is megjelenik ez, a hozzá tartozó assembly programsorral együtt. Ha ilyen állományokat nem kértünk, akkor csak a képernyőre íródik ki ez az üzenet.
Ha egy címke vagy EQU által értelmezett paraméter többszörösen definiált a programban és történik hivatkozás rá, akkor a

'Duplicate label:'

vagy

'Duplicate parameter:'

hibaüzenetek jelennek meg a képernyőn, a hozzájuk tartozó forrásprogrambeli utasítással együtt. Ilyenkor az INLINE formátumú állományba nem kerül hibaüzenet, hanem a cimke vagy paraméter legelső előfordulása szerepel benne.
Ha a JR vagy DJNZ utasítás argumentumában szereplő offset érték -126-nál kisebb, vagy +128-nál nagyobb, akkor a képernyőn a

'Phase error.'

üzenet jelenik meg, a hozzátartozó kóddal együtt és ez a létrehozandó INLINE formátumú állományba is kiíródik. Ha JR vagy DJNZ argumentumában belső címke áll, amely többszörösen értelmezett és a legelső definíció is fázishibás (tehát két hiba egyszerre fordul elő), akkor a fázishibának lesz prioritása és csak azt jelzi ki a fordító.
A fordító egyéb hibaellenőrzést nem végez.

Megjegyezzük még, hogy a fordítás első menetének elindítása előtt a STOP vagy a Ctrl-C billentyűk megnyomásával a program megszakítható. Mivel a fordítás első menete automatikusan elindul, ha parancsfüzérrel vezéreljük a TURBO ASSEMBLER-t, ezért ekkor a felhasználói (kíméletes) megszakításra nincs lehetőség.

Kristóf János
Budapest, 1988. november 24.