diff --git a/6502-monitor/6502-monitor.ino b/6502-monitor/6502-monitor.ino new file mode 100644 index 0000000..e4eafe4 --- /dev/null +++ b/6502-monitor/6502-monitor.ino @@ -0,0 +1,45 @@ +const char ADDR[] = {22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52}; +const char DATA[] = {39, 41, 43, 45, 47, 49, 51, 53}; +#define CLOCK 2 +#define READ_WRITE 3 + +void setup() { + for (int n = 0; n < 16; n += 1) { + pinMode(ADDR[n], INPUT); + } + for (int n = 0; n < 8; n += 1) { + pinMode(DATA[n], INPUT); + } + pinMode(CLOCK, INPUT); + pinMode(READ_WRITE, INPUT); + + attachInterrupt(digitalPinToInterrupt(CLOCK), onClock, RISING); + + Serial.begin(115200);sx +} + +void onClock() { + char output[15]; + + unsigned int address = 0; + for (int n = 0; n < 16; n += 1) { + int bit = digitalRead(ADDR[n]) ? 1 : 0; + Serial.print(bit); + address = (address << 1) + bit; + } + + Serial.print(" "); + + unsigned int data = 0; + for (int n = 0; n < 8; n += 1) { + int bit = digitalRead(DATA[n]) ? 1 : 0; + Serial.print(bit); + data = (data << 1) + bit; + } + + sprintf(output, " %04x %c %02x", address, digitalRead(READ_WRITE) ? 'r' : 'W', data); + Serial.println(output); +} + +void loop() { +} diff --git a/hello-world.s b/hello-world.s new file mode 100644 index 0000000..9d67459 --- /dev/null +++ b/hello-world.s @@ -0,0 +1,168 @@ +PORTB = $6000 +PORTA = $6001 +DDRB = $6002 +DDRA = $6003 + +E = %10000000 +RW = %01000000 +RS = %00100000 + + .org $8000 + +reset: + lda #%11111111 ; Set all pins on port B to output + sta DDRB + + lda #%11100000 ; Set top 3 pins on port A to output + sta DDRA + + lda #%00111000 ; Set 8-bit mode; 2-line display; 5x8 font + sta PORTB + lda #0 ; Clear RS/RW/E bits + sta PORTA + lda #E ; Set E bit to send instruction + sta PORTA + lda #0 ; Clear RS/RW/E bits + sta PORTA + + lda #%00001110 ; Display on; cursor on; blink off + sta PORTB + lda #0 ; Clear RS/RW/E bits + sta PORTA + lda #E ; Set E bit to send instruction + sta PORTA + lda #0 ; Clear RS/RW/E bits + sta PORTA + + lda #%00000110 ; Increment and shift cursor; don't shift display + sta PORTB + lda #0 ; Clear RS/RW/E bits + sta PORTA + lda #E ; Set E bit to send instruction + sta PORTA + lda #0 ; Clear RS/RW/E bits + sta PORTA + + lda #"H" + sta PORTB + lda #RS ; Set RS; Clear RW/E bits + sta PORTA + lda #(RS | E) ; Set E bit to send instruction + sta PORTA + lda #RS ; Clear E bits + sta PORTA + + lda #"e" + sta PORTB + lda #RS ; Set RS; Clear RW/E bits + sta PORTA + lda #(RS | E) ; Set E bit to send instruction + sta PORTA + lda #RS ; Clear E bits + sta PORTA + + lda #"l" + sta PORTB + lda #RS ; Set RS; Clear RW/E bits + sta PORTA + lda #(RS | E) ; Set E bit to send instruction + sta PORTA + lda #RS ; Clear E bits + sta PORTA + + lda #"l" + sta PORTB + lda #RS ; Set RS; Clear RW/E bits + sta PORTA + lda #(RS | E) ; Set E bit to send instruction + sta PORTA + lda #RS ; Clear E bits + sta PORTA + + lda #"o" + sta PORTB + lda #RS ; Set RS; Clear RW/E bits + sta PORTA + lda #(RS | E) ; Set E bit to send instruction + sta PORTA + lda #RS ; Clear E bits + sta PORTA + + lda #"," + sta PORTB + lda #RS ; Set RS; Clear RW/E bits + sta PORTA + lda #(RS | E) ; Set E bit to send instruction + sta PORTA + lda #RS ; Clear E bits + sta PORTA + + lda #" " + sta PORTB + lda #RS ; Set RS; Clear RW/E bits + sta PORTA + lda #(RS | E) ; Set E bit to send instruction + sta PORTA + lda #RS ; Clear E bits + sta PORTA + + lda #"w" + sta PORTB + lda #RS ; Set RS; Clear RW/E bits + sta PORTA + lda #(RS | E) ; Set E bit to send instruction + sta PORTA + lda #RS ; Clear E bits + sta PORTA + + lda #"o" + sta PORTB + lda #RS ; Set RS; Clear RW/E bits + sta PORTA + lda #(RS | E) ; Set E bit to send instruction + sta PORTA + lda #RS ; Clear E bits + sta PORTA + + lda #"r" + sta PORTB + lda #RS ; Set RS; Clear RW/E bits + sta PORTA + lda #(RS | E) ; Set E bit to send instruction + sta PORTA + lda #RS ; Clear E bits + sta PORTA + + lda #"l" + sta PORTB + lda #RS ; Set RS; Clear RW/E bits + sta PORTA + lda #(RS | E) ; Set E bit to send instruction + sta PORTA + lda #RS ; Clear E bits + sta PORTA + + lda #"d" + sta PORTB + lda #RS ; Set RS; Clear RW/E bits + sta PORTA + lda #(RS | E) ; Set E bit to send instruction + sta PORTA + lda #RS ; Clear E bits + sta PORTA + + lda #"!" + sta PORTB + lda #RS ; Set RS; Clear RW/E bits + sta PORTA + lda #(RS | E) ; Set E bit to send instruction + sta PORTA + lda #RS ; Clear E bits + sta PORTA + +loop: + jmp loop + + .org $fffc + .word reset + .word $0000 diff --git a/makecounter.py b/makecounter.py new file mode 100644 index 0000000..cb3095a --- /dev/null +++ b/makecounter.py @@ -0,0 +1,25 @@ +# +# Please see this video for details: +# https://www.youtube.com/watch?v=yl8vPW5hydQ +# +code = bytearray([ + 0xa9, 0xff, # lda #$ff # set output register B + 0x8d, 0x02, 0x60, # sta $6002 + + 0xa9, 0x00, # lda #$00 # Start at 0 + + 0x8d, 0x00, 0x60, # sta $6000 # Latch out + 0x69, 0x01, # add #$1 + + 0x4c, 0x07, 0x80, # jmp $8005 # go back to first output + ]) + +rom = code + bytearray([0xea] * (32768 - len(code))) + + +# Reset Vector +rom[0x7ffc] = 0x00 +rom[0x7ffd] = 0x80 + +with open("rom-counter.bin", "wb") as out_file: + out_file.write(rom) diff --git a/mult.s b/mult.s new file mode 100644 index 0000000..d0e9685 --- /dev/null +++ b/mult.s @@ -0,0 +1,14 @@ + .org $8000 + reset: + + lda $9000 + + + .org $9000 + .byte 18 + .byte 03 + .org $fffc + .word reset + .word $0000 + + diff --git a/rom-counter.s b/rom-counter.s new file mode 100644 index 0000000..7df8fae --- /dev/null +++ b/rom-counter.s @@ -0,0 +1,26 @@ + .org $8000 + reset: + ;; set output register + lda #$ff + sta $6002 + + ;; flash the lights + ;; lda #$50 + ;; sta $6000 + + ;; start at 0 + lda #$00 + +loop: + sta $6000 + ;; increment + adc #$1 + + jmp loop + + + .org $fffc + .word reset + .word $0000 + + diff --git a/template.s b/template.s new file mode 100644 index 0000000..e80bf7a --- /dev/null +++ b/template.s @@ -0,0 +1,11 @@ + .org $8000 + reset: + + ;; main program goes here + + + .org $fffc + .word reset + .word $0000 + + diff --git a/vasm/Makefile b/vasm/Makefile new file mode 100644 index 0000000..46ad3e3 --- /dev/null +++ b/vasm/Makefile @@ -0,0 +1,18 @@ +# Unix +# Define CC, when no compiler with the name "cc" exists. + +TARGET = +TARGETEXTENSION = +OUTFMTS = -DOUTAOUT -DOUTBIN -DOUTELF -DOUTHUNK -DOUTSREC -DOUTTOS -DOUTVOBJ \ + -DOUTXFIL + +CCOUT = -o +COPTS = -c -O2 -DUNIX $(OUTFMTS) + +LD = $(CC) +LDOUT = $(CCOUT) +LDFLAGS = -lm + +RM = rm -f + +include make.rules diff --git a/vasm/Makefile.68k b/vasm/Makefile.68k new file mode 100644 index 0000000..b4658f2 --- /dev/null +++ b/vasm/Makefile.68k @@ -0,0 +1,18 @@ +# AmigaOS/68k + +TARGET = _os3 +TARGETEXTENSION = +OUTFMTS = -DOUTAOUT -DOUTBIN -DOUTELF -DOUTHUNK -DOUTSREC -DOUTTOS -DOUTVOBJ \ + -DOUTXFIL + +CC = vc +aos68k +CCOUT = -o +COPTS = -c -c99 -cpu=68020 -DAMIGA $(OUTFMTS) -O1 + +LD = $(CC) +LDOUT = $(CCOUT) +LDFLAGS = -lmieee + +RM = delete force quiet + +include make.rules diff --git a/vasm/Makefile.Cygwin b/vasm/Makefile.Cygwin new file mode 100644 index 0000000..406feb5 --- /dev/null +++ b/vasm/Makefile.Cygwin @@ -0,0 +1,18 @@ +# Windows compiled with gcc + +TARGET = _win32 +TARGETEXTENSION = .exe +OUTFMTS = -DOUTAOUT -DOUTBIN -DOUTELF -DOUTHUNK -DOUTSREC -DOUTTOS -DOUTVOBJ \ + -DOUTXFIL + +CC = gcc +CCOUT = -o +COPTS = -c -O2 -D_WIN32 $(OUTFMTS) + +LD = $(CC) +LDOUT = $(CCOUT) +LDFLAGS = -lm + +RM = rm -f + +include make.rules diff --git a/vasm/Makefile.Haiku b/vasm/Makefile.Haiku new file mode 100644 index 0000000..05e0096 --- /dev/null +++ b/vasm/Makefile.Haiku @@ -0,0 +1,18 @@ +# Unix + +TARGET = +TARGETEXTENSION = +OUTFMTS = -DOUTAOUT -DOUTBIN -DOUTELF -DOUTHUNK -DOUTSREC -DOUTTOS -DOUTVOBJ \ + -DOUTXFIL + +CC = gcc +CCOUT = -o +COPTS = -c -O2 $(OUTFMTS) + +LD = $(CC) +LDOUT = $(CCOUT) +LDFLAGS = + +RM = rm -f + +include make.rules diff --git a/vasm/Makefile.MOS b/vasm/Makefile.MOS new file mode 100644 index 0000000..9abda33 --- /dev/null +++ b/vasm/Makefile.MOS @@ -0,0 +1,18 @@ +# MorphOS + +TARGET = _mos +TARGETEXTENSION = +OUTFMTS = -DOUTAOUT -DOUTBIN -DOUTELF -DOUTHUNK -DOUTSREC -DOUTTOS -DOUTVOBJ \ + -DOUTXFIL + +CC = vc +morphos +CCOUT = -o +COPTS = -c -DAMIGA -O1 $(OUTFMTS) + +LD = $(CC) +LDOUT = $(CCOUT) +LDFLAGS = -lm + +RM = delete force quiet + +include make.rules diff --git a/vasm/Makefile.MiNT b/vasm/Makefile.MiNT new file mode 100644 index 0000000..996da19 --- /dev/null +++ b/vasm/Makefile.MiNT @@ -0,0 +1,18 @@ +# Atari TOS/MiNT + +TARGET = _MiNT +TARGETEXTENSION = +OUTFMTS = -DOUTAOUT -DOUTBIN -DOUTELF -DOUTHUNK -DOUTSREC -DOUTTOS -DOUTVOBJ \ + -DOUTXFIL + +CC = vc +mint +CCOUT = -o +COPTS = -c -c99 -cpu=68020 -O1 $(OUTFMTS) + +LD = $(CC) +LDOUT = $(CCOUT) +LDFLAGS = -lm + +RM = rm -f + +include make.rules diff --git a/vasm/Makefile.OS4 b/vasm/Makefile.OS4 new file mode 100644 index 0000000..b4bec72 --- /dev/null +++ b/vasm/Makefile.OS4 @@ -0,0 +1,18 @@ +# AmigaOS 4.x/PPC + +TARGET = _os4 +TARGETEXTENSION = +OUTFMTS = -DOUTAOUT -DOUTBIN -DOUTELF -DOUTHUNK -DOUTSREC -DOUTTOS -DOUTVOBJ \ + -DOUTXFIL + +CC = vc +aosppc +CCOUT = -o +COPTS = -c -DAMIGA -D__USE_INLINE__ -O1 $(OUTFMTS) + +LD = $(CC) +LDOUT = $(CCOUT) +LDFLAGS = -lm + +RM = delete force quiet + +include make.rules diff --git a/vasm/Makefile.PUp b/vasm/Makefile.PUp new file mode 100644 index 0000000..91c0fe7 --- /dev/null +++ b/vasm/Makefile.PUp @@ -0,0 +1,18 @@ +# PowerUp + +TARGET = _pup +TARGETEXTENSION = +OUTFMTS = -DOUTAOUT -DOUTBIN -DOUTELF -DOUTHUNK -DOUTSREC -DOUTTOS -DOUTVOBJ \ + -DOUTXFIL + +CC = vc +powerup +CCOUT = -o +COPTS = -c -c99 -DAMIGA -O1 $(OUTFMTS) + +LD = $(CC) +LDOUT = $(CCOUT) +LDFLAGS = -lm -lamiga + +RM = delete force quiet + +include make.rules diff --git a/vasm/Makefile.TOS b/vasm/Makefile.TOS new file mode 100644 index 0000000..1608a17 --- /dev/null +++ b/vasm/Makefile.TOS @@ -0,0 +1,18 @@ +# Atari TOS + +TARGET = _TOS +TARGETEXTENSION = .ttp +OUTFMTS = -DOUTAOUT -DOUTBIN -DOUTELF -DOUTHUNK -DOUTSREC -DOUTTOS -DOUTVOBJ \ + -DOUTXFIL + +CC = vc +tos +CCOUT = -o +COPTS = -c -c99 -O1 -DATARI $(OUTFMTS) + +LD = $(CC) +LDOUT = $(CCOUT) +LDFLAGS = -lm + +RM = rm -f + +include make.rules diff --git a/vasm/Makefile.WOS b/vasm/Makefile.WOS new file mode 100644 index 0000000..ff0b062 --- /dev/null +++ b/vasm/Makefile.WOS @@ -0,0 +1,18 @@ +# WarpOS + +TARGET = _wos +TARGETEXTENSION = +OUTFMTS = -DOUTAOUT -DOUTBIN -DOUTELF -DOUTHUNK -DOUTSREC -DOUTTOS -DOUTVOBJ \ + -DOUTXFIL + +CC = vc +warpos +CCOUT = -o +COPTS = -c -c99 -DAMIGA -O1 $(OUTFMTS) + +LD = $(CC) +LDOUT = $(CCOUT) +LDFLAGS = -lm -lamiga + +RM = delete force quiet + +include make.rules diff --git a/vasm/Makefile.Win32 b/vasm/Makefile.Win32 new file mode 100644 index 0000000..fe1fb82 --- /dev/null +++ b/vasm/Makefile.Win32 @@ -0,0 +1,30 @@ +# Windows +# Tested with Visual Studio 2017: works fine under the Developer Command Prompt for VS2017 +# Tested with Visual Studio 2005 Express Edition: works fine +# Tested with Visual C++ Toolkit 2003: works fine, but needs an external make tool (nmake is not included) + +TARGET = _win32 +TARGETEXTENSION = .exe +OUTFMTS = -DOUTAOUT -DOUTBIN -DOUTELF -DOUTHUNK -DOUTSREC -DOUTTOS -DOUTVOBJ \ + -DOUTXFIL + +# If Visual Studio is unable to find when compiling vlink, try enabling the two +# lines below, and point them to where you have installed the Win32 Platform SDK. + +#WIN32_PLATFORMSDK_INCLUDE = "/IC:\Code\Win32 Platform SDK\Include" +#WIN32_PLATFORMSDK_LIB = "/LIBPATH:C:\Code\Win32 Platform SDK\Lib" + +CC = cl +CCOUT = /Fo +COPTS = $(OUTFMTS) /nologo /O2 /MT /Zp1 /c +COPTS = $(COPTS) /wd4996 # Disable warning regarding deprecated functions + # ("use strcpy_s instead of strcpy" etc) +COPTS = $(COPTS) $(WIN32_PLATFORMSDK_INCLUDE) + +LD = link +LDOUT = /OUT: +LDFLAGS = /NOLOGO $(WIN32_PLATFORMSDK_LIB) + +RM = rem + +include make.rules diff --git a/vasm/Makefile.Win32FromLinux b/vasm/Makefile.Win32FromLinux new file mode 100644 index 0000000..2134417 --- /dev/null +++ b/vasm/Makefile.Win32FromLinux @@ -0,0 +1,22 @@ +# Windows compiled on a Linux machine with mingw + +TARGET = _win32 +TARGETEXTENSION = .exe +OUTFMTS = -DOUTAOUT -DOUTBIN -DOUTELF -DOUTHUNK -DOUTSREC -DOUTTOS -DOUTVOBJ \ + -DOUTXFIL + + +#CC = /usr/bin/i586-mingw32msvc-gcc +CC = /usr/bin/i686-w64-mingw32-gcc +CCOUT = -o +COPTS = -c -O2 $(OUTFMTS) + +LD = $(CC) +LDOUT = $(CCOUT) +LDFLAGS = -lm + +RM = rm -f + + + +include make.rules diff --git a/vasm/atom.c b/vasm/atom.c new file mode 100644 index 0000000..dbeb11b --- /dev/null +++ b/vasm/atom.c @@ -0,0 +1,661 @@ +/* atom.c - atomic objects from source */ +/* (c) in 2010-2019 by Volker Barthelmann and Frank Wille */ + +#include "vasm.h" + + +/* searches mnemonic list and tries to parse (via the cpu module) + the operands according to the mnemonic requirements; returns an + instruction or 0 */ +instruction *new_inst(char *inst,int len,int op_cnt,char **op,int *op_len) +{ +#if MAX_OPERANDS!=0 + operand ops[MAX_OPERANDS]; + int j,k,mnemo_opcnt,omitted,skipped; +#endif + int i,inst_found=0; + hashdata data; + instruction *new; + + new = mymalloc(sizeof(*new)); +#if HAVE_INSTRUCTION_EXTENSION + init_instruction_ext(&new->ext); +#endif +#if MAX_OPERANDS!=0 && NEED_CLEARED_OPERANDS!=0 + /* reset operands to allow the cpu-backend to parse them only once */ + memset(ops,0,sizeof(ops)); +#endif + + if (find_namelen_nc(mnemohash,inst,len,&data)) { + i = data.idx; + + /* try all mnemonics with the same name until operands match */ + do { + inst_found = 1; + if (!MNEMONIC_VALID(i)) { + i++; + continue; /* try next */ + } + +#if MAX_OPERANDS!=0 + +#if ALLOW_EMPTY_OPS + mnemo_opcnt = op_cnt= op_cnt) /* missing mandatory operands */ + break; + + rc = parse_operand(op[k],op_len[k],&ops[j], + mnemonics[i].operand_type[j]); + + if (rc == PO_CORRUPT) { + myfree(new); + restore_symbols(); + return 0; + } + if (rc == PO_NOMATCH) + break; + + /* MATCH, move to next parsed operand */ + k++; + if (rc == PO_SKIP) { /* but skip next operand type from table */ + j++; + skipped++; + } + } + } + +#if IGNORE_FIRST_EXTRA_OP + if (mnemo_opcnt > 0) +#endif + if (jop[j] = mymalloc(sizeof(operand)); + *new->op[j] = ops[j]; + } + for(; jop[j] = 0; + +#endif /* MAX_OPERANDS!=0 */ + + new->code = i; + return new; + } + while (isize = 0; + new->data = 0; + new->relocs = 0; + return new; +} + + +sblock *new_sblock(expr *space,size_t size,expr *fill) +{ + sblock *sb = mymalloc(sizeof(sblock)); + + sb->space = 0; + sb->space_exp = space; + sb->size = size; + if (!(sb->fill_exp = fill)) + memset(sb->fill,0,MAXPADBYTES); + sb->relocs = 0; + sb->maxalignbytes = 0; + sb->flags = 0; + return sb; +} + + +static size_t space_size(sblock *sb,section *sec,taddr pc) +{ + utaddr space=0; + + if (eval_expr(sb->space_exp,&space,sec,pc) || !final_pass) + sb->space = space; + else + general_error(30); /* expression must be constant */ + + if (final_pass && sb->fill_exp) { + if (sb->size <= sizeof(taddr)) { + /* space is filled with an expression which may also need relocations */ + symbol *base=NULL; + taddr fill; + utaddr i; + + if (!eval_expr(sb->fill_exp,&fill,sec,pc)) { + if (find_base(sb->fill_exp,&base,sec,pc)==BASE_ILLEGAL) + general_error(38); /* illegal relocation */ + } + copy_cpu_taddr(sb->fill,fill,sb->size); + if (base && !sb->relocs) { + /* generate relocations */ + for (i=0; irelocs,base,fill,REL_ABS, + 0,sb->size<<3,sb->size*i); + } + } + else + general_error(30); /* expression must be constant */ + } + + return sb->size * space; +} + + +static size_t roffs_size(expr *offsexp,section *sec,taddr pc) +{ + taddr offs; + + eval_expr(offsexp,&offs,sec,pc); + offs = sec->org + offs - pc; + return offs>0 ? offs : 0; +} + + +/* adds an atom to the specified section; if sec==0, the current + section is used */ +void add_atom(section *sec,atom *a) +{ + if (!sec) { + sec = default_section(); + if (!sec) { + general_error(3); + return; + } + } + + a->changes = 0; + a->src = cur_src; + a->line = cur_src!=NULL ? cur_src->line : 0; + + if (sec->last) { + atom *pa = sec->last; + + pa->next = a; + /* make sure that a label on the same line gets the same alignment */ + if (pa->type==LABEL && pa->line==a->line && + (a->type==INSTRUCTION || a->type==DATADEF || a->type==SPACE)) + pa->align = a->align; + } + else + sec->first = a; + a->next = 0; + sec->last = a; + + sec->pc = pcalign(a,sec->pc); + a->lastsize = atom_size(a,sec,sec->pc); + sec->pc += a->lastsize; + if (a->align > sec->align) + sec->align = a->align; + + if (listena) { + a->list = last_listing; + if (last_listing) { + if (!last_listing->atom) + last_listing->atom = a; + } + } + else + a->list = 0; +} + + +size_t atom_size(atom *p,section *sec,taddr pc) +{ + switch(p->type) { + case LABEL: + case LINE: + case OPTS: + case PRINTTEXT: + case PRINTEXPR: + case RORG: + case RORGEND: + case ASSERT: + case NLIST: /* it has a size, but not in the current section */ + return 0; + case DATA: + return p->content.db->size; + case INSTRUCTION: + return p->content.inst->code>=0? + instruction_size(p->content.inst,sec,pc):0; + case SPACE: + return space_size(p->content.sb,sec,pc); + case DATADEF: + return (p->content.defb->bitsize+7)/8; + case ROFFS: + return roffs_size(p->content.roffs,sec,pc); + default: + ierror(0); + break; + } + return 0; +} + + +static void print_instruction(FILE *f,instruction *p) +{ + int i; + + printf("inst %d(%s) ",p->code,p->code>=0?mnemonics[p->code].name:"deleted"); +#if MAX_OPERANDS!=0 + for (i=0; iop[i]); +#endif +} + + +void print_atom(FILE *f,atom *p) +{ + size_t i; + rlist *rl; + + switch (p->type) { + case LABEL: + fprintf(f,"symbol: "); + print_symbol(f,p->content.label); + break; + case DATA: + fprintf(f,"data(%lu): ",(unsigned long)p->content.db->size); + for (i=0;icontent.db->size;i++) + fprintf(f,"%02x ",p->content.db->data[i]); + for (rl=p->content.db->relocs; rl; rl=rl->next) + print_reloc(f,rl->type,rl->reloc); + break; + case INSTRUCTION: + print_instruction(f,p->content.inst); + break; + case SPACE: + fprintf(f,"space(%lu,fill=", + (unsigned long)(p->content.sb->space*p->content.sb->size)); + for (i=0; icontent.sb->size; i++) + fprintf(f,"%02x%c",(unsigned char)p->content.sb->fill[i], + (i==p->content.sb->size-1)?')':' '); + for (rl=p->content.sb->relocs; rl; rl=rl->next) + print_reloc(f,rl->type,rl->reloc); + break; + case DATADEF: + fprintf(f,"datadef(%lu bits)",(unsigned long)p->content.defb->bitsize); + break; + case LINE: + fprintf(f,"line: %d of %s",p->content.srcline,getdebugname()); + break; +#if HAVE_CPU_OPTS + case OPTS: + print_cpu_opts(f,p->content.opts); + break; +#endif + case PRINTTEXT: + fprintf(f,"text: \"%s\"",p->content.ptext); + break; + case PRINTEXPR: + fprintf(f,"expr: "); + print_expr(f,p->content.pexpr->print_exp); + break; + case ROFFS: + fprintf(f,"roffs: offset "); + print_expr(f,p->content.roffs); + break; + case RORG: + fprintf(f,"rorg: relocate to 0x%llx",ULLTADDR(*p->content.rorg)); + break; + case RORGEND: + fprintf(f,"rorg end"); + break; + case ASSERT: + fprintf(f,"assert: %s (message: %s)\n",p->content.assert->expstr, + p->content.assert->msgstr?p->content.assert->msgstr:emptystr); + break; + case NLIST: + fprintf(f,"nlist: %s (type %d, other %d, desc %d) with value ", + p->content.nlist->name!=NULL ? p->content.nlist->name : "", + p->content.nlist->type,p->content.nlist->other, + p->content.nlist->desc); + if (p->content.nlist->value != NULL) + print_expr(f,p->content.nlist->value); + else + fprintf(f,"NULL"); + break; + default: + ierror(0); + } +} + + +/* prints and formats an expression from a PRINTEXPR atom */ +void atom_printexpr(printexpr *pexp,section *sec,taddr pc) +{ + taddr t; + long long v; + int i; + + eval_expr(pexp->print_exp,&t,sec,pc); + if (pexp->type==PEXP_SDEC && (t&(1LL<<(pexp->size-1)))!=0) { + /* signed decimal */ + v = -1; + v &= ~(long long)MAKEMASK(pexp->size); + } + else + v = 0; + v |= t & MAKEMASK(pexp->size); + + switch (pexp->type) { + case PEXP_HEX: + printf("%llX",(unsigned long long)v); + break; + case PEXP_SDEC: + printf("%lld",v); + break; + case PEXP_UDEC: + printf("%llu",(unsigned long long)v); + break; + case PEXP_BIN: + for (i=pexp->size-1; i>=0; i--) + putchar((v & (1LL<size+7)>>3)-1; i>=0; i--) { + unsigned char c = (v>>(i*8))&0xff; + putchar(isprint(c) ? c : '.'); + } + break; + default: + ierror(0); + break; + } +} + + +atom *clone_atom(atom *a) +{ + atom *new = mymalloc(sizeof(atom)); + void *p; + + memcpy(new,a,sizeof(atom)); + + switch (a->type) { + /* INSTRUCTION and DATADEF have to be cloned as well, because they will + be deallocated and transformed into DATA during assemble() */ + case INSTRUCTION: + p = mymalloc(sizeof(instruction)); + memcpy(p,a->content.inst,sizeof(instruction)); + new->content.inst = p; + break; + case DATADEF: + p = mymalloc(sizeof(defblock)); + memcpy(p,a->content.defb,sizeof(defblock)); + new->content.defb = p; + break; + default: + break; + } + + new->next = 0; + new->src = NULL; + new->line = 0; + new->list = NULL; + return new; +} + + +atom *add_data_atom(section *sec,size_t sz,taddr alignment,taddr c) +{ + dblock *db = new_dblock(); + atom *a; + + db->size = sz; + db->data = mymalloc(sz); + if (sz > 1) + setval(BIGENDIAN,db->data,sz,c); + else + *(db->data) = c; + + a = new_data_atom(db,alignment); + add_atom(sec,a); + return a; +} + + +void add_leb128_atom(section *sec,taddr c) +{ + taddr b; + + do { + b = c & 0x7f; + if ((c >>= 7) != 0) + b |= 0x80; + add_data_atom(sec,1,1,b); + } while (c != 0); +} + + +void add_sleb128_atom(section *sec,taddr c) +{ + int done = 0; + taddr b; + + do { + b = c & 0x7f; + c >>= 7; /* assumes arithmetic shifts! */ + if ((c==0 && !(b&0x40)) || (c==-1 && (b&0x40))) + done = 1; + else + b |= 0x80; + add_data_atom(sec,1,1,b); + } while (!done); +} + + +atom *add_bytes_atom(section *sec,void *p,size_t sz) +{ + dblock *db = new_dblock(); + atom *a; + + db->size = sz; + db->data = mymalloc(sz); + memcpy(db->data,p,sz); + a = new_data_atom(db,1); + add_atom(sec,a); + return a; +} + + +static atom *new_atom(int type,taddr align) +{ + atom *new = mymalloc(sizeof(*new)); + + new->next = NULL; + new->type = type; + new->align = align; + return new; +} + + +atom *new_inst_atom(instruction *p) +{ + atom *new = new_atom(INSTRUCTION,inst_alignment); + + new->content.inst = p; + return new; +} + + +atom *new_data_atom(dblock *p,taddr align) +{ + atom *new = new_atom(DATA,align); + + new->content.db = p; + return new; +} + + +atom *new_label_atom(symbol *p) +{ + atom *new = new_atom(LABEL,1); + + new->content.label = p; + return new; +} + + +atom *new_space_atom(expr *space,size_t size,expr *fill) +{ + atom *new = new_atom(SPACE,1); + int i; + + if (size<1) + ierror(0); /* usually an error in syntax-module */ + new->content.sb = new_sblock(space,size,fill); + return new; +} + + +atom *new_datadef_atom(size_t bitsize,operand *op) +{ + atom *new = new_atom(DATADEF,DATA_ALIGN(bitsize)); + + new->content.defb = mymalloc(sizeof(*new->content.defb)); + new->content.defb->bitsize = bitsize; + new->content.defb->op = op; + return new; +} + + +atom *new_srcline_atom(int line) +{ + atom *new = new_atom(LINE,1); + + new->content.srcline = line; + return new; +} + + +atom *new_opts_atom(void *o) +{ + atom *new = new_atom(OPTS,1); + + new->content.opts = o; + return new; +} + + +atom *new_text_atom(char *txt) +{ + atom *new = new_atom(PRINTTEXT,1); + + new->content.ptext = txt ? txt : "\n"; + return new; +} + + +atom *new_expr_atom(expr *exp,int type,int size) +{ + atom *new = new_atom(PRINTEXPR,1); + + new->content.pexpr = mymalloc(sizeof(*new->content.pexpr)); + if (exp==NULL || typePEXP_ASC || size<1 + || size>sizeof(long long)*8) + ierror(0); + new->content.pexpr->print_exp = exp; + new->content.pexpr->type = type; + new->content.pexpr->size = size; + return new; +} + + +atom *new_roffs_atom(expr *offs) +{ + atom *new = new_atom(ROFFS,1); + + new->content.roffs = offs; + return new; +} + + +atom *new_rorg_atom(taddr raddr) +{ + atom *new = new_atom(RORG,1); + taddr *newrorg = mymalloc(sizeof(taddr)); + + *newrorg = raddr; + new->content.rorg = newrorg; + return new; +} + + +atom *new_rorgend_atom(void) +{ + return new_atom(RORGEND,1); +} + + +atom *new_assert_atom(expr *aexp,char *exp,char *msg) +{ + atom *new = new_atom(ASSERT,1); + + new->content.assert = mymalloc(sizeof(*new->content.assert)); + new->content.assert->assert_exp = aexp; + new->content.assert->expstr = exp; + new->content.assert->msgstr = msg; + return new; +} + + +atom *new_nlist_atom(char *name,int type,int other,int desc,expr *value) +{ + atom *new = new_atom(NLIST,1); + + new->content.nlist = mymalloc(sizeof(*new->content.nlist)); + new->content.nlist->name = name; + new->content.nlist->type = type; + new->content.nlist->other = other; + new->content.nlist->desc = desc; + new->content.nlist->value = value; + return new; +} diff --git a/vasm/atom.h b/vasm/atom.h new file mode 100644 index 0000000..5d1e1f1 --- /dev/null +++ b/vasm/atom.h @@ -0,0 +1,145 @@ +/* atom.h - atomic objects from source */ +/* (c) in 2010-2019 by Volker Barthelmann and Frank Wille */ + +#ifndef ATOM_H +#define ATOM_H + +/* types of atoms */ +#define LABEL 1 +#define DATA 2 +#define INSTRUCTION 3 +#define SPACE 4 +#define DATADEF 5 +#define LINE 6 +#define OPTS 7 +#define PRINTTEXT 8 +#define PRINTEXPR 9 +#define ROFFS 10 +#define RORG 11 +#define RORGEND 12 +#define ASSERT 13 +#define NLIST 14 + +/* a machine instruction */ +typedef struct instruction { + int code; +#if MAX_QUALIFIERS!=0 + char *qualifiers[MAX_QUALIFIERS]; +#endif +#if MAX_OPERANDS!=0 + operand *op[MAX_OPERANDS]; +#endif +#if HAVE_INSTRUCTION_EXTENSION + instruction_ext ext; +#endif +} instruction; + +typedef struct defblock { + size_t bitsize; + operand *op; +} defblock; + +struct dblock { + size_t size; + unsigned char *data; + rlist *relocs; +}; + +struct sblock { + size_t space; + expr *space_exp; /* copied to space, when evaluated as constant */ + size_t size; + uint8_t fill[MAXPADBYTES]; + expr *fill_exp; /* copied to fill, when evaluated - may be NULL */ + rlist *relocs; + taddr maxalignbytes; + uint32_t flags; +}; +#define SPC_DATABSS 1 /* make sure no to allocate space in a data section */ + +typedef struct printexpr { + expr *print_exp; + short type; /* hex, signed, unsigned */ + short size; /* precision in bits */ +} printexpr; +#define PEXP_HEX 0 +#define PEXP_SDEC 1 +#define PEXP_UDEC 2 +#define PEXP_BIN 3 +#define PEXP_ASC 4 + +typedef struct assertion { + expr *assert_exp; + char *expstr; + char *msgstr; +} assertion; + +typedef struct aoutnlist { + char *name; + int type; + int other; + int desc; + expr *value; +} aoutnlist; + +/* an atomic element of data */ +typedef struct atom { + struct atom *next; + int type; + taddr align; + size_t lastsize; + unsigned changes; + source *src; + int line; + listing *list; + union { + instruction *inst; + dblock *db; + symbol *label; + sblock *sb; + defblock *defb; + void *opts; + int srcline; + char *ptext; + printexpr *pexpr; + expr *roffs; + taddr *rorg; + assertion *assert; + aoutnlist *nlist; + } content; +} atom; + +#define MAXSIZECHANGES 5 /* warning, when atom changed size so many times */ + +instruction *new_inst(char *inst,int len,int op_cnt,char **op,int *op_len); +dblock *new_dblock(); +sblock *new_sblock(expr *,size_t,expr *); + +void add_atom(section *,atom *); +size_t atom_size(atom *,section *,taddr); +void print_atom(FILE *,atom *); +void atom_printexpr(printexpr *,section *,taddr); +atom *clone_atom(atom *); + +atom *add_data_atom(section *,size_t,taddr,taddr); +void add_leb128_atom(section *,taddr); +void add_sleb128_atom(section *,taddr); +atom *add_bytes_atom(section *,void *,size_t); +#define add_string_atom(s,p) add_bytes_atom(s,p,strlen(p)+1) + +atom *new_inst_atom(instruction *); +atom *new_data_atom(dblock *,taddr); +atom *new_label_atom(symbol *); +atom *new_space_atom(expr *,size_t,expr *); +atom *new_datadef_atom(size_t,operand *); +atom *new_srcline_atom(int); +atom *new_opts_atom(void *); +atom *new_text_atom(char *); +atom *new_expr_atom(expr *,int,int); +atom *new_roffs_atom(expr *); +atom *new_rorg_atom(taddr); +atom *new_rorgend_atom(void); +atom *new_assert_atom(expr *,char *,char *); +atom *new_nlist_atom(char *,int,int,int,expr *); + +#endif diff --git a/vasm/cond.c b/vasm/cond.c new file mode 100644 index 0000000..dd46320 --- /dev/null +++ b/vasm/cond.c @@ -0,0 +1,85 @@ +/* cond.c - conditional assembly support routines */ +/* (c) in 2015 by Frank Wille */ + +#include "vasm.h" + +int clev; /* conditional level */ + +static char cond[MAXCONDLEV+1]; +static char *condsrc[MAXCONDLEV+1]; +static int condline[MAXCONDLEV+1]; +static int ifnesting; + + +/* initialize conditional assembly */ +void cond_init(void) +{ + cond[0] = 1; + clev = ifnesting = 0; +} + + +/* return true, when current level allows assembling */ +int cond_state(void) +{ + return cond[clev]; +} + + +/* ensures that all conditional block are closed at the end of the source */ +void cond_check(void) +{ + if (clev > 0) + general_error(66,condsrc[clev],condline[clev]); /* "endc/endif missing */ +} + + +/* establish a new level of conditional assembly */ +void cond_if(char flag) +{ + if (++clev >= MAXCONDLEV) + general_error(65,clev); /* nesting depth exceeded */ + + cond[clev] = flag; + condsrc[clev] = cur_src->name; + condline[clev] = cur_src->line; +} + + +/* handle skipped if statement */ +void cond_skipif(void) +{ + ifnesting++; +} + + +/* handle else statement after skipped if-branch */ +void cond_else(void) +{ + if (ifnesting == 0) + cond[clev] = 1; +} + + +/* handle else statement after assembled if-branch */ +void cond_skipelse(void) +{ + if (clev > 0) + cond[clev] = 0; + else + general_error(63); /* else without if */ +} + + +/* handle end-if statement */ +void cond_endif(void) +{ + if (ifnesting == 0) { + if (clev > 0) + clev--; + else + general_error(64); /* unexpected endif without if */ + } + else /* the whole conditional block was ignored */ + ifnesting--; +} diff --git a/vasm/cond.h b/vasm/cond.h new file mode 100644 index 0000000..90a155c --- /dev/null +++ b/vasm/cond.h @@ -0,0 +1,25 @@ +/* cond.h - conditional assembly support routines */ +/* (c) in 2015 by Frank Wille */ + +#ifndef COND_H +#define COND_H + +/* defines */ +#ifndef MAXCONDLEV +#define MAXCONDLEV 63 +#endif + +/* global variables */ +extern int clev; + +/* functions */ +void cond_init(void); +int cond_state(void); +void cond_check(void); +void cond_if(char); +void cond_skipif(void); +void cond_else(void); +void cond_skipelse(void); +void cond_endif(void); + +#endif /* COND_H */ diff --git a/vasm/cpus/6502/cpu.c b/vasm/cpus/6502/cpu.c new file mode 100644 index 0000000..218eed5 --- /dev/null +++ b/vasm/cpus/6502/cpu.c @@ -0,0 +1,574 @@ +/* +** cpu.c 650x/65C02/6510/6280 cpu-description file +** (c) in 2002,2006,2008-2012,2014-2018 by Frank Wille +*/ + +#include "vasm.h" + +mnemonic mnemonics[] = { +#include "opcodes.h" +}; + +int mnemonic_cnt=sizeof(mnemonics)/sizeof(mnemonics[0]); + +char *cpu_copyright="vasm 6502 cpu backend 0.8 (c) 2002,2006,2008-2012,2014-2018 Frank Wille"; +char *cpuname = "6502"; +int bitsperbyte = 8; +int bytespertaddr = 2; + +static uint16_t cpu_type = M6502; +static int branchopt = 0; +static int modifier; /* set by find_base() */ +static utaddr dpage = 0; /* default zero/direct page - set with SETDP */ + + +int ext_unary_eval(int type,taddr val,taddr *result,int cnst) +{ + switch (type) { + case LOBYTE: + *result = cnst ? (val & 0xff) : val; + return 1; + case HIBYTE: + *result = cnst ? ((val >> 8) & 0xff) : val; + return 1; + default: + break; + } + return 0; /* unknown type */ +} + + +int ext_find_base(symbol **base,expr *p,section *sec,taddr pc) +{ + /* addr/256 equals >addr, addr%256 and addr&255 equal type==DIV || p->type==MOD) { + if (p->right->type==NUM && p->right->c.val==256) + p->type = p->type == DIV ? HIBYTE : LOBYTE; + } + else if (p->type==BAND && p->right->type==NUM && p->right->c.val==255) + p->type = LOBYTE; + + if (p->type==LOBYTE || p->type==HIBYTE) { + modifier = p->type; + return find_base(p->left,base,sec,pc); + } + return BASE_ILLEGAL; +} + + +int parse_operand(char *p,int len,operand *op,int required) +{ + char *start = p; + int indir = 0; + + p = skip(p); + if (len>0 && required!=DATAOP && check_indir(p,start+len)) { + indir = 1; + p = skip(p+1); + } + + switch (required) { + case IMMED: + if (*p++ != '#') + return PO_NOMATCH; + p = skip(p); + break; + case INDIR: + case INDIRX: + case INDX: + case INDY: + case DPINDIR: + if (!indir) + return PO_NOMATCH; + break; + case WBIT: + if (*p == '#') /* # is optional */ + p = skip(p+1); + default: + if (indir) + return PO_NOMATCH; + break; + } + + op->dp = dpage; + if (required < ACCU) + op->value = parse_expr(&p); + else + op->value = NULL; + + switch (required) { + case INDX: + case INDIRX: + if (*p++ == ',') { + p = skip(p); + if (toupper((unsigned char)*p++) != 'X') + return PO_NOMATCH; + } + else + return PO_NOMATCH; + break; + case ACCU: + if (len != 0) { + if (len!=1 || toupper((unsigned char)*p++) != 'A') + return PO_NOMATCH; + } + break; + case DUMX: + if (toupper((unsigned char)*p++) != 'X') + return PO_NOMATCH; + break; + case DUMY: + if (toupper((unsigned char)*p++) != 'Y') + return PO_NOMATCH; + break; + } + + if (required==INDIR || required==INDX || required==INDY + || required==DPINDIR || required==INDIRX) { + p = skip(p); + if (*p++ != ')') { + cpu_error(2); /* missing closing parenthesis */ + return PO_CORRUPT; + } + } + + p = skip(p); + if (p-start < len) + cpu_error(1); /* trailing garbage in operand */ + op->type = required; + return PO_MATCH; +} + + +char *parse_cpu_special(char *start) +{ + char *name=start,*s=start; + + if (ISIDSTART(*s)) { + s++; + while (ISIDCHAR(*s)) + s++; + + if (s-name==5 && !strnicmp(name,"setdp",5)) { + s = skip(s); + dpage = (utaddr)parse_constexpr(&s); + eol(s); + return skip_line(s); + } + } + return start; +} + + +static instruction *copy_instruction(instruction *ip) +/* copy an instruction and its operands */ +{ + static instruction newip; + static operand newop[MAX_OPERANDS]; + int i; + + newip.code = ip->code; + + for (i=0; iop[i] != NULL) { + newip.op[i] = &newop[i]; + *newip.op[i] = *ip->op[i]; + } + else + newip.op[i] = NULL; + } + + return &newip; +} + + +static void optimize_instruction(instruction *ip,section *sec, + taddr pc,int final) +{ + mnemonic *mnemo = &mnemonics[ip->code]; + operand *op = ip->op[0]; + taddr val; + int i; + + for (i=0; iop[i]) != NULL) { + if (op->value != NULL) { + if (eval_expr(op->value,&val,sec,pc)) { + if ((op->type==ABS || op->type==ABSX || op->type==ABSY) + && ((val>=0 && val<=0xff) || + ((utaddr)val>=op->dp && (utaddr)val<=op->dp+0xff)) + && mnemo->ext.zp_opcode!=0) { + /* we can use a zero page addressing mode */ + op->type += ZPAGE-ABS; + } + } + else { + symbol *base; + + if (find_base(op->value,&base,sec,pc) == BASE_OK) { + if ((op->type==ABS || op->type==ABSX || op->type==ABSY) + && base->sec!=NULL && (base->sec->flags & ABSOLUTE) + && ((val>=0 && val<=0xff) || + ((utaddr)val>=op->dp && (utaddr)val<=op->dp+0xff)) + && mnemo->ext.zp_opcode!=0) { + /* we can use a zero page addressing mode */ + op->type += ZPAGE-ABS; + } + else if (op->type==REL && LOCREF(base) && base->sec==sec) { + taddr bd = val - (pc + 2); + + if ((bd<-0x80 || bd>0x7f) && branchopt) { + /* branch dest. out of range: use a B!cc/JMP combination */ + op->type = RELJMP; + } + } + } + } + } + } + } +} + + +static size_t get_inst_size(instruction *ip) +{ + size_t sz = 1; + int i; + + for (i=0; iop[i] != NULL) { + switch (ip->op[i]->type) { + case REL: + case INDX: + case INDY: + case DPINDIR: + case IMMED: + case ZPAGE: + case ZPAGEX: + case ZPAGEY: + sz += 1; + break; + case ABS: + case ABSX: + case ABSY: + case INDIR: + case INDIRX: + sz += 2; + break; + case RELJMP: + sz += 4; + break; + } + } + } + return sz; +} + + +size_t instruction_size(instruction *ip,section *sec,taddr pc) +{ + instruction *ipcopy; + int i; + + for (i=0; iop[i]!=NULL && ip->op[i+1]!=NULL) { + if (ip->op[i]->type == ABS) { + if (ip->op[i+1]->type == DUMX) { + ip->op[i]->type = ABSX; + break; + } + else if (ip->op[i+1]->type == DUMY) { + ip->op[i]->type = ABSY; + break; + } + } + else if (ip->op[i]->type == INDIR) { + if (ip->op[i+1]->type == DUMY) { + ip->op[0]->type = INDY; + break; + } + } + } + } + + if (++i < MAX_OPERANDS) { + /* we removed a DUMX/DUMY operand at the end */ + myfree(ip->op[i]); + ip->op[i] = NULL; + } + + ipcopy = copy_instruction(ip); + optimize_instruction(ipcopy,sec,pc,0); + return get_inst_size(ipcopy); +} + + +static void rangecheck(taddr val,operand *op) +{ + switch (op->type) { + case ZPAGE: + case ZPAGEX: + case ZPAGEY: + case INDX: + case INDY: + case DPINDIR: + if ((utaddr)val>=op->dp && (utaddr)val<=op->dp+0xff) + break; + case IMMED: + if (val<-0x80 || val>0xff) + cpu_error(5); /* operand doesn't fit into 8-bits */ + break; + case REL: + if (val<-0x80 || val>0x7f) + cpu_error(6); /* branch destination out of range */ + break; + case WBIT: + if (val<0 || val>7) + cpu_error(7); /* illegal bit number */ + break; + } +} + + +dblock *eval_instruction(instruction *ip,section *sec,taddr pc) +{ + dblock *db = new_dblock(); + unsigned char *d,oc; + int optype,i; + taddr val; + + optimize_instruction(ip,sec,pc,1); /* really execute optimizations now */ + + db->size = get_inst_size(ip); + d = db->data = mymalloc(db->size); + + /* write opcode */ + oc = mnemonics[ip->code].ext.opcode; + for (i=0; iop[i]!=NULL ? ip->op[i]->type : IMPLIED; + switch (optype) { + case ZPAGE: + case ZPAGEX: + case ZPAGEY: + oc = mnemonics[ip->code].ext.zp_opcode; + break; + case RELJMP: + oc ^= 0x20; /* B!cc branch */ + break; + } + } + *d++ = oc; + + for (i=0; iop[i] != NULL){ + operand *op = ip->op[i]; + int offs = d - db->data; + symbol *base; + + optype = (int)op->type; + if (op->value != NULL) { + if (!eval_expr(op->value,&val,sec,pc)) { + modifier = 0; + if (optype!=WBIT && find_base(op->value,&base,sec,pc) == BASE_OK) { + if (optype==REL && !is_pc_reloc(base,sec)) { + /* relative branch requires no relocation */ + val = val - (pc + offs + 1); + } + else { + int type = REL_ABS; + int size; + rlist *rl; + + switch (optype) { + case ABS: + case ABSX: + case ABSY: + case INDIR: + case INDIRX: + size = 16; + break; + case INDX: + case INDY: + case DPINDIR: + case ZPAGE: + case ZPAGEX: + case ZPAGEY: + case IMMED: + size = 8; + break; + case RELJMP: + size = 16; + offs = 3; + break; + case REL: + type = REL_PC; + size = 8; + break; + default: + ierror(0); + break; + } + + rl = add_extnreloc(&db->relocs,base,val,type,0,size,offs); + switch (modifier) { + case LOBYTE: + if (rl) + ((nreloc *)rl->reloc)->mask = 0xff; + val = val & 0xff; + break; + case HIBYTE: + if (rl) + ((nreloc *)rl->reloc)->mask = 0xff00; + val = (val >> 8) & 0xff; + break; + } + } + } + else + general_error(38); /* illegal relocation */ + } + + rangecheck(val,op); + + /* write operand data */ + switch (optype) { + case ABSX: + case ABSY: + if (!*(db->data)) /* STX/STY allow only ZeroPage addressing mode */ + cpu_error(5); /* operand doesn't fit into 8-bits */ + case ABS: + case INDIR: + case INDIRX: + *d++ = val & 0xff; + *d++ = (val>>8) & 0xff; + break; + case DPINDIR: + case INDX: + case INDY: + case ZPAGE: + case ZPAGEX: + case ZPAGEY: + if ((utaddr)val>=op->dp && (utaddr)val<=op->dp+0xff) + val -= op->dp; + case IMMED: + case REL: + *d++ = val & 0xff; + break; + case RELJMP: + if (d - db->data > 1) + ierror(0); + *d++ = 3; /* B!cc *+3 */ + *d++ = 0x4c; /* JMP */ + *d++ = val & 0xff; + *d++ = (val>>8) & 0xff; + break; + case WBIT: + *(db->data) |= (val&7) << 4; /* set bit number in opcode */ + break; + } + } + } + } + + return db; +} + + +dblock *eval_data(operand *op,size_t bitsize,section *sec,taddr pc) +{ + dblock *db = new_dblock(); + taddr val; + + if (bitsize!=8 && bitsize!=16) + cpu_error(3,bitsize); /* data size not supported */ + + db->size = bitsize >> 3; + db->data = mymalloc(db->size); + if (!eval_expr(op->value,&val,sec,pc)) { + symbol *base; + int btype; + rlist *rl; + + modifier = 0; + btype = find_base(op->value,&base,sec,pc); + if (btype==BASE_OK || (btype==BASE_PCREL && modifier==0)) { + rl = add_extnreloc(&db->relocs,base,val, + btype==BASE_PCREL?REL_PC:REL_ABS,0,bitsize,0); + switch (modifier) { + case LOBYTE: + if (rl) + ((nreloc *)rl->reloc)->mask = 0xff; + val = val & 0xff; + break; + case HIBYTE: + if (rl) + ((nreloc *)rl->reloc)->mask = 0xff00; + val = (val >> 8) & 0xff; + break; + } + } + else if (btype != BASE_NONE) + general_error(38); /* illegal relocation */ + } + if (bitsize < 16) { + if (val<-0x80 || val>0xff) + cpu_error(5); /* operand doesn't fit into 8-bits */ + } + + switch (db->size) { + case 2: + db->data[1] = (val>>8) & 0xff; + case 1: + db->data[0] = val & 0xff; + break; + default: + ierror(0); + break; + } + + return db; +} + + +operand *new_operand() +{ + operand *new = mymalloc(sizeof(*new)); + new->type = -1; + return new; +} + + +int cpu_available(int idx) +{ + return (mnemonics[idx].ext.available & cpu_type) != 0; +} + + +int init_cpu() +{ + return 1; +} + + +int cpu_args(char *p) +{ + if (!strcmp(p,"-opt-branch")) + branchopt = 1; + else if (!strcmp(p,"-illegal")) + cpu_type |= ILL; + else if (!strcmp(p,"-dtv")) + cpu_type |= DTV; + else if (!strcmp(p,"-c02")) + cpu_type = M6502 | M65C02; + else if (!strcmp(p,"-wdc02")) + cpu_type = M6502 | M65C02 | WDC02; + else if (!strcmp(p,"-ce02")) + cpu_type = M6502 | M65C02 | WDC02 | CSGCE02; + else if (!strcmp(p,"-6280")) + cpu_type = M6502 | M65C02 | WDC02 | HU6280; + else + return 0; + + return 1; +} diff --git a/vasm/cpus/6502/cpu.h b/vasm/cpus/6502/cpu.h new file mode 100644 index 0000000..91324d0 --- /dev/null +++ b/vasm/cpus/6502/cpu.h @@ -0,0 +1,91 @@ +/* +** cpu.h 650x/65C02/6510/6280 cpu-description header-file +** (c) in 2002,2008,2009,2014,2018 by Frank Wille +*/ + +#define BIGENDIAN 0 +#define LITTLEENDIAN 1 +#define VASM_CPU_650X 1 + +/* maximum number of operands for one mnemonic */ +#define MAX_OPERANDS 3 + +/* maximum number of mnemonic-qualifiers per mnemonic */ +#define MAX_QUALIFIERS 0 + +/* data type to represent a target-address */ +typedef int16_t taddr; +typedef uint16_t utaddr; + +/* minimum instruction alignment */ +#define INST_ALIGN 1 + +/* default alignment for n-bit data */ +#define DATA_ALIGN(n) 1 + +/* operand class for n-bit data definitions */ +#define DATA_OPERAND(n) DATAOP + +/* returns true when instruction is valid for selected cpu */ +#define MNEMONIC_VALID(i) cpu_available(i) + +/* we define two additional unary operations, '<' and '>' */ +int ext_unary_eval(int,taddr,taddr *,int); +int ext_find_base(symbol **,expr *,section *,taddr); +#define LOBYTE (LAST_EXP_TYPE+1) +#define HIBYTE (LAST_EXP_TYPE+2) +#define EXT_UNARY_NAME(s) (*s=='<'||*s=='>') +#define EXT_UNARY_TYPE(s) (*s=='<'?LOBYTE:HIBYTE) +#define EXT_UNARY_EVAL(t,v,r,c) ext_unary_eval(t,v,r,c) +#define EXT_FIND_BASE(b,e,s,p) ext_find_base(b,e,s,p) + +/* type to store each operand */ +typedef struct { + int type; + expr *value; + utaddr dp; +} operand; + + +/* additional mnemonic data */ +typedef struct { + unsigned char opcode; + unsigned char zp_opcode; /* !=0 means optimization to zero page allowed */ + uint16_t available; +} mnemonic_extension; + +/* available */ +#define M6502 1 /* standard 6502 instruction set */ +#define ILL 2 /* illegal 6502 instructions */ +#define DTV 4 /* C64 DTV instruction set extension */ +#define M65C02 8 /* basic 65C02 extensions on 6502 instruction set */ +#define WDC02 16 /* WDC65C02 extensions on 65C02 instruction set */ +#define CSGCE02 32 /* CSG65CE02 extensions on WDC65C02 instruction set */ +#define HU6280 64 /* HuC6280 extensions on WDC65C02 instruction set */ + + +/* adressing modes */ +#define IMPLIED 0 +#define ABS 1 /* $1234 */ +#define ABSX 2 /* $1234,X */ +#define ABSY 3 /* $1234,Y */ +#define INDIR 4 /* ($1234) - JMP only */ +#define INDX 5 /* ($12,X) */ +#define INDY 6 /* ($12),Y */ +#define DPINDIR 7 /* ($12) */ +#define INDIRX 8 /* ($1234,X) - JMP only */ +#define ZPAGE 9 /* add ZPAGE-ABS to optimize ABS/ABSX/ABSY */ +#define ZPAGEX 10 +#define ZPAGEY 11 +#define RELJMP 12 /* B!cc/JMP construction */ +#define REL 13 /* $1234 - 8-bit signed relative branch */ +#define IMMED 14 /* #$12 */ +#define WBIT 15 /* bit-number (WDC65C02) */ +#define DATAOP 16 /* data operand */ +#define ACCU 17 /* A */ +#define DUMX 18 /* dummy X as 'second' operand */ +#define DUMY 19 /* dummy Y as 'second' operand */ + + +/* exported by cpu.c */ +int cpu_available(int); diff --git a/vasm/cpus/6502/cpu_errors.h b/vasm/cpus/6502/cpu_errors.h new file mode 100644 index 0000000..21d3683 --- /dev/null +++ b/vasm/cpus/6502/cpu_errors.h @@ -0,0 +1,8 @@ + "instruction not supported on selected architecture",ERROR, + "trailing garbage in operand",WARNING, + "missing closing parenthesis in addressing mode",ERROR, + "data size %d not supported",ERROR, + "relocation does not allow hi/lo modifier",ERROR, + "operand doesn't fit into 8-bits",ERROR, /* 05 */ + "branch destination out of range",ERROR, + "illegal bit number",ERROR, diff --git a/vasm/cpus/6502/opcodes.h b/vasm/cpus/6502/opcodes.h new file mode 100644 index 0000000..058b338 --- /dev/null +++ b/vasm/cpus/6502/opcodes.h @@ -0,0 +1,287 @@ + "adc", {IMMED , }, {0x69,0x00,M6502}, + "adc", {ABS , }, {0x6d,0x65,M6502}, + "adc", {INDX , }, {0x61,0x00,M6502}, + "adc", {DPINDIR, }, {0x72,0x00,M65C02}, + "adc", {INDIR ,DUMY }, {0x71,0x00,M6502}, + "adc", {ABS ,DUMX }, {0x7d,0x75,M6502}, + "adc", {ABS ,DUMY }, {0x79,0x00,M6502}, + "ahx", {INDIR ,DUMY }, {0x93,0x00,ILL}, + "ahx", {ABS ,DUMY }, {0x9f,0x00,ILL}, + "alr", {IMMED , }, {0x4b,0x00,ILL}, + "anc", {IMMED , }, {0x0b,0x00,ILL}, + "anc2", {IMMED , }, {0x2b,0x00,ILL}, + "and", {IMMED , }, {0x29,0x00,M6502}, + "and", {ABS , }, {0x2d,0x25,M6502}, + "and", {INDX , }, {0x21,0x00,M6502}, + "and", {DPINDIR, }, {0x32,0x00,M65C02}, + "and", {INDIR ,DUMY }, {0x31,0x00,M6502}, + "and", {ABS ,DUMX }, {0x3d,0x35,M6502}, + "and", {ABS ,DUMY }, {0x39,0x00,M6502}, + "arr", {IMMED , }, {0x6b,0x00,ILL}, + "asl", {ACCU , }, {0x0a,0x00,M6502}, + "asl", {ABS , }, {0x0e,0x06,M6502}, + "asl", {IMPLIED, }, {0x0a,0x00,M6502}, + "asl", {ABS ,DUMX }, {0x1e,0x16,M6502}, + "aso", {ABS , }, {0x0f,0x07,ILL}, + "aso", {INDX , }, {0x03,0x00,ILL}, + "aso", {INDIR ,DUMY }, {0x13,0x00,ILL}, + "aso", {ABS ,DUMX }, {0x1f,0x17,ILL}, + "aso", {ABS ,DUMY }, {0x1b,0x00,ILL}, + "axa", {INDIR ,DUMY }, {0x93,0x00,ILL}, + "axa", {ABS ,DUMY }, {0x9f,0x00,ILL}, + "axs", {IMMED , }, {0xcb,0x00,ILL}, + "axs", {ABS , }, {0x8f,0x87,ILL}, + "axs", {INDX , }, {0x83,0x00,ILL}, + "axs", {ABS ,DUMY }, {0x00,0x97,ILL}, + "bbr", {WBIT ,ZPAGE ,REL }, {0x00,0x0f,WDC02}, + "bbr0", {ZPAGE ,REL }, {0x00,0x0f,WDC02}, + "bbr1", {ZPAGE ,REL }, {0x00,0x1f,WDC02}, + "bbr2", {ZPAGE ,REL }, {0x00,0x2f,WDC02}, + "bbr3", {ZPAGE ,REL }, {0x00,0x3f,WDC02}, + "bbr4", {ZPAGE ,REL }, {0x00,0x4f,WDC02}, + "bbr5", {ZPAGE ,REL }, {0x00,0x5f,WDC02}, + "bbr6", {ZPAGE ,REL }, {0x00,0x6f,WDC02}, + "bbr7", {ZPAGE ,REL }, {0x00,0x7f,WDC02}, + "bbs", {WBIT ,ZPAGE ,REL }, {0x00,0x8f,WDC02}, + "bbs0", {ZPAGE ,REL }, {0x00,0x8f,WDC02}, + "bbs1", {ZPAGE ,REL }, {0x00,0x9f,WDC02}, + "bbs2", {ZPAGE ,REL }, {0x00,0xaf,WDC02}, + "bbs3", {ZPAGE ,REL }, {0x00,0xbf,WDC02}, + "bbs4", {ZPAGE ,REL }, {0x00,0xcf,WDC02}, + "bbs5", {ZPAGE ,REL }, {0x00,0xdf,WDC02}, + "bbs6", {ZPAGE ,REL }, {0x00,0xef,WDC02}, + "bbs7", {ZPAGE ,REL }, {0x00,0xff,WDC02}, + "bcc", {REL , }, {0x90,0x00,M6502}, + "bcs", {REL , }, {0xb0,0x00,M6502}, + "beq", {REL , }, {0xf0,0x00,M6502}, + "bit", {IMMED , }, {0x89,0x00,M65C02}, + "bit", {ABS , }, {0x2c,0x24,M6502}, + "bit", {ABS ,DUMX }, {0x3c,0x34,M65C02}, + "bmi", {REL , }, {0x30,0x00,M6502}, + "bne", {REL , }, {0xd0,0x00,M6502}, + "bpl", {REL , }, {0x10,0x00,M6502}, + "bra", {REL , }, {0x12,0x00,DTV}, + "bra", {REL , }, {0x80,0x00,M65C02}, + "brk", {IMPLIED, }, {0x00,0x00,M6502}, + "bsr", {REL , }, {0x44,0x00,HU6280}, + "bvc", {REL , }, {0x50,0x00,M6502}, + "bvs", {REL , }, {0x70,0x00,M6502}, + "cla", {IMPLIED, }, {0x62,0x00,HU6280}, + "clc", {IMPLIED, }, {0x18,0x00,M6502}, + "cld", {IMPLIED, }, {0xd8,0x00,M6502}, + "cli", {IMPLIED, }, {0x58,0x00,M6502}, + "clv", {IMPLIED, }, {0xb8,0x00,M6502}, + "clx", {IMPLIED, }, {0x82,0x00,HU6280}, + "cly", {IMPLIED, }, {0xc2,0x00,HU6280}, + "cmp", {IMMED , }, {0xc9,0x00,M6502}, + "cmp", {ABS , }, {0xcd,0xc5,M6502}, + "cmp", {INDX , }, {0xc1,0x00,M6502}, + "cmp", {DPINDIR, }, {0xd2,0x00,M65C02}, + "cmp", {INDIR ,DUMY }, {0xd1,0x00,M6502}, + "cmp", {ABS ,DUMX }, {0xdd,0xd5,M6502}, + "cmp", {ABS ,DUMY }, {0xd9,0x00,M6502}, + "cpx", {IMMED , }, {0xe0,0x00,M6502}, + "cpx", {ABS , }, {0xec,0xe4,M6502}, + "cpy", {IMMED , }, {0xc0,0x00,M6502}, + "cpy", {ABS , }, {0xcc,0xc4,M6502}, + "csh", {IMPLIED, }, {0xd4,0x00,HU6280}, + "csl", {IMPLIED, }, {0x54,0x00,HU6280}, + "dcm", {ABS , }, {0xcf,0xc7,ILL}, + "dcm", {INDX , }, {0xc3,0x00,ILL}, + "dcm", {INDIR ,DUMY }, {0xd3,0x00,ILL}, + "dcm", {ABS ,DUMX }, {0xdf,0xd7,ILL}, + "dcm", {ABS ,DUMY }, {0xdb,0x00,ILL}, + "dcp", {ABS , }, {0xcf,0xc7,ILL}, + "dcp", {INDX , }, {0xc3,0x00,ILL}, + "dcp", {INDIR ,DUMY }, {0xd3,0x00,ILL}, + "dcp", {ABS ,DUMX }, {0xdf,0xd7,ILL}, + "dcp", {ABS ,DUMY }, {0xdb,0x00,ILL}, + "dea", {IMPLIED, }, {0x3a,0x00,M65C02}, + "dec", {ACCU , }, {0x3a,0x00,M65C02}, + "dec", {IMPLIED, }, {0x3a,0x00,M65C02}, + "dec", {ABS , }, {0xce,0xc6,M6502}, + "dec", {ABS ,DUMX }, {0xde,0xd6,M6502}, + "dex", {IMPLIED, }, {0xca,0x00,M6502}, + "dey", {IMPLIED, }, {0x88,0x00,M6502}, + "eor", {IMMED , }, {0x49,0x00,M6502}, + "eor", {ABS , }, {0x4d,0x45,M6502}, + "eor", {INDX , }, {0x41,0x00,M6502}, + "eor", {DPINDIR, }, {0x52,0x00,M65C02}, + "eor", {INDIR ,DUMY }, {0x51,0x00,M6502}, + "eor", {ABS ,DUMX }, {0x5d,0x55,M6502}, + "eor", {ABS ,DUMY }, {0x59,0x00,M6502}, + "ina", {IMPLIED, }, {0x1a,0x00,M65C02}, + "inc", {ACCU , }, {0x1a,0x00,M65C02}, + "inc", {IMPLIED, }, {0x1a,0x00,M65C02}, + "inc", {ABS , }, {0xee,0xe6,M6502}, + "inc", {ABS ,DUMX }, {0xfe,0xf6,M6502}, + "ins", {ABS , }, {0xef,0xe7,ILL}, + "ins", {INDX , }, {0xe3,0x00,ILL}, + "ins", {INDIR ,DUMY }, {0xf3,0x00,ILL}, + "ins", {ABS ,DUMX }, {0xff,0xf7,ILL}, + "ins", {ABS ,DUMY }, {0xfb,0x00,ILL}, + "inx", {IMPLIED, }, {0xe8,0x00,M6502}, + "iny", {IMPLIED, }, {0xc8,0x00,M6502}, + "isc", {ABS , }, {0xef,0xe7,ILL}, + "isc", {INDX , }, {0xe3,0x00,ILL}, + "isc", {INDIR ,DUMY }, {0xf3,0x00,ILL}, + "isc", {ABS ,DUMX }, {0xff,0xf7,ILL}, + "isc", {ABS ,DUMY }, {0xfb,0x00,ILL}, + "jmp", {ABS , }, {0x4c,0x00,M6502}, + "jmp", {INDIRX , }, {0x7c,0x00,M65C02}, + "jmp", {INDIR , }, {0x6c,0x00,M6502}, + "jsr", {ABS , }, {0x20,0x00,M6502}, + "las", {ABS ,DUMY }, {0xbb,0x00,ILL}, + "lax", {IMMED , }, {0xab,0x00,ILL}, + "lax", {ABS , }, {0xaf,0xa7,ILL}, + "lax", {ABS ,DUMY }, {0xbf,0xb7,ILL}, + "lax", {INDX , }, {0xa3,0x00,ILL}, + "lax", {INDIR ,DUMY }, {0xb3,0x00,ILL}, + "lda", {IMMED , }, {0xa9,0x00,M6502}, + "lda", {ABS , }, {0xad,0xa5,M6502}, + "lda", {INDX , }, {0xa1,0x00,M6502}, + "lda", {DPINDIR, }, {0xb2,0x00,M65C02}, + "lda", {INDIR ,DUMY }, {0xb1,0x00,M6502}, + "lda", {ABS ,DUMX }, {0xbd,0xb5,M6502}, + "lda", {ABS ,DUMY }, {0xb9,0x00,M6502}, + "ldx", {IMMED , }, {0xa2,0x00,M6502}, + "ldx", {ABS , }, {0xae,0xa6,M6502}, + "ldx", {ABS ,DUMY }, {0xbe,0xb6,M6502}, + "ldy", {IMMED , }, {0xa0,0x00,M6502}, + "ldy", {ABS , }, {0xac,0xa4,M6502}, + "ldy", {ABS ,DUMX }, {0xbc,0xb4,M6502}, + "lse", {ABS , }, {0x4f,0x47,ILL}, + "lse", {INDX , }, {0x43,0x00,ILL}, + "lse", {INDIR ,DUMY }, {0x53,0x00,ILL}, + "lse", {ABS ,DUMX }, {0x5f,0x57,ILL}, + "lse", {ABS ,DUMY }, {0x5b,0x00,ILL}, + "lsr", {ACCU , }, {0x4a,0x00,M6502}, + "lsr", {ABS , }, {0x4e,0x46,M6502}, + "lsr", {IMPLIED, }, {0x4a,0x00,M6502}, + "lsr", {ABS ,DUMX }, {0x5e,0x56,M6502}, + "nop", {IMPLIED, }, {0xea,0x00,M6502}, + "oal", {IMMED , }, {0xab,0x00,ILL}, + "ora", {IMMED , }, {0x09,0x00,M6502}, + "ora", {ABS , }, {0x0d,0x05,M6502}, + "ora", {INDX , }, {0x01,0x00,M6502}, + "ora", {DPINDIR, }, {0x12,0x00,M65C02}, + "ora", {INDIR ,DUMY }, {0x11,0x00,M6502}, + "ora", {ABS ,DUMX }, {0x1d,0x15,M6502}, + "ora", {ABS ,DUMY }, {0x19,0x00,M6502}, + "pha", {IMPLIED, }, {0x48,0x00,M6502}, + "php", {IMPLIED, }, {0x08,0x00,M6502}, + "phx", {IMPLIED, }, {0xda,0x00,M65C02}, + "phy", {IMPLIED, }, {0x5a,0x00,M65C02}, + "pla", {IMPLIED, }, {0x68,0x00,M6502}, + "plp", {IMPLIED, }, {0x28,0x00,M6502}, + "plx", {IMPLIED, }, {0xfa,0x00,M65C02}, + "ply", {IMPLIED, }, {0x7a,0x00,M65C02}, + "rla", {ABS , }, {0x2f,0x27,ILL}, + "rla", {INDX , }, {0x23,0x00,ILL}, + "rla", {INDIR ,DUMY }, {0x33,0x00,ILL}, + "rla", {ABS ,DUMX }, {0x3f,0x37,ILL}, + "rla", {ABS ,DUMY }, {0x3b,0x00,ILL}, + "rmb", {WBIT ,ZPAGE }, {0x00,0x07,WDC02}, + "rmb0", {ZPAGE }, {0x00,0x07,WDC02}, + "rmb1", {ZPAGE }, {0x00,0x17,WDC02}, + "rmb2", {ZPAGE }, {0x00,0x27,WDC02}, + "rmb3", {ZPAGE }, {0x00,0x37,WDC02}, + "rmb4", {ZPAGE }, {0x00,0x47,WDC02}, + "rmb5", {ZPAGE }, {0x00,0x57,WDC02}, + "rmb6", {ZPAGE }, {0x00,0x67,WDC02}, + "rmb7", {ZPAGE }, {0x00,0x77,WDC02}, + "rol", {ACCU , }, {0x2a,0x00,M6502}, + "rol", {ABS , }, {0x2e,0x26,M6502}, + "rol", {IMPLIED, }, {0x2a,0x00,M6502}, + "rol", {ABS ,DUMX }, {0x3e,0x36,M6502}, + "ror", {ACCU , }, {0x6a,0x00,M6502}, + "ror", {ABS , }, {0x6e,0x66,M6502}, + "ror", {IMPLIED, }, {0x6a,0x00,M6502}, + "ror", {ABS ,DUMX }, {0x7e,0x76,M6502}, + "rra", {ABS , }, {0x6f,0x67,ILL}, + "rra", {INDX , }, {0x63,0x00,ILL}, + "rra", {INDIR ,DUMY }, {0x73,0x00,ILL}, + "rra", {ABS ,DUMX }, {0x7f,0x77,ILL}, + "rra", {ABS ,DUMY }, {0x7b,0x00,ILL}, + "rti", {IMPLIED, }, {0x40,0x00,M6502}, + "rts", {IMPLIED, }, {0x60,0x00,M6502}, + "sac", {IMMED , }, {0x32,0x00,DTV}, + "sax", {IMMED , }, {0xcb,0x00,ILL}, + "sax", {ABS , }, {0x8f,0x87,ILL}, + "sax", {INDX , }, {0x83,0x00,ILL}, + "sax", {ABS ,DUMY }, {0x00,0x97,ILL}, + "sax", {IMPLIED, }, {0x22,0x00,HU6280}, + "say", {ABS ,DUMX }, {0x9c,0x00,ILL}, + "say", {IMPLIED, }, {0x42,0x00,HU6280}, + "sbc", {IMMED , }, {0xe9,0x00,M6502}, + "sbc", {ABS , }, {0xed,0xe5,M6502}, + "sbc", {INDX , }, {0xe1,0x00,M6502}, + "sbc", {DPINDIR, }, {0xf2,0x00,M65C02}, + "sbc", {INDIR ,DUMY }, {0xf1,0x00,M6502}, + "sbc", {ABS ,DUMX }, {0xfd,0xf5,M6502}, + "sbc", {ABS ,DUMY }, {0xf9,0x00,M6502}, + "sbc2", {IMMED , }, {0xeb,0x00,ILL}, + "sec", {IMPLIED, }, {0x38,0x00,M6502}, + "sed", {IMPLIED, }, {0xf8,0x00,M6502}, + "sei", {IMPLIED, }, {0x78,0x00,M6502}, + "set", {IMPLIED, }, {0xf4,0x00,HU6280}, + "shx", {ABS ,DUMY }, {0x9e,0x00,ILL}, + "shy", {ABS ,DUMX }, {0x9c,0x00,ILL}, + "sir", {IMMED , }, {0x42,0x00,DTV}, + "slo", {ABS , }, {0x0f,0x07,ILL}, + "slo", {INDX , }, {0x03,0x00,ILL}, + "slo", {INDIR ,DUMY }, {0x13,0x00,ILL}, + "slo", {ABS ,DUMX }, {0x1f,0x17,ILL}, + "slo", {ABS ,DUMY }, {0x1b,0x00,ILL}, + "smb", {WBIT ,ZPAGE }, {0x00,0x87,WDC02}, + "smb0", {ZPAGE }, {0x00,0x87,WDC02}, + "smb1", {ZPAGE }, {0x00,0x97,WDC02}, + "smb2", {ZPAGE }, {0x00,0xa7,WDC02}, + "smb3", {ZPAGE }, {0x00,0xb7,WDC02}, + "smb4", {ZPAGE }, {0x00,0xc7,WDC02}, + "smb5", {ZPAGE }, {0x00,0xd7,WDC02}, + "smb6", {ZPAGE }, {0x00,0xe7,WDC02}, + "smb7", {ZPAGE }, {0x00,0xf7,WDC02}, + "sre", {ABS , }, {0x4f,0x47,ILL}, + "sre", {INDX , }, {0x43,0x00,ILL}, + "sre", {INDIR ,DUMY }, {0x53,0x00,ILL}, + "sre", {ABS ,DUMX }, {0x5f,0x57,ILL}, + "sre", {ABS ,DUMY }, {0x5b,0x00,ILL}, + "st0", {IMMED , }, {0x03,0x00,HU6280}, + "st1", {IMMED , }, {0x13,0x00,HU6280}, + "st2", {IMMED , }, {0x23,0x00,HU6280}, + "sta", {ABS , }, {0x8d,0x85,M6502}, + "sta", {INDX , }, {0x81,0x00,M6502}, + "sta", {DPINDIR, }, {0x92,0x00,M65C02}, + "sta", {INDIR ,DUMY }, {0x91,0x00,M6502}, + "sta", {ABS ,DUMX }, {0x9d,0x95,M6502}, + "sta", {ABS ,DUMY }, {0x99,0x00,M6502}, + "stp", {IMPLIED, }, {0xdb,0x00,WDC02}, + "stx", {ABS , }, {0x8e,0x86,M6502}, + "stx", {ABS ,DUMY }, {0x00,0x96,M6502}, + "sty", {ABS , }, {0x8c,0x84,M6502}, + "sty", {ABS ,DUMX }, {0x00,0x94,M6502}, + "stz", {ABS , }, {0x9c,0x64,M65C02}, + "stz", {ABS ,DUMX }, {0x9e,0x74,M65C02}, + "sxy", {IMPLIED, }, {0x02,0x00,HU6280}, + "tai", {ABS ,ABS ,ABS }, {0xf3,0x00,HU6280}, + "tam", {IMMED , }, {0x53,0x00,HU6280}, + "tas", {ABS ,DUMY }, {0x9b,0x00,ILL}, + "tax", {IMPLIED, }, {0xaa,0x00,M6502}, + "tay", {IMPLIED, }, {0xa8,0x00,M6502}, + "tdd", {ABS ,ABS ,ABS }, {0xc3,0x00,HU6280}, + "tia", {ABS ,ABS ,ABS }, {0xe3,0x00,HU6280}, + "tii", {ABS ,ABS ,ABS }, {0x73,0x00,HU6280}, + "tin", {ABS ,ABS ,ABS }, {0xd3,0x00,HU6280}, + "tma", {IMMED , }, {0x43,0x00,HU6280}, + "trb", {ABS , }, {0x1c,0x14,M65C02}, + "tsb", {ABS , }, {0x0c,0x04,M65C02}, + "tst", {IMMED ,ABS }, {0x93,0x83,HU6280}, + "tst", {IMMED ,ABS ,DUMX }, {0xb3,0xa3,HU6280}, + "tsx", {IMPLIED, }, {0xba,0x00,M6502}, + "txa", {IMPLIED, }, {0x8a,0x00,M6502}, + "txs", {IMPLIED, }, {0x9a,0x00,M6502}, + "tya", {IMPLIED, }, {0x98,0x00,M6502}, + "wai", {IMPLIED, }, {0xcb,0x00,WDC02}, + "xaa", {IMMED , }, {0x8b,0x00,ILL}, + "xas", {ABS ,DUMY }, {0x9e,0x00,ILL}, diff --git a/vasm/cpus/6800/cpu.c b/vasm/cpus/6800/cpu.c new file mode 100644 index 0000000..95a8798 --- /dev/null +++ b/vasm/cpus/6800/cpu.c @@ -0,0 +1,392 @@ +/* + * cpu.c 6800 cpu description file + * (c) in 2013-2016 by Esben Norby and Frank Wille + */ + +#include "vasm.h" + +mnemonic mnemonics[] = { +#include "opcodes.h" +}; + +int mnemonic_cnt = sizeof(mnemonics) / sizeof(mnemonics[0]); + +int bitsperbyte = 8; +int bytespertaddr = 2; +char * cpu_copyright = "vasm 6800/6801/68hc11 cpu backend 0.3 (c) 2013-2016 Esben Norby"; +char * cpuname = "6800"; + +static uint8_t cpu_type = M6800; +static int modifier; /* set by find_base() */ + + +int +init_cpu() +{ + return 1; +} + + +int +cpu_args(char *p) +{ + if (!strncmp(p, "-m68", 4)) { + p += 4; + if (p[0] == '0' && p[3] == '\0') { + switch(p[1]) { + case '0': + case '2': + case '8': + /* 6802 and 6808 are a 6800 with embedded ROM/RAM */ + cpu_type = M6800; + break; + case '1': + case '3': + /* 6803 is a 6801 with embedded ROM/RAM */ + cpu_type = M6801; + break; + default: + /* 6804 and 6805 are not opcode compatible + * 6809 is somewhat compatible, but not completely + * 6806 and 6807 do not exist, to my knowledge + */ + return 0; + } + } else if (!stricmp(p, "hc11")) + cpu_type = M68HC11; + else + return 0; + return 1; + } + return 0; +} + + +char * +parse_cpu_special(char *start) +{ + return start; +} + + +operand * +new_operand() +{ + operand *new = mymalloc(sizeof(*new)); + new->type = -1; + return new; +} + + +int +parse_operand(char *p, int len, operand *op, int required) +{ + char *start = p; + + op->value = NULL; + + switch (required) { + case IMM: + case IMM16: + if (*p++ != '#') + return PO_NOMATCH; + p = skip(p); + /* fall through */ + case REL: + case DATAOP: + op->value = parse_expr(&p); + break; + + case ADDR: + if (*p == '<') { + required = DIR; + p++; + } + else if (*p == '>') { + required = EXT; + p++; + } + op->value = parse_expr(&p); + break; + + case DIR: + if (*p == '>') + return PO_NOMATCH; + else if (*p == '<') + p++; + op->value = parse_expr(&p); + break; + + case EXT: + if (*p == '<') + return PO_NOMATCH; + else if (*p == '>') + p++; + op->value = parse_expr(&p); + break; + + case REGX: + if (toupper((unsigned char)*p++) != 'X') + return PO_NOMATCH; + break; + case REGY: + if (toupper((unsigned char)*p++) != 'Y') + return PO_NOMATCH; + break; + + default: + return PO_NOMATCH; + } + + op->type = required; + return PO_MATCH; +} + + +static size_t +eval_oper(operand *op, section *sec, taddr pc, taddr offs, dblock *db) +{ + size_t size = 0; + symbol *base = NULL; + int btype; + taddr val; + + if (op->value != NULL && !eval_expr(op->value, &val, sec, pc)) { + modifier = 0; + btype = find_base(op->value, &base, sec, pc); + } + + switch (op->type) { + case ADDR: + if (base != NULL || val < 0 || val > 0xff) { + op->type = EXT; + size = 2; + } + else { + op->type = DIR; + size = 1; + } + break; + case DIR: + size = 1; + if (db != NULL && (val < 0 || val > 0xff)) + cpu_error(1); /* operand doesn't fit into 8-bits */ + break; + case IMM: + size = 1; + if (db != NULL && !modifier && (val < -0x80 || val > 0xff)) + cpu_error(1); /* operand doesn't fit into 8-bits */ + break; + case EXT: + case IMM16: + size = 2; + break; + case REL: + size = 1; + break; + } + + if (size > 0 && db != NULL) { + /* create relocation entry and code for this operand */ + if (base != NULL && btype == BASE_OK && op->type == REL) { + /* relative branches */ + if (!is_pc_reloc(base, sec)) { + val = val - (pc + offs + 1); + if (val < -0x80 || val > 0x7f) + cpu_error(2); /* branch out of range */ + } + else + add_extnreloc(&db->relocs, base, val, REL_PC, + 0, 8, offs); + } + else if (base != NULL && btype != BASE_ILLEGAL) { + rlist *rl; + + rl = add_extnreloc(&db->relocs, base, val, + btype==BASE_PCREL? REL_PC : REL_ABS, + 0, size << 3, offs); + switch (modifier) { + case LOBYTE: + if (rl) ((nreloc *)rl->reloc)->mask = 0xff; + val &= 0xff; + break; + case HIBYTE: + if (rl) ((nreloc *)rl->reloc)->mask = 0xff00; + val = (val >> 8) & 0xff; + break; + } + } + else if (base != NULL) + general_error(38); /* illegal relocation */ + + if (size == 1) { + op->code[0] = val & 0xff; + } + else if (size == 2) { + op->code[0] = (val >> 8) & 0xff; + op->code[1] = val & 0xff; + } + else + ierror(0); + } + return (size); +} + + +size_t +instruction_size(instruction *ip, section *sec, taddr pc) +{ + operand op; + int i; + size_t size; + + size = (mnemonics[ip->code].ext.prebyte != 0) ? 2 : 1; + + for (i = 0; i < MAX_OPERANDS && ip->op[i] != NULL; i++) { + op = *(ip->op[i]); + size += eval_oper(&op, sec, pc, size, NULL); + } + + return (size); +} + + +dblock * +eval_instruction(instruction *ip, section *sec, taddr pc) +{ + dblock *db = new_dblock(); + uint8_t opcode; + uint8_t *d; + int i; + size_t size; + + /* evaluate operands and determine instruction size */ + opcode = mnemonics[ip->code].ext.opcode; + size = (mnemonics[ip->code].ext.prebyte != 0) ? 2 : 1; + for (i = 0; i < MAX_OPERANDS && ip->op[i] != NULL; i++) { + size += eval_oper(ip->op[i], sec, pc, size, db); + if (ip->op[i]->type == DIR) + opcode = mnemonics[ip->code].ext.dir_opcode; + } + + /* allocate and fill data block */ + db->size = size; + d = db->data = mymalloc(size); + + if (mnemonics[ip->code].ext.prebyte != 0) + *d++ = mnemonics[ip->code].ext.prebyte; + *d++ = opcode; + + /* write operands */ + for (i = 0; i < MAX_OPERANDS && ip->op[i] != NULL; i++) { + switch (ip->op[i]->type) { + case IMM: + case DIR: + case REL: + *d++ = ip->op[i]->code[0]; + break; + case IMM16: + case EXT: + *d++ = ip->op[i]->code[0]; + *d++ = ip->op[i]->code[1]; + break; + } + } + + return (db); +} + +dblock * +eval_data(operand *op, size_t bitsize, section *sec, taddr pc) +{ + dblock *db = new_dblock(); + uint8_t *d; + taddr val; + + if (bitsize != 8 && bitsize != 16) + cpu_error(0,bitsize); /* data size not supported */ + + db->size = bitsize >> 3; + d = db->data = mymalloc(db->size); + + if (!eval_expr(op->value, &val, sec, pc)) { + symbol *base; + int btype; + rlist *rl; + + modifier = 0; + btype = find_base(op->value, &base, sec, pc); + if (btype==BASE_OK || (btype==BASE_PCREL && modifier==0)) { + rl = add_extnreloc(&db->relocs, base, val, + btype==BASE_PCREL ? REL_PC : REL_ABS, + 0, bitsize, 0); + switch (modifier) { + case LOBYTE: + if (rl) ((nreloc *)rl->reloc)->mask = 0xff; + val &= 0xff; + break; + case HIBYTE: + if (rl) ((nreloc *)rl->reloc)->mask = 0xff00; + val = (val >> 8) & 0xff; + break; + } + } + else if (btype != BASE_NONE) + general_error(38); /* illegal relocation */ + } + + if (bitsize == 8) { + if (val < -0x80 || val > 0xff) + cpu_error(1); /* operand doesn't fit into 8-bits */ + } + else /* 16 bits */ + *d++ = (val >> 8) & 0xff; + *d = val & 0xff; + + return (db); +} + + +int +ext_unary_eval(int type, taddr val, taddr *result, int cnst) +{ + switch (type) { + case LOBYTE: + *result = cnst ? (val & 0xff) : val; + return 1; + case HIBYTE: + *result = cnst ? ((val >> 8) & 0xff) : val; + return 1; + default: + break; + } + + return 0; /* unknown type */ +} + + +int +ext_find_base(symbol **base, expr *p, section *sec, taddr pc) +{ + /* addr/256 equals >addr, addr%256 and addr&255 equal type==DIV || p->type==MOD) { + if (p->right->type==NUM && p->right->c.val==256) + p->type = p->type == DIV ? HIBYTE : LOBYTE; + } + else if (p->type==BAND && p->right->type==NUM && p->right->c.val==255) + p->type = LOBYTE; + + if (p->type==LOBYTE || p->type==HIBYTE) { + modifier = p->type; + return find_base(p->left,base,sec,pc); + } + + return BASE_ILLEGAL; +} + + +int +cpu_available(int idx) +{ + return (mnemonics[idx].ext.available & cpu_type) != 0; +} diff --git a/vasm/cpus/6800/cpu.h b/vasm/cpus/6800/cpu.h new file mode 100644 index 0000000..69c739a --- /dev/null +++ b/vasm/cpus/6800/cpu.h @@ -0,0 +1,80 @@ +/* + * cpu.h 6800 cpu description file + * (c) in 2013-2014 by Esben Norby and Frank Wille +*/ + +#define BIGENDIAN 1 +#define LITTLEENDIAN 0 +#define VASM_CPU_6800 1 + +/* maximum number of operands for one mnemonic */ +#define MAX_OPERANDS 4 + +/* maximum number of mnemonic-qualifiers per mnemonic */ +#define MAX_QUALIFIERS 0 + +/* data type to represent a target-address */ +typedef int16_t taddr; +typedef uint16_t utaddr; + +/* minimum instruction alignment */ +#define INST_ALIGN 1 + +/* default alignment for n-bit data */ +#define DATA_ALIGN(n) 1 + +/* operand class for n-bit data definitions */ +#define DATA_OPERAND(n) DATAOP + +/* returns true when instruction is valid for selected cpu */ +#define MNEMONIC_VALID(i) cpu_available(i) + +/* allow commas and blanks at the same time to separate instruction operands */ +#define OPERSEP_COMMA 1 +#define OPERSEP_BLANK 1 + +/* we define two additional unary operations, '<' and '>' */ +int ext_unary_eval(int,taddr,taddr *,int); +int ext_find_base(symbol **,expr *,section *,taddr); +#define LOBYTE (LAST_EXP_TYPE+1) +#define HIBYTE (LAST_EXP_TYPE+2) +#define EXT_UNARY_NAME(s) (*s=='<'||*s=='>') +#define EXT_UNARY_TYPE(s) (*s=='<'?LOBYTE:HIBYTE) +#define EXT_UNARY_EVAL(t,v,r,c) ext_unary_eval(t,v,r,c) +#define EXT_FIND_BASE(b,e,s,p) ext_find_base(b,e,s,p) + +/* type to store each operand */ +typedef struct { + uint16_t type; + uint8_t code[2]; + expr *value; +} operand; + + +/* additional mnemonic data */ +typedef struct { + unsigned char prebyte; + unsigned char opcode; + unsigned char dir_opcode; /* !=0 means optimization to DIR allowed */ + uint8_t available; +} mnemonic_extension; + +/* available */ +#define M6800 1 +#define M6801 2 /* 6801/6803: Adds D register and some extras */ +#define M68HC11 4 /* standard 68HC11 instruction set */ + +/* adressing modes */ +#define INH 0 +#define IMM 1 /* #$12 */ /* IMM ii */ +#define IMM16 2 /* #$1234 */ /* IMM jj kk */ +#define ADDR 3 +#define EXT 4 /* EXT hh */ +#define DIR 5 /* DIR dd */ +#define REL 6 /* REL rr */ +#define REGX 7 +#define REGY 8 +#define DATAOP 9 /* data operand */ + +/* exported by cpu.c */ +int cpu_available(int); diff --git a/vasm/cpus/6800/cpu_errors.h b/vasm/cpus/6800/cpu_errors.h new file mode 100644 index 0000000..f752cc0 --- /dev/null +++ b/vasm/cpus/6800/cpu_errors.h @@ -0,0 +1,3 @@ + "data size %d not supported",ERROR, + "operand doesn't fit into 8-bits",ERROR, + "branch destination out of range",ERROR, diff --git a/vasm/cpus/6800/opcodes.h b/vasm/cpus/6800/opcodes.h new file mode 100644 index 0000000..7ffb13a --- /dev/null +++ b/vasm/cpus/6800/opcodes.h @@ -0,0 +1,339 @@ + "aba", {INH }, {0x00, 0x1B, 0x00, M6800|M6801|M68HC11}, + "abx", {INH }, {0x00, 0x3A, 0x00, M6801|M68HC11}, + "aby", {INH }, {0x18, 0x3A, 0x00, M68HC11}, + "adca", {IMM }, {0x00, 0x89, 0x00, M6800|M6801|M68HC11}, + "adca", {ADDR }, {0x00, 0xB9, 0x99, M6800|M6801|M68HC11}, + "adca", {DIR, REGX }, {0x00, 0x00, 0xA9, M6800|M6801|M68HC11}, + "adca", {DIR, REGY }, {0x18, 0x00, 0xA9, M68HC11}, + "adcb", {IMM }, {0x00, 0xC9, 0x00, M6800|M6801|M68HC11}, + "adcb", {ADDR }, {0x00, 0xF9, 0xD9, M6800|M6801|M68HC11}, + "adcb", {DIR, REGX }, {0x00, 0x00, 0xE9, M6800|M6801|M68HC11}, + "adcb", {DIR, REGY }, {0x18, 0x00, 0xE9, M68HC11}, + "adda", {IMM }, {0x00, 0x8B, 0x00, M6800|M6801|M68HC11}, + "adda", {ADDR }, {0x00, 0xBB, 0x9B, M6800|M6801|M68HC11}, + "adda", {DIR, REGX }, {0x00, 0x00, 0xAB, M6800|M6801|M68HC11}, + "adda", {DIR, REGY }, {0x18, 0x00, 0xAB, M68HC11}, + "addb", {IMM }, {0x00, 0xCB, 0x00, M6800|M6801|M68HC11}, + "addb", {ADDR }, {0x00, 0xFB, 0xDB, M6800|M6801|M68HC11}, + "addb", {DIR, REGX }, {0x00, 0x00, 0xEB, M6800|M6801|M68HC11}, + "addb", {DIR, REGY }, {0x18, 0x00, 0xEB, M68HC11}, + "addd", {IMM16 }, {0x00, 0xC3, 0x00, M6801|M68HC11}, + "addd", {ADDR }, {0x00, 0xF3, 0xD3, M6801|M68HC11}, + "addd", {DIR, REGX }, {0x00, 0x00, 0xE3, M6801|M68HC11}, + "addd", {DIR, REGY }, {0x18, 0x00, 0xE3, M68HC11}, + "anda", {IMM }, {0x00, 0x84, 0x00, M6800|M6801|M68HC11}, + "anda", {ADDR }, {0x00, 0xB4, 0x94, M6800|M6801|M68HC11}, + "anda", {DIR, REGX }, {0x00, 0x00, 0xA4, M6800|M6801|M68HC11}, + "anda", {DIR, REGY }, {0x18, 0x00, 0xA4, M68HC11}, + "andb", {IMM }, {0x00, 0xC4, 0x00, M6800|M6801|M68HC11}, + "andb", {ADDR }, {0x00, 0xF4, 0xD4, M6800|M6801|M68HC11}, + "andb", {DIR, REGX }, {0x00, 0x00, 0xE4, M6800|M6801|M68HC11}, + "andb", {DIR, REGY }, {0x18, 0x00, 0xE4, M68HC11}, + "asl", {EXT }, {0x00, 0x78, 0x00, M6800|M6801|M68HC11}, + "asl", {DIR, REGX }, {0x00, 0x00, 0x68, M6800|M6801|M68HC11}, + "asl", {DIR, REGY }, {0x18, 0x00, 0x68, M68HC11}, + "asla", {INH }, {0x00, 0x48, 0x00, M6800|M6801|M68HC11}, + "aslb", {INH }, {0x00, 0x58, 0x00, M6800|M6801|M68HC11}, + "asld", {INH }, {0x00, 0x05, 0x00, M6800|M6801|M68HC11}, + "asr", {EXT }, {0x00, 0x77, 0x00, M6800|M6801|M68HC11}, + "asr", {DIR, REGX }, {0x00, 0x00, 0x67, M6800|M6801|M68HC11}, + "asr", {DIR, REGY }, {0x18, 0x00, 0x67, M68HC11}, + "asra", {INH, }, {0x00, 0x47, 0x00, M6800|M6801|M68HC11}, + "asrb", {INH, }, {0x00, 0x57, 0x00, M6800|M6801|M68HC11}, + + "bclr", {DIR, IMM }, {0x00, 0x00, 0x15, M68HC11}, + "bclr", {DIR, REGX, IMM }, {0x00, 0x00, 0x1D, M68HC11}, + "bclr", {DIR, REGY, IMM }, {0x18, 0x00, 0x1D, M68HC11}, + + "bcc", {REL, }, {0x00, 0x24, 0x00, M6800|M6801|M68HC11}, + "bcs", {REL, }, {0x00, 0x25, 0x00, M6800|M6801|M68HC11}, + "beq", {REL, }, {0x00, 0x27, 0x00, M6800|M6801|M68HC11}, + "bge", {REL, }, {0x00, 0x2C, 0x00, M6800|M6801|M68HC11}, + "bgt", {REL, }, {0x00, 0x2E, 0x00, M6800|M6801|M68HC11}, + "bhi", {REL, }, {0x00, 0x22, 0x00, M6800|M6801|M68HC11}, + "bhs", {REL, }, {0x00, 0x24, 0x00, M6800|M6801|M68HC11}, + "bita", {IMM }, {0x00, 0x85, 0x00, M6800|M6801|M68HC11}, + "bita", {ADDR, }, {0x00, 0xB5, 0x95, M6800|M6801|M68HC11}, + "bita", {DIR, REGX }, {0x00, 0x00, 0xA5, M6800|M6801|M68HC11}, + "bita", {DIR, REGY }, {0x18, 0x00, 0xA5, M68HC11}, + "bitb", {IMM }, {0x00, 0xC5, 0x00, M6800|M6801|M68HC11}, + "bitb", {ADDR, }, {0x00, 0xF5, 0xD5, M6800|M6801|M68HC11}, + "bitb", {DIR, REGX }, {0x00, 0x00, 0xE5, M6800|M6801|M68HC11}, + "bitb", {DIR, REGY }, {0x18, 0x00, 0xE5, M68HC11}, + + "ble", {REL, }, {0x00, 0x2F, 0x00, M6800|M6801|M68HC11}, + "blo", {REL, }, {0x00, 0x25, 0x00, M6800|M6801|M68HC11}, + "bls", {REL, }, {0x00, 0x23, 0x00, M6800|M6801|M68HC11}, + "blt", {REL, }, {0x00, 0x2D, 0x00, M6800|M6801|M68HC11}, + "bmi", {REL, }, {0x00, 0x2B, 0x00, M6800|M6801|M68HC11}, + "bne", {REL, }, {0x00, 0x26, 0x00, M6800|M6801|M68HC11}, + "bpl", {REL, }, {0x00, 0x2A, 0x00, M6800|M6801|M68HC11}, + "bra", {REL, }, {0x00, 0x20, 0x00, M6800|M6801|M68HC11}, + "jr", {REL, }, {0x00, 0x20, 0x00, M6800|M6801|M68HC11}, /* bra */ + + "brclr",{DIR, IMM, REL }, {0x00, 0x00, 0x13, M68HC11}, + "brclr",{DIR, REGX, IMM, REL}, {0x00, 0x00, 0x1F, M68HC11}, + "brclr",{DIR, REGY, IMM, REL}, {0x18, 0x00, 0x1F, M68HC11}, + + "brn", {REL, }, {0x00, 0x21, 0x00, M6801|M68HC11}, + + "brset",{DIR, IMM, REL }, {0x00, 0x00, 0x12, M68HC11}, + "brset",{DIR, REGX, IMM, REL}, {0x00, 0x00, 0x1E, M68HC11}, + "brset",{DIR, REGY, IMM, REL}, {0x18, 0x00, 0x1E, M68HC11}, + + "bset", {DIR, IMM }, {0x00, 0x00, 0x14, M68HC11}, + "bset", {DIR, REGX, IMM }, {0x00, 0x00 ,0x1C, M68HC11}, + "bset", {DIR, REGY, IMM }, {0x18, 0x00 ,0x1C, M68HC11}, + + "bsr", {REL, }, {0x00, 0x8D, 0x00, M6800|M6801|M68HC11}, + "callr",{REL, }, {0x00, 0x8D, 0x00, M6800|M6801|M68HC11}, /* bsr */ + "bvc", {REL, }, {0x00, 0x28, 0x00, M6800|M6801|M68HC11}, + "bvs", {REL, }, {0x00, 0x29, 0x00, M6800|M6801|M68HC11}, + "cba", {INH, }, {0x00, 0x11, 0x00, M6800|M6801|M68HC11}, + "clc", {INH, }, {0x00, 0x0C, 0x00, M6800|M6801|M68HC11}, + "cli", {INH, }, {0x00, 0x0E, 0x00, M6800|M6801|M68HC11}, + "ei", {INH, }, {0x00, 0x0E, 0x00, M6800|M6801|M68HC11}, /* cli */ + "clr", {EXT, }, {0x00, 0x7F, 0x00, M6800|M6801|M68HC11}, + "clr", {DIR, REGX }, {0x00, 0x00, 0x6F, M6800|M6801|M68HC11}, + "clr", {DIR, REGY }, {0x18, 0x00, 0x6F, M68HC11}, + "clra", {INH, }, {0x00, 0x4F, 0x00, M6800|M6801|M68HC11}, + "clrb", {INH, }, {0x00, 0x5F, 0x00, M6800|M6801|M68HC11}, + "clv", {INH, }, {0x00, 0x0A, 0x00, M6800|M6801|M68HC11}, + "cmpa", {IMM }, {0x00, 0x81, 0x00, M6800|M6801|M68HC11}, + "cmpa", {ADDR, }, {0x00, 0xB1, 0x91, M6800|M6801|M68HC11}, + "cmpa", {DIR, REGX }, {0x00, 0x00, 0xA1, M6800|M6801|M68HC11}, + "cmpa", {DIR, REGY }, {0x18, 0x00, 0xA1, M68HC11}, + "cmpb", {IMM }, {0x00, 0xC1, 0x00, M6800|M6801|M68HC11}, + "cmpb", {ADDR, }, {0x00, 0xF1, 0xD1, M6800|M6801|M68HC11}, + "cmpb", {DIR, REGX }, {0x00, 0x00, 0xE1, M6800|M6801|M68HC11}, + "cmpb", {DIR, REGY }, {0x18, 0x00, 0xE1, M68HC11}, + "com", {EXT, }, {0x00, 0x73, 0x00, M6800|M6801|M68HC11}, + "com", {DIR, REGX }, {0x00, 0x00, 0x63, M6800|M6801|M68HC11}, + "com", {DIR, REGY }, {0x18, 0x00, 0x63, M68HC11}, + "coma", {INH, }, {0x00, 0x43, 0x00, M6800|M6801|M68HC11}, + "comb", {INH, }, {0x00, 0x53, 0x00, M6800|M6801|M68HC11}, + "cpd", {IMM16 }, {0x1A, 0x83, 0x00, M68HC11}, + "cpd", {ADDR, }, {0x1A, 0xB3, 0x93, M68HC11}, + "cpd", {DIR, REGX }, {0x1A, 0x00, 0xA3, M68HC11}, + "cpd", {DIR, REGY }, {0xCD, 0x00, 0xA3, M68HC11}, + "cmpd", {IMM16 }, {0x1A, 0x83, 0x00, M68HC11}, /* cpd */ + "cmpd", {ADDR, }, {0x1A, 0xB3, 0x93, M68HC11}, /* cpd */ + "cmpd", {DIR, REGX }, {0x1A, 0x00, 0xA3, M68HC11}, /* cpd */ + "cmpd", {DIR, REGY }, {0xCD, 0x00, 0xA3, M68HC11}, /* cpd */ + "cpx", {IMM16 }, {0x00, 0x8C, 0x00, M6800|M6801|M68HC11}, + "cpx", {ADDR, }, {0x00, 0xBC, 0x9C, M6800|M6801|M68HC11}, + "cpx", {DIR, REGX }, {0x00, 0x00, 0xAC, M6800|M6801|M68HC11}, + "cpx", {DIR, REGY }, {0xCD, 0x00, 0xAC, M68HC11}, + "cmpx", {IMM16 }, {0x00, 0x8C, 0x00, M6800|M6801|M68HC11}, /* cpx */ + "cmpx", {ADDR, }, {0x00, 0xBC, 0x9C, M6800|M6801|M68HC11}, /* cpx */ + "cmpx", {DIR, REGX }, {0x00, 0x00, 0xAC, M6800|M6801|M68HC11}, /* cpx */ + "cmpx", {DIR, REGY }, {0xCD, 0x00, 0xAC, M68HC11}, /* cpx */ + "cpy", {IMM16 }, {0x18, 0x8C, 0x00, M68HC11}, + "cpy", {ADDR, }, {0x18, 0xBC, 0x9C, M68HC11}, + "cpy", {DIR, REGX }, {0x1A, 0x00, 0xAC, M68HC11}, + "cpy", {DIR, REGY }, {0x18, 0x00, 0xAC, M68HC11}, + "cmpy", {IMM16 }, {0x18, 0x8C, 0x00, M68HC11}, /* cpy */ + "cmpy", {ADDR, }, {0x18, 0xBC, 0x9C, M68HC11}, /* cpy */ + "cmpy", {DIR, REGX }, {0x1A, 0x00, 0xAC, M68HC11}, /* cpy */ + "cmpy", {DIR, REGY }, {0x18, 0x00, 0xAC, M68HC11}, /* cpy */ + "daa", {INH, }, {0x00, 0x19, 0x00, M6800|M6801|M68HC11}, + "dec", {EXT, }, {0x00, 0x7A, 0x00, M6800|M6801|M68HC11}, + "dec", {DIR, REGX }, {0x00, 0x00, 0x6A, M6800|M6801|M68HC11}, + "dec", {DIR, REGY }, {0x18, 0x00, 0x6A, M68HC11}, + "deca", {INH, }, {0x00, 0x4A, 0x00, M6800|M6801|M68HC11}, + "decb", {INH, }, {0x00, 0x5A, 0x00, M6800|M6801|M68HC11}, + "des", {INH, }, {0x00, 0x34, 0x00, M6800|M6801|M68HC11}, + "decs", {INH, }, {0x00, 0x34, 0x00, M6800|M6801|M68HC11}, /* des */ + "dex", {INH, }, {0x00, 0x09, 0x00, M6800|M6801|M68HC11}, + "decx", {INH, }, {0x00, 0x09, 0x00, M6800|M6801|M68HC11}, /* dex */ + "dey", {INH, }, {0x18, 0x09, 0x00, M68HC11}, + "decy", {INH, }, {0x18, 0x09, 0x00, M68HC11}, /* dey */ + "eora", {IMM, }, {0x00, 0x88, 0x00, M6800|M6801|M68HC11}, + "eora", {ADDR, }, {0x00, 0xB8, 0x98, M6800|M6801|M68HC11}, + "eora", {DIR, REGX }, {0x00, 0x00, 0xA8, M6800|M6801|M68HC11}, + "eora", {DIR, REGY }, {0x18, 0x00, 0xA8, M68HC11}, + "xora", {IMM, }, {0x00, 0x88, 0x00, M6800|M6801|M68HC11}, /* eora */ + "xora", {ADDR, }, {0x00, 0xB8, 0x98, M6800|M6801|M68HC11}, /* eora */ + "xora", {DIR, REGX }, {0x00, 0x00, 0xA8, M6800|M6801|M68HC11}, /* eora */ + "xora", {DIR, REGY }, {0x18, 0x00, 0xA8, M68HC11}, /* eora */ + "eorb", {IMM, }, {0x00, 0xC8, 0x00, M6800|M6801|M68HC11}, + "eorb", {ADDR, }, {0x00, 0xF8, 0xD8, M6800|M6801|M68HC11}, + "eorb", {DIR, REGX }, {0x00, 0x00, 0xE8, M6800|M6801|M68HC11}, + "eorb", {DIR, REGY }, {0x18, 0x00, 0xE8, M68HC11}, + "xorb", {IMM, }, {0x00, 0xC8, 0x00, M6800|M6801|M68HC11}, /* eorb */ + "xorb", {ADDR, }, {0x00, 0xF8, 0xD8, M6800|M6801|M68HC11}, /* eorb */ + "xorb", {DIR, REGX }, {0x00, 0x00, 0xE8, M6800|M6801|M68HC11}, /* eorb */ + "xorb", {DIR, REGY }, {0x18, 0x00, 0xE8, M68HC11}, /* eorb */ + "fdiv", {INH, }, {0x00, 0x03, 0x00, M68HC11}, + "idiv", {INH, }, {0x00, 0x02, 0x00, M68HC11}, + "inc", {EXT, }, {0x00, 0x7C, 0x00, M6800|M6801|M68HC11}, + "inc", {DIR, REGX }, {0x00, 0x00, 0x6C, M6800|M6801|M68HC11}, + "inc", {DIR, REGY }, {0x18, 0x00, 0x6C, M68HC11}, + "inca", {INH, }, {0x00, 0x4C, 0x00, M6800|M6801|M68HC11}, + "incb", {INH, }, {0x00, 0x5C, 0x00, M6800|M6801|M68HC11}, + "ins", {INH, }, {0x00, 0x31, 0x00, M6800|M6801|M68HC11}, + "incs", {INH, }, {0x00, 0x31, 0x00, M6800|M6801|M68HC11}, /* ins */ + "inx", {INH, }, {0x00, 0x08, 0x00, M6800|M6801|M68HC11}, + "incx", {INH, }, {0x00, 0x08, 0x00, M6800|M6801|M68HC11}, /* inx */ + "iny", {INH, }, {0x18, 0x08, 0x00, M68HC11}, + "incy", {INH, }, {0x18, 0x08, 0x00, M68HC11}, /* iny */ + "jmp", {EXT, }, {0x00, 0x7E, 0x00, M6800|M6801|M68HC11}, + "jmp", {DIR, REGX }, {0x00, 0x00, 0x6E, M6800|M6801|M68HC11}, + "jmp", {DIR, REGY }, {0x18, 0x00, 0x6E, M68HC11}, + "jsr", {ADDR, }, {0x00, 0xBD, 0x9D, M6800|M6801|M68HC11}, + "jsr", {DIR, REGX }, {0x00, 0x00, 0xAD, M6800|M6801|M68HC11}, + "jsr", {DIR, REGY }, {0x18, 0x00, 0xAD, M68HC11}, + "call", {ADDR, }, {0x00, 0xBD, 0x9D, M6800|M6801|M68HC11}, /* jsr */ + "call", {DIR, REGX }, {0x00, 0x00, 0xAD, M6800|M6801|M68HC11}, /* jsr */ + "call", {DIR, REGY }, {0x18, 0x00, 0xAD, M68HC11}, /* jsr */ + "lda", {IMM, }, {0x00, 0x86, 0x00, M6800|M6801|M68HC11}, + "lda", {ADDR, }, {0x00, 0xB6, 0x96, M6800|M6801|M68HC11}, + "lda", {DIR, REGX }, {0x00, 0x00, 0xA6, M6800|M6801|M68HC11}, + "lda", {DIR, REGY }, {0x18, 0x00, 0xA6, M68HC11}, + "ldaa", {IMM, }, {0x00, 0x86, 0x00, M6800|M6801|M68HC11}, + "ldaa", {ADDR, }, {0x00, 0xB6, 0x96, M6800|M6801|M68HC11}, + "ldaa", {DIR, REGX }, {0x00, 0x00, 0xA6, M6800|M6801|M68HC11}, + "ldaa", {DIR, REGY }, {0x18, 0x00, 0xA6, M68HC11}, + "ldb", {IMM, }, {0x00, 0xC6, 0x00, M6800|M6801|M68HC11}, + "ldb", {ADDR, }, {0x00, 0xF6, 0xD6, M6800|M6801|M68HC11}, + "ldb", {DIR, REGX }, {0x00, 0x00, 0xE6, M6800|M6801|M68HC11}, + "ldb", {DIR, REGY }, {0x18, 0x00, 0xE6, M68HC11}, + "ldab", {IMM, }, {0x00, 0xC6, 0x00, M6800|M6801|M68HC11}, + "ldab", {ADDR, }, {0x00, 0xF6, 0xD6, M6800|M6801|M68HC11}, + "ldab", {DIR, REGX }, {0x00, 0x00, 0xE6, M6800|M6801|M68HC11}, + "ldab", {DIR, REGY }, {0x18, 0x00, 0xE6, M68HC11}, + "ldd", {IMM16, }, {0x00, 0xCC, 0x00, M6801|M68HC11}, + "ldd", {ADDR, }, {0x00, 0xFC, 0xDC, M6801|M68HC11}, + "ldd", {DIR, REGX }, {0x00, 0x00, 0xEC, M6801|M68HC11}, + "ldd", {DIR, REGY }, {0x18, 0x00, 0xEC, M68HC11}, + "lds", {IMM16, }, {0x00, 0x8E, 0x00, M6800|M6801|M68HC11}, + "lds", {ADDR, }, {0x00, 0xBE, 0x9E, M6800|M6801|M68HC11}, + "lds", {DIR, REGX }, {0x00, 0x00, 0xAE, M6800|M6801|M68HC11}, + "lds", {DIR, REGY }, {0x18, 0x00, 0xAE, M68HC11}, + "ldx", {IMM16, }, {0x00, 0xCE, 0x00, M6800|M6801|M68HC11}, + "ldx", {ADDR, }, {0x00, 0xFE, 0xDE, M6800|M6801|M68HC11}, + "ldx", {DIR, REGX }, {0x00, 0x00, 0xEE, M6800|M6801|M68HC11}, + "ldx", {DIR, REGY }, {0xCD, 0x00, 0xEE, M68HC11}, + "ldy", {IMM16, }, {0x18, 0xCE, 0x00, M68HC11}, + "ldy", {ADDR, }, {0x18, 0xFE, 0xDE, M68HC11}, + "ldy", {DIR, REGX }, {0x1A, 0x00, 0xEE, M68HC11}, + "ldy", {DIR, REGY }, {0x18, 0x00, 0xEE, M68HC11}, + "lsl", {EXT, }, {0x00, 0x78, 0x00, M6800|M6801|M68HC11}, + "lsl", {DIR, REGX }, {0x00, 0x00, 0x68, M6800|M6801|M68HC11}, + "lsl", {DIR, REGY }, {0x18, 0x00, 0x68, M68HC11}, + "lsla", {INH, }, {0x00, 0x48, 0x00, M6800|M6801|M68HC11}, + "lslb", {INH, }, {0x00, 0x58, 0x00, M6800|M6801|M68HC11}, + "lsld", {INH, }, {0x00, 0x05, 0x00, M6801|M68HC11}, + "lsr", {EXT, }, {0x00, 0x74, 0x00, M6800|M6801|M68HC11}, + "lsr", {DIR, REGX }, {0x00, 0x00, 0x64, M6800|M6801|M68HC11}, + "lsr", {DIR, REGY }, {0x18, 0x00, 0x64, M68HC11}, + "lsra", {INH, }, {0x00, 0x44, 0x00, M6800|M6801|M68HC11}, + "lsrb", {INH, }, {0x00, 0x54, 0x00, M6800|M6801|M68HC11}, + "lsrd", {INH, }, {0x00, 0x04, 0x00, M6801|M68HC11}, + "mul", {INH, }, {0x00, 0x3D, 0x00, M6801|M68HC11}, + "neg", {EXT, }, {0x00, 0x70, 0x00, M6800|M6801|M68HC11}, + "neg", {DIR, REGX }, {0x00, 0x00, 0x60, M6800|M6801|M68HC11}, + "neg", {DIR, REGY }, {0x18, 0x00, 0x60, M68HC11}, + "nega", {INH, }, {0x00, 0x40, 0x00, M6800|M6801|M68HC11}, + "negb", {INH, }, {0x00, 0x50, 0x00, M6800|M6801|M68HC11}, + "nop", {INH, }, {0x00, 0x01, 0x00, M6800|M6801|M68HC11}, + "oraa", {IMM, }, {0x00, 0x8A, 0x00, M6800|M6801|M68HC11}, + "oraa", {ADDR, }, {0x00, 0xBA, 0x9A, M6800|M6801|M68HC11}, + "oraa", {DIR, REGX }, {0x00, 0x00, 0xAA, M6800|M6801|M68HC11}, + "oraa", {DIR, REGY }, {0x18, 0x00, 0xAA, M68HC11}, + "orab", {IMM, }, {0x00, 0xCA, 0x00, M6800|M6801|M68HC11}, + "orab", {ADDR, }, {0x00, 0xFA, 0xDA, M6800|M6801|M68HC11}, + "orab", {DIR, REGX }, {0x00, 0x00, 0xEA, M6800|M6801|M68HC11}, + "orab", {DIR, REGY }, {0x18, 0x00, 0xEA, M68HC11}, + "psha", {INH, }, {0x00, 0x36, 0x00, M6800|M6801|M68HC11}, + "pusha",{INH, }, {0x00, 0x36, 0x00, M6800|M6801|M68HC11}, /* psha */ + "pshb", {INH, }, {0x00, 0x37, 0x00, M6800|M6801|M68HC11}, + "pushb",{INH, }, {0x00, 0x37, 0x00, M6800|M6801|M68HC11}, /* pshb */ + "pshx", {INH, }, {0x00, 0x3C, 0x00, M6801|M68HC11}, + "pushx",{INH, }, {0x00, 0x3C, 0x00, M6801|M68HC11}, /* pshx */ + "pshy", {INH, }, {0x18, 0x3C, 0x00, M68HC11}, + "pushy", {INH, }, {0x18, 0x3C, 0x00, M68HC11}, /* pshy */ + "pula", {INH, }, {0x00, 0x32, 0x00, M6800|M6801|M68HC11}, + "popa", {INH, }, {0x00, 0x32, 0x00, M6800|M6801|M68HC11}, /* pula */ + "pulb", {INH, }, {0x00, 0x33, 0x00, M6800|M6801|M68HC11}, + "popb", {INH, }, {0x00, 0x33, 0x00, M6800|M6801|M68HC11}, /* pulb */ + "pulx", {INH, }, {0x00, 0x38, 0x00, M6801|M68HC11}, + "popx", {INH, }, {0x00, 0x38, 0x00, M6801|M68HC11}, /* pulx */ + "puly", {INH, }, {0x18, 0x38, 0x00, M68HC11}, + "popy", {INH, }, {0x18, 0x38, 0x00, M68HC11}, /* puly */ + "rol", {EXT, }, {0x00, 0x79, 0x00, M6800|M6801|M68HC11}, + "rol", {DIR, REGX }, {0x00, 0x00, 0x69, M6800|M6801|M68HC11}, + "rol", {DIR, REGY }, {0x18, 0x00, 0x69, M68HC11}, + "rola", {INH, }, {0x00, 0x49, 0x00, M6800|M6801|M68HC11}, + "rolb", {INH, }, {0x00, 0x59, 0x00, M6800|M6801|M68HC11}, + "ror", {EXT, }, {0x00, 0x76, 0x00, M6800|M6801|M68HC11}, + "ror", {DIR, REGX }, {0x00, 0x00, 0x66, M6800|M6801|M68HC11}, + "ror", {DIR, REGY }, {0x18, 0x00, 0x66, M68HC11}, + "rora", {INH, }, {0x00, 0x46, 0x00, M6800|M6801|M68HC11}, + "rorb", {INH, }, {0x00, 0x56, 0x00, M6800|M6801|M68HC11}, + "rti", {INH, }, {0x00, 0x3B, 0x00, M6800|M6801|M68HC11}, + "reti", {INH, }, {0x00, 0x3B, 0x00, M6800|M6801|M68HC11}, /* rti */ + "rts", {INH, }, {0x00, 0x39, 0x00, M6800|M6801|M68HC11}, + "ret", {INH, }, {0x00, 0x39, 0x00, M6800|M6801|M68HC11}, /* rts */ + "sba", {INH, }, {0x00, 0x10, 0x00, M6800|M6801|M68HC11}, + "sbca", {IMM, }, {0x00, 0x82, 0x00, M6800|M6801|M68HC11}, + "sbca", {ADDR, }, {0x00, 0xB2, 0x92, M6800|M6801|M68HC11}, + "sbca", {DIR, REGX }, {0x00, 0x00, 0xA2, M6800|M6801|M68HC11}, + "sbca", {DIR, REGY }, {0x18, 0x00, 0xA2, M68HC11}, + "sbcb", {IMM, }, {0x00, 0xC2, 0x00, M6800|M6801|M68HC11}, + "sbcb", {ADDR, }, {0x00, 0xF2, 0xD2, M6800|M6801|M68HC11}, + "sbcb", {DIR, REGX }, {0x00, 0x00, 0xE2, M6800|M6801|M68HC11}, + "sbcb", {DIR, REGY }, {0x18, 0x00, 0xE2, M68HC11}, + "sec", {INH, }, {0x00, 0x0D, 0x00, M6800|M6801|M68HC11}, + "sei", {INH, }, {0x00, 0x0F, 0x00, M6800|M6801|M68HC11}, + "di", {INH, }, {0x00, 0x0F, 0x00, M6800|M6801|M68HC11}, /* sei */ + "sev", {INH, }, {0x00, 0x0B, 0x00, M6800|M6801|M68HC11}, + "staa", {ADDR, }, {0x00, 0xB7, 0x97, M6800|M6801|M68HC11}, + "staa", {DIR, REGX }, {0x00, 0x00, 0xA7, M6800|M6801|M68HC11}, + "staa", {DIR, REGY }, {0x18, 0x00, 0xA7, M68HC11}, + "stab", {ADDR, }, {0x00, 0xF7, 0xD7, M6800|M6801|M68HC11}, + "stab", {DIR, REGX }, {0x00, 0x00, 0xE7, M6800|M6801|M68HC11}, + "stab", {DIR, REGY }, {0x18, 0x00, 0xE7, M68HC11}, + "std", {ADDR, }, {0x00, 0xFD, 0xDD, M6801|M68HC11}, + "std", {DIR, REGX }, {0x00, 0x00, 0xED, M6801|M68HC11}, + "std", {DIR, REGY }, {0x18, 0x00, 0xED, M68HC11}, + "stop", {INH, }, {0x00, 0xCF, 0x00, M68HC11}, + "sts", {ADDR, }, {0x00, 0xBF, 0x9F, M6800|M6801|M68HC11}, + "sts", {DIR, REGX }, {0x00, 0x00, 0xAF, M6800|M6801|M68HC11}, + "sts", {DIR, REGY }, {0x18, 0x00, 0xAF, M68HC11}, + "stx", {ADDR, }, {0x00, 0xFF, 0xDF, M6800|M6801|M68HC11}, + "stx", {DIR, REGX }, {0x00, 0x00, 0xEF, M6800|M6801|M68HC11}, + "stx", {DIR, REGY }, {0xCD, 0x00, 0xEF, M68HC11}, + "sty", {ADDR, }, {0x18, 0xFF, 0xDF, M68HC11}, + "sty", {DIR, REGX }, {0x1A, 0x00, 0xEF, M68HC11}, + "sty", {DIR, REGY }, {0x18, 0x00, 0xEF, M68HC11}, + "suba", {IMM, }, {0x00, 0x80, 0x00, M6800|M6801|M68HC11}, + "suba", {ADDR, }, {0x00, 0xB0, 0x90, M6800|M6801|M68HC11}, + "suba", {DIR, REGX }, {0x00, 0x00, 0xA0, M6800|M6801|M68HC11}, + "suba", {DIR, REGY }, {0x18, 0x00, 0xA0, M68HC11}, + "subb", {IMM, }, {0x00, 0xC0, 0x00, M6800|M6801|M68HC11}, + "subb", {ADDR, }, {0x00, 0xF0, 0xD0, M6800|M6801|M68HC11}, + "subb", {DIR, REGX }, {0x00, 0x00, 0xE0, M6800|M6801|M68HC11}, + "subb", {DIR, REGY }, {0x18, 0x00, 0xE0, M68HC11}, + "subd", {IMM16, }, {0x00, 0x83, 0x00, M6801|M68HC11}, + "subd", {ADDR, }, {0x00, 0xB3, 0x93, M6801|M68HC11}, + "subd", {DIR, REGX }, {0x00, 0x00, 0xA3, M6801|M68HC11}, + "subd", {DIR, REGY }, {0x18, 0x00, 0xA3, M68HC11}, + "swi", {INH, }, {0x00, 0x3F, 0x00, M6800|M6801|M68HC11}, + "tab", {INH, }, {0x00, 0x16, 0x00, M6800|M6801|M68HC11}, + "tap", {INH, }, {0x00, 0x06, 0x00, M6800|M6801|M68HC11}, + "tba", {INH, }, {0x00, 0x17, 0x00, M6800|M6801|M68HC11}, + "test", {INH, }, {0x00, 0x00, 0x00, M68HC11}, + "tpa", {INH, }, {0x00, 0x07, 0x00, M6800|M6801|M68HC11}, + "tst", {EXT, }, {0x00, 0x7D, 0x00, M6800|M6801|M68HC11}, + "tst", {DIR, REGX }, {0x00, 0x00, 0x6D, M6800|M6801|M68HC11}, + "tst", {DIR, REGY }, {0x18, 0x00, 0x6D, M68HC11}, + "tsta", {INH, }, {0x00, 0x4D, 0x00, M6800|M6801|M68HC11}, + "tstb", {INH, }, {0x00, 0x5D, 0x00, M6800|M6801|M68HC11}, + "tsx", {INH, }, {0x00, 0x30, 0x00, M6800|M6801|M68HC11}, + "tsy", {INH, }, {0x18, 0x30, 0x00, M68HC11}, + "txs", {INH, }, {0x00, 0x35, 0x00, M6800|M6801|M68HC11}, + "tys", {INH, }, {0x18, 0x35, 0x00, M68HC11}, + "wai", {INH, }, {0x00, 0x3E, 0x00, M6800|M6801|M68HC11}, + "xgdx", {INH, }, {0x00, 0x8F, 0x00, M68HC11}, + "xgdy", {INH, }, {0x18, 0x8F, 0x00, M68HC11}, diff --git a/vasm/cpus/arm/cpu.c b/vasm/cpus/arm/cpu.c new file mode 100644 index 0000000..9f49f96 --- /dev/null +++ b/vasm/cpus/arm/cpu.c @@ -0,0 +1,1755 @@ +/* +** cpu.c ARM cpu-description file +** (c) in 2004,2006,2010,2011,2014-2018 by Frank Wille +*/ + +#include "vasm.h" + +mnemonic mnemonics[] = { +#include "opcodes.h" +}; +int mnemonic_cnt = sizeof(mnemonics)/sizeof(mnemonics[0]); + +char *cpu_copyright = "vasm ARM cpu backend 0.4e (c) 2004,2006,2010,2011,2014-2018 Frank Wille"; +char *cpuname = "ARM"; +int bitsperbyte = 8; +int bytespertaddr = 4; + +uint32_t cpu_type = AAANY; +int arm_be_mode = 0; /* Little-endian is default */ +int thumb_mode = 0; /* 1: Thumb instruction set (16 bit) is active */ + +/* options */ +static unsigned char opt_ldrpc = 0; /* LDR r,sym -> ADD / LDR */ +static unsigned char opt_adr = 0; /* ADR r,sym -> ADRL (ADD/ADD|SUB/SUB) */ + +/* constant data */ +static const char *condition_codes = "eqnecsccmiplvsvchilsgeltgtlealnvhsloul"; + +static const char *addrmode_strings[] = { + "da","ia","db","ib", + "fa","fd","ea","ed", + "bt","tb","sb","sh","t","b","h","s","l", + "p",NULL,"" +}; +enum { + AM_DA=0,AM_IA,AM_DB,AM_IB,AM_FA,AM_FD,AM_EA,AM_ED, + AM_BT,AM_TB,AM_SB,AM_SH,AM_T,AM_B,AM_H,AM_S,AM_L, + AM_P,AM_NULL,AM_NONE +}; + +#define NUM_SHIFTTYPES 6 +static const char *shift_strings[NUM_SHIFTTYPES] = { + "LSL","LSR","ASR","ROR","RRX","ASL" +}; + +static int OC_SWP,OC_NOP; +static int elfoutput = 0; /* output will be an ELF object file */ + +static section *last_section = 0; +static int last_data_type = -1; /* for mapping symbol generation */ +#define TYPE_ARM 0 +#define TYPE_THUMB 1 +#define TYPE_DATA 2 + +#define THB_PREFETCH 4 /* prefetch-correction for Thumb-branches */ +#define ARM_PREFETCH 8 /* prefetch-correction for ARM-branches */ + + + +operand *new_operand(void) +{ + return mycalloc(sizeof(operand)); +} + + +int cpu_available(int idx) +{ + return (mnemonics[idx].ext.available & cpu_type) != 0; +} + + +char *parse_cpu_special(char *start) +/* parse cpu-specific directives; return pointer to end of + cpu-specific text */ +{ + char *name=start,*s=start; + + if (ISIDSTART(*s)) { + s++; + while (ISIDCHAR(*s)) + s++; + if (s-name==6 && !strncmp(name,".thumb",6)) { + thumb_mode = 1; + if (inst_alignment > 1) + inst_alignment = 2; + return s; + } + else if (s-name==4 && !strncmp(name,".arm",4)) { + thumb_mode = 0; + if (inst_alignment > 1) + inst_alignment = 4; + return s; + } + } + return start; +} + + +char *parse_instruction(char *s,int *inst_len,char **ext,int *ext_len, + int *ext_cnt) +/* parse instruction and save extension locations */ +{ + char *inst = s; + int cnt = *ext_cnt; + + while (*s && !isspace((unsigned char)*s)) + s++; + + if (thumb_mode) { /* no qualifiers in THUMB code */ + *inst_len = s - inst; + } + + else { /* ARM mode - we might have up to 2 different qualifiers */ + int len = s - inst; + char c = tolower((unsigned char)*inst); + + if (len > 2) { + if (c=='b' && strnicmp(inst,"bic",3) && (len==3 || len==4)) { + *inst_len = len - 2; + } + else if ((c=='u' || c=='s') && + tolower((unsigned char)*(inst+1))=='m' && len>=5) { + *inst_len = 5; + } + else + *inst_len = 3; + len -= *inst_len; + + if (len > 0) { + char *p = inst + *inst_len; + + if (len >= 2) { + const char *cc = condition_codes; + + while (*cc) { + if (!strnicmp(p,cc,2)) + break; + cc += 2; + } + if (*cc) { /* matched against a condition code */ + ext[cnt] = p; + ext_len[cnt++] = 2; + p += 2; + len -= 2; + } + } + if (len >= 1) { + const char **am = addrmode_strings; + + do { + if (len==strlen(*am) && !strnicmp(*am,p,len)) + break; + am++; + } + while (*am); + if (*am!=NULL || (len==1 && tolower((unsigned char)*p)=='s')) { + ext[cnt] = p; + ext_len[cnt++] = len; + } + } + } + else if (len < 0) + ierror(0); + } + else + *inst_len = len; + + *ext_cnt = cnt; + } + + return s; +} + + +int set_default_qualifiers(char **q,int *q_len) +/* fill in pointers to default qualifiers, return number of qualifiers */ +{ + return 0; +} + + +static int parse_reg(char **pp) +/* parse register, return -1 on error */ +{ + char *p = *pp; + char *name = p; + regsym *sym; + + if (ISIDSTART(*p)) { + p++; + while (ISIDCHAR(*p)) + p++; + if (sym = find_regsym_nc(name,p-name)) { + *pp = p; + return sym->reg_num; + } + } + return -1; /* no valid register found */ +} + + +static int parse_reglist(char **pp) +/* parse register-list, return -1 on error */ +{ + int r=0,list=0,lastreg=-1; + char *p = *pp; + char *name; + regsym *sym; + + if (*p++ == '{') { + p = skip(p); + + do { + if (ISIDSTART(*p)) { + name = p++; + while (ISIDCHAR(*p)) + p++; + if (sym = find_regsym_nc(name,p-name)) { + r = sym->reg_num; + if (lastreg >= 0) { /* range-mode? */ + if (lastreg < r) { + r = lastreg; + lastreg = sym->reg_num; + } + for (; r<=lastreg; list |= 1<type = optype; + op->flags = 0; + op->value = NULL; + p = skip(p); + + if (optype == DATA64_OP) { + op->value = parse_expr_huge(&p); + } + + else if (thumb_mode) { + if (ARMOPER(optype)) { /* standard ARM instruction */ + return PO_NOMATCH; + } + + else if (THREGOPER(optype)) { + /* parse a register */ + int r; + + if (optype==TR5IN || optype==TPCPR || optype==TSPPR) { + if (*p++ != '[') + return PO_NOMATCH; + p = skip(p); + } + + if ((r = parse_reg(&p)) < 0) + return PO_NOMATCH; + op->value = number_expr((taddr)r); + + if (optype==TPCRG || optype==TPCPR) { + if (r != 15) + return PO_NOMATCH; + } + else if (optype==TSPRG || optype==TSPPR) { + if (r != 13) + return PO_NOMATCH; + } + else if (optype==THR02 || optype==THR05) { + if (r<8 || r>15) + return PO_NOMATCH; + } + else { + if (r<0 || r>7) + return PO_NOMATCH; + } + if (optype == TR8IN) { + p = skip(p); + if (*p++ != ']') + return PO_NOMATCH; + } + else if (optype == TR10W) { + if (*p++ != '!') + return PO_NOMATCH; + } + } + + else if (THREGLIST(optype)) { + taddr list = parse_reglist(&p); + + if (optype == TRLST) { + if (list & ~0xff) + return PO_NOMATCH; /* only r0-r7 allowed */ + } + else { + if ((list&0x8000) && optype==TRLPC) { + list = list&~0x8000 | 0x100; + } + else if ((list&0x4000) && optype==TRLLR) { + list = list&~0x4000 | 0x100; + } + if (list & ~0x1ff) + return PO_NOMATCH; /* only r0-r7 / pc / lr allowed */ + } + op->value = number_expr(list); + } + + else if (THIMMOPER(optype)) { + if (*p++ != '#') + return PO_NOMATCH; + p = skip(p); + op->value = parse_expr(&p); + + if (THIMMINDIR(optype)) { + p = skip(p); + if (*p++ != ']') + return PO_NOMATCH; + } + } + + else { /* just parse an expression */ + char *q = p; + + /* check that this isn't any other valid operand */ + if (*p=='#' || *p=='[' || *p=='{' || parse_reg(&q)>=0) + return PO_NOMATCH; + op->value = parse_expr(&p); + } + } + + else { /* ARM mode */ + if (THUMBOPER(optype)) { /* Thumb instruction */ + return PO_NOMATCH; + } + + else if (STDOPER(optype)) { + /* parse an expression (register, label, imm.) and assign to 'value' */ + if (IMMEDOPER(optype)) { + if (*p++ != '#') + return PO_NOMATCH; + p = skip(p); + } + else if (optype==R19PR || optype==R19PO) { + if (*p++ != '[') + return PO_NOMATCH; + p = skip(p); + } + + if (UPDOWNOPER(optype)) { + if (*p == '-') { + p = skip(p+1); + } + else { + if (*p == '+') + p = skip(p+1); + op->flags |= OFL_UP; + } + } + + if (REGOPER(optype)) { + int r = parse_reg(&p); + + if (r >= 0) + op->value = number_expr((taddr)r); + else + return PO_NOMATCH; + } + else { /* an expression */ + if (ISIDSTART(*p) || isdigit((unsigned char)*p) || + (!UPDOWNOPER(optype) && (*p=='-' || *p=='+'))) + op->value = parse_expr(&p); + else + return PO_NOMATCH; + } + + if (optype==R19PO || optype==R3UD1 || optype==IMUD1 || optype==IMCP1) { + p = skip(p); + if (*p++ != ']') + return PO_NOMATCH; + } + if (optype==R19WB || optype==R3UD1 || optype==IMUD1 || optype==IMCP1) { + if (*p == '!') { + p++; + op->flags |= OFL_WBACK; + } + } + } + + else if (SHIFTOPER(optype)) { + char *name = p; + int i; + + p = skip_identifier(p); + if (p == NULL) + return PO_NOMATCH; + for (i=0; i= NUM_SHIFTTYPES) + return PO_NOMATCH; + if (i == 4) { + /* RRX is ROR with immediate value 0 */ + op->flags |= OFL_IMMEDSHIFT; + op->value = number_expr(0); + i = 3; /* ROR */ + } + else { + /* parse immediate or register for LSL, LSR, ASR, ROR */ + p = skip(p); + if (i == 5) + i = 0; /* ASL -> LSL */ + if (*p == '#') { + p++; + op->flags |= OFL_IMMEDSHIFT; + op->value = parse_expr(&p); + } + else if (optype == SHIFT) { + int r = parse_reg(&p); + + if (r >= 0) + op->value = number_expr((taddr)r); + else + return PO_NOMATCH; + } + else + return PO_NOMATCH; /* no shift-count in register allowed */ + } + op->flags |= i & OFL_SHIFTOP; + + if (optype == SHIM1) { + /* check for pre-indexed with optional write-back */ + p = skip(p); + if (*p++ != ']') + return PO_NOMATCH; + if (*p == '!') { + p++; + op->flags |= OFL_WBACK; + } + } + } + + else if (optype == CSPSR) { + char *name = p; + + p = skip_identifier(p); + if (p == NULL) + return PO_NOMATCH; + if (!strnicmp(name,"CPSR",p-name)) + op->flags &= ~OFL_SPSR; + else if (!strnicmp(name,"SPSR",p-name)) + op->flags |= OFL_SPSR; + else + return PO_NOMATCH; + op->value = number_expr(0xf); /* all fields f,s,x,c */ + } + + else if (optype == PSR_F) { + char *name = p; + taddr fields = 0xf; + + p = skip_identifier(p); + if (p==NULL || (p-name)<4) + return PO_NOMATCH; + if (!strnicmp(name,"CPSR",4)) + op->flags &= ~OFL_SPSR; + else if (!strnicmp(name,"SPSR",4)) + op->flags |= OFL_SPSR; + else + return PO_NOMATCH; + + if ((p-name)>5 && *(name+4)=='_') { + fields = 0; + name += 5; + while (name < p) { + switch (tolower((unsigned char)*name++)) { + case 'f': fields |= 1; break; + case 's': fields |= 2; break; + case 'x': fields |= 4; break; + case 'c': fields |= 8; break; + default: return PO_NOMATCH; + } + } + } + else if ((p-name) > 4) + return PO_NOMATCH; + op->value = number_expr(fields); + } + + else if (optype == RLIST) { + taddr list = parse_reglist(&p); + + if (list >= 0) { + op->value = number_expr(list); + if (*p == '^') { + p++; + op->flags |= OFL_FORCE; /* set "load PSR / force user mode" flag */ + } + } + else + return PO_NOMATCH; + } + + else + ierror(0); + } + + return (skip(p)-start < len) ? PO_NOMATCH : PO_MATCH; +} + + +static void create_mapping_symbol(int type,section *sec,taddr pc) +/* create mapping symbol ($a, $t, $d) as required by ARM ELF ABI */ +{ + static char names[3][4] = { "$a","$t","$d" }; + static int types[3] = { TYPE_FUNCTION,TYPE_FUNCTION,TYPE_OBJECT }; + symbol *sym; + + if (typeTYPE_DATA) + ierror(0); + if (elfoutput) { + sym = mymalloc(sizeof(symbol)); + sym->type = LABSYM; + sym->flags = types[type]; + sym->name = names[type]; + sym->sec = sec; + sym->pc = pc; + sym->expr = 0; + sym->size = 0; + sym->align = 0; + add_symbol(sym); + } + last_data_type = type; +} + + +size_t eval_thumb_operands(instruction *ip,section *sec,taddr pc, + uint16_t *insn,dblock *db) +/* evaluate expressions and try to optimize THUMB instruction, + return size of instruction */ +{ + operand op; + mnemonic *mnemo = &mnemonics[ip->code]; + int opcnt = 0; + size_t isize = 2; + + if (insn) { + if (pc & 1) + cpu_error(27); /* instruction at unaligned address */ + + if (ip->op[0] == NULL) { + /* handle inst. without operands, which don't have Thumb entries */ + if (ip->code == OC_NOP) + *insn = 0x46c0; /* nop => mov r0,r0 */ + + return 2; + } + else + *insn = (uint16_t)mnemo->ext.opcode; + } + + for (opcnt=0; opcntop[opcnt]!=NULL; opcnt++) { + taddr val; + symbol *base = NULL; + int btype; + + op = *(ip->op[opcnt]); + if (!eval_expr(op.value,&val,sec,pc)) + btype = find_base(op.value,&base,sec,pc); + + /* do optimizations first */ + + if (op.type==TPCLW || THBRANCH(op.type)) { + /* PC-relative offsets (take prefetch into account: PC+4) */ + if (base!=NULL && btype==BASE_OK) { + if (!is_pc_reloc(base,sec)) { + /* no relocation required, can be resolved immediately */ + if (op.type == TPCLW) { + /* bit 1 of PC is forced to 0 */ + val -= (pc&~2) + 4; + } + else + val -= pc + 4; + + if (op.type == TBR08) { + if (val<-0x100 || val>0xfe) { + /* optimize to: B .+4 ; B label */ + if (insn) { + *insn++ ^= 0x100; /* negate branch-condition */ + *insn = 0xe000; /* B unconditional to label */ + } + if (val < 0) + val -= 2; /* backward-branches are 2 bytes longer */ + isize += 2; + op.type = TBR11; + } + } + else if (op.type == TBRHL) { + /* BL always consists of two instructions */ + isize += 2; + } + else if (op.type == TPCLW) { + /* @@@ optimization makes any sense? */ + op.type = TUIMA; + base = NULL; /* no more checks */ + } + } + else { + /* symbol is in a different section or externally declared */ + if (op.type == TBRHL) { + val -= THB_PREFETCH; + if (db) { + add_extnreloc_masked(&db->relocs,base,val,REL_PC, + arm_be_mode?5:0,11,0,0x7ff000); + add_extnreloc_masked(&db->relocs,base,val,REL_PC, + arm_be_mode?16+5:16+0,11,0,0xffe); + } + isize += 2; /* we need two instructions for a 23-bit branch */ + } + else if (op.type == TPCLW) { + /* val -= THB_PREFETCH; @@@ only positive offsets allowed! */ + op.type = TUIMA; + if (db) + add_extnreloc_masked(&db->relocs,base,val,REL_PC, + arm_be_mode?8:0,8,0,0x3fc); + base = NULL; /* no more checks */ + } + else if (insn) + cpu_error(22); /* operation not allowed on external symbols */ + } + } + else if (insn) + cpu_error(2); /* label from current section required */ + } + + /* optimizations should be finished at this stage - + inserts operands into the opcode now: */ + + if (insn) { + + if (THREGOPER(op.type)) { + /* insert register operand, check was already done in parse_operand */ + if (!THPCORSP(op.type)) { + switch (op.type) { + case TRG02: + case THR02: + *insn |= val&7; + break; + case TRG05: + case THR05: + case TR5IN: + *insn |= (val&7) << 3; + break; + case TRG08: + case TR8IN: + *insn |= (val&7) << 6; + break; + case TRG10: + case TR10W: + *insn |= (val&7) << 8; + break; + default: + ierror(0); + break; + } + } + } + + else if (THREGLIST(op.type)) { + /* register list was already checked in parse_operand - just insert */ + *insn |= val; + } + + else if (THIMMOPER(op.type) || op.type==TSWI8) { + /* immediate operand */ + switch (op.type) { + case TUIM3: + if (val>=0 && val<=7) { + *insn |= val<<6; + } + else + cpu_error(25,3,(long)val); /* immediate offset out of range */ + break; + case TUIM5: + case TUI5I: + if (val>=0 && val<=0x1f) { + *insn |= val<<6; + } + else + cpu_error(25,5,(long)val); /* immediate offset out of range */ + break; + case TUI6I: + if (val>=0 && val<=0x3e) { + if ((val & 1) == 0) + *insn |= (val&0x3e)<<5; + else + cpu_error(26,2); /* offset has to be a multiple of 2 */ + } + else + cpu_error(25,6,(long)val); /* immediate offset out of range */ + break; + case TUI7I: + if (val>=0 && val<=0x7c) { + if ((val & 3) == 0) + *insn |= (val&0x7c)<<4; + else + cpu_error(26,4); /* offset has to be a multiple of 4 */ + } + else + cpu_error(25,7,(long)val); /* immediate offset out of range */ + break; + case TUIM8: + case TSWI8: + if (val>=0 && val<=0xff) { + *insn |= val; + } + else + cpu_error(25,8,(long)val); /* immediate offset out of range */ + break; + case TUIM9: + if (val>=0 && val<=0x1fc) { + if ((val & 3) == 0) + *insn |= val>>2; + else + cpu_error(26,4); /* offset has to be a multiple of 4 */ + } + else + cpu_error(25,9,(long)val); /* immediate offset out of range */ + break; + case TUIMA: + case TUIAI: + if (val>=0 && val<=0x3fc) { + if ((val & 3) == 0) + *insn |= val>>2; + else + cpu_error(26,4); /* offset has to be a multiple of 4 */ + } + else + cpu_error(25,10,(long)val); /* immediate offset out of range */ + break; + } + + if (base!=NULL && db!=NULL) { + if (btype == BASE_OK) { + if (op.type==TUIM5 || op.type==TUI5I) + add_extnreloc_masked(&db->relocs,base,val,REL_ABS, + arm_be_mode?5:6,5,0,0x1f); + else if (op.type == TSWI8) + add_extnreloc_masked(&db->relocs,base,val,REL_ABS, + arm_be_mode?8:0,8,0,0xff); + else + cpu_error(6); /* constant integer expression required */ + } + else + general_error(38); /* illegal relocation */ + } + } + + else if (op.type == TBR08) { + /* only write offset, relocs and optimizations are handled above */ + if (val & 1) + cpu_error(8,(long)val); /* branch to unaligned address */ + *insn |= (val>>1) & 0xff; + } + + else if (op.type == TBR11) { + /* only write offset, relocs and optimizations are handled above */ + if (val<-0x800 || val>0x7fe) + cpu_error(3,(long)val); /* branch offset is out of range */ + if (val & 1) + cpu_error(8,(long)val); /* branch to unaligned address */ + *insn |= (val>>1) & 0x7ff; + } + + else if (op.type == TBRHL) { + /* split 23-bit offset over two instructions, ignoring bit 0 */ + if (val<-0x400000 || val>0x3ffffe) + cpu_error(3,(long)val); /* branch offset is out of range */ + if (val & 1) + cpu_error(8,(long)val); /* branch to unaligned address */ + *insn++ |= (val>>12) & 0x7ff; + *insn = 0xf800 | ((val>>1) & 0x7ff); + } + + else + ierror(0); + } + } + + return isize; +} + + +#define ROTFAIL (0xffffff) + +static uint32_t rotated_immediate(uint32_t val) +/* check if a 32-bit value can be represented as 8-bit-rotated, + return ROTFAIL when impossible */ +{ + uint32_t i,a; + + for (i=0; i<32; i+=2) { + if ((a = val<>(32-i)) <= 0xff) + return (i<<7) | a; + } + return ROTFAIL; +} + + +static int negated_rot_immediate(uint32_t val,mnemonic *mnemo, + uint32_t *insn) +/* check if negating the ALU-operation makes a valid 8-bit-rotated value, + insert it into the current instruction, when successful */ +{ + uint32_t neg = rotated_immediate(-val); + uint32_t inv = rotated_immediate(~val); + uint32_t op = (mnemo->ext.opcode & 0x01e00000) >> 21; + + switch (op) { + /* AND <-> BIC */ + case 0: op=14; val=inv; break; + case 14: op=0; val=inv; break; + /* ADD <-> SUB */ + case 2: op=4; val=neg; break; + case 4: op=2; val=neg; break; + /* ADC <-> SBC */ + case 5: op=6; val=inv; break; + case 6: op=5; val=inv; break; + /* CMP <-> CMN */ + case 10: op=11; val=neg; break; + case 11: op=10; val=neg; break; + /* MOV <-> MVN */ + case 13: op=15; val=inv; break; + case 15: op=13; val=inv; break; + + default: return 0; + } + + if (val == ROTFAIL) + return 0; + + if (insn) { + *insn &= ~0x01e00000; + *insn |= (op<<21) | val; + } + return 1; +} + + +static uint32_t double_rot_immediate(uint32_t val,uint32_t *hi) +/* check if a 32-bit value can be represented by combining two + 8-bit rotated values, return ROTFAIL otherwise */ +{ + uint32_t i,a; + + for (i=0; i<32; i+=2) { + if (((a = val<>(32-i)) & 0xff) != 0) { + if (a & 0xff00) { + if (a & 0xffff0000) + continue; + *hi = ((i+24)<<7) | (a>>8); + } + else if (a & 0xff0000) { + if (a & 0xff000000) + continue; + *hi = ((i+16)<<7) | (a>>16); + } + else if (a & 0xff000000) + *hi = ((i+8)<<7) | (a>>24); + else + ierror(0); + + return (i<<7) | (a&0xff); + } + } + + return ROTFAIL; +} + + +static uint32_t calc_2nd_rot_opcode(uint32_t op) +/* calculates ALU operation for second instruction */ +{ + if (op == 13) + op = 12; /* MOV + ORR */ + else if (op == 15) + op = 1; /* MVN + EOR */ + /* ADD and SUB stay the same */ + + return op << 21; +} + + +static int negated_double_rot_immediate(uint32_t val,uint32_t *insn) +/* check if negating the ALU-operation and/or a second ADD/SUB operation + makes a valid 8-bit-rotated value, insert it into the current + instruction, when successful */ +{ + uint32_t op = (*insn & 0x01e00000) >> 21; + + if ((op==2 || op==4 || op==13 || op==15) && insn!=NULL) { + /* combined instructions only possible for ADD/SUB/MOV/MVN */ + uint32_t lo,hi; + + *(insn+1) = *insn & ~0x01ef0000; + *(insn+1) |= (*insn&0xf000) << 4; /* Rn = Rd of first instruction */ + + if ((lo = double_rot_immediate(val,&hi)) != ROTFAIL) { + *insn++ |= hi; + *insn |= calc_2nd_rot_opcode(op) | lo; + return 1; + } + + /* @@@ try negated or inverted values */ + } + + return 0; +} + + +static uint32_t get_condcode(instruction *ip) +/* returns condition (bit 31-28) from instruction's qualifiers */ +{ + const char *cc = condition_codes; + char *q; + + if (q = ip->qualifiers[0]) { + uint32_t code = 0; + + while (*cc) { + if (!strnicmp(q,cc,2) && *(q+2)=='\0') + break; + cc += 2; + code++; + } + if (*cc) { /* condition code in qualifier valid */ + if (code == 16) /* hs -> cs */ + code = 2; + else if (code==17 || code==18) /* lo/ul -> cc */ + code = 3; + + return code<<28; + } + } + + return 0xe0000000; /* AL - always */ +} + + +static int get_addrmode(instruction *ip) +/* return addressing mode from instruction's qualifiers */ +{ + char *q; + + if ((q = ip->qualifiers[1]) == NULL) + q = ip->qualifiers[0]; + + if (q) { + const char **am = addrmode_strings; + int mode = AM_DA; + + do { + if (!stricmp(*am,q)) + break; + am++; + mode++; + } + while (*am); + + if (*am != NULL) + return mode; + } + + return AM_NONE; +} + + +size_t eval_arm_operands(instruction *ip,section *sec,taddr pc, + uint32_t *insn,dblock *db) +/* evaluate expressions and try to optimize ARM instruction, + return size of instruction */ +{ + operand op; + mnemonic *mnemo = &mnemonics[ip->code]; + int am = get_addrmode(ip); + int aa4ldst = 0; + int opcnt = 0; + size_t isize = 4; + taddr chkreg = -1; + + if (insn) { + if (pc & 3) + cpu_error(27); /* instruction at unaligned address */ + + *insn = mnemo->ext.opcode | get_condcode(ip); + + if ((mnemo->ext.flags & SETCC)!=0 && am==AM_S) + *insn |= 0x00100000; /* set-condition-codes flag */ + + if ((mnemo->ext.flags & SETPSR)!=0 && am==AM_P) { + /* Rd = R15 for changing the PSR. Recommended for ARM2/250/3 only. */ + *insn |= 0x0000f000; + if (cpu_type & ~AA2) + cpu_error(28); /* deprecated on 32-bit architectures */ + } + + if (!strcmp(mnemo->name,"ldr") || !strcmp(mnemo->name,"str")) { + if (am==AM_T || am==AM_B || am==AM_BT || am==AM_TB) { /* std. ldr/str */ + if (am != AM_B) { + *insn |= 0x00200000; /* W-flag for post-indexed mode */ + *insn &= ~0x01000000; /* force post-indexed */ + } + if (am != AM_T) + *insn |= 0x00400000; /* B-flag for byte-transfer */ + } + else if (am==AM_SB || am==AM_H || am==AM_SH) { /* arch.4 ldr/str */ + if (cpu_type & AA4UP) { + /* take P-, I- and L-bit from previous standard instruction */ + *insn = (*insn&0xf1100000) + | (((*insn&0x02000000)^0x02000000)>>3) /* I-bit is flipped */ + | 0x90; + if (am != AM_H) { + if (*insn & 0x00100000) /* load */ + *insn |= 0x40; /* signed transfer */ + else + cpu_error(18,addrmode_strings[am]); /* illegal addr. mode */ + } + if (am != AM_SB) + *insn |= 0x20; /* halfword-transfer */ + aa4ldst = 1; + } + else + cpu_error(0); /* instruction not supported on selected arch. */ + } + else if (am != AM_NONE) + cpu_error(18,addrmode_strings[am]); /* illegal addr. mode */ + } + else if (ip->code == OC_SWP) { + if (am == AM_B) + *insn |= 0x00400000; /* swap bytes */ + else if (am != AM_NONE) + cpu_error(18,addrmode_strings[am]); /* illegal addr. mode */ + } + } + else { /* called by instruction_size() */ + if (am==AM_SB || am==AM_H || am==AM_SH) + aa4ldst = 1; + } + + for (opcnt=0; opcntop[opcnt]!=NULL; opcnt++) { + taddr val; + symbol *base = NULL; + int btype; + + op = *(ip->op[opcnt]); + if (!eval_expr(op.value,&val,sec,pc)) + btype = find_base(op.value,&base,sec,pc); + + /* do optimizations first */ + + if (op.type==PCL12 || op.type==PCLRT || + op.type==PCLCP || op.type==BRA24) { + /* PC-relative offsets (take prefetch into account: PC+8) */ + if (base!=NULL && btype==BASE_OK) { + if (!is_pc_reloc(base,sec)) { + /* no relocation required, can be resolved immediately */ + val -= pc + 8; + + switch (op.type) { + case BRA24: + if (val>=0x2000000 || val<-0x2000000) { + /* @@@ optimize? to what? */ + if (insn) + cpu_error(3,(long)val); /* branch offset is out of range */ + } + break; + + case PCL12: + if ((!aa4ldst && val<0x1000 && val>-0x1000) || + (aa4ldst && val<0x100 && val>-0x100)) { + op.type = IMUD2; /* handle as normal #+/-Imm12 */ + if (val < 0) + val = -val; + else + op.flags |= OFL_UP; + base = NULL; /* no more checks */ + } + else { + if (opt_ldrpc && + ((!aa4ldst && val<0x100000 && val>-0x100000) || + (aa4ldst && val<0x10000 && val>-0x10000))) { + /* ADD/SUB Rd,PC,#offset&0xff000 */ + /* LDR/STR Rd,[Rd,#offset&0xfff] */ + if (insn) { + taddr v; + + *(insn+1) = *insn; + *insn &= 0xf0000000; /* clear all except cond. */ + if (val < 0) { + v = -val; + *insn |= 0x024f0a00; /* SUB */ + *(insn+1) &= ~0x00800000; /* clear U-bit */ + } + else { + v = val; + *insn |= 0x028f0a00; /* ADD */ + *(insn+1) |= 0x00800000; /* set U-bit */ + } + if (aa4ldst) + *insn |= (*(insn+1)&0xf000) | ((v&0xff00)>>8); + else + *insn |= (*(insn+1)&0xf000) | ((v&0xff000)>>12); + *(insn+1) &= ~0x000f0000; /* replace PC by Rd */ + *(insn+1) |= (*insn & 0xf000) << 4; + insn++; + } + if (val < 0) + val = -val; + else + op.flags |= OFL_UP; + val = aa4ldst ? (val & 0xff) : (val & 0xfff); + isize += 4; + op.type = IMUD2; + base = NULL; /* no more checks */ + } + else { + op.type = NOOP; + if (insn) + cpu_error(4,val); /* PC-relative ldr/str out of range */ + } + } + break; + + case PCLCP: + if (val<0x400 && val>-0x400) { + op.type = IMCP2; /* handle as normal #+/-Imm10>>2 */ + if (val < 0) + val = -val; + else + op.flags |= OFL_UP; + base = NULL; /* no more checks */ + } + else { + /* no optimization, because we don't have a free register */ + op.type = NOOP; + if (insn) + cpu_error(4,val); /* PC-relative ldc/stc out of range */ + } + break; + + case PCLRT: + op.type = NOOP; /* is handled here */ + if (val < 0) { + /* use SUB instead of ADD */ + if (insn) + *insn ^= 0x00c00000; + val = -val; + } + if ((val = rotated_immediate(val)) != ROTFAIL) { + if (insn) + *insn |= val; + } + else if (opt_adr || am==AM_L) { + /* ADRL or optimize ADR automatically to ADRL */ + uint32_t hi,lo; + + isize += 4; + if ((lo = double_rot_immediate(val,&hi)) != ROTFAIL) { + /* ADD/SUB Rd,PC,#hi8rotated */ + /* ADD/SUB Rd,Rd,#lo8rotated */ + if (insn) { + *(insn+1) = *insn & ~0xf0000; + *(insn+1) |= (*insn&0xf000) << 4; + *insn++ |= hi; + *insn |= lo; + } + } + else if (insn) + cpu_error(5,(uint32_t)val); /* Cannot make rot.immed.*/ + } + else if (insn) + cpu_error(5,(uint32_t)val); /* Cannot make rot.immed.*/ + break; + + default: + ierror(0); + } + } + else { + /* symbol is in a different section or externally declared */ + switch (op.type) { + case BRA24: + val -= ARM_PREFETCH; + if (db) + add_extnreloc_masked(&db->relocs,base,val,REL_PC, + arm_be_mode?8:0,24,0,0x3fffffc); + break; + case PCL12: + op.type = IMUD2; + if (db) { + if (val<0x1000 && val>-0x1000) { + add_extnreloc_masked(&db->relocs,base,val,REL_PC, + arm_be_mode?20:0,12,0,0x1fff); + base = NULL; /* don't add another REL_ABS below */ + } + else + cpu_error(22); /* operation not allowed on external symbols */ + } + break; + case PCLCP: + if (db) + cpu_error(22); /* operation not allowed on external symbols */ + break; + case PCLRT: + op.type = NOOP; + if (am==AM_L && val==0) { /* ADRL */ + isize += 4; /* always reserve two ADD instructions */ + if (insn!=NULL && db!=NULL) { + *(insn+1) = *insn & ~0xf0000; + *(insn+1) |= (*insn&0xf000) << 4; + add_extnreloc_masked(&db->relocs,base,val,REL_PC, + arm_be_mode?24:0,8,0,0xff00); + add_extnreloc_masked(&db->relocs,base,val,REL_PC, + arm_be_mode?32+24:32+0,8,0,0xff); + } + } + else if (val == 0) { /* ADR */ + if (db) + add_extnreloc_masked(&db->relocs,base,val,REL_PC, + arm_be_mode?24:0,8,0,0xff); + } + else if (db) + cpu_error(22); /* operation not allowed on external symbols */ + break; + default: + ierror(0); + } + } + } + else if (insn) { + op.type = NOOP; + cpu_error(2); /* label from current section required */ + } + } + + else if (op.type == IMROT) { + op.type = NOOP; /* is handled here */ + + if (base == NULL) { + uint32_t rotval; + + if ((rotval = rotated_immediate(val)) != ROTFAIL) { + if (insn) + *insn |= rotval; + } + else if (!negated_rot_immediate(val,mnemo,insn)) { + /* rotation, negation and inversion failed - try a 2nd operation */ + isize += 4; + if (insn) { + if (!negated_double_rot_immediate(val,insn)) + cpu_error(7,(uint32_t)val); /* const not suitable */ + } + } + } + else if (insn) + cpu_error(6); /* constant integer expression required */ + } + + /* optimizations should be finished at this stage - + inserts operands into the opcode now: */ + + if (insn) { + + if (REGOPER(op.type)) { + /* insert register operand */ + if (base!=NULL || val<0 || val>15) + cpu_error(9); /* not a valid ARM register */ + + if (REG19OPER(op.type)) + *insn |= val<<16; + else if (REG15OPER(op.type)) + *insn |= val<<12; + else if (REG11OPER(op.type)) + *insn |= val<<8; + else if (REG03OPER(op.type)) + *insn |= val; + + if (op.type==R3UD1 && !(*insn&0x01000000)) + cpu_error(21); /* post-indexed addressing mode expected */ + if (op.flags & OFL_WBACK) + *insn |= 0x00200000; + if (op.flags & OFL_UP) + *insn |= 0x00800000; + + /* some more checks: */ + if ((mnemo->ext.flags&NOPC) && val==15) + cpu_error(10); /* PC (r15) not allowed in this mode */ + if ((mnemo->ext.flags&NOPCR03) && val==15 && REG03OPER(op.type)) + cpu_error(11); /* PC (r15) not allowed for offset register Rm */ + if ((mnemo->ext.flags&NOPC) && val==15 && (op.flags&OFL_WBACK)) + cpu_error(12); /* PC (r15) not allowed with write-back */ + + /* check for illegal double register specifications */ + if (((mnemo->ext.flags&DIFR03) && REG03OPER(op.type)) || + ((mnemo->ext.flags&DIFR11) && REG11OPER(op.type)) || + ((mnemo->ext.flags&DIFR15) && REG15OPER(op.type)) || + ((mnemo->ext.flags&DIFR19) && REG19OPER(op.type))) { + if (chkreg != -1) { + if (val == chkreg) + cpu_error(13,(long)val); /* register was used multiple times */ + } + else + chkreg = val; + } + } + + else if (op.type == BRA24) { + /* only write offset, relocs and optimizations are handled above */ + if (val & 3) + cpu_error(8,(long)val); /* branch to unaligned address */ + *insn |= (val>>2) & 0xffffff; + } + + else if (op.type==IMUD1 || op.type==IMUD2) { + if (aa4ldst) { + /* insert splitted 8-bit immediate for signed/halfword ldr/str */ + if (val>=0 && val<=0xff) { + *insn |= ((val&0xf0)<<4) | (val&0x0f); + } + else + cpu_error(20,8,(long)val); /* immediate offset out of range */ + } + else { + /* insert immediate 12-bit with up/down flag */ + if (val>=0 && val<=0xfff) { + *insn |= val; + } + else + cpu_error(20,12,(long)val); /* immediate offset out of range */ + } + + if (op.type==IMUD1 && !(*insn&0x01000000)) + cpu_error(21); /* post-indexed addressing mode exptected */ + if (op.flags & OFL_WBACK) + *insn |= 0x00200000; /* set write-back flag */ + if (op.flags & OFL_UP) + *insn |= 0x00800000; /* set UP-flag */ + + if (base) { + if (btype == BASE_OK) { + if (EXTREF(base)) { + if (!aa4ldst) { + /* @@@ does this make any sense? */ + *insn |= 0x00800000; /* only UP */ + add_extnreloc_masked(&db->relocs,base,val,REL_ABS, + arm_be_mode?20:0,12,0,0xfff); + } + else + cpu_error(22); /* operation not allowed on external symbols */ + } + else + cpu_error(6); /* constant integer expression required */ + } + else + general_error(38); /* illegal relocation */ + } + } + + else if (op.type==IMCP1 || op.type==IMCP2) { + /* insert immediate 10-bit shifted left by 2, with up/down flag */ + if (val>=0 && val<=0x3ff) { + if ((val & 3) == 0) + *insn |= val>>2; + else + cpu_error(23); /* ldc/stc offset has to be a multiple of 4 */ + } + else + cpu_error(20,10,(long)val); /* immediate offset out of range */ + + if (op.flags & OFL_WBACK) + *insn |= 0x00200000; /* set write-back flag */ + if (op.flags & OFL_UP) + *insn |= 0x00800000; /* set UP-flag */ + + if (base) + cpu_error(6); /* constant integer expression required */ + } + + else if (op.type == SWI24) { + /* insert 24-bit immediate (SWI instruction) */ + if (val>=0 && val<0x1000000) { + *insn |= val; + if (base!=NULL && db!=NULL) + add_extnreloc_masked(&db->relocs,base,val,REL_ABS, + arm_be_mode?8:0,24,0,0xffffff); + } + else + cpu_error(16); /* 24-bit unsigned immediate expected */ + if (base) + cpu_error(6); /* constant integer expression required */ + } + + else if (op.type == IROTV) { + /* insert 4-bit rotate constant (even value, shifted right) */ + if (val>=0 && val<=30 && (val&1)==0) + *insn |= val << 7; + else + cpu_error(29,(long)val); /* must be even number between 0 and 30 */ + if (base) + cpu_error(6); /* constant integer expression required */ + } + + else if (op.type == IMMD8) { + /* unsigned 8-bit immediate constant, used together with IROTV */ + if (val>=0 && val<0x100 && base==NULL) + *insn |= val; + else + cpu_error(30,8,(long)val); /* 8-bit unsigned constant required */ + } + + else if (SHIFTOPER(op.type)) { + /* insert a register- or immediate shift */ + int sh_op = op.flags & OFL_SHIFTOP; + + if (aa4ldst) + cpu_error(19); /* signed/halfword ldr/str doesn't support shifts */ + if (op.type==SHIM1 && !(*insn&0x01000000)) + cpu_error(21); /* post-indexed addressing mode exptected */ + + if (op.flags & OFL_IMMEDSHIFT) { + if (sh_op==1 || sh_op==2) { /* lsr/asr permit shift-count #32 */ + if (val == 32) + val = 0; + } + if (base==NULL && val>=0 && val<32) { + *insn |= (val<<7) | ((op.flags&OFL_SHIFTOP)<<5); + if (op.flags & OFL_WBACK) + *insn |= 0x00200000; + } + else + cpu_error(14,(long)val); /* illegal immediate shift count */ + } + else { /* shift count in register */ + if (base==NULL && val>=0 && val<16) { + *insn |= (val<<8) | ((op.flags&OFL_SHIFTOP)<<5) | 0x10; + } + else + cpu_error(15); /* not a valid shift register */ + } + } + + else if (CPOPCODE(op.type)) { + /* insert coprocessor operation/type */ + if (base == NULL) { + switch (op.type) { + case CPOP3: + if (val>=0 && val<8) + *insn |= val<<21; + else + cpu_error(24,val); /* illegal coprocessor operation */ + break; + case CPOP4: + if (val>=0 && val<16) + *insn |= val<<20; + else + cpu_error(24,val); /* illegal coprocessor operation */ + break; + case CPTYP: + if (val>=0 && val<8) + *insn |= val<<5; + else + cpu_error(24,val); /* illegal coprocessor operation */ + break; + default: ierror(0); + } + } + else + cpu_error(24,val); /* illegal coprocessor operation */ + } + + else if (op.type==CSPSR || op.type==PSR_F) { + /* insert PSR type - no checks needed */ + *insn |= val<<16; + if (op.flags & OFL_SPSR) + *insn |= 0x00400000; + } + + else if (op.type == RLIST) { + /* insert register-list field */ + if (amAM_ED) + cpu_error(18,addrmode_strings[am]); /* illegal addr. mode */ + if (am>=AM_FA && am<=AM_ED) { + /* fix stack-addressing mode */ + if (!(mnemo->ext.opcode & 0x00100000)) + am ^= 3; /* invert P/U modes for store operations */ + } + *insn |= ((am&3)<<23) | val; + if (op.flags & OFL_FORCE) + *insn |= 0x00400000; + } + + else if (op.type != NOOP) + ierror(0); + } + } + + return isize; +} + + +size_t instruction_size(instruction *ip,section *sec,taddr pc) +/* Calculate the size of the current instruction; must be identical + to the data created by eval_instruction. */ +{ + if (mnemonics[ip->code].ext.flags & THUMB) + return eval_thumb_operands(ip,sec,pc,NULL,NULL); + + /* ARM mode */ + return eval_arm_operands(ip,sec,pc,NULL,NULL); +} + + +dblock *eval_instruction(instruction *ip,section *sec,taddr pc) +/* Convert an instruction into a DATA atom including relocations, + if necessary. */ +{ + dblock *db = new_dblock(); + int inst_type; + + if (sec != last_section) { + last_section = sec; + last_data_type = -1; + } + inst_type = (mnemonics[ip->code].ext.flags & THUMB) ? TYPE_THUMB : TYPE_ARM; + + if (inst_type == TYPE_THUMB) { + uint16_t insn[2]; + + if (db->size = eval_thumb_operands(ip,sec,pc,insn,db)) { + unsigned char *d = db->data = mymalloc(db->size); + int i; + + for (i=0; isize/2; i++) + d = setval(arm_be_mode,d,2,insn[i]); + } + } + + else { /* ARM mode */ + uint32_t insn[2]; + + if (db->size = eval_arm_operands(ip,sec,pc,insn,db)) { + unsigned char *d = db->data = mymalloc(db->size); + int i; + + for (i=0; isize/4; i++) + d = setval(arm_be_mode,d,4,insn[i]); + } + } + + if (inst_type != last_data_type) + create_mapping_symbol(inst_type,sec,pc); + + return db; +} + + +dblock *eval_data(operand *op,size_t bitsize,section *sec,taddr pc) +/* Create a dblock (with relocs, if necessary) for size bits of data. */ +{ + dblock *db = new_dblock(); + taddr val; + + if (sec != last_section) { + last_section = sec; + last_data_type = -1; + } + + if ((bitsize & 7) || bitsize > 64) + cpu_error(17,bitsize); /* data size not supported */ + + if (op->type!=DATA_OP && op->type!=DATA64_OP) + ierror(0); + + db->size = bitsize >> 3; + db->data = mymalloc(db->size); + + if (op->type == DATA64_OP) { + thuge hval; + + if (!eval_expr_huge(op->value,&hval)) + general_error(59); /* cannot evaluate huge integer */ + huge_to_mem(arm_be_mode,db->data,db->size,hval); + } + else { + if (!eval_expr(op->value,&val,sec,pc)) { + symbol *base; + int btype; + + btype = find_base(op->value,&base,sec,pc); + if (base) + add_extnreloc(&db->relocs,base,val, + btype==BASE_PCREL?REL_PC:REL_ABS,0,bitsize,0); + else if (btype != BASE_NONE) + general_error(38); /* illegal relocation */ + } + switch (db->size) { + case 1: + db->data[0] = val & 0xff; + break; + case 2: + case 4: + setval(arm_be_mode,db->data,db->size,val); + break; + default: + ierror(0); + break; + } + } + + if (last_data_type != TYPE_DATA) + create_mapping_symbol(TYPE_DATA,sec,pc); + + return db; +} + + +int init_cpu() +{ + char r[4]; + int i; + + for (i=0; i 1) + inst_alignment = thumb_mode ? 2 : 4; + return 1; +} + + +int cpu_args(char *p) +{ + if (!strncmp(p,"-m",2)) { + p += 2; + if (!strcmp(p,"2")) cpu_type = ARM2; + else if (!strcmp(p,"250")) cpu_type = ARM250; + else if (!strcmp(p,"3")) cpu_type = ARM3; + else if (!strcmp(p,"6")) cpu_type = ARM6; + else if (!strcmp(p,"600")) cpu_type = ARM600; + else if (!strcmp(p,"610")) cpu_type = ARM610; + else if (!strcmp(p,"7")) cpu_type = ARM7; + else if (!strcmp(p,"710")) cpu_type = ARM710; + else if (!strcmp(p,"7500")) cpu_type = ARM7500; + else if (!strcmp(p,"7d")) cpu_type = ARM7d; + else if (!strcmp(p,"7di")) cpu_type = ARM7di; + else if (!strcmp(p,"7dm")) cpu_type = ARM7dm; + else if (!strcmp(p,"7dmi")) cpu_type = ARM7dmi; + else if (!strcmp(p,"7tdmi")) cpu_type = ARM7tdmi; + else if (!strcmp(p,"8")) cpu_type = ARM8; + else if (!strcmp(p,"810")) cpu_type = ARM810; + else if (!strcmp(p,"9")) cpu_type = ARM9; + else if (!strcmp(p,"920")) cpu_type = ARM920; + else if (!strcmp(p,"920t")) cpu_type = ARM920t; + else if (!strcmp(p,"9tdmi")) cpu_type = ARM9tdmi; + else if (!strcmp(p,"sa1")) cpu_type = SA1; + else if (!strcmp(p,"strongarm")) cpu_type = STRONGARM; + else if (!strcmp(p,"strongarm110")) cpu_type = STRONGARM110; + else if (!strcmp(p,"strongarm1100")) cpu_type = STRONGARM1100; + else return 0; + } + else if (!strncmp(p,"-a",2)) { + p += 2; + if (!strcmp(p,"2")) cpu_type = AA2; + else if (!strcmp(p,"3")) cpu_type = AA3; + else if (!strcmp(p,"3m")) cpu_type = AA3M; + else if (!strcmp(p,"4")) cpu_type = AA4; + else if (!strcmp(p,"4t")) cpu_type = AA4T; + else return 0; + } + else if (!strcmp(p,"-little")) + arm_be_mode = 0; + else if (!strcmp(p,"-big")) + arm_be_mode = 1; + else if (!strcmp(p,"-thumb")) + thumb_mode = 1; + else if (!strcmp(p,"-opt-ldrpc")) + opt_ldrpc = 1; + else if (!strcmp(p,"-opt-adr")) + opt_adr = 1; + + return 1; +} diff --git a/vasm/cpus/arm/cpu.h b/vasm/cpus/arm/cpu.h new file mode 100644 index 0000000..0c0bbd7 --- /dev/null +++ b/vasm/cpus/arm/cpu.h @@ -0,0 +1,214 @@ +/* cpu.h ARM cpu-description header-file */ +/* (c) in 2004,2014,2016 by Frank Wille */ + +#define LITTLEENDIAN (!arm_be_mode) +#define BIGENDIAN (arm_be_mode) +#define VASM_CPU_ARM 1 + +/* maximum number of operands in one mnemonic */ +#define MAX_OPERANDS 6 + +/* maximum number of mnemonic-qualifiers per mnemonic */ +#define MAX_QUALIFIERS 2 +/* but no qualifiers for macros */ +#define NO_MACRO_QUALIFIERS + +/* valid parentheses for cpu's operands */ +#define START_PARENTH(x) ((x)=='(' || (x)=='{') +#define END_PARENTH(x) ((x)==')' || (x)=='}') + +/* data type to represent a target-address */ +typedef int32_t taddr; +typedef uint32_t utaddr; + +/* minimum instruction alignment */ +#define INST_ALIGN 0 /* Handled internally! */ + +/* default alignment for n-bit data */ +#define DATA_ALIGN(n) ((n)<=8 ? 1 : ((n)<=16 ? 2 : 4)) + +/* operand class for n-bit data definitions */ +#define DATA_OPERAND(n) (n==64 ? DATA64_OP : DATA_OP) + +/* returns true when instruction is valid for selected cpu */ +#define MNEMONIC_VALID(i) cpu_available(i) + + +/* type to store each operand */ +typedef struct { + uint16_t type; /* type of operand from mnemonic.operand_type */ + uint16_t flags; /* see below */ + expr *value; /* single register, immed. val. or branch loc.*/ +} operand; + +/* flags: */ +#define OFL_SHIFTOP (0x0003) /* mask for shift-operation */ +#define OFL_IMMEDSHIFT (0x0004) /* uses immediate shift value */ +#define OFL_WBACK (0x0008) /* set write-back flag in opcode */ +#define OFL_UP (0x0010) /* set up-flag, add offset to base */ +#define OFL_SPSR (0x0020) /* 1:SPSR, 0:CPSR */ +#define OFL_FORCE (0x0040) /* LDM/STM PSR & force user bit */ + + +/* operand types - WARNING: the order is important! See defines below. */ +enum { + /* ARM operands */ + NOOP=0, + DATA_OP, /* data operand */ + DATA64_OP, /* 64-bit data operand (greater than taddr) */ + BRA24, /* 24-bit branch offset to label */ + PCL12, /* 12-bit PC-relative offset with up/down-flag to label */ + PCLCP, /* 8-bit * 4 PC-relative offset with up/down-flag to label */ + PCLRT, /* 8-bit rotated PC-relative offset to label */ + CPOP4, /* 4-bit coprocessor operation code at 23..20 */ + CPOP3, /* 3-bit coprocessor operation code at 23..21 */ + CPTYP, /* 3-bit coprocessor operation type at 7..5 */ + SWI24, /* 24-bit immediate at 23..0 (SWI instruction) */ + IROTV, /* explicit 4-bit rotate value at 11..8 */ + REG03, /* Rn at 3..0 */ + REG11, /* Rn at 11..8 */ + REG15, /* Rn at 15..12 */ + REG19, /* Rn at 19..16 */ + R19WB, /* Rn at 19..16 with optional write-back '!' */ + R19PR, /* [Rn, pre-indexed at 19..16 */ + R19PO, /* [Rn], post-indexed or indir. without index, at 19..16 */ + R3UD1, /* +/-Rn], pre-indexed at 3..0 with optional write-back '!' */ + R3UD2, /* +/-Rn, at 3..0, pre- or post-indexed */ + IMUD1, /* #+/-Imm12] pre-indexed with ']' and optional w-back '!' */ + IMUD2, /* #+/-Imm12 post-indexed */ + IMCP1, /* #+/-Imm10>>2 pre-indexed with ']' and optional w-back '!' */ + IMCP2, /* #+/-Imm10>>2 post-indexed */ + IMMD8, /* #Immediate, 8-bit */ + IMROT, /* #Imm32, 8-bit auto-rotated */ + SHIFT, /* Rs | #Imm5 | RRX = ROR #0 */ + SHIM1, /* #Imm5 | RRX, pre-indexed with terminating ] or ]! */ + SHIM2, /* #Imm5 | RRX, post-indexed */ + CSPSR, /* CPSR or SPSR */ + PSR_F, /* PSR-field: SPSR_, CPSR_ */ + RLIST, /* register list */ + + /* THUMB operands */ + TRG02, /* Rn at 2..0 */ + TRG05, /* Rn at 5..3 */ + TRG08, /* Rn at 8..6 */ + TRG10, /* Rn at 10..8 */ + THR02, /* Hi-Rn at 2..0 */ + THR05, /* Hi-Rn at 5..3 */ + TR5IN, /* [Rn at 5..3 */ + TR8IN, /* Rn] at 8..6 */ + TR10W, /* Rn! at 10..8 with write-back '!' */ + TPCRG, /* "PC" */ + TSPRG, /* "SP" */ + TPCPR, /* "[PC" */ + TSPPR, /* "[SP" */ + TRLST, /* register list for r0-r7 */ + TRLLR, /* extended register list, includes LR */ + TRLPC, /* extended register list, includes PC */ + TUIM3, /* 3-bit unsigned immediate at 8..6 */ + TUIM5, /* 5-bit unsigned immediate at 10..6 */ + TUIM8, /* 8-bit unsigned immediate at 7..0 */ + TUIM9, /* 9-bit unsigned immediate >> 2 at 6..0 */ + TUIMA, /* 10-bit unsigned immediate >> 2 at 7..0 */ + TUI5I, /* 5-bit unsigned immediate at 10..6 with terminating ] */ + TUI6I, /* 6-bit unsigned immediate >> 1 at 10..6 with terminating ] */ + TUI7I, /* 7-bit unsigned immediate >> 2 at 10..6 with terminating ] */ + TUIAI, /* 10-bit unsigned immediate >> 2 at 7..0 with terminating ] */ + TSWI8, /* 8-bit immediate at 7..0 (SWI instruction) */ + TPCLW, /* PC-relative label, has to fit into 10-bit uns.imm. >> 2 */ + TBR08, /* 9-bit branch offset >> 1 to label at 7..0 */ + TBR11, /* 12-bit branch offset >> 1 to label at 10..0 */ + TBRHL /* 23-bit branch offset >> 1 splitted into two 11-bit instr. */ +}; + +#define ARMOPER(x) ((x)>=BRA24 && (x)<=RLIST) +#define STDOPER(x) ((x)>=DATA_OP && (x)<=IMROT) +#define CPOPCODE(x) ((x)>=CPOP4 && (x)<=CPTYP) +#define REGOPER(x) ((x)>=REG03 && (x)<=R3UD2) +#define REG19OPER(x) ((x)>=REG19 && (x)<=R19PO) +#define REG15OPER(x) ((x)==REG15) +#define REG11OPER(x) ((x)==REG11) +#define REG03OPER(x) ((x)==REG03 || (x)==R3UD1 || (x)==R3UD2) +#define UPDOWNOPER(x) ((x)>=R3UD1 && (x)<=IMCP2) +#define IMMEDOPER(x) ((x)>=IMUD1 && (x)<=IMROT) +#define SHIFTOPER(x) ((x)>=SHIFT && (x)<=SHIM2) + +#define THUMBOPER(x) ((x)==0 || (x)>=TRG02) +#define THREGOPER(x) ((x)>=TRG02 && (x)<=TSPPR) +#define THPCORSP(x) ((x)>=TPCRG && (x)<=TSPPR) +#define THREGLIST(x) ((x)>=TRLST && (x)<=TRLPC) +#define THIMMOPER(x) ((x)>=TUIM3 && (x)<=TUIAI) +#define THIMMINDIR(x) ((x)>=TUI5I && (x)<=TUIAI) +#define THBRANCH(x) ((x)>=TBR08 && (x)<=TBRHL) + + +/* additional mnemonic data */ +typedef struct { + uint32_t opcode; + uint32_t available; + uint32_t flags; +} mnemonic_extension; + +/* flags: */ +#define DIFR19 (0x00000001) /* DIFFRxx registers must be different */ +#define DIFR15 (0x00000002) +#define DIFR11 (0x00000004) +#define DIFR03 (0x00000008) +#define NOPC (0x00000010) /* R15 is not allowed as source or dest. */ +#define NOPCR03 (0x00000020) /* R15 is not allowed for Rm (3..0) */ +#define NOPCWB (0x00000040) /* R15 is not allowed in Write-Back mode */ +#define SETCC (0x00000100) /* instruction supports S-bit */ +#define SETPSR (0x00000200) /* instruction supports P-bit */ +#define THUMB (0x10000000) /* THUMB instruction */ + + +/* register symbols */ +#define HAVE_REGSYMS +#define REGSYMHTSIZE 256 + + +/* cpu types for availability check */ +#define ARM2 (1L<<0) +#define ARM250 (1L<<1) +#define ARM3 (1L<<2) +#define ARM6 (1L<<3) +#define ARM60 (1L<<4) +#define ARM600 (1L<<5) +#define ARM610 (1L<<6) +#define ARM7 (1L<<7) +#define ARM710 (1L<<8) +#define ARM7500 (1L<<9) +#define ARM7d (1L<<10) +#define ARM7di (1L<<11) +#define ARM7dm (1L<<12) +#define ARM7dmi (1L<<13) +#define ARM7tdmi (1L<<14) +#define ARM8 (1L<<15) +#define ARM810 (1L<<16) +#define ARM9 (1L<<17) +#define ARM920 (1L<<18) +#define ARM920t (1L<<19) +#define ARM9tdmi (1L<<20) +#define SA1 (1L<<21) +#define STRONGARM (1L<<22) +#define STRONGARM110 (1L<<23) +#define STRONGARM1100 (1L<<24) + +/* ARM architectures */ +#define AA2 (ARM2|ARM250|ARM3) +#define AA3 (ARM6|ARM60|ARM600|ARM610|ARM7|ARM710|ARM7500|ARM7d|ARM7di) +#define AA3M (ARM7dm|ARM7dmi) +#define AA4 (ARM8|ARM810|ARM9|ARM920|SA1|STRONGARM|STRONGARM110|STRONGARM1100) +#define AA4T (ARM7tdmi|ARM920t|ARM9tdmi) + +#define AA4TUP (AA4T) +#define AA4UP (AA4|AA4T) +#define AA3MUP (AA3M|AA4|AA4T) +#define AA3UP (AA3|AA3M|AA4|AA4T) +#define AA2UP (AA2|AA3|AA3M|AA4|AA4T) +#define AAANY (~0) + + +/* exported by cpu.c */ +extern int arm_be_mode; + +int cpu_available(int); diff --git a/vasm/cpus/arm/cpu_errors.h b/vasm/cpus/arm/cpu_errors.h new file mode 100644 index 0000000..66f7f46 --- /dev/null +++ b/vasm/cpus/arm/cpu_errors.h @@ -0,0 +1,31 @@ + "instruction not supported on selected architecture",ERROR, + "trailing garbage in operand",WARNING, + "label from current section required",ERROR, + "branch offset (%ld) is out of range",ERROR, + "PC-relative load/store (offset %ld) out of range",ERROR, + "cannot make rotated immediate from PC-relative offset (0x%lx)",ERROR,/*05*/ + "constant integer expression required",ERROR, + "constant (0x%lx) not suitable for 8-bit rotated immediate",ERROR, + "branch to an unaligned address (offset %ld)",ERROR, + "not a valid ARM register",ERROR, + "PC (r15) not allowed in this mode",ERROR, /*10*/ + "PC (r15) not allowed for offset register Rm",ERROR, + "PC (r15) not allowed with write-back",ERROR, + "register r%ld was used multiple times",ERROR, + "illegal immediate shift count (%ld)",ERROR, + "not a valid shift register",ERROR, /*15*/ + "24-bit unsigned immediate expected",ERROR, + "data size %d not supported",ERROR, + "illegal addressing mode: %s",ERROR, + "signed/halfword ldr/str doesn't support shifts",ERROR, + "%d-bit immediate offset out of range (%ld)",ERROR, /*20*/ + "post-indexed addressing mode exptected",ERROR, + "operation not allowed on external symbols",ERROR, + "ldc/stc offset has to be a multiple of 4",ERROR, + "illegal coprocessor operation mode or type: %ld\n",ERROR, + "%d-bit unsigned immediate offset out of range (%ld)",ERROR, /*25*/ + "offset has to be a multiple of %d",ERROR, + "instruction at unaligned address",ERROR, + "TSTP/TEQP/CMNP/CMPP deprecated on 32-bit architectures",WARNING, + "rotate constant must be an even number between 0 and 30: %ld",ERROR, + "%d-bit unsigned constant required: %ld",ERROR, /*30*/ diff --git a/vasm/cpus/arm/opcodes.h b/vasm/cpus/arm/opcodes.h new file mode 100644 index 0000000..3302fb2 --- /dev/null +++ b/vasm/cpus/arm/opcodes.h @@ -0,0 +1,201 @@ + "add", {REG15,REG19,IMROT}, {0x02800000,AAANY,SETCC}, + "add", {REG15,REG19,IMMD8,IROTV}, {0x02800000,AAANY,SETCC}, + "add", {REG15,REG19,REG03}, {0x00800000,AAANY,SETCC}, + "add", {REG15,REG19,REG03,SHIFT}, {0x00800000,AAANY,SETCC}, + "add", {TRG02,TRG05,TRG08}, {0x1800,AA4TUP,THUMB}, + "add", {TRG02,TRG05,TUIM3}, {0x1c00,AA4TUP,THUMB}, + "add", {TRG10,TUIM8}, {0x3000,AA4TUP,THUMB}, + "add", {TRG02,THR05}, {0x4440,AA4TUP,THUMB}, + "add", {THR02,TRG05}, {0x4480,AA4TUP,THUMB}, + "add", {THR02,THR05}, {0x44c0,AA4TUP,THUMB}, + "add", {TRG10,TPCRG,TUIMA}, {0xa000,AA4TUP,THUMB}, + "add", {TRG10,TSPRG,TUIMA}, {0xa800,AA4TUP,THUMB}, + "add", {TSPRG,TUIM9}, {0xb000,AA4TUP,THUMB}, + "adc", {REG15,REG19,IMROT}, {0x02a00000,AAANY,SETCC}, + "adc", {REG15,REG19,IMMD8,IROTV}, {0x02a00000,AAANY,SETCC}, + "adc", {REG15,REG19,REG03}, {0x00a00000,AAANY,SETCC}, + "adc", {REG15,REG19,REG03,SHIFT}, {0x00a00000,AAANY,SETCC}, + "adc", {TRG02,TRG05}, {0x4140,AA4TUP,THUMB}, + "adr", {REG15,PCLRT}, {0x028f0000,AAANY,0}, + "adr", {TRG10,TPCLW}, {0xa000,AA4TUP,THUMB}, + "and", {REG15,REG19,IMROT}, {0x02000000,AAANY,SETCC}, + "and", {REG15,REG19,IMMD8,IROTV}, {0x02000000,AAANY,SETCC}, + "and", {REG15,REG19,REG03}, {0x00000000,AAANY,SETCC}, + "and", {REG15,REG19,REG03,SHIFT}, {0x00000000,AAANY,SETCC}, + "and", {TRG02,TRG05}, {0x4000,AA4TUP,THUMB}, + "asr", {TRG02,TRG05,TUIM5}, {0x1000,AA4TUP,THUMB}, + "asr", {TRG02,TRG05}, {0x4100,AA4TUP,THUMB}, + "b", {BRA24}, {0x0a000000,AAANY,0}, + "b", {TBR11}, {0xe000,AA4TUP,THUMB}, + "beq", {TBR08}, {0xd000,AA4TUP,THUMB}, + "bne", {TBR08}, {0xd100,AA4TUP,THUMB}, + "bcs", {TBR08}, {0xd200,AA4TUP,THUMB}, + "bcc", {TBR08}, {0xd300,AA4TUP,THUMB}, + "bmi", {TBR08}, {0xd400,AA4TUP,THUMB}, + "bpl", {TBR08}, {0xd500,AA4TUP,THUMB}, + "bvs", {TBR08}, {0xd600,AA4TUP,THUMB}, + "bvc", {TBR08}, {0xd700,AA4TUP,THUMB}, + "bhi", {TBR08}, {0xd800,AA4TUP,THUMB}, + "bls", {TBR08}, {0xd900,AA4TUP,THUMB}, + "bge", {TBR08}, {0xda00,AA4TUP,THUMB}, + "blt", {TBR08}, {0xdb00,AA4TUP,THUMB}, + "bgt", {TBR08}, {0xdc00,AA4TUP,THUMB}, + "ble", {TBR08}, {0xdd00,AA4TUP,THUMB}, + "bhs", {TBR08}, {0xd200,AA4TUP,THUMB}, + "blo", {TBR08}, {0xd300,AA4TUP,THUMB}, + "bul", {TBR08}, {0xd300,AA4TUP,THUMB}, + "bic", {REG15,REG19,IMROT}, {0x03c00000,AAANY,SETCC}, + "bic", {REG15,REG19,IMMD8,IROTV}, {0x03c00000,AAANY,SETCC}, + "bic", {REG15,REG19,REG03}, {0x01c00000,AAANY,SETCC}, + "bic", {REG15,REG19,REG03,SHIFT}, {0x01c00000,AAANY,SETCC}, + "bic", {TRG02,TRG05}, {0x4380,AA4TUP,THUMB}, + "bl", {BRA24}, {0x0b000000,AAANY,0}, + "bl", {TBRHL}, {0xf000,AA4TUP,THUMB}, + "bx", {REG03}, {0x012fff10,AA4TUP,NOPC}, + "bx", {TRG05}, {0x4700,AA4TUP,THUMB}, + "bx", {THR05}, {0x4740,AA4TUP,THUMB}, + "cdp", {REG11,CPOP4,REG15,REG19,REG03}, {0x0e000000,AA2UP,0}, + "cdp", {REG11,CPOP4,REG15,REG19,REG03,CPTYP},{0x0e000000,AA2UP,0}, + "cmn", {REG19,IMROT}, {0x03700000,AAANY,SETPSR}, + "cmn", {REG19,IMMD8,IROTV}, {0x03700000,AAANY,SETPSR}, + "cmn", {REG19,REG03}, {0x01700000,AAANY,SETPSR}, + "cmn", {REG19,REG03,SHIFT}, {0x01700000,AAANY,SETPSR}, + "cmn", {TRG02,TRG05}, {0x42c0,AA4TUP,THUMB}, + "cmp", {REG19,IMROT}, {0x03500000,AAANY,SETPSR}, + "cmp", {REG19,IMMD8,IROTV}, {0x03500000,AAANY,SETPSR}, + "cmp", {REG19,REG03}, {0x01500000,AAANY,SETPSR}, + "cmp", {REG19,REG03,SHIFT}, {0x01500000,AAANY,SETPSR}, + "cmp", {TRG10,TUIM8}, {0x2800,AA4TUP,THUMB}, + "cmp", {TRG02,TRG05}, {0x4280,AA4TUP,THUMB}, + "cmp", {TRG02,THR05}, {0x4540,AA4TUP,THUMB}, + "cmp", {THR02,TRG05}, {0x4580,AA4TUP,THUMB}, + "cmp", {THR02,THR05}, {0x45c0,AA4TUP,THUMB}, + "eor", {REG15,REG19,IMROT}, {0x02200000,AAANY,SETCC}, + "eor", {REG15,REG19,IMMD8,IROTV}, {0x02200000,AAANY,SETCC}, + "eor", {REG15,REG19,REG03}, {0x00200000,AAANY,SETCC}, + "eor", {REG15,REG19,REG03,SHIFT}, {0x00200000,AAANY,SETCC}, + "eor", {TRG02,TRG05}, {0x4040,AA4TUP,THUMB}, + "ldc", {REG11,REG15,PCLCP}, {0x0d1f0000,AA2UP,0}, + "ldc", {REG11,REG15,R19PR,IMCP1}, {0x0d100000,AA2UP,0}, + "ldc", {REG11,REG15,R19PO}, {0x0d100000,AA2UP,0}, + "ldc", {REG11,REG15,R19PO,IMCP2}, {0x0c100000,AA2UP,0}, + "ldm", {R19WB,RLIST}, {0x08100000,AAANY,NOPC}, + "ldmia",{TR10W,TRLST}, {0xc800,AA4TUP,THUMB}, + "ldr", {REG15,PCL12}, {0x051f0000,AAANY,NOPCWB}, + "ldr", {REG15,R19PR,IMUD1}, {0x05100000,AAANY,NOPCWB}, + "ldr", {REG15,R19PR,R3UD1}, {0x07100000,AAANY,NOPCWB|NOPCR03}, + "ldr", {REG15,R19PR,R3UD2,SHIM1}, {0x07100000,AAANY,NOPCWB|NOPCR03}, + "ldr", {REG15,R19PO}, {0x05900000,AAANY,NOPCWB}, + "ldr", {REG15,R19PO,IMUD2}, {0x04100000,AAANY,NOPCWB}, + "ldr", {REG15,R19PO,R3UD2}, {0x06100000,AAANY,NOPCWB|NOPCR03}, + "ldr", {REG15,R19PO,R3UD2,SHIM2}, {0x06100000,AAANY,NOPCWB|NOPCR03}, + "ldr", {TRG10,TPCLW}, {0x4800,AA4TUP,THUMB}, + "ldr", {TRG10,TPCPR,TUIAI}, {0x4800,AA4TUP,THUMB}, + "ldr", {TRG02,TR5IN,TR8IN}, {0x5800,AA4TUP,THUMB}, + "ldr", {TRG02,TR5IN,TUI7I}, {0x6800,AA4TUP,THUMB}, + "ldr", {TRG10,TSPPR,TUIAI}, {0x9800,AA4TUP,THUMB}, + "ldrb", {TRG02,TR5IN,TR8IN}, {0x5c00,AA4TUP,THUMB}, + "ldrb", {TRG02,TR5IN,TUI5I}, {0x7800,AA4TUP,THUMB}, + "ldrh", {TRG02,TR5IN,TR8IN}, {0x5a00,AA4TUP,THUMB}, + "ldrh", {TRG02,TR5IN,TUI6I}, {0x8800,AA4TUP,THUMB}, + "ldsb", {TRG02,TR5IN,TR8IN}, {0x5600,AA4TUP,THUMB}, + "ldsh", {TRG02,TR5IN,TR8IN}, {0x5e00,AA4TUP,THUMB}, + "lsl", {TRG02,TRG05,TUIM5}, {0x0000,AA4TUP,THUMB}, + "lsl", {TRG02,TRG05}, {0x4080,AA4TUP,THUMB}, + "lsr", {TRG02,TRG05,TUIM5}, {0x0800,AA4TUP,THUMB}, + "lsr", {TRG02,TRG05}, {0x40c0,AA4TUP,THUMB}, + "mcr", {REG11,CPOP3,REG15,REG19,REG03}, {0x0e000010,AA2UP,0}, + "mcr", {REG11,CPOP3,REG15,REG19,REG03,CPTYP},{0x0e000010,AA2UP,0}, + "mov", {REG15,IMROT}, {0x03a00000,AAANY,SETCC}, + "mov", {REG15,IMMD8,IROTV}, {0x03a00000,AAANY,SETCC}, + "mov", {REG15,REG03}, {0x01a00000,AAANY,SETCC}, + "mov", {REG15,REG03,SHIFT}, {0x01a00000,AAANY,SETCC}, + "mov", {TRG10,TUIM8}, {0x2000,AA4TUP,THUMB}, + "mov", {TRG02,TRG05}, {0x1c00,AA4TUP,THUMB}, + "mov", {TRG02,THR05}, {0x4640,AA4TUP,THUMB}, + "mov", {THR02,TRG05}, {0x4680,AA4TUP,THUMB}, + "mov", {THR02,THR05}, {0x46c0,AA4TUP,THUMB}, + "mrc", {REG11,CPOP3,REG15,REG19,REG03}, {0x0e100010,AA2UP,0}, + "mrc", {REG11,CPOP3,REG15,REG19,REG03,CPTYP},{0x0e100010,AA2UP,0}, + "mrs", {REG15,CSPSR}, {0x01000000,AA3UP,NOPC}, + "mla", {REG19,REG03,REG11,REG15}, {0x00200090,AA2UP,SETCC|NOPC|DIFR19|DIFR03}, + "msr", {PSR_F,IMROT}, {0x0320f000,AA3UP,0}, + "msr", {PSR_F,IMMD8,IROTV}, {0x0320f000,AA3UP,0}, + "msr", {PSR_F,REG03}, {0x0120f000,AA3UP,NOPC}, + "mul", {REG19,REG03,REG11}, {0x00000090,AA2UP,SETCC|NOPC|DIFR19|DIFR03}, + "mul", {TRG02,TRG05}, {0x4340,AA4TUP,THUMB}, + "mvn", {REG15,IMROT}, {0x03e00000,AAANY,SETCC}, + "mvn", {REG15,IMMD8,IROTV}, {0x03e00000,AAANY,SETCC}, + "mvn", {REG15,REG03}, {0x01e00000,AAANY,SETCC}, + "mvn", {REG15,REG03,SHIFT}, {0x01e00000,AAANY,SETCC}, + "mvn", {TRG02,TRG05}, {0x43c0,AA4TUP,THUMB}, + "neg", {TRG02,TRG05}, {0x4240,AA4TUP,THUMB}, + "nop", {0}, {0x01a00000,AAANY,0}, + "orr", {REG15,REG19,IMROT}, {0x03800000,AAANY,SETCC}, + "orr", {REG15,REG19,IMMD8,IROTV}, {0x03800000,AAANY,SETCC}, + "orr", {REG15,REG19,REG03}, {0x01800000,AAANY,SETCC}, + "orr", {REG15,REG19,REG03,SHIFT}, {0x01800000,AAANY,SETCC}, + "orr", {TRG02,TRG05}, {0x4300,AA4TUP,THUMB}, + "pop", {TRLPC}, {0xbc00,AA4TUP,THUMB}, + "push", {TRLLR}, {0xb400,AA4TUP,THUMB}, + "ror", {TRG02,TRG05}, {0x41c0,AA4TUP,THUMB}, + "rsb", {REG15,REG19,IMROT}, {0x02600000,AAANY,SETCC}, + "rsb", {REG15,REG19,IMMD8,IROTV}, {0x02600000,AAANY,SETCC}, + "rsb", {REG15,REG19,REG03}, {0x00600000,AAANY,SETCC}, + "rsb", {REG15,REG19,REG03,SHIFT}, {0x00600000,AAANY,SETCC}, + "rsc", {REG15,REG19,IMROT}, {0x02e00000,AAANY,SETCC}, + "rsc", {REG15,REG19,IMMD8,IROTV}, {0x02e00000,AAANY,SETCC}, + "rsc", {REG15,REG19,REG03}, {0x00e00000,AAANY,SETCC}, + "rsc", {REG15,REG19,REG03,SHIFT}, {0x00e00000,AAANY,SETCC}, + "smlal", {REG15,REG19,REG03,REG11}, {0x00e00090,AA3MUP,SETCC|NOPC|DIFR19|DIFR15|DIFR03}, + "smull", {REG15,REG19,REG03,REG11}, {0x00c00090,AA3MUP,SETCC|NOPC|DIFR19|DIFR15|DIFR03}, + "stc", {REG11,REG15,PCLCP}, {0x0d0f0000,AA2UP,0}, + "stc", {REG11,REG15,R19PR,IMCP1}, {0x0d000000,AA2UP,0}, + "stc", {REG11,REG15,R19PO}, {0x0d000000,AA2UP,0}, + "stc", {REG11,REG15,R19PO,IMCP2}, {0x0c000000,AA2UP,0}, + "stm", {R19WB,RLIST}, {0x08000000,AAANY,NOPC}, + "stmia",{TR10W,TRLST}, {0xc000,AA4TUP,THUMB}, + "str", {REG15,PCL12}, {0x050f0000,AAANY,NOPCWB}, + "str", {REG15,R19PR,IMUD1}, {0x05000000,AAANY,NOPCWB}, + "str", {REG15,R19PR,R3UD1}, {0x07000000,AAANY,NOPCWB|NOPCR03}, + "str", {REG15,R19PR,R3UD2,SHIM1}, {0x07000000,AAANY,NOPCWB|NOPCR03}, + "str", {REG15,R19PO}, {0x05800000,AAANY,NOPCWB}, + "str", {REG15,R19PO,IMUD2}, {0x04000000,AAANY,NOPCWB}, + "str", {REG15,R19PO,R3UD2}, {0x06000000,AAANY,NOPCWB|NOPCR03}, + "str", {REG15,R19PO,R3UD2,SHIM2}, {0x06000000,AAANY,NOPCWB|NOPCR03}, + "str", {TRG02,TR5IN,TR8IN}, {0x5000,AA4TUP,THUMB}, + "str", {TRG02,TR5IN,TUI7I}, {0x6000,AA4TUP,THUMB}, + "str", {TRG10,TSPPR,TUIAI}, {0x9000,AA4TUP,THUMB}, + "strb", {TRG02,TR5IN,TR8IN}, {0x5400,AA4TUP,THUMB}, + "strb", {TRG02,TR5IN,TUI5I}, {0x7000,AA4TUP,THUMB}, + "strh", {TRG02,TR5IN,TR8IN}, {0x5200,AA4TUP,THUMB}, + "strh", {TRG02,TR5IN,TUI6I}, {0x8000,AA4TUP,THUMB}, + "sbc", {REG15,REG19,IMROT}, {0x02c00000,AAANY,SETCC}, + "sbc", {REG15,REG19,IMMD8,IROTV}, {0x02c00000,AAANY,SETCC}, + "sbc", {REG15,REG19,REG03}, {0x00c00000,AAANY,SETCC}, + "sbc", {REG15,REG19,REG03,SHIFT}, {0x00c00000,AAANY,SETCC}, + "sbc", {TRG02,TRG05}, {0x4180,AA4TUP,THUMB}, + "sub", {REG15,REG19,IMROT}, {0x02400000,AAANY,SETCC}, + "sub", {REG15,REG19,IMMD8,IROTV}, {0x02400000,AAANY,SETCC}, + "sub", {REG15,REG19,REG03}, {0x00400000,AAANY,SETCC}, + "sub", {REG15,REG19,REG03,SHIFT}, {0x00400000,AAANY,SETCC}, + "sub", {TRG02,TRG05,TRG08}, {0x1a00,AA4TUP,THUMB}, + "sub", {TRG02,TRG05,TUIM3}, {0x1e00,AA4TUP,THUMB}, + "sub", {TRG10,TUIM8}, {0x3800,AA4TUP,THUMB}, + "sub", {TSPRG,TUIM9}, {0xb080,AA4TUP,THUMB}, + "svc", {SWI24}, {0x0f000000,AAANY,0}, + "svc", {TSWI8}, {0xdf00,AA4TUP,THUMB}, + "swi", {SWI24}, {0x0f000000,AAANY,0}, + "swi", {TSWI8}, {0xdf00,AA4TUP,THUMB}, + "swp", {REG15,REG03,R19PO}, {0x01000090,AA3UP,NOPC}, + "teq", {REG19,IMROT}, {0x03300000,AAANY,SETPSR}, + "teq", {REG19,IMMD8,IROTV}, {0x03300000,AAANY,SETPSR}, + "teq", {REG19,REG03}, {0x01300000,AAANY,SETPSR}, + "teq", {REG19,REG03,SHIFT}, {0x01300000,AAANY,SETPSR}, + "tst", {REG19,IMROT}, {0x03100000,AAANY,SETPSR}, + "tst", {REG19,IMMD8,IROTV}, {0x03100000,AAANY,SETPSR}, + "tst", {REG19,REG03}, {0x01100000,AAANY,SETPSR}, + "tst", {REG19,REG03,SHIFT}, {0x01100000,AAANY,SETPSR}, + "tst", {TRG02,TRG05}, {0x4200,AA4TUP,THUMB}, + "umlal", {REG15,REG19,REG03,REG11}, {0x00a00090,AA3MUP,SETCC|NOPC|DIFR19|DIFR15|DIFR03}, + "umull", {REG15,REG19,REG03,REG11}, {0x00800090,AA3MUP,SETCC|NOPC|DIFR19|DIFR15|DIFR03}, diff --git a/vasm/cpus/c16x/cpu.c b/vasm/cpus/c16x/cpu.c new file mode 100644 index 0000000..da65530 --- /dev/null +++ b/vasm/cpus/c16x/cpu.c @@ -0,0 +1,786 @@ +/* cpu.c example cpu-description file */ +/* (c) in 2002 by Volker Barthelmann */ + +#include "vasm.h" + +char *cpu_copyright="vasm c16x/st10 cpu backend 0.2c (c) in 2002-2005 Volker Barthelmann"; +char *cpuname="c16x"; + +mnemonic mnemonics[]={ +#include "opcodes.h" +}; + +int mnemonic_cnt=sizeof(mnemonics)/sizeof(mnemonics[0]); + +int bitsperbyte=8; +int bytespertaddr=4; + +static int JMPA,JMPR,JMPS,JNB,JB,JBC,JNBS,JMP; +static int notrans,tojmpa; + +#define JMPCONV 256 +#define INVCC(c) (((c)&1)?(c)-1:(c)+1) + +#define ISBIT 1 + +typedef struct sfr { + struct sfr *next; + int flags; + unsigned int laddr,saddr,boffset; +} sfr; + + +sfr *first_sfr; +#define SFRHTSIZE 1024 +hashtable *sfrhash; + +static char *skip_reg(char *s,int *reg) +{ + int r=-1; + if(*s!='r'&&*s!='R'){ + cpu_error(1); + return s; + } + s++; + if(*s<'0'||*s>'9'){ + cpu_error(1); + return s; + } + r=*s++-'0'; + if(*s>='0'&&*s<='5') + r=10*r+*s++-'0'; + *reg=r; + return s; +} + +int parse_operand(char *p,int len,operand *op,int requires) +{ + op->type=-1; + op->mod=-1; + p=skip(p); + if(requires==OP_REL){ + char *s=p; + op->type=OP_REL; + op->offset=parse_expr(&s); + simplify_expr(op->offset); + if(s==p) + return 0; + else + return 1; + } + if(requires==OP_CC){ + op->type=OP_CC; + if(len<4||len>6||p[0]!='c'||p[1]!='c'||p[2]!='_') + return 0; + if(len==4){ + if(p[3]=='z') + op->cc=2; + else if(p[3]=='v') + op->cc=4; + else if(p[3]=='n') + op->cc=6; + else if(p[3]=='c') + op->cc=8; + else + return 0; + }else if(len==5){ + if(p[3]=='u'&&p[4]=='c') + op->cc=0; + else if(p[3]=='n'&&p[4]=='z') + op->cc=3; + else if(p[3]=='n'&&p[4]=='v') + op->cc=5; + else if(p[3]=='n'&&p[4]=='n') + op->cc=7; + else if(p[3]=='n'&&p[4]=='c') + op->cc=0; + else if(p[3]=='e'&&p[4]=='q') + op->cc=2; + else if(p[3]=='n'&&p[4]=='e') + op->cc=3; + else + return 0; + }else if(len==6){ + if(!strncmp(p+3,"ult",3)) + op->cc=8; + else if(!strncmp(p+3,"ule",3)) + op->cc=0xf; + else if(!strncmp(p+3,"uge",3)) + op->cc=0x9; + else if(!strncmp(p+3,"ugt",3)) + op->cc=0xe; + else if(!strncmp(p+3,"slt",3)) + op->cc=0xc; + else if(!strncmp(p+3,"sle",3)) + op->cc=0xb; + else if(!strncmp(p+3,"sge",3)) + op->cc=0xd; + else if(!strncmp(p+3,"sgt",3)) + op->cc=0xa; + else if(!strncmp(p+3,"net",3)) + op->cc=0x1; + else + return 0; + } + return 1; + } + if((p[0]=='r'||p[0]=='R')&&p[1]>='0'&&p[1]<='9'&&(len==2||p[2]=='.')){ + op->type=OP_GPR; + op->reg=p[1]-'0'; + op->regsfr=op->reg+0xf0; + if(len>2){ + op->type=OP_BADDR; + if(requires==OP_BADDR){ + p=skip(p+3); + op->boffset=parse_expr(&p); + op->offset=number_expr(op->regsfr); + } + } + }else if((p[0]=='r'||p[0]=='R')&&p[1]=='1'&&p[2]>='0'&&p[2]<='5'&&(len==3||p[3]=='.')){ + op->type=OP_GPR; + op->reg=(p[1]-'0')*10+p[2]-'0'; + op->regsfr=op->reg+0xf0; + if(len>3){ + op->type=OP_BADDR; + if(requires==OP_BADDR){ + p=skip(p+4); + op->boffset=parse_expr(&p); + op->offset=number_expr(op->regsfr); + } + } + }else if(len==3&&(p[0]=='r'||p[0]=='R')&&(p[1]=='l'||p[1]=='L')&&p[2]>='0'&&p[2]<='7'){ + op->type=OP_BGPR; + op->reg=(p[2]-'0')*2; + op->regsfr=op->reg+0xf0; + }else if(len==3&&(p[0]=='r'||p[0]=='R')&&(p[1]=='h'||p[1]=='H')&&p[2]>='0'&&p[2]<='7'){ + op->type=OP_BGPR; + op->reg=(p[2]-'0')*2+1; + op->regsfr=op->reg+0xf0; + }else if(p[0]=='#'){ + op->type=OP_IMM16; + p=skip(p+1); + if((!strncmp("SOF",p,3)||!strncmp("sof",p,3))&&isspace((unsigned char)p[3])){op->mod=MOD_SOF;p=skip(p+3);} + if((!strncmp("SEG",p,3)||!strncmp("seg",p,3))&&isspace((unsigned char)p[3])){op->mod=MOD_SEG;p=skip(p+3);} + if((!strncmp("DPP0:",p,5)||!strncmp("dpp0:",p,5))){op->mod=MOD_DPP0;p=skip(p+5);} + if((!strncmp("DPP1:",p,5)||!strncmp("dpp1:",p,5))){op->mod=MOD_DPP1;p=skip(p+5);} + if((!strncmp("DPP2:",p,5)||!strncmp("dpp2:",p,5))){op->mod=MOD_DPP2;p=skip(p+5);} + if((!strncmp("DPP3:",p,5)||!strncmp("dpp3:",p,5))){op->mod=MOD_DPP3;p=skip(p+5);} + if((!strncmp("DPPX:",p,5)||!strncmp("dppx:",p,5))){op->mod=MOD_DPPX;p=skip(p+5);} + op->offset=parse_expr(&p); + simplify_expr(op->offset); +#if 0 + if(op->offset->type==NUM){ + taddr val=op->offset->c.val; + if(val>=0&&val<=7) + op->type=OP_IMM3; + else if(val>=0&&val<=15) + op->type=OP_IMM4; + else if(val>=0&&val<=127) + op->type=OP_IMM7; + else if(val>=0&&val<=255) + op->type=OP_IMM8; + } +#endif + }else if(*p=='['){ + p=skip(p+1); + if(*p=='-'){ + p=skip(p+1); + p=skip_reg(p,&op->reg); + p=skip(p); + if(*p!=']') + cpu_error(0); + if(op->reg<=3) + op->type=OP_PREDEC03; + else + op->type=OP_PREDEC; + }else{ + p=skip_reg(p,&op->reg); + p=skip(p); + if(*p=='+'){ + p=skip(p+1); + if(*p==']'){ + if(op->reg<=3) + op->type=OP_POSTINC03; + else + op->type=OP_POSTINC; + }else{ + if(*p!='#') + cpu_error(0); + p=skip(p+1); + op->offset=parse_expr(&p); + p=skip(p); + op->type=OP_REGDISP; + } + }else{ + if(op->reg<=3) + op->type=OP_REG03IND; + else + op->type=OP_REGIND; + } + if(*p!=']') + cpu_error(0); + } + }else{ + if(ISIDSTART(*p)){ + char *name=p; + hashdata data; + while((p==name||ISIDCHAR(*p))&&*p!='.') + p++; + if(find_namelen(sfrhash,name,p-name,&data)){ + sfr *sfr; + sfr=data.ptr; + if(sfr->flags&ISBIT){ + op->offset=number_expr(sfr->saddr); + op->type=OP_BADDR; + op->boffset=number_expr(sfr->boffset); + }else{ + if(requires==OP_SFR||requires==OP_BSFR||requires==OP_BWORD){ + op->offset=number_expr(sfr->saddr); + op->type=requires; + }else if(requires==OP_BADDR&&*p=='.'){ + op->offset=number_expr(sfr->saddr); + p=skip(p+1); + op->boffset=parse_expr(&p); + op->type=OP_BADDR; + }else if(requires==OP_ABS||requires==OP_BABS){ + op->type=requires; + op->offset=number_expr((2*sfr->saddr)+(sfr->laddr<<8)); + } + } + } + if(op->type==-1) + p=name; + } + if(op->type==-1){ + if((!strncmp("SOF",p,3)||!strncmp("sof",p,3))&&isspace((unsigned char)p[3])){op->mod=MOD_SOF;p=skip(p+3);} + if((!strncmp("SEG",p,3)||!strncmp("seg",p,3))&&isspace((unsigned char)p[3])){op->mod=MOD_SEG;p=skip(p+3);} + if((!strncmp("DPP0:",p,5)||!strncmp("dpp0:",p,5))){op->mod=MOD_DPP0;p=skip(p+5);} + if((!strncmp("DPP1:",p,5)||!strncmp("dpp1:",p,5))){op->mod=MOD_DPP1;p=skip(p+5);} + if((!strncmp("DPP2:",p,5)||!strncmp("dpp2:",p,5))){op->mod=MOD_DPP2;p=skip(p+5);} + if((!strncmp("DPP3:",p,5)||!strncmp("dpp3:",p,5))){op->mod=MOD_DPP3;p=skip(p+5);} + if((!strncmp("DPPX:",p,5)||!strncmp("dppx:",p,5))){op->mod=MOD_DPPX;p=skip(p+5);} + op->offset=parse_expr(&p); + op->type=OP_ABS; + } + } + if(requires==op->type) + return 1; + if(requires==OP_BWORD&&op->type==OP_SFR) + return 1; + if(op->type==OP_IMM16&&(requires>=OP_IMM2&&requires<=OP_IMM16)) + return 1; + if(op->type==OP_PREDEC03&&requires==OP_PREDEC) + return 1; + if(op->type==OP_POSTINC03&&requires==OP_POSTINC) + return 1; + if(op->type==OP_REG03IND&&requires==OP_REGIND) + return 1; + if((requires==OP_SFR&&op->type==OP_GPR)|| + (requires==OP_BWORD&&op->type==OP_GPR)|| + (requires==OP_BWORD&&op->type==OP_BGPR)|| + (requires==OP_BSFR&&op->type==OP_BGPR)){ + op->offset=number_expr(op->regsfr); + return 1; + } + if(requires==OP_BSFR&&op->type==OP_BGPR) + return 1; + if(requires==OP_JADDR&&op->type==OP_ABS) + return 1; + if(requires==OP_BABS&&op->type==OP_ABS) + return 1; + /*FIXME*/ + return 0; +} + +static taddr reloffset(expr *tree,section *sec,taddr pc) +{ + symbol *sym; + int btype; + taddr val; + simplify_expr(tree); + if(tree->type==NUM){ + /* should we do it like this?? */ + val=tree->c.val; + }else{ + btype=find_base(tree,&sym,sec,pc); + if(btype!=BASE_OK||!LOCREF(sym)||sym->sec!=sec) + val=0xffff; + else{ + eval_expr(tree,&val,sec,pc); + val=val-pc; + } + } + return val; +} + +static taddr absoffset2(expr *tree,int mod,section *sec,taddr pc,rlist **relocs,int roffset,int size,taddr mask) +{ + taddr val; + if(mod==MOD_SOF){ + if(mask!=0xffffffff&&mask!=0xffff) cpu_error(5); + mask=0xffff; + } + if(mod==MOD_SEG){ + if(mask!=0xff&&mask!=0xffff&&mask!=0xffffffff) cpu_error(6); + mask<<=16; + } + if(mod==MOD_DPP0||mod==MOD_DPP1||mod==MOD_DPP2||mod==MOD_DPP3||mod==MOD_DPPX){ + if(mask!=0xffffffff&&mask!=0xffff) cpu_error(7); + mask=0x3fff; + } + if(!eval_expr(tree,&val,sec,pc)){ + taddr addend=val; + symbol *base; + if(find_base(tree,&base,sec,pc)!=BASE_OK){ + general_error(38); + return val; + } + if(mod==MOD_DPP1) val|=0x4000; + if(mod==MOD_DPP2) val|=0x8000; + if(mod==MOD_DPP3) val|=0xc000; + if(mod==MOD_DPPX){ + static int dpplen; + static char *dppname; + char *id=base->name; + symbol *dppsym; + size-=2; + if(strlen(id)+9>dpplen){ + myfree(dppname); + dppname=mymalloc(dpplen=strlen(id)+9); + } + strcpy(dppname,"___DPP_"); + strcat(dppname,id); + dppsym=new_import(dppname); + if(dppsym->type==EXPRESSION){ + if(!eval_expr(dppsym->expr,&val,0,0)) + ierror(0); + val<<=14; + }else{ + add_nreloc_masked(relocs,dppsym,0,REL_ABS,2,roffset+14,0x3); + } + } + add_nreloc_masked(relocs,base,addend,REL_ABS,size,roffset,mask); + return val; + } + val&=mask; + if(mod==MOD_DPPX) cpu_error(7); + if(mod==MOD_DPP1) val|=0x4000; + if(mod==MOD_DPP2) val|=0x8000; + if(mod==MOD_DPP3) val|=0xc000; + if(mod==MOD_SEG) val>>=16; + /*FIXME: range check */ +#if 1 + if(size==16) + return val&0xffff; + else + return val&((1<4) + cpu_error(3,2); + return val; + }else if(val<0||val>=(1<code; + taddr val; + /* choose one of jmpr/jmpa */ + if(c==JMP||(!notrans&&(c==JMPA||c==JMPR||c==JB||c==JNB))){ + val=reloffset(p->op[1]->offset,sec,pc); + if(val<-256||val>254||val%2){ + if(c==JB) return JNB|JMPCONV; + if(c==JNB) return JB|JMPCONV; + if(c==JMPA) return JMPA; + if(tojmpa) return JMPA; + if(p->op[0]->cc==0) + return JMPS; + else + return JMPR|JMPCONV; + }else{ + if(c==JB||c==JNB) + return c; + return JMPR; + } + } + /* choose between gpr,#imm3 and reg,#imm16 */ + if(mnemonics[c].operand_type[1]==OP_IMM3){ + if(!eval_expr(p->op[1]->offset,&val,sec,pc)||val<0||val>7){ + if(!strcmp(mnemonics[c].name,mnemonics[c+1].name)) + return c+1; + } + } + /* choose between gpr,#imm4 and reg,#imm16 */ + if(mnemonics[c].operand_type[1]==OP_IMM4){ + if(!eval_expr(p->op[1]->offset,&val,sec,pc)||val<0||val>7){ + if(!strcmp(mnemonics[c].name,mnemonics[c+1].name)) + return c+1; + } + } + return c; +} + +/* Convert an instruction into a DATA atom including relocations, + if necessary. */ +dblock *eval_instruction(instruction *p,section *sec,taddr pc) +{ + dblock *db=new_dblock(); + int opcode,c,jmpconv=0,osize; + unsigned long code; + char *d; + taddr val; + rlist *relocs=0; + operand *jmpaddr; + + c=translate(p,sec,pc); + if(c&JMPCONV){ jmpconv=1;c&=~JMPCONV;} + if((mnemonics[p->code].operand_type[0]==OP_GPR&&mnemonics[c].operand_type[0]==OP_SFR)|| + (mnemonics[p->code].operand_type[0]==OP_BGPR&&mnemonics[c].operand_type[0]==OP_BSFR)) + p->op[0]->offset=number_expr(p->op[0]->regsfr); + + + db->size=osize=mnemonics[c].ext.len*2; + if(jmpconv) db->size+=4; + db->data=mymalloc(db->size); + + opcode=mnemonics[c].ext.opcode; + switch(mnemonics[c].ext.encoding){ + case 0: + code=opcode<<16|(opcode>>8)<<8|opcode>>8; + break; + case 1: + code=opcode; + break; + case 2: + code=opcode|p->op[0]->reg<<4|p->op[1]->reg; + break; + case 3: + code=opcode|p->op[0]->reg|p->op[1]->reg<<4; + break; + case 4: + code=opcode|p->op[0]->reg<<4|p->op[1]->reg|8; + break; + case 5: + code=opcode|p->op[0]->reg<<4|p->op[1]->reg|12; + break; + case 6: + code=opcode|p->op[0]->reg<<4|absval(p->op[1]->offset,sec,pc,3); + break; + case 7: + /* fall through */ + case 8: + code=opcode<<16|absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,20,8)<<16|absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,16,16); + break; + case 9: + code=opcode|p->op[0]->reg|absval(p->op[1]->offset,sec,pc,4)<<4; + break; + case 10: +/* rfi: reorder bmov operands */ + code=opcode<<16| + absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,8,8)<<16| + absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,16,8)<<0| + absoffset(p->op[1]->boffset,0,sec,pc,&relocs,24,4)<<12| + absoffset(p->op[0]->boffset,0,sec,pc,&relocs,28,4)<<8; + break; + case 11: + code=opcode|absoffset(p->op[0]->boffset,0,sec,pc,&relocs,0,4)<<12|absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,8,8); + break; + case 12: + code=opcode<<16| + absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,8,8)<<16| + absoffset(p->op[2]->offset,p->op[2]->mod,sec,pc,&relocs,16,8)<<8| + absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,24,8); + break; + case 13: + code=opcode<<16| + absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,8,8)<<16| + absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,16,8)<<8| + absoffset(p->op[2]->offset,p->op[2]->mod,sec,pc,&relocs,24,8); + break; + case 14: + code=opcode<<16|p->op[0]->cc<<20|absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,16,16); + break; + case 15: + code=opcode|p->op[0]->cc<<4|p->op[1]->reg; + break; + case 16: + val=((reloffset(p->op[0]->offset,sec,pc)-2)>>1)&255; + code=opcode|val; + break; + case 17: + if(p->op[0]->type==OP_CC){ + /* jmp cc_uc was converted to jmps */ + code=opcode<<16|absoffset2(p->op[1]->offset,0,sec,pc,&relocs,8,8,0xffff0000)<<16|absoffset2(p->op[1]->offset,0,sec,pc,&relocs,16,16,0xffff); + }else{ + code=opcode<<16|absoffset2(p->op[0]->offset,0,sec,pc,&relocs,8,8,0xffff0000)<<16|absoffset2(p->op[0]->offset,0,sec,pc,&relocs,16,16,0xffff); + } + break; + case 18: + /* fall through */ + case 19: + code=opcode<<16|0xf<<20|p->op[0]->reg<<16|absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,16,16); + break; + case 20: + code=opcode|p->op[0]->reg<<4; + break; + case 21: + code=opcode|p->op[0]->reg<<4|p->op[0]->reg; + break; + case 22: + if(!jmpconv){ + val=((reloffset(p->op[1]->offset,sec,pc)-4)>>1)&255; + code=opcode<<16| + absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,8,8)<<16| + absoffset(p->op[0]->boffset,0,sec,pc,&relocs,24,4)<<12| + val; + }else{ + code=opcode<<16| + absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,8,8)<<16| + absoffset(p->op[0]->boffset,0,sec,pc,&relocs,24,4)<<12| + 2; + jmpaddr=p->op[1]; + } + break; + case 23: + if(!jmpconv){ + val=((reloffset(p->op[1]->offset,sec,pc)-2)>>1)&255; + code=opcode|p->op[0]->cc<<12|val; + }else{ + code=opcode|INVCC(p->op[0]->cc)<<12|2; + jmpaddr=p->op[1]; + } + break; + case 24: + code=opcode<<16|p->op[0]->reg<<20|p->op[1]->reg<<16|absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,16,16); + break; + case 25: + code=opcode<<16|p->op[0]->reg<<16|absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,16,16); + break; + case 26: + code=opcode|absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,8,8); + break; + case 27: + code=opcode|absval(p->op[0]->offset,sec,pc,7)<<1; + break; + case 28: + code=opcode<<16|p->op[0]->reg<<16|p->op[1]->reg<<20|absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,16,16); + break; + case 29: + code=opcode<<16|absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,8,8)<<16|absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,16,16); + break; + case 30: + code=opcode<<16|p->op[1]->reg<<16|absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,16,16); + break; + case 31: + code=opcode|((absval(p->op[0]->offset,sec,pc,2)-1)<<4); + break; + case 32: + code=opcode|p->op[0]->reg|((absval(p->op[1]->offset,sec,pc,2)-1)<<4); + break; + case 34: + code=opcode<<16|((absval(p->op[1]->offset,sec,pc,2)-1)<<20)|absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,16,8); + break; + case 33: + default: + ierror(mnemonics[c].ext.encoding); + } + + d=db->data; + if(osize==4){ + *d++=code>>24; + *d++=code>>16; + *d++=code; + *d++=code>>8; + }else{ + *d++=code>>8; + *d++=code; + } + if(jmpconv){ + *d++=0xfa; + *d++=absoffset2(jmpaddr->offset,0,sec,pc,&relocs,8+8*osize,8,0xffff0000); + val=absoffset2(jmpaddr->offset,0,sec,pc,&relocs,16+8*osize,16,0xffff); + *d++=val>>8; + *d++=val; + } + db->relocs=relocs; + return db; +} + +/* Create a dblock (with relocs, if necessary) for size bits of data. */ +dblock *eval_data(operand *op,size_t bitsize,section *sec,taddr pc) +{ + dblock *new=new_dblock(); + taddr val; + new->size=(bitsize+7)/8; + new->data=mymalloc(new->size); + if(op->type!=OP_ABS) + ierror(0); + if(bitsize!=8&&bitsize!=16&&bitsize!=32) + cpu_error(4); + val=absoffset(op->offset,op->mod,sec,pc,&new->relocs,0,bitsize); + if(bitsize==32){ + new->data[3]=val>>24; + new->data[2]=val>>16; + new->data[1]=val>>8; + new->data[0]=val; + }else if(bitsize==16){ + new->data[1]=val>>8; + new->data[0]=val; + }else + new->data[0]=val; + return new; +} + + +/* Calculate the size of the current instruction; must be identical + to the data created by eval_instruction. */ +size_t instruction_size(instruction *p,section *sec,taddr pc) +{ + int c=translate(p,sec,pc),add=0; + if(c&JMPCONV){ add=4;c&=~JMPCONV;} + return mnemonics[c].ext.len*2+add; +} + +operand *new_operand() +{ + operand *new=mymalloc(sizeof(*new)); + new->type=-1; + return new; +} + +/* return true, if initialization was successfull */ +int init_cpu() +{ + int i; + for(i=0;inext=first_sfr; + first_sfr=new; + } + new->flags=new->laddr=new->saddr=0; + new->boffset=0; + s=skip(s); + if(*s!=',') + cpu_error(0); + else + s=skip(s+1); + tree=parse_expr(&s); + simplify_expr(tree); + if(!tree||tree->type!=NUM) + cpu_error(0); + else + new->laddr=tree->c.val; + s=skip(s); + if(tree->c.val==0xfe||tree->c.val==0xf0){ + if(*s!=',') + cpu_error(0); + else + s=skip(s+1); + free_expr(tree); + tree=parse_expr(&s); + simplify_expr(tree); + if(!tree||tree->type!=NUM) + cpu_error(0); + else + new->saddr=tree->c.val; + free_expr(tree); + s=skip(s); + }else{ + if(tree->c.val>=0xfe00) + new->laddr=0xfe; + else + new->laddr=0xf0; + new->saddr=(tree->c.val-(new->laddr<<8))/2; + if((new->laddr<<8)+2*new->saddr!=tree->c.val) ierror(0); + free_expr(tree); + } + if(*s==','){ + s=skip(s+1); + tree=parse_expr(&s); + simplify_expr(tree); + new->boffset=tree->c.val; + new->flags|=ISBIT; + free_expr(tree); + } + return skip(s); + } + } + return merk; +} diff --git a/vasm/cpus/c16x/cpu.h b/vasm/cpus/c16x/cpu.h new file mode 100644 index 0000000..9b282dd --- /dev/null +++ b/vasm/cpus/c16x/cpu.h @@ -0,0 +1,88 @@ +/* cpu.h c16x/st10 cpu-description header-file */ +/* (c) in 2002 by Volker Barthelmann */ + + +/* maximum number of operands in one mnemonic */ +#define MAX_OPERANDS 3 + +/* maximum number of mnemonic-qualifiers per mnemonic */ +#define MAX_QUALIFIERS 0 + +/* maximum number of additional command-line-flags for this cpu */ + +/* data type to represent a target-address */ +typedef int32_t taddr; +typedef uint32_t utaddr; + +#define LITTLEENDIAN 1 +#define BIGENDIAN 0 +#define VASM_CPU_C16X 1 + +/* minimum instruction alignment */ +#define INST_ALIGN 2 + +/* default alignment for n-bit data */ +#define DATA_ALIGN(n) ((n)<=8?1:2) + +/* operand class for n-bit data definitions */ +#define DATA_OPERAND(n) OP_ABS + +#define cc reg + +/* type to store each operand */ +typedef struct { + int type; + int mod; + int reg,regsfr; /* also cc and boff */ + expr *offset,*boffset; +} operand; + +/* operand-types */ +#define OP_GPR 1 +#define OP_BGPR 2 +#define OP_SFR 3 +#define OP_BSFR 4 +#define OP_ABS 5 +#define OP_SEG OP_ABS +#define OP_BABS 6 +#define OP_REGDISP 7 +#define OP_REGIND 8 +#define OP_REG03IND 9 +#define OP_BWORD 10 +#define OP_BADDR 11 +#define OP_IMM2 12 +#define OP_IMM3 13 +#define OP_IMM4 14 +#define OP_IMM7 15 +#define OP_IMM8 16 +#define OP_IMM16 17 +#define OP_CC 18 +#define OP_REL 19 +#define OP_JADDR 20 +#define OP_POSTINC03 21 +#define OP_PREDEC03 22 +#define OP_POSTINC 23 +#define OP_PREDEC 24 +#define OP_PROTECTED 0 + +/* mod types */ +#define MOD_SOF 1 +#define MOD_SEG 2 +#define MOD_DPP0 3 +#define MOD_DPP1 4 +#define MOD_DPP2 5 +#define MOD_DPP3 6 +#define MOD_DPPX 7 + +#define CPU_C166 1 +#define CPU_C167 2 +#define CPU_ALL (-1) + +typedef struct { + unsigned int len; + unsigned int opcode; + unsigned int match; + unsigned int lose; + unsigned int encoding; + unsigned int available; +} mnemonic_extension; diff --git a/vasm/cpus/c16x/cpu_errors.h b/vasm/cpus/c16x/cpu_errors.h new file mode 100644 index 0000000..cffdaad --- /dev/null +++ b/vasm/cpus/c16x/cpu_errors.h @@ -0,0 +1,8 @@ + "illegal operand",ERROR, + "word register expected",ERROR, + "",ERROR, + "value does not find in %d bits",WARNING, + "data size not supported",ERROR, + "illegal use of SOF",WARNING, + "illegal use of SEG",WARNING, + "illegal use of DPP prefix",WARNING, diff --git a/vasm/cpus/c16x/opcodes.h b/vasm/cpus/c16x/opcodes.h new file mode 100644 index 0000000..5b546be --- /dev/null +++ b/vasm/cpus/c16x/opcodes.h @@ -0,0 +1,233 @@ +"add",OP_GPR,OP_GPR,0,1,0x0000,0x0000,0xff00,2,CPU_ALL, +"add",OP_GPR,OP_REG03IND,0,1,0x0800,0x0808,0xf704,4,CPU_ALL, +"add",OP_GPR,OP_POSTINC03,0,1,0x0800,0x080c,0xf700,5,CPU_ALL, +"add",OP_GPR,OP_IMM3,0,1,0x0800,0x0800,0xf708,6,CPU_ALL, +"add",OP_SFR,OP_IMM16,0,2,0x0600,0x0600,0xf900,7,CPU_ALL, +"add",OP_SFR,OP_ABS,0,2,0x0200,0x0200,0xfd00,8,CPU_ALL, +"add",OP_ABS,OP_SFR,0,2,0x0400,0x0400,0xfb00,29,CPU_ALL, +"addb",OP_BGPR,OP_BGPR,0,1,0x0100,0x0100,0xfe00,2,CPU_ALL, +"addb",OP_BGPR,OP_REG03IND,0,1,0x0900,0x0908,0xf604,4,CPU_ALL, +"addb",OP_BGPR,OP_POSTINC03,0,1,0x0900,0x090c,0xf600,5,CPU_ALL, +"addb",OP_BGPR,OP_IMM3,0,1,0x0900,0x0900,0xf608,6,CPU_ALL, +"addb",OP_BSFR,OP_IMM16,0,2,0x0700,0x0700,0xf800,7,CPU_ALL, +"addb",OP_BSFR,OP_BABS,0,2,0x0300,0x0300,0xfc00,8,CPU_ALL, +"addb",OP_BABS,OP_BSFR,0,2,0x0500,0x0500,0xfa00,29,CPU_ALL, +"addc",OP_GPR,OP_GPR,0,1,0x1000,0x1000,0xef00,2,CPU_ALL, +"addc",OP_GPR,OP_REG03IND,0,1,0x1800,0x1808,0xe704,4,CPU_ALL, +"addc",OP_GPR,OP_POSTINC03,0,1,0x1800,0x180c,0xe700,5,CPU_ALL, +"addc",OP_GPR,OP_IMM3,0,1,0x1800,0x1800,0xe708,6,CPU_ALL, +"addc",OP_SFR,OP_IMM16,0,2,0x1600,0x1600,0xe900,7,CPU_ALL, +"addc",OP_SFR,OP_ABS,0,2,0x1200,0x1200,0xed00,8,CPU_ALL, +"addc",OP_ABS,OP_SFR,0,2,0x1400,0x1400,0xeb00,29,CPU_ALL, +"addcb",OP_BGPR,OP_BGPR,0,1,0x1100,0x1100,0xee00,2,CPU_ALL, +"addcb",OP_BGPR,OP_REG03IND,0,1,0x1900,0x1908,0xe604,4,CPU_ALL, +"addcb",OP_BGPR,OP_POSTINC03,0,1,0x1900,0x190c,0xe600,5,CPU_ALL, +"addcb",OP_BGPR,OP_IMM3,0,1,0x1900,0x1900,0xe608,6,CPU_ALL, +"addcb",OP_BSFR,OP_IMM16,0,2,0x1700,0x1700,0xe800,7,CPU_ALL, +"addcb",OP_BSFR,OP_BABS,0,2,0x1300,0x1300,0xec00,8,CPU_ALL, +"addcb",OP_BABS,OP_BSFR,0,2,0x1500,0x1500,0xea00,29,CPU_ALL, +"and",OP_GPR,OP_GPR,0,1,0x6000,0x6000,0x9f00,2,CPU_ALL, +"and",OP_GPR,OP_REG03IND,0,1,0x6800,0x6808,0x9704,4,CPU_ALL, +"and",OP_GPR,OP_POSTINC03,0,1,0x6800,0x680c,0x9700,5,CPU_ALL, +"and",OP_GPR,OP_IMM3,0,1,0x6800,0x6800,0x9708,6,CPU_ALL, +"and",OP_SFR,OP_IMM16,0,2,0x6600,0x6600,0x9900,7,CPU_ALL, +"and",OP_SFR,OP_ABS,0,2,0x6200,0x6200,0x9d00,8,CPU_ALL, +"and",OP_ABS,OP_SFR,0,2,0x6400,0x6400,0x9b00,29,CPU_ALL, +"andb",OP_BGPR,OP_BGPR,0,1,0x6100,0x6100,0x9e00,2,CPU_ALL, +"andb",OP_BGPR,OP_REG03IND,0,1,0x6900,0x6908,0x9604,4,CPU_ALL, +"andb",OP_BGPR,OP_POSTINC03,0,1,0x6900,0x690c,0x9600,5,CPU_ALL, +"andb",OP_BGPR,OP_IMM3,0,1,0x6900,0x6900,0x9608,6,CPU_ALL, +"andb",OP_BSFR,OP_IMM16,0,2,0x6700,0x6700,0x9800,7,CPU_ALL, +"andb",OP_BSFR,OP_BABS,0,2,0x6300,0x6300,0x9c00,8,CPU_ALL, +"andb",OP_BABS,OP_BSFR,0,2,0x6500,0x6500,0x9a00,29,CPU_ALL, +"ashr",OP_GPR,OP_GPR,0,1,0xac00,0xac00,0x5300,2,CPU_ALL, +"ashr",OP_GPR,OP_IMM4,0,1,0xbc00,0xbc00,0x4300,9,CPU_ALL, +"atomic",OP_IMM3,0,0,1,0xd100,0xd100,0x2ecf,31,CPU_C167, +"band",OP_BADDR,OP_BADDR,0,2,0x6a00,0x6a00,0x9500,10,CPU_ALL, +"bclr",OP_BADDR,0,0,1,0x0e00,0x0e00,0x0100,11,CPU_ALL, +"bcmp",OP_BADDR,OP_BADDR,0,2,0x2a00,0x2a00,0xd500,10,CPU_ALL, +"bfldh",OP_BWORD,OP_IMM8,OP_IMM8,2,0x1a00,0x1a00,0xe500,12,CPU_ALL, +"bfldl",OP_BWORD,OP_IMM8,OP_IMM8,2,0x0a00,0x0a00,0xf500,13,CPU_ALL, +"bmov",OP_BADDR,OP_BADDR,0,2,0x4a00,0x4a00,0xb500,10,CPU_ALL, +"bmovn",OP_BADDR,OP_BADDR,0,2,0x3a00,0x3a00,0xc500,10,CPU_ALL, +"bor",OP_BADDR,OP_BADDR,0,2,0x5a00,0x5a00,0xa500,10,CPU_ALL, +"bset",OP_BADDR,0,0,1,0x0f00,0x0f00,0x0000,11,CPU_ALL, +"bxor",OP_BADDR,OP_BADDR,0,2,0x7a00,0x7a00,0x8500,10,CPU_ALL, +"call",OP_REL,0,0,1,0xbb00,0xbb00,0x4400,16,CPU_ALL, +"calla",OP_CC,OP_JADDR,0,2,0xca00,0xca00,0x350f,14,CPU_ALL, +"calli",OP_CC,OP_REGIND,0,1,0xab00,0xab00,0x5400,15,CPU_ALL, +"callr",OP_REL,0,0,1,0xbb00,0xbb00,0x4400,16,CPU_ALL, +"calls",OP_JADDR,0,0,2,0xda00,0xda00,0x2500,17,CPU_ALL, +"cmp",OP_GPR,OP_GPR,0,1,0x4000,0x4000,0xbf00,2,CPU_ALL, +"cmp",OP_GPR,OP_REG03IND,0,1,0x4800,0x4808,0xb704,4,CPU_ALL, +"cmp",OP_GPR,OP_POSTINC03,0,1,0x4800,0x480c,0xb700,5,CPU_ALL, +"cmp",OP_GPR,OP_IMM3,0,1,0x4800,0x4800,0xb708,6,CPU_ALL, +"cmp",OP_SFR,OP_IMM16,0,2,0x4600,0x4600,0xb900,7,CPU_ALL, +"cmp",OP_SFR,OP_ABS,0,2,0x4200,0x4200,0xbd00,8,CPU_ALL, +"cmpb",OP_BGPR,OP_BGPR,0,1,0x4100,0x4100,0xbe00,2,CPU_ALL, +"cmpb",OP_BGPR,OP_REG03IND,0,1,0x4900,0x4908,0xb604,4,CPU_ALL, +"cmpb",OP_BGPR,OP_POSTINC03,0,1,0x4900,0x490c,0xb600,5,CPU_ALL, +"cmpb",OP_BGPR,OP_IMM3,0,1,0x4900,0x4900,0xb608,6,CPU_ALL, +"cmpb",OP_BSFR,OP_IMM16,0,2,0x4700,0x4700,0xb800,7,CPU_ALL, +"cmpb",OP_BSFR,OP_BABS,0,2,0x4300,0x4300,0xbc00,8,CPU_ALL, +"cmpd1",OP_GPR,OP_IMM4,0,1,0xa000,0xa000,0x5f00,9,CPU_ALL, +"cmpd1",OP_GPR,OP_IMM16,0,2,0xa600,0xa6f0,0x5900,18,CPU_ALL, +"cmpd1",OP_GPR,OP_ABS,0,2,0xa200,0xa2f0,0x5d00,19,CPU_ALL, +"cmpd2",OP_GPR,OP_IMM4,0,1,0xb000,0xb000,0x4f00,9,CPU_ALL, +"cmpd2",OP_GPR,OP_IMM16,0,2,0xb600,0xb6f0,0x4900,18,CPU_ALL, +"cmpd2",OP_GPR,OP_ABS,0,2,0xb200,0xb2f0,0x4d00,19,CPU_ALL, +"cmpi1",OP_GPR,OP_IMM4,0,1,0x8000,0x8000,0x7f00,9,CPU_ALL, +"cmpi1",OP_GPR,OP_IMM16,0,2,0x8600,0x86f0,0x7900,18,CPU_ALL, +"cmpi1",OP_GPR,OP_ABS,0,2,0x8200,0x82f0,0x7d00,19,CPU_ALL, +"cmpi2",OP_GPR,OP_IMM4,0,1,0x9000,0x9000,0x6f00,9,CPU_ALL, +"cmpi2",OP_GPR,OP_IMM16,0,2,0x9600,0x96f0,0x6900,18,CPU_ALL, +"cmpi2",OP_GPR,OP_ABS,0,2,0x9200,0x92f0,0x6d00,19,CPU_ALL, +"cpl",OP_GPR,0,0,1,0x9100,0x9100,0x6e0f,20,CPU_ALL, +"cplb",OP_BGPR,0,0,1,0xb100,0xb100,0x4e0f,20,CPU_ALL, +"diswdt",OP_PROTECTED,0,0,2,0xa55a,0xa55a,0x5aa5,0,CPU_ALL, +"div",OP_GPR,0,0,1,0x4b00,0x4b00,0xb400,21,CPU_ALL, +"divl",OP_GPR,0,0,1,0x6b00,0x6b00,0x9400,21,CPU_ALL, +"divlu",OP_GPR,0,0,1,0x7b00,0x7b00,0x8400,21,CPU_ALL, +"divu",OP_GPR,0,0,1,0x5b00,0x5b00,0xa400,21,CPU_ALL, +"einit",OP_PROTECTED,0,0,2,0xb54a,0xb54a,0x4ab5,0,CPU_ALL, + +"extp",OP_GPR,OP_IMM3,0,1,0xdc40,0xdc40,0x2380,32,CPU_C167, +"extp",OP_IMM16,OP_IMM3,0,2,0xd740,0xd740,0x288f,33,CPU_C167, +"extr",OP_IMM3,0,0,1,0xd180,0xd180,0x2e4f,31,CPU_C167, +"extpr",OP_GPR,OP_IMM3,0,1,0xdcc0,0xdcc0,0x2300,32,CPU_C167, +"extpr",OP_IMM16,OP_IMM3,0,2,0xd7c0,0xd7c0,0x280f,33,CPU_C167, +"exts",OP_GPR,OP_IMM3,0,1,0xdc00,0xdc00,0x23c0,32,CPU_C167, +"exts",OP_IMM8,OP_IMM3,0,2,0xd700,0xd700,0x28cf,34,CPU_C167, +"extsr",OP_GPR,OP_IMM3,0,1,0xdc80,0xdc80,0x2340,32,CPU_C167, +"extsr",OP_IMM8,OP_IMM3,0,2,0xd780,0xd780,0x284f,34,CPU_C167, +"idle",OP_PROTECTED,0,0,2,0x8778,0x8778,0x7887,0,CPU_ALL, +"jb",OP_BADDR,OP_REL,0,2,0x8a00,0x8a00,0x7500,22,CPU_ALL, +"jbc",OP_BADDR,OP_REL,0,2,0xaa00,0xaa00,0x5500,22,CPU_ALL, +"jmp",OP_CC,OP_REL,0,1,0x0d00,0x0d00,0x0200,23,CPU_ALL, +"jmpa",OP_CC,OP_JADDR,0,2,0xea00,0xea00,0x150f,14,CPU_ALL, +"jmpi",OP_CC,OP_REGIND,0,1,0x9c00,0x9c00,0x6300,15,CPU_ALL, +"jmpr",OP_CC,OP_REL,0,1,0x0d00,0x0d00,0x0200,23,CPU_ALL, +"jmps",OP_JADDR,0,0,2,0xfa00,0xfa00,0x0500,17,CPU_ALL, +"jnb",OP_BADDR,OP_REL,0,2,0x9a00,0x9a00,0x6500,22,CPU_ALL, +"jnbs",OP_BADDR,OP_REL,0,2,0xba00,0xba00,0x4500,22,CPU_ALL, +"mov",OP_GPR,OP_GPR,0,1,0xf000,0xf000,0x0f00,2,CPU_ALL, +"mov",OP_GPR,OP_IMM4,0,1,0xe000,0xe000,0x1f00,9,CPU_ALL, +"mov",OP_SFR,OP_IMM16,0,2,0xe600,0xe600,0x1900,7,CPU_ALL, +"mov",OP_GPR,OP_REGIND,0,1,0xa800,0xa800,0x5700,2,CPU_ALL, +"mov",OP_GPR,OP_POSTINC,0,1,0x9800,0x9800,0x6700,2,CPU_ALL, +"mov",OP_REGIND,OP_GPR,0,1,0xb800,0xb800,0x4700,3,CPU_ALL, +"mov",OP_PREDEC,OP_GPR,0,1,0x8800,0x8800,0x7700,3,CPU_ALL, +"mov",OP_REGIND,OP_REGIND,0,1,0xc800,0xc800,0x3700,2,CPU_ALL, +"mov",OP_POSTINC,OP_REGIND,0,1,0xd800,0xd800,0x2700,2,CPU_ALL, +"mov",OP_REGIND,OP_POSTINC,0,1,0xe800,0xe800,0x1700,2,CPU_ALL, +"mov",OP_GPR,OP_REGDISP,0,2,0xd400,0xd400,0x2b00,24,CPU_ALL, +"mov",OP_REGDISP,OP_GPR,0,2,0xc400,0xc400,0x3b00,28,CPU_ALL, +"mov",OP_REGIND,OP_ABS,0,2,0x8400,0x8400,0x7bf0,25,CPU_ALL, +"mov",OP_ABS,OP_REGIND,0,2,0x9400,0x9400,0x6bf0,30,CPU_ALL, +"mov",OP_SFR,OP_ABS,0,2,0xf200,0xf200,0x0d00,8,CPU_ALL, +"mov",OP_ABS,OP_SFR,0,2,0xf600,0xf600,0x0900,29,CPU_ALL, +"movb",OP_BGPR,OP_BGPR,0,1,0xf100,0xf100,0x0e00,2,CPU_ALL, +"movb",OP_BGPR,OP_IMM4,0,1,0xe100,0xe100,0x1e00,9,CPU_ALL, +"movb",OP_BSFR,OP_IMM16,0,2,0xe700,0xe700,0x1800,7,CPU_ALL, +"movb",OP_BGPR,OP_REGIND,0,1,0xa900,0xa900,0x5600,2,CPU_ALL, +"movb",OP_BGPR,OP_POSTINC,0,1,0x9900,0x9900,0x6600,2,CPU_ALL, +"movb",OP_REGIND,OP_BGPR,0,1,0xb900,0xb900,0x4600,3,CPU_ALL, +"movb",OP_PREDEC,OP_BGPR,0,1,0x8900,0x8900,0x7600,3,CPU_ALL, +"movb",OP_REGIND,OP_REGIND,0,1,0xc900,0xc900,0x3600,2,CPU_ALL, +"movb",OP_POSTINC,OP_REGIND,0,1,0xd900,0xd900,0x2600,2,CPU_ALL, +"movb",OP_REGIND,OP_POSTINC,0,1,0xe900,0xe900,0x1600,2,CPU_ALL, +"movb",OP_BGPR,OP_REGDISP,0,2,0xf400,0xf400,0x0b00,24,CPU_ALL, +"movb",OP_REGDISP,OP_BGPR,0,2,0xe400,0xe400,0x1b00,28,CPU_ALL, +"movb",OP_REGIND,OP_BABS,0,2,0xa400,0xa400,0x5bf0,25,CPU_ALL, +"movb",OP_BABS,OP_REGIND,0,2,0xb400,0xb400,0x4bf0,30,CPU_ALL, +"movb",OP_BSFR,OP_BABS,0,2,0xf300,0xf300,0x0c00,8,CPU_ALL, +"movb",OP_BABS,OP_BSFR,0,2,0xf700,0xf700,0x0800,29,CPU_ALL, +"movbs",OP_GPR,OP_BGPR,0,1,0xd000,0xd000,0x2f00,3,CPU_ALL, +"movbs",OP_SFR,OP_BABS,0,2,0xd200,0xd200,0x2d00,8,CPU_ALL, +"movbs",OP_ABS,OP_BSFR,0,2,0xd500,0xd500,0x2a00,29,CPU_ALL, +"movbz",OP_GPR,OP_BGPR,0,1,0xc000,0xc000,0x3f00,3,CPU_ALL, +"movbz",OP_SFR,OP_BABS,0,2,0xc200,0xc200,0x3d00,8,CPU_ALL, +"movbz",OP_ABS,OP_BSFR,0,2,0xc500,0xc500,0x3a00,29,CPU_ALL, +"mul",OP_GPR,OP_GPR,0,1,0x0b00,0x0b00,0xf400,2,CPU_ALL, +"mulu",OP_GPR,OP_GPR,0,1,0x1b00,0x1b00,0xe400,2,CPU_ALL, +"neg",OP_GPR,0,0,1,0x8100,0x8100,0x7e0f,20,CPU_ALL, +"negb",OP_BGPR,0,0,1,0xa100,0xa100,0x5e0f,20,CPU_ALL, +"nop",0,0,0,1,0xcc00,0xcc00,0x33ff,1,CPU_ALL, +"or",OP_GPR,OP_GPR,0,1,0x7000,0x7000,0x8f00,2,CPU_ALL, +"or",OP_GPR,OP_REG03IND,0,1,0x7800,0x7808,0x8704,4,CPU_ALL, +"or",OP_GPR,OP_POSTINC03,0,1,0x7800,0x780c,0x8700,5,CPU_ALL, +"or",OP_GPR,OP_IMM3,0,1,0x7800,0x7800,0x8708,6,CPU_ALL, +"or",OP_SFR,OP_IMM16,0,2,0x7600,0x7600,0x8900,7,CPU_ALL, +"or",OP_SFR,OP_ABS,0,2,0x7200,0x7200,0x8d00,8,CPU_ALL, +"or",OP_ABS,OP_SFR,0,2,0x7400,0x7400,0x8b00,29,CPU_ALL, +"orb",OP_BGPR,OP_BGPR,0,1,0x7100,0x7100,0x8e00,2,CPU_ALL, +"orb",OP_BGPR,OP_REG03IND,0,1,0x7900,0x7908,0x8604,4,CPU_ALL, +"orb",OP_BGPR,OP_POSTINC03,0,1,0x7900,0x790c,0x8600,5,CPU_ALL, +"orb",OP_BGPR,OP_IMM3,0,1,0x7900,0x7900,0x8608,6,CPU_ALL, +"orb",OP_BSFR,OP_IMM16,0,2,0x7700,0x7700,0x8800,7,CPU_ALL, +"orb",OP_BSFR,OP_BABS,0,2,0x7300,0x7300,0x8c00,8,CPU_ALL, +"orb",OP_BABS,OP_BSFR,0,2,0x7500,0x7500,0x8a00,29,CPU_ALL, +"pcall",OP_SFR,OP_JADDR,0,2,0xe200,0xe200,0x1d00,8,CPU_ALL, +"pop",OP_SFR,0,0,1,0xfc00,0xfc00,0x0300,26,CPU_ALL, +"prior",OP_GPR,OP_GPR,0,1,0x2b00,0x2b00,0xd400,2,CPU_ALL, +"push",OP_SFR,0,0,1,0xec00,0xec00,0x1300,26,CPU_ALL, +"pwrdn",OP_PROTECTED,0,0,2,0x9768,0x9768,0x6897,0,CPU_ALL, +"ret",0,0,0,1,0xcb00,0xcb00,0x34ff,1,CPU_ALL, +"reti",0,0,0,1,0xfb88,0xfb88,0x0477,1,CPU_ALL, +"retp",OP_SFR,0,0,1,0xeb00,0xeb00,0x1400,26,CPU_ALL, +"rets",0,0,0,1,0xdb00,0xdb00,0x24ff,1,CPU_ALL, +"rol",OP_GPR,OP_GPR,0,1,0x0c00,0x0c00,0xf300,2,CPU_ALL, +"rol",OP_GPR,OP_IMM4,0,1,0x1c00,0x1c00,0xe300,9,CPU_ALL, +"ror",OP_GPR,OP_GPR,0,1,0x2c00,0x2c00,0xd300,2,CPU_ALL, +"ror",OP_GPR,OP_IMM4,0,1,0x3c00,0x3c00,0xc300,9,CPU_ALL, +"scxt",OP_SFR,OP_IMM16,0,2,0xc600,0xc600,0x3900,7,CPU_ALL, +"scxt",OP_SFR,OP_ABS,0,2,0xd600,0xd600,0x2900,8,CPU_ALL, +"shl",OP_GPR,OP_GPR,0,1,0x4c00,0x4c00,0xb300,2,CPU_ALL, +"shl",OP_GPR,OP_IMM4,0,1,0x5c00,0x5c00,0xa300,9,CPU_ALL, +"shr",OP_GPR,OP_GPR,0,1,0x6c00,0x6c00,0x9300,2,CPU_ALL, +"shr",OP_GPR,OP_IMM4,0,1,0x7c00,0x7c00,0x8300,9,CPU_ALL, +"srst",OP_PROTECTED,0,0,2,0xb748,0xb748,0x48b7,0,CPU_ALL, +"srvwdt",OP_PROTECTED,0,0,2,0xa758,0xa758,0x58a7,0,CPU_ALL, +"sub",OP_GPR,OP_GPR,0,1,0x2000,0x2000,0xdf00,2,CPU_ALL, +"sub",OP_GPR,OP_REG03IND,0,1,0x2800,0x2808,0xd704,4,CPU_ALL, +"sub",OP_GPR,OP_POSTINC03,0,1,0x2800,0x280c,0xd700,5,CPU_ALL, +"sub",OP_GPR,OP_IMM3,0,1,0x2800,0x2800,0xd708,6,CPU_ALL, +"sub",OP_SFR,OP_IMM16,0,2,0x2600,0x2600,0xd900,7,CPU_ALL, +"sub",OP_SFR,OP_ABS,0,2,0x2200,0x2200,0xdd00,8,CPU_ALL, +"sub",OP_ABS,OP_SFR,0,2,0x2400,0x2400,0xdb00,29,CPU_ALL, +"subb",OP_BGPR,OP_BGPR,0,1,0x2100,0x2100,0xde00,2,CPU_ALL, +"subb",OP_BGPR,OP_REG03IND,0,1,0x2900,0x2908,0xd604,4,CPU_ALL, +"subb",OP_BGPR,OP_POSTINC03,0,1,0x2900,0x290c,0xd600,5,CPU_ALL, +"subb",OP_BGPR,OP_IMM3,0,1,0x2900,0x2900,0xd608,6,CPU_ALL, +"subb",OP_BSFR,OP_IMM16,0,2,0x2700,0x2700,0xd800,7,CPU_ALL, +"subb",OP_BSFR,OP_BABS,0,2,0x2300,0x2300,0xdc00,8,CPU_ALL, +"subb",OP_BABS,OP_BSFR,0,2,0x2500,0x2500,0xda00,29,CPU_ALL, +"subc",OP_GPR,OP_GPR,0,1,0x3000,0x3000,0xcf00,2,CPU_ALL, +"subc",OP_GPR,OP_REG03IND,0,1,0x3800,0x3808,0xc704,4,CPU_ALL, +"subc",OP_GPR,OP_POSTINC03,0,1,0x3800,0x380c,0xc700,5,CPU_ALL, +"subc",OP_GPR,OP_IMM3,0,1,0x3800,0x3800,0xc708,6,CPU_ALL, +"subc",OP_SFR,OP_IMM16,0,2,0x3600,0x3600,0xc900,7,CPU_ALL, +"subc",OP_SFR,OP_ABS,0,2,0x3200,0x3200,0xcd00,8,CPU_ALL, +"subc",OP_ABS,OP_SFR,0,2,0x3400,0x3400,0xcb00,29,CPU_ALL, +"subcb",OP_BGPR,OP_BGPR,0,1,0x3100,0x3100,0xce00,2,CPU_ALL, +"subcb",OP_BGPR,OP_REG03IND,0,1,0x3900,0x3908,0xc604,4,CPU_ALL, +"subcb",OP_BGPR,OP_POSTINC03,0,1,0x3900,0x390c,0xc600,5,CPU_ALL, +"subcb",OP_BGPR,OP_IMM3,0,1,0x3900,0x3900,0xc608,6,CPU_ALL, +"subcb",OP_BSFR,OP_IMM16,0,2,0x3700,0x3700,0xc800,7,CPU_ALL, +"subcb",OP_BSFR,OP_BABS,0,2,0x3300,0x3300,0xcc00,8,CPU_ALL, +"subcb",OP_BABS,OP_BSFR,0,2,0x3500,0x3500,0xca00,29,CPU_ALL, +"trap",OP_IMM7,0,0,1,0x9b00,0x9b00,0x6401,27,CPU_ALL, +"xor",OP_GPR,OP_GPR,0,1,0x5000,0x5000,0xaf00,2,CPU_ALL, +"xor",OP_GPR,OP_REG03IND,0,1,0x5800,0x5808,0xa704,4,CPU_ALL, +"xor",OP_GPR,OP_POSTINC03,0,1,0x5800,0x580c,0xa700,5,CPU_ALL, +"xor",OP_GPR,OP_IMM3,0,1,0x5800,0x5800,0xa708,6,CPU_ALL, +"xor",OP_SFR,OP_IMM16,0,2,0x5600,0x5600,0xa900,7,CPU_ALL, +"xor",OP_SFR,OP_ABS,0,2,0x5200,0x5200,0xad00,8,CPU_ALL, +"xor",OP_ABS,OP_SFR,0,2,0x5400,0x5400,0xab00,29,CPU_ALL, +"xorb",OP_BGPR,OP_BGPR,0,1,0x5100,0x5100,0xae00,2,CPU_ALL, +"xorb",OP_BGPR,OP_REG03IND,0,1,0x5900,0x5908,0xa604,4,CPU_ALL, +"xorb",OP_BGPR,OP_POSTINC03,0,1,0x5900,0x590c,0xa600,5,CPU_ALL, +"xorb",OP_BGPR,OP_IMM3,0,1,0x5900,0x5900,0xa608,6,CPU_ALL, +"xorb",OP_BSFR,OP_IMM16,0,2,0x5700,0x5700,0xa800,7,CPU_ALL, +"xorb",OP_BSFR,OP_BABS,0,2,0x5300,0x5300,0xac00,8,CPU_ALL, +"xorb",OP_BABS,OP_BSFR,0,2,0x5500,0x5500,0xaa00,29,CPU_ALL, diff --git a/vasm/cpus/jagrisc/cpu.c b/vasm/cpus/jagrisc/cpu.c new file mode 100644 index 0000000..da0194d --- /dev/null +++ b/vasm/cpus/jagrisc/cpu.c @@ -0,0 +1,617 @@ +/* + * cpu.c Jaguar RISC cpu description file + * (c) in 2014-2017 by Frank Wille + */ + +#include "vasm.h" + +mnemonic mnemonics[] = { +#include "opcodes.h" +}; +int mnemonic_cnt = sizeof(mnemonics) / sizeof(mnemonics[0]); + +char *cpu_copyright = "vasm Jaguar RISC cpu backend 0.4c (c) 2014-2017 Frank Wille"; +char *cpuname = "jagrisc"; +int bitsperbyte = 8; +int bytespertaddr = 4; + +int jag_big_endian = 1; /* defaults to big-endian (Atari Jaguar 68000) */ + +static uint8_t cpu_type = GPU|DSP; +static int OC_MOVEI,OC_UNPACK; + +/* condition codes */ +static regsym cc_regsyms[] = { + {"T", RTYPE_CC, 0, 0x00}, + {"NE", RTYPE_CC, 0, 0x01}, + {"EQ", RTYPE_CC, 0, 0x02}, + {"CC", RTYPE_CC, 0, 0x04}, + {"HI", RTYPE_CC, 0, 0x05}, + {"CS", RTYPE_CC, 0, 0x08}, + {"PL", RTYPE_CC, 0, 0x14}, + {"MI", RTYPE_CC, 0, 0x18}, + {"t", RTYPE_CC, 0, 0x00}, + {"ne", RTYPE_CC, 0, 0x01}, + {"eq", RTYPE_CC, 0, 0x02}, + {"cc", RTYPE_CC, 0, 0x04}, + {"hi", RTYPE_CC, 0, 0x05}, + {"cs", RTYPE_CC, 0, 0x08}, + {"pl", RTYPE_CC, 0, 0x14}, + {"mi", RTYPE_CC, 0, 0x18}, + {NULL, 0, 0, 0} +}; + + +int init_cpu(void) +{ + int i; + regsym *r; + + for (i=0; ireg_name!=NULL; r++) + add_regsym(r); + + return 1; +} + + +int cpu_args(char *p) +{ + if (!strncmp(p,"-m",2)) { + p += 2; + if (!stricmp(p,"gpu") || !stricmp(p,"tom")) + cpu_type = GPU; + else if (!stricmp(p,"dsp") || !stricmp(p,"jerry")) + cpu_type = DSP; + else if (!strcmp(p,"any")) + cpu_type = GPU|DSP; + else + return 0; + } + else if (!strcmp(p,"-big")) + jag_big_endian = 1; + else if (!strcmp(p,"-little")) + jag_big_endian = 0; + else + return 0; + + return 1; +} + + +static int parse_reg(char **p) +{ + int reg = -1; + char *rp = skip(*p); + char *s; + + if (s = skip_identifier(rp)) { + regsym *sym = find_regsym(rp,s-rp); + + if (sym!=NULL && sym->reg_type==RTYPE_R) { + reg = sym->reg_num; + } + else if (toupper((unsigned char)*rp++) == 'R') { + if (sscanf(rp,"%d",®)!=1 || reg<0 || reg>31) + reg = -1; + } + + if (reg >= 0) + *p = s; + } + + return reg; +} + + +static expr *parse_cc(char **p) +{ + char *end; + + *p = skip(*p); + + if (end = skip_identifier(*p)) { + regsym *sym = find_regsym(*p,end-*p); + + if (sym!=NULL && sym->reg_type==RTYPE_CC) { + *p = end; + return number_expr((taddr)sym->reg_num); + } + } + + /* otherwise the condition code is any expression */ + return parse_expr(p); +} + + +static void jagswap32(char *d,int32_t w) +/* write a 32-bit word with swapped halfs (Jaguar MOVEI) */ +{ + if (jag_big_endian) { + *d++ = (w >> 8) & 0xff; + *d++ = w & 0xff; + *d++ = (w >> 24) & 0xff; + *d = (w >> 16) & 0xff; + } + else { + /* @@@ Need to verify this! */ + *d++ = w & 0xff; + *d++ = (w >> 8) & 0xff; + *d++ = (w >> 16) & 0xff; + *d = (w >> 24) & 0xff; + } +} + + +char *parse_cpu_special(char *start) +/* parse cpu-specific directives; return pointer to end of cpu-specific text */ +{ + char *name=start; + char *s; + + if (s = skip_identifier(name)) { + /* Atari MadMac compatibility directives */ + if (*name == '.') /* ignore leading dot */ + name++; + + if (s-name==3 && !strnicmp(name,"dsp",3)) { + cpu_type = DSP; + eol(s); + return skip_line(s); + } + + else if (s-name==3 && !strnicmp(name,"gpu",3)) { + cpu_type = GPU; + eol(s); + return skip_line(s); + } + + else if (s-name==8 && !strnicmp(name,"regundef",8) || + s-name==9 && !strnicmp(name,"equrundef",9)) { + /* undefine a register symbol */ + s = skip(s); + if (name = parse_identifier(&s)) { + undef_regsym(name,0,RTYPE_R); + myfree(name); + eol(s); + return skip_line(s); + } + } + + else if (s-name==7 && !strnicmp(name,"ccundef",7)) { + /* undefine a condition code symbol */ + s = skip(s); + if (name = parse_identifier(&s)) { + undef_regsym(name,0,RTYPE_CC); + myfree(name); + eol(s); + return skip_line(s); + } + } + } + + return start; +} + + +int parse_cpu_label(char *labname,char **start) +/* parse cpu-specific directives following a label field, + return zero when no valid directive was recognized */ +{ + char *dir=*start; + char *s,*name; + hashdata data; + + if (*dir == '.') /* ignore leading dot */ + dir++; + + if (s = skip_identifier(dir)) { + + if (s-dir==6 && !strnicmp(dir,"regequ",6) || + s-dir==4 && !strnicmp(dir,"equr",4)) { + /* label REGEQU Rn || label EQUR Rn */ + int r; + + if ((r = parse_reg(&s)) >= 0) + new_regsym(0,0,labname,RTYPE_R,0,r); + else + cpu_error(3); /* register expected */ + eol(s); + *start = skip_line(s); + return 1; + } + + else if (s-dir==5 && !strnicmp(dir,"ccdef",5)) { + /* label CCDEF expr */ + expr *ccexp; + taddr val; + + if ((ccexp = parse_cc(&s)) != NULL) { + if (eval_expr(ccexp,&val,NULL,0)) + new_regsym(0,0,labname,RTYPE_CC,0,(int)val); + else + general_error(30); /* expression must be a constant */ + } + else + general_error(9); /* @@@ */ + eol(s); + *start = skip_line(s); + return 1; + } + } + + return 0; +} + + +operand *new_operand(void) +{ + operand *new = mymalloc(sizeof(*new)); + + new->type = NO_OP; + return new; +} + + +int jag_data_operand(int bits) +/* return data operand type for these number of bits */ +{ + if (bits & OPSZ_SWAP) + return DATAI_OP; + return bits==64 ? DATA64_OP : DATA_OP; +} + + +int parse_operand(char *p, int len, operand *op, int required) +{ + int reg; + + switch (required) { + case IMM0: + case IMM1: + case IMM1S: + case SIMM: + case IMMLW: + if (*p == '#') + p = skip(p+1); /* skip optional '#' */ + case REL: + case DATA_OP: + case DATAI_OP: + if (required == IMM1S) { + op->val = make_expr(SUB,number_expr(32),parse_expr(&p)); + required = IMM1; /* turn into IMM1 32-val for SHLQ */ + } + else + op->val = parse_expr(&p); + break; + + case DATA64_OP: + op->val = parse_expr_huge(&p); + break; + + case REG: /* Rn */ + op->reg = parse_reg(&p); + if (op->reg < 0) + return PO_NOMATCH; + break; + + case IREG: /* (Rn) */ + if (*p++ != '(') + return PO_NOMATCH; + op->reg = parse_reg(&p); + if (op->reg < 0) + return PO_NOMATCH; + if (*p != ')') + return PO_NOMATCH; + break; + + case IR14D: /* (R14+d) */ + case IR15D: /* (R15+d) */ + if (*p++ != '(') + return PO_NOMATCH; + reg = parse_reg(&p); + if ((required==IR14D && reg!=14) || (required==IR15D && reg!=15)) + return PO_NOMATCH; + if (*p++ != '+') + return PO_NOMATCH; + p = skip(p); + op->val = parse_expr(&p); + p = skip(p); + if (*p != ')') + return PO_NOMATCH; + break; + + case IR14R: /* (R14+Rn) */ + case IR15R: /* (R15+Rn) */ + if (*p++ != '(') + return PO_NOMATCH; + reg = parse_reg(&p); + if ((required==IR14R && reg!=14) || (required==IR15R && reg!=15)) + return PO_NOMATCH; + if (*p++ != '+') + return PO_NOMATCH; + op->reg = parse_reg(&p); + if (op->reg < 0) + return PO_NOMATCH; + if (*p != ')') + return PO_NOMATCH; + break; + + case CC: /* condition code: t, eq, ne, mi, pl, cc, cs, ... */ + op->val = parse_cc(&p); + break; + + case PC: /* PC register */ + if (toupper((unsigned char)*p) != 'P' || + toupper((unsigned char)*(p+1)) != 'C' || + ISIDCHAR(*(p+2))) + return PO_NOMATCH; + break; + + default: + return PO_NOMATCH; + } + + op->type = required; + return PO_MATCH; +} + + +static int32_t eval_oper(instruction *ip,operand *op,section *sec, + taddr pc,dblock *db) +{ + symbol *base = NULL; + int optype = op->type; + int btype; + taddr val,loval,hival,mask; + + switch (optype) { + case PC: + return 0; + + case REG: + case IREG: + case IR14R: + case IR15R: + return op->reg; + + case IMM0: + case IMM1: + case SIMM: + case IMMLW: + case IR14D: + case IR15D: + case REL: + case CC: + mask = 0x1f; + if (!eval_expr(op->val,&val,sec,pc)) + btype = find_base(op->val,&base,sec,pc); + + if (optype==IMM0 || optype==CC || optype==IMM1 || optype==SIMM) { + if (base != NULL) { + loval = -32; + hival = 32; + if (btype != BASE_ILLEGAL) { + if (db) { + add_extnreloc_masked(&db->relocs,base,val, + btype==BASE_PCREL?REL_PC:REL_ABS, + jag_big_endian?6:5,5,0,0x1f); + base = NULL; + } + } + } + else if (optype==IMM1) { + loval = 1; + hival = 32; + } + else if (optype==SIMM) { + loval = -16; + hival = 15; + } + else { + loval = 0; + hival = 31; + } + } + else if (optype==IR14D || optype==IR15D) { + if (base==NULL && val==0) { + /* Optimize (Rn+0) to (Rn). Assume that the "load/store (Rn+d)" + instructions follow directly after "load/store (Rn)". */ + ip->code -= optype==IR14D ? 1 : 2; + op->type = IREG; + op->reg = optype==IR14D ? 14 : 15; + return op->reg; + } + loval = 1; + hival = 32; + } + else if (optype==IMMLW) { + mask = ~0; + if (base != NULL) { + if (btype != BASE_ILLEGAL) { + if (db) { + /* two relocations for LSW first, then MSW */ + add_extnreloc_masked(&db->relocs,base,val, + btype==BASE_PCREL?REL_PC:REL_ABS, + 0,16,2,0xffff); + add_extnreloc_masked(&db->relocs,base,val, + btype==BASE_PCREL?REL_PC:REL_ABS, + 16,16,2,0xffff0000); + base = NULL; + } + } + } + } + else if (optype==REL) { + loval = -16; + hival = 15; + if (base!=NULL && btype==BASE_OK) { + if (is_pc_reloc(base,sec)) { + /* external label or from a different section (distance / 2) */ + add_extnreloc_masked(&db->relocs,base,val-2,REL_PC, + jag_big_endian?6:5,5,0,0x3e); + } + else if (LOCREF(base)) { + /* known label from the same section doesn't need a reloc */ + val = (val - (pc + 2)) / 2; + } + base = NULL; + } + } + else ierror(0); + + if (base != NULL) + general_error(38); /* bad or unhandled reloc: illegal relocation */ + + /* range check for this addressing mode */ + if (mask!=~0 && (valhival)) + cpu_error(1,(long)loval,(long)hival); + return val & mask; + + default: + ierror(0); + break; + } + + return 0; /* default */ +} + + +size_t instruction_size(instruction *ip, section *sec, taddr pc) +{ + return ip->code==OC_MOVEI ? 6 : 2; +} + + +dblock *eval_instruction(instruction *ip, section *sec, taddr pc) +{ + dblock *db = new_dblock(); + int32_t src=0,dst=0,extra; + int size = 2; + uint16_t inst; + + /* get source and destination argument, when present */ + if (ip->op[0]) + dst = eval_oper(ip,ip->op[0],sec,pc,db); + if (ip->op[1]) { + if (ip->code == OC_MOVEI) { + extra = dst; + size = 6; + } + else + src = dst; + dst = eval_oper(ip,ip->op[1],sec,pc,db); + } + else if (ip->code == OC_UNPACK) + src = 1; /* pack(src=0)/unpack(src=1) use the same opcode */ + + /* store and jump instructions need the second operand in the source field */ + if (mnemonics[ip->code].ext.flags & OPSWAP) { + extra = src; + src = dst; + dst = extra; + } + + /* allocate dblock data for instruction */ + db->size = size; + db->data = mymalloc(size); + + /* construct the instruction word out of opcode and source/dest. value */ + inst = (mnemonics[ip->code].ext.opcode & 63) << 10; + inst |= ((src&31) << 5) | (dst & 31); + + /* write instruction */ + if (jag_big_endian) { + db->data[0] = (inst >> 8) & 0xff; + db->data[1] = inst & 0xff; + } + else { + db->data[0] = inst & 0xff; + db->data[1] = (inst >> 8) & 0xff; + } + + /* extra words for MOVEI are always written in the order lo-word, hi-word */ + if (size == 6) + jagswap32(&db->data[2],extra); + + return db; +} + + +dblock *eval_data(operand *op, size_t bitsize, section *sec, taddr pc) +{ + dblock *db = new_dblock(); + taddr val; + + if (bitsize!=8 && bitsize!=16 && bitsize!=32 && bitsize!=64) + cpu_error(0,bitsize); /* data size not supported */ + + if (op->type!=DATA_OP && op->type!=DATA64_OP && op->type!=DATAI_OP) + ierror(0); + + db->size = bitsize >> 3; + db->data = mymalloc(db->size); + + if (op->type == DATA64_OP) { + thuge hval; + + if (!eval_expr_huge(op->val,&hval)) + general_error(59); /* cannot evaluate huge integer */ + huge_to_mem(jag_big_endian,db->data,db->size,hval); + } + else { + if (!eval_expr(op->val,&val,sec,pc)) { + symbol *base; + int btype; + + btype = find_base(op->val,&base,sec,pc); + if (base!=NULL && btype!=BASE_ILLEGAL) { + if (op->type == DATAI_OP) { + /* swapped: two relocations for LSW first, then MSW */ + add_extnreloc_masked(&db->relocs,base,val, + btype==BASE_PCREL?REL_PC:REL_ABS, + 0,16,0,0xffff); + add_extnreloc_masked(&db->relocs,base,val, + btype==BASE_PCREL?REL_PC:REL_ABS, + 16,16,0,0xffff0000); + } + else /* normal 8, 16, 32 bit relocation */ + add_extnreloc(&db->relocs,base,val, + btype==BASE_PCREL?REL_PC:REL_ABS,0,bitsize,0); + } + else if (btype != BASE_NONE) + general_error(38); /* illegal relocation */ + } + + switch (db->size) { + case 1: + db->data[0] = val & 0xff; + break; + case 2: + case 4: + if (op->type == DATAI_OP) + jagswap32(db->data,(int32_t)val); + else + setval(jag_big_endian,db->data,db->size,val); + break; + default: + ierror(0); + break; + } + } + + return db; +} + + +int cpu_available(int idx) +{ + return (mnemonics[idx].ext.flags & cpu_type) != 0; +} diff --git a/vasm/cpus/jagrisc/cpu.h b/vasm/cpus/jagrisc/cpu.h new file mode 100644 index 0000000..27d086e --- /dev/null +++ b/vasm/cpus/jagrisc/cpu.h @@ -0,0 +1,86 @@ +/* +** cpu.h Jaguar RISC cpu-description header-file +** (c) in 2014-2017 by Frank Wille +*/ + +extern int jag_big_endian; +#define BIGENDIAN (jag_big_endian) +#define LITTLEENDIAN (!jag_big_endian) +#define VASM_CPU_JAGRISC 1 + +/* maximum number of operands for one mnemonic */ +#define MAX_OPERANDS 2 + +/* maximum number of mnemonic-qualifiers per mnemonic */ +#define MAX_QUALIFIERS 0 + +/* data type to represent a target-address */ +typedef int32_t taddr; +typedef uint32_t utaddr; + +/* minimum instruction alignment */ +#define INST_ALIGN 2 + +/* default alignment for n-bit data */ +#define DATA_ALIGN(n) ((n<=8)?1:2) + +/* operand class for n-bit data definitions */ +int jag_data_operand(int); +#define DATA_OPERAND(n) jag_data_operand(n) + +/* returns true when instruction is valid for selected cpu */ +#define MNEMONIC_VALID(i) cpu_available(i) + +/* type to store each operand */ +typedef struct { + uint8_t type; + int8_t reg; + expr *val; +} operand; + +/* operand types */ +enum { + NO_OP=0, + DATA_OP, + DATA64_OP, + DATAI_OP, /* 32-bit with swapped halfwords */ + REG, /* register Rn */ + IMM0, /* 5-bit immediate expression (0-31) */ + IMM1, /* 5-bit immediate expression (1-32) */ + IMM1S, /* 5-bit immediate expression 32-(1-32) for SHLQ */ + SIMM, /* 5-bit signed immediate expression (-16 - 15) */ + IMMLW, /* 32-bit immediate expression in extra longword */ + IREG, /* register indirect (Rn) */ + IR14D, /* register R14 plus displacement indirect (R14+n) */ + IR15D, /* register R15 plus displacement indirect (R15+n) */ + IR14R, /* register R14 plus register Rn indirect (R14+Rn) */ + IR15R, /* register R15 plus register Rn indirect (R15+Rn) */ + CC, /* condition code, t, cc, cs, eq, ne, mi, pl, hi */ + REL, /* relative branch, PC + 2 + (-16..15) words */ + PC /* PC register */ +}; + +/* additional mnemonic data */ +typedef struct { + uint8_t opcode; + uint8_t flags; +} mnemonic_extension; + +/* Values defined for the 'flags' field of mnemonic_extension. */ +#define GPU 1 +#define DSP 2 +#define ANY GPU|DSP + +#define OPSWAP 128 /* swapped operands in instruction word encoding */ + +/* Register symbols */ +#define HAVE_REGSYMS +#define REGSYMHTSIZE 64 +#define RTYPE_R 0 /* R0-R31 */ +#define RTYPE_CC 1 /* condition codes (0-31) */ + +/* Prototypes */ +int cpu_available(int); + +int parse_cpu_label(char *,char **); +#define PARSE_CPU_LABEL(l,s) parse_cpu_label(l,s) diff --git a/vasm/cpus/jagrisc/cpu_errors.h b/vasm/cpus/jagrisc/cpu_errors.h new file mode 100644 index 0000000..68db758 --- /dev/null +++ b/vasm/cpus/jagrisc/cpu_errors.h @@ -0,0 +1,3 @@ + "data size %d not supported",ERROR, + "value from %ld to %ld required",ERROR, + "register expected",ERROR, diff --git a/vasm/cpus/jagrisc/opcodes.h b/vasm/cpus/jagrisc/opcodes.h new file mode 100644 index 0000000..0995abb --- /dev/null +++ b/vasm/cpus/jagrisc/opcodes.h @@ -0,0 +1,72 @@ + "abs", { REG }, { 22, ANY }, + "add", { REG, REG }, { 0, ANY }, + "addc", { REG, REG }, { 1, ANY }, + "addq", { IMM1, REG }, { 2, ANY }, + "addqmod", { IMM1, REG }, { 63, DSP }, + "addqt", { IMM1, REG }, { 3, ANY }, + "and", { REG, REG }, { 9, ANY }, + "bclr", { IMM0, REG }, { 15, ANY }, + "bset", { IMM0, REG }, { 14, ANY }, + "btst", { IMM0, REG }, { 13, ANY }, + "cmp", { REG, REG }, { 30, ANY }, + "cmpq", { SIMM, REG }, { 31, ANY }, + "div", { REG, REG }, { 21, ANY }, + "imacn", { REG, REG }, { 20, ANY }, + "imult", { REG, REG }, { 17, ANY }, + "imultn", { REG, REG }, { 18, ANY }, + "jr", { REL }, { 53, ANY|OPSWAP }, + "jr", { CC, REL }, { 53, ANY|OPSWAP }, + "jump", { IREG }, { 52, ANY|OPSWAP }, + "jump", { CC, IREG }, { 52, ANY|OPSWAP }, + "load", { IREG, REG }, { 41, ANY }, + "load", { IR14R, REG }, { 58, ANY }, + "load", { IR15R, REG }, { 59, ANY }, + "load", { IR14D, REG }, { 43, ANY }, + "load", { IR15D, REG }, { 44, ANY }, + "loadb", { IREG, REG }, { 39, ANY }, + "loadp", { IREG, REG }, { 42, GPU }, + "loadw", { IREG, REG }, { 40, ANY }, + "mirror", { REG, IREG }, { 48, DSP|OPSWAP }, + "mmult", { REG, REG }, { 54, ANY }, + "move", { REG, REG }, { 34, ANY }, + "move", { PC, REG }, { 51, ANY }, + "movefa", { REG, REG }, { 37, ANY }, + "movei", { IMMLW, REG }, { 38, ANY }, + "moveq", { IMM0, REG }, { 35, ANY }, + "moveta", { REG, REG }, { 36, ANY }, + "mtoi", { REG, REG }, { 55, ANY }, + "mult", { REG, REG }, { 16, ANY }, + "neg", { REG }, { 8, ANY }, + "nop", { NO_OP }, { 57, ANY }, + "normi", { REG, REG }, { 56, ANY }, + "not", { REG }, { 12, ANY }, + "or", { REG, REG }, { 10, ANY }, + "pack", { REG }, { 63, GPU }, + "resmac", { REG }, { 19, ANY }, + "ror", { REG, REG }, { 28, ANY }, + "rorq", { IMM1, REG }, { 29, ANY }, + "sat8", { REG }, { 32, GPU }, + "sat16", { REG }, { 33, GPU }, + "sat16s", { REG }, { 33, DSP }, + "sat24", { REG }, { 62, GPU }, + "sat32s", { REG }, { 42, DSP }, + "sh", { REG, REG }, { 23, ANY }, + "sha", { REG, REG }, { 26, ANY }, + "sharq", { IMM1, REG }, { 27, ANY }, + "shlq", { IMM1S, REG }, { 24, ANY }, + "shrq", { IMM1, REG }, { 25, ANY }, + "store", { REG, IREG }, { 47, ANY|OPSWAP }, + "store", { REG, IR14R }, { 60, ANY|OPSWAP }, + "store", { REG, IR15R }, { 61, ANY|OPSWAP }, + "store", { REG, IR14D }, { 49, ANY|OPSWAP }, + "store", { REG, IR15D }, { 50, ANY|OPSWAP }, + "storeb", { REG, IREG }, { 45, ANY|OPSWAP }, + "storep", { REG, IREG }, { 48, GPU|OPSWAP }, + "storew", { REG, IREG }, { 46, ANY|OPSWAP }, + "sub", { REG, REG }, { 4, ANY }, + "subc", { REG, REG }, { 5, ANY }, + "subq", { IMM1, REG }, { 6, ANY }, + "subqmod", { IMM1, REG }, { 32, DSP }, + "subqt", { IMM1, REG }, { 7, ANY }, + "unpack", { REG }, { 63, GPU }, + "xor", { REG, REG }, { 11, ANY }, diff --git a/vasm/cpus/m68k/cpu.c b/vasm/cpus/m68k/cpu.c new file mode 100644 index 0000000..1714736 --- /dev/null +++ b/vasm/cpus/m68k/cpu.c @@ -0,0 +1,5819 @@ +/* +** cpu.c Motorola M68k, CPU32 and ColdFire cpu-description file +** (c) in 2002-2019 by Frank Wille +*/ + +#include +#include "vasm.h" +#include "error.h" + +#include "operands.h" + +struct specreg SpecRegs[] = { +#include "specregs.h" +}; +static int specreg_cnt = sizeof(SpecRegs)/sizeof(SpecRegs[0]); + +mnemonic mnemonics[] = { +#include "opcodes.h" +}; +int mnemonic_cnt = sizeof(mnemonics)/sizeof(mnemonics[0]); + +struct cpu_models models[] = { +#include "cpu_models.h" +}; +int model_cnt = sizeof(models)/sizeof(models[0]); + + +char *cpu_copyright="vasm M68k/CPU32/ColdFire cpu backend 2.3f (c) 2002-2019 Frank Wille"; +char *cpuname = "M68k"; +int bitsperbyte = 8; +int bytespertaddr = 4; + +int m68k_mid = 1; /* default a.out MID: 68000/68010 */ + +static uint32_t cpu_type = m68000; +static expr *baseexp[7]; /* basereg: expression loaded to reg. */ +static signed char sdreg = -1; /* current small-data base register */ +static signed char last_sdreg = -1; +static unsigned char gas = 0; /* true enables GNU-as mnemonics */ +static unsigned char sgs = 0; /* true enables & as immediate prefix */ +static unsigned char no_fpu = 0; /* true: FPU code/direct. disallowed */ +static unsigned char elfregs = 0; /* true: %Rn instead of Rn reg. names */ +static unsigned char fpu_id = 1; /* default coprocessor id for FPU */ +static unsigned char opt_gen = 1; /* generic optimizations (not Devpac) */ +static unsigned char opt_movem = 0; /* MOVEM Rn -> MOVE Rn */ +static unsigned char opt_pea = 0; /* MOVE.L #x,-(sp) -> PEA x */ +static unsigned char opt_clr = 0; /* MOVE #0, -> CLR */ +static unsigned char opt_st = 0; /* MOVE.B #-1, -> ST */ +static unsigned char opt_lsl = 0; /* LSL #1,Dn -> ADD Dn,Dn */ +static unsigned char opt_mul = 0; /* MULU/MULS #n,Dn -> LSL/ASL #n,Dn */ +static unsigned char opt_div = 0; /* DIVU/DIVS.L #n,Dn -> LSR/ASR #n,Dn */ +static unsigned char opt_fconst = 1; /* Fxxx.D #m,FPn -> Fxxx.S #m,FPn */ +static unsigned char opt_brajmp = 0; /* branch to different sect. into jump */ +static unsigned char opt_pc = 1; /*