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