1----------------------------------------------------------------------------
2-- LuaJIT PPC disassembler module.
3--
4-- Copyright (C) 2005-2017 Mike Pall. All rights reserved.
5-- Released under the MIT/X license. See Copyright Notice in luajit.h
6----------------------------------------------------------------------------
7-- This is a helper module used by the LuaJIT machine code dumper module.
8--
9-- It disassembles all common, non-privileged 32/64 bit PowerPC instructions
10-- plus the e500 SPE instructions and some Cell/Xenon extensions.
11--
12-- NYI: VMX, VMX128
13------------------------------------------------------------------------------
14
15local type = type
16local byte, format = string.byte, string.format
17local match, gmatch, gsub = string.match, string.gmatch, string.gsub
18local concat = table.concat
19local bit = require("bit")
20local band, bor, tohex = bit.band, bit.bor, bit.tohex
21local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
22
23------------------------------------------------------------------------------
24-- Primary and extended opcode maps
25------------------------------------------------------------------------------
26
27local map_crops = {
28  shift = 1, mask = 1023,
29  [0] = "mcrfXX",
30  [33] = "crnor|crnotCCC=", [129] = "crandcCCC",
31  [193] = "crxor|crclrCCC%", [225] = "crnandCCC",
32  [257] = "crandCCC", [289] = "creqv|crsetCCC%",
33  [417] = "crorcCCC", [449] = "cror|crmoveCCC=",
34  [16] = "b_lrKB", [528] = "b_ctrKB",
35  [150] = "isync",
36}
37
38local map_rlwinm = setmetatable({
39  shift = 0, mask = -1,
40},
41{ __index = function(t, x)
42    local rot = band(rshift(x, 11), 31)
43    local mb = band(rshift(x, 6), 31)
44    local me = band(rshift(x, 1), 31)
45    if mb == 0 and me == 31-rot then
46      return "slwiRR~A."
47    elseif me == 31 and mb == 32-rot then
48      return "srwiRR~-A."
49    else
50      return "rlwinmRR~AAA."
51    end
52  end
53})
54
55local map_rld = {
56  shift = 2, mask = 7,
57  [0] = "rldiclRR~HM.", "rldicrRR~HM.", "rldicRR~HM.", "rldimiRR~HM.",
58  {
59    shift = 1, mask = 1,
60    [0] = "rldclRR~RM.", "rldcrRR~RM.",
61  },
62}
63
64local map_ext = setmetatable({
65  shift = 1, mask = 1023,
66
67  [0] = "cmp_YLRR", [32] = "cmpl_YLRR",
68  [4] = "twARR", [68] = "tdARR",
69
70  [8] = "subfcRRR.", [40] = "subfRRR.",
71  [104] = "negRR.", [136] = "subfeRRR.",
72  [200] = "subfzeRR.", [232] = "subfmeRR.",
73  [520] = "subfcoRRR.", [552] = "subfoRRR.",
74  [616] = "negoRR.", [648] = "subfeoRRR.",
75  [712] = "subfzeoRR.", [744] = "subfmeoRR.",
76
77  [9] = "mulhduRRR.", [73] = "mulhdRRR.", [233] = "mulldRRR.",
78  [457] = "divduRRR.", [489] = "divdRRR.",
79  [745] = "mulldoRRR.",
80  [969] = "divduoRRR.", [1001] = "divdoRRR.",
81
82  [10] = "addcRRR.", [138] = "addeRRR.",
83  [202] = "addzeRR.", [234] = "addmeRR.", [266] = "addRRR.",
84  [522] = "addcoRRR.", [650] = "addeoRRR.",
85  [714] = "addzeoRR.", [746] = "addmeoRR.", [778] = "addoRRR.",
86
87  [11] = "mulhwuRRR.", [75] = "mulhwRRR.", [235] = "mullwRRR.",
88  [459] = "divwuRRR.", [491] = "divwRRR.",
89  [747] = "mullwoRRR.",
90  [971] = "divwouRRR.", [1003] = "divwoRRR.",
91
92  [15] = "iselltRRR", [47] = "iselgtRRR", [79] = "iseleqRRR",
93
94  [144] = { shift = 20, mask = 1, [0] = "mtcrfRZ~", "mtocrfRZ~", },
95  [19] = { shift = 20, mask = 1, [0] = "mfcrR", "mfocrfRZ", },
96  [371] = { shift = 11, mask = 1023, [392] = "mftbR", [424] = "mftbuR", },
97  [339] = {
98    shift = 11, mask = 1023,
99    [32] = "mferR", [256] = "mflrR", [288] = "mfctrR", [16] = "mfspefscrR",
100  },
101  [467] = {
102    shift = 11, mask = 1023,
103    [32] = "mtxerR", [256] = "mtlrR", [288] = "mtctrR", [16] = "mtspefscrR",
104  },
105
106  [20] = "lwarxRR0R", [84] = "ldarxRR0R",
107
108  [21] = "ldxRR0R", [53] = "lduxRRR",
109  [149] = "stdxRR0R", [181] = "stduxRRR",
110  [341] = "lwaxRR0R", [373] = "lwauxRRR",
111
112  [23] = "lwzxRR0R", [55] = "lwzuxRRR",
113  [87] = "lbzxRR0R", [119] = "lbzuxRRR",
114  [151] = "stwxRR0R", [183] = "stwuxRRR",
115  [215] = "stbxRR0R", [247] = "stbuxRRR",
116  [279] = "lhzxRR0R", [311] = "lhzuxRRR",
117  [343] = "lhaxRR0R", [375] = "lhauxRRR",
118  [407] = "sthxRR0R", [439] = "sthuxRRR",
119
120  [54] = "dcbst-R0R", [86] = "dcbf-R0R",
121  [150] = "stwcxRR0R.", [214] = "stdcxRR0R.",
122  [246] = "dcbtst-R0R", [278] = "dcbt-R0R",
123  [310] = "eciwxRR0R", [438] = "ecowxRR0R",
124  [470] = "dcbi-RR",
125
126  [598] = {
127    shift = 21, mask = 3,
128    [0] = "sync", "lwsync", "ptesync",
129  },
130  [758] = "dcba-RR",
131  [854] = "eieio", [982] = "icbi-R0R", [1014] = "dcbz-R0R",
132
133  [26] = "cntlzwRR~", [58] = "cntlzdRR~",
134  [122] = "popcntbRR~",
135  [154] = "prtywRR~", [186] = "prtydRR~",
136
137  [28] = "andRR~R.", [60] = "andcRR~R.", [124] = "nor|notRR~R=.",
138  [284] = "eqvRR~R.", [316] = "xorRR~R.",
139  [412] = "orcRR~R.", [444] = "or|mrRR~R=.", [476] = "nandRR~R.",
140  [508] = "cmpbRR~R",
141
142  [512] = "mcrxrX",
143
144  [532] = "ldbrxRR0R", [660] = "stdbrxRR0R",
145
146  [533] = "lswxRR0R", [597] = "lswiRR0A",
147  [661] = "stswxRR0R", [725] = "stswiRR0A",
148
149  [534] = "lwbrxRR0R", [662] = "stwbrxRR0R",
150  [790] = "lhbrxRR0R", [918] = "sthbrxRR0R",
151
152  [535] = "lfsxFR0R", [567] = "lfsuxFRR",
153  [599] = "lfdxFR0R", [631] = "lfduxFRR",
154  [663] = "stfsxFR0R", [695] = "stfsuxFRR",
155  [727] = "stfdxFR0R", [759] = "stfduxFR0R",
156  [855] = "lfiwaxFR0R",
157  [983] = "stfiwxFR0R",
158
159  [24] = "slwRR~R.",
160
161  [27] = "sldRR~R.", [536] = "srwRR~R.",
162  [792] = "srawRR~R.", [824] = "srawiRR~A.",
163
164  [794] = "sradRR~R.", [826] = "sradiRR~H.", [827] = "sradiRR~H.",
165  [922] = "extshRR~.", [954] = "extsbRR~.", [986] = "extswRR~.",
166
167  [539] = "srdRR~R.",
168},
169{ __index = function(t, x)
170    if band(x, 31) == 15 then return "iselRRRC" end
171  end
172})
173
174local map_ld = {
175  shift = 0, mask = 3,
176  [0] = "ldRRE", "lduRRE", "lwaRRE",
177}
178
179local map_std = {
180  shift = 0, mask = 3,
181  [0] = "stdRRE", "stduRRE",
182}
183
184local map_fps = {
185  shift = 5, mask = 1,
186  {
187    shift = 1, mask = 15,
188    [0] = false, false, "fdivsFFF.", false,
189    "fsubsFFF.", "faddsFFF.", "fsqrtsF-F.", false,
190    "fresF-F.", "fmulsFF-F.", "frsqrtesF-F.", false,
191    "fmsubsFFFF~.", "fmaddsFFFF~.", "fnmsubsFFFF~.", "fnmaddsFFFF~.",
192  }
193}
194
195local map_fpd = {
196  shift = 5, mask = 1,
197  [0] = {
198    shift = 1, mask = 1023,
199    [0] = "fcmpuXFF", [32] = "fcmpoXFF", [64] = "mcrfsXX",
200    [38] = "mtfsb1A.", [70] = "mtfsb0A.", [134] = "mtfsfiA>>-A>",
201    [8] = "fcpsgnFFF.", [40] = "fnegF-F.", [72] = "fmrF-F.",
202    [136] = "fnabsF-F.", [264] = "fabsF-F.",
203    [12] = "frspF-F.",
204    [14] = "fctiwF-F.", [15] = "fctiwzF-F.",
205    [583] = "mffsF.", [711] = "mtfsfZF.",
206    [392] = "frinF-F.", [424] = "frizF-F.",
207    [456] = "fripF-F.", [488] = "frimF-F.",
208    [814] = "fctidF-F.", [815] = "fctidzF-F.", [846] = "fcfidF-F.",
209  },
210  {
211    shift = 1, mask = 15,
212    [0] = false, false, "fdivFFF.", false,
213    "fsubFFF.", "faddFFF.", "fsqrtF-F.", "fselFFFF~.",
214    "freF-F.", "fmulFF-F.", "frsqrteF-F.", false,
215    "fmsubFFFF~.", "fmaddFFFF~.", "fnmsubFFFF~.", "fnmaddFFFF~.",
216  }
217}
218
219local map_spe = {
220  shift = 0, mask = 2047,
221
222  [512] = "evaddwRRR", [514] = "evaddiwRAR~",
223  [516] = "evsubwRRR~", [518] = "evsubiwRAR~",
224  [520] = "evabsRR", [521] = "evnegRR",
225  [522] = "evextsbRR", [523] = "evextshRR", [524] = "evrndwRR",
226  [525] = "evcntlzwRR", [526] = "evcntlswRR",
227
228  [527] = "brincRRR",
229
230  [529] = "evandRRR", [530] = "evandcRRR", [534] = "evxorRRR",
231  [535] = "evor|evmrRRR=", [536] = "evnor|evnotRRR=",
232  [537] = "eveqvRRR", [539] = "evorcRRR", [542] = "evnandRRR",
233
234  [544] = "evsrwuRRR", [545] = "evsrwsRRR",
235  [546] = "evsrwiuRRA", [547] = "evsrwisRRA",
236  [548] = "evslwRRR", [550] = "evslwiRRA",
237  [552] = "evrlwRRR", [553] = "evsplatiRS",
238  [554] = "evrlwiRRA", [555] = "evsplatfiRS",
239  [556] = "evmergehiRRR", [557] = "evmergeloRRR",
240  [558] = "evmergehiloRRR", [559] = "evmergelohiRRR",
241
242  [560] = "evcmpgtuYRR", [561] = "evcmpgtsYRR",
243  [562] = "evcmpltuYRR", [563] = "evcmpltsYRR",
244  [564] = "evcmpeqYRR",
245
246  [632] = "evselRRR", [633] = "evselRRRW",
247  [634] = "evselRRRW", [635] = "evselRRRW",
248  [636] = "evselRRRW", [637] = "evselRRRW",
249  [638] = "evselRRRW", [639] = "evselRRRW",
250
251  [640] = "evfsaddRRR", [641] = "evfssubRRR",
252  [644] = "evfsabsRR", [645] = "evfsnabsRR", [646] = "evfsnegRR",
253  [648] = "evfsmulRRR", [649] = "evfsdivRRR",
254  [652] = "evfscmpgtYRR", [653] = "evfscmpltYRR", [654] = "evfscmpeqYRR",
255  [656] = "evfscfuiR-R", [657] = "evfscfsiR-R",
256  [658] = "evfscfufR-R", [659] = "evfscfsfR-R",
257  [660] = "evfsctuiR-R", [661] = "evfsctsiR-R",
258  [662] = "evfsctufR-R", [663] = "evfsctsfR-R",
259  [664] = "evfsctuizR-R", [666] = "evfsctsizR-R",
260  [668] = "evfststgtYRR", [669] = "evfststltYRR", [670] = "evfststeqYRR",
261
262  [704] = "efsaddRRR", [705] = "efssubRRR",
263  [708] = "efsabsRR", [709] = "efsnabsRR", [710] = "efsnegRR",
264  [712] = "efsmulRRR", [713] = "efsdivRRR",
265  [716] = "efscmpgtYRR", [717] = "efscmpltYRR", [718] = "efscmpeqYRR",
266  [719] = "efscfdR-R",
267  [720] = "efscfuiR-R", [721] = "efscfsiR-R",
268  [722] = "efscfufR-R", [723] = "efscfsfR-R",
269  [724] = "efsctuiR-R", [725] = "efsctsiR-R",
270  [726] = "efsctufR-R", [727] = "efsctsfR-R",
271  [728] = "efsctuizR-R", [730] = "efsctsizR-R",
272  [732] = "efststgtYRR", [733] = "efststltYRR", [734] = "efststeqYRR",
273
274  [736] = "efdaddRRR", [737] = "efdsubRRR",
275  [738] = "efdcfuidR-R", [739] = "efdcfsidR-R",
276  [740] = "efdabsRR", [741] = "efdnabsRR", [742] = "efdnegRR",
277  [744] = "efdmulRRR", [745] = "efddivRRR",
278  [746] = "efdctuidzR-R", [747] = "efdctsidzR-R",
279  [748] = "efdcmpgtYRR", [749] = "efdcmpltYRR", [750] = "efdcmpeqYRR",
280  [751] = "efdcfsR-R",
281  [752] = "efdcfuiR-R", [753] = "efdcfsiR-R",
282  [754] = "efdcfufR-R", [755] = "efdcfsfR-R",
283  [756] = "efdctuiR-R", [757] = "efdctsiR-R",
284  [758] = "efdctufR-R", [759] = "efdctsfR-R",
285  [760] = "efdctuizR-R", [762] = "efdctsizR-R",
286  [764] = "efdtstgtYRR", [765] = "efdtstltYRR", [766] = "efdtsteqYRR",
287
288  [768] = "evlddxRR0R", [769] = "evlddRR8",
289  [770] = "evldwxRR0R", [771] = "evldwRR8",
290  [772] = "evldhxRR0R", [773] = "evldhRR8",
291  [776] = "evlhhesplatxRR0R", [777] = "evlhhesplatRR2",
292  [780] = "evlhhousplatxRR0R", [781] = "evlhhousplatRR2",
293  [782] = "evlhhossplatxRR0R", [783] = "evlhhossplatRR2",
294  [784] = "evlwhexRR0R", [785] = "evlwheRR4",
295  [788] = "evlwhouxRR0R", [789] = "evlwhouRR4",
296  [790] = "evlwhosxRR0R", [791] = "evlwhosRR4",
297  [792] = "evlwwsplatxRR0R", [793] = "evlwwsplatRR4",
298  [796] = "evlwhsplatxRR0R", [797] = "evlwhsplatRR4",
299
300  [800] = "evstddxRR0R", [801] = "evstddRR8",
301  [802] = "evstdwxRR0R", [803] = "evstdwRR8",
302  [804] = "evstdhxRR0R", [805] = "evstdhRR8",
303  [816] = "evstwhexRR0R", [817] = "evstwheRR4",
304  [820] = "evstwhoxRR0R", [821] = "evstwhoRR4",
305  [824] = "evstwwexRR0R", [825] = "evstwweRR4",
306  [828] = "evstwwoxRR0R", [829] = "evstwwoRR4",
307
308  [1027] = "evmhessfRRR", [1031] = "evmhossfRRR", [1032] = "evmheumiRRR",
309  [1033] = "evmhesmiRRR", [1035] = "evmhesmfRRR", [1036] = "evmhoumiRRR",
310  [1037] = "evmhosmiRRR", [1039] = "evmhosmfRRR", [1059] = "evmhessfaRRR",
311  [1063] = "evmhossfaRRR", [1064] = "evmheumiaRRR", [1065] = "evmhesmiaRRR",
312  [1067] = "evmhesmfaRRR", [1068] = "evmhoumiaRRR", [1069] = "evmhosmiaRRR",
313  [1071] = "evmhosmfaRRR", [1095] = "evmwhssfRRR", [1096] = "evmwlumiRRR",
314  [1100] = "evmwhumiRRR", [1101] = "evmwhsmiRRR", [1103] = "evmwhsmfRRR",
315  [1107] = "evmwssfRRR", [1112] = "evmwumiRRR", [1113] = "evmwsmiRRR",
316  [1115] = "evmwsmfRRR", [1127] = "evmwhssfaRRR", [1128] = "evmwlumiaRRR",
317  [1132] = "evmwhumiaRRR", [1133] = "evmwhsmiaRRR", [1135] = "evmwhsmfaRRR",
318  [1139] = "evmwssfaRRR", [1144] = "evmwumiaRRR", [1145] = "evmwsmiaRRR",
319  [1147] = "evmwsmfaRRR",
320
321  [1216] = "evaddusiaawRR", [1217] = "evaddssiaawRR",
322  [1218] = "evsubfusiaawRR", [1219] = "evsubfssiaawRR",
323  [1220] = "evmraRR",
324  [1222] = "evdivwsRRR", [1223] = "evdivwuRRR",
325  [1224] = "evaddumiaawRR", [1225] = "evaddsmiaawRR",
326  [1226] = "evsubfumiaawRR", [1227] = "evsubfsmiaawRR",
327
328  [1280] = "evmheusiaawRRR", [1281] = "evmhessiaawRRR",
329  [1283] = "evmhessfaawRRR", [1284] = "evmhousiaawRRR",
330  [1285] = "evmhossiaawRRR", [1287] = "evmhossfaawRRR",
331  [1288] = "evmheumiaawRRR", [1289] = "evmhesmiaawRRR",
332  [1291] = "evmhesmfaawRRR", [1292] = "evmhoumiaawRRR",
333  [1293] = "evmhosmiaawRRR", [1295] = "evmhosmfaawRRR",
334  [1320] = "evmhegumiaaRRR", [1321] = "evmhegsmiaaRRR",
335  [1323] = "evmhegsmfaaRRR", [1324] = "evmhogumiaaRRR",
336  [1325] = "evmhogsmiaaRRR", [1327] = "evmhogsmfaaRRR",
337  [1344] = "evmwlusiaawRRR", [1345] = "evmwlssiaawRRR",
338  [1352] = "evmwlumiaawRRR", [1353] = "evmwlsmiaawRRR",
339  [1363] = "evmwssfaaRRR", [1368] = "evmwumiaaRRR",
340  [1369] = "evmwsmiaaRRR", [1371] = "evmwsmfaaRRR",
341  [1408] = "evmheusianwRRR", [1409] = "evmhessianwRRR",
342  [1411] = "evmhessfanwRRR", [1412] = "evmhousianwRRR",
343  [1413] = "evmhossianwRRR", [1415] = "evmhossfanwRRR",
344  [1416] = "evmheumianwRRR", [1417] = "evmhesmianwRRR",
345  [1419] = "evmhesmfanwRRR", [1420] = "evmhoumianwRRR",
346  [1421] = "evmhosmianwRRR", [1423] = "evmhosmfanwRRR",
347  [1448] = "evmhegumianRRR", [1449] = "evmhegsmianRRR",
348  [1451] = "evmhegsmfanRRR", [1452] = "evmhogumianRRR",
349  [1453] = "evmhogsmianRRR", [1455] = "evmhogsmfanRRR",
350  [1472] = "evmwlusianwRRR", [1473] = "evmwlssianwRRR",
351  [1480] = "evmwlumianwRRR", [1481] = "evmwlsmianwRRR",
352  [1491] = "evmwssfanRRR", [1496] = "evmwumianRRR",
353  [1497] = "evmwsmianRRR", [1499] = "evmwsmfanRRR",
354}
355
356local map_pri = {
357  [0] = false,	false,		"tdiARI",	"twiARI",
358  map_spe,	false,		false,		"mulliRRI",
359  "subficRRI",	false,		"cmpl_iYLRU",	"cmp_iYLRI",
360  "addicRRI",	"addic.RRI",	"addi|liRR0I",	"addis|lisRR0I",
361  "b_KBJ",	"sc",		 "bKJ",		map_crops,
362  "rlwimiRR~AAA.", map_rlwinm,	false,		"rlwnmRR~RAA.",
363  "oriNRR~U",	"orisRR~U",	"xoriRR~U",	"xorisRR~U",
364  "andi.RR~U",	"andis.RR~U",	map_rld,	map_ext,
365  "lwzRRD",	"lwzuRRD",	"lbzRRD",	"lbzuRRD",
366  "stwRRD",	"stwuRRD",	"stbRRD",	"stbuRRD",
367  "lhzRRD",	"lhzuRRD",	"lhaRRD",	"lhauRRD",
368  "sthRRD",	"sthuRRD",	"lmwRRD",	"stmwRRD",
369  "lfsFRD",	"lfsuFRD",	"lfdFRD",	"lfduFRD",
370  "stfsFRD",	"stfsuFRD",	"stfdFRD",	"stfduFRD",
371  false,	false,		map_ld,		map_fps,
372  false,	false,		map_std,	map_fpd,
373}
374
375------------------------------------------------------------------------------
376
377local map_gpr = {
378  [0] = "r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7",
379  "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
380  "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
381  "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
382}
383
384local map_cond = { [0] = "lt", "gt", "eq", "so", "ge", "le", "ne", "ns", }
385
386-- Format a condition bit.
387local function condfmt(cond)
388  if cond <= 3 then
389    return map_cond[band(cond, 3)]
390  else
391    return format("4*cr%d+%s", rshift(cond, 2), map_cond[band(cond, 3)])
392  end
393end
394
395------------------------------------------------------------------------------
396
397-- Output a nicely formatted line with an opcode and operands.
398local function putop(ctx, text, operands)
399  local pos = ctx.pos
400  local extra = ""
401  if ctx.rel then
402    local sym = ctx.symtab[ctx.rel]
403    if sym then extra = "\t->"..sym end
404  end
405  if ctx.hexdump > 0 then
406    ctx.out(format("%08x  %s  %-7s %s%s\n",
407	    ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
408  else
409    ctx.out(format("%08x  %-7s %s%s\n",
410	    ctx.addr+pos, text, concat(operands, ", "), extra))
411  end
412  ctx.pos = pos + 4
413end
414
415-- Fallback for unknown opcodes.
416local function unknown(ctx)
417  return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
418end
419
420-- Disassemble a single instruction.
421local function disass_ins(ctx)
422  local pos = ctx.pos
423  local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
424  local op = bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
425  local operands = {}
426  local last = nil
427  local rs = 21
428  ctx.op = op
429  ctx.rel = nil
430
431  local opat = map_pri[rshift(b0, 2)]
432  while type(opat) ~= "string" do
433    if not opat then return unknown(ctx) end
434    opat = opat[band(rshift(op, opat.shift), opat.mask)]
435  end
436  local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
437  local altname, pat2 = match(pat, "|([a-z0-9_.]*)(.*)")
438  if altname then pat = pat2 end
439
440  for p in gmatch(pat, ".") do
441    local x = nil
442    if p == "R" then
443      x = map_gpr[band(rshift(op, rs), 31)]
444      rs = rs - 5
445    elseif p == "F" then
446      x = "f"..band(rshift(op, rs), 31)
447      rs = rs - 5
448    elseif p == "A" then
449      x = band(rshift(op, rs), 31)
450      rs = rs - 5
451    elseif p == "S" then
452      x = arshift(lshift(op, 27-rs), 27)
453      rs = rs - 5
454    elseif p == "I" then
455      x = arshift(lshift(op, 16), 16)
456    elseif p == "U" then
457      x = band(op, 0xffff)
458    elseif p == "D" or p == "E" then
459      local disp = arshift(lshift(op, 16), 16)
460      if p == "E" then disp = band(disp, -4) end
461      if last == "r0" then last = "0" end
462      operands[#operands] = format("%d(%s)", disp, last)
463    elseif p >= "2" and p <= "8" then
464      local disp = band(rshift(op, rs), 31) * p
465      if last == "r0" then last = "0" end
466      operands[#operands] = format("%d(%s)", disp, last)
467    elseif p == "H" then
468      x = band(rshift(op, rs), 31) + lshift(band(op, 2), 4)
469      rs = rs - 5
470    elseif p == "M" then
471      x = band(rshift(op, rs), 31) + band(op, 0x20)
472    elseif p == "C" then
473      x = condfmt(band(rshift(op, rs), 31))
474      rs = rs - 5
475    elseif p == "B" then
476      local bo = rshift(op, 21)
477      local cond = band(rshift(op, 16), 31)
478      local cn = ""
479      rs = rs - 10
480      if band(bo, 4) == 0 then
481	cn = band(bo, 2) == 0 and "dnz" or "dz"
482	if band(bo, 0x10) == 0 then
483	  cn = cn..(band(bo, 8) == 0 and "f" or "t")
484	end
485	if band(bo, 0x10) == 0 then x = condfmt(cond) end
486	name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
487      elseif band(bo, 0x10) == 0 then
488	cn = map_cond[band(cond, 3) + (band(bo, 8) == 0 and 4 or 0)]
489	if cond > 3 then x = "cr"..rshift(cond, 2) end
490	name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
491      end
492      name = gsub(name, "_", cn)
493    elseif p == "J" then
494      x = arshift(lshift(op, 27-rs), 29-rs)*4
495      if band(op, 2) == 0 then x = ctx.addr + pos + x end
496      ctx.rel = x
497      x = "0x"..tohex(x)
498    elseif p == "K" then
499      if band(op, 1) ~= 0 then name = name.."l" end
500      if band(op, 2) ~= 0 then name = name.."a" end
501    elseif p == "X" or p == "Y" then
502      x = band(rshift(op, rs+2), 7)
503      if x == 0 and p == "Y" then x = nil else x = "cr"..x end
504      rs = rs - 5
505    elseif p == "W" then
506      x = "cr"..band(op, 7)
507    elseif p == "Z" then
508      x = band(rshift(op, rs-4), 255)
509      rs = rs - 10
510    elseif p == ">" then
511      operands[#operands] = rshift(operands[#operands], 1)
512    elseif p == "0" then
513      if last == "r0" then
514	operands[#operands] = nil
515	if altname then name = altname end
516      end
517    elseif p == "L" then
518      name = gsub(name, "_", band(op, 0x00200000) ~= 0 and "d" or "w")
519    elseif p == "." then
520      if band(op, 1) == 1 then name = name.."." end
521    elseif p == "N" then
522      if op == 0x60000000 then name = "nop"; break end
523    elseif p == "~" then
524      local n = #operands
525      operands[n-1],  operands[n] = operands[n], operands[n-1]
526    elseif p == "=" then
527      local n = #operands
528      if last == operands[n-1] then
529	operands[n] = nil
530	name = altname
531      end
532    elseif p == "%" then
533      local n = #operands
534      if last == operands[n-1] and last == operands[n-2] then
535	operands[n] = nil
536	operands[n-1] = nil
537	name = altname
538      end
539    elseif p == "-" then
540      rs = rs - 5
541    else
542      assert(false)
543    end
544    if x then operands[#operands+1] = x; last = x end
545  end
546
547  return putop(ctx, name, operands)
548end
549
550------------------------------------------------------------------------------
551
552-- Disassemble a block of code.
553local function disass_block(ctx, ofs, len)
554  if not ofs then ofs = 0 end
555  local stop = len and ofs+len or #ctx.code
556  stop = stop - stop % 4
557  ctx.pos = ofs - ofs % 4
558  ctx.rel = nil
559  while ctx.pos < stop do disass_ins(ctx) end
560end
561
562-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
563local function create(code, addr, out)
564  local ctx = {}
565  ctx.code = code
566  ctx.addr = addr or 0
567  ctx.out = out or io.write
568  ctx.symtab = {}
569  ctx.disass = disass_block
570  ctx.hexdump = 8
571  return ctx
572end
573
574-- Simple API: disassemble code (a string) at address and output via out.
575local function disass(code, addr, out)
576  create(code, addr, out):disass()
577end
578
579-- Return register name for RID.
580local function regname(r)
581  if r < 32 then return map_gpr[r] end
582  return "f"..(r-32)
583end
584
585-- Public module functions.
586return {
587  create = create,
588  disass = disass,
589  regname = regname
590}
591
592