| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061 |
- IMPLEMENTATION MODULE Runtime ;
- (* 8086 runtime library, assembled byte by byte. Each little emitter below is
- one 8086 instruction; the ModR/M byte is spelled out in the comment so the
- encoding can be checked by hand against an 8086 table. This is the same
- approach the compiler itself takes in Compiler.mod (Ebyte/Eword/EmCall),
- so the library needs no external assembler and no binary artifact in the
- tree - RT_Build assembles it every run, which is why the entry offsets are
- known exactly and are not guesses.
- Memory model: a .COM image, so CS = DS = ES = SS = 0 and the whole thing
- lives in one 64K segment. The runtime occupies image offsets
- RT_Build's base .. base + RT_Size - 1, the generated program follows, and
- the program's globals follow at that + 1000H.
- The base is a parameter rather than an assumption, and that is not
- decoration. The image begins with a three-byte JMP - a .COM is entered at
- file offset 0, and without it a .COM built by this compiler starts by
- executing initmem - so the runtime does NOT sit at image offset 0. Every
- address the runtime bakes into its own code (its data block, and the entry
- offsets it hands back) therefore has to be biased by that base. Two places
- do it, and only two: FixUp for the data block, and RT_Entry for the
- entries. The emitted BYTES are identical for any base, because both are
- computed after assembly; tests/check_runtime.py and tests/runtime.golden
- pass base 0 and are unaffected.
- Calling conventions, matching Compiler.IoCall:
- WrInt/WrChar/WrBool/WrReal one 16-bit value on the stack (caller pops)
- RdInt/RdChar/RdBool one address on the stack (caller pops)
- WrLn/RdLn/StackChk nothing
- InitMem AX = offset of the program header word block
- ProgEnd/Halt nothing; exits with code 0
- All entries preserve BP and SP, and every register except the documented
- result, so they can be called from the middle of an expression. *)
- FROM SYSTEM IMPORT BYTE ;
- CONST
- MaxRt = 4096 ;
- (* MaxLbl and MaxFix were 64 and 400, and M/AddFix used to DROP once full.
- That limit does not announce itself: a dropped label leaves its bytes in
- place, so the runtime still has the right LENGTH and the only symptom is
- one branch whose rel8 came out as LblOff's "not found" value. String
- concatenation added the four labels that crossed 64, and strcat's
- `jcxz sc_done' ended up pointing 47 bytes past the code - a wrong target
- that ran the machine into the zero gap. Sizing them with margin fixes
- that case; the fill checks below make any future overflow a HALT instead
- of wrong code. tests/check_runtime.py's branch-boundary check is what
- caught this one. *)
- MaxLbl = 320 ; (* fmtreal/ascreal added ~70 labels to the MBF core's 160 *)
- MaxFix = 768 ;
- MaxNm = 15 ;
- (* Offsets inside the runtime's own data block. These must agree with the
- bytes EmitData emits, and EmitData ASSERTS that they do - because the
- only other way to find out is to run a program, and a D_ constant that
- is one too high does not crash: the $ that terminates the string it
- points at is simply somewhere else, and a $-string read from the wrong
- address runs on until it happens to find a 24h. wrln did exactly that
- and printed a screenful of memory, which is what found this. D_TRUE was
- the only one that was right, and the golden (instructions only) could
- not see any of it. *)
- D_NUM = 0 ; (* 8 bytes, decimal conversion scratch *)
- D_TRUE = 8 ; (* "TRUE$" - 5 bytes, 8..12 *)
- D_FALSE = 13 ; (* "FALSE$" - 6 bytes, 13..18 *)
- D_CRLF = 19 ; (* CR LF '$' - 3 bytes, 19..21 *)
- D_REAL = 22 ; (* "?REAL?" - 6 bytes, 22..27; reals are not formatted yet *)
- D_PUSH = 28 ; (* 2 bytes, 28..29: the one-character pushback. The high
- byte is 1 whenever a character is pending, so the
- word is 0 exactly when the slot is empty - a NUL in
- the input would otherwise be indistinguishable from
- "nothing pushed back". Byte 30 is unused. *)
- D_RSIGN = 30 ; (* 2 bytes: the result sign word; D_RESAVE (31) is its
- high byte, which is the sign the reference keeps *)
- D_RESAVE = 31 ; (* the high byte of D_RSIGN: the saved sign *)
- D_RMANT = 32 ; (* 2 bytes: the second operand's saved MSB *)
- D_RMANT1 = 33 ; (* its high byte, mixed with operand 1's sign *)
- D_CVDEC = 34 ; (* 1 byte: the second exponent during alignment *)
- D_RMUL11 = 35 ; (* a's saved AX; D_RMUL11+1 is its high byte (AH) *)
- D_RMUL12 = 37 ; (* a's saved BX *)
- D_RMUL13 = 39 ; (* a's saved DX *)
- D_RMUL21 = 41 ; (* b's saved CX (low byte is the exponent, skipped) *)
- D_RMUL22 = 43 ; (* b's saved SI *)
- D_RMUL23 = 45 ; (* b's saved DI *)
- D_CVOUT = 47 ; (* 12 bytes: realdec's decimal digits *)
- D_CVDECEXP = 59 ; (* realrang's decimal exponent, sign in bit 7 *)
- D_CVEXPCNT = 60 ; (* ascreal's decimal-point counter *)
- D_FLTDEC = 61 ; (* 10 x 6 bytes: 1e0, 1e4, ... 1e36 (TPSRC3) *)
- D_FMTBUF = 121 ; (* 64 bytes: fmtreal's unpadded output *)
- D_FMTLEN = 185 ; (* 2 bytes: its length *)
- D_FMTW = 187 ; (* 2 bytes: the requested field width *)
- D_FMTPAD = 189 ; (* 2 bytes: left padding *)
- D_FMTTOT = 191 ; (* 2 bytes: pad + length *)
- D_ASCSRC = 193 ; (* 2 bytes: ascreal's source pointer *)
- D_END = 195 ; (* the block is padded to here *)
- (* THE LOAD BIAS. Everything above is an offset into the image as this
- compiler numbers it: the entry JMP is at image 0. But a DOS .COM is
- NOT loaded at that offset - DOS loads it at CS:0100, because 0000..00FF
- is the PSP - so image offset K lives at CS:(K + 0100h), and since a
- .COM has CS = DS, every address baked into the image as a literal has
- to carry that +0100h.
- Getting this wrong is the single most confusing failure this compiler
- can have, because nothing crashes. The entry JMP is *relative*, so it
- still lands on the prologue; every CALL is relative, so every call
- still lands on the right runtime entry; initmem still runs and still
- zeroes the data area. The program therefore starts, runs, and prints
- its first field correctly - and then a $ that is 0100h too low resolves
- to code instead of to data, so the string walk runs on through the
- runtime's own instructions until it happens to hit a 24h. That is
- exactly what writeln('hi') did: `hi' out of wrtin's inline data, then
- 249 bytes of runtime machine code, stopping only at the '$' inside
- "TRUE$". See tests/fixtures/t02_writeln.out.
- The bias is a CONSTANT, not a variable to be relocated at load time,
- because the 8086 has no way to relocode an image in place and no way
- to set a segment register to a sub-paragraph boundary. It is the same
- +0100h the original's own output has: TPSRC7 opendest copies the
- generated image to 100h and runs it there, so the original's addresses
- are 0100h-relative too. The difference is only in how the bytes are
- *numbered* - ours are 0-based in the file, the original's are
- segment-relative - and this constant is where that difference stops.
- The constant itself is declared in Runtime.def, because that is the only
- declaration site a compiler can import - an implementation module does not
- re-declare what its definition module already declared. *)
- TYPE
- LblRec = RECORD
- nm : ARRAY [0..MaxNm] OF CHAR ;
- off : CARDINAL ;
- END ;
- FixRec = RECORD
- kind : CARDINAL ; (* 0 = rel8, 1 = rel16, 2 = data address *)
- place : CARDINAL ; (* offset of the displacement/address field *)
- nm : ARRAY [0..MaxNm] OF CHAR ;
- val : CARDINAL ;
- END ;
- VAR
- rt : ARRAY [0..MaxRt - 1] OF BYTE ;
- rpos : CARDINAL ;
- lbl : ARRAY [0..MaxLbl - 1] OF LblRec ;
- ltop : CARDINAL ;
- fix : ARRAY [0..MaxFix - 1] OF FixRec ;
- nfix : CARDINAL ;
- dataAt : CARDINAL ;
- rtBase : CARDINAL ;
- built : BOOLEAN ;
- entNm : ARRAY [0..30] OF ARRAY [0..MaxNm] OF CHAR ;
- (* ---------------------------------------------------------------- *)
- (* name helpers *)
- (* ---------------------------------------------------------------- *)
- PROCEDURE StrEq (a, b : ARRAY OF CHAR ) : BOOLEAN ;
- VAR i : CARDINAL ;
- BEGIN
- i := 0 ;
- WHILE (i <= HIGH (a)) AND (i <= HIGH (b)) DO
- IF a [i] # b [i] THEN
- RETURN FALSE
- END ;
- INC (i)
- END ;
- RETURN TRUE
- END StrEq ;
- PROCEDURE SetStr (VAR dst : ARRAY OF CHAR ; src : ARRAY OF CHAR ) ;
- VAR i : CARDINAL ;
- BEGIN
- i := 0 ;
- WHILE (i <= HIGH (dst)) AND (i <= HIGH (src)) DO
- dst [i] := src [i] ;
- INC (i)
- END ;
- IF i <= HIGH (dst) THEN
- dst [i] := 0C
- END
- END SetStr ;
- (* ---------------------------------------------------------------- *)
- (* primitive emitters *)
- (* ---------------------------------------------------------------- *)
- PROCEDURE B (b : CARDINAL ) ;
- (* emit exactly ONE byte. A two-byte opcode must be written as two B calls -
- B masks to 100H, so B (8BE4H) would silently emit just E4. *)
- BEGIN
- IF rpos >= MaxRt THEN
- RETURN (* blob is oversized: drop the byte *)
- END ;
- rt [rpos] := VAL (BYTE, b MOD 100H) ;
- INC (rpos)
- END B ;
- PROCEDURE W (w : CARDINAL ) ;
- BEGIN
- B (w MOD 100H) ;
- B ((w DIV 100H) MOD 100H) (* little endian *)
- END W ;
- PROCEDURE M (nm : ARRAY OF CHAR ) ;
- (* mark: nm is the current offset *)
- VAR i : CARDINAL ;
- BEGIN
- (* A dropped label is not a smaller runtime, it is a WRONG one: its bytes
- are still emitted, so only a branch that names it goes off target. That
- shows up in neither the size nor the entry table, so refuse to build
- rather than drop. *)
- IF ltop >= MaxLbl THEN
- HALT
- END ;
- (* A DUPLICATE label is worse than a missing one: LblOff returns the FIRST
- spelling, so the second body (and every branch meant for it) silently
- runs the first. That is exactly how real_div came to call readd. *)
- i := 0 ;
- WHILE i < ltop DO
- IF StrEq (lbl [i].nm, nm) THEN
- HALT
- END ;
- INC (i)
- END ;
- SetStr (lbl [ltop].nm, nm) ;
- lbl [ltop].off := rpos ;
- INC (ltop)
- END M ;
- PROCEDURE AddFix (kind, place : CARDINAL ; nm : ARRAY OF CHAR ; val : CARDINAL ) ;
- (* Record a displacement to be patched once every label is known. A dropped
- fixup is an address that is never patched - the same silent-wrong-code
- failure the check in M exists to prevent. *)
- BEGIN
- IF nfix >= MaxFix THEN
- HALT
- END ;
- fix [nfix].kind := kind ;
- fix [nfix].place := place ;
- fix [nfix].val := val ;
- SetStr (fix [nfix].nm, nm) ;
- INC (nfix)
- END AddFix ;
- PROCEDURE LblOff (nm : ARRAY OF CHAR ) : CARDINAL ;
- VAR i : CARDINAL ;
- BEGIN
- i := 0 ;
- WHILE i < ltop DO
- IF StrEq (lbl [i].nm, nm) THEN
- RETURN lbl [i].off
- END ;
- INC (i)
- END ;
- RETURN 0
- END LblOff ;
- PROCEDURE J8 (nm : ARRAY OF CHAR ) ;
- BEGIN
- B (0EBH) ; (* JMP rel8 *)
- AddFix (0, rpos, nm, 0) ;
- B (0)
- END J8 ;
- PROCEDURE C8 (nm : ARRAY OF CHAR ) ;
- BEGIN
- B (0E8H) ; (* CALL rel16 *)
- AddFix (1, rpos, nm, 0) ;
- W (0)
- END C8 ;
- PROCEDURE Jcc (code : CARDINAL ; nm : ARRAY OF CHAR ) ;
- BEGIN
- B (code) ; (* Jcc rel8 *)
- AddFix (0, rpos, nm, 0) ;
- B (0)
- END Jcc ;
- PROCEDURE Dd (delta : CARDINAL ) ;
- (* emit a 16-bit address into the runtime's data block; the value is only
- known once the data block has been placed, so it is a fixup *)
- BEGIN
- AddFix (2, rpos, "", delta) ;
- W (0)
- END Dd ;
- (* --- 8-bit / 16-bit register and memory forms, one instruction each --- *)
- (* THE 16-BIT ModR/M EFFECTIVE-ADDRESS TABLE, MEASURED NOT REMEMBERED.
- Every address form below was confirmed by EXECUTING it on a real 8086
- (qemu-system-i386) with a probe that stores a marker through the candidate
- encoding and then reports which physical address received it; see
- tests/probe/modrm19.s. The mod=11 column is not an address at all -- it
- names a register -- so it is confirmed instead by asking GNU as to
- ENCODE the eight register moves and checking FCML's decode of the result
- (tests/probe/modrm11.py, which also carries the hand-checking anchors).
- Do not "fix" any of these from memory. The version that comes to mind is
- wrong in exactly the cells called out below, and every one of those
- mistakes shipped as code that decoded cleanly:
- r/m mod=00 mod=01 mod=10 mod=11
- 000 [BX+SI] [BX+SI]+disp [BX+SI]+disp AX
- 001 [BX+DI] [BX+DI]+disp [BX+DI]+disp CX
- 010 [BP+SI] [BP+SI]+disp [BP+SI]+disp DX
- 011 [BP+DI] [BP+DI]+disp [BP+DI]+disp BX
- 100 [SI] [SI]+disp [SI]+disp SP
- 101 [DI] [DI]+disp [DI]+disp BP
- 110 disp16 [BP]+disp [BP]+disp SI
- 111 [BX] [BX]+disp [BX]+disp DI
- "disp" is disp8 for mod=01 and disp16 for mod=10.
- In mod=11 BOTH fields name a register and BOTH use the same list,
- AX CX DX BX SP BP SI DI -- the reg field and the r/m field do not
- differ, and there is no second BX at code 7. The memorable version
- drops AX off the front and invents a duplicate BX at the end, which
- shifts every code down by one; that is precisely how MovSiBx and
- CmpSiBx came to be written "89 DC" and "39 DC", which are MOV SP,BX
- and CMP SP,BX. Note what the shifted table gets right by luck: SP is
- at 100 either way, so the mistake is invisible until you check a
- register below it. rm=100 is SP, and SI is rm=110, not rm=100.
- And reg/rm swap direction with the opcode, which is the other trap:
- 88 /r MOV r/m8,r8 89 /r MOV r/m16,r16 reg is the SOURCE
- 8A /r MOV r8,r/m8 8B /r MOV r16,r/m16 reg is the TARGET
- So ModRM C6 names "DH and AL" either way, but 88 C6 is DH:=AL while
- 8A C6 is AL:=DH. The 8-bit register list is also its own, and it is not
- the word list:
- 000 001 010 011 100 101 110 111 = AL CL DL BL AH CH DH BH
- The two lists agree at every code except 100, where the byte form is AH
- and the word form is SP. Reading the same ModRM byte as the other size
- silently swaps AH for SP, and 88 C2 is DL:=AL while 88 17 is [BX]<-DL.
- The word-form list, and the four anchors nobody writes by hand:
- 83 C4 08 ADD SP, 8 rm=100 -> SP
- 83 C6 02 ADD SI, 2 rm=110 -> SI
- 8B EC MOV BP, SP reg=101 rm=100
- 8B E5 MOV SP, BP reg=100 rm=101
- Asking "which register is rm=100?" and answering SI is the single most
- common error in this file's history.
- Concrete recipes for opcode 8r / 9r (r/m = rm, 16-bit form):
- mod=00 rm=110 -> 06 <disp16> the only direct form
- mod=00 rm=111 -> 07 = [BX] 2 bytes
- mod=00 rm=101 -> 05 = [DI] 2 bytes
- mod=01 rm=110 -> 46 disp8 = [BP]+disp8
- mod=01 rm=111 -> 47 disp8 = [BX]+disp8
- mod=01 rm=101 -> 45 disp8 = [DI]+disp8
- There is no [SP] form in 16-bit mode: SIB bytes are 386-only. Anything
- wanting the top of the stack has to go through BP. *)
- PROCEDURE PushBp ; BEGIN B (55H) END PushBp ;
- PROCEDURE PopBp ; BEGIN B (5DH) END PopBp ;
- PROCEDURE MovBpSp ; BEGIN B (8BH) ; B (0ECH) END MovBpSp ; (* 8B EC: MOV BP,SP *)
- PROCEDURE MovSpBp ; BEGIN B (89H) ; B (0ECH) END MovSpBp ; (* 89 EC: MOV SP,BP *)
- PROCEDURE LeaveR ; BEGIN B (0C9H) END LeaveR ;
- PROCEDURE RetR ; BEGIN B (0C3H) END RetR ;
- PROCEDURE Int21 ; BEGIN B (0CDH) ; B (21H) END Int21 ;
- PROCEDURE PushDs ; BEGIN B (1EH) END PushDs ;
- PROCEDURE PopEs ; BEGIN B (7H) END PopEs ;
- PROCEDURE PushAx ; BEGIN B (50H) END PushAx ;
- PROCEDURE PopAx ; BEGIN B (58H) END PopAx ;
- PROCEDURE PushBx ; BEGIN B (53H) END PushBx ;
- PROCEDURE PopBx ; BEGIN B (5BH) END PopBx ;
- PROCEDURE PushCx ; BEGIN B (51H) END PushCx ;
- PROCEDURE PopCx ; BEGIN B (59H) END PopCx ;
- PROCEDURE PushDx ; BEGIN B (52H) END PushDx ;
- PROCEDURE PopDx ; BEGIN B (5AH) END PopDx ;
- PROCEDURE PushDi ; BEGIN B (57H) END PushDi ;
- PROCEDURE PopDi ; BEGIN B (5FH) END PopDi ;
- PROCEDURE PushSi ; BEGIN B (56H) END PushSi ;
- PROCEDURE PopSi ; BEGIN B (5EH) END PopSi ;
- PROCEDURE XorAxAx ; BEGIN B (31H) ; B (0C0H) END XorAxAx ; (* 11 000 000 *)
- PROCEDURE XorCxCx ; BEGIN B (31H) ; B (0C9H) END XorCxCx ; (* 11 001 001 *)
- PROCEDURE XorDxDx ; BEGIN B (31H) ; B (0D2H) END XorDxDx ; (* 11 010 010 *)
- PROCEDURE XorDiDi ; BEGIN B (31H) ; B (0FFH) END XorDiDi ; (* 11 111 111 *)
- PROCEDURE IncCx ; BEGIN B (41H) END IncCx ;
- PROCEDURE IncSi ; BEGIN B (46H) END IncSi ;
- PROCEDURE IncDi ; BEGIN B (47H) END IncDi ;
- PROCEDURE DecSi ; BEGIN B (4EH) END DecSi ;
- PROCEDURE AddDi2 ; BEGIN B (83H) ; B (0C7H) ; B (2) END AddDi2 ; (* 11 000 111 *)
- PROCEDURE CmpAl (v : CARDINAL ) ; BEGIN B (3CH) ; B (v) END CmpAl ;
- PROCEDURE CmpAx0 ; BEGIN B (83H) ; B (0F8H) ; B (0) END CmpAx0 ;
- PROCEDURE CmpCx0 ; BEGIN B (83H) ; B (0F9H) ; B (0) END CmpCx0 ;
- PROCEDURE CmpArg2W0 ;
- BEGIN
- B (8BH) ; B (0ECH) ; (* MOV BP,SP *)
- B (83H) ; B (7EH) ; B (2) ; B (0) ; (* CMP WORD [BP+2],0 *)
- B (89H) ; B (0ECH) (* MOV SP,BP *)
- END CmpArg2W0 ;
- PROCEDURE CmpArg4W0 ;
- (* CMP WORD PTR [BP+4],0 - the same comparison, in the frame where the entry
- pushed BP first.
- The 2 and the 4 are the whole point of having two names. [SP] is not
- encodable in 16-bit mode, so an entry that takes one 16-bit argument has to
- borrow BP to reach it, and how far up the argument then sits depends on
- whether BP was saved first: 2 bytes without the PUSH, 4 with it. A single
- emitter taking the displacement as a parameter is the obvious way to write
- this and the wrong one -- a parameter is invisible to tests/audit_helpers.py,
- which reads the NAME and compares it against the decode, so `CmpArgW0 (2)'
- and `CmpArgW0 (4)' would differ by one byte with nothing in the codebase
- able to tell which was meant. In the name, the audit pins it: a helper
- called CmpArg4W0 that emitted 83 7E 02 00 would fail the run.
- Note the sandwich: MOV BP,SP before and MOV SP,BP after, and no POP BP, so
- the two forms are the ONLY difference. (Was 83 7C 24 00 00, which decodes
- as CMP WORD [SI+24h],0.) The imm8 of the 83 form is sign-extended to 16
- bits, so 0 really is a 16-bit zero. *)
- BEGIN
- B (8BH) ; B (0ECH) ; (* MOV BP,SP *)
- B (83H) ; B (7EH) ; B (4) ; B (0) ; (* CMP WORD [BP+4],0 *)
- B (89H) ; B (0ECH) (* MOV SP,BP *)
- END CmpArg4W0 ;
- PROCEDURE CmpSiBx ; BEGIN B (39H) ; B (0DEH) END CmpSiBx ; (* 39 DE: CMP SI,BX *)
- PROCEDURE CmpCxDx ; BEGIN B (39H) ; B (0D1H) END CmpCxDx ; (* 11 010 001 *)
- PROCEDURE CmpAlDl ; BEGIN B (3AH) ; B (0C2H) END CmpAlDl ; (* 3A C2: CMP AL,DL.
- The byte-sized comparator cmpstr's loop needs, and the two opcodes are NOT
- interchangeable: 3A /r is `CMP r8,r/m8' (reg is the TARGET) and 38 /r is
- `CMP r/m8,r8' (reg is the SOURCE). AL is reg=000 and DL is r/m=010, so
- 3A C2 is CMP AL,DL and 38 C2 would be CMP DL,AL - the same flags negated,
- which turns every `<` into a `>'. And there is no `CMP AL,[SI]' name
- pattern in tests/audit_helpers.py (only the Ld/St memory names are), so the
- right-hand character is loaded into DL first rather than compared in
- memory. *)
- PROCEDURE CmpDiCx ; BEGIN B (39H) ; B (0CFH) END CmpDiCx ; (* 11 001 111 *)
- PROCEDURE CmpBx0 ; BEGIN B (83H) ; B (0FBH) ; B (0) END CmpBx0 ;
- PROCEDURE CmpCxV (v : CARDINAL ) ; BEGIN B (83H) ; B (0F9H) ; B (v) END CmpCxV ;
- PROCEDURE AddDl (v : CARDINAL ) ; BEGIN B (80H) ; B (0C2H) ; B (v) END AddDl ;
- PROCEDURE SubAl (v : CARDINAL ) ; BEGIN B (2CH) ; B (v) END SubAl ;
- PROCEDURE SubAxBx ; BEGIN B (2BH) ; B (0C3H) END SubAxBx ; (* 2B C3: AX:=AX-BX *)
- PROCEDURE NegAx ; BEGIN B (0F7H) ; B (0D8H) END NegAx ;
- PROCEDURE NegDi ; BEGIN B (0F7H) ; B (0DFH) END NegDi ;
- PROCEDURE DivCx ; BEGIN B (0F7H) ; B (0F1H) END DivCx ; (* 11 110 001 *)
- PROCEDURE MulBx ; BEGIN B (0F7H) ; B (0E3H) END MulBx ; (* 11 100 011 *)
- PROCEDURE MovAh (v : CARDINAL ) ; BEGIN B (0B4H) ; B (v) END MovAh ;
- PROCEDURE MovDl (v : CARDINAL ) ; BEGIN B (0B2H) ; B (v) END MovDl ;
- (* The runtime's own data block, addressed by absolute image address. Four
- emitters cover it, and the difference between them is the whole subject of
- the mistakes recorded below, so they are named systematically:
- Mov<reg>Vx MOV reg, Vx -- reg := the ADDRESS (BB / BA + Dd)
- Ld<reg>Vx MOV reg, [Vx] -- reg := the CONTENTS (8B 1E + Dd)
- StVx<reg> MOV [Vx], reg -- [Vx] := reg (89 1E + Dd)
- "Vx" is the operand word for "a 16-bit absolute address into the data
- block", spelled with a V precisely so it cannot be confused with a base
- register: on the 8086 there is no [BX] memory form, so the address has to
- go through mod=00 / rm=110, and rm=111 is [BX+SI] - a different, perfectly
- decodable instruction. tests/audit_helpers.py checks all four.
- The two traps here, both of which happened:
- - Dd versus W. Dd turns a D_ offset into a FIXUP, so the address can be
- placed only once the data block has been given its final position in the
- image. W emits the number as it stands. The two produce the SAME
- instruction, and getch once used MovBxImm where it wanted the address, so
- `MOV BX,D_PUSH' came out as `MOV BX,001Ch' - the raw offset 1Ch, inside
- the entry JMP. Non-zero, so the pushback slot was never examined, getch
- served a byte of the runtime's own code forever, and readln hung. Hence
- the Imm suffix: the raw-immediate emitters are named for what they do.
- - address versus contents. LdBxVx and MovBxVx are both `8B`/`BB` + Dd to
- the eye and completely different instructions. getch's first cut used
- MovBxVx where it wanted the contents, so it tested the ADDRESS for zero,
- found 02AFh every time, and returned the low byte of memory 02AFh - 0,
- the value it had just stored there - instead of asking DOS. *)
- PROCEDURE MovBxImm (v : CARDINAL ) ; BEGIN B (0BBH) ; W (v) END MovBxImm ;
- PROCEDURE MovCxImm (v : CARDINAL ) ; BEGIN B (0B9H) ; W (v) END MovCxImm ;
- PROCEDURE MovDxImm (v : CARDINAL ) ; BEGIN B (0BAH) ; W (v) END MovDxImm ;
- PROCEDURE MovBxVx (delta : CARDINAL ) ; BEGIN B (0BBH) ; Dd (delta) END MovBxVx ;
- PROCEDURE MovDxVx (delta : CARDINAL ) ; BEGIN B (0BAH) ; Dd (delta) END MovDxVx ;
- PROCEDURE LdBxVx (delta : CARDINAL ) ;
- (* 8B 1E lo hi: BX := WORD PTR [Vx] - mod=00 / rm=110, the only 16-bit form
- that can carry a bare absolute address. *)
- BEGIN
- B (8BH) ; B (01EH) ; Dd (delta)
- END LdBxVx ;
- PROCEDURE MovSiAx ; BEGIN B (8BH) ; B (0F0H) END MovSiAx ; (* 8B F0: MOV SI,AX *)
- PROCEDURE MovAxDi ; BEGIN B (8BH) ; B (0C7H) END MovAxDi ; (* 11 000 111 *)
- PROCEDURE MovCxSi6 ; BEGIN B (8BH) ; B (4CH) ; B (6) END MovCxSi6 ;
- PROCEDURE MovCxDx ; BEGIN B (8BH) ; B (0CAH) END MovCxDx ; (* 8B CA: MOV CX,DX *)
- PROCEDURE MovDxSi2 ; BEGIN B (8BH) ; B (54H) ; B (2) END MovDxSi2 ;
- (* The program header is a block of words laid out by the compiler:
- +0 hdrFlag 1 = image is valid
- +2 hdrCS end of the generated code, in bytes
- +4 hdrDS base of the data area <- initmem wants these two
- +6 hdrHeap end of the data area <-
- +8 hdrMax max open files
- initmem must read the words the COMPILER WRITES, and those are +4 (hdrDS,
- the data base) and +6 (hdrHeap, the data end). It used to read +8, which
- is hdrMax -- and the compiler patches that to 0 -- so CX came out as 0,
- "cmp cx,dx / jbe im_done" fired immediately, and initmem silently zeroed
- nothing at all. A 3-byte instruction, so no entry offset moved either
- way; only the header offset in the byte changed. Nothing caught it
- because the loop was well formed - it just did nothing. The cross-check
- that would have: tests/run_com_tests.sh now asserts the runtime's SI-relative
- reads against the same header offsets it verifies the words at. *)
- PROCEDURE MovDxSi4 ; BEGIN B (8BH) ; B (54H) ; B (4) END MovDxSi4 ;
- PROCEDURE MovAxBp4 ; BEGIN B (8BH) ; B (46H) ; B (4) END MovAxBp4 ; (* AX:=[BP+4] *)
- PROCEDURE MovAxBp6 ; BEGIN B (8BH) ; B (46H) ; B (6) END MovAxBp6 ; (* AX:=[BP+6] *)
- PROCEDURE MovBxBp4 ; BEGIN B (8BH) ; B (5EH) ; B (4) END MovBxBp4 ; (* BX:=[BP+4] *)
- PROCEDURE MovBxBp6 ; BEGIN B (8BH) ; B (5EH) ; B (6) END MovBxBp6 ; (* BX:=[BP+6] *)
- PROCEDURE MovAlDh ; BEGIN B (8AH) ; B (0C6H) END MovAlDh ; (* 8A C6: AL:=DH *)
- PROCEDURE MovDlSi ; BEGIN B (8AH) ; B (14H) END MovDlSi ;
- PROCEDURE MovDlArg2 ; BEGIN B (8AH) ; B (56H) ; B (2) END MovDlArg2 ; (* DL:=[BP+2] *)
- (* The AL twins of the three above. INT 21h AH=02h displays the character in
- AL, not DL, so every character this runtime writes has to arrive in AL.
- DL is the natural register for the digit scratch in wrint and for a stack
- argument in wrchar, and both of them were loading DL and then calling
- AH=02h - which printed whatever happened to be left in AL. For wrint that
- was the QUOTIENT's low byte from the preceding DIV, so writeln(1) printed a
- NUL and writeln(2) printed a NUL, and a two-digit number printed its
- quotient instead of itself. A wrong register is not a wrong encoding: the
- golden bytes were right, `mov dl,[si]' is a perfectly good instruction, and
- every byte-level check stayed green. Only running it found this. *)
- PROCEDURE MovAlD (v : CARDINAL ) ; BEGIN B (0B0H) ; B (v) END MovAlD ; (* MOV AL,imm8 *)
- PROCEDURE MovAlSi ; BEGIN B (8AH) ; B (04H) END MovAlSi ; (* 8A 04: AL:=[SI] *)
- PROCEDURE MovAlArg2 ; BEGIN B (8AH) ; B (46H) ; B (2) END MovAlArg2 ; (* AL:=[BP+2] *)
- PROCEDURE MovAlArg4 ; BEGIN B (8AH) ; B (46H) ; B (4) END MovAlArg4 ; (* AL:=[BP+4] *)
- PROCEDURE MovAlDl ; BEGIN B (8AH) ; B (0C2H) END MovAlDl ; (* 8A C2: AL:=DL *)
- PROCEDURE StDiAx ; BEGIN B (89H) ; B (5H) END StDiAx ; (* 89 05: [DI]:=AX *)
- PROCEDURE StDiAl ; BEGIN B (88H) ; B (5H) END StDiAl ; (* 88 05: [DI]:=AL *)
- PROCEDURE StSiDl ; BEGIN B (88H) ; B (14H) END StSiDl ; (* 88 14: [SI]:=DL *)
- PROCEDURE StDiDl ; BEGIN B (88H) ; B (15H) END StDiDl ; (* 88 15: [DI]:=DL *)
- PROCEDURE StDiCx ; BEGIN B (89H) ; B (0DH) END StDiCx ; (* 89 0D: [DI]:=CX *)
- PROCEDURE MovDiBp4 ; BEGIN B (8BH) ; B (7EH) ; B (4) END MovDiBp4 ; (* DI:=[BP+4] *)
- (* There is NO "store through BX" instruction on the 8086, and writing one
- anyway is the single most expensive mistake this file has produced, so the
- reasoning is recorded rather than left in the probe history.
- In 16-bit addressing the r/m column is a LOCATION, not a register list: for
- mod=00, rm=000..101 are [BX+SI] [BX+DI] [BP+SI] [BP+DI] [SI] [DI], and
- rm=110 is the only one that means "a direct displacement". rm=111 is
- [BX+SI], NOT [BX]. So `89 1D' - the ModR/M the first version of this used
- for "MOV [BX],CX" - is mod=00 reg=BX r/m=DI, i.e. MOV [DI],BX: the two
- operands swapped AND [BX] inexpressible. GNU as and objdump in -m i8086
- agree, and the bytes are perfectly well formed, which is why nothing
- objected. What the program saw was its own address being written over the
- interrupt vector table, and the variable it was asked to fill left holding
- whatever the loader put there: readln(n) then printed 0, and readln(c)
- printed a NUL. Correct by comparison: the caller puts the address in BX,
- and the store has to go through DI or SI, so the sequence is
- MOV DI,[BP+4] / MOV [DI],<value> - see StoreThrough below. *)
- PROCEDURE MovDlAl ; BEGIN B (88H) ; B (0C2H) END MovDlAl ;
- PROCEDURE MovDlCl ; BEGIN B (8AH) ; B (0D1H) END MovDlCl ; (* 8A D1: MOV DL,CL *)
- PROCEDURE XchgAxDi ; BEGIN B (87H) ; B (0C7H) END XchgAxDi ; (* 87 C7: AX<->DI *)
- PROCEDURE AddAxDi ; BEGIN B (03H) ; B (0C7H) END AddAxDi ; (* 03 C7: AX:=AX+DI *)
- PROCEDURE MovSiBx ; BEGIN B (89H) ; B (0DEH) END MovSiBx ; (* 89 DE: MOV SI,BX *)
- PROCEDURE MovDiDx ; BEGIN B (89H) ; B (0D7H) END MovDiDx ;
- PROCEDURE MovDiAx ; BEGIN B (89H) ; B (0C7H) END MovDiAx ;
- PROCEDURE AddDiAx ; BEGIN B (1H) ; B (0C7H) END AddDiAx ;
- (* The register-pair emitters strcat (EmitStrCat) needs. Two registers, so
- the audit's two-register pattern reads them positionally: `AddDxAx' is
- 01 C2 = ADD DX,AX (01 /r is `op r/m16,r16'', DX is r/m and AX is reg), and
- the mirror `SubDxAx' is 29 C2. MovCxAx is 89 C1 (89 /r: reg is the
- source, so CX is r/m and AX is reg). MovDiSi is 8B FE (8B /r: reg is the
- destination). *)
- PROCEDURE MovCxAx ; BEGIN B (89H) ; B (0C1H) END MovCxAx ; (* 89 C1: MOV CX,AX *)
- PROCEDURE MovDiSi ; BEGIN B (8BH) ; B (0FEH) END MovDiSi ; (* 8B FE: MOV DI,SI *)
- PROCEDURE AddDxAx ; BEGIN B (1H) ; B (0C2H) END AddDxAx ; (* 01 C2: DX:=DX+AX *)
- PROCEDURE SubDxAx ; BEGIN B (29H) ; B (0C2H) END SubDxAx ; (* 29 C2: DX:=DX-AX *)
- PROCEDURE AddDiDx ; BEGIN B (1H) ; B (0D7H) END AddDiDx ; (* 01 D7: DI:=DI+DX *)
- PROCEDURE StSiCl ; BEGIN B (88H) ; B (0CH) END StSiCl ; (* 88 0C: [SI]:=CL *)
- PROCEDURE IncBx ; BEGIN B (43H) END IncBx ;
- PROCEDURE LdAlBx ; BEGIN B (8AH) ; B (07H) END LdAlBx ; (* 8A 07: AL:=[BX] *)
- PROCEDURE MovAlBl ; BEGIN B (8AH) ; B (0C3H) END MovAlBl ; (* 8A C3: AL:=BL *)
- (* 8A 07 and 8A C3 are one letter apart and do opposite things: the
- first reads the byte AT the pointer in BX, the second takes the low
- byte OF BX. getch's pushback path wanted the second and used the
- first, so it read memory 010Ah - the address of the character - and
- returned whatever was there. Hence the Ld/Mov split. *)
- PROCEDURE MovClBx ; BEGIN B (8AH) ; B (0FH) END MovClBx ; (* 8A 0F: CL:=[BX] *)
- PROCEDURE XorBxBx ; BEGIN B (31H) ; B (0DBH) END XorBxBx ; (* 31 DB: BX:=0 *)
- PROCEDURE MovBlDl ; BEGIN B (8AH) ; B (0DAH) END MovBlDl ; (* 8A DA: BL:=DL *)
- PROCEDURE MovBhAh ; BEGIN B (88H) ; B (0E7H) END MovBhAh ; (* 88 E7: BH:=AH *)
- PROCEDURE MovDxAx ; BEGIN B (89H) ; B (0C2H) END MovDxAx ; (* 89 C2: DX:=AX *)
- PROCEDURE MovSiBp4 ; BEGIN B (8BH) ; B (76H) ; B (4) END MovSiBp4 ; (* SI:=[BP+4] *)
- PROCEDURE MovSiBp6 ; BEGIN B (8BH) ; B (76H) ; B (6) END MovSiBp6 ; (* SI:=[BP+6] *)
- PROCEDURE LdAxSi ; BEGIN B (8BH) ; B (04H) END LdAxSi ; (* 8B 04: AX:=[SI] *)
- PROCEDURE StDiOffBx (off : CARDINAL) ;
- BEGIN B (89H) ; B (5DH) ; B (off MOD 256) END StDiOffBx ; (* MOV [DI+o],BX *)
- PROCEDURE StDiOffDx (off : CARDINAL) ;
- BEGIN B (89H) ; B (55H) ; B (off MOD 256) END StDiOffDx ; (* MOV [DI+o],DX *)
- PROCEDURE Js8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (78H, nm) END Js8 ;
- PROCEDURE Jns8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (79H, nm) END Jns8 ;
- PROCEDURE Jmp8 (nm : ARRAY OF CHAR ) ;
- BEGIN B (0EBH) ; AddFix (0, rpos, nm, 0) ; B (0) END Jmp8 ;
- PROCEDURE MovAxImm (v : CARDINAL ) ; BEGIN B (0B8H) ; W (v) END MovAxImm ;
- PROCEDURE XchgDlDh ; BEGIN B (86H) ; B (0D6H) END XchgDlDh ; (* 86 D6: DL<->DH *)
- PROCEDURE DecAl ; BEGIN B (0FEH) ; B (0C8H) END DecAl ; (* FE C8 *)
- PROCEDURE ShlDx1 ; BEGIN B (0D1H) ; B (0E2H) END ShlDx1 ; (* D1 E2: SHL DX,1 *)
- PROCEDURE NegDx ; BEGIN B (0F7H) ; B (0DAH) END NegDx ; (* F7 DA: NEG DX *)
- PROCEDURE CmpDh0 ; BEGIN B (80H) ; B (0FEH) ; B (0) END CmpDh0 ; (* 80 FE 00 *)
- PROCEDURE CmpBh0 ; BEGIN B (80H) ; B (0FFH) ; B (0) END CmpBh0 ; (* 80 FF 00 *)
- PROCEDURE CmpDx0 ; BEGIN B (83H) ; B (0FAH) ; B (0) END CmpDx0 ; (* 83 FA 00 *)
- PROCEDURE CmpAl0 ; BEGIN B (3CH) ; B (0) END CmpAl0 ; (* 3C 00 *)
- PROCEDURE AndDhImm (v : CARDINAL) ;
- BEGIN B (80H) ; B (0E6H) ; B (v MOD 256) END AndDhImm ; (* 80 E6 imm *)
- (* --- the MBF core's register moves and flag helpers --------------------- *)
- PROCEDURE MovAxCx ; BEGIN B (89H) ; B (0C8H) END MovAxCx ; (* AX:=CX *)
- PROCEDURE MovBxSi ; BEGIN B (89H) ; B (0F3H) END MovBxSi ; (* BX:=SI *)
- PROCEDURE MovDxDi ; BEGIN B (89H) ; B (0FAH) END MovDxDi ; (* DX:=DI *)
- PROCEDURE MovBxCx ; BEGIN B (89H) ; B (0CBH) END MovBxCx ; (* BX:=CX *)
- PROCEDURE MovBxDx ; BEGIN B (89H) ; B (0D3H) END MovBxDx ; (* BX:=DX *)
- PROCEDURE MovAhBh ; BEGIN B (88H) ; B (0FCH) END MovAhBh ; (* AH:=BH *)
- PROCEDURE MovAhBl ; BEGIN B (88H) ; B (0DCH) END MovAhBl ; (* AH:=BL *)
- PROCEDURE MovBlBh ; BEGIN B (88H) ; B (0FBH) END MovBlBh ; (* BL:=BH *)
- PROCEDURE MovBhDl ; BEGIN B (88H) ; B (0D7H) END MovBhDl ; (* BH:=DL *)
- PROCEDURE MovDlDh ; BEGIN B (88H) ; B (0F2H) END MovDlDh ; (* DL:=DH *)
- PROCEDURE MovDhDl ; BEGIN B (88H) ; B (0D6H) END MovDhDl ; (* DH:=DL *)
- PROCEDURE MovDlBh ; BEGIN B (88H) ; B (0FAH) END MovDlBh ; (* DL:=BH *)
- PROCEDURE MovBlAh ; BEGIN B (88H) ; B (0E3H) END MovBlAh ; (* BL:=AH *)
- PROCEDURE XchgAxCx ; BEGIN B (91H) END XchgAxCx ; (* XCHG AX,CX *)
- PROCEDURE XchgBxSi ; BEGIN B (87H) ; B (0F3H) END XchgBxSi ;
- PROCEDURE XchgDxDi ; BEGIN B (87H) ; B (0FAH) END XchgDxDi ;
- PROCEDURE XchgAhCh ; BEGIN B (86H) ; B (0ECH) END XchgAhCh ;
- PROCEDURE XorDhDh ; BEGIN B (30H) ; B (0F6H) END XorDhDh ; (* XOR DH,DH *)
- PROCEDURE XorAhAh ; BEGIN B (30H) ; B (0E4H) END XorAhAh ; (* XOR AH,AH *)
- PROCEDURE NotAh ; BEGIN B (0F6H) ; B (0D4H) END NotAh ; (* NOT AH *)
- PROCEDURE NotBx ; BEGIN B (0F7H) ; B (0D3H) END NotBx ; (* NOT BX *)
- PROCEDURE NotDx ; BEGIN B (0F7H) ; B (0D2H) END NotDx ; (* NOT DX *)
- PROCEDURE ShrDx1 ; BEGIN B (0D1H) ; B (0EAH) END ShrDx1 ; (* SHR DX,1 *)
- PROCEDURE RcrBx1 ; BEGIN B (0D1H) ; B (0DBH) END RcrBx1 ; (* RCR BX,1 *)
- PROCEDURE RcrDx1 ; BEGIN B (0D1H) ; B (0DAH) END RcrDx1 ; (* RCR DX,1 *)
- PROCEDURE RcrAh1 ; BEGIN B (0D0H) ; B (0DCH) END RcrAh1 ; (* RCR AH,1 *)
- PROCEDURE RclBx1 ; BEGIN B (0D1H) ; B (0D3H) END RclBx1 ; (* RCL BX,1 *)
- PROCEDURE RclDx1 ; BEGIN B (0D1H) ; B (0D2H) END RclDx1 ; (* RCL DX,1 *)
- PROCEDURE ShlAh1 ; BEGIN B (0D0H) ; B (0E4H) END ShlAh1 ; (* SHL AH,1 *)
- PROCEDURE IncAl ; BEGIN B (0FEH) ; B (0C0H) END IncAl ; (* INC AL *)
- PROCEDURE DecCl ; BEGIN B (0FEH) ; B (0C9H) END DecCl ; (* DEC CL *)
- PROCEDURE Stc ; BEGIN B (0F9H) END Stc ; (* STC *)
- PROCEDURE CmpAlCl ; BEGIN B (38H) ; B (0C8H) END CmpAlCl ; (* CMP AL,CL *)
- PROCEDURE CmpCl0 ; BEGIN B (80H) ; B (0F9H) ; B (0) END CmpCl0 ; (* 80 F9 00 *)
- PROCEDURE MovBhBl ; BEGIN B (88H) ; B (0DFH) END MovBhBl ; (* BH:=BL *)
- PROCEDURE CmpCh0 ; BEGIN B (80H) ; B (0FDH) ; B (0) END CmpCh0 ;
- PROCEDURE AddAlCl ; BEGIN B (00H) ; B (0C8H) END AddAlCl ; (* AL:=AL+CL *)
- PROCEDURE AddAlImm (v : CARDINAL) ;
- BEGIN B (04H) ; B (v MOD 256) END AddAlImm ;
- PROCEDURE MovSiImm (v : CARDINAL) ;
- BEGIN B (0BEH) ; W (v) END MovSiImm ;
- PROCEDURE RcrCh1 ; BEGIN B (0D0H) ; B (0DDH) END RcrCh1 ; (* RCR CH,1 *)
- PROCEDURE RclCh1 ; BEGIN B (0D0H) ; B (0D5H) END RclCh1 ; (* RCL CH,1 *)
- PROCEDURE Lahf ; BEGIN B (9FH) END Lahf ;
- PROCEDURE Sahf ; BEGIN B (9EH) END Sahf ;
- PROCEDURE XorCxDi ; BEGIN B (31H) ; B (0F9H) END XorCxDi ; (* CX:=CX XOR DI *)
- PROCEDURE XorDxDi ; BEGIN B (31H) ; B (0FAH) END XorDxDi ; (* DX:=DX XOR DI *)
- PROCEDURE NotCh ; BEGIN B (0F6H) ; B (0D5H) END NotCh ; (* NOT CH *)
- PROCEDURE AndDxDi ; BEGIN B (21H) ; B (0FAH) END AndDxDi ; (* AND DX,DI *)
- PROCEDURE AndBxSi ; BEGIN B (21H) ; B (0F3H) END AndBxSi ; (* AND BX,SI *)
- PROCEDURE AndAhCh ; BEGIN B (20H) ; B (0ECH) END AndAhCh ; (* AND AH,CH *)
- PROCEDURE CmpDxDi ; BEGIN B (39H) ; B (0FAH) END CmpDxDi ; (* CMP DX,DI *)
- PROCEDURE CmpBxSi ; BEGIN B (39H) ; B (0F3H) END CmpBxSi ; (* CMP BX,SI *)
- PROCEDURE CmpAhCh ; BEGIN B (38H) ; B (0ECH) END CmpAhCh ; (* CMP AH,CH *)
- PROCEDURE MovDhImm (v : CARDINAL) ;
- BEGIN B (0B6H) ; B (v MOD 256) END MovDhImm ; (* MOV DH,imm8 *)
- PROCEDURE AndChImm (v : CARDINAL) ;
- BEGIN B (80H) ; B (0E5H) ; B (v MOD 256) END AndChImm ;
- PROCEDURE MovDiData (delta : CARDINAL) ;
- BEGIN B (0BFH) ; Dd (delta) END MovDiData ;
- PROCEDURE LdChDiOff ; BEGIN B (8AH) ; B (2DH) END LdChDiOff ; (* CH:=[DI] *)
- PROCEDURE AddAhData (delta : CARDINAL) ;
- BEGIN B (02H) ; B (26H) ; Dd (delta) END AddAhData ; (* ADD AH,[Vx] *)
- PROCEDURE AdcBxData (delta : CARDINAL) ;
- BEGIN B (13H) ; B (1EH) ; Dd (delta) END AdcBxData ; (* ADC BX,[Vx] *)
- PROCEDURE AdcDxData (delta : CARDINAL) ;
- BEGIN B (13H) ; B (16H) ; Dd (delta) END AdcDxData ; (* ADC DX,[Vx] *)
- PROCEDURE StDataCh (delta : CARDINAL) ;
- BEGIN B (88H) ; B (2EH) ; Dd (delta) END StDataCh ;
- PROCEDURE StDataCx (delta : CARDINAL) ;
- BEGIN B (89H) ; B (0EH) ; Dd (delta) END StDataCx ;
- PROCEDURE StDataSi (delta : CARDINAL) ;
- BEGIN B (89H) ; B (36H) ; Dd (delta) END StDataSi ;
- PROCEDURE StDataAx (delta : CARDINAL) ;
- BEGIN B (0A3H) ; Dd (delta) END StDataAx ;
- PROCEDURE StDataBx (delta : CARDINAL) ;
- BEGIN B (89H) ; B (1EH) ; Dd (delta) END StDataBx ;
- PROCEDURE StDataDx (delta : CARDINAL) ;
- BEGIN B (89H) ; B (16H) ; Dd (delta) END StDataDx ;
- PROCEDURE StDataAl (delta : CARDINAL) ;
- BEGIN B (0A2H) ; Dd (delta) END StDataAl ;
- PROCEDURE MovCxData (delta : CARDINAL) ;
- BEGIN B (8BH) ; B (0EH) ; Dd (delta) END MovCxData ;
- PROCEDURE MovBxData (delta : CARDINAL) ;
- BEGIN B (8BH) ; B (1EH) ; Dd (delta) END MovBxData ;
- PROCEDURE MovDxData (delta : CARDINAL) ;
- BEGIN B (8BH) ; B (16H) ; Dd (delta) END MovDxData ;
- PROCEDURE CmpDxData (delta : CARDINAL) ;
- BEGIN B (3BH) ; B (16H) ; Dd (delta) END CmpDxData ;
- PROCEDURE CmpBxData (delta : CARDINAL) ;
- BEGIN B (3BH) ; B (1EH) ; Dd (delta) END CmpBxData ;
- PROCEDURE CmpAhData (delta : CARDINAL) ;
- BEGIN B (3AH) ; B (26H) ; Dd (delta) END CmpAhData ;
- PROCEDURE SubAhData (delta : CARDINAL) ;
- BEGIN B (2AH) ; B (26H) ; Dd (delta) END SubAhData ; (* SUB.B AH,remul21a: ignores the carry *)
- (* --- the real formatter/parser's instruction set (fmtreal/ascreal) ------ *)
- PROCEDURE Jl8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (7CH, nm) END Jl8 ;
- PROCEDURE Jg8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (7FH, nm) END Jg8 ;
- PROCEDURE MovAxBx ; BEGIN B (89H) ; B (0D8H) END MovAxBx ; (* AX:=BX *)
- PROCEDURE MovAxSi ; BEGIN B (89H) ; B (0F0H) END MovAxSi ; (* AX:=SI *)
- PROCEDURE MovSiDi ; BEGIN B (89H) ; B (0FEH) END MovSiDi ; (* SI:=DI *)
- PROCEDURE MovBlCl ; BEGIN B (88H) ; B (0CBH) END MovBlCl ; (* BL:=CL *)
- PROCEDURE MovChDh ; BEGIN B (88H) ; B (0F5H) END MovChDh ; (* CH:=DH *)
- PROCEDURE MovChCl ; BEGIN B (88H) ; B (0CDH) END MovChCl ; (* CH:=CL *)
- PROCEDURE MovDlCh ; BEGIN B (88H) ; B (0EAH) END MovDlCh ; (* DL:=CH *)
- PROCEDURE MovDhAl ; BEGIN B (88H) ; B (0C6H) END MovDhAl ; (* DH:=AL *)
- PROCEDURE MovClAh ; BEGIN B (88H) ; B (0E1H) END MovClAh ; (* CL:=AH *)
- PROCEDURE MovClAl ; BEGIN B (88H) ; B (0C1H) END MovClAl ; (* CL:=AL *)
- PROCEDURE MovAhAl ; BEGIN B (88H) ; B (0C4H) END MovAhAl ; (* AH:=AL *)
- PROCEDURE MovAlCl ; BEGIN B (88H) ; B (0C8H) END MovAlCl ; (* AL:=CL *)
- PROCEDURE MovChImm (v : CARDINAL) ;
- BEGIN B (0B5H) ; B (v MOD 256) END MovChImm ; (* MOV CH,imm8 *)
- PROCEDURE CmpDxImm (v : CARDINAL) ;
- BEGIN B (81H) ; B (0FAH) ; W (v) END CmpDxImm ; (* CMP DX,imm16 *)
- PROCEDURE CmpAxImm (v : CARDINAL) ;
- BEGIN B (3DH) ; W (v) END CmpAxImm ; (* CMP AX,imm16 *)
- PROCEDURE CmpDl0 ; BEGIN B (80H) ; B (0FAH) ; B (0) END CmpDl0 ;
- PROCEDURE TestChImm (v : CARDINAL) ;
- BEGIN B (0F6H) ; B (0C5H) ; B (v MOD 256) END TestChImm ; (* TEST CH,imm8 *)
- PROCEDURE TestDiOffImm (off, v : CARDINAL) ;
- BEGIN B (0F6H) ; B (45H) ; B (off MOD 256) ; B (v MOD 256) END TestDiOffImm ;
- PROCEDURE AndClImm (v : CARDINAL) ;
- BEGIN B (80H) ; B (0E1H) ; B (v MOD 256) END AndClImm ; (* AND CL,imm8 *)
- PROCEDURE AndBlImm (v : CARDINAL) ;
- BEGIN B (80H) ; B (0E3H) ; B (v MOD 256) END AndBlImm ; (* AND BL,imm8 *)
- PROCEDURE OrChImm (v : CARDINAL) ;
- BEGIN B (80H) ; B (0CDH) ; B (v MOD 256) END OrChImm ; (* OR CH,imm8 *)
- PROCEDURE AddChImm (v : CARDINAL) ;
- BEGIN B (80H) ; B (0C5H) ; B (v MOD 256) END AddChImm ; (* ADD CH,imm8 *)
- PROCEDURE AddClImm (v : CARDINAL) ;
- BEGIN B (80H) ; B (0C1H) ; B (v MOD 256) END AddClImm ; (* ADD CL,imm8 *)
- PROCEDURE ShrChCl ; BEGIN B (0D2H) ; B (0EDH) END ShrChCl ; (* SHR CH,CL *)
- PROCEDURE ShrBl1 ; BEGIN B (0D0H) ; B (0EBH) END ShrBl1 ; (* SHR BL,1 *)
- PROCEDURE ShlAx1 ; BEGIN B (0D1H) ; B (0E0H) END ShlAx1 ; (* SHL AX,1 *)
- PROCEDURE ShlAl1 ; BEGIN B (0D0H) ; B (0E0H) END ShlAl1 ; (* SHL AL,1 *)
- PROCEDURE AddBlBh ; BEGIN B (00H) ; B (0FBH) END AddBlBh ; (* BL:=BL+BH *)
- PROCEDURE XorBhBh ; BEGIN B (30H) ; B (0FFH) END XorBhBh ; (* BH:=0 *)
- PROCEDURE AddAlAh ; BEGIN B (00H) ; B (0E0H) END AddAlAh ; (* AL:=AL+AH *)
- PROCEDURE AddAxCx ; BEGIN B (03H) ; B (0C1H) END AddAxCx ; (* AX:=AX+CX *)
- PROCEDURE AdcBxCx ; BEGIN B (13H) ; B (0D9H) END AdcBxCx ; (* BX:=BX+CX+CF *)
- PROCEDURE AdcDxCx ; BEGIN B (13H) ; B (0D1H) END AdcDxCx ; (* DX:=DX+CX+CF *)
- PROCEDURE AddAxDx ; BEGIN B (03H) ; B (0C2H) END AddAxDx ; (* AX:=AX+DX *)
- PROCEDURE AddBxAx ; BEGIN B (03H) ; B (0D8H) END AddBxAx ; (* BX:=BX+AX *)
- PROCEDURE SubAxDx ; BEGIN B (2BH) ; B (0C2H) END SubAxDx ; (* AX:=AX-DX *)
- PROCEDURE AddAxImm (v : CARDINAL) ;
- BEGIN B (05H) ; W (v) END AddAxImm ; (* ADD AX,imm16 *)
- PROCEDURE DecBx ; BEGIN B (4BH) END DecBx ; (* DEC BX *)
- PROCEDURE DecCh ; BEGIN B (0FEH) ; B (0CDH) END DecCh ; (* DEC CH *)
- PROCEDURE DecDl ; BEGIN B (0FEH) ; B (0CAH) END DecDl ; (* DEC DL *)
- PROCEDURE NegCl ; BEGIN B (0F6H) ; B (0D9H) END NegCl ; (* NEG CL *)
- PROCEDURE Cbw ; BEGIN B (98H) END Cbw ; (* AL -> AX *)
- PROCEDURE ImulDx ; BEGIN B (0F7H) ; B (0EAH) END ImulDx ; (* IMUL DX *)
- PROCEDURE StSiImm (v : CARDINAL) ;
- BEGIN B (0C7H) ; B (04H) ; W (v) END StSiImm ; (* [SI]:=imm16 *)
- PROCEDURE StSiAl ; BEGIN B (88H) ; B (04H) END StSiAl ; (* [SI]:=AL *)
- PROCEDURE StSiCh ; BEGIN B (88H) ; B (2CH) END StSiCh ; (* [SI]:=CH *)
- PROCEDURE StBxAl ; BEGIN B (88H) ; B (07H) END StBxAl ; (* [BX]:=AL *)
- PROCEDURE LdAlSi ; BEGIN B (8AH) ; B (04H) END LdAlSi ; (* AL:=[SI] *)
- PROCEDURE LdClBx ; BEGIN B (8AH) ; B (0FH) END LdClBx ; (* CL:=[BX] *)
- PROCEDURE LdAlDi ; BEGIN B (8AH) ; B (05H) END LdAlDi ; (* AL:=[DI] *)
- PROCEDURE XorDiOffImm (off, v : CARDINAL) ;
- BEGIN B (80H) ; B (75H) ; B (off MOD 256) ; B (v MOD 256) END XorDiOffImm ;
- PROCEDURE LdClSi ; BEGIN B (8AH) ; B (0CH) END LdClSi ; (* CL:=[SI] *)
- PROCEDURE LdAxDi ; BEGIN B (8BH) ; B (05H) END LdAxDi ; (* AX:=[DI] *)
- PROCEDURE LdBxDiOff (off : CARDINAL) ;
- BEGIN B (8BH) ; B (5DH) ; B (off MOD 256) END LdBxDiOff ; (* [DI+off] *)
- PROCEDURE LdDxDiOff (off : CARDINAL) ;
- BEGIN B (8BH) ; B (55H) ; B (off MOD 256) END LdDxDiOff ; (* [DI+off] *)
- PROCEDURE LeaDiBxData (delta : CARDINAL) ;
- BEGIN B (8DH) ; B (0BFH) ; Dd (delta) END LeaDiBxData ; (* LEA DI,[BX+data] *)
- PROCEDURE MovSiData (delta : CARDINAL) ;
- BEGIN B (0BEH) ; Dd (delta) END MovSiData ;
- PROCEDURE MovBxDataAddr (delta : CARDINAL) ;
- BEGIN B (0BBH) ; Dd (delta) END MovBxDataAddr ;
- PROCEDURE CmpSiData (delta : CARDINAL) ;
- BEGIN B (81H) ; B (0FEH) ; Dd (delta) END CmpSiData ;
- PROCEDURE MovAxData (delta : CARDINAL) ;
- BEGIN B (8BH) ; B (06H) ; Dd (delta) END MovAxData ;
- PROCEDURE LdClData (delta : CARDINAL) ;
- BEGIN B (8AH) ; B (0EH) ; Dd (delta) END LdClData ;
- PROCEDURE DecData (delta : CARDINAL) ;
- BEGIN B (0FEH) ; B (0EH) ; Dd (delta) END DecData ;
- PROCEDURE CmpBxByte (v : CARDINAL) ;
- BEGIN B (80H) ; B (3FH) ; B (v MOD 256) END CmpBxByte ; (* CMP [BX],imm8 *)
- PROCEDURE MovBxByte (v : CARDINAL) ;
- BEGIN B (0C6H) ; B (07H) ; B (v MOD 256) END MovBxByte ; (* [BX]:=imm8 *)
- PROCEDURE MovBxOff1Byte (v : CARDINAL) ;
- BEGIN B (0C6H) ; B (47H) ; B (1) ; B (v MOD 256) END MovBxOff1Byte ;
- PROCEDURE IncBxByte ; BEGIN B (0FEH) ; B (07H) END IncBxByte ; (* INC [BX] *)
- PROCEDURE MovDiBp6 ; BEGIN B (8BH) ; B (7EH) ; B (6) END MovDiBp6 ;
- PROCEDURE MovDxBp6 ; BEGIN B (8BH) ; B (56H) ; B (6) END MovDxBp6 ;
- PROCEDURE PushBp6 ; BEGIN B (0FFH) ; B (76H) ; B (6) END PushBp6 ;
- PROCEDURE MovCxBp6 ; BEGIN B (8BH) ; B (4EH) ; B (6) END MovCxBp6 ;
- PROCEDURE MovDxBp4 ; BEGIN B (8BH) ; B (56H) ; B (4) END MovDxBp4 ;
- PROCEDURE MovDiBp8 ; BEGIN B (8BH) ; B (7EH) ; B (8) END MovDiBp8 ;
- PROCEDURE SbbBxData (delta : CARDINAL) ;
- BEGIN B (1BH) ; B (1EH) ; Dd (delta) END SbbBxData ;
- PROCEDURE SbbDxData (delta : CARDINAL) ;
- BEGIN B (1BH) ; B (16H) ; Dd (delta) END SbbDxData ;
- PROCEDURE StDiCh ; BEGIN B (88H) ; B (2DH) END StDiCh ; (* [DI]:=CH *)
- PROCEDURE Cmc ; BEGIN B (0F5H) END Cmc ;
- PROCEDURE Clc ; BEGIN B (0F8H) END Clc ;
- PROCEDURE IncCl ; BEGIN B (0FEH) ; B (0C1H) END IncCl ;
- PROCEDURE DecDi ; BEGIN B (4FH) END DecDi ;
- PROCEDURE SubAhCh ; BEGIN B (28H) ; B (0ECH) END SubAhCh ; (* AH:=AH-CH *)
- PROCEDURE SubAlDl ; BEGIN B (2AH) ; B (0C2H) END SubAlDl ; (* AL:=AL-DL *)
- PROCEDURE IncCh ; BEGIN B (0FEH) ; B (0C5H) END IncCh ; (* INC CH *)
- PROCEDURE IncDl ; BEGIN B (0FEH) ; B (0C2H) END IncDl ; (* INC DL *)
- PROCEDURE MovClImm (v : CARDINAL) ;
- BEGIN B (0B1H) ; B (v MOD 256) END MovClImm ;
- PROCEDURE CmpClImm (v : CARDINAL) ;
- BEGIN B (80H) ; B (0F9H) ; B (v MOD 256) END CmpClImm ;
- PROCEDURE SubClAl ; BEGIN B (28H) ; B (0C1H) END SubClAl ; (* CL:=CL-AL *)
- PROCEDURE SubAlCl ; BEGIN B (28H) ; B (0C8H) END SubAlCl ; (* AL:=AL-CL *)
- PROCEDURE SubClImm (v : CARDINAL) ;
- BEGIN B (80H) ; B (0E9H) ; B (v MOD 256) END SubClImm ;
- PROCEDURE AddAhCh ; BEGIN B (00H) ; B (0ECH) END AddAhCh ; (* AH:=AH+CH *)
- PROCEDURE AddAhImm (v : CARDINAL) ;
- BEGIN B (80H) ; B (0C4H) ; B (v MOD 256) END AddAhImm ;
- PROCEDURE AdcBxSi ; BEGIN B (11H) ; B (0F3H) END AdcBxSi ;
- PROCEDURE AdcDxDi ; BEGIN B (11H) ; B (0FAH) END AdcDxDi ;
- PROCEDURE AdcBxImm (v : CARDINAL) ;
- BEGIN B (83H) ; B (0D3H) ; B (v MOD 256) END AdcBxImm ;
- PROCEDURE AdcDxImm (v : CARDINAL) ;
- BEGIN B (83H) ; B (0D2H) ; B (v MOD 256) END AdcDxImm ;
- PROCEDURE SbbBxSi ; BEGIN B (19H) ; B (0F3H) END SbbBxSi ;
- PROCEDURE SbbDxDi ; BEGIN B (19H) ; B (0FAH) END SbbDxDi ;
- PROCEDURE OrDiImm (v : CARDINAL) ;
- BEGIN B (81H) ; B (0CFH) ; W (v) END OrDiImm ; (* DI:=DI OR imm16 *)
- PROCEDURE OrDhImm (v : CARDINAL) ;
- BEGIN B (80H) ; B (0CEH) ; B (v MOD 256) END OrDhImm ;
- PROCEDURE TestDhImm (v : CARDINAL) ;
- BEGIN B (0F6H) ; B (0C6H) ; B (v MOD 256) END TestDhImm ;
- PROCEDURE StDataImm (delta, v : CARDINAL) ;
- BEGIN B (0C7H) ; B (06H) ; Dd (delta) ; W (v) END StDataImm ;
- PROCEDURE StDataByte (delta, v : CARDINAL) ;
- BEGIN B (0C6H) ; B (06H) ; Dd (delta) ; B (v MOD 256) END StDataByte ;
- PROCEDURE StDataCl (delta : CARDINAL) ;
- BEGIN B (88H) ; B (0EH) ; Dd (delta) END StDataCl ;
- PROCEDURE StDataDi (delta : CARDINAL) ;
- BEGIN B (89H) ; B (3EH) ; Dd (delta) END StDataDi ;
- PROCEDURE LdAlData (delta : CARDINAL) ;
- BEGIN B (0A0H) ; Dd (delta) END LdAlData ;
- PROCEDURE XorDiData (delta : CARDINAL) ;
- BEGIN B (33H) ; B (3EH) ; Dd (delta) END XorDiData ; (* XOR DI,[Vx] *)
- PROCEDURE XorDataDh (delta : CARDINAL) ;
- BEGIN B (30H) ; B (36H) ; Dd (delta) END XorDataDh ; (* XOR [Vx],DH *)
- PROCEDURE XorDhData (delta : CARDINAL) ;
- BEGIN B (32H) ; B (36H) ; Dd (delta) END XorDhData ; (* XOR DH,[Vx] *)
- PROCEDURE XorDataImm (delta, v : CARDINAL) ;
- BEGIN B (80H) ; B (36H) ; Dd (delta) ; B (v MOD 256) END XorDataImm ;
- PROCEDURE AndDataImm (delta, v : CARDINAL) ;
- BEGIN B (80H) ; B (26H) ; Dd (delta) ; B (v MOD 256) END AndDataImm ;
- PROCEDURE TestDataImm (delta, v : CARDINAL) ;
- BEGIN B (0F6H) ; B (06H) ; Dd (delta) ; B (v MOD 256) END TestDataImm ;
- PROCEDURE LdCxSiOff (off : CARDINAL) ;
- BEGIN B (8BH) ; B (4CH) ; B (off MOD 256) END LdCxSiOff ;
- PROCEDURE LdDxSiOff (off : CARDINAL) ;
- BEGIN B (8BH) ; B (54H) ; B (off MOD 256) END LdDxSiOff ;
- PROCEDURE LdCxDiOff (off : CARDINAL) ;
- BEGIN B (8BH) ; B (4DH) ; B (off MOD 256) END LdCxDiOff ;
- PROCEDURE LdSiDiOff (off : CARDINAL) ;
- BEGIN B (8BH) ; B (75H) ; B (off MOD 256) END LdSiDiOff ;
- PROCEDURE LdDiDiOff (off : CARDINAL) ;
- BEGIN B (8BH) ; B (7DH) ; B (off MOD 256) END LdDiDiOff ;
- PROCEDURE PushBp4 ; BEGIN B (0FFH) ; B (76H) ; B (4) END PushBp4 ; (* PUSH [BP+4] *)
- PROCEDURE JccNear (code : CARDINAL ; nm : ARRAY OF CHAR) ;
- (* A conditional near jump: the 8086 has no Jcc rel16, so an INVERSE Jcc_s
- +3 over an E9 rel16. The inverse is what makes the E9 run when `code'
- HOLDS; a raw Jcc here would jump when it does NOT (remult's zero test did
- exactly that and multiplied everything to zero). Use this when the target
- is more than 127 bytes away; Jcc's rel8 wraps silently otherwise (the
- FixUp guard below now refuses that). *)
- BEGIN
- IF (code MOD 2) = 0 THEN
- B (code + 1)
- ELSE
- B (code - 1)
- END ;
- B (3) ; B (0E9H) ; AddFix (1, rpos, nm, 0) ; W (0)
- END JccNear ;
- PROCEDURE JmpNear (nm : ARRAY OF CHAR) ;
- BEGIN B (0E9H) ; AddFix (1, rpos, nm, 0) ; W (0) END JmpNear ;
- PROCEDURE Call (nm : ARRAY OF CHAR) ;
- BEGIN B (0E8H) ; AddFix (1, rpos, nm, 0) ; W (0) END Call ;
- PROCEDURE StVxBx (delta : CARDINAL ) ;
- (* 89 1E lo hi: MOV [Vx],BX - the mirror of LdBxVx above, and it must use the
- same mod=00 / rm=110 and the same Dd fixup kind, or one of the two
- addresses a different place. *)
- BEGIN
- B (89H) ; B (01EH) ; Dd (delta)
- END StVxBx ;
- PROCEDURE JmpBx ; BEGIN B (0FFH) ; B (0E3H) END JmpBx ; (* FF E3: JMP BX *)
- (* JE 74 JNE 75 JB 72 JBE 76 JGE 7D *)
- PROCEDURE Je8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (74H, nm) END Je8 ;
- PROCEDURE Jne8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (75H, nm) END Jne8 ;
- PROCEDURE Jb8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (72H, nm) END Jb8 ;
- PROCEDURE Jbe8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (76H, nm) END Jbe8 ;
- PROCEDURE Jge8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (7DH, nm) END Jge8 ;
- PROCEDURE Jle8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (7EH, nm) END Jle8 ;
- PROCEDURE Ja8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (77H, nm) END Ja8 ;
- PROCEDURE Jae8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (73H, nm) END Jae8 ;
- PROCEDURE Jcxz8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (0E3H, nm) END Jcxz8 ;
- PROCEDURE Loop8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (0E2H, nm) END Loop8 ;
- (* ---------------------------------------------------------------- *)
- (* the entries *)
- (* ---------------------------------------------------------------- *)
- PROCEDURE EmitInitMem ;
- (* AX = offset of the program header. The header holds, at +4 the base of
- the program's data area and at +8 its end, so the globals can be zeroed -
- Pascal leaves them undefined, TP3's runtime clears them. Also makes
- ES = DS so that any string instruction in the library would work. *)
- BEGIN
- M ("initmem") ;
- MovSiAx ; (* SI = AX = the header offset the caller passed *)
- MovDxSi4 ; (* DX = [SI+4] = hdrDS = data base *)
- MovCxSi6 ; (* CX = [SI+6] = hdrHeap = data end *)
- CmpCxDx ;
- Jbe8 ("im_done") ;
- MovDiDx ; (* DI = data base *)
- M ("im_zero") ;
- XorAxAx ; (* AX = 0: the value written into every global.
- The caller passes the header offset in AX, so
- without this the "zeroing" loop would write the
- header offset into all of them. *)
- StDiAx ;
- AddDi2 ;
- CmpDiCx ;
- Jb8 ("im_zero") ;
- M ("im_done") ;
- PushDs ; PopEs ;
- RetR
- END EmitInitMem ;
- PROCEDURE EmitEnd ;
- (* progend and halt are the same code: the compiler already zeroes AX for
- progend, and a HALT argument is discarded at compile time, so both leave
- with exit code 0. *)
- BEGIN
- M ("progend") ;
- M ("halt") ;
- XorAxAx ;
- MovAh (4CH) ;
- Int21 ;
- RetR
- END EmitEnd ;
- PROCEDURE EmitStackChk ;
- (* called from every procedure prologue. Range and stack checking are not
- compiled in yet, so this must do nothing at all - in particular it must
- not touch a register, because the call site is in the middle of a
- partially evaluated expression. *)
- BEGIN
- M ("stackchk") ;
- RetR
- END EmitStackChk ;
- PROCEDURE EmitGetCh ;
- (* AL = next character, 1Ah at end of input. INT 21h AH=08h reads without
- echoing, so a redirected stdin behaves the same as a keyboard.
- This is TP3's `getbyte' (TPSRC4:94), not a bare INT 21h. getbyte serves a
- buffered character from the file record without advancing the buffer
- pointer when the "char pre-read" flag ($02) is set, and readnum sets that
- flag on the character that ENDS a number - the terminating blank is *not*
- consumed. xreadln is then called and it is the xreadln that consumes the
- newline.
- This matters because `readln(n)' is emitted as xrdint followed by xreadln
- (TPSRC8 prdrdln emits xreadln whenever rdlnflg is set). A getch that
- always consumed its character left xreadln starting on the *following*
- line, so `readln(n); readln(c)' read the char from line 3 of the input.
- One byte of pushback reproduces the flag. *)
- BEGIN
- M ("getch") ;
- LdBxVx (D_PUSH) ; (* BX := the slot's contents *)
- CmpBx0 ;
- Je8 ("gc_dos") ;
- MovAlBl ; (* AL := the pending character, i.e. the
- low byte OF BX - not the byte at [BX] *)
- XorBxBx ;
- StVxBx (D_PUSH) ; (* and empty the slot *)
- RetR ;
- M ("gc_dos") ;
- MovAh (08H) ;
- Int21 ;
- RetR
- END EmitGetCh ;
- PROCEDURE EmitUnGetCh ;
- (* put AL back, so the next getch returns it again. TP3 does this by
- rewinding the buffer pointer; a one-byte slot is the same thing. *)
- BEGIN
- M ("ungetch") ;
- MovDlAl ;
- MovBxImm (1) ; (* BH := 1 marks the slot occupied, so
- a pushed-back NUL is still a
- pushed-back NUL *)
- MovBlDl ;
- StVxBx (D_PUSH) ;
- RetR
- END EmitUnGetCh ;
- PROCEDURE EmitWrInt ;
- (* one signed 16-bit value on the stack. Div CX gives the remainder in DX,
- which is turned into a digit and stored backwards from the end of the
- scratch area, then printed forwards. *)
- BEGIN
- M ("wrint") ;
- PushBp ; MovBpSp ;
- MovAxBp4 ;
- CmpAx0 ;
- Jge8 ("wi_pos") ;
- PushAx ;
- MovAlD (ORD ("-")) ; MovAh (2) ; Int21 ;
- PopAx ;
- NegAx ;
- M ("wi_pos") ;
- MovCxImm (10) ;
- MovBxVx (D_NUM + 8) ; (* BX = one past the last digit *)
- MovSiBx ;
- M ("wi_dig") ;
- XorDxDx ;
- DivCx ;
- AddDl (ORD ("0")) ;
- DecSi ;
- StSiDl ;
- CmpAx0 ;
- Jne8 ("wi_dig") ;
- M ("wi_out") ;
- CmpSiBx ;
- Je8 ("wi_done") ;
- MovAlSi ; (* the digit: AH=02h wants it in AL *)
- MovAh (02H) ; Int21 ;
- IncSi ;
- J8 ("wi_out") ;
- M ("wi_done") ;
- MovSpBp ; PopBp ; RetR
- END EmitWrInt ;
- PROCEDURE EmitWrIntW ;
- (* `writeln(I:2)': an integer in a field of `width' columns, right-justified.
- The caller pushes the VALUE first and the WIDTH second, so the value is at
- [BP+6] and the width at [BP+4]. The number of characters the signed
- decimal will occupy is counted first (one extra when negative); the pad is
- max(width - chars, 0) spaces. The digit loop is wrint's, duplicated rather
- than shared: the two entries have different argument offsets, and a jump
- into the middle of the other entry's frame would be a convention nobody
- could see from either name. *)
- BEGIN
- M ("wrintw") ;
- PushBp ; MovBpSp ;
- (* BX := number of characters: sign + digits *)
- MovBxImm (0) ;
- MovAxBp6 ;
- CmpAx0 ;
- Jge8 ("wiw_abs") ;
- IncBx ; (* the '-' counts as one column *)
- NegAx ;
- M ("wiw_abs") ;
- MovCxImm (10) ;
- M ("wiw_cnt") ;
- IncBx ;
- XorDxDx ;
- DivCx ;
- CmpAx0 ;
- Jne8 ("wiw_cnt") ;
- (* pad := width - chars; only a positive pad prints *)
- MovAxBp4 ;
- SubAxBx ;
- CmpAx0 ;
- Jle8 ("wiw_body") ;
- MovCxAx ;
- M ("wiw_pad") ;
- MovAlD (ORD (' ')) ; MovAh (02H) ; Int21 ;
- Loop8 ("wiw_pad") ;
- M ("wiw_body") ;
- (* the signed decimal, exactly as wrint prints it *)
- MovAxBp6 ;
- CmpAx0 ;
- Jge8 ("wiw_pos") ;
- PushAx ;
- MovAlD (ORD ('-')) ; MovAh (2) ; Int21 ;
- PopAx ;
- NegAx ;
- M ("wiw_pos") ;
- MovCxImm (10) ;
- MovBxVx (D_NUM + 8) ;
- MovSiBx ;
- M ("wiw_dig") ;
- XorDxDx ;
- DivCx ;
- AddDl (ORD ('0')) ;
- DecSi ;
- StSiDl ;
- CmpAx0 ;
- Jne8 ("wiw_dig") ;
- M ("wiw_out") ;
- CmpSiBx ;
- Je8 ("wiw_done") ;
- MovAlSi ;
- MovAh (02H) ; Int21 ;
- IncSi ;
- J8 ("wiw_out") ;
- M ("wiw_done") ;
- MovSpBp ; PopBp ; RetR
- END EmitWrIntW ;
- PROCEDURE EmitWrChar ;
- (* the low byte of the one 16-bit argument, which sits above the return
- address. There is no [SP] addressing in 16-bit mode, so BP stands in for
- the stack pointer -- and it has to be SAVED, because BP is the one register
- a caller is entitled to expect back: the test driver keeps its cursor into
- the case record there, so an entry that borrows BP without returning it
- sends the next call to a garbage address. That is not hypothetical: wrchar
- and wrbool both did it, and "writeln(42) writeln TRUE in one program" hung
- the machine with the record header printed twice, because the driver had
- been sent back to the top of its own loop. Load BP, use it, hand it back.
- MovAlArg4, not MovAlArg2: BP is pushed first, so the argument is four bytes
- up, not two. AL, not DL: see the AL twins above. *)
- BEGIN
- M ("wrchar") ;
- PushBp ; MovBpSp ;
- MovAlArg4 ;
- MovSpBp ; PopBp ;
- MovAh (02H) ;
- Int21 ;
- RetR
- END EmitWrChar ;
- PROCEDURE EmitWrInl ;
- (* Write an inline string literal - TP3 TPSRC4 "xwrtinl".
- The compiler emits
- CALL wrtinl <length byte> <character>...
- so the return address on the stack points at the length byte that follows
- the call. POP BX takes that address, CX picks up the length, and the
- routine finishes with JMP BX - returning to just past the last character.
- That is the whole trick: the literal is self-delimiting, so it needs no
- terminator, no length table and no space in the data segment, and it costs
- the code stream only the characters themselves (TPSRC10 "estring" emits
- exactly <length byte><chars> for the same reason).
- Consequently this entry has NO stack argument, unlike WrInt/WrChar: the
- return address has already been consumed by the POP. *)
- BEGIN
- M ("wrtinl") ;
- PopBx ; (* BX := address of the length byte *)
- XorCxCx ;
- MovClBx ; (* CX := length *)
- IncBx ; (* BX -> first character *)
- MovAh (02H) ; (* INT 21h/02h: put character, AL *)
- Jcxz8 ("wn_end") ; (* empty string -> nothing to do *)
- M ("wn_loop") ;
- LdAlBx ; (* AL := next character *)
- Int21 ; (* (preserves every register but AL) *)
- IncBx ;
- Loop8 ("wn_loop") ;
- M ("wn_end") ;
- JmpBx (* resume past the string; no RET here,
- the return address is already gone *)
- END EmitWrInl ;
- PROCEDURE EmitWrStr ;
- (* Write a string VARIABLE - TP3 TPSRC4 "xwrtstr", minus the field-width
- formatting this compiler does not compile yet.
- The compiler emits
- <PUSH the string's address> from EmPushVarAddr
- CALL wrstr
- so the address is the one 16-bit argument on the stack, read through BP
- exactly as WrChar reads its value (there is no [SP] form). The first byte
- at that address is the current LENGTH and the characters follow it, which
- is the layout TPSRC8's `string' type gives a variable: TPSRC8:83 does
- `INC BX / MOV varsize,BX' for `string [n]', so n+1 bytes, length byte
- first.
- The loop is wrtinl's, pointed at the caller's bytes instead of at the
- bytes behind the call: CL := [BX] is the length, BX moves to the first
- character, and one INT 21h/02h prints each one until CX runs out. A
- length of 0 prints nothing, so an empty string is silent. *)
- BEGIN
- M ("wrstr") ;
- PushBp ; MovBpSp ;
- PushBx ; PushCx ; PushAx ;
- MovBxBp4 ; (* BX := the caller's string address *)
- XorCxCx ;
- MovClBx ; (* CX := the length byte at [BX] *)
- IncBx ; (* BX -> the first character *)
- MovAh (02H) ; (* INT 21h/02h: put the character in AL *)
- Jcxz8 ("ws_end") ; (* empty string -> nothing to do *)
- M ("ws_loop") ;
- LdAlBx ; (* AL := next character *)
- Int21 ; (* preserves every register but AL *)
- IncBx ;
- Loop8 ("ws_loop") ;
- M ("ws_end") ;
- PopAx ; PopCx ; PopBx ;
- MovSpBp ; PopBp ;
- RetR
- END EmitWrStr ;
- PROCEDURE EmitWrBool ;
- (* 4, not 2, for the reason given on CmpArg4W0: BP is pushed before it is
- borrowed, so the argument is four bytes up. POP BP before the RET, and
- note that CmpArg4W0 has already handed SP back, so the pop lands on the
- saved BP and not on the argument. *)
- BEGIN
- M ("wrbool") ;
- PushBp ;
- CmpArg4W0 ;
- Jne8 ("wb_t") ;
- MovDxVx (D_FALSE) ;
- J8 ("wb_o") ;
- M ("wb_t") ;
- MovDxVx (D_TRUE) ;
- M ("wb_o") ;
- MovAh (09H) ;
- Int21 ;
- PopBp ;
- RetR
- END EmitWrBool ;
- PROCEDURE EmitWrLn ;
- BEGIN
- M ("wrln") ;
- MovDxVx (D_CRLF) ;
- MovAh (09H) ;
- Int21 ;
- RetR
- END EmitWrLn ;
- PROCEDURE EmitWrReal ;
- (* the 6-byte real is on the stack but is not formatted: the compiler does
- not yet load real operands into a form the runtime could read. A visible
- marker beats printing the mantissa as an integer. *)
- BEGIN
- M ("wrreal") ;
- MovDxVx (D_REAL) ;
- MovAh (09H) ;
- Int21 ;
- RetR
- END EmitWrReal ;
- PROCEDURE EmitRdInt ;
- (* Read a number into a 16-bit variable: the caller's address is on the
- stack. The scan itself is EmitRdScan, shared with rdbyte; an entry owns
- its frame, its address load and its store, nothing else. *)
- BEGIN
- M ("rdint") ;
- PushBp ; MovBpSp ;
- PushAx ; PushBx ; PushCx ; PushDx ; PushDi ;
- C8 ("rdscan") ;
- CmpCxV (2) ;
- Je8 ("ri_skip") ; (* nothing entered: leave the variable
- alone, as TP3's xrdint at rdierr does *)
- MovDiBp4 ; (* ...the caller's address into DI... *)
- StDiAx ; (* ...and store the word. MovDiBp4 and
- not a [BX] form: there is none; see
- the note on the store emitters. *)
- M ("ri_skip") ;
- PopDi ; PopDx ; PopCx ; PopBx ; PopAx ;
- MovSpBp ; PopBp ; RetR
- END EmitRdInt ;
- PROCEDURE EmitRdScan ;
- (* The number reader, called by rdint (word store) and by rdbyte (byte
- store). A transcription of TP3 TPSRC4 xrdint/readnum, minus the store:
- rnspace: getbyte ; ^Z ? -> rnend
- consume ; <= 20h ? -> rnspace
- rndig: [buf]=char ; getbyte ; <= 20h ? -> rnend (NOT consumed)
- consume ; -> rndig
- rnend: buf[0] := 0
- It builds NO frame of its own and touches neither BP nor SP, so it is
- called from the caller's frame and that frame's [BP+4] is still the
- caller's argument when it returns. It clobbers AX BX CX DX DI, which the
- caller has already saved.
- Returns AX = the value, and CX = 0 positive, 1 negative, 2 nothing read.
- CX = 2 is set HERE, on the ^Z path: it is the state both callers test to
- decide not to store, and before this split the check existed only as a
- jump to the exit, so the state itself was never produced. AX is
- meaningless when CX = 2.
- Two behaviours are load-bearing and neither is obvious:
- - the character that ENDS the scan is left pending, because readnum tests
- it *before* clearing getbyte's pre-read flag. xreadln, which the
- compiler emits next for a readln, is what consumes the newline. See the
- note on getch.
- - ^Z before any digit returns without a value, and the caller then returns
- WITHOUT touching the variable: TP3's xrdint reaches rdierr and RETs.
- Sign in CX, value in DI, value in AX on the way out. *)
- BEGIN
- M ("rdscan") ;
- M ("ri_lead") ;
- C8 ("getch") ;
- CmpAl (1AH) ; (* ^Z: end of input *)
- Je8 ("ri_none") ;
- CmpAl (20H) ; (* every control char and the space.
- 20H, not 20: readnum skips
- everything <= #$20, and a literal
- that READS like hex but is
- decimal compares AL against 14h,
- so a leading space is never
- skipped and the scan ends with
- nothing read. Every other
- literal in this module is
- explicit for the same reason. *)
- Jbe8 ("ri_lead") ;
- XorCxCx ;
- CmpAl (ORD ("-")) ;
- Jne8 ("ri_nos") ;
- IncCx ;
- C8 ("getch") ;
- J8 ("ri_dig0") ;
- M ("ri_nos") ;
- CmpAl (ORD ("+")) ;
- Jne8 ("ri_dig0") ;
- C8 ("getch") ;
- M ("ri_dig0") ;
- XorDiDi ;
- M ("ri_dig") ;
- CmpAl (ORD ("0")) ;
- Jb8 ("ri_done") ;
- CmpAl (ORD ("9")) ;
- Ja8 ("ri_done") ;
- SubAl (ORD ("0")) ;
- MovAh (0) ; (* AX := the digit, 0..9 *)
- XchgAxDi ; (* AX := the value so far, DI := the digit.
- The digit has to survive MUL, and the 8-bit
- reg field of 8A/88 has only AL/CL/DL/BL/AH/
- CH/DH/BH - there is no SI or DI byte to park
- it in. MUL r/m16 writes DX, so the previous
- version's "keep the digit across the multiply"
- comment was describing a register the multiply
- owns. Every read of an integer therefore
- returned 0: the digit was computed, moved to
- DH, and multiplied away one instruction later. *)
- MovBxImm (10) ;
- MulBx ; (* DX:AX := value * 10 *)
- AddAxDi ; (* AX := low word + digit. A carry out of bit 15
- is dropped, which is what a 16-bit INTEGER
- does anyway - the high word of the product is
- never stored. *)
- MovDiAx ;
- C8 ("getch") ;
- J8 ("ri_dig") ;
- M ("ri_done") ;
- C8 ("ungetch") ; (* the delimiter stays pending *)
- CmpCx0 ;
- Je8 ("ri_pos") ;
- NegDi ;
- M ("ri_pos") ;
- MovAxDi ; (* the parsed value out of DI and into AX,
- where both callers store it from *)
- RetR ;
- M ("ri_none") ; (* ^Z first: no value, and CX says so *)
- MovCxImm (2) ;
- RetR
- END EmitRdScan ;
- PROCEDURE EmitRdByte ;
- (* Read a number into a BYTE variable: the same scan, a one-byte store.
- TPSRC8 prdnum dispatches on the variable's size - `CMP varsize,#$01 /
- JA prdemit' keeps xrdint for anything wider and falls through to
- `MOV AX,#xrdbyte' for a one-byte variable - and TPSRC4's two entries
- differ in exactly one store: xrdbyte is `CLC', xrdint is `STC', and rdi1
- stores AX or AL on `JNB rdibyt'.
- The flag form is not transcribed. It would put the STC at the head of the
- rdint entry, and the BP contract in tests/rt_exec.py requires an entry
- that borrows BP to *start* with `55 8B EC' and to hand BP back from inside
- its own slice - which is what an entry owning its own frame does. Two
- frames and two stores around one scan is the same idea, with that
- constraint respected.
- The store is the whole reason this entry exists: rdint writes two bytes,
- and the second one is the byte that FOLLOWS the variable, so
- `readln (b)' was reading into b and into whatever the compiler had put
- next to it. *)
- BEGIN
- M ("rdbyte") ;
- PushBp ; MovBpSp ;
- PushAx ; PushBx ; PushCx ; PushDx ; PushDi ;
- C8 ("rdscan") ;
- CmpCxV (2) ;
- Je8 ("rby_skip") ; (* nothing entered: leave it alone *)
- MovDiBp4 ; (* the caller's address into DI *)
- StDiAl ; (* ONE byte - 88 05, AL not AX *)
- M ("rby_skip") ;
- PopDi ; PopDx ; PopCx ; PopBx ; PopAx ;
- MovSpBp ; PopBp ; RetR
- END EmitRdByte ;
- PROCEDURE EmitRdChar ;
- BEGIN
- M ("rdchar") ;
- PushBp ; MovBpSp ;
- PushAx ; PushBx ; PushDi ;
- C8 ("getch") ;
- MovDlAl ;
- MovDiBp4 ; (* no [BX] form: the address goes in DI *)
- StDiDl ;
- PopDi ; PopBx ; PopAx ;
- MovSpBp ; PopBp ; RetR
- END EmitRdChar ;
- PROCEDURE EmitRdBool ;
- (* one character, classified the way TP3 does: T/t/Y/y/1 true, anything else
- false. *)
- BEGIN
- M ("rdbool") ;
- PushBp ; MovBpSp ;
- PushAx ; PushBx ; PushCx ; PushDi ;
- C8 ("getch") ;
- XorCxCx ;
- CmpAl (ORD ("T")) ; Je8 ("rb_t") ;
- CmpAl (ORD ("t")) ; Je8 ("rb_t") ;
- CmpAl (ORD ("Y")) ; Je8 ("rb_t") ;
- CmpAl (ORD ("y")) ; Je8 ("rb_t") ;
- CmpAl (ORD ("1")) ; Je8 ("rb_t") ;
- J8 ("rb_s") ;
- M ("rb_t") ;
- IncCx ;
- M ("rb_s") ;
- MovDiBp4 ; (* DI = the caller's address, because there is
- no [BX] to store through *)
- StDiCx ;
- PopDi ; PopCx ; PopBx ; PopAx ;
- MovSpBp ; PopBp ; RetR
- END EmitRdBool ;
- PROCEDURE EmitRdLn ;
- (* discard the rest of the line, including the terminator *)
- BEGIN
- M ("rdln") ;
- PushAx ;
- M ("rl_loop") ;
- C8 ("getch") ;
- CmpAl (0DH) ; Je8 ("rl_e") ;
- CmpAl (0AH) ; Je8 ("rl_e") ;
- CmpAl (1AH) ; Je8 ("rl_e") ; (* ^Z: end of input *)
- J8 ("rl_loop") ;
- M ("rl_e") ;
- PopAx ;
- RetR
- END EmitRdLn ;
- PROCEDURE EmitRdStr ;
- (* readln(s)/read(s) of a string variable: TPSRC4's xrdstr, the runtime entry
- behind both prdstr and prdrdln. The caller pushes the variable's address
- (one stack word, read here via BP), sets CX = max_len = varsize-1, and pops
- the word after the call.
- Keyed to ours rather than TP3's exactly: TP3 emits MOV CL,imm8 and clears
- CH the length load; this runtime has no byte-register move into CH/CL, so
- the compiler emits a full MOV CX,imm16 and CX *is* the limit (Loop8 / the
- count arithmetic below rely on it).
- Characters come from getch, which clobbers only AX and BX, so the running
- count lives in BX behind a push/pop around each call. Reading stops at CR,
- LF or ^Z - LF because our input files are LF-terminated - and the terminator
- is left PENDING via ungetch, exactly as TP3's xrdstr leaves its CR pending
- through the pre-read flag: the readln wrapper then consumes the newline,
- while a bare read(s) leaves it for the next read. The LOOP truncates a line
- longer than max_len. dest[0] := length; dest[1..] := chars. *)
- BEGIN
- M ("rdstr") ;
- PushBp ; MovBpSp ;
- PushAx ; PushBx ; PushCx ; PushDx ; PushDi ;
- MovDiBp4 ; (* DI := dest base, for the length byte *)
- MovAxBp4 ; MovSiAx ; (* SI := dest, walking *)
- IncSi ; (* SI := dest + 1: chars live at dest[1..], the
- length byte at dest[0] is stored at the end *)
- XorBxBx ; (* BX := 0: count of characters read *)
- M ("rs_loop") ;
- PushBx ; (* getch clobbers BX (and AX) *)
- C8 ("getch") ;
- PopBx ;
- CmpAl (0DH) ; Je8 ("rs_term") ; (* CR *)
- CmpAl (0AH) ; Je8 ("rs_term") ; (* LF *)
- CmpAl (1AH) ; Je8 ("rs_term") ; (* ^Z *)
- MovDlAl ; (* DL := char *)
- IncBx ; (* count := count + 1 *)
- StSiDl ; (* dest[i] := char *)
- IncSi ;
- Loop8 ("rs_loop") ; (* CX--; loop while CX # 0: truncation *)
- M ("rs_end") ;
- MovAlBl ; (* AL := count *)
- StDiAl ; (* dest[0] := length *)
- PopDi ; PopDx ; PopCx ; PopBx ; PopAx ;
- MovSpBp ; PopBp ; RetR ;
- M ("rs_term") ;
- PushBx ; (* ungetch clobbers BX too *)
- C8 ("ungetch") ;
- PopBx ;
- J8 ("rs_end")
- END EmitRdStr ;
- PROCEDURE EmitStrStore ;
- (* s := t for string variables: TPSRC2's strstore with TPSRC8 asnvar's stack
- shape. The caller pushes BOTH addresses - destination first, source second,
- so the LAST push (dest) sits at [BP+4] and the source at [BP+6] - sets
- CX = max_len = varsize-1 of the DESTINATION, calls, and pops both words
- (this runtime is caller-pops, unlike TP3's strstore which pops itself).
- TP3's strstore rewrites the length byte of the stack copy of the source to
- min(src[0], max), then copies length byte + chars, truncating at max, with
- the destination TAIL past the new length left untouched. Here, with both
- variables in memory and no stack copy, that becomes: count := min(src[0],
- max); dest[0] := count; copy count chars src[1..] -> dest[1..]. The source
- variable is never written - truncation is expressed in the destination's own
- length byte only. A source longer than max is truncated; a shorter one is
- copied whole (CX := src length, count including the empty case where LOOP
- simply never runs). *)
- BEGIN
- M ("strstore") ;
- PushBp ; MovBpSp ;
- PushAx ; PushBx ; PushCx ; PushDx ;
- MovAxBp4 ; MovSiAx ; (* SI := dest, walking *)
- MovBxBp6 ; (* BX := source *)
- XorDxDx ;
- LdAlBx ; MovDlAl ; (* DL := src[0], the source length *)
- IncBx ; (* BX -> first source character *)
- CmpCxDx ; (* CMP CX (max), DX (src length) *)
- Jb8 ("ss_trunc") ;
- MovCxDx ; (* not truncated: CX := src length *)
- J8 ("ss_len") ;
- M ("ss_trunc") ;
- MovDlCl ; (* truncated: DL := max, for the length store *)
- M ("ss_len") ;
- StSiDl ; (* dest[0] := length *)
- IncSi ; (* dest -> first character *)
- Jcxz8 ("ss_done") ; (* an empty source copies nothing; LOOP with
- CX = 0 would run 65536 times, not zero *)
- M ("ss_loop") ;
- LdAlBx ; (* AL := src char *)
- MovDlAl ; (* DL := char *)
- StSiDl ; (* dest char := src char *)
- IncBx ; IncSi ;
- Loop8 ("ss_loop") ; (* CX--; loop while CX # 0 *)
- M ("ss_done") ;
- PopDx ; PopCx ; PopBx ; PopAx ;
- MovSpBp ; PopBp ; RetR
- END EmitStrStore ;
- PROCEDURE EmitCmpStr ;
- (* cmpstr: lexicographic comparison of two strings, TPSRC2's cmpstr with the
- addresses taken as arguments rather than the strings pushed by value.
- The caller pushes BOTH addresses - the FIRST (left) operand first, so it
- sits at [BP+6], the second (right) at [BP+4], the same order strstore takes
- its two - calls, and pops both words (caller-pops, unlike TP3's cmpstr,
- which pops itself). It returns AX = -1, 0 or +1 for left < right, left =
- right, left > right.
- Why AX and not the flags, when TP3's cmpstr leaves them set? Because TP3's
- cmpstr also REMOVES its own arguments (`MOV SP,BX', TPSRC2 960), so its six
- wrappers (csteq/cstne/cstge/cstle/cstg/cstl, TPSRC2 890-925) can branch on
- the flags immediately after the CALL. Here the caller pushes the two
- addresses, and dropping them with ADD SP,4 - the one-instruction way - is
- in the CPU's arithmetic group and DESTROYS the flags. (POP does not, but
- then the flags would have to survive the caller's next emission, which is
- exactly the invisible coupling the notes in this file keep warning about.)
- A signed value in AX costs a few instructions inside the entry and keeps the
- flag convention the compiler's own business, where the six-arm table already
- lives: the caller runs OR AX,AX and the same EmSetcc it runs after every
- numeric comparison, so the two paths cannot drift apart.
- That tail is not invented. A TP3 wrapper is `MOV AX,#1 / Jcc / DEC AX /
- OR AX,AX' - the same EmSetcc our numeric arm emits, followed by the same
- OR AX,AX - and the only thing this design moves is WHERE it happens: out of
- six one-per-operator runtime entries and into the one shared table. TP3
- needs the six because its cmpstr is reached through vcompare's vector table
- (TPSRC9 133) by type; we already have the operator as a number.
- The comparison is TPSRC2's, translated to an explicit byte loop because
- this runtime has no REPZ CMPS: min(lenL,lenR) bytes are compared in order,
- the first difference decides, and if the common prefix is equal the LENGTHS
- decide (a prefix is less than the string that continues it). The byte
- compare is unsigned - CF is the borrow out of AL-DL - which is what
- "lexicographic" means for characters. The tiebreak re-reads the lengths
- from the arguments rather than keeping them in registers across the loop,
- so CX is free to be LOOP's count, and the empty string is the case where
- that count is 0 and LOOP would run 65536 times instead of none. *)
- BEGIN
- M ("cmpstr") ;
- PushBp ; MovBpSp ;
- PushBx ; PushCx ; PushDx ; (* AX is NOT saved: it is the result *)
- XorCxCx ; (* CX := 0, to make the CL load below a length *)
- XorDxDx ;
- MovBxBp6 ; (* BX := the first (left) string's address *)
- MovClBx ; (* CL := lenL *)
- MovAxBp4 ; MovSiAx ; (* SI := the second (right) string's address *)
- MovDlSi ; (* DL := lenR *)
- CmpCxDx ; (* CMP lenL, lenR *)
- Jbe8 ("cs_min") ; (* lenL <= lenR: CX is already the shorter *)
- MovCxDx ; (* CX := lenR *)
- M ("cs_min") ; (* CX = min (lenL,lenR) *)
- Jcxz8 ("cs_len") ; (* an empty operand: no bytes to compare *)
- IncBx ; IncSi ; (* past the length byte, to the characters *)
- M ("cs_loop") ;
- LdAlBx ; (* AL := left[i] *)
- MovDlSi ; (* DL := right[i] *)
- CmpAlDl ; (* CMP AL,DL - unsigned, so CF is the order *)
- Jne8 ("cs_flag") ; (* the first difference decides *)
- IncBx ; IncSi ;
- Loop8 ("cs_loop") ;
- M ("cs_len") ; (* equal over the common prefix: lengths decide *)
- MovBxBp6 ; MovClBx ; (* CX := lenL again - BX walked past it *)
- MovAxBp4 ; MovSiAx ;
- XorDxDx ; MovDlSi ; (* DX := lenR *)
- CmpCxDx ;
- M ("cs_flag") ; (* flags are (left - right): CF=1 less, ZF=1 equal *)
- MovAlD (0) ; MovAh (0) ; (* AX := 0. MOV leaves the flags alone, which
- is what lets the two tests below still read
- the comparison. *)
- Jb8 ("cs_less") ;
- Je8 ("cs_done") ;
- MovAlD (1) ; (* greater *)
- J8 ("cs_done") ;
- M ("cs_less") ;
- MovAlD (0FFH) ; MovAh (0FFH) ; (* AX := -1 *)
- M ("cs_done") ;
- PopDx ; PopCx ; PopBx ; (* but NOT AX: AX is the RESULT. The first
- version pushed AX with the others and
- popped it back here, which restored the
- CALLER's AX - for `a < b' that is the
- address of the RIGHT operand, still in AX
- from its LEA - and every ordering test read
- a positive number. `<', `<=', `=', `<>',
- `>', `>=' over any left<right string came
- back FALSE TRUE FALSE FALSE TRUE TRUE,
- independently confirmed on qemu. Saving AX
- at all is wrong: there is nothing to keep. *)
- MovSpBp ; PopBp ; RetR
- END EmitCmpStr ;
- PROCEDURE EmitStrCat ;
- (* strcat: dest := dest + src, TPSRC2's xconcat (963) with the two strings
- taken as ADDRESSES rather than pushed by value.
- The caller pushes the SOURCE first and the DESTINATION last, sets CX = the
- destination's capacity in characters, calls, and pops both words (caller
- pops). That is exactly strstore's argument shape, so the compiler's two
- emitted calls for `a + b' differ only in which entry they name - see
- Compiler.StrConcat.
- TP3's xconcat adds the two length bytes (`ADD.B DL,CL') and takes the CARRY
- as "string too long" (`JB concerr'), then concerr loads runerr's code 16;
- the copy itself is the same append this does. So an overflowing
- concatenation is a RUNTIME ERROR, not a silent truncation: if dest[0] +
- src[0] exceeds CX the process leaves through INT 21h AH=4Ch with exit code
- 16 (10H), which is where TP3's error 16 ends up. (strstore truncates
- instead, which is also what TP3 does there - the two entries deliberately
- do not agree, because TP3's two routines do not.)
- AX, BX, CX, DX, SI and DI are all preserved: the compiler may hold a string
- element's address in DI across the call that pushes the operand AFTER this
- one (see Compiler.EmPushStrOperand), so strcat cannot be the entry that
- quietly clobbers it. *)
- BEGIN
- M ("strcat") ;
- PushBp ; MovBpSp ;
- PushAx ; PushBx ; PushCx ; PushDx ; PushSi ; PushDi ;
- MovAxBp4 ; MovSiAx ; (* SI := dest *)
- MovBxBp6 ; (* BX := src *)
- XorDxDx ;
- MovDlSi ; (* DL := dest[0], the destination length *)
- XorAxAx ;
- LdAlBx ; (* AL := src[0]; AX is now the source length *)
- AddDxAx ; (* DX := dest length + source length *)
- CmpCxDx ; (* CMP CX (max), DX (total) *)
- Jae8 ("sc_ok") ; (* max >= total: it fits *)
- MovAlD (10H) ; (* 10H = 16: TP3's `String too long' error code *)
- MovAh (4CH) ; (* AH = 4Ch: terminate *)
- Int21 ; (* leaving now; the caller's stack is abandoned *)
- M ("sc_ok") ;
- MovCxDx ; (* CX := the new length *)
- StSiCl ; (* dest[0] := new length (CL) *)
- SubDxAx ; (* DX := total - source length = old dest length *)
- MovDiSi ; (* DI := dest *)
- IncDi ; (* -> first character *)
- AddDiDx ; (* -> the append position, dest+1+old length *)
- IncBx ; (* BX -> the source's first character *)
- MovCxAx ; (* CX := source length, LOOP's count *)
- Jcxz8 ("sc_done") ; (* an empty source appends nothing *)
- M ("sc_loop") ;
- LdAlBx ; (* AL := src char *)
- StDiAl ; (* dest char := AL *)
- IncBx ; IncDi ;
- Loop8 ("sc_loop") ;
- M ("sc_done") ;
- PopDi ; PopSi ; PopDx ; PopCx ; PopBx ; PopAx ;
- MovSpBp ; PopBp ; RetR
- END EmitStrCat ;
- PROCEDURE Str5 (s : ARRAY OF CHAR ; n : CARDINAL) ;
- (* emit n characters, so the strings below are visibly the same length as the
- D_ constants claim and an off-by-one in either place is a compile-time
- mismatch rather than a wrong pointer nobody notices *)
- VAR i : CARDINAL ;
- BEGIN
- i := 0 ;
- WHILE i < n DO
- B (ORD (s [i])) ;
- INC (i)
- END
- END Str5 ;
- PROCEDURE EmitRealFromInt ;
- (* intreal (TPSRC3 130): an INTEGER -> the 6-byte MBF real.
- ABI: the source word's ADDRESS is at [BP+6] and the destination's at
- [BP+4]; the caller pushes the source first, so [BP+6] is the source. (The
- reference keeps the real in AX/BX/DX, where AL is the exponent byte, AH
- and BX the low 39 mantissa bits and DX the high 16 with the sign in DH's
- bit 7. Packing is therefore just the two stores.)
- The arithmetic is the reference's, instruction for instruction:
- zero -> all six bytes zero
- sign -> BH, DX := |value|
- AX := 0090h (exponent 144, clear mantissa)
- small value: exponent 88h and shift the mantissa 8
- normalise: shift DX left and decrement AL until DX's sign bit is set
- positive: clear DH bit 7; negative: leave it as the sign
- clear the rest of the mantissa (BX)
- The sign convention here is the packed one: bit 7 of the last byte is the
- sign, and the implicit 1 is not stored. That is why intreal leaves DX's
- top bit alone for negatives (it becomes the sign) and clears it for
- positives. *)
- BEGIN
- M ("real_from_int") ;
- PushBp ; MovBpSp ;
- PushBx ; PushDx ; PushSi ; PushDi ;
- MovSiBp6 ; (* SI := source word *)
- LdAxSi ; (* AX := the integer *)
- Call ("rf_core") ;
- M ("rf_store") ;
- MovDiBp4 ; (* DI := the destination *)
- StDiAx ; (* bytes 0..1 *)
- StDiOffBx (2) ; (* bytes 2..3 *)
- StDiOffDx (4) ; (* bytes 4..5, sign in DH bit 7 *)
- PopDi ; PopSi ; PopDx ; PopBx ;
- MovSpBp ; PopBp ; RetR ;
- (* ---- the callable integer -> real core: AX in, AX/BX/DX out ---- *)
- M ("rf_core") ;
- CmpAx0 ;
- Jne8 ("rf_nz") ;
- XorBxBx ;
- XorDxDx ;
- Jmp8 ("rf_core_ret") ;
- M ("rf_nz") ;
- MovBhAh ; (* BH := the integer's sign byte *)
- MovDxAx ;
- CmpDx0 ;
- Jns8 ("rf_pos") ;
- NegDx ;
- M ("rf_pos") ;
- MovAxImm (0090H) ; (* AL = exponent 144, AH = 0 *)
- CmpDh0 ;
- Jne8 ("rf_16") ;
- MovAlD (088H) ; (* 16-bit value in the top byte: bias 88 *)
- XchgDlDh ;
- M ("rf_16") ;
- CmpDx0 ;
- Js8 ("rf_done") ;
- M ("rf_nrm") ;
- DecAl ;
- ShlDx1 ;
- Jns8 ("rf_nrm") ;
- M ("rf_done") ;
- CmpBh0 ;
- Js8 ("rf_pos2") ;
- AndDhImm (7FH) ; (* positive: bit 7 of DH is not a sign *)
- M ("rf_pos2") ;
- XorBxBx ; (* only DX's 16 bits carry the value now *)
- M ("rf_core_ret") ;
- RetR
- END EmitRealFromInt ;
- PROCEDURE EmitRealAddSub ;
- (* readd/resub, TPSRC2 1421-1530, transcribed.
- ABI: [BP+4] is the address of a 12-byte block holding a (6 bytes) then b
- (6 bytes); the result overwrites a. Both operands are read before the
- result is written, so the in-place destination is safe. The reference
- keeps a in AX/BX/DX and b in CX/SI/DI, with AL the exponent, AH+BX the low
- mantissa and DX the high 16 carrying the sign in DH bit 7. The scratch
- bytes are the reference's own (resign/resave, remant/remant1, cvdecexp).
- The sign of b is toggled for subtraction with XOR DI,resign, exactly as
- resub does, and the rest is one shared core. An exponent overflow leaves
- through the reference's STC + RET; no caller consumes that yet. *)
- BEGIN
- M ("real_add") ;
- PushBp ; MovBpSp ;
- PushBx ; PushDx ; PushSi ; PushDi ;
- PushBp4 ; (* keep the block address for the store *)
- MovSiBp4 ;
- LdAxSi ;
- LdCxSiOff (2) ; LdDxSiOff (4) ;
- MovBxCx ;
- MovDiSi ;
- LdCxDiOff (6) ; LdSiDiOff (8) ; LdDiDiOff (10) ;
- StDataImm (D_RSIGN, 0) ;
- Call ("rd_core") ;
- PopDi ;
- StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
- PopDi ; PopSi ; PopDx ; PopBx ;
- MovSpBp ; PopBp ; RetR ;
- M ("real_sub") ;
- PushBp ; MovBpSp ;
- PushBx ; PushDx ; PushSi ; PushDi ;
- PushBp4 ;
- MovSiBp4 ;
- LdAxSi ;
- LdCxSiOff (2) ; LdDxSiOff (4) ;
- MovBxCx ;
- MovDiSi ;
- LdCxDiOff (6) ; LdSiDiOff (8) ; LdDiDiOff (10) ;
- StDataImm (D_RSIGN, 8000H) ;
- Call ("rd_core") ;
- PopDi ;
- StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
- PopDi ; PopSi ; PopDx ; PopBx ;
- MovSpBp ; PopBp ; RetR ;
- M ("rd_core") ;
- CmpCl0 ;
- Je8 ("raret") ;
- XorDiData (D_RSIGN) ;
- CmpAl0 ;
- Jne8 ("ranotriv") ;
- M ("raretn2") ;
- MovAxCx ; MovBxSi ; MovDxDi ;
- M ("raret") ;
- RetR ;
- M ("ranotriv") ;
- CmpAlCl ;
- Jbe8 ("ranoswap") ;
- XchgAxCx ; XchgBxSi ; XchgDxDi ;
- M ("ranoswap") ;
- StDataCl (D_CVDEC) ;
- SubClAl ;
- CmpClImm (28H) ;
- Jb8 ("ranoundr") ;
- LdClData (D_CVDEC) ;
- Jmp8 ("raretn2") ;
- M ("ranoundr") ;
- StDataDi (D_RSIGN) ; (* resign; D_RESAVE is its high byte *)
- AndDataImm (D_RESAVE, 80H) ;
- StDataDi (D_RMANT) ;
- XorDataDh (D_RMANT1) ; (* sign1 XOR sign2 in the high byte *)
- OrDiImm (8000H) ; (* b's hidden 1 *)
- OrDhImm (80H) ; (* a's hidden 1 *)
- M ("raadj16l") ;
- CmpClImm (10H) ;
- Jb8 ("raadj8") ;
- MovAhBh ; MovBxDx ; XorDxDx ; SubClImm (10H) ;
- Jmp8 ("raadj16l") ;
- M ("raadj8") ;
- CmpClImm (08H) ;
- Jb8 ("raadj8l") ;
- MovAhBl ; MovBlBh ; MovBhDl ; MovDlDh ; XorDhDh ; SubClImm (08H) ;
- M ("raadj8l") ;
- CmpCl0 ;
- Je8 ("raadjend") ;
- M ("raadj1l") ;
- ShrDx1 ; RcrBx1 ; RcrAh1 ; DecCl ; Jne8 ("raadj1l") ;
- M ("raadjend") ;
- LdAlData (D_CVDEC) ;
- TestDataImm (D_RMANT1, 80H) ;
- Jne8 ("radosub") ;
- AddAhCh ; AdcBxSi ; AdcDxDi ;
- Jae8 ("rasign") ;
- RcrDx1 ; RcrBx1 ; RcrAh1 ; IncAl ; Jne8 ("rasign") ;
- Stc ; RetR ;
- M ("radosub") ;
- XchgAhCh ; XchgBxSi ; XchgDxDi ;
- SubAhCh ; SbbBxSi ; SbbDxDi ;
- Jae8 ("ranoneg") ;
- XorDataImm (D_RESAVE, 80H) ;
- NotAh ; NotBx ; NotDx ;
- AddAhImm (1) ; AdcBxImm (0) ; AdcDxImm (0) ;
- M ("ranoneg") ;
- MovClImm (5) ;
- M ("ranrm8l") ;
- CmpDh0 ;
- Jne8 ("ranrm1l") ;
- MovDhDl ; MovDlBh ; MovBhBl ; MovBlAh ; XorAhAh ;
- SubAl (08H) ;
- Jb8 ("razero") ;
- DecCl ;
- Jne8 ("ranrm8l") ;
- Jmp8 ("razero") ;
- M ("ranrm1l") ;
- TestDhImm (80H) ;
- Jne8 ("rasign") ;
- ShlAh1 ; RclBx1 ; RclDx1 ; DecAl ; Jne8 ("ranrm1l") ;
- M ("razero") ;
- XorAxAx ; XorBxBx ; XorDxDx ;
- RetR ;
- M ("rasign") ;
- AndDhImm (7FH) ;
- XorDhData (D_RESAVE) ;
- RetR
- END EmitRealAddSub ;
- PROCEDURE EmitRealMul ;
- (* remult, TPSRC2 1532-1587, with testexp (TPSRC3 50-78) inlined.
- ABI and register convention are real_add's: [BP+4] is a 12-byte block
- holding a then b; the result overwrites a; a is AX/BX/DX and b CX/SI/DI.
- testexp is inlined because the reference's version is called and, on
- underflow or overflow, POPS ITS RETURN ADDRESS to leave remult entirely.
- That trick cannot survive being called from our wrapper, so the two
- exponent fault paths jump to this core's zero/error exits instead.
- Multiplication is the reference's shift-and-add over the five mantissa
- bytes: for each byte, if it is zero the 40-bit result is shifted right
- eight; otherwise each of its eight bits conditionally adds a's mantissa
- and shifts the result one. LAHF/SAHF carry the guard bit across the
- normalisation TEST. Overflow leaves through TP3's error 1 (exit code 1);
- underflow returns zero. *)
- BEGIN
- M ("real_mul") ;
- PushBp ; MovBpSp ;
- PushBx ; PushDx ; PushSi ; PushDi ;
- PushBp4 ;
- MovSiBp4 ;
- LdAxSi ;
- LdCxSiOff (2) ; LdDxSiOff (4) ;
- MovBxCx ;
- MovDiSi ;
- LdCxDiOff (6) ; LdSiDiOff (8) ; LdDiDiOff (10) ;
- Call ("rm_core") ;
- PopDi ;
- StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
- PopDi ; PopSi ; PopDx ; PopBx ;
- MovSpBp ; PopBp ; RetR ;
- M ("rm_core") ;
- CmpCl0 ; JccNear (74H, "rm_zero") ;
- CmpAl0 ; JccNear (74H, "rm_ret") ;
- AddAlCl ;
- Jb8 ("rm_ovr1") ;
- AddAlImm (80H) ;
- Jb8 ("rm_ok") ;
- JmpNear ("rm_zero") ; (* underflow *)
- M ("rm_ovr1") ;
- AddAlImm (80H) ;
- Jae8 ("rm_ok") ;
- (* overflow: TP3 error 1 *)
- MovAlD (1) ; MovAh (4CH) ; Int21 ;
- M ("rm_ok") ;
- (* testexp's tail *)
- StDataCx (D_RMUL21) ;
- MovCxDx ; XorCxDi ; NotCh ; AndChImm (80H) ; StDataCh (D_RESAVE) ;
- OrDhImm (80H) ; OrDiImm (8000H) ;
- StDataSi (D_RMUL22) ; StDataDi (D_RMUL23) ;
- (* remult *)
- StDataAx (D_RMUL11) ; StDataBx (D_RMUL12) ; StDataDx (D_RMUL13) ;
- XorAhAh ; XorBxBx ; XorDxDx ;
- MovDiData (D_RMUL21) ;
- MovClImm (5) ;
- M ("rm_byt") ;
- IncDi ;
- LdChDiOff ;
- CmpCh0 ; Jne8 ("rm_domul") ;
- MovAhBl ; MovBlBh ; MovBhDl ; MovDlDh ; XorDhDh ;
- Jmp8 ("rm_nxt") ;
- M ("rm_domul") ;
- MovSiImm (8) ;
- M ("rm_mul") ;
- RcrCh1 ;
- Jae8 ("rm_bit0") ;
- AddAhData (D_RMUL11 + 1) ;
- AdcBxData (D_RMUL12) ;
- AdcDxData (D_RMUL13) ;
- M ("rm_bit0") ;
- RcrDx1 ; RcrBx1 ; RcrAh1 ;
- DecSi ; Jne8 ("rm_mul") ;
- M ("rm_nxt") ;
- DecCl ; Jne8 ("rm_byt") ;
- XchgAxCx ;
- Lahf ;
- TestDhImm (80H) ; Jne8 ("rm_noadj") ;
- Sahf ;
- RclCh1 ; RclBx1 ; RclDx1 ;
- CmpCl0 ; Je8 ("rm_noadj") ;
- DecCl ;
- M ("rm_noadj") ;
- XchgAxCx ;
- XorDhData (D_RESAVE) ;
- CmpAl0 ; Jne8 ("rm_ret") ;
- M ("rm_zero") ;
- XorAxAx ; XorBxBx ; XorDxDx ;
- M ("rm_ret") ;
- RetR ;
- M ("real_div") ;
- PushBp ; MovBpSp ;
- PushBx ; PushDx ; PushSi ; PushDi ;
- PushBp4 ;
- MovSiBp4 ;
- LdAxSi ;
- LdCxSiOff (2) ; LdDxSiOff (4) ;
- MovBxCx ;
- MovDiSi ;
- LdCxDiOff (6) ; LdSiDiOff (8) ; LdDiDiOff (10) ;
- Call ("rv_core") ;
- PopDi ;
- StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
- PopDi ; PopSi ; PopDx ; PopBx ;
- MovSpBp ; PopBp ; RetR ;
- M ("rv_core") ;
- CmpAl0 ; JccNear (74H, "rm_ret") ;
- SubAlCl ;
- Cmc ;
- Jb8 ("rd_ovr1") ;
- AddAlImm (80H) ;
- Jb8 ("rd_okx") ;
- JmpNear ("rm_zero") ; (* underflow *)
- M ("rd_ovr1") ;
- AddAlImm (80H) ;
- Jae8 ("rd_okx") ;
- MovAlD (1) ; MovAh (4CH) ; Int21 ;
- M ("rd_okx") ;
- (* testexp's tail *)
- StDataCx (D_RMUL21) ;
- MovCxDx ; XorCxDi ; NotCh ; AndChImm (80H) ; StDataCh (D_RESAVE) ;
- OrDhImm (80H) ; OrDiImm (8000H) ;
- StDataSi (D_RMUL22) ; StDataDi (D_RMUL23) ;
- (* rediv *)
- StDataAl (D_RMUL11) ;
- MovDiData (D_RMUL21 - 1) ; (* remul21b: one before the mantissa *)
- MovClImm (5) ;
- MovSiImm (8) ;
- M ("rd_loop") ;
- CmpDxData (D_RMUL23) ; Jne8 ("rd_cmp") ;
- CmpBxData (D_RMUL22) ; Jne8 ("rd_cmp") ;
- CmpAhData (D_RMUL21 + 1) ;
- M ("rd_cmp") ;
- Jb8 ("rd_shft") ;
- SubAhData (D_RMUL21 + 1) ; SbbBxData (D_RMUL22) ; SbbDxData (D_RMUL23) ;
- M ("rd_shft") ;
- Cmc ;
- RclCh1 ;
- DecSi ; Jne8 ("rd_bit") ;
- StDiCh ;
- DecCl ; Je8 ("rd_last") ;
- DecDi ;
- MovSiImm (8) ;
- M ("rd_bit") ;
- ShlAh1 ; RclBx1 ; RclDx1 ;
- Jae8 ("rd_loop") ;
- SubAhData (D_RMUL21 + 1) ; SbbBxData (D_RMUL22) ; SbbDxData (D_RMUL23) ;
- Clc ;
- Jmp8 ("rd_shft") ;
- M ("rd_last") ;
- ShlAh1 ; RclBx1 ; RclDx1 ;
- Jb8 ("rd_shft2") ;
- CmpDxData (D_RMUL23) ; Jne8 ("rd_cmp2") ;
- CmpBxData (D_RMUL22) ; Jne8 ("rd_cmp2") ;
- CmpAhData (D_RMUL21 + 1) ;
- M ("rd_cmp2") ;
- Cmc ;
- M ("rd_shft2") ;
- MovCxData (D_RMUL11) ; MovBxData (D_RMUL12) ; MovDxData (D_RMUL13) ;
- Lahf ;
- TestDhImm (80H) ; Jne8 ("rd_nrm") ;
- Sahf ;
- RclCh1 ; RclBx1 ; RclDx1 ;
- Jmp8 ("rd_ok") ;
- M ("rd_nrm") ;
- IncCl ;
- Jne8 ("rd_ok") ;
- MovAlD (1) ; MovAh (4CH) ; Int21 ;
- M ("rd_ok") ;
- JmpNear ("rm_noadj")
- END EmitRealMul ;
- PROCEDURE EmitRealCmp ;
- (* recmp: compare two packed 6-byte MBF reals.
- ABI: [BP+4] points at a 12-byte block holding a then b (the same block the
- four arithmetic entries take). Nothing is written; the result is AX =
- -1/0/+1 for a<b, a=b, a>b.
- TP3's recmp leaves the flags set and its six wrappers branch on them. Here
- the caller is our compiler, which already has one comparison arm for reals
- and strings: EmSetcc over AX. cmpstr returns the same -1/0/+1, so the two
- cannot drift. The reference's core is kept intact; only the final
- flag-to-AX step is new, and it is cmpstr's: MOV AX,0 leaves the flags alone,
- then JB/JE/MOV decide.
- The comparison is on the packed representation: the sign is bit 7 of each
- high byte, the magnitude is exponent then DX/BX/AH (TP3's 40-bit order), and
- a negative pair inverts the magnitude result. Different signs are decided
- by RCL on a's high word, which shifts the sign into CF - exactly TPSRC's
- trick, and it leaves ZF clear because the XOR found the signs different. *)
- BEGIN
- M ("real_cmp") ;
- PushBp ; MovBpSp ;
- PushBx ; PushDx ; PushSi ; PushDi ;
- MovSiBp4 ;
- LdAxSi ;
- LdCxSiOff (2) ; LdDxSiOff (4) ;
- MovBxCx ;
- MovDiSi ;
- LdCxDiOff (6) ; LdSiDiOff (8) ; LdDiDiOff (10) ;
- (* recmp core *)
- PushDx ; XorDxDi ; PopDx ;
- Jns8 ("rc_sign") ;
- PushDx ; RclDx1 ; PopDx ;
- Jmp8 ("rc_flags") ;
- M ("rc_sign") ;
- TestDhImm (80H) ;
- Je8 ("rc_pos") ;
- Call ("rc_num") ;
- Je8 ("rc_flags") ;
- Cmc ;
- Jmp8 ("rc_flags") ;
- M ("rc_pos") ;
- Call ("rc_num") ;
- M ("rc_flags") ;
- MovAlD (0) ; MovAh (0) ; (* AX := 0; MOV leaves the flags alone *)
- Jb8 ("rc_less") ;
- Je8 ("rc_done") ;
- MovAlD (1) ;
- Jmp8 ("rc_done") ;
- M ("rc_less") ;
- MovAlD (0FFH) ; MovAh (0FFH) ;
- M ("rc_done") ;
- PopDi ; PopSi ; PopDx ; PopBx ;
- MovSpBp ; PopBp ; RetR ;
- (* the shared magnitude compare; CALL/RET do not touch the flags *)
- M ("rc_num") ;
- CmpAlCl ; Jne8 ("rc_num_ret") ;
- CmpAl0 ; Je8 ("rc_num_ret") ;
- CmpDxDi ; Jne8 ("rc_num_ret") ;
- CmpBxSi ; Jne8 ("rc_num_ret") ;
- CmpAhCh ;
- M ("rc_num_ret") ;
- RetR
- END EmitRealCmp ;
- PROCEDURE EmitRealInt ;
- (* reint: Int(real), the real-valued integer part, in place.
- ABI: [BP+4] is the address of a 6-byte packed MBF real; the result
- overwrites it. TPSRC3's reint works directly on the packed bytes: it builds
- a mask by shifting 1s right by (exponent - 129) bits and ANDs it with the
- mantissa, which clears the fraction while keeping the sign bit and the
- implicit leading 1. Exponents >= 0A8h are already integral and returned
- unchanged; exponents <= 80h give zero.
- The 16/8-bit fast paths are the reference's, including the byte moves that
- shift the mask instead of running the bit loop. Only the wrapper is ours
- (the caller passes an address rather than the value in registers). *)
- BEGIN
- M ("real_int") ;
- PushBp ; MovBpSp ;
- PushBx ; PushDx ; PushSi ; PushDi ;
- PushBp4 ;
- MovSiBp4 ;
- LdAxSi ;
- LdCxSiOff (2) ; LdDxSiOff (4) ;
- MovBxCx ;
- (* reint core *)
- CmpAl (0A8H) ;
- Jae8 ("ri_store") ; (* already integral: return as-is *)
- MovCxAx ; MovSiBx ; MovDiDx ; (* save the packed mantissa for the mask *)
- XorAhAh ; XorBxBx ; XorDxDx ; (* build the mask in AH/BX/DX *)
- SubClImm (80H) ; (* CL := exponent - 129 + 1 *)
- Jbe8 ("ri_zero") ;
- M ("ri_16") ;
- CmpClImm (10H) ; Jb8 ("ri_8") ;
- MovAhBh ; MovBxDx ; MovDxImm (0FFFFH) ;
- SubClImm (10H) ; Jmp8 ("ri_16") ;
- M ("ri_8") ;
- CmpClImm (08H) ; Jb8 ("ri_1") ;
- MovAhBl ; MovBlBh ; MovBhDl ; MovDlDh ; MovDhImm (0FFH) ;
- SubClImm (08H) ; Jmp8 ("ri_8") ;
- M ("ri_1") ;
- CmpCl0 ; Je8 ("ri_and") ;
- M ("ri_1l") ;
- Stc ; RcrDx1 ; RcrBx1 ; RcrAh1 ; DecCl ; Jne8 ("ri_1l") ;
- M ("ri_and") ;
- AndDxDi ; AndBxSi ; AndAhCh ;
- M ("ri_store") ;
- PopDi ;
- StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
- PopDi ; PopSi ; PopDx ; PopBx ;
- MovSpBp ; PopBp ; RetR ;
- M ("ri_zero") ;
- MovAlD (0) ;
- Jmp8 ("ri_store")
- END EmitRealInt ;
- PROCEDURE EmitRealConv ;
- (* fmtreal / ascreal: the decimal<->binary conversions, TPSRC3 880-1250.
- The cores are transcribed with their reference register conventions:
- m10 multiplies AX/BX/DX by ten in place; rrange multiplies by 10**CL
- through the fltdec table and the remult/rediv cores; rdec turns a packed
- real into twelve decimal digits in D_CVOUT and returns the sign in CH and
- the decimal exponent in CL; round and fdig* are the reference's helpers.
- They all use CALL/RET internally, so a caller's flags survive where the
- reference relies on that (the digit loop's CH flags are pushed explicitly
- because rd_core uses CX for its second operand).
- Two entries:
- real_fmt (28): [BP+4] real ptr, [BP+6] destination; CX = width in CL
- and decimals in CH. Writes a length-prefixed string
- (dest[0] = length) padded on the LEFT to width, and
- returns the length in AX. DEC >= 25 selects the
- reference's scientific mode and derives the number of
- fraction digits from the width.
- real_val (29): [BP+4] ASCIIZ source, [BP+6] destination real. Returns
- AX = the offset of the first character not consumed, or
- FFFFh on a hard error (in which case the destination is
- untouched). A trailing non-digit is NOT an error; the
- offset tells Val where to report it, as TPSRC's ascreal
- returns BX = end. *)
- BEGIN
- (* ---------------------------------------------------------- real_fmt *)
- M ("real_fmt") ;
- PushBp ; MovBpSp ;
- PushBx ; PushDx ; PushSi ; PushDi ;
- MovDiBp4 ; (* DI := the real *)
- MovDlCh ; (* DL := decimals *)
- MovChImm (0) ; (* CX := width *)
- MovDhImm (0) ;
- PushCx ; (* keep the width across rdec *)
- MovBxDataAddr (D_FMTBUF) ;
- Call ("rc_fmt") ;
- PopCx ;
- MovAxBx ; (* AX := end of the formatted text *)
- MovBxDataAddr (D_FMTBUF) ;
- SubAxBx ; (* AX := its length *)
- StDataAx (D_FMTLEN) ;
- StDataCx (D_FMTW) ;
- (* pad := max (width - length, 0) *)
- MovAxData (D_FMTW) ;
- MovDxData (D_FMTLEN) ;
- SubAxDx ;
- CmpAx0 ;
- Jge8 ("rc_fpadok") ;
- XorAxAx ;
- M ("rc_fpadok") ;
- StDataAx (D_FMTPAD) ;
- MovDxData (D_FMTLEN) ;
- AddAxDx ; (* AX := pad + length *)
- CmpAxImm (0FFH) ; (* a Pascal string's length is one byte *)
- JccNear (76H, "rc_ftotok") ;
- MovAxImm (0FFH) ;
- M ("rc_ftotok") ;
- StDataAx (D_FMTTOT) ;
- (* dest[0] := length; then pad spaces, then the number *)
- MovDxBp6 ; MovDiDx ;
- StDiAl ;
- IncDi ;
- MovCxData (D_FMTPAD) ;
- Jcxz8 ("rc_fpad_done") ;
- M ("rc_fpad_loop") ;
- MovAlD (20H) ; StDiAl ; IncDi ; Loop8 ("rc_fpad_loop") ;
- M ("rc_fpad_done") ;
- MovSiData (D_FMTBUF) ;
- MovCxData (D_FMTLEN) ;
- Jcxz8 ("rc_fcopy_done") ;
- M ("rc_fcopy_loop") ;
- LdAlSi ; StDiAl ; IncSi ; IncDi ; Loop8 ("rc_fcopy_loop") ;
- M ("rc_fcopy_done") ;
- MovAxData (D_FMTTOT) ;
- PopDi ; PopSi ; PopDx ; PopBx ;
- MovSpBp ; PopBp ; RetR ;
- (* ---------------------------------------------------------- real_val *)
- M ("real_val") ;
- PushBp ; MovBpSp ;
- PushBx ; PushDx ; PushSi ; PushDi ;
- MovBxBp4 ;
- MovDiBp6 ;
- StDataBx (D_ASCSRC) ;
- Call ("rc_asc") ;
- JccNear (72H, "rc_averr") ;
- MovAxBx ; (* AX := end pointer *)
- MovDxData (D_ASCSRC) ;
- SubAxDx ; (* AX := consumed length *)
- JmpNear ("rc_avdone") ;
- M ("rc_averr") ;
- MovAxImm (0FFFFH) ;
- M ("rc_avdone") ;
- PopDi ; PopSi ; PopDx ; PopBx ;
- MovSpBp ; PopBp ; RetR ;
- (* ---------------------------------------------------------- m10 *)
- (* AX/BX/DX := value * 10; CF = overflow; CX is preserved. The reference
- multiplies by 1.25 (add the /4 value) and then by 8 via the exponent. *)
- M ("rc_m10") ;
- CmpAl0 ;
- JccNear (75H, "rc_m10nz") ;
- RetR ;
- M ("rc_m10nz") ;
- OrDhImm (80H) ;
- PushCx ; PushDx ; PushBx ; PushAx ;
- ShrDx1 ; RcrBx1 ; RcrAh1 ;
- ShrDx1 ; RcrBx1 ; RcrAh1 ;
- PopCx ; AddAhCh ;
- PopCx ; AdcBxCx ;
- PopCx ; AdcDxCx ;
- PopCx ;
- JccNear (73H, "rc_m10nrm") ;
- RcrDx1 ; RcrBx1 ; RcrAh1 ;
- IncAl ;
- JccNear (75H, "rc_m10nrm") ;
- Stc ; RetR ;
- M ("rc_m10nrm") ;
- AndDhImm (7FH) ;
- AddAlImm (3) ;
- RetR ;
- (* -------------------------------------------------------- rrange *)
- (* AX/BX/DX := value * 10**CL, signed CL, via the fltdec table. *)
- M ("rc_range") ;
- PushDx ; PushBx ; PushAx ;
- StDataCl (D_CVDECEXP) ;
- CmpCl0 ;
- JccNear (79H, "rc_rpos") ;
- NegCl ;
- M ("rc_rpos") ;
- MovBlCl ;
- AndBlImm (0FCH) ;
- MovBhBl ;
- ShrBl1 ;
- AddBlBh ;
- XorBhBh ;
- LeaDiBxData (D_FLTDEC) ;
- LdAxDi ; LdBxDiOff (2) ; LdDxDiOff (4) ;
- AndClImm (3) ;
- JccNear (74H, "rc_rdone") ;
- M ("rc_rmul") ;
- Call ("rc_m10") ;
- DecCl ;
- JccNear (75H, "rc_rmul") ;
- M ("rc_rdone") ;
- MovCxAx ; MovSiBx ; MovDiDx ;
- PopAx ; PopBx ; PopDx ;
- TestDataImm (D_CVDECEXP, 80H) ;
- JccNear (75H, "rc_rdiv") ;
- JmpNear ("rm_core") ;
- M ("rc_rdiv") ;
- JmpNear ("rv_core") ;
- (* ------------------------------------------------------- rangem *)
- (* the signed range check around rrange: |CL| <= 38 *)
- M ("rc_rangem") ;
- CmpClImm (0DAH) ; JccNear (7CH, "rc_rerr") ;
- CmpClImm (26H) ; JccNear (7FH, "rc_rerr") ;
- PushCx ; PushSi ; PushDi ;
- Call ("rc_range") ;
- PopDi ; PopSi ; PopCx ;
- RetR ;
- M ("rc_rerr") ;
- Stc ; RetR ;
- (* ---------------------------------------------------------- rdec *)
- (* DI -> real. D_CVOUT gets twelve digits; CH = sign, CL = decimal
- exponent. The estimate is 77/256 * (exponent - 128), TPSRC's
- log10(2) approximation. *)
- M ("rc_rdec") ;
- LdAxDi ; LdBxDiOff (2) ; LdDxDiOff (4) ;
- CmpAl0 ;
- JccNear (75H, "rc_rdno0") ;
- MovSiData (D_CVOUT) ;
- M ("rc_rdfill") ;
- StSiImm (3030H) ;
- IncSi ; IncSi ;
- CmpSiData (D_CVOUT + 12) ;
- JccNear (75H, "rc_rdfill") ;
- MovCxImm (0) ;
- RetR ;
- M ("rc_rdno0") ;
- MovChDh ;
- AndDhImm (7FH) ;
- PushAx ; PushDx ;
- SubAl (80H) ; Cbw ; MovDxImm (004DH) ; ImulDx ; AddAxImm (5) ;
- MovClAh ;
- PopDx ; PopAx ;
- CmpClImm (0D9H) ;
- JccNear (75H, "rc_rdnocomp") ;
- IncCl ;
- M ("rc_rdnocomp") ;
- PushCx ;
- NegCl ;
- Call ("rc_range") ;
- PopCx ;
- CmpAl (81H) ;
- JccNear (73H, "rc_rdnoadj") ;
- Call ("rc_m10") ;
- DecCl ;
- M ("rc_rdnoadj") ;
- PushCx ;
- OrDhImm (80H) ;
- MovClImm (84H) ;
- SubClAl ;
- MovAlD (0) ;
- CmpCl0 ;
- JccNear (74H, "rc_rdnosh") ;
- M ("rc_rdshift") ;
- ShrDx1 ; RcrBx1 ; RcrAh1 ;
- DecCl ;
- JccNear (75H, "rc_rdshift") ;
- M ("rc_rdnosh") ;
- MovSiData (D_CVOUT) ;
- M ("rc_rdconv") ;
- MovChDh ; MovClImm (4) ; ShrChCl ; AddChImm (30H) ;
- StSiCh ;
- AndDhImm (0FH) ;
- PushDx ; PushBx ; PushAx ;
- ShlAx1 ; RclBx1 ; RclDx1 ;
- ShlAx1 ; RclBx1 ; RclDx1 ;
- PopCx ; AddAxCx ;
- PopCx ; AdcBxCx ;
- PopCx ; AdcDxCx ;
- ShlAx1 ; RclBx1 ; RclDx1 ;
- IncSi ;
- CmpSiData (D_CVOUT + 12) ;
- JccNear (75H, "rc_rdconv") ;
- PopCx ;
- RetR ;
- (* --------------------------------------------------------- round *)
- (* round the digits at index AL: look at the next digit, and carry if it is
- 5 or more. CL is the decimal exponent and is incremented on carry-out. *)
- M ("rc_round") ;
- XorAhAh ;
- MovBxDataAddr (D_CVOUT) ;
- AddBxAx ;
- CmpBxByte (35H) ;
- MovBxByte (0) ;
- JccNear (72H, "rc_rounddone") ;
- M ("rc_roundloop") ;
- DecAl ;
- JccNear (78H, "rc_roundexp") ;
- DecBx ;
- IncBxByte ;
- CmpBxByte (3AH) ;
- JccNear (72H, "rc_rounddone") ;
- MovBxByte (0) ;
- JmpNear ("rc_roundloop") ;
- M ("rc_roundexp") ;
- MovBxByte (31H) ;
- MovBxOff1Byte (0) ;
- IncCl ;
- M ("rc_rounddone") ;
- RetR ;
- (* ------------------------------------------------------------ fdig *)
- M ("rc_fdig") ;
- LdAlSi ;
- CmpAl0 ;
- JccNear (74H, "rc_fdig0") ;
- IncSi ;
- JmpNear ("rc_fdigsto") ;
- M ("rc_fdig0") ;
- MovAlD (30H) ;
- M ("rc_fdigsto") ;
- StBxAl ;
- IncBx ;
- RetR ;
- (* ---------------------------------------------------------- fcore *)
- (* DI = real, BX = destination, CX = width, DX = decimals. Writes raw
- characters (no padding) and leaves BX one past the end. *)
- M ("rc_fmt") ;
- PushBx ;
- CmpDxImm (19H) ;
- JccNear (72H, "rc_fnlim2") ;
- MovAxCx ;
- CmpCh0 ;
- JccNear (74H, "rc_flimok") ;
- TestChImm (80H) ;
- JccNear (74H, "rc_flimpos") ;
- XorAxAx ;
- JmpNear ("rc_flimok") ;
- M ("rc_flimpos") ;
- MovAxImm (0FFH) ;
- M ("rc_flimok") ;
- MovDl (7) ;
- TestDiOffImm (5, 80H) ;
- JccNear (74H, "rc_frpos") ;
- IncDl ;
- M ("rc_frpos") ;
- SubAlDl ;
- JccNear (73H, "rc_frnound") ;
- MovAlD (0) ;
- M ("rc_frnound") ;
- CmpAl (9) ;
- JccNear (72H, "rc_frnolim") ;
- MovAlD (9) ;
- M ("rc_frnolim") ;
- IncAl ;
- MovDlAl ;
- MovDhAl ;
- M ("rc_fnlim2") ;
- PushDx ;
- Call ("rc_rdec") ;
- PopDx ;
- MovAlDl ;
- IncAl ;
- CmpDh0 ;
- JccNear (75H, "rc_frround") ;
- AddAlCl ;
- JccNear (79H, "rc_frlimprc") ;
- StDataByte (D_CVOUT, 0) ;
- JmpNear ("rc_frfmtit") ;
- M ("rc_frlimprc") ;
- CmpAl (0CH) ;
- JccNear (72H, "rc_frround") ;
- MovAlD (0BH) ;
- M ("rc_frround") ;
- Call ("rc_round") ;
- M ("rc_frfmtit") ;
- PopBx ;
- MovSiData (D_CVOUT) ;
- TestChImm (80H) ;
- JccNear (74H, "rc_frpos2") ;
- MovAlD (2DH) ;
- Call ("rc_fdigsto") ;
- M ("rc_frpos2") ;
- MovChCl ;
- CmpDh0 ;
- JccNear (74H, "rc_frzer") ;
- MovChImm (0) ;
- M ("rc_frzer") ;
- CmpCh0 ;
- JccNear (79H, "rc_frint") ;
- Call ("rc_fdig0") ;
- JmpNear ("rc_frfrac0") ;
- M ("rc_frint") ;
- Call ("rc_fdig") ;
- DecCh ;
- JccNear (79H, "rc_frint") ;
- M ("rc_frfrac0") ;
- CmpDl0 ;
- JccNear (74H, "rc_frexp") ;
- MovAlD (2EH) ;
- Call ("rc_fdigsto") ;
- M ("rc_frfill0") ;
- IncCh ;
- JccNear (74H, "rc_frfrac") ;
- Call ("rc_fdig0") ;
- DecDl ;
- JccNear (75H, "rc_frfill0") ;
- M ("rc_frfrac") ;
- DecDl ;
- JccNear (78H, "rc_frexp") ;
- Call ("rc_fdig") ;
- JmpNear ("rc_frfrac") ;
- M ("rc_frexp") ;
- CmpDh0 ;
- JccNear (75H, "rc_frputexp") ;
- RetR ;
- M ("rc_frputexp") ;
- MovAlD (45H) ;
- Call ("rc_fdigsto") ;
- MovAlD (2BH) ;
- CmpCl0 ;
- JccNear (79H, "rc_frposexp") ;
- NegCl ;
- MovAlD (2DH) ;
- M ("rc_frposexp") ;
- Call ("rc_fdigsto") ;
- MovAlD (2FH) ;
- M ("rc_frexpsub") ;
- IncAl ;
- SubClImm (0AH) ;
- JccNear (73H, "rc_frexpsub") ;
- Call ("rc_fdigsto") ;
- AddClImm (3AH) ;
- MovAlCl ;
- JmpNear ("rc_fdigsto") ;
- (* ------------------------------------------------------------ asc *)
- (* BX = ASCIIZ source, DI = destination real. Returns BX = end, CF = 1 on
- a hard error. The digit add keeps the reference's stack dance: the
- digit's real goes in a and the accumulated value in b (addition is
- commutative), while the pointers and the CH flags are saved around the
- call because rd_core uses CX/SI/DI. *)
- M ("rc_asc") ;
- LdClBx ;
- CmpClImm (2DH) ;
- JccNear (75H, "rc_asign") ;
- IncBx ;
- M ("rc_asign") ;
- PushCx ;
- Call ("rc_asc2") ;
- PopCx ;
- JccNear (72H, "rc_aouterr") ;
- CmpClImm (2DH) ;
- JccNear (75H, "rc_aok") ;
- LdAlDi ;
- CmpAl0 ;
- JccNear (74H, "rc_aok") ;
- XorDiOffImm (5, 80H) ;
- M ("rc_aok") ;
- Clc ;
- RetR ;
- M ("rc_aouterr") ;
- Stc ;
- RetR ;
- M ("rc_asc2") ;
- MovSiBx ;
- XorAxAx ; XorBxBx ; XorDxDx ; XorCxCx ;
- StDataByte (D_CVEXPCNT, 0) ;
- M ("rc_aloop") ;
- LdClSi ;
- CmpClImm (61H) ; JccNear (72H, "rc_aup") ;
- CmpClImm (7AH) ; Ja8 ("rc_aup") ;
- SubClImm (20H) ;
- M ("rc_aup") ;
- Call ("rc_adigit2") ;
- JccNear (72H, "rc_anodig") ;
- Call ("rc_m10") ;
- JccNear (72H, "rc_aerr") ;
- PushDi ; PushSi ; PushCx ;
- PushDx ; PushBx ; PushAx ;
- MovAlCl ;
- XorAhAh ;
- Call ("rf_core") ;
- MovCxAx ; MovSiBx ; MovDiDx ;
- PopAx ; PopBx ; PopDx ;
- StDataImm (D_RSIGN, 0) ; (* readd, not resub: resign := 0 *)
- Call ("rd_core") ;
- PopCx ; PopSi ; PopDi ;
- TestChImm (40H) ;
- JccNear (74H, "rc_anext") ;
- DecData (D_CVEXPCNT) ;
- M ("rc_anext") ;
- IncSi ;
- JmpNear ("rc_aloop") ;
- M ("rc_anodig") ;
- CmpClImm (2EH) ;
- JccNear (75H, "rc_aexp") ;
- TestChImm (40H) ;
- JccNear (75H, "rc_aerr") ;
- OrChImm (40H) ;
- JmpNear ("rc_anext") ;
- M ("rc_aexp") ;
- CmpClImm (45H) ;
- LdClData (D_CVEXPCNT) ;
- JccNear (75H, "rc_aexp3") ;
- Call ("rc_rangem") ;
- JccNear (72H, "rc_aerr") ;
- IncSi ;
- LdClSi ;
- CmpClImm (2BH) ;
- JccNear (74H, "rc_aposexp") ;
- CmpClImm (2DH) ;
- JccNear (75H, "rc_aexp2") ;
- OrChImm (20H) ;
- M ("rc_aposexp") ;
- IncSi ;
- M ("rc_aexp2") ;
- Call ("rc_adigit") ;
- JccNear (72H, "rc_aerr") ;
- PushAx ;
- MovAlCl ;
- IncSi ;
- Call ("rc_adigit") ;
- JccNear (72H, "rc_aonedig") ;
- MovAhAl ;
- ShlAl1 ; ShlAl1 ; AddAlAh ; ShlAl1 ; AddAlCl ;
- IncSi ;
- M ("rc_aonedig") ;
- MovClAl ;
- PopAx ;
- TestChImm (20H) ;
- JccNear (74H, "rc_aexp3") ;
- NegCl ;
- M ("rc_aexp3") ;
- Call ("rc_rangem") ;
- JccNear (72H, "rc_aerr") ;
- StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
- Clc ;
- JmpNear ("rc_aret") ;
- M ("rc_aerr") ;
- Stc ;
- M ("rc_aret") ;
- MovBxSi ;
- RetR ;
- (* -------------------------------------------------------- adigit *)
- M ("rc_adigit") ;
- LdClSi ;
- M ("rc_adigit2") ;
- CmpClImm (30H) ;
- JccNear (72H, "rc_adret") ;
- CmpClImm (3AH) ;
- Cmc ;
- JccNear (72H, "rc_adret") ;
- SubClImm (30H) ;
- M ("rc_adret") ;
- RetR ;
- (* --------------------------------------------------------- real_write *)
- (* [BP+4] = decimals, [BP+6] = width, [BP+8] = real. Formats into the
- internal buffer, then pads and prints: the same field real_fmt builds
- as a string, sent straight to the console. `Write(Value:FW:DEC)' uses
- this; the default widths are the compiler's (18 columns, decimals
- FFFFh, which fmtreal turns into its scientific mode). *)
- M ("real_write") ;
- PushBp ; MovBpSp ;
- PushBx ; PushDx ; PushSi ; PushDi ;
- MovAxBp6 ; StDataAx (D_FMTW) ; (* width *)
- MovDxBp4 ; (* DX := decimals *)
- MovCxBp6 ; (* CX := width *)
- MovDiBp8 ; (* DI := the real *)
- MovBxDataAddr (D_FMTBUF) ;
- Call ("rc_fmt") ; (* BX := end of the formatted text *)
- MovAxBx ;
- MovBxDataAddr (D_FMTBUF) ;
- SubAxBx ; (* AX := length *)
- StDataAx (D_FMTLEN) ;
- MovAxData (D_FMTW) ;
- MovDxData (D_FMTLEN) ;
- SubAxDx ;
- CmpAx0 ;
- Jge8 ("rc_wpadok") ;
- XorAxAx ;
- M ("rc_wpadok") ;
- StDataAx (D_FMTPAD) ;
- MovCxData (D_FMTPAD) ;
- Jcxz8 ("rc_wbody") ;
- M ("rc_wpad") ;
- MovAlD (20H) ; MovAh (02H) ; Int21 ; Loop8 ("rc_wpad") ;
- M ("rc_wbody") ;
- MovSiData (D_FMTBUF) ;
- MovCxData (D_FMTLEN) ;
- Jcxz8 ("rc_wdone") ;
- M ("rc_wout") ;
- LdAlSi ; MovAh (02H) ; Int21 ; IncSi ; Loop8 ("rc_wout") ;
- M ("rc_wdone") ;
- PopDi ; PopSi ; PopDx ; PopBx ;
- MovSpBp ; PopBp ; RetR
- END EmitRealConv ;
- PROCEDURE EmitData ;
- (* The runtime's data block. The D_ constants name the offsets in it and are
- ASSERTED against what is emitted here, three ways: the starting offset of
- each string, the offset just past it, and the block's total length. A D_
- constant is a 16-bit immediate inside a MOV, so a wrong one is a
- perfectly well-formed instruction that reads the wrong memory - see the
- note on the D_ block. *)
- BEGIN
- dataAt := rpos ;
- (* Each D_ is checked BEFORE the string it names is emitted, because it
- names that string's START. *)
- IF dataAt + D_NUM # rpos THEN
- HALT
- END ;
- (* 8 bytes of scratch, never read before written *)
- B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
- IF dataAt + D_TRUE # rpos THEN
- HALT
- END ;
- Str5 ("TRUE$", 5) ;
- IF dataAt + D_FALSE # rpos THEN
- HALT
- END ;
- Str5 ("FALSE$", 6) ;
- IF dataAt + D_CRLF # rpos THEN
- HALT
- END ;
- B (13) ; B (10) ; B (ORD ("$")) ;
- IF dataAt + D_REAL # rpos THEN
- HALT
- END ;
- Str5 ("?REAL?", 6) ;
- IF dataAt + D_PUSH # rpos THEN
- HALT
- END ;
- B (0) ; B (0) ; (* the pushback slot, empty to begin with *)
- IF dataAt + D_RSIGN # rpos THEN
- HALT
- END ;
- B (0) ; B (0) ; (* resign/resave *)
- IF dataAt + D_RMANT # rpos THEN
- HALT
- END ;
- B (0) ; B (0) ; (* remant/remant1 *)
- IF dataAt + D_CVDEC # rpos THEN
- HALT
- END ;
- B (0) ;
- IF dataAt + D_RMUL11 # rpos THEN
- HALT
- END ;
- B (0) ; B (0) ;
- IF dataAt + D_RMUL12 # rpos THEN
- HALT
- END ;
- B (0) ; B (0) ;
- IF dataAt + D_RMUL13 # rpos THEN
- HALT
- END ;
- B (0) ; B (0) ;
- IF dataAt + D_RMUL21 # rpos THEN
- HALT
- END ;
- B (0) ; B (0) ;
- IF dataAt + D_RMUL22 # rpos THEN
- HALT
- END ;
- B (0) ; B (0) ;
- IF dataAt + D_RMUL23 # rpos THEN
- HALT
- END ;
- B (0) ; B (0) ;
- IF dataAt + D_CVOUT # rpos THEN
- HALT
- END ;
- (* twelve decimal digit slots; rdconv fills all of them *)
- B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
- B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
- IF dataAt + D_CVDECEXP # rpos THEN
- HALT
- END ;
- B (0) ;
- IF dataAt + D_CVEXPCNT # rpos THEN
- HALT
- END ;
- B (0) ;
- IF dataAt + D_FLTDEC # rpos THEN
- HALT
- END ;
- (* 1e0, 1e4, ... 1e36 as the reference's AX:BX:DX triples, low byte first *)
- B (081H) ; B (000H) ; B (000H) ; B (000H) ; B (000H) ; B (000H) ;
- B (08EH) ; B (000H) ; B (000H) ; B (000H) ; B (040H) ; B (01CH) ;
- B (09BH) ; B (000H) ; B (000H) ; B (020H) ; B (0BCH) ; B (03EH) ;
- B (0A8H) ; B (000H) ; B (010H) ; B (0A5H) ; B (0D4H) ; B (068H) ;
- B (0B6H) ; B (004H) ; B (0BFH) ; B (0C9H) ; B (01BH) ; B (00EH) ;
- B (0C3H) ; B (0ACH) ; B (0C5H) ; B (0EBH) ; B (078H) ; B (02DH) ;
- B (0D0H) ; B (0CDH) ; B (0CEH) ; B (01BH) ; B (0C2H) ; B (053H) ;
- B (0DEH) ; B (0F9H) ; B (078H) ; B (039H) ; B (03FH) ; B (001H) ;
- B (0EBH) ; B (02BH) ; B (0A8H) ; B (0ADH) ; B (0C5H) ; B (01DH) ;
- B (0F8H) ; B (0C9H) ; B (07BH) ; B (0CEH) ; B (097H) ; B (040H) ;
- IF dataAt + D_FMTBUF # rpos THEN
- HALT
- END ;
- (* 64 bytes of formatted output; a fixed-format 1e38 with 10 decimals is
- about 50 characters, so this has room *)
- B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
- B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
- B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
- B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
- B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
- B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
- B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
- B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
- IF dataAt + D_FMTLEN # rpos THEN
- HALT
- END ;
- B (0) ; B (0) ;
- IF dataAt + D_FMTW # rpos THEN
- HALT
- END ;
- B (0) ; B (0) ;
- IF dataAt + D_FMTPAD # rpos THEN
- HALT
- END ;
- B (0) ; B (0) ;
- IF dataAt + D_FMTTOT # rpos THEN
- HALT
- END ;
- B (0) ; B (0) ;
- IF dataAt + D_ASCSRC # rpos THEN
- HALT
- END ;
- B (0) ; B (0) ;
- WHILE rpos < dataAt + D_END DO
- B (0)
- END ;
- (* the total, so D_END is checked too and not just used as a pad target *)
- IF rpos - dataAt # D_END THEN
- HALT
- END
- END EmitData ;
- PROCEDURE FixUp ;
- VAR i, t, rel : CARDINAL ;
- BEGIN
- i := 0 ;
- WHILE i < nfix DO
- IF fix [i].kind = 2 THEN
- (* A data address, and the ONE kind that is absolute rather than
- relative: rtBase says where the blob sits in the image, val says
- where inside the blob, and LoadBias says where the image itself
- sits once DOS has loaded it. All three are needed; omitting any
- one produces a readable-looking address into the wrong bytes. *)
- t := (dataAt + rtBase + fix [i].val + LoadBias) MOD 10000H ;
- rt [fix [i].place] := VAL (BYTE, t MOD 100H) ;
- rt [fix [i].place + 1] := VAL (BYTE, (t DIV 100H) MOD 100H)
- ELSE
- t := LblOff (fix [i].nm) ;
- IF fix [i].kind = 0 THEN
- (* rel8 is measured from the end of the instruction, i.e. one
- byte past the displacement field. A target beyond +127/-128
- would wrap silently and send the machine into the middle of
- an instruction; that happened in remult's early zero/return
- jumps. Refuse it. *)
- IF (VAL (LONGINT, t) - VAL (LONGINT, fix [i].place + 1) < -128)
- OR (VAL (LONGINT, t) - VAL (LONGINT, fix [i].place + 1) > 127)
- THEN
- HALT
- END ;
- rel := (t + 100H - (fix [i].place + 1)) MOD 100H ;
- rt [fix [i].place] := VAL (BYTE, rel)
- ELSE
- rel := (t + 10000H - (fix [i].place + 2)) MOD 10000H ;
- rt [fix [i].place] := VAL (BYTE, rel MOD 100H) ;
- rt [fix [i].place + 1] := VAL (BYTE, (rel DIV 100H) MOD 100H)
- END
- END ;
- INC (i)
- END
- END FixUp ;
- (* ---------------------------------------------------------------- *)
- (* public interface *)
- (* ---------------------------------------------------------------- *)
- PROCEDURE RT_Build (base : CARDINAL) ;
- BEGIN
- IF built THEN
- RETURN
- END ;
- rtBase := base ; (* must precede every Emit*, they read it *)
- rpos := 0 ; ltop := 0 ; nfix := 0 ; dataAt := 0 ;
- EmitInitMem ;
- EmitEnd ;
- EmitStackChk ;
- EmitWrInt ; EmitWrIntW ; EmitWrChar ; EmitWrBool ; EmitWrReal ; EmitWrLn ;
- EmitWrInl ; EmitWrStr ;
- EmitRdInt ; EmitRdScan ; EmitRdByte ;
- EmitRdChar ; EmitRdBool ; EmitRdLn ;
- EmitGetCh ; EmitUnGetCh ;
- EmitRdStr ; EmitStrStore ; EmitCmpStr ; EmitStrCat ;
- EmitRealFromInt ; EmitRealAddSub ; EmitRealMul ;
- EmitRealCmp ; EmitRealInt ; EmitRealConv ;
- EmitData ;
- FixUp ;
- SetStr (entNm [0], "initmem") ;
- SetStr (entNm [1], "progend") ;
- SetStr (entNm [2], "stackchk") ;
- SetStr (entNm [3], "wrint") ;
- SetStr (entNm [4], "wrchar") ;
- SetStr (entNm [5], "wrbool") ;
- SetStr (entNm [6], "wrreal") ;
- SetStr (entNm [7], "wrln") ;
- SetStr (entNm [8], "rdint") ;
- SetStr (entNm [9], "rdchar") ;
- SetStr (entNm [10], "rdbool") ;
- SetStr (entNm [11], "rdln") ;
- SetStr (entNm [12], "halt") ;
- SetStr (entNm [13], "wrtinl") ;
- SetStr (entNm [14], "rdbyte") ;
- SetStr (entNm [15], "wrstr") ;
- SetStr (entNm [16], "rdstr") ;
- SetStr (entNm [17], "strstore") ;
- SetStr (entNm [18], "cmpstr") ;
- SetStr (entNm [19], "strcat") ;
- SetStr (entNm [20], "wrintw") ;
- SetStr (entNm [21], "real_from_int") ;
- SetStr (entNm [22], "real_add") ;
- SetStr (entNm [23], "real_sub") ;
- SetStr (entNm [24], "real_mul") ;
- SetStr (entNm [25], "real_div") ;
- SetStr (entNm [26], "real_cmp") ;
- SetStr (entNm [27], "real_int") ;
- SetStr (entNm [28], "real_fmt") ;
- SetStr (entNm [29], "real_val") ;
- SetStr (entNm [30], "real_write") ;
- built := TRUE
- END RT_Build ;
- PROCEDURE RT_Size () : CARDINAL ;
- BEGIN
- IF NOT built THEN
- RT_Build (0)
- END ;
- RETURN rpos
- END RT_Size ;
- PROCEDURE RT_Byte (i : CARDINAL ) : BYTE ;
- BEGIN
- IF NOT built THEN
- RT_Build (0)
- END ;
- IF i >= rpos THEN
- RETURN 0
- END ;
- RETURN rt [i]
- END RT_Byte ;
- PROCEDURE RT_Entry (i : CARDINAL ) : CARDINAL ;
- BEGIN
- IF NOT built THEN
- RT_Build (0)
- END ;
- IF i > 30 THEN
- RETURN 0
- END ;
- RETURN rtBase + LblOff (entNm [i])
- END RT_Entry ;
- PROCEDURE RT_CodeEnd () : CARDINAL ;
- BEGIN
- IF NOT built THEN
- RT_Build (0)
- END ;
- RETURN dataAt
- END RT_CodeEnd ;
- END Runtime.
|