OSDN Git Service

* public snapshot of sid simulator
[pf3gnuchains/pf3gnuchains3x.git] / sid / component / lcd / HD44780U.cxx
1 // HD44780U.cxx - description.  -*- C++ -*-
2
3 // Copyright (C) 1999, 2000 Red Hat.
4 // This file is part of SID and is licensed under the GPL.
5 // See the file COPYING.SID for conditions for redistribution.
6
7 #include "HD44780U.h"
8
9 #include <sidso.h>
10 #include <stdio.h>
11
12 using sid::component_library;
13 using sid::COMPONENT_LIBRARY_MAGIC;
14 using sidutil::make_attribute;
15 using sidutil::parse_attribute;
16
17 extern void init_rom( unsigned char rom[][8] );
18 extern void init_rom_japan( unsigned char rom[][8] );
19 extern void init_rom_europe( unsigned char rom[][8] );
20 extern void init_rom_5X10( unsigned char rom[][11] );
21
22 HD44780U :: HD44780U( bool use_japan_rom ) : 
23   busif( this, &(HD44780U::busRead), &(HD44780U::busWrite) ),
24   refresh_sync( "refresh-sync", this, &(HD44780U::refresh) ),
25   trigger_mgr( this )
26 {
27   int i, j;
28
29   add_pin( "row-col", &row_col_pin );
30   add_attribute( "row-col", &row_col_pin, "pin" );
31   trigger_mgr.add_watchable_attribute( "row-col" );
32   categorize( "row-col", "watchable" );
33
34   add_pin( "FR", &frame_pin );
35   add_attribute( "FR", &frame_pin, "pin" );
36   trigger_mgr.add_watchable_attribute( "FR" );
37   categorize( "FR", "watchable" );
38
39   add_bus( "bus", &busif );
40
41   add_attribute( "IR", &ir, "register" );
42   trigger_mgr.add_watchable_value( "IR", &ir );
43   categorize( "IR", "watchable" );
44
45   add_attribute( "DR", &dr, "register" );
46   trigger_mgr.add_watchable_value( "DR", &dr );
47   categorize( "DR", "watchable" );
48
49   add_attribute( "AC", &ac, "register" );
50   trigger_mgr.add_watchable_value( "AC", &ac );
51   categorize( "AC", "watchable" );
52
53   refresh_period = 500;         // default is 500 msec
54   add_attribute( "refresh-period-msec", &refresh_period, "setting" );
55
56   add_attribute_virtual ("state-snapshot", this,
57                          &HD44780U::save_state, &HD44780U::restore_state);
58
59   verbose = false;
60   add_attribute( "verbose?", &verbose, "setting" );
61
62   current_schedule = NO_SCHED;
63
64   for( i=0; i<ROM_SIZE; i++ )
65     for( j=0; j<8; j++ ) rom[i][j] = 0;
66
67   if( use_japan_rom ) {
68     init_rom( rom );
69     init_rom_japan( rom );
70     init_rom_5X10( rom10 );
71   }
72   else
73     init_rom_europe( rom );
74
75   use_europe_rom = !use_japan_rom;
76
77   reset();
78 }
79
80 void
81 HD44780U :: reset() {
82   int i;
83
84   two_lines = 0;
85   big_font = 0;
86   display = 0;
87   cursor = 0;
88   blink = 0;
89   incr = 1;
90
91   blinking_chars_are_visible = false;
92   shift_on_write = false;
93
94   access_cgram = false;
95   addr_bound = DRAM_SIZE;
96
97   ac = 0;
98   display_offset = 0;
99
100   for( i=0; i<DRAM_SIZE; i++ ) dram[i] = ' ';
101   for( i=0; i<CGRAM_SIZE; i++ ) cgram[i] = 0;
102 }
103
104 inline void
105 HD44780U :: incr_ac() {
106   // I don't actually know if ac is supposed to wrap
107   ac = ac + incr;
108   if( ac == ((host_int_1) -1) )
109     ac = addr_bound;
110   else if( ac == addr_bound )
111     ac = 0;
112 }
113
114 sid::bus::status
115 HD44780U :: busRead( host_int_4 laddr, host_int_1& data ) {
116   int val;
117
118   if( laddr == 0 ) {
119     // Read the address counter. Note that the busy flag is never set.
120     if( two_lines && !access_cgram && (ac > 0x27) )
121       val = ac + 0x18;
122     else
123       val = ac;
124
125     data = val;
126     return sid::bus::ok;
127   }
128   else if( laddr == 1 ) {
129     // Read the data from the DR, adjust AC for next read, and reload DR.
130     data = dr;
131
132     incr_ac();
133
134     if( access_cgram )
135       dr = cgram[ac];
136     else 
137       dr = dram[ac];
138
139     // check for triggerpoints
140     trigger_mgr.check_and_dispatch();
141
142     return sid::bus::ok;
143   }
144
145   return sid::bus::unmapped;
146 }
147
148 sid::bus::status
149 HD44780U :: busWrite( host_int_4 laddr, host_int_1 data ) {
150   if( laddr == 0 ) {
151     // write the IR (busy is not used, so don't need to worry about it)
152     ir = data;
153     execute( ir );
154   }
155   else if( laddr == 1 ) {
156     // Write the data to the DR and from there to DRAM/CGRAM.
157     // Then increment AC for next write.
158     dr = data;
159
160     if( access_cgram ) {
161       // printf( "write CGRAM: 0x%02x\n", dr );
162
163       cgram[ac] = dr;
164     }
165     else {
166       // printf( "write DDRAM %d: 0x%02x (%c)\n", ac, dr, dr );
167
168       dram[ac] = dr;
169
170       if( shift_on_write )
171         display_offset += incr;
172     }
173
174     incr_ac();
175   }
176   else
177     return sid::bus::unmapped;
178
179   if( blink ) {
180     switch( current_schedule ) {
181     case REGULAR_SCHED:
182       break;                    // already set up
183
184     case ONE_TIME_SCHED:
185       refresh_sync.cancel();    // cancel current schedule
186       // fall-through
187
188     case NO_SCHED:
189       // printf( "setting up regular schedule\n" );
190       refresh_sync.schedule_regular( refresh_period );
191       current_schedule = REGULAR_SCHED;
192       break;
193
194     default:
195       break;                    // ??
196     }
197   }
198   else if( current_schedule == REGULAR_SCHED ) {
199     // The blinkin' lights have stopped - cancel the schedule
200     refresh_sync.cancel();
201     current_schedule = NO_SCHED;
202   }
203
204   if( current_schedule == NO_SCHED ) {
205    /* We just wrote something, which could change the display.
206     * Make sure we refresh it so we can see the change (if any).
207     */
208     refresh_sync.schedule_irregular( refresh_period );
209     current_schedule = ONE_TIME_SCHED;
210   }
211
212   // check for triggerpoints
213   trigger_mgr.check_and_dispatch ();
214
215   if( verbose )
216     cerr << "HD44780U::write - schedule " << current_schedule << endl;
217
218   return sid::bus::ok;
219 }
220
221 void
222 HD44780U :: execute( unsigned char val ) {
223   if( verbose )
224     cerr << "HD44780U::execute "
225          << make_numeric_attribute (val, ios::hex | ios::showbase)
226          << endl;
227
228   if( val == 1 ) {                      // clear display
229     for( int i=0; i<DRAM_SIZE; i++ )    // perform partial reset
230       dram[i] = ' ';
231
232     ac = 0;
233     incr = 1;
234     access_cgram = false;
235     addr_bound = DRAM_SIZE;
236     display_offset = 0;
237   }
238   else if( (val & ~1) == 0x2 ) {        // return home
239     ac = 0;
240     access_cgram = false;
241     addr_bound = DRAM_SIZE;
242     display_offset = 0;
243   }
244   else if( (val & ~3) == 0x4 ) {        // entry mode set
245     if( val & 1 )                       // shift display on DDRAM writes
246       shift_on_write = true;
247     else
248       shift_on_write = false;
249
250     if( val & 0x2 )
251       incr = 1;
252     else
253       incr = -1;
254   }
255   else if( (val & ~7) == 0x08 ) {       // display on/off control
256     display = val & 4;
257     cursor  = val & 2;
258     blink   = val & 1;
259   }
260   else if( (val & ~0x0f) == 0x10 ) {    // cursor/display shift
261     switch( (val >> 2) & 0x3 ) {
262     case 0:     // shift cursor left
263       --ac;
264       if( ac == ((host_int_1) -1) )
265         ac = addr_bound;
266       break;
267     case 1:     // shift cursor right
268       ++ac;
269       if( ac == addr_bound )
270         ac = 0;
271       break;
272     case 2:     // shift display left
273       ++display_offset;
274       if( two_lines ) {
275         if( display_offset >= 40 )
276           display_offset = 0;
277       }
278       else {
279         if( display_offset >= 80 )
280           display_offset = 0;
281       }
282       break;
283     case 3:     // shift display right
284       --display_offset;
285       if( two_lines ) {
286         if( display_offset <= -40 )
287           display_offset = 0;
288       }
289       else {
290         if( display_offset <= -80 )
291           display_offset = 0;
292       }
293       break;
294     }
295   }
296   else if( (val & ~0x1f) == 0x20 ) {    // function set
297     two_lines = val & 0x08;
298     big_font  = val & 4;
299   }
300   else if( (val & ~0x3f) == 0x40 ) {    // set CGRAM address
301     ac = val & 0x3f;
302     access_cgram = true;
303     addr_bound = CGRAM_SIZE;
304
305     dr = cgram[ac];
306   }
307   else if( (val & ~0x7f) == 0x80 ) {    // set DDRAM address
308     val &= 0x7f;
309
310     if( two_lines && (val >= 0x40) )
311       ac = val - 0x18;
312     else
313       ac = val;
314
315     access_cgram = false;
316     addr_bound = DRAM_SIZE;
317
318     dr = dram[ac];
319   }
320 }
321
322 void
323 HD44780U :: refresh() {
324   int i, j, k;
325   unsigned char *cur, bits;
326   unsigned addr;
327   int last_row;
328   bool using_big_font;
329   const unsigned char first_5X10 = 0xe0;
330
331   if( verbose )
332     cerr << "HD44780U::refresh" << endl;
333
334   frame_pin.drive( 1 );                 // start of refresh
335
336   if( !display ) {                      // display is off
337     frame_pin.drive( 0 );
338
339     // No more refreshes til the display is re-enabled
340     refresh_sync.cancel();
341     current_schedule = NO_SCHED;
342
343     if( verbose )
344       cerr << "HD44780U::refresh - display is off" << endl;
345
346     return;
347   }
348
349   if( current_schedule == ONE_TIME_SCHED ) {
350     // This is the callback for the scheduler, so our one-time is now.
351     current_schedule = NO_SCHED;
352   }
353
354   // write the first line
355   if( big_font )
356     last_row = 11;
357   else
358     last_row = 8;
359
360   if( verbose )
361     cerr << "HD44780U::refresh - first row: ";
362
363   for( i=0; i<8; i++ ) {
364     addr = display_offset + i;
365     if( (display_offset + i) < 0 ) {
366       if( two_lines )
367         addr = 0x28 + (display_offset + i);
368       else
369         addr = 0x50 + (display_offset + i);
370     }
371
372     if( (ac == addr) && blink && !blinking_chars_are_visible )
373       continue;
374
375     if( dram[addr] < 0x10 ) {
376       cur = &(cgram[dram[addr] << 3]);
377       using_big_font = (last_row == 11);
378     }
379     else if( (dram[addr] >= first_5X10) && !use_europe_rom ) {
380       cur = &(rom10[dram[addr] - first_5X10][0]);
381       using_big_font = (last_row == 11);
382     }
383     else {
384       cur = &(rom[dram[addr]][0]);
385       using_big_font = false;
386     }
387
388     for( j=0; j<last_row; j++ ) {
389       if( (j == last_row-1) && cursor && (ac == addr) )
390         bits = 0x1f;
391       else if( j>7 && !using_big_font )
392         continue;
393       else
394         bits = cur[j];
395
396       if( verbose && bits )
397         cerr << "[" << i << "," << j << "=" 
398              << make_numeric_attribute (bits, ios::hex | ios::showbase)
399              << "]";
400
401       for( k=4; k>=0; k-- ) {
402         if( bits & 0x1 )
403           row_col_pin.drive( (j << 16) | (i*5 + k ) );
404
405         bits >>= 1;
406       }
407     }
408   }
409
410   if( verbose )
411     cerr << endl;
412
413   if( two_lines ) {
414     for( i=0; i<8; i++ ) {
415       addr = 0x28 + display_offset + i;
416       if( (display_offset + i) < 0 )
417         addr = 0x50 + (display_offset + i);
418
419       if( (ac == addr) && blink && !blinking_chars_are_visible )
420         continue;
421
422       if( (dram[addr] >= first_5X10) && !use_europe_rom )
423         cur = &(rom10[dram[addr] - first_5X10][0]);
424       else if( dram[addr] < 0x10 ) 
425         cur = &(cgram[dram[addr] << 3]);
426       else
427         cur = &(rom[dram[addr]][0]);
428
429       for( j=0; j<8; j++ ) {
430         if( (j == 7) && cursor && (ac == addr) )
431           bits = 0x1f;
432         else
433           bits = cur[j];
434
435         for( k=4; k>=0; k-- ) {
436           if( bits & 0x1 )
437             row_col_pin.drive( ((8 + j) << 16) | (i*5 + k ) );
438
439           bits >>= 1;
440         }
441       }
442     }
443   }
444
445   frame_pin.drive( 0 );
446
447   blinking_chars_are_visible = !blinking_chars_are_visible;
448 }
449
450 string
451 HD44780U :: save_state() { 
452   return make_attribute ( *this );
453 }
454
455 component::status
456 HD44780U :: restore_state( const string& state)
457 {
458   return parse_attribute( state, *this );
459 }
460
461 ostream& 
462 operator << ( ostream& op, const HD44780U& copy_obj ) {       
463   op << "IR " << host_int_4 (copy_obj.ir) << ":";
464   op << "DR " << host_int_4 (copy_obj.dr) << ":";
465   op << "AC " << host_int_4 (copy_obj.ac) << ":";
466   op << "Lines " << (host_int_4) copy_obj.two_lines << ":";
467   op << "Font " << (host_int_4) copy_obj.big_font << ":";
468   op << "Display " << (host_int_4) copy_obj.display << ":";
469   op << "Cursor " << (host_int_4) copy_obj.cursor << ":";
470   op << "Blink " << (host_int_4) copy_obj.blink << ":";
471   op << "Incr " << (host_int_4) copy_obj.incr << ":";
472
473   op << "DDRAM ";
474   for (int j=0; j < (DRAM_SIZE - 1); j++ )
475      op << (host_int_4) copy_obj.dram[j] << " ";
476   op << (host_int_4) copy_obj.dram[DRAM_SIZE - 1] << ":";
477
478   op << "CGRAM ";
479   for (int j=0; j < (CGRAM_SIZE - 1); j++ )
480      op << (host_int_4) copy_obj.cgram[j] << " ";
481   op << (host_int_4) copy_obj.cgram[CGRAM_SIZE - 1] << ":";
482
483   op << "AccessCgram " << copy_obj.access_cgram << ":";
484   op << "AddrBound "   << copy_obj.addr_bound << ":";
485   op << "DisOffset " << copy_obj.display_offset << ":";
486   op << "ShiftOnWrite " << copy_obj.shift_on_write << ":";
487   op << "EuropeRom " << copy_obj.use_europe_rom << ":";
488   op << "BlinkVisible " << copy_obj.blinking_chars_are_visible << ":";
489   op << "CurrSched " << copy_obj.current_schedule << ":";
490   op << "RefreshPeriod " << copy_obj.refresh_period << ":";
491   op << "RowColPin " << copy_obj.row_col_pin << ":";
492   op << "FramePin " << copy_obj.frame_pin;
493
494   return op;
495 }
496
497 #define GET_TOKEN(_str_) {              \
498         string token;                   \
499         ip >> token;                    \
500         if( token != _str_ ) {          \
501           ip.setstate( ios::badbit );   \
502           return ip;                    \
503         }}      
504           
505
506 #define GET_SEPARATOR   {               \
507         char sep;                       \
508         ip.get( sep );                  \
509         if( sep != ':' ) {              \
510           ip.setstate( ios::badbit );   \
511           return ip;                    \
512         }}
513
514 istream &
515 operator >> ( istream& ip, HD44780U& ovwrite_obj ) {
516   host_int_4 temp_val;
517   int j;
518
519   GET_TOKEN( "IR" );    
520   ip >> temp_val;
521   ovwrite_obj.ir = temp_val;
522   GET_SEPARATOR;        
523   
524   GET_TOKEN( "DR" );    
525   ip >> temp_val;
526   ovwrite_obj.dr = temp_val;
527   GET_SEPARATOR;        
528
529   GET_TOKEN( "AC" );    
530   ip >> temp_val;
531   ovwrite_obj.ac = temp_val;
532   GET_SEPARATOR;        
533
534   GET_TOKEN( "Lines" ); 
535   ip >> temp_val;
536   ovwrite_obj.two_lines = temp_val;
537   GET_SEPARATOR;
538
539   GET_TOKEN( "Font" );  
540   ip >> temp_val;
541   ovwrite_obj.big_font = temp_val;
542   GET_SEPARATOR;        
543
544   GET_TOKEN( "Display" );       
545   ip >> temp_val;
546   ovwrite_obj.display = temp_val;
547   GET_SEPARATOR;        
548
549   GET_TOKEN( "Cursor" );        
550   ip >> temp_val;
551   ovwrite_obj.cursor = temp_val;
552   GET_SEPARATOR;        
553
554   GET_TOKEN( "Blink" ); 
555   ip >> temp_val;
556   ovwrite_obj.blink = temp_val;
557   GET_SEPARATOR;        
558
559   GET_TOKEN( "Incr" );  
560   ip >> temp_val;
561   ovwrite_obj.incr = temp_val;
562   GET_SEPARATOR;        
563
564   GET_TOKEN( "DDRAM" ); 
565   for( j=0; j<DRAM_SIZE; j++) {
566     ip >> temp_val;
567     ovwrite_obj.dram[j] = temp_val;
568   }
569   GET_SEPARATOR;        
570
571   GET_TOKEN( "CGRAM" ); 
572   for( j=0; j<CGRAM_SIZE; j++ ) {
573     ip >> temp_val;
574     ovwrite_obj.cgram[j] = temp_val;
575   }
576   GET_SEPARATOR;        
577
578   GET_TOKEN( "AccessCgram" );   
579   ip >> ovwrite_obj.access_cgram;
580   GET_SEPARATOR;        
581
582   GET_TOKEN( "AddrBound" );
583   ip >> ovwrite_obj.addr_bound;
584   GET_SEPARATOR;        
585
586   GET_TOKEN( "DisOffset" );
587   ip >> ovwrite_obj.display_offset;
588   GET_SEPARATOR;        
589
590   GET_TOKEN( "ShiftOnWrite" );
591   ip >> ovwrite_obj.shift_on_write;
592   GET_SEPARATOR;        
593
594   GET_TOKEN( "EuropeRom" );
595   ip >> ovwrite_obj.use_europe_rom;
596   GET_SEPARATOR;        
597
598   GET_TOKEN( "BlinkVisible" );
599   ip >> ovwrite_obj.blinking_chars_are_visible;
600   GET_SEPARATOR;        
601
602   GET_TOKEN( "CurrSched" );
603   ip >> ovwrite_obj.current_schedule;
604   GET_SEPARATOR;        
605
606   GET_TOKEN( "RefreshPeriod" );
607   ip >> ovwrite_obj.refresh_period;
608   GET_SEPARATOR;
609
610   GET_TOKEN("RowColPin");
611   ip >> ovwrite_obj.row_col_pin;
612   GET_SEPARATOR;
613
614   GET_TOKEN("FramePin");
615   ip >> ovwrite_obj.frame_pin;
616
617   return ip;
618 }
619
620 // Standard DLL wrapper-stuff
621
622 static vector<string>
623 HD44780U_ListTypes() {
624   vector<string> types;
625
626   types.push_back( string( "hw-lcd-hd44780u-a00" ) );
627   types.push_back( string( "hw-lcd-hd44780u-a02" ) );
628
629   return types;
630 }
631
632 static component*
633 HD44780U_Create( const string& typeName ) {
634   if( typeName == "hw-lcd-hd44780u-a00" )
635     return new HD44780U( true );
636   else if( typeName == "hw-lcd-hd44780u-a02" )
637     return new HD44780U( false );
638   else
639     return 0;
640 }
641
642 static void
643 HD44780U_Delete( component* c ) {
644   delete dynamic_cast<HD44780U*>(c);
645 }
646
647 // static object
648 extern const component_library hd44780u_component_library;
649
650 const component_library hd44780u_component_library DLLEXPORT = 
651 {
652   COMPONENT_LIBRARY_MAGIC,
653   & HD44780U_ListTypes, 
654   & HD44780U_Create,
655   & HD44780U_Delete
656 };