Subversion Repositories pentevo

Rev

Rev 1138 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed | ?url?

  1. #include "std.h"
  2.  
  3. #include "emul.h"
  4. #include "vars.h"
  5. #include "font.h"
  6. #include "font16.h"
  7. #include "gs.h"
  8. #include "tape.h"
  9. #include "draw.h"
  10. #include "debug.h"
  11. #include "dbgbpx.h"
  12. #include "memory.h"
  13. #include "leds.h"
  14.  
  15. #include "util.h"
  16.  
  17. static unsigned pitch;
  18.  
  19. void text_i(unsigned char *dst, const char *text, unsigned char ink, unsigned off)
  20. {
  21.    ink &= 0x0F;
  22.    for (const u8 *x = (const u8*)text; *x; x++) {
  23.       unsigned char *d0 = dst;
  24.       for (unsigned y = 0; y < 8; y++) {
  25.          unsigned char byte = font[(*x)*8+y];
  26.          d0[0] = (byte >> off) + (d0[0] & ~(0xFC >> off));
  27.          d0[1] = (d0[1] & 0xF0) + ink;
  28.          if (off > 2) {
  29.             d0[2] = u8((byte << (8-off)) + (d0[2] & ~(0xFC << (8-off))));
  30.             d0[3] = (d0[3] & 0xF0) + ink;
  31.          }
  32.          d0 += pitch;
  33.       }
  34.       off += 6;
  35.       if(off & 8)
  36.       {
  37.           off -= 8;
  38.           dst += 2;
  39.       }
  40.    }
  41. }
  42.  
  43. static void text_16(unsigned char *dst, const char *text, unsigned char attr)
  44. {
  45.     for(; *text; text++, dst += 2)
  46.     {
  47.         for(unsigned y = 0; y < 16; y++)
  48.         {
  49.             dst[y*pitch] = font16[16 * *(const u8*)text + y];
  50.             dst[y*pitch + 1] = attr;
  51.         }
  52.     }
  53. }
  54.  
  55. static unsigned char *aypos;
  56. static void paint_led(unsigned level, unsigned char at)
  57. {
  58.    if (level) {
  59.        if(level > 15)
  60.        {
  61.            level = 15; at = 0x0E;
  62.        }
  63.       unsigned mask = (0xFFFF0000 >> level) & 0xFFFF;
  64.       aypos[0] = u8(mask >> 8);
  65.       aypos[2] = (unsigned char)mask;
  66.       aypos[1] = (aypos[1] & 0xF0) + at;
  67.       aypos[3] = (aypos[3] & 0xF0) + at;
  68.    }
  69.    aypos += pitch;
  70. }
  71.  
  72. static void ay_led()
  73. {
  74.    aypos = temp.led.ay;
  75.    unsigned char sum=0;
  76.  
  77.    int max_ay = (conf.sound.ay_scheme > AY_SCHEME_PSEUDO)? 2 : 1;
  78.    for (int n_ay = 0; n_ay < max_ay; n_ay++) {
  79.       for (unsigned i = 0; i < 3; i++) {
  80.          unsigned char r_mix = ay[n_ay].get_reg(7);
  81.          unsigned char tone = (r_mix >> i) & 1,
  82.                        noise = (r_mix >> (i+3)) & 1;
  83.          unsigned char c1 = 0, c2 = 0;
  84.          unsigned char v = ay[n_ay].get_reg(i+8);
  85.          if (!tone) c1 = c2 = 0x0F;
  86.          if (!noise) c2 = 0x0E;
  87.          if (v & 0x10) {
  88.             unsigned r_envT = ay[n_ay].get_reg(11) + 0x100*ay[n_ay].get_reg(12);
  89.             if (r_envT < 0x400) {
  90.                v = (3-(r_envT>>3)) & 0x0F;
  91.                if (!v) v = 6;
  92.             } else v = u8(ay[n_ay].get_env()/2);
  93.             c1 = 0x0C;
  94.          } else v &= 0x0F;
  95.          if (!c1) c1 = c2;
  96.          if (!c2) c2 = c1;
  97.          if (!c1) v = 0;
  98.          sum |= v;
  99.          paint_led(v, c1);
  100.          paint_led(v, c2);
  101.          paint_led(0, 0);
  102.       }
  103.    }
  104.  
  105.    const unsigned FMvols[]={4,9,15,23,35,48,70,105,140,195,243,335,452,608,761,1023};
  106.    #define _cBlue 0x09
  107.    #define _cRed 0x0a
  108.    #define _cPurp 0x0b
  109.    #define _cGreen 0x0c
  110.    #define _cCyan 0x0d
  111.    #define _cYell 0x0e
  112.    #define _cWhite 0x0f
  113.  
  114.    const u8 FMalg1[]={
  115.    _cBlue , _cPurp , _cGreen, _cWhite,
  116.    _cPurp , _cPurp , _cGreen, _cWhite,
  117.    _cGreen, _cPurp , _cGreen, _cWhite,
  118.    _cPurp , _cGreen, _cGreen, _cWhite,
  119.  
  120.    _cGreen, _cWhite, _cGreen, _cWhite,
  121.    _cPurp , _cWhite, _cWhite, _cWhite,
  122.    _cGreen, _cWhite, _cWhite, _cWhite,
  123.    _cWhite, _cWhite, _cWhite, _cWhite
  124.    };
  125.  
  126.    const u8 FMalg2[]={
  127.    _cBlue , _cPurp , _cGreen, _cYell ,
  128.    _cPurp , _cPurp , _cGreen, _cYell ,
  129.    _cGreen, _cPurp , _cGreen, _cYell ,
  130.    _cPurp , _cGreen, _cGreen, _cYell ,
  131.  
  132.    _cGreen, _cYell , _cGreen, _cYell ,
  133.    _cPurp , _cYell , _cYell , _cYell ,
  134.    _cGreen, _cYell , _cYell , _cYell ,
  135.    _cYell , _cYell , _cYell , _cYell
  136.    };
  137.  
  138.    const int FMslots[]={0,2,1,3};
  139. /*
  140.    const int FMalg1[]={
  141.    _cPurp , _cGreen, _cCyan , _cWhite,
  142.    _cPurp , _cPurp , _cGreen, _cWhite,
  143.    _cGreen, _cPurp , _cCyan , _cWhite,
  144.    _cPurp , _cCyan , _cGreen, _cWhite,
  145.  
  146.    _cPurp , _cWhite, _cGreen, _cWhite,
  147.    _cPurp , _cWhite, _cWhite, _cWhite,
  148.    _cGreen, _cWhite, _cWhite, _cWhite,
  149.    _cWhite, _cWhite, _cWhite, _cWhite
  150.    };
  151.  
  152.    const int FMalg2[]={
  153.    _cPurp , _cGreen, _cCyan , _cYell ,
  154.    _cPurp , _cPurp , _cGreen, _cYell ,
  155.    _cGreen, _cPurp , _cCyan , _cYell ,
  156.    _cPurp , _cCyan , _cGreen, _cYell ,
  157.  
  158.    _cPurp , _cYell , _cGreen, _cYell ,
  159.    _cPurp , _cYell , _cYell , _cYell ,
  160.    _cGreen, _cYell , _cYell , _cYell ,
  161.    _cYell , _cYell , _cYell , _cYell
  162.    };
  163. */
  164.    if ( conf.sound.ay_chip == SNDCHIP::CHIP_YM2203 ) {
  165.       for (int ayN = 0; ayN < max_ay; ayN++) {
  166.          for (int i = 0; i < 3; i++) {
  167.             for (int j = 0; j < 4; j++) {
  168.                unsigned v=ay[ayN].Chip2203->CH[i].SLOT[j].vol_out;
  169.                if (v>1023) v=1023;
  170.                unsigned c; //Alone Coder 0.36.7
  171.                for (/*int*/ c=0;c<16;c++)
  172.                   if (FMvols[c]>=v) break;
  173.                if ( (i == 2) && (((ay[ayN].Chip2203->OPN.ST.mode) & 0xc0) == 0x40) )
  174.                   paint_led(15-c, FMalg2[ay[ayN].Chip2203->CH[i].ALGO * 4 + FMslots[j]]);
  175.                else
  176.                   paint_led(15-c, FMalg1[ay[ayN].Chip2203->CH[i].ALGO * 4 + FMslots[j]]);
  177.             }
  178.          paint_led(0, 0);
  179.          }
  180.       }
  181.    } //Dexus
  182.  
  183.    #ifdef MOD_GS
  184.    if (sum || !conf.gs_type) return; // else show GS indicators
  185.    aypos = temp.led.ay; // reset y-pos, if nothing above
  186.    for (unsigned ch = 0; ch < 8; ch++) {
  187.       unsigned v = gsleds[ch].level, a = gsleds[ch].attrib;
  188.       paint_led(v, u8(a));
  189.       paint_led(v, u8(a));
  190.       paint_led(0, 0);
  191.  
  192. #if 0
  193.       printf("{%2u}", v);
  194.       for(unsigned i = 0; i < v; i++)
  195.       {
  196.           printf("#");
  197.    }
  198.       printf("\n");
  199.    }
  200.    printf("-------------------------\n");
  201. #else
  202.    }
  203. #endif
  204.  
  205.    #endif
  206. }
  207.  
  208. static void load_led()
  209. {
  210.     char ln[20]; unsigned char diskcolor = 0;
  211.  
  212. #ifdef GS_BASS
  213.     if(gs.loadmod)
  214.     {
  215.         text_i(temp.led.load, "", 0x0D);
  216.         gs.loadmod = 0;
  217.     }
  218.     else if(gs.loadfx)
  219.     {
  220.         sprintf(ln, "\x0D%d", gs.loadfx);
  221.         text_i(temp.led.load, ln, 0x0D);
  222.         gs.loadfx = 0;
  223.     }
  224.     else
  225. #endif
  226.         if(trdos_format)
  227.         {
  228.             diskcolor = (trdos_format < ROMLED_TIME * 3 / 4) ? 0x06 : 0x0E;
  229.             trdos_format--;
  230.         }
  231.         else if(trdos_save)
  232.         {
  233.             diskcolor = (trdos_save < ROMLED_TIME * 3 / 4) ? 0x02 : 0x0A;
  234.             trdos_save--;
  235.         }
  236.         else if(trdos_load)
  237.         {
  238.             diskcolor = (trdos_load < ROMLED_TIME * 3 / 4) ? 0x01 : 0x09;
  239.             trdos_load--;
  240.         }
  241.         else if(trdos_seek)
  242.         {
  243.             trdos_seek--;
  244.         }
  245.         else if(!comp.tape.stopped)
  246.         {
  247.             static unsigned char tapeled[11 * 2] =
  248.             {
  249.                 0x7F, 0xFE, 0x80, 0x01, 0x80, 0x01, 0x93, 0xC9, 0xAA, 0x55, 0x93, 0xC9,
  250.                 0x80, 0x01, 0x8F, 0xF1, 0x80, 0x01, 0xB5, 0xA9, 0xFF, 0xFF
  251.             };
  252.             const int tapecolor = 0x51;
  253.             for(unsigned i = 0; i < 11; i++)
  254.             {
  255.                 temp.led.load[pitch*i + 0] = tapeled[2 * i];
  256.                 temp.led.load[pitch*i + 1] = tapecolor;
  257.                 temp.led.load[pitch*i + 2] = tapeled[2 * i + 1];
  258.                 temp.led.load[pitch*i + 3] = tapecolor;
  259.             }
  260.             int time = (int)(temp.led.tape_started + tapeinfo[comp.tape.index].t_size - comp.t_states);
  261.             if(time < 0)
  262.             {
  263.                 find_tape_index(); time = 0;
  264.                 temp.led.tape_started = comp.t_states;
  265.                 unsigned char *ptr = tape_image + tapeinfo[comp.tape.index].pos;
  266.                 if(ptr == comp.tape.play_pointer && comp.tape.index)
  267.                 {
  268.                     comp.tape.index--;
  269.                     ptr = tape_image + tapeinfo[comp.tape.index].pos;
  270.                 }
  271.                 for(; ptr < comp.tape.play_pointer; ptr++)
  272.                 {
  273.                     temp.led.tape_started -= (tape_pulse[*ptr] & tape_pulse_mask);
  274.                 }
  275.             }
  276.             time /= (conf.frame * conf.intfq);
  277.             sprintf(ln, "%X:%02d", time / 60, time % 60);
  278.             text_i(temp.led.load + pitch * 12 - 2, ln, 0x0D);
  279.         }
  280.     if(diskcolor | trdos_seek)
  281.     {
  282.         if(diskcolor)
  283.         {
  284.             unsigned *ptr = (unsigned*)temp.led.load;
  285.             int i; //Alone Coder 0.36.7
  286.             for(/*int*/ i = 0; i < 7; i++, ptr = (unsigned*)((char*)ptr + pitch))
  287.             {
  288.                 *ptr = (*ptr & WORD4(0, 0xF0, 0, 0xF0)) | WORD4(0x3F, diskcolor, 0xFC, diskcolor);
  289.             }
  290.  
  291.             static unsigned char disk[] = { 0x38, 0x1C, 0x3B, 0x9C, 0x3B, 0x9C, 0x3B, 0x9C, 0x38, 0x1C };
  292.             for(i = 0; i < 5; i++, ptr = (unsigned*)((char*)ptr + pitch))
  293.             {
  294.                 *ptr = (*ptr & WORD4(0, 0xF0, 0, 0xF0)) | WORD4(disk[2 * i], diskcolor, disk[2 * i + 1], diskcolor);
  295.             }
  296.         }
  297.         if(comp.wd.seldrive->track != 0xFF)
  298.         {
  299.             sprintf(ln, "%02X", comp.wd.seldrive->track * 2 + comp.wd.side);
  300.             text_i(temp.led.load + pitch - 4, ln, 0x05 + (diskcolor & 8));
  301.         }
  302.     }
  303. }
  304.  
  305. static unsigned p_frames = 1;
  306. static u64 led_updtime, p_time;
  307. static double p_fps;
  308. __inline void update_perf_led()
  309. {
  310.    u64 now = led_updtime - p_time;
  311.    if (now >= temp.cpufq) // усреднение за секунду
  312.    {
  313.       p_fps = (p_frames * temp.cpufq) / double(now) + 0.005;
  314.       p_frames = 0;
  315.       p_time = led_updtime;
  316.    }
  317.    p_frames++;
  318. }
  319.  
  320. static void perf_led()
  321. {
  322.    char bf[0x20]; unsigned PSZ;
  323.    if(conf.led.perf_t)
  324.    {
  325.        sprintf(bf, "%6u*%2.2f", cpu.haltpos ? cpu.haltpos : cpu.t, p_fps);
  326.        PSZ = 7;
  327.    }
  328.    else
  329.    {
  330.        sprintf(bf, "%2.2f fps", p_fps);
  331.        PSZ = 5;
  332.    }
  333.    text_i(temp.led.perf, bf, 0x0E);
  334.    if (cpu.haltpos) {
  335.       unsigned char *ptr = temp.led.perf + pitch*8;
  336.       unsigned xx; //Alone Coder 0.36.7
  337.       for (/*unsigned*/ xx = 0; xx < PSZ; xx++) *(unsigned short*)(ptr+xx*2) = 0x9A00;
  338.       unsigned mx = cpu.haltpos*PSZ*8/conf.frame;
  339.       for (xx = 1; xx < mx; xx++) ptr[(xx>>2)&0xFE] |= (0x80 >> (xx & 7));
  340.    }
  341. }
  342.  
  343. static void breakpoints_led()
  344. {
  345.    if (bkpts_ena)
  346.    {
  347.       const char* p_Label = "Br";
  348.       text_i(temp.led.bkpts, p_Label, 0x0E);
  349.    }
  350. }
  351.  
  352. static void input_led()
  353. {
  354.    if (input.kbdled != 0xFF) {
  355.       unsigned char k0 = 0x99, k1 = 0x9F, k2 = 0x90;
  356.       if(input.keymode == K_INPUT::KM_PASTE_HOLD)
  357.       {
  358.           k0 = 0xAA; k1 = 0xAF; k2 = 0xA0;
  359.       }
  360.       if(input.keymode == K_INPUT::KM_PASTE_RELEASE)
  361.       {
  362.           k0 = 0x22; k1 = 0x2F; k2 = 0x20;
  363.       }
  364.  
  365.       unsigned i; //Alone Coder 0.36.7
  366.       for (/*int*/ i = 0; i < 5; i++)
  367.          temp.led.input[1+i*2*pitch] = temp.led.input[3+i*2*pitch] = k0;
  368.       for(i = 0; i < 4; i++)
  369.       {
  370.           temp.led.input[pitch*(2 * i + 1)] = 0x7F;
  371.           temp.led.input[pitch*(2 * i + 1) + 2] = 0xFE;
  372.       }
  373.       temp.led.input[pitch*1+1] = (input.kbdled & 0x08)? k2 : k1;
  374.       temp.led.input[pitch*3+1] = (input.kbdled & 0x04)? k2 : k1;
  375.       temp.led.input[pitch*5+1] = (input.kbdled & 0x02)? k2 : k1;
  376.       temp.led.input[pitch*7+1] = (input.kbdled & 0x01)? k2 : k1;
  377.       temp.led.input[pitch*1+3] = (input.kbdled & 0x10)? k2 : k1;
  378.       temp.led.input[pitch*3+3] = (input.kbdled & 0x20)? k2 : k1;
  379.       temp.led.input[pitch*5+3] = (input.kbdled & 0x40)? k2 : k1;
  380.       temp.led.input[pitch*7+3] = (input.kbdled & 0x80)? k2 : k1;
  381.    }
  382.    static unsigned char joy[] =   { 0x10, 0x38, 0x1C, 0x1C, 0x1C, 0x1C, 0x08, 0x00, 0x7E, 0xFF, 0x00, 0xE7 };
  383.    static unsigned char mouse[] = { 0x0C, 0x12, 0x01, 0x79, 0xB5, 0xB5, 0xB5, 0xFC, 0xFC, 0xFC, 0xFC, 0x78 };
  384.    if(input.mouse_joy_led & 2)
  385.    {
  386.        for(size_t i = 0; i < sizeof joy; i++)
  387.        {
  388.            temp.led.input[4 + pitch * i] = joy[i];
  389.            temp.led.input[4 + pitch * i + 1] = (temp.led.input[4 + pitch * i + 1] & 0xF0) + 0x0F;
  390.        }
  391.    }
  392.    if(input.mouse_joy_led & 1)
  393.    {
  394.        for(size_t i = 0; i < sizeof mouse; i++)
  395.        {
  396.            temp.led.input[6 + pitch * i] = mouse[i];
  397.            temp.led.input[6 + pitch * i + 1] = (temp.led.input[6 + pitch * i + 1] & 0xF0) + 0x0F;
  398.        }
  399.    }
  400.    input.mouse_joy_led = 0; input.kbdled = 0xFF;
  401. }
  402.  
  403. #ifdef MOD_MONITOR
  404. static void debug_led()
  405. {
  406.    unsigned char *ptr = temp.led.osw;
  407.    if (trace_rom | trace_ram) {
  408.       set_banks();
  409.       if (trace_rom) {
  410.          const unsigned char off = 0x01, on = 0x0C;
  411.          text_i(ptr + 2,           "B48", used_banks[(base_sos_rom - memory) / PAGE] ? on : off);
  412.          text_i(ptr + 8,           "DOS", used_banks[(base_dos_rom - memory) / PAGE] ? on : off);
  413.          text_i(ptr + pitch*8 + 2, "128", used_banks[(base_128_rom - memory) / PAGE] ? on : off);
  414.          text_i(ptr + pitch*8 + 8, "SYS", used_banks[(base_sys_rom - memory) / PAGE] ? on : off);
  415.          ptr += pitch*16;
  416.       }
  417.       if (trace_ram) {
  418.          unsigned num_rows = conf.ramsize/128;
  419.          unsigned j; //Alone Coder 0.36.7
  420.          for (unsigned  i = 0; i < num_rows; i++) {
  421.             char ln[9];
  422.             for (/*unsigned*/ j = 0; j < 8; j++)
  423.                ln[j] = used_banks[i*8+j]? '*' : '-';
  424.             ln[j] = 0;
  425.             text_i(ptr, ln, 0x0D);
  426.             ptr += pitch*8;
  427.          }
  428.       }
  429.       for (unsigned j = 0; j < MAX_PAGES; j++) used_banks[j] = 0;
  430.    }
  431.    for (unsigned w = 0; w < 4; w++) if (watch_enabled[w])
  432.    {
  433.       char bf[12]; sprintf(bf, "%8X", calc(&cpu, watch_script[w]));
  434.       text_i(ptr,bf,0x0F); ptr += pitch*8;
  435.    }
  436. }
  437. #endif
  438.  
  439. #ifdef MOD_MEMBAND_LED
  440. static void show_mband(unsigned char *dst, unsigned start)
  441. {
  442.    unsigned char* dst_m = dst + 2;
  443.    char xx[8]; sprintf(xx, "%02X", start >> 8);
  444.    text_i(dst, xx, 0x0B); dst += 4;
  445.  
  446.    Z80 &cpu = CpuMgr.Cpu();
  447.    unsigned char band[128];
  448.    unsigned i; //Alone Coder 0.36.7
  449.    for (/*unsigned*/ i = 0; i < 128; i++) {
  450.       unsigned char res = 0;
  451.       for (unsigned q = 0; q < conf.led.bandBpp; q++)
  452.          res |= cpu.membits[start++];
  453.       band[i] = res;
  454.    }
  455.  
  456.    for (unsigned p = 0; p < 16; p++, dst+=2) {
  457.       unsigned char r=0, w=0, x=0;
  458.       for (unsigned b = 0; b < 8; b++) {
  459.           r *= 2; w *= 2; x *= 2;
  460.          if (band[p*8+b] & MEMBITS_R) r |= 1;
  461.          if (band[p*8+b] & MEMBITS_W) w |= 1;
  462.          if (band[p*8+b] & MEMBITS_X) x |= 1;
  463.       }
  464.  
  465.       unsigned char t = (p && !(p & 3))? 0x7F : 0xFF;
  466.  
  467.       dst[0*pitch] = t; dst[0*pitch+1] = 0x9B;
  468.  
  469.       dst[1*pitch] = r; dst[1*pitch+1] = 0x1C;
  470.       dst[2*pitch] = r; dst[2*pitch+1] = 0x1C;
  471.       dst[3*pitch] = w; dst[3*pitch+1] = 0x1A;
  472.       dst[4*pitch] = w; dst[4*pitch+1] = 0x1A;
  473.       dst[5*pitch] = x; dst[5*pitch+1] = 0x1F;
  474.       dst[6*pitch] = x; dst[6*pitch+1] = 0x1F;
  475.  
  476.       dst[7*pitch] = t; dst[7*pitch+1] = 0x9B;
  477.    }
  478.  
  479.    sprintf(xx, "%02X", (start-1) >> 8);
  480.    text_i(dst, xx, 0x0B, 2);
  481.  
  482.    for(i = 0; i < 8; i++)
  483.    {
  484.        dst[i*pitch] |= 0x80;
  485.      //dst[i*pitch - 17LL * 2LL] |= 0x01;
  486.        dst_m[i * pitch] |= 0x01;
  487.    }
  488. }
  489.  
  490. static void memband_led()
  491. {
  492.    unsigned char *dst = temp.led.memband;
  493.    for (unsigned start = 0x0000; start < 0x10000;) {
  494.       show_mband(dst, start);
  495.       start += conf.led.bandBpp * 128;
  496.       dst += 10*pitch;
  497.    }
  498.  
  499.    Z80 &cpu = CpuMgr.Cpu();
  500.    for (unsigned i = 0; i < 0x10000; i++)
  501.       cpu.membits[i] &= ripper | unsigned(~(MEMBITS_R | MEMBITS_W | MEMBITS_X));
  502. }
  503. #endif
  504.  
  505.  
  506. HANDLE hndKbdDev;
  507.  
  508. void init_leds()
  509. {
  510.    DefineDosDevice(DDD_RAW_TARGET_PATH, "Kbd_unreal_spec", "\\Device\\KeyboardClass0");
  511.    hndKbdDev = CreateFile("\\\\.\\Kbd_unreal_spec", GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
  512.    if(hndKbdDev == INVALID_HANDLE_VALUE)
  513.    {
  514.        hndKbdDev = nullptr;
  515.        conf.led.flash_ay_kbd = 0;
  516.    }
  517. }
  518.  
  519. void done_leds()
  520. {
  521.    if (hndKbdDev) {
  522.       DefineDosDevice(DDD_REMOVE_DEFINITION, "Kbd_unreal_spec", nullptr);
  523.       CloseHandle(hndKbdDev); hndKbdDev = nullptr;
  524.    }
  525. }
  526.  
  527. static void ay_kbd()
  528. {
  529.    static unsigned char pA, pB, pC;
  530.    static unsigned prev_keyled = -1U;
  531.  
  532.    KEYBOARD_INDICATOR_PARAMETERS InputBuffer;
  533.    InputBuffer.LedFlags = InputBuffer.UnitId = 0;
  534.  
  535.    if (ay[0].get_reg( 8) > pA) InputBuffer.LedFlags |= KEYBOARD_NUM_LOCK_ON;
  536.    if (ay[0].get_reg( 9) > pB) InputBuffer.LedFlags |= KEYBOARD_CAPS_LOCK_ON;
  537.    if (ay[0].get_reg(10) > pC) InputBuffer.LedFlags |= KEYBOARD_SCROLL_LOCK_ON;
  538.  
  539.    pA = ay[0].get_reg(8); pB = ay[0].get_reg(9); pC = ay[0].get_reg(10);
  540.  
  541.    DWORD xx;
  542.    if(prev_keyled != InputBuffer.LedFlags)
  543.    {
  544.        prev_keyled = InputBuffer.LedFlags;
  545.        DeviceIoControl(hndKbdDev, IOCTL_KEYBOARD_SET_INDICATORS, &InputBuffer, sizeof(KEYBOARD_INDICATOR_PARAMETERS),
  546.            nullptr, 0, &xx, nullptr);
  547.    }
  548. }
  549.  
  550. static void key_led()
  551. {
  552.    #define key_x 1U
  553.    #define key_y 1U
  554.    unsigned i; //Alone Coder 0.36.7
  555.    for (/*int*/ i = 0; i < 9; i++) text_16(rbuf+(key_y+i)*pitch*16+key_x*2, "                                 ", 0x40);
  556.    static char ks[] = "cZXCVASDFGQWERT1234509876POIUYeLKJHssMNB";
  557.    for (i = 0; i < 8; i++) {
  558.       for (unsigned j = 0; j < 5; j++) {
  559.          unsigned x, y, at;
  560.          if(i < 4)
  561.          {
  562.              y = 7 - 2 * i + key_y;
  563.              x = 3 * j + 2 + key_x;
  564.          }
  565.          else
  566.          {
  567.              y = 2 * (i - 4) + 1 + key_y;
  568.              x = 29 - 3 * j + key_x;
  569.          }
  570.          unsigned a = u8(ks[i*5+j])*0x100U+u8(' ');
  571.          at = (input.kbd[i] & (1<<j))? 0x07 : ((input.rkbd[i] & (1<<j)) ? 0xA0:0xD0);
  572.          text_16(rbuf + 2 * x + y * pitch * 16, (char*)&a, u8(at));
  573.       }
  574.    }
  575. }
  576.  
  577. static void time_led()
  578. {
  579.    static u64 prev_time;
  580.    static char bf[8];
  581.    if (led_updtime - prev_time > 5000) {
  582.       prev_time = led_updtime;
  583.       SYSTEMTIME st; GetLocalTime(&st);
  584.       sprintf(bf, "%2d:%02d", st.wHour, st.wMinute);
  585.    }
  586.    text_i(temp.led.time, bf, 0x0D);
  587. }
  588.  
  589. // Вызывается раз в кадр
  590. void showleds()
  591. {
  592.    led_updtime = rdtsc();
  593.    update_perf_led();
  594.  
  595.    if (temp.vidblock) return;
  596.  
  597.    pitch = temp.scx/4;
  598.  
  599.    if (statcnt) { statcnt--; text_i(rbuf + ((pitch/2-strlen(statusline)*6/8) & 0xFE) + (temp.scy-10)*pitch, statusline, 0x09); }
  600.  
  601.    if (!conf.led.enabled) return;
  602.  
  603.    if (temp.led.ay) ay_led();
  604.    if (temp.led.perf) perf_led();
  605.    if (temp.led.load) load_led();
  606.    if (temp.led.input) input_led();
  607.    if (temp.led.time) time_led();
  608. #ifdef MOD_MONITOR
  609.    if (temp.led.osw) debug_led();
  610. #endif
  611. #ifdef MOD_MEMBAND_LED
  612.    if (temp.led.memband) memband_led();
  613. #endif
  614.    if (conf.led.flash_ay_kbd && hndKbdDev) ay_kbd();
  615.    if (input.keymode == K_INPUT::KM_KEYSTICK) key_led();
  616.    if(temp.led.bkpts) breakpoints_led();
  617. }
  618.