gnusto.debug.js 164 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704
  1. /*
  2. Gnusto
  3. ======
  4. Built: 2014-10-05
  5. Copyright (c) 2003-2014 The Gnusto Contributors
  6. GNU GPL licenced
  7. https://github.com/curiousdannii/gnusto
  8. String.prototype.quote() taken from "Remedial Javascript" by Douglas Crockford: http://javascript.crockford.com/remedial.html
  9. */
  10. /*
  11. Quetzal Common Save-File Format
  12. ===============================
  13. Copyright (c) 2013 The ifvms.js team
  14. BSD licenced
  15. http://github.com/curiousdannii/ifvms.js
  16. */
  17. // A savefile
  18. var Quetzal = IFF.subClass({
  19. // Parse a Quetzal savefile, or make a blank one
  20. init: function(bytes)
  21. {
  22. this._super(bytes);
  23. if (bytes)
  24. {
  25. // Check this is a Quetzal savefile
  26. if (this.type !== 'IFZS')
  27. {
  28. throw new Error('Not a Quetzal savefile');
  29. }
  30. // Go through the chunks and extract the useful ones
  31. for (var i = 0, l = this.chunks.length; i < l; i++)
  32. {
  33. var type = this.chunks[i].type, data = this.chunks[i].data;
  34. // Memory and stack chunks. Overwrites existing data if more than one of each is present!
  35. if (type === 'CMem' || type === 'UMem')
  36. {
  37. this.memory = data;
  38. this.compressed = (type === 'CMem');
  39. }
  40. else if (type === 'Stks')
  41. {
  42. this.stacks = data;
  43. }
  44. // Story file data
  45. else if (type === 'IFhd')
  46. {
  47. this.release = data.slice(0, 2);
  48. this.serial = data.slice(2, 8);
  49. // The checksum isn't used, but if we throw it away we can't round-trip
  50. this.checksum = data.slice(8, 10);
  51. this.pc = data[10] << 16 | data[11] << 8 | data[12];
  52. }
  53. }
  54. }
  55. },
  56. // Write out a savefile
  57. write: function()
  58. {
  59. // Reset the IFF type
  60. this.type = 'IFZS';
  61. // Format the IFhd chunk correctly
  62. var pc = this.pc,
  63. ifhd = this.release.concat(
  64. this.serial,
  65. this.checksum,
  66. (pc >> 16) & 0xFF, (pc >> 8) & 0xFF, pc & 0xFF
  67. );
  68. // Add the chunks
  69. this.chunks = [
  70. {type: 'IFhd', data: ifhd},
  71. {type: (this.compressed ? 'CMem' : 'UMem'), data: this.memory},
  72. {type: 'Stks', data: this.stacks}
  73. ];
  74. // Return the byte array
  75. return this._super();
  76. }
  77. });
  78. // Taken from "Remedial Javascript" by Douglas Crockford:
  79. // http://javascript.crockford.com/remedial.html
  80. String.prototype.quote = function () {
  81. var c, i, l = this.length, o = '"';
  82. for (i = 0; i < l; i += 1) {
  83. c = this.charAt(i);
  84. if (c >= ' ') {
  85. if (c === '\\' || c === '"') {
  86. o += '\\';
  87. }
  88. o += c;
  89. } else {
  90. switch (c) {
  91. case '\b':
  92. o += '\\b';
  93. break;
  94. case '\f':
  95. o += '\\f';
  96. break;
  97. case '\n':
  98. o += '\\n';
  99. break;
  100. case '\r':
  101. o += '\\r';
  102. break;
  103. case '\t':
  104. o += '\\t';
  105. break;
  106. default:
  107. c = c.charCodeAt();
  108. o += '\\u00' + Math.floor(c / 16).toString(16) +
  109. (c % 16).toString(16);
  110. }
  111. }
  112. }
  113. return o + '"';
  114. };
  115. // gnusto-lib.js || -*- Mode: Javascript; tab-width: 2; -*-
  116. // The Gnusto JavaScript Z-machine library.
  117. // $Header: /cvs/gnusto/src/xpcom/engine/gnusto-engine.js,v 1.116 2005/04/26 01:50:32 naltrexone42 Exp $
  118. //
  119. // Copyright (c) 2003-2011 The Gnusto Contributors
  120. //
  121. // The latest code is available at http://github.com/curiousdannii/gnusto/
  122. //
  123. // This program is free software; you can redistribute it and/or modify
  124. // it under the terms of version 2 of the GNU General Public License
  125. // as published by the Free Software Foundation.
  126. //
  127. // This program is distributed in the hope that it will be useful,
  128. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  129. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  130. // GNU General Public License for more details.
  131. //
  132. // You should have be able to view the GNU General Public License at
  133. // http://www.gnu.org/copyleft/gpl.html ; if not, write to the Free Software
  134. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  135. var CVS_VERSION = '$Date: 2005/04/26 01:50:32 $';
  136. var ENGINE_DESCRIPTION = "Gnusto's interactive fiction engine";
  137. ////////////////////////////////////////////////////////////////
  138. //
  139. // PART THE FIRST
  140. //
  141. // STUFF FROM GNUSTO-LIB WHICH STILL NEEDS MERGING IN
  142. //
  143. ////////////////////////////////////////////////////////////////
  144. var default_unicode_translation_table = {
  145. 155:0xe4, // a-diaeresis
  146. 156:0xf6, // o-diaeresis
  147. 157:0xfc, // u-diaeresis
  148. 158:0xc4, // A-diaeresis
  149. 159:0xd6, // O-diaeresis
  150. 160:0xdc, // U-diaeresis
  151. 161:0xdf, // German "sz" ligature
  152. 162:0xbb, // right quotation marks
  153. 163:0xab, // left quotation marks
  154. 164:0xeb, // e-diaeresis
  155. 165:0xef, // i-diaeresis
  156. 166:0xff, // y-diaeresis
  157. 167:0xcb, // E-diaeresis
  158. 168:0xcf, // I-diaeresis
  159. 169:0xe1, // a-acute
  160. 170:0xe9, // e-acute
  161. 171:0xed, // i-acute
  162. 172:0xf3, // o-acute
  163. 173:0xfa, // u-acute
  164. 174:0xfd, // y-acute
  165. 175:0xc1, // A-acute
  166. 176:0xc9, // E-acute
  167. 177:0xcd, // I-acute
  168. 178:0xd3, // O-acute
  169. 179:0xda, // U-acute
  170. 180:0xdd, // Y-acute
  171. 181:0xe0, // a-grave
  172. 182:0xe8, // e-grave
  173. 183:0xec, // i-grave
  174. 184:0xf2, // o-grave
  175. 185:0xf9, // u-grave
  176. 186:0xc0, // A-grave
  177. 187:0xc8, // E-grave
  178. 188:0xcc, // I-grave
  179. 189:0xd2, // O-grave
  180. 190:0xd9, // U-grave
  181. 191:0xe2, // a-circumflex
  182. 192:0xea, // e-circumflex
  183. 193:0xee, // i-circumflex
  184. 194:0xf4, // o-circumflex
  185. 195:0xfb, // u-circumflex
  186. 196:0xc2, // A-circumflex
  187. 197:0xca, // E-circumflex
  188. 198:0xce, // I-circumflex
  189. 199:0xd4, // O-circumflex
  190. 200:0xdb, // U-circumflex
  191. 201:0xe5, // a-ring
  192. 202:0xc5, // A-ring
  193. 203:0xf8, // o-slash
  194. 204:0xd8, // O-slash
  195. 205:0xe3, // a-tilde
  196. 206:0xf1, // n-tilde
  197. 207:0xf5, // o-tilde
  198. 208:0xc3, // A-tilde
  199. 209:0xd1, // N-tilde
  200. 210:0xd5, // O-tilde
  201. 211:0xe6, // ae-ligature
  202. 212:0xc6, // AE-ligature
  203. 213:0xe7, // c-cedilla
  204. 214:0xc7, // C-cedilla
  205. 215:0xfe, // thorn
  206. 216:0xf0, // eth
  207. 217:0xde, // Thorn
  208. 218:0xd0, // Eth
  209. 219:0xa3, // pound sterling sign
  210. 220:0x153, // oe-ligature
  211. 221:0x152, // OE-ligature
  212. 222:0xa1, // inverted pling
  213. 223:0xbf // inverted query
  214. };
  215. var reverse_unicode_table = {};
  216. var isNotConst = /\D/;
  217. var temp_var = 0;
  218. var PARENT_REC = 0;
  219. var SIBLING_REC = 1;
  220. var CHILD_REC = 2;
  221. var CALLED_FROM_INTERRUPT = 0;
  222. // Not everywhere has "window" defined
  223. if ( !window )
  224. {
  225. this.window = {};
  226. }
  227. // Temporary variables used in JITspace; they need to be
  228. // defined for QML, though browser JS would allow them to be
  229. // properties of the global object.
  230. var dummy;
  231. var t;
  232. var t2;
  233. var PARCHMENT_SECURITY_OVERRIDE = window.PARCHMENT_SECURITY_OVERRIDE;
  234. // Placeholder when decoding arguments for opcodes to indicate that
  235. // an argument needs to be popped from the stack.
  236. //var ARG_STACK_POP = "SP";
  237. ////////////////////////////////////////////////////////////////
  238. // Effect codes, returned from run(). See the explanation below
  239. // for |handlers|.
  240. // Returned when we're expecting a line of keyboard input.
  241. //
  242. // Answer with the string the user has entered.
  243. var GNUSTO_EFFECT_INPUT = '"RS"';
  244. // Returned when we're expecting a single keypress (or mouse click).
  245. // TODO: The lowest nibble may be 1 if the Z-machine has asked
  246. // for timed input.
  247. //
  248. // Answer with the ZSCII code for the key pressed (see the Z-spec).
  249. var GNUSTO_EFFECT_INPUT_CHAR = '"RC"';
  250. // Returned when the Z-machine requests we save the game.
  251. // Answer as in the Z-spec: 0 if we can't save, 1 if we can, or
  252. // 2 if we've just restored.
  253. var GNUSTO_EFFECT_SAVE = '"DS"';
  254. // Returned when the Z-machine requests we load a game.
  255. // Answer 0 if we can't load. (If we can, we won't be around to answer.)
  256. var GNUSTO_EFFECT_RESTORE = '"DR"';
  257. // Returned when the Z-machine requests we quit.
  258. // Not to be answered, obviously.
  259. var GNUSTO_EFFECT_QUIT = '"QU"';
  260. // Returned when the Z-machine requests that we restart a game.
  261. // Assumedly, we won't be around to answer it.
  262. var GNUSTO_EFFECT_RESTART = '"NU"';
  263. // Returned if we've run for more than a certain number of iterations.
  264. // This means that the environment gets a chance to do some housekeeping
  265. // if we're stuck deep in computation, or to break an infinite loop
  266. // within the Z-code.
  267. //
  268. // Any value may be used as an answer; it will be ignored.
  269. var GNUSTO_EFFECT_WIMP_OUT = '"WO"';
  270. // Returned if we hit a breakpoint.
  271. // Any value may be used as an answer; it will be ignored.
  272. var GNUSTO_EFFECT_BREAKPOINT = '"BP"';
  273. // Returned if either of the two header bits which
  274. // affect printing have changed since last time
  275. // (or if either of them is set on first printing).
  276. var GNUSTO_EFFECT_FLAGS_CHANGED = '"XC"';
  277. // Returned if the story wants to check whether it's been pirated.
  278. // Answer 1 if it is, or 0 if it isn't.
  279. // You probably just want to return 0.
  280. var GNUSTO_EFFECT_PIRACY = '"CP"';
  281. // Returned if the story wants to set the text style.
  282. // effect_parameters() will return a list:
  283. // [0] = a bitcoded text style, as in the Z-spec,
  284. // or -1 not to set the style.
  285. // [1] = the foreground colour to use, as in the Z-spec
  286. // [2] = the background colour to use, as in the Z-spec
  287. // Any value may be used as an answer; it will be ignored.
  288. var GNUSTO_EFFECT_STYLE = '"SS"';
  289. // Returned if the story wants to cause a sound effect.
  290. // effect_parameters() will return a list, whose
  291. // vales aren't fully specified at present.
  292. // (Just go "bleep" for now.)
  293. //
  294. // Any value may be used as an answer; it will be ignored.
  295. var GNUSTO_EFFECT_SOUND = '"FX"';
  296. var GNUSTO_EFFECT_SPLITWINDOW = '"TW"';
  297. var GNUSTO_EFFECT_SETWINDOW = '"SW"';
  298. var GNUSTO_EFFECT_ERASEWINDOW = '"YW"';
  299. var GNUSTO_EFFECT_ERASELINE = '"YL"';
  300. // Returned if the story wants to set the position of
  301. // the cursor in the upper window. The upper window should
  302. // be currently active.
  303. //
  304. // effect_parameters() will return a list:
  305. // [0] = the new Y coordinate
  306. // [1] = the new X coordinate
  307. // Any value may be used as an answer; it will be ignored.
  308. var GNUSTO_EFFECT_SETCURSOR = '"SC"';
  309. var GNUSTO_EFFECT_SETBUFFERMODE = '"SB"';
  310. var GNUSTO_EFFECT_SETINPUTSTREAM = '"SI"';
  311. var GNUSTO_EFFECT_GETCURSOR = '"GC"';
  312. // Returned if the story wants to print a table, as with
  313. // @print_table. (This is complicated enough to get its
  314. // own effect code, rather than just using an internal buffer
  315. // as most printing does.)
  316. //
  317. // effect_parameters() will return a list of lines to print.
  318. //
  319. // Any value may be used as an answer; it will be ignored.
  320. var GNUSTO_EFFECT_PRINTTABLE = '"PT"';
  321. ////////////////////////////////////////////////////////////////
  322. //
  323. // PART THE SECOND
  324. //
  325. // THE HANDLERS AND HANDLER ARRAYS
  326. //
  327. ////////////////////////////////////////////////////////////////
  328. // JavaScript seems to have a problem with pointers to methods.
  329. // We solve this in a Pythonesque manner. Each instruction handler
  330. // is a simple function which takes two parameters: 1) the engine
  331. // asking the question (i.e. the value which would be "this" if
  332. // the function was a method), and 2) the list of actual arguments
  333. // given in the z-code for that function.
  334. function handleZ_je(engine, a) {
  335. if (a.length<2) {
  336. return ''; // it's a no-op
  337. } else if (a.length==2) {
  338. return engine._brancher(a[0]+'=='+a[1]);
  339. } else {
  340. var condition = '';
  341. for (var i=1; i<a.length; i++) {
  342. if (i!=1) condition = condition + '||';
  343. condition = condition + 't=='+a[i];
  344. }
  345. return 't='+a[0]+';'+engine._brancher(condition);
  346. }
  347. }
  348. function handleZ_jl(engine, a) {
  349. // Convert both arguments to signed for the comparison.
  350. var t1code, t2code;
  351. if (isNotConst.test(a[0]))
  352. t1code = 't=' + a[0] + ';t=((t & 0x8000 ? ~0xFFFF : 0) | t);';
  353. else
  354. t1code = 't=' + engine._unsigned2signed(a[0]) + ';';
  355. if (isNotConst.test(a[1]))
  356. t2code = 't2=' + a[1] + ';t2=((t2 & 0x8000 ? ~0xFFFF : 0) | t2);';
  357. else
  358. t2code = 't2=' + engine._unsigned2signed(a[1]) + ';';
  359. return t1code + t2code + engine._brancher('t<t2');
  360. }
  361. function handleZ_jg(engine, a) {
  362. // Convert both arguments to signed for the comparison.
  363. var t1code, t2code;
  364. if (isNotConst.test(a[0]))
  365. t1code = 't=' + a[0] + ';t=((t & 0x8000 ? ~0xFFFF : 0) | t);';
  366. else
  367. t1code = 't=' + engine._unsigned2signed(a[0]) + ';';
  368. if (isNotConst.test(a[1]))
  369. t2code = 't2=' + a[1] + ';t2=((t2 & 0x8000 ? ~0xFFFF : 0) | t2);';
  370. else
  371. t2code = 't2=' + engine._unsigned2signed(a[1]) + ';';
  372. return t1code + t2code + engine._brancher('t>t2');
  373. }
  374. /***
  375. function handleZ_dec_chk(engine, a) {
  376. return 't='+a[0]+';t2=_varcode_get(t)-1;_varcode_set(t2,t);'+engine._brancher('t2<'+a[1]);
  377. }
  378. function handleZ_inc_chk(engine, a) {
  379. return 't='+a[0]+';t2=_varcode_get(t)+1;_varcode_set(t2,t);'+engine._brancher('t2>'+a[1]);
  380. }
  381. ***/
  382. // Increment/decrement a variable and branch
  383. // Calls the generic function and adds a brancher to the end
  384. function handleZ_inc_chk(engine, a)
  385. {
  386. var tcode = handleZ_inc(engine, a);
  387. tcode += '{var t1=incdec; t1 = ((t1 & 0x8000 ? ~0xFFFF : 0) | t1); var t2=' + a[1] + '; t2=((t2 & 0x8000 ? ~0xFFFF : 0) | t2);' + engine._brancher('t1 > t2') + '}';
  388. return tcode;
  389. /*
  390. var tcode, t2code;
  391. tcode = handleZ_incdec(engine, a[0], '+', 1);
  392. // Convert both arguments to signed for the comparison.
  393. if (isNotConst.test(a[1]))
  394. t2code = 't2=' + a[1] + ';t2=((t2 & 0x8000 ? ~0xFFFF : 0) | t2);';
  395. else
  396. t2code = 't2=' + engine._unsigned2signed(a[1]) + ';';
  397. return tcode + t2code + 'tmp_'+temp_var+'=(('+'tmp_'+temp_var+' & 0x8000 ? ~0xFFFF : 0) | '+'tmp_'+temp_var+');' + engine._brancher('tmp_'+temp_var+' > t2');
  398. */
  399. }
  400. function handleZ_dec_chk(engine, a)
  401. {
  402. var tcode = handleZ_dec(engine, a);
  403. tcode += '{var t1=incdec; t1 = ((t1 & 0x8000 ? ~0xFFFF : 0) | t1); var t2=' + a[1] + '; t2=((t2 & 0x8000 ? ~0xFFFF : 0) | t2);' + engine._brancher('t1 < t2') + '}';
  404. return tcode;
  405. /*
  406. var tcode, t2code;
  407. tcode = handleZ_incdec(engine, a[0], '-', 1);
  408. // Convert both arguments to signed for the comparison.
  409. if (isNotConst.test(a[1]))
  410. t2code = 't2=' + a[1] + ';t2=((t2 & 0x8000 ? ~0xFFFF : 0) | t2);';
  411. else
  412. t2code = 't2=' + engine._unsigned2signed(a[1]) + ';';
  413. return tcode + t2code + 'tmp_'+temp_var+'=(('+'tmp_'+temp_var+' & 0x8000 ? ~0xFFFF : 0) | '+'tmp_'+temp_var+');' + engine._brancher('tmp_'+temp_var+' < t2');
  414. */
  415. }
  416. function handleZ_jin(engine, a) {
  417. return engine._brancher("_obj_in("+a[0]+','+a[1]+')');
  418. }
  419. function handleZ_test(engine, a) {
  420. return 't='+a[1]+';'+engine._brancher('('+a[0]+'&t)==t');
  421. }
  422. function handleZ_or(engine, a) {
  423. return engine._storer('('+a[0]+'|'+a[1]+')');
  424. }
  425. function handleZ_and(engine, a) {
  426. return engine._storer('('+a[0]+'&'+a[1]+')');
  427. }
  428. function handleZ_test_attr(engine, a) {
  429. return engine._brancher('_test_attr('+a[0]+','+a[1]+')');
  430. }
  431. function handleZ_set_attr(engine, a) {
  432. return '_set_attr('+a[0]+','+a[1]+')';
  433. }
  434. function handleZ_clear_attr(engine, a) {
  435. return '_clear_attr('+a[0]+','+a[1]+')';
  436. }
  437. /***
  438. function handleZ_store(engine, a) {
  439. return "_varcode_set("+a[1]+","+a[0]+")";
  440. }
  441. ***/
  442. // Store a variable
  443. // Rather than calling _varcode_set() this function now access the variables directly
  444. // a[0] is interpreted as in ZSD 4.2.2:
  445. // 0 = top of game stack
  446. // 1-15 = local variables
  447. // 16 up = global variables
  448. function handleZ_store(engine, a)
  449. {
  450. var code;
  451. if (isNaN(a[0])) { // branch at runtime
  452. code = '(' + a[0] + ' == 0) ? m_gamestack[m_gamestack.length - 1] = ' + a[1] + ' : (' + a[0] + ' < 0x10) ? m_locals[( ' + a[0] + ' - 1 )] = ' + a[1] + ' : setWord(' + a[1] + ', m_vars_start + ( ' + a[0] + ' - 16 ) * 2 )';
  453. } else {
  454. if (a[0] == 0) {
  455. code = 'm_gamestack[m_gamestack.length - 1] = ' + a[1];
  456. } else if (a[0] < 0x10) {
  457. code = 'm_locals[( ' + a[0] + ' - 1 )] = ' + a[1];
  458. } else {
  459. code = 'setWord(' + a[1] + ', m_vars_start + ( ' + a[0] + ' - 16 ) * 2 )';
  460. }
  461. }
  462. return code;
  463. /*
  464. ;;; if (isNotConst.test(a[0])) { engine.logger('Z_store', a[0]); }
  465. ;;; if (a[0] == null || a[0] === true || a[0] === false || a[0] < 0 || a[0] > 0xFFFF) { engine.logger('Z_store address', a[0]); }
  466. ;;; if (a[1] == null || a[1] === true || a[1] === false || a[1] < 0 || a[1] > 0xFFFF) { engine.logger('Z_store value', a[1]); }
  467. if (a[0] == 0)
  468. return 'm_gamestack[m_gamestack.length - 1] = ' + a[1] + ';';
  469. else if (a[0] < 16)
  470. return 'm_locals[' + (a[0]-1) + '] = ' + a[1];
  471. else
  472. {
  473. // If the variable is a function rather than a constant it will have to be determined at run time
  474. if (isNotConst.test(a[0]))
  475. var code = 'var high = m_vars_start + (' + a[0] + ' - 16) * 2, low = high + 1;', high = 'high', low = 'low';
  476. else
  477. var code = '', high = engine.m_vars_start + (a[0] - 16) * 2, low = high + 1;
  478. // If we are setting a constant get the high and low bytes at compile time
  479. if (!isNotConst.test(a[1]))
  480. {
  481. var value = a[1];
  482. return code + 'm_memory[' + high + '] = ' + ((value >> 8) & 0xFF) + ';' +
  483. 'm_memory[' + low + '] = ' + (value & 0xFF);
  484. }
  485. else
  486. {
  487. var tmp = 'tmp_' + (++temp_var);
  488. return code + 'var ' + tmp + ' = ' + a[1] + ';' +
  489. 'm_memory[' + high + '] = (' + tmp + ' >> 8) & 0xFF;' +
  490. 'm_memory[' + low + '] = ' + tmp + ' & 0xFF;';
  491. }
  492. }*/
  493. }
  494. function handleZ_insert_obj(engine, a) {
  495. return "_insert_obj("+a[0]+','+a[1]+")";
  496. }
  497. // Load an array word
  498. function handleZ_loadw(engine, a)
  499. {
  500. // Inline this getUnsignedWord call
  501. //return engine._storer("getUnsignedWord(("+a[0]+"+2*"+a[1]+")&0xFFFF)");
  502. // Calculate the address
  503. // BUG: fails to wrap if the address is split across addresses 0xFFFF
  504. // and 0x0000. I don't care. --Z
  505. if (isNotConst.test(a[0]) || isNotConst.test(a[1]))
  506. var code = 'var tmp_' + (++temp_var) + ' = (' + a[0] + ' + 2 * ' + a[1] + ') & 0xFFFF, ',
  507. addr = 'tmp_' + temp_var,
  508. addr1 = addr+'+1';
  509. else
  510. var code = 'var ',
  511. addr = (a[0] + 2 * a[1]) & 0xFFFF,
  512. addr1 = addr+1;
  513. // Get the value and store it
  514. var tmp = 'tmp_' + (++temp_var);
  515. return code + tmp + ' = (m_memory[' + addr + '] << 8) | m_memory[' + addr1 + '];' +
  516. engine._storer(tmp);
  517. }
  518. function handleZ_loadb(engine, a) {
  519. return engine._storer("m_memory[0xFFFF&("+a[0]+"+"+a[1]+")]");
  520. }
  521. function handleZ_get_prop(engine, a) {
  522. return engine._storer("_get_prop("+a[0]+','+a[1]+')');
  523. }
  524. function handleZ_get_prop_addr(engine, a) {
  525. return engine._storer("_get_prop_addr("+a[0]+','+a[1]+')');
  526. }
  527. function handleZ_get_next_prop(engine, a) {
  528. return engine._storer("_get_next_prop("+a[0]+','+a[1]+')');
  529. }
  530. // Should add/mul/div also check for overflows?
  531. function handleZ_add(engine, a) {
  532. return engine._storer( '(' + a[0]+' + '+a[1] + ') & 0xFFFF'); }
  533. /***
  534. function handleZ_sub(engine, a) {
  535. return engine._storer(a[0]+'-'+a[1]); }
  536. ***/
  537. // Subtract and store
  538. function handleZ_sub(engine, a)
  539. {
  540. return engine._storer( '(' + a[0] + ' - ' + a[1] + ') & 0xFFFF' );
  541. }
  542. function handleZ_mul(engine, a) {
  543. return engine._storer( '(' + a[0]+'*'+a[1] + ') & 0xFFFF'); }
  544. function handleZ_div(engine, a) {
  545. return engine._storer('_trunc_divide('+a[0]+','+a[1]+')');
  546. }
  547. function handleZ_mod(engine, a) {
  548. return engine._storer('_trunc_modulo('+a[0]+','+a[1]+')');
  549. }
  550. function handleZ_set_colour(engine, a) {
  551. return "m_pc="+engine.m_pc+";m_effects=["+GNUSTO_EFFECT_STYLE+",-1,"+a[0]+','+a[1]+"];return";
  552. }
  553. function handleZ_throw(engine, a) {
  554. engine.m_compilation_running = 0;
  555. return "_throw_stack_frame("+a[0]+");return";
  556. }
  557. function handleZ_jz(engine, a) {
  558. return engine._brancher(a[0]+'==0');
  559. }
  560. function handleZ_get_sibling(engine, a) {
  561. return "t=_get_sibling("+a[0]+");"+engine._storer("t")+";"+engine._brancher("t");
  562. }
  563. function handleZ_get_child(engine, a) {
  564. return "t=_get_child("+a[0]+");"+
  565. engine._storer("t")+";"+
  566. engine._brancher("t");
  567. }
  568. function handleZ_get_parent(engine, a) {
  569. return engine._storer("_get_parent("+a[0]+")");
  570. }
  571. function handleZ_get_prop_len(engine, a) {
  572. return engine._storer("_get_prop_len("+a[0]+')');
  573. }
  574. /***
  575. function handleZ_inc(engine, a) {
  576. return "t="+a[0]+';_varcode_set(_varcode_get(t)+1, t)';
  577. }
  578. function handleZ_dec(engine, a) {
  579. return "t="+a[0]+';_varcode_set(_varcode_get(t)-1, t)';
  580. }
  581. ***/
  582. // Increment and decrement opcodes
  583. // Calls the following generic function
  584. function handleZ_inc(engine, a)
  585. {
  586. // return handleZ_incdec(engine, a[0], '+');
  587. var code = 'var incdec; ';
  588. if (isNaN(a[0])) { // branch at runtime
  589. code += 'var incdec; if (' + a[0] + ' == 0) { incdec = m_gamestack[m_gamestack.length - 1] = (m_gamestack[m_gamestack.length - 1] + 1) & 0xFFFF; } else if (' + a[0] + ' < 0x10) { incdec = m_locals[( ' + a[0] + ' - 1 )] = (m_locals[( ' + a[0] + ' - 1 )] + 1) & 0xFFFF; } else { var val = getUnsignedWord(m_vars_start + ( ' + a[0] + ' - 16 ) * 2 ); val++; setWord(val, m_vars_start + ( ' + a[0] + ' - 16 ) * 2 ); incdec = val; }';
  590. } else {
  591. if (a[0] == 0) {
  592. code += 'incdec = m_gamestack[m_gamestack.length - 1] = (m_gamestack[m_gamestack.length - 1] + 1) & 0xFFFF;';
  593. } else if (a[0] < 0x10) {
  594. code += 'incdec = m_locals[( ' + a[0] + ' - 1 )] = (m_locals[( ' + a[0] + ' - 1 )] + 1) & 0xFFFF;';
  595. } else {
  596. code += '{var val = getWord(m_vars_start + ( ' + a[0] + ' - 16 ) * 2 ); val++; setWord(val, m_vars_start + ( ' + a[0] + ' - 16 ) * 2 ); incdec = val;}';
  597. }
  598. }
  599. return code;
  600. }
  601. function handleZ_dec(engine, a)
  602. {
  603. // return handleZ_incdec(engine, a[0], '-');
  604. var code = 'var incdec; ';
  605. if (isNaN(a[0])) { // branch at runtime
  606. code += 'if (' + a[0] + ' == 0) { incdec = m_gamestack[m_gamestack.length - 1] = (m_gamestack[m_gamestack.length - 1] - 1) & 0xFFFF; } else if (' + a[0] + ' < 0x10) { incdec = m_locals[( ' + a[0] + ' - 1 )] = (m_locals[( ' + a[0] + ' - 1 )] - 1) & 0xFFFF; } else { var val = getUnsignedWord(m_vars_start + ( ' + a[0] + ' - 16 ) * 2 ); val--; setWord(val, m_vars_start + ( ' + a[0] + ' - 16 ) * 2 ); incdec = val; }';
  607. } else {
  608. if (a[0] == 0) {
  609. code += 'incdec = m_gamestack[m_gamestack.length - 1] = (m_gamestack[m_gamestack.length - 1] - 1) & 0xFFFF;';
  610. } else if (a[0] < 0x10) {
  611. code += 'incdec = m_locals[( ' + a[0] + ' - 1 )] = (m_locals[( ' + a[0] + ' - 1 )] - 1) & 0xFFFF;';
  612. } else {
  613. code += '{var val = getUnsignedWord(m_vars_start + ( ' + a[0] + ' - 16 ) * 2 ); val--; setWord(val, m_vars_start + ( ' + a[0] + ' - 16 ) * 2 ); incdec = val;}';
  614. }
  615. }
  616. return code;
  617. }
  618. // Increment and decrement variables
  619. // A generic function used by dec, dec_chk, inc and inc_chk
  620. // Rather than calling _varcode_set() and _varcode_get() this function now accesses the variables directly
  621. // variable is interpreted as in ZSD 4.2.2:
  622. // 0 = top of game stack
  623. // 1-15 = local variables
  624. // 16 up = global variables
  625. /*
  626. function handleZ_incdec(engine, variable, sign, varRequired)
  627. {
  628. if (isNotConst.test(variable))
  629. engine.logger('Z_incdec', variable);
  630. var tmp = 'tmp_' + (++temp_var);
  631. if (variable == 0)
  632. return (varRequired ? 'var ' + tmp + ' = ' : '') + '(m_gamestack[m_gamestack.length - 1] = (m_gamestack[m_gamestack.length - 1]'+sign+'1)&0xFFFF);';
  633. else if (variable < 0x10)
  634. return (varRequired ? 'var ' + tmp + ' = ' : '') + '(m_locals[' + (variable-1) + '] = (m_locals[' + (variable-1) + ']'+sign+'1)&0xFFFF);';
  635. else
  636. {
  637. // If the variable is a function rather than a constant it will have to be determined at run time
  638. if (isNotConst.test(variable))
  639. var code = 'var add = m_vars_start + (' + variable + ' - 16) * 2, ', add = 'add';
  640. else
  641. var code = 'var ', add = engine.m_vars_start + (variable - 16) * 2;
  642. // Get the value from memory and inc/dec it!
  643. return code + tmp + ' = (m_memory[' + add + '] << 8) | m_memory[' + add + ' + 1];' +
  644. tmp + ' = (' + tmp + ' ' + sign + ' 1) & 0xFFFF;' +
  645. 'm_memory[' + add + '] = (' + tmp + ' >> 8) & 0xFF;' +
  646. 'm_memory[' + add + ' + 1] = ' + tmp + ' & 0xFF;';
  647. }
  648. }
  649. */
  650. function handleZ_print_addr(engine, a) {
  651. return engine._handler_zOut('_zscii_from('+a[0]+')',0);
  652. }
  653. function handleZ_remove_obj(engine, a) {
  654. return "_remove_obj("+a[0]+','+a[1]+")";
  655. }
  656. function handleZ_print_obj(engine, a) {
  657. return engine._handler_zOut("_name_of_object("+a[0]+")",0);
  658. }
  659. function handleZ_ret(engine, a) {
  660. engine.m_compilation_running=0;
  661. return "_func_return("+a[0]+');return';
  662. }
  663. function handleZ_jump(engine, a) {
  664. engine.m_compilation_running=0;
  665. if (a[0] & 0x8000) {
  666. a[0] = (~0xFFFF) | a[0];
  667. }
  668. var addr=(a[0] + engine.m_pc) - 2;
  669. return "m_pc="+addr+";return";
  670. }
  671. function handleZ_print_paddr(engine, a) {
  672. return engine._handler_zOut("_zscii_from("+engine.m_pc_translate_for_string(a[0])+")",0);
  673. }
  674. function handleZ_load( engine, a )
  675. {
  676. var code;
  677. if (isNaN(a[0])) { // branch at runtime
  678. code = '(' + a[0] + ' == 0) ? m_gamestack[m_gamestack.length - 1] : (' + a[0] + ' < 0x10) ? m_locals[( ' + a[0] + ' - 1 )] : getUnsignedWord( m_vars_start + ( ' + a[0] + ' - 16 ) * 2 )';
  679. } else {
  680. if (a[0] == 0) {
  681. code = 'm_gamestack[m_gamestack.length - 1]';
  682. } else if (a[0] < 0x10) {
  683. code = 'm_locals[( ' + a[0] + ' - 1 )]';
  684. } else {
  685. code = 'getUnsignedWord( m_vars_start + ( ' + a[0] + ' - 16 ) * 2 )';
  686. }
  687. }
  688. return engine._storer( code );
  689. //return engine._storer('_varcode_get('+a[0]+')');
  690. /* var code;
  691. if ( a[0] == 0 ) {
  692. code = 'm_gamestack[m_gamestack.length - 1]';
  693. } else if ( a[0] < 0x10 ) {
  694. code = 'm_locals[( ' + a[0] + ' - 1 )]';
  695. } else {
  696. code = 'getUnsignedWord( m_vars_start + ( ' + a[0] + ' - 16 ) * 2 )';
  697. }
  698. return engine._storer( code );
  699. */
  700. }
  701. function handleZ_rtrue(engine, a) {
  702. engine.m_compilation_running=0;
  703. return "_func_return(1);return";
  704. }
  705. function handleZ_rfalse(engine, a) {
  706. engine.m_compilation_running=0;
  707. return "_func_return(0);return";
  708. }
  709. function handleZ_print(engine, a) {
  710. return engine._handler_print('', 0);
  711. }
  712. function handleZ_print_ret(engine, a) {
  713. engine.m_compilation_running = 0;
  714. return engine._handler_print('\n', 1)+';_func_return(1);return';
  715. }
  716. function handleZ_nop(engine, a) {
  717. return "";
  718. }
  719. function handleZ_restart(engine, a) {
  720. engine.m_compilation_running=0;
  721. return "m_effects=["+GNUSTO_EFFECT_RESTART+"];return";
  722. }
  723. function handleZ_ret_popped(engine, a) {
  724. engine.m_compilation_running=0;
  725. return "_func_return(m_gamestack.pop());return";
  726. }
  727. function handleZ_catch(engine, a) {
  728. // The stack frame cookie is specified by Quetzal 1.3b s6.2
  729. // to be the number of frames on the stack.
  730. return engine._storer("m_call_stack.length");
  731. }
  732. function handleZ_pop(engine, a) {
  733. return "m_gamestack.pop()";
  734. }
  735. function handleZ_quit(engine, a) {
  736. engine.m_compilation_running=0;
  737. return "m_effects=["+GNUSTO_EFFECT_QUIT+"];return";
  738. }
  739. function handleZ_new_line(engine, a) {
  740. return engine._handler_zOut("'\\n'",0);
  741. }
  742. function handleZ_show_status(engine, a){ //(illegal from V4 onward)
  743. engine._handler_zOut(''); //chalk forces repaint of status bar
  744. return "";
  745. }
  746. function handleZ_verify(engine, a) {
  747. return engine._brancher('_verify()');
  748. }
  749. function handleZ_illegal_extended(engine, a) {
  750. // 190 can't be generated; it's the start of an extended opcode
  751. gnusto_error(199);
  752. }
  753. function handleZ_piracy(engine, a) {
  754. engine.m_compilation_running = 0;
  755. var setter = 'm_rebound=function(){'+engine._brancher('(!0)')+'};';//m_answers[0])')+'};';
  756. return "m_pc="+engine.m_pc+";"+setter+"m_effects=["+GNUSTO_EFFECT_PIRACY+"];return";
  757. }
  758. ////////////////////////////////////////////////////////////////
  759. //
  760. // Call handlers:
  761. //
  762. // Gosub-generating functions, in increasing order of
  763. // arity (no args, one arg, many args), with the
  764. // no-store versions first. The "*_vs2" instructions
  765. // are conceptually identical to the corresponding
  766. // "*_vs" instructions, and share the same handlers.
  767. //
  768. // naltrexone-- I've removed the VERBOSE lines
  769. // which were rendered incorrect by this. If you need to turn
  770. // them on again, I'll put them back in in the new form.
  771. function handleZ_call_1n(engine, a) {
  772. return engine._generate_gosub(a[0], '', 0);
  773. }
  774. function handleZ_call_1s(engine, a) {
  775. return engine._generate_gosub(a[0], '', 1);
  776. }
  777. function handleZ_call_2n(engine, a) {
  778. return engine._generate_gosub(a[0], a[1], 0);
  779. }
  780. function handleZ_call_2s(engine, a) {
  781. return engine._generate_gosub(a[0], a[1], 1);
  782. }
  783. function handleZ_call_vn(engine, a) {
  784. return engine._generate_gosub(a[0], a.slice(1), 0);
  785. }
  786. function handleZ_call_vs(engine, a) {
  787. return engine._generate_gosub(a[0], a.slice(1), 1);
  788. }
  789. ////////////////////////////////////////////////////////////////
  790. /***
  791. function handleZ_store_w(engine, a) {
  792. return "setWord("+a[2]+","+a[0]+"+2*"+a[1]+")";
  793. }
  794. ***/
  795. // Store a value in an array
  796. function handleZ_store_w(engine, a)
  797. {
  798. // Calculate the address
  799. if (isNotConst.test(a[0]) || isNotConst.test(a[1]))
  800. var code = 'var tmp_' + (++temp_var) + ' = (' + a[0] + ' + 2 * ' + a[1] + ') & 0xFFFF;', addr = 'tmp_' + temp_var, addr1 = addr+'+1';
  801. else
  802. var code = '', addr = (a[0] + 2 * a[1]) & 0xFFFF, addr1 = addr+1;
  803. // If we are setting a constant get the high and low bytes at compile time
  804. if (!isNotConst.test(a[2]))
  805. {
  806. if (engine.m_value_asserts) {
  807. if (a[2] == null || a[2] === true || a[2] === false || a[2] < 0 || a[2] > 0xFFFF)
  808. engine.logger('Z_store_w value', a[2]);
  809. }
  810. return code + 'm_memory[' + addr + '] = ' + ((a[2] >> 8) & 0xFF) + ';' +
  811. 'm_memory[' + addr1 + '] = ' + (a[2] & 0xFF);
  812. }
  813. else
  814. {
  815. var tmp = 'tmp_' + (++temp_var);
  816. return code + 'var ' + tmp + ' = ' + a[2] + ';' +
  817. 'm_memory[' + addr + '] = (' + tmp + ' >> 8) & 0xFF;' +
  818. 'm_memory[' + addr1 + '] = ' + tmp + ' & 0xFF;';
  819. }
  820. }
  821. function handleZ_storeb(engine, a) {
  822. return "setByte("+a[2]+",("+a[0]+"+"+a[1]+")&0xFFFF)";
  823. }
  824. function handleZ_putprop(engine, a) {
  825. return "_put_prop("+a[0]+','+a[1]+','+a[2]+')';
  826. }
  827. // read, aread, sread, whatever it's called today.
  828. // That's something that we can't deal with within gnusto:
  829. // ask the environment to magic something up for us.
  830. function handleZ_read(engine, a) {
  831. // JS representing number of deciseconds to wait before a
  832. // timeout should occur, or 0 if there shouldn't be one.
  833. var timeout_deciseconds;
  834. // JS representing the address of the timeout routine,
  835. // or 0 if there isn't one.
  836. var address_of_timeout_routine;
  837. engine.m_compilation_running = 0;
  838. // Since a[0] (address of the text buffer) is referenced so often,
  839. // we introduce a variable |a0| into JITspace with the same value.
  840. // A JS string telling us what to do if there isn't a timeout.
  841. var rebound_for_no_timeout =
  842. "_aread(m_answers[0],m_rebound_args[1],"+
  843. "m_rebound_args[2],m_answers[1])";
  844. // A JS string telling how to get the number of characters to "recap".
  845. var recaps_getter;
  846. // A JS string telling us how to get the number of characters the
  847. // text buffer can hold.
  848. var char_count_getter;
  849. if (engine.m_version>=5) {
  850. // In z5-z8, @read is a store instruction.
  851. rebound_for_no_timeout = engine._storer(rebound_for_no_timeout);
  852. } // Otherwise we just leave the call to _aread() as it is.
  853. if (engine.m_version>=5) {
  854. // z5+ use two header bytes at the start of the table.
  855. recaps_getter = "m_memory[0xFFFF&a0+1]";
  856. char_count_getter = "m_memory[0xFFFF&a0]";
  857. } else {
  858. // z1-z4 only use one. (They don't have recaps.)
  859. recaps_getter = '0';
  860. char_count_getter = "m_memory[0xFFFF&a0]+1";
  861. }
  862. if (a[2] && a[3] && (engine.m_version>=4)) {
  863. // This is a timed routine.
  864. // a[3] is the routine to call after a[2] deciseconds.
  865. timeout_deciseconds = a[2];
  866. address_of_timeout_routine = engine.m_pc_translate_for_routine(a[3]);
  867. } else {
  868. // No timeout.
  869. timeout_deciseconds = '0';
  870. address_of_timeout_routine = '0';
  871. // Optimisation: In this case we could optimise rebound_setter
  872. // so that it doesn't check whether to call the interrupt
  873. // service routine. We haven't done this here for simplicity,
  874. // but we did it in the simpler @read_char.
  875. }
  876. // JS for a function to handle the answer to this effect.
  877. // The answer will be one integer; if the integer is zero,
  878. // it's a request for a timeout; if it's nonzero, it's a
  879. // keycode to be stored as the present instruction dictates.
  880. var rebound_setter = "m_rebound=function(){"+
  881. "var t=1*m_answers[0];" +
  882. "if(t<0){"+
  883. "_func_interrupt(m_rebound_args[0],onISRReturn_for_read);"+ // -ve: timeout
  884. "}else{"+
  885. rebound_for_no_timeout + ";" +
  886. "}"+
  887. "};";
  888. var rebound_args_setter =
  889. "m_rebound_args=["+
  890. address_of_timeout_routine + "," + // Where to jump on timeout
  891. "a0,"+ // Address of text buffer
  892. a[1]+","+ // Address of parse buffer
  893. "];";
  894. /****************************************************************/
  895. return "var a0=eval("+ a[0] + ");" +
  896. "m_pc=" + engine.m_pc + ";" +
  897. rebound_args_setter +
  898. rebound_setter +
  899. "m_effects=["+
  900. GNUSTO_EFFECT_INPUT + "," +
  901. timeout_deciseconds + "," +
  902. recaps_getter + "," +
  903. char_count_getter + "," +
  904. "_terminating_characters()];return";
  905. }
  906. function handleZ_print_char(engine, a) {
  907. return engine._handler_zOut('_zscii_char_to_ascii('+a[0]+')',0);
  908. }
  909. function handleZ_print_num(engine, a) {
  910. return engine._handler_zOut('""+_unsigned2signed('+a[0]+')',0);
  911. }
  912. function handleZ_random(engine, a) {
  913. return engine._storer("_random_number("+a[0]+")");
  914. }
  915. function handleZ_push(engine, a) {
  916. return 'm_gamestack.push('+a[0]+')';
  917. }
  918. function handleZ_pull(engine, a)
  919. {
  920. // return '_varcode_set(m_gamestack.pop(),'+a[0]+')';
  921. var code = 'var pull = m_gamestack.pop(); ';
  922. return code += handleZ_store(engine, [a[0], 'pull']);
  923. }
  924. function handleZ_split_window(engine, a) {
  925. engine.m_compilation_running=0;
  926. return "m_pc="+engine.m_pc+";m_effects=["+GNUSTO_EFFECT_SPLITWINDOW+","+a[0]+"];return";
  927. }
  928. function handleZ_set_window(engine, a) {
  929. engine.m_compilation_running=0;
  930. return "m_pc="+engine.m_pc+";m_effects=["+GNUSTO_EFFECT_SETWINDOW+","+a[0]+"];return";
  931. }
  932. function handleZ_erase_window(engine, a) {
  933. engine.m_compilation_running=0;
  934. return "m_pc="+engine.m_pc+";m_effects=["+GNUSTO_EFFECT_ERASEWINDOW+","+engine._unsigned2signed(a[0])+"];return";
  935. }
  936. function handleZ_erase_line(engine, a) {
  937. engine.m_compilation_running=0;
  938. return "m_pc="+engine.m_pc+";m_effects=["+GNUSTO_EFFECT_ERASELINE+","+a[0]+"];return";
  939. }
  940. function handleZ_set_cursor(engine, a) {
  941. engine.m_compilation_running=0;
  942. return "m_pc="+engine.m_pc+";m_effects=["+GNUSTO_EFFECT_SETCURSOR+","+a[0]+","+a[1]+"];return";
  943. }
  944. function handleZ_get_cursor(engine, a) {
  945. engine.m_compilation_running=0;
  946. return "m_pc="+engine.m_pc+";m_effects=["+GNUSTO_EFFECT_GETCURSOR+","+a[0]+"];return";
  947. }
  948. function handleZ_set_text_style(engine, a) {
  949. engine.m_compilation_running=0;
  950. return "m_pc="+engine.m_pc+";m_effects=["+GNUSTO_EFFECT_STYLE+","+a[0]+",0,0];return";
  951. }
  952. function handleZ_buffer_mode(engine, a) {
  953. engine.m_compilation_running=0;
  954. return "m_pc="+engine.m_pc+";m_effects=["+GNUSTO_EFFECT_SETBUFFERMODE+","+a[0]+"];return";
  955. }
  956. function handleZ_output_stream(engine, a) {
  957. return '_set_output_stream('+a[0]+','+a[1]+')';
  958. }
  959. function handleZ_input_stream(engine, a) {
  960. engine.m_compilation_running=0;
  961. return "m_pc="+engine.m_pc+";m_effects=["+GNUSTO_EFFECT_SETINPUTSTREAM+","+a[0]+"];return";
  962. }
  963. function handleZ_sound_effect(engine, a) {
  964. // We're rather glossing over whether and how we
  965. // deal with callbacks at present.
  966. engine.m_compilation_running=0;
  967. while (a.length < 5) { a.push(0); }
  968. return "m_pc="+engine.m_pc+';m_effects=['+GNUSTO_EFFECT_SOUND+','+a[0]+','+a[1]+','+a[2]+','+a[3]+','+a[4]+'];return';
  969. }
  970. // Maybe factor out "read" and this?
  971. function handleZ_read_char(engine, a) {
  972. // JS representing number of deciseconds to wait before a
  973. // timeout should occur, or 0 if there shouldn't be one.
  974. var timeout_deciseconds;
  975. // JS to set m_rebound_args to show where to jump if there's
  976. // a timeout. If there's not going to be a timeout, this should
  977. // be blank.
  978. var rebound_args_setter;
  979. // JS for a function to handle the answer to this effect.
  980. // The answer will be one integer; if the integer is zero,
  981. // it's a request for a timeout; if it's nonzero, it's a
  982. // keycode to be stored as the present instruction dictates.
  983. var rebound_setter;
  984. // Stop the engine! We want to get off!
  985. engine.m_compilation_running = 0;
  986. // a[0] is always 1; probably not worth checking for this.
  987. if (a[1] && a[2] && (engine.m_version>=4)) {
  988. // This is a timed routine.
  989. // a[2] is the routine to call after a[1] deciseconds.
  990. timeout_deciseconds = a[1];
  991. rebound_args_setter = "m_rebound_args=["+
  992. engine.m_pc_translate_for_routine(a[2])+'];';
  993. rebound_setter = "m_rebound=function(){"+
  994. "var t=m_answers[0];" +
  995. "if(t<0){"+
  996. "_func_interrupt(m_rebound_args[0],onISRReturn_for_read_char);"+ // -ve: timeout
  997. "}else{"+
  998. engine._storer("_ascii_code_to_zscii_code(t)") + // otherwise, a result to store.
  999. "}"+
  1000. "};";
  1001. } else {
  1002. // No timeout.
  1003. timeout_deciseconds = '0';
  1004. // We only set m_rebound_args when there's a timeout.
  1005. rebound_args_setter = '';
  1006. // A much simpler rebound function, since zero isn't
  1007. // a magic answer.
  1008. rebound_setter = "m_rebound=function(){"+
  1009. engine._storer("_ascii_code_to_zscii_code(m_answers[0])") +
  1010. "};";
  1011. }
  1012. return "m_pc="+engine.m_pc+";"+
  1013. rebound_args_setter +
  1014. rebound_setter +
  1015. "m_effects=["+GNUSTO_EFFECT_INPUT_CHAR+
  1016. ","+timeout_deciseconds+"];return";
  1017. }
  1018. function handleZ_scan_table(engine, a) {
  1019. if (a.length == 4) {
  1020. return "t=_scan_table("+a[0]+','+a[1]+"&0xFFFF,"+a[2]+"&0xFFFF," + a[3]+");" +
  1021. engine._storer("t") + ";" + engine._brancher('t');
  1022. } else { // must use the default for Form, 0x82
  1023. return "t=_scan_table("+a[0]+','+a[1]+"&0xFFFF,"+a[2]+"&0xFFFF," + 0x82 +");" +
  1024. engine._storer("t") + ";" + engine._brancher('t');
  1025. }
  1026. }
  1027. function handleZ_not(engine, a) {
  1028. return engine._storer('~'+a[0]+'&0xffff');
  1029. }
  1030. function handleZ_tokenise(engine, a) {
  1031. return "_tokenise("+a[0]+","+a[1]+","+a[2]+","+a[3]+")";
  1032. }
  1033. function handleZ_encode_text(engine, a) {
  1034. return "_encode_text("+a[0]+","+a[1]+","+a[2]+","+a[3]+")";
  1035. }
  1036. function handleZ_copy_table(engine, a) {
  1037. return "_copy_table("+a[0]+','+a[1]+','+a[2]+")";
  1038. }
  1039. function handleZ_print_table(engine, a) {
  1040. // Jam in defaults:
  1041. if (a.length < 3) { a.push(1); } // default height
  1042. if (a.length < 4) { a.push(0); } // default skip
  1043. return "m_pc="+engine.m_pc+";m_effects=_print_table("+a[0]+","+a[1]+","+a[2]+","+a[3]+");return";
  1044. }
  1045. function handleZ_check_arg_count(engine, a) {
  1046. return engine._brancher(a[0]+'<=_param_count()');
  1047. }
  1048. function handleZ_saveV123(engine, a) {
  1049. engine.m_compilation_running=0;
  1050. var setter = 'm_rebound=function(){'+
  1051. engine._brancher('m_answers[0]')+'};';
  1052. return "m_state_to_save=_saveable_state(1);m_pc="+engine.m_pc+";"+setter+";m_effects=["+GNUSTO_EFFECT_SAVE+"];return";
  1053. }
  1054. function handleZ_saveV45678(engine, a) {
  1055. engine.m_compilation_running=0;
  1056. var setter = "m_rebound=function() { " +
  1057. engine._storer('m_answers[0]') + "};";
  1058. return "m_state_to_save=_saveable_state("+
  1059. (engine.m_version==4? '1': '3') +
  1060. ");m_pc="+engine.m_pc+";" +
  1061. setter+";m_effects=["+GNUSTO_EFFECT_SAVE+"];return";
  1062. }
  1063. function handleZ_restoreV123(engine, a) {
  1064. engine.m_compilation_running=0;
  1065. engine._brancher(''); // Throw it away; it's never used
  1066. return "m_pc="+engine.m_pc+";m_effects=["+GNUSTO_EFFECT_RESTORE+"];return";
  1067. }
  1068. function handleZ_restoreV45678(engine, a) {
  1069. engine.m_compilation_running=0;
  1070. var setter = 'm_rebound=function() { ' +
  1071. 'var t=m_answers[0]; if (t==0){' +
  1072. engine._storer('t') + '}};';
  1073. return "m_pc="+engine.m_pc+";" + setter +
  1074. "m_effects=["+GNUSTO_EFFECT_RESTORE+"];return";
  1075. }
  1076. function handleZ_log_shift(engine, a) {
  1077. // logical-bit-shift. Right shifts are zero-padded
  1078. return engine._storer("_log_shift("+a[0]+','+a[1]+')');
  1079. }
  1080. function handleZ_art_shift(engine, a) {
  1081. // arithmetic-bit-shift. Right shifts are sign-extended
  1082. return engine._storer("_art_shift("+a[0]+','+a[1]+')');
  1083. }
  1084. function handleZ_set_font(engine, a) {
  1085. // We only provide font 1.
  1086. return engine._storer('('+a[0]+'<2?1:0)');
  1087. }
  1088. function handleZ_save_undo(engine, a) {
  1089. // Gnusto can't be relied upon to have the correct PC at runtime, so store it at compile time instead
  1090. return engine._storer( '_save_undo(' + engine.m_pc + ')' );
  1091. }
  1092. function handleZ_restore_undo(engine, a) {
  1093. // If the restore was successful, return from this block immediately
  1094. // so that execution can continue with the new PC value. If that
  1095. // doesn't happen, it must have failed, so store zero.
  1096. return 'if(_restore_undo())return;'+engine._storer('0');
  1097. }
  1098. function handleZ_print_unicode(engine, a) {
  1099. return engine._handler_zOut("String.fromCharCode(" +a[0]+")",0);
  1100. }
  1101. function handleZ_check_unicode(engine, a) {
  1102. // We have no way of telling from JS whether we can
  1103. // read or write a character, so let's assume we can
  1104. // read and write all of them. We can always provide
  1105. // methods to do so somehow (e.g. with an onscreen keyboard).
  1106. return engine._storer('3');
  1107. }
  1108. function handleZ_gestalt( engine, a )
  1109. {
  1110. // 1.2 Standard @gestalt
  1111. return engine._storer('gestalt(' + a[0] + ', ' + ( a.length < 2 ? 0 : a[1] ) + ')' );
  1112. }
  1113. function handleZ_parchment( engine, a )
  1114. {
  1115. return engine._storer('op_parchment(' + a[0] + ', ' + ( a.length < 2 ? 0 : a[1] ) + ')' );
  1116. }
  1117. ////////////////////////////////////////////////////////////////
  1118. //
  1119. // |handlers|
  1120. //
  1121. // An array mapping opcodes to functions. Each function is passed
  1122. // a series of arguments (between zero and eight, as the Z-machine
  1123. // allows) as an array, called |a| below. It returns a string of JS,
  1124. // called |r| in these comments, which can be evaluated to do the job of that
  1125. // opcode. Note, again, that this is a string (not a function object).
  1126. //
  1127. // Extended ("EXT") opcodes are stored 1000 higher than their number.
  1128. // For example, 1 is "je", but 1001 is "restore".
  1129. //
  1130. // |r|'s code may set |engine.m_compilation_running| to 0 to stop compile() from producing
  1131. // code for any more opcodes after this one. (compile() likes to group
  1132. // code up into blocks, where it can.)
  1133. //
  1134. // |r|'s code may contain a return statement to prevent the execution of
  1135. // any further generated code before we get to take our bearings again.
  1136. // For example, |r| must cause a return if it knows that a jump occurred.
  1137. // If a handler wishes to send an effect to the environment, it should
  1138. // set |m_effects| in the engine to a non-empty list and return.
  1139. var handlers_v578 = {
  1140. 1: handleZ_je,
  1141. 2: handleZ_jl,
  1142. 3: handleZ_jg,
  1143. 4: handleZ_dec_chk,
  1144. 5: handleZ_inc_chk,
  1145. 6: handleZ_jin,
  1146. 7: handleZ_test,
  1147. 8: handleZ_or,
  1148. 9: handleZ_and,
  1149. 10: handleZ_test_attr,
  1150. 11: handleZ_set_attr,
  1151. 12: handleZ_clear_attr,
  1152. 13: handleZ_store,
  1153. 14: handleZ_insert_obj,
  1154. 15: handleZ_loadw,
  1155. 16: handleZ_loadb,
  1156. 17: handleZ_get_prop,
  1157. 18: handleZ_get_prop_addr,
  1158. 19: handleZ_get_next_prop,
  1159. 20: handleZ_add,
  1160. 21: handleZ_sub,
  1161. 22: handleZ_mul,
  1162. 23: handleZ_div,
  1163. 24: handleZ_mod,
  1164. 25: handleZ_call_2s,
  1165. 26: handleZ_call_2n,
  1166. 27: handleZ_set_colour,
  1167. 28: handleZ_throw,
  1168. 128: handleZ_jz,
  1169. 129: handleZ_get_sibling,
  1170. 130: handleZ_get_child,
  1171. 131: handleZ_get_parent,
  1172. 132: handleZ_get_prop_len,
  1173. 133: handleZ_inc,
  1174. 134: handleZ_dec,
  1175. 135: handleZ_print_addr,
  1176. 136: handleZ_call_1s,
  1177. 137: handleZ_remove_obj,
  1178. 138: handleZ_print_obj,
  1179. 139: handleZ_ret,
  1180. 140: handleZ_jump,
  1181. 141: handleZ_print_paddr,
  1182. 142: handleZ_load,
  1183. 143: handleZ_call_1n,
  1184. 176: handleZ_rtrue,
  1185. 177: handleZ_rfalse,
  1186. 178: handleZ_print,
  1187. 179: handleZ_print_ret,
  1188. 180: handleZ_nop,
  1189. //181: save (illegal in V5)
  1190. //182: restore (illegal in V5)
  1191. 183: handleZ_restart,
  1192. 184: handleZ_ret_popped,
  1193. 185: handleZ_catch,
  1194. 186: handleZ_quit,
  1195. 187: handleZ_new_line,
  1196. // 188: show_status -- illegal from V4 onward
  1197. 189: handleZ_verify,
  1198. 190: handleZ_illegal_extended,
  1199. 191: handleZ_piracy,
  1200. 224: handleZ_call_vs,
  1201. 225: handleZ_store_w,
  1202. 226: handleZ_storeb,
  1203. 227: handleZ_putprop,
  1204. 228: handleZ_read,
  1205. 229: handleZ_print_char,
  1206. 230: handleZ_print_num,
  1207. 231: handleZ_random,
  1208. 232: handleZ_push,
  1209. 233: handleZ_pull,
  1210. 234: handleZ_split_window,
  1211. 235: handleZ_set_window,
  1212. 236: handleZ_call_vs, // call_vs2
  1213. 237: handleZ_erase_window,
  1214. 238: handleZ_erase_line,
  1215. 239: handleZ_set_cursor,
  1216. 240: handleZ_get_cursor,
  1217. 241: handleZ_set_text_style,
  1218. 242: handleZ_buffer_mode,
  1219. 243: handleZ_output_stream,
  1220. 244: handleZ_input_stream,
  1221. 245: handleZ_sound_effect,
  1222. 246: handleZ_read_char,
  1223. 247: handleZ_scan_table,
  1224. 248: handleZ_not,
  1225. 249: handleZ_call_vn,
  1226. 250: handleZ_call_vn, // call_vn2,
  1227. 251: handleZ_tokenise,
  1228. 252: handleZ_encode_text,
  1229. 253: handleZ_copy_table,
  1230. 254: handleZ_print_table,
  1231. 255: handleZ_check_arg_count,
  1232. 1000: handleZ_saveV45678,
  1233. 1001: handleZ_restoreV45678,
  1234. 1002: handleZ_log_shift,
  1235. 1003: handleZ_art_shift,
  1236. 1004: handleZ_set_font,
  1237. //1005: draw_picture (V6 opcode)
  1238. //1006: picture_dat (V6 opcode)
  1239. //1007: erase_picture (V6 opcode)
  1240. //1008: set_margins (V6 opcode)
  1241. 1009: handleZ_save_undo,
  1242. 1010: handleZ_restore_undo,
  1243. 1011: handleZ_print_unicode,
  1244. 1012: handleZ_check_unicode,
  1245. //1013-1015: illegal
  1246. //1016: move_window (V6 opcode)
  1247. //1017: window_size (V6 opcode)
  1248. //1018: window_style (V6 opcode)
  1249. //1019: get_wind_prop (V6 opcode)
  1250. //1020: scroll_window (V6 opcode)
  1251. //1021: pop_stack (V6 opcode)
  1252. //1022: read_mouse (V6 opcode)
  1253. //1023: mouse_window (V6 opcode)
  1254. //1024: push_stack (V6 opcode)
  1255. //1025: put_wind_prop (V6 opcode)
  1256. //1026: print_form (V6 opcode)
  1257. //1027: make_menu (V6 opcode)
  1258. //1028: picture_table (V6 opcode)
  1259. 1030: handleZ_gestalt,
  1260. 1031: handleZ_parchment
  1261. };
  1262. // Differences between each version and v5.
  1263. // Set a whole version to undefined if it's not implemented.
  1264. // If a version is identical to v5, use '' rather than {} to
  1265. // make the engine work with the original array rather than a copy.
  1266. // When an opcode is illegal in the given version but not in v5,
  1267. // it's marked with a zero. If you're working with a version which
  1268. // doesn't support extended opcodes (below v5), don't worry about
  1269. // zeroing out codes above 999-- they can't be accessed anyway.
  1270. var handlers_fixups = {
  1271. 1: {
  1272. 25: 0, // call_2s
  1273. 26: 0, // call_2n
  1274. 27: 0, // set_colour
  1275. 28: 0, // throw
  1276. 136: 0, // call_1s
  1277. 143: handleZ_not, // replaces call_1n
  1278. 181: handleZ_saveV123,
  1279. 182: handleZ_restoreV123,
  1280. 185: handleZ_pop, // replaces catch
  1281. 188: handleZ_show_status,
  1282. 190: 0, // extended opcodes
  1283. 191: 0, // piracy
  1284. // 224 is shown in the ZMSD as being "call" before v4 and
  1285. // "call_vs" thence; this appears to be simply a name change.
  1286. // 228, similarly, is "sread" and then "aread".
  1287. 236: 0, // call_vs
  1288. 237: 0, // erase_window
  1289. 238: 0, // erase_line
  1290. 239: 0, // set_cursor
  1291. 240: 0, // get_cursor
  1292. 241: 0, // set_text_style
  1293. 242: 0, // buffer_mode
  1294. 246: 0, // read_char
  1295. 247: 0, // scan_table,
  1296. 248: 0, // not
  1297. 249: 0, // call_vn
  1298. 250: 0, // call_vn2
  1299. 251: 0, // tokenise
  1300. 252: 0, // encode_text
  1301. 253: 0, // copy_table
  1302. 254: 0, // print_table
  1303. 255: 0 // check_arg_count
  1304. },
  1305. 2: {
  1306. 25: 0, // call_2s
  1307. 26: 0, // call_2n
  1308. 27: 0, // set_colour
  1309. 28: 0, // throw
  1310. 136: 0, // call_1s
  1311. 143: handleZ_not, // replaces call_1n
  1312. 181: handleZ_saveV123,
  1313. 182: handleZ_restoreV123,
  1314. 185: handleZ_pop, // replaces catch
  1315. 188: handleZ_show_status,
  1316. 190: 0, // extended opcodes
  1317. 191: 0, // piracy
  1318. // 224 is shown in the ZMSD as being "call" before v4 and
  1319. // "call_vs" thence; this appears to be simply a name change.
  1320. // 228, similarly, is "sread" and then "aread".
  1321. 236: 0, // call_vs
  1322. 237: 0, // erase_window
  1323. 238: 0, // erase_line
  1324. 239: 0, // set_cursor
  1325. 240: 0, // get_cursor
  1326. 241: 0, // set_text_style
  1327. 242: 0, // buffer_mode
  1328. 246: 0, // read_char
  1329. 247: 0, // scan_table,
  1330. 248: 0, // not
  1331. 249: 0, // call_vn
  1332. 250: 0, // call_vn2
  1333. 251: 0, // tokenise
  1334. 252: 0, // encode_text
  1335. 253: 0, // copy_table
  1336. 254: 0, // print_table
  1337. 255: 0 // check_arg_count
  1338. },
  1339. 3: {
  1340. 25: 0, // call_2s
  1341. 26: 0, // call_2n
  1342. 27: 0, // set_colour
  1343. 28: 0, // throw
  1344. 136: 0, // call_1s
  1345. 143: handleZ_not, // replaces call_1n
  1346. 181: handleZ_saveV123,
  1347. 182: handleZ_restoreV123,
  1348. 185: handleZ_pop, // replaces catch
  1349. 188: handleZ_show_status,
  1350. 190: 0, // extended opcodes
  1351. 191: 0, // piracy
  1352. // 224 is shown in the ZMSD as being "call" before v4 and
  1353. // "call_vs" thence; this appears to be simply a name change.
  1354. // 228, similarly, is "sread" and then "aread".
  1355. 236: 0, // call_vs
  1356. 237: 0, // erase_window
  1357. 238: 0, // erase_line
  1358. 239: 0, // set_cursor
  1359. 240: 0, // get_cursor
  1360. 241: 0, // set_text_style
  1361. 242: 0, // buffer_mode
  1362. 246: 0, // read_char
  1363. 247: 0, // scan_table,
  1364. 248: 0, // not
  1365. 249: 0, // call_vn
  1366. 250: 0, // call_vn2
  1367. 251: 0, // tokenise
  1368. 252: 0, // encode_text
  1369. 253: 0, // copy_table
  1370. 254: 0, // print_table
  1371. 255: 0 // check_arg_count
  1372. },
  1373. 4: { // z4 is fittingly somewhere between z3 and z5
  1374. 26: 0, // call_2n
  1375. 27: 0, // set_colour
  1376. 28: 0, // throw
  1377. 143: handleZ_not, // replaces call_1n
  1378. 181: handleZ_saveV45678, // was illegal in v5 (EXT used instead)
  1379. 182: handleZ_restoreV45678, // ditto
  1380. 185: handleZ_pop, // replaces catch
  1381. 190: 0, // extended opcodes
  1382. 191: 0, // piracy
  1383. 248: 0, // not
  1384. 249: 0, // call_vn
  1385. 250: 0, // call_vn2
  1386. 251: 0, // tokenise
  1387. 252: 0, // encode_text
  1388. 253: 0, // copy_table
  1389. 254: 0, // print_table
  1390. 255: 0 // check_arg_count
  1391. },
  1392. 5: '', // The base copy *is* v5
  1393. 6: undefined, // very complicated, and not yet implemented-- see bug 3621
  1394. 7: '', // Defined to be the same as 5
  1395. 8: '' // Defined to be the same as 5
  1396. };
  1397. ////////////////////////////////////////////////////////////////
  1398. //
  1399. // pc_translate_*
  1400. //
  1401. // Each of these functions returns a string of JS code to set the PC
  1402. // to the address in |packed_target|, based on the current architecture.
  1403. //
  1404. // TODO: Would be good if we could pick up when it was a constant.
  1405. function pc_translate_v123(p) { return '(('+p+')&0xFFFF)*2'; }
  1406. function pc_translate_v45(p) { return '(('+p+')&0xFFFF)*4'; }
  1407. function pc_translate_v67R(p) { return '(('+p+')&0xFFFF)*4+'+this.m_routine_start; }
  1408. function pc_translate_v67S(p) { return '(('+p+')&0xFFFF)*4+'+this.m_string_start; }
  1409. function pc_translate_v8(p) { return '(('+p+')&0xFFFF)*8'; }
  1410. ////////////////////////////////////////////////////////////////
  1411. //
  1412. // PART THE THIRD
  1413. //
  1414. // THE NEW AMAZING COMPONENT WHICH PLAYS GAMES AND WASHES DISHES
  1415. // AND LAYS THE TABLE AND WALKS THE DOG AND CLEANS THE OVEN AND...
  1416. //
  1417. ////////////////////////////////////////////////////////////////
  1418. function gnusto_error(number) {
  1419. message ='Component: engine\n';
  1420. message += 'Code: ' + number + '\n';
  1421. for (var i=1; i<arguments.length; i++) {
  1422. if (arguments[i] && arguments[i].toString) {
  1423. message += '\nDetail: '+arguments[i].toString();
  1424. }
  1425. }
  1426. throw new FatalError(message);
  1427. }
  1428. ////////////////////////////////////////////////////////////////
  1429. //
  1430. // onISRReturn_...
  1431. //
  1432. // When a rebound function causes an interrupt, it may nominate
  1433. // another function to clear up when the interrupt's done.
  1434. // These are those functions.
  1435. //
  1436. // The PC will be reset before calling these functions; they
  1437. // only have to deal with rebounds, causing further effects and
  1438. // so on.
  1439. //
  1440. // Because these functions will be called as a result of an @return
  1441. // (or @rtrue or whatever) you can be sure they're at the end of a
  1442. // block in JITspace.
  1443. //
  1444. function onISRReturn_for_read_char(interrupt_info, result) {
  1445. if (result) {
  1446. // If an ISR returns true, we return as from the original
  1447. // effect, storing zero for the keypress.
  1448. interrupt_info.engine.m_answers[0] = 0;
  1449. interrupt_info.rebound();
  1450. } else {
  1451. // If an ISR returns false, we cause the same effect again.
  1452. interrupt_info.engine.m_effects = interrupt_info.effects;
  1453. interrupt_info.engine.m_rebound = interrupt_info.rebound;
  1454. interrupt_info.engine.m_rebound_args = interrupt_info.rebound_args;
  1455. }
  1456. }
  1457. function onISRReturn_for_read(interrupt_info, result) {
  1458. var engine = interrupt_info.engine;
  1459. if (result) {
  1460. // If an ISR returns true, we return as from the original
  1461. // effect. The terminating keypress is given as zero.
  1462. // The contents of the text buffer are set to zero.
  1463. engine.m_answers[0] = 0;
  1464. // From this, the rebound will save the text and
  1465. // parse buffers correctly:
  1466. engine.m_answers[1] = '';
  1467. interrupt_info.rebound();
  1468. } else {
  1469. // This is the really tricky part:
  1470. // XXX FIXME:
  1471. // If the effect has printed anything... what?
  1472. engine.m_effects = interrupt_info.effects;
  1473. engine.m_rebound = interrupt_info.rebound;
  1474. engine.m_rebound_args = interrupt_info.rebound_args;
  1475. }
  1476. }
  1477. ////////////////////////////////////////////////////////////////
  1478. //
  1479. // The Engine
  1480. //
  1481. // The object itself...
  1482. function GnustoEngine(logfunc) {
  1483. if (logfunc)
  1484. this.logger = function(a, b) {
  1485. logfunc("gnusto-engine: " + a + ": " + b);
  1486. };
  1487. else
  1488. this.logger = function() { };
  1489. }
  1490. GnustoEngine.prototype = {
  1491. ////////////////////////////////////////////////////////////////
  1492. ////////////////////////////////////////////////////////////////
  1493. // //
  1494. // PUBLIC METHODS //
  1495. // //
  1496. ////////////////////////////////////////////////////////////////
  1497. ////////////////////////////////////////////////////////////////
  1498. loadStory: function ge_loadStory(sourceFile) {
  1499. this.m_memory = sourceFile;
  1500. this._initial_setup();
  1501. },
  1502. loadSavedGame: function ge_loadSavedGame(savefile)
  1503. {
  1504. // Load the Quetzal savefile
  1505. var quetzal = new Quetzal(savefile);
  1506. var mem = quetzal.memory;
  1507. var stacks = quetzal.stacks;
  1508. var pc = quetzal.pc;
  1509. // FIXME: Still to do here:
  1510. // There's a bit which should survive restore.
  1511. // There are several bytes which should be reset (e.g. terp ID).
  1512. function decodeStackInt(offset, length) {
  1513. var result = stacks[offset++];
  1514. for (var i=1; i<length; i++) {
  1515. result = (result<<8)|stacks[offset++];
  1516. }
  1517. return result;
  1518. }
  1519. if (quetzal.compressed) {
  1520. // Welcome to the decompression chamber.
  1521. var temp = [];
  1522. var cursor_compressed = 0;
  1523. var cursor_original = 0;
  1524. while (cursor_compressed < mem.length) {
  1525. if (cursor_original >= this.m_original_memory.length) {
  1526. // FIXME: proper error message
  1527. gnusto_error(999, "overshoot in decompression");
  1528. }
  1529. var candidate = mem[cursor_compressed++];
  1530. if (candidate == 0) {
  1531. // Sequence of identical bytes.
  1532. var run_length = mem[cursor_compressed++]+1;
  1533. temp = temp.concat(this.m_original_memory.slice(cursor_original,
  1534. cursor_original+run_length));
  1535. cursor_original += run_length;
  1536. } else {
  1537. // One different byte, XORed with the original.
  1538. temp.push(candidate ^
  1539. this.m_original_memory[cursor_original++]);
  1540. }
  1541. }
  1542. mem = temp;
  1543. }
  1544. // Firstly, zap all the important variables...
  1545. // FIXME: Eventually we should work into copies,
  1546. // and only move these over when we're sure everything's good.
  1547. // Otherwise we could want to go back to how things were before.
  1548. this.m_call_stack = [];
  1549. this.m_gamestack = [];
  1550. this.m_locals_stack = [];
  1551. this.m_locals = [];
  1552. this.m_result_targets = [];
  1553. var evals_count = 0;
  1554. // Pick up the amount of eval stack used by the bootstrap.
  1555. evals_count = decodeStackInt(7, 1);
  1556. this.m_gamestack_callbreaks = [];
  1557. // Highest value yet pushed to m_gamestack_callbreaks
  1558. var callbreaks_top = evals_count;
  1559. var cursor = 8;
  1560. for (var m=0; m<evals_count; m++) {
  1561. this.m_gamestack.push(decodeStackInt(cursor, 2));
  1562. cursor+=2;
  1563. }
  1564. while (cursor < stacks.length) {
  1565. this.m_call_stack.push(decodeStackInt(cursor, 3));
  1566. cursor+=3;
  1567. ////////////////////////////////////////////////////////////////
  1568. var flags = stacks[cursor++];
  1569. var varcode = stacks[cursor++];
  1570. if (flags & 0x10) {
  1571. // Flag set to show that we should throw away
  1572. // the result of this call. We represent that
  1573. // by a varcode of -1.
  1574. varcode = -1;
  1575. }
  1576. var locals_count = flags & 0xF;
  1577. this.m_locals_stack.unshift(locals_count);
  1578. this.m_result_targets.push(varcode);
  1579. var logArgs = stacks[cursor++]+1;
  1580. var argCount = 0;
  1581. while (logArgs>1) {
  1582. logArgs >>= 1;
  1583. argCount++;
  1584. }
  1585. this.m_param_counts.unshift(argCount);
  1586. evals_count = decodeStackInt(cursor, 2);
  1587. cursor += 2;
  1588. callbreaks_top += evals_count;
  1589. this.m_gamestack_callbreaks.push(callbreaks_top);
  1590. var locals_temp = [];
  1591. for (var k=0; k<locals_count; k++) {
  1592. locals_temp.push(decodeStackInt(cursor, 2));
  1593. cursor+=2;
  1594. }
  1595. this.m_locals = locals_temp.concat(this.m_locals);
  1596. for (var m=0; m<evals_count; m++) {
  1597. this.m_gamestack.push(decodeStackInt(cursor, 2));
  1598. cursor+=2;
  1599. }
  1600. }
  1601. // Base locals aren't saved, so restore them as zeroes.
  1602. for (var n=0; n<16; n++) {
  1603. this.m_locals.push(0);
  1604. }
  1605. // Restore the memory.
  1606. this.m_memory = mem.concat(this.m_memory.slice(mem.length));
  1607. var offset = (this.m_version<=4? 1: 3) + 1;
  1608. this.m_pc = pc-offset; // rewind to before the varcode_offset
  1609. if (this.m_version <= 3) {
  1610. // This is pretty ugly, but then the design isn't too beautiful either.
  1611. // The Quetzal code loads up with the PC pointing at the end of the @save
  1612. // which saved it. The end is the half-an-instruction which gives a branch
  1613. // address. (Ick.) But _brancher will compile that half-an-instruction into
  1614. // JS of the form
  1615. // if (<condition>){m_pc=<whatever>;return;}
  1616. // So what we do is call |_brancher()| to compile this, and then
  1617. // immediately evaluate the result to make the jump. The condition is
  1618. // '1'-- we always want it to make the jump. And we have to wrap the
  1619. // whole thing in a temporary function so that the "return" doesn't
  1620. // mess things up. (The alternative would be to special-case
  1621. // |_brancher()|, but this case is so very, very rare and perverted
  1622. // that that seems inelegant.)
  1623. eval("var t=new Function('with(this){'+this._brancher('1')+'}');t.call(this);");
  1624. } else {
  1625. // The PC we're given is actually pointing at the varcode
  1626. // into which the success code must be stored. It should be 2.
  1627. // (This is specified by section 5.8 of the Quetzal document,
  1628. // version 1.4.)
  1629. this._varcode_set(2, this.m_memory[this.m_pc++]);
  1630. }
  1631. },
  1632. resetStory: function ge_resetStory() {
  1633. this.m_memory = this.m_original_memory.slice(); // Make a copy.
  1634. this._initial_setup();
  1635. },
  1636. version: function ge_version() {
  1637. gnusto_error(101, "'version' not implemented");
  1638. },
  1639. signature: function ge_signature() {
  1640. gnusto_error(101, "'signature' not implemented");
  1641. },
  1642. cvsVersion: function ge_cvsVersion() {
  1643. return CVS_VERSION.substring(7, 26);
  1644. },
  1645. setGoldenTrail: function ge_setGoldenTrail(value) {
  1646. if (value) {
  1647. this.m_goldenTrail = 1;
  1648. } else {
  1649. this.m_goldenTrail = 0;
  1650. }
  1651. },
  1652. setCopperTrail: function ge_setCopperTrail(value) {
  1653. if (value) {
  1654. this.m_copperTrail = 1;
  1655. } else {
  1656. this.m_copperTrail = 0;
  1657. }
  1658. },
  1659. effect: function ge_effect(which) {
  1660. return this.m_effects[which];
  1661. },
  1662. answer: function ge_answer(which, what) {
  1663. this.m_answers[which] = what;
  1664. },
  1665. // Main point of entry for gnusto. Be sure to call start_game()
  1666. // before calling this the first time.
  1667. run: function ge_run() {
  1668. var start_pc = 0;
  1669. var turns = 0;
  1670. var jscode;
  1671. var turns_limit = this.m_single_step? 1: 10000;
  1672. if (this.m_rebound) {
  1673. this.m_rebound();
  1674. this.m_rebound = 0;
  1675. this.m_rebound_args = [];
  1676. }
  1677. this.m_effects = [];
  1678. while(this.m_effects.length == 0) {
  1679. if (turns++ >= turns_limit) {
  1680. // Wimp out for now.
  1681. // Can't use GNUSTO_EFFECT_WIMP_OUT directly
  1682. // because it has "" around it.
  1683. this.m_effects = ['WO'];
  1684. return 1;
  1685. }
  1686. start_pc = this.m_pc;
  1687. if (this.m_jit[start_pc]) {
  1688. jscode = this.m_jit[start_pc];
  1689. } else {
  1690. jscode=eval('with (this) {dummy='+this._compile()+'}');
  1691. // Store it away, if it's in static memory (there's
  1692. // not much point caching JIT from dynamic memory!)
  1693. if (start_pc >= this.m_stat_start) {
  1694. this.m_jit[start_pc] = jscode;
  1695. }
  1696. }
  1697. // Some useful debugging code:
  1698. // if (this.m_copperTrail) {
  1699. // this.logger('pc', start_pc.toString(16));
  1700. // this.logger('jit', jscode);
  1701. // }
  1702. jscode();
  1703. }
  1704. },
  1705. walk: function ge_walk(answer) {
  1706. gnusto_error(101, "'walk' not implemented");
  1707. },
  1708. setRandomSeed: function ge_setRandomSeed(seed) {
  1709. // This can be done by the private function _random_number(),
  1710. // provided we give it a negative argument. We can also
  1711. // pass it zero, which means the same to it as it does to us:
  1712. // that we should return to nonseeded output. (Well,
  1713. // non-obviously seeded anyway.)
  1714. if (seed>0) {
  1715. this._random_number(-seed);
  1716. } else {
  1717. this._random_number(seed);
  1718. }
  1719. },
  1720. ////////////////////////////////////////////////////////////////
  1721. //
  1722. // saveGame
  1723. //
  1724. // Saves a game out to a file.
  1725. //
  1726. saveGame: function ge_saveGame()
  1727. {
  1728. // Returns an array of |bytecount| integers, each
  1729. // representing a byte of |number| in network byte order.
  1730. function int_to_bytes(number, bytecount)
  1731. {
  1732. var result = [];
  1733. for (var i = 0; i < bytecount; i++)
  1734. {
  1735. result[(bytecount - i) - 1] = number & 0xFF;
  1736. number >>= 8;
  1737. }
  1738. return result;
  1739. }
  1740. // The state we are saving
  1741. var state = this.m_state_to_save,
  1742. // Locals for compressing the memory
  1743. compressed = [], same_count = 0,
  1744. // Locals for the stack
  1745. locals_cursor = this.m_locals.length - 16, gamestack_cursor = 0,
  1746. stacks = [ // Firstly, the dummy first record
  1747. 0x00, 0x00, 0x00, // PC
  1748. 0x00, // flags
  1749. 0x00, // varcode
  1750. 0x00 // args
  1751. ],
  1752. // Make a Quetzal instance
  1753. quetzal = new Quetzal();
  1754. quetzal.release = state.m_memory.slice(0x02, 0x04);
  1755. quetzal.serial = state.m_memory.slice(0x12, 0x18);
  1756. quetzal.checksum = state.m_memory.slice(0x1C, 0x1E);
  1757. quetzal.pc = state.m_pc;
  1758. quetzal.compressed = 1;
  1759. //if (this.m_compress_save_files) {
  1760. // Compress the memory
  1761. for (var i = 0, s = this.m_stat_start; i < s; i++)
  1762. {
  1763. if (state.m_memory[i] == this.m_original_memory[i])
  1764. {
  1765. same_count++;
  1766. if (same_count == 256)
  1767. {
  1768. compressed.push(0);
  1769. compressed.push(255);
  1770. same_count = 0;
  1771. }
  1772. }
  1773. else
  1774. {
  1775. if (same_count != 0)
  1776. {
  1777. compressed.push(0);
  1778. compressed.push(same_count - 1);
  1779. same_count = 0;
  1780. }
  1781. compressed.push(state.m_memory[i] ^ this.m_original_memory[i]);
  1782. }
  1783. }
  1784. if (same_count != 0)
  1785. {
  1786. // write out remaining same count
  1787. compressed.push(0);
  1788. compressed.push(same_count - 1);
  1789. }
  1790. // Add it to the Quetzal instance
  1791. quetzal.memory = compressed;
  1792. /*
  1793. }
  1794. else
  1795. {
  1796. // Not using compressed memory.
  1797. quetzal.memory = this.m_memory.slice(0, this.m_stat_start);
  1798. }
  1799. */
  1800. ////////////////////////////////////////////////////////////////
  1801. // Write out the stacks.
  1802. // And top it off with the amount of eval stack used.
  1803. stacks = stacks.concat(int_to_bytes(this.m_gamestack_callbreaks[0], 2));
  1804. for (var m = 0; m < this.m_gamestack_callbreaks[0]; m++)
  1805. stacks = stacks.concat(int_to_bytes(this.m_gamestack[gamestack_cursor++], 2));
  1806. for (var j = 0; j < this.m_call_stack.length; j++)
  1807. {
  1808. stacks = stacks.concat(int_to_bytes(this.m_call_stack[j], 3));
  1809. // m_locals_stack is back to front so that we can always
  1810. // refer to the current frame as m_l_s[x].
  1811. var local_count = this.m_locals_stack[this.m_locals_stack.length - (j + 1)],
  1812. flags = local_count,
  1813. target = this.m_result_targets[j],
  1814. // FIXME: This is ugly too. Why is m_p_c back to front?
  1815. args_supplied = this.m_param_counts[this.m_param_counts.length - (j + 1)],
  1816. eval_taken = this.m_gamestack_callbreaks[j] - gamestack_cursor;
  1817. if (target == -1)
  1818. {
  1819. // This is a call-and-throw-away rather than a
  1820. // call-and-store. We represent that with a magic
  1821. // varcode of -1, but Quetzal sets a flag instead.
  1822. target = 0;
  1823. flags |= 0x10;
  1824. }
  1825. stacks = stacks.concat([
  1826. flags,
  1827. target,
  1828. // I'm assuming that once a bit is set here,
  1829. // all bits to its right are set too.
  1830. // So we raise 2 to the power of the number
  1831. // and subtract one.
  1832. (1 << args_supplied) - 1,
  1833. (eval_taken >> 8) & 0xFF,
  1834. eval_taken & 0xFF
  1835. ]);
  1836. locals_cursor -= local_count;
  1837. for (var k = 0; k < local_count; k++)
  1838. stacks = stacks.concat(int_to_bytes(this.m_locals[locals_cursor + k], 2));
  1839. for (var m = 0; m < eval_taken; m++)
  1840. stacks = stacks.concat(int_to_bytes(this.m_gamestack[gamestack_cursor++], 2));
  1841. }
  1842. // Write out the Quetzal
  1843. quetzal.stacks = stacks;
  1844. this.m_quetzal_image = quetzal.write();
  1845. return this.m_quetzal_image.length;
  1846. },
  1847. saveGameData: function ge_saveGameData(len, result) {
  1848. var temp = this.m_quetzal_image;
  1849. this.m_quetzal_image = 0;
  1850. return temp;
  1851. },
  1852. architecture: function ge_architecture() {
  1853. return 'none';
  1854. },
  1855. piracy: function ge_piracy() {
  1856. return -1;
  1857. },
  1858. tandy: function ge_tandy() {
  1859. return -1;
  1860. },
  1861. status: function ge_status() {
  1862. return 'this is the status, hurrah!';
  1863. },
  1864. getStatusLine: function ge_getStatusLine(width) {
  1865. //fnugry
  1866. var current_room_object_number = this.getUnsignedWord(this.m_vars_start);
  1867. var object_properties_address = this.getUnsignedWord(this.m_property_list_addr_start+(this.m_object_size*current_room_object_number));
  1868. var outtext = this._zscii_from(object_properties_address+1);
  1869. if (outtext.length > width) {
  1870. outtext = outtext.substring(0,width-3);
  1871. var outtext2 = '...';
  1872. var spacebuffer = '';
  1873. } else {
  1874. if ((this.m_version > 3) && ((this.getByte(1)&0x02)==2)) { // if it is a time game
  1875. var hours = this.getUnsignedWord(this.m_vars_start+2);
  1876. var minutes = this.getUnsignedWord(this.m_vars_start+4);
  1877. if (minutes < 10) {
  1878. var outtext2 = hours + ':0' + minutes;
  1879. } else {
  1880. var outtext2 = hours + ':' + minutes;
  1881. }
  1882. } else { // if it is a score game
  1883. var outtext2 = 'Score: ' + this.getUnsignedWord(this.m_vars_start+2) + ' Moves: ' + this.getUnsignedWord(this.m_vars_start+4);
  1884. }
  1885. if ((outtext.length + outtext2.length + 1) > width) {
  1886. outtext2 = ' S:' + this.getUnsignedWord(this.m_vars_start+2) + ' M:' + this.getUnsignedWord(this.m_vars_start+4);
  1887. if ((outtext.length + outtext2.length + 1) > width) {
  1888. outtext2 = ' ' + this.getUnsignedWord(this.m_vars_start+2) + '/' + this.getUnsignedWord(this.m_vars_start+4);
  1889. }
  1890. if ((outtext.length + outtext2.length + 1) > width) {
  1891. outtext2 = '';
  1892. }
  1893. }
  1894. var spacebuffer = '';
  1895. while ((outtext.length + outtext2.length + spacebuffer.length) < width) {
  1896. spacebuffer += ' ';
  1897. }
  1898. }
  1899. return outtext + spacebuffer + outtext2;
  1900. },
  1901. // @gestalt selectors
  1902. gestalt: function( id, arg )
  1903. {
  1904. // 1: Standard Revision
  1905. if ( id == 1 )
  1906. return 0x0102;
  1907. // 0x20: @parchment
  1908. if ( id == 0x20 )
  1909. {
  1910. if (
  1911. arg == 0 ||
  1912. // 1: Raw eval()
  1913. arg == 1 && PARCHMENT_SECURITY_OVERRIDE
  1914. )
  1915. return 1;
  1916. }
  1917. return 0;
  1918. },
  1919. // @parchment
  1920. op_parchment: function( id, arg )
  1921. {
  1922. var self = this;
  1923. // 1: raw eval()
  1924. if ( id == 1 && PARCHMENT_SECURITY_OVERRIDE )
  1925. {
  1926. // Enable raw eval() mode
  1927. if ( arg == 1 )
  1928. {
  1929. self.op_parchment_data.saved_buffer = self.m_console_buffer;
  1930. self.m_console_buffer = '';
  1931. self.op_parchment_data.raw_eval = 1;
  1932. }
  1933. // Return to normal mode, evaluating the buffer
  1934. else
  1935. {
  1936. if ( self.op_parchment_data.raw_eval )
  1937. {
  1938. eval( self.m_console_buffer );
  1939. self.m_console_buffer = self.op_parchment_data.saved_buffer;
  1940. }
  1941. self.op_parchment_data.raw_eval = 0;
  1942. }
  1943. return 1;
  1944. }
  1945. return 0;
  1946. },
  1947. ////////////////////////////////////////////////////////////////
  1948. ////////////////////////////////////////////////////////////////
  1949. // //
  1950. // PRIVATE METHODS //
  1951. // //
  1952. ////////////////////////////////////////////////////////////////
  1953. ////////////////////////////////////////////////////////////////
  1954. ////////////////////////////////////////////////////////////////
  1955. // _initial_setup
  1956. //
  1957. // Initialises our variables.
  1958. //
  1959. _initial_setup: function ge_initial_setup() {
  1960. this.m_jit = [];
  1961. this.m_compilation_running = 0;
  1962. this.m_gamestack = [];
  1963. this.m_gamestack_callbreaks = [];
  1964. this.m_call_stack = [];
  1965. this.m_locals = [];
  1966. this.m_locals_stack = [];
  1967. this.m_param_counts = [];
  1968. this.m_result_targets = [];
  1969. this.m_goldenTrail = 0;
  1970. this.m_copperTrail = 0;
  1971. this.m_version = this.m_memory[0];
  1972. this.m_original_memory = this.m_memory.slice(); // Make a copy.
  1973. this.m_himem = this.getUnsignedWord(0x4);
  1974. this.m_pc = this.getUnsignedWord(0x6);
  1975. this.m_dict_start = this.getUnsignedWord(0x8);
  1976. this.m_objs_start = this.getUnsignedWord(0xA);
  1977. this.m_vars_start = this.getUnsignedWord(0xC);
  1978. this.m_stat_start = this.getUnsignedWord(0xE);
  1979. this.m_abbr_start = this.getUnsignedWord(0x18);
  1980. if (this.m_version>=4) {
  1981. this.m_alpha_start = this.getUnsignedWord(0x34);
  1982. this.m_object_tree_start = this.m_objs_start + 112;
  1983. this.m_property_list_addr_start = this.m_object_tree_start + 12;
  1984. this.m_object_size = 14;
  1985. } else {
  1986. this.m_alpha_start = 0;
  1987. this.m_object_tree_start = this.m_objs_start + 53;
  1988. this.m_property_list_addr_start = this.m_object_tree_start + 7;
  1989. this.m_object_size = 9;
  1990. }
  1991. this.m_hext_start = this.getUnsignedWord(0x36);
  1992. // Use the correct addressing mode for this Z-machine version...
  1993. if (this.m_version<=3) {
  1994. // Versions 1 and 2 (prehistoric) and 3 ("Standard")
  1995. this.m_pc_translate_for_routine = pc_translate_v123;
  1996. this.m_pc_translate_for_string = pc_translate_v123;
  1997. } else if (this.m_version<=5) {
  1998. // Versions 4 ("Plus") and 5 ("Advanced")
  1999. this.m_pc_translate_for_routine = pc_translate_v45;
  2000. this.m_pc_translate_for_string = pc_translate_v45;
  2001. } else if (this.m_version<=7) {
  2002. // Versions 6 (the graphical one) and 7 (rare postInfocom extension)
  2003. this.m_routine_start = this.getUnsignedWord(0x28)*8;
  2004. this.m_string_start = this.getUnsignedWord(0x2a)*8;
  2005. this.m_pc_translate_for_routine = pc_translate_v67R;
  2006. this.m_pc_translate_for_string = pc_translate_v67S;
  2007. } else if (this.m_version==8) {
  2008. // Version 8 (normal postInfocom extension)
  2009. this.m_pc_translate_for_routine = pc_translate_v8;
  2010. this.m_pc_translate_for_string = pc_translate_v8;
  2011. } else {
  2012. gnusto_error(170, 'impossible: unknown z-version got this far');
  2013. }
  2014. // And pick up the relevant instruction set.
  2015. if (!(this.m_version in handlers_fixups)) {
  2016. gnusto_error(311, 'unknown z-machine version');
  2017. }
  2018. var fixups = handlers_fixups[this.m_version];
  2019. switch (typeof(fixups)) {
  2020. case 'undefined':
  2021. gnusto_error(101, 'z-machine version not implemented');
  2022. break;
  2023. case 'string':
  2024. this.m_handlers = handlers_v578;
  2025. break;
  2026. case 'object':
  2027. this.m_handlers = {};
  2028. for (var original in handlers_v578) {
  2029. this.m_handlers[original] = handlers_v578[original];
  2030. }
  2031. for (var changed in fixups) {
  2032. if ((typeof fixups[changed])=='function') {
  2033. this.m_handlers[changed] = fixups[changed];
  2034. } else {
  2035. delete this.m_handlers[changed];
  2036. }
  2037. }
  2038. break;
  2039. default:
  2040. gnusto_error(170, 'impossible: weird stuff in fixups table');
  2041. }
  2042. // Set up separators.
  2043. this.m_separator_count = this.m_memory[this.m_dict_start];
  2044. for (var i=0; i<this.m_separator_count; i++) {
  2045. this.m_separators[i]=this._zscii_char_to_ascii(this.m_memory[this.m_dict_start + i+1]);
  2046. }
  2047. // If there is a header extension...
  2048. if (this.m_hext_start > 0) {
  2049. // get start of custom unicode table, if any
  2050. this.m_unicode_start = this.getUnsignedWord(this.m_hext_start+6);
  2051. if (this.m_unicode_start > 0) { // if there is one, get the char count-- characters beyond that point are undefined.
  2052. this.m_custom_unicode_charcount = this.m_memory[this.m_unicode_start];
  2053. this.m_unicode_start += 1;
  2054. // Populate reverse lookup table
  2055. for(var i=0; i<this.m_custom_unicode_charcount; i++)
  2056. reverse_unicode_table[this.getUnsignedWord(this.m_unicode_start + (i*2))] = i + 155;
  2057. }
  2058. }
  2059. if(!(this.m_unicode_start>0)) {
  2060. // The game doesn't provide its own set of unicode characters, so
  2061. // now is the time to populate the reverse_unicode_table with the
  2062. // default unicode characters
  2063. for(var i in default_unicode_translation_table)
  2064. reverse_unicode_table[default_unicode_translation_table[i]] = i;
  2065. }
  2066. this.m_rebound = 0;
  2067. this.m_rebound_args = [];
  2068. this.m_output_to_console = 1;
  2069. this.m_streamthrees = [];
  2070. this.m_output_to_script = 0;
  2071. this.m_console_buffer = '';
  2072. this.m_transcript_buffer = '';
  2073. this.m_zalphabet[0] = 'abcdefghijklmnopqrstuvwxyz';
  2074. this.m_zalphabet[1] = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  2075. // T = magic ten bit flag
  2076. if (this.m_version==1) {
  2077. this.m_zalphabet[2] = 'T0123456789.,!?_#\'"/\\<-:()';
  2078. } else {
  2079. this.m_zalphabet[2] = 'T\n0123456789.,!?_#\'"/\\-:()';
  2080. }
  2081. var newchar;
  2082. var newcharcode;
  2083. if (this.m_alpha_start > 0) { // If there's a custom alphabet...
  2084. for (var alpharow=0; alpharow<3; alpharow++){
  2085. var alphaholder = '';
  2086. for (var alphacol=0; alphacol<26; alphacol++) {
  2087. newcharcode = this.m_memory[this.m_alpha_start + (alpharow*26) + alphacol];
  2088. if ((newcharcode >=155) && (newcharcode <=251)) {
  2089. // Yes, custom alphabets can refer to custom unicode tables. Whee...
  2090. if (this.m_unicode_start == 0) {
  2091. alphaholder += String.fromCharCode(default_unicode_translation_table[newcharcode]);
  2092. } else {
  2093. if ((newcharcode-154)<= this.m_custom_unicode_charcount)
  2094. alphaholder += String.fromCharCode(this.getUnsignedWord(this.m_unicode_start + ((newcharcode-155)*2)));
  2095. else
  2096. alphaholder += ' ';
  2097. }
  2098. } else {
  2099. newchar = String.fromCharCode(newcharcode);
  2100. if (newchar == '^') newchar = '\n'; // This is hackish, but I don't know a better way.
  2101. alphaholder += newchar;
  2102. }
  2103. }
  2104. this.m_zalphabet[alpharow]= alphaholder; // Replace the current row with the newly constructed one.
  2105. }
  2106. }
  2107. // We don't also reset the debugging variables, because
  2108. // they need to persist across re-creations of this object.
  2109. // FIXME: Is this still true?
  2110. // Clear the Z-engine's local variables.
  2111. for (var i=0; i<16; i++) this.m_locals[i]=0;
  2112. this.m_printing_header_bits = 0;
  2113. this.m_leftovers = '';
  2114. // Set some header variables
  2115. this.m_memory[0x1E] = 1; // DEC
  2116. this.m_memory[0x1F] = 0; // no letter ('F' would indicate that we're frotz, which may not be desirable)
  2117. // set in runner.js in run();
  2118. // this.m_memory[0x20] = 25; // screen height (255 = infinite)
  2119. // this.m_memory[0x21] = 80; // screen width in characters ('0')
  2120. // this.setWord(80, 0x22); // screen width in 'units'
  2121. // this.setWord(25, 0x24);
  2122. this.m_memory[1] |= 0x1D; // announce support for styled text and color
  2123. this.m_memory[0x26] = 1; // font width/height (dep. version) in 'units'
  2124. this.m_memory[0x27] = 1; // font width/height (dep. version) in 'units'
  2125. // Z Machine Spec version
  2126. // For now only set 1.2 if PARCHMENT_SECURITY_OVERRIDE is set
  2127. this.m_memory[0x32] = 1;
  2128. this.m_memory[0x33] = PARCHMENT_SECURITY_OVERRIDE ? 2 : 0;
  2129. },
  2130. // Inlined some of these functions...
  2131. _unsigned2signed: function ge_unsigned2signed(value) {
  2132. // The argument must be between 0 and 0xFFFF!
  2133. // The return value will be signed (-8000..7FFF).
  2134. return ((value & 0x8000)?~0xFFFF:0)|value;
  2135. },
  2136. _signed2unsigned: function ge_signed2unsigned(value) {
  2137. return value & 0xFFFF;
  2138. },
  2139. // Note that getByte/getWord can read any memory address. setByte/setWord
  2140. // only work on RAM addresses.
  2141. getByte: function ge_getbyte(address) {
  2142. if (this.m_value_asserts) {
  2143. if (address == null || address === true || address === false || address < 0 || address >= this.m_original_memory.length)
  2144. this.logger('getByte addr', address);
  2145. var val = this.m_memory[address];
  2146. if (val == null || val === true || val === false || val < 0 || val > 0xFF)
  2147. this.logger('getByte byte', val);
  2148. }
  2149. return this.m_memory[address];
  2150. },
  2151. setByte: function ge_setByte(value, address) {
  2152. // The value is safely truncated, but the address must be valid
  2153. if (this.m_value_asserts) {
  2154. if (address == null || address === true || address === false || address < 0 || address >= this.m_stat_start)
  2155. this.logger('setByte addr', address);
  2156. }
  2157. this.m_memory[address] = value & 0xFF;
  2158. },
  2159. getWord: function ge_getWord(address) {
  2160. // The return value will be signed (-8000..7FFF).
  2161. if (this.m_value_asserts) {
  2162. if (address == null || address === true || address === false || address < 0 || address >= this.m_original_memory.length)
  2163. this.logger('getWord addr', address);
  2164. var val = this.m_memory[address];
  2165. if (val == null || val === true || val === false || val < 0 || val > 0xFF)
  2166. this.logger('getWord high byte', val);
  2167. var val = this.m_memory[address+1];
  2168. if (val == null || val === true || val === false || val < 0 || val > 0xFF)
  2169. this.logger('getWord low byte', val);
  2170. }
  2171. // return this._unsigned2signed((this.m_memory[address]<<8)|
  2172. // this.m_memory[address+1]);
  2173. var value = (this.m_memory[address] << 8) | this.m_memory[address + 1];
  2174. return ((value & 0x8000) ? ~0xFFFF : 0) | value;
  2175. },
  2176. getUnsignedWord: function ge_getUnsignedWord(address) {
  2177. if (this.m_value_asserts) {
  2178. if (address == null || address === true || address === false || address < 0 || address >= this.m_original_memory.length)
  2179. this.logger('getUnsignedWord addr', address);
  2180. var val = this.m_memory[address];
  2181. if (val == null || val === true || val === false || val < 0 || val > 0xFF)
  2182. this.logger('getUnsignedWord high byte', val);
  2183. var val = this.m_memory[address+1];
  2184. if (val == null || val === true || val === false || val < 0 || val > 0xFF)
  2185. this.logger('getUnsignedWord low byte', val);
  2186. }
  2187. return (this.m_memory[address]<<8)|this.m_memory[address+1];
  2188. },
  2189. setWord: function ge_setWord(value, address) {
  2190. // The value is safely truncated, but the address must be valid
  2191. if (this.m_value_asserts) {
  2192. if (address == null || address === true || address === false || address < 0 || address >= this.m_stat_start)
  2193. this.logger('setWord', address);
  2194. }
  2195. // this.setByte((value>>8) & 0xFF, address);
  2196. this.m_memory[address] = (value >> 8) & 0xFF;
  2197. // this.setByte((value) & 0xFF, address+1);
  2198. this.m_memory[address + 1] = (value) & 0xFF;
  2199. },
  2200. // Inelegant function to load parameters according to a VAR byte (or word).
  2201. _handle_variable_parameters: function ge_handle_var_parameters(args, types, bytecount) {
  2202. var argcursor = 0, code = '', varcode;
  2203. if (bytecount==1) {
  2204. types = (types<<8) | 0xFF;
  2205. }
  2206. while (1) {
  2207. var current = types & 0xC000;
  2208. if (current==0xC000) {
  2209. return code;
  2210. } else if (current==0x0000) {
  2211. args[argcursor++] = this.getUnsignedWord(this.m_pc);
  2212. this.m_pc+=2;
  2213. } else if (current==0x4000) {
  2214. args[argcursor++] = this.m_memory[this.m_pc++];
  2215. } else if (current==0x8000) {
  2216. // args[argcursor++] = this._code_for_varcode(this.m_memory[this.m_pc++]);
  2217. varcode = this._code_for_varcode(this.m_memory[this.m_pc++]);
  2218. code += varcode[0];
  2219. args[argcursor++] = varcode[1];
  2220. // } else {
  2221. // gnusto_error(171); // impossible
  2222. }
  2223. types = (types << 2) | 0x3;
  2224. }
  2225. },
  2226. // _compile() returns a string of JavaScript code representing the
  2227. // instruction at the program counter (and possibly the next few
  2228. // instructions, too). It will change the PC to point to the end of the
  2229. // code it's compiled.
  2230. _compile: function ge_compile() {
  2231. this.m_compilation_running = 1;
  2232. var code = '', starting_pc = this.m_pc, varcode, funcname;
  2233. // Counter for naming any temporary variables that we create.
  2234. // var temp_var_counter = 0;
  2235. temp_var = 0;
  2236. do {
  2237. // List of arguments to the opcode.
  2238. var args = [];
  2239. /* DEBUG */
  2240. var this_instr_pc = this.m_pc;
  2241. if ( this_instr_pc == null || this_instr_pc < 0 || this_instr_pc >= this.m_original_memory.length )
  2242. gnusto_error(206, this_instr_pc);
  2243. /* ENDDEBUG */
  2244. // Add the touch (see bug 4687). This lets us track progress simply.
  2245. // touch() has a huge overhead and without tracing there's no need for it. Whether this replacement is even useful is a good question...
  2246. // code = code + '_touch('+this.m_pc+');';
  2247. // code = code + 'm_pc = ' + this.m_pc + ';';
  2248. // So here we go...
  2249. // what's the opcode?
  2250. var instr = this.m_memory[this.m_pc++];
  2251. if (instr==0) {
  2252. // If we just get a zero, we've probably
  2253. // been directed off into deep space somewhere.
  2254. gnusto_error(201); // lost in space
  2255. } else if (instr==190) { // Extended opcode.
  2256. instr = 1000+this.m_memory[this.m_pc++];
  2257. code += this._handle_variable_parameters(args, this.m_memory[this.m_pc++], 1);
  2258. } else if (instr & 0x80) {
  2259. if (instr & 0x40) { // Variable params
  2260. if (!(instr & 0x20))
  2261. // This is a 2-op, despite having
  2262. // variable parameters; reassign it.
  2263. instr &= 0x1F;
  2264. if (instr==250 || instr==236) {
  2265. // We get more of them!
  2266. var types = this.getUnsignedWord(this.m_pc);
  2267. this.m_pc += 2;
  2268. code += this._handle_variable_parameters(args, types, 2);
  2269. } else
  2270. code += this._handle_variable_parameters(args, this.m_memory[this.m_pc++], 1);
  2271. } else { // Short. All 1-OPs except for one 0-OP.
  2272. switch(instr & 0x30) {
  2273. case 0x00:
  2274. args[0] = this.getUnsignedWord(this.m_pc);
  2275. this.m_pc+=2;
  2276. instr = (instr & 0x0F) | 0x80;
  2277. break;
  2278. case 0x10:
  2279. args[0] = this.m_memory[this.m_pc++];
  2280. instr = (instr & 0x0F) | 0x80;
  2281. break;
  2282. case 0x20:
  2283. // args[0] = this._code_for_varcode(this.m_memory[this.m_pc++]);
  2284. varcode = this._code_for_varcode(this.m_memory[this.m_pc++]);
  2285. code += varcode[0];
  2286. args[0] = varcode[1];
  2287. instr = (instr & 0x0F) | 0x80;
  2288. break;
  2289. case 0x30:
  2290. // 0-OP. We don't need to get parameters, but we
  2291. // *do* need to translate the opcode.
  2292. instr = (instr & 0x0F) | 0xB0;
  2293. break;
  2294. }
  2295. }
  2296. } else { // Long
  2297. if (instr & 0x40)
  2298. {
  2299. // args[0] = this._code_for_varcode(this.m_memory[this.m_pc++]);
  2300. varcode = this._code_for_varcode(this.m_memory[this.m_pc++]);
  2301. code += varcode[0];
  2302. args[0] = varcode[1];
  2303. }
  2304. else
  2305. args[0] = this.m_memory[this.m_pc++];
  2306. if (instr & 0x20)
  2307. {
  2308. // args[1] = this._code_for_varcode(this.m_memory[this.m_pc++]);
  2309. varcode = this._code_for_varcode(this.m_memory[this.m_pc++]);
  2310. code += varcode[0];
  2311. args[1] = varcode[1];
  2312. }
  2313. else
  2314. args[1] = this.m_memory[this.m_pc++];
  2315. instr &= 0x1F;
  2316. }
  2317. /***
  2318. // We need to ensure that arguments are popped from the
  2319. // stack in the right order, regardless of the order in
  2320. // which code in JITspace uses them; so here we figure out
  2321. // what arguments are taken from the stack and evaluate them
  2322. // into temporary variables before calling the generated JIT
  2323. // code for the instruction. See issue 43 for more information:
  2324. //
  2325. // http://code.google.com/p/parchment/issues/detail?id=43
  2326. for (var i = 0; i < args.length; i++)
  2327. if (args[i] == ARG_STACK_POP) {
  2328. var temp_var_name = "tmp_" + temp_var_counter++;
  2329. code += "var " + temp_var_name + " = m_gamestack.pop();";
  2330. args[i] = temp_var_name;
  2331. }
  2332. ***/
  2333. // Output the instruction number
  2334. //code = code + '/*' + instr + '*/';
  2335. if (this.m_handlers[instr]) {
  2336. code = code + this.m_handlers[instr](this, args)+';';
  2337. // NOTE: insert this.logger here to debug a particular opcode
  2338. } else if (instr>=1128 && instr<=1255 &&
  2339. "special_instruction_EXT"+(instr-1000) in this) {
  2340. // ZMSD 14.2: We provide a hook for plug-in instructions.
  2341. // FIXME: This will no longer work in a component.
  2342. // Can we do anything else instead?
  2343. // (Maybe a component named @gnusto.org/specialinstr?op=XXX.)
  2344. code = code +
  2345. this["special_instruction_EXT"+(instr-1000)](args)+
  2346. ';';
  2347. } else {
  2348. gnusto_error(200,
  2349. this.m_pc.toString(16)); // no handler
  2350. }
  2351. } while(this.m_compilation_running);
  2352. // When we're not in debug mode, dissembly only stops at places where
  2353. // the THIS.M_PC must be reset; but in debug mode it's perfectly possible
  2354. // to have |code| not read or write to the PC at all. So we need to
  2355. // set it automatically at the end of each fragment.
  2356. if (this.m_single_step||this.m_debug_mode) {
  2357. code = code + 'm_pc='+this.m_pc;
  2358. }
  2359. // Code optimisations
  2360. // Don't push() and pop(), just set variables directly
  2361. code = code.replace(/m_gamestack\.push\(([^;]+)\);var tmp_(\d+) = m_gamestack\.pop\(\);/, 'var tmp_$2 = $1;');
  2362. // Name the function after the starting position, to make life easier for debuggers
  2363. funcname = 'function JIT_' + starting_pc.toString(16) + '_' + starting_pc;
  2364. // If we have function names append them
  2365. ;;; var find_func_name = function(pc) { while ( !vm_functions[pc] && pc > 0 ) { pc--; } return vm_functions[pc]; };
  2366. ;;; funcname = funcname + ( window.vm_functions ? '_' + find_func_name(starting_pc) : '' );
  2367. return funcname + '(){' + code + '}';
  2368. },
  2369. _param_count: function ge_param_count() {
  2370. return this.m_param_counts[0];
  2371. },
  2372. _set_output_stream: function ge_set_output_stream(target, address) {
  2373. target = this._unsigned2signed(target);
  2374. if (target==0) {
  2375. // then it's a no-op.
  2376. } else if (target==1) {
  2377. this.m_output_to_console = 1;
  2378. } else if (target==2) {
  2379. this.m_memory[0x11] |= 0x1;
  2380. } else if (target==3) {
  2381. if (this.m_streamthrees.length>15) {
  2382. gnusto_error(202); // too many nested stream-3s
  2383. }
  2384. this.m_streamthrees.unshift([address, address+2]);
  2385. } else if (target==4) {
  2386. this.m_output_to_script = 1;
  2387. } else if (target==-1) {
  2388. this.m_output_to_console = 0;
  2389. } else if (target==-2) {
  2390. this.m_memory[0x11] &= ~0x1;
  2391. } else if (target==-3) {
  2392. if (this.m_streamthrees.length<1) {
  2393. gnusto_error(203); // not enough nested stream-3s
  2394. }
  2395. var latest = this.m_streamthrees.shift();
  2396. this.setWord((latest[1]-latest[0])-2, latest[0]);
  2397. } else if (target==-4) {
  2398. this.m_output_to_script = 0;
  2399. } else {
  2400. gnusto_error(204, target); // weird output stream number
  2401. }
  2402. },
  2403. _trunc_divide: function ge_trunc_divide(over, under) {
  2404. var result;
  2405. over = this._unsigned2signed(over);
  2406. under = this._unsigned2signed(under);
  2407. if (under==0) {
  2408. gnusto_error(701); // division by zero
  2409. return 0;
  2410. }
  2411. result = over / under;
  2412. if (result > 0) {
  2413. return Math.floor(result) & 0xFFFF;
  2414. } else {
  2415. return Math.ceil(result) & 0xFFFF;
  2416. }
  2417. },
  2418. _trunc_modulo: function ge_trunc_modulo(over, under) {
  2419. over = this._unsigned2signed(over);
  2420. under = this._unsigned2signed(under);
  2421. if (under==0) {
  2422. gnusto_error(701); // division by zero
  2423. return 0;
  2424. }
  2425. return (over % under) & 0xFFFF;
  2426. },
  2427. _zscii_char_to_ascii: function ge_zscii_char_to_ascii(zscii_code) {
  2428. if (zscii_code < 0) {
  2429. gnusto_error(702, zscii_code); // illegal zscii code
  2430. }
  2431. var result;
  2432. if ( zscii_code == 0 )
  2433. {
  2434. return '';
  2435. }
  2436. else if ( zscii_code == 10 || zscii_code == 13 )
  2437. {
  2438. return '\n';
  2439. }
  2440. else if ((zscii_code>=32 && zscii_code<=126) || zscii_code==0) {
  2441. result = zscii_code;
  2442. } else if (zscii_code>=155 && zscii_code<=251) {
  2443. // Extra characters.
  2444. if (this.m_unicode_start == 0)
  2445. return String.fromCharCode(default_unicode_translation_table[zscii_code]);
  2446. else { // if we're using a custom unicode translation table...
  2447. if ((zscii_code-154)<= this.m_custom_unicode_charcount)
  2448. return String.fromCharCode(this.getUnsignedWord(this.m_unicode_start + ((zscii_code-155)*2)));
  2449. else
  2450. gnusto_error(703, zscii_code); // unknown zscii code
  2451. }
  2452. // FIXME: It's not clear what to do if they request a character
  2453. // that's off the end of the table.
  2454. } else {
  2455. //let's do nothing for the release-- we'll check the spec afterwards.
  2456. // FIXME: what release was that, and what are we doing now?
  2457. // Is there anything in Bugzilla to track this?
  2458. return "*";//gnusto_error(703, zscii_code); // unknown zscii code
  2459. }
  2460. return String.fromCharCode(result);
  2461. },
  2462. _ascii_code_to_zscii_code: function ge_ascii_char_to_zscii( ascii_code ) {
  2463. // ZSCII code 13 must be used for the enter key
  2464. // Correct for the arrow keys
  2465. var ZSCII_corrections = {
  2466. 10: 13, // Enter
  2467. 13: 13,
  2468. 37: 131, // Left
  2469. 38: 129, // Up
  2470. 39: 132, // Right
  2471. 40: 130 // Down
  2472. };
  2473. // Are we converting a char input event?
  2474. if ( isNaN( ascii_code ) )
  2475. {
  2476. // Correct for some ZSCII differences
  2477. if ( ascii_code.keyCode && ZSCII_corrections[ascii_code.keyCode] )
  2478. {
  2479. return ZSCII_corrections[ascii_code.keyCode];
  2480. }
  2481. else
  2482. {
  2483. var ascii_code = ascii_code.charCode;
  2484. }
  2485. }
  2486. // Return linefeeds correctly
  2487. if ( ascii_code == 10 || ascii_code == 13 )
  2488. return 13;
  2489. // Standard ASCII characters, except for the arrow keys, plus NULL
  2490. if ( ( ascii_code > 31 && ascii_code < 127 ) || ascii_code == 0 )
  2491. {
  2492. // Most common case - keep it as fast as possible
  2493. return ascii_code;
  2494. }
  2495. if (ascii_code < 0) {
  2496. gnusto_error(702, 'Illegal unicode character:' + ascii_code); // illegal ascii code
  2497. }
  2498. // Must be among extra characters.
  2499. var result = reverse_unicode_table[ascii_code];
  2500. if(!result) {
  2501. // gnusto_error(703, 'No ZSCII equivalent found for this unicode character code: ' + ascii_code); // unknown ascii code
  2502. // Let's translate it into '*' for now. Should we raise an error instead?
  2503. result = 42;
  2504. }
  2505. return result;
  2506. },
  2507. _random_number: function ge_random_number(arg) {
  2508. arg = this._unsigned2signed(arg);
  2509. if (arg==0) {
  2510. // zero returns to true random mode-- seed from system clock
  2511. this.m_random_use_seed = this.m_random_use_sequence = 0;
  2512. return 0;
  2513. } else if (arg<-999) {
  2514. // Large negative numbers cause us to enter a predictable
  2515. // but non-sequential state. (In other words, the numbers
  2516. // always come in the same order, but can't be trivially
  2517. // predicted by humans.)
  2518. this.m_random_state = Math.abs(arg);
  2519. this.m_random_use_seed = 1;
  2520. this.m_random_use_sequence = 0;
  2521. return 0;
  2522. } else if (arg<0) {
  2523. // Small negative numbers cause us to enter a predictable sequential
  2524. // state: according to the spec, 1, 2, 3 ... arg, 1, 2, 3...
  2525. // (but according to Frotz, 1, 2, 3... arg-1, 1, 2, 3...)
  2526. // BTW, the spec says "lower than 1000", but this is clearly
  2527. // an error, because *all* negative numbers are lower than
  2528. // 1000. We follow Frotz's lead in treating this as -1000
  2529. // and using the absolute value of the argument as the
  2530. // sequence wrapping point.
  2531. this.m_random_sequence_max = Math.abs(arg)-1;
  2532. this.m_random_state = 0;
  2533. this.m_random_use_seed = 0;
  2534. this.m_random_use_sequence = 1;
  2535. return 0;
  2536. } else {
  2537. // Positive argument. So they actually want a random number,
  2538. // between 1 and arg inclusive.
  2539. // Are we using any sort of predictable seeding?
  2540. if (this.m_random_use_seed) {
  2541. // Yes, given a particular seed.
  2542. this.m_random_state--;
  2543. return 1+(Math.round(Math.abs(Math.tan(this.m_random_state))*8.71*arg)%arg);
  2544. } else if (this.m_random_use_sequence) {
  2545. // Yes, given a particular sequence.
  2546. var previous = this.m_random_state;
  2547. this.m_random_state = this.m_random_state+1;
  2548. if (this.m_random_state > this.m_random_sequence_max) {
  2549. this.m_random_state = 0;
  2550. }
  2551. return 1 + (previous % arg);
  2552. } else {
  2553. // No. Use JS's random numbers.
  2554. // (Hope these are generally good enough.)
  2555. return 1 + Math.round((arg -1) * Math.random());
  2556. }
  2557. }
  2558. gnusto_error(170, 'random'); // impossible
  2559. },
  2560. ////////////////////////////////////////////////////////////////
  2561. //
  2562. // _func_gosub
  2563. //
  2564. // Jumps to a subroutine within the Z-code, saving the current
  2565. // state so that calling _func_return() will return to it.
  2566. //
  2567. // |to_address| -- address within the Z-code to jump to
  2568. // |actuals| -- list of actual parameters
  2569. // |from_address| -- source address
  2570. // |result_target| -- varcode for where to put the result
  2571. //
  2572. _func_gosub: function ge_gosub(to_address, actuals, from_address, result_target) {
  2573. this.m_call_stack.push(from_address);
  2574. this.m_pc = to_address;
  2575. var count = this.m_memory[this.m_pc++];
  2576. // Before version 5, Z-code put initial values for formal parameters
  2577. // into the code itself. If we're running a version earlier than z5,
  2578. // we have to interpret these.
  2579. if (this.m_version<5) {
  2580. var templocals = [];
  2581. for (var i3=0; i3<count; i3++) {
  2582. if (i3<actuals.length) {
  2583. templocals.push(actuals[i3]);
  2584. } else {
  2585. templocals.push(this.getUnsignedWord(this.m_pc));
  2586. }
  2587. this.m_pc += 2;
  2588. }
  2589. this.m_locals = templocals.concat(this.m_locals);
  2590. } else {
  2591. for (var i5=count; i5>0; i5--) {
  2592. if (i5<=actuals.length) {
  2593. this.m_locals.unshift(actuals[i5-1]);
  2594. } else {
  2595. this.m_locals.unshift(0);
  2596. }
  2597. }
  2598. }
  2599. this.m_locals_stack.unshift(count);
  2600. this.m_param_counts.unshift(actuals.length);
  2601. this.m_result_targets.push(result_target);
  2602. this.m_gamestack_callbreaks.push(this.m_gamestack.length);
  2603. if (to_address==0) {
  2604. // Rare special case: a call to 0 returns only false.
  2605. this._func_return(0);
  2606. }
  2607. },
  2608. ////////////////////////////////////////////////////////////////
  2609. //
  2610. // _func_interrupt
  2611. //
  2612. // Like _func_gosub, except that it's used to break into a
  2613. // running routine. This may only be called from a rebound function.
  2614. //
  2615. // |to_address| -- address of Z-code interrupt service routine.
  2616. // |on_return| -- function to call when the routine is finished.
  2617. //
  2618. // |on_return| will be called with two parameters:
  2619. // |info| : an object containing these fields:
  2620. // |rebound|: saved value of m_rebound
  2621. // |pc|: saved value of m_pc
  2622. // |result|: the value the ISR returned.
  2623. //
  2624. _func_interrupt: function ge_interrupt(to_address, on_return) {
  2625. this.m_interrupt_information.push({
  2626. 'on_return': on_return,
  2627. 'rebound': this.m_rebound,
  2628. 'rebound_args': this.m_rebound_args,
  2629. 'engine': this,
  2630. 'pc': this.m_pc,
  2631. 'effects': this.m_effects
  2632. });
  2633. this._func_gosub(to_address, [],
  2634. CALLED_FROM_INTERRUPT,
  2635. -1);
  2636. },
  2637. ////////////////////////////////////////////////////////////////
  2638. //
  2639. // Tokenises a string.
  2640. //
  2641. // See aread() for caveats.
  2642. // Maybe we should allow aread() to pass in the correct value stored
  2643. // in text_buffer, since it knows it already. It means we don't have
  2644. // to figure it out ourselves.
  2645. //
  2646. _tokenise: function ge_tokenise(text_buffer, parse_buffer, dictionary, overwrite) {
  2647. var tokenised_word_count = 0;
  2648. var cursor = parse_buffer + 2;
  2649. var words_count_addr = parse_buffer + 1;
  2650. if (isNaN(dictionary)) dictionary = 0;
  2651. if (isNaN(overwrite)) overwrite = 0;
  2652. function look_up(engine, word, dict_addr) {
  2653. function compare(engine, typed, mem_addr) {
  2654. var j=0;
  2655. var mem_char, typed_char;
  2656. while (1) {
  2657. if (j==typed.length) {
  2658. // then they're the same
  2659. return 0;
  2660. }
  2661. mem_char = engine.m_memory[mem_addr+j];
  2662. typed_char = typed.charCodeAt(j);
  2663. if (mem_char==typed_char) {
  2664. j++;
  2665. } else if (mem_char<typed_char) {
  2666. // less than...
  2667. return -1;
  2668. } else {
  2669. return 1;
  2670. }
  2671. }
  2672. }
  2673. var entry_length = engine.m_memory[dict_addr+engine.m_separator_count+1];
  2674. var entries_count = engine.getWord(dict_addr+engine.m_separator_count+2);
  2675. var entries_start = engine.m_dict_start+engine.m_separator_count+4;
  2676. // Whether the dictionary is sorted.
  2677. var is_sorted = 1;
  2678. if (entries_count < 0) {
  2679. // This should actually only happen on user dictionaries,
  2680. // but the distinction isn't a useful one, and so we don't
  2681. // bother to check.
  2682. is_sorted = 0;
  2683. entries_count = -entries_count;
  2684. }
  2685. var oldword = word;
  2686. word = engine._into_zscii(word);
  2687. if (is_sorted) {
  2688. var low=0, high=entries_count-1;
  2689. var median;
  2690. var median_address;
  2691. var comparison;
  2692. while(1) {
  2693. median = low + Math.round((high-low)/2);
  2694. median_address = entries_start+median*entry_length;
  2695. comparison = compare(engine, word, median_address);
  2696. if (comparison<0) {
  2697. if (low==high) { return 0; }
  2698. low = median+1;
  2699. } else if (comparison>0) {
  2700. if (low==high) { return 0; }
  2701. high = median-1;
  2702. } else {
  2703. return median_address;
  2704. }
  2705. if (low>high) {
  2706. return 0;
  2707. }
  2708. }
  2709. } else {
  2710. // Unsorted search. Much simpler, but slower
  2711. for (var i=0; i<entries_count; i++) {
  2712. var address = entries_start+i*entry_length;
  2713. if (compare(engine, word, address)==0) {
  2714. return address;
  2715. }
  2716. }
  2717. }
  2718. return 0;
  2719. }
  2720. function add_to_parse_table(engine, dictionary, curword, wordpos) {
  2721. var lexical = look_up(engine, curword, dictionary);
  2722. if (!(overwrite && lexical==0)) {
  2723. engine.setWord(lexical, cursor);
  2724. cursor+=2;
  2725. engine.setByte(curword.length, cursor++);
  2726. engine.setByte(wordpos, cursor++);
  2727. } else {
  2728. // In overwrite mode, if we don't know a word, we skip
  2729. // the corresponding record.
  2730. cursor +=4;
  2731. }
  2732. tokenised_word_count++;
  2733. return 1;
  2734. }
  2735. ////////////////////////////////////////////////////////////////
  2736. //
  2737. // Prepare |source|, a string containing all the characters in
  2738. // text_buffer. (FIXME: Why don't we just work out of text_buffer?)
  2739. var max_chars = this.m_memory[text_buffer];
  2740. var source = '';
  2741. if (dictionary==0) {
  2742. // Use the standard game dictionary.
  2743. dictionary = this.m_dict_start;
  2744. }
  2745. if (this.m_version <= 4) {
  2746. max_chars ++; // Value stored in pre-z5 is one too low.
  2747. var copycursor = text_buffer + 1;
  2748. while(1) {
  2749. var ch = this.m_memory[copycursor++];
  2750. if (ch==0) break;
  2751. source += String.fromCharCode(ch);
  2752. }
  2753. } else {
  2754. for (var i=0;i<this.m_memory[text_buffer + 1];i++) {
  2755. source += String.fromCharCode(this.m_memory[text_buffer + 2 + i]);
  2756. }
  2757. }
  2758. var words = [];
  2759. var curword = '';
  2760. var wordindex = 0;
  2761. var wordpos_increment;
  2762. if (this.m_version <= 4) {
  2763. wordpos_increment = 1;
  2764. } else {
  2765. wordpos_increment = 2;
  2766. }
  2767. // FIXME: Do this with regexps, for goodness' sake.
  2768. for (var cpos=0; cpos < source.length; cpos++) {
  2769. if (source.charAt(cpos) == ' ') {
  2770. if (curword != '') {
  2771. words[wordindex] = curword;
  2772. add_to_parse_table(this, dictionary, words[wordindex],
  2773. (cpos - words[wordindex].length) + wordpos_increment);
  2774. wordindex++;
  2775. curword = '';
  2776. }
  2777. } else {
  2778. if (this._is_separator(source.charAt(cpos))) {
  2779. if (curword != '') {
  2780. words[wordindex] = curword;
  2781. add_to_parse_table(this, dictionary, words[wordindex],
  2782. (cpos - words[wordindex].length) + wordpos_increment);
  2783. wordindex++;
  2784. }
  2785. words[wordindex] = source.charAt(cpos);
  2786. add_to_parse_table(this, dictionary, words[wordindex],
  2787. cpos + wordpos_increment);
  2788. wordindex++;
  2789. curword = '';
  2790. } else {
  2791. curword += source.charAt(cpos);
  2792. }
  2793. }
  2794. }
  2795. if (curword != '') {
  2796. words[wordindex] = curword;
  2797. add_to_parse_table(this, dictionary, words[wordindex],
  2798. (cpos - words[wordindex].length) + wordpos_increment);
  2799. }
  2800. this.setByte(tokenised_word_count, words_count_addr);
  2801. },
  2802. // Very very very limited implementation:
  2803. // * Doesn't handle word separators. (FIXME: Does it yet?)
  2804. // FIXME: Consider having no parameters; they're always filled in from
  2805. // the same fields anyway.
  2806. _aread: function ge_aread(terminating_keypress, text_buffer, parse_buffer, entered) {
  2807. text_buffer &= 0xFFFF;
  2808. parse_buffer &= 0xFFFF;
  2809. var max_chars;
  2810. var result;
  2811. var storage;
  2812. if (this.m_version <= 4) {
  2813. // In z1-z4, the array is null-terminated.
  2814. max_chars = this.m_memory[text_buffer]+1;
  2815. result = entered.substring(0,max_chars).toLowerCase();
  2816. storage = text_buffer + 1;
  2817. this.setByte(0, text_buffer + 1 + result.length);
  2818. } else {
  2819. // In z5-z8, the array starts with a size byte.
  2820. max_chars = this.m_memory[text_buffer];
  2821. result = entered.substring(0,max_chars).toLowerCase();
  2822. storage = text_buffer + 2;
  2823. this.setByte(result.length, text_buffer + 1);
  2824. }
  2825. // Turn into ZSCII and store in text buffer
  2826. for (var i=0;i<result.length;i++) {
  2827. this.setByte(this._ascii_code_to_zscii_code(result.charCodeAt(i)), storage + i);
  2828. }
  2829. if (parse_buffer!=0 || this.m_version<5) {
  2830. this._tokenise(text_buffer, parse_buffer, 0, 0);
  2831. }
  2832. if (terminating_keypress == 13) {
  2833. return 10; // goodness knows why, but it's in the spec
  2834. } else {
  2835. return terminating_keypress;
  2836. }
  2837. },
  2838. // Returns a list of current terminating characters.
  2839. // ASCII 13 will always be in the list, since the Enter key
  2840. // is always a terminating character.
  2841. _terminating_characters: function ge_terminating_characters() {
  2842. if (this.m_version < 5) {
  2843. // Versions before z5 don't have terminating character tables.
  2844. return '\r';
  2845. } else {
  2846. var terms_address = this.getUnsignedWord(0x2e);
  2847. var result = '\r';
  2848. while(1) {
  2849. var ch = this.m_memory[terms_address++];
  2850. if (ch==0) {
  2851. // Zero is a terminator.
  2852. break;
  2853. } else if ((ch>=129 && ch<=154) || (ch>=252)) {
  2854. // Only function-key codes make it into the string.
  2855. result += String.fromCharCode(ch);
  2856. }
  2857. }
  2858. return result;
  2859. }
  2860. },
  2861. ////////////////////////////////////////////////////////////////
  2862. //
  2863. // _func_return
  2864. //
  2865. // Returns from a Z-code routine.
  2866. //
  2867. // |value| -- the numeric result of the routine.
  2868. // It can also be null, in which case the store
  2869. // won't happen (useful for returning from @throw).
  2870. //
  2871. _func_return: function ge_func_return(value) {
  2872. // Remove this function's locals
  2873. this.m_locals = this.m_locals.slice(this.m_locals_stack.shift());
  2874. this.m_param_counts.shift();
  2875. this.m_pc = this.m_call_stack.pop();
  2876. // Force the gamestack to be the length it was when this
  2877. // routine started. (ZMSD 6.3.2.)
  2878. this.m_gamestack.length = this.m_gamestack_callbreaks.pop();
  2879. var target = this.m_result_targets.pop();
  2880. if (target != -1 && value != null)
  2881. {
  2882. //this._varcode_set(value, target);
  2883. // Rather than calling _varcode_set() this function now accesses the variables directly
  2884. // target is interpreted as in ZSD 4.2.2:
  2885. // 0 = top of game stack
  2886. // 1-15 = local variables
  2887. // 16 up = global variables
  2888. if (target == 0)
  2889. this.m_gamestack.push(value);
  2890. else if (target < 0x10)
  2891. this.m_locals[target - 1] = value;
  2892. else
  2893. this.setWord(value, this.m_vars_start + (target - 16) * 2);
  2894. }
  2895. if (this.m_pc == CALLED_FROM_INTERRUPT) {
  2896. var interrupt_info = this.m_interrupt_information.pop();
  2897. this.m_pc = interrupt_info.pc;
  2898. interrupt_info.on_return(interrupt_info, value);
  2899. }
  2900. },
  2901. _throw_stack_frame: function throw_stack_frame(cookie) {
  2902. // The cookie is the value of call_stack.length when @catch was
  2903. // called. It cannot be less than 1 or greater than the current
  2904. // value of call_stack.length.
  2905. if (cookie>this.m_call_stack.length || cookie<1) {
  2906. gnusto_error(207, cookie);
  2907. }
  2908. while (this.m_call_stack.length > cookie-1) {
  2909. this._func_return(null);
  2910. }
  2911. },
  2912. _get_prop_addr: function ge_get_prop_addr(object, property) {
  2913. if (object==0) {return 0;}
  2914. var result = this._property_search(object, property, -1);
  2915. if (result[2]) {
  2916. return result[0];
  2917. } else {
  2918. return 0;
  2919. }
  2920. },
  2921. _get_prop_len: function ge_get_prop_len(address)
  2922. {
  2923. ;;; if (address == null || address === true || address === false || address < 0 || address >= this.m_stat_start) { this.logger('get_prop_len', address); }
  2924. // Spec 1.1 clarification: @get_prop_len 0 must return 0
  2925. if ( address == 0 )
  2926. {
  2927. return 0;
  2928. }
  2929. if (this.m_version<4) {
  2930. return 1+(this.m_memory[address-1] >> 5);
  2931. } else {
  2932. // The last byte before the data is either the size byte of a 2-byte
  2933. // field, or the only byte of a 1-byte field. We can tell the
  2934. // difference using the top bit.
  2935. var value = this.m_memory[address-1];
  2936. if (value & 0x80) {
  2937. // A two-byte field, so we take the bottom five bits.
  2938. value = value & 0x3F;
  2939. if (value==0) {
  2940. return 64;
  2941. } else {
  2942. return value;
  2943. }
  2944. } else {
  2945. // A one-byte field. Our choice rests on a single bit.
  2946. if (value & 0x40) {
  2947. return 2;
  2948. } else {
  2949. return 1;
  2950. }
  2951. }
  2952. }
  2953. },
  2954. _get_next_prop: function ge_get_next_prop(object, property) {
  2955. if (object==0) return 0; // Kill that V0EFH before it starts.
  2956. var result = this._property_search(object, -1, property);
  2957. if (result[2]) {
  2958. // There's a real property number in there;
  2959. // return it.
  2960. return result[3];
  2961. } else {
  2962. // There wasn't a valid property following the one
  2963. // we wanted. Why not?
  2964. if (result[4]) {
  2965. // Because the one we wanted was the last one.
  2966. // Tell them to go back to square one.
  2967. return 0;
  2968. } else {
  2969. // Because the one we wanted didn't exist.
  2970. // They shouldn't have asked for it: barf.
  2971. gnusto_error(205, property);
  2972. }
  2973. }
  2974. gnusto_error(173); // impossible
  2975. },
  2976. _get_prop: function ge_get_prop(object, property) {
  2977. if (object==0) return 0; // Kill that V0EFH before it starts.
  2978. var temp = this._property_search(object, property, -1);
  2979. if (this.m_value_asserts) {
  2980. if (temp[0] == null || temp[0] === true || temp[0] === false || temp[0] < 0 || temp[0] >= this.m_stat_start)
  2981. this.logger('get_prop', temp[0]);
  2982. }
  2983. if (temp[1] == 1)
  2984. return this.m_memory[temp[0]];
  2985. else if (temp[1] == 2)
  2986. {
  2987. // Inline this call to getUnsignedWord()
  2988. //return this.getUnsignedWord(temp[0]);
  2989. return (this.m_memory[temp[0]] << 8) | this.m_memory[temp[0] + 1];
  2990. }
  2991. else
  2992. {
  2993. // get_prop used on a property of the wrong length
  2994. // Christminster (and perhaps others) use this vaguely
  2995. // illegal hack to just check to see if a property exists
  2996. // for a particular object. Evidently, only Zinc throws an
  2997. // error here, so we probably shouldn't either. Also, currently
  2998. // calling gnusto_error here tanks the browser. Don't yet know
  2999. // why. So commenting it out for now.
  3000. // gnusto_error(706, object, property);
  3001. return this.getUnsignedWord(temp[0]);
  3002. }
  3003. gnusto_error(174); // impossible
  3004. },
  3005. // This is the function which does all searching of property lists.
  3006. // It takes three parameters:
  3007. // |object| -- the number of the object you're interested in
  3008. //
  3009. // The next parameters allow us to specify the property in two ways.
  3010. // If you use both, it will "or" them together.
  3011. // |property| -- the number of the property you're interested in,
  3012. // or -1 if you don't mind.
  3013. // |previous_property| -- the number of the property BEFORE the one
  3014. // you're interested in, or 0 if you want the first one,
  3015. // or -1 if you don't mind.
  3016. //
  3017. // If you specify a valid property, and the property doesn't exist, this
  3018. // function will return the default value instead (and tell you it's done so).
  3019. //
  3020. // The function returns an array with these elements:
  3021. // [0] = the property address.
  3022. // [1] = the property length.
  3023. // [2] = 1 if this property really belongs to the object, or
  3024. // 0 if it doesn't (and if it doesn't, and you've specified
  3025. // a valid |property|, then [0] and [1] will be properly
  3026. // set to defaults.)
  3027. // [3] = the number of the property.
  3028. // Equal to |property| if you specified it.
  3029. // May be -1, if |property| is -1 and [2]==0.
  3030. // [4] = a piece of state only useful to get_next_prop():
  3031. // if the object does not contain the property (i.e. if [2]==0)
  3032. // then this will be 1 if the final property was equal to
  3033. // |previous_property|, and 0 otherwise. At all other times it will
  3034. // be 0.
  3035. _property_search: function ge_property_search(object, property, previous_property) {
  3036. // Find the address of the property table.
  3037. var props_address = this.getUnsignedWord(this.m_property_list_addr_start +
  3038. object*this.m_object_size);
  3039. // Skip the property table's header.
  3040. props_address = props_address + this.m_memory[props_address]*2 + 1;
  3041. // Now loop over each property and consider it.
  3042. var previous_prop = 0;
  3043. while(1) {
  3044. var len = 1;
  3045. var prop = this.m_memory[props_address++];
  3046. if (this.m_version < 4) {
  3047. len = (prop>>5)+1;
  3048. prop = prop & 0x1F;
  3049. } else {
  3050. if (prop & 0x80) {
  3051. // Long format.
  3052. len = this.m_memory[props_address++] & 0x3F;
  3053. if (len==0) len = 64;
  3054. } else {
  3055. // Short format.
  3056. if (prop & 0x40) len = 2;
  3057. }
  3058. prop = prop & 0x3F;
  3059. }
  3060. if (prop==property || previous_prop==previous_property) {
  3061. return [props_address, len, 1, prop, 0];
  3062. } else if (prop < property) {
  3063. // So it's not there. Can we get it from the defaults?
  3064. if (property>0)
  3065. // Yes, because it's a real property.
  3066. return [this.m_objs_start + (property-1)*2,
  3067. 2, 0, property, 0];
  3068. else
  3069. // No: they didn't specify a particular
  3070. // property.
  3071. return [-1, -1, 0, property,
  3072. previous_prop==property];
  3073. }
  3074. props_address += len;
  3075. previous_prop = prop;
  3076. }
  3077. gnusto_error(175); // impossible
  3078. },
  3079. ////////////////////////////////////////////////////////////////
  3080. // Functions that modify the object tree
  3081. _set_attr: function ge_set_attr(object, bit) {
  3082. if (object==0) return; // Kill that V0EFH before it starts.
  3083. var address = this.m_object_tree_start + object*this.m_object_size + (bit>>3);
  3084. var value = this.m_memory[address];
  3085. this.setByte(value | (128>>(bit%8)), address);
  3086. },
  3087. _clear_attr: function ge_clear_attr(object, bit) {
  3088. if (object==0) return; // Kill that V0EFH before it starts.
  3089. var address = this.m_object_tree_start + object*this.m_object_size + (bit>>3);
  3090. var value = this.m_memory[address];
  3091. this.setByte(value & ~(128>>(bit%8)), address);
  3092. },
  3093. _test_attr: function ge_test_attr(object, bit) {
  3094. if (object==0) return 0; // Kill that V0EFH before it starts.
  3095. if ((this.m_memory[this.m_object_tree_start + object*this.m_object_size +(bit>>3)] &
  3096. (128>>(bit%8)))) {
  3097. return 1;
  3098. } else {
  3099. return 0;
  3100. }
  3101. },
  3102. _put_prop: function put_prop(object, property, value) {
  3103. if (object == 0) return;
  3104. var address = this._property_search(object, property, -1);
  3105. if (!address[2]) {
  3106. gnusto_error(704); // undefined property
  3107. }
  3108. if (address[1]==1) {
  3109. this.setByte(value, address[0]);
  3110. } else if (address[1]==2) {
  3111. this.setWord(value, address[0]);
  3112. } else {
  3113. gnusto_error(705); // weird length
  3114. }
  3115. },
  3116. _get_older_sibling: function ge_get_older_sibling(object) {
  3117. if (object==0) { return 0;}
  3118. // Start at the eldest child.
  3119. var candidate = this._get_child(this._get_parent(object));
  3120. if (object==candidate) {
  3121. // evidently nothing doing there.
  3122. return 0;
  3123. }
  3124. while (candidate) {
  3125. var next_along = this._get_sibling(candidate);
  3126. if (next_along==object) {
  3127. return candidate; // Yay! Got it!
  3128. }
  3129. candidate = next_along;
  3130. }
  3131. // We ran out, so the answer's 0.
  3132. return 0;
  3133. },
  3134. _insert_obj: function ge_insert_obj(mover, new_parent) {
  3135. // First, remove mover from wherever it is in the tree now.
  3136. var old_parent = this._get_parent(mover);
  3137. var older_sibling = this._get_older_sibling(mover);
  3138. var younger_sibling = this._get_sibling(mover);
  3139. if (old_parent && this._get_child(old_parent)==mover) {
  3140. this._set_child(old_parent, younger_sibling);
  3141. }
  3142. if (older_sibling) {
  3143. this._set_sibling(older_sibling, younger_sibling);
  3144. }
  3145. // Now, slip it into the new place.
  3146. this._set_parent(mover, new_parent);
  3147. if (new_parent) {
  3148. this._set_sibling(mover, this._get_child(new_parent));
  3149. this._set_child(new_parent, mover);
  3150. }
  3151. },
  3152. // FIXME: Why the new_parent?!
  3153. _remove_obj: function ge_remove_obj(mover, new_parent) {
  3154. this._insert_obj(mover, 0);
  3155. },
  3156. _get_family: function ge_get_family(from, relationship) {
  3157. if (from==0) {return 0;}
  3158. if (this.m_version < 4) {
  3159. return this.m_memory[this.m_object_tree_start +
  3160. 4+relationship +
  3161. from*this.m_object_size];
  3162. } else {
  3163. // v4 and above.
  3164. return this.getUnsignedWord(this.m_object_tree_start +
  3165. 6+relationship*2 +
  3166. from*this.m_object_size);
  3167. }
  3168. gnusto_error(170, 'get_family'); // impossible
  3169. },
  3170. _get_parent: function ge_get_parent(from)
  3171. { return this._get_family(from, PARENT_REC); },
  3172. _get_child: function ge_get_child(from)
  3173. { return this._get_family(from, CHILD_REC); },
  3174. _get_sibling: function ge_get_sibling(from)
  3175. { return this._get_family(from, SIBLING_REC); },
  3176. _set_family: function ge_set_family(from, to, relationship) {
  3177. if (this.m_version < 4) {
  3178. this.setByte(to,
  3179. this.m_object_tree_start +
  3180. 4+relationship +
  3181. from*this.m_object_size);
  3182. } else {
  3183. // v4 and above.
  3184. this.setWord(to,
  3185. this.m_object_tree_start +
  3186. 6+relationship*2 +
  3187. from*this.m_object_size);
  3188. }
  3189. },
  3190. _set_parent: function ge_set_parent(from, to)
  3191. { this._set_family(from, to, PARENT_REC); },
  3192. _set_child: function ge_set_child(from, to)
  3193. { this._set_family(from, to, CHILD_REC); },
  3194. _set_sibling: function ge_set_sibling(from, to)
  3195. { this._set_family(from, to, SIBLING_REC); },
  3196. _obj_in: function ge_obj_in(child, parent)
  3197. { return this._get_parent(child) == parent; },
  3198. ////////////////////////////////////////////////////////////////
  3199. // Implements @copy_table, as in the Z-spec.
  3200. _copy_table: function ge_copy_table(first, second, size) {
  3201. size = this._unsigned2signed(size);
  3202. if (second==0) {
  3203. // Zero out the first |size| bytes of |first|.
  3204. for (var i=0; i<size; i++) {
  3205. this.setByte(0, i+first);
  3206. }
  3207. } else {
  3208. // Copy |size| bytes of |first| into |second|.
  3209. var copy_forwards = 0;
  3210. if (size<0) {
  3211. size = -size;
  3212. copy_forwards = 1;
  3213. } else {
  3214. if (first > second) {
  3215. copy_forwards = 1;
  3216. } else {
  3217. copy_forwards = 0;
  3218. }
  3219. }
  3220. if (copy_forwards) {
  3221. for (var i=0; i<size; i++) {
  3222. this.setByte(this.m_memory[first+i], second+i);
  3223. }
  3224. } else {
  3225. for (var i=size-1; i>=0; i--) {
  3226. this.setByte(this.m_memory[first+i], second+i);
  3227. }
  3228. }
  3229. }
  3230. },
  3231. ////////////////////////////////////////////////////////////////
  3232. // Implements @scan_table, as in the Z-spec.
  3233. _scan_table: function ge_scan_table(target_word, target_table,
  3234. table_length, table_form)
  3235. {
  3236. // TODO: Optimise this some.
  3237. var jumpby = table_form & 0x7F;
  3238. var usewords = ((table_form & 0x80) == 0x80);
  3239. var lastlocation = target_table + (table_length*jumpby);
  3240. if (usewords) {
  3241. //if the table is in the form of word values
  3242. while (target_table < lastlocation) {
  3243. if (((this.m_memory[0xFFFF&target_table]&0xFF) == ((target_word>>8)&0xFF)) &&
  3244. ((this.m_memory[0xFFFF&target_table+1]&0xFF) == (target_word&0xFF))) {
  3245. return target_table;
  3246. }
  3247. target_table += jumpby;
  3248. }
  3249. } else {
  3250. //if the table is in the form of byte values
  3251. while (target_table < lastlocation) {
  3252. if ((this.m_memory[0xFFFF&target_table]&0xFF) == (target_word&0xFFFF)) {
  3253. return target_table;
  3254. }
  3255. target_table += jumpby;
  3256. }
  3257. }
  3258. return 0;
  3259. },
  3260. ////////////////////////////////////////////////////////////////
  3261. // Returns the lines that @print_table should draw, as in
  3262. // the Z-spec.
  3263. //
  3264. // It's rather poorly defined there:
  3265. // * How is the text in memory encoded?
  3266. // [Straight ZSCII, not five-bit encoded.]
  3267. // * What happens to the cursor? Moved?
  3268. // [We're guessing not.]
  3269. // * Is the "table" a table in the Z-machine sense, or just
  3270. // a contiguous block of memory?
  3271. // [Just a contiguous block.]
  3272. // * What if the width takes us off the edge of the screen?
  3273. // * What if the height causes a [MORE] event?
  3274. //
  3275. // It also goes largely un-noted that this is the only possible
  3276. // way to draw on the lower window away from the current
  3277. // cursor position. (If we take the view that v5 windows are
  3278. // roughly the same thing as v6 windows, though, windows don't
  3279. // "own" any part of the screen, so there's no such thing as
  3280. // drawing on the lower window per se.)
  3281. //
  3282. // FIXME: Add note that we now start with G_E_PRINTTABLE
  3283. _print_table: function ge_print_table(address, width, height, skip) {
  3284. var lines = [];
  3285. for (var y=0; y<height; y++) {
  3286. var s='';
  3287. for (var x=0; x<width; x++) {
  3288. if (address<0) { address &= 0xFFFF; }
  3289. s=s+this._zscii_char_to_ascii(this.m_memory[address++]);
  3290. }
  3291. lines.push(s);
  3292. address += skip;
  3293. }
  3294. var result = ['PT', lines.length];
  3295. result = result.concat(lines);
  3296. return result;
  3297. },
  3298. _zscii_from: function ge_zscii_from(address, max_length, tell_length) {
  3299. if (address in this.m_jit) {
  3300. // Already seen this one.
  3301. if (tell_length)
  3302. return this.m_jit[address];
  3303. else
  3304. return this.m_jit[address][0];
  3305. }
  3306. var temp = '';
  3307. var running = 1;
  3308. var start_address = address;
  3309. var home_alph=0;
  3310. var alph = home_alph;
  3311. // Should be:
  3312. // -2 if we're not expecting a ten-bit character
  3313. // -1 if we are, but we haven't seen any of it
  3314. // n if we've seen half of one, where n is what we've seen
  3315. var tenbit = -2;
  3316. // Should be:
  3317. // 0 if we're not expecting an abbreviation
  3318. // z if we are, where z is the prefix
  3319. var abbreviation = 0;
  3320. if (!max_length) max_length = 65535;
  3321. var stopping_place = address + max_length;
  3322. while (running) {
  3323. var word = this.getUnsignedWord(address);
  3324. address += 2;
  3325. running = ((word & 0x8000)==0) && address<stopping_place;
  3326. for (var j=2; j>=0; j--) {
  3327. var code = ((word>>(j*5))&0x1f);
  3328. if (abbreviation) {
  3329. temp = temp + this._zscii_from(this.getUnsignedWord((32*(abbreviation-1)+code)*2+this.m_abbr_start)*2);
  3330. abbreviation = 0;
  3331. alph=home_alph;
  3332. } else if (tenbit==-2) {
  3333. if (code>5) {
  3334. if (alph==2 && code==6)
  3335. tenbit = -1;
  3336. else {
  3337. temp = temp + this.m_zalphabet[alph].charAt(code-6);
  3338. alph = home_alph;
  3339. }
  3340. } else {
  3341. if (code==0) { temp = temp + ' '; alph=home_alph; }
  3342. else if (code<4) {
  3343. if (this.getByte(0) > 2) {abbreviation = code;}
  3344. else {
  3345. if (code==2){
  3346. alph += 1;
  3347. if (alph > 2) {alph=0;}
  3348. } else if (code==3) {
  3349. alph -= 1;
  3350. if (alph < 0) {alph=2;}
  3351. }
  3352. else {
  3353. if (this.getByte(0)==2) {
  3354. abbreviation=1;}
  3355. else {
  3356. temp = temp + '\n'; //in z-1 this is a newline
  3357. alph = home_alph;
  3358. }
  3359. }
  3360. }
  3361. }
  3362. else {
  3363. if (this.getByte(0) > 2) {alph = code-3;}
  3364. else {
  3365. if (code==4){
  3366. home_alph += 1;
  3367. if (home_alph > 2) {home_alph=0;}
  3368. } else {
  3369. home_alph -= 1;
  3370. if (home_alph < 0) {home_alph=2;}
  3371. }
  3372. alph=home_alph;
  3373. }
  3374. }
  3375. }
  3376. } else if (tenbit==-1) {
  3377. tenbit = code;
  3378. } else {
  3379. temp = temp + this._zscii_char_to_ascii((tenbit<<5) + code);
  3380. tenbit = -2;
  3381. alph=home_alph;
  3382. }
  3383. }
  3384. }
  3385. if (start_address >= this.m_stat_start) {
  3386. this.m_jit[start_address] = [temp, address];
  3387. }
  3388. if (tell_length) {
  3389. return [temp, address];
  3390. } else {
  3391. return temp;
  3392. }
  3393. },
  3394. ////////////////////////////////////////////////////////////////
  3395. //
  3396. // encode_text
  3397. //
  3398. // Implements the @encode_text opcode.
  3399. // |zscii_text|+|from| is the address of the unencoded text.
  3400. // |length| is its length.
  3401. // (It may also be terminated by a zero byte.)
  3402. // |coded_text| is where to put the six bytes of encoded text.
  3403. _encode_text: function ge_encode_text(zscii_text, length, from, coded_text) {
  3404. zscii_text = (zscii_text + from) & 0xFFFF;
  3405. var source = '';
  3406. while (length>0) {
  3407. var b = this.m_memory[zscii_text];
  3408. if (b==0) break;
  3409. source = source + String.fromCharCode(b);
  3410. zscii_text++;
  3411. length--;
  3412. }
  3413. var result = this._into_zscii(source);
  3414. for (var i=0; i<result.length; i++) {
  3415. var c = result[i].charCodeAt(0);
  3416. this.setByte(c, coded_text++);
  3417. }
  3418. },
  3419. ////////////////////////////////////////////////////////////////
  3420. //
  3421. // Encodes the ZSCII string |str| to its compressed form,
  3422. // and returns it.
  3423. //
  3424. _into_zscii: function ge_into_zscii(str) {
  3425. var result = '';
  3426. var buffer = [];
  3427. var dictionary_entry_length;
  3428. if (this.m_version < 4) {
  3429. dictionary_entry_length = 4;
  3430. } else {
  3431. dictionary_entry_length = 6;
  3432. }
  3433. function emit(value) {
  3434. buffer.push(value);
  3435. if (buffer.length==3) {
  3436. var temp = (buffer[0]<<10 | buffer[1]<<5 | buffer[2]);
  3437. if (result.length == dictionary_entry_length-2) {
  3438. // This'll be the last word. We need to set the stop bit.
  3439. temp |= 0x8000;
  3440. }
  3441. result = result +
  3442. String.fromCharCode(temp >> 8) +
  3443. String.fromCharCode(temp & 0xFF);
  3444. buffer = [];
  3445. }
  3446. }
  3447. // Huge thanks to fredrik.ramsberg for fixing the following section!
  3448. var ch, cursor = 0, z2;
  3449. while (cursor < str.length && result.length < dictionary_entry_length)
  3450. {
  3451. ch = str.charCodeAt(cursor++);
  3452. // Downcase any uppercase characters
  3453. if (ch >= 65 && ch <= 90)
  3454. ch += 32;
  3455. else if (ch > 154)
  3456. {
  3457. if(this.m_unicode_start == 0)
  3458. {
  3459. // It's an extended character AND the game uses the regular
  3460. // unicode translation table, so we know how to downcase.
  3461. if ((ch >= 158 && ch <= 160) || (ch >= 167 && ch <= 168) || (ch >= 208 && ch <= 210))
  3462. ch -= 3;
  3463. else if (ch >= 175 && ch <= 180)
  3464. ch -= 6;
  3465. else if ((ch >= 186 && ch <= 190) || (ch >= 196 && ch <= 200))
  3466. ch -= 5;
  3467. else if (ch == 217 || ch == 218)
  3468. ch -= 2;
  3469. else if (ch == 202 || ch == 204 || ch == 212 || ch == 214 || ch == 221)
  3470. ch -= 1;
  3471. }
  3472. else
  3473. {
  3474. // For extended characters using custom unicode translation table,
  3475. // rely on JavaScripts downcasing function
  3476. var cnew = this._ascii_code_to_zscii_code(this._zscii_char_to_ascii(ch).toLowerCase().charCodeAt(0));
  3477. if(cnew > 0 && cnew <= 251 && cnew != '*'.charCodeAt(0))
  3478. ch = cnew;
  3479. }
  3480. }
  3481. // Convert ch to unicode, since alphabet tables are in unicode.
  3482. var ch_uni = String.fromCharCode(this._zscii_char_to_ascii(ch).charCodeAt(0));
  3483. z2 = this.m_zalphabet[0].indexOf(ch_uni);
  3484. if (z2 != -1)
  3485. // ch was found in alphabet A0: Just output position + 6
  3486. emit(z2 + 6);
  3487. else
  3488. {
  3489. z2=this.m_zalphabet[1].indexOf(ch_uni);
  3490. if (z2 != -1)
  3491. {
  3492. // ch was found in alphabet A1. Output a shift character and ch + 6
  3493. if (this.getByte(0) > 2)
  3494. emit(4); // shift to A1
  3495. else
  3496. emit(2); // shift is positioned differently in z1-2
  3497. emit(z2 + 6);
  3498. } else {
  3499. z2 = this.m_zalphabet[2].indexOf(ch_uni);
  3500. if (z2 != -1)
  3501. {
  3502. // ch was found in alphabet A2. Output a shift character and ch + 6
  3503. if (this.getByte(0) > 2)
  3504. emit(5); // shift to A2
  3505. else
  3506. emit(3); // shift is positioned differently in in z1-2
  3507. emit(z2 + 6);
  3508. } else {
  3509. if (this.getByte(0) > 2)
  3510. emit(5);
  3511. else
  3512. emit(3); //shift is positioned differently in z1-2
  3513. emit(6);
  3514. emit(ch >> 5);
  3515. emit(ch & 0x1F);
  3516. }
  3517. }
  3518. }
  3519. }
  3520. while (result.length<dictionary_entry_length) {
  3521. emit(5);
  3522. }
  3523. return result.substring(0, dictionary_entry_length);
  3524. },
  3525. _name_of_object: function ge_name_of_object(object) {
  3526. if (object==0) {
  3527. return "<void>";
  3528. } else {
  3529. var aa = this.m_property_list_addr_start + object*this.m_object_size;
  3530. return this._zscii_from(this.getUnsignedWord(aa)+1);
  3531. }
  3532. },
  3533. ////////////////////////////////////////////////////////////////
  3534. //
  3535. // Function to print the contents of leftovers.
  3536. _print_leftovers: function ge_print_leftovers() {
  3537. this._zOut(this.m_leftovers);
  3538. // May as well clear it out and save memory,
  3539. // although we won't be called again until it's
  3540. // set otherwise.
  3541. this.m_leftovers = '';
  3542. },
  3543. ////////////////////////////////////////////////////////////////
  3544. //
  3545. // Prints the text |text| on whatever input streams are
  3546. // currently enabled.
  3547. //
  3548. // If this returns false, the text has been printed.
  3549. // If it returns true, the text hasn't been printed, but
  3550. // you must return the GNUSTO_EFFECT_FLAGS_CHANGED effect
  3551. // to your caller. (There's a function handler_zOut()
  3552. // which does all this for you.)
  3553. _zOut: function ge_zOut(text) {
  3554. //this.logger("_zOut", text);
  3555. if (this.m_streamthrees.length) {
  3556. // Stream threes disable any other stream while they're on.
  3557. // (And they can't cause flag changes, I suppose.)
  3558. var current = this.m_streamthrees[0];
  3559. var address = this.m_streamthrees[0][1];
  3560. // Argument "text" is in Unicode. For storage in Z-machine memory, we
  3561. // need to convert it to ZSCII
  3562. for (var i = 0; i < text.length; i++)
  3563. this.setByte(this._ascii_code_to_zscii_code(text.charCodeAt(i)), address++);
  3564. this.m_streamthrees[0][1] = address;
  3565. } else {
  3566. var bits = this.m_memory[0x11] & 0x03;
  3567. var changed = bits != this.m_printing_header_bits;
  3568. //var effect_parameters = this.m_printing_header_bits;
  3569. this.m_printing_header_bits = bits;
  3570. // OK, so should we bail?
  3571. if (changed) {
  3572. this.m_leftovers = text;
  3573. this.m_rebound = this._print_leftovers;
  3574. return 1;
  3575. } else {
  3576. if (this.m_output_to_console) {
  3577. this.m_console_buffer = this.m_console_buffer + text;
  3578. }
  3579. if (bits & 1) {
  3580. this.m_transcript_buffer = this.m_transcript_buffer + text;
  3581. }
  3582. }
  3583. }
  3584. return 0;
  3585. },
  3586. ////////////////////////////////////////////////////////////////
  3587. consoleText: function ge_console_text()
  3588. {
  3589. var self = this,
  3590. temp = self.m_console_buffer.replace('\x00','','g');
  3591. // Don't print anything in @parchment's raw eval() mode
  3592. if ( self.op_parchment_data.raw_eval )
  3593. {
  3594. return '';
  3595. }
  3596. else
  3597. {
  3598. self.m_console_buffer = '';
  3599. return temp;
  3600. }
  3601. },
  3602. _transcript_text: function ge_transcript_text() {
  3603. var temp = this.m_transcript_buffer.replace('\x00','','g');
  3604. this.m_transcript_buffer = '';
  3605. return temp;
  3606. },
  3607. ////////////////////////////////////////////////////////////////
  3608. _is_separator: function ge_IsSeparator(value) {
  3609. for (var sepindex=0; sepindex < this.m_separator_count; sepindex++) {
  3610. if (value == this.m_separators[sepindex]) return 1;
  3611. }
  3612. return 0;
  3613. },
  3614. ////////////////////////////////////////////////////////////////
  3615. //
  3616. // code_for_varcode
  3617. // Generates code to access variable operands
  3618. // variable is interpreted as in ZSD 4.2.2:
  3619. // 0 = top of game stack
  3620. // 1-15 = local variables
  3621. // 16 up = global variables
  3622. /***
  3623. _code_for_varcode: function ge_code_for_varcode(varcode) {
  3624. if (varcode==0) {
  3625. return ARG_STACK_POP;
  3626. } else if (varcode < 0x10) {
  3627. return 'm_locals['+(varcode-1)+']';
  3628. } else {
  3629. return 'getUnsignedWord('+(this.m_vars_start+(varcode-16)*2)+')';
  3630. }
  3631. gnusto_error(170, 'code_for_varcode'); // impossible
  3632. },
  3633. ***/
  3634. _code_for_varcode: function ge_code_for_varcode(variable) {
  3635. var code = '', arg;
  3636. if (variable == 0)
  3637. {
  3638. code = 'var tmp_' + (++temp_var) + ' = m_gamestack.pop();';
  3639. arg = 'tmp_' + temp_var;
  3640. }
  3641. else if (variable < 0x10)
  3642. arg = 'm_locals[' + (variable - 1) + ']';
  3643. else
  3644. {
  3645. var high = this.m_vars_start + (variable - 16) * 2, low = high + 1;
  3646. var tmp = 'tmp_' + (++temp_var);
  3647. code = 'var ' + tmp + ' = (m_memory[' + high + '] << 8) | m_memory[' + low + '];';
  3648. arg = tmp;
  3649. }
  3650. return [code, arg];
  3651. },
  3652. ////////////////////////////////////////////////////////////////
  3653. //
  3654. // varcode_get
  3655. //
  3656. // Retrieves the value specified by |varcode|, and returns it.
  3657. // |varcode| is interpreted as in ZSD 4.2.2:
  3658. // 0 = pop from game stack
  3659. // 1-15 = local variables
  3660. // 16 up = global variables
  3661. //
  3662. // TODO: We need a varcode_getcode() which returns a JS string
  3663. // which will perform the same job as this function, to save us
  3664. // the extra call we use when encoding "varcode_get(constant)".
  3665. /***
  3666. Not in current use... inlined in handleZ_load
  3667. _varcode_get: function ge_varcode_get(varcode) {
  3668. if (varcode==0) {
  3669. return this.m_gamestack.pop();
  3670. } else if (varcode < 0x10) {
  3671. return this.m_locals[(varcode-1)];
  3672. } else {
  3673. return this.getUnsignedWord(this.m_vars_start+(varcode-16)*2);
  3674. }
  3675. gnusto_error(170, 'varcode_get'); // impossible
  3676. },
  3677. ***/
  3678. ////////////////////////////////////////////////////////////////
  3679. //
  3680. // varcode_set
  3681. //
  3682. // Stores the value |value| in the place specified by |varcode|.
  3683. // |varcode| is interpreted as in ZSD 4.2.2.
  3684. // 0 = push to game stack
  3685. // 1-15 = local variables
  3686. // 16 up = global variables
  3687. //
  3688. // TODO: We need a varcode_setcode() which returns a JS string
  3689. // which will perform the same job as this function, to save us
  3690. // the extra call we use when encoding "varcode_set(n, constant)".
  3691. _varcode_set: function ge_varcode_set(value, varcode) {
  3692. if (varcode==0) {
  3693. this.m_gamestack.push(value);
  3694. } else if (varcode < 0x10) {
  3695. this.m_locals[varcode-1] = value;
  3696. } else {
  3697. this.setWord(value, this.m_vars_start+(varcode-16)*2);
  3698. }
  3699. },
  3700. _brancher: function ge_brancher(condition) {
  3701. var inverted = 1;
  3702. var temp = this.m_memory[this.m_pc++];
  3703. var target_address = temp & 0x3F;
  3704. if (temp & 0x80) {
  3705. inverted = 0;
  3706. }
  3707. if (!(temp & 0x40)) {
  3708. target_address = (target_address << 8) | this.m_memory[this.m_pc++];
  3709. // and it's signed...
  3710. if (target_address & 0x2000) {
  3711. // sign bit's set; propagate it
  3712. target_address = (~0x1FFF) | (target_address & 0x1FFF);
  3713. }
  3714. }
  3715. var if_statement = condition;
  3716. if (inverted) {
  3717. if_statement = 'if(!('+if_statement+'))';
  3718. } else {
  3719. if_statement = 'if('+if_statement+')';
  3720. }
  3721. // Branches to the addresses 0 and 1 are actually returns with
  3722. // those values.
  3723. if (target_address == 0) {
  3724. return if_statement + '{_func_return(0);return;}';
  3725. }
  3726. if (target_address == 1) {
  3727. return if_statement + '{_func_return(1);return;}';
  3728. }
  3729. target_address = (this.m_pc + target_address) - 2;
  3730. // This is an optimisation that's currently unimplemented:
  3731. // if there's no code there, we should mark that we want it later.
  3732. // [ if (!this.m_jit[target_address]) this.m_jit[target_address]=0; ]
  3733. return if_statement + '{m_pc='+(target_address)+';return;}';
  3734. },
  3735. _storer: function ge_storer(rvalue) {
  3736. var lvalue_varcode = this.m_memory[this.m_pc++];
  3737. if (rvalue.substring && rvalue.substring(0,11)=='_func_gosub') {
  3738. // Special case: the results of gosubs can't
  3739. // be stored synchronously.
  3740. this.m_compilation_running = 0; // just to be sure we stop here.
  3741. if (rvalue.substring(rvalue.length-4)!=',-1)') {
  3742. // You really shouldn't pass us gosubs with
  3743. // the result target filled in.
  3744. gnusto_error(100, rvalue); // can't modify gosub
  3745. }
  3746. return rvalue.substring(0,rvalue.length-3) +
  3747. lvalue_varcode + ')';
  3748. // Otherwise it must be a synchronous write, so...
  3749. } else if (lvalue_varcode==0) {
  3750. return 'm_gamestack.push('+rvalue+')';
  3751. } else if (lvalue_varcode < 0x10) {
  3752. return 'm_locals['+(lvalue_varcode-1)+']='+rvalue;
  3753. } else {
  3754. return 'setWord('+rvalue+','+(this.m_vars_start+(lvalue_varcode-16)*2)+')';
  3755. }
  3756. gnusto_error(170, 'storer'); // impossible
  3757. },
  3758. ////////////////////////////////////////////////////////////////
  3759. //
  3760. // _generate_gosub
  3761. //
  3762. // Returns a JITstring which enters a new Zroutine (by calling
  3763. // _func_gosub).
  3764. //
  3765. // |target| is the packed address to jump to. (The packing
  3766. // algorithm varies according to the version of the Z-machine
  3767. // we're using.
  3768. //
  3769. // |args| is something whose string representation is a
  3770. // comma-delimited list of actual parameters to the function.
  3771. // (An array is fine for this, as is a single number, as is
  3772. // an empty string.)
  3773. //
  3774. // If |get_varcode| is defined and nonzero, we read one byte
  3775. // and use that varcode as the return target of the call.
  3776. // Otherwise the call will throw away its result.
  3777. //
  3778. _generate_gosub: function call_vn(target, arguments, get_varcode) {
  3779. this.m_compilation_running = 0;
  3780. var varcode = -1;
  3781. if (get_varcode) {
  3782. varcode = this.m_memory[this.m_pc++];
  3783. }
  3784. return '_func_gosub('+
  3785. this.m_pc_translate_for_routine(target)+','+
  3786. '['+arguments.toString()+'],'+
  3787. this.m_pc+','+
  3788. varcode+')';
  3789. },
  3790. ////////////////////////////////////////////////////////////////
  3791. // Returns a JS string that calls zOut() correctly to print
  3792. // the line of text in |text|. (See zOut() for details of
  3793. // what constitutes "correctly".)
  3794. //
  3795. // If |is_return| is set, the result will cause a Z-machine
  3796. // return with a result of 1 (the same result as @rtrue).
  3797. // If it's clear, the result will set the PC to the
  3798. // address immediately after the current instruction.
  3799. //
  3800. _handler_zOut: function ge_handler_zOut(text, is_return) {
  3801. var setter;
  3802. if (is_return) {
  3803. setter = '_func_return(1)';
  3804. } else {
  3805. setter = 'm_pc=0x'+this.m_pc.toString(16);
  3806. }
  3807. return 'if(_zOut('+text+')){' + setter +
  3808. ';m_effects=['+ GNUSTO_EFFECT_FLAGS_CHANGED + '];return 1}';
  3809. },
  3810. ////////////////////////////////////////////////////////////////
  3811. // Returns a JS string which will print the text encoded
  3812. // immediately after the current instruction.
  3813. //
  3814. // |suffix| is a string to add to the encoded string. It may
  3815. // be null, in which case no string will be added.
  3816. //
  3817. // |is_return| is passed through unchanged to handler_zOut()
  3818. // (this function is written in terms of that function).
  3819. // See the comments for that function for details.
  3820. _handler_print: function ge_handler_print(suffix, is_return) {
  3821. var zf = this._zscii_from(this.m_pc,65535,1);
  3822. var message = zf[0];
  3823. if (suffix) message = message + suffix;
  3824. // The quote() method is added to the String prototype by
  3825. // Douglas Crockford's remedial.js. It deals with edge cases
  3826. // not anticipated by the original inline quoting function
  3827. // that used to be here.
  3828. message=message.quote();
  3829. this.m_pc=zf[1];
  3830. return this._handler_zOut(message, is_return);
  3831. },
  3832. _log_shift: function ge_log_shift(value, shiftbits) {
  3833. // logical-bit-shift. Right shifts are zero-padded
  3834. // the arguments are unsigned, remember
  3835. if (shiftbits & 0x8000) {
  3836. return (value >>> (0x10000-shiftbits)) & 0xFFFF;
  3837. } else {
  3838. return (value << shiftbits) & 0xFFFF;
  3839. }
  3840. },
  3841. _art_shift: function ge_art_shift(value, shiftbits) {
  3842. // arithmetic-bit-shift. Right shifts are sign-extended
  3843. // the arguments are unsigned, remember
  3844. value = this._unsigned2signed(value);
  3845. if (shiftbits & 0x8000) {
  3846. return (value >> (0x10000-shiftbits)) & 0xFFFF;
  3847. } else {
  3848. return (value << shiftbits) & 0xFFFF;
  3849. }
  3850. },
  3851. _touch: function ge_touch(address) {
  3852. // Check for a breakpoint.
  3853. // Actually, don't for now: we have no plans as to what we'd do
  3854. // if we found one.
  3855. // if (this.m_pc in this.m_breakpoints) {
  3856. // if (address in this.m_breakpoints) {
  3857. // if (this.m_breakpoints[address]==2) {
  3858. // // A breakpoint we've just reurned from.
  3859. // this.m_breakpoints[addr]=1; // set it ready for next time
  3860. // return 0; // it doesn't trigger again this time.
  3861. // } else if (this.m_breakpoints[addr]==1) {
  3862. // // a genuine breakpoint!
  3863. // this.m_pc = address;
  3864. // return 1;
  3865. // }
  3866. //
  3867. // return 0;
  3868. // }
  3869. // }
  3870. if (this.m_goldenTrail) {
  3871. this.logger("pc", address.toString(16));
  3872. }
  3873. this.m_pc = address;
  3874. },
  3875. _save_undo: function ge_save_undo( pc ) {
  3876. // Save the game state
  3877. // As Gnusto can't be relied upon to have the correct PC at runtime, we store it at compile time
  3878. this.m_undo.push({
  3879. 'm_call_stack': this.m_call_stack.slice(),
  3880. 'm_locals': this.m_locals.slice(),
  3881. 'm_locals_stack': this.m_locals_stack.slice(),
  3882. 'm_param_counts': this.m_param_counts.slice(),
  3883. 'm_result_targets': this.m_result_targets.slice(),
  3884. 'm_gamestack': this.m_gamestack.slice(),
  3885. 'm_gamestack_callbreaks': this.m_gamestack_callbreaks.slice(),
  3886. 'm_memory': this.m_memory.slice(0, this.m_stat_start),
  3887. 'm_resultvar': this.m_memory[pc], // move onto target varcode,
  3888. 'm_pc': pc + 1
  3889. });
  3890. return 1;
  3891. },
  3892. ////////////////////////////////////////////////////////////////
  3893. //
  3894. // _restore_undo
  3895. //
  3896. // Restores the undo information saved in m_undo.
  3897. //
  3898. // Returns zero if the restore failed, nonzero if it succeeded.
  3899. // (If the function returns nonzero, the caller should return
  3900. // control immediately rather than continuing on with the current
  3901. // block of instructions: the PC is already set up. Think of it as
  3902. // a particularly funky kind of goto.)
  3903. //
  3904. _restore_undo: function ge_restore_undo() {
  3905. if (this.m_undo.length == 0) {
  3906. // No undo information is saved in m_undo.
  3907. return 0;
  3908. }
  3909. // Get the most recent undo save data
  3910. var undo_data = this.m_undo.pop();
  3911. this.m_call_stack =undo_data.m_call_stack;
  3912. this.m_locals = undo_data.m_locals;
  3913. this.m_locals_stack = undo_data.m_locals_stack;
  3914. this.m_param_counts = undo_data.m_param_counts;
  3915. this.m_result_targets = undo_data.m_result_targets;
  3916. this.m_gamestack = undo_data.m_gamestack;
  3917. this.m_gamestack_callbreaks = undo_data.m_gamestack_callbreaks;
  3918. var mem = undo_data.m_memory;
  3919. this.m_memory = mem.concat(this.m_memory.slice(mem.length));
  3920. // Set the @save_undo result var to 2, and fix the PC
  3921. this._varcode_set(2, undo_data.m_resultvar);
  3922. this.m_pc = undo_data.m_pc;
  3923. return 1;
  3924. },
  3925. _saveable_state: function ge_saveable_state(varcode_offset) {
  3926. var result = {
  3927. 'm_memory': this.m_memory.slice(0, this.m_stat_start),
  3928. 'm_pc': this.m_pc + varcode_offset, // move onto target varcode,
  3929. 'm_call_stack': this.m_call_stack.slice(),
  3930. 'm_locals': this.m_locals.slice(),
  3931. 'm_locals_stack': this.m_locals_stack.slice(),
  3932. 'm_param_counts': this.m_param_counts.slice(),
  3933. 'm_result_targets': this.m_result_targets.slice(),
  3934. 'm_gamestack': this.m_gamestack.slice(),
  3935. 'm_gamestack_callbreaks': this.m_gamestack_callbreaks.slice()
  3936. };
  3937. return result;
  3938. },
  3939. // Returns nonzero iff the memory verifies correctly for @verify
  3940. // in our copy of the original file of this game, m_original_memory.
  3941. // That is, all bytes after the header must total to the checksum
  3942. // given in the header. We use the value in the orignal file's
  3943. // header for comparison, not the one in the current header.
  3944. _verify: function ge_verify() {
  3945. var total = 0;
  3946. var checksum = (this.m_original_memory[0x1c]<<8 |
  3947. this.m_original_memory[0x1d]);
  3948. for (var i=0x40; i<this.m_original_memory.length; i++) {
  3949. total += this.m_original_memory[i];
  3950. }
  3951. return (total & 0xFFFF) == checksum;
  3952. },
  3953. ////////////////////////////////////////////////////////////////
  3954. ////////////////////////////////////////////////////////////////
  3955. // //
  3956. // PRIVATE VARIABLES //
  3957. // //
  3958. ////////////////////////////////////////////////////////////////
  3959. ////////////////////////////////////////////////////////////////
  3960. // This will hold the filename of the current game file (so we can
  3961. // reset the memory from it as needed.
  3962. // XXXFIXME: this implies things about where we get game data!
  3963. m_local_game_file: 0,
  3964. // These are all initialised in the function start_game().
  3965. // The actual memory of the Z-machine, one byte per element.
  3966. m_memory: [],
  3967. // Hash mapping Z-code instructions to functions which return a
  3968. // JavaScript string to handle them.
  3969. m_handlers: 0,
  3970. // |this.m_jit| is a cache for the results of compile(): it maps
  3971. // memory locations to JS function objects. Theoretically,
  3972. // executing the function associated with a given address is
  3973. // equivalent to executing the z-code at that address.
  3974. //
  3975. // Note: the results of dissembly in dynamic memory should never
  3976. // be put into this array, since the code can change.
  3977. //
  3978. // Planned features:
  3979. // 1) compile() should know about this array, and should stop
  3980. // dissembly if its program counter reaches any key in it.
  3981. // 2) putting a flag value (probably zero) into this array will
  3982. // have the effect of 1), but won't stop us replacing it with
  3983. // real code later.
  3984. m_jit: [],
  3985. // If this is nonzero, the engine will report as it passes each instruction.
  3986. m_goldenTrail: 0,
  3987. // When this is nonzero, we should print JIT information to the burin,
  3988. // for debugging.
  3989. m_copperTrail: 0,
  3990. // In ordinary use, compile() attempts to make the functions
  3991. // it creates as long as possible. Sometimes, though, we have to
  3992. // stop dissembling (for example, when we reach a RETURN) or it
  3993. // will seem a good idea (say, when we meet an unconditional jump).
  3994. // In such cases, a subroutine anywhere along the line may set
  3995. // |m_compilation_running| to 0, and compile() will stop after the current
  3996. // iteration.
  3997. m_compilation_running: 0,
  3998. // |gamestack| is the Z-machine's stack.
  3999. m_gamestack: 0,
  4000. // Stack which stores the depths of |m_gamestack| at each function call
  4001. // on the function stack. (Quetzal needs to know this.)
  4002. m_gamestack_callbreaks: [],
  4003. // |himem| is the high memory mark. This is rarely used in practice;
  4004. // we might stop noting it.
  4005. m_himem: 0,
  4006. // |pc| is the Z-machine's program counter.
  4007. //
  4008. // During compilation:
  4009. // it points at the place in memory which we're currently decoding.
  4010. // This may be in the middle of an instruction. (See m_current_instr
  4011. // for a way not to have this problem.)
  4012. // During execution (within or outside JITspace):
  4013. // it points to the next address to be executed. It gets set
  4014. // using _touch().
  4015. m_pc: 0,
  4016. // |this_instr_pc| is the address of the start of the current instruction.
  4017. // during compilation. This is not identical to |m_pc|, because that
  4018. // can point to addresses within the middles of instructions.
  4019. //m_this_instr_pc: 0,
  4020. // |dict_start| is the address of the dictionary in the Z-machine's memory.
  4021. m_dict_start: 0,
  4022. // |objs_start| is the address of the object table in the Z-machine's memory.
  4023. m_objs_start: 0,
  4024. // |vars_start| is the address of the global variables in the Z-machine's
  4025. // memory.
  4026. m_vars_start: 0,
  4027. // |stat_start| is the address of the bottom of static memory.
  4028. // Anything below this can change during the games. Anything
  4029. // above this does not change like the shifting shadows.
  4030. m_stat_start: 0,
  4031. // Address of the start of the abbreviations table in memory. (Can this
  4032. // be changed? If not, we could decode them all first.)
  4033. m_abbr_start: 0,
  4034. // Address of the start of the header extension table in memory.
  4035. m_hext_start: 0,
  4036. // Address of custom alphabet table (if any).
  4037. m_alpha_start: 0,
  4038. // Holder for the z-alphabet
  4039. m_zalphabet: [],
  4040. // Address of start of strings.
  4041. // Only used in versions 6 and 7; otherwise undefined.
  4042. m_string_start: 0,
  4043. // Address of start of routines.
  4044. // Only used in versions 6 and 7; otherwise undefined.
  4045. m_routine_start: 0,
  4046. // Address of Unicode Translation Table (if any).
  4047. m_unicode_start: 0,
  4048. m_custom_unicode_charcount: 0,
  4049. // Information about the defined list of word separators
  4050. m_separator_count: 0,
  4051. m_separators: [],
  4052. // |version| is the current Z-machine version.
  4053. m_version: 0,
  4054. // |call_stack| stores all the return addresses for all the functions
  4055. // which are currently executing.
  4056. m_call_stack: [],
  4057. // |locals| is an array of the Z-machine's local variables.
  4058. m_locals: [],
  4059. // |locals_stack| is a stack of the values of |locals| for functions
  4060. // further down the call stack than the current function.
  4061. m_locals_stack: 0,
  4062. // |param_counts| is an array which stores the number of parameters for
  4063. // each of the variables on |call_stack|, and the current function (so
  4064. // that the number of parameters to the current function is in
  4065. // param_counts[0]). (Hmm, that's a bit inconsistent. We should change it.)
  4066. m_param_counts: 0,
  4067. // |result_targets| is a stack whose use parallels |call_stack|. Instead of
  4068. // storing return addresses, though, |result_targets| stores varcodes to
  4069. // store a function's result into as it returns. For example, if a
  4070. // function contains:
  4071. //
  4072. // b000: locals[7] = foo(locals[1])
  4073. // b005: something else
  4074. //
  4075. // and we're just now returning from the call to foo() in b000, the only
  4076. // legitimate value we can set the PC to is b005 (b000 would cause an
  4077. // infinite loop, after all), but we can't go on to b005 because we haven't
  4078. // finished executing b000 yet. So on the top of |result_targets| there'll be
  4079. // a varcode which represents locals[7]. Varcodes are as defined in ZSD 4.2.2:
  4080. // 0 = push to game stack
  4081. // 1-15 = local variables
  4082. // 16 up = global variables
  4083. //
  4084. // Also, the magic value -1 causes the function's result to be thrown away.
  4085. m_result_targets: [],
  4086. // The function object to run first next time run() gets called,
  4087. // before any other execution gets under way. Its argument will be the
  4088. // |answer| formal parameter of run(). It can also be 0, which
  4089. // is a no-op. run() will clear it to 0 after running it, whatever
  4090. // happens.
  4091. m_rebound: 0,
  4092. // Any extra arguments for m_rebound.
  4093. m_rebound_args: [],
  4094. // Whether we're writing output to the ordinary screen (stream 1).
  4095. m_output_to_console: 0,
  4096. // Stream 2 is whether we're writing output to a game transcript,
  4097. // but the state for that is stored in a bit in "Flags 2" in the header.
  4098. // A list of streams writing out to main memory (collectively, stream 3).
  4099. // The stream at the start of the list is the current one.
  4100. // Each stream is represented as a list with two elements: [|start|, |end|].
  4101. // |start| is the address at the start of the memory block where the length
  4102. // of the block will be stored after the stream is closed. |end| is the
  4103. // current end of the block.
  4104. m_streamthrees: [],
  4105. // Whether we're writing copies of input to a script file (stream 4).
  4106. // fixme: This is really something we need to tell the environment about,
  4107. // since we can't deal with it ourselves.
  4108. m_output_to_script: 0,
  4109. // FIXME: Clarify the distinction here
  4110. // If this is 1, run() will "wimp out" after every opcode.
  4111. m_single_step: 0,
  4112. m_debug_mode: 0,
  4113. m_parser_debugging: 0,
  4114. // assert when any value outside 0..FFFF is written or read
  4115. m_value_asserts: 0,
  4116. // Hash of breakpoints. If compile() reaches one of these, it will stop
  4117. // before executing that instruction with GNUSTO_EFFECT_BREAKPOINT, and the
  4118. // PC set to the address of that instruction.
  4119. //
  4120. // The keys of the hash are opcode numbers. The values are not yet stably
  4121. // defined; at present, all values should be 1, except that if a breakpoint's
  4122. // value is 2, it won't trigger, but it will be reset to 1.
  4123. m_breakpoints: {},
  4124. // Buffer of text written to console.
  4125. m_console_buffer: '',
  4126. // Buffer of text written to transcript.
  4127. m_transcript_buffer: '',
  4128. // |effects| holds the current effect in its zeroth element, and any
  4129. // parameters needed in the following elements.
  4130. m_effects: [],
  4131. // |answers| is a list of answers to an effect, given by the environment.
  4132. m_answers: [],
  4133. m_random_state: 0,
  4134. m_random_use_seed: 0,
  4135. m_random_use_sequence: 0,
  4136. m_random_sequence_max: 0,
  4137. // Values of the bottom two bits in Flags 2 (address 0x11),
  4138. // used by the zOut function.
  4139. // See http://code.google.com/p/parchment/issues/detail?id=14.
  4140. m_printing_header_bits: 0,
  4141. // Leftover text which should be printed next run(), since
  4142. // we couldn't print it this time because the flags had
  4143. // changed.
  4144. m_leftovers: '',
  4145. // These pointers point at the currently-selected functions:
  4146. m_pc_translate_for_routine: pc_translate_v45,
  4147. m_pc_translate_for_string: pc_translate_v45,
  4148. // An array of undo save data.
  4149. // If this is an object, it should contain copies of other
  4150. // properties of the engine object with the same names,
  4151. // backed up for @save_undo. These should be the same as
  4152. // their namesakes at the moment of saving, except that:
  4153. // m_memory needs only to hold dynamic memory
  4154. // m_pc points at the } address (<z5) { which receives the
  4155. // } varcode (>=z5) { success result.
  4156. //
  4157. // If the array is empty, no undo information is saved.
  4158. m_undo: [],
  4159. // Like m_undo, but only well-defined during a save effect.
  4160. m_state_to_save: 0,
  4161. // Saved Quetzal image. Only well-defined between a call to saveGame()
  4162. // and a call to saveGameData().
  4163. m_quetzal_image: 0,
  4164. // Original state of the story file. Used when saving to produce
  4165. // a compressed image by comparison.
  4166. //
  4167. // FIXME: This should make the restart effect redundant.
  4168. // Make it so.
  4169. m_original_memory: [],
  4170. // Whether to save compressed or uncompressed games. This trades
  4171. // having small files for saving slightly faster, and isn't
  4172. // really worth it. We may hardwire it on permanently.
  4173. //m_compress_save_files: 1,
  4174. // Offset of the (notional) 0th entry in the object tree from the
  4175. // start of the object block, in bytes. (This is the size of the
  4176. // property defaults table less m_object_size.)
  4177. m_object_tree_start: 0,
  4178. // Address of the property list address within the (notional) 0th
  4179. // entry in the object block. This is m_object_tree_start plus
  4180. // the offset within the record for that field (which varies by
  4181. // architecture).
  4182. m_property_list_addr_start: 0,
  4183. // Size of an object in the objects table, in bytes.
  4184. m_object_size: 14,
  4185. // A stack of information about the routines that were suspended
  4186. // for the current interrupt service routines to do their jobs.
  4187. // Usually ISRs don't interrupt other ISRs, so this stack will have
  4188. // either one or no elements.
  4189. m_interrupt_information: [],
  4190. // Stuff for @parchment
  4191. op_parchment_data: {}
  4192. };
  4193. // EOF gnusto-engine.js //
  4194. /*
  4195. Z-Machine UI
  4196. ============
  4197. Copyright (c) 2013 The ifvms.js team
  4198. BSD licenced
  4199. http://github.com/curiousdannii/ifvms.js
  4200. */
  4201. /*
  4202. Note: is used by both ZVM and Gnusto. In the case of Gnusto the engine is actually GnustoRunner.
  4203. The engine must have a StructIO modified env
  4204. */
  4205. var ZVMUI = Class.subClass({
  4206. init: function( engine, headerbit )
  4207. {
  4208. this.e = engine;
  4209. this.buffer = '';
  4210. // Use the requested formatter (classes is default)
  4211. extend( this, this.formatters[engine.env.formatter] || {} );
  4212. if ( DEBUG )
  4213. {
  4214. this.reverse = 0;
  4215. this.bold = 0;
  4216. this.italic = 0;
  4217. this.fg = undefined;
  4218. this.bg = undefined;
  4219. }
  4220. // Bit 0 is for @set_style, bit 1 for the header, and bit 2 for @set_font
  4221. this.mono = headerbit;
  4222. this.process_colours();
  4223. // Upper window stuff
  4224. this.currentwin = 0;
  4225. this.status = []; // Status window orders
  4226. // Construct the basic windows
  4227. engine.orders.push(
  4228. {
  4229. code: 'stream',
  4230. name: 'status'
  4231. },
  4232. {
  4233. code: 'stream',
  4234. name: 'main'
  4235. },
  4236. {
  4237. code: 'find',
  4238. name: 'main'
  4239. }
  4240. );
  4241. },
  4242. // Clear the lower window
  4243. clear_window: function()
  4244. {
  4245. this.e.orders.push({
  4246. code: 'clear',
  4247. name: 'main',
  4248. bg: this.bg
  4249. });
  4250. },
  4251. erase_line: function( value )
  4252. {
  4253. if ( value === 1 )
  4254. {
  4255. this.flush();
  4256. this.status.push( { code: "eraseline" } );
  4257. }
  4258. },
  4259. erase_window: function( window )
  4260. {
  4261. this.flush();
  4262. if ( window < 1 )
  4263. {
  4264. this.clear_window();
  4265. }
  4266. if ( window === -1 )
  4267. {
  4268. this.split_window( 0 );
  4269. }
  4270. if ( window === -2 || window === 1 )
  4271. {
  4272. this.status.push( { code: "clear" } );
  4273. }
  4274. },
  4275. // Flush the buffer to the orders
  4276. flush: function()
  4277. {
  4278. // If we have a buffer transfer it to the orders
  4279. if ( this.buffer !== '' )
  4280. {
  4281. var order = {
  4282. code: 'stream',
  4283. text: this.buffer,
  4284. props: this.format()
  4285. };
  4286. ( this.currentwin ? this.status : this.e.orders ).push( order );
  4287. this.buffer = '';
  4288. }
  4289. },
  4290. format: function()
  4291. {
  4292. var props = {},
  4293. temp,
  4294. classes = [],
  4295. fg = this.fg,
  4296. bg = this.bg;
  4297. if ( this.bold )
  4298. {
  4299. classes.push( 'zvm-bold' );
  4300. }
  4301. if ( this.italic )
  4302. {
  4303. classes.push( 'zvm-italic' );
  4304. }
  4305. if ( this.mono )
  4306. {
  4307. classes.push( 'zvm-mono' );
  4308. }
  4309. if ( this.reverse )
  4310. {
  4311. temp = fg;
  4312. fg = bg || this.env.bg;
  4313. bg = temp || this.env.fg;
  4314. }
  4315. if ( typeof fg !== 'undefined' )
  4316. {
  4317. if ( isNaN( fg ) )
  4318. {
  4319. props.css = { color: fg };
  4320. }
  4321. else
  4322. {
  4323. classes.push( 'zvm-fg-' + fg );
  4324. }
  4325. }
  4326. if ( typeof bg !== 'undefined' )
  4327. {
  4328. if ( isNaN( bg ) )
  4329. {
  4330. if ( !props.css )
  4331. {
  4332. props.css = {};
  4333. }
  4334. props.css['background-color'] = bg;
  4335. }
  4336. else
  4337. {
  4338. classes.push( 'zvm-bg-' + bg );
  4339. }
  4340. }
  4341. if ( classes.length )
  4342. {
  4343. props['class'] = classes.join( ' ' );
  4344. }
  4345. return props;
  4346. },
  4347. get_cursor: function( array )
  4348. {
  4349. // act() will flush
  4350. this.status.push({
  4351. code: 'get_cursor',
  4352. addr: array
  4353. });
  4354. this.e.act();
  4355. },
  4356. // Process CSS default colours
  4357. process_colours: function()
  4358. {
  4359. // Convert RGB to a Z-Machine true colour
  4360. // RGB is a css colour code. rgb(), #000000 and #000 formats are supported.
  4361. function convert_RGB( code )
  4362. {
  4363. var round = Math.round,
  4364. data = /(\d+),\s*(\d+),\s*(\d+)|#(\w{1,2})(\w{1,2})(\w{1,2})/.exec( code ),
  4365. result;
  4366. // Nice rgb() code
  4367. if ( data[1] )
  4368. {
  4369. result = [ data[1], data[2], data[3] ];
  4370. }
  4371. else
  4372. {
  4373. // Messy CSS colour code
  4374. result = [ parseInt( data[4], 16 ), parseInt( data[5], 16 ), parseInt( data[6], 16 ) ];
  4375. // Stretch out compact #000 codes to their full size
  4376. if ( code.length === 4 )
  4377. {
  4378. result = [ result[0] << 4 | result[0], result[1] << 4 | result[1], result[2] << 4 | result[2] ];
  4379. }
  4380. }
  4381. // Convert to a 15bit colour
  4382. return round( result[2] / 8.226 ) << 10 | round( result[1] / 8.226 ) << 5 | round( result[0] / 8.226 );
  4383. }
  4384. // Standard colours
  4385. var colours = [
  4386. 0xFFFE, // Current
  4387. 0xFFFF, // Default
  4388. 0x0000, // Black
  4389. 0x001D, // Red
  4390. 0x0340, // Green
  4391. 0x03BD, // Yellow
  4392. 0x59A0, // Blue
  4393. 0x7C1F, // Magenta
  4394. 0x77A0, // Cyan
  4395. 0x7FFF, // White
  4396. 0x5AD6, // Light grey
  4397. 0x4631, // Medium grey
  4398. 0x2D6B // Dark grey
  4399. ],
  4400. // Start with CSS colours provided by the runner
  4401. fg_css = this.e.env.fgcolour,
  4402. bg_css = this.e.env.bgcolour,
  4403. // Convert to true colour for storing in the header
  4404. fg_true = fg_css ? convert_RGB( fg_css ) : 0xFFFF,
  4405. bg_true = bg_css ? convert_RGB( bg_css ) : 0xFFFF,
  4406. // Search the list of standard colours
  4407. fg = colours.indexOf( fg_true ),
  4408. bg = colours.indexOf( bg_true );
  4409. // ZVMUI must have colours for reversing text, even if we don't write them to the header
  4410. // So use the given colours or assume black on white
  4411. if ( fg < 2 )
  4412. {
  4413. fg = fg_css || 2;
  4414. }
  4415. if ( bg < 2 )
  4416. {
  4417. bg = bg_css || 9;
  4418. }
  4419. this.env = {
  4420. fg: fg,
  4421. bg: bg,
  4422. fg_true: fg_true,
  4423. bg_true: bg_true
  4424. };
  4425. },
  4426. set_colour: function( foreground, background )
  4427. {
  4428. this.flush();
  4429. if ( foreground === 1 )
  4430. {
  4431. this.fg = undefined;
  4432. }
  4433. if ( foreground > 1 && foreground < 13 )
  4434. {
  4435. this.fg = foreground;
  4436. }
  4437. if ( background === 1 )
  4438. {
  4439. this.bg = undefined;
  4440. }
  4441. if ( background > 1 && background < 13 )
  4442. {
  4443. this.bg = background;
  4444. }
  4445. },
  4446. set_cursor: function( row, col )
  4447. {
  4448. this.flush();
  4449. this.status.push({
  4450. code: 'cursor',
  4451. to: [row - 1, col - 1]
  4452. });
  4453. },
  4454. set_font: function( font )
  4455. {
  4456. // We only support fonts 1 and 4
  4457. if ( font !== 1 && font !== 4 )
  4458. {
  4459. return 0;
  4460. }
  4461. var returnval = this.mono & 0x04 ? 4 : 1;
  4462. if ( font !== returnval )
  4463. {
  4464. this.flush();
  4465. this.mono ^= 0x04;
  4466. }
  4467. return returnval;
  4468. },
  4469. // Set styles
  4470. set_style: function( stylebyte )
  4471. {
  4472. this.flush();
  4473. // Setting the style to Roman will clear the others
  4474. if ( stylebyte === 0 )
  4475. {
  4476. this.reverse = this.bold = this.italic = 0;
  4477. this.mono &= 0xFE;
  4478. }
  4479. if ( stylebyte & 0x01 )
  4480. {
  4481. this.reverse = 1;
  4482. }
  4483. if ( stylebyte & 0x02 )
  4484. {
  4485. this.bold = 1;
  4486. }
  4487. if ( stylebyte & 0x04 )
  4488. {
  4489. this.italic = 1;
  4490. }
  4491. if ( stylebyte & 0x08 )
  4492. {
  4493. this.mono |= 0x01;
  4494. }
  4495. },
  4496. // Set true colours
  4497. set_true_colour: function( foreground, background )
  4498. {
  4499. // Convert a 15 bit colour to RGB
  4500. function convert_true_colour( colour )
  4501. {
  4502. // Stretch the five bits per colour out to 8 bits
  4503. var newcolour = Math.round( ( colour & 0x1F ) * 8.226 ) << 16
  4504. | Math.round( ( ( colour & 0x03E0 ) >> 5 ) * 8.226 ) << 8
  4505. | Math.round( ( ( colour & 0x7C00 ) >> 10 ) * 8.226 );
  4506. newcolour = newcolour.toString( 16 );
  4507. // Ensure the colour is 6 bytes long
  4508. while ( newcolour.length < 6 )
  4509. {
  4510. newcolour = '0' + newcolour;
  4511. }
  4512. return '#' + newcolour;
  4513. }
  4514. this.flush();
  4515. if ( foreground === 0xFFFF )
  4516. {
  4517. this.fg = undefined;
  4518. }
  4519. else if ( foreground < 0x8000 )
  4520. {
  4521. this.fg = convert_true_colour( foreground );
  4522. }
  4523. if ( background === 0xFFFF )
  4524. {
  4525. this.bg = undefined;
  4526. }
  4527. else if ( background < 0x8000 )
  4528. {
  4529. this.bg = convert_true_colour( background );
  4530. }
  4531. },
  4532. set_window: function( window )
  4533. {
  4534. this.flush();
  4535. this.currentwin = window;
  4536. this.e.orders.push({
  4537. code: 'find',
  4538. name: window ? 'status' : 'main'
  4539. });
  4540. if ( window )
  4541. {
  4542. this.status.push({
  4543. code: 'cursor',
  4544. to: [0, 0]
  4545. });
  4546. }
  4547. },
  4548. split_window: function( lines )
  4549. {
  4550. this.flush();
  4551. this.status.push({
  4552. code: "height",
  4553. lines: lines
  4554. });
  4555. },
  4556. // Update ZVM's header with correct colour information
  4557. // If colours weren't provided then the default colour will be used for both
  4558. update_header: function()
  4559. {
  4560. var memory = this.e.m;
  4561. memory.setUint8( 0x2C, isNaN( this.env.bg ) ? 1 : this.env.bg );
  4562. memory.setUint8( 0x2D, isNaN( this.env.fg ) ? 1 : this.env.fg );
  4563. this.e.extension_table( 5, this.env.fg_true );
  4564. this.e.extension_table( 6, this.env.bg_true );
  4565. },
  4566. // Formatters allow you to change how styles are marked
  4567. // The desired formatter should be passed in through env
  4568. formatters: {}
  4569. });
  4570. /*
  4571. Gnusto runner
  4572. =============
  4573. Copyright (c) 2012 The Parchment Contributors
  4574. BSD licenced
  4575. http://code.google.com/p/parchment
  4576. */
  4577. // A Gnusto runner
  4578. var GnustoRunner = Object.subClass({
  4579. init: function( env, engine )
  4580. {
  4581. var self = this;
  4582. engine = window.engine = this.e = new GnustoEngine( window.console && function() { console.log( msg ); } || function(){} );
  4583. this.io = new StructIO( env );
  4584. this.env = this.io.env;
  4585. // Set the appropriate event handlers
  4586. this.io.TextInput.callback = function( event ) { self.inputEvent( event ); };
  4587. },
  4588. // Handler for events from Parchment
  4589. fromParchment: function( event )
  4590. {
  4591. var code = event.code,
  4592. engine = this.e,
  4593. run;
  4594. // Load the story file
  4595. if ( code == 'load' )
  4596. {
  4597. engine.loadStory( event.data );
  4598. }
  4599. // (Re)start the engine
  4600. if ( code == 'restart' )
  4601. {
  4602. this.restart();
  4603. run = 1;
  4604. }
  4605. // Save a savefile
  4606. if ( code == 'save' )
  4607. {
  4608. engine.answer( 0, event.result || 1 );
  4609. run = 1;
  4610. }
  4611. // Restore a savefile
  4612. if ( code == 'restore' )
  4613. {
  4614. if ( !this.ui )
  4615. {
  4616. this.restart();
  4617. }
  4618. if ( event.data )
  4619. {
  4620. engine.loadSavedGame( event.data )
  4621. }
  4622. else
  4623. {
  4624. engine.answer( 0, 0 );
  4625. }
  4626. run = 1;
  4627. }
  4628. if ( run )
  4629. {
  4630. this.run();
  4631. }
  4632. },
  4633. restart: function()
  4634. {
  4635. var engine = this.e,
  4636. io = this.io;
  4637. // Header variables
  4638. engine.setByte( 255, 0x20 );
  4639. engine.setByte( io.env.width, 0x21 );
  4640. engine.setWord( io.env.width, 0x22 );
  4641. engine.setWord( 255, 0x24 );
  4642. // Set up the ifvms.js ZVMUI
  4643. io.target = io.container.empty();
  4644. this.orders = [];
  4645. this.ui = new ZVMUI( this, engine.getByte( 0x11 ) & 0x02 );
  4646. io.event( this.orders );
  4647. this.orders = [];
  4648. },
  4649. // Handle Gnusto's non-StructIO friendly IO protocol
  4650. run: function()
  4651. {
  4652. var engine = this.e,
  4653. ui = this.ui,
  4654. text,
  4655. effect, effect1, effect2,
  4656. stop,
  4657. i;
  4658. this.orders = [];
  4659. while ( !stop )
  4660. {
  4661. engine.run();
  4662. text = engine.consoleText();
  4663. if ( text )
  4664. {
  4665. ui.buffer += text;
  4666. }
  4667. effect = '"' + engine.effect( 0 ) + '"';
  4668. effect1 = engine.effect( 1 );
  4669. effect2 = engine.effect( 2 );
  4670. if ( effect == GNUSTO_EFFECT_INPUT )
  4671. {
  4672. stop = 1;
  4673. ui.flush();
  4674. this.orders.push({
  4675. code: 'read',
  4676. target: this.currentwin
  4677. });
  4678. }
  4679. if ( effect == GNUSTO_EFFECT_INPUT_CHAR )
  4680. {
  4681. stop = 1;
  4682. this.orders.push({
  4683. code: 'char'
  4684. });
  4685. }
  4686. if ( effect == GNUSTO_EFFECT_SAVE )
  4687. {
  4688. stop = 1;
  4689. engine.saveGame();
  4690. this.toParchment({
  4691. code: 'save',
  4692. data: engine.saveGameData()
  4693. });
  4694. }
  4695. if ( effect == GNUSTO_EFFECT_RESTORE )
  4696. {
  4697. stop = 1;
  4698. this.toParchment({ code: 'restore' });
  4699. }
  4700. if ( effect == GNUSTO_EFFECT_QUIT )
  4701. {
  4702. stop = 1;
  4703. }
  4704. if ( effect == GNUSTO_EFFECT_RESTART )
  4705. {
  4706. engine.resetStory();
  4707. this.restart();
  4708. ui = this.ui;
  4709. }
  4710. if ( effect == GNUSTO_EFFECT_FLAGS_CHANGED )
  4711. {
  4712. ui.flush();
  4713. ui.mono = ( ui.mono & 0xFD ) | engine.m_printing_header_bits & 0x2;
  4714. }
  4715. if ( effect == GNUSTO_EFFECT_STYLE )
  4716. {
  4717. if ( effect1 < 0 )
  4718. {
  4719. ui.set_colour( effect2, engine.effect(3) );
  4720. }
  4721. else
  4722. {
  4723. ui.set_style( effect1 );
  4724. }
  4725. }
  4726. if ( effect == GNUSTO_EFFECT_SPLITWINDOW )
  4727. {
  4728. ui.split_window( effect1 );
  4729. }
  4730. if ( effect == GNUSTO_EFFECT_SETWINDOW )
  4731. {
  4732. ui.set_window( effect1 );
  4733. }
  4734. if ( effect == GNUSTO_EFFECT_ERASEWINDOW )
  4735. {
  4736. ui.erase_window( effect1 );
  4737. }
  4738. if ( effect == GNUSTO_EFFECT_ERASELINE )
  4739. {
  4740. ui.erase_line( effect1 );
  4741. }
  4742. if ( effect == GNUSTO_EFFECT_SETCURSOR )
  4743. {
  4744. ui.set_cursor( effect1, effect2 );
  4745. }
  4746. if ( effect == GNUSTO_EFFECT_GETCURSOR )
  4747. {
  4748. stop = 1;
  4749. ui.get_cursor( effect1 );
  4750. }
  4751. if ( effect == GNUSTO_EFFECT_PRINTTABLE )
  4752. {
  4753. for ( i = 0; i < effect1; i++ )
  4754. {
  4755. ui.buffer += '\n' + engine.effect( 2 + i );
  4756. }
  4757. }
  4758. }
  4759. // Flush the buffer
  4760. ui.flush();
  4761. // Flush the status if we need to
  4762. // Should instead it be the first order? Might be better for screen readers etc
  4763. if ( ui.status.length )
  4764. {
  4765. this.orders.push({
  4766. code: 'stream',
  4767. to: 'status',
  4768. data: this.ui.status
  4769. });
  4770. ui.status = [];
  4771. }
  4772. // Process the orders
  4773. this.io.event( this.orders );
  4774. },
  4775. // Handler for input events to send to the VM
  4776. inputEvent: function( data )
  4777. {
  4778. var engine = this.e,
  4779. code = data.code,
  4780. response;
  4781. // Handle line input
  4782. if ( code == 'read' )
  4783. {
  4784. this.ui.buffer += data.response + '\n';
  4785. engine.answer( 0, data.terminator );
  4786. engine.answer( 1, data.response );
  4787. }
  4788. // Handle character input
  4789. if ( code == 'char' )
  4790. {
  4791. engine.answer( 0, data.response );
  4792. }
  4793. // Write the status window's cursor position
  4794. if ( code == 'get_cursor' )
  4795. {
  4796. engine.setWord( data.addr, data.pos[0] );
  4797. engine.setWord( data.addr + 2, data.pos[1] );
  4798. }
  4799. // Resume normal operation
  4800. this.run();
  4801. },
  4802. // Dummy func needed by get_cursor()
  4803. act: function(){}
  4804. });