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