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