1 /*
2 * mon_assemble.c - The VICE built-in monitor, 6502 assembler module.
3 *
4 * Written by
5 * Daniel Sladic <sladic@eecg.toronto.edu>
6 * Ettore Perazzoli <ettore@comm2000.it>
7 * Andreas Boose <viceteam@t-online.de>
8 *
9 * This file is part of VICE, the Versatile Commodore Emulator.
10 * See README for copyright notice.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
25 * 02111-1307 USA.
26 *
27 */
28
29 #include "vice.h"
30
31 #include <string.h>
32
33 #ifdef HAVE_STRINGS_H
34 #include <strings.h>
35 #endif
36
37 #include "asm.h"
38 #include "montypes.h"
39 #include "mon_assemble.h"
40 #include "mon_util.h"
41 #include "types.h"
42 #include "uimon.h"
43 #include "util.h"
44
mon_assemble_instr(const char * opcode_name,asm_mode_addr_info_t operand)45 static int mon_assemble_instr(const char *opcode_name, asm_mode_addr_info_t operand)
46 {
47 uint16_t operand_value = operand.param;
48 uint16_t operand_mode = operand.addr_mode;
49 uint8_t i = 0, opcode = 0;
50 int len, branch_offset;
51 bool found = FALSE;
52 MEMSPACE mem;
53 uint16_t loc;
54
55 mem = addr_memspace(asm_mode_addr);
56 loc = addr_location(asm_mode_addr);
57
58 do {
59 const asm_opcode_info_t *opinfo;
60
61 opinfo = (monitor_cpu_for_memspace[mem]->asm_opcode_info_get)(i, 0, 0);
62 if (!strcasecmp(opinfo->mnemonic, opcode_name)) {
63 if (opinfo->addr_mode == operand_mode) {
64 opcode = i;
65 found = TRUE;
66 break;
67 }
68
69 /* Special case: Register A not specified for ACCUMULATOR mode. */
70 if (operand_mode == ASM_ADDR_MODE_IMPLIED
71 && opinfo->addr_mode == ASM_ADDR_MODE_ACCUMULATOR) {
72 opcode = i;
73 operand_mode = ASM_ADDR_MODE_ACCUMULATOR;
74 found = TRUE;
75 break;
76 }
77
78 /* Special case: RELATIVE mode looks like ZERO_PAGE or ABSOLUTE
79 modes. */
80 if ((operand_mode == ASM_ADDR_MODE_ZERO_PAGE
81 || operand_mode == ASM_ADDR_MODE_ABSOLUTE)
82 && opinfo->addr_mode == ASM_ADDR_MODE_RELATIVE) {
83 branch_offset = (operand_value - loc - 2) & 0xffff;
84 if (branch_offset > 0x7f && branch_offset < 0xff80) {
85 mon_out("Branch offset too large.\n");
86 return -1;
87 }
88 operand_value = (branch_offset & 0xff);
89 operand_mode = ASM_ADDR_MODE_RELATIVE;
90 opcode = i;
91 found = TRUE;
92 break;
93 }
94
95 /* Special case: opcode A - is A a register or $A? */
96 /* If second case, is it zero page or absolute? */
97 if (operand_mode == ASM_ADDR_MODE_ACCUMULATOR
98 && opinfo->addr_mode == ASM_ADDR_MODE_ZERO_PAGE) {
99 opcode = i;
100 operand_mode = ASM_ADDR_MODE_ZERO_PAGE;
101 operand_value = 0x000a;
102 found = TRUE;
103 break;
104 }
105 /* It's safe to assume ABSOLUTE if ZERO_PAGE not yet found since
106 * ZERO_PAGE versions always precede ABSOLUTE versions if they
107 * exist.
108 */
109 if (operand_mode == ASM_ADDR_MODE_ACCUMULATOR
110 && opinfo->addr_mode == ASM_ADDR_MODE_ABSOLUTE) {
111 opcode = i;
112 operand_mode = ASM_ADDR_MODE_ABSOLUTE;
113 operand_value = 0x000a;
114 found = TRUE;
115 break;
116 }
117
118 /* It's safe to assume ABSOULTE if ZERO_PAGE not yet found since
119 * ZERO_PAGE versions always precede ABSOLUTE versions if they
120 * exist.
121 */
122 if (operand_mode == ASM_ADDR_MODE_ZERO_PAGE
123 && opinfo->addr_mode == ASM_ADDR_MODE_ABSOLUTE) {
124 opcode = i;
125 operand_mode = ASM_ADDR_MODE_ABSOLUTE;
126 found = TRUE;
127 break;
128 }
129 }
130 i++;
131 } while (i != 0);
132
133 if (!found) {
134 mon_out("Instruction not valid.\n");
135 return -1;
136 }
137
138 len = (monitor_cpu_for_memspace[mem]->asm_addr_mode_get_size)
139 ((unsigned int)(operand_mode), 0, 0, 0);
140
141 mon_set_mem_val(mem, loc, opcode);
142 if (len >= 2) {
143 mon_set_mem_val(mem, (uint16_t)(loc + 1), (uint8_t)(operand_value & 0xff));
144 }
145 if (len >= 3) {
146 mon_set_mem_val(mem, (uint16_t)(loc + 2),
147 (uint8_t)((operand_value >> 8) & 0xff));
148 }
149
150 if (len >= 0) {
151 mon_inc_addr_location(&asm_mode_addr, len);
152 dot_addr[mem] = asm_mode_addr;
153 } else {
154 mon_out("Assemble error: %d\n", len);
155 }
156 return len;
157 }
158
mon_assemble6502_init(monitor_cpu_type_t * monitor_cpu_type)159 void mon_assemble6502_init(monitor_cpu_type_t *monitor_cpu_type)
160 {
161 monitor_cpu_type->mon_assemble_instr = mon_assemble_instr;
162 }
163