Make it clear that the player has to be restarted after initializing the database
[Rockbox.git] / firmware / drivers / lcd-h100-remote.c
blobe3669067240b45a668425961393847639b07a2eb
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Richard S. La Charité III
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include "config.h"
21 #include "cpu.h"
22 #include "lcd.h"
23 #include "lcd-remote.h"
24 #include "kernel.h"
25 #include "thread.h"
26 #include <string.h>
27 #include <stdlib.h>
28 #include "file.h"
29 #include "debug.h"
30 #include "system.h"
31 #include "font.h"
32 #include "rbunicode.h"
33 #include "bidi.h"
35 /*** definitions ***/
37 #define LCD_REMOTE_CNTL_ADC_NORMAL 0xa0
38 #define LCD_REMOTE_CNTL_ADC_REVERSE 0xa1
39 #define LCD_REMOTE_CNTL_SHL_NORMAL 0xc0
40 #define LCD_REMOTE_CNTL_SHL_REVERSE 0xc8
41 #define LCD_REMOTE_CNTL_DISPLAY_ON_OFF 0xae
42 #define LCD_REMOTE_CNTL_ENTIRE_ON_OFF 0xa4
43 #define LCD_REMOTE_CNTL_REVERSE_ON_OFF 0xa6
44 #define LCD_REMOTE_CNTL_NOP 0xe3
45 #define LCD_REMOTE_CNTL_POWER_CONTROL 0x2b
46 #define LCD_REMOTE_CNTL_SELECT_REGULATOR 0x20
47 #define LCD_REMOTE_CNTL_SELECT_BIAS 0xa2
48 #define LCD_REMOTE_CNTL_SELECT_VOLTAGE 0x81
49 #define LCD_REMOTE_CNTL_INIT_LINE 0x40
50 #define LCD_REMOTE_CNTL_SET_PAGE_ADDRESS 0xB0
52 #define LCD_REMOTE_CNTL_HIGHCOL 0x10 /* Upper column address */
53 #define LCD_REMOTE_CNTL_LOWCOL 0x00 /* Lower column address */
55 #define CS_LO and_l(~0x00000004, &GPIO1_OUT)
56 #define CS_HI or_l(0x00000004, &GPIO1_OUT)
57 #define CLK_LO and_l(~0x10000000, &GPIO_OUT)
58 #define CLK_HI or_l(0x10000000, &GPIO_OUT)
59 #define DATA_LO and_l(~0x00040000, &GPIO1_OUT)
60 #define DATA_HI or_l(0x00040000, &GPIO1_OUT)
61 #define RS_LO and_l(~0x00010000, &GPIO_OUT)
62 #define RS_HI or_l(0x00010000, &GPIO_OUT)
64 #define SCROLLABLE_LINES 13
66 /*** globals ***/
68 fb_remote_data lcd_remote_framebuffer[LCD_REMOTE_HEIGHT/8][LCD_REMOTE_WIDTH]
69 IBSS_ATTR;
71 static int drawmode = DRMODE_SOLID;
72 static int xmargin = 0;
73 static int ymargin = 0;
74 static int curfont = FONT_SYSFIXED;
76 #ifndef SIMULATOR
77 static int xoffset; /* needed for flip */
79 /* timeout counter for deasserting /CS after access, <0 means not counting */
80 static int cs_countdown IDATA_ATTR = 0;
81 #define CS_TIMEOUT (HZ/10)
83 #ifdef HAVE_REMOTE_LCD_TICKING
84 /* If set to true, will prevent "ticking" to headphones. */
85 static bool emireduce = false;
86 static int byte_delay = 0;
87 #endif
89 /* remote hotplug */
90 static struct event_queue remote_scroll_queue;
91 #define REMOTE_INIT_LCD 1
92 #define REMOTE_DEINIT_LCD 2
94 static bool remote_initialized = false;
95 static int _remote_type = REMOTETYPE_UNPLUGGED;
97 /* cached settings values */
98 static bool cached_invert = false;
99 static bool cached_flip = false;
100 static int cached_contrast = DEFAULT_REMOTE_CONTRAST_SETTING;
101 #endif
103 /* scrolling */
104 static volatile int scrolling_lines=0; /* Bitpattern of which lines are scrolling */
105 static void scroll_thread(void);
106 static long scroll_stack[DEFAULT_STACK_SIZE/sizeof(long)];
107 static const char scroll_name[] = "remote_scroll";
108 static int scroll_ticks = 12; /* # of ticks between updates*/
109 static int scroll_delay = HZ/2; /* ticks delay before start */
110 static int scroll_step = 6; /* pixels per scroll step */
111 static int bidir_limit = 50; /* percent */
112 static struct scrollinfo scroll[SCROLLABLE_LINES];
114 static const char scroll_tick_table[16] = {
115 /* Hz values:
116 1, 1.25, 1.55, 2, 2.5, 3.12, 4, 5, 6.25, 8.33, 10, 12.5, 16.7, 20, 25, 33 */
117 100, 80, 64, 50, 40, 32, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3
120 /*** driver routines ***/
122 #ifndef SIMULATOR
124 #ifdef HAVE_REMOTE_LCD_TICKING
125 static inline void _byte_delay(int delay)
127 asm (
128 "move.l %[dly], %%d0 \n"
129 "ble.s 2f \n"
130 "1: \n"
131 "subq.l #1, %%d0 \n"
132 "bne.s 1b \n"
133 "2: \n"
134 : /* outputs */
135 : /* inputs */
136 [dly]"d"(delay)
137 : /* clobbers */
138 "d0"
141 #endif /* HAVE_REMOTE_LCD_TICKING */
143 /* Standard low-level byte writer. Requires CLK low on entry */
144 static inline void _write_byte(unsigned data)
146 asm volatile (
147 "move.l (%[gpo1]), %%d0 \n" /* Get current state of data line */
148 "and.l %[dbit], %%d0 \n"
149 "beq.s 1f \n" /* and set it as previous-state bit */
150 "bset #8, %[data] \n"
151 "1: \n"
152 "move.l %[data], %%d0 \n" /* Compute the 'bit derivative', i.e. a value */
153 "lsr.l #1, %%d0 \n" /* with 1's where the data changes from the */
154 "eor.l %%d0, %[data] \n" /* previous state, and 0's where it doesn't */
155 "swap %[data] \n" /* Shift data to upper byte */
156 "lsl.l #8, %[data] \n"
158 "lsl.l #1,%[data] \n" /* Shift out MSB */
159 "bcc.s 1f \n"
160 "eor.l %[dbit], (%[gpo1]) \n" /* 1: flip DATA */
161 "1: \n"
162 "eor.l %[cbit], (%[gpo0]) \n" /* Flip CLK */
163 "eor.l %[cbit], (%[gpo0]) \n" /* Flip CLK */
165 "lsl.l #1,%[data] \n" /* ..unrolled.. */
166 "bcc.s 1f \n"
167 "eor.l %[dbit], (%[gpo1]) \n"
168 "1: \n"
169 "eor.l %[cbit], (%[gpo0]) \n"
170 "eor.l %[cbit], (%[gpo0]) \n"
172 "lsl.l #1,%[data] \n"
173 "bcc.s 1f \n"
174 "eor.l %[dbit], (%[gpo1]) \n"
175 "1: \n"
176 "eor.l %[cbit], (%[gpo0]) \n"
177 "eor.l %[cbit], (%[gpo0]) \n"
179 "lsl.l #1,%[data] \n"
180 "bcc.s 1f \n"
181 "eor.l %[dbit], (%[gpo1]) \n"
182 "1: \n"
183 "eor.l %[cbit], (%[gpo0]) \n"
184 "eor.l %[cbit], (%[gpo0]) \n"
186 "lsl.l #1,%[data] \n"
187 "bcc.s 1f \n"
188 "eor.l %[dbit], (%[gpo1]) \n"
189 "1: \n"
190 "eor.l %[cbit], (%[gpo0]) \n"
191 "eor.l %[cbit], (%[gpo0]) \n"
193 "lsl.l #1,%[data] \n"
194 "bcc.s 1f \n"
195 "eor.l %[dbit], (%[gpo1]) \n"
196 "1: \n"
197 "eor.l %[cbit], (%[gpo0]) \n"
198 "eor.l %[cbit], (%[gpo0]) \n"
200 "lsl.l #1,%[data] \n"
201 "bcc.s 1f \n"
202 "eor.l %[dbit], (%[gpo1]) \n"
203 "1: \n"
204 "eor.l %[cbit], (%[gpo0]) \n"
205 "eor.l %[cbit], (%[gpo0]) \n"
207 "lsl.l #1,%[data] \n"
208 "bcc.s 1f \n"
209 "eor.l %[dbit], (%[gpo1]) \n"
210 "1: \n"
211 "eor.l %[cbit], (%[gpo0]) \n"
212 "eor.l %[cbit], (%[gpo0]) \n"
213 : /* outputs */
214 [data]"+d"(data)
215 : /* inputs */
216 [gpo0]"a"(&GPIO_OUT),
217 [cbit]"d"(0x10000000),
218 [gpo1]"a"(&GPIO1_OUT),
219 [dbit]"d"(0x00040000)
220 : /* clobbers */
221 "d0"
225 /* Fast low-level byte writer. Don't use with high CPU clock.
226 * Requires CLK low on entry */
227 static inline void _write_fast(unsigned data)
229 asm volatile (
230 "move.w %%sr,%%d3 \n" /* Get current interrupt level */
231 "move.w #0x2700,%%sr \n" /* Disable interrupts */
233 "move.l (%[gpo1]), %%d0 \n" /* Get current state of data port */
234 "move.l %%d0, %%d1 \n"
235 "and.l %[dbit], %%d1 \n" /* Check current state of data line */
236 "beq.s 1f \n" /* and set it as previous-state bit */
237 "bset #8, %[data] \n"
238 "1: \n"
239 "move.l %[data], %%d1 \n" /* Compute the 'bit derivative', i.e. a value */
240 "lsr.l #1, %%d1 \n" /* with 1's where the data changes from the */
241 "eor.l %%d1, %[data] \n" /* previous state, and 0's where it doesn't */
242 "swap %[data] \n" /* Shift data to upper byte */
243 "lsl.l #8, %[data] \n"
245 "move.l (%[gpo0]), %%d1 \n" /* Get current state of clock port */
246 "move.l %[cbit], %%d2 \n" /* Precalculate opposite state of clock line */
247 "eor.l %%d1, %%d2 \n"
249 "lsl.l #1,%[data] \n" /* Shift out MSB */
250 "bcc.s 1f \n"
251 "eor.l %[dbit], %%d0 \n" /* 1: flip data bit */
252 "move.l %%d0, (%[gpo1]) \n" /* and output new DATA state */
253 "1: \n"
254 "move.l %%d2, (%[gpo0]) \n" /* Set CLK */
255 "move.l %%d1, (%[gpo0]) \n" /* Reset CLK */
257 "lsl.l #1,%[data] \n" /* ..unrolled.. */
258 "bcc.s 1f \n"
259 "eor.l %[dbit], %%d0 \n"
260 "move.l %%d0, (%[gpo1]) \n"
261 "1: \n"
262 "move.l %%d2, (%[gpo0]) \n"
263 "move.l %%d1, (%[gpo0]) \n"
265 "lsl.l #1,%[data] \n"
266 "bcc.s 1f \n"
267 "eor.l %[dbit], %%d0 \n"
268 "move.l %%d0, (%[gpo1]) \n"
269 "1: \n"
270 "move.l %%d2, (%[gpo0]) \n"
271 "move.l %%d1, (%[gpo0]) \n"
273 "lsl.l #1,%[data] \n"
274 "bcc.s 1f \n"
275 "eor.l %[dbit], %%d0 \n"
276 "move.l %%d0, (%[gpo1]) \n"
277 "1: \n"
278 "move.l %%d2, (%[gpo0]) \n"
279 "move.l %%d1, (%[gpo0]) \n"
281 "lsl.l #1,%[data] \n"
282 "bcc.s 1f \n"
283 "eor.l %[dbit], %%d0 \n"
284 "move.l %%d0, (%[gpo1]) \n"
285 "1: \n"
286 "move.l %%d2, (%[gpo0]) \n"
287 "move.l %%d1, (%[gpo0]) \n"
289 "lsl.l #1,%[data] \n"
290 "bcc.s 1f \n"
291 "eor.l %[dbit], %%d0 \n"
292 "move.l %%d0, (%[gpo1]) \n"
293 "1: \n"
294 "move.l %%d2, (%[gpo0]) \n"
295 "move.l %%d1, (%[gpo0]) \n"
297 "lsl.l #1,%[data] \n"
298 "bcc.s 1f \n"
299 "eor.l %[dbit], %%d0 \n"
300 "move.l %%d0, (%[gpo1]) \n"
301 "1: \n"
302 "move.l %%d2, (%[gpo0]) \n"
303 "move.l %%d1, (%[gpo0]) \n"
305 "lsl.l #1,%[data] \n"
306 "bcc.s 1f \n"
307 "eor.l %[dbit], %%d0 \n"
308 "move.l %%d0, (%[gpo1]) \n"
309 "1: \n"
310 "move.l %%d2, (%[gpo0]) \n"
311 "move.l %%d1, (%[gpo0]) \n"
313 "move.w %%d3, %%sr \n" /* Restore interrupt level */
314 : /* outputs */
315 [data]"+d"(data)
316 : /* inputs */
317 [gpo0]"a"(&GPIO_OUT),
318 [cbit]"i"(0x10000000),
319 [gpo1]"a"(&GPIO1_OUT),
320 [dbit]"d"(0x00040000)
321 : /* clobbers */
322 "d0", "d1", "d2", "d3"
326 void lcd_remote_write_command(int cmd)
328 cs_countdown = 0;
329 RS_LO;
330 CS_LO;
332 _write_byte(cmd);
333 #ifdef HAVE_REMOTE_LCD_TICKING
334 _byte_delay(byte_delay - 148);
335 #endif
337 cs_countdown = CS_TIMEOUT;
340 void lcd_remote_write_command_ex(int cmd, int data)
342 cs_countdown = 0;
343 RS_LO;
344 CS_LO;
346 _write_byte(cmd);
347 #ifdef HAVE_REMOTE_LCD_TICKING
348 _byte_delay(byte_delay - 148);
349 #endif
350 _write_byte(data);
351 #ifdef HAVE_REMOTE_LCD_TICKING
352 _byte_delay(byte_delay - 148);
353 #endif
355 cs_countdown = CS_TIMEOUT;
358 void lcd_remote_write_data(const unsigned char* p_bytes, int count) ICODE_ATTR;
359 void lcd_remote_write_data(const unsigned char* p_bytes, int count)
361 const unsigned char *p_end = p_bytes + count;
363 cs_countdown = 0;
364 RS_HI;
365 CS_LO;
367 /* This is safe as long as lcd_remote_write_data() isn't called from within
368 * an ISR. */
369 if (cpu_frequency < 50000000)
371 while (p_bytes < p_end)
373 _write_fast(*p_bytes++);
374 #ifdef HAVE_REMOTE_LCD_TICKING
375 _byte_delay(byte_delay - 87);
376 #endif
379 else
381 while (p_bytes < p_end)
383 _write_byte(*p_bytes++);
384 #ifdef HAVE_REMOTE_LCD_TICKING
385 _byte_delay(byte_delay - 148);
386 #endif
390 cs_countdown = CS_TIMEOUT;
392 #endif /* !SIMULATOR */
394 /*** hardware configuration ***/
396 int lcd_remote_default_contrast(void)
398 return DEFAULT_REMOTE_CONTRAST_SETTING;
401 #ifndef SIMULATOR
403 #ifdef HAVE_REMOTE_LCD_TICKING
404 void lcd_remote_emireduce(bool state)
406 emireduce = state;
408 #endif
410 void lcd_remote_powersave(bool on)
412 if (remote_initialized)
414 lcd_remote_write_command(LCD_REMOTE_CNTL_DISPLAY_ON_OFF | (on ? 0 : 1));
415 lcd_remote_write_command(LCD_REMOTE_CNTL_ENTIRE_ON_OFF | (on ? 1 : 0));
419 void lcd_remote_set_contrast(int val)
421 cached_contrast = val;
422 if (remote_initialized)
423 lcd_remote_write_command_ex(LCD_REMOTE_CNTL_SELECT_VOLTAGE, val);
426 void lcd_remote_set_invert_display(bool yesno)
428 cached_invert = yesno;
429 if (remote_initialized)
430 lcd_remote_write_command(LCD_REMOTE_CNTL_REVERSE_ON_OFF | (yesno?1:0));
433 /* turn the display upside down (call lcd_remote_update() afterwards) */
434 void lcd_remote_set_flip(bool yesno)
436 cached_flip = yesno;
437 if (yesno)
439 xoffset = 0;
440 if (remote_initialized)
442 lcd_remote_write_command(LCD_REMOTE_CNTL_ADC_NORMAL);
443 lcd_remote_write_command(LCD_REMOTE_CNTL_SHL_NORMAL);
446 else
448 xoffset = 132 - LCD_REMOTE_WIDTH;
449 if (remote_initialized)
451 lcd_remote_write_command(LCD_REMOTE_CNTL_ADC_REVERSE);
452 lcd_remote_write_command(LCD_REMOTE_CNTL_SHL_REVERSE);
457 /* The actual LCD init */
458 static void remote_lcd_init(void)
460 CS_HI;
461 CLK_LO;
462 lcd_remote_write_command(LCD_REMOTE_CNTL_SELECT_BIAS | 0x0);
464 lcd_remote_write_command(LCD_REMOTE_CNTL_POWER_CONTROL | 0x5);
465 sleep(1);
466 lcd_remote_write_command(LCD_REMOTE_CNTL_POWER_CONTROL | 0x6);
467 sleep(1);
468 lcd_remote_write_command(LCD_REMOTE_CNTL_POWER_CONTROL | 0x7);
470 lcd_remote_write_command(LCD_REMOTE_CNTL_SELECT_REGULATOR | 0x4); // 0x4 Select regulator @ 5.0 (default);
472 sleep(1);
474 lcd_remote_write_command(LCD_REMOTE_CNTL_INIT_LINE | 0x0); // init line
475 lcd_remote_write_command(LCD_REMOTE_CNTL_SET_PAGE_ADDRESS | 0x0); // page address
476 lcd_remote_write_command_ex(0x10, 0x00); // Column MSB + LSB
478 lcd_remote_write_command(LCD_REMOTE_CNTL_DISPLAY_ON_OFF | 1);
480 remote_initialized = true;
482 lcd_remote_set_flip(cached_flip);
483 lcd_remote_set_contrast(cached_contrast);
484 lcd_remote_set_invert_display(cached_invert);
487 bool remote_detect(void)
489 return (GPIO_READ & 0x40000000)?false:true;
492 int remote_type(void)
494 return _remote_type;
497 /* Monitor remote hotswap */
498 static void remote_tick(void)
500 static bool last_status = false;
501 static int countdown = 0;
502 static int init_delay = 0;
503 bool current_status;
504 int val;
505 int level;
507 current_status = remote_detect();
508 /* Only report when the status has changed */
509 if (current_status != last_status)
511 last_status = current_status;
512 countdown = current_status ? 20*HZ : 1;
514 else
516 /* Count down until it gets negative */
517 if (countdown >= 0)
518 countdown--;
520 if (current_status)
522 if (!(countdown % 8))
524 /* Determine which type of remote it is */
525 level = set_irq_level(HIGHEST_IRQ_LEVEL);
526 val = adc_scan(ADC_REMOTEDETECT);
527 set_irq_level(level);
529 if (val < ADCVAL_H100_LCD_REMOTE_HOLD)
531 if (val < ADCVAL_H100_LCD_REMOTE)
532 if (val < ADCVAL_H300_LCD_REMOTE)
533 _remote_type = REMOTETYPE_H300_LCD; /* hold off */
534 else
535 _remote_type = REMOTETYPE_H100_LCD; /* hold off */
536 else
537 if (val < ADCVAL_H300_LCD_REMOTE_HOLD)
538 _remote_type = REMOTETYPE_H300_LCD; /* hold on */
539 else
540 _remote_type = REMOTETYPE_H100_LCD; /* hold on */
542 if (--init_delay <= 0)
544 queue_post(&remote_scroll_queue, REMOTE_INIT_LCD, 0);
545 init_delay = 6;
548 else
550 _remote_type = REMOTETYPE_H300_NONLCD; /* hold on or off */
554 else
556 if (countdown == 0)
558 _remote_type = REMOTETYPE_UNPLUGGED;
560 queue_post(&remote_scroll_queue, REMOTE_DEINIT_LCD, 0);
565 /* handle chip select timeout */
566 if (cs_countdown >= 0)
567 cs_countdown--;
568 if (cs_countdown == 0)
569 CS_HI;
571 #endif /* !SIMULATOR */
573 /* LCD init */
574 #ifdef SIMULATOR
576 void lcd_remote_init(void)
578 create_thread(scroll_thread, scroll_stack,
579 sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE));
581 #else /* !SIMULATOR */
583 /* Initialise ports and kick off monitor */
584 void lcd_remote_init(void)
586 #ifdef IRIVER_H300_SERIES
587 or_l(0x10010000, &GPIO_FUNCTION); /* GPIO16: RS
588 GPIO28: CLK */
590 or_l(0x00040006, &GPIO1_FUNCTION); /* GPO33: Backlight
591 GPIO34: CS
592 GPIO50: Data */
593 or_l(0x10010000, &GPIO_ENABLE);
594 or_l(0x00040006, &GPIO1_ENABLE);
595 #else
596 or_l(0x10010800, &GPIO_FUNCTION); /* GPIO11: Backlight
597 GPIO16: RS
598 GPIO28: CLK */
600 or_l(0x00040004, &GPIO1_FUNCTION); /* GPIO34: CS
601 GPIO50: Data */
602 or_l(0x10010800, &GPIO_ENABLE);
603 or_l(0x00040004, &GPIO1_ENABLE);
604 #endif
605 lcd_remote_clear_display();
607 /* private queue */
608 queue_init(&remote_scroll_queue, false);
609 tick_add_task(remote_tick);
610 create_thread(scroll_thread, scroll_stack,
611 sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE));
614 /*** update functions ***/
616 /* Update the display.
617 This must be called after all other LCD functions that change the display. */
618 void lcd_remote_update(void) ICODE_ATTR;
619 void lcd_remote_update(void)
621 int y;
623 if (!remote_initialized)
624 return;
626 #ifdef HAVE_REMOTE_LCD_TICKING
627 /* Adjust byte delay for emi reduction. */
628 byte_delay = emireduce ? cpu_frequency / 197600 + 28: 0;
629 #endif
631 /* Copy display bitmap to hardware */
632 for (y = 0; y < LCD_REMOTE_HEIGHT/8; y++)
634 lcd_remote_write_command(LCD_REMOTE_CNTL_SET_PAGE_ADDRESS | y);
635 lcd_remote_write_command(LCD_REMOTE_CNTL_HIGHCOL | ((xoffset >> 4) & 0xf));
636 lcd_remote_write_command(LCD_REMOTE_CNTL_LOWCOL | (xoffset & 0xf));
637 lcd_remote_write_data(lcd_remote_framebuffer[y], LCD_REMOTE_WIDTH);
641 /* Update a fraction of the display. */
642 void lcd_remote_update_rect(int, int, int, int) ICODE_ATTR;
643 void lcd_remote_update_rect(int x, int y, int width, int height)
645 int ymax;
647 if (!remote_initialized)
648 return;
650 /* The Y coordinates have to work on even 8 pixel rows */
651 ymax = (y + height-1) >> 3;
652 y >>= 3;
654 if(x + width > LCD_REMOTE_WIDTH)
655 width = LCD_REMOTE_WIDTH - x;
656 if (width <= 0)
657 return; /* nothing left to do, 0 is harmful to lcd_write_data() */
658 if(ymax >= LCD_REMOTE_HEIGHT/8)
659 ymax = LCD_REMOTE_HEIGHT/8-1;
661 #ifdef HAVE_REMOTE_LCD_TICKING
662 /* Adjust byte delay for emi reduction */
663 byte_delay = emireduce ? cpu_frequency / 197600 + 28: 0;
664 #endif
666 /* Copy specified rectange bitmap to hardware */
667 for (; y <= ymax; y++)
669 lcd_remote_write_command(LCD_REMOTE_CNTL_SET_PAGE_ADDRESS | y);
670 lcd_remote_write_command(LCD_REMOTE_CNTL_HIGHCOL | (((x+xoffset) >> 4) & 0xf));
671 lcd_remote_write_command(LCD_REMOTE_CNTL_LOWCOL | ((x+xoffset) & 0xf));
672 lcd_remote_write_data(&lcd_remote_framebuffer[y][x], width);
675 #endif /* !SIMULATOR */
677 /*** parameter handling ***/
679 void lcd_remote_set_drawmode(int mode)
681 drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
684 int lcd_remote_get_drawmode(void)
686 return drawmode;
689 void lcd_remote_setmargins(int x, int y)
691 xmargin = x;
692 ymargin = y;
695 int lcd_remote_getxmargin(void)
697 return xmargin;
700 int lcd_remote_getymargin(void)
702 return ymargin;
706 void lcd_remote_setfont(int newfont)
708 curfont = newfont;
711 int lcd_remote_getstringsize(const unsigned char *str, int *w, int *h)
713 return font_getstringsize(str, w, h, curfont);
716 /*** low-level drawing functions ***/
718 static void setpixel(int x, int y)
720 lcd_remote_framebuffer[y>>3][x] |= 1 << (y & 7);
723 static void clearpixel(int x, int y)
725 lcd_remote_framebuffer[y>>3][x] &= ~(1 << (y & 7));
728 static void flippixel(int x, int y)
730 lcd_remote_framebuffer[y>>3][x] ^= 1 << (y & 7);
733 static void nopixel(int x, int y)
735 (void)x;
736 (void)y;
739 lcd_remote_pixelfunc_type* const lcd_remote_pixelfuncs[8] = {
740 flippixel, nopixel, setpixel, setpixel,
741 nopixel, clearpixel, nopixel, clearpixel
744 static void flipblock(fb_remote_data *address, unsigned mask, unsigned bits)
745 ICODE_ATTR;
746 static void flipblock(fb_remote_data *address, unsigned mask, unsigned bits)
748 *address ^= bits & mask;
751 static void bgblock(fb_remote_data *address, unsigned mask, unsigned bits)
752 ICODE_ATTR;
753 static void bgblock(fb_remote_data *address, unsigned mask, unsigned bits)
755 *address &= bits | ~mask;
758 static void fgblock(fb_remote_data *address, unsigned mask, unsigned bits)
759 ICODE_ATTR;
760 static void fgblock(fb_remote_data *address, unsigned mask, unsigned bits)
762 *address |= bits & mask;
765 static void solidblock(fb_remote_data *address, unsigned mask, unsigned bits)
766 ICODE_ATTR;
767 static void solidblock(fb_remote_data *address, unsigned mask, unsigned bits)
769 unsigned data = *address;
771 bits ^= data;
772 *address = data ^ (bits & mask);
775 static void flipinvblock(fb_remote_data *address, unsigned mask, unsigned bits)
776 ICODE_ATTR;
777 static void flipinvblock(fb_remote_data *address, unsigned mask, unsigned bits)
779 *address ^= ~bits & mask;
782 static void bginvblock(fb_remote_data *address, unsigned mask, unsigned bits)
783 ICODE_ATTR;
784 static void bginvblock(fb_remote_data *address, unsigned mask, unsigned bits)
786 *address &= ~(bits & mask);
789 static void fginvblock(fb_remote_data *address, unsigned mask, unsigned bits)
790 ICODE_ATTR;
791 static void fginvblock(fb_remote_data *address, unsigned mask, unsigned bits)
793 *address |= ~bits & mask;
796 static void solidinvblock(fb_remote_data *address, unsigned mask, unsigned bits)
797 ICODE_ATTR;
798 static void solidinvblock(fb_remote_data *address, unsigned mask, unsigned bits)
800 unsigned data = *address;
802 bits = ~bits ^ data;
803 *address = data ^ (bits & mask);
806 lcd_remote_blockfunc_type* const lcd_remote_blockfuncs[8] = {
807 flipblock, bgblock, fgblock, solidblock,
808 flipinvblock, bginvblock, fginvblock, solidinvblock
811 /*** drawing functions ***/
813 /* Clear the whole display */
814 void lcd_remote_clear_display(void)
816 unsigned bits = (drawmode & DRMODE_INVERSEVID) ? 0xFFu : 0;
818 memset(lcd_remote_framebuffer, bits, sizeof lcd_remote_framebuffer);
819 scrolling_lines = 0;
822 /* Set a single pixel */
823 void lcd_remote_drawpixel(int x, int y)
825 if (((unsigned)x < LCD_REMOTE_WIDTH) && ((unsigned)y < LCD_REMOTE_HEIGHT))
826 lcd_remote_pixelfuncs[drawmode](x, y);
829 /* Draw a line */
830 void lcd_remote_drawline(int x1, int y1, int x2, int y2)
832 int numpixels;
833 int i;
834 int deltax, deltay;
835 int d, dinc1, dinc2;
836 int x, xinc1, xinc2;
837 int y, yinc1, yinc2;
838 lcd_remote_pixelfunc_type *pfunc = lcd_remote_pixelfuncs[drawmode];
840 deltax = abs(x2 - x1);
841 deltay = abs(y2 - y1);
842 xinc2 = 1;
843 yinc2 = 1;
845 if (deltax >= deltay)
847 numpixels = deltax;
848 d = 2 * deltay - deltax;
849 dinc1 = deltay * 2;
850 dinc2 = (deltay - deltax) * 2;
851 xinc1 = 1;
852 yinc1 = 0;
854 else
856 numpixels = deltay;
857 d = 2 * deltax - deltay;
858 dinc1 = deltax * 2;
859 dinc2 = (deltax - deltay) * 2;
860 xinc1 = 0;
861 yinc1 = 1;
863 numpixels++; /* include endpoints */
865 if (x1 > x2)
867 xinc1 = -xinc1;
868 xinc2 = -xinc2;
871 if (y1 > y2)
873 yinc1 = -yinc1;
874 yinc2 = -yinc2;
877 x = x1;
878 y = y1;
880 for (i = 0; i < numpixels; i++)
882 if (((unsigned)x < LCD_REMOTE_WIDTH) && ((unsigned)y < LCD_REMOTE_HEIGHT))
883 pfunc(x, y);
885 if (d < 0)
887 d += dinc1;
888 x += xinc1;
889 y += yinc1;
891 else
893 d += dinc2;
894 x += xinc2;
895 y += yinc2;
900 /* Draw a horizontal line (optimised) */
901 void lcd_remote_hline(int x1, int x2, int y)
903 int x;
904 fb_remote_data *dst, *dst_end;
905 unsigned mask;
906 lcd_remote_blockfunc_type *bfunc;
908 /* direction flip */
909 if (x2 < x1)
911 x = x1;
912 x1 = x2;
913 x2 = x;
916 /* nothing to draw? */
917 if (((unsigned)y >= LCD_REMOTE_HEIGHT) || (x1 >= LCD_REMOTE_WIDTH)
918 || (x2 < 0))
919 return;
921 /* clipping */
922 if (x1 < 0)
923 x1 = 0;
924 if (x2 >= LCD_REMOTE_WIDTH)
925 x2 = LCD_REMOTE_WIDTH-1;
927 bfunc = lcd_remote_blockfuncs[drawmode];
928 dst = &lcd_remote_framebuffer[y>>3][x1];
929 mask = 1 << (y & 7);
931 dst_end = dst + x2 - x1;
933 bfunc(dst++, mask, 0xFFu);
934 while (dst <= dst_end);
937 /* Draw a vertical line (optimised) */
938 void lcd_remote_vline(int x, int y1, int y2)
940 int ny;
941 fb_remote_data *dst;
942 unsigned mask, mask_bottom;
943 lcd_remote_blockfunc_type *bfunc;
945 /* direction flip */
946 if (y2 < y1)
948 ny = y1;
949 y1 = y2;
950 y2 = ny;
953 /* nothing to draw? */
954 if (((unsigned)x >= LCD_REMOTE_WIDTH) || (y1 >= LCD_REMOTE_HEIGHT)
955 || (y2 < 0))
956 return;
958 /* clipping */
959 if (y1 < 0)
960 y1 = 0;
961 if (y2 >= LCD_REMOTE_HEIGHT)
962 y2 = LCD_REMOTE_HEIGHT-1;
964 bfunc = lcd_remote_blockfuncs[drawmode];
965 dst = &lcd_remote_framebuffer[y1>>3][x];
966 ny = y2 - (y1 & ~7);
967 mask = 0xFFu << (y1 & 7);
968 mask_bottom = 0xFFu >> (~ny & 7);
970 for (; ny >= 8; ny -= 8)
972 bfunc(dst, mask, 0xFFu);
973 dst += LCD_REMOTE_WIDTH;
974 mask = 0xFFu;
976 mask &= mask_bottom;
977 bfunc(dst, mask, 0xFFu);
980 /* Draw a rectangular box */
981 void lcd_remote_drawrect(int x, int y, int width, int height)
983 if ((width <= 0) || (height <= 0))
984 return;
986 int x2 = x + width - 1;
987 int y2 = y + height - 1;
989 lcd_remote_vline(x, y, y2);
990 lcd_remote_vline(x2, y, y2);
991 lcd_remote_hline(x, x2, y);
992 lcd_remote_hline(x, x2, y2);
995 /* Fill a rectangular area */
996 void lcd_remote_fillrect(int x, int y, int width, int height)
998 int ny;
999 fb_remote_data *dst, *dst_end;
1000 unsigned mask, mask_bottom;
1001 unsigned bits = 0;
1002 lcd_remote_blockfunc_type *bfunc;
1003 bool fillopt = false;
1005 /* nothing to draw? */
1006 if ((width <= 0) || (height <= 0) || (x >= LCD_REMOTE_WIDTH)
1007 || (y >= LCD_REMOTE_HEIGHT) || (x + width <= 0) || (y + height <= 0))
1008 return;
1010 /* clipping */
1011 if (x < 0)
1013 width += x;
1014 x = 0;
1016 if (y < 0)
1018 height += y;
1019 y = 0;
1021 if (x + width > LCD_REMOTE_WIDTH)
1022 width = LCD_REMOTE_WIDTH - x;
1023 if (y + height > LCD_REMOTE_HEIGHT)
1024 height = LCD_REMOTE_HEIGHT - y;
1026 if (drawmode & DRMODE_INVERSEVID)
1028 if (drawmode & DRMODE_BG)
1030 fillopt = true;
1033 else
1035 if (drawmode & DRMODE_FG)
1037 fillopt = true;
1038 bits = 0xFFu;
1041 bfunc = lcd_remote_blockfuncs[drawmode];
1042 dst = &lcd_remote_framebuffer[y>>3][x];
1043 ny = height - 1 + (y & 7);
1044 mask = 0xFFu << (y & 7);
1045 mask_bottom = 0xFFu >> (~ny & 7);
1047 for (; ny >= 8; ny -= 8)
1049 if (fillopt && (mask == 0xFFu))
1050 memset(dst, bits, width);
1051 else
1053 fb_remote_data *dst_row = dst;
1055 dst_end = dst_row + width;
1057 bfunc(dst_row++, mask, 0xFFu);
1058 while (dst_row < dst_end);
1061 dst += LCD_REMOTE_WIDTH;
1062 mask = 0xFFu;
1064 mask &= mask_bottom;
1066 if (fillopt && (mask == 0xFFu))
1067 memset(dst, bits, width);
1068 else
1070 dst_end = dst + width;
1072 bfunc(dst++, mask, 0xFFu);
1073 while (dst < dst_end);
1077 /* About Rockbox' internal bitmap format:
1079 * A bitmap contains one bit for every pixel that defines if that pixel is
1080 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
1081 * at top.
1082 * The bytes are stored in row-major order, with byte 0 being top left,
1083 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
1084 * 0..7, the second row defines pixel row 8..15 etc.
1086 * This is the same as the internal lcd hw format. */
1088 /* Draw a partial bitmap */
1089 void lcd_remote_bitmap_part(const unsigned char *src, int src_x, int src_y,
1090 int stride, int x, int y, int width, int height)
1091 ICODE_ATTR;
1092 void lcd_remote_bitmap_part(const unsigned char *src, int src_x, int src_y,
1093 int stride, int x, int y, int width, int height)
1095 int shift, ny;
1096 fb_remote_data *dst, *dst_end;
1097 unsigned mask, mask_bottom;
1098 lcd_remote_blockfunc_type *bfunc;
1100 /* nothing to draw? */
1101 if ((width <= 0) || (height <= 0) || (x >= LCD_REMOTE_WIDTH)
1102 || (y >= LCD_REMOTE_HEIGHT) || (x + width <= 0) || (y + height <= 0))
1103 return;
1105 /* clipping */
1106 if (x < 0)
1108 width += x;
1109 src_x -= x;
1110 x = 0;
1112 if (y < 0)
1114 height += y;
1115 src_y -= y;
1116 y = 0;
1118 if (x + width > LCD_REMOTE_WIDTH)
1119 width = LCD_REMOTE_WIDTH - x;
1120 if (y + height > LCD_REMOTE_HEIGHT)
1121 height = LCD_REMOTE_HEIGHT - y;
1123 src += stride * (src_y >> 3) + src_x; /* move starting point */
1124 src_y &= 7;
1125 y -= src_y;
1126 dst = &lcd_remote_framebuffer[y>>3][x];
1127 shift = y & 7;
1128 ny = height - 1 + shift + src_y;
1130 bfunc = lcd_remote_blockfuncs[drawmode];
1131 mask = 0xFFu << (shift + src_y);
1132 mask_bottom = 0xFFu >> (~ny & 7);
1134 if (shift == 0)
1136 bool copyopt = (drawmode == DRMODE_SOLID);
1138 for (; ny >= 8; ny -= 8)
1140 if (copyopt && (mask == 0xFFu))
1141 memcpy(dst, src, width);
1142 else
1144 const unsigned char *src_row = src;
1145 fb_remote_data *dst_row = dst;
1147 dst_end = dst_row + width;
1149 bfunc(dst_row++, mask, *src_row++);
1150 while (dst_row < dst_end);
1153 src += stride;
1154 dst += LCD_REMOTE_WIDTH;
1155 mask = 0xFFu;
1157 mask &= mask_bottom;
1159 if (copyopt && (mask == 0xFFu))
1160 memcpy(dst, src, width);
1161 else
1163 dst_end = dst + width;
1165 bfunc(dst++, mask, *src++);
1166 while (dst < dst_end);
1169 else
1171 dst_end = dst + width;
1174 const unsigned char *src_col = src++;
1175 fb_remote_data *dst_col = dst++;
1176 unsigned mask_col = mask;
1177 unsigned data = 0;
1179 for (y = ny; y >= 8; y -= 8)
1181 data |= *src_col << shift;
1183 if (mask_col & 0xFFu)
1185 bfunc(dst_col, mask_col, data);
1186 mask_col = 0xFFu;
1188 else
1189 mask_col >>= 8;
1191 src_col += stride;
1192 dst_col += LCD_REMOTE_WIDTH;
1193 data >>= 8;
1195 data |= *src_col << shift;
1196 bfunc(dst_col, mask_col & mask_bottom, data);
1198 while (dst < dst_end);
1202 /* Draw a full bitmap */
1203 void lcd_remote_bitmap(const unsigned char *src, int x, int y, int width,
1204 int height)
1206 lcd_remote_bitmap_part(src, 0, 0, width, x, y, width, height);
1209 /* put a string at a given pixel position, skipping first ofs pixel columns */
1210 static void lcd_remote_putsxyofs(int x, int y, int ofs, const unsigned char *str)
1212 unsigned short ch;
1213 unsigned short *ucs;
1214 struct font* pf = font_get(curfont);
1216 ucs = bidi_l2v(str, 1);
1218 while ((ch = *ucs++) != 0 && x < LCD_REMOTE_WIDTH)
1220 int width;
1221 const unsigned char *bits;
1223 /* get proportional width and glyph bits */
1224 width = font_get_width(pf, ch);
1226 if (ofs > width)
1228 ofs -= width;
1229 continue;
1232 bits = font_get_bits(pf, ch);
1234 lcd_remote_bitmap_part(bits, ofs, 0, width, x, y, width - ofs,
1235 pf->height);
1237 x += width - ofs;
1238 ofs = 0;
1242 /* put a string at a given pixel position */
1243 void lcd_remote_putsxy(int x, int y, const unsigned char *str)
1245 lcd_remote_putsxyofs(x, y, 0, str);
1248 /*** line oriented text output ***/
1250 /* put a string at a given char position */
1251 void lcd_remote_puts(int x, int y, const unsigned char *str)
1253 lcd_remote_puts_style_offset(x, y, str, STYLE_DEFAULT, 0);
1256 void lcd_remote_puts_style(int x, int y, const unsigned char *str, int style)
1258 lcd_remote_puts_style_offset(x, y, str, style, 0);
1261 void lcd_remote_puts_offset(int x, int y, const unsigned char *str, int offset)
1263 lcd_remote_puts_style_offset(x, y, str, STYLE_DEFAULT, offset);
1266 /* put a string at a given char position, style, and pixel position,
1267 * skipping first offset pixel columns */
1268 void lcd_remote_puts_style_offset(int x, int y, const unsigned char *str,
1269 int style, int offset)
1271 int xpos,ypos,w,h,xrect;
1272 int lastmode = drawmode;
1274 /* make sure scrolling is turned off on the line we are updating */
1275 scrolling_lines &= ~(1 << y);
1277 if(!str || !str[0])
1278 return;
1280 lcd_remote_getstringsize(str, &w, &h);
1281 xpos = xmargin + x*w / utf8length((char *)str);
1282 ypos = ymargin + y*h;
1283 drawmode = (style & STYLE_INVERT) ?
1284 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
1285 lcd_remote_putsxyofs(xpos, ypos, offset, str);
1286 drawmode ^= DRMODE_INVERSEVID;
1287 xrect = xpos + MAX(w - offset, 0);
1288 lcd_remote_fillrect(xrect, ypos, LCD_REMOTE_WIDTH - xrect, h);
1289 drawmode = lastmode;
1292 /*** scrolling ***/
1294 /* Reverse the invert setting of the scrolling line (if any) at given char
1295 position. Setting will go into affect next time line scrolls. */
1296 void lcd_remote_invertscroll(int x, int y)
1298 struct scrollinfo* s;
1300 (void)x;
1302 s = &scroll[y];
1303 s->invert = !s->invert;
1306 void lcd_remote_stop_scroll(void)
1308 scrolling_lines=0;
1311 void lcd_remote_scroll_speed(int speed)
1313 scroll_ticks = scroll_tick_table[speed];
1316 void lcd_remote_scroll_step(int step)
1318 scroll_step = step;
1321 void lcd_remote_scroll_delay(int ms)
1323 scroll_delay = ms / (HZ / 10);
1326 void lcd_remote_bidir_scroll(int percent)
1328 bidir_limit = percent;
1331 void lcd_remote_puts_scroll(int x, int y, const unsigned char *string)
1333 lcd_remote_puts_scroll_style(x, y, string, STYLE_DEFAULT);
1336 void lcd_remote_puts_scroll_style(int x, int y, const unsigned char *string, int style)
1338 lcd_remote_puts_scroll_style_offset(x, y, string, style, 0);
1341 void lcd_remote_puts_scroll_offset(int x, int y, const unsigned char *string, int offset)
1343 lcd_remote_puts_scroll_style_offset(x, y, string, STYLE_DEFAULT, offset);
1346 void lcd_remote_puts_scroll_style_offset(int x, int y, const unsigned char *string,
1347 int style, int offset)
1349 struct scrollinfo* s;
1350 int w, h;
1352 s = &scroll[y];
1354 s->start_tick = current_tick + scroll_delay;
1355 s->invert = false;
1356 if (style & STYLE_INVERT) {
1357 s->invert = true;
1358 lcd_remote_puts_style_offset(x,y,string,STYLE_INVERT,offset);
1360 else
1361 lcd_remote_puts_offset(x,y,string,offset);
1363 lcd_remote_getstringsize(string, &w, &h);
1365 if (LCD_REMOTE_WIDTH - x * 8 - xmargin < w) {
1366 /* prepare scroll line */
1367 char *end;
1369 memset(s->line, 0, sizeof s->line);
1370 strcpy(s->line, (char *)string);
1372 /* get width */
1373 s->width = lcd_remote_getstringsize((unsigned char *)s->line, &w, &h);
1375 /* scroll bidirectional or forward only depending on the string
1376 width */
1377 if ( bidir_limit ) {
1378 s->bidir = s->width < (LCD_REMOTE_WIDTH - xmargin) *
1379 (100 + bidir_limit) / 100;
1381 else
1382 s->bidir = false;
1384 if (!s->bidir) { /* add spaces if scrolling in the round */
1385 strcat(s->line, " ");
1386 /* get new width incl. spaces */
1387 s->width = lcd_remote_getstringsize((unsigned char *)s->line, &w, &h);
1390 end = strchr(s->line, '\0');
1391 strncpy(end, (char *)string, LCD_REMOTE_WIDTH/2);
1393 s->len = utf8length((char *)string);
1394 s->offset = offset;
1395 s->startx = xmargin + x * s->width / s->len;;
1396 s->backward = false;
1397 scrolling_lines |= (1<<y);
1399 else
1400 /* force a bit switch-off since it doesn't scroll */
1401 scrolling_lines &= ~(1<<y);
1404 static void scroll_thread(void)
1406 struct font* pf;
1407 struct scrollinfo* s;
1408 long next_tick = current_tick;
1409 long delay = 0;
1410 int index;
1411 int xpos, ypos;
1412 int lastmode;
1413 #ifndef SIMULATOR
1414 struct event ev;
1415 #endif
1417 /* initialize scroll struct array */
1418 scrolling_lines = 0;
1420 while ( 1 ) {
1422 #ifdef SIMULATOR
1423 sleep(delay);
1424 #else
1425 if (remote_initialized)
1426 queue_wait_w_tmo(&remote_scroll_queue, &ev, delay);
1427 else
1428 queue_wait(&remote_scroll_queue, &ev);
1430 switch (ev.id)
1432 case REMOTE_INIT_LCD:
1433 remote_lcd_init();
1434 lcd_remote_update();
1435 break;
1437 case REMOTE_DEINIT_LCD:
1438 CLK_LO;
1439 CS_HI;
1440 remote_initialized = false;
1441 break;
1444 delay = next_tick - current_tick - 1;
1445 if (delay >= 0)
1446 continue;
1447 #endif
1448 for ( index = 0; index < SCROLLABLE_LINES; index++ ) {
1449 /* really scroll? */
1450 if ( !(scrolling_lines&(1<<index)) )
1451 continue;
1453 s = &scroll[index];
1455 /* check pause */
1456 if (TIME_BEFORE(current_tick, s->start_tick))
1457 continue;
1459 if (s->backward)
1460 s->offset -= scroll_step;
1461 else
1462 s->offset += scroll_step;
1464 pf = font_get(curfont);
1465 xpos = s->startx;
1466 ypos = ymargin + index * pf->height;
1468 if (s->bidir) { /* scroll bidirectional */
1469 if (s->offset <= 0) {
1470 /* at beginning of line */
1471 s->offset = 0;
1472 s->backward = false;
1473 s->start_tick = current_tick + scroll_delay * 2;
1475 if (s->offset >= s->width - (LCD_REMOTE_WIDTH - xpos)) {
1476 /* at end of line */
1477 s->offset = s->width - (LCD_REMOTE_WIDTH - xpos);
1478 s->backward = true;
1479 s->start_tick = current_tick + scroll_delay * 2;
1482 else {
1483 /* scroll forward the whole time */
1484 if (s->offset >= s->width)
1485 s->offset %= s->width;
1488 lastmode = drawmode;
1489 drawmode = s->invert ?
1490 (DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
1491 lcd_remote_putsxyofs(xpos, ypos, s->offset, s->line);
1492 drawmode = lastmode;
1493 lcd_remote_update_rect(xpos, ypos, LCD_REMOTE_WIDTH - xpos, pf->height);
1496 next_tick += scroll_ticks;
1497 delay = next_tick - current_tick - 1;
1498 if (delay < 0)
1500 next_tick = current_tick + 1;
1501 delay = 0;