summaryrefslogtreecommitdiff
path: root/firmware/drivers
diff options
context:
space:
mode:
authorJens Arnold <amiconn@rockbox.org>2004-05-10 11:38:24 +0000
committerJens Arnold <amiconn@rockbox.org>2004-05-10 11:38:24 +0000
commite48cc2a491b205a96e2b5aa8c4d02f4cf84ae724 (patch)
tree7815bafa113763327b38ee287ef33930899f33ad /firmware/drivers
parent4fb536cff7c637164067e8e1f919251bd5b08a91 (diff)
downloadrockbox-e48cc2a491b205a96e2b5aa8c4d02f4cf84ae724.tar.gz
rockbox-e48cc2a491b205a96e2b5aa8c4d02f4cf84ae724.zip
Fully assembler optimized lcd driver (another 10% real-world speedup on recorder), replaces lcd.c
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4602 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/drivers')
-rwxr-xr-xfirmware/drivers/lcd.S307
-rw-r--r--firmware/drivers/lcd.c362
2 files changed, 307 insertions, 362 deletions
diff --git a/firmware/drivers/lcd.S b/firmware/drivers/lcd.S
new file mode 100755
index 0000000000..3ab993c1d0
--- /dev/null
+++ b/firmware/drivers/lcd.S
@@ -0,0 +1,307 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2004 by Jens Arnold
11 * Based on the work of Alan Korr and Jörg Hohensohn
12 *
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21#include "config.h"
22#include "sh7034.h"
23
24#define LCDR (PBDR_ADDR+1)
25
26#ifdef HAVE_LCD_CHARCELLS
27#define LCD_DS 1 /* PB0 = 1 --- 0001 --- LCD-DS */
28#define LCD_CS 2 /* PB1 = 1 --- 0010 --- /LCD-CS */
29#define LCD_SD 4 /* PB2 = 1 --- 0100 --- LCD-SD */
30#define LCD_SC 8 /* PB3 = 1 --- 1000 --- LCD-SC */
31#else
32#define LCD_SD 1 /* PB0 = 1 --- 0001 */
33#define LCD_SC 2 /* PB1 = 1 --- 0010 */
34#define LCD_RS 4 /* PB2 = 1 --- 0100 */
35#define LCD_CS 8 /* PB3 = 1 --- 1000 */
36#define LCD_DS LCD_RS
37#endif
38
39/*
40 * About /CS,DS,SC,SD
41 * ------------------
42 *
43 * LCD on JBP and JBR uses a SPI protocol to receive orders (SDA and SCK lines)
44 *
45 * - /CS -> Chip Selection line :
46 * 0 : LCD chipset is activated.
47 * - DS -> Data Selection line, latched at the rising edge
48 * of the 8th serial clock (*) :
49 * 0 : instruction register,
50 * 1 : data register;
51 * - SC -> Serial Clock line (SDA).
52 * - SD -> Serial Data line (SCK), latched at the rising edge
53 * of each serial clock (*).
54 *
55 * _ _
56 * /CS \ /
57 * \______________________________________________________/
58 * _____ ____ ____ ____ ____ ____ ____ ____ ____ _____
59 * SD \/ D7 \/ D6 \/ D5 \/ D4 \/ D3 \/ D2 \/ D1 \/ D0 \/
60 * _____/\____/\____/\____/\____/\____/\____/\____/\____/\_____
61 *
62 * _____ _ _ _ _ _ _ _ ________
63 * SC \ * \ * \ * \ * \ * \ * \ * \ *
64 * \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
65 * _ _________________________________________________________
66 * DS \/
67 * _/\_________________________________________________________
68 *
69 */
70
71 .section .icode,"ax",@progbits
72
73 .align 2
74 .global _lcd_write_command
75 .type _lcd_write_command,@function
76
77/* Write a command byte to the lcd controller
78 *
79 * Arguments:
80 * r4 - data byte (int)
81 *
82 * Register usage:
83 * r0 - scratch
84 * r1 - data byte (copied)
85 * r2 - precalculated port value (CS, DS and SC low, SD high),
86 * negated (neg)!
87 * r3 - lcd port address
88 * r5 - 1 (byte count for reuse of the loop in _lcd_write_data)
89 */
90
91_lcd_write_command:
92 mov.l .lcdr,r3 /* put lcd data port address in r3 */
93 mov r4,r1 /* copy data byte to r1 */
94 mov #1,r5 /* set byte count to 1 (!) */
95
96 /* This code will fail if an interrupt changes the contents of PBDRL.
97 * If so, we must disable the interrupt here. */
98
99 mov.b @r3,r0 /* r0 = PBDRL */
100 or #(LCD_SD),r0 /* r0 |= LCD_SD */
101 and #(~(LCD_CS|LCD_DS|LCD_SC)),r0 /* r0 &= ~(LCD_CS|LCD_DS|LCD_SC) */
102
103 bra .single_transfer /* jump into the transfer loop */
104 neg r0,r2 /* r2 = 0 - r0 */
105
106
107 .align 2
108 .global _lcd_write_data
109 .type _lcd_write_data,@function
110
111
112/* A high performance function to write data to the display,
113 * one or multiple bytes.
114 *
115 * Arguments:
116 * r4 - data address
117 * r5 - byte count
118 *
119 * Register usage:
120 * r0 - scratch
121 * r1 - current data byte
122 * r2 - precalculated port value (CS and SC low, DS and SD high),
123 * negated (neg)!
124 * r3 - lcd port address
125 */
126
127_lcd_write_data:
128 mov.l .lcdr,r3 /* put lcd data port address in r3 */
129 nop /* align here */
130
131 /* This code will fail if an interrupt changes the contents of PBDRL.
132 * If so, we must disable the interrupt here. If disabling interrupts
133 * for a long time (~9200 clks = ~830 µs for transferring 112 bytes on
134 * recorders)is undesirable, the loop has to be rewritten to
135 * disable/precalculate/transfer/enable for each iteration. However,
136 * this would significantly decrease performance. */
137
138 mov.b @r3,r0 /* r0 = PBDRL */
139 or #(LCD_DS|LCD_SD),r0 /* r0 |= LCD_DS|LCD_SD */
140 and #(~(LCD_CS|LCD_SC)),r0 /* r0 &= ~(LCD_CS|LCD_SC) */
141 neg r0,r2 /* r2 = 0 - r0 */
142
143#ifdef HAVE_LCD_CHARCELLS
144/* optimized player version, also works for recorders */
145
146 .align 2
147.multi_transfer:
148 mov.b @r4+,r1 /* load data byte from memory */
149
150.single_transfer:
151 shll16 r1 /* shift data to most significant byte */
152 shll8 r1
153
154 shll r1 /* shift the msb into carry */
155 neg r2,r0 /* copy negated precalculated port value */
156 /* uses neg here for compatibility with recorder version */
157 bt 1f /* data bit = 1? */
158 and #(~LCD_SD),r0 /* no: r0 &= ~LCD_SD */
159 1:
160 shll r1 /* next shift here for alignment */
161 mov.b r0,@r3 /* set data to port */
162 or #(LCD_SC),r0 /* rise SC (independent of SD level) */
163 mov.b r0,@r3 /* set to port */
164
165 neg r2,r0
166 bt 1f
167 and #(~LCD_SD),r0
168 1:
169 mov.b r0,@r3
170 or #(LCD_SC),r0
171 mov.b r0,@r3
172
173 shll r1
174 neg r2,r0
175 bt 1f
176 and #(~LCD_SD),r0
177 1:
178 shll r1
179 mov.b r0,@r3
180 or #(LCD_SC),r0
181 mov.b r0,@r3
182
183 neg r2,r0
184 bt 1f
185 and #(~LCD_SD),r0
186 1:
187 mov.b r0,@r3
188 or #(LCD_SC),r0
189 mov.b r0,@r3
190
191 shll r1
192 neg r2,r0
193 bt 1f
194 and #(~LCD_SD),r0
195 1:
196 shll r1
197 mov.b r0,@r3
198 or #(LCD_SC),r0
199 mov.b r0,@r3
200
201 neg r2,r0
202 bt 1f
203 and #(~LCD_SD),r0
204 1:
205 mov.b r0,@r3
206 or #(LCD_SC),r0
207 mov.b r0,@r3
208
209 shll r1
210 neg r2,r0
211 bt 1f
212 and #(~LCD_SD),r0
213 1:
214 shll r1
215 mov.b r0,@r3
216 or #(LCD_SC),r0
217 mov.b r0,@r3
218
219 neg r2,r0
220 bt 1f
221 and #(~LCD_SD),r0
222 1:
223 mov.b r0,@r3
224 or #(LCD_SC),r0
225 mov.b r0,@r3
226
227#else /* HAVE_LCD_CHARCELLS */
228/* further optimized version, exploits that SD is on bit 0 for recorders */
229
230 .align 2
231.multi_transfer:
232 mov.b @r4+,r1 /* load data byte from memory */
233 nop
234
235.single_transfer:
236 shll16 r1 /* shift data to most significant byte */
237 shll8 r1
238 not r1,r1 /* and invert for use with negc */
239
240 shll r1 /* shift the MSB into carry */
241 negc r2,r0 /* carry to SD, SC low */
242 shll r1 /* next shift here for alignment */
243 mov.b r0,@r3 /* set data to port */
244 or #(LCD_SC),r0 /* rise SC (independent of SD level) */
245 mov.b r0,@r3 /* set to port */
246
247 negc r2,r0
248 mov.b r0,@r3
249 or #(LCD_SC),r0
250 mov.b r0,@r3
251
252 shll r1
253 negc r2,r0
254 shll r1
255 mov.b r0,@r3
256 or #(LCD_SC),r0
257 mov.b r0,@r3
258
259 negc r2,r0
260 mov.b r0,@r3
261 or #(LCD_SC),r0
262 mov.b r0,@r3
263
264 shll r1
265 negc r2,r0
266 shll r1
267 mov.b r0,@r3
268 or #(LCD_SC),r0
269 mov.b r0,@r3
270
271 negc r2,r0
272 mov.b r0,@r3
273 or #(LCD_SC),r0
274 mov.b r0,@r3
275
276 shll r1
277 negc r2,r0
278 shll r1
279 mov.b r0,@r3
280 or #(LCD_SC),r0
281 mov.b r0,@r3
282
283 negc r2,r0
284 mov.b r0,@r3
285 or #(LCD_SC),r0
286 mov.b r0,@r3
287
288#endif /* HAVE_LCD_CHARCELLS */
289
290 add #-1,r5 /* decrease byte count */
291 tst r5,r5 /* r5 == 0 ? */
292 bf .multi_transfer /* no: next iteration */
293
294 or #(LCD_CS|LCD_DS|LCD_SD|LCD_SC),r0 /* restore port */
295 rts
296 mov.b r0,@r3
297
298 /* This is the place to reenable the interrupts, if we have disabled
299 * them. See above. */
300
301 .align 2
302.lcdr:
303 .long LCDR
304
305.end:
306 .size _lcd_write_command,.end-_lcd_write_command
307
diff --git a/firmware/drivers/lcd.c b/firmware/drivers/lcd.c
deleted file mode 100644
index 68627f7c51..0000000000
--- a/firmware/drivers/lcd.c
+++ /dev/null
@@ -1,362 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Alan Korr, speedup by Jörg Hohensohn
11 * Further speedup and reorganization by Jens Arnold
12 *
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21#include <stdbool.h>
22#include "system.h"
23
24#define LCDR (PBDR_ADDR+1)
25
26#ifdef HAVE_LCD_CHARCELLS
27#define LCD_DS 1 /* PB0 = 1 --- 0001 --- LCD-DS */
28#define LCD_CS 2 /* PB1 = 1 --- 0010 --- /LCD-CS */
29#define LCD_SD 4 /* PB2 = 1 --- 0100 --- LCD-SD */
30#define LCD_SC 8 /* PB3 = 1 --- 1000 --- LCD-SC */
31#else
32#define LCD_SD 1 /* PB0 = 1 --- 0001 */
33#define LCD_SC 2 /* PB1 = 1 --- 0010 */
34#define LCD_RS 4 /* PB2 = 1 --- 0100 */
35#define LCD_CS 8 /* PB3 = 1 --- 1000 */
36#define LCD_DS LCD_RS
37#endif
38
39/*
40 * About /CS,DS,SC,SD
41 * ------------------
42 *
43 * LCD on JBP and JBR uses a SPI protocol to receive orders (SDA and SCK lines)
44 *
45 * - /CS -> Chip Selection line :
46 * 0 : LCD chipset is activated.
47 * - DS -> Data Selection line, latched at the rising edge
48 * of the 8th serial clock (*) :
49 * 0 : instruction register,
50 * 1 : data register;
51 * - SC -> Serial Clock line (SDA).
52 * - SD -> Serial Data line (SCK), latched at the rising edge
53 * of each serial clock (*).
54 *
55 * _ _
56 * /CS \ /
57 * \______________________________________________________/
58 * _____ ____ ____ ____ ____ ____ ____ ____ ____ _____
59 * SD \/ D7 \/ D6 \/ D5 \/ D4 \/ D3 \/ D2 \/ D1 \/ D0 \/
60 * _____/\____/\____/\____/\____/\____/\____/\____/\____/\_____
61 *
62 * _____ _ _ _ _ _ _ _ ________
63 * SC \ * \ * \ * \ * \ * \ * \ * \ *
64 * \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
65 * _ _________________________________________________________
66 * DS \/
67 * _/\_________________________________________________________
68 *
69 */
70
71/*
72 * The only way to do logical operations in an atomic way
73 * on SH1 is using :
74 *
75 * or.b/and.b/tst.b/xor.b #imm,@(r0,gbr)
76 *
77 * but GCC doesn't generate them at all so some assembly
78 * codes are needed here.
79 *
80 * The Global Base Register gbr is expected to be zero
81 * and r0 is the address of one register in the on-chip
82 * peripheral module.
83 *
84 */
85
86void lcd_write_command(int byte) __attribute__ ((section (".icode")));
87void lcd_write_command(int byte)
88{
89 asm (
90 "and.b %0, @(r0,gbr)"
91 : /* outputs */
92 : /* inputs */
93 /* %0 */ "I"(~(LCD_CS|LCD_DS|LCD_SD|LCD_SC)),
94 /* %1 = r0 */ "z"(LCDR)
95 );
96
97 asm (
98 "0: \n"
99 "and.b %2,@(r0,gbr) \n"
100 "shll %0 \n"
101 "bf 1f \n"
102 "or.b %3,@(r0,gbr) \n"
103 "1: \n"
104 "or.b %4,@(r0,gbr) \n"
105 "add #-1,%1 \n"
106 "cmp/pl %1 \n"
107 "bt 0b \n"
108 : /* outputs */
109 : /* inputs */
110 /* %0 */ "r"(((unsigned)byte)<<24),
111 /* %1 */ "r"(8),
112 /* %2 */ "I"(~(LCD_SC|LCD_SD|LCD_DS)),
113 /* %3 */ "I"(LCD_SD),
114 /* %4 */ "I"(LCD_SC),
115 /* %5 = r0 */ "z"(LCDR)
116 );
117
118 asm (
119 "or.b %0, @(r0,gbr)"
120 : /* outputs */
121 : /* inputs */
122 /* %0 */ "I"(LCD_CS|LCD_DS|LCD_SD|LCD_SC),
123 /* %1 = r0 */ "z"(LCDR)
124 );
125}
126
127
128/* A high performance function to write data to the display,
129 one or multiple bytes. */
130void lcd_write_data(unsigned char* p_bytes, int count) __attribute__ ((section (".icode")));
131
132#ifdef HAVE_LCD_CHARCELLS
133/* This version works for both Player and Recorder models */
134void lcd_write_data(unsigned char* p_bytes, int count)
135{
136 do
137 {
138 unsigned int byte;
139 unsigned int sda1; /* precalculated SC=low,SD=1 */
140
141 byte = *p_bytes++ << 24; /* fetch to MSB position */
142
143 /* This code will fail if an interrupt changes the contents of PBDRL.
144 If so, we must disable the interrupt here. */
145
146 /* precalculate the values for later bit toggling, init data write */
147 asm (
148 "mov.b @%1,r0 \n" /* r0 = PBDRL */
149 "or %3,r0 \n" /* r0 |= LCD_DS | LCD_SD DS and SD high */
150 "and %2,r0 \n" /* r0 &= ~(LCD_CS | LCD_SC) CS and SC low */
151 "mov.b r0,@%1 \n" /* PBDRL = r0 */
152 "mov r0,%0 \n" /* sda1 = r0 */
153 : /* outputs */
154 /* %0 */ "=r"(sda1)
155 : /* inputs */
156 /* %1 */ "r"(LCDR),
157 /* %2 */ "I"(~(LCD_CS | LCD_SC)),
158 /* %3 */ "I"(LCD_DS | LCD_SD)
159 : /* trashed */
160 "r0"
161 );
162
163 /* unrolled loop to serialize the byte */
164 asm (
165 "shll %0 \n" /* shift the msb into carry */
166 ".align 2 \n"
167 "mov %1,r0 \n" /* copy precalculated port value */
168 "bt 1f \n" /* data bit = 1? */
169 "and %5,r0 \n" /* no: r0 &= ~LCD_SD */
170 "1: \n"
171 "shll %0 \n" /* next shift here for alignment */
172 "mov.b r0,@%3 \n" /* set data to port */
173 "or %2,r0 \n" /* rise SC (independent of SD level) */
174 "mov.b r0,@%3 \n" /* set to port */
175
176 "mov %1,r0 \n"
177 "bt 1f \n"
178 "and %5,r0 \n"
179 "1: \n"
180 "mov.b r0,@%3 \n"
181 "or %2,r0 \n"
182 "mov.b r0,@%3 \n"
183
184 "shll %0 \n"
185 "mov %1,r0 \n"
186 "bt 1f \n"
187 "and %5,r0 \n"
188 "1: \n"
189 "shll %0 \n"
190 "mov.b r0,@%3 \n"
191 "or %2,r0 \n"
192 "mov.b r0,@%3 \n"
193
194 "mov %1,r0 \n"
195 "bt 1f \n"
196 "and %5,r0 \n"
197 "1: \n"
198 "mov.b r0,@%3 \n"
199 "or %2,r0 \n"
200 "mov.b r0,@%3 \n"
201
202 "shll %0 \n"
203 "mov %1,r0 \n"
204 "bt 1f \n"
205 "and %5,r0 \n"
206 "1: \n"
207 "shll %0 \n"
208 "mov.b r0,@%3 \n"
209 "or %2,r0 \n"
210 "mov.b r0,@%3 \n"
211
212 "mov %1,r0 \n"
213 "bt 1f \n"
214 "and %5,r0 \n"
215 "1: \n"
216 "mov.b r0,@%3 \n"
217 "or %2,r0 \n"
218 "mov.b r0,@%3 \n"
219
220 "shll %0 \n"
221 "mov %1,r0 \n"
222 "bt 1f \n"
223 "and %5,r0 \n"
224 "1: \n"
225 "shll %0 \n"
226 "mov.b r0,@%3 \n"
227 "or %2,r0 \n"
228 "mov.b r0,@%3 \n"
229
230 "mov %1,r0 \n"
231 "bt 1f \n"
232 "and %5,r0 \n"
233 "1: \n"
234 "mov.b r0,@%3 \n"
235 "or %2,r0 \n"
236 "mov.b r0,@%3 \n"
237
238 "or %4,r0 \n" /* restore port */
239 "mov.b r0,@%3 \n"
240 : /* outputs */
241 : /* inputs */
242 /* %0 */ "r"(byte),
243 /* %1 */ "r"(sda1),
244 /* %2 */ "I"(LCD_SC),
245 /* %3 */ "r"(LCDR),
246 /* %4 */ "I"(LCD_CS | LCD_DS | LCD_SD | LCD_SC),
247 /* %5 */ "I"(~(LCD_SD))
248 : /* trashed */
249 "r0"
250 );
251
252 /* This is the place to reenable the interrupts, if we have disabled
253 them. See above. */
254
255 } while (--count); /* tail loop is faster */
256}
257
258#else /* #ifdef HAVE_LCD_CHARCELLS */
259/* A further optimized version, exploits that SD is on bit 0 for recorders */
260void lcd_write_data(unsigned char* p_bytes, int count)
261{
262 do
263 {
264 unsigned byte;
265 unsigned sda1; /* precalculated SC=low,SD=1 */
266
267 /* take inverse data, so I can use the NEGC instruction below, it is
268 the only carry add/sub which does not destroy a source register */
269 byte = ~(*p_bytes++ << 24); /* fetch to MSB position */
270
271 /* This code will fail if an interrupt changes the contents of PBDRL.
272 If so, we must disable the interrupt here. */
273
274 /* precalculate the values for later bit toggling, init data write */
275 asm (
276 "mov.b @%1,r0 \n" /* r0 = PBDRL */
277 "or %3,r0 \n" /* r0 |= LCD_DS | LCD_SD DS and SD high, */
278 "and %2,r0 \n" /* r0 &= ~(LCD_CS | LCD_SC) CS and SC low */
279 "mov.b r0,@%1 \n" /* PBDRL = r0 */
280 "neg r0,%0 \n" /* sda1 = 0-r0 */
281 : /* outputs: */
282 /* %0 */ "=r"(sda1)
283 : /* inputs: */
284 /* %1 */ "r"(LCDR),
285 /* %2 */ "I"(~(LCD_CS | LCD_SC)),
286 /* %3 */ "I"(LCD_DS | LCD_SD)
287 : /* trashed */
288 "r0"
289 );
290
291 /* unrolled loop to serialize the byte */
292 asm (
293 "shll %0 \n" /* shift the MSB into carry */
294 ".align 2 \n"
295 "negc %1, r0 \n" /* carry to SD, SC low */
296 "shll %0 \n" /* next shift here for alignment */
297 "mov.b r0,@%3 \n" /* set data to port */
298 "or %2, r0 \n" /* rise SC (independent of SD level) */
299 "mov.b r0,@%3 \n" /* set to port */
300
301 "negc %1, r0 \n"
302 "mov.b r0,@%3 \n"
303 "or %2, r0 \n"
304 "mov.b r0,@%3 \n"
305
306 "shll %0 \n"
307 "negc %1, r0 \n"
308 "shll %0 \n"
309 "mov.b r0,@%3 \n"
310 "or %2, r0 \n"
311 "mov.b r0,@%3 \n"
312
313 "negc %1, r0 \n"
314 "mov.b r0,@%3 \n"
315 "or %2, r0 \n"
316 "mov.b r0,@%3 \n"
317
318 "shll %0 \n"
319 "negc %1, r0 \n"
320 "shll %0 \n"
321 "mov.b r0,@%3 \n"
322 "or %2, r0 \n"
323 "mov.b r0,@%3 \n"
324
325 "negc %1, r0 \n"
326 "mov.b r0,@%3 \n"
327 "or %2, r0 \n"
328 "mov.b r0,@%3 \n"
329
330 "shll %0 \n"
331 "negc %1, r0 \n"
332 "shll %0 \n"
333 "mov.b r0,@%3 \n"
334 "or %2, r0 \n"
335 "mov.b r0,@%3 \n"
336
337 "negc %1, r0 \n"
338 "mov.b r0,@%3 \n"
339 "or %2, r0 \n"
340 "mov.b r0,@%3 \n"
341
342 "or %4, r0 \n" /* restore port */
343 "mov.b r0,@%3 \n"
344 : /* outputs: */
345 : /* inputs: */
346 /* %0 */ "r"(byte),
347 /* %1 */ "r"(sda1),
348 /* %2 */ "I"(LCD_SC),
349 /* %3 */ "r"(LCDR),
350 /* %4 */ "I"(LCD_CS|LCD_DS|LCD_SD|LCD_SC)
351 : /* trashed: */
352 "r0"
353 );
354
355 /* This is the place to reenable the interrupts, if we have disabled
356 them. See above. */
357
358 } while (--count); /* tail loop is faster */
359}
360#endif /* #ifdef HAVE_LCD_CHARCELLS */
361
362