Add a 7 segments + led display, by Herve Poussineau.
[qemu] / hw / jazz_led.c
1 /*
2  * QEMU JAZZ LED emulator.
3  * 
4  * Copyright (c) 2007 HervĂ© Poussineau
5  * 
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24
25 #include "vl.h"
26 #include "pixel_ops.h"
27
28 //#define DEBUG_LED
29
30 typedef enum {
31     REDRAW_NONE = 0, REDRAW_SEGMENTS = 1, REDRAW_BACKGROUND = 2,
32 } screen_state_t;
33
34 typedef struct LedState {
35     target_phys_addr_t base;
36     uint8_t segments;
37     DisplayState *ds;
38     screen_state_t state;
39 } LedState;
40
41 static uint32_t led_readb(void *opaque, target_phys_addr_t addr)
42 {
43     LedState *s = opaque;
44     int relative_addr = addr - s->base;
45     uint32_t val;
46
47     switch (relative_addr) {
48         case 0:
49             val = s->segments;
50             break;
51         default:
52 #ifdef DEBUG_LED
53             printf("jazz led: invalid read [0x%x]\n", relative_addr);
54 #endif
55             val = 0;
56     }
57
58     return val;
59 }
60
61 static uint32_t led_readw(void *opaque, target_phys_addr_t addr)
62 {
63     uint32_t v;
64 #ifdef TARGET_WORDS_BIGENDIAN
65     v = led_readb(opaque, addr) << 8;
66     v |= led_readb(opaque, addr + 1);
67 #else
68     v = led_readb(opaque, addr);
69     v |= led_readb(opaque, addr + 1) << 8;
70 #endif
71     return v;
72 }
73
74 static uint32_t led_readl(void *opaque, target_phys_addr_t addr)
75 {
76     uint32_t v;
77 #ifdef TARGET_WORDS_BIGENDIAN
78     v = led_readb(opaque, addr) << 24;
79     v |= led_readb(opaque, addr + 1) << 16;
80     v |= led_readb(opaque, addr + 2) << 8;
81     v |= led_readb(opaque, addr + 3);
82 #else
83     v = led_readb(opaque, addr);
84     v |= led_readb(opaque, addr + 1) << 8;
85     v |= led_readb(opaque, addr + 2) << 16;
86     v |= led_readb(opaque, addr + 3) << 24;
87 #endif
88     return v;
89 }
90
91 static void led_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
92 {
93     LedState *s = opaque;
94     int relative_addr = addr - s->base;
95
96     switch (relative_addr) {
97         case 0:
98             s->segments = val;
99             s->state |= REDRAW_SEGMENTS;
100             break;
101         default:
102 #ifdef DEBUG_LED
103             printf("jazz led: invalid write of 0x%02x at [0x%x]\n", val, relative_addr);
104 #endif
105             break;
106     }
107 }
108
109 static void led_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
110 {
111 #ifdef TARGET_WORDS_BIGENDIAN
112     led_writeb(opaque, addr, (val >> 8) & 0xff);
113     led_writeb(opaque, addr + 1, val & 0xff);
114 #else
115     led_writeb(opaque, addr, val & 0xff);
116     led_writeb(opaque, addr + 1, (val >> 8) & 0xff);
117 #endif
118 }
119
120 static void led_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
121 {
122 #ifdef TARGET_WORDS_BIGENDIAN
123     led_writeb(opaque, addr, (val >> 24) & 0xff);
124     led_writeb(opaque, addr + 1, (val >> 16) & 0xff);
125     led_writeb(opaque, addr + 2, (val >> 8) & 0xff);
126     led_writeb(opaque, addr + 3, val & 0xff);
127 #else
128     led_writeb(opaque, addr, val & 0xff);
129     led_writeb(opaque, addr + 1, (val >> 8) & 0xff);
130     led_writeb(opaque, addr + 2, (val >> 16) & 0xff);
131     led_writeb(opaque, addr + 3, (val >> 24) & 0xff);
132 #endif
133 }
134
135 static CPUReadMemoryFunc *led_read[3] = {
136     led_readb,
137     led_readw,
138     led_readl,
139 };
140
141 static CPUWriteMemoryFunc *led_write[3] = {
142     led_writeb,
143     led_writew,
144     led_writel,
145 };
146
147 /***********************************************************/
148 /* jazz_led display */
149
150 static void draw_horizontal_line(DisplayState *ds, int posy, int posx1, int posx2, uint32_t color)
151 {
152     uint8_t *d;
153     int x, bpp;
154
155     bpp = (ds->depth + 7) >> 3;
156     d = ds->data + ds->linesize * posy + bpp * posx1;
157     switch(bpp) {
158         case 1:
159             for (x = posx1; x <= posx2; x++) {
160                 *((uint8_t *)d) = color;
161                 d++;
162             }
163             break;
164         case 2:
165             for (x = posx1; x <= posx2; x++) {
166                 *((uint16_t *)d) = color;
167                 d += 2;
168             }
169             break;
170         case 4:
171             for (x = posx1; x <= posx2; x++) {
172                 *((uint32_t *)d) = color;
173                 d += 4;
174             }
175             break;
176     }
177 }
178
179 static void draw_vertical_line(DisplayState *ds, int posx, int posy1, int posy2, uint32_t color)
180 {
181     uint8_t *d;
182     int y, bpp;
183
184     bpp = (ds->depth + 7) >> 3;
185     d = ds->data + ds->linesize * posy1 + bpp * posx;
186     switch(bpp) {
187         case 1:
188             for (y = posy1; y <= posy2; y++) {
189                 *((uint8_t *)d) = color;
190                 d += ds->linesize;
191             }
192             break;
193         case 2:
194             for (y = posy1; y <= posy2; y++) {
195                 *((uint16_t *)d) = color;
196                 d += ds->linesize;
197             }
198             break;
199         case 4:
200             for (y = posy1; y <= posy2; y++) {
201                 *((uint32_t *)d) = color;
202                 d += ds->linesize;
203             }
204             break;
205     }
206 }
207
208 static void jazz_led_update_display(void *opaque)
209 {
210     LedState *s = opaque;
211     DisplayState *ds = s->ds;
212     uint8_t *d1;
213     uint32_t color_segment, color_led;
214     int y, bpp;
215
216     if (s->state & REDRAW_BACKGROUND) {
217         /* clear screen */
218         bpp = (ds->depth + 7) >> 3;
219         d1 = ds->data;
220         for (y = 0; y < ds->height; y++) {
221             memset(d1, 0x00, ds->width * bpp);
222             d1 += ds->linesize;
223         }
224     }
225
226     if (s->state & REDRAW_SEGMENTS) {
227         /* set colors according to bpp */
228         switch (ds->depth) {
229             case 8:
230                 color_segment = rgb_to_pixel8(0xaa, 0xaa, 0xaa);
231                 color_led = rgb_to_pixel8(0x00, 0xff, 0x00);
232                 break;
233             case 15:
234                 color_segment = rgb_to_pixel15(0xaa, 0xaa, 0xaa);
235                 color_led = rgb_to_pixel15(0x00, 0xff, 0x00);
236                 break;
237             case 16:
238                 color_segment = rgb_to_pixel16(0xaa, 0xaa, 0xaa);
239                 color_led = rgb_to_pixel16(0x00, 0xff, 0x00);
240             case 24:
241                 color_segment = rgb_to_pixel24(0xaa, 0xaa, 0xaa);
242                 color_led = rgb_to_pixel24(0x00, 0xff, 0x00);
243                 break;
244             case 32:
245                 color_segment = rgb_to_pixel32(0xaa, 0xaa, 0xaa);
246                 color_led = rgb_to_pixel32(0x00, 0xff, 0x00);
247                 break;
248             default:
249                 return;
250         }
251
252         /* display segments */
253         draw_horizontal_line(ds, 40, 10, 40, (s->segments & 0x02) ? color_segment : 0);
254         draw_vertical_line(ds, 10, 10, 40, (s->segments & 0x04) ? color_segment : 0);
255         draw_vertical_line(ds, 10, 40, 70, (s->segments & 0x08) ? color_segment : 0);
256         draw_horizontal_line(ds, 70, 10, 40, (s->segments & 0x10) ? color_segment : 0);
257         draw_vertical_line(ds, 40, 40, 70, (s->segments & 0x20) ? color_segment : 0);
258         draw_vertical_line(ds, 40, 10, 40, (s->segments & 0x40) ? color_segment : 0);
259         draw_horizontal_line(ds, 10, 10, 40, (s->segments & 0x80) ? color_segment : 0);
260
261         /* display led */
262         if (!(s->segments & 0x01))
263             color_led = 0; /* black */
264         draw_horizontal_line(ds, 68, 50, 50, color_led);
265         draw_horizontal_line(ds, 69, 49, 51, color_led);
266         draw_horizontal_line(ds, 70, 48, 52, color_led);
267         draw_horizontal_line(ds, 71, 49, 51, color_led);
268         draw_horizontal_line(ds, 72, 50, 50, color_led);
269     }
270
271     s->state = REDRAW_NONE;
272     dpy_update(ds, 0, 0, ds->width, ds->height);
273 }
274
275 static void jazz_led_invalidate_display(void *opaque)
276 {
277     LedState *s = opaque;
278     s->state |= REDRAW_SEGMENTS | REDRAW_BACKGROUND;
279 }
280
281 static void jazz_led_screen_dump(void *opaque, const char *filename)
282 {
283     printf("jazz_led_screen_dump() not implemented\n");
284 }
285
286 void jazz_led_init(DisplayState *ds, target_phys_addr_t base)
287 {
288     LedState *s;
289     int io;
290
291     s = qemu_mallocz(sizeof(LedState));
292     if (!s)
293         return;
294
295     s->base = base;
296     s->ds = ds;
297     s->state = REDRAW_SEGMENTS | REDRAW_BACKGROUND;
298
299     io = cpu_register_io_memory(0, led_read, led_write, s);
300     cpu_register_physical_memory(s->base, 1, io);
301
302     graphic_console_init(ds, jazz_led_update_display, jazz_led_invalidate_display, jazz_led_screen_dump, s);
303 }