Hardware/LPC/NCT677X.cs
author moel.mich
Thu, 01 Aug 2013 11:57:09 +0000
changeset 417 4865cf06a7fa
parent 415 a67db9051d19
child 419 07d44b20bbd4
permissions -rw-r--r--
Added experimental support for Intel Ivy Bridge-E CPUs.
moel@245
     1
/*
moel@245
     2
 
moel@344
     3
  This Source Code Form is subject to the terms of the Mozilla Public
moel@344
     4
  License, v. 2.0. If a copy of the MPL was not distributed with this
moel@344
     5
  file, You can obtain one at http://mozilla.org/MPL/2.0/.
moel@245
     6
 
moel@413
     7
  Copyright (C) 2010-2013 Michael Möller <mmoeller@openhardwaremonitor.org>
moel@344
     8
	
moel@245
     9
*/
moel@245
    10
moel@355
    11
using System;
moel@245
    12
using System.Globalization;
moel@245
    13
using System.Text;
moel@245
    14
moel@245
    15
namespace OpenHardwareMonitor.Hardware.LPC {
moel@245
    16
  internal class NCT677X : ISuperIO {
moel@245
    17
moel@245
    18
    private readonly ushort port;
moel@245
    19
    private readonly byte revision;
moel@245
    20
moel@245
    21
    private readonly Chip chip;
moel@245
    22
moel@415
    23
    private readonly float?[] voltages = new float?[0];
moel@415
    24
    private readonly float?[] temperatures = new float?[0];
moel@415
    25
    private readonly float?[] fans = new float?[0];
moel@415
    26
    private readonly float?[] controls = new float?[0];
moel@245
    27
moel@245
    28
    // Hardware Monitor
moel@245
    29
    private const uint ADDRESS_REGISTER_OFFSET = 0x05;
moel@245
    30
    private const uint DATA_REGISTER_OFFSET = 0x06;
moel@245
    31
    private const byte BANK_SELECT_REGISTER = 0x4E;
moel@245
    32
moel@245
    33
    private byte ReadByte(ushort address) {
moel@245
    34
      byte bank = (byte)(address >> 8);
moel@245
    35
      byte register = (byte)(address & 0xFF);
moel@245
    36
      Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER);
moel@245
    37
      Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, bank);
moel@245
    38
      Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, register);
moel@245
    39
      return Ring0.ReadIoPort(port + DATA_REGISTER_OFFSET);
moel@323
    40
    }
moel@323
    41
moel@323
    42
    private void WriteByte(ushort address, byte value) {
moel@323
    43
      byte bank = (byte)(address >> 8);
moel@323
    44
      byte register = (byte)(address & 0xFF);
moel@323
    45
      Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, BANK_SELECT_REGISTER);
moel@323
    46
      Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, bank);
moel@323
    47
      Ring0.WriteIoPort(port + ADDRESS_REGISTER_OFFSET, register);
moel@323
    48
      Ring0.WriteIoPort(port + DATA_REGISTER_OFFSET, value);
moel@356
    49
    }
moel@245
    50
moel@245
    51
    // Consts 
moel@245
    52
    private const ushort NUVOTON_VENDOR_ID = 0x5CA3;
moel@245
    53
moel@245
    54
    // Hardware Monitor Registers    
moel@245
    55
    private const ushort VENDOR_ID_HIGH_REGISTER = 0x804F;
moel@355
    56
    private const ushort VENDOR_ID_LOW_REGISTER = 0x004F;  
moel@355
    57
    
moel@355
    58
    private readonly ushort[] FAN_PWM_OUT_REG = 
moel@413
    59
      { 0x001, 0x003, 0x011, 0x013, 0x015, 0x017 };
moel@355
    60
    private readonly ushort[] FAN_PWM_COMMAND_REG = 
moel@413
    61
      { 0x109, 0x209, 0x309, 0x809, 0x909, 0xA09 };
moel@355
    62
    private readonly ushort[] FAN_CONTROL_MODE_REG = 
moel@413
    63
      { 0x102, 0x202, 0x302, 0x802, 0x902, 0xA02 };
moel@245
    64
moel@355
    65
    private readonly ushort fanRpmBaseRegister;
moel@265
    66
    private readonly int minFanRPM;
moel@245
    67
moel@414
    68
    private bool[] restoreDefaultFanControlRequired = new bool[6];       
moel@414
    69
    private byte[] initialFanControlMode = new byte[6];
moel@414
    70
    private byte[] initialFanPwmCommand = new byte[6];
moel@355
    71
moel@355
    72
    private readonly ushort[] voltageRegisters;
moel@355
    73
    private readonly ushort voltageVBatRegister;
moel@355
    74
moel@355
    75
    private readonly byte[] temperaturesSource;
moel@355
    76
moel@355
    77
    private readonly ushort[] temperatureRegister;
moel@355
    78
    private readonly ushort[] temperatureHalfRegister;
moel@355
    79
    private readonly int[] temperatureHalfBit;  
moel@355
    80
    private readonly ushort[] temperatureSourceRegister;        
moel@355
    81
moel@355
    82
    private readonly ushort?[] alternateTemperatureRegister;
moel@323
    83
moel@276
    84
    private enum SourceNCT6771F : byte {
moel@245
    85
      SYSTIN = 1,
moel@245
    86
      CPUTIN = 2,
moel@245
    87
      AUXTIN = 3,
moel@245
    88
      SMBUSMASTER = 4,
moel@276
    89
      PECI_0 = 5, 
moel@276
    90
      PECI_1 = 6, 
moel@276
    91
      PECI_2 = 7,
moel@276
    92
      PECI_3 = 8,
moel@276
    93
      PECI_4 = 9,
moel@276
    94
      PECI_5 = 10,
moel@276
    95
      PECI_6 = 11,
moel@276
    96
      PECI_7 = 12,
moel@245
    97
      PCH_CHIP_CPU_MAX_TEMP = 13,
moel@245
    98
      PCH_CHIP_TEMP = 14,
moel@245
    99
      PCH_CPU_TEMP = 15,
moel@245
   100
      PCH_MCH_TEMP = 16, 
moel@245
   101
      PCH_DIM0_TEMP = 17,
moel@245
   102
      PCH_DIM1_TEMP = 18,
moel@245
   103
      PCH_DIM2_TEMP = 19,
moel@245
   104
      PCH_DIM3_TEMP = 20
moel@245
   105
    }
moel@245
   106
moel@276
   107
    private enum SourceNCT6776F : byte {
moel@276
   108
      SYSTIN = 1,
moel@276
   109
      CPUTIN = 2,
moel@276
   110
      AUXTIN = 3,
moel@276
   111
      SMBUSMASTER_0 = 4,
moel@276
   112
      SMBUSMASTER_1 = 5,
moel@276
   113
      SMBUSMASTER_2 = 6,
moel@276
   114
      SMBUSMASTER_3 = 7,
moel@276
   115
      SMBUSMASTER_4 = 8,
moel@276
   116
      SMBUSMASTER_5 = 9,
moel@276
   117
      SMBUSMASTER_6 = 10,
moel@276
   118
      SMBUSMASTER_7 = 11,
moel@276
   119
      PECI_0 = 12,
moel@276
   120
      PECI_1 = 13,
moel@276
   121
      PCH_CHIP_CPU_MAX_TEMP = 14,
moel@276
   122
      PCH_CHIP_TEMP = 15,
moel@276
   123
      PCH_CPU_TEMP = 16,
moel@276
   124
      PCH_MCH_TEMP = 17,
moel@276
   125
      PCH_DIM0_TEMP = 18,
moel@276
   126
      PCH_DIM1_TEMP = 19,
moel@276
   127
      PCH_DIM2_TEMP = 20,
moel@276
   128
      PCH_DIM3_TEMP = 21,
moel@276
   129
      BYTE_TEMP = 22
moel@276
   130
    }
moel@276
   131
moel@413
   132
    private enum SourceNCT67XXD : byte {
moel@355
   133
      SYSTIN = 1,
moel@355
   134
      CPUTIN = 2,
moel@355
   135
      AUXTIN0 = 3,
moel@355
   136
      AUXTIN1 = 4,
moel@355
   137
      AUXTIN2 = 5,
moel@355
   138
      AUXTIN3 = 6,      
moel@355
   139
      SMBUSMASTER_0 = 8,
moel@355
   140
      SMBUSMASTER_1 = 9,
moel@355
   141
      SMBUSMASTER_2 = 10,
moel@355
   142
      SMBUSMASTER_3 = 11,
moel@355
   143
      SMBUSMASTER_4 = 12,
moel@355
   144
      SMBUSMASTER_5 = 13,
moel@355
   145
      SMBUSMASTER_6 = 14,
moel@355
   146
      SMBUSMASTER_7 = 15,
moel@355
   147
      PECI_0 = 16,
moel@355
   148
      PECI_1 = 17,
moel@355
   149
      PCH_CHIP_CPU_MAX_TEMP = 18,
moel@355
   150
      PCH_CHIP_TEMP = 19,
moel@355
   151
      PCH_CPU_TEMP = 20,
moel@355
   152
      PCH_MCH_TEMP = 21,
moel@355
   153
      PCH_DIM0_TEMP = 22,
moel@355
   154
      PCH_DIM1_TEMP = 23,
moel@355
   155
      PCH_DIM2_TEMP = 24,
moel@355
   156
      PCH_DIM3_TEMP = 25,
moel@355
   157
      BYTE_TEMP = 26
moel@355
   158
    }
moel@355
   159
moel@245
   160
    public NCT677X(Chip chip, byte revision, ushort port) {
moel@245
   161
      this.chip = chip;
moel@245
   162
      this.revision = revision;
moel@245
   163
      this.port = port;
moel@245
   164
moel@245
   165
      if (!IsNuvotonVendor())
moel@265
   166
        return;
moel@265
   167
moel@265
   168
      switch (chip) {
moel@355
   169
        case Chip.NCT6771F:
moel@355
   170
        case Chip.NCT6776F:          
moel@355
   171
          if (chip == Chip.NCT6771F) {
moel@355
   172
            fans = new float?[4];
moel@355
   173
moel@355
   174
            // min value RPM value with 16-bit fan counter
moel@355
   175
            minFanRPM = (int)(1.35e6 / 0xFFFF);
moel@355
   176
moel@355
   177
            temperaturesSource = new byte[] {
moel@355
   178
              (byte)SourceNCT6771F.PECI_0,
moel@355
   179
              (byte)SourceNCT6771F.CPUTIN,
moel@355
   180
              (byte)SourceNCT6771F.AUXTIN,
moel@355
   181
              (byte)SourceNCT6771F.SYSTIN
moel@355
   182
            };            
moel@355
   183
          } else {
moel@355
   184
            fans = new float?[5];
moel@355
   185
moel@355
   186
            // min value RPM value with 13-bit fan counter
moel@355
   187
            minFanRPM = (int)(1.35e6 / 0x1FFF);
moel@355
   188
moel@355
   189
            temperaturesSource = new byte[] {
moel@355
   190
              (byte)SourceNCT6776F.PECI_0,
moel@355
   191
              (byte)SourceNCT6776F.CPUTIN,
moel@355
   192
              (byte)SourceNCT6776F.AUXTIN,
moel@355
   193
              (byte)SourceNCT6776F.SYSTIN 
moel@355
   194
            };
moel@355
   195
          }
moel@355
   196
          fanRpmBaseRegister = 0x656;
moel@355
   197
moel@355
   198
          controls = new float?[3];
moel@355
   199
moel@355
   200
          voltages = new float?[9];
moel@355
   201
          voltageRegisters = new ushort[] 
moel@355
   202
            { 0x020, 0x021, 0x022, 0x023, 0x024, 0x025, 0x026, 0x550, 0x551 };
moel@355
   203
          voltageVBatRegister = 0x551;
moel@355
   204
moel@355
   205
          temperatures = new float?[4];
moel@355
   206
          temperatureRegister = new ushort[]
moel@355
   207
            { 0x027, 0x073, 0x075, 0x077, 0x150, 0x250, 0x62B, 0x62C, 0x62D };
moel@355
   208
          temperatureHalfRegister = new ushort[]
moel@355
   209
            { 0, 0x074, 0x076, 0x078, 0x151, 0x251, 0x62E, 0x62E, 0x62E };
moel@355
   210
          temperatureHalfBit = new int[] 
moel@355
   211
            { -1, 7, 7, 7, 7, 7, 0, 1, 2 };
moel@355
   212
          temperatureSourceRegister = new ushort[]
moel@355
   213
            { 0x621, 0x100, 0x200, 0x300, 0x622, 0x623, 0x624, 0x625, 0x626 };
moel@355
   214
moel@355
   215
          alternateTemperatureRegister = new ushort?[] 
moel@355
   216
            { null, null, null, null };
moel@265
   217
          break;
moel@356
   218
moel@355
   219
        case Chip.NCT6779D:
moel@413
   220
        case Chip.NCT6791D:
moel@413
   221
          if (chip == Chip.NCT6779D) {
moel@413
   222
            fans = new float?[5];
moel@413
   223
            controls = new float?[5];
moel@413
   224
          } else {
moel@413
   225
            fans = new float?[6];
moel@413
   226
            controls = new float?[6];
moel@413
   227
          }
moel@413
   228
moel@355
   229
          fanRpmBaseRegister = 0x4C0;
moel@355
   230
moel@265
   231
          // min value RPM value with 13-bit fan counter
moel@265
   232
          minFanRPM = (int)(1.35e6 / 0x1FFF);
moel@355
   233
moel@355
   234
          voltages = new float?[15];
moel@355
   235
          voltageRegisters = new ushort[] 
moel@355
   236
            { 0x480, 0x481, 0x482, 0x483, 0x484, 0x485, 0x486, 0x487, 0x488, 
moel@355
   237
              0x489, 0x48A, 0x48B, 0x48C, 0x48D, 0x48E };
moel@355
   238
          voltageVBatRegister = 0x488;
moel@355
   239
moel@355
   240
          temperatures = new float?[7];
moel@355
   241
          temperaturesSource = new byte[] {
moel@413
   242
            (byte)SourceNCT67XXD.PECI_0,
moel@413
   243
            (byte)SourceNCT67XXD.CPUTIN,
moel@413
   244
            (byte)SourceNCT67XXD.SYSTIN,
moel@413
   245
            (byte)SourceNCT67XXD.AUXTIN0,
moel@413
   246
            (byte)SourceNCT67XXD.AUXTIN1,
moel@413
   247
            (byte)SourceNCT67XXD.AUXTIN2,
moel@413
   248
            (byte)SourceNCT67XXD.AUXTIN3
moel@355
   249
          };
moel@355
   250
moel@355
   251
          temperatureRegister = new ushort[]
moel@355
   252
            { 0x027, 0x073, 0x075, 0x077, 0x079, 0x07B, 0x150 };
moel@355
   253
          temperatureHalfRegister = new ushort[]
moel@355
   254
            { 0, 0x074, 0x076, 0x078, 0x07A, 0x07C, 0x151 };              
moel@355
   255
          temperatureHalfBit = new int[]
moel@355
   256
            { -1, 7, 7, 7, 7, 7, 7 };
moel@355
   257
          temperatureSourceRegister = new ushort[] 
moel@355
   258
            { 0x621, 0x100, 0x200, 0x300, 0x800, 0x900, 0x622 };
moel@355
   259
moel@355
   260
          alternateTemperatureRegister = new ushort?[] 
moel@355
   261
            {null, 0x491, 0x490, 0x492, 0x493, 0x494, 0x495 };
moel@355
   262
moel@413
   263
          break;
moel@265
   264
      }
moel@245
   265
    }
moel@245
   266
moel@245
   267
    private bool IsNuvotonVendor() {
moel@245
   268
      return ((ReadByte(VENDOR_ID_HIGH_REGISTER) << 8) |
moel@245
   269
        ReadByte(VENDOR_ID_LOW_REGISTER)) == NUVOTON_VENDOR_ID;
moel@245
   270
    }
moel@245
   271
moel@245
   272
    public byte? ReadGPIO(int index) {
moel@245
   273
      return null;
moel@245
   274
    }
moel@245
   275
moel@245
   276
    public void WriteGPIO(int index, byte value) { }
moel@245
   277
moel@323
   278
moel@323
   279
    private void SaveDefaultFanControl(int index) {
moel@323
   280
      if (!restoreDefaultFanControlRequired[index]) {
moel@323
   281
        initialFanControlMode[index] = ReadByte(FAN_CONTROL_MODE_REG[index]);
moel@323
   282
        initialFanPwmCommand[index] = ReadByte(FAN_PWM_COMMAND_REG[index]);
moel@323
   283
        restoreDefaultFanControlRequired[index] = true;
moel@323
   284
      }
moel@323
   285
    }
moel@323
   286
moel@323
   287
    private void RestoreDefaultFanControl(int index) {
moel@323
   288
      if (restoreDefaultFanControlRequired[index]) {
moel@323
   289
        WriteByte(FAN_CONTROL_MODE_REG[index], initialFanControlMode[index]);
moel@323
   290
        WriteByte(FAN_PWM_COMMAND_REG[index], initialFanPwmCommand[index]);
moel@323
   291
        restoreDefaultFanControlRequired[index] = false;
moel@323
   292
      }
moel@323
   293
    }
moel@323
   294
moel@323
   295
    public void SetControl(int index, byte? value) {
moel@355
   296
      if (index < 0 || index >= controls.Length)
moel@355
   297
        throw new ArgumentOutOfRangeException("index");
moel@355
   298
moel@323
   299
      if (!Ring0.WaitIsaBusMutex(10))
moel@323
   300
        return;
moel@323
   301
moel@323
   302
      if (value.HasValue) {
moel@323
   303
        SaveDefaultFanControl(index);
moel@323
   304
moel@323
   305
        // set manual mode
moel@323
   306
        WriteByte(FAN_CONTROL_MODE_REG[index], 0);
moel@323
   307
moel@323
   308
        // set output value
moel@323
   309
        WriteByte(FAN_PWM_COMMAND_REG[index], value.Value);  
moel@323
   310
      } else {
moel@323
   311
        RestoreDefaultFanControl(index);
moel@323
   312
      }
moel@323
   313
moel@323
   314
      Ring0.ReleaseIsaBusMutex();
moel@323
   315
    }   
moel@323
   316
moel@245
   317
    public Chip Chip { get { return chip; } }
moel@245
   318
    public float?[] Voltages { get { return voltages; } }
moel@245
   319
    public float?[] Temperatures { get { return temperatures; } }
moel@245
   320
    public float?[] Fans { get { return fans; } }
moel@323
   321
    public float?[] Controls { get { return controls; } }
moel@245
   322
moel@245
   323
    public void Update() {
moel@245
   324
      if (!Ring0.WaitIsaBusMutex(10))
moel@245
   325
        return;
moel@245
   326
moel@245
   327
      for (int i = 0; i < voltages.Length; i++) {
moel@355
   328
        float value = 0.008f * ReadByte(voltageRegisters[i]);
moel@245
   329
        bool valid = value > 0;
moel@245
   330
moel@245
   331
        // check if battery voltage monitor is enabled
moel@355
   332
        if (valid && voltageRegisters[i] == voltageVBatRegister) 
moel@245
   333
          valid = (ReadByte(0x005D) & 0x01) > 0;
moel@245
   334
moel@245
   335
        voltages[i] = valid ? value : (float?)null;
moel@245
   336
      }
moel@245
   337
moel@355
   338
      int temperatureSourceMask = 0;
moel@355
   339
      for (int i = temperatureRegister.Length - 1; i >= 0 ; i--) {
moel@355
   340
        int value = ((sbyte)ReadByte(temperatureRegister[i])) << 1;
moel@355
   341
        if (temperatureHalfBit[i] > 0) {
moel@355
   342
          value |= ((ReadByte(temperatureHalfRegister[i]) >>
moel@355
   343
            temperatureHalfBit[i]) & 0x1);
moel@245
   344
        }
moel@245
   345
moel@355
   346
        byte source = ReadByte(temperatureSourceRegister[i]);
moel@355
   347
        temperatureSourceMask |= 1 << source;
moel@245
   348
moel@246
   349
        float? temperature = 0.5f * value;
moel@246
   350
        if (temperature > 125 || temperature < -55)
moel@246
   351
          temperature = null;
moel@246
   352
moel@355
   353
        for (int j = 0; j < temperatures.Length; j++) 
moel@355
   354
          if (temperaturesSource[j] == source)
moel@355
   355
            temperatures[j] = temperature; 
moel@355
   356
      }
moel@355
   357
      for (int i = 0; i < alternateTemperatureRegister.Length; i++) {
moel@355
   358
        if (!alternateTemperatureRegister[i].HasValue)
moel@355
   359
          continue;
moel@355
   360
moel@355
   361
        if ((temperatureSourceMask & (1 << temperaturesSource[i])) > 0)
moel@355
   362
          continue;
moel@355
   363
moel@356
   364
        float? temperature = (sbyte)
moel@356
   365
          ReadByte(alternateTemperatureRegister[i].Value);
moel@356
   366
moel@356
   367
        if (temperature > 125 || temperature < -55)
moel@356
   368
          temperature = null;
moel@356
   369
moel@356
   370
        temperatures[i] = temperature;
moel@245
   371
      }
moel@245
   372
moel@245
   373
      for (int i = 0; i < fans.Length; i++) {
moel@355
   374
        byte high = ReadByte((ushort)(fanRpmBaseRegister + (i << 1)));
moel@355
   375
        byte low = ReadByte((ushort)(fanRpmBaseRegister + (i << 1) + 1));
moel@265
   376
        int value = (high << 8) | low;
moel@265
   377
moel@265
   378
        fans[i] = value > minFanRPM ? value : 0;
moel@245
   379
      }
moel@245
   380
moel@323
   381
      for (int i = 0; i < controls.Length; i++) {
moel@323
   382
        int value = ReadByte(FAN_PWM_OUT_REG[i]);
moel@323
   383
        controls[i] = value / 2.55f;
moel@323
   384
      }
moel@323
   385
moel@245
   386
      Ring0.ReleaseIsaBusMutex();
moel@245
   387
    }
moel@245
   388
moel@245
   389
    public string GetReport() {
moel@245
   390
      StringBuilder r = new StringBuilder();
moel@245
   391
moel@245
   392
      r.AppendLine("LPC " + this.GetType().Name);
moel@245
   393
      r.AppendLine();
moel@245
   394
      r.Append("Chip ID: 0x"); r.AppendLine(chip.ToString("X"));
moel@245
   395
      r.Append("Chip revision: 0x");
moel@245
   396
      r.AppendLine(revision.ToString("X", CultureInfo.InvariantCulture));
moel@245
   397
      r.Append("Base Adress: 0x");
moel@245
   398
      r.AppendLine(port.ToString("X4", CultureInfo.InvariantCulture));
moel@245
   399
      r.AppendLine();
moel@245
   400
moel@245
   401
      if (!Ring0.WaitIsaBusMutex(100))
moel@245
   402
        return r.ToString();
moel@245
   403
moel@246
   404
      ushort[] addresses = new ushort[] { 
moel@246
   405
        0x000, 0x010, 0x020, 0x030, 0x040, 0x050, 0x060, 0x070,
moel@246
   406
        0x100, 0x110, 0x120, 0x130, 0x140, 0x150, 
moel@413
   407
        0x200,        0x220, 0x230, 0x240, 0x250, 0x260,
moel@413
   408
        0x300,        0x320, 0x330, 0x340,        0x360,
moel@413
   409
        0x400, 0x410, 0x420,        0x440, 0x450, 0x460, 0x480, 0x490, 0x4B0, 
moel@413
   410
                                                                0x4C0, 0x4F0,
moel@413
   411
        0x500,                             0x550, 0x560,
moel@246
   412
        0x600, 0x610 ,0x620, 0x630, 0x640, 0x650, 0x660, 0x670, 
moel@413
   413
        0x700, 0x710, 0x720, 0x730,
moel@413
   414
        0x800,        0x820, 0x830, 0x840,
moel@413
   415
        0x900,        0x920, 0x930, 0x940,        0x960,
moel@413
   416
        0xA00, 0xA10, 0xA20, 0xA30, 0xA40, 0xA50, 0xA60, 0xA70, 
moel@246
   417
        0xB00, 0xB10, 0xB20, 0xB30,        0xB50, 0xB60, 0xB70, 
moel@246
   418
        0xC00, 0xC10, 0xC20, 0xC30,        0xC50, 0xC60, 0xC70,
moel@246
   419
        0xD00, 0xD10, 0xD20, 0xD30,        0xD50, 0xD60, 
moel@246
   420
        0xE00, 0xE10, 0xE20, 0xE30, 
moel@417
   421
        0xF00, 0xF10, 0xF20, 0xF30,
moel@417
   422
        0x8040};
moel@246
   423
moel@245
   424
      r.AppendLine("Hardware Monitor Registers");
moel@245
   425
      r.AppendLine();
moel@417
   426
      r.AppendLine("        00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
moel@245
   427
      r.AppendLine();
moel@246
   428
      foreach (ushort address in addresses) {
moel@245
   429
          r.Append(" ");
moel@417
   430
          r.Append(address.ToString("X4", CultureInfo.InvariantCulture));
moel@245
   431
          r.Append("  ");
moel@246
   432
          for (ushort j = 0; j <= 0xF; j++) {
moel@245
   433
            r.Append(" ");
moel@246
   434
            r.Append(ReadByte((ushort)(address | j)).ToString(
moel@245
   435
              "X2", CultureInfo.InvariantCulture));
moel@245
   436
          }
moel@245
   437
          r.AppendLine();
moel@245
   438
      }
moel@245
   439
      r.AppendLine();
moel@245
   440
moel@245
   441
      Ring0.ReleaseIsaBusMutex();
moel@245
   442
moel@245
   443
      return r.ToString();
moel@245
   444
    }
moel@245
   445
  }
moel@245
   446
}