/* * Main assembly code for rasm */ #define _REENTRANT #include #include #include #include #include #include "rasm.h" #include "symtab.h" #include "util.h" #include "output.h" int current_line; unsigned char data[MEMSIZE]; int data_size; void compile_error(char *msg) { fprintf(stderr, "Compiler error at line %d: %s\n", current_line, msg); exit(1); } int compile_to_memory(char *infile, unsigned char *data, int pass) { char line[BUFSIZE]; int fd, addr = 0; FILE *in; fd = open(infile, O_RDONLY); if (fd < 0) { perror("opening input file"); exit(1); } in = fdopen(fd, "r"); if (in == NULL) { perror("opening input file stream"); exit(1); } clear_hole_list(); current_line = 0; while (fgets(line, sizeof(line), in)) { char *ptr = line; char *pos; char *lasts; ++current_line; /* Remove any comments from the line */ pos = strchr(ptr, ';'); if (pos) *pos = '\0'; /* Remove leading whitespace */ while (*ptr == ' ' || *ptr == '\t') ++ptr; /* Remove trailing whitespace / newlines */ while (1) { int len = strlen(ptr); if (len == 0) break; pos = ptr + len - 1; if (*pos == ' ' || *pos == '\t' || *pos == '\r' || *pos == '\n') *pos = '\0'; else break; } /* If there's a "foo:" at the beginning, take that to be a * symbol and put it in the symbol table, if we're in pass 1. */ pos = strchr(ptr, ':'); if (pos) { char *space; char *symbol; space = strchr(ptr, ' '); if (space && space < pos) pos = 0; space = strchr(ptr, '\t'); if (space && space < pos) pos = 0; if (pos != 0) { *pos = '\0'; symbol = ptr; ptr = pos+1; while (*ptr && (*ptr == ' ' || *ptr == '\t')) ptr++; if (pass == 1) symtab_store(symbol, addr); } } /* Now strtok_r() the rest of the line -- space tab and , are * all valid separators. */ ptr = strtok_r(ptr, OP_SEPARATORS, &lasts); if (ptr == NULL || *ptr == 0) { /* Blank line? */ continue; } else if (0 == strcasecmp(ptr, "org")) { char *arg = strtok_r(0, OP_SEPARATORS, &lasts); int new_addr; if (!is_number(arg)) compile_error("org argument must be a const"); new_addr = str_to_int(arg); if (new_addr < addr) compile_error("cannot jump back with org"); if (new_addr > addr) { add_hole(addr, new_addr-1); addr = new_addr; } } else if (0 == strcasecmp(ptr, "dw")) { char *arg = strtok_r(0, OP_SEPARATORS, &lasts); reftype arg_type = gettype(arg); int arg_value = getvalue(arg); if (pass == 1 || REF_DIRECT == arg_type) { unsigned char b; b = (arg_value & 0xff00) >> 8; data[addr++] = b; b = (arg_value & 0x00ff); data[addr++] = b; } else { printf("argtype = %d\n", arg_type); compile_error("unknown argument type to dw"); } } else if (0 == strcasecmp(ptr, "db")) { while (1) { char *arg; char lasts_buf[BUFSIZE]; char *lasts_orig = lasts; if (lasts) strncpy(lasts_buf, lasts, sizeof(lasts_buf)); arg = strtok_r(0, OP_SEPARATORS, &lasts); if (0 == arg) { break; } else if (*arg == '"') { char *endptr, *nlasts; nlasts = strchr(lasts_buf, '"') + 1; endptr = strchr(nlasts, '"'); lasts = lasts_orig + (endptr - nlasts) + 3; if (0 == endptr) compile_error("missing closing quote"); while (nlasts < endptr) data[addr++] = *(nlasts++); endptr++; } else if (is_number(arg)) { int value = str_to_int(arg); data[addr++] = value; } else { printf("arg = %s\n", arg); compile_error("unknown const in db statement"); } } } else if (0 == strcasecmp(ptr, "subb") || 0 == strcasecmp(ptr, "add") || 0 == strcasecmp(ptr, "addc")) { char *arg1 = strtok_r(0, OP_SEPARATORS, &lasts); char *src = strtok_r(0, OP_SEPARATORS, &lasts); reftype src_type = gettype(src); int src_value = getvalue(src); int opbase = (0 == strcasecmp(ptr, "subb")) ? 0x90 : (0 == strcasecmp(ptr, "add")) ? 0x20 : (0 == strcasecmp(ptr, "addc")) ? 0x30 : /* really should NEVER happen */ 0xff; if (opbase == 0xff) compile_error("wtf?"); if (0 != strcasecmp(arg1, "a")) compile_error("add/subb must use a"); if (src_type == REF_DIRECT) { data[addr++] = opbase | 0x05; data[addr++] = src_value; } else if (src_type == REF_INDIR && (src_value == 0 || src_value == 1)) { data[addr++] = opbase | 0x06 | src_value; } else if (src_type == REF_IMMED) { data[addr++] = opbase | 0x04; data[addr++] = src_value; } else if (src_type == REF_REG) { data[addr++] = opbase | 0x08 | src_value; } else compile_error("invalid argument type to add/addc/subb"); } else if (0 == strcasecmp(ptr, "anl")) { char *dst = strtok_r(0, OP_SEPARATORS, &lasts); char *src = strtok_r(0, OP_SEPARATORS, &lasts); reftype dst_type = gettype(dst); int dst_value = getvalue(dst); reftype src_type = gettype(src); int src_value = getvalue(src); if (dst_type == REF_ACCUM && src_type == REF_DIRECT) { data[addr++] = 0x55; data[addr++] = src_value; } else if (dst_type == REF_ACCUM && src_type == REF_IMMED) { data[addr++] = 0x54; data[addr++] = src_value; } else if (dst_type == REF_DIRECT && src_type == REF_ACCUM) { data[addr++] = 0x52; data[addr++] = dst_value; } else compile_error("unknown source/destination types for anl"); } else if (0 == strcasecmp(ptr, "movc")) { char *dst = strtok_r(0, OP_SEPARATORS, &lasts); char *src = strtok_r(0, OP_SEPARATORS, &lasts); if (0 != strcasecmp(dst, "a")) compile_error("movc must use a"); if (0 != strncasecmp(src, "@a+", 3)) compile_error("movc must use @a+"); if (0 == strcasecmp(src+3, "dptr")) { data[addr++] = 0x93; } else if (0 == strcasecmp(src+3, "pc")) { data[addr++] = 0x83; } else compile_error("invalid arguments to movc"); } else if (0 == strcasecmp(ptr, "movx")) { char *dst = strtok_r(0, OP_SEPARATORS, &lasts); char *src = strtok_r(0, OP_SEPARATORS, &lasts); reftype dst_type = gettype(dst); int dst_value = getvalue(dst); reftype src_type = gettype(src); int src_value = getvalue(src); if (dst_type == REF_ACCUM && src_type == REF_INDIR) { data[addr++] = 0xe2 | src_value; } else if (dst_type == REF_ACCUM && src_type == REF_IDPTR) { data[addr++] = 0xe0; } else if (dst_type == REF_INDIR && src_type == REF_ACCUM) { data[addr++] = 0xf2 | dst_value; } else if (dst_type == REF_IDPTR && src_type == REF_ACCUM) { data[addr++] = 0xf0; } else compile_error("invalid source/destination pair"); } else if (0 == strcasecmp(ptr, "mov")) { char *dst = strtok_r(0, OP_SEPARATORS, &lasts); char *src = strtok_r(0, OP_SEPARATORS, &lasts); reftype dst_type = gettype(dst); int dst_value = getvalue(dst); reftype src_type = gettype(src); int src_value = getvalue(src); char src_bitaddr = resolve_bit_address(src); char dst_bitaddr = resolve_bit_address(dst); if (dst_type == REF_DIRECT && src_type == REF_IMMED) { data[addr++] = 0x75; data[addr++] = dst_value; data[addr++] = src_value; } else if (dst_type == REF_DIRECT && src_type == REF_REG) { data[addr++] = 0x88 | src_value; data[addr++] = dst_value; } else if (dst_type == REF_DIRECT && src_type == REF_ACCUM) { data[addr++] = 0xf5; data[addr++] = dst_value; } else if (dst_type == REF_DIRECT && src_type == REF_DIRECT) { data[addr++] = 0x85; data[addr++] = src_value; data[addr++] = dst_value; } else if (dst_type == REF_ACCUM && src_type == REF_REG) { data[addr++] = 0xe8 | src_value; } else if (dst_type == REF_ACCUM && src_type == REF_IMMED) { data[addr++] = 0x74; data[addr++] = src_value; } else if (dst_type == REF_ACCUM && src_type == REF_DIRECT) { data[addr++] = 0xe5; data[addr++] = src_value; } else if (dst_type == REF_ACCUM && src_type == REF_INDIR) { data[addr++] = 0xe6 | src_value; } else if (dst_type == REF_REG && src_type == REF_ACCUM) { data[addr++] = 0xf8 | dst_value; } else if (dst_type == REF_REG && src_type == REF_IMMED) { data[addr++] = 0x78 | dst_value; data[addr++] = src_value; } else if (dst_type == REF_REG && src_type == REF_DIRECT) { data[addr++] = 0xa8 | dst_value; data[addr++] = src_value; } else if ((dst_type == REF_DPTR && src_type == REF_IMMED) || (dst_type == REF_DPTR && pass == 1)) { data[addr++] = 0x90; data[addr++] = (src_value & 0xff00) >> 8; data[addr++] = (src_value & 0x00ff); } else if (src_type == REF_CARRY && dst_bitaddr != -1) { data[addr++] = 0x92; data[addr++] = dst_bitaddr; } else if (dst_type == REF_CARRY && src_bitaddr != -1) { data[addr++] = 0xa2; data[addr++] = src_bitaddr; } else { printf("dst=%d src=%d\n", dst_type, src_type); compile_error("invalid source and destination combination"); } } else if (0 == strcasecmp(ptr, "nop")) { data[addr++] = 0x00; } else if (0 == strcasecmp(ptr, "ret")) { data[addr++] = 0x22; } else if (0 == strcasecmp(ptr, "reti")) { data[addr++] = 0x32; } else if (0 == strcasecmp(ptr, "mul") || 0 == strcasecmp(ptr, "div")) { char *arg = strtok_r(0, OP_SEPARATORS, &lasts); if (0 == arg || strcasecmp(arg, "ab")) compile_error("mul/div arg must be ab"); data[addr++] = (0 == strcasecmp(ptr, "mul")) ? 0xa4 : 0x84;; } else if (0 == strcasecmp(ptr, "swap")) { char *arg = strtok_r(0, OP_SEPARATORS, &lasts); if (0 == arg || strcasecmp(arg, "a")) compile_error("swap arg must be a"); data[addr++] = 0xc4; } else if (0 == strcasecmp(ptr, "orl")) { char *arg1 = strtok_r(0, OP_SEPARATORS, &lasts); char *arg2 = strtok_r(0, OP_SEPARATORS, &lasts); reftype arg1_type, arg2_type; int arg1_value, arg2_value; if (!arg1 || !arg2) compile_error("not enough args to orl"); arg1_type = gettype(arg1); arg2_type = gettype(arg2); arg1_value = getvalue(arg1); arg2_value = getvalue(arg2); if (arg1_type == REF_ACCUM && arg2_type == REF_DIRECT) { data[addr++] = 0x45; data[addr++] = arg2_value; } else if (arg1_type == REF_ACCUM && arg2_type == REF_IMMED) { data[addr++] = 0x44; data[addr++] = arg2_value; } else compile_error("unknown args to orl"); } else if (0 == strcasecmp(ptr, "rl") || 0 == strcasecmp(ptr, "rr")) { char *arg = strtok_r(0, OP_SEPARATORS, &lasts); int opcode = (0 == strcasecmp(ptr, "rl")) ? 0x23 : 0x03; if (0 == arg || strcasecmp(arg, "a")) compile_error("rl/rr arg must be a"); data[addr++] = opcode; } else if (0 == strcasecmp(ptr, "rlc") || 0 == strcasecmp(ptr, "rrc")) { char *arg = strtok_r(0, OP_SEPARATORS, &lasts); int opcode = (0 == strcasecmp(ptr, "rlc")) ? 0x33 : 0x13; if (0 == arg || strcasecmp(arg, "a")) compile_error("rlc/rrc arg must be a"); data[addr++] = opcode; } else if (0 == strcasecmp(ptr, "djnz")) { char *dec = strtok_r(0, OP_SEPARATORS, &lasts); char *jmp = strtok_r(0, OP_SEPARATORS, &lasts); int jmpaddr = symtab_lookup(jmp); reftype dec_type = gettype(dec); int dec_value = getvalue(dec); int reloff = jmpaddr - (addr+2); if (pass == 2 && (reloff > 126 || reloff < -126)) compile_error("reljump too far"); if (pass == 2 && jmpaddr == -1) compile_error("unknown label"); if (dec_type == REF_REG) { data[addr++] = 0xd8 | dec_value; data[addr++] = reloff; } else if (dec_type == REF_DIRECT) { data[addr++] = 0xd5; data[addr++] = dec_value; data[addr++] = reloff; } else compile_error("invalid decrement type"); } else if (0 == strcasecmp(ptr, "sjmp")) { char *jmp = strtok_r(0, OP_SEPARATORS, &lasts); int jmpaddr = symtab_lookup(jmp); int reloff = jmpaddr - (addr+2); unsigned char reloff_chr = reloff; if (pass == 2 && (reloff > 126 || reloff < -126)) compile_error("sjmp too far"); data[addr++] = 0x80; data[addr++] = reloff_chr; } else if (0 == strcasecmp(ptr, "jmp")) { char *arg = strtok_r(0, OP_SEPARATORS, &lasts); if (0 == strcasecmp(arg, "@a+dptr")) { data[addr++] = 0x73; } else compile_error("invalid argument to jmp"); } else if (0 == strcasecmp(ptr, "cjne")) { char *arg1 = strtok_r(0, OP_SEPARATORS, &lasts); char *arg2 = strtok_r(0, OP_SEPARATORS, &lasts); char *arg3 = strtok_r(0, OP_SEPARATORS, &lasts); int arg1_value, arg2_value; reftype arg1_type, arg2_type; int jumpaddr, reloff; if (!arg1 || !arg2 || !arg3) compile_error("not enough args to cjne"); arg1_value = getvalue(arg1); arg2_value = getvalue(arg2); arg1_type = gettype(arg1); arg2_type = gettype(arg2); jumpaddr = symtab_lookup(arg3); reloff = jumpaddr - (addr+3); if (pass == 2 && (reloff > 126 || reloff < -126)) compile_error("cjne too far"); if (arg1_type == REF_ACCUM && arg2_type == REF_DIRECT) { data[addr++] = 0xb5; data[addr++] = arg2_value; data[addr++] = reloff; } else if (arg1_type == REF_ACCUM && arg2_type == REF_IMMED) { data[addr++] = 0xb4; data[addr++] = arg2_value; data[addr++] = reloff; } else if (arg1_type == REF_REG && arg2_type == REF_IMMED) { data[addr++] = 0xb8 | arg1_value; data[addr++] = arg2_value; data[addr++] = reloff; } else compile_error("unknown compare values for cjne"); } else if (0 == strcasecmp(ptr, "jnz") || 0 == strcasecmp(ptr, "jz") || 0 == strcasecmp(ptr, "jnc") || 0 == strcasecmp(ptr, "jc")) { char *jmp = strtok_r(0, OP_SEPARATORS, &lasts); int jmpaddr = symtab_lookup(jmp); int reloff = jmpaddr - (addr+2); unsigned char opcode; if (pass == 2 && (reloff > 126 || reloff < -126)) compile_error("reljump too far"); if (0 == strcasecmp(ptr, "jnz")) opcode = 0x70; if (0 == strcasecmp(ptr, "jz")) opcode = 0x60; if (0 == strcasecmp(ptr, "jnc")) opcode = 0x50; if (0 == strcasecmp(ptr, "jc")) opcode = 0x40; data[addr++] = opcode; data[addr++] = reloff; } else if (0 == strcasecmp(ptr, "jnb") || 0 == strcasecmp(ptr, "jb")) { char *bit = strtok_r(0, OP_SEPARATORS, &lasts); char *jmp = strtok_r(0, OP_SEPARATORS, &lasts); int jmpaddr = symtab_lookup(jmp); int reloff = jmpaddr - (addr+3); unsigned char reloff_chr = reloff; unsigned char baseop = (0 == strcasecmp(ptr, "jnb")) ? 0x30 : 0x20; int bitaddr = resolve_bit_address(bit); char bitaddr_ch = bitaddr; if (pass == 2 && (reloff > 126 || reloff < -126)) compile_error("reljump too far"); if (bitaddr == -1) compile_error("invalid bit argument to jb/jnb"); data[addr++] = baseop; data[addr++] = bitaddr_ch; data[addr++] = reloff_chr; } else if (0 == strcasecmp(ptr, "ljmp") || 0 == strcasecmp(ptr, "lcall")) { char *arg = strtok_r(0, OP_SEPARATORS, &lasts); int jmpaddr = symtab_lookup(arg); int opcode; if (0 == strcasecmp(ptr, "ljmp")) opcode = 0x02; if (0 == strcasecmp(ptr, "lcall")) opcode = 0x12; if (pass == 2 && jmpaddr == -1) compile_error("unknown label"); data[addr++] = opcode; data[addr++] = (jmpaddr & 0xff00) >> 8; data[addr++] = jmpaddr & 0xff; } else if (0 == strcasecmp(ptr, "push") || 0 == strcasecmp(ptr, "pop")) { char *arg = strtok_r(0, OP_SEPARATORS, &lasts); reftype arg_type = gettype(arg); int arg_value = getvalue(arg); int opcode = (0 == strcasecmp(ptr, "pop")) ? 0xd0 : 0xc0; if (arg_type == REF_DIRECT) { data[addr++] = opcode; data[addr++] = arg_value; } else compile_error("can only push/pop directs"); } else if (0 == strcasecmp(ptr, "inc") || 0 == strcasecmp(ptr, "dec")) { char *arg = strtok_r(0, OP_SEPARATORS, &lasts); reftype arg_type = gettype(arg); int arg_value = getvalue(arg); int opbase = (0 == strcasecmp(ptr, "inc")) ? 0x00 : 0x10; if (arg_type == REF_ACCUM) { data[addr++] = opbase | 0x04; } else if (arg_type == REF_REG) { data[addr++] = opbase | 0x08 | arg_value; } else if (arg_type == REF_DIRECT) { data[addr++] = opbase | 0x05; data[addr++] = arg_value; } else if (arg_type == REF_DPTR && 0==strcasecmp(ptr, "inc")) { data[addr++] = 0xa3; } else compile_error("unknown argument to inc/dec"); } else if (0 == strcasecmp(ptr, "xch")) { char *arg1 = strtok_r(0, OP_SEPARATORS, &lasts); char *arg2 = strtok_r(0, OP_SEPARATORS, &lasts); reftype arg1type, arg2type; int arg1value, arg2value; if (!arg1 || !arg2) compile_error("not enough args to xch"); arg1type=gettype(arg1); arg2type=gettype(arg2); arg1value=getvalue(arg1); arg2value=getvalue(arg2); if (arg1type != REF_ACCUM) compile_error("must xch with accum"); if (arg2type == REF_REG) { data[addr++] = 0xc8 | arg2value; } else if (arg2type == REF_DIRECT) { data[addr++] = 0xc5; data[addr++] = arg2value; } else compile_error("invalid args to xch"); } else if (0 == strcasecmp(ptr, "clr") || 0 == strcasecmp(ptr, "setb") || 0 == strcasecmp(ptr, "cpl")) { char *arg = strtok_r(0, OP_SEPARATORS, &lasts); int bitaddr = resolve_bit_address(arg); int clr = (0 == strcasecmp(ptr, "clr")); int setb = (0 == strcasecmp(ptr, "setb")); int cpl = (0 == strcasecmp(ptr, "cpl")); reftype arg_type = gettype(arg); if (bitaddr != -1) { /* clr setb cpl */ data[addr++] = clr ? 0xc2 : setb ? 0xd2 : 0xb2; data[addr++] = bitaddr; } else if (arg_type == REF_ACCUM && clr) { data[addr++] = 0xe4; } else if (arg_type == REF_ACCUM && cpl) { data[addr++] = 0xf4; } else if (arg_type == REF_CARRY) { /* clr setb cpl */ data[addr++] = clr ? 0xc3 : setb ? 0xd3 : 0xb3; } else compile_error("unknown argument type to clr/setb"); } else { char *arg = strtok_r(0, OP_SEPARATORS, &lasts); if (arg && 0 == strcasecmp(arg, "equ")) { char *arg2 = strtok_r(0, OP_SEPARATORS, &lasts); if (arg2) { if (pass == 1) alias_store(ptr, arg2); } else { compile_error("missing value in equ"); } } else { printf("opcode: %s\n", ptr); compile_error("unknown opcode"); } } } fclose(in); close(fd); return addr; }