altos: use %lu for APRS altitude in sprintf
[fw/altos] / src / cc1111 / ao_dbg.c
1 /*
2  * Copyright © 2009 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include "ao.h"
20 #include "ao_pins.h"
21
22 static void
23 ao_dbg_send_bits(uint8_t msk, uint8_t val) __reentrant
24 {
25         DBG_PORT = (DBG_PORT & ~msk) | (val & msk);
26         __asm
27                 nop
28                 nop
29         __endasm;
30 }
31
32 void
33 ao_dbg_send_byte(uint8_t byte)
34 {
35         __pdata uint8_t b, d;
36
37         DBG_PORT |= DBG_DATA;
38         DBG_PORT_DIR |= DBG_DATA;
39         for (b = 0; b < 8; b++) {
40                 d = 0;
41                 if (byte & 0x80)
42                         d = DBG_DATA;
43                 byte <<= 1;
44                 ao_dbg_send_bits(DBG_CLOCK|DBG_DATA, DBG_CLOCK|d);
45                 ao_dbg_send_bits(DBG_CLOCK|DBG_DATA,     0    |d);
46         }
47         DBG_PORT_DIR &= ~DBG_DATA;
48 }
49
50 uint8_t
51 ao_dbg_recv_byte(void)
52 {
53         __pdata uint8_t byte, b;
54
55         byte = 0;
56         for (b = 0; b < 8; b++) {
57                 byte = byte << 1;
58                 ao_dbg_send_bits(DBG_CLOCK, DBG_CLOCK);
59                 if (DBG_DATA_PIN)
60                         byte |= 1;
61                 ao_dbg_send_bits(DBG_CLOCK, 0);
62         }
63         return byte;
64 }
65
66 /* 8051 instructions
67  */
68 #define NOP                     0x00
69 #define MOV_direct_data         0x75
70 #define LJMP                    0x02
71 #define MOV_Rn_data(n)          (0x78 | (n))
72 #define DJNZ_Rn_rel(n)          (0xd8 | (n))
73 #define MOV_A_direct            0xe5
74 #define MOV_direct1_direct2     0x85
75 #define MOV_direct_A            0xf5
76 #define MOV_DPTR_data16         0x90
77 #define MOV_A_data              0x74
78 #define MOVX_atDPTR_A           0xf0
79 #define MOVX_A_atDPTR           0xe0
80 #define INC_DPTR                0xa3
81 #define TRAP                    0xa5
82 #define SJMP                    0x80
83 #define JB                      0x20
84
85 #define DEBUG_INSTR(l)          (0x54 | (l))
86
87 #define SFR_PSW                 0xD0
88 #define SFR_DPL0                0x82
89 #define SFR_DPH0                0x83
90 #define SFR_DPL1                0x84
91 #define SFR_DPH1                0x85
92
93 __pdata uint8_t save_acc;
94 __pdata uint8_t save_psw;
95 __pdata uint8_t save_dpl0;
96 __pdata uint8_t save_dph0;
97 __pdata uint8_t save_dpl1;
98 __pdata uint8_t save_dph1;
99
100 static uint8_t
101 ao_dbg_inst1(uint8_t a) __reentrant
102 {
103         ao_dbg_send_byte(DEBUG_INSTR(1));
104         ao_dbg_send_byte(a);
105         return ao_dbg_recv_byte();
106 }
107
108 static uint8_t
109 ao_dbg_inst2(uint8_t a, uint8_t b) __reentrant
110 {
111         ao_dbg_send_byte(DEBUG_INSTR(2));
112         ao_dbg_send_byte(a);
113         ao_dbg_send_byte(b);
114         return ao_dbg_recv_byte();
115 }
116
117 static uint8_t
118 ao_dbg_inst3(uint8_t a, uint8_t b, uint8_t c) __reentrant
119 {
120         ao_dbg_send_byte(DEBUG_INSTR(3));
121         ao_dbg_send_byte(a);
122         ao_dbg_send_byte(b);
123         ao_dbg_send_byte(c);
124         return ao_dbg_recv_byte();
125 }
126
127 void
128 ao_dbg_start_transfer(uint16_t addr)
129 {
130         save_acc  = ao_dbg_inst1(NOP);
131         save_psw  = ao_dbg_inst2(MOV_A_direct, SFR_PSW);
132         save_dpl0 = ao_dbg_inst2(MOV_A_direct, SFR_DPL0);
133         save_dph0 = ao_dbg_inst2(MOV_A_direct, SFR_DPH0);
134         save_dpl1 = ao_dbg_inst2(MOV_A_direct, SFR_DPL1);
135         save_dph1 = ao_dbg_inst2(MOV_A_direct, SFR_DPH1);
136         ao_dbg_inst3(MOV_DPTR_data16, addr >> 8, addr);
137 }
138
139 void
140 ao_dbg_end_transfer(void)
141 {
142         ao_dbg_inst3(MOV_direct_data, SFR_DPL0, save_dpl0);
143         ao_dbg_inst3(MOV_direct_data, SFR_DPH0, save_dph0);
144         ao_dbg_inst3(MOV_direct_data, SFR_DPL1, save_dpl1);
145         ao_dbg_inst3(MOV_direct_data, SFR_DPH1, save_dph1);
146         ao_dbg_inst3(MOV_direct_data, SFR_PSW, save_psw);
147         ao_dbg_inst2(MOV_A_data, save_acc);
148 }
149
150 void
151 ao_dbg_write_byte(uint8_t byte)
152 {
153         ao_dbg_inst2(MOV_A_data, byte);
154         ao_dbg_inst1(MOVX_atDPTR_A);
155         ao_dbg_inst1(INC_DPTR);
156 }
157
158 uint8_t
159 ao_dbg_read_byte(void)
160 {
161         ao_dbg_inst1(MOVX_A_atDPTR);
162         return ao_dbg_inst1(INC_DPTR);
163 }
164
165 static void
166 ao_dbg_set_pins(void)
167 {
168         /* Make the DBG pins GPIOs. On TeleMetrum, this will
169          * disable the SPI link, so don't expect SPI to work after
170          * using the debugger.
171          */
172         DBG_PORT_SEL &= ~(DBG_CLOCK|DBG_DATA|DBG_RESET_N);
173
174         /* make DBG_DATA tri-state */
175         DBG_PORT_INP |= DBG_DATA;
176
177         /* Raise RESET_N and CLOCK */
178         DBG_PORT |= DBG_RESET_N | DBG_CLOCK;
179
180         /* RESET_N and CLOCK are outputs now */
181         DBG_PORT_DIR |= DBG_RESET_N | DBG_CLOCK;
182         DBG_PORT_DIR &= ~DBG_DATA;
183 }
184
185 static void
186 ao_dbg_long_delay(void)
187 {
188         uint8_t n;
189
190         for (n = 0; n < 20; n++)
191                 __asm nop __endasm;
192 }
193
194 #define AO_RESET_LOW_DELAY      AO_MS_TO_TICKS(100)
195 #define AO_RESET_HIGH_DELAY     AO_MS_TO_TICKS(100)
196
197 static void
198 ao_dbg_send_bits_delay(uint8_t msk, uint8_t val)
199 {
200         ao_dbg_long_delay();
201         ao_dbg_send_bits(msk, val);
202 }
203
204 void
205 ao_dbg_do_reset(uint8_t clock_up)
206 {
207         ao_dbg_set_pins();
208         ao_dbg_send_bits_delay(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|DBG_RESET_N);
209         ao_dbg_send_bits_delay(DBG_CLOCK|DBG_DATA|DBG_RESET_N, clock_up |DBG_DATA|    0    );
210         ao_delay(AO_RESET_LOW_DELAY);
211         ao_dbg_send_bits      (DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|    0    );
212         ao_dbg_send_bits_delay(DBG_CLOCK|DBG_DATA|DBG_RESET_N, clock_up |DBG_DATA|    0    );
213         ao_dbg_send_bits_delay(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|    0    );
214         ao_dbg_send_bits_delay(DBG_CLOCK|DBG_DATA|DBG_RESET_N, clock_up |DBG_DATA|DBG_RESET_N);
215         ao_delay(AO_RESET_HIGH_DELAY);
216 }
217
218 static void
219 debug_enable(void)
220 {
221         /* toggle clock line while holding reset low */
222         ao_dbg_do_reset(0);
223 }
224
225 static void
226 debug_reset(void)
227 {
228         /* hold clock high while holding reset low */
229         ao_dbg_do_reset(DBG_CLOCK);
230 }
231
232 static void
233 debug_put(void)
234 {
235         for (;;) {
236                 ao_cmd_white ();
237                 if (ao_cmd_lex_c == '\n')
238                         break;
239                 ao_cmd_hex();
240                 if (ao_cmd_status != ao_cmd_success)
241                         break;
242                 ao_dbg_send_byte(ao_cmd_lex_i);
243         }
244 }
245
246 static void
247 debug_get(void)
248 {
249         __pdata uint16_t count;
250         __pdata uint16_t i;
251         __pdata uint8_t byte;
252         ao_cmd_hex();
253         if (ao_cmd_status != ao_cmd_success)
254                 return;
255         count = ao_cmd_lex_i;
256         if (count > 256) {
257                 ao_cmd_status = ao_cmd_syntax_error;
258                 return;
259         }
260         for (i = 0; i < count; i++) {
261                 if (i && (i & 7) == 0)
262                         putchar('\n');
263                 byte = ao_dbg_recv_byte();
264                 ao_cmd_put8(byte);
265                 putchar(' ');
266         }
267         putchar('\n');
268 }
269
270 static void
271 debug_input(void)
272 {
273         __pdata uint16_t count;
274         __pdata uint16_t addr;
275         __pdata uint8_t b;
276         __pdata uint8_t i;
277
278         ao_cmd_hex();
279         count = ao_cmd_lex_i;
280         ao_cmd_hex();
281         addr = ao_cmd_lex_i;
282         if (ao_cmd_status != ao_cmd_success)
283                 return;
284         ao_dbg_start_transfer(addr);
285         i = 0;
286         while (count--) {
287                 if (!(i++ & 7))
288                         putchar('\n');
289                 b = ao_dbg_read_byte();
290                 ao_cmd_put8(b);
291         }
292         ao_dbg_end_transfer();
293         putchar('\n');
294 }
295
296 static void
297 debug_output(void)
298 {
299         __pdata uint16_t count;
300         __pdata uint16_t addr;
301         __pdata uint8_t b;
302
303         ao_cmd_hex();
304         count = ao_cmd_lex_i;
305         ao_cmd_hex();
306         addr = ao_cmd_lex_i;
307         if (ao_cmd_status != ao_cmd_success)
308                 return;
309         ao_dbg_start_transfer(addr);
310         while (count--) {
311                 b = ao_getnibble() << 4;
312                 b |= ao_getnibble();
313                 if (ao_cmd_status != ao_cmd_success)
314                         return;
315                 ao_dbg_write_byte(b);
316         }
317         ao_dbg_end_transfer();
318 }
319
320 __code struct ao_cmds ao_dbg_cmds[7] = {
321         { debug_enable, "D\0Enable debug" },
322         { debug_get,    "G <count>\0Get data" },
323         { debug_input,  "I <count> <addr>\0Input <count> at <addr>" },
324         { debug_output, "O <count> <addr>\0Output <count> at <addr>" },
325         { debug_put,    "P <byte> ...\0Put data" },
326         { debug_reset,  "R\0Reset" },
327         { 0, NULL },
328 };
329
330 void
331 ao_dbg_init(void)
332 {
333         ao_cmd_register(&ao_dbg_cmds[0]);
334 }