emu48-mirror/Sources/Emu48/ROMCRC.C
Gwenhael Le Moine c62947dac5
2023-09-17: Updated to version 1.66
Signed-off-by: Gwenhael Le Moine <gwenhael.le.moine@gmail.com>
2024-03-20 08:15:25 +01:00

250 lines
5.7 KiB
C

/*
* romcrc.c
*
* This file is part of Emu48
*
* Copyright (C) 2022 Christoph Gießelink
*
*/
#include "pch.h"
#include "Emu48.h"
#include "ops.h"
// flash page types
#define BOOT 0x86
#define FS 0x18
#define SYSTEM 0x32
#define ROM 0x0F
#define RAM 0xF0
//################
//#
//# Restore HP38G/HP48GX/SX ROM CRC
//#
//################
// Clarke/Yorke CRC for HP38G and HP48GX/SX
#define a0 0x0 // Start Address
#define d0 (n0*16) // Address offset
#define n0 0x4000 // Reads/Half-Sector
#define s0 1 // #Sectors (Sector Size=2*d)
// rebuild of the calculator =CHECKSUM function for the Clarke and the Yorke chip ROM
static WORD Checksum(LPBYTE pbyROM, DWORD dwStart, DWORD dwOffset, INT nReads, INT nSector)
{
int i,j;
WORD wCrc = 0;
for (;nSector > 0; --nSector) // evaluate each sector
{
LPBYTE pbyAddr1 = pbyROM + dwStart;
LPBYTE pbyAddr2 = pbyAddr1 + dwOffset;
for (i = 0; i < nReads; ++i) // no. of reads in sector
{
for (j = 0; j < 16; ++j) wCrc = UpCRC(wCrc,*pbyAddr1++);
for (j = 0; j < 16; ++j) wCrc = UpCRC(wCrc,*pbyAddr2++);
}
dwStart += 2 * dwOffset; // next start page
}
return wCrc;
}
// calculate proper checksum to produce final CRC of FFFF
static __inline WORD CalcChksum(WORD wCrc, WORD wChksum)
{
WORD q, r = wCrc, s = wChksum;
// first take the last 4-nib back out of the CRC
r = (((r >> 12) * 0x811) ^ (r << 4) ^ (s & 0xf));
s >>= 4;
r = (((r >> 12) * 0x811) ^ (r << 4) ^ (s & 0xf));
s >>= 4;
r = (((r >> 12) * 0x811) ^ (r << 4) ^ (s & 0xf));
s >>= 4;
r = (((r >> 12) * 0x811) ^ (r << 4) ^ (s & 0xf));
// calculate new checksum to correct the CRC
s = 0xf831; // required MSNs to make goal
q = (q<<4) | ((r ^ s) & 0xf); // get 1st (least sig) nib
r = (r>>4) ^ ((s & 0xf) * 0x1081);
s >>= 4;
q = (q<<4) | ((r ^ s) & 0xf);
r = (r>>4) ^ ((s & 0xf) * 0x1081);
s >>= 4;
q = (q<<4) | ((r ^ s) & 0xf);
r = (r>>4) ^ ((s & 0xf) * 0x1081);
s >>= 4;
q = (q<<4) | ((r ^ s) & 0xf);
return q;
}
static VOID CorrectCrc(DWORD dwAddrCrc, WORD wCrc)
{
if (wCrc != 0xFFFF) // wrong crc result
{
INT s;
// get actual crc correction value
const WORD wChkAct = (pbyRom[dwAddrCrc+0] << 12)
| (pbyRom[dwAddrCrc+1] << 8)
| (pbyRom[dwAddrCrc+2] << 4)
| (pbyRom[dwAddrCrc+3]);
wCrc = CalcChksum(wCrc,wChkAct); // calculate new checksum
for (s = 3; s >= 0; --s) // write new checksum
{
PatchNibble(dwAddrCrc + s,(BYTE) (wCrc & 0xf));
wCrc >>= 4;
}
}
return;
}
//################
//#
//# Restore HP49G ROM CRC
//#
//################
static VOID CorrectFlashCrc(LPBYTE pbyMem, DWORD dwSize, DWORD dwOffset, DWORD dwLength)
{
// address overflow (data length + 4 nibble CRC)
if (dwOffset + dwLength + 4 <= dwSize)
{
WORD wRefCrc,wCrc = 0;
pbyMem += dwOffset; // start address
for (; dwLength > 0; --dwLength) // update CRC
{
wCrc = UpCRC(wCrc,*pbyMem++);
}
wRefCrc = (WORD) Npack(pbyMem,4); // read reference CRC
if(wRefCrc != wCrc) // wrong page CRC
{
INT s;
// linear CRC address in ROM
DWORD dwAddrCrc = (DWORD) (pbyMem - pbyRom);
wRefCrc = wCrc; // working copy of CRC
for (s = 0; s < 4; ++s) // write new checksum
{
PatchNibble(dwAddrCrc++,(BYTE) (wRefCrc & 0xf));
wRefCrc >>= 4;
}
_ASSERT(wCrc == (WORD) Npack(pbyMem,4));
}
}
return;
}
static __inline VOID CorrectFlashRom(LPBYTE pbyMem, DWORD dwSize)
{
CorrectFlashCrc(pbyMem,dwSize,0x20A,Npack(pbyMem+0x20A,5));
return;
}
static __inline VOID CorrectFlashSystem(LPBYTE pbyMem, DWORD dwSize)
{
CorrectFlashCrc(pbyMem,dwSize,0x20A,Npack(pbyMem+0x100,5));
return;
}
static __inline VOID CorrectFlashPage(LPBYTE pbyMem, DWORD dwSize, DWORD dwPage)
{
_ASSERT(dwPage >= 0 && dwPage < 16);
dwPage *= _KB(128); // convert page no. to data offset
if (dwPage + _KB(128) <= dwSize) // page inside flash chip
{
BYTE byType;
pbyMem += dwPage; // page address
dwPage = _KB(128); // page size
// get bank type
byType = (BYTE) Npack(pbyMem+0x200,2);
if (byType == BOOT) // 1st half of page is the boot bank
{
pbyMem += _KB(64); // 2nd half of page
dwPage = _KB(64); // page size
// get bank type
byType = (BYTE) Npack(pbyMem+0x200,2);
}
_ASSERT(dwPage == _KB(64) || dwPage == _KB(128));
switch (byType)
{
case FS: // FRBankFileSystem
case ROM: // FRBankRom
CorrectFlashRom(pbyMem,dwPage);
break;
case SYSTEM: // FRBankSystem
CorrectFlashSystem(pbyMem,dwPage);
break;
case RAM: // FRBankRam
default: // illegal bank identifier
break;
}
}
return;
}
static __inline VOID CorrectAllFlashPages(VOID)
{
DWORD dwPage;
// check CRC of all pages
CONST DWORD dwLastPage = dwRomSize / _KB(128);
for (dwPage = 0; dwPage < dwLastPage; ++dwPage)
{
// correct CRC of page
CorrectFlashPage(pbyRom,dwRomSize,dwPage);
}
return;
}
//################
//#
//# Restore ROM CRC
//#
//################
VOID RebuildRomCrc(VOID)
{
// HP38G, HP48GX, HP48SX
if ((strchr("6AGS",cCurrentRomType)) && dwRomSize >= _KB(256))
{
// first 256KB
CorrectCrc(0x7FFFC,Checksum(pbyRom,a0,d0,n0,s0));
}
// HP38G, HP48GX
if ((strchr("6AG",cCurrentRomType)) && dwRomSize == _KB(512))
{
// second 256KB
CorrectCrc(0xFFFFC,Checksum(pbyRom,a0+_KB(256),d0,n0,s0));
}
// HP39G/40G
if (cCurrentRomType == 'E')
{
// has no Crc
}
// HP49G
if (cCurrentRomType == 'X' && dwRomSize == _KB(2048))
{
CorrectAllFlashPages(); // go through all pages
}
return;
}