Runtime.mod 163 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225
  1. IMPLEMENTATION MODULE Runtime ;
  2. (* 8086 runtime library, assembled byte by byte. Each little emitter below is
  3. one 8086 instruction; the ModR/M byte is spelled out in the comment so the
  4. encoding can be checked by hand against an 8086 table. This is the same
  5. approach the compiler itself takes in Compiler.mod (Ebyte/Eword/EmCall),
  6. so the library needs no external assembler and no binary artifact in the
  7. tree - RT_Build assembles it every run, which is why the entry offsets are
  8. known exactly and are not guesses.
  9. Memory model: a .COM image, so CS = DS = ES = SS = 0 and the whole thing
  10. lives in one 64K segment. The runtime occupies image offsets
  11. RT_Build's base .. base + RT_Size - 1, the generated program follows, and
  12. the program's globals follow at that + 1000H.
  13. The base is a parameter rather than an assumption, and that is not
  14. decoration. The image begins with a three-byte JMP - a .COM is entered at
  15. file offset 0, and without it a .COM built by this compiler starts by
  16. executing initmem - so the runtime does NOT sit at image offset 0. Every
  17. address the runtime bakes into its own code (its data block, and the entry
  18. offsets it hands back) therefore has to be biased by that base. Two places
  19. do it, and only two: FixUp for the data block, and RT_Entry for the
  20. entries. The emitted BYTES are identical for any base, because both are
  21. computed after assembly; tests/check_runtime.py and tests/runtime.golden
  22. pass base 0 and are unaffected.
  23. Calling conventions, matching Compiler.IoCall:
  24. WrInt/WrChar/WrBool/WrReal one 16-bit value on the stack (caller pops)
  25. RdInt/RdChar/RdBool one address on the stack (caller pops)
  26. WrLn/RdLn/StackChk nothing
  27. InitMem AX = offset of the program header word block
  28. ProgEnd/Halt nothing; exits with code 0
  29. FileAssign [BP+4]=file block, [BP+6]=<len><name>
  30. FileReset/Rewrite/Close [BP+4]=file block
  31. IoResult no argument; AX = last DOS status, cleared
  32. The file operations use real DOS INT 21h calls and leave their status in
  33. the runtime's D_IORES word.
  34. All entries preserve BP and SP, and every register except the documented
  35. result, so they can be called from the middle of an expression. *)
  36. FROM SYSTEM IMPORT BYTE ;
  37. CONST
  38. MaxRt = 32768 ;
  39. (* MaxLbl and MaxFix were 64 and 400, and M/AddFix used to DROP once full.
  40. That limit does not announce itself: a dropped label leaves its bytes in
  41. place, so the runtime still has the right LENGTH and the only symptom is
  42. one branch whose rel8 came out as LblOff's "not found" value. String
  43. concatenation added the four labels that crossed 64, and strcat's
  44. `jcxz sc_done' ended up pointing 47 bytes past the code - a wrong target
  45. that ran the machine into the zero gap. Sizing them with margin fixes
  46. that case; the fill checks below make any future overflow a HALT instead
  47. of wrong code. tests/check_runtime.py's branch-boundary check is what
  48. caught this one. *)
  49. MaxLbl = 512 ; (* fmtreal/ascreal added ~70 labels to the MBF core's 160 *)
  50. MaxFix = 8192 ;
  51. MaxNm = 15 ;
  52. (* Offsets inside the runtime's own data block. These must agree with the
  53. bytes EmitData emits, and EmitData ASSERTS that they do - because the
  54. only other way to find out is to run a program, and a D_ constant that
  55. is one too high does not crash: the $ that terminates the string it
  56. points at is simply somewhere else, and a $-string read from the wrong
  57. address runs on until it happens to find a 24h. wrln did exactly that
  58. and printed a screenful of memory, which is what found this. D_TRUE was
  59. the only one that was right, and the golden (instructions only) could
  60. not see any of it. *)
  61. D_NUM = 0 ; (* 8 bytes, decimal conversion scratch *)
  62. D_TRUE = 8 ; (* "TRUE$" - 5 bytes, 8..12 *)
  63. D_FALSE = 13 ; (* "FALSE$" - 6 bytes, 13..18 *)
  64. D_CRLF = 19 ; (* CR LF '$' - 3 bytes, 19..21 *)
  65. D_REAL = 22 ; (* "?REAL?" - 6 bytes, 22..27; reals are not formatted yet *)
  66. D_PUSH = 28 ; (* 2 bytes, 28..29: the one-character pushback. The high
  67. byte is 1 whenever a character is pending, so the
  68. word is 0 exactly when the slot is empty - a NUL in
  69. the input would otherwise be indistinguishable from
  70. "nothing pushed back". Byte 30 is unused. *)
  71. D_RSIGN = 30 ; (* 2 bytes: the result sign word; D_RESAVE (31) is its
  72. high byte, which is the sign the reference keeps *)
  73. D_RESAVE = 31 ; (* the high byte of D_RSIGN: the saved sign *)
  74. D_RMANT = 32 ; (* 2 bytes: the second operand's saved MSB *)
  75. D_RMANT1 = 33 ; (* its high byte, mixed with operand 1's sign *)
  76. D_CVDEC = 34 ; (* 1 byte: the second exponent during alignment *)
  77. D_RMUL11 = 35 ; (* a's saved AX; D_RMUL11+1 is its high byte (AH) *)
  78. D_RMUL12 = 37 ; (* a's saved BX *)
  79. D_RMUL13 = 39 ; (* a's saved DX *)
  80. D_RMUL21 = 41 ; (* b's saved CX (low byte is the exponent, skipped) *)
  81. D_RMUL22 = 43 ; (* b's saved SI *)
  82. D_RMUL23 = 45 ; (* b's saved DI *)
  83. D_CVOUT = 47 ; (* 12 bytes: realdec's decimal digits *)
  84. D_CVDECEXP = 59 ; (* realrang's decimal exponent, sign in bit 7 *)
  85. D_CVEXPCNT = 60 ; (* ascreal's decimal-point counter *)
  86. D_FLTDEC = 61 ; (* 10 x 6 bytes: 1e0, 1e4, ... 1e36 (TPSRC3) *)
  87. D_FMTBUF = 121 ; (* 64 bytes: fmtreal's unpadded output *)
  88. D_FMTLEN = 185 ; (* 2 bytes: its length *)
  89. D_FMTW = 187 ; (* 2 bytes: the requested field width *)
  90. D_FMTPAD = 189 ; (* 2 bytes: left padding *)
  91. D_FMTTOT = 191 ; (* 2 bytes: pad + length *)
  92. D_ASCSRC = 193 ; (* 2 bytes: ascreal's source pointer *)
  93. D_IORES = 195 ; (* 2 bytes: the last file-operation DOS status *)
  94. D_HEAP = 197 ; (* 2 bytes: the bump allocator's next free byte *)
  95. D_TXEOF = 199 ; (* 1 byte: text Readln saw ^Z or physical EOF *)
  96. D_TXCH = 200 ; (* 1 byte: one-byte INT 21h read scratch *)
  97. D_VALSIGN = 201 ; (* 1 byte: Val integer sign flag *)
  98. D_VALCH = 202 ; (* 2 bytes: Val digit scratch *)
  99. D_VALBUF = 204 ; (* 256 bytes: counted string -> ASCIIZ for val_real *)
  100. D_MATHC = 460 ; (* 6 bytes: the saved original in Sqrt *)
  101. D_MATHX = 466 ; (* 6 bytes: the current Newton iterate *)
  102. D_MATHQ = 472 ; (* 6 bytes: a division result *)
  103. D_MATHI = 478 ; (* 1 byte: a small loop counter *)
  104. D_ONE = 479 ; (* 1.0 *)
  105. D_EXP256 = 485 ; (* 256.0: the Exp range-reduction divisor *)
  106. D_LN2 = 491 ; (* ln 2 *)
  107. D_HALF = 497 ; (* 0.5 *)
  108. D_LNC = 503 ; (* 8 x 6 bytes: 1/15 .. 1 for Ln *)
  109. D_EXPC = 551 ; (* 12 x 6 bytes: 1/0! .. 1/11! for Exp *)
  110. D_PI = 623 ; (* pi *)
  111. D_PI2 = 629 ; (* pi/2 *)
  112. D_2PI = 635 ; (* 2*pi *)
  113. D_FLTSIN = 641 ; (* 7 x 6 bytes: the sin/cos polynomial *)
  114. D_FLTATN = 683 ; (* 5 x 6 bytes: the arctan polynomial *)
  115. D_FLTATNRG = 713 ; (* 8 x 6 bytes: arctan range table *)
  116. D_X = 761 ; (* 6 bytes: math argument *)
  117. D_Y = 767 ; (* 6 bytes: math temporary *)
  118. D_P = 773 ; (* 6 bytes: polynomial accumulator *)
  119. D_BLK = 779 ; (* 12 bytes: a/b block for the runtime entries *)
  120. D_E = 791 ; (* 6 bytes: saved exponent as a real *)
  121. D_EI = 797 ; (* 1 byte: exponent integer *)
  122. D_NPI = 798 ; (* -pi *)
  123. D_SIGN = 804 ; (* arctan sign flag *)
  124. D_RECIP = 805 ; (* arctan reciprocal flag *)
  125. D_TRANS = 806 ; (* arctan transformation flag *)
  126. D_ATNT = 807 ; (* tan(pi/8) *)
  127. D_PI4 = 813 ; (* pi/4 *)
  128. D_ZERO = 819 ; (* 0.0 *)
  129. D_HT = 825 ; (* 2 bytes: Horner table pointer *)
  130. D_HC = 827 ; (* 1 byte: Horner/square iteration count *)
  131. D_HF = 828 ; (* 1 byte: Horner final +1 flag *)
  132. D_ISTMP = 829 ; (* 2 bytes: Insert index *)
  133. D_ITMP = 831 ; (* 2 bytes: Insert new length *)
  134. D_END = 833 ; (* the block is padded to here *) (* the block is padded to here *)
  135. (* THE LOAD BIAS. Everything above is an offset into the image as this
  136. compiler numbers it: the entry JMP is at image 0. But a DOS .COM is
  137. NOT loaded at that offset - DOS loads it at CS:0100, because 0000..00FF
  138. is the PSP - so image offset K lives at CS:(K + 0100h), and since a
  139. .COM has CS = DS, every address baked into the image as a literal has
  140. to carry that +0100h.
  141. Getting this wrong is the single most confusing failure this compiler
  142. can have, because nothing crashes. The entry JMP is *relative*, so it
  143. still lands on the prologue; every CALL is relative, so every call
  144. still lands on the right runtime entry; initmem still runs and still
  145. zeroes the data area. The program therefore starts, runs, and prints
  146. its first field correctly - and then a $ that is 0100h too low resolves
  147. to code instead of to data, so the string walk runs on through the
  148. runtime's own instructions until it happens to hit a 24h. That is
  149. exactly what writeln('hi') did: `hi' out of wrtin's inline data, then
  150. 249 bytes of runtime machine code, stopping only at the '$' inside
  151. "TRUE$". See tests/fixtures/t02_writeln.out.
  152. The bias is a CONSTANT, not a variable to be relocated at load time,
  153. because the 8086 has no way to relocode an image in place and no way
  154. to set a segment register to a sub-paragraph boundary. It is the same
  155. +0100h the original's own output has: TPSRC7 opendest copies the
  156. generated image to 100h and runs it there, so the original's addresses
  157. are 0100h-relative too. The difference is only in how the bytes are
  158. *numbered* - ours are 0-based in the file, the original's are
  159. segment-relative - and this constant is where that difference stops.
  160. The constant itself is declared in Runtime.def, because that is the only
  161. declaration site a compiler can import - an implementation module does not
  162. re-declare what its definition module already declared. *)
  163. TYPE
  164. LblRec = RECORD
  165. nm : ARRAY [0..MaxNm] OF CHAR ;
  166. off : CARDINAL ;
  167. END ;
  168. FixRec = RECORD
  169. kind : CARDINAL ; (* 0 = rel8, 1 = rel16, 2 = data address *)
  170. place : CARDINAL ; (* offset of the displacement/address field *)
  171. nm : ARRAY [0..MaxNm] OF CHAR ;
  172. val : CARDINAL ;
  173. END ;
  174. VAR
  175. rt : ARRAY [0..MaxRt - 1] OF BYTE ;
  176. rpos : CARDINAL ;
  177. lbl : ARRAY [0..MaxLbl - 1] OF LblRec ;
  178. ltop : CARDINAL ;
  179. fix : ARRAY [0..MaxFix - 1] OF FixRec ;
  180. nfix : CARDINAL ;
  181. dataAt : CARDINAL ;
  182. rtBase : CARDINAL ;
  183. built : BOOLEAN ;
  184. entNm : ARRAY [0..63] OF ARRAY [0..MaxNm] OF CHAR ;
  185. (* ---------------------------------------------------------------- *)
  186. (* name helpers *)
  187. (* ---------------------------------------------------------------- *)
  188. PROCEDURE StrEq (a, b : ARRAY OF CHAR ) : BOOLEAN ;
  189. VAR i : CARDINAL ;
  190. BEGIN
  191. i := 0 ;
  192. WHILE (i <= HIGH (a)) AND (i <= HIGH (b)) DO
  193. IF a [i] # b [i] THEN
  194. RETURN FALSE
  195. END ;
  196. INC (i)
  197. END ;
  198. RETURN TRUE
  199. END StrEq ;
  200. PROCEDURE SetStr (VAR dst : ARRAY OF CHAR ; src : ARRAY OF CHAR ) ;
  201. VAR i : CARDINAL ;
  202. BEGIN
  203. i := 0 ;
  204. WHILE (i <= HIGH (dst)) AND (i <= HIGH (src)) DO
  205. dst [i] := src [i] ;
  206. INC (i)
  207. END ;
  208. IF i <= HIGH (dst) THEN
  209. dst [i] := 0C
  210. END
  211. END SetStr ;
  212. (* ---------------------------------------------------------------- *)
  213. (* primitive emitters *)
  214. (* ---------------------------------------------------------------- *)
  215. PROCEDURE B (b : CARDINAL ) ;
  216. (* emit exactly ONE byte. A two-byte opcode must be written as two B calls -
  217. B masks to 100H, so B (8BE4H) would silently emit just E4. *)
  218. BEGIN
  219. IF rpos >= MaxRt THEN
  220. RETURN (* blob is oversized: drop the byte *)
  221. END ;
  222. rt [rpos] := VAL (BYTE, b MOD 100H) ;
  223. INC (rpos)
  224. END B ;
  225. PROCEDURE W (w : CARDINAL ) ;
  226. BEGIN
  227. B (w MOD 100H) ;
  228. B ((w DIV 100H) MOD 100H) (* little endian *)
  229. END W ;
  230. PROCEDURE M (nm : ARRAY OF CHAR ) ;
  231. (* mark: nm is the current offset *)
  232. VAR i : CARDINAL ;
  233. BEGIN
  234. (* A dropped label is not a smaller runtime, it is a WRONG one: its bytes
  235. are still emitted, so only a branch that names it goes off target. That
  236. shows up in neither the size nor the entry table, so refuse to build
  237. rather than drop. *)
  238. IF ltop >= MaxLbl THEN
  239. HALT
  240. END ;
  241. (* A DUPLICATE label is worse than a missing one: LblOff returns the FIRST
  242. spelling, so the second body (and every branch meant for it) silently
  243. runs the first. That is exactly how real_div came to call readd. *)
  244. i := 0 ;
  245. WHILE i < ltop DO
  246. IF StrEq (lbl [i].nm, nm) THEN
  247. HALT
  248. END ;
  249. INC (i)
  250. END ;
  251. SetStr (lbl [ltop].nm, nm) ;
  252. lbl [ltop].off := rpos ;
  253. INC (ltop)
  254. END M ;
  255. PROCEDURE AddFix (kind, place : CARDINAL ; nm : ARRAY OF CHAR ; val : CARDINAL ) ;
  256. (* Record a displacement to be patched once every label is known. A dropped
  257. fixup is an address that is never patched - the same silent-wrong-code
  258. failure the check in M exists to prevent. *)
  259. BEGIN
  260. IF nfix >= MaxFix THEN
  261. HALT
  262. END ;
  263. fix [nfix].kind := kind ;
  264. fix [nfix].place := place ;
  265. fix [nfix].val := val ;
  266. SetStr (fix [nfix].nm, nm) ;
  267. INC (nfix)
  268. END AddFix ;
  269. PROCEDURE LblOff (nm : ARRAY OF CHAR ) : CARDINAL ;
  270. VAR i : CARDINAL ;
  271. BEGIN
  272. i := 0 ;
  273. WHILE i < ltop DO
  274. IF StrEq (lbl [i].nm, nm) THEN
  275. RETURN lbl [i].off
  276. END ;
  277. INC (i)
  278. END ;
  279. RETURN 0
  280. END LblOff ;
  281. PROCEDURE J8 (nm : ARRAY OF CHAR ) ;
  282. BEGIN
  283. B (0EBH) ; (* JMP rel8 *)
  284. AddFix (0, rpos, nm, 0) ;
  285. B (0)
  286. END J8 ;
  287. PROCEDURE C8 (nm : ARRAY OF CHAR ) ;
  288. BEGIN
  289. B (0E8H) ; (* CALL rel16 *)
  290. AddFix (1, rpos, nm, 0) ;
  291. W (0)
  292. END C8 ;
  293. PROCEDURE Jcc (code : CARDINAL ; nm : ARRAY OF CHAR ) ;
  294. BEGIN
  295. B (code) ; (* Jcc rel8 *)
  296. AddFix (0, rpos, nm, 0) ;
  297. B (0)
  298. END Jcc ;
  299. PROCEDURE Dd (delta : CARDINAL ) ;
  300. (* emit a 16-bit address into the runtime's data block; the value is only
  301. known once the data block has been placed, so it is a fixup *)
  302. BEGIN
  303. AddFix (2, rpos, "", delta) ;
  304. W (0)
  305. END Dd ;
  306. (* --- 8-bit / 16-bit register and memory forms, one instruction each --- *)
  307. (* THE 16-BIT ModR/M EFFECTIVE-ADDRESS TABLE, MEASURED NOT REMEMBERED.
  308. Every address form below was confirmed by EXECUTING it on a real 8086
  309. (qemu-system-i386) with a probe that stores a marker through the candidate
  310. encoding and then reports which physical address received it; see
  311. tests/probe/modrm19.s. The mod=11 column is not an address at all -- it
  312. names a register -- so it is confirmed instead by asking GNU as to
  313. ENCODE the eight register moves and checking FCML's decode of the result
  314. (tests/probe/modrm11.py, which also carries the hand-checking anchors).
  315. Do not "fix" any of these from memory. The version that comes to mind is
  316. wrong in exactly the cells called out below, and every one of those
  317. mistakes shipped as code that decoded cleanly:
  318. r/m mod=00 mod=01 mod=10 mod=11
  319. 000 [BX+SI] [BX+SI]+disp [BX+SI]+disp AX
  320. 001 [BX+DI] [BX+DI]+disp [BX+DI]+disp CX
  321. 010 [BP+SI] [BP+SI]+disp [BP+SI]+disp DX
  322. 011 [BP+DI] [BP+DI]+disp [BP+DI]+disp BX
  323. 100 [SI] [SI]+disp [SI]+disp SP
  324. 101 [DI] [DI]+disp [DI]+disp BP
  325. 110 disp16 [BP]+disp [BP]+disp SI
  326. 111 [BX] [BX]+disp [BX]+disp DI
  327. "disp" is disp8 for mod=01 and disp16 for mod=10.
  328. In mod=11 BOTH fields name a register and BOTH use the same list,
  329. AX CX DX BX SP BP SI DI -- the reg field and the r/m field do not
  330. differ, and there is no second BX at code 7. The memorable version
  331. drops AX off the front and invents a duplicate BX at the end, which
  332. shifts every code down by one; that is precisely how MovSiBx and
  333. CmpSiBx came to be written "89 DC" and "39 DC", which are MOV SP,BX
  334. and CMP SP,BX. Note what the shifted table gets right by luck: SP is
  335. at 100 either way, so the mistake is invisible until you check a
  336. register below it. rm=100 is SP, and SI is rm=110, not rm=100.
  337. And reg/rm swap direction with the opcode, which is the other trap:
  338. 88 /r MOV r/m8,r8 89 /r MOV r/m16,r16 reg is the SOURCE
  339. 8A /r MOV r8,r/m8 8B /r MOV r16,r/m16 reg is the TARGET
  340. So ModRM C6 names "DH and AL" either way, but 88 C6 is DH:=AL while
  341. 8A C6 is AL:=DH. The 8-bit register list is also its own, and it is not
  342. the word list:
  343. 000 001 010 011 100 101 110 111 = AL CL DL BL AH CH DH BH
  344. The two lists agree at every code except 100, where the byte form is AH
  345. and the word form is SP. Reading the same ModRM byte as the other size
  346. silently swaps AH for SP, and 88 C2 is DL:=AL while 88 17 is [BX]<-DL.
  347. The word-form list, and the four anchors nobody writes by hand:
  348. 83 C4 08 ADD SP, 8 rm=100 -> SP
  349. 83 C6 02 ADD SI, 2 rm=110 -> SI
  350. 8B EC MOV BP, SP reg=101 rm=100
  351. 8B E5 MOV SP, BP reg=100 rm=101
  352. Asking "which register is rm=100?" and answering SI is the single most
  353. common error in this file's history.
  354. Concrete recipes for opcode 8r / 9r (r/m = rm, 16-bit form):
  355. mod=00 rm=110 -> 06 <disp16> the only direct form
  356. mod=00 rm=111 -> 07 = [BX] 2 bytes
  357. mod=00 rm=101 -> 05 = [DI] 2 bytes
  358. mod=01 rm=110 -> 46 disp8 = [BP]+disp8
  359. mod=01 rm=111 -> 47 disp8 = [BX]+disp8
  360. mod=01 rm=101 -> 45 disp8 = [DI]+disp8
  361. There is no [SP] form in 16-bit mode: SIB bytes are 386-only. Anything
  362. wanting the top of the stack has to go through BP. *)
  363. PROCEDURE PushBp ; BEGIN B (55H) END PushBp ;
  364. PROCEDURE PopBp ; BEGIN B (5DH) END PopBp ;
  365. PROCEDURE MovBpSp ; BEGIN B (8BH) ; B (0ECH) END MovBpSp ; (* 8B EC: MOV BP,SP *)
  366. PROCEDURE MovSpBp ; BEGIN B (89H) ; B (0ECH) END MovSpBp ; (* 89 EC: MOV SP,BP *)
  367. PROCEDURE AddSp2 ; BEGIN B (83H) ; B (0C4H) ; B (2) END AddSp2 ; (* ADD SP,2 *)
  368. PROCEDURE LeaveR ; BEGIN B (0C9H) END LeaveR ;
  369. PROCEDURE RetR ; BEGIN B (0C3H) END RetR ;
  370. PROCEDURE Int21 ; BEGIN B (0CDH) ; B (21H) END Int21 ;
  371. PROCEDURE PushDs ; BEGIN B (1EH) END PushDs ;
  372. PROCEDURE PopEs ; BEGIN B (7H) END PopEs ;
  373. PROCEDURE PushAx ; BEGIN B (50H) END PushAx ;
  374. PROCEDURE PopAx ; BEGIN B (58H) END PopAx ;
  375. PROCEDURE PushBx ; BEGIN B (53H) END PushBx ;
  376. PROCEDURE PopBx ; BEGIN B (5BH) END PopBx ;
  377. PROCEDURE PushCx ; BEGIN B (51H) END PushCx ;
  378. PROCEDURE PopCx ; BEGIN B (59H) END PopCx ;
  379. PROCEDURE PushDx ; BEGIN B (52H) END PushDx ;
  380. PROCEDURE PopDx ; BEGIN B (5AH) END PopDx ;
  381. PROCEDURE PushDi ; BEGIN B (57H) END PushDi ;
  382. PROCEDURE PopDi ; BEGIN B (5FH) END PopDi ;
  383. PROCEDURE PushSi ; BEGIN B (56H) END PushSi ;
  384. PROCEDURE PopSi ; BEGIN B (5EH) END PopSi ;
  385. PROCEDURE XorAxAx ; BEGIN B (31H) ; B (0C0H) END XorAxAx ; (* 11 000 000 *)
  386. PROCEDURE XorCxCx ; BEGIN B (31H) ; B (0C9H) END XorCxCx ; (* 11 001 001 *)
  387. PROCEDURE XorDxDx ; BEGIN B (31H) ; B (0D2H) END XorDxDx ; (* 11 010 010 *)
  388. PROCEDURE XorDiDi ; BEGIN B (31H) ; B (0FFH) END XorDiDi ; (* 11 111 111 *)
  389. PROCEDURE IncCx ; BEGIN B (41H) END IncCx ;
  390. PROCEDURE IncSi ; BEGIN B (46H) END IncSi ;
  391. PROCEDURE DecSi ; BEGIN B (4EH) END DecSi ;
  392. PROCEDURE AddDi2 ; BEGIN B (83H) ; B (0C7H) ; B (2) END AddDi2 ; (* 11 000 111 *)
  393. PROCEDURE AddDiImm (v : CARDINAL) ;
  394. BEGIN B (81H) ; B (0C7H) ; W (v) END AddDiImm ; (* ADD DI,imm16 *)
  395. PROCEDURE CmpAl (v : CARDINAL ) ; BEGIN B (3CH) ; B (v) END CmpAl ;
  396. PROCEDURE CmpAx0 ; BEGIN B (83H) ; B (0F8H) ; B (0) END CmpAx0 ;
  397. PROCEDURE CmpCx0 ; BEGIN B (83H) ; B (0F9H) ; B (0) END CmpCx0 ;
  398. PROCEDURE CmpArg2W0 ;
  399. BEGIN
  400. B (8BH) ; B (0ECH) ; (* MOV BP,SP *)
  401. B (83H) ; B (7EH) ; B (2) ; B (0) ; (* CMP WORD [BP+2],0 *)
  402. B (89H) ; B (0ECH) (* MOV SP,BP *)
  403. END CmpArg2W0 ;
  404. PROCEDURE CmpArg4W0 ;
  405. (* CMP WORD PTR [BP+4],0 - the same comparison, in the frame where the entry
  406. pushed BP first.
  407. The 2 and the 4 are the whole point of having two names. [SP] is not
  408. encodable in 16-bit mode, so an entry that takes one 16-bit argument has to
  409. borrow BP to reach it, and how far up the argument then sits depends on
  410. whether BP was saved first: 2 bytes without the PUSH, 4 with it. A single
  411. emitter taking the displacement as a parameter is the obvious way to write
  412. this and the wrong one -- a parameter is invisible to tests/audit_helpers.py,
  413. which reads the NAME and compares it against the decode, so `CmpArgW0 (2)'
  414. and `CmpArgW0 (4)' would differ by one byte with nothing in the codebase
  415. able to tell which was meant. In the name, the audit pins it: a helper
  416. called CmpArg4W0 that emitted 83 7E 02 00 would fail the run.
  417. Note the sandwich: MOV BP,SP before and MOV SP,BP after, and no POP BP, so
  418. the two forms are the ONLY difference. (Was 83 7C 24 00 00, which decodes
  419. as CMP WORD [SI+24h],0.) The imm8 of the 83 form is sign-extended to 16
  420. bits, so 0 really is a 16-bit zero. *)
  421. BEGIN
  422. B (8BH) ; B (0ECH) ; (* MOV BP,SP *)
  423. B (83H) ; B (7EH) ; B (4) ; B (0) ; (* CMP WORD [BP+4],0 *)
  424. B (89H) ; B (0ECH) (* MOV SP,BP *)
  425. END CmpArg4W0 ;
  426. PROCEDURE CmpSiBx ; BEGIN B (39H) ; B (0DEH) END CmpSiBx ; (* 39 DE: CMP SI,BX *)
  427. PROCEDURE CmpCxDx ; BEGIN B (39H) ; B (0D1H) END CmpCxDx ; (* 11 010 001 *)
  428. PROCEDURE CmpAlDl ; BEGIN B (3AH) ; B (0C2H) END CmpAlDl ; (* 3A C2: CMP AL,DL.
  429. The byte-sized comparator cmpstr's loop needs, and the two opcodes are NOT
  430. interchangeable: 3A /r is `CMP r8,r/m8' (reg is the TARGET) and 38 /r is
  431. `CMP r/m8,r8' (reg is the SOURCE). AL is reg=000 and DL is r/m=010, so
  432. 3A C2 is CMP AL,DL and 38 C2 would be CMP DL,AL - the same flags negated,
  433. which turns every `<` into a `>'. And there is no `CMP AL,[SI]' name
  434. pattern in tests/audit_helpers.py (only the Ld/St memory names are), so the
  435. right-hand character is loaded into DL first rather than compared in
  436. memory. *)
  437. PROCEDURE CmpDiCx ; BEGIN B (39H) ; B (0CFH) END CmpDiCx ; (* 11 001 111 *)
  438. PROCEDURE CmpBx0 ; BEGIN B (83H) ; B (0FBH) ; B (0) END CmpBx0 ;
  439. PROCEDURE CmpCxV (v : CARDINAL ) ; BEGIN B (83H) ; B (0F9H) ; B (v) END CmpCxV ;
  440. PROCEDURE AddDl (v : CARDINAL ) ; BEGIN B (80H) ; B (0C2H) ; B (v) END AddDl ;
  441. PROCEDURE SubAl (v : CARDINAL ) ; BEGIN B (2CH) ; B (v) END SubAl ;
  442. PROCEDURE SubAxBx ; BEGIN B (2BH) ; B (0C3H) END SubAxBx ; (* 2B C3: AX:=AX-BX *)
  443. PROCEDURE NegAx ; BEGIN B (0F7H) ; B (0D8H) END NegAx ;
  444. PROCEDURE NegDi ; BEGIN B (0F7H) ; B (0DFH) END NegDi ;
  445. PROCEDURE DivCx ; BEGIN B (0F7H) ; B (0F1H) END DivCx ; (* 11 110 001 *)
  446. PROCEDURE MulBx ; BEGIN B (0F7H) ; B (0E3H) END MulBx ; (* 11 100 011 *)
  447. PROCEDURE MovAh (v : CARDINAL ) ; BEGIN B (0B4H) ; B (v) END MovAh ;
  448. PROCEDURE MovDl (v : CARDINAL ) ; BEGIN B (0B2H) ; B (v) END MovDl ;
  449. (* The runtime's own data block, addressed by absolute image address. Four
  450. emitters cover it, and the difference between them is the whole subject of
  451. the mistakes recorded below, so they are named systematically:
  452. Mov<reg>Vx MOV reg, Vx -- reg := the ADDRESS (BB / BA + Dd)
  453. Ld<reg>Vx MOV reg, [Vx] -- reg := the CONTENTS (8B 1E + Dd)
  454. StVx<reg> MOV [Vx], reg -- [Vx] := reg (89 1E + Dd)
  455. "Vx" is the operand word for "a 16-bit absolute address into the data
  456. block", spelled with a V precisely so it cannot be confused with a base
  457. register: on the 8086 there is no [BX] memory form, so the address has to
  458. go through mod=00 / rm=110, and rm=111 is [BX+SI] - a different, perfectly
  459. decodable instruction. tests/audit_helpers.py checks all four.
  460. The two traps here, both of which happened:
  461. - Dd versus W. Dd turns a D_ offset into a FIXUP, so the address can be
  462. placed only once the data block has been given its final position in the
  463. image. W emits the number as it stands. The two produce the SAME
  464. instruction, and getch once used MovBxImm where it wanted the address, so
  465. `MOV BX,D_PUSH' came out as `MOV BX,001Ch' - the raw offset 1Ch, inside
  466. the entry JMP. Non-zero, so the pushback slot was never examined, getch
  467. served a byte of the runtime's own code forever, and readln hung. Hence
  468. the Imm suffix: the raw-immediate emitters are named for what they do.
  469. - address versus contents. LdBxVx and MovBxVx are both `8B`/`BB` + Dd to
  470. the eye and completely different instructions. getch's first cut used
  471. MovBxVx where it wanted the contents, so it tested the ADDRESS for zero,
  472. found 02AFh every time, and returned the low byte of memory 02AFh - 0,
  473. the value it had just stored there - instead of asking DOS. *)
  474. PROCEDURE MovBxImm (v : CARDINAL ) ; BEGIN B (0BBH) ; W (v) END MovBxImm ;
  475. PROCEDURE MovCxImm (v : CARDINAL ) ; BEGIN B (0B9H) ; W (v) END MovCxImm ;
  476. PROCEDURE MovDxImm (v : CARDINAL ) ; BEGIN B (0BAH) ; W (v) END MovDxImm ;
  477. PROCEDURE MovBxVx (delta : CARDINAL ) ; BEGIN B (0BBH) ; Dd (delta) END MovBxVx ;
  478. PROCEDURE MovDxVx (delta : CARDINAL ) ; BEGIN B (0BAH) ; Dd (delta) END MovDxVx ;
  479. PROCEDURE LdBxVx (delta : CARDINAL ) ;
  480. (* 8B 1E lo hi: BX := WORD PTR [Vx] - mod=00 / rm=110, the only 16-bit form
  481. that can carry a bare absolute address. *)
  482. BEGIN
  483. B (8BH) ; B (01EH) ; Dd (delta)
  484. END LdBxVx ;
  485. PROCEDURE MovSiAx ; BEGIN B (8BH) ; B (0F0H) END MovSiAx ; (* 8B F0: MOV SI,AX *)
  486. PROCEDURE MovAxDi ; BEGIN B (8BH) ; B (0C7H) END MovAxDi ; (* 11 000 111 *)
  487. PROCEDURE MovCxSi6 ; BEGIN B (8BH) ; B (4CH) ; B (6) END MovCxSi6 ;
  488. PROCEDURE MovCxDx ; BEGIN B (8BH) ; B (0CAH) END MovCxDx ; (* 8B CA: MOV CX,DX *)
  489. PROCEDURE MovDxSi2 ; BEGIN B (8BH) ; B (54H) ; B (2) END MovDxSi2 ;
  490. (* The program header is a block of words laid out by the compiler:
  491. +0 hdrFlag 1 = image is valid
  492. +2 hdrCS end of the generated code, in bytes
  493. +4 hdrDS base of the data area <- initmem wants these two
  494. +6 hdrHeap end of the data area <-
  495. +8 hdrMax max open files
  496. initmem must read the words the COMPILER WRITES, and those are +4 (hdrDS,
  497. the data base) and +6 (hdrHeap, the data end). It used to read +8, which
  498. is hdrMax -- and the compiler patches that to 0 -- so CX came out as 0,
  499. "cmp cx,dx / jbe im_done" fired immediately, and initmem silently zeroed
  500. nothing at all. A 3-byte instruction, so no entry offset moved either
  501. way; only the header offset in the byte changed. Nothing caught it
  502. because the loop was well formed - it just did nothing. The cross-check
  503. that would have: tests/run_com_tests.sh now asserts the runtime's SI-relative
  504. reads against the same header offsets it verifies the words at. *)
  505. PROCEDURE MovDxSi4 ; BEGIN B (8BH) ; B (54H) ; B (4) END MovDxSi4 ;
  506. PROCEDURE MovAxBp4 ; BEGIN B (8BH) ; B (46H) ; B (4) END MovAxBp4 ; (* AX:=[BP+4] *)
  507. PROCEDURE MovAxBp6 ; BEGIN B (8BH) ; B (46H) ; B (6) END MovAxBp6 ; (* AX:=[BP+6] *)
  508. PROCEDURE MovAxBp8 ; BEGIN B (8BH) ; B (46H) ; B (8) END MovAxBp8 ; (* AX:=[BP+8] *)
  509. PROCEDURE MovBxBp4 ; BEGIN B (8BH) ; B (5EH) ; B (4) END MovBxBp4 ; (* BX:=[BP+4] *)
  510. PROCEDURE MovBxBp6 ; BEGIN B (8BH) ; B (5EH) ; B (6) END MovBxBp6 ; (* BX:=[BP+6] *)
  511. PROCEDURE MovAlDh ; BEGIN B (8AH) ; B (0C6H) END MovAlDh ; (* 8A C6: AL:=DH *)
  512. PROCEDURE MovDlSi ; BEGIN B (8AH) ; B (14H) END MovDlSi ;
  513. PROCEDURE MovDlArg2 ; BEGIN B (8AH) ; B (56H) ; B (2) END MovDlArg2 ; (* DL:=[BP+2] *)
  514. (* The AL twins of the three above. DOS INT 21h AH=02h displays the
  515. character in DL, so every output site copies AL into DL. bootcom.s now
  516. reads DL too; the copy also keeps AL in step with the byte the surrounding
  517. digit/character code has just built.
  518. The earlier wiring only put it in AL. That happened to work under the old
  519. bootcom handler, which read AL, and failed under a real DOS: FreeDOS and
  520. DOSBox-X printed the register's residual value instead, which is exactly the
  521. garbage-screen report. The test harness's private INT 21h had hidden the
  522. divergence. *)
  523. PROCEDURE MovAlD (v : CARDINAL ) ; BEGIN B (0B0H) ; B (v) END MovAlD ; (* MOV AL,imm8 *)
  524. PROCEDURE MovAlSi ; BEGIN B (8AH) ; B (04H) END MovAlSi ; (* 8A 04: AL:=[SI] *)
  525. PROCEDURE MovAlArg2 ; BEGIN B (8AH) ; B (46H) ; B (2) END MovAlArg2 ; (* AL:=[BP+2] *)
  526. PROCEDURE MovAlArg4 ; BEGIN B (8AH) ; B (46H) ; B (4) END MovAlArg4 ; (* AL:=[BP+4] *)
  527. PROCEDURE MovAlDl ; BEGIN B (8AH) ; B (0C2H) END MovAlDl ; (* 8A C2: AL:=DL *)
  528. PROCEDURE StDiAx ; BEGIN B (89H) ; B (5H) END StDiAx ; (* 89 05: [DI]:=AX *)
  529. PROCEDURE StDiAl ; BEGIN B (88H) ; B (5H) END StDiAl ; (* 88 05: [DI]:=AL *)
  530. PROCEDURE StSiDl ; BEGIN B (88H) ; B (14H) END StSiDl ; (* 88 14: [SI]:=DL *)
  531. PROCEDURE StDiDl ; BEGIN B (88H) ; B (15H) END StDiDl ; (* 88 15: [DI]:=DL *)
  532. PROCEDURE StDiCx ; BEGIN B (89H) ; B (0DH) END StDiCx ; (* 89 0D: [DI]:=CX *)
  533. PROCEDURE MovDiBp4 ; BEGIN B (8BH) ; B (7EH) ; B (4) END MovDiBp4 ; (* DI:=[BP+4] *)
  534. (* There is NO "store through BX" instruction on the 8086, and writing one
  535. anyway is the single most expensive mistake this file has produced, so the
  536. reasoning is recorded rather than left in the probe history.
  537. In 16-bit addressing the r/m column is a LOCATION, not a register list: for
  538. mod=00, rm=000..101 are [BX+SI] [BX+DI] [BP+SI] [BP+DI] [SI] [DI], and
  539. rm=110 is the only one that means "a direct displacement". rm=111 is
  540. [BX+SI], NOT [BX]. So `89 1D' - the ModR/M the first version of this used
  541. for "MOV [BX],CX" - is mod=00 reg=BX r/m=DI, i.e. MOV [DI],BX: the two
  542. operands swapped AND [BX] inexpressible. GNU as and objdump in -m i8086
  543. agree, and the bytes are perfectly well formed, which is why nothing
  544. objected. What the program saw was its own address being written over the
  545. interrupt vector table, and the variable it was asked to fill left holding
  546. whatever the loader put there: readln(n) then printed 0, and readln(c)
  547. printed a NUL. Correct by comparison: the caller puts the address in BX,
  548. and the store has to go through DI or SI, so the sequence is
  549. MOV DI,[BP+4] / MOV [DI],<value> - see StoreThrough below. *)
  550. PROCEDURE MovDlAl ; BEGIN B (88H) ; B (0C2H) END MovDlAl ;
  551. PROCEDURE MovDlCl ; BEGIN B (8AH) ; B (0D1H) END MovDlCl ; (* 8A D1: MOV DL,CL *)
  552. PROCEDURE XchgAxDi ; BEGIN B (87H) ; B (0C7H) END XchgAxDi ; (* 87 C7: AX<->DI *)
  553. PROCEDURE AddAxDi ; BEGIN B (03H) ; B (0C7H) END AddAxDi ; (* 03 C7: AX:=AX+DI *)
  554. PROCEDURE MovSiBx ; BEGIN B (89H) ; B (0DEH) END MovSiBx ; (* 89 DE: MOV SI,BX *)
  555. PROCEDURE MovDiDx ; BEGIN B (89H) ; B (0D7H) END MovDiDx ;
  556. PROCEDURE MovDiAx ; BEGIN B (89H) ; B (0C7H) END MovDiAx ;
  557. PROCEDURE AddDiAx ; BEGIN B (1H) ; B (0C7H) END AddDiAx ;
  558. (* The register-pair emitters strcat (EmitStrCat) needs. Two registers, so
  559. the audit's two-register pattern reads them positionally: `AddDxAx' is
  560. 01 C2 = ADD DX,AX (01 /r is `op r/m16,r16'', DX is r/m and AX is reg), and
  561. the mirror `SubDxAx' is 29 C2. MovCxAx is 89 C1 (89 /r: reg is the
  562. source, so CX is r/m and AX is reg). MovDiSi is 8B FE (8B /r: reg is the
  563. destination). *)
  564. PROCEDURE MovCxAx ; BEGIN B (89H) ; B (0C1H) END MovCxAx ; (* 89 C1: MOV CX,AX *)
  565. PROCEDURE MovDiSi ; BEGIN B (8BH) ; B (0FEH) END MovDiSi ; (* 8B FE: MOV DI,SI *)
  566. PROCEDURE AddDxAx ; BEGIN B (1H) ; B (0C2H) END AddDxAx ; (* 01 C2: DX:=DX+AX *)
  567. PROCEDURE SubDxAx ; BEGIN B (29H) ; B (0C2H) END SubDxAx ; (* 29 C2: DX:=DX-AX *)
  568. PROCEDURE AddDiDx ; BEGIN B (1H) ; B (0D7H) END AddDiDx ; (* 01 D7: DI:=DI+DX *)
  569. PROCEDURE StSiCl ; BEGIN B (88H) ; B (0CH) END StSiCl ; (* 88 0C: [SI]:=CL *)
  570. PROCEDURE IncBx ; BEGIN B (43H) END IncBx ;
  571. PROCEDURE LdAlBx ; BEGIN B (8AH) ; B (07H) END LdAlBx ; (* 8A 07: AL:=[BX] *)
  572. PROCEDURE MovAlBl ; BEGIN B (8AH) ; B (0C3H) END MovAlBl ; (* 8A C3: AL:=BL *)
  573. (* 8A 07 and 8A C3 are one letter apart and do opposite things: the
  574. first reads the byte AT the pointer in BX, the second takes the low
  575. byte OF BX. getch's pushback path wanted the second and used the
  576. first, so it read memory 010Ah - the address of the character - and
  577. returned whatever was there. Hence the Ld/Mov split. *)
  578. PROCEDURE MovClBx ; BEGIN B (8AH) ; B (0FH) END MovClBx ; (* 8A 0F: CL:=[BX] *)
  579. PROCEDURE XorBxBx ; BEGIN B (31H) ; B (0DBH) END XorBxBx ; (* 31 DB: BX:=0 *)
  580. PROCEDURE MovBlDl ; BEGIN B (8AH) ; B (0DAH) END MovBlDl ; (* 8A DA: BL:=DL *)
  581. PROCEDURE MovBhAh ; BEGIN B (88H) ; B (0E7H) END MovBhAh ; (* 88 E7: BH:=AH *)
  582. PROCEDURE MovDxAx ; BEGIN B (89H) ; B (0C2H) END MovDxAx ; (* 89 C2: DX:=AX *)
  583. PROCEDURE MovAxDx ; BEGIN B (89H) ; B (0D0H) END MovAxDx ; (* 89 D0: AX:=DX *)
  584. PROCEDURE MovSiBp4 ; BEGIN B (8BH) ; B (76H) ; B (4) END MovSiBp4 ; (* SI:=[BP+4] *)
  585. PROCEDURE MovSiBp6 ; BEGIN B (8BH) ; B (76H) ; B (6) END MovSiBp6 ; (* SI:=[BP+6] *)
  586. PROCEDURE LdAxSi ; BEGIN B (8BH) ; B (04H) END LdAxSi ; (* 8B 04: AX:=[SI] *)
  587. PROCEDURE StDiOffBx (off : CARDINAL) ;
  588. BEGIN B (89H) ; B (5DH) ; B (off MOD 256) END StDiOffBx ; (* MOV [DI+o],BX *)
  589. PROCEDURE StDiOffDx (off : CARDINAL) ;
  590. BEGIN B (89H) ; B (55H) ; B (off MOD 256) END StDiOffDx ; (* MOV [DI+o],DX *)
  591. PROCEDURE Js8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (78H, nm) END Js8 ;
  592. PROCEDURE Jns8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (79H, nm) END Jns8 ;
  593. PROCEDURE Jmp8 (nm : ARRAY OF CHAR ) ;
  594. BEGIN B (0EBH) ; AddFix (0, rpos, nm, 0) ; B (0) END Jmp8 ;
  595. PROCEDURE MovAxImm (v : CARDINAL ) ; BEGIN B (0B8H) ; W (v) END MovAxImm ;
  596. PROCEDURE XchgDlDh ; BEGIN B (86H) ; B (0D6H) END XchgDlDh ; (* 86 D6: DL<->DH *)
  597. PROCEDURE DecAl ; BEGIN B (0FEH) ; B (0C8H) END DecAl ; (* FE C8 *)
  598. PROCEDURE ShlDx1 ; BEGIN B (0D1H) ; B (0E2H) END ShlDx1 ; (* D1 E2: SHL DX,1 *)
  599. PROCEDURE NegDx ; BEGIN B (0F7H) ; B (0DAH) END NegDx ; (* F7 DA: NEG DX *)
  600. PROCEDURE CmpDh0 ; BEGIN B (80H) ; B (0FEH) ; B (0) END CmpDh0 ; (* 80 FE 00 *)
  601. PROCEDURE CmpBh0 ; BEGIN B (80H) ; B (0FFH) ; B (0) END CmpBh0 ; (* 80 FF 00 *)
  602. PROCEDURE CmpDx0 ; BEGIN B (83H) ; B (0FAH) ; B (0) END CmpDx0 ; (* 83 FA 00 *)
  603. PROCEDURE CmpAl0 ; BEGIN B (3CH) ; B (0) END CmpAl0 ; (* 3C 00 *)
  604. PROCEDURE AndDhImm (v : CARDINAL) ;
  605. BEGIN B (80H) ; B (0E6H) ; B (v MOD 256) END AndDhImm ; (* 80 E6 imm *)
  606. (* --- the MBF core's register moves and flag helpers --------------------- *)
  607. PROCEDURE MovAxCx ; BEGIN B (89H) ; B (0C8H) END MovAxCx ; (* AX:=CX *)
  608. PROCEDURE MovBxSi ; BEGIN B (89H) ; B (0F3H) END MovBxSi ; (* BX:=SI *)
  609. PROCEDURE MovDxDi ; BEGIN B (89H) ; B (0FAH) END MovDxDi ; (* DX:=DI *)
  610. PROCEDURE MovBxCx ; BEGIN B (89H) ; B (0CBH) END MovBxCx ; (* BX:=CX *)
  611. PROCEDURE MovBxDx ; BEGIN B (89H) ; B (0D3H) END MovBxDx ; (* BX:=DX *)
  612. PROCEDURE MovAhBh ; BEGIN B (88H) ; B (0FCH) END MovAhBh ; (* AH:=BH *)
  613. PROCEDURE MovAhBl ; BEGIN B (88H) ; B (0DCH) END MovAhBl ; (* AH:=BL *)
  614. PROCEDURE MovBlBh ; BEGIN B (88H) ; B (0FBH) END MovBlBh ; (* BL:=BH *)
  615. PROCEDURE MovBhDl ; BEGIN B (88H) ; B (0D7H) END MovBhDl ; (* BH:=DL *)
  616. PROCEDURE MovDlDh ; BEGIN B (88H) ; B (0F2H) END MovDlDh ; (* DL:=DH *)
  617. PROCEDURE MovDhDl ; BEGIN B (88H) ; B (0D6H) END MovDhDl ; (* DH:=DL *)
  618. PROCEDURE MovDlBh ; BEGIN B (88H) ; B (0FAH) END MovDlBh ; (* DL:=BH *)
  619. PROCEDURE MovBlAh ; BEGIN B (88H) ; B (0E3H) END MovBlAh ; (* BL:=AH *)
  620. PROCEDURE XchgAxCx ; BEGIN B (91H) END XchgAxCx ; (* XCHG AX,CX *)
  621. PROCEDURE XchgBxSi ; BEGIN B (87H) ; B (0F3H) END XchgBxSi ;
  622. PROCEDURE XchgDxDi ; BEGIN B (87H) ; B (0FAH) END XchgDxDi ;
  623. PROCEDURE XchgAhCh ; BEGIN B (86H) ; B (0ECH) END XchgAhCh ;
  624. PROCEDURE XorDhDh ; BEGIN B (30H) ; B (0F6H) END XorDhDh ; (* XOR DH,DH *)
  625. PROCEDURE XorAhAh ; BEGIN B (30H) ; B (0E4H) END XorAhAh ; (* XOR AH,AH *)
  626. PROCEDURE NotAh ; BEGIN B (0F6H) ; B (0D4H) END NotAh ; (* NOT AH *)
  627. PROCEDURE NotBx ; BEGIN B (0F7H) ; B (0D3H) END NotBx ; (* NOT BX *)
  628. PROCEDURE NotDx ; BEGIN B (0F7H) ; B (0D2H) END NotDx ; (* NOT DX *)
  629. PROCEDURE ShrDx1 ; BEGIN B (0D1H) ; B (0EAH) END ShrDx1 ; (* SHR DX,1 *)
  630. PROCEDURE RcrBx1 ; BEGIN B (0D1H) ; B (0DBH) END RcrBx1 ; (* RCR BX,1 *)
  631. PROCEDURE RcrDx1 ; BEGIN B (0D1H) ; B (0DAH) END RcrDx1 ; (* RCR DX,1 *)
  632. PROCEDURE RcrAh1 ; BEGIN B (0D0H) ; B (0DCH) END RcrAh1 ; (* RCR AH,1 *)
  633. PROCEDURE RclBx1 ; BEGIN B (0D1H) ; B (0D3H) END RclBx1 ; (* RCL BX,1 *)
  634. PROCEDURE RclDx1 ; BEGIN B (0D1H) ; B (0D2H) END RclDx1 ; (* RCL DX,1 *)
  635. PROCEDURE ShlAh1 ; BEGIN B (0D0H) ; B (0E4H) END ShlAh1 ; (* SHL AH,1 *)
  636. PROCEDURE IncAl ; BEGIN B (0FEH) ; B (0C0H) END IncAl ; (* INC AL *)
  637. PROCEDURE DecCl ; BEGIN B (0FEH) ; B (0C9H) END DecCl ; (* DEC CL *)
  638. PROCEDURE SarCl1 ; BEGIN B (0D0H) ; B (0F9H) END SarCl1 ; (* SAR CL,1 *)
  639. PROCEDURE Stc ; BEGIN B (0F9H) END Stc ; (* STC *)
  640. PROCEDURE CmpAlCl ; BEGIN B (38H) ; B (0C8H) END CmpAlCl ; (* CMP AL,CL *)
  641. PROCEDURE CmpCl0 ; BEGIN B (80H) ; B (0F9H) ; B (0) END CmpCl0 ; (* 80 F9 00 *)
  642. PROCEDURE MovBhBl ; BEGIN B (88H) ; B (0DFH) END MovBhBl ; (* BH:=BL *)
  643. PROCEDURE CmpCh0 ; BEGIN B (80H) ; B (0FDH) ; B (0) END CmpCh0 ;
  644. PROCEDURE AddAlCl ; BEGIN B (00H) ; B (0C8H) END AddAlCl ; (* AL:=AL+CL *)
  645. PROCEDURE AddAlImm (v : CARDINAL) ;
  646. BEGIN B (04H) ; B (v MOD 256) END AddAlImm ;
  647. PROCEDURE MovSiImm (v : CARDINAL) ;
  648. BEGIN B (0BEH) ; W (v) END MovSiImm ;
  649. PROCEDURE RcrCh1 ; BEGIN B (0D0H) ; B (0DDH) END RcrCh1 ; (* RCR CH,1 *)
  650. PROCEDURE RclCh1 ; BEGIN B (0D0H) ; B (0D5H) END RclCh1 ; (* RCL CH,1 *)
  651. PROCEDURE Lahf ; BEGIN B (9FH) END Lahf ;
  652. PROCEDURE Sahf ; BEGIN B (9EH) END Sahf ;
  653. PROCEDURE XorCxDi ; BEGIN B (31H) ; B (0F9H) END XorCxDi ; (* CX:=CX XOR DI *)
  654. PROCEDURE XorDxDi ; BEGIN B (31H) ; B (0FAH) END XorDxDi ; (* DX:=DX XOR DI *)
  655. PROCEDURE NotCh ; BEGIN B (0F6H) ; B (0D5H) END NotCh ; (* NOT CH *)
  656. PROCEDURE AndDxDi ; BEGIN B (21H) ; B (0FAH) END AndDxDi ; (* AND DX,DI *)
  657. PROCEDURE AndBxSi ; BEGIN B (21H) ; B (0F3H) END AndBxSi ; (* AND BX,SI *)
  658. PROCEDURE AndAhCh ; BEGIN B (20H) ; B (0ECH) END AndAhCh ; (* AND AH,CH *)
  659. PROCEDURE CmpDxDi ; BEGIN B (39H) ; B (0FAH) END CmpDxDi ; (* CMP DX,DI *)
  660. PROCEDURE CmpBxSi ; BEGIN B (39H) ; B (0F3H) END CmpBxSi ; (* CMP BX,SI *)
  661. PROCEDURE CmpAhCh ; BEGIN B (38H) ; B (0ECH) END CmpAhCh ; (* CMP AH,CH *)
  662. PROCEDURE MovDhImm (v : CARDINAL) ;
  663. BEGIN B (0B6H) ; B (v MOD 256) END MovDhImm ; (* MOV DH,imm8 *)
  664. PROCEDURE AndChImm (v : CARDINAL) ;
  665. BEGIN B (80H) ; B (0E5H) ; B (v MOD 256) END AndChImm ;
  666. PROCEDURE MovDiData (delta : CARDINAL) ;
  667. BEGIN B (0BFH) ; Dd (delta) END MovDiData ;
  668. PROCEDURE LdChDiOff ; BEGIN B (8AH) ; B (2DH) END LdChDiOff ; (* CH:=[DI] *)
  669. PROCEDURE AddAhData (delta : CARDINAL) ;
  670. BEGIN B (02H) ; B (26H) ; Dd (delta) END AddAhData ; (* ADD AH,[Vx] *)
  671. PROCEDURE AdcBxData (delta : CARDINAL) ;
  672. BEGIN B (13H) ; B (1EH) ; Dd (delta) END AdcBxData ; (* ADC BX,[Vx] *)
  673. PROCEDURE AdcDxData (delta : CARDINAL) ;
  674. BEGIN B (13H) ; B (16H) ; Dd (delta) END AdcDxData ; (* ADC DX,[Vx] *)
  675. PROCEDURE StDataCh (delta : CARDINAL) ;
  676. BEGIN B (88H) ; B (2EH) ; Dd (delta) END StDataCh ;
  677. PROCEDURE StDataCx (delta : CARDINAL) ;
  678. BEGIN B (89H) ; B (0EH) ; Dd (delta) END StDataCx ;
  679. PROCEDURE StDataSi (delta : CARDINAL) ;
  680. BEGIN B (89H) ; B (36H) ; Dd (delta) END StDataSi ;
  681. PROCEDURE StDataAx (delta : CARDINAL) ;
  682. BEGIN B (0A3H) ; Dd (delta) END StDataAx ;
  683. PROCEDURE StDataBx (delta : CARDINAL) ;
  684. BEGIN B (89H) ; B (1EH) ; Dd (delta) END StDataBx ;
  685. PROCEDURE StDataDx (delta : CARDINAL) ;
  686. BEGIN B (89H) ; B (16H) ; Dd (delta) END StDataDx ;
  687. PROCEDURE StDataAl (delta : CARDINAL) ;
  688. BEGIN B (0A2H) ; Dd (delta) END StDataAl ;
  689. PROCEDURE MovCxData (delta : CARDINAL) ;
  690. BEGIN B (8BH) ; B (0EH) ; Dd (delta) END MovCxData ;
  691. PROCEDURE MovBxData (delta : CARDINAL) ;
  692. BEGIN B (8BH) ; B (1EH) ; Dd (delta) END MovBxData ;
  693. PROCEDURE MovDxData (delta : CARDINAL) ;
  694. BEGIN B (8BH) ; B (16H) ; Dd (delta) END MovDxData ;
  695. PROCEDURE CmpDxData (delta : CARDINAL) ;
  696. BEGIN B (3BH) ; B (16H) ; Dd (delta) END CmpDxData ;
  697. PROCEDURE CmpBxData (delta : CARDINAL) ;
  698. BEGIN B (3BH) ; B (1EH) ; Dd (delta) END CmpBxData ;
  699. PROCEDURE CmpAhData (delta : CARDINAL) ;
  700. BEGIN B (3AH) ; B (26H) ; Dd (delta) END CmpAhData ;
  701. PROCEDURE SubAhData (delta : CARDINAL) ;
  702. BEGIN B (2AH) ; B (26H) ; Dd (delta) END SubAhData ; (* SUB.B AH,remul21a: ignores the carry *)
  703. (* --- the real formatter/parser's instruction set (fmtreal/ascreal) ------ *)
  704. PROCEDURE Jl8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (7CH, nm) END Jl8 ;
  705. PROCEDURE Jg8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (7FH, nm) END Jg8 ;
  706. PROCEDURE MovAxBx ; BEGIN B (89H) ; B (0D8H) END MovAxBx ; (* AX:=BX *)
  707. PROCEDURE MovAxSi ; BEGIN B (89H) ; B (0F0H) END MovAxSi ; (* AX:=SI *)
  708. PROCEDURE MovSiDi ; BEGIN B (89H) ; B (0FEH) END MovSiDi ; (* SI:=DI *)
  709. PROCEDURE MovBlCl ; BEGIN B (88H) ; B (0CBH) END MovBlCl ; (* BL:=CL *)
  710. PROCEDURE MovChDh ; BEGIN B (88H) ; B (0F5H) END MovChDh ; (* CH:=DH *)
  711. PROCEDURE MovChCl ; BEGIN B (88H) ; B (0CDH) END MovChCl ; (* CH:=CL *)
  712. PROCEDURE MovDlCh ; BEGIN B (88H) ; B (0EAH) END MovDlCh ; (* DL:=CH *)
  713. PROCEDURE MovDhAl ; BEGIN B (88H) ; B (0C6H) END MovDhAl ; (* DH:=AL *)
  714. PROCEDURE MovClAh ; BEGIN B (88H) ; B (0E1H) END MovClAh ; (* CL:=AH *)
  715. PROCEDURE MovClAl ; BEGIN B (88H) ; B (0C1H) END MovClAl ; (* CL:=AL *)
  716. PROCEDURE MovAhAl ; BEGIN B (88H) ; B (0C4H) END MovAhAl ; (* AH:=AL *)
  717. PROCEDURE MovAlCl ; BEGIN B (88H) ; B (0C8H) END MovAlCl ; (* AL:=CL *)
  718. PROCEDURE MovChImm (v : CARDINAL) ;
  719. BEGIN B (0B5H) ; B (v MOD 256) END MovChImm ; (* MOV CH,imm8 *)
  720. PROCEDURE CmpDxImm (v : CARDINAL) ;
  721. BEGIN B (81H) ; B (0FAH) ; W (v) END CmpDxImm ; (* CMP DX,imm16 *)
  722. PROCEDURE CmpAxImm (v : CARDINAL) ;
  723. BEGIN B (3DH) ; W (v) END CmpAxImm ; (* CMP AX,imm16 *)
  724. PROCEDURE CmpDl0 ; BEGIN B (80H) ; B (0FAH) ; B (0) END CmpDl0 ;
  725. PROCEDURE TestChImm (v : CARDINAL) ;
  726. BEGIN B (0F6H) ; B (0C5H) ; B (v MOD 256) END TestChImm ; (* TEST CH,imm8 *)
  727. PROCEDURE TestDiOffImm (off, v : CARDINAL) ;
  728. BEGIN B (0F6H) ; B (45H) ; B (off MOD 256) ; B (v MOD 256) END TestDiOffImm ;
  729. PROCEDURE AndClImm (v : CARDINAL) ;
  730. BEGIN B (80H) ; B (0E1H) ; B (v MOD 256) END AndClImm ; (* AND CL,imm8 *)
  731. PROCEDURE AndBlImm (v : CARDINAL) ;
  732. BEGIN B (80H) ; B (0E3H) ; B (v MOD 256) END AndBlImm ; (* AND BL,imm8 *)
  733. PROCEDURE OrChImm (v : CARDINAL) ;
  734. BEGIN B (80H) ; B (0CDH) ; B (v MOD 256) END OrChImm ; (* OR CH,imm8 *)
  735. PROCEDURE AddChImm (v : CARDINAL) ;
  736. BEGIN B (80H) ; B (0C5H) ; B (v MOD 256) END AddChImm ; (* ADD CH,imm8 *)
  737. PROCEDURE AddClImm (v : CARDINAL) ;
  738. BEGIN B (80H) ; B (0C1H) ; B (v MOD 256) END AddClImm ; (* ADD CL,imm8 *)
  739. PROCEDURE ShrChCl ; BEGIN B (0D2H) ; B (0EDH) END ShrChCl ; (* SHR CH,CL *)
  740. PROCEDURE ShrBl1 ; BEGIN B (0D0H) ; B (0EBH) END ShrBl1 ; (* SHR BL,1 *)
  741. PROCEDURE ShlAx1 ; BEGIN B (0D1H) ; B (0E0H) END ShlAx1 ; (* SHL AX,1 *)
  742. PROCEDURE ShlAl1 ; BEGIN B (0D0H) ; B (0E0H) END ShlAl1 ; (* SHL AL,1 *)
  743. PROCEDURE AddBlBh ; BEGIN B (00H) ; B (0FBH) END AddBlBh ; (* BL:=BL+BH *)
  744. PROCEDURE XorBhBh ; BEGIN B (30H) ; B (0FFH) END XorBhBh ; (* BH:=0 *)
  745. PROCEDURE AddAlAh ; BEGIN B (00H) ; B (0E0H) END AddAlAh ; (* AL:=AL+AH *)
  746. PROCEDURE AddAxCx ; BEGIN B (03H) ; B (0C1H) END AddAxCx ; (* AX:=AX+CX *)
  747. PROCEDURE AddAxBx ; BEGIN B (03H) ; B (0C3H) END AddAxBx ; (* AX:=AX+BX *)
  748. PROCEDURE AddSiAx ; BEGIN B (01H) ; B (0C6H) END AddSiAx ; (* SI:=SI+AX *)
  749. PROCEDURE CmpAxBx ; BEGIN B (39H) ; B (0D8H) END CmpAxBx ; (* CMP AX,BX *)
  750. PROCEDURE SubAxSi ; BEGIN B (2BH) ; B (0C6H) END SubAxSi ; (* AX:=AX-SI *)
  751. PROCEDURE MovSiDx ; BEGIN B (89H) ; B (0D6H) END MovSiDx ; (* SI:=DX *)
  752. PROCEDURE SubAxCx ; BEGIN B (2BH) ; B (0C1H) END SubAxCx ; (* AX:=AX-CX *)
  753. PROCEDURE AdcBxCx ; BEGIN B (13H) ; B (0D9H) END AdcBxCx ; (* BX:=BX+CX+CF *)
  754. PROCEDURE AdcDxCx ; BEGIN B (13H) ; B (0D1H) END AdcDxCx ; (* DX:=DX+CX+CF *)
  755. PROCEDURE AddAxDx ; BEGIN B (03H) ; B (0C2H) END AddAxDx ; (* AX:=AX+DX *)
  756. PROCEDURE AddBxAx ; BEGIN B (03H) ; B (0D8H) END AddBxAx ; (* BX:=BX+AX *)
  757. PROCEDURE AddBxImm (v : CARDINAL) ;
  758. BEGIN B (81H) ; B (0C3H) ; W (v) END AddBxImm ; (* ADD BX,imm16 *)
  759. PROCEDURE SubAxDx ; BEGIN B (2BH) ; B (0C2H) END SubAxDx ; (* AX:=AX-DX *)
  760. PROCEDURE AddAxImm (v : CARDINAL) ;
  761. BEGIN B (05H) ; W (v) END AddAxImm ; (* ADD AX,imm16 *)
  762. PROCEDURE DecBx ; BEGIN B (4BH) END DecBx ; (* DEC BX *)
  763. PROCEDURE DecCh ; BEGIN B (0FEH) ; B (0CDH) END DecCh ; (* DEC CH *)
  764. PROCEDURE DecDl ; BEGIN B (0FEH) ; B (0CAH) END DecDl ; (* DEC DL *)
  765. PROCEDURE NegCl ; BEGIN B (0F6H) ; B (0D9H) END NegCl ; (* NEG CL *)
  766. PROCEDURE Cbw ; BEGIN B (98H) END Cbw ; (* AL -> AX *)
  767. PROCEDURE ImulDx ; BEGIN B (0F7H) ; B (0EAH) END ImulDx ; (* IMUL DX *)
  768. PROCEDURE StSiImm (v : CARDINAL) ;
  769. BEGIN B (0C7H) ; B (04H) ; W (v) END StSiImm ; (* [SI]:=imm16 *)
  770. PROCEDURE StSiAl ; BEGIN B (88H) ; B (04H) END StSiAl ; (* [SI]:=AL *)
  771. PROCEDURE StSiCh ; BEGIN B (88H) ; B (2CH) END StSiCh ; (* [SI]:=CH *)
  772. PROCEDURE StBxAl ; BEGIN B (88H) ; B (07H) END StBxAl ; (* [BX]:=AL *)
  773. PROCEDURE StBxCl ; BEGIN B (88H) ; B (0FH) END StBxCl ; (* [BX]:=CL *)
  774. PROCEDURE StBxAx ; BEGIN B (89H) ; B (07H) END StBxAx ; (* [BX]:=AX *)
  775. PROCEDURE IncAx ; BEGIN B (40H) END IncAx ; (* INC AX *)
  776. PROCEDURE XorChCh ; BEGIN B (30H) ; B (0EDH) END XorChCh ; (* XOR CH,CH *)
  777. PROCEDURE CmpAxCx ; BEGIN B (39H) ; B (0C8H) END CmpAxCx ; (* CMP AX,CX *)
  778. PROCEDURE CmpAxSi ; BEGIN B (39H) ; B (0F0H) END CmpAxSi ; (* CMP AX,SI *)
  779. PROCEDURE AddBxDx ; BEGIN B (01H) ; B (0D3H) END AddBxDx ; (* BX:=BX+DX *)
  780. PROCEDURE LdAlSi ; BEGIN B (8AH) ; B (04H) END LdAlSi ; (* AL:=[SI] *)
  781. PROCEDURE LdClBx ; BEGIN B (8AH) ; B (0FH) END LdClBx ; (* CL:=[BX] *)
  782. PROCEDURE LdAlDi ; BEGIN B (8AH) ; B (05H) END LdAlDi ; (* AL:=[DI] *)
  783. PROCEDURE XorDiOffImm (off, v : CARDINAL) ;
  784. BEGIN B (80H) ; B (75H) ; B (off MOD 256) ; B (v MOD 256) END XorDiOffImm ;
  785. PROCEDURE LdClSi ; BEGIN B (8AH) ; B (0CH) END LdClSi ; (* CL:=[SI] *)
  786. PROCEDURE LdAxDi ; BEGIN B (8BH) ; B (05H) END LdAxDi ; (* AX:=[DI] *)
  787. PROCEDURE LdBxDiOff (off : CARDINAL) ;
  788. BEGIN B (8BH) ; B (5DH) ; B (off MOD 256) END LdBxDiOff ; (* [DI+off] *)
  789. PROCEDURE LdDxDiOff (off : CARDINAL) ;
  790. BEGIN B (8BH) ; B (55H) ; B (off MOD 256) END LdDxDiOff ; (* [DI+off] *)
  791. PROCEDURE LeaDiBxData (delta : CARDINAL) ;
  792. BEGIN B (8DH) ; B (0BFH) ; Dd (delta) END LeaDiBxData ; (* LEA DI,[BX+data] *)
  793. PROCEDURE MovSiData (delta : CARDINAL) ;
  794. BEGIN B (0BEH) ; Dd (delta) END MovSiData ;
  795. PROCEDURE MovBxDataAddr (delta : CARDINAL) ;
  796. BEGIN B (0BBH) ; Dd (delta) END MovBxDataAddr ;
  797. PROCEDURE CmpSiData (delta : CARDINAL) ;
  798. BEGIN B (81H) ; B (0FEH) ; Dd (delta) END CmpSiData ;
  799. PROCEDURE MovAxData (delta : CARDINAL) ;
  800. BEGIN B (8BH) ; B (06H) ; Dd (delta) END MovAxData ;
  801. PROCEDURE LdClData (delta : CARDINAL) ;
  802. BEGIN B (8AH) ; B (0EH) ; Dd (delta) END LdClData ;
  803. PROCEDURE DecData (delta : CARDINAL) ;
  804. BEGIN B (0FEH) ; B (0EH) ; Dd (delta) END DecData ;
  805. PROCEDURE CmpBxByte (v : CARDINAL) ;
  806. BEGIN B (80H) ; B (3FH) ; B (v MOD 256) END CmpBxByte ; (* CMP [BX],imm8 *)
  807. PROCEDURE MovBxByte (v : CARDINAL) ;
  808. BEGIN B (0C6H) ; B (07H) ; B (v MOD 256) END MovBxByte ; (* [BX]:=imm8 *)
  809. PROCEDURE MovBxOff1Byte (v : CARDINAL) ;
  810. BEGIN B (0C6H) ; B (47H) ; B (1) ; B (v MOD 256) END MovBxOff1Byte ;
  811. PROCEDURE IncBxByte ; BEGIN B (0FEH) ; B (07H) END IncBxByte ; (* INC [BX] *)
  812. PROCEDURE MovDiBp6 ; BEGIN B (8BH) ; B (7EH) ; B (6) END MovDiBp6 ;
  813. PROCEDURE MovDxBp6 ; BEGIN B (8BH) ; B (56H) ; B (6) END MovDxBp6 ;
  814. PROCEDURE PushBp6 ; BEGIN B (0FFH) ; B (76H) ; B (6) END PushBp6 ;
  815. PROCEDURE MovCxBp6 ; BEGIN B (8BH) ; B (4EH) ; B (6) END MovCxBp6 ;
  816. PROCEDURE MovCxBp4 ; BEGIN B (8BH) ; B (4EH) ; B (4) END MovCxBp4 ;
  817. PROCEDURE MovBxBp8 ; BEGIN B (8BH) ; B (5EH) ; B (8) END MovBxBp8 ;
  818. PROCEDURE MovBxBp10 ; BEGIN B (8BH) ; B (5EH) ; B (0AH) END MovBxBp10 ;
  819. PROCEDURE MovSiBp8 ; BEGIN B (8BH) ; B (76H) ; B (8) END MovSiBp8 ;
  820. PROCEDURE MovSiBp10 ; BEGIN B (8BH) ; B (76H) ; B (0AH) END MovSiBp10 ;
  821. PROCEDURE LdDlDi ; BEGIN B (8AH) ; B (15H) END LdDlDi ; (* DL:=[DI] *)
  822. PROCEDURE AndAlDl ; BEGIN B (20H) ; B (0D0H) END AndAlDl ; (* AL:=AL AND DL *)
  823. PROCEDURE OrAlDl ; BEGIN B (08H) ; B (0D0H) END OrAlDl ; (* AL:=AL OR DL *)
  824. PROCEDURE NotDl ; BEGIN B (0F6H) ; B (0D2H) END NotDl ; (* NOT DL *)
  825. PROCEDURE NotAl ; BEGIN B (0F6H) ; B (0D0H) END NotAl ; (* NOT AL *)
  826. PROCEDURE MovBlAl ; BEGIN B (88H) ; B (0C3H) END MovBlAl ; (* BL:=AL *)
  827. PROCEDURE AndAlDh ; BEGIN B (20H) ; B (0F0H) END AndAlDh ; (* AL:=AL AND DH *)
  828. PROCEDURE CmpBlDh ; BEGIN B (38H) ; B (0F3H) END CmpBlDh ; (* CMP BL,DH *)
  829. PROCEDURE AndAxImm (v : CARDINAL) ;
  830. BEGIN B (25H) ; W (v) END AndAxImm ; (* AND AX,imm16 *)
  831. PROCEDURE TestAxImm (v : CARDINAL) ;
  832. BEGIN B (0A9H) ; W (v) END TestAxImm ; (* TEST AX,imm16 *)
  833. PROCEDURE AndAhImm (v : CARDINAL) ;
  834. BEGIN B (80H) ; B (0E4H) ; B (v MOD 256) END AndAhImm ; (* AND AH,imm8 *)
  835. PROCEDURE TestAhImm (v : CARDINAL) ;
  836. BEGIN B (0F6H) ; B (0C4H) ; B (v MOD 256) END TestAhImm ; (* TEST AH,imm8 *)
  837. PROCEDURE MovAlAh ; BEGIN B (88H) ; B (0E0H) END MovAlAh ; (* AL:=AH *)
  838. PROCEDURE CmpAhImm (v : CARDINAL) ;
  839. BEGIN B (80H) ; B (0FCH) ; B (v MOD 256) END CmpAhImm ; (* CMP AH,imm8 *)
  840. PROCEDURE DecDh ; BEGIN B (0FEH) ; B (0CEH) END DecDh ; (* DEC DH *)
  841. PROCEDURE IncDi ; BEGIN B (47H) END IncDi ;
  842. PROCEDURE MovDxBp4 ; BEGIN B (8BH) ; B (56H) ; B (4) END MovDxBp4 ;
  843. PROCEDURE MovDiBp8 ; BEGIN B (8BH) ; B (7EH) ; B (8) END MovDiBp8 ;
  844. PROCEDURE SbbBxData (delta : CARDINAL) ;
  845. BEGIN B (1BH) ; B (1EH) ; Dd (delta) END SbbBxData ;
  846. PROCEDURE SbbDxData (delta : CARDINAL) ;
  847. BEGIN B (1BH) ; B (16H) ; Dd (delta) END SbbDxData ;
  848. PROCEDURE StDiCh ; BEGIN B (88H) ; B (2DH) END StDiCh ; (* [DI]:=CH *)
  849. PROCEDURE Cmc ; BEGIN B (0F5H) END Cmc ;
  850. PROCEDURE Clc ; BEGIN B (0F8H) END Clc ;
  851. PROCEDURE IncCl ; BEGIN B (0FEH) ; B (0C1H) END IncCl ;
  852. PROCEDURE DecDi ; BEGIN B (4FH) END DecDi ;
  853. PROCEDURE SubAhCh ; BEGIN B (28H) ; B (0ECH) END SubAhCh ; (* AH:=AH-CH *)
  854. PROCEDURE SubAlDl ; BEGIN B (2AH) ; B (0C2H) END SubAlDl ; (* AL:=AL-DL *)
  855. PROCEDURE IncCh ; BEGIN B (0FEH) ; B (0C5H) END IncCh ; (* INC CH *)
  856. PROCEDURE IncDl ; BEGIN B (0FEH) ; B (0C2H) END IncDl ; (* INC DL *)
  857. PROCEDURE MovClImm (v : CARDINAL) ;
  858. BEGIN B (0B1H) ; B (v MOD 256) END MovClImm ;
  859. PROCEDURE CmpClImm (v : CARDINAL) ;
  860. BEGIN B (80H) ; B (0F9H) ; B (v MOD 256) END CmpClImm ;
  861. PROCEDURE SubClAl ; BEGIN B (28H) ; B (0C1H) END SubClAl ; (* CL:=CL-AL *)
  862. PROCEDURE SubAlCl ; BEGIN B (28H) ; B (0C8H) END SubAlCl ; (* AL:=AL-CL *)
  863. PROCEDURE SubClImm (v : CARDINAL) ;
  864. BEGIN B (80H) ; B (0E9H) ; B (v MOD 256) END SubClImm ;
  865. PROCEDURE AddAhCh ; BEGIN B (00H) ; B (0ECH) END AddAhCh ; (* AH:=AH+CH *)
  866. PROCEDURE AddAhImm (v : CARDINAL) ;
  867. BEGIN B (80H) ; B (0C4H) ; B (v MOD 256) END AddAhImm ;
  868. PROCEDURE AdcBxSi ; BEGIN B (11H) ; B (0F3H) END AdcBxSi ;
  869. PROCEDURE AdcDxDi ; BEGIN B (11H) ; B (0FAH) END AdcDxDi ;
  870. PROCEDURE AdcBxImm (v : CARDINAL) ;
  871. BEGIN B (83H) ; B (0D3H) ; B (v MOD 256) END AdcBxImm ;
  872. PROCEDURE AdcDxImm (v : CARDINAL) ;
  873. BEGIN B (83H) ; B (0D2H) ; B (v MOD 256) END AdcDxImm ;
  874. PROCEDURE SbbBxSi ; BEGIN B (19H) ; B (0F3H) END SbbBxSi ;
  875. PROCEDURE SbbDxDi ; BEGIN B (19H) ; B (0FAH) END SbbDxDi ;
  876. PROCEDURE OrDiImm (v : CARDINAL) ;
  877. BEGIN B (81H) ; B (0CFH) ; W (v) END OrDiImm ; (* DI:=DI OR imm16 *)
  878. PROCEDURE OrDhImm (v : CARDINAL) ;
  879. BEGIN B (80H) ; B (0CEH) ; B (v MOD 256) END OrDhImm ;
  880. PROCEDURE TestDhImm (v : CARDINAL) ;
  881. BEGIN B (0F6H) ; B (0C6H) ; B (v MOD 256) END TestDhImm ;
  882. PROCEDURE StDataImm (delta, v : CARDINAL) ;
  883. BEGIN B (0C7H) ; B (06H) ; Dd (delta) ; W (v) END StDataImm ;
  884. PROCEDURE StDataByte (delta, v : CARDINAL) ;
  885. BEGIN B (0C6H) ; B (06H) ; Dd (delta) ; B (v MOD 256) END StDataByte ;
  886. PROCEDURE StDataCl (delta : CARDINAL) ;
  887. BEGIN B (88H) ; B (0EH) ; Dd (delta) END StDataCl ;
  888. PROCEDURE StDataDi (delta : CARDINAL) ;
  889. BEGIN B (89H) ; B (3EH) ; Dd (delta) END StDataDi ;
  890. PROCEDURE LdAlData (delta : CARDINAL) ;
  891. BEGIN B (0A0H) ; Dd (delta) END LdAlData ;
  892. PROCEDURE XorDiData (delta : CARDINAL) ;
  893. BEGIN B (33H) ; B (3EH) ; Dd (delta) END XorDiData ; (* XOR DI,[Vx] *)
  894. PROCEDURE XorDataDh (delta : CARDINAL) ;
  895. BEGIN B (30H) ; B (36H) ; Dd (delta) END XorDataDh ; (* XOR [Vx],DH *)
  896. PROCEDURE XorDhData (delta : CARDINAL) ;
  897. BEGIN B (32H) ; B (36H) ; Dd (delta) END XorDhData ; (* XOR DH,[Vx] *)
  898. PROCEDURE XorDataImm (delta, v : CARDINAL) ;
  899. BEGIN B (80H) ; B (36H) ; Dd (delta) ; B (v MOD 256) END XorDataImm ;
  900. PROCEDURE AndDataImm (delta, v : CARDINAL) ;
  901. BEGIN B (80H) ; B (26H) ; Dd (delta) ; B (v MOD 256) END AndDataImm ;
  902. PROCEDURE TestDataImm (delta, v : CARDINAL) ;
  903. BEGIN B (0F6H) ; B (06H) ; Dd (delta) ; B (v MOD 256) END TestDataImm ;
  904. PROCEDURE LdCxSiOff (off : CARDINAL) ;
  905. BEGIN B (8BH) ; B (4CH) ; B (off MOD 256) END LdCxSiOff ;
  906. PROCEDURE LdDxSiOff (off : CARDINAL) ;
  907. BEGIN B (8BH) ; B (54H) ; B (off MOD 256) END LdDxSiOff ;
  908. PROCEDURE LdCxDiOff (off : CARDINAL) ;
  909. BEGIN B (8BH) ; B (4DH) ; B (off MOD 256) END LdCxDiOff ;
  910. PROCEDURE LdSiDiOff (off : CARDINAL) ;
  911. BEGIN B (8BH) ; B (75H) ; B (off MOD 256) END LdSiDiOff ;
  912. PROCEDURE LdDiDiOff (off : CARDINAL) ;
  913. BEGIN B (8BH) ; B (7DH) ; B (off MOD 256) END LdDiDiOff ;
  914. PROCEDURE PushBp4 ; BEGIN B (0FFH) ; B (76H) ; B (4) END PushBp4 ; (* PUSH [BP+4] *)
  915. PROCEDURE JccNear (code : CARDINAL ; nm : ARRAY OF CHAR) ;
  916. (* A conditional near jump: the 8086 has no Jcc rel16, so an INVERSE Jcc_s
  917. +3 over an E9 rel16. The inverse is what makes the E9 run when `code'
  918. HOLDS; a raw Jcc here would jump when it does NOT (remult's zero test did
  919. exactly that and multiplied everything to zero). Use this when the target
  920. is more than 127 bytes away; Jcc's rel8 wraps silently otherwise (the
  921. FixUp guard below now refuses that). *)
  922. BEGIN
  923. IF (code MOD 2) = 0 THEN
  924. B (code + 1)
  925. ELSE
  926. B (code - 1)
  927. END ;
  928. B (3) ; B (0E9H) ; AddFix (1, rpos, nm, 0) ; W (0)
  929. END JccNear ;
  930. PROCEDURE JmpNear (nm : ARRAY OF CHAR) ;
  931. BEGIN B (0E9H) ; AddFix (1, rpos, nm, 0) ; W (0) END JmpNear ;
  932. PROCEDURE Call (nm : ARRAY OF CHAR) ;
  933. BEGIN B (0E8H) ; AddFix (1, rpos, nm, 0) ; W (0) END Call ;
  934. PROCEDURE StVxBx (delta : CARDINAL ) ;
  935. (* 89 1E lo hi: MOV [Vx],BX - the mirror of LdBxVx above, and it must use the
  936. same mod=00 / rm=110 and the same Dd fixup kind, or one of the two
  937. addresses a different place. *)
  938. BEGIN
  939. B (89H) ; B (01EH) ; Dd (delta)
  940. END StVxBx ;
  941. PROCEDURE JmpBx ; BEGIN B (0FFH) ; B (0E3H) END JmpBx ; (* FF E3: JMP BX *)
  942. (* JE 74 JNE 75 JB 72 JBE 76 JGE 7D *)
  943. PROCEDURE Je8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (74H, nm) END Je8 ;
  944. PROCEDURE Jne8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (75H, nm) END Jne8 ;
  945. PROCEDURE Jb8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (72H, nm) END Jb8 ;
  946. PROCEDURE Jbe8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (76H, nm) END Jbe8 ;
  947. PROCEDURE Jge8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (7DH, nm) END Jge8 ;
  948. PROCEDURE Jle8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (7EH, nm) END Jle8 ;
  949. PROCEDURE Ja8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (77H, nm) END Ja8 ;
  950. PROCEDURE Jae8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (73H, nm) END Jae8 ;
  951. PROCEDURE Jcxz8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (0E3H, nm) END Jcxz8 ;
  952. PROCEDURE Loop8 (nm : ARRAY OF CHAR ) ; BEGIN Jcc (0E2H, nm) END Loop8 ;
  953. (* ---------------------------------------------------------------- *)
  954. (* the entries *)
  955. (* ---------------------------------------------------------------- *)
  956. PROCEDURE EmitInitMem ;
  957. (* AX = offset of the program header. The header holds, at +4 the base of
  958. the program's data area and at +8 its end, so the globals can be zeroed -
  959. Pascal leaves them undefined, TP3's runtime clears them. Also makes
  960. ES = DS so that any string instruction in the library would work. *)
  961. BEGIN
  962. M ("initmem") ;
  963. MovSiAx ; (* SI = AX = the header offset the caller passed *)
  964. MovDxSi4 ; (* DX = [SI+4] = hdrDS = data base *)
  965. MovCxSi6 ; (* CX = [SI+6] = hdrHeap = data end *)
  966. CmpCxDx ;
  967. Jbe8 ("im_done") ;
  968. MovDiDx ; (* DI = data base *)
  969. M ("im_zero") ;
  970. XorAxAx ; (* AX = 0: the value written into every global.
  971. The caller passes the header offset in AX, so
  972. without this the "zeroing" loop would write the
  973. header offset into all of them. *)
  974. StDiAx ;
  975. AddDi2 ;
  976. CmpDiCx ;
  977. Jb8 ("im_zero") ;
  978. M ("im_done") ;
  979. (* The heap starts at the end of the globals. CX still holds hdrHeap from
  980. the header, including on the no-globals path. *)
  981. MovAxCx ;
  982. StDataAx (D_HEAP) ;
  983. PushDs ; PopEs ;
  984. RetR
  985. END EmitInitMem ;
  986. PROCEDURE EmitEnd ;
  987. (* progend and halt are the same code: the compiler already zeroes AX for
  988. progend, and a HALT argument is discarded at compile time, so both leave
  989. with exit code 0. *)
  990. BEGIN
  991. M ("progend") ;
  992. M ("halt") ;
  993. XorAxAx ;
  994. MovAh (4CH) ;
  995. Int21 ;
  996. RetR
  997. END EmitEnd ;
  998. PROCEDURE EmitStackChk ;
  999. (* called from every procedure prologue. Range and stack checking are not
  1000. compiled in yet, so this must do nothing at all - in particular it must
  1001. not touch a register, because the call site is in the middle of a
  1002. partially evaluated expression. *)
  1003. BEGIN
  1004. M ("stackchk") ;
  1005. RetR
  1006. END EmitStackChk ;
  1007. PROCEDURE EmitGetCh ;
  1008. (* AL = next character, 1Ah at end of input. INT 21h AH=08h reads without
  1009. echoing, so a redirected stdin behaves the same as a keyboard.
  1010. This is TP3's `getbyte' (TPSRC4:94), not a bare INT 21h. getbyte serves a
  1011. buffered character from the file record without advancing the buffer
  1012. pointer when the "char pre-read" flag ($02) is set, and readnum sets that
  1013. flag on the character that ENDS a number - the terminating blank is *not*
  1014. consumed. xreadln is then called and it is the xreadln that consumes the
  1015. newline.
  1016. This matters because `readln(n)' is emitted as xrdint followed by xreadln
  1017. (TPSRC8 prdrdln emits xreadln whenever rdlnflg is set). A getch that
  1018. always consumed its character left xreadln starting on the *following*
  1019. line, so `readln(n); readln(c)' read the char from line 3 of the input.
  1020. One byte of pushback reproduces the flag. *)
  1021. BEGIN
  1022. M ("getch") ;
  1023. LdBxVx (D_PUSH) ; (* BX := the slot's contents *)
  1024. CmpBx0 ;
  1025. Je8 ("gc_dos") ;
  1026. MovAlBl ; (* AL := the pending character, i.e. the
  1027. low byte OF BX - not the byte at [BX] *)
  1028. XorBxBx ;
  1029. StVxBx (D_PUSH) ; (* and empty the slot *)
  1030. RetR ;
  1031. M ("gc_dos") ;
  1032. MovAh (08H) ;
  1033. Int21 ;
  1034. RetR
  1035. END EmitGetCh ;
  1036. PROCEDURE EmitUnGetCh ;
  1037. (* put AL back, so the next getch returns it again. TP3 does this by
  1038. rewinding the buffer pointer; a one-byte slot is the same thing. *)
  1039. BEGIN
  1040. M ("ungetch") ;
  1041. MovDlAl ;
  1042. MovBxImm (1) ; (* BH := 1 marks the slot occupied, so
  1043. a pushed-back NUL is still a
  1044. pushed-back NUL *)
  1045. MovBlDl ;
  1046. StVxBx (D_PUSH) ;
  1047. RetR
  1048. END EmitUnGetCh ;
  1049. PROCEDURE EmitWrInt ;
  1050. (* one signed 16-bit value on the stack. Div CX gives the remainder in DX,
  1051. which is turned into a digit and stored backwards from the end of the
  1052. scratch area, then printed forwards. *)
  1053. BEGIN
  1054. M ("wrint") ;
  1055. PushBp ; MovBpSp ;
  1056. MovAxBp4 ;
  1057. CmpAx0 ;
  1058. Jge8 ("wi_pos") ;
  1059. PushAx ;
  1060. MovAlD (ORD ("-")) ; MovDlAl ; MovAh (2) ; Int21 ;
  1061. PopAx ;
  1062. NegAx ;
  1063. M ("wi_pos") ;
  1064. MovCxImm (10) ;
  1065. MovBxVx (D_NUM + 8) ; (* BX = one past the last digit *)
  1066. MovSiBx ;
  1067. M ("wi_dig") ;
  1068. XorDxDx ;
  1069. DivCx ;
  1070. AddDl (ORD ("0")) ;
  1071. DecSi ;
  1072. StSiDl ;
  1073. CmpAx0 ;
  1074. Jne8 ("wi_dig") ;
  1075. M ("wi_out") ;
  1076. CmpSiBx ;
  1077. Je8 ("wi_done") ;
  1078. MovAlSi ; (* the digit *)
  1079. MovDlAl ; (* DOS AH=02h wants it in DL *)
  1080. MovAh (02H) ; Int21 ;
  1081. IncSi ;
  1082. J8 ("wi_out") ;
  1083. M ("wi_done") ;
  1084. MovSpBp ; PopBp ; RetR
  1085. END EmitWrInt ;
  1086. PROCEDURE EmitWrIntW ;
  1087. (* `writeln(I:2)': an integer in a field of `width' columns, right-justified.
  1088. The caller pushes the VALUE first and the WIDTH second, so the value is at
  1089. [BP+6] and the width at [BP+4]. The number of characters the signed
  1090. decimal will occupy is counted first (one extra when negative); the pad is
  1091. max(width - chars, 0) spaces. The digit loop is wrint's, duplicated rather
  1092. than shared: the two entries have different argument offsets, and a jump
  1093. into the middle of the other entry's frame would be a convention nobody
  1094. could see from either name. *)
  1095. BEGIN
  1096. M ("wrintw") ;
  1097. PushBp ; MovBpSp ;
  1098. (* BX := number of characters: sign + digits *)
  1099. MovBxImm (0) ;
  1100. MovAxBp6 ;
  1101. CmpAx0 ;
  1102. Jge8 ("wiw_abs") ;
  1103. IncBx ; (* the '-' counts as one column *)
  1104. NegAx ;
  1105. M ("wiw_abs") ;
  1106. MovCxImm (10) ;
  1107. M ("wiw_cnt") ;
  1108. IncBx ;
  1109. XorDxDx ;
  1110. DivCx ;
  1111. CmpAx0 ;
  1112. Jne8 ("wiw_cnt") ;
  1113. (* pad := width - chars; only a positive pad prints *)
  1114. MovAxBp4 ;
  1115. SubAxBx ;
  1116. CmpAx0 ;
  1117. Jle8 ("wiw_body") ;
  1118. MovCxAx ;
  1119. M ("wiw_pad") ;
  1120. MovAlD (ORD (' ')) ; MovDlAl ; MovAh (02H) ; Int21 ;
  1121. Loop8 ("wiw_pad") ;
  1122. M ("wiw_body") ;
  1123. (* the signed decimal, exactly as wrint prints it *)
  1124. MovAxBp6 ;
  1125. CmpAx0 ;
  1126. Jge8 ("wiw_pos") ;
  1127. PushAx ;
  1128. MovAlD (ORD ('-')) ; MovDlAl ; MovAh (2) ; Int21 ;
  1129. PopAx ;
  1130. NegAx ;
  1131. M ("wiw_pos") ;
  1132. MovCxImm (10) ;
  1133. MovBxVx (D_NUM + 8) ;
  1134. MovSiBx ;
  1135. M ("wiw_dig") ;
  1136. XorDxDx ;
  1137. DivCx ;
  1138. AddDl (ORD ('0')) ;
  1139. DecSi ;
  1140. StSiDl ;
  1141. CmpAx0 ;
  1142. Jne8 ("wiw_dig") ;
  1143. M ("wiw_out") ;
  1144. CmpSiBx ;
  1145. Je8 ("wiw_done") ;
  1146. MovAlSi ;
  1147. MovDlAl ;
  1148. MovAh (02H) ; Int21 ;
  1149. IncSi ;
  1150. J8 ("wiw_out") ;
  1151. M ("wiw_done") ;
  1152. MovSpBp ; PopBp ; RetR
  1153. END EmitWrIntW ;
  1154. PROCEDURE EmitWrChar ;
  1155. (* the low byte of the one 16-bit argument, which sits above the return
  1156. address. There is no [SP] addressing in 16-bit mode, so BP stands in for
  1157. the stack pointer -- and it has to be SAVED, because BP is the one register
  1158. a caller is entitled to expect back: the test driver keeps its cursor into
  1159. the case record there, so an entry that borrows BP without returning it
  1160. sends the next call to a garbage address. That is not hypothetical: wrchar
  1161. and wrbool both did it, and "writeln(42) writeln TRUE in one program" hung
  1162. the machine with the record header printed twice, because the driver had
  1163. been sent back to the top of its own loop. Load BP, use it, hand it back.
  1164. MovAlArg4, not MovAlArg2: BP is pushed first, so the argument is four bytes
  1165. up, not two. AL is copied to DL before the DOS call: see the AL twins
  1166. above. *)
  1167. BEGIN
  1168. M ("wrchar") ;
  1169. PushBp ; MovBpSp ;
  1170. MovAlArg4 ;
  1171. MovSpBp ; PopBp ;
  1172. MovDlAl ;
  1173. MovAh (02H) ;
  1174. Int21 ;
  1175. RetR
  1176. END EmitWrChar ;
  1177. PROCEDURE EmitWrInl ;
  1178. (* Write an inline string literal - TP3 TPSRC4 "xwrtinl".
  1179. The compiler emits
  1180. CALL wrtinl <length byte> <character>...
  1181. so the return address on the stack points at the length byte that follows
  1182. the call. POP BX takes that address, CX picks up the length, and the
  1183. routine finishes with JMP BX - returning to just past the last character.
  1184. That is the whole trick: the literal is self-delimiting, so it needs no
  1185. terminator, no length table and no space in the data segment, and it costs
  1186. the code stream only the characters themselves (TPSRC10 "estring" emits
  1187. exactly <length byte><chars> for the same reason).
  1188. Consequently this entry has NO stack argument, unlike WrInt/WrChar: the
  1189. return address has already been consumed by the POP. *)
  1190. BEGIN
  1191. M ("wrtinl") ;
  1192. PopBx ; (* BX := address of the length byte *)
  1193. XorCxCx ;
  1194. MovClBx ; (* CX := length *)
  1195. IncBx ; (* BX -> first character *)
  1196. MovAh (02H) ; (* AH=02h; DL is set per character *)
  1197. Jcxz8 ("wn_end") ; (* empty string -> nothing to do *)
  1198. M ("wn_loop") ;
  1199. LdAlBx ; (* AL := next character *)
  1200. MovDlAl ; (* DOS AH=02h reads DL *)
  1201. Int21 ; (* (preserves every register but AL) *)
  1202. IncBx ;
  1203. Loop8 ("wn_loop") ;
  1204. M ("wn_end") ;
  1205. JmpBx (* resume past the string; no RET here,
  1206. the return address is already gone *)
  1207. END EmitWrInl ;
  1208. PROCEDURE EmitWrStr ;
  1209. (* Write a string VARIABLE - TP3 TPSRC4 "xwrtstr", minus the field-width
  1210. formatting this compiler does not compile yet.
  1211. The compiler emits
  1212. <PUSH the string's address> from EmPushVarAddr
  1213. CALL wrstr
  1214. so the address is the one 16-bit argument on the stack, read through BP
  1215. exactly as WrChar reads its value (there is no [SP] form). The first byte
  1216. at that address is the current LENGTH and the characters follow it, which
  1217. is the layout TPSRC8's `string' type gives a variable: TPSRC8:83 does
  1218. `INC BX / MOV varsize,BX' for `string [n]', so n+1 bytes, length byte
  1219. first.
  1220. The loop is wrtinl's, pointed at the caller's bytes instead of at the
  1221. bytes behind the call: CL := [BX] is the length, BX moves to the first
  1222. character, and one INT 21h/02h prints each one until CX runs out. A
  1223. length of 0 prints nothing, so an empty string is silent. *)
  1224. BEGIN
  1225. M ("wrstr") ;
  1226. PushBp ; MovBpSp ;
  1227. PushBx ; PushCx ; PushAx ;
  1228. MovBxBp4 ; (* BX := the caller's string address *)
  1229. XorCxCx ;
  1230. MovClBx ; (* CX := the length byte at [BX] *)
  1231. IncBx ; (* BX -> the first character *)
  1232. MovAh (02H) ; (* AH=02h; DL is set per character *)
  1233. Jcxz8 ("ws_end") ; (* empty string -> nothing to do *)
  1234. M ("ws_loop") ;
  1235. LdAlBx ; (* AL := next character *)
  1236. MovDlAl ; (* DOS AH=02h reads DL *)
  1237. Int21 ; (* preserves every register but AL *)
  1238. IncBx ;
  1239. Loop8 ("ws_loop") ;
  1240. M ("ws_end") ;
  1241. PopAx ; PopCx ; PopBx ;
  1242. MovSpBp ; PopBp ;
  1243. RetR
  1244. END EmitWrStr ;
  1245. PROCEDURE EmitWrBool ;
  1246. (* 4, not 2, for the reason given on CmpArg4W0: BP is pushed before it is
  1247. borrowed, so the argument is four bytes up. POP BP before the RET, and
  1248. note that CmpArg4W0 has already handed SP back, so the pop lands on the
  1249. saved BP and not on the argument. *)
  1250. BEGIN
  1251. M ("wrbool") ;
  1252. PushBp ;
  1253. CmpArg4W0 ;
  1254. Jne8 ("wb_t") ;
  1255. MovDxVx (D_FALSE) ;
  1256. J8 ("wb_o") ;
  1257. M ("wb_t") ;
  1258. MovDxVx (D_TRUE) ;
  1259. M ("wb_o") ;
  1260. MovAh (09H) ;
  1261. Int21 ;
  1262. PopBp ;
  1263. RetR
  1264. END EmitWrBool ;
  1265. PROCEDURE EmitWrLn ;
  1266. BEGIN
  1267. M ("wrln") ;
  1268. MovDxVx (D_CRLF) ;
  1269. MovAh (09H) ;
  1270. Int21 ;
  1271. RetR
  1272. END EmitWrLn ;
  1273. PROCEDURE EmitWrReal ;
  1274. (* the 6-byte real is on the stack but is not formatted: the compiler does
  1275. not yet load real operands into a form the runtime could read. A visible
  1276. marker beats printing the mantissa as an integer. *)
  1277. BEGIN
  1278. M ("wrreal") ;
  1279. MovDxVx (D_REAL) ;
  1280. MovAh (09H) ;
  1281. Int21 ;
  1282. RetR
  1283. END EmitWrReal ;
  1284. PROCEDURE EmitRdInt ;
  1285. (* Read a number into a 16-bit variable: the caller's address is on the
  1286. stack. The scan itself is EmitRdScan, shared with rdbyte; an entry owns
  1287. its frame, its address load and its store, nothing else. *)
  1288. BEGIN
  1289. M ("rdint") ;
  1290. PushBp ; MovBpSp ;
  1291. PushAx ; PushBx ; PushCx ; PushDx ; PushDi ;
  1292. C8 ("rdscan") ;
  1293. CmpCxV (2) ;
  1294. Je8 ("ri_skip") ; (* nothing entered: leave the variable
  1295. alone, as TP3's xrdint at rdierr does *)
  1296. MovDiBp4 ; (* ...the caller's address into DI... *)
  1297. StDiAx ; (* ...and store the word. MovDiBp4 and
  1298. not a [BX] form: there is none; see
  1299. the note on the store emitters. *)
  1300. M ("ri_skip") ;
  1301. PopDi ; PopDx ; PopCx ; PopBx ; PopAx ;
  1302. MovSpBp ; PopBp ; RetR
  1303. END EmitRdInt ;
  1304. PROCEDURE EmitRdScan ;
  1305. (* The number reader, called by rdint (word store) and by rdbyte (byte
  1306. store). A transcription of TP3 TPSRC4 xrdint/readnum, minus the store:
  1307. rnspace: getbyte ; ^Z ? -> rnend
  1308. consume ; <= 20h ? -> rnspace
  1309. rndig: [buf]=char ; getbyte ; <= 20h ? -> rnend (NOT consumed)
  1310. consume ; -> rndig
  1311. rnend: buf[0] := 0
  1312. It builds NO frame of its own and touches neither BP nor SP, so it is
  1313. called from the caller's frame and that frame's [BP+4] is still the
  1314. caller's argument when it returns. It clobbers AX BX CX DX DI, which the
  1315. caller has already saved.
  1316. Returns AX = the value, and CX = 0 positive, 1 negative, 2 nothing read.
  1317. CX = 2 is set HERE, on the ^Z path: it is the state both callers test to
  1318. decide not to store, and before this split the check existed only as a
  1319. jump to the exit, so the state itself was never produced. AX is
  1320. meaningless when CX = 2.
  1321. Two behaviours are load-bearing and neither is obvious:
  1322. - the character that ENDS the scan is left pending, because readnum tests
  1323. it *before* clearing getbyte's pre-read flag. xreadln, which the
  1324. compiler emits next for a readln, is what consumes the newline. See the
  1325. note on getch.
  1326. - ^Z before any digit returns without a value, and the caller then returns
  1327. WITHOUT touching the variable: TP3's xrdint reaches rdierr and RETs.
  1328. Sign in CX, value in DI, value in AX on the way out. *)
  1329. BEGIN
  1330. M ("rdscan") ;
  1331. M ("ri_lead") ;
  1332. C8 ("getch") ;
  1333. CmpAl (1AH) ; (* ^Z: end of input *)
  1334. Je8 ("ri_none") ;
  1335. CmpAl (20H) ; (* every control char and the space.
  1336. 20H, not 20: readnum skips
  1337. everything <= #$20, and a literal
  1338. that READS like hex but is
  1339. decimal compares AL against 14h,
  1340. so a leading space is never
  1341. skipped and the scan ends with
  1342. nothing read. Every other
  1343. literal in this module is
  1344. explicit for the same reason. *)
  1345. Jbe8 ("ri_lead") ;
  1346. XorCxCx ;
  1347. CmpAl (ORD ("-")) ;
  1348. Jne8 ("ri_nos") ;
  1349. IncCx ;
  1350. C8 ("getch") ;
  1351. J8 ("ri_dig0") ;
  1352. M ("ri_nos") ;
  1353. CmpAl (ORD ("+")) ;
  1354. Jne8 ("ri_dig0") ;
  1355. C8 ("getch") ;
  1356. M ("ri_dig0") ;
  1357. XorDiDi ;
  1358. M ("ri_dig") ;
  1359. CmpAl (ORD ("0")) ;
  1360. Jb8 ("ri_done") ;
  1361. CmpAl (ORD ("9")) ;
  1362. Ja8 ("ri_done") ;
  1363. SubAl (ORD ("0")) ;
  1364. MovAh (0) ; (* AX := the digit, 0..9 *)
  1365. XchgAxDi ; (* AX := the value so far, DI := the digit.
  1366. The digit has to survive MUL, and the 8-bit
  1367. reg field of 8A/88 has only AL/CL/DL/BL/AH/
  1368. CH/DH/BH - there is no SI or DI byte to park
  1369. it in. MUL r/m16 writes DX, so the previous
  1370. version's "keep the digit across the multiply"
  1371. comment was describing a register the multiply
  1372. owns. Every read of an integer therefore
  1373. returned 0: the digit was computed, moved to
  1374. DH, and multiplied away one instruction later. *)
  1375. MovBxImm (10) ;
  1376. MulBx ; (* DX:AX := value * 10 *)
  1377. AddAxDi ; (* AX := low word + digit. A carry out of bit 15
  1378. is dropped, which is what a 16-bit INTEGER
  1379. does anyway - the high word of the product is
  1380. never stored. *)
  1381. MovDiAx ;
  1382. C8 ("getch") ;
  1383. J8 ("ri_dig") ;
  1384. M ("ri_done") ;
  1385. C8 ("ungetch") ; (* the delimiter stays pending *)
  1386. CmpCx0 ;
  1387. Je8 ("ri_pos") ;
  1388. NegDi ;
  1389. M ("ri_pos") ;
  1390. MovAxDi ; (* the parsed value out of DI and into AX,
  1391. where both callers store it from *)
  1392. RetR ;
  1393. M ("ri_none") ; (* ^Z first: no value, and CX says so *)
  1394. MovCxImm (2) ;
  1395. RetR
  1396. END EmitRdScan ;
  1397. PROCEDURE EmitRdByte ;
  1398. (* Read a number into a BYTE variable: the same scan, a one-byte store.
  1399. TPSRC8 prdnum dispatches on the variable's size - `CMP varsize,#$01 /
  1400. JA prdemit' keeps xrdint for anything wider and falls through to
  1401. `MOV AX,#xrdbyte' for a one-byte variable - and TPSRC4's two entries
  1402. differ in exactly one store: xrdbyte is `CLC', xrdint is `STC', and rdi1
  1403. stores AX or AL on `JNB rdibyt'.
  1404. The flag form is not transcribed. It would put the STC at the head of the
  1405. rdint entry, and the BP contract in tests/rt_exec.py requires an entry
  1406. that borrows BP to *start* with `55 8B EC' and to hand BP back from inside
  1407. its own slice - which is what an entry owning its own frame does. Two
  1408. frames and two stores around one scan is the same idea, with that
  1409. constraint respected.
  1410. The store is the whole reason this entry exists: rdint writes two bytes,
  1411. and the second one is the byte that FOLLOWS the variable, so
  1412. `readln (b)' was reading into b and into whatever the compiler had put
  1413. next to it. *)
  1414. BEGIN
  1415. M ("rdbyte") ;
  1416. PushBp ; MovBpSp ;
  1417. PushAx ; PushBx ; PushCx ; PushDx ; PushDi ;
  1418. C8 ("rdscan") ;
  1419. CmpCxV (2) ;
  1420. Je8 ("rby_skip") ; (* nothing entered: leave it alone *)
  1421. MovDiBp4 ; (* the caller's address into DI *)
  1422. StDiAl ; (* ONE byte - 88 05, AL not AX *)
  1423. M ("rby_skip") ;
  1424. PopDi ; PopDx ; PopCx ; PopBx ; PopAx ;
  1425. MovSpBp ; PopBp ; RetR
  1426. END EmitRdByte ;
  1427. PROCEDURE EmitRdChar ;
  1428. BEGIN
  1429. M ("rdchar") ;
  1430. PushBp ; MovBpSp ;
  1431. PushAx ; PushBx ; PushDi ;
  1432. C8 ("getch") ;
  1433. MovDlAl ;
  1434. MovDiBp4 ; (* no [BX] form: the address goes in DI *)
  1435. StDiDl ;
  1436. PopDi ; PopBx ; PopAx ;
  1437. MovSpBp ; PopBp ; RetR
  1438. END EmitRdChar ;
  1439. PROCEDURE EmitRdBool ;
  1440. (* one character, classified the way TP3 does: T/t/Y/y/1 true, anything else
  1441. false. *)
  1442. BEGIN
  1443. M ("rdbool") ;
  1444. PushBp ; MovBpSp ;
  1445. PushAx ; PushBx ; PushCx ; PushDi ;
  1446. C8 ("getch") ;
  1447. XorCxCx ;
  1448. CmpAl (ORD ("T")) ; Je8 ("rb_t") ;
  1449. CmpAl (ORD ("t")) ; Je8 ("rb_t") ;
  1450. CmpAl (ORD ("Y")) ; Je8 ("rb_t") ;
  1451. CmpAl (ORD ("y")) ; Je8 ("rb_t") ;
  1452. CmpAl (ORD ("1")) ; Je8 ("rb_t") ;
  1453. J8 ("rb_s") ;
  1454. M ("rb_t") ;
  1455. IncCx ;
  1456. M ("rb_s") ;
  1457. MovDiBp4 ; (* DI = the caller's address, because there is
  1458. no [BX] to store through *)
  1459. StDiCx ;
  1460. PopDi ; PopCx ; PopBx ; PopAx ;
  1461. MovSpBp ; PopBp ; RetR
  1462. END EmitRdBool ;
  1463. PROCEDURE EmitRdLn ;
  1464. (* discard the rest of the line, including the terminator *)
  1465. BEGIN
  1466. M ("rdln") ;
  1467. PushAx ;
  1468. M ("rl_loop") ;
  1469. C8 ("getch") ;
  1470. CmpAl (0DH) ; Je8 ("rl_e") ;
  1471. CmpAl (0AH) ; Je8 ("rl_e") ;
  1472. CmpAl (1AH) ; Je8 ("rl_e") ; (* ^Z: end of input *)
  1473. J8 ("rl_loop") ;
  1474. M ("rl_e") ;
  1475. PopAx ;
  1476. RetR
  1477. END EmitRdLn ;
  1478. PROCEDURE EmitRdStr ;
  1479. (* readln(s)/read(s) of a string variable: TPSRC4's xrdstr, the runtime entry
  1480. behind both prdstr and prdrdln. The caller pushes the variable's address
  1481. (one stack word, read here via BP), sets CX = max_len = varsize-1, and pops
  1482. the word after the call.
  1483. Keyed to ours rather than TP3's exactly: TP3 emits MOV CL,imm8 and clears
  1484. CH the length load; this runtime has no byte-register move into CH/CL, so
  1485. the compiler emits a full MOV CX,imm16 and CX *is* the limit (Loop8 / the
  1486. count arithmetic below rely on it).
  1487. Characters come from getch, which clobbers only AX and BX, so the running
  1488. count lives in BX behind a push/pop around each call. Reading stops at CR,
  1489. LF or ^Z - LF because our input files are LF-terminated - and the terminator
  1490. is left PENDING via ungetch, exactly as TP3's xrdstr leaves its CR pending
  1491. through the pre-read flag: the readln wrapper then consumes the newline,
  1492. while a bare read(s) leaves it for the next read. The LOOP truncates a line
  1493. longer than max_len. dest[0] := length; dest[1..] := chars. *)
  1494. BEGIN
  1495. M ("rdstr") ;
  1496. PushBp ; MovBpSp ;
  1497. PushAx ; PushBx ; PushCx ; PushDx ; PushDi ;
  1498. MovDiBp4 ; (* DI := dest base, for the length byte *)
  1499. MovAxBp4 ; MovSiAx ; (* SI := dest, walking *)
  1500. IncSi ; (* SI := dest + 1: chars live at dest[1..], the
  1501. length byte at dest[0] is stored at the end *)
  1502. XorBxBx ; (* BX := 0: count of characters read *)
  1503. M ("rs_loop") ;
  1504. PushBx ; (* getch clobbers BX (and AX) *)
  1505. C8 ("getch") ;
  1506. PopBx ;
  1507. CmpAl (0DH) ; Je8 ("rs_term") ; (* CR *)
  1508. CmpAl (0AH) ; Je8 ("rs_term") ; (* LF *)
  1509. CmpAl (1AH) ; Je8 ("rs_term") ; (* ^Z *)
  1510. MovDlAl ; (* DL := char *)
  1511. IncBx ; (* count := count + 1 *)
  1512. StSiDl ; (* dest[i] := char *)
  1513. IncSi ;
  1514. Loop8 ("rs_loop") ; (* CX--; loop while CX # 0: truncation *)
  1515. M ("rs_end") ;
  1516. MovAlBl ; (* AL := count *)
  1517. StDiAl ; (* dest[0] := length *)
  1518. PopDi ; PopDx ; PopCx ; PopBx ; PopAx ;
  1519. MovSpBp ; PopBp ; RetR ;
  1520. M ("rs_term") ;
  1521. PushBx ; (* ungetch clobbers BX too *)
  1522. C8 ("ungetch") ;
  1523. PopBx ;
  1524. J8 ("rs_end")
  1525. END EmitRdStr ;
  1526. PROCEDURE EmitStrStore ;
  1527. (* s := t for string variables: TPSRC2's strstore with TPSRC8 asnvar's stack
  1528. shape. The caller pushes BOTH addresses - destination first, source second,
  1529. so the LAST push (dest) sits at [BP+4] and the source at [BP+6] - sets
  1530. CX = max_len = varsize-1 of the DESTINATION, calls, and pops both words
  1531. (this runtime is caller-pops, unlike TP3's strstore which pops itself).
  1532. TP3's strstore rewrites the length byte of the stack copy of the source to
  1533. min(src[0], max), then copies length byte + chars, truncating at max, with
  1534. the destination TAIL past the new length left untouched. Here, with both
  1535. variables in memory and no stack copy, that becomes: count := min(src[0],
  1536. max); dest[0] := count; copy count chars src[1..] -> dest[1..]. The source
  1537. variable is never written - truncation is expressed in the destination's own
  1538. length byte only. A source longer than max is truncated; a shorter one is
  1539. copied whole (CX := src length, count including the empty case where LOOP
  1540. simply never runs). *)
  1541. BEGIN
  1542. M ("strstore") ;
  1543. PushBp ; MovBpSp ;
  1544. PushAx ; PushBx ; PushCx ; PushDx ;
  1545. MovAxBp4 ; MovSiAx ; (* SI := dest, walking *)
  1546. MovBxBp6 ; (* BX := source *)
  1547. XorDxDx ;
  1548. LdAlBx ; MovDlAl ; (* DL := src[0], the source length *)
  1549. IncBx ; (* BX -> first source character *)
  1550. CmpCxDx ; (* CMP CX (max), DX (src length) *)
  1551. Jb8 ("ss_trunc") ;
  1552. MovCxDx ; (* not truncated: CX := src length *)
  1553. J8 ("ss_len") ;
  1554. M ("ss_trunc") ;
  1555. MovDlCl ; (* truncated: DL := max, for the length store *)
  1556. M ("ss_len") ;
  1557. StSiDl ; (* dest[0] := length *)
  1558. IncSi ; (* dest -> first character *)
  1559. Jcxz8 ("ss_done") ; (* an empty source copies nothing; LOOP with
  1560. CX = 0 would run 65536 times, not zero *)
  1561. M ("ss_loop") ;
  1562. LdAlBx ; (* AL := src char *)
  1563. MovDlAl ; (* DL := char *)
  1564. StSiDl ; (* dest char := src char *)
  1565. IncBx ; IncSi ;
  1566. Loop8 ("ss_loop") ; (* CX--; loop while CX # 0 *)
  1567. M ("ss_done") ;
  1568. PopDx ; PopCx ; PopBx ; PopAx ;
  1569. MovSpBp ; PopBp ; RetR
  1570. END EmitStrStore ;
  1571. PROCEDURE EmitCmpStr ;
  1572. (* cmpstr: lexicographic comparison of two strings, TPSRC2's cmpstr with the
  1573. addresses taken as arguments rather than the strings pushed by value.
  1574. The caller pushes BOTH addresses - the FIRST (left) operand first, so it
  1575. sits at [BP+6], the second (right) at [BP+4], the same order strstore takes
  1576. its two - calls, and pops both words (caller-pops, unlike TP3's cmpstr,
  1577. which pops itself). It returns AX = -1, 0 or +1 for left < right, left =
  1578. right, left > right.
  1579. Why AX and not the flags, when TP3's cmpstr leaves them set? Because TP3's
  1580. cmpstr also REMOVES its own arguments (`MOV SP,BX', TPSRC2 960), so its six
  1581. wrappers (csteq/cstne/cstge/cstle/cstg/cstl, TPSRC2 890-925) can branch on
  1582. the flags immediately after the CALL. Here the caller pushes the two
  1583. addresses, and dropping them with ADD SP,4 - the one-instruction way - is
  1584. in the CPU's arithmetic group and DESTROYS the flags. (POP does not, but
  1585. then the flags would have to survive the caller's next emission, which is
  1586. exactly the invisible coupling the notes in this file keep warning about.)
  1587. A signed value in AX costs a few instructions inside the entry and keeps the
  1588. flag convention the compiler's own business, where the six-arm table already
  1589. lives: the caller runs OR AX,AX and the same EmSetcc it runs after every
  1590. numeric comparison, so the two paths cannot drift apart.
  1591. That tail is not invented. A TP3 wrapper is `MOV AX,#1 / Jcc / DEC AX /
  1592. OR AX,AX' - the same EmSetcc our numeric arm emits, followed by the same
  1593. OR AX,AX - and the only thing this design moves is WHERE it happens: out of
  1594. six one-per-operator runtime entries and into the one shared table. TP3
  1595. needs the six because its cmpstr is reached through vcompare's vector table
  1596. (TPSRC9 133) by type; we already have the operator as a number.
  1597. The comparison is TPSRC2's, translated to an explicit byte loop because
  1598. this runtime has no REPZ CMPS: min(lenL,lenR) bytes are compared in order,
  1599. the first difference decides, and if the common prefix is equal the LENGTHS
  1600. decide (a prefix is less than the string that continues it). The byte
  1601. compare is unsigned - CF is the borrow out of AL-DL - which is what
  1602. "lexicographic" means for characters. The tiebreak re-reads the lengths
  1603. from the arguments rather than keeping them in registers across the loop,
  1604. so CX is free to be LOOP's count, and the empty string is the case where
  1605. that count is 0 and LOOP would run 65536 times instead of none. *)
  1606. BEGIN
  1607. M ("cmpstr") ;
  1608. PushBp ; MovBpSp ;
  1609. PushBx ; PushCx ; PushDx ; (* AX is NOT saved: it is the result *)
  1610. XorCxCx ; (* CX := 0, to make the CL load below a length *)
  1611. XorDxDx ;
  1612. MovBxBp6 ; (* BX := the first (left) string's address *)
  1613. MovClBx ; (* CL := lenL *)
  1614. MovAxBp4 ; MovSiAx ; (* SI := the second (right) string's address *)
  1615. MovDlSi ; (* DL := lenR *)
  1616. CmpCxDx ; (* CMP lenL, lenR *)
  1617. Jbe8 ("cs_min") ; (* lenL <= lenR: CX is already the shorter *)
  1618. MovCxDx ; (* CX := lenR *)
  1619. M ("cs_min") ; (* CX = min (lenL,lenR) *)
  1620. Jcxz8 ("cs_len") ; (* an empty operand: no bytes to compare *)
  1621. IncBx ; IncSi ; (* past the length byte, to the characters *)
  1622. M ("cs_loop") ;
  1623. LdAlBx ; (* AL := left[i] *)
  1624. MovDlSi ; (* DL := right[i] *)
  1625. CmpAlDl ; (* CMP AL,DL - unsigned, so CF is the order *)
  1626. Jne8 ("cs_flag") ; (* the first difference decides *)
  1627. IncBx ; IncSi ;
  1628. Loop8 ("cs_loop") ;
  1629. M ("cs_len") ; (* equal over the common prefix: lengths decide *)
  1630. MovBxBp6 ; MovClBx ; (* CX := lenL again - BX walked past it *)
  1631. MovAxBp4 ; MovSiAx ;
  1632. XorDxDx ; MovDlSi ; (* DX := lenR *)
  1633. CmpCxDx ;
  1634. M ("cs_flag") ; (* flags are (left - right): CF=1 less, ZF=1 equal *)
  1635. MovAlD (0) ; MovAh (0) ; (* AX := 0. MOV leaves the flags alone, which
  1636. is what lets the two tests below still read
  1637. the comparison. *)
  1638. Jb8 ("cs_less") ;
  1639. Je8 ("cs_done") ;
  1640. MovAlD (1) ; (* greater *)
  1641. J8 ("cs_done") ;
  1642. M ("cs_less") ;
  1643. MovAlD (0FFH) ; MovAh (0FFH) ; (* AX := -1 *)
  1644. M ("cs_done") ;
  1645. PopDx ; PopCx ; PopBx ; (* but NOT AX: AX is the RESULT. The first
  1646. version pushed AX with the others and
  1647. popped it back here, which restored the
  1648. CALLER's AX - for `a < b' that is the
  1649. address of the RIGHT operand, still in AX
  1650. from its LEA - and every ordering test read
  1651. a positive number. `<', `<=', `=', `<>',
  1652. `>', `>=' over any left<right string came
  1653. back FALSE TRUE FALSE FALSE TRUE TRUE,
  1654. independently confirmed on qemu. Saving AX
  1655. at all is wrong: there is nothing to keep. *)
  1656. MovSpBp ; PopBp ; RetR
  1657. END EmitCmpStr ;
  1658. PROCEDURE EmitStrCat ;
  1659. (* strcat: dest := dest + src, TPSRC2's xconcat (963) with the two strings
  1660. taken as ADDRESSES rather than pushed by value.
  1661. The caller pushes the SOURCE first and the DESTINATION last, sets CX = the
  1662. destination's capacity in characters, calls, and pops both words (caller
  1663. pops). That is exactly strstore's argument shape, so the compiler's two
  1664. emitted calls for `a + b' differ only in which entry they name - see
  1665. Compiler.StrConcat.
  1666. TP3's xconcat adds the two length bytes (`ADD.B DL,CL') and takes the CARRY
  1667. as "string too long" (`JB concerr'), then concerr loads runerr's code 16;
  1668. the copy itself is the same append this does. So an overflowing
  1669. concatenation is a RUNTIME ERROR, not a silent truncation: if dest[0] +
  1670. src[0] exceeds CX the process leaves through INT 21h AH=4Ch with exit code
  1671. 16 (10H), which is where TP3's error 16 ends up. (strstore truncates
  1672. instead, which is also what TP3 does there - the two entries deliberately
  1673. do not agree, because TP3's two routines do not.)
  1674. AX, BX, CX, DX, SI and DI are all preserved: the compiler may hold a string
  1675. element's address in DI across the call that pushes the operand AFTER this
  1676. one (see Compiler.EmPushStrOperand), so strcat cannot be the entry that
  1677. quietly clobbers it. *)
  1678. BEGIN
  1679. M ("strcat") ;
  1680. PushBp ; MovBpSp ;
  1681. PushAx ; PushBx ; PushCx ; PushDx ; PushSi ; PushDi ;
  1682. MovAxBp4 ; MovSiAx ; (* SI := dest *)
  1683. MovBxBp6 ; (* BX := src *)
  1684. XorDxDx ;
  1685. MovDlSi ; (* DL := dest[0], the destination length *)
  1686. XorAxAx ;
  1687. LdAlBx ; (* AL := src[0]; AX is now the source length *)
  1688. AddDxAx ; (* DX := dest length + source length *)
  1689. CmpCxDx ; (* CMP CX (max), DX (total) *)
  1690. Jae8 ("sc_ok") ; (* max >= total: it fits *)
  1691. MovAlD (10H) ; (* 10H = 16: TP3's `String too long' error code *)
  1692. MovAh (4CH) ; (* AH = 4Ch: terminate *)
  1693. Int21 ; (* leaving now; the caller's stack is abandoned *)
  1694. M ("sc_ok") ;
  1695. MovCxDx ; (* CX := the new length *)
  1696. StSiCl ; (* dest[0] := new length (CL) *)
  1697. SubDxAx ; (* DX := total - source length = old dest length *)
  1698. MovDiSi ; (* DI := dest *)
  1699. IncDi ; (* -> first character *)
  1700. AddDiDx ; (* -> the append position, dest+1+old length *)
  1701. IncBx ; (* BX -> the source's first character *)
  1702. MovCxAx ; (* CX := source length, LOOP's count *)
  1703. Jcxz8 ("sc_done") ; (* an empty source appends nothing *)
  1704. M ("sc_loop") ;
  1705. LdAlBx ; (* AL := src char *)
  1706. StDiAl ; (* dest char := AL *)
  1707. IncBx ; IncDi ;
  1708. Loop8 ("sc_loop") ;
  1709. M ("sc_done") ;
  1710. PopDi ; PopSi ; PopDx ; PopCx ; PopBx ; PopAx ;
  1711. MovSpBp ; PopBp ; RetR
  1712. END EmitStrCat ;
  1713. PROCEDURE Str5 (s : ARRAY OF CHAR ; n : CARDINAL) ;
  1714. (* emit n characters, so the strings below are visibly the same length as the
  1715. D_ constants claim and an off-by-one in either place is a compile-time
  1716. mismatch rather than a wrong pointer nobody notices *)
  1717. VAR i : CARDINAL ;
  1718. BEGIN
  1719. i := 0 ;
  1720. WHILE i < n DO
  1721. B (ORD (s [i])) ;
  1722. INC (i)
  1723. END
  1724. END Str5 ;
  1725. PROCEDURE EmitStrCopy ;
  1726. (* str_copy: Copy(S, start, len), TPSRC2's xcopy shape.
  1727. ABI: [BP+4]=counted source, [BP+6]=counted destination, [BP+8]=start
  1728. (1-based), [BP+10]=length. The destination receives min(len, |S|-start+1)
  1729. characters; start < 1 is clamped to 1 and a start past the end yields an
  1730. empty string. AX returns the copied length. *)
  1731. BEGIN
  1732. M ("str_copy") ;
  1733. PushBp ; MovBpSp ;
  1734. PushBx ; PushCx ; PushDx ; PushSi ; PushDi ;
  1735. MovDxBp6 ; (* DX := destination *)
  1736. MovDiDx ; (* DI := destination + 1 *)
  1737. XorAxAx ; StDiAl ; IncDi ; (* dest[0] := 0 *)
  1738. PushDx ; (* keep the destination base *)
  1739. MovBxBp4 ; (* BX := source *)
  1740. LdAlBx ; XorAhAh ; MovCxAx ; (* CX := source length *)
  1741. MovSiBx ; IncSi ; (* SI := source characters *)
  1742. MovAxBp8 ; (* AX := start *)
  1743. CmpAxImm (1) ; Jge8 ("cps_start") ;
  1744. MovAxImm (1) ;
  1745. M ("cps_start") ;
  1746. CmpAxCx ; Jbe8 ("cps_inrange") ;
  1747. Jmp8 ("cps_empty") ; (* start past the end -> empty *)
  1748. M ("cps_inrange") ;
  1749. MovDxAx ; (* DX := start *)
  1750. MovAxCx ; SubAxDx ; AddAxImm (1) ;(* AX := available *)
  1751. MovSiBp10 ; (* SI := requested length *)
  1752. CmpAxSi ; Jbe8 ("cps_count") ;
  1753. MovAxSi ;
  1754. M ("cps_count") ;
  1755. MovCxAx ; (* CX := count *)
  1756. PushCx ; (* keep it across LOOP *)
  1757. MovBxBp4 ; IncBx ; AddBxDx ; DecBx ; (* BX := first source character *)
  1758. Jcxz8 ("cps_copied") ;
  1759. M ("cps_loop") ;
  1760. LdAlBx ; StDiAl ; IncBx ; IncDi ; Loop8 ("cps_loop") ;
  1761. M ("cps_copied") ;
  1762. PopCx ; PopBx ; StBxCl ; (* destination length *)
  1763. Jmp8 ("cps_exit") ;
  1764. M ("cps_empty") ;
  1765. PopBx ; XorAxAx ; StBxAl ; (* destination length 0 *)
  1766. M ("cps_exit") ;
  1767. PopDi ; PopSi ; PopDx ; PopCx ; PopBx ;
  1768. MovSpBp ; PopBp ; RetR
  1769. END EmitStrCopy ;
  1770. PROCEDURE EmitRealFromInt ;
  1771. (* intreal (TPSRC3 130): an INTEGER -> the 6-byte MBF real.
  1772. ABI: the source word's ADDRESS is at [BP+6] and the destination's at
  1773. [BP+4]; the caller pushes the source first, so [BP+6] is the source. (The
  1774. reference keeps the real in AX/BX/DX, where AL is the exponent byte, AH
  1775. and BX the low 39 mantissa bits and DX the high 16 with the sign in DH's
  1776. bit 7. Packing is therefore just the two stores.)
  1777. The arithmetic is the reference's, instruction for instruction:
  1778. zero -> all six bytes zero
  1779. sign -> BH, DX := |value|
  1780. AX := 0090h (exponent 144, clear mantissa)
  1781. small value: exponent 88h and shift the mantissa 8
  1782. normalise: shift DX left and decrement AL until DX's sign bit is set
  1783. positive: clear DH bit 7; negative: leave it as the sign
  1784. clear the rest of the mantissa (BX)
  1785. The sign convention here is the packed one: bit 7 of the last byte is the
  1786. sign, and the implicit 1 is not stored. That is why intreal leaves DX's
  1787. top bit alone for negatives (it becomes the sign) and clears it for
  1788. positives. *)
  1789. BEGIN
  1790. M ("real_from_int") ;
  1791. PushBp ; MovBpSp ;
  1792. PushBx ; PushDx ; PushSi ; PushDi ;
  1793. MovSiBp6 ; (* SI := source word *)
  1794. LdAxSi ; (* AX := the integer *)
  1795. Call ("rf_core") ;
  1796. M ("rf_store") ;
  1797. MovDiBp4 ; (* DI := the destination *)
  1798. StDiAx ; (* bytes 0..1 *)
  1799. StDiOffBx (2) ; (* bytes 2..3 *)
  1800. StDiOffDx (4) ; (* bytes 4..5, sign in DH bit 7 *)
  1801. PopDi ; PopSi ; PopDx ; PopBx ;
  1802. MovSpBp ; PopBp ; RetR ;
  1803. (* ---- the callable integer -> real core: AX in, AX/BX/DX out ---- *)
  1804. M ("rf_core") ;
  1805. CmpAx0 ;
  1806. Jne8 ("rf_nz") ;
  1807. XorBxBx ;
  1808. XorDxDx ;
  1809. Jmp8 ("rf_core_ret") ;
  1810. M ("rf_nz") ;
  1811. MovBhAh ; (* BH := the integer's sign byte *)
  1812. MovDxAx ;
  1813. CmpDx0 ;
  1814. Jns8 ("rf_pos") ;
  1815. NegDx ;
  1816. M ("rf_pos") ;
  1817. MovAxImm (0090H) ; (* AL = exponent 144, AH = 0 *)
  1818. CmpDh0 ;
  1819. Jne8 ("rf_16") ;
  1820. MovAlD (088H) ; (* 16-bit value in the top byte: bias 88 *)
  1821. XchgDlDh ;
  1822. M ("rf_16") ;
  1823. CmpDx0 ;
  1824. Js8 ("rf_done") ;
  1825. M ("rf_nrm") ;
  1826. DecAl ;
  1827. ShlDx1 ;
  1828. Jns8 ("rf_nrm") ;
  1829. M ("rf_done") ;
  1830. CmpBh0 ;
  1831. Js8 ("rf_pos2") ;
  1832. AndDhImm (7FH) ; (* positive: bit 7 of DH is not a sign *)
  1833. M ("rf_pos2") ;
  1834. XorBxBx ; (* only DX's 16 bits carry the value now *)
  1835. M ("rf_core_ret") ;
  1836. RetR
  1837. END EmitRealFromInt ;
  1838. PROCEDURE EmitRealAddSub ;
  1839. (* readd/resub, TPSRC2 1421-1530, transcribed.
  1840. ABI: [BP+4] is the address of a 12-byte block holding a (6 bytes) then b
  1841. (6 bytes); the result overwrites a. Both operands are read before the
  1842. result is written, so the in-place destination is safe. The reference
  1843. keeps a in AX/BX/DX and b in CX/SI/DI, with AL the exponent, AH+BX the low
  1844. mantissa and DX the high 16 carrying the sign in DH bit 7. The scratch
  1845. bytes are the reference's own (resign/resave, remant/remant1, cvdecexp).
  1846. The sign of b is toggled for subtraction with XOR DI,resign, exactly as
  1847. resub does, and the rest is one shared core. An exponent overflow leaves
  1848. through the reference's STC + RET; no caller consumes that yet. *)
  1849. BEGIN
  1850. M ("real_add") ;
  1851. PushBp ; MovBpSp ;
  1852. PushBx ; PushDx ; PushSi ; PushDi ;
  1853. PushBp4 ; (* keep the block address for the store *)
  1854. MovSiBp4 ;
  1855. LdAxSi ;
  1856. LdCxSiOff (2) ; LdDxSiOff (4) ;
  1857. MovBxCx ;
  1858. MovDiSi ;
  1859. LdCxDiOff (6) ; LdSiDiOff (8) ; LdDiDiOff (10) ;
  1860. StDataImm (D_RSIGN, 0) ;
  1861. Call ("rd_core") ;
  1862. PopDi ;
  1863. StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
  1864. PopDi ; PopSi ; PopDx ; PopBx ;
  1865. MovSpBp ; PopBp ; RetR ;
  1866. M ("real_sub") ;
  1867. PushBp ; MovBpSp ;
  1868. PushBx ; PushDx ; PushSi ; PushDi ;
  1869. PushBp4 ;
  1870. MovSiBp4 ;
  1871. LdAxSi ;
  1872. LdCxSiOff (2) ; LdDxSiOff (4) ;
  1873. MovBxCx ;
  1874. MovDiSi ;
  1875. LdCxDiOff (6) ; LdSiDiOff (8) ; LdDiDiOff (10) ;
  1876. StDataImm (D_RSIGN, 8000H) ;
  1877. Call ("rd_core") ;
  1878. PopDi ;
  1879. StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
  1880. PopDi ; PopSi ; PopDx ; PopBx ;
  1881. MovSpBp ; PopBp ; RetR ;
  1882. M ("rd_core") ;
  1883. CmpCl0 ;
  1884. Je8 ("raret") ;
  1885. XorDiData (D_RSIGN) ;
  1886. CmpAl0 ;
  1887. Jne8 ("ranotriv") ;
  1888. M ("raretn2") ;
  1889. MovAxCx ; MovBxSi ; MovDxDi ;
  1890. M ("raret") ;
  1891. RetR ;
  1892. M ("ranotriv") ;
  1893. CmpAlCl ;
  1894. Jbe8 ("ranoswap") ;
  1895. XchgAxCx ; XchgBxSi ; XchgDxDi ;
  1896. M ("ranoswap") ;
  1897. StDataCl (D_CVDEC) ;
  1898. SubClAl ;
  1899. CmpClImm (28H) ;
  1900. Jb8 ("ranoundr") ;
  1901. LdClData (D_CVDEC) ;
  1902. Jmp8 ("raretn2") ;
  1903. M ("ranoundr") ;
  1904. StDataDi (D_RSIGN) ; (* resign; D_RESAVE is its high byte *)
  1905. AndDataImm (D_RESAVE, 80H) ;
  1906. StDataDi (D_RMANT) ;
  1907. XorDataDh (D_RMANT1) ; (* sign1 XOR sign2 in the high byte *)
  1908. OrDiImm (8000H) ; (* b's hidden 1 *)
  1909. OrDhImm (80H) ; (* a's hidden 1 *)
  1910. M ("raadj16l") ;
  1911. CmpClImm (10H) ;
  1912. Jb8 ("raadj8") ;
  1913. MovAhBh ; MovBxDx ; XorDxDx ; SubClImm (10H) ;
  1914. Jmp8 ("raadj16l") ;
  1915. M ("raadj8") ;
  1916. CmpClImm (08H) ;
  1917. Jb8 ("raadj8l") ;
  1918. MovAhBl ; MovBlBh ; MovBhDl ; MovDlDh ; XorDhDh ; SubClImm (08H) ;
  1919. M ("raadj8l") ;
  1920. CmpCl0 ;
  1921. Je8 ("raadjend") ;
  1922. M ("raadj1l") ;
  1923. ShrDx1 ; RcrBx1 ; RcrAh1 ; DecCl ; Jne8 ("raadj1l") ;
  1924. M ("raadjend") ;
  1925. LdAlData (D_CVDEC) ;
  1926. TestDataImm (D_RMANT1, 80H) ;
  1927. Jne8 ("radosub") ;
  1928. AddAhCh ; AdcBxSi ; AdcDxDi ;
  1929. Jae8 ("rasign") ;
  1930. RcrDx1 ; RcrBx1 ; RcrAh1 ; IncAl ; Jne8 ("rasign") ;
  1931. Stc ; RetR ;
  1932. M ("radosub") ;
  1933. XchgAhCh ; XchgBxSi ; XchgDxDi ;
  1934. SubAhCh ; SbbBxSi ; SbbDxDi ;
  1935. Jae8 ("ranoneg") ;
  1936. XorDataImm (D_RESAVE, 80H) ;
  1937. NotAh ; NotBx ; NotDx ;
  1938. AddAhImm (1) ; AdcBxImm (0) ; AdcDxImm (0) ;
  1939. M ("ranoneg") ;
  1940. MovClImm (5) ;
  1941. M ("ranrm8l") ;
  1942. CmpDh0 ;
  1943. Jne8 ("ranrm1l") ;
  1944. MovDhDl ; MovDlBh ; MovBhBl ; MovBlAh ; XorAhAh ;
  1945. SubAl (08H) ;
  1946. Jb8 ("razero") ;
  1947. DecCl ;
  1948. Jne8 ("ranrm8l") ;
  1949. Jmp8 ("razero") ;
  1950. M ("ranrm1l") ;
  1951. TestDhImm (80H) ;
  1952. Jne8 ("rasign") ;
  1953. ShlAh1 ; RclBx1 ; RclDx1 ; DecAl ; Jne8 ("ranrm1l") ;
  1954. M ("razero") ;
  1955. XorAxAx ; XorBxBx ; XorDxDx ;
  1956. RetR ;
  1957. M ("rasign") ;
  1958. AndDhImm (7FH) ;
  1959. XorDhData (D_RESAVE) ;
  1960. RetR
  1961. END EmitRealAddSub ;
  1962. PROCEDURE EmitRealMul ;
  1963. (* remult, TPSRC2 1532-1587, with testexp (TPSRC3 50-78) inlined.
  1964. ABI and register convention are real_add's: [BP+4] is a 12-byte block
  1965. holding a then b; the result overwrites a; a is AX/BX/DX and b CX/SI/DI.
  1966. testexp is inlined because the reference's version is called and, on
  1967. underflow or overflow, POPS ITS RETURN ADDRESS to leave remult entirely.
  1968. That trick cannot survive being called from our wrapper, so the two
  1969. exponent fault paths jump to this core's zero/error exits instead.
  1970. Multiplication is the reference's shift-and-add over the five mantissa
  1971. bytes: for each byte, if it is zero the 40-bit result is shifted right
  1972. eight; otherwise each of its eight bits conditionally adds a's mantissa
  1973. and shifts the result one. LAHF/SAHF carry the guard bit across the
  1974. normalisation TEST. Overflow leaves through TP3's error 1 (exit code 1);
  1975. underflow returns zero. *)
  1976. BEGIN
  1977. M ("real_mul") ;
  1978. PushBp ; MovBpSp ;
  1979. PushBx ; PushDx ; PushSi ; PushDi ;
  1980. PushBp4 ;
  1981. MovSiBp4 ;
  1982. LdAxSi ;
  1983. LdCxSiOff (2) ; LdDxSiOff (4) ;
  1984. MovBxCx ;
  1985. MovDiSi ;
  1986. LdCxDiOff (6) ; LdSiDiOff (8) ; LdDiDiOff (10) ;
  1987. Call ("rm_core") ;
  1988. PopDi ;
  1989. StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
  1990. PopDi ; PopSi ; PopDx ; PopBx ;
  1991. MovSpBp ; PopBp ; RetR ;
  1992. M ("rm_core") ;
  1993. CmpCl0 ; JccNear (74H, "rm_zero") ;
  1994. CmpAl0 ; JccNear (74H, "rm_ret") ;
  1995. AddAlCl ;
  1996. Jb8 ("rm_ovr1") ;
  1997. AddAlImm (80H) ;
  1998. Jb8 ("rm_ok") ;
  1999. JmpNear ("rm_zero") ; (* underflow *)
  2000. M ("rm_ovr1") ;
  2001. AddAlImm (80H) ;
  2002. Jae8 ("rm_ok") ;
  2003. (* overflow: TP3 error 1 *)
  2004. MovAlD (1) ; MovAh (4CH) ; Int21 ;
  2005. M ("rm_ok") ;
  2006. (* testexp's tail *)
  2007. StDataCx (D_RMUL21) ;
  2008. MovCxDx ; XorCxDi ; NotCh ; AndChImm (80H) ; StDataCh (D_RESAVE) ;
  2009. OrDhImm (80H) ; OrDiImm (8000H) ;
  2010. StDataSi (D_RMUL22) ; StDataDi (D_RMUL23) ;
  2011. (* remult *)
  2012. StDataAx (D_RMUL11) ; StDataBx (D_RMUL12) ; StDataDx (D_RMUL13) ;
  2013. XorAhAh ; XorBxBx ; XorDxDx ;
  2014. MovDiData (D_RMUL21) ;
  2015. MovClImm (5) ;
  2016. M ("rm_byt") ;
  2017. IncDi ;
  2018. LdChDiOff ;
  2019. CmpCh0 ; Jne8 ("rm_domul") ;
  2020. MovAhBl ; MovBlBh ; MovBhDl ; MovDlDh ; XorDhDh ;
  2021. Jmp8 ("rm_nxt") ;
  2022. M ("rm_domul") ;
  2023. MovSiImm (8) ;
  2024. M ("rm_mul") ;
  2025. RcrCh1 ;
  2026. Jae8 ("rm_bit0") ;
  2027. AddAhData (D_RMUL11 + 1) ;
  2028. AdcBxData (D_RMUL12) ;
  2029. AdcDxData (D_RMUL13) ;
  2030. M ("rm_bit0") ;
  2031. RcrDx1 ; RcrBx1 ; RcrAh1 ;
  2032. DecSi ; Jne8 ("rm_mul") ;
  2033. M ("rm_nxt") ;
  2034. DecCl ; Jne8 ("rm_byt") ;
  2035. XchgAxCx ;
  2036. Lahf ;
  2037. TestDhImm (80H) ; Jne8 ("rm_noadj") ;
  2038. Sahf ;
  2039. RclCh1 ; RclBx1 ; RclDx1 ;
  2040. CmpCl0 ; Je8 ("rm_noadj") ;
  2041. DecCl ;
  2042. M ("rm_noadj") ;
  2043. XchgAxCx ;
  2044. XorDhData (D_RESAVE) ;
  2045. CmpAl0 ; Jne8 ("rm_ret") ;
  2046. M ("rm_zero") ;
  2047. XorAxAx ; XorBxBx ; XorDxDx ;
  2048. M ("rm_ret") ;
  2049. RetR ;
  2050. M ("real_div") ;
  2051. PushBp ; MovBpSp ;
  2052. PushBx ; PushDx ; PushSi ; PushDi ;
  2053. PushBp4 ;
  2054. MovSiBp4 ;
  2055. LdAxSi ;
  2056. LdCxSiOff (2) ; LdDxSiOff (4) ;
  2057. MovBxCx ;
  2058. MovDiSi ;
  2059. LdCxDiOff (6) ; LdSiDiOff (8) ; LdDiDiOff (10) ;
  2060. Call ("rv_core") ;
  2061. PopDi ;
  2062. StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
  2063. PopDi ; PopSi ; PopDx ; PopBx ;
  2064. MovSpBp ; PopBp ; RetR ;
  2065. M ("rv_core") ;
  2066. CmpAl0 ; JccNear (74H, "rm_ret") ;
  2067. SubAlCl ;
  2068. Cmc ;
  2069. Jb8 ("rd_ovr1") ;
  2070. AddAlImm (80H) ;
  2071. Jb8 ("rd_okx") ;
  2072. JmpNear ("rm_zero") ; (* underflow *)
  2073. M ("rd_ovr1") ;
  2074. AddAlImm (80H) ;
  2075. Jae8 ("rd_okx") ;
  2076. MovAlD (1) ; MovAh (4CH) ; Int21 ;
  2077. M ("rd_okx") ;
  2078. (* testexp's tail *)
  2079. StDataCx (D_RMUL21) ;
  2080. MovCxDx ; XorCxDi ; NotCh ; AndChImm (80H) ; StDataCh (D_RESAVE) ;
  2081. OrDhImm (80H) ; OrDiImm (8000H) ;
  2082. StDataSi (D_RMUL22) ; StDataDi (D_RMUL23) ;
  2083. (* rediv *)
  2084. StDataAl (D_RMUL11) ;
  2085. MovDiData (D_RMUL21 - 1) ; (* remul21b: one before the mantissa *)
  2086. MovClImm (5) ;
  2087. MovSiImm (8) ;
  2088. M ("rd_loop") ;
  2089. CmpDxData (D_RMUL23) ; Jne8 ("rd_cmp") ;
  2090. CmpBxData (D_RMUL22) ; Jne8 ("rd_cmp") ;
  2091. CmpAhData (D_RMUL21 + 1) ;
  2092. M ("rd_cmp") ;
  2093. Jb8 ("rd_shft") ;
  2094. SubAhData (D_RMUL21 + 1) ; SbbBxData (D_RMUL22) ; SbbDxData (D_RMUL23) ;
  2095. M ("rd_shft") ;
  2096. Cmc ;
  2097. RclCh1 ;
  2098. DecSi ; Jne8 ("rd_bit") ;
  2099. StDiCh ;
  2100. DecCl ; Je8 ("rd_last") ;
  2101. DecDi ;
  2102. MovSiImm (8) ;
  2103. M ("rd_bit") ;
  2104. ShlAh1 ; RclBx1 ; RclDx1 ;
  2105. Jae8 ("rd_loop") ;
  2106. SubAhData (D_RMUL21 + 1) ; SbbBxData (D_RMUL22) ; SbbDxData (D_RMUL23) ;
  2107. Clc ;
  2108. Jmp8 ("rd_shft") ;
  2109. M ("rd_last") ;
  2110. ShlAh1 ; RclBx1 ; RclDx1 ;
  2111. Jb8 ("rd_shft2") ;
  2112. CmpDxData (D_RMUL23) ; Jne8 ("rd_cmp2") ;
  2113. CmpBxData (D_RMUL22) ; Jne8 ("rd_cmp2") ;
  2114. CmpAhData (D_RMUL21 + 1) ;
  2115. M ("rd_cmp2") ;
  2116. Cmc ;
  2117. M ("rd_shft2") ;
  2118. MovCxData (D_RMUL11) ; MovBxData (D_RMUL12) ; MovDxData (D_RMUL13) ;
  2119. Lahf ;
  2120. TestDhImm (80H) ; Jne8 ("rd_nrm") ;
  2121. Sahf ;
  2122. RclCh1 ; RclBx1 ; RclDx1 ;
  2123. Jmp8 ("rd_ok") ;
  2124. M ("rd_nrm") ;
  2125. IncCl ;
  2126. Jne8 ("rd_ok") ;
  2127. MovAlD (1) ; MovAh (4CH) ; Int21 ;
  2128. M ("rd_ok") ;
  2129. JmpNear ("rm_noadj")
  2130. END EmitRealMul ;
  2131. PROCEDURE EmitRealCmp ;
  2132. (* recmp: compare two packed 6-byte MBF reals.
  2133. ABI: [BP+4] points at a 12-byte block holding a then b (the same block the
  2134. four arithmetic entries take). Nothing is written; the result is AX =
  2135. -1/0/+1 for a<b, a=b, a>b.
  2136. TP3's recmp leaves the flags set and its six wrappers branch on them. Here
  2137. the caller is our compiler, which already has one comparison arm for reals
  2138. and strings: EmSetcc over AX. cmpstr returns the same -1/0/+1, so the two
  2139. cannot drift. The reference's core is kept intact; only the final
  2140. flag-to-AX step is new, and it is cmpstr's: MOV AX,0 leaves the flags alone,
  2141. then JB/JE/MOV decide.
  2142. The comparison is on the packed representation: the sign is bit 7 of each
  2143. high byte, the magnitude is exponent then DX/BX/AH (TP3's 40-bit order), and
  2144. a negative pair inverts the magnitude result. Different signs are decided
  2145. by RCL on a's high word, which shifts the sign into CF - exactly TPSRC's
  2146. trick, and it leaves ZF clear because the XOR found the signs different. *)
  2147. BEGIN
  2148. M ("real_cmp") ;
  2149. PushBp ; MovBpSp ;
  2150. PushBx ; PushDx ; PushSi ; PushDi ;
  2151. MovSiBp4 ;
  2152. LdAxSi ;
  2153. LdCxSiOff (2) ; LdDxSiOff (4) ;
  2154. MovBxCx ;
  2155. MovDiSi ;
  2156. LdCxDiOff (6) ; LdSiDiOff (8) ; LdDiDiOff (10) ;
  2157. (* recmp core *)
  2158. PushDx ; XorDxDi ; PopDx ;
  2159. Jns8 ("rc_sign") ;
  2160. PushDx ; RclDx1 ; PopDx ;
  2161. Jmp8 ("rc_flags") ;
  2162. M ("rc_sign") ;
  2163. TestDhImm (80H) ;
  2164. Je8 ("rc_pos") ;
  2165. Call ("rc_num") ;
  2166. Je8 ("rc_flags") ;
  2167. Cmc ;
  2168. Jmp8 ("rc_flags") ;
  2169. M ("rc_pos") ;
  2170. Call ("rc_num") ;
  2171. M ("rc_flags") ;
  2172. MovAlD (0) ; MovAh (0) ; (* AX := 0; MOV leaves the flags alone *)
  2173. Jb8 ("rc_less") ;
  2174. Je8 ("rc_done") ;
  2175. MovAlD (1) ;
  2176. Jmp8 ("rc_done") ;
  2177. M ("rc_less") ;
  2178. MovAlD (0FFH) ; MovAh (0FFH) ;
  2179. M ("rc_done") ;
  2180. PopDi ; PopSi ; PopDx ; PopBx ;
  2181. MovSpBp ; PopBp ; RetR ;
  2182. (* the shared magnitude compare; CALL/RET do not touch the flags *)
  2183. M ("rc_num") ;
  2184. CmpAlCl ; Jne8 ("rc_num_ret") ;
  2185. CmpAl0 ; Je8 ("rc_num_ret") ;
  2186. CmpDxDi ; Jne8 ("rc_num_ret") ;
  2187. CmpBxSi ; Jne8 ("rc_num_ret") ;
  2188. CmpAhCh ;
  2189. M ("rc_num_ret") ;
  2190. RetR
  2191. END EmitRealCmp ;
  2192. PROCEDURE EmitRealInt ;
  2193. (* reint: Int(real), the real-valued integer part, in place.
  2194. ABI: [BP+4] is the address of a 6-byte packed MBF real; the result
  2195. overwrites it. TPSRC3's reint works directly on the packed bytes: it builds
  2196. a mask by shifting 1s right by (exponent - 129) bits and ANDs it with the
  2197. mantissa, which clears the fraction while keeping the sign bit and the
  2198. implicit leading 1. Exponents >= 0A8h are already integral and returned
  2199. unchanged; exponents <= 80h give zero.
  2200. The 16/8-bit fast paths are the reference's, including the byte moves that
  2201. shift the mask instead of running the bit loop. Only the wrapper is ours
  2202. (the caller passes an address rather than the value in registers). *)
  2203. BEGIN
  2204. M ("real_int") ;
  2205. PushBp ; MovBpSp ;
  2206. PushBx ; PushDx ; PushSi ; PushDi ;
  2207. PushBp4 ;
  2208. MovSiBp4 ;
  2209. LdAxSi ;
  2210. LdCxSiOff (2) ; LdDxSiOff (4) ;
  2211. MovBxCx ;
  2212. (* reint core *)
  2213. CmpAl (0A8H) ;
  2214. Jae8 ("ri_store") ; (* already integral: return as-is *)
  2215. MovCxAx ; MovSiBx ; MovDiDx ; (* save the packed mantissa for the mask *)
  2216. XorAhAh ; XorBxBx ; XorDxDx ; (* build the mask in AH/BX/DX *)
  2217. SubClImm (80H) ; (* CL := exponent - 129 + 1 *)
  2218. Jbe8 ("ri_zero") ;
  2219. M ("ri_16") ;
  2220. CmpClImm (10H) ; Jb8 ("ri_8") ;
  2221. MovAhBh ; MovBxDx ; MovDxImm (0FFFFH) ;
  2222. SubClImm (10H) ; Jmp8 ("ri_16") ;
  2223. M ("ri_8") ;
  2224. CmpClImm (08H) ; Jb8 ("ri_1") ;
  2225. MovAhBl ; MovBlBh ; MovBhDl ; MovDlDh ; MovDhImm (0FFH) ;
  2226. SubClImm (08H) ; Jmp8 ("ri_8") ;
  2227. M ("ri_1") ;
  2228. CmpCl0 ; Je8 ("ri_and") ;
  2229. M ("ri_1l") ;
  2230. Stc ; RcrDx1 ; RcrBx1 ; RcrAh1 ; DecCl ; Jne8 ("ri_1l") ;
  2231. M ("ri_and") ;
  2232. AndDxDi ; AndBxSi ; AndAhCh ;
  2233. M ("ri_store") ;
  2234. PopDi ;
  2235. StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
  2236. PopDi ; PopSi ; PopDx ; PopBx ;
  2237. MovSpBp ; PopBp ; RetR ;
  2238. M ("ri_zero") ;
  2239. MovAlD (0) ;
  2240. Jmp8 ("ri_store")
  2241. END EmitRealInt ;
  2242. PROCEDURE Copy6 (src, dst : CARDINAL) ;
  2243. (* emit a six-byte move between two runtime-data offsets *)
  2244. BEGIN
  2245. MovAxData (src) ; StDataAx (dst) ;
  2246. MovBxData (src + 2) ; StDataBx (dst + 2) ;
  2247. MovDxData (src + 4) ; StDataDx (dst + 4)
  2248. END Copy6 ;
  2249. PROCEDURE MathAdd (a, b, dst : CARDINAL) ;
  2250. BEGIN
  2251. Copy6 (a, D_BLK) ; Copy6 (b, D_BLK + 6) ;
  2252. MovBxDataAddr (D_BLK) ; PushBx ; Call ("real_add") ; AddSp2 ;
  2253. Copy6 (D_BLK, dst)
  2254. END MathAdd ;
  2255. PROCEDURE MathSub (a, b, dst : CARDINAL) ;
  2256. BEGIN
  2257. Copy6 (a, D_BLK) ; Copy6 (b, D_BLK + 6) ;
  2258. MovBxDataAddr (D_BLK) ; PushBx ; Call ("real_sub") ; AddSp2 ;
  2259. Copy6 (D_BLK, dst)
  2260. END MathSub ;
  2261. PROCEDURE MathMul (a, b, dst : CARDINAL) ;
  2262. BEGIN
  2263. Copy6 (a, D_BLK) ; Copy6 (b, D_BLK + 6) ;
  2264. MovBxDataAddr (D_BLK) ; PushBx ; Call ("real_mul") ; AddSp2 ;
  2265. Copy6 (D_BLK, dst)
  2266. END MathMul ;
  2267. PROCEDURE MathDiv (a, b, dst : CARDINAL) ;
  2268. BEGIN
  2269. Copy6 (a, D_BLK) ; Copy6 (b, D_BLK + 6) ;
  2270. MovBxDataAddr (D_BLK) ; PushBx ; Call ("real_div") ; AddSp2 ;
  2271. Copy6 (D_BLK, dst)
  2272. END MathDiv ;
  2273. PROCEDURE MathCmp (a, b : CARDINAL) ;
  2274. BEGIN
  2275. Copy6 (a, D_BLK) ; Copy6 (b, D_BLK + 6) ;
  2276. MovBxDataAddr (D_BLK) ; PushBx ; Call ("real_cmp") ; AddSp2
  2277. END MathCmp ;
  2278. PROCEDURE MathAbs (a, dst : CARDINAL) ;
  2279. BEGIN
  2280. Copy6 (a, D_BLK) ;
  2281. MovBxDataAddr (D_BLK) ; PushBx ; Call ("real_abs") ; AddSp2 ;
  2282. Copy6 (D_BLK, dst)
  2283. END MathAbs ;
  2284. PROCEDURE MathNeg (a, dst : CARDINAL) ;
  2285. BEGIN
  2286. Copy6 (a, dst) ;
  2287. MovDiData (dst) ; XorDiOffImm (5, 80H)
  2288. END MathNeg ;
  2289. PROCEDURE MathInt (a, dst : CARDINAL) ;
  2290. BEGIN
  2291. Copy6 (a, D_BLK) ;
  2292. MovBxDataAddr (D_BLK) ; PushBx ; Call ("real_int") ; AddSp2 ;
  2293. Copy6 (D_BLK, dst)
  2294. END MathInt ;
  2295. PROCEDURE EmitRealMath ;
  2296. (* REAL math entries. The caller passes a 12-byte block; the first six bytes
  2297. are the argument and are overwritten by the result. Abs is a sign clear.
  2298. Sqrt is Newton's iteration with a fixed count: the 8086 has no FPU, so the
  2299. working precision is the same packed MBF format the arithmetic entries use.
  2300. The internal mt_* bridges call the existing register cores directly. *)
  2301. PROCEDURE LoadMath (off : CARDINAL) ;
  2302. BEGIN
  2303. MovAxData (off) ; MovBxData (off + 2) ; MovDxData (off + 4)
  2304. END LoadMath ;
  2305. PROCEDURE StoreMath (off : CARDINAL) ;
  2306. BEGIN
  2307. StDataAx (off) ; StDataBx (off + 2) ; StDataDx (off + 4)
  2308. END StoreMath ;
  2309. BEGIN
  2310. M ("mt_add") ;
  2311. StDataImm (D_RSIGN, 0) ; Call ("rd_core") ; RetR ;
  2312. M ("mt_div") ;
  2313. Call ("rv_core") ; RetR ;
  2314. (* mt_horner: D_P := c0; for each remaining table entry c, D_P := D_P*D_Y+c.
  2315. D_HF then adds a final D_P*D_Y+1. D_HT points at the first coefficient
  2316. and is advanced by six; D_HC is the total count. *)
  2317. M ("mt_horner") ;
  2318. MovBxData (D_HT) ; MovSiBx ;
  2319. LdAxSi ; StDataAx (D_P) ;
  2320. LdCxSiOff (2) ; MovBxCx ; StDataBx (D_P + 2) ;
  2321. LdDxSiOff (4) ; StDataDx (D_P + 4) ;
  2322. MovBxData (D_HT) ; AddBxImm (6) ; StDataBx (D_HT) ;
  2323. DecData (D_HC) ; JccNear (74H, "mh_final") ;
  2324. M ("mh_loop") ;
  2325. (* block := p, y *)
  2326. MovAxData (D_P) ; StDataAx (D_BLK) ;
  2327. MovBxData (D_P + 2) ; StDataBx (D_BLK + 2) ;
  2328. MovDxData (D_P + 4) ; StDataDx (D_BLK + 4) ;
  2329. MovAxData (D_Y) ; StDataAx (D_BLK + 6) ;
  2330. MovBxData (D_Y + 2) ; StDataBx (D_BLK + 8) ;
  2331. MovDxData (D_Y + 4) ; StDataDx (D_BLK + 10) ;
  2332. MovBxDataAddr (D_BLK) ; PushBx ; Call ("real_mul") ; AddSp2 ;
  2333. MovAxData (D_BLK) ; StDataAx (D_P) ;
  2334. MovBxData (D_BLK + 2) ; StDataBx (D_P + 2) ;
  2335. MovDxData (D_BLK + 4) ; StDataDx (D_P + 4) ;
  2336. (* block := p, next coefficient *)
  2337. MovAxData (D_P) ; StDataAx (D_BLK) ;
  2338. MovBxData (D_P + 2) ; StDataBx (D_BLK + 2) ;
  2339. MovDxData (D_P + 4) ; StDataDx (D_BLK + 4) ;
  2340. MovBxData (D_HT) ; MovSiBx ;
  2341. LdAxSi ; StDataAx (D_BLK + 6) ;
  2342. LdCxSiOff (2) ; MovBxCx ; StDataBx (D_BLK + 8) ;
  2343. LdDxSiOff (4) ; StDataDx (D_BLK + 10) ;
  2344. MovBxDataAddr (D_BLK) ; PushBx ; Call ("real_add") ; AddSp2 ;
  2345. MovAxData (D_BLK) ; StDataAx (D_P) ;
  2346. MovBxData (D_BLK + 2) ; StDataBx (D_P + 2) ;
  2347. MovDxData (D_BLK + 4) ; StDataDx (D_P + 4) ;
  2348. MovBxData (D_HT) ; AddBxImm (6) ; StDataBx (D_HT) ;
  2349. DecData (D_HC) ; JccNear (75H, "mh_loop") ;
  2350. M ("mh_final") ;
  2351. TestDataImm (D_HF, 1) ; JccNear (74H, "mh_done") ;
  2352. (* final p*y + 1 *)
  2353. MovAxData (D_P) ; StDataAx (D_BLK) ;
  2354. MovBxData (D_P + 2) ; StDataBx (D_BLK + 2) ;
  2355. MovDxData (D_P + 4) ; StDataDx (D_BLK + 4) ;
  2356. MovAxData (D_Y) ; StDataAx (D_BLK + 6) ;
  2357. MovBxData (D_Y + 2) ; StDataBx (D_BLK + 8) ;
  2358. MovDxData (D_Y + 4) ; StDataDx (D_BLK + 10) ;
  2359. MovBxDataAddr (D_BLK) ; PushBx ; Call ("real_mul") ; AddSp2 ;
  2360. MovAxData (D_BLK) ; StDataAx (D_P) ;
  2361. MovBxData (D_BLK + 2) ; StDataBx (D_P + 2) ;
  2362. MovDxData (D_BLK + 4) ; StDataDx (D_P + 4) ;
  2363. MovAxData (D_P) ; StDataAx (D_BLK) ;
  2364. MovBxData (D_P + 2) ; StDataBx (D_BLK + 2) ;
  2365. MovDxData (D_P + 4) ; StDataDx (D_BLK + 4) ;
  2366. MovAxData (D_ONE) ; StDataAx (D_BLK + 6) ;
  2367. MovBxData (D_ONE + 2) ; StDataBx (D_BLK + 8) ;
  2368. MovDxData (D_ONE + 4) ; StDataDx (D_BLK + 10) ;
  2369. MovBxDataAddr (D_BLK) ; PushBx ; Call ("real_add") ; AddSp2 ;
  2370. MovAxData (D_BLK) ; StDataAx (D_P) ;
  2371. MovBxData (D_BLK + 2) ; StDataBx (D_P + 2) ;
  2372. MovDxData (D_BLK + 4) ; StDataDx (D_P + 4) ;
  2373. M ("mh_done") ;
  2374. RetR ;
  2375. (* mt_square: D_P := D_P^(2^D_HC). *)
  2376. M ("mt_square") ;
  2377. M ("msq_loop") ;
  2378. MovAxData (D_P) ; StDataAx (D_BLK) ;
  2379. MovBxData (D_P + 2) ; StDataBx (D_BLK + 2) ;
  2380. MovDxData (D_P + 4) ; StDataDx (D_BLK + 4) ;
  2381. MovAxData (D_P) ; StDataAx (D_BLK + 6) ;
  2382. MovBxData (D_P + 2) ; StDataBx (D_BLK + 8) ;
  2383. MovDxData (D_P + 4) ; StDataDx (D_BLK + 10) ;
  2384. MovBxDataAddr (D_BLK) ; PushBx ; Call ("real_mul") ; AddSp2 ;
  2385. MovAxData (D_BLK) ; StDataAx (D_P) ;
  2386. MovBxData (D_BLK + 2) ; StDataBx (D_P + 2) ;
  2387. MovDxData (D_BLK + 4) ; StDataDx (D_P + 4) ;
  2388. DecData (D_HC) ; JccNear (75H, "msq_loop") ;
  2389. RetR ;
  2390. M ("real_abs") ;
  2391. PushBp ; MovBpSp ; PushBx ; PushDx ; PushSi ; PushDi ;
  2392. PushBp4 ;
  2393. MovDiBp4 ;
  2394. LdAxDi ; LdCxDiOff (2) ; MovBxCx ; LdDxDiOff (4) ;
  2395. AndDhImm (7FH) ;
  2396. StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
  2397. PopDi ; PopSi ; PopDx ; PopBx ; MovSpBp ; PopBp ; RetR ;
  2398. M ("real_sqrt") ;
  2399. PushBp ; MovBpSp ; PushBx ; PushDx ; PushSi ; PushDi ;
  2400. PushBp4 ;
  2401. MovSiBp4 ;
  2402. LdAxSi ; LdCxSiOff (2) ; MovBxCx ; LdDxSiOff (4) ;
  2403. CmpAl0 ; JccNear (74H, "ms_zero") ;
  2404. TestDhImm (80H) ; JccNear (75H, "ms_err") ;
  2405. StoreMath (D_MATHC) ;
  2406. (* initial guess: same mantissa, half the exponent *)
  2407. MovClAl ; AddClImm (80H) ; SarCl1 ; AddClImm (80H) ; MovAlCl ;
  2408. StoreMath (D_MATHX) ;
  2409. StDataByte (D_MATHI, 12) ;
  2410. M ("ms_loop") ;
  2411. MovDiBp4 ;
  2412. MovAxData (D_MATHC) ; StDiAx ;
  2413. MovBxData (D_MATHC + 2) ; StDiOffBx (2) ;
  2414. MovDxData (D_MATHC + 4) ; StDiOffDx (4) ;
  2415. MovDiBp4 ; AddDiImm (6) ;
  2416. MovAxData (D_MATHX) ; StDiAx ;
  2417. MovBxData (D_MATHX + 2) ; StDiOffBx (2) ;
  2418. MovDxData (D_MATHX + 4) ; StDiOffDx (4) ;
  2419. PushBp4 ; Call ("real_div") ; AddSp2 ;
  2420. MovSiBp4 ; LdAxSi ; LdCxSiOff (2) ; MovBxCx ; LdDxSiOff (4) ; StoreMath (D_MATHQ) ;
  2421. MovDiBp4 ;
  2422. MovAxData (D_MATHX) ; StDiAx ;
  2423. MovBxData (D_MATHX + 2) ; StDiOffBx (2) ;
  2424. MovDxData (D_MATHX + 4) ; StDiOffDx (4) ;
  2425. MovDiBp4 ; AddDiImm (6) ;
  2426. MovAxData (D_MATHQ) ; StDiAx ;
  2427. MovBxData (D_MATHQ + 2) ; StDiOffBx (2) ;
  2428. MovDxData (D_MATHQ + 4) ; StDiOffDx (4) ;
  2429. PushBp4 ; Call ("real_add") ; AddSp2 ;
  2430. MovSiBp4 ; LdAxSi ; LdCxSiOff (2) ; MovBxCx ; LdDxSiOff (4) ;
  2431. DecAl ;
  2432. StoreMath (D_MATHX) ;
  2433. DecData (D_MATHI) ; JccNear (75H, "ms_loop") ;
  2434. M ("ms_storex") ;
  2435. LoadMath (D_MATHX) ;
  2436. Jmp8 ("ms_store2") ;
  2437. M ("ms_storeq") ;
  2438. LoadMath (D_MATHQ) ;
  2439. M ("ms_store2") ;
  2440. PopDi ;
  2441. StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
  2442. Jmp8 ("ms_done") ;
  2443. M ("ms_zero") ;
  2444. PopDi ;
  2445. M ("ms_done") ;
  2446. M ("ms_done2") ;
  2447. PopDi ; PopSi ; PopDx ; PopBx ; MovSpBp ; PopBp ; RetR ;
  2448. M ("ms_err") ;
  2449. PopDi ;
  2450. MovAlD (3) ; MovAh (4CH) ; Int21 ;
  2451. M ("real_exp") ;
  2452. PushBp ; MovBpSp ; PushBx ; PushDx ; PushSi ; PushDi ;
  2453. PushBp4 ;
  2454. MovSiBp4 ; LdAxSi ; LdCxSiOff (2) ; MovBxCx ; LdDxSiOff (4) ;
  2455. StoreMath (D_X) ;
  2456. MathDiv (D_X, D_EXP256, D_Y) ;
  2457. MovBxDataAddr (D_EXPC) ; StDataBx (D_HT) ;
  2458. StDataByte (D_HC, 12) ; StDataByte (D_HF, 0) ;
  2459. Call ("mt_horner") ;
  2460. StDataByte (D_HC, 8) ; Call ("mt_square") ;
  2461. PopDi ; LoadMath (D_P) ;
  2462. StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
  2463. Jmp8 ("ex_done") ;
  2464. M ("ex_done") ;
  2465. PopDi ; PopSi ; PopDx ; PopBx ; MovSpBp ; PopBp ; RetR ;
  2466. M ("real_ln") ;
  2467. PushBp ; MovBpSp ; PushBx ; PushDx ; PushSi ; PushDi ;
  2468. PushBp4 ;
  2469. MovSiBp4 ; LdAxSi ; LdCxSiOff (2) ; MovBxCx ; LdDxSiOff (4) ;
  2470. CmpAl0 ; JccNear (74H, "ln_err") ;
  2471. TestDhImm (80H) ; JccNear (75H, "ln_err") ;
  2472. AddAlImm (7FH) ; Cbw ; Call ("rf_core") ; StoreMath (D_E) ;
  2473. MovDiBp4 ; MovAlD (081H) ; StDiAl ;
  2474. MovSiBp4 ; LdAxSi ; LdCxSiOff (2) ; MovBxCx ; LdDxSiOff (4) ;
  2475. StoreMath (D_X) ;
  2476. MathSub (D_X, D_ONE, D_Y) ;
  2477. MathAdd (D_X, D_ONE, D_P) ;
  2478. MathDiv (D_Y, D_P, D_Y) ;
  2479. Copy6 (D_Y, D_X) ; (* keep y for the final y*p *)
  2480. MathMul (D_Y, D_Y, D_Y) ; (* y^2 *)
  2481. MovBxDataAddr (D_LNC) ; StDataBx (D_HT) ;
  2482. StDataByte (D_HC, 8) ; StDataByte (D_HF, 0) ;
  2483. Call ("mt_horner") ;
  2484. MathMul (D_X, D_P, D_P) ; (* y * p *)
  2485. MathAdd (D_P, D_P, D_P) ; (* 2*y*p *)
  2486. MathMul (D_E, D_LN2, D_E) ;
  2487. MathAdd (D_P, D_E, D_P) ;
  2488. PopDi ; LoadMath (D_P) ;
  2489. StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
  2490. Jmp8 ("ln_done") ;
  2491. M ("ln_err") ;
  2492. PopDi ;
  2493. MovAlD (4) ; MovAh (4CH) ; Int21 ;
  2494. M ("ln_done") ;
  2495. PopDi ; PopSi ; PopDx ; PopBx ; MovSpBp ; PopBp ; RetR ;
  2496. M ("real_sin") ;
  2497. PushBp ; MovBpSp ; PushBx ; PushDx ; PushSi ; PushDi ;
  2498. PushBp4 ;
  2499. MovSiBp4 ; LdAxSi ; LdCxSiOff (2) ; MovBxCx ; LdDxSiOff (4) ;
  2500. StoreMath (D_X) ;
  2501. (* Reduce x modulo 2*pi. *)
  2502. MathDiv (D_X, D_2PI, D_Y) ;
  2503. MathInt (D_Y, D_Y) ;
  2504. MathMul (D_Y, D_2PI, D_Y) ;
  2505. MathSub (D_X, D_Y, D_X) ;
  2506. (* Bring into [-pi, pi]. *)
  2507. MathCmp (D_X, D_PI) ; CmpAx0 ; JccNear (7EH, "sn_high_done") ;
  2508. MathSub (D_X, D_2PI, D_X) ;
  2509. M ("sn_high_done") ;
  2510. MathCmp (D_X, D_NPI) ; CmpAx0 ; JccNear (7DH, "sn_low_done") ;
  2511. MathAdd (D_X, D_2PI, D_X) ;
  2512. M ("sn_low_done") ;
  2513. (* sin(x) = x * (1 + c1*x^2 + ...), the TPSRC fltsin table. *)
  2514. MathMul (D_X, D_X, D_Y) ;
  2515. MovBxDataAddr (D_FLTSIN) ; StDataBx (D_HT) ;
  2516. StDataByte (D_HC, 7) ; StDataByte (D_HF, 1) ;
  2517. Call ("mt_horner") ;
  2518. MathMul (D_P, D_X, D_P) ;
  2519. PopDi ; LoadMath (D_P) ;
  2520. StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
  2521. Jmp8 ("sn_done") ;
  2522. M ("sn_done") ;
  2523. PopDi ; PopSi ; PopDx ; PopBx ; MovSpBp ; PopBp ; RetR ;
  2524. M ("real_cos") ;
  2525. PushBp ; MovBpSp ; PushBx ; PushDx ; PushSi ; PushDi ;
  2526. PushBp4 ;
  2527. MovSiBp4 ; LdAxSi ; LdCxSiOff (2) ; MovBxCx ; LdDxSiOff (4) ;
  2528. StoreMath (D_X) ;
  2529. MathAdd (D_X, D_PI2, D_X) ;
  2530. PopDi ; LoadMath (D_X) ;
  2531. StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
  2532. PushBp4 ; Call ("real_sin") ; AddSp2 ;
  2533. PopDi ; PopSi ; PopDx ; PopBx ; MovSpBp ; PopBp ; RetR ;
  2534. M ("real_arctan") ;
  2535. PushBp ; MovBpSp ; PushBx ; PushDx ; PushSi ; PushDi ;
  2536. PushBp4 ;
  2537. MovSiBp4 ; LdAxSi ; LdCxSiOff (2) ; MovBxCx ; LdDxSiOff (4) ;
  2538. StoreMath (D_X) ;
  2539. MathCmp (D_X, D_ZERO) ; CmpAx0 ; JccNear (7DH, "at_pos") ;
  2540. StDataByte (D_SIGN, 1) ; JmpNear ("at_abs") ;
  2541. M ("at_pos") ;
  2542. StDataByte (D_SIGN, 0) ;
  2543. M ("at_abs") ;
  2544. MathAbs (D_X, D_Y) ;
  2545. StDataByte (D_RECIP, 0) ;
  2546. MathCmp (D_Y, D_ONE) ; CmpAx0 ; JccNear (7EH, "at_no_recip") ;
  2547. StDataByte (D_RECIP, 1) ;
  2548. MathDiv (D_ONE, D_Y, D_Y) ;
  2549. M ("at_no_recip") ;
  2550. StDataByte (D_TRANS, 0) ;
  2551. MathCmp (D_Y, D_ATNT) ; CmpAx0 ; JccNear (7EH, "at_no_trans") ;
  2552. StDataByte (D_TRANS, 1) ;
  2553. MathSub (D_Y, D_ONE, D_X) ;
  2554. MathAdd (D_Y, D_ONE, D_P) ;
  2555. MathDiv (D_X, D_P, D_X) ;
  2556. JmpNear ("at_poly") ;
  2557. M ("at_no_trans") ;
  2558. Copy6 (D_Y, D_X) ;
  2559. M ("at_poly") ;
  2560. MathMul (D_X, D_X, D_Y) ;
  2561. MovBxDataAddr (D_FLTATN) ; StDataBx (D_HT) ;
  2562. StDataByte (D_HC, 5) ; StDataByte (D_HF, 1) ;
  2563. Call ("mt_horner") ;
  2564. MathMul (D_P, D_X, D_Y) ;
  2565. TestDataImm (D_TRANS, 1) ; JccNear (74H, "at_after_trans") ;
  2566. MathAdd (D_Y, D_PI4, D_Y) ;
  2567. M ("at_after_trans") ;
  2568. TestDataImm (D_RECIP, 1) ; JccNear (74H, "at_after_recip") ;
  2569. MathSub (D_PI2, D_Y, D_Y) ;
  2570. M ("at_after_recip") ;
  2571. TestDataImm (D_SIGN, 1) ; JccNear (74H, "at_after_sign") ;
  2572. MathNeg (D_Y, D_Y) ;
  2573. M ("at_after_sign") ;
  2574. PopDi ; LoadMath (D_Y) ;
  2575. StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
  2576. Jmp8 ("at_done") ;
  2577. M ("at_done") ;
  2578. PopDi ; PopSi ; PopDx ; PopBx ; MovSpBp ; PopBp ; RetR
  2579. END EmitRealMath ;
  2580. PROCEDURE EmitRealConv ;
  2581. (* fmtreal / ascreal: the decimal<->binary conversions, TPSRC3 880-1250.
  2582. The cores are transcribed with their reference register conventions:
  2583. m10 multiplies AX/BX/DX by ten in place; rrange multiplies by 10**CL
  2584. through the fltdec table and the remult/rediv cores; rdec turns a packed
  2585. real into twelve decimal digits in D_CVOUT and returns the sign in CH and
  2586. the decimal exponent in CL; round and fdig* are the reference's helpers.
  2587. They all use CALL/RET internally, so a caller's flags survive where the
  2588. reference relies on that (the digit loop's CH flags are pushed explicitly
  2589. because rd_core uses CX for its second operand).
  2590. Two entries:
  2591. real_fmt (28): [BP+4] real ptr, [BP+6] destination; CX = width in CL
  2592. and decimals in CH. Writes a length-prefixed string
  2593. (dest[0] = length) padded on the LEFT to width, and
  2594. returns the length in AX. DEC >= 25 selects the
  2595. reference's scientific mode and derives the number of
  2596. fraction digits from the width.
  2597. real_val (29): [BP+4] ASCIIZ source, [BP+6] destination real. Returns
  2598. AX = the offset of the first character not consumed, or
  2599. FFFFh on a hard error (in which case the destination is
  2600. untouched). A trailing non-digit is NOT an error; the
  2601. offset tells Val where to report it, as TPSRC's ascreal
  2602. returns BX = end. *)
  2603. BEGIN
  2604. (* ---------------------------------------------------------- real_fmt *)
  2605. M ("real_fmt") ;
  2606. PushBp ; MovBpSp ;
  2607. PushBx ; PushDx ; PushSi ; PushDi ;
  2608. MovDiBp4 ; (* DI := the real *)
  2609. MovDlCh ; (* DL := decimals *)
  2610. MovChImm (0) ; (* CX := width *)
  2611. MovDhImm (0) ;
  2612. PushCx ; (* keep the width across rdec *)
  2613. MovBxDataAddr (D_FMTBUF) ;
  2614. Call ("rc_fmt") ;
  2615. PopCx ;
  2616. MovAxBx ; (* AX := end of the formatted text *)
  2617. MovBxDataAddr (D_FMTBUF) ;
  2618. SubAxBx ; (* AX := its length *)
  2619. StDataAx (D_FMTLEN) ;
  2620. StDataCx (D_FMTW) ;
  2621. (* pad := max (width - length, 0) *)
  2622. MovAxData (D_FMTW) ;
  2623. MovDxData (D_FMTLEN) ;
  2624. SubAxDx ;
  2625. CmpAx0 ;
  2626. Jge8 ("rc_fpadok") ;
  2627. XorAxAx ;
  2628. M ("rc_fpadok") ;
  2629. StDataAx (D_FMTPAD) ;
  2630. MovDxData (D_FMTLEN) ;
  2631. AddAxDx ; (* AX := pad + length *)
  2632. CmpAxImm (0FFH) ; (* a Pascal string's length is one byte *)
  2633. JccNear (76H, "rc_ftotok") ;
  2634. MovAxImm (0FFH) ;
  2635. M ("rc_ftotok") ;
  2636. StDataAx (D_FMTTOT) ;
  2637. (* dest[0] := length; then pad spaces, then the number *)
  2638. MovDxBp6 ; MovDiDx ;
  2639. StDiAl ;
  2640. IncDi ;
  2641. MovCxData (D_FMTPAD) ;
  2642. Jcxz8 ("rc_fpad_done") ;
  2643. M ("rc_fpad_loop") ;
  2644. MovAlD (20H) ; StDiAl ; IncDi ; Loop8 ("rc_fpad_loop") ;
  2645. M ("rc_fpad_done") ;
  2646. MovSiData (D_FMTBUF) ;
  2647. MovCxData (D_FMTLEN) ;
  2648. Jcxz8 ("rc_fcopy_done") ;
  2649. M ("rc_fcopy_loop") ;
  2650. LdAlSi ; StDiAl ; IncSi ; IncDi ; Loop8 ("rc_fcopy_loop") ;
  2651. M ("rc_fcopy_done") ;
  2652. MovAxData (D_FMTTOT) ;
  2653. PopDi ; PopSi ; PopDx ; PopBx ;
  2654. MovSpBp ; PopBp ; RetR ;
  2655. (* ---------------------------------------------------------- real_val *)
  2656. M ("real_val") ;
  2657. PushBp ; MovBpSp ;
  2658. PushBx ; PushDx ; PushSi ; PushDi ;
  2659. MovBxBp4 ;
  2660. MovDiBp6 ;
  2661. StDataBx (D_ASCSRC) ;
  2662. Call ("rc_asc") ;
  2663. JccNear (72H, "rc_averr") ;
  2664. MovAxBx ; (* AX := end pointer *)
  2665. MovDxData (D_ASCSRC) ;
  2666. SubAxDx ; (* AX := consumed length *)
  2667. JmpNear ("rc_avdone") ;
  2668. M ("rc_averr") ;
  2669. MovAxImm (0FFFFH) ;
  2670. M ("rc_avdone") ;
  2671. PopDi ; PopSi ; PopDx ; PopBx ;
  2672. MovSpBp ; PopBp ; RetR ;
  2673. (* ---------------------------------------------------------- m10 *)
  2674. (* AX/BX/DX := value * 10; CF = overflow; CX is preserved. The reference
  2675. multiplies by 1.25 (add the /4 value) and then by 8 via the exponent. *)
  2676. M ("rc_m10") ;
  2677. CmpAl0 ;
  2678. JccNear (75H, "rc_m10nz") ;
  2679. RetR ;
  2680. M ("rc_m10nz") ;
  2681. OrDhImm (80H) ;
  2682. PushCx ; PushDx ; PushBx ; PushAx ;
  2683. ShrDx1 ; RcrBx1 ; RcrAh1 ;
  2684. ShrDx1 ; RcrBx1 ; RcrAh1 ;
  2685. PopCx ; AddAhCh ;
  2686. PopCx ; AdcBxCx ;
  2687. PopCx ; AdcDxCx ;
  2688. PopCx ;
  2689. JccNear (73H, "rc_m10nrm") ;
  2690. RcrDx1 ; RcrBx1 ; RcrAh1 ;
  2691. IncAl ;
  2692. JccNear (75H, "rc_m10nrm") ;
  2693. Stc ; RetR ;
  2694. M ("rc_m10nrm") ;
  2695. AndDhImm (7FH) ;
  2696. AddAlImm (3) ;
  2697. RetR ;
  2698. (* -------------------------------------------------------- rrange *)
  2699. (* AX/BX/DX := value * 10**CL, signed CL, via the fltdec table. *)
  2700. M ("rc_range") ;
  2701. PushDx ; PushBx ; PushAx ;
  2702. StDataCl (D_CVDECEXP) ;
  2703. CmpCl0 ;
  2704. JccNear (79H, "rc_rpos") ;
  2705. NegCl ;
  2706. M ("rc_rpos") ;
  2707. MovBlCl ;
  2708. AndBlImm (0FCH) ;
  2709. MovBhBl ;
  2710. ShrBl1 ;
  2711. AddBlBh ;
  2712. XorBhBh ;
  2713. LeaDiBxData (D_FLTDEC) ;
  2714. LdAxDi ; LdBxDiOff (2) ; LdDxDiOff (4) ;
  2715. AndClImm (3) ;
  2716. JccNear (74H, "rc_rdone") ;
  2717. M ("rc_rmul") ;
  2718. Call ("rc_m10") ;
  2719. DecCl ;
  2720. JccNear (75H, "rc_rmul") ;
  2721. M ("rc_rdone") ;
  2722. MovCxAx ; MovSiBx ; MovDiDx ;
  2723. PopAx ; PopBx ; PopDx ;
  2724. TestDataImm (D_CVDECEXP, 80H) ;
  2725. JccNear (75H, "rc_rdiv") ;
  2726. JmpNear ("rm_core") ;
  2727. M ("rc_rdiv") ;
  2728. JmpNear ("rv_core") ;
  2729. (* ------------------------------------------------------- rangem *)
  2730. (* the signed range check around rrange: |CL| <= 38 *)
  2731. M ("rc_rangem") ;
  2732. CmpClImm (0DAH) ; JccNear (7CH, "rc_rerr") ;
  2733. CmpClImm (26H) ; JccNear (7FH, "rc_rerr") ;
  2734. PushCx ; PushSi ; PushDi ;
  2735. Call ("rc_range") ;
  2736. PopDi ; PopSi ; PopCx ;
  2737. RetR ;
  2738. M ("rc_rerr") ;
  2739. Stc ; RetR ;
  2740. (* ---------------------------------------------------------- rdec *)
  2741. (* DI -> real. D_CVOUT gets twelve digits; CH = sign, CL = decimal
  2742. exponent. The estimate is 77/256 * (exponent - 128), TPSRC's
  2743. log10(2) approximation. *)
  2744. M ("rc_rdec") ;
  2745. LdAxDi ; LdBxDiOff (2) ; LdDxDiOff (4) ;
  2746. CmpAl0 ;
  2747. JccNear (75H, "rc_rdno0") ;
  2748. MovSiData (D_CVOUT) ;
  2749. M ("rc_rdfill") ;
  2750. StSiImm (3030H) ;
  2751. IncSi ; IncSi ;
  2752. CmpSiData (D_CVOUT + 12) ;
  2753. JccNear (75H, "rc_rdfill") ;
  2754. MovCxImm (0) ;
  2755. RetR ;
  2756. M ("rc_rdno0") ;
  2757. MovChDh ;
  2758. AndDhImm (7FH) ;
  2759. PushAx ; PushDx ;
  2760. SubAl (80H) ; Cbw ; MovDxImm (004DH) ; ImulDx ; AddAxImm (5) ;
  2761. MovClAh ;
  2762. PopDx ; PopAx ;
  2763. CmpClImm (0D9H) ;
  2764. JccNear (75H, "rc_rdnocomp") ;
  2765. IncCl ;
  2766. M ("rc_rdnocomp") ;
  2767. PushCx ;
  2768. NegCl ;
  2769. Call ("rc_range") ;
  2770. PopCx ;
  2771. CmpAl (81H) ;
  2772. JccNear (73H, "rc_rdnoadj") ;
  2773. Call ("rc_m10") ;
  2774. DecCl ;
  2775. M ("rc_rdnoadj") ;
  2776. PushCx ;
  2777. OrDhImm (80H) ;
  2778. MovClImm (84H) ;
  2779. SubClAl ;
  2780. MovAlD (0) ;
  2781. CmpCl0 ;
  2782. JccNear (74H, "rc_rdnosh") ;
  2783. M ("rc_rdshift") ;
  2784. ShrDx1 ; RcrBx1 ; RcrAh1 ;
  2785. DecCl ;
  2786. JccNear (75H, "rc_rdshift") ;
  2787. M ("rc_rdnosh") ;
  2788. MovSiData (D_CVOUT) ;
  2789. M ("rc_rdconv") ;
  2790. MovChDh ; MovClImm (4) ; ShrChCl ; AddChImm (30H) ;
  2791. StSiCh ;
  2792. AndDhImm (0FH) ;
  2793. PushDx ; PushBx ; PushAx ;
  2794. ShlAx1 ; RclBx1 ; RclDx1 ;
  2795. ShlAx1 ; RclBx1 ; RclDx1 ;
  2796. PopCx ; AddAxCx ;
  2797. PopCx ; AdcBxCx ;
  2798. PopCx ; AdcDxCx ;
  2799. ShlAx1 ; RclBx1 ; RclDx1 ;
  2800. IncSi ;
  2801. CmpSiData (D_CVOUT + 12) ;
  2802. JccNear (75H, "rc_rdconv") ;
  2803. PopCx ;
  2804. RetR ;
  2805. (* --------------------------------------------------------- round *)
  2806. (* round the digits at index AL: look at the next digit, and carry if it is
  2807. 5 or more. CL is the decimal exponent and is incremented on carry-out. *)
  2808. M ("rc_round") ;
  2809. XorAhAh ;
  2810. MovBxDataAddr (D_CVOUT) ;
  2811. AddBxAx ;
  2812. CmpBxByte (35H) ;
  2813. MovBxByte (0) ;
  2814. JccNear (72H, "rc_rounddone") ;
  2815. M ("rc_roundloop") ;
  2816. DecAl ;
  2817. JccNear (78H, "rc_roundexp") ;
  2818. DecBx ;
  2819. IncBxByte ;
  2820. CmpBxByte (3AH) ;
  2821. JccNear (72H, "rc_rounddone") ;
  2822. MovBxByte (0) ;
  2823. JmpNear ("rc_roundloop") ;
  2824. M ("rc_roundexp") ;
  2825. MovBxByte (31H) ;
  2826. MovBxOff1Byte (0) ;
  2827. IncCl ;
  2828. M ("rc_rounddone") ;
  2829. RetR ;
  2830. (* ------------------------------------------------------------ fdig *)
  2831. M ("rc_fdig") ;
  2832. LdAlSi ;
  2833. CmpAl0 ;
  2834. JccNear (74H, "rc_fdig0") ;
  2835. IncSi ;
  2836. JmpNear ("rc_fdigsto") ;
  2837. M ("rc_fdig0") ;
  2838. MovAlD (30H) ;
  2839. M ("rc_fdigsto") ;
  2840. StBxAl ;
  2841. IncBx ;
  2842. RetR ;
  2843. (* ---------------------------------------------------------- fcore *)
  2844. (* DI = real, BX = destination, CX = width, DX = decimals. Writes raw
  2845. characters (no padding) and leaves BX one past the end. *)
  2846. M ("rc_fmt") ;
  2847. PushBx ;
  2848. CmpDxImm (19H) ;
  2849. JccNear (72H, "rc_fnlim2") ;
  2850. MovAxCx ;
  2851. CmpCh0 ;
  2852. JccNear (74H, "rc_flimok") ;
  2853. TestChImm (80H) ;
  2854. JccNear (74H, "rc_flimpos") ;
  2855. XorAxAx ;
  2856. JmpNear ("rc_flimok") ;
  2857. M ("rc_flimpos") ;
  2858. MovAxImm (0FFH) ;
  2859. M ("rc_flimok") ;
  2860. MovDl (7) ;
  2861. TestDiOffImm (5, 80H) ;
  2862. JccNear (74H, "rc_frpos") ;
  2863. IncDl ;
  2864. M ("rc_frpos") ;
  2865. SubAlDl ;
  2866. JccNear (73H, "rc_frnound") ;
  2867. MovAlD (0) ;
  2868. M ("rc_frnound") ;
  2869. CmpAl (9) ;
  2870. JccNear (72H, "rc_frnolim") ;
  2871. MovAlD (9) ;
  2872. M ("rc_frnolim") ;
  2873. IncAl ;
  2874. MovDlAl ;
  2875. MovDhAl ;
  2876. M ("rc_fnlim2") ;
  2877. PushDx ;
  2878. Call ("rc_rdec") ;
  2879. PopDx ;
  2880. MovAlDl ;
  2881. IncAl ;
  2882. CmpDh0 ;
  2883. JccNear (75H, "rc_frround") ;
  2884. AddAlCl ;
  2885. JccNear (79H, "rc_frlimprc") ;
  2886. StDataByte (D_CVOUT, 0) ;
  2887. JmpNear ("rc_frfmtit") ;
  2888. M ("rc_frlimprc") ;
  2889. CmpAl (0CH) ;
  2890. JccNear (72H, "rc_frround") ;
  2891. MovAlD (0BH) ;
  2892. M ("rc_frround") ;
  2893. Call ("rc_round") ;
  2894. M ("rc_frfmtit") ;
  2895. PopBx ;
  2896. MovSiData (D_CVOUT) ;
  2897. TestChImm (80H) ;
  2898. JccNear (74H, "rc_frpos2") ;
  2899. MovAlD (2DH) ;
  2900. Call ("rc_fdigsto") ;
  2901. M ("rc_frpos2") ;
  2902. MovChCl ;
  2903. CmpDh0 ;
  2904. JccNear (74H, "rc_frzer") ;
  2905. MovChImm (0) ;
  2906. M ("rc_frzer") ;
  2907. CmpCh0 ;
  2908. JccNear (79H, "rc_frint") ;
  2909. Call ("rc_fdig0") ;
  2910. JmpNear ("rc_frfrac0") ;
  2911. M ("rc_frint") ;
  2912. Call ("rc_fdig") ;
  2913. DecCh ;
  2914. JccNear (79H, "rc_frint") ;
  2915. M ("rc_frfrac0") ;
  2916. CmpDl0 ;
  2917. JccNear (74H, "rc_frexp") ;
  2918. MovAlD (2EH) ;
  2919. Call ("rc_fdigsto") ;
  2920. M ("rc_frfill0") ;
  2921. IncCh ;
  2922. JccNear (74H, "rc_frfrac") ;
  2923. Call ("rc_fdig0") ;
  2924. DecDl ;
  2925. JccNear (75H, "rc_frfill0") ;
  2926. M ("rc_frfrac") ;
  2927. DecDl ;
  2928. JccNear (78H, "rc_frexp") ;
  2929. Call ("rc_fdig") ;
  2930. JmpNear ("rc_frfrac") ;
  2931. M ("rc_frexp") ;
  2932. CmpDh0 ;
  2933. JccNear (75H, "rc_frputexp") ;
  2934. RetR ;
  2935. M ("rc_frputexp") ;
  2936. MovAlD (45H) ;
  2937. Call ("rc_fdigsto") ;
  2938. MovAlD (2BH) ;
  2939. CmpCl0 ;
  2940. JccNear (79H, "rc_frposexp") ;
  2941. NegCl ;
  2942. MovAlD (2DH) ;
  2943. M ("rc_frposexp") ;
  2944. Call ("rc_fdigsto") ;
  2945. MovAlD (2FH) ;
  2946. M ("rc_frexpsub") ;
  2947. IncAl ;
  2948. SubClImm (0AH) ;
  2949. JccNear (73H, "rc_frexpsub") ;
  2950. Call ("rc_fdigsto") ;
  2951. AddClImm (3AH) ;
  2952. MovAlCl ;
  2953. JmpNear ("rc_fdigsto") ;
  2954. (* ------------------------------------------------------------ asc *)
  2955. (* BX = ASCIIZ source, DI = destination real. Returns BX = end, CF = 1 on
  2956. a hard error. The digit add keeps the reference's stack dance: the
  2957. digit's real goes in a and the accumulated value in b (addition is
  2958. commutative), while the pointers and the CH flags are saved around the
  2959. call because rd_core uses CX/SI/DI. *)
  2960. M ("rc_asc") ;
  2961. LdClBx ;
  2962. CmpClImm (2DH) ;
  2963. JccNear (75H, "rc_asign") ;
  2964. IncBx ;
  2965. M ("rc_asign") ;
  2966. PushCx ;
  2967. Call ("rc_asc2") ;
  2968. PopCx ;
  2969. JccNear (72H, "rc_aouterr") ;
  2970. CmpClImm (2DH) ;
  2971. JccNear (75H, "rc_aok") ;
  2972. LdAlDi ;
  2973. CmpAl0 ;
  2974. JccNear (74H, "rc_aok") ;
  2975. XorDiOffImm (5, 80H) ;
  2976. M ("rc_aok") ;
  2977. Clc ;
  2978. RetR ;
  2979. M ("rc_aouterr") ;
  2980. Stc ;
  2981. RetR ;
  2982. M ("rc_asc2") ;
  2983. MovSiBx ;
  2984. XorAxAx ; XorBxBx ; XorDxDx ; XorCxCx ;
  2985. StDataByte (D_CVEXPCNT, 0) ;
  2986. M ("rc_aloop") ;
  2987. LdClSi ;
  2988. CmpClImm (61H) ; JccNear (72H, "rc_aup") ;
  2989. CmpClImm (7AH) ; Ja8 ("rc_aup") ;
  2990. SubClImm (20H) ;
  2991. M ("rc_aup") ;
  2992. Call ("rc_adigit2") ;
  2993. JccNear (72H, "rc_anodig") ;
  2994. Call ("rc_m10") ;
  2995. JccNear (72H, "rc_aerr") ;
  2996. PushDi ; PushSi ; PushCx ;
  2997. PushDx ; PushBx ; PushAx ;
  2998. MovAlCl ;
  2999. XorAhAh ;
  3000. Call ("rf_core") ;
  3001. MovCxAx ; MovSiBx ; MovDiDx ;
  3002. PopAx ; PopBx ; PopDx ;
  3003. StDataImm (D_RSIGN, 0) ; (* readd, not resub: resign := 0 *)
  3004. Call ("rd_core") ;
  3005. PopCx ; PopSi ; PopDi ;
  3006. TestChImm (40H) ;
  3007. JccNear (74H, "rc_anext") ;
  3008. DecData (D_CVEXPCNT) ;
  3009. M ("rc_anext") ;
  3010. IncSi ;
  3011. JmpNear ("rc_aloop") ;
  3012. M ("rc_anodig") ;
  3013. CmpClImm (2EH) ;
  3014. JccNear (75H, "rc_aexp") ;
  3015. TestChImm (40H) ;
  3016. JccNear (75H, "rc_aerr") ;
  3017. OrChImm (40H) ;
  3018. JmpNear ("rc_anext") ;
  3019. M ("rc_aexp") ;
  3020. CmpClImm (45H) ;
  3021. LdClData (D_CVEXPCNT) ;
  3022. JccNear (75H, "rc_aexp3") ;
  3023. Call ("rc_rangem") ;
  3024. JccNear (72H, "rc_aerr") ;
  3025. IncSi ;
  3026. LdClSi ;
  3027. CmpClImm (2BH) ;
  3028. JccNear (74H, "rc_aposexp") ;
  3029. CmpClImm (2DH) ;
  3030. JccNear (75H, "rc_aexp2") ;
  3031. OrChImm (20H) ;
  3032. M ("rc_aposexp") ;
  3033. IncSi ;
  3034. M ("rc_aexp2") ;
  3035. Call ("rc_adigit") ;
  3036. JccNear (72H, "rc_aerr") ;
  3037. PushAx ;
  3038. MovAlCl ;
  3039. IncSi ;
  3040. Call ("rc_adigit") ;
  3041. JccNear (72H, "rc_aonedig") ;
  3042. MovAhAl ;
  3043. ShlAl1 ; ShlAl1 ; AddAlAh ; ShlAl1 ; AddAlCl ;
  3044. IncSi ;
  3045. M ("rc_aonedig") ;
  3046. MovClAl ;
  3047. PopAx ;
  3048. TestChImm (20H) ;
  3049. JccNear (74H, "rc_aexp3") ;
  3050. NegCl ;
  3051. M ("rc_aexp3") ;
  3052. Call ("rc_rangem") ;
  3053. JccNear (72H, "rc_aerr") ;
  3054. StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
  3055. Clc ;
  3056. JmpNear ("rc_aret") ;
  3057. M ("rc_aerr") ;
  3058. Stc ;
  3059. M ("rc_aret") ;
  3060. MovBxSi ;
  3061. RetR ;
  3062. (* -------------------------------------------------------- adigit *)
  3063. M ("rc_adigit") ;
  3064. LdClSi ;
  3065. M ("rc_adigit2") ;
  3066. CmpClImm (30H) ;
  3067. JccNear (72H, "rc_adret") ;
  3068. CmpClImm (3AH) ;
  3069. Cmc ;
  3070. JccNear (72H, "rc_adret") ;
  3071. SubClImm (30H) ;
  3072. M ("rc_adret") ;
  3073. RetR ;
  3074. (* --------------------------------------------------------- real_write *)
  3075. (* [BP+4] = decimals, [BP+6] = width, [BP+8] = real. Formats into the
  3076. internal buffer, then pads and prints: the same field real_fmt builds
  3077. as a string, sent straight to the console. `Write(Value:FW:DEC)' uses
  3078. this; the default widths are the compiler's (18 columns, decimals
  3079. FFFFh, which fmtreal turns into its scientific mode). *)
  3080. M ("real_write") ;
  3081. PushBp ; MovBpSp ;
  3082. PushBx ; PushDx ; PushSi ; PushDi ;
  3083. MovAxBp6 ; StDataAx (D_FMTW) ; (* width *)
  3084. MovDxBp4 ; (* DX := decimals *)
  3085. MovCxBp6 ; (* CX := width *)
  3086. MovDiBp8 ; (* DI := the real *)
  3087. MovBxDataAddr (D_FMTBUF) ;
  3088. Call ("rc_fmt") ; (* BX := end of the formatted text *)
  3089. MovAxBx ;
  3090. MovBxDataAddr (D_FMTBUF) ;
  3091. SubAxBx ; (* AX := length *)
  3092. StDataAx (D_FMTLEN) ;
  3093. MovAxData (D_FMTW) ;
  3094. MovDxData (D_FMTLEN) ;
  3095. SubAxDx ;
  3096. CmpAx0 ;
  3097. Jge8 ("rc_wpadok") ;
  3098. XorAxAx ;
  3099. M ("rc_wpadok") ;
  3100. StDataAx (D_FMTPAD) ;
  3101. MovCxData (D_FMTPAD) ;
  3102. Jcxz8 ("rc_wbody") ;
  3103. M ("rc_wpad") ;
  3104. MovAlD (20H) ; MovDlAl ; MovAh (02H) ; Int21 ; Loop8 ("rc_wpad") ;
  3105. M ("rc_wbody") ;
  3106. MovSiData (D_FMTBUF) ;
  3107. MovCxData (D_FMTLEN) ;
  3108. Jcxz8 ("rc_wdone") ;
  3109. M ("rc_wout") ;
  3110. LdAlSi ; MovDlAl ; MovAh (02H) ; Int21 ; IncSi ; Loop8 ("rc_wout") ;
  3111. M ("rc_wdone") ;
  3112. PopDi ; PopSi ; PopDx ; PopBx ;
  3113. MovSpBp ; PopBp ; RetR
  3114. END EmitRealConv ;
  3115. PROCEDURE EmitSetOps ;
  3116. (* Set algebra over byte arrays. A set VALUE is an address plus a length;
  3117. the compiler picks the length from the declared set type. All three
  3118. entries take the length on the stack, so no register state survives a
  3119. call except what the entry itself preserves.
  3120. set_op: [BP+4] = (op << 8) | size, [BP+6] = dest, [BP+8] = right,
  3121. [BP+10] = left. op 0 = AND (intersection), 1 = OR (union),
  3122. 2 = left AND NOT right (difference).
  3123. set_cmp: [BP+4] = size, [BP+6] = right, [BP+8] = left. AX = mask:
  3124. bit 0 = equal, bit 1 = strict subset, bit 2 = strict superset.
  3125. set_copy: [BP+4] = size, [BP+6] = source, [BP+8] = destination. *)
  3126. BEGIN
  3127. M ("set_op") ;
  3128. PushBp ; MovBpSp ;
  3129. PushBx ; PushCx ; PushDx ; PushSi ; PushDi ;
  3130. MovAxBp4 ; (* AH = op, AL = size *)
  3131. MovDhAl ; (* DH = byte counter *)
  3132. MovSiBp10 ; (* SI = left *)
  3133. MovDiBp8 ; (* DI = right *)
  3134. MovBxBp6 ; (* BX = destination *)
  3135. CmpDh0 ;
  3136. Je8 ("so_done") ;
  3137. M ("so_loop") ;
  3138. LdAlSi ; LdDlDi ;
  3139. CmpAhImm (0) ; Je8 ("so_and") ;
  3140. CmpAhImm (1) ; Je8 ("so_or") ;
  3141. NotDl ; AndAlDl ; (* difference *)
  3142. Jmp8 ("so_store") ;
  3143. M ("so_and") ;
  3144. AndAlDl ;
  3145. Jmp8 ("so_store") ;
  3146. M ("so_or") ;
  3147. OrAlDl ;
  3148. M ("so_store") ;
  3149. StBxAl ;
  3150. IncSi ; IncDi ; IncBx ;
  3151. DecDh ;
  3152. Jne8 ("so_loop") ;
  3153. M ("so_done") ;
  3154. PopDi ; PopSi ; PopDx ; PopCx ; PopBx ;
  3155. MovSpBp ; PopBp ; RetR ;
  3156. M ("set_cmp") ;
  3157. PushBp ; MovBpSp ;
  3158. PushBx ; PushCx ; PushDx ; PushSi ; PushDi ;
  3159. MovCxBp4 ; (* CX = size *)
  3160. MovSiBp8 ; (* left *)
  3161. MovDiBp6 ; (* right *)
  3162. MovAh (7) ; (* equal | subset | superset; AH, not AX:
  3163. AL is the set byte under test *)
  3164. Jcxz8 ("scm_final") ;
  3165. M ("scm_loop") ;
  3166. LdAlSi ; LdDlDi ;
  3167. MovBlAl ; MovDhDl ; (* keep the originals *)
  3168. CmpAlDl ;
  3169. Je8 ("scm_eq") ;
  3170. AndAhImm (0FEH) ; (* not equal *)
  3171. M ("scm_eq") ;
  3172. NotDl ; AndAlDl ; (* left AND NOT right *)
  3173. CmpAl0 ;
  3174. Je8 ("scm_sub") ;
  3175. AndAhImm (0FDH) ; (* not a subset *)
  3176. M ("scm_sub") ;
  3177. MovAlBl ; NotAl ; AndAlDh ; (* right AND NOT left *)
  3178. CmpAl0 ;
  3179. Je8 ("scm_sup") ;
  3180. AndAhImm (0FBH) ; (* not a superset *)
  3181. M ("scm_sup") ;
  3182. IncSi ; IncDi ;
  3183. Loop8 ("scm_loop") ;
  3184. (* equality is not a STRICT subset/superset *)
  3185. M ("scm_final") ;
  3186. TestAhImm (1) ;
  3187. Je8 ("scm_done") ;
  3188. AndAhImm (0F9H) ;
  3189. M ("scm_done") ;
  3190. MovAlAh ; (* AX := the mask *)
  3191. MovAh (0) ;
  3192. PopDi ; PopSi ; PopDx ; PopCx ; PopBx ;
  3193. MovSpBp ; PopBp ; RetR ;
  3194. M ("set_copy") ;
  3195. PushBp ; MovBpSp ;
  3196. PushBx ; PushCx ; PushSi ;
  3197. MovCxBp4 ;
  3198. MovSiBp6 ;
  3199. MovBxBp8 ;
  3200. Jcxz8 ("scp_done") ;
  3201. M ("scp_loop") ;
  3202. LdAlSi ; StBxAl ; IncSi ; IncBx ;
  3203. Loop8 ("scp_loop") ;
  3204. M ("scp_done") ;
  3205. PopSi ; PopCx ; PopBx ;
  3206. MovSpBp ; PopBp ; RetR
  3207. END EmitSetOps ;
  3208. PROCEDURE EmitStrDelete ;
  3209. (* str_delete: Delete(S, pos, len). S is a counted string modified in place;
  3210. pos is 1-based. A non-positive length, pos < 1 or pos > |S| are no-ops.
  3211. The tail after the deleted range is moved down and the length byte is
  3212. rewritten. *)
  3213. BEGIN
  3214. M ("str_delete") ;
  3215. PushBp ; MovBpSp ;
  3216. PushBx ; PushCx ; PushDx ; PushSi ; PushDi ;
  3217. MovBxBp4 ; (* BX := S *)
  3218. LdAlBx ; XorAhAh ; MovCxAx ; (* CX := |S| *)
  3219. MovAxBp6 ; (* AX := pos *)
  3220. CmpAxImm (1) ; Jb8 ("strdel_done") ;
  3221. CmpAxCx ; Ja8 ("strdel_done") ;
  3222. MovDxAx ; (* DX := pos *)
  3223. MovAxBp8 ; (* AX := len *)
  3224. CmpAx0 ; Jle8 ("strdel_done") ;
  3225. MovSiAx ; (* SI := len *)
  3226. MovAxCx ; SubAxDx ; AddAxImm (1) ; (* AX := available *)
  3227. CmpAxSi ; Jbe8 ("strdel_count") ;
  3228. MovAxSi ;
  3229. M ("strdel_count") ;
  3230. MovCxAx ; (* CX := count *)
  3231. MovAxDx ; AddAxBx ; MovDiAx ; (* DI := S + pos *)
  3232. AddAxCx ; MovSiAx ; (* SI := first tail byte *)
  3233. (* tail = |S| - pos - count + 1 *)
  3234. LdAlBx ; XorAhAh ; SubAxDx ; SubAxCx ; AddAxImm (1) ; PushAx ;
  3235. LdAlBx ; XorAhAh ; SubAxCx ; StBxAl ; (* new length *)
  3236. PopAx ; MovCxAx ;
  3237. Jcxz8 ("strdel_done") ;
  3238. M ("strdel_loop") ;
  3239. LdAlSi ; StDiAl ; IncSi ; IncDi ; Loop8 ("strdel_loop") ;
  3240. M ("strdel_done") ;
  3241. PopDi ; PopSi ; PopDx ; PopCx ; PopBx ; MovSpBp ; PopBp ; RetR
  3242. END EmitStrDelete ;
  3243. PROCEDURE EmitStrInsert ;
  3244. (* str_insert: Insert(Source, Dest, Index, Max). The compiler pushes Max,
  3245. Index, Dest, Source in that order, so Source is at [BP+10], Dest at
  3246. [BP+8], Index at [BP+6], Max at [BP+4]. Both strings are counted; the
  3247. operation is in place. Index is clamped to [1, |Dest|+1], and an
  3248. overflowing result leaves through TP3 error 16 ("String too long"). *)
  3249. BEGIN
  3250. M ("str_insert") ;
  3251. PushBp ; MovBpSp ;
  3252. PushBx ; PushCx ; PushDx ; PushSi ; PushDi ;
  3253. MovBxBp10 ; (* BX := source *)
  3254. LdAlBx ; XorAhAh ; MovCxAx ; (* CX := source length *)
  3255. CmpAx0 ; JccNear (74H, "ins_done") ;
  3256. MovDiBp8 ; (* DI := destination *)
  3257. LdAlDi ; XorAhAh ; MovDxAx ; (* DX := destination length *)
  3258. MovAxBp6 ; (* AX := index *)
  3259. CmpAxImm (1) ; Jae8 ("ins_idx") ;
  3260. MovAxImm (1) ;
  3261. M ("ins_idx") ;
  3262. MovSiDx ; IncSi ; (* |Dest| + 1 *)
  3263. CmpAxSi ; Jbe8 ("ins_idx2") ;
  3264. MovAxSi ;
  3265. M ("ins_idx2") ;
  3266. StDataAx (D_ISTMP) ;
  3267. MovAxCx ; AddAxDx ; (* total length *)
  3268. StDataAx (D_ITMP) ;
  3269. MovBxBp4 ; (* BX := max *)
  3270. CmpAxBx ; Jbe8 ("ins_len") ;
  3271. MovAlD (10H) ; MovAh (4CH) ; Int21 ;
  3272. M ("ins_len") ;
  3273. (* Move the tail backwards: tail = |Dest| - Index + 1. *)
  3274. MovAxData (D_ISTMP) ; MovSiAx ; (* SI := index *)
  3275. MovAxDx ; SubAxSi ; AddAxImm (1) ;
  3276. MovCxAx ; (* tail count *)
  3277. MovAxDx ; AddAxDi ; MovSiAx ; (* SI := dest + destlen *)
  3278. MovBxBp10 ; LdAlBx ; XorAhAh ; (* source length *)
  3279. MovDiSi ; AddDiAx ; (* DI := SI + source length *)
  3280. Jcxz8 ("ins_tail_done") ;
  3281. M ("ins_tail") ;
  3282. LdAlSi ; StDiAl ; DecSi ; DecDi ; Loop8 ("ins_tail") ;
  3283. M ("ins_tail_done") ;
  3284. (* Copy the source forward at dest+index. *)
  3285. MovDiBp8 ; MovAxData (D_ISTMP) ; AddDiAx ;
  3286. MovBxBp10 ; LdAlBx ; XorAhAh ; MovCxAx ; IncBx ;
  3287. Jcxz8 ("ins_copy_done") ;
  3288. M ("ins_copy") ;
  3289. LdAlBx ; StDiAl ; IncBx ; IncDi ; Loop8 ("ins_copy") ;
  3290. M ("ins_copy_done") ;
  3291. MovAxData (D_ITMP) ; MovDiBp8 ; StDiAl ;
  3292. M ("ins_done") ;
  3293. PopDi ; PopSi ; PopDx ; PopCx ; PopBx ; MovSpBp ; PopBp ; RetR
  3294. END EmitStrInsert ;
  3295. PROCEDURE EmitFileOps ;
  3296. (* File control-block operations. The block is Compiler.FileVarSize:
  3297. +0 DOS handle (FFFFh when closed)
  3298. +2 mode (0 closed, 1 read, 2 write)
  3299. +4 128-byte NUL-terminated filename
  3300. Assign copies the counted string into +4; Reset/Rewrite/Close issue the DOS
  3301. calls and leave their status in D_IORES. IOResult reads and clears that
  3302. word. Eof is a later increment: it needs position/size arithmetic, and the
  3303. compile ladder has not reached it yet.
  3304. The caller pushes arguments left-to-right before the CALL, so [BP+4] is the
  3305. last push. assign takes the filename first and the file block last:
  3306. [BP+4]=file, [BP+6]=<length><filename>. The one-argument entries take the
  3307. file block at [BP+4]. *)
  3308. BEGIN
  3309. (* Assign(F, Name) *)
  3310. M ("file_assign") ;
  3311. PushBp ; MovBpSp ;
  3312. PushBx ; PushCx ; PushDx ; PushSi ; PushDi ;
  3313. MovDiBp4 ; (* DI = file block *)
  3314. MovAxImm (0FFFFH) ; StDiAx ; (* handle := invalid *)
  3315. XorAxAx ;
  3316. B (89H) ; B (45H) ; B (2) ; (* MOV [DI+2],AX: mode := closed *)
  3317. MovSiBp6 ; (* SI = counted filename *)
  3318. MovAlSi ; (* AL := length byte *)
  3319. IncSi ;
  3320. B (3CH) ; B (7FH) ; (* CMP AL,127 *)
  3321. Jbe8 ("fa_len") ;
  3322. MovAlD (127) ; (* clamp to the name buffer *)
  3323. M ("fa_len") ;
  3324. MovClAl ; (* CL := length *)
  3325. B (30H) ; B (0EDH) ; (* XOR CH,CH: CX := length *)
  3326. B (83H) ; B (0C7H) ; B (4) ; (* ADD DI,4: name buffer *)
  3327. Jcxz8 ("fa_nul") ;
  3328. M ("fa_copy") ;
  3329. LdAlSi ; StDiAl ; (* copy one byte, no string instr *)
  3330. IncSi ;
  3331. B (47H) ; (* INC DI *)
  3332. Loop8 ("fa_copy") ;
  3333. M ("fa_nul") ;
  3334. B (0C6H) ; B (05H) ; B (0) ; (* MOV byte [DI],0: NUL *)
  3335. PopDi ; PopSi ; PopDx ; PopCx ; PopBx ;
  3336. MovSpBp ; PopBp ; RetR ;
  3337. (* Reset(F): AX=3D00h, DS:DX=name *)
  3338. M ("file_reset") ;
  3339. PushBp ; MovBpSp ;
  3340. PushBx ; PushCx ; PushDx ; PushDi ;
  3341. MovDiBp4 ;
  3342. B (8DH) ; B (55H) ; B (4) ; (* LEA DX,[DI+4] *)
  3343. MovAxImm (3D00H) ; Int21 ;
  3344. Jb8 ("fr_err") ;
  3345. StDiAx ; (* handle := AX *)
  3346. MovAxImm (1) ; B (89H) ; B (45H) ; B (2) ; (* mode := read *)
  3347. XorAxAx ; StDataAx (D_IORES) ;
  3348. Jmp8 ("fr_out") ;
  3349. M ("fr_err") ;
  3350. B (89H) ; B (0C3H) ; (* MOV BX,AX: keep the DOS status *)
  3351. MovAxImm (0FFFFH) ; StDiAx ;
  3352. XorAxAx ; B (89H) ; B (45H) ; B (2) ; (* mode := closed *)
  3353. StDataBx (D_IORES) ;
  3354. M ("fr_out") ;
  3355. PopDi ; PopDx ; PopCx ; PopBx ;
  3356. MovSpBp ; PopBp ; RetR ;
  3357. (* Rewrite(F): AX=3C00h, CX=0, DS:DX=name *)
  3358. M ("file_rewrite") ;
  3359. PushBp ; MovBpSp ;
  3360. PushBx ; PushCx ; PushDx ; PushDi ;
  3361. MovDiBp4 ;
  3362. B (8DH) ; B (55H) ; B (4) ; (* LEA DX,[DI+4] *)
  3363. XorCxCx ;
  3364. MovAxImm (3C00H) ; Int21 ;
  3365. Jb8 ("fw_err") ;
  3366. StDiAx ;
  3367. MovAxImm (2) ; B (89H) ; B (45H) ; B (2) ; (* mode := write *)
  3368. XorAxAx ; StDataAx (D_IORES) ;
  3369. Jmp8 ("fw_out") ;
  3370. M ("fw_err") ;
  3371. B (89H) ; B (0C3H) ;
  3372. MovAxImm (0FFFFH) ; StDiAx ;
  3373. XorAxAx ; B (89H) ; B (45H) ; B (2) ;
  3374. StDataBx (D_IORES) ;
  3375. M ("fw_out") ;
  3376. PopDi ; PopDx ; PopCx ; PopBx ;
  3377. MovSpBp ; PopBp ; RetR ;
  3378. (* Close(F): AX=3E00h, BX=handle; a closed block needs no DOS call *)
  3379. M ("file_close") ;
  3380. PushBp ; MovBpSp ;
  3381. PushBx ; PushDx ; PushDi ;
  3382. MovDiBp4 ;
  3383. LdAxDi ;
  3384. CmpAxImm (0FFFFH) ;
  3385. Je8 ("fc_out") ;
  3386. B (89H) ; B (0C3H) ; (* BX := handle *)
  3387. MovAh (3EH) ; Int21 ;
  3388. B (89H) ; B (0C3H) ; (* BX := status *)
  3389. MovAxImm (0FFFFH) ; StDiAx ;
  3390. XorAxAx ; B (89H) ; B (45H) ; B (2) ;
  3391. StDataBx (D_IORES) ;
  3392. M ("fc_out") ;
  3393. PopDi ; PopDx ; PopBx ;
  3394. MovSpBp ; PopBp ; RetR ;
  3395. (* IOResult: AX := D_IORES; D_IORES := 0 *)
  3396. M ("ioresult") ;
  3397. MovAxData (D_IORES) ;
  3398. StDataImm (D_IORES, 0) ;
  3399. RetR ;
  3400. (* Typed-file block I/O is lowered by the compiler to INT 21h; it calls
  3401. this with AX = the DOS result so the next IOResult can report it. *)
  3402. M ("io_store") ;
  3403. StDataAx (D_IORES) ;
  3404. RetR ;
  3405. (* New(P): allocate Size bytes from the bump heap, store the address in P. *)
  3406. M ("heap_new") ;
  3407. PushBp ; MovBpSp ;
  3408. PushBx ; PushCx ; PushDx ;
  3409. MovBxData (D_HEAP) ;
  3410. MovDxBp4 ;
  3411. B (89H) ; B (1AH) ; (* MOV [DX],BX: P^ := the block *)
  3412. MovCxBp6 ;
  3413. MovAxBx ;
  3414. AddAxCx ;
  3415. StDataAx (D_HEAP) ;
  3416. PopDx ; PopCx ; PopBx ;
  3417. MovSpBp ; PopBp ; RetR ;
  3418. (* Eof(F): D_TXEOF, or DOS current position >= size. *)
  3419. M ("file_eof") ;
  3420. PushBp ; MovBpSp ;
  3421. PushBx ; PushCx ; PushDx ; PushSi ; PushDi ;
  3422. LdAlData (D_TXEOF) ; CmpAl0 ; Jne8 ("fe_yes") ;
  3423. MovDiBp4 ; LdAxDi ; CmpAxImm (0FFFFH) ; Je8 ("fe_yes") ;
  3424. B (89H) ; B (0C3H) ; (* BX := handle *)
  3425. XorCxCx ; XorDxDx ; MovAxImm (4201H) ; Int21 ;
  3426. MovSiAx ; MovDiDx ; (* current low/high *)
  3427. XorCxCx ; XorDxDx ; MovAxImm (4202H) ; Int21 ;
  3428. CmpDxDi ; Jb8 ("fe_yes_restore") ; Ja8 ("fe_no_restore") ;
  3429. B (39H) ; B (0F0H) ; (* CMP AX,SI *)
  3430. Jb8 ("fe_yes_restore") ; Ja8 ("fe_no_restore") ;
  3431. M ("fe_yes_restore") ;
  3432. MovAxImm (1) ; Jmp8 ("fe_restore") ;
  3433. M ("fe_no_restore") ;
  3434. XorAxAx ;
  3435. M ("fe_restore") ;
  3436. PushAx ;
  3437. B (89H) ; B (0F9H) ; (* MOV CX,DI: current high *)
  3438. B (89H) ; B (0F2H) ; (* MOV DX,SI: current low *)
  3439. MovAxImm (4200H) ; Int21 ;
  3440. PopAx ;
  3441. Jmp8 ("fe_done") ;
  3442. M ("fe_yes") ;
  3443. MovAxImm (1) ;
  3444. M ("fe_done") ;
  3445. PopDi ; PopSi ; PopDx ; PopCx ; PopBx ;
  3446. MovSpBp ; PopBp ; RetR ;
  3447. (* Readln(F, S): read one line into the counted string at [BP+6]. CR is
  3448. skipped, LF terminates, ^Z or a zero-byte read marks text EOF. *)
  3449. M ("file_readln") ;
  3450. PushBp ; MovBpSp ;
  3451. PushBx ; PushCx ; PushDx ; PushSi ; PushDi ;
  3452. MovDiBp6 ;
  3453. B (0C6H) ; B (05H) ; B (0) ; (* dest[0] := 0 *)
  3454. IncDi ;
  3455. XorCxCx ; (* CX := stored character count *)
  3456. MovSiBp4 ; (* SI := file block *)
  3457. M ("frl_get") ;
  3458. LdAxSi ; (* AX := handle *)
  3459. B (89H) ; B (0C3H) ; (* BX := handle *)
  3460. MovDxVx (D_TXCH) ;
  3461. MovCxImm (1) ;
  3462. MovAh (3FH) ; Int21 ;
  3463. CmpAx0 ; Je8 ("frl_eof") ;
  3464. LdAlData (D_TXCH) ;
  3465. B (3CH) ; B (1AH) ; Je8 ("frl_eofset") ; (* ^Z *)
  3466. B (3CH) ; B (0DH) ; Je8 ("frl_get") ; (* ignore CR, wait for LF *)
  3467. B (3CH) ; B (0AH) ; Je8 ("frl_done") ; (* LF ends the line *)
  3468. B (8BH) ; B (46H) ; B (8) ; (* MOV AX,[BP+8]: capacity *)
  3469. B (39H) ; B (0C8H) ; (* CMP AX,CX *)
  3470. Jbe8 ("frl_get") ; (* full: consume the rest of the line *)
  3471. StDiAl ; IncDi ; IncCx ;
  3472. Jmp8 ("frl_get") ;
  3473. M ("frl_eof") ;
  3474. StDataByte (D_TXEOF, 1) ;
  3475. Jmp8 ("frl_done") ;
  3476. M ("frl_eofset") ;
  3477. StDataByte (D_TXEOF, 1) ;
  3478. M ("frl_done") ;
  3479. MovBxBp6 ;
  3480. B (88H) ; B (0FH) ; (* MOV [BX],CL: dest[0] := length *)
  3481. XorAxAx ; StDataAx (D_IORES) ;
  3482. PopDi ; PopSi ; PopDx ; PopCx ; PopBx ;
  3483. MovSpBp ; PopBp ; RetR ;
  3484. (* IntFmt: decimal integer -> counted string, for Str(integer, S).
  3485. [BP+4]=dest, [BP+6]=value. D_TXCH carries the sign flag. *)
  3486. M ("int_fmt") ;
  3487. PushBp ; MovBpSp ;
  3488. PushBx ; PushCx ; PushDx ; PushSi ; PushDi ;
  3489. MovDiBp4 ;
  3490. B (0C6H) ; B (05H) ; B (0) ; (* dest[0] := 0 *)
  3491. IncDi ;
  3492. MovAxBp6 ;
  3493. StDataByte (D_TXCH, 0) ;
  3494. CmpAx0 ; Jge8 ("if_pos") ;
  3495. StDataByte (D_TXCH, 1) ;
  3496. NegAx ;
  3497. M ("if_pos") ;
  3498. MovCxImm (10) ;
  3499. MovBxVx (D_NUM + 8) ;
  3500. MovSiBx ;
  3501. M ("if_dig") ;
  3502. XorDxDx ;
  3503. DivCx ;
  3504. AddDl (ORD ("0")) ;
  3505. DecSi ; StSiDl ;
  3506. CmpAx0 ; Jne8 ("if_dig") ;
  3507. (* write the sign before the digits *)
  3508. LdAlData (D_TXCH) ;
  3509. CmpAl0 ; Je8 ("if_nosign") ;
  3510. B (0C6H) ; B (05H) ; B (2DH) ; (* MOV byte [DI],'-' *)
  3511. IncDi ;
  3512. M ("if_nosign") ;
  3513. (* length := end - first, plus the sign *)
  3514. B (89H) ; B (0D9H) ; (* MOV CX,BX *)
  3515. B (29H) ; B (0F1H) ; (* SUB CX,SI *)
  3516. LdAlData (D_TXCH) ;
  3517. CmpAl0 ; Je8 ("if_len") ;
  3518. IncCx ;
  3519. M ("if_len") ;
  3520. MovDlCl ;
  3521. M ("if_copy") ;
  3522. LdAlSi ; StDiAl ; IncSi ; IncDi ;
  3523. Loop8 ("if_copy") ;
  3524. MovBxBp4 ;
  3525. B (88H) ; B (17H) ; (* MOV [BX],DL: dest[0] := length *)
  3526. PopDi ; PopSi ; PopDx ; PopCx ; PopBx ;
  3527. MovSpBp ; PopBp ; RetR ;
  3528. (* Val integer: [BP+4]=counted string, [BP+6]=dest integer,
  3529. [BP+8]=error position. Code is 1-based and 0 on success; on error the
  3530. destination is left unchanged, matching TPSRC2's valerr path. *)
  3531. M ("val_int") ;
  3532. PushBp ; MovBpSp ;
  3533. PushBx ; PushCx ; PushDx ; PushSi ; PushDi ;
  3534. MovDiBp4 ;
  3535. LdAlDi ; MovClAl ; B (30H) ; B (0EDH) ; (* CL=len, CH=0 *)
  3536. IncDi ;
  3537. B (89H) ; B (0FEH) ; (* MOV SI,DI: first character *)
  3538. XorBxBx ;
  3539. StDataByte (D_VALSIGN, 0) ;
  3540. Jcxz8 ("vi_empty") ;
  3541. LdAlDi ;
  3542. B (3CH) ; B (2DH) ; Je8 ("vi_neg") ; (* '-' *)
  3543. Jmp8 ("vi_loop") ;
  3544. M ("vi_neg") ;
  3545. StDataByte (D_VALSIGN, 1) ;
  3546. IncDi ; B (49H) ; (* INC DI, DEC CX *)
  3547. Jcxz8 ("vi_err") ;
  3548. M ("vi_loop") ;
  3549. LdAlDi ;
  3550. B (3CH) ; B (30H) ; Jb8 ("vi_err") ; (* < '0' *)
  3551. B (3CH) ; B (39H) ; Ja8 ("vi_err") ; (* > '9' *)
  3552. SubAl (ORD ("0")) ;
  3553. XorAhAh ;
  3554. StDataAx (D_VALCH) ;
  3555. MovAxBx ; MovBxImm (10) ; MulBx ;
  3556. B (03H) ; B (06H) ; Dd (D_VALCH) ; (* ADD AX,[D_VALCH] *)
  3557. B (89H) ; B (0C3H) ; (* MOV BX,AX *)
  3558. IncDi ; B (49H) ; (* INC DI, DEC CX *)
  3559. Jcxz8 ("vi_ok") ;
  3560. Jmp8 ("vi_loop") ;
  3561. M ("vi_empty") ;
  3562. MovBxBp8 ; XorAxAx ; B (89H) ; B (07H) ; (* error := 0 *)
  3563. Jmp8 ("vi_done") ;
  3564. M ("vi_ok") ;
  3565. LdAlData (D_VALSIGN) ; CmpAl0 ; Je8 ("vi_store") ;
  3566. B (0F7H) ; B (0DBH) ; (* NEG BX *)
  3567. M ("vi_store") ;
  3568. MovAxBx ; MovBxBp6 ; B (89H) ; B (07H) ; (* dest := value *)
  3569. MovBxBp8 ; XorAxAx ; B (89H) ; B (07H) ; (* error := 0 *)
  3570. Jmp8 ("vi_done") ;
  3571. M ("vi_err") ;
  3572. MovAxDi ; B (2BH) ; B (0C6H) ; (* SUB AX,SI *)
  3573. B (40H) ; (* INC AX: 1-based position *)
  3574. MovBxBp8 ; B (89H) ; B (07H) ; (* error := position *)
  3575. M ("vi_done") ;
  3576. PopDi ; PopSi ; PopDx ; PopCx ; PopBx ;
  3577. MovSpBp ; PopBp ; RetR
  3578. END EmitFileOps ;
  3579. PROCEDURE EmitValReal ;
  3580. (* val_real: Val(S, real, error) for a counted source.
  3581. ABI: [BP+4]=counted source, [BP+6]=destination REAL, [BP+8]=error word.
  3582. ascreal wants an ASCIIZ string, so the counted bytes are copied to
  3583. D_VALBUF and NUL-terminated first. On success the destination is written
  3584. and error is 0 when the whole string was consumed, else the 1-based first
  3585. unconsumed character. On a hard parse error the destination is untouched
  3586. and error is 1. *)
  3587. BEGIN
  3588. M ("val_real") ;
  3589. PushBp ; MovBpSp ;
  3590. PushBx ; PushCx ; PushDx ; PushSi ; PushDi ;
  3591. MovSiBp4 ; LdAlSi ; XorAhAh ; MovCxAx ; IncSi ;
  3592. MovDiData (D_VALBUF) ;
  3593. Jcxz8 ("vr_copied") ;
  3594. M ("vr_copy") ;
  3595. LdAlSi ; StDiAl ; IncSi ; IncDi ; Loop8 ("vr_copy") ;
  3596. M ("vr_copied") ;
  3597. XorAxAx ; StDiAl ; (* NUL terminate *)
  3598. MovBxDataAddr (D_VALBUF) ;
  3599. MovDiBp6 ;
  3600. StDataBx (D_ASCSRC) ;
  3601. Call ("rc_asc") ;
  3602. Jb8 ("vr_hard") ;
  3603. MovAxBx ; (* consumed end pointer *)
  3604. MovDxData (D_ASCSRC) ;
  3605. SubAxDx ; (* AX := consumed length *)
  3606. MovSiBp4 ; LdClSi ; XorChCh ; (* CX := source length *)
  3607. CmpAxCx ; Je8 ("vr_ok") ;
  3608. IncAx ;
  3609. Jmp8 ("vr_err") ;
  3610. M ("vr_ok") ;
  3611. XorAxAx ;
  3612. M ("vr_err") ;
  3613. MovBxBp8 ; StBxAx ; (* error := AX *)
  3614. Jmp8 ("vr_done") ;
  3615. M ("vr_hard") ;
  3616. MovAxImm (1) ; MovBxBp8 ; StBxAx ;
  3617. M ("vr_done") ;
  3618. PopDi ; PopSi ; PopDx ; PopCx ; PopBx ;
  3619. MovSpBp ; PopBp ; RetR
  3620. END EmitValReal ;
  3621. PROCEDURE Data6 (ax, bx, dx : CARDINAL) ;
  3622. (* emit one six-byte MBF constant in the runtime data block *)
  3623. BEGIN W (ax) ; W (bx) ; W (dx) END Data6 ;
  3624. PROCEDURE EmitData ;
  3625. (* The runtime's data block. The D_ constants name the offsets in it and are
  3626. ASSERTED against what is emitted here, three ways: the starting offset of
  3627. each string, the offset just past it, and the block's total length. A D_
  3628. constant is a 16-bit immediate inside a MOV, so a wrong one is a
  3629. perfectly well-formed instruction that reads the wrong memory - see the
  3630. note on the D_ block. *)
  3631. BEGIN
  3632. dataAt := rpos ;
  3633. (* Each D_ is checked BEFORE the string it names is emitted, because it
  3634. names that string's START. *)
  3635. IF dataAt + D_NUM # rpos THEN
  3636. HALT
  3637. END ;
  3638. (* 8 bytes of scratch, never read before written *)
  3639. B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
  3640. IF dataAt + D_TRUE # rpos THEN
  3641. HALT
  3642. END ;
  3643. Str5 ("TRUE$", 5) ;
  3644. IF dataAt + D_FALSE # rpos THEN
  3645. HALT
  3646. END ;
  3647. Str5 ("FALSE$", 6) ;
  3648. IF dataAt + D_CRLF # rpos THEN
  3649. HALT
  3650. END ;
  3651. B (13) ; B (10) ; B (ORD ("$")) ;
  3652. IF dataAt + D_REAL # rpos THEN
  3653. HALT
  3654. END ;
  3655. Str5 ("?REAL?", 6) ;
  3656. IF dataAt + D_PUSH # rpos THEN
  3657. HALT
  3658. END ;
  3659. B (0) ; B (0) ; (* the pushback slot, empty to begin with *)
  3660. IF dataAt + D_RSIGN # rpos THEN
  3661. HALT
  3662. END ;
  3663. B (0) ; B (0) ; (* resign/resave *)
  3664. IF dataAt + D_RMANT # rpos THEN
  3665. HALT
  3666. END ;
  3667. B (0) ; B (0) ; (* remant/remant1 *)
  3668. IF dataAt + D_CVDEC # rpos THEN
  3669. HALT
  3670. END ;
  3671. B (0) ;
  3672. IF dataAt + D_RMUL11 # rpos THEN
  3673. HALT
  3674. END ;
  3675. B (0) ; B (0) ;
  3676. IF dataAt + D_RMUL12 # rpos THEN
  3677. HALT
  3678. END ;
  3679. B (0) ; B (0) ;
  3680. IF dataAt + D_RMUL13 # rpos THEN
  3681. HALT
  3682. END ;
  3683. B (0) ; B (0) ;
  3684. IF dataAt + D_RMUL21 # rpos THEN
  3685. HALT
  3686. END ;
  3687. B (0) ; B (0) ;
  3688. IF dataAt + D_RMUL22 # rpos THEN
  3689. HALT
  3690. END ;
  3691. B (0) ; B (0) ;
  3692. IF dataAt + D_RMUL23 # rpos THEN
  3693. HALT
  3694. END ;
  3695. B (0) ; B (0) ;
  3696. IF dataAt + D_CVOUT # rpos THEN
  3697. HALT
  3698. END ;
  3699. (* twelve decimal digit slots; rdconv fills all of them *)
  3700. B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
  3701. B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
  3702. IF dataAt + D_CVDECEXP # rpos THEN
  3703. HALT
  3704. END ;
  3705. B (0) ;
  3706. IF dataAt + D_CVEXPCNT # rpos THEN
  3707. HALT
  3708. END ;
  3709. B (0) ;
  3710. IF dataAt + D_FLTDEC # rpos THEN
  3711. HALT
  3712. END ;
  3713. (* 1e0, 1e4, ... 1e36 as the reference's AX:BX:DX triples, low byte first *)
  3714. B (081H) ; B (000H) ; B (000H) ; B (000H) ; B (000H) ; B (000H) ;
  3715. B (08EH) ; B (000H) ; B (000H) ; B (000H) ; B (040H) ; B (01CH) ;
  3716. B (09BH) ; B (000H) ; B (000H) ; B (020H) ; B (0BCH) ; B (03EH) ;
  3717. B (0A8H) ; B (000H) ; B (010H) ; B (0A5H) ; B (0D4H) ; B (068H) ;
  3718. B (0B6H) ; B (004H) ; B (0BFH) ; B (0C9H) ; B (01BH) ; B (00EH) ;
  3719. B (0C3H) ; B (0ACH) ; B (0C5H) ; B (0EBH) ; B (078H) ; B (02DH) ;
  3720. B (0D0H) ; B (0CDH) ; B (0CEH) ; B (01BH) ; B (0C2H) ; B (053H) ;
  3721. B (0DEH) ; B (0F9H) ; B (078H) ; B (039H) ; B (03FH) ; B (001H) ;
  3722. B (0EBH) ; B (02BH) ; B (0A8H) ; B (0ADH) ; B (0C5H) ; B (01DH) ;
  3723. B (0F8H) ; B (0C9H) ; B (07BH) ; B (0CEH) ; B (097H) ; B (040H) ;
  3724. IF dataAt + D_FMTBUF # rpos THEN
  3725. HALT
  3726. END ;
  3727. (* 64 bytes of formatted output; a fixed-format 1e38 with 10 decimals is
  3728. about 50 characters, so this has room *)
  3729. B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
  3730. B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
  3731. B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
  3732. B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
  3733. B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
  3734. B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
  3735. B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
  3736. B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ; B (0) ;
  3737. IF dataAt + D_FMTLEN # rpos THEN
  3738. HALT
  3739. END ;
  3740. B (0) ; B (0) ;
  3741. IF dataAt + D_FMTW # rpos THEN
  3742. HALT
  3743. END ;
  3744. B (0) ; B (0) ;
  3745. IF dataAt + D_FMTPAD # rpos THEN
  3746. HALT
  3747. END ;
  3748. B (0) ; B (0) ;
  3749. IF dataAt + D_FMTTOT # rpos THEN
  3750. HALT
  3751. END ;
  3752. B (0) ; B (0) ;
  3753. IF dataAt + D_ASCSRC # rpos THEN
  3754. HALT
  3755. END ;
  3756. B (0) ; B (0) ;
  3757. IF dataAt + D_IORES # rpos THEN
  3758. HALT
  3759. END ;
  3760. B (0) ; B (0) ; (* the last file-operation status *)
  3761. IF dataAt + D_HEAP # rpos THEN
  3762. HALT
  3763. END ;
  3764. B (0) ; B (0) ; (* the bump heap's next free byte *)
  3765. IF dataAt + D_TXEOF # rpos THEN
  3766. HALT
  3767. END ;
  3768. B (0) ; (* text EOF flag *)
  3769. IF dataAt + D_TXCH # rpos THEN
  3770. HALT
  3771. END ;
  3772. B (0) ; (* one-byte text read scratch *)
  3773. IF dataAt + D_VALSIGN # rpos THEN
  3774. HALT
  3775. END ;
  3776. B (0) ; (* Val sign flag *)
  3777. IF dataAt + D_VALCH # rpos THEN
  3778. HALT
  3779. END ;
  3780. B (0) ; B (0) ; (* Val digit scratch *)
  3781. WHILE rpos < dataAt + D_ONE DO
  3782. B (0) (* the 256-byte ASCIIZ Val buffer *)
  3783. END ;
  3784. IF dataAt + D_ONE # rpos THEN HALT END ;
  3785. Data6 (0081H, 0000H, 0000H) ;
  3786. IF dataAt + D_EXP256 # rpos THEN HALT END ;
  3787. Data6 (0089H, 0000H, 0000H) ;
  3788. IF dataAt + D_LN2 # rpos THEN HALT END ;
  3789. Data6 (0D280H, 17F7H, 3172H) ;
  3790. IF dataAt + D_HALF # rpos THEN HALT END ;
  3791. Data6 (0080H, 0000H, 0000H) ;
  3792. IF dataAt + D_LNC # rpos THEN HALT END ;
  3793. Data6 (897DH, 8888H, 0888H) ;
  3794. Data6 (8A7DH, 0D89DH, 1D89H) ;
  3795. Data6 (0E97DH, 8BA2H, 3A2EH) ;
  3796. Data6 (8E7DH, 38E3H, 638EH) ;
  3797. Data6 (497EH, 2492H, 1249H) ;
  3798. Data6 (0CD7EH, 0CCCCH, 4CCCH) ;
  3799. Data6 (0AB7FH, 0AAAAH, 2AAAH) ;
  3800. Data6 (0081H, 0000H, 0000H) ;
  3801. IF dataAt + D_EXPC # rpos THEN HALT END ;
  3802. Data6 (0AA67H, 2B3FH, 5732H) ;
  3803. Data6 (0C56BH, 7DBBH, 13F2H) ;
  3804. Data6 (0B66EH, 1D2AH, 38EFH) ;
  3805. Data6 (0D71H, 00D0H, 500DH) ;
  3806. Data6 (0D74H, 00D0H, 500DH) ;
  3807. Data6 (0B77H, 60B6H, 360BH) ;
  3808. Data6 (897AH, 8888H, 0888H) ;
  3809. Data6 (0AB7CH, 0AAAAH, 2AAAH) ;
  3810. Data6 (0AB7EH, 0AAAAH, 2AAAH) ;
  3811. Data6 (0080H, 0000H, 0000H) ;
  3812. Data6 (0081H, 0000H, 0000H) ;
  3813. Data6 (0081H, 0000H, 0000H) ;
  3814. IF dataAt + D_PI # rpos THEN HALT END ;
  3815. Data6 (2182H, 0DAA2H, 490FH) ;
  3816. IF dataAt + D_PI2 # rpos THEN HALT END ;
  3817. Data6 (2181H, 0DAA2H, 490FH) ;
  3818. IF dataAt + D_2PI # rpos THEN HALT END ;
  3819. Data6 (2183H, 0DAA2H, 490FH) ;
  3820. IF dataAt + D_FLTSIN # rpos THEN HALT END ;
  3821. Data6 (9D58H, 9F39H, 0D73FH) ;
  3822. Data6 (4360H, 309DH, 3092H) ;
  3823. Data6 (0AA67H, 283FH, 0D732H) ;
  3824. Data6 (0B66EH, 1D2AH, 38EFH) ;
  3825. Data6 (0D74H, 00D0H, 0D00DH) ;
  3826. Data6 (887AH, 8888H, 0888H) ;
  3827. Data6 (0AB7EH, 0AAAAH, 0AAAAH) ;
  3828. IF dataAt + D_FLTATN # rpos THEN HALT END ;
  3829. Data6 (0E87DH, 8BA2H, 0BA2EH) ;
  3830. Data6 (8E7DH, 38E3H, 638EH) ;
  3831. Data6 (497EH, 2492H, 9249H) ;
  3832. Data6 (0CD7EH, 0CCCCH, 4CCCH) ;
  3833. Data6 (0AB7FH, 0AAAAH, 0AAAAH) ;
  3834. IF dataAt + D_FLTATNRG # rpos THEN HALT END ;
  3835. Data6 (0E77FH, 0CCCFH, 5413H) ;
  3836. Data6 (0F67FH, 0A2F4H, 0930H) ;
  3837. Data6 (6A7FH, 91C1H, 060AH) ;
  3838. Data6 (0B580H, 8A9EH, 446FH) ;
  3839. Data6 (8280H, 3A2CH, 13CDH) ;
  3840. Data6 (6A80H, 91C1H, 060AH) ;
  3841. Data6 (0081H, 0000H, 0000H) ;
  3842. Data6 (2180H, 0DAA2H, 490FH) ;
  3843. WHILE rpos < dataAt + D_NPI DO
  3844. B (0)
  3845. END ;
  3846. IF dataAt + D_NPI # rpos THEN HALT END ;
  3847. Data6 (2182H, 0DAA2H, 0C90FH) ;
  3848. IF dataAt + D_SIGN # rpos THEN HALT END ;
  3849. B (0) ; B (0) ; B (0) ; (* sign, reciprocal, transform *)
  3850. IF dataAt + D_ATNT # rpos THEN HALT END ;
  3851. Data6 (0E77FH, 0CCCFH, 5413H) ;
  3852. IF dataAt + D_PI4 # rpos THEN HALT END ;
  3853. Data6 (2180H, 0DAA2H, 490FH) ;
  3854. IF dataAt + D_ZERO # rpos THEN HALT END ;
  3855. Data6 (0000H, 0000H, 0000H) ;
  3856. WHILE rpos < dataAt + D_END DO
  3857. B (0)
  3858. END ;
  3859. (* the total, so D_END is checked too and not just used as a pad target *)
  3860. IF rpos - dataAt # D_END THEN
  3861. HALT
  3862. END
  3863. END EmitData ;
  3864. PROCEDURE FixUp ;
  3865. VAR i, t, rel : CARDINAL ;
  3866. BEGIN
  3867. i := 0 ;
  3868. WHILE i < nfix DO
  3869. IF fix [i].kind = 2 THEN
  3870. (* A data address, and the ONE kind that is absolute rather than
  3871. relative: rtBase says where the blob sits in the image, val says
  3872. where inside the blob, and LoadBias says where the image itself
  3873. sits once DOS has loaded it. All three are needed; omitting any
  3874. one produces a readable-looking address into the wrong bytes. *)
  3875. t := (dataAt + rtBase + fix [i].val + LoadBias) MOD 10000H ;
  3876. rt [fix [i].place] := VAL (BYTE, t MOD 100H) ;
  3877. rt [fix [i].place + 1] := VAL (BYTE, (t DIV 100H) MOD 100H)
  3878. ELSE
  3879. t := LblOff (fix [i].nm) ;
  3880. IF fix [i].kind = 0 THEN
  3881. (* rel8 is measured from the end of the instruction, i.e. one
  3882. byte past the displacement field. A target beyond +127/-128
  3883. would wrap silently and send the machine into the middle of
  3884. an instruction; that happened in remult's early zero/return
  3885. jumps. Refuse it. *)
  3886. IF (VAL (LONGINT, t) - VAL (LONGINT, fix [i].place + 1) < -128)
  3887. OR (VAL (LONGINT, t) - VAL (LONGINT, fix [i].place + 1) > 127)
  3888. THEN
  3889. HALT
  3890. END ;
  3891. rel := (t + 100H - (fix [i].place + 1)) MOD 100H ;
  3892. rt [fix [i].place] := VAL (BYTE, rel)
  3893. ELSE
  3894. rel := (t + 10000H - (fix [i].place + 2)) MOD 10000H ;
  3895. rt [fix [i].place] := VAL (BYTE, rel MOD 100H) ;
  3896. rt [fix [i].place + 1] := VAL (BYTE, (rel DIV 100H) MOD 100H)
  3897. END
  3898. END ;
  3899. INC (i)
  3900. END
  3901. END FixUp ;
  3902. (* ---------------------------------------------------------------- *)
  3903. (* public interface *)
  3904. (* ---------------------------------------------------------------- *)
  3905. PROCEDURE RT_Build (base : CARDINAL) ;
  3906. BEGIN
  3907. IF built THEN
  3908. RETURN
  3909. END ;
  3910. rtBase := base ; (* must precede every Emit*, they read it *)
  3911. rpos := 0 ; ltop := 0 ; nfix := 0 ; dataAt := 0 ;
  3912. EmitInitMem ;
  3913. EmitEnd ;
  3914. EmitStackChk ;
  3915. EmitWrInt ; EmitWrIntW ; EmitWrChar ; EmitWrBool ; EmitWrReal ; EmitWrLn ;
  3916. EmitWrInl ; EmitWrStr ;
  3917. EmitRdInt ; EmitRdScan ; EmitRdByte ;
  3918. EmitRdChar ; EmitRdBool ; EmitRdLn ;
  3919. EmitGetCh ; EmitUnGetCh ;
  3920. EmitRdStr ; EmitStrStore ; EmitCmpStr ; EmitStrCat ; EmitStrCopy ;
  3921. EmitRealFromInt ; EmitRealAddSub ; EmitRealMul ;
  3922. EmitRealCmp ; EmitRealInt ; EmitRealMath ; EmitRealConv ; EmitSetOps ;
  3923. EmitStrDelete ; EmitStrInsert ; EmitFileOps ; EmitValReal ;
  3924. EmitData ;
  3925. FixUp ;
  3926. SetStr (entNm [0], "initmem") ;
  3927. SetStr (entNm [1], "progend") ;
  3928. SetStr (entNm [2], "stackchk") ;
  3929. SetStr (entNm [3], "wrint") ;
  3930. SetStr (entNm [4], "wrchar") ;
  3931. SetStr (entNm [5], "wrbool") ;
  3932. SetStr (entNm [6], "wrreal") ;
  3933. SetStr (entNm [7], "wrln") ;
  3934. SetStr (entNm [8], "rdint") ;
  3935. SetStr (entNm [9], "rdchar") ;
  3936. SetStr (entNm [10], "rdbool") ;
  3937. SetStr (entNm [11], "rdln") ;
  3938. SetStr (entNm [12], "halt") ;
  3939. SetStr (entNm [13], "wrtinl") ;
  3940. SetStr (entNm [14], "rdbyte") ;
  3941. SetStr (entNm [15], "wrstr") ;
  3942. SetStr (entNm [16], "rdstr") ;
  3943. SetStr (entNm [17], "strstore") ;
  3944. SetStr (entNm [18], "cmpstr") ;
  3945. SetStr (entNm [19], "strcat") ;
  3946. SetStr (entNm [20], "wrintw") ;
  3947. SetStr (entNm [21], "real_from_int") ;
  3948. SetStr (entNm [22], "real_add") ;
  3949. SetStr (entNm [23], "real_sub") ;
  3950. SetStr (entNm [24], "real_mul") ;
  3951. SetStr (entNm [25], "real_div") ;
  3952. SetStr (entNm [26], "real_cmp") ;
  3953. SetStr (entNm [27], "real_int") ;
  3954. SetStr (entNm [28], "real_fmt") ;
  3955. SetStr (entNm [29], "real_val") ;
  3956. SetStr (entNm [30], "real_write") ;
  3957. SetStr (entNm [31], "set_op") ;
  3958. SetStr (entNm [32], "set_cmp") ;
  3959. SetStr (entNm [33], "set_copy") ;
  3960. SetStr (entNm [34], "file_assign") ;
  3961. SetStr (entNm [35], "file_reset") ;
  3962. SetStr (entNm [36], "file_rewrite") ;
  3963. SetStr (entNm [37], "file_close") ;
  3964. SetStr (entNm [38], "ioresult") ;
  3965. SetStr (entNm [39], "io_store") ;
  3966. SetStr (entNm [40], "heap_new") ;
  3967. SetStr (entNm [41], "file_eof") ;
  3968. SetStr (entNm [42], "file_readln") ;
  3969. SetStr (entNm [43], "int_fmt") ;
  3970. SetStr (entNm [44], "val_int") ;
  3971. SetStr (entNm [45], "str_copy") ;
  3972. SetStr (entNm [46], "val_real") ;
  3973. SetStr (entNm [47], "real_abs") ;
  3974. SetStr (entNm [48], "real_sqrt") ;
  3975. SetStr (entNm [49], "real_ln") ;
  3976. SetStr (entNm [50], "real_exp") ;
  3977. SetStr (entNm [51], "real_sin") ;
  3978. SetStr (entNm [52], "real_cos") ;
  3979. SetStr (entNm [53], "real_arctan") ;
  3980. SetStr (entNm [54], "str_delete") ;
  3981. SetStr (entNm [55], "str_insert") ;
  3982. built := TRUE
  3983. END RT_Build ;
  3984. PROCEDURE RT_Size () : CARDINAL ;
  3985. BEGIN
  3986. IF NOT built THEN
  3987. RT_Build (0)
  3988. END ;
  3989. RETURN rpos
  3990. END RT_Size ;
  3991. PROCEDURE RT_Byte (i : CARDINAL ) : BYTE ;
  3992. BEGIN
  3993. IF NOT built THEN
  3994. RT_Build (0)
  3995. END ;
  3996. IF i >= rpos THEN
  3997. RETURN 0
  3998. END ;
  3999. RETURN rt [i]
  4000. END RT_Byte ;
  4001. PROCEDURE RT_Entry (i : CARDINAL ) : CARDINAL ;
  4002. BEGIN
  4003. IF NOT built THEN
  4004. RT_Build (0)
  4005. END ;
  4006. IF i > 55 THEN
  4007. RETURN 0
  4008. END ;
  4009. RETURN rtBase + LblOff (entNm [i])
  4010. END RT_Entry ;
  4011. PROCEDURE RT_CodeEnd () : CARDINAL ;
  4012. BEGIN
  4013. IF NOT built THEN
  4014. RT_Build (0)
  4015. END ;
  4016. RETURN dataAt
  4017. END RT_CodeEnd ;
  4018. END Runtime.