xref: /inferno-os/utils/tl/thumb.c (revision 50b0dbb170df61467e42c7ea4deb0b5692d15f4c)
1 #include "l.h"
2 
3 static long thumboprr(int);
4 static long thumboprrr(int, int);
5 static long thumbopirr(int , int);
6 static long thumbopri(int);
7 static long thumbophh(int);
8 static long thumbopbra(int);
9 static long thumbopmv(int, int);
10 static void lowreg(Prog *, int);
11 static void mult(Prog *, int, int);
12 static void numr(Prog *, int, int, int);
13 static void regis(Prog *, int, int, int);
14 static void dis(int, int);
15 
16 // build a constant using neg, add and shift - only worth it if < 6 bytes */
17 static int
18 immbuildcon(int c, Prog *p)
19 {
20 	int n = 0;
21 
22 	USED(p);
23 	if(c >= 0 && c <= 255)
24 		return 0;			// mv
25 	if(c >= -255 && c < 0)	// mv, neg
26 		return 1;
27 	if(c >= 256 && c <= 510)	// mv, add
28 		return 1;
29 	if(c < 0)
30 		return 0;
31 	while(!(c & 1)){
32 		n++;
33 		c >>= 1;
34 	}
35 	if(c >= 0 && c <= 255)	// mv, lsl
36 		return 1;
37 	return 0;
38 }
39 
40 // positive 5 bit offset from register - O(R)
41 // positive 8 bit offset from register - mov O, R then [R, R]
42 // otherwise O goes in literal pool - mov O1(PC), R then [R, R]
43 static int
44 immoreg(int off, Prog *p)
45 {
46 	int v = 1;
47 	int as = p->as;
48 
49 	if(off < 0)
50 		return C_GOREG;
51 	if(as == AMOVW)
52 		v = 4;
53 	else if(as == AMOVH || as == AMOVHU)
54 		v = 2;
55 	else if(as == AMOVB || as == AMOVBU)
56 		v = 1;
57 	else
58 		diag("bad op in immoreg");
59 	if(off/v <= 31)
60 		return C_SOREG;
61 	if(off <= 255)
62 		return C_LOREG;
63 	return C_GOREG;
64 }
65 
66 // positive 8 bit - mov O, R then 0(R)
67 // otherwise O goes in literal pool - mov O1(PC), R then 0(R)
68 static int
69 immacon(int off, Prog *p, int t1, int t2)
70 {
71 	USED(p);
72 	if(off < 0)
73 		return t2;
74 	if(off <= 255)
75 		return t1;
76 	return t2;
77 }
78 
79 // unsigned 8 bit in words
80 static int
81 immauto(int off, Prog *p)
82 {
83 	if(p->as != AMOVW)
84 		diag("bad op in immauto");
85 	mult(p, off, 4);
86 	if(off >= 0 && off <= 1020)
87 		return C_SAUTO;
88 	return C_LAUTO;
89 }
90 
91 static int
92 immsmall(int off, Prog *p, int t1, int t2, int t3)
93 {
94 	USED(p);
95 	if(off >= 0 && off <= 7)
96 		return t1;
97 	if(off >= 0 && off <= 255)
98 		return t2;
99 	return t3;
100 }
101 
102 static int
103 immcon(int off, Prog *p)
104 {
105 	int as = p->as;
106 
107 	if(as == ASLL || as == ASRL || as == ASRA)
108 		return C_SCON;
109 	if(p->to.type == D_REG && p->to.reg == REGSP){
110 		if(as == AADD || as == ASUB){
111 			if(off >= 0 && off <= 508)
112 				return C_SCON;
113 			if(as == ASUB){
114 				p->as = AADD;
115 				p->from.offset = -p->from.offset;
116 			}
117 			return C_LCON;
118 		}
119 		diag("unknown type in immcon");
120 	}
121 	if(as == AADD || as == ASUB){
122 		if(p->reg != NREG)
123 			return immsmall(off, p, C_SCON, C_LCON, C_GCON);
124 		return immacon(off, p, C_SCON, C_LCON);
125 	}
126 	if(as == AMOVW && p->from.type == D_CONST && p->to.type == D_REG && immbuildcon(off, p))
127 		return C_BCON;
128 	if(as == ACMP && p->from.type == D_CONST && immbuildcon(off, p))
129 		return C_BCON;
130 	if(as == ACMP || as == AMOVW)
131 		return immacon(off, p, C_SCON, C_LCON);
132 	return C_LCON;
133 }
134 
135 int
136 thumbaclass(Adr *a, Prog *p)
137 {
138 	Sym *s;
139 	int t;
140 
141 	switch(a->type) {
142 	case D_NONE:
143 		return C_NONE;
144 	case D_REG:
145 		if(a->reg == REGSP)
146 			return C_SP;
147 		if(a->reg == REGPC)
148 			return C_PC;
149 		if(a->reg >= 8)
150 			return C_HREG;
151 		return C_REG;
152 	case D_SHIFT:
153 		diag("D_SHIFT in thumbaclass");
154 		return C_SHIFT;
155 	case D_FREG:
156 		diag("D_FREG in thumbaclass");
157 		return C_FREG;
158 	case D_FPCR:
159 		diag("D_FPCR in thumbaclass");
160 		return C_FCR;
161 	case D_OREG:
162 		switch(a->name) {
163 		case D_EXTERN:
164 		case D_STATIC:
165 			if(a->sym == 0 || a->sym->name == 0) {
166 				print("null sym external\n");
167 				print("%D\n", a);
168 				return C_GOK;
169 			}
170 			t = a->sym->type;
171 			if(t == 0 || t == SXREF) {
172 				diag("undefined external: %s in %s\n",
173 					a->sym->name, TNAME);
174 				a->sym->type = SDATA;
175 			}
176 			instoffset = a->sym->value + a->offset + INITDAT;
177 			return C_LEXT;	/* INITDAT unknown at this stage */
178 			// return immacon(instoffset, p, C_SEXT, C_LEXT);
179 		case D_AUTO:
180 			instoffset = autosize + a->offset;
181 			return immauto(instoffset, p);
182 		case D_PARAM:
183 			instoffset = autosize + a->offset + 4L;
184 // print("D_PARAM %s %d+%d+%d = %d\n", a->sym != S ? a->sym->name : "noname", autosize, a->offset, 4, autosize+a->offset+4);
185 			return immauto(instoffset, p);
186 		case D_NONE:
187 			instoffset = a->offset;
188 			if(a->reg == REGSP)
189 				return immauto(instoffset, p);
190 			else
191 				return immoreg(instoffset, p);
192 		}
193 		return C_GOK;
194 	case D_PSR:
195 		diag("D_PSR in thumbaclass");
196 		return C_PSR;
197 	case D_OCONST:
198 		switch(a->name) {
199 		case D_EXTERN:
200 		case D_STATIC:
201 			s = a->sym;
202 			t = s->type;
203 			if(t == 0 || t == SXREF) {
204 				diag("undefined external: %s in %s\n",
205 					s->name, TNAME);
206 				s->type = SDATA;
207 			}
208 			instoffset = s->value + a->offset + INITDAT;
209 			if(s->type == STEXT || s->type == SLEAF){
210 				instoffset = s->value + a->offset;
211 #ifdef CALLEEBX
212 				instoffset += fnpinc(s);
213 #else
214 				if(s->thumb)
215 					instoffset++;	// T bit
216 #endif
217 				return C_LCON;
218 			}
219 			return C_LCON;	/* INITDAT unknown at this stage */
220 			// return immcon(instoffset, p);
221 		}
222 		return C_GOK;
223 	case D_FCONST:
224 		diag("D_FCONST in thumaclass");
225 		return C_FCON;
226 	case D_CONST:
227 		switch(a->name) {
228 		case D_NONE:
229 			instoffset = a->offset;
230 			if(a->reg != NREG)
231 				goto aconsize;
232 			return immcon(instoffset, p);
233 		case D_EXTERN:
234 		case D_STATIC:
235 			s = a->sym;
236 			if(s == S)
237 				break;
238 			t = s->type;
239 			switch(t) {
240 			case 0:
241 			case SXREF:
242 				diag("undefined external: %s in %s\n",
243 					s->name, TNAME);
244 				s->type = SDATA;
245 				break;
246 			case SCONST:
247 			case STEXT:
248 			case SLEAF:
249 				instoffset = s->value + a->offset;
250 #ifdef CALLEEBX
251 				instoffset += fnpinc(s);
252 #else
253 				if(s->thumb)
254 					instoffset++;	// T bit
255 #endif
256 				return C_LCON;
257 			}
258 			instoffset = s->value + a->offset + INITDAT;
259 			return C_LCON;	/* INITDAT unknown at this stage */
260 			// return immcon(instoffset, p);
261 		case D_AUTO:
262 			instoffset = autosize + a->offset;
263 			goto aconsize;
264 		case D_PARAM:
265 			instoffset = autosize + a->offset + 4L;
266 		aconsize:
267 			if(p->from.reg == REGSP || p->from.reg == NREG)
268 				return instoffset >= 0 && instoffset < 1024 ? C_SACON : C_GACON;
269 			else if(p->from.reg == p->to.reg)
270 				return immacon(instoffset, p, C_SACON, C_GACON);
271 			return immsmall(instoffset, p, C_SACON, C_LACON, C_GACON);
272 		}
273 		return C_GOK;
274 	case D_BRANCH: {
275 		int v, va;
276 
277 		p->align = 0;
278 		v = -4;
279 		va = 0;
280 		if(p->cond != P){
281 			v = (p->cond->pc - p->pc) - 4;
282 			va = p->cond->pc;
283 		}
284 		instoffset = v;
285 		if(p->as == AB){
286 			if(v >= -2048 && v <= 2046)
287 				return C_SBRA;
288 			p->align = 4;
289 			instoffset = va;
290 			return C_LBRA;
291 		}
292 		if(p->as == ABL){
293 #ifdef CALLEEBX
294 			int e;
295 
296 			if((e = fninc(p->to.sym))) {
297 				v += e;
298 				va += e;
299 				instoffset += e;
300 			}
301 #endif
302 			if(v >= -4194304 && v <= 4194302)
303 				return C_SBRA;
304 			p->align = 2;
305 			instoffset = va;
306 			return C_LBRA;
307 		}
308 		if(p->as == ABX){
309 			v = va;
310 			if(v >= 0 && v <= 255)
311 				return C_SBRA;
312 			p->align = 2;
313 			instoffset = va;
314 			return C_LBRA;
315 		}
316 		if(v >= -256 && v <= 254)
317 			return C_SBRA;
318 		if(v >= -(2048-2) && v <= (2046+2))
319 			return C_LBRA;
320 		p->align = 2;
321 		instoffset = va;
322 		return C_GBRA;
323 	}
324 	}
325 	return C_GOK;
326 }
327 
328 // as a1 a2 a3 type size param lit vers
329 Optab thumboptab[] =
330 {
331 	{ ATEXT,		C_LEXT,		C_NONE,		C_LCON,		0,	0,	0 },
332 	{ ATEXT,		C_LEXT,		C_REG,		C_LCON,		0,	0,	0 },
333 	{ AMVN,		C_REG,		C_NONE,		C_REG,		1,	2,	0 },
334 	{ ASRL,		C_REG,		C_NONE,		C_REG,		1,	2,	0 },
335 	{ ACMP,		C_REG,		C_REG,		C_NONE,		1,	2,	0 },
336 	{ ACMN,		C_REG,		C_REG,		C_NONE,		1,	2,	0 },
337 	{ AADD,		C_REG,		C_REG,		C_REG,		2,	2,	0 },
338 	{ AADD,		C_REG,		C_NONE,		C_REG,		2,	2,	0 },
339 	{ AADD,		C_SCON,		C_REG,		C_REG,		3,	2,	0 },
340 	{ AADD,		C_LCON,		C_REG,		C_REG,		49,	4,	0 },
341 	{ AADD,		C_GCON,		C_REG,		C_REG,		36,	4,	0,	LFROM },
342 	// { AADD,		C_LCON,		C_NONE,		C_REG,		3,	2,	0,	LFROM },
343 	{ ASRL,		C_SCON,		C_REG,		C_REG,		4,	2,	0 },
344 	{ ASRL,		C_SCON,		C_NONE,		C_REG,		4,	2,	0 },
345 	{ AADD,		C_SCON,		C_NONE,		C_REG,		5,	2,	0 },
346 	{ AADD,		C_LCON,		C_NONE,		C_REG,		37,	4,	0,	LFROM },
347 	{ ACMP,		C_SCON,		C_REG,		C_NONE,		5,	2,	0 },
348 	{ ACMP,		C_BCON,		C_REG,		C_NONE,		48,	6,	0 },
349 	{ ACMP,		C_LCON,		C_REG,		C_NONE,		39,	4,	0,	LFROM },
350 	{ AMOVW,		C_SCON,		C_NONE,		C_REG,		5,	2,	0 },
351 	{ AMOVW,		C_BCON,		C_NONE,		C_REG,		47,	4,	0 },
352 	{ AMOVW,		C_LCON,		C_NONE,		C_REG,		38,	2,	0,	LFROM },
353 	// { AADD,		C_LCON,		C_PC,		C_REG,		6,	2,	0,	LFROM },
354 	// { AADD,		C_LCON,		C_SP,		C_REG,		6,	2,	0,	LFROM },
355 	{ AADD,		C_SCON,		C_NONE,		C_SP,		7,	2,	0 },
356 	{ AADD,		C_LCON,		C_NONE,		C_SP,		40,	4,	0,	LFROM },
357 	{ AADD,		C_REG,		C_NONE,		C_HREG,		8,	2,	0 },
358 	{ AADD,		C_HREG,		C_NONE,		C_REG,		8,	2,	0 },
359 	{ AADD,		C_HREG,		C_NONE,		C_HREG,		8,	2,	0 },
360 	{ AMOVW,		C_REG,		C_NONE,		C_HREG,		8,	2,	0 },
361 	{ AMOVW,		C_HREG,		C_NONE,		C_REG,		8,	2,	0 },
362 	{ AMOVW,		C_HREG,		C_NONE,		C_HREG,		8,	2,	0 },
363 	{ ACMP,		C_REG,		C_HREG,		C_NONE,		8,	2,	0 },
364 	{ ACMP,		C_HREG,		C_REG,		C_NONE,		8,	2,	0 },
365 	{ ACMP,		C_HREG,		C_HREG,		C_NONE,		8,	2,	0 },
366 	{ AB,			C_NONE,		C_NONE,		C_SBRA,		9,	2,	0,	LPOOL },
367 	{ ABEQ,		C_NONE,		C_NONE,		C_SBRA,		10,	2,	0 },
368 	{ ABL,		C_NONE,		C_NONE,		C_SBRA,		11,	4,	0 },
369 	{ ABX,		C_NONE,		C_NONE,		C_SBRA,		12,	10,	0 },
370 	{ AB,			C_NONE,		C_NONE,		C_LBRA,		41,	8,	0,	LPOOL },
371 	{ ABEQ,		C_NONE,		C_NONE,		C_LBRA,		46,	4,	0 },
372 	{ ABL,		C_NONE,		C_NONE,		C_LBRA,		43,	14,	0 },
373 	{ ABX,		C_NONE,		C_NONE,		C_LBRA,		44,	14,	0 },
374 	{ ABEQ,		C_NONE,		C_NONE,		C_GBRA,		42,  10, 	0 },
375 	// { AB,		C_NONE,		C_NONE,		C_SOREG,		13,	0,	0 },
376 	// { ABL,		C_NONE,		C_NONE,		C_SOREG,		14,	0,	0 },
377 	{ ABL,		C_NONE,		C_NONE,		C_REG,		51,	4,	0 },
378 	{ ABX,		C_NONE,		C_NONE,		C_REG,		15,	8,	0 },
379 	{ ABX,		C_NONE,		C_NONE,		C_HREG,		15,	8,	0 },
380 	{ ABXRET,		C_NONE,		C_NONE,		C_REG,		45,	2,	0 },
381 	{ ABXRET,		C_NONE,		C_NONE,		C_HREG,		45,	2,	0 },
382 	{ ASWI,		C_NONE,		C_NONE,		C_LCON,		16,	2,	0 },
383 	{ AWORD,		C_NONE,		C_NONE,		C_LCON,		17,	4,	0 },
384 	{ AWORD,		C_NONE,		C_NONE,		C_GCON,		17,	4,	0 },
385 	{ AWORD,		C_NONE,		C_NONE,		C_LEXT,		17,	4, 	0 },
386 	{ ADWORD,	C_LCON,		C_NONE,		C_LCON,		50,	8,	0 },
387 	{ AMOVW,		C_SAUTO,		C_NONE,		C_REG,		18,	2,	REGSP },
388 	{ AMOVW,		C_LAUTO,		C_NONE,		C_REG,		33,	6,	0,	LFROM  },
389 	// { AMOVW,		C_OFFPC,		C_NONE,		C_REG,		18,	2,	REGPC,	LFROM  },
390 	{ AMOVW,		C_SEXT,		C_NONE,		C_REG,		30,	4,	0 },
391 	{ AMOVW,		C_SOREG,		C_NONE,		C_REG,		19,	2,	0 },
392 	{ AMOVHU,	C_SEXT,		C_NONE,		C_REG,		30,	4,	0 },
393 	{ AMOVHU,	C_SOREG,		C_NONE,		C_REG,		19,	2,	0 },
394 	{ AMOVBU,	C_SEXT,		C_NONE,		C_REG,		30,	4,	0 },
395 	{ AMOVBU,	C_SOREG,		C_NONE,		C_REG,		19,	2,	0 },
396 	{ AMOVW,		C_REG,		C_NONE,		C_SAUTO,		20,	2,	0 },
397 	{ AMOVW,		C_REG,		C_NONE,		C_LAUTO,		34,	6,	0,	LTO },
398 	{ AMOVW,		C_REG,		C_NONE,		C_SEXT,		31,	4,	0 },
399 	{ AMOVW,		C_REG,		C_NONE,		C_SOREG,		21,	2,	0 },
400 	{ AMOVH,		C_REG,		C_NONE,		C_SEXT,		31,	4,	0 },
401 	{ AMOVH,		C_REG,		C_NONE,		C_SOREG,		21,	2,	0 },
402 	{ AMOVB,		C_REG,		C_NONE,		C_SEXT,		31,	4,	0 },
403 	{ AMOVB,		C_REG,		C_NONE,		C_SOREG,		21,	2,	0 },
404 	{ AMOVHU,	C_REG,		C_NONE,		C_SEXT,		31,	4,	0 },
405 	{ AMOVHU,	C_REG,		C_NONE,		C_SOREG,		21,	2,	0 },
406 	{ AMOVBU,	C_REG,		C_NONE,		C_SEXT,		31,	4,	0 },
407 	{ AMOVBU,	C_REG,		C_NONE,		C_SOREG,		21,	2,	0 },
408 	{ AMOVW,		C_REG,		C_NONE,		C_REG,		22,	2,	0 },
409 	{ AMOVB,		C_REG,		C_NONE,		C_REG,		23,	4,	0 },
410 	{ AMOVH,		C_REG,		C_NONE,		C_REG,		23,	4,	0 },
411 	{ AMOVBU,	C_REG,		C_NONE,		C_REG,		23,	4,	0 },
412 	{ AMOVHU,	C_REG,		C_NONE,		C_REG,		23,	4,	0 },
413 	{ AMOVH,		C_SEXT,		C_NONE,		C_REG,		32,	6,	0 },
414 	{ AMOVH,		C_SOREG,		C_NONE,		C_REG,		24,	4,	0 },
415 	{ AMOVB,		C_SEXT,		C_NONE,		C_REG,		32,	6,	0 },
416 	{ AMOVB,		C_SOREG,		C_NONE,		C_REG,		24,	4,	0 },
417 	{ AMOVW,		C_SACON,	C_NONE,		C_REG,		25,	2,	0 },
418 	{ AMOVW,		C_LACON,	C_NONE,		C_REG,		35,	4,	0 },
419 	{ AMOVW,		C_GACON,	C_NONE,		C_REG,		35,	4,	0,	LFROM },
420 	{ AMOVM,		C_LCON,		C_NONE,		C_REG,		26,	2,	0 },
421 	{ AMOVM,		C_REG,		C_NONE,		C_LCON,		27,	2,	0 },
422 	{ AMOVW,		C_LOREG,		C_NONE,		C_REG,		28,	4,	0 },
423 	{ AMOVH,		C_LOREG,		C_NONE,		C_REG,		28,	4,	0 },
424 	{ AMOVB,		C_LOREG,		C_NONE,		C_REG,		28,	4,	0 },
425 	{ AMOVHU,	C_LOREG,		C_NONE,		C_REG,		28,	4,	0 },
426 	{ AMOVBU,	C_LOREG,		C_NONE,		C_REG,		28,	4,	0 },
427 	{ AMOVW,		C_REG,		C_NONE,		C_LOREG,		29,	4,	0 },
428 	{ AMOVH,		C_REG,		C_NONE,		C_LOREG,		29,	4,	0 },
429 	{ AMOVB,		C_REG,		C_NONE,		C_LOREG,		29,	4,	0 },
430 	{ AMOVHU,	C_REG,		C_NONE,		C_LOREG,		29,	4,	0 },
431 	{ AMOVBU,	C_REG,		C_NONE,		C_LOREG,		29,	4,	0 },
432 	{ AMOVW,		C_GOREG,		C_NONE,		C_REG,		28,	4,	0,	LFROM },
433 	{ AMOVH,		C_GOREG,		C_NONE,		C_REG,		28,	4,	0,	LFROM },
434 	{ AMOVB,		C_GOREG,		C_NONE,		C_REG,		28,	4,	0,	LFROM },
435 	{ AMOVHU,	C_GOREG,		C_NONE,		C_REG,		28,	4,	0,	LFROM },
436 	{ AMOVBU,	C_GOREG,		C_NONE,		C_REG,		28,	4,	0,	LFROM },
437 	{ AMOVW,		C_REG,		C_NONE,		C_GOREG,		29,	4,	0,	LTO },
438 	{ AMOVH,		C_REG,		C_NONE,		C_GOREG,		29,	4,	0,	LTO },
439 	{ AMOVB,		C_REG,		C_NONE,		C_GOREG,		29,	4,	0,	LTO },
440 	{ AMOVHU,	C_REG,		C_NONE,		C_GOREG,		29,	4,	0,	LTO },
441 	{ AMOVBU,	C_REG,		C_NONE,		C_GOREG,		29,	4,	0,	LTO },
442 	{ AMOVW,		C_LEXT,		C_NONE,		C_REG,		30,	4,	0,	LFROM },
443 	{ AMOVH,		C_LEXT,		C_NONE,		C_REG,		32,	6,	0,	LFROM },
444 	{ AMOVB,		C_LEXT,		C_NONE,		C_REG,		32,	6,	0,	LFROM },
445 	{ AMOVHU,	C_LEXT,		C_NONE,		C_REG,		30,	4,	0,	LFROM },
446 	{ AMOVBU,	C_LEXT,		C_NONE,		C_REG,		30,	4,	0,	LFROM },
447 	{ AMOVW,		C_REG,		C_NONE,		C_LEXT,		31,	4,	0,	LTO },
448 	{ AMOVH,		C_REG,		C_NONE,		C_LEXT,		31,	4,	0,	LTO },
449 	{ AMOVB,		C_REG,		C_NONE,		C_LEXT,		31,	4,	0,	LTO },
450 	{ AMOVHU,	C_REG,		C_NONE,		C_LEXT,		31,	4,	0,	LTO },
451 	{ AMOVBU,	C_REG,		C_NONE,		C_LEXT,		31,	4,	0,	LTO },
452 
453 	{ AXXX,		C_NONE,		C_NONE,		C_NONE,		0,	2,	0 },
454 };
455 
456 #define OPCNTSZ	52
457 int opcount[OPCNTSZ];
458 
459 // is this too pessimistic ?
460 int
461 brextra(Prog *p)
462 {
463 	int c;
464 
465 	// +2 is for padding
466 	if(p->as == ATEXT)
467 		return 0-0+2;
468 	if(!isbranch(p))
469 		diag("bad op in brextra()");
470 	c = thumbaclass(&p->to, p);
471 	switch(p->as){
472 	case AB:
473 		if(c != C_SBRA)
474 			return 0;
475 		return 8-2+2;
476 	case ABL:
477 		if(c != C_SBRA)
478 			return 0;
479 		return 14-4+2;
480 	case ABX:
481 		if(c == C_REG || c == C_HREG)
482 			return 0;
483 #ifdef CALLEEBX
484 		diag("ABX $I in brextra");
485 #endif
486 		if(c != C_SBRA)
487 			return 0;
488 		return 14-10+2;
489 	default:
490 		if(c == C_GBRA)
491 			return 0;
492 		if(c == C_LBRA)
493 			return 10-4+2;
494 		return 10-2+2;
495 	}
496 }
497 
498 #define high(r)	((r)>=8)
499 
500 static long
501 mv(Prog *p, int r, int off)
502 {
503 	int v, o;
504 	if(p != nil && p->cond != nil){	// in literal pool
505 		v = p->cond->pc - p->pc - 4;
506 		if(p->cond->pc & 3)
507 			diag("mv: bad literal pool alignment");
508 		if(v & 3)
509 			v += 2;	// ensure M(4) offset
510 		mult(p, v, 4);
511 		off = v/4;
512 		numr(p, off, 0, 255);
513 		o = 0x9<<11;
514 	}
515 	else{
516 		numr(p, off, 0, 255);
517 		o = 0x4<<11;
518 	}
519 	o |= (r<<8) | off;
520 	return o;
521 }
522 
523 static void
524 mvcon(Prog *p, int r, int c, long *o1, long *o2)
525 {
526 	int op = 0, n = 0;
527 
528 	if(c >= 0 && c <= 255)
529 		diag("bad c in mvcon");
530 	if(c >= -255 && c < 0)	// mv, neg
531 		c = -c;
532 	else if(c >= 256 && c <= 510){	// mv, add
533 		n = rand()%(511-c) + (c-255);
534 		c -= n;
535 		// n = c-255;
536 		// c = 255;
537 		op = AADD;
538 	}
539 	else{
540 		if(c < 0)
541 			diag("-ve in mvcon");
542 		while(!(c & 1)){
543 			n++;
544 			c >>= 1;
545 		}
546 		if(c >= 0 && c <= 255)	// mv, lsl
547 			op = ASLL;
548 		else
549 			diag("bad shift in mvcon");
550 	}
551 	*o1 = mv(p, r, c);
552 	switch(op){
553 		case 0:
554 			*o2 = (1<<14) | (9<<6) | (r<<3) | r;
555 			break;
556 		case AADD:
557 			*o2 = (6<<11) | (r<<8) | n;
558 			break;
559 		case ASLL:
560 			*o2 = (n<<6) | (r<<3) | r;
561 			break;
562 	}
563 }
564 
565 static long
566 mvlh(int rs, int rd)
567 {
568 	int o = 0x46<<8;
569 
570 	if(high(rs)){
571 		rs -= 8;
572 		o |= 1<<6;
573 	}
574 	if(high(rd)){
575 		rd -= 8;
576 		o |= 1<<7;
577 	}
578 	o |= (rs<<3) | rd;
579 	return o;
580 }
581 
582 void
583 thumbbuildop()
584 {
585 	int i, n, r;
586 	Optab *optab = thumboptab;
587 	Oprang *oprange = thumboprange;
588 
589 	for(n=0; optab[n].as != AXXX; n++)
590 		;
591 	qsort(optab, n, sizeof(optab[0]), ocmp);
592 	for(i=0; i<n; i++) {
593 		r = optab[i].as;
594 		oprange[r].start = optab+i;
595 		while(optab[i].as == r)
596 			i++;
597 		oprange[r].stop = optab+i;
598 		i--;
599 
600 		switch(r)
601 		{
602 		default:
603 			break;
604 		case ABEQ:
605 			oprange[ABNE] = oprange[r];
606 			oprange[ABCS] = oprange[r];
607 			oprange[ABHS] = oprange[r];
608 			oprange[ABCC] = oprange[r];
609 			oprange[ABLO] = oprange[r];
610 			oprange[ABMI] = oprange[r];
611 			oprange[ABPL] = oprange[r];
612 			oprange[ABVS] = oprange[r];
613 			oprange[ABVC] = oprange[r];
614 			oprange[ABHI] = oprange[r];
615 			oprange[ABLS] = oprange[r];
616 			oprange[ABGE] = oprange[r];
617 			oprange[ABLT] = oprange[r];
618 			oprange[ABGT] = oprange[r];
619 			oprange[ABLE] = oprange[r];
620 			break;
621 		case AMVN:
622 			oprange[AADC] = oprange[r];
623 			oprange[ASBC] = oprange[r];
624 			oprange[AMUL] = oprange[r];
625 			oprange[AAND] = oprange[r];
626 			oprange[AEOR] = oprange[r];
627 			oprange[AORR] = oprange[r];
628 			oprange[ABIC] = oprange[r];
629 			oprange[AMULU] = oprange[r];
630 			break;
631 		case ACMN:
632 			oprange[ATST] = oprange[r];
633 			break;
634 		case ASRL:
635 			oprange[ASRA] = oprange[r];
636 			oprange[ASLL] = oprange[r];
637 			break;
638 		case AADD:
639 			oprange[ASUB] = oprange[r];
640 			break;
641 		}
642 	}
643 }
644 
645 void
646 thumbasmout(Prog *p, Optab *o)
647 {
648 	long o1, o2, o3, o4, o5, o6, o7, v;
649 	int r, rf, rt;
650 
651 	rf = p->from.reg;
652 	rt = p->to.reg;
653 	r = p->reg;
654 	o1 = o2 = o3 = o4 = o5 = o6 = o7 = 0;
655 if(debug['P']) print("%ulx: %P	type %d %d\n", (ulong)(p->pc), p, o->type, p->align);
656 	opcount[o->type] += o->size;
657 	switch(o->type) {
658 	default:
659 		diag("unknown asm %d", o->type);
660 		prasm(p);
661 		break;
662 	case 0:		/* pseudo ops */
663 if(debug['G']) print("%ulx: %s: thumb\n", (ulong)(p->pc), p->from.sym->name);
664 		break;
665 	case 1:		/* op R, -, R or op R, R, - */
666 		o1 = thumboprr(p->as);
667 		if(rt == NREG)
668 			rt = r;
669 		lowreg(p, rf);
670 		lowreg(p, rt);
671 		o1 |= (0x10<<10) | (rf<<3) | rt;
672 		break;
673 	case 2:		/* add/sub R, R, R or add/sub R, -, R */
674 		o1 = p->as == AADD ? 0x0<<9 : 0x1<<9;
675 		if(r == NREG)
676 			r = rt;
677 		lowreg(p, rf);
678 		lowreg(p, r);
679 		lowreg(p, rt);
680 		o1 |= (0x6<<10) | (rf<<6) | (r<<3) | rt;
681 		break;
682 	case 3:		/* add/sub $I, R, R or add/sub $I, -, R */
683 		thumbaclass(&p->from, p);
684 		o1 = p->as == AADD ? 0x0<<9 : 0x1<<9;
685 		if(r == NREG)
686 			r = rt;
687 		numr(p, instoffset, 0, 7);
688 		lowreg(p, r);
689 		lowreg(p, rt);
690 		o1 |= (0x7<<10) | (instoffset<<6) | (r<<3) | rt;
691 		break;
692 	case 4:		/* shift $I, R, R or shift $I, -, R */
693 		thumbaclass(&p->from, p);
694 		if(instoffset < 0)
695 			diag("negative shift in thumbasmout");
696 		instoffset %= 32;
697 		o1 = thumbopri(p->as);
698 		if(r == NREG)
699 			r = rt;
700 		numr(p, instoffset, 0, 31);
701 		lowreg(p, r);
702 		lowreg(p, rt);
703 		o1 |= (0x0<<13) | (instoffset<<6) | (r<<3) | rt;
704 		break;
705 	case 5:		/* add/sub/mov $I, -, R or cmp $I, R, - */
706 		thumbaclass(&p->from, p);
707 		o1 = thumbopri(p->as);
708 		if(rt == NREG)
709 			rt = r;
710 		numr(p, instoffset, 0, 255);
711 		lowreg(p, rt);
712 		o1 |= (0x1<<13) | (rt<<8) | instoffset;
713 		break;
714 	case 6:		/* add $I, PC/SP, R */
715 		if(p->as == ASUB)
716 			diag("subtract in add $I, PC/SP, R");
717 		thumbaclass(&p->from, p);
718 		o1 = r == REGSP ? 0x1<<11 : 0x0<<11;
719 		numr(p, instoffset, 0, 255);
720 		regis(p, r, REGSP, REGPC);
721 		lowreg(p, rt);
722 		o1 |= (0xa<<12) | (rt<<8) | instoffset;
723 		break;
724 	case 7:		/* add, sub $I, SP */
725 		thumbaclass(&p->from, p);
726 		o1 = p->as == AADD ? 0x0<<7 : 0x1<<7;
727 		numr(p, instoffset, 0, 508);
728 		mult(p, instoffset, 4);
729 		regis(p, rt, REGSP, REGSP);
730 		o1 |= (0xb0<<8) | (instoffset>>2);
731 		break;
732 	case 8:		/* add/mov/cmp R, R where at least 1 reg is high */
733 		o1 = 0;
734 		if(rt == NREG)
735 			rt = r;
736 		if(high(rf)){
737 			o1 |= 1<<6;
738 			rf -= 8;
739 		}
740 		if(high(rt)){
741 			o1 |= 2<<6;
742 			rt -= 8;
743 		}
744 		if(o1 == 0)
745 			diag("no high register(%P)", p);
746 		o1 |= thumbophh(p->as);
747 		o1 |= (0x11<<10) | (rf<<3) | rt;
748 		break;
749 	case 9:		/* B	$I */
750 		thumbaclass(&p->to, p);
751 		numr(p, instoffset, -2048, 2046);
752 		o1 = (0x1c<<11) | ((instoffset>>1)&0x7ff);
753 		break;
754 	case 10:		/* Bcc $I */
755 		thumbaclass(&p->to, p);
756 		numr(p, instoffset, -256, 254);
757 		o1 = thumbopbra(p->as);
758 		o1 |= (0xd<<12) | ((instoffset>>1)&0xff);
759 		break;
760 	case 11:		/* BL $I */
761 		thumbaclass(&p->to, p);
762 		numr(p, instoffset, -4194304, 4194302);
763 		o1 = (0x1e<<11) | ((instoffset>>12)&0x7ff);
764 		o2 = (0x1f<<11) | ((instoffset>>1)&0x7ff);
765 		break;
766 	case 12:		/* BX $I */
767 #ifdef CALLEEBX
768 		diag("BX $I case");
769 #endif
770 		thumbaclass(&p->to, p);
771 		if(p->to.sym->thumb)
772 			instoffset  |= 1;	// T bit
773 		o1 = mvlh(REGPC, REGTMPT);
774 		o2 = (0x6<<11) | (REGTMPT<<8) | 7;	// add 7, RTMP	(T bit + PC offset)
775 		o3 = mvlh(REGTMPT, REGLINK);
776 		o4 = mv(nil, REGTMPT, instoffset);
777 		o5 = (0x11c<<6) | (REGTMPT<<3);
778 		// o1 = mv(nil, REGTMPT, v);
779 		// o2 = (0x11b<<6) | (REGPC<<3) | REGLINK;
780 		// o3 = (0x11c<<6) | (REGTMPT<<3);
781 		break;
782 	case 13:		/* B O(R)  */
783 		diag("B O(R)");
784 		break;
785 	case 14:		/* BL O(R) */
786 		diag("BL O(R)");
787 		break;
788 	case 15:		/* BX R */
789 		o1 = mvlh(REGPC, REGTMPT);
790 		o2 = (0x6<<11) | (REGTMPT<<8) | 5;	// add 5, RTMP (T bit + PC offset)
791 		o3 = mvlh(REGTMPT, REGLINK);
792 		o4 = 0;
793 		if(high(rt)){
794 			rt -= 8;
795 			o4 |= 1<<6;
796 		}
797 		o4 |= (0x8e<<7) | (rt<<3);
798 		// o1 = (0x11c<<6) | (rt<<3);
799 		break;
800 	case 16:		/* SWI $I */
801 		thumbaclass(&p->to, p);
802 		numr(p, instoffset, 0, 255);
803 		o1 = (0xdf<<8) | instoffset;
804 		break;
805 	case 17:		/* AWORD */
806 		thumbaclass(&p->to, p);
807 		o1 = instoffset&0xffff;
808 		o2 = (instoffset>>16)&0xffff;
809 		break;
810 	case 18:		/* AMOVW O(SP), R and AMOVW O(PC), R */
811 		thumbaclass(&p->from, p);
812 		rf = o->param;
813 		o1 = rf == REGSP ? 0x13<<11 : 0x9<<11;
814 		regis(p, rf, REGSP, REGPC);
815 		lowreg(p, rt);
816 		mult(p, instoffset, 4);
817 		numr(p, instoffset/4, 0, 255);
818 		o1 |= (rt<<8) | (instoffset/4);
819 		break;
820 	case 19:		/* AMOVW... O(R), R */
821 		thumbaclass(&p->from, p);
822 		o1 = thumbopmv(p->as, 1);
823 		v = 4;
824 		if(p->as == AMOVHU)
825 			v = 2;
826 		else if(p->as == AMOVBU)
827 			v = 1;
828 		mult(p, instoffset, v);
829 		lowreg(p, rf);
830 		lowreg(p, rt);
831 		numr(p, instoffset/v, 0, 31);
832 		o1 |= ((instoffset/v)<<6) | (rf<<3) | rt;
833 		break;
834 	case 20:		/* AMOVW R, O(SP) */
835 		thumbaclass(&p->to, p);
836 		o1 = 0x12<<11;
837 		if(rt != NREG) regis(p, rt, REGSP, REGSP);
838 		lowreg(p, rf);
839 		mult(p, instoffset, 4);
840 		numr(p, instoffset/4, 0, 255);
841 		o1 |= (rf<<8) | (instoffset/4);
842 		break;
843 	case 21:		/* AMOVW... R, O(R) */
844 		thumbaclass(&p->to, p);
845 		o1 = thumbopmv(p->as, 0);
846 		v = 4;
847 		if(p->as == AMOVHU || p->as == AMOVH)
848 			v = 2;
849 		else if(p->as == AMOVBU || p->as == AMOVB)
850 			v = 1;
851 		lowreg(p, rf);
852 		lowreg(p, rt);
853 		mult(p, instoffset, v);
854 		numr(p, instoffset/v, 0, 31);
855 		o1 |= ((instoffset/v)<<6) | (rt<<3) | rf;
856 		break;
857 	case 22:		/* AMOVW R, R -> ASLL $0, R, R */
858 		o1 = thumbopri(ASLL);
859 		lowreg(p, rf);
860 		lowreg(p, rt);
861 		o1 |= (0x0<<13) | (rf<<3) | rt;
862 		break;
863 	case 23:		/* AMOVB/AMOVH/AMOVBU/AMOVHU R, R */
864 		o1 = thumbopri(ASLL);
865 		o2 = p->as == AMOVB || p->as == AMOVH ? thumbopri(ASRA) : thumbopri(ASRL);
866 		v = p->as == AMOVB || p->as == AMOVBU ? 24 : 16;
867 		lowreg(p, rf);
868 		lowreg(p, rt);
869 		o1 |= (0x0<<13) | (v<<6) | (rf<<3) | rt;
870 		o2 |= (0x0<<13) | (v<<6) | (rt<<3) | rt;
871 		break;
872 	case 24:	/* AMOVH/AMOVB O(R), R -> AMOVH/AMOVB [R, R], R */
873 		thumbaclass(&p->from, p);
874 		lowreg(p, rf);
875 		lowreg(p, rt);
876 		if(rf == rt)
877 			r = REGTMPT;
878 		else
879 			r = rt;
880 		if(p->as == AMOVB)
881 			numr(p, instoffset, 0, 31);
882 		else{
883 			mult(p, instoffset, 2);
884 			numr(p, instoffset, 0, 62);
885 		}
886 		o1 = mv(p, r, instoffset);
887 		o2 = p->as == AMOVH ? 0x2f<<9 : 0x2b<<9;
888 		o2 |= (r<<6) | (rf<<3) | rt;
889 		break;
890 	case 25:	/* MOVW $sacon, R */
891 		thumbaclass(&p->from, p);
892 // print("25: %d %d %d %d\n", instoffset, rf, r, rt);
893 		if(rf == NREG)
894 			rf = REGSP;
895 		lowreg(p, rt);
896 		if(rf == REGSP){
897 			mult(p, instoffset, 4);
898 			numr(p, instoffset>>2, 0, 255);
899 			o1 = (0x15<<11) | (rt<<8) | (instoffset>>2);	// add $O, SP, R
900 		}
901 		else if(rf == rt){
902 			numr(p, instoffset, 0, 255);
903 			o1 = (0x6<<11) | (rt<<8) | instoffset;		// add $O, R
904 		}
905 		else{
906 			lowreg(p, rf);
907 			numr(p, instoffset, 0, 7);
908 			o1 = (0xe<<9) | (instoffset<<6) | (rf<<3) | rt;	// add $O, Rs, Rd
909 		}
910 		break;
911 	case 26:	/* AMOVM $c, oreg -> stmia */
912 		lowreg(p, rt);
913 		numr(p, p->from.offset, -256, 255);
914 		o1 = (0x18<<11) | (rt<<8) | (p->from.offset&0xff);
915 		break;
916 	case 27:	/* AMOVM oreg, $c ->ldmia */
917 		lowreg(p, rf);
918 		numr(p, p->to.offset, -256, 256);
919 		o1 = (0x19<<11) | (rf<<8) | (p->to.offset&0xff);
920 		break;
921 	case 28:	/* AMOV* O(R), R -> AMOV* [R, R], R 	(offset large)	*/
922 		thumbaclass(&p->from, p);
923 		lowreg(p, rf);
924 		lowreg(p, rt);
925 		if(rf == rt)
926 			r = REGTMPT;
927 		else
928 			r = rt;
929 		o1 = mv(p, r, instoffset);
930 		o2 = thumboprrr(p->as, 1);
931 		o2 |= (r<<6) | (rf<<3) | rt;
932 		break;
933 	case 29:	/* AMOV* R, O(R) -> AMOV* R, [R, R]	(offset large)	*/
934 		thumbaclass(&p->to, p);
935 		lowreg(p, rf);
936 		lowreg(p, rt);
937 		if(rt == REGTMPT){	// used as tmp reg
938 			if(instoffset >= 0 && instoffset <= 255){
939 				o1 = (1<<13) | (2<<11) | (rt<<8) | instoffset;	// add $O, R7
940 				o2 = thumbopirr(p->as, 0);
941 				o2 |= (0<<6) | (rt<<3) | rf;					// mov* R, 0(R)
942 			}
943 			else
944 				diag("big offset - case 29");
945 		}
946 		else{
947 			o1 = mv(p, REGTMPT, instoffset);
948 			o2 = thumboprrr(p->as, 0);
949 			o2 |= (REGTMPT<<6) | (rt<<3) | rf;
950 		}
951 		break;
952 	case 30:		/* AMOVW... *addr, R */
953 		thumbaclass(&p->from, p);
954 		o1 = mv(p, rt, instoffset);		// MOV addr, rtmp
955 		o2 = thumbopmv(p->as, 1);
956 		lowreg(p, rt);
957 		o2 |= (rt<<3) | rt;			// MOV* 0(rtmp), R
958 		break;
959 	case 31:		/* AMOVW... R, *addr */
960 		thumbaclass(&p->to, p);
961 		o1 = mv(p, REGTMPT, instoffset);
962 		o2 = thumbopmv(p->as, 0);
963 		lowreg(p, rf);
964 		o2 |= (REGTMPT<<3) | rf;
965 		break;
966 	case 32:	/* AMOVH/AMOVB *addr, R -> AMOVH/AMOVB [R, R], R */
967 		thumbaclass(&p->from, p);
968 		o1 = mv(p, rt, instoffset);
969 		lowreg(p, rt);
970 		o2 = mv(nil, REGTMPT, 0);
971 		o3 = p->as == AMOVH ? 0x2f<<9 : 0x2b<<9;
972 		o3 |= (REGTMPT<<6) | (rt<<3) | rt;
973 		break;
974 	case 33:	/* AMOVW O(SP), R	(O large) */
975 		thumbaclass(&p->from, p);
976 		lowreg(p, rt);
977 		o1 = mv(p, rt, instoffset);
978 		o2 = (0x111<<6) | (REGSP-8)<<3 | rt;	// add SP, rt
979 		o3 = thumbopmv(p->as, 1);
980 		o3 |= (rt<<3) | rt;
981 		break;
982 	case 34:	/* AMOVW R, O(SP)	(O large) */
983 		thumbaclass(&p->to, p);
984 		lowreg(p, rf);
985 		o1 = mv(p, REGTMPT, instoffset);
986 		o2 = (0x111<<6) | (REGSP-8)<<3 | REGTMPT;	// add SP, REGTMP
987 		o3 = thumbopmv(p->as, 0);
988 		o3 |= (REGTMPT<<3) | rf;
989 		break;
990 	case 35:	/* AMOVW $lacon, R */
991 		thumbaclass(&p->from, p);
992 		lowreg(p, rt);
993 		if(rf == NREG)
994 			rf = REGSP;
995 		if(rf == rt)
996 			rf = r = REGTMPT;
997 		else
998 			r = rt;
999 // print("35: io=%d rf=%d rt=%d\n", instoffset, rf, rt);
1000 		o1 = mv(p, r, instoffset);		// mov O, Rd
1001 		if(high(rf))
1002 			o2 = (0x44<<8) | (0x1<<6) | ((rf-8)<<3) | rt;	// add Rs, Rd
1003 		else
1004 			o2 = (0x6<<10) | (rf<<6) | (rt<<3) | rt;		// add Rs, Rd
1005 		break;
1006 	case 36:	/* AADD/ASUB $i, r, r when $i too big */
1007 		thumbaclass(&p->from, p);
1008 		lowreg(p, r);
1009 		lowreg(p, rt);
1010 		o1 = mv(p, REGTMPT, instoffset);
1011 		o2 = p->as == AADD ? 0xc<<9 : 0xd<<9;
1012 		o2 |= (REGTMPT<<6) | (r<<3) | rt;
1013 		break;
1014 	case 37:	/* AADD/ASUB $i, r when $i too big */
1015 		thumbaclass(&p->from, p);
1016 		lowreg(p, rt);
1017 		o1 = mv(p, REGTMPT, instoffset);
1018 		o2 = p->as == AADD ? 0xc<<9 : 0xd<<9;
1019 		o2 |= (REGTMPT<<6) | (rt<<3) | rt;
1020 		break;
1021 	case 38:	/* AMOVW $i, r when $i too big */
1022 		thumbaclass(&p->from, p);
1023 		lowreg(p, rt);
1024 		o1 = mv(p, rt, instoffset);
1025 		break;
1026 	case 39:	/* ACMP $i, r when $i too big */
1027 		thumbaclass(&p->from, p);
1028 		lowreg(p, r);
1029 		o1 = mv(p, REGTMPT, instoffset);
1030 		o2 = (0x10a<<6) | (REGTMPT<<3) | r;
1031 		break;
1032 	case 40:		/* add, sub $I, SP when $I large*/
1033 		thumbaclass(&p->from, p);
1034 		if(p->as == ASUB)
1035 			instoffset = -instoffset;
1036 		o1 = mv(p, REGTMPT, instoffset);
1037 		o2 = (0x112<<6) | (REGTMPT<<3) | (REGSP-8);
1038 		regis(p, rt, REGSP, REGSP);
1039 		break;
1040 	case	41:		/* BL LBRA */
1041 		thumbaclass(&p->to, p);
1042 		o1 = (0x9<<11) | (REGTMPT<<8);	// mov 0(pc), r7
1043 		o2 = mvlh(REGTMPT, REGPC);		// mov r7, pc
1044 		o3 = instoffset&0xffff;			// $lab
1045 		o4 = (instoffset>>16)&0xffff;
1046 		break;
1047 	case 42:		/* Bcc GBRA */
1048 		thumbaclass(&p->to, p);
1049 		o1 = (0xd<<12) | thumbopbra(relinv(p->as)) | (6>>1);		// bccnot
1050 		// ab lbra
1051 		o2 = (0x9<<11) | (REGTMPT<<8);	// mov 0(pc), r7
1052 		o3 = mvlh(REGTMPT, REGPC);		// mov r7, pc
1053 		o4 = instoffset&0xffff;			// $lab
1054 		o5 = (instoffset>>16)&0xffff;
1055 		break;
1056 	case 43:		/* BL LBRA */
1057 		thumbaclass(&p->to, p);
1058 		o1 = mvlh(REGPC, REGTMPT);						// mov pc, r7
1059 		o2 = (0x6<<11) | (REGTMPT<<8) | 10;				// add 10, r7
1060 		o3 = mvlh(REGTMPT, REGLINK);					// mov r7, lr
1061 		o4 = (0x9<<11) | (REGTMPT<<8);					// mov o(pc), r7
1062 		o5 = mvlh(REGTMPT, REGPC);						// mov r7, pc
1063 		o6 = instoffset&0xffff;							// $lab
1064 		o7 = (instoffset>>16)&0xffff;
1065 		break;
1066 	case 44:		/* BX LBRA */
1067 #ifdef CALLEEBX
1068 		diag("BX LBRA case");
1069 #endif
1070 		thumbaclass(&p->to, p);
1071 		if(p->to.sym->thumb)
1072 			instoffset  |= 1;	// T bit
1073 		o1 = mvlh(REGPC, REGTMPT);						// mov pc, r7
1074 		o2 = (0x6<<11) | (REGTMPT<<8) | 11;				// add 11, r7
1075 		o3 = mvlh(REGTMPT, REGLINK);					// mov r7, lr
1076 		o4 = (0x9<<11) | (REGTMPT<<8);					// mov o(pc), r7
1077 		o5 = (0x11c<<6) | (REGTMPT<<3);					// bx r7
1078 		o6 = instoffset&0xffff;							// $lab
1079 		o7 = (instoffset>>16)&0xffff;
1080 		break;
1081 	case 45:	/* BX R when returning from fn */
1082 		o1 = 0;
1083 		if(high(rt)){
1084 			rt -= 8;
1085 			o1 |= 1<<6;
1086 		}
1087 		o1 |= (0x8e<<7) | (rt<<3);
1088 		break;
1089 	case 46:		/* Bcc LBRA */
1090 		thumbaclass(&p->to, p);
1091 		o1 = (0xd<<12) | thumbopbra(relinv(p->as)) | (0>>1);		// bccnot
1092 		// ab lbra
1093 		instoffset -= 2;
1094 		numr(p, instoffset, -2048, 2046);
1095 		o2 = (0x1c<<11) | ((instoffset>>1)&0x7ff);
1096 		break;
1097 	case 47:	/* mov $i, R where $i can be built */
1098 		thumbaclass(&p->from, p);
1099 		mvcon(p, rt, instoffset, &o1, &o2);
1100 		break;
1101 	case 48: /* ACMP $i, r when $i built up */
1102 		thumbaclass(&p->from, p);
1103 		lowreg(p, r);
1104 		mvcon(p, REGTMPT, instoffset, &o1, &o2);
1105 		o3 = (0x10a<<6) | (REGTMPT<<3) | r;
1106 		break;
1107 	case 49:	/* AADD $i, r, r when $i is between 0 and 255 - could merge with case 36 */
1108 		thumbaclass(&p->from, p);
1109 		lowreg(p, r);
1110 		lowreg(p, rt);
1111 		numr(p, instoffset, 0, 255);
1112 		o1 = mv(p, REGTMPT, instoffset);
1113 		o2 = p->as == AADD ? 0xc<<9 : 0xd<<9;
1114 		o2 |= (REGTMPT<<6) | (r<<3) | rt;
1115 		break;
1116 	case 50:		/* ADWORD */
1117 		thumbaclass(&p->from, p);
1118 		o1 = instoffset&0xffff;
1119 		o2 = (instoffset>>16)&0xffff;
1120 		thumbaclass(&p->to, p);
1121 		o3 = instoffset&0xffff;
1122 		o4 = (instoffset>>16)&0xffff;
1123 		break;
1124 	case 51:	/* BL r */
1125 		o1 = mvlh(REGPC, REGLINK);	// mov pc, lr
1126 		o2 = mvlh(rt, REGPC);		// mov r, pc
1127 		break;
1128 	}
1129 
1130 	v = p->pc;
1131 	switch(o->size) {
1132 	default:
1133 		if(debug['a'])
1134 			Bprint(&bso, " %.8lux:\t\t%P\n", v, p);
1135 		break;
1136 	case 2:
1137 		if(debug['a'])
1138 			Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p);
1139 		hputl(o1);
1140 		break;
1141 	case 4:
1142 		if(debug['a'])
1143 			Bprint(&bso, " %.8lux: %.8lux %.8lux\t%P\n", v, o1, o2, p);
1144 		hputl(o1);
1145 		hputl(o2);
1146 		break;
1147 	case 6:
1148 		if(debug['a'])
1149 			Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, p);
1150 		hputl(o1);
1151 		hputl(o2);
1152 		hputl(o3);
1153 		break;
1154 	case 8:
1155 		if(debug['a'])
1156 			Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, o4, p);
1157 		hputl(o1);
1158 		hputl(o2);
1159 		hputl(o3);
1160 		hputl(o4);
1161 		break;
1162 	case 10:
1163 		if(debug['a'])
1164 			Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, o4, o5, p);
1165 		hputl(o1);
1166 		hputl(o2);
1167 		hputl(o3);
1168 		hputl(o4);
1169 		hputl(o5);
1170 		break;
1171 	case 12:
1172 		if(debug['a'])
1173 			Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, o4, o5, o6, p);
1174 		hputl(o1);
1175 		hputl(o2);
1176 		hputl(o3);
1177 		hputl(o4);
1178 		hputl(o5);
1179 		hputl(o6);
1180 		break;
1181 	case 14:
1182 		if(debug['a'])
1183 			Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, o4, o5, o6, o7, p);
1184 		hputl(o1);
1185 		hputl(o2);
1186 		hputl(o3);
1187 		hputl(o4);
1188 		hputl(o5);
1189 		hputl(o6);
1190 		hputl(o7);
1191 		break;
1192 	}
1193 	if(debug['G']){
1194 		if(o->type == 17){
1195 			print("%lx:	word %ld\n", p->pc, (o2<<16)+o1);
1196 			return;
1197 		}
1198 		if(o->type == 50){
1199 			print("%lx:	word %ld\n", p->pc, (o2<<16)+o1);
1200 			print("%lx:	word %ld\n", p->pc, (o4<<16)+o3);
1201 			return;
1202 		}
1203 		if(o->size > 0) dis(o1, p->pc);
1204 		if(o->size > 2) dis(o2, p->pc+2);
1205 		if(o->size > 4) dis(o3, p->pc+4);
1206 		if(o->size > 6) dis(o4, p->pc+6);
1207 		if(o->size > 8) dis(o5, p->pc+8);
1208 		if(o->size > 10) dis(o6, p->pc+10);
1209 		if(o->size > 12) dis(o7, p->pc+12);
1210 		// if(o->size > 14) dis(o8, p->pc+14);
1211 	}
1212 }
1213 
1214 static long
1215 thumboprr(int a)
1216 {
1217 	switch(a) {
1218 	case AMVN:	return 0xf<<6;
1219 	case ACMP:	return 0xa<<6;
1220 	case ACMN:	return 0xb<<6;
1221 	case ATST:	return 0x8<<6;
1222 	case AADC:	return 0x5<<6;
1223 	case ASBC:	return 0x6<<6;
1224 	case AMUL:
1225 	case AMULU:	return 0xd<<6;
1226 	case AAND:	return 0x0<<6;
1227 	case AEOR:	return 0x1<<6;
1228 	case AORR:	return 0xc<<6;
1229 	case ABIC:	return 0xe<<6;
1230 	case ASRL:	return 0x3<<6;
1231 	case ASRA:	return 0x4<<6;
1232 	case ASLL:	return 0x2<<6;
1233 	}
1234 	diag("bad thumbop oprr %d", a);
1235 	prasm(curp);
1236 	return 0;
1237 }
1238 
1239 static long
1240 thumbopirr(int a, int ld)
1241 {
1242 	if(ld)
1243 		diag("load in thumbopirr");
1244 	switch(a){
1245 		case AMOVW:	return 0xc<<11;
1246 		case AMOVH:
1247 		case AMOVHU:	return 0x10<<11;
1248 		case AMOVB:
1249 		case AMOVBU:	return 0xe<<11;
1250 	}
1251 	return 0;
1252 }
1253 
1254 static long
1255 thumboprrr(int a, int ld)
1256 {
1257 	if(ld){
1258 		switch(a){
1259 		case AMOVW:	return 0x2c<<9;
1260 		case AMOVH:	return 0x2f<<9;
1261 		case AMOVB:	return 0x2b<<9;
1262 		case AMOVHU:	return 0x2d<<9;
1263 		case AMOVBU:	return 0x2e<<9;
1264 		}
1265 	}
1266 	else{
1267 		switch(a){
1268 		case AMOVW:	return 0x28<<9;
1269 		case AMOVHU:
1270 		case AMOVH:	return 0x29<<9;
1271 		case AMOVBU:
1272 		case AMOVB:	return 0x2a<<9;
1273 		}
1274 	}
1275 	diag("bad thumbop oprrr %d", a);
1276 	prasm(curp);
1277 	return 0;
1278 }
1279 
1280 static long
1281 thumbopri(int a)
1282 {
1283 	switch(a) {
1284 	case ASRL:	return 0x1<<11;
1285 	case ASRA:	return 0x2<<11;
1286 	case ASLL:	return 0x0<<11;
1287 	case AADD:	return 0x2<<11;
1288 	case ASUB:	return 0x3<<11;
1289 	case AMOVW:	return 0x0<<11;
1290 	case ACMP:	return 0x1<<11;
1291 	}
1292 	diag("bad thumbop opri %d", a);
1293 	prasm(curp);
1294 	return 0;
1295 }
1296 
1297 static long
1298 thumbophh(int a)
1299 {
1300 	switch(a) {
1301 	case AADD:	return 0x0<<8;
1302 	case AMOVW:	return 0x2<<8;
1303 	case ACMP:	return 0x1<<8;
1304 	}
1305 	diag("bad thumbop ophh %d", a);
1306 	prasm(curp);
1307 	return 0;
1308 }
1309 
1310 static long
1311 thumbopbra(int a)
1312 {
1313 	switch(a) {
1314 	case ABEQ:	return 0x0<<8;
1315 	case ABNE:	return 0x1<<8;
1316 	case ABCS:	return 0x2<<8;
1317 	case ABHS:	return 0x2<<8;
1318 	case ABCC:	return 0x3<<8;
1319 	case ABLO:	return 0x3<<8;
1320 	case ABMI:	return 0x4<<8;
1321 	case ABPL:	return 0x5<<8;
1322 	case ABVS:	return 0x6<<8;
1323 	case ABVC:	return 0x7<<8;
1324 	case ABHI:	return 0x8<<8;
1325 	case ABLS:	return 0x9<<8;
1326 	case ABGE:	return 0xa<<8;
1327 	case ABLT:	return 0xb<<8;
1328 	case ABGT:	return 0xc<<8;
1329 	case ABLE:	return 0xd<<8;
1330 	}
1331 	diag("bad thumbop opbra %d", a);
1332 	prasm(curp);
1333 	return 0;
1334 }
1335 
1336 static long
1337 thumbopmv(int a, int ld)
1338 {
1339 	switch(a) {
1340 	case AMOVW: 	return (ld ? 0xd : 0xc)<<11;
1341 	case AMOVH:
1342 	case AMOVHU:	return (ld ? 0x11: 0x10)<<11;
1343 	case AMOVB:
1344 	case AMOVBU:	return (ld ? 0xf : 0xe)<<11;
1345 	}
1346 	diag("bad thumbop opmv %d", a);
1347 	prasm(curp);
1348 	return 0;
1349 }
1350 
1351 static void
1352 lowreg(Prog *p, int r)
1353 {
1354 	if(high(r))
1355 		diag("high reg [%P]", p);
1356 }
1357 
1358 static void
1359 mult(Prog *p, int n, int m)
1360 {
1361 	if(m*(n/m) != n)
1362 		diag("%d not M(%d) [%P]", n, m, p);
1363 }
1364 
1365 static void
1366 numr(Prog *p, int n, int min, int max)
1367 {
1368 	if(n < min || n > max)
1369 		diag("%d not in %d-%d [%P]", n, min, max, p);
1370 }
1371 
1372 static void
1373 regis(Prog *p, int r, int r1, int r2)
1374 {
1375 	if(r != r1 && r != r2)
1376 		diag("reg %d not %d or %d [%P]", r, r1, r2, p);
1377 }
1378 
1379 void
1380 hputl(int n)
1381 {
1382 	cbp[1] = n>>8;
1383 	cbp[0] = n;
1384 	cbp += 2;
1385 	cbc -= 2;
1386 	if(cbc <= 0)
1387 		cflush();
1388 }
1389 
1390 void
1391 thumbcount()
1392 {
1393 	int i, c = 0, t = 0;
1394 
1395 	for (i = 0; i < OPCNTSZ; i++)
1396 		t += opcount[i];
1397 	if(t == 0)
1398 		return;
1399 	for (i = 0; i < OPCNTSZ; i++){
1400 		c += opcount[i];
1401 		print("%d:	%d %d %d%%\n", i, opcount[i], c, (opcount[i]*100+t/2)/t);
1402 	}
1403 }
1404 
1405 char *op1[] = { "lsl", "lsr", "asr" };
1406 char *op2[] = { "add", "sub" };
1407 char *op3[] = { "movw", "cmp", "add", "sub" };
1408 char *op4[] = { "and", "eor", "lsl", "lsr", "asr", "adc", "sbc", "ror",
1409 		        "tst", "neg", "cmp", "cmpn", "or", "mul", "bitc", "movn" };
1410 char *op5[] = { "add", "cmp", "movw", "bx" };
1411 char *op6[] = { "smovw", "smovh", "smovb", "lmovb", "lmovw", "lmovhu", "lmovbu", "lmovh" };
1412 char *op7[] = { "smovw", "lmovw", "smovb", "lmovbu" };
1413 char *op8[] = { "smovh", "lmovhu" };
1414 char *op9[] = { "smovw", "lmovw" };
1415 char *op10[] = { "push", "pop" };
1416 char *op11[] = { "stmia", "ldmia" };
1417 
1418 char *cond[] = { "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc",
1419 			 "hi", "ls", "ge", "lt", "gt", "le", "al", "nv" };
1420 
1421 #define B(h, l)		bits(i, h, l)
1422 #define IMM(h, l)	B(h, l)
1423 #define REG(h, l)	reg(B(h, l))
1424 #define LHREG(h, l, lh)	lhreg(B(h, l), B(lh, lh))
1425 #define COND(h, l)	cond[B(h, l)]
1426 #define OP1(h, l)	op1[B(h, l)]
1427 #define OP2(h, l)	op2[B(h, l)]
1428 #define OP3(h, l)	op3[B(h, l)]
1429 #define OP4(h, l)	op4[B(h, l)]
1430 #define OP5(h, l)	op5[B(h, l)]
1431 #define OP6(h, l)	op6[B(h, l)]
1432 #define OP7(h, l)	op7[B(h, l)]
1433 #define OP8(h, l)	op8[B(h, l)]
1434 #define OP9(h, l)	op9[B(h, l)]
1435 #define OP10(h, l)	op10[B(h, l)]
1436 #define OP11(h, l)	op11[B(h, l)]
1437 #define SBZ(h, l)	if(IMM(h, l) != 0) diag("%x: %x bits %d,%d not zero", pc, i, h, l)
1438 #define SNBZ(h, l)	if(IMM(h, l) == 0) diag("%x: %x bits %d,%d zero", pc, i, h, l)
1439 #define SBO(h, l)	if(IMM(h, l) != 1) diag("%x: %x bits %d,%d not one", pc, i, h, l)
1440 
1441 static int
1442 bits(int i, int h, int l)
1443 {
1444 	if(h < l)
1445 		diag("h < l in bits");
1446 	return (i&(((1<<(h-l+1))-1)<<l))>>l;
1447 }
1448 
1449 static char *
1450 reg(int r)
1451 {
1452 	static char s[4][4];
1453 	static int i = 0;
1454 
1455 	if(r < 0 || r > 7)
1456 		diag("register %d out of range", r);
1457 	i++;
1458 	if(i == 4)
1459 		i = 0;
1460 	sprint(s[i], "r%d", r);
1461 	return s[i];
1462 }
1463 
1464 static char *regnames[] = { "sp", "lr", "pc" };
1465 
1466 static char *
1467 lhreg(int r, int lh)
1468 {
1469 	static char s[4][4];
1470 	static int i = 0;
1471 
1472 	if(lh == 0)
1473 		return reg(r);
1474 	if(r < 0 || r > 7)
1475 		diag("high register %d out of range", r);
1476 	i++;
1477 	if(i == 4)
1478 		i = 0;
1479 	if(r >= 5)
1480 		sprint(s[i], "%s", regnames[r-5]);
1481 	else
1482 		sprint(s[i], "r%d", r+8);
1483 	return s[i];
1484 }
1485 
1486 static void
1487 illegal(int i, int pc)
1488 {
1489 	diag("%x: %x illegal instruction", pc, i);
1490 }
1491 
1492 static void
1493 dis(int i, int pc)
1494 {
1495 	static int lasto;
1496 	int o, l;
1497 	char *op;
1498 
1499 	print("%x: %x:	", pc, i);
1500 	if(i&0xffff0000)
1501 		illegal(i, pc);
1502 	o = B(15, 13);
1503 	switch(o){
1504 	case 0:
1505 		o = B(12, 11);
1506 		switch(o){
1507 			case 0:
1508 			case 1:
1509 			case 2:
1510 				print("%s	%d, %s, %s\n", OP1(12, 11), IMM(10, 6), REG(5, 3), REG(2, 0));
1511 				return;
1512 			case 3:
1513 				if(B(10, 10) == 0)
1514 					print("%s	%s, %s, %s\n", OP2(9, 9), REG(8, 6), REG(5, 3), REG(2, 0));
1515 				else
1516 					print("%s	%d, %s, %s\n", OP2(9, 9), IMM(8, 6), REG(5, 3), REG(2, 0));
1517 				return;
1518 		}
1519 	case 1:
1520 		print("%s	%d, %s\n", OP3(12, 11), IMM(7, 0), REG(10, 8));
1521 		return;
1522 	case 2:
1523 		o = B(12, 10);
1524 		if(o == 0){
1525 			print("%s	%s, %s\n", OP4(9, 6), REG(5, 3), REG(2, 0));
1526 			return;
1527 		}
1528 		if(o == 1){
1529 			o = B(9, 8);
1530 			if(o == 3){
1531 				SBZ(7, 7);
1532 				SBZ(2, 0);
1533 				print("%s	%s\n", OP5(9, 8), LHREG(5, 3, 6));
1534 				return;
1535 			}
1536 			SNBZ(7, 6);
1537 			print("%s	%s, %s\n", OP5(9, 8), LHREG(5, 3, 6), LHREG(2, 0, 7));
1538 			return;
1539 		}
1540 		if(o == 2 || o == 3){
1541 			print("movw	%d(pc)[%x], %s\n", 4*IMM(7, 0), 4*IMM(7, 0)+pc+4, REG(10, 8));
1542 			return;
1543 		}
1544 		op = OP6(11, 9);
1545 		if(*op == 'l')
1546 			print("%s	[%s, %s], %s\n", op+1, REG(8, 6), REG(5, 3), REG(2, 0));
1547 		else
1548 			print("%s	%s, [%s, %s]\n", op+1, REG(2, 0), REG(8, 6), REG(5, 3));
1549 		return;
1550 	case 3:
1551 		op = OP7(12, 11);
1552 		if(B(12, 11) == 0 || B(12,11) == 1)
1553 			l = 4;
1554 		else
1555 			l = 1;
1556 		if(*op == 'l')
1557 			print("%s	%d(%s), %s\n", op+1, l*IMM(10, 6), REG(5, 3), REG(2, 0));
1558 		else
1559 			print("%s	%s, %d(%s)\n", op+1, REG(2, 0), l*IMM(10, 6), REG(5, 3));
1560 		return;
1561 	case 4:
1562 		if(B(12, 12) == 0){
1563 			op = OP8(11, 11);
1564 			if(*op == 'l')
1565 				print("%s	%d(%s), %s\n", op+1, 2*IMM(10, 6), REG(5, 3), REG(2, 0));
1566 			else
1567 				print("%s	%s, %d(%s)\n", op+1, REG(2, 0), 2*IMM(10, 6), REG(5, 3));
1568 			return;
1569 		}
1570 		op = OP9(11, 11);
1571 		if(*op == 'l')
1572 			print("%s	%d(sp), %s\n", op+1, 4*IMM(7, 0), REG(10, 8));
1573 		else
1574 			print("%s	%s, %d(sp)\n", op+1, REG(10, 8), 4*IMM(7, 0));
1575 		return;
1576 	case 5:
1577 		if(B(12, 12) == 0){
1578 			if(B(11, 11) == 0)
1579 				print("add	%d, pc, %s\n", 4*IMM(7, 0), REG(10, 8));
1580 			else
1581 				print("add	%d, sp, %s\n", 4*IMM(7, 0), REG(10, 8));
1582 			return;
1583 		}
1584 		if(B(11, 8) == 0){
1585 			print("%s	%d, sp\n", OP2(7, 7), 4*IMM(6, 0));
1586 			return;
1587 		}
1588 		SBO(10, 10);
1589 		SBZ(9, 9);
1590 		if(B(8, 8) == 0)
1591 			print("%s	sp, %d\n", OP10(11, 11), IMM(7, 0));
1592 		else
1593 			print("%s	sp, %d|15\n", OP10(11, 11), IMM(7, 0));
1594 		return;
1595 	case 6:
1596 		if(B(12, 12) == 0){
1597 			print("%s	%s, %d\n", OP11(11, 11), REG(10, 8), IMM(7, 0));
1598 			return;
1599 		}
1600 		if(B(11, 8) == 0xf){
1601 			print("swi	%d\n", IMM(7, 0));
1602 			return;
1603 		}
1604 		o = IMM(7, 0);
1605 		if(o&0x80)
1606 			o |= 0xffffff00;
1607 		o = pc+4+(o<<1);
1608 		print("b%s	%x\n", COND(11, 8), o);
1609 		return;
1610 	case 7:
1611 		o = B(12, 11);
1612 		switch(o){
1613 			case 0:
1614 				o = IMM(10, 0);
1615 				if(o&0x400)
1616 					o |= 0xfffff800;
1617 				o = pc+4+(o<<1);
1618 				print("b	%x\n", o);
1619 				return;
1620 			case 1:
1621 				illegal(i, pc);
1622 				return;
1623 			case 2:
1624 				lasto = IMM(10, 0);
1625 				print("bl\n");
1626 				return;
1627 			case 3:
1628 				if(lasto&0x400)
1629 					lasto |= 0xfffff800;
1630 				o = IMM(10, 0);
1631 				o = (pc-2)+4+(o<<1)+(lasto<<12);
1632 				print("bl %x\n", o);
1633 				return;
1634 		}
1635 	}
1636 }
1637