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