1 // HD44780U.cxx - description. -*- C++ -*-
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.
12 using sid::component_library;
13 using sid::COMPONENT_LIBRARY_MAGIC;
14 using sidutil::make_attribute;
15 using sidutil::parse_attribute;
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] );
22 HD44780U :: HD44780U( bool use_japan_rom ) :
23 busif( this, &(HD44780U::busRead), &(HD44780U::busWrite) ),
24 refresh_sync( "refresh-sync", this, &(HD44780U::refresh) ),
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" );
34 add_pin( "FR", &frame_pin );
35 add_attribute( "FR", &frame_pin, "pin" );
36 trigger_mgr.add_watchable_attribute( "FR" );
37 categorize( "FR", "watchable" );
39 add_bus( "bus", &busif );
41 add_attribute( "IR", &ir, "register" );
42 trigger_mgr.add_watchable_value( "IR", &ir );
43 categorize( "IR", "watchable" );
45 add_attribute( "DR", &dr, "register" );
46 trigger_mgr.add_watchable_value( "DR", &dr );
47 categorize( "DR", "watchable" );
49 add_attribute( "AC", &ac, "register" );
50 trigger_mgr.add_watchable_value( "AC", &ac );
51 categorize( "AC", "watchable" );
53 refresh_period = 500; // default is 500 msec
54 add_attribute( "refresh-period-msec", &refresh_period, "setting" );
56 add_attribute_virtual ("state-snapshot", this,
57 &HD44780U::save_state, &HD44780U::restore_state);
60 add_attribute( "verbose?", &verbose, "setting" );
62 current_schedule = NO_SCHED;
64 for( i=0; i<ROM_SIZE; i++ )
65 for( j=0; j<8; j++ ) rom[i][j] = 0;
69 init_rom_japan( rom );
70 init_rom_5X10( rom10 );
73 init_rom_europe( rom );
75 use_europe_rom = !use_japan_rom;
91 blinking_chars_are_visible = false;
92 shift_on_write = false;
95 addr_bound = DRAM_SIZE;
100 for( i=0; i<DRAM_SIZE; i++ ) dram[i] = ' ';
101 for( i=0; i<CGRAM_SIZE; i++ ) cgram[i] = 0;
105 HD44780U :: incr_ac() {
106 // I don't actually know if ac is supposed to wrap
108 if( ac == ((host_int_1) -1) )
110 else if( ac == addr_bound )
115 HD44780U :: busRead( host_int_4 laddr, host_int_1& data ) {
119 // Read the address counter. Note that the busy flag is never set.
120 if( two_lines && !access_cgram && (ac > 0x27) )
128 else if( laddr == 1 ) {
129 // Read the data from the DR, adjust AC for next read, and reload DR.
139 // check for triggerpoints
140 trigger_mgr.check_and_dispatch();
145 return sid::bus::unmapped;
149 HD44780U :: busWrite( host_int_4 laddr, host_int_1 data ) {
151 // write the IR (busy is not used, so don't need to worry about it)
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.
161 // printf( "write CGRAM: 0x%02x\n", dr );
166 // printf( "write DDRAM %d: 0x%02x (%c)\n", ac, dr, dr );
171 display_offset += incr;
177 return sid::bus::unmapped;
180 switch( current_schedule ) {
182 break; // already set up
185 refresh_sync.cancel(); // cancel current schedule
189 // printf( "setting up regular schedule\n" );
190 refresh_sync.schedule_regular( refresh_period );
191 current_schedule = REGULAR_SCHED;
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;
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).
208 refresh_sync.schedule_irregular( refresh_period );
209 current_schedule = ONE_TIME_SCHED;
212 // check for triggerpoints
213 trigger_mgr.check_and_dispatch ();
216 cerr << "HD44780U::write - schedule " << current_schedule << endl;
222 HD44780U :: execute( unsigned char val ) {
224 cerr << "HD44780U::execute "
225 << make_numeric_attribute (val, ios::hex | ios::showbase)
228 if( val == 1 ) { // clear display
229 for( int i=0; i<DRAM_SIZE; i++ ) // perform partial reset
234 access_cgram = false;
235 addr_bound = DRAM_SIZE;
238 else if( (val & ~1) == 0x2 ) { // return home
240 access_cgram = false;
241 addr_bound = DRAM_SIZE;
244 else if( (val & ~3) == 0x4 ) { // entry mode set
245 if( val & 1 ) // shift display on DDRAM writes
246 shift_on_write = true;
248 shift_on_write = false;
255 else if( (val & ~7) == 0x08 ) { // display on/off control
260 else if( (val & ~0x0f) == 0x10 ) { // cursor/display shift
261 switch( (val >> 2) & 0x3 ) {
262 case 0: // shift cursor left
264 if( ac == ((host_int_1) -1) )
267 case 1: // shift cursor right
269 if( ac == addr_bound )
272 case 2: // shift display left
275 if( display_offset >= 40 )
279 if( display_offset >= 80 )
283 case 3: // shift display right
286 if( display_offset <= -40 )
290 if( display_offset <= -80 )
296 else if( (val & ~0x1f) == 0x20 ) { // function set
297 two_lines = val & 0x08;
300 else if( (val & ~0x3f) == 0x40 ) { // set CGRAM address
303 addr_bound = CGRAM_SIZE;
307 else if( (val & ~0x7f) == 0x80 ) { // set DDRAM address
310 if( two_lines && (val >= 0x40) )
315 access_cgram = false;
316 addr_bound = DRAM_SIZE;
323 HD44780U :: refresh() {
325 unsigned char *cur, bits;
329 const unsigned char first_5X10 = 0xe0;
332 cerr << "HD44780U::refresh" << endl;
334 frame_pin.drive( 1 ); // start of refresh
336 if( !display ) { // display is off
337 frame_pin.drive( 0 );
339 // No more refreshes til the display is re-enabled
340 refresh_sync.cancel();
341 current_schedule = NO_SCHED;
344 cerr << "HD44780U::refresh - display is off" << endl;
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;
354 // write the first line
361 cerr << "HD44780U::refresh - first row: ";
363 for( i=0; i<8; i++ ) {
364 addr = display_offset + i;
365 if( (display_offset + i) < 0 ) {
367 addr = 0x28 + (display_offset + i);
369 addr = 0x50 + (display_offset + i);
372 if( (ac == addr) && blink && !blinking_chars_are_visible )
375 if( dram[addr] < 0x10 ) {
376 cur = &(cgram[dram[addr] << 3]);
377 using_big_font = (last_row == 11);
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);
384 cur = &(rom[dram[addr]][0]);
385 using_big_font = false;
388 for( j=0; j<last_row; j++ ) {
389 if( (j == last_row-1) && cursor && (ac == addr) )
391 else if( j>7 && !using_big_font )
396 if( verbose && bits )
397 cerr << "[" << i << "," << j << "="
398 << make_numeric_attribute (bits, ios::hex | ios::showbase)
401 for( k=4; k>=0; k-- ) {
403 row_col_pin.drive( (j << 16) | (i*5 + k ) );
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);
419 if( (ac == addr) && blink && !blinking_chars_are_visible )
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]);
427 cur = &(rom[dram[addr]][0]);
429 for( j=0; j<8; j++ ) {
430 if( (j == 7) && cursor && (ac == addr) )
435 for( k=4; k>=0; k-- ) {
437 row_col_pin.drive( ((8 + j) << 16) | (i*5 + k ) );
445 frame_pin.drive( 0 );
447 blinking_chars_are_visible = !blinking_chars_are_visible;
451 HD44780U :: save_state() {
452 return make_attribute ( *this );
456 HD44780U :: restore_state( const string& state)
458 return parse_attribute( state, *this );
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 << ":";
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] << ":";
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] << ":";
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;
497 #define GET_TOKEN(_str_) { \
500 if( token != _str_ ) { \
501 ip.setstate( ios::badbit ); \
506 #define GET_SEPARATOR { \
510 ip.setstate( ios::badbit ); \
515 operator >> ( istream& ip, HD44780U& ovwrite_obj ) {
521 ovwrite_obj.ir = temp_val;
526 ovwrite_obj.dr = temp_val;
531 ovwrite_obj.ac = temp_val;
534 GET_TOKEN( "Lines" );
536 ovwrite_obj.two_lines = temp_val;
541 ovwrite_obj.big_font = temp_val;
544 GET_TOKEN( "Display" );
546 ovwrite_obj.display = temp_val;
549 GET_TOKEN( "Cursor" );
551 ovwrite_obj.cursor = temp_val;
554 GET_TOKEN( "Blink" );
556 ovwrite_obj.blink = temp_val;
561 ovwrite_obj.incr = temp_val;
564 GET_TOKEN( "DDRAM" );
565 for( j=0; j<DRAM_SIZE; j++) {
567 ovwrite_obj.dram[j] = temp_val;
571 GET_TOKEN( "CGRAM" );
572 for( j=0; j<CGRAM_SIZE; j++ ) {
574 ovwrite_obj.cgram[j] = temp_val;
578 GET_TOKEN( "AccessCgram" );
579 ip >> ovwrite_obj.access_cgram;
582 GET_TOKEN( "AddrBound" );
583 ip >> ovwrite_obj.addr_bound;
586 GET_TOKEN( "DisOffset" );
587 ip >> ovwrite_obj.display_offset;
590 GET_TOKEN( "ShiftOnWrite" );
591 ip >> ovwrite_obj.shift_on_write;
594 GET_TOKEN( "EuropeRom" );
595 ip >> ovwrite_obj.use_europe_rom;
598 GET_TOKEN( "BlinkVisible" );
599 ip >> ovwrite_obj.blinking_chars_are_visible;
602 GET_TOKEN( "CurrSched" );
603 ip >> ovwrite_obj.current_schedule;
606 GET_TOKEN( "RefreshPeriod" );
607 ip >> ovwrite_obj.refresh_period;
610 GET_TOKEN("RowColPin");
611 ip >> ovwrite_obj.row_col_pin;
614 GET_TOKEN("FramePin");
615 ip >> ovwrite_obj.frame_pin;
620 // Standard DLL wrapper-stuff
622 static vector<string>
623 HD44780U_ListTypes() {
624 vector<string> types;
626 types.push_back( string( "hw-lcd-hd44780u-a00" ) );
627 types.push_back( string( "hw-lcd-hd44780u-a02" ) );
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 );
643 HD44780U_Delete( component* c ) {
644 delete dynamic_cast<HD44780U*>(c);
648 extern const component_library hd44780u_component_library;
650 const component_library hd44780u_component_library DLLEXPORT =
652 COMPONENT_LIBRARY_MAGIC,
653 & HD44780U_ListTypes,