计组机试盘点
Contents
programming07
实验要求是分别实现实模式、分段式、段页式三种内存的地址转换与数据加载功能
资料核心点:
实模式和保护模式都是CPU的工作模式的一种。
实模式出现于早期8088CPU时期。当时由于CPU的性能有限,一共只有 20 位地址线(所以地址空间只有1MB),以及一些 16 位的寄存器。为了能够通过这些 16 位的寄存器去构成 20 位的主存地址,通常需要用两个寄存器,第一个寄存器表示基址,第二个寄存器表示偏移量。那么两个 16 位的值如何组合成一个 20位的地址呢?实模式采用的方式是把基址先向左移 4 位,然后再与偏移量相加。即:
物理地址 = 基址 << 4 + 偏移量
而在本实验中,
logicAddr 48位 = 16位段寄存器 + 32位offset,计算公式为:①(16-bits段寄存器左移4位 + offset的低16-bits) = 20-bits物理地址 ②高位补0到32-bits
@return 32-bits实模式线性地址
因为物理地址只有20位,所以只要偏移量有效的低20位即可,为了最后是32位注意使用inttobinary转换可以自动补齐至32位
分段过程实现将逻辑地址转换为线性地址,分页过程再实现将线性地址转换为物理地址。
段选择符和段寄存器
段选择符格式如下图所示,其中TI和RPL字段我们暂时不用关心。高 13 位的索引值用来确定当前使用的段描述符在描述符表中的位置,表示是其中的第几个段表项
。
段选择符存放在段寄存器中,共有 6 个段寄存器: CS、SS、DS、ES、FS和GS。其中,CS是代码段寄存器,指向程序代码所在的段。SS是栈段寄存器,指向栈区所在的段。DS是数据段寄存器,指向程序的全局静态数据区所在的段。其他 3 个段寄存器可以指向任意的数据段。
段描述符
段描述符是一种数据结构,实际上就是分段方式下的段表项。
一个段描述符占用 8 个字节,其一般格式如下图所示,包括 32 位的基地址(Base)、 20 位的限界(SegLimit)和一些属性位。属性位比较多,我们只介绍属性位G。
属性位G表示粒度大小。G=1说明段以页(4KB)为基本单位,G=0则段以字节为基本单位。由于界限为20 位,所以当G=0时,最大的段为1B * 2^20 = 1MB。当G=1时,最大的段为4KB * 2^20 = 4GB。
Programming 05 cache
package memory.cache;
import memory.Memory;
import memory.cache.cacheReplacementStrategy.ReplacementStrategy;
import util.Transformer;
import java.util.Arrays;
/**
* 高速缓存抽象类
*/
public class Cache {
public static final boolean isAvailable = true; // 默认启用Cache
public static final int CACHE_SIZE_B = 32 * 1024; // 32 KB 总大小
public static final int LINE_SIZE_B = 64; // 64 B 行大小
private final CacheLine[] cache = new CacheLine[CACHE_SIZE_B / LINE_SIZE_B];
private int SETS; // 组数
private int setSize; // 每组行数
// 单例模式
private static final Cache cacheInstance = new Cache();
private Cache() {
for (int i = 0; i < cache.length; i++) {
cache[i] = new CacheLine();
}
}
public static Cache getCache() {
return cacheInstance;
}
private ReplacementStrategy replacementStrategy; // 替换策略
public static boolean isWriteBack; // 写策略
/**
* 读取[pAddr, pAddr + len)范围内的连续数据,可能包含多个数据块的内容
*
* @param pAddr 数据起始点(32位物理地址 = 26位块号 + 6位块内地址)
* @param len 待读数据的字节数
* @return 读取出的数据,以char数组的形式返回
*/
public byte[] read(String pAddr, int len) {
byte[] data = new byte[len];
int addr = Integer.parseInt(Transformer.binaryToInt("0" + pAddr));
int upperBound = addr + len;
int index = 0;
while (addr < upperBound) {
int nextSegLen = LINE_SIZE_B - (addr % LINE_SIZE_B);
if (addr + nextSegLen >= upperBound) {
nextSegLen = upperBound - addr;
}//get the length of the next segment
//if exceed the upper bound, then set the length to the rest of the data
int rowNO = fetch(Transformer.intToBinary(String.valueOf(addr)));
byte[] cache_data = cache[rowNO].getData();
int i = 0;
while (i < nextSegLen) {
data[index] = cache_data[addr % LINE_SIZE_B + i];
index++;
i++;
}
addr += nextSegLen;
}
return data;
}
/**
* 向cache中写入[pAddr, pAddr + len)范围内的连续数据,可能包含多个数据块的内容
*
* @param pAddr 数据起始点(32位物理地址 = 26位块号 + 6位块内地址)
* @param len 待写数据的字节数
* @param data 待写数据
*/
public void write(String pAddr, int len, byte[] data) {
int addr = Integer.parseInt(Transformer.binaryToInt("0" + pAddr));
int upperBound = addr + len;
int index = 0;
while (addr < upperBound) {
int nextSegLen = LINE_SIZE_B - (addr % LINE_SIZE_B);
if (addr + nextSegLen >= upperBound) {
nextSegLen = upperBound - addr;
}
int rowNO = fetch(Transformer.intToBinary(String.valueOf(addr)));
byte[] cache_data = cache[rowNO].getData();
int i = 0;
while (i < nextSegLen) {
cache_data[addr % LINE_SIZE_B + i] = data[index];
index++;
i++;
}
//TODO but done
//write the data according to the write strategy
//1. write through
if(!isWriteBack){
//dirty bit is always 0
//write the data to memory
Memory.getMemory().write(pAddr, len, data);
//update the cache and using strategy
addTimeStamp();
update(rowNO, getTag(pAddr), cache_data);
setTimeStamp(rowNO);
}
else{
//get the dirty bit
cache[rowNO].dirty = true;
//update the cache and using strategy
addTimeStamp();
update(rowNO, getTag(pAddr), cache_data);
setTimeStamp(rowNO);
}
addr += nextSegLen;
}
}
/**
* 从32位物理地址(26位块号 + 6位块内地址)获取目标数据的tag
* 首先将32位物理地址转换为26位,然后将其前面补0至32位
* @param pAddr 32位物理地址
* @return 数据的tag
*
*/
public char[] getTag(String pAddr) {
int tagSize = 26 - (int) (Math.log(SETS) / Math.log(2));
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 26 - tagSize; i++) {
builder.append("0");
}
builder.append(pAddr.substring(0, tagSize));
return builder.toString().toCharArray();
}
/**
* 查询{@link Cache#cache}表以确认包含pAddr的数据块是否在cache内
* 如果目标数据块不在Cache内,则将其从内存加载到Cache
*
* @param pAddr 数据起始点(32位物理地址 = 26位块号 + 6位块内地址)
* @return 数据块在Cache中的对应行号
*/
private int fetch(String pAddr) {
//TODO but done
int blockNO = getBlockNO(pAddr);
int rowNO = map(blockNO);
int start = (blockNO%SETS)*setSize;
int last = start + setSize;
//get the tag
char[] tag = getTag(pAddr);
//if didn't hit
if(rowNO == -1){
//get the data from memory(pAddr is 26 bits but the address in memory is 32 bits)
byte[] data = Memory.getMemory().read(pAddr.substring(0,26)+"000000", LINE_SIZE_B);
int availableRow = -1;
//find an available row
for(int i = start; i < last; i++){
if(!cache[i].validBit){
availableRow = i;
break;
}
}
//if there is an available row
if(availableRow != -1){
//update the cache
addTimeStamp();
update(availableRow, tag, data);
//update the strategy
setTimeStamp(availableRow);
rowNO = availableRow;
}
else{
//if there is no available row
//replace the row
int replace = replacementStrategy.replace(start, last, tag, data);
if(cache[replace].dirty){
//if the dirty bit is 1
//write the data to memory
Memory.getMemory().write(pAddr.substring(0,26)+"000000", LINE_SIZE_B, cache[replace].getData());
cache[replace].dirty = false;
}
//update the cache
addTimeStamp();
update(replace, tag, data);
setTimeStamp(replace);
return replace;
}
}
else {
//if hit
//update the strategy
replacementStrategy.hit(rowNO);
return rowNO;
}
return rowNO;
}
public String getpAddr(int rowNO) {
// System.out.println(getBlockNO("00000000000000000000000001000000"));
// rowNO = setSize * blocknum + blockAddr
int n = 0;
while (Math.pow(2, n) != SETS) {
n += 1;
}
String tag = String.valueOf(this.cache[rowNO].tag).substring(n, 26);
int blocknum = rowNO / setSize;
String block = Transformer.intToBinary("0" + blocknum).substring(32 - n, 32);
String blockAddr = Transformer.intToBinary("0" + (rowNO - setSize * blocknum)).substring(26, 32);
return tag + block + blockAddr;
}
/**
* 根据目标数据内存地址前26位的int表示,进行映射
*
* @param blockNO 数据在内存中的块号
* @return 返回cache中所对应的行,-1表示未命中
*/
private int map(int blockNO) {
//TODO but done
//search for the rowNO
int start = (blockNO%SETS)*setSize;
int last = start + setSize;
for(int i = start; i < last; i++){
if(cache[i].validBit){
if(Integer.parseInt(Transformer.binaryToInt(String.valueOf(cache[i].getTag()))) == blockNO/SETS){
return i;
}
}
}
return -1;
}
/**
* 更新cache
*
* @param rowNO 需要更新的cache行号
* @param tag 待更新数据的Tag
* @param input 待更新的数据
*/
public void update(int rowNO, char[] tag, byte[] input) {
//TODO but done
//update the cache
cache[rowNO].tag = tag;
cache[rowNO].data = input;
cache[rowNO].validBit = true;
//cache[rowNO].dirty = true;
//replacementStrategy.hit(rowNO);
}
/**
* 从32位物理地址(26位块号 + 6位块内地址)获取目标数据在内存中对应的块号
*
* @param pAddr 32位物理地址
* @return 数据在内存中的块号
*/
private int getBlockNO(String pAddr) {
return Integer.parseInt(Transformer.binaryToInt("0" + pAddr.substring(0, 26)));
}
public void setDirty(int rowNO){
cache[rowNO].dirty = false;
}
/**
* 该方法会被用于测试,请勿修改
* 使用策略模式,设置cache的替换策略
*
* @param replacementStrategy 替换策略
*/
public void setReplacementStrategy(ReplacementStrategy replacementStrategy) {
this.replacementStrategy = replacementStrategy;
}
/**
* 该方法会被用于测试,请勿修改
*
* @param SETS 组数
*/
public void setSETS(int SETS) {
this.SETS = SETS;
}
/**
* 该方法会被用于测试,请勿修改
*
* @param setSize 每组行数
*/
public void setSetSize(int setSize) {
this.setSize = setSize;
}
/**
* 告知Cache某个连续地址范围内的数据发生了修改,缓存失效
* 该方法仅在memory类中使用,请勿修改
*
* @param pAddr 发生变化的数据段的起始地址
* @param len 数据段长度
*/
public void invalid(String pAddr, int len) {
int from = getBlockNO(pAddr);
int to = getBlockNO(Transformer.intToBinary(String.valueOf(Integer.parseInt(Transformer.binaryToInt("0" + pAddr)) + len - 1)));
for (int blockNO = from; blockNO <= to; blockNO++) {
int rowNO = map(blockNO);
if (rowNO != -1) {
cache[rowNO].validBit = false;
}
}
}
/**
* 清除Cache全部缓存
* 该方法会被用于测试,请勿修改
*/
public void clear() {
for (CacheLine line : cache) {
if (line != null) {
line.validBit = false;
line.dirty = false;
line.timeStamp = 0L;
line.visited = 0;
}
}
}
/**
* 输入行号和对应的预期值,判断Cache当前状态是否符合预期
* 这个方法仅用于测试,请勿修改
*
* @param lineNOs 行号
* @param validations 有效值
* @param tags tag
* @return 判断结果
*/
public boolean checkStatus(int[] lineNOs, boolean[] validations, char[][] tags) {
if (lineNOs.length != validations.length || validations.length != tags.length) {
return false;
}
for (int i = 0; i < lineNOs.length; i++) {
CacheLine line = cache[lineNOs[i]];
if (line.validBit != validations[i]) {
return false;
}
if (!Arrays.equals(line.getTag(), tags[i])) {
System.out.println(Arrays.toString(line.getTag()));
System.out.println(Arrays.toString(tags[i]));
return false;
}
}
return true;
}
// 获取有效位
public boolean isValid(int rowNO){
return cache[rowNO].validBit;
}
// 获取脏位
public boolean isDirty(int rowNO){
return cache[rowNO].dirty;
}
// LFU算法增加访问次数
public void addVisited(int rowNO){
cache[rowNO].visited++;
}
// 获取访问次数
public int getVisited(int rowNO){
return cache[rowNO].visited;
}
// 用于LRU算法,重置时间戳
public void setTimeStamp(int rowNO){
cache[rowNO].timeStamp = 0L;
}
//增加时间戳
public void addTimeStamp(){
for(int i = 0; i < cache.length; i++){
if(cache[i].validBit)
{
cache[i].timeStamp++;
}
}
}
//用于FIFO算法,重置时间戳
public void setTimeStampFIFO(int rowNo){
cache[rowNo].timeStamp = 1L;
if((rowNo+1)%setSize == 0){
cache[rowNo+1-setSize].timeStamp = 0L;
}else{
cache[rowNo+1].timeStamp = 0L;
}
}
// 获取时间戳
public long getTimeStamp(int rowNO){
return cache[rowNO].timeStamp;
}
// 获取该行数据
public byte[] getData(int rowNO){
return cache[rowNO].data;
}
/**
* Cache行,每行长度为(1+22+{@link Cache#LINE_SIZE_B})
*/
private static class CacheLine {
// 有效位,标记该条数据是否有效
boolean validBit = false;
// 脏位,标记该条数据是否被修改
boolean dirty = false;
// 用于LFU算法,记录该条cache使用次数
int visited = 0;
// 用于LRU和FIFO算法,记录该条数据时间戳
Long timeStamp = 0L;
// 标记,占位长度为26位,有效长度取决于映射策略:
// 直接映射: 17 位
// 全关联映射: 26 位
// (2^n)-路组关联映射: 26-(9-n) 位
// 注意,tag在物理地址中用高位表示,如:直接映射(32位)=tag(17位)+行号(9位)+块内地址(6位),
// 那么对于值为0b1111的tag应该表示为00000000000000000000001111,其中低12位为有效长度
char[] tag = new char[26];
// 数据
byte[] data = new byte[LINE_SIZE_B];
byte[] getData() {
return this.data;
}
char[] getTag() {
return this.tag;
}
}
}
package cpu.mmu;
import memory.Memory;
import memory.cache.Cache;
import memory.tlb.TLB;
import util.Transformer;
/**
* MMU内存管理单元抽象类
* 接收一个48-bits的逻辑地址,并最终将其转换成32-bits的物理地址
* Memory.SEGMENT和Memory.PAGE标志用于表示是否开启分段和分页。
* 实际上在机器里从实模式进入保护模式后分段一定会保持开启(即使实模式也会使用段寄存器),因此一共只要实现三种模式的内存管理即可:实模式、只有分段、段页式
* 所有模式的物理地址都采用32-bits,实模式的物理地址高位补0
*/
public class MMU {
private static final MMU mmuInstance = new MMU();
private MMU() {
}
public static MMU getMMU() {
return mmuInstance;
}
private final Memory memory = Memory.getMemory();
private final Cache cache = Cache.getCache();
private final TLB tlb = TLB.getTLB();
/**
* 读取数据
*
* @param logicAddr 48-bits逻辑地址
* @param length 读取数据的长度
* @return 内存中的数据
*/
public byte[] read(String logicAddr, int length) {
String physicalAddr = addressTranslation(logicAddr, length);
if(Cache.isAvailable){
return cache.read(physicalAddr, length);
}
return memory.read(physicalAddr, length);
}
/**
* 写数据
*
* @param logicAddr 48-bits逻辑地址
* @param length 读取数据的长度
*/
public void write(String logicAddr, int length, byte[] data) {
String physicalAddr = addressTranslation(logicAddr, length);
if(Cache.isAvailable){
cache.write(physicalAddr, length, data);
return;
}
memory.write(physicalAddr, length, data);
}
/**
* 地址转换
*
* @param logicAddr 48-bits逻辑地址
* @param length 读取数据的长度
* @return 32-bits物理地址
*/
private String addressTranslation(String logicAddr, int length) {
String linearAddr; // 32位线性地址
String physicalAddr; // 32位物理地址
if (!Memory.SEGMENT) {
// 实模式:线性地址等于物理地址
linearAddr = toRealLinearAddr(logicAddr);
memory.real_load(linearAddr, length); // 从磁盘中加载到内存
physicalAddr = linearAddr;
} else {
// 分段模式
int segIndex = getSegIndex(logicAddr);
if (!memory.isValidSegDes(segIndex)) {
// 缺段中断,该段不在内存中,内存从磁盘加载该段索引的数据
memory.seg_load(segIndex);
}
linearAddr = toSegLinearAddr(logicAddr);
// 权限检查
int start = Integer.parseInt(Transformer.binaryToInt(linearAddr));
int base = chars2int(memory.getBaseOfSegDes(segIndex));
long limit = chars2int(memory.getLimitOfSegDes(segIndex));
if (memory.isGranularitySegDes(segIndex)) {
limit = (limit + 1) * Memory.PAGE_SIZE_B - 1;
}
if ((start < base) || (start + length > base + limit)) {
throw new SecurityException("Segmentation Fault");
}
if (!Memory.PAGE) {
// 分段模式:线性地址等于物理地址
physicalAddr = linearAddr;
} else {
// 段页式
int startvPageNo = Integer.parseInt(Transformer.binaryToInt(linearAddr.substring(0, 20))); // 高20位表示虚拟页号
int offset = Integer.parseInt(Transformer.binaryToInt(linearAddr.substring(20, 32))); // 低12位的页内偏移
int pages = (length - offset + Memory.PAGE_SIZE_B - 1) / Memory.PAGE_SIZE_B;
if (offset > 0) pages++;
int endvPageNo = startvPageNo + pages - 1;
for (int i = startvPageNo; i <= endvPageNo; i++) {
if(TLB.isAvailable){
if(!tlb.isValidPage(i)){
// 缺页中断,该页不在内存中,内存从磁盘加载该页的数据
memory.page_load(i);
tlb.write(i);
}
}
if (!memory.isValidPage(i)) {
// 缺页中断,该页不在内存中,内存从磁盘加载该页的数据
memory.page_load(i);
}
}
physicalAddr = toPagePhysicalAddr(linearAddr);
}
}
return physicalAddr;
}
/**
* 实模式下的逻辑地址转线性地址
*
* @param logicAddr 48位 = 16位段寄存器 + 32位offset,计算公式为:①(16-bits段寄存器左移4位 + offset的低16-bits) = 20-bits物理地址 ②高位补0到32-bits
* @return 32-bits实模式线性地址
*/
private String toRealLinearAddr(String logicAddr) {
return Transformer.intToBinary(""+(Integer.valueOf(logicAddr.substring(0, 16)+"0000",2)+Integer.valueOf("0000"+logicAddr.substring(48-16),2)));
}
private String add(char[] base, String offsetStr) {
char[] offset = offsetStr.toCharArray();
StringBuilder res = new StringBuilder();
char carry = '0';
for (int i = base.length - 1; i >= 0; i--) {
res.append((carry - '0') ^ (base[i] - '0') ^ (offset[i] - '0'));
carry = (char) (((carry - '0') & (base[i] - '0')) | ((carry - '0') & (offset[i] - '0')) | ((base[i] - '0') & (offset[i] - '0')) + '0');
}
int t = res.length();
for (int i = 0; i < 32 - t; i++) {
res.append("0");
}
return res.reverse().toString();
}
/**
* 分段模式下的逻辑地址转线性地址
*
* @param logicAddr 48位 = 16位段选择符(高13位index选择段表项) + 32位段内偏移
* @return 32-bits 线性地址
*/
private String toSegLinearAddr(String logicAddr) {
String offset = logicAddr.substring(16, 48);
int segIndex = Integer.parseInt(Transformer.binaryToInt(logicAddr.substring(0, 13))); // 段描述符索引
char[] segBase = memory.getBaseOfSegDes(segIndex); // 内存基址base 32位
return add(segBase, offset);
}
/**
* 段页式下的线性地址转物理地址
*
* @param linearAddr 32位
* @return 32-bits 物理地址
*/
private String toPagePhysicalAddr(String linearAddr) {
String offset = linearAddr.substring(20);
int vPageNo = Integer.parseInt(Transformer.binaryToInt("000000000000"+linearAddr.substring(0,20)));
return TLB.isAvailable ? (new String(tlb.getFrameOfPage(vPageNo)) + offset) : (String.valueOf(memory.getFrameOfPage(vPageNo)) + offset);
}
/**
* 根据逻辑地址找到对应的段索引
*
* @param logicAddr 逻辑地址
* @return 整数表示的段索引
*/
private int getSegIndex(String logicAddr) {
String indexStr = logicAddr.substring(0, 13);
return Integer.parseInt(Transformer.binaryToInt(indexStr)); // 段描述符索引
}
private int chars2int(char[] chars) {
return Integer.parseInt(Transformer.binaryToInt(String.valueOf(chars)));
}
public void clear() {
memory.clear();
if (Cache.isAvailable) {
cache.clear();
}
if (TLB.isAvailable) {
tlb.clear();
}
}
}
Programming 03 Integer Add & Sub
package cpu.alu;
import util.DataType;
import util.Transformer;
/**
* Arithmetic Logic Unit
* ALU封装类
*/
public class ALU {
/**
* 返回两个二进制整数的乘积(结果低位截取后32位)
* dest * src
*Put multiplicand in BR and multiplier in QR
* and then the algorithm works as per the following conditions :
* 1. If Qn and Qn+1 are same i.e. 00 or 11 perform arithmetic shift by 1 bit.
* 2. If Qn Qn+1 = 01 do A= A + BR and perform arithmetic shift by 1 bit.
* 3. If Qn Qn+1 = 10 do A= A – BR and perform arithmetic shift by 1 bit.
* @param src 32-bits
* @param dest 32-bits
* @return 32-bits
*/
public DataType mul(DataType src, DataType dest) {
//Booth Algorithm
String srcStr = src.toString();
String desStr = dest.toString();
StringBuilder srcStrBuilder = new StringBuilder("00000000000000000000000000000000");
srcStrBuilder.append(srcStr);
srcStrBuilder.append("0");//to fill the first
int len=srcStrBuilder.length();//should be 65
for(int i=0;i<32;i++){
if(srcStrBuilder.charAt(len-1)=='0'&&srcStrBuilder.charAt(len-2)=='1'){
srcStrBuilder.replace(0,32,sub(desStr,srcStrBuilder.substring(0,32)));
}
else if(srcStrBuilder.charAt(len-1)=='1'&&srcStrBuilder.charAt(len-2)=='0'){
srcStrBuilder.replace(0,32,add(srcStrBuilder.substring(0,32),desStr));
}
srcStrBuilder.insert(0,srcStrBuilder.charAt(0));
srcStrBuilder.deleteCharAt(len);
}
return new DataType(srcStrBuilder.substring(32,64));
}
static DataType remainderReg = new DataType("00000000000000000000000000000000");
/**
* 返回两个二进制整数的除法结果
* dest ÷ src
*
* @param src 32-bits
* @param dest 32-bits
* @return 32-bits
*/
public DataType div(DataType src, DataType dest) {
String srcStr = src.toString();
String desStr = dest.toString();
String retu="";
StringBuilder ans=new StringBuilder("00000000000000000000000000000000");
if(srcStr.equals(ans.toString())){
throw new ArithmeticException("Divisor cannot be 0");
}
if(desStr.equals(ans.toString())){
return dest;
}
int flag=0;
if(srcStr.charAt(0)==desStr.charAt(0)){
flag=0;
if(srcStr.charAt(0)=='1'){
srcStr=not(srcStr);
desStr=not(desStr);
}
}
else{
flag=1;
if(srcStr.charAt(0)=='1'){
srcStr=not(srcStr);
}
else {
desStr=not(desStr);
}
}
ans.append(desStr);
int len=ans.length();
String temp="";
for(int i=1;i<=32;i++){
temp=sub(srcStr,ans.substring(i,i+32));
if(temp.charAt(0)=='0'){
ans.replace(i,i+32,temp);
retu+="1";
}
else{
retu+="0";
}
}
if(flag==1){
retu=not(retu);
}
remainderReg=new DataType(ans.substring(32,64));
if(dest.toString().charAt(0)=='1')
remainderReg=new DataType(not(ans.substring(32,64)));
return new DataType(retu);
}
public static void main(String[] args){
ALU alu=new ALU();
DataType src = new DataType(intToBinary("10"));
DataType dest = new DataType(intToBinary("-25"));
String ans=alu.div(src,dest).toString();
System.out.println(binaryToInt(remainderReg.toString()));
System.out.println(binaryToInt(ans));
}
public String add(String src, String dest) {
int c=0;
int s1,d1;
String s = src.toString();
String d = dest.toString();
StringBuilder ans = new StringBuilder();
for(int i=s.length()-1;i>=0;i--){
s1 = s.charAt(i) - '0';
d1 = d.charAt(i) - '0';
ans.append(c ^ s1 ^ d1);
c = (s1 & d1) | (c & d1) | (s1 & c);
}
return ans.reverse().toString();
}
/*
sub: dest - src
*/
public String sub(String srcStr, String destStr) {
return add(destStr, not(srcStr));
}
public String not(String srcStr){
String ans = "";
for(int i = 0;i < 32;i++){
if(srcStr.charAt(i) == '0'){
ans = ans + '1';
}
else{
ans = ans + '0';
}
}
return add(ans,"00000000000000000000000000000001");
}
public static String intToBinary(String numStr) {
return BinarytoComplement(DecimaltoBinary(numStr,true));
}
public static String binaryToInt(String binStr) {
int num=0;
int len=binStr.length(),result =0;
for(int i=1;i<=len-1;i++)
{
num=binStr.charAt(i)-'0';
result+=num*Math.pow(2,len-1-i);
}
if(binStr.charAt(0)=='1')
result-=Math.pow(2,len-1);
return String.valueOf(result);
}
public static String BinarytoComplement(String binStr){
int len=binStr.length();
StringBuilder result=new StringBuilder();
if(binStr.charAt(0)=='1'){
result.append("1");
for(int i=1;i<len;i++) {
if (binStr.charAt(i) == '0')
result.append("1");
else
result.append("0");
}
//System.out.println(result.toString());
StringBuilder temp=new StringBuilder();
int i=result.length()-1;
while(result.charAt(i)=='1'&&i>0){
temp.append("0");
i--;
}
if(i>0)
temp.append("1");
i--;
for(;i>0;i--){
temp.append(result.charAt(i));
}
temp.append("1");
return temp.reverse().toString();
}
else {
return binStr;
}
}
public static String DecimaltoBinary(String decimalStr,boolean f){
StringBuilder result=new StringBuilder();
int num=Integer.parseInt(decimalStr);
int flag=0;
if(num<0){
num=-num;
flag=1;
}
int len=0;
while(num>0){
result.append(num%2);
num/=2;
len++;
}
while(len<31&&f)
{
result.append("0");
len++;
}
if (f)
{
if (flag == 1) {
result.append("1");
} else
result.append("0");
}
return result.reverse().toString();
}
}
Programmming 05 cache
package memory.cache;
import memory.Memory;
import memory.cache.cacheReplacementStrategy.ReplacementStrategy;
import util.Transformer;
import java.util.Arrays;
/**
* 高速缓存抽象类
*/
public class Cache {
public static final boolean isAvailable = true; // 默认启用Cache
public static final int CACHE_SIZE_B = 32 * 1024; // 32 KB 总大小
public static final int LINE_SIZE_B = 64; // 64 B 行大小
private final CacheLine[] cache = new CacheLine[CACHE_SIZE_B / LINE_SIZE_B];
private int SETS; // 组数
private int setSize; // 每组行数
// 单例模式
private static final Cache cacheInstance = new Cache();
private Cache() {
for (int i = 0; i < cache.length; i++) {
cache[i] = new CacheLine();
}
}
public static Cache getCache() {
return cacheInstance;
}
private ReplacementStrategy replacementStrategy; // 替换策略
public static boolean isWriteBack; // 写策略
/**
* 读取[pAddr, pAddr + len)范围内的连续数据,可能包含多个数据块的内容
*
* @param pAddr 数据起始点(32位物理地址 = 26位块号 + 6位块内地址)
* @param len 待读数据的字节数
* @return 读取出的数据,以char数组的形式返回
*/
public byte[] read(String pAddr, int len) {
byte[] data = new byte[len];
int addr = Integer.parseInt(Transformer.binaryToInt("0" + pAddr));
int upperBound = addr + len;
int index = 0;
while (addr < upperBound) {
int nextSegLen = LINE_SIZE_B - (addr % LINE_SIZE_B);
if (addr + nextSegLen >= upperBound) {
nextSegLen = upperBound - addr;
}//get the length of the next segment
//if exceed the upper bound, then set the length to the rest of the data
int rowNO = fetch(Transformer.intToBinary(String.valueOf(addr)));
byte[] cache_data = cache[rowNO].getData();
int i = 0;
while (i < nextSegLen) {
data[index] = cache_data[addr % LINE_SIZE_B + i];
index++;
i++;
}
addr += nextSegLen;
}
return data;
}
/**
* 向cache中写入[pAddr, pAddr + len)范围内的连续数据,可能包含多个数据块的内容
*
* @param pAddr 数据起始点(32位物理地址 = 26位块号 + 6位块内地址)
* @param len 待写数据的字节数
* @param data 待写数据
*/
public void write(String pAddr, int len, byte[] data) {
int addr = Integer.parseInt(Transformer.binaryToInt("0" + pAddr));
int upperBound = addr + len;
int index = 0;
while (addr < upperBound) {
int nextSegLen = LINE_SIZE_B - (addr % LINE_SIZE_B);
if (addr + nextSegLen >= upperBound) {
nextSegLen = upperBound - addr;
}
int rowNO = fetch(Transformer.intToBinary(String.valueOf(addr)));
byte[] cache_data = cache[rowNO].getData();
int i = 0;
while (i < nextSegLen) {
cache_data[addr % LINE_SIZE_B + i] = data[index];
index++;
i++;
}
//TODO but done
//write the data according to the write strategy
//1. write through
if(!isWriteBack){
//dirty bit is always 0
//write the data to memory
Memory.getMemory().write(pAddr, len, data);
//update the cache and using strategy
addTimeStamp();
update(rowNO, getTag(pAddr), cache_data);
setTimeStamp(rowNO);
}
else{
//get the dirty bit
cache[rowNO].dirty = true;
//update the cache and using strategy
addTimeStamp();
update(rowNO, getTag(pAddr), cache_data);
setTimeStamp(rowNO);
}
addr += nextSegLen;
}
}
/**
* 从32位物理地址(26位块号 + 6位块内地址)获取目标数据的tag
* 首先将32位物理地址转换为26位,然后将其前面补0至32位
* @param pAddr 32位物理地址
* @return 数据的tag
*
*/
public char[] getTag(String pAddr) {
int tagSize = 26 - (int) (Math.log(SETS) / Math.log(2));
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 26 - tagSize; i++) {
builder.append("0");
}
builder.append(pAddr.substring(0, tagSize));
return builder.toString().toCharArray();
}
/**
* 查询{@link Cache#cache}表以确认包含pAddr的数据块是否在cache内
* 如果目标数据块不在Cache内,则将其从内存加载到Cache
*
* @param pAddr 数据起始点(32位物理地址 = 26位块号 + 6位块内地址)
* @return 数据块在Cache中的对应行号
*/
private int fetch(String pAddr) {
//TODO but done
int blockNO = getBlockNO(pAddr);
int rowNO = map(blockNO);
int start = (blockNO%SETS)*setSize;
int last = start + setSize;
//get the tag
char[] tag = getTag(pAddr);
//if didn't hit
if(rowNO == -1){
//get the data from memory(pAddr is 26 bits but the address in memory is 32 bits)
byte[] data = Memory.getMemory().read(pAddr.substring(0,26)+"000000", LINE_SIZE_B);
int availableRow = -1;
//find an available row
for(int i = start; i < last; i++){
if(!cache[i].validBit){
availableRow = i;
break;
}
}
//if there is an available row
if(availableRow != -1){
//update the cache
addTimeStamp();
update(availableRow, tag, data);
//update the strategy
setTimeStamp(availableRow);
rowNO = availableRow;
}
else{
//if there is no available row
//replace the row
int replace = replacementStrategy.replace(start, last, tag, data);
if(cache[replace].dirty){
//if the dirty bit is 1
//write the data to memory
Memory.getMemory().write(pAddr.substring(0,26)+"000000", LINE_SIZE_B, cache[replace].getData());
cache[replace].dirty = false;
}
//update the cache
addTimeStamp();
update(replace, tag, data);
setTimeStamp(replace);
return replace;
}
}
else {
//if hit
//update the strategy
replacementStrategy.hit(rowNO);
return rowNO;
}
return rowNO;
}
public String getpAddr(int rowNO) {
// System.out.println(getBlockNO("00000000000000000000000001000000"));
// rowNO = setSize * blocknum + blockAddr
int n = 0;
while (Math.pow(2, n) != SETS) {
n += 1;
}
String tag = String.valueOf(this.cache[rowNO].tag).substring(n, 26);
int blocknum = rowNO / setSize;
String block = Transformer.intToBinary("0" + blocknum).substring(32 - n, 32);
String blockAddr = Transformer.intToBinary("0" + (rowNO - setSize * blocknum)).substring(26, 32);
return tag + block + blockAddr;
}
/**
* 根据目标数据内存地址前26位的int表示,进行映射
*
* @param blockNO 数据在内存中的块号
* @return 返回cache中所对应的行,-1表示未命中
*/
private int map(int blockNO) {
//TODO but done
//search for the rowNO
int start = (blockNO%SETS)*setSize;
int last = start + setSize;
for(int i = start; i < last; i++){
if(cache[i].validBit){
if(Integer.parseInt(Transformer.binaryToInt(String.valueOf(cache[i].getTag()))) == blockNO/SETS){
return i;
}
}
}
return -1;
}
/**
* 更新cache
*
* @param rowNO 需要更新的cache行号
* @param tag 待更新数据的Tag
* @param input 待更新的数据
*/
public void update(int rowNO, char[] tag, byte[] input) {
//TODO but done
//update the cache
cache[rowNO].tag = tag;
cache[rowNO].data = input;
cache[rowNO].validBit = true;
//cache[rowNO].dirty = true;
//replacementStrategy.hit(rowNO);
}
/**
* 从32位物理地址(26位块号 + 6位块内地址)获取目标数据在内存中对应的块号
*
* @param pAddr 32位物理地址
* @return 数据在内存中的块号
*/
private int getBlockNO(String pAddr) {
return Integer.parseInt(Transformer.binaryToInt("0" + pAddr.substring(0, 26)));
}
public void setDirty(int rowNO){
cache[rowNO].dirty = false;
}
/**
* 该方法会被用于测试,请勿修改
* 使用策略模式,设置cache的替换策略
*
* @param replacementStrategy 替换策略
*/
public void setReplacementStrategy(ReplacementStrategy replacementStrategy) {
this.replacementStrategy = replacementStrategy;
}
/**
* 该方法会被用于测试,请勿修改
*
* @param SETS 组数
*/
public void setSETS(int SETS) {
this.SETS = SETS;
}
/**
* 该方法会被用于测试,请勿修改
*
* @param setSize 每组行数
*/
public void setSetSize(int setSize) {
this.setSize = setSize;
}
/**
* 告知Cache某个连续地址范围内的数据发生了修改,缓存失效
* 该方法仅在memory类中使用,请勿修改
*
* @param pAddr 发生变化的数据段的起始地址
* @param len 数据段长度
*/
public void invalid(String pAddr, int len) {
int from = getBlockNO(pAddr);
int to = getBlockNO(Transformer.intToBinary(String.valueOf(Integer.parseInt(Transformer.binaryToInt("0" + pAddr)) + len - 1)));
for (int blockNO = from; blockNO <= to; blockNO++) {
int rowNO = map(blockNO);
if (rowNO != -1) {
cache[rowNO].validBit = false;
}
}
}
/**
* 清除Cache全部缓存
* 该方法会被用于测试,请勿修改
*/
public void clear() {
for (CacheLine line : cache) {
if (line != null) {
line.validBit = false;
line.dirty = false;
line.timeStamp = 0L;
line.visited = 0;
}
}
}
/**
* 输入行号和对应的预期值,判断Cache当前状态是否符合预期
* 这个方法仅用于测试,请勿修改
*
* @param lineNOs 行号
* @param validations 有效值
* @param tags tag
* @return 判断结果
*/
public boolean checkStatus(int[] lineNOs, boolean[] validations, char[][] tags) {
if (lineNOs.length != validations.length || validations.length != tags.length) {
return false;
}
for (int i = 0; i < lineNOs.length; i++) {
CacheLine line = cache[lineNOs[i]];
if (line.validBit != validations[i]) {
return false;
}
if (!Arrays.equals(line.getTag(), tags[i])) {
System.out.println(Arrays.toString(line.getTag()));
System.out.println(Arrays.toString(tags[i]));
return false;
}
}
return true;
}
// 获取有效位
public boolean isValid(int rowNO){
return cache[rowNO].validBit;
}
// 获取脏位
public boolean isDirty(int rowNO){
return cache[rowNO].dirty;
}
// LFU算法增加访问次数
public void addVisited(int rowNO){
cache[rowNO].visited++;
}
// 获取访问次数
public int getVisited(int rowNO){
return cache[rowNO].visited;
}
// 用于LRU算法,重置时间戳
public void setTimeStamp(int rowNO){
cache[rowNO].timeStamp = 0L;
}
//增加时间戳
public void addTimeStamp(){
for(int i = 0; i < cache.length; i++){
if(cache[i].validBit)
{
cache[i].timeStamp++;
}
}
}
//用于FIFO算法,重置时间戳
public void setTimeStampFIFO(int rowNo){
cache[rowNo].timeStamp = 1L;
if((rowNo+1)%setSize == 0){
cache[rowNo+1-setSize].timeStamp = 0L;
}else{
cache[rowNo+1].timeStamp = 0L;
}
}
// 获取时间戳
public long getTimeStamp(int rowNO){
return cache[rowNO].timeStamp;
}
// 获取该行数据
public byte[] getData(int rowNO){
return cache[rowNO].data;
}
/**
* Cache行,每行长度为(1+22+{@link Cache#LINE_SIZE_B})
*/
private static class CacheLine {
// 有效位,标记该条数据是否有效
boolean validBit = false;
// 脏位,标记该条数据是否被修改
boolean dirty = false;
// 用于LFU算法,记录该条cache使用次数
int visited = 0;
// 用于LRU和FIFO算法,记录该条数据时间戳
Long timeStamp = 0L;
// 标记,占位长度为26位,有效长度取决于映射策略:
// 直接映射: 17 位
// 全关联映射: 26 位
// (2^n)-路组关联映射: 26-(9-n) 位
// 注意,tag在物理地址中用高位表示,如:直接映射(32位)=tag(17位)+行号(9位)+块内地址(6位),
// 那么对于值为0b1111的tag应该表示为00000000000000000000001111,其中低12位为有效长度
char[] tag = new char[26];
// 数据
byte[] data = new byte[LINE_SIZE_B];
byte[] getData() {
return this.data;
}
char[] getTag() {
return this.tag;
}
}
}
Programming 04 Float add and mul and div
public String floatAddition(String operand1, String operand2, int eLength, int sLength, int gLength) {
//floatAddition加法的特殊情况,如果有0,答案就是另一个,如果有一个是+Inf结果就是Inf
if (allZero(operand1.substring(1))) {
return "0" + operand2;
}
if (allZero(operand2.substring(1))) {
return "0" + operand1;
}
//处理阶码,考虑阶码的2种特殊的情况,全0和全1.全0就+1,全1的话,返回本身
int Xexponent = Integer.valueOf(operand1.substring(1, 1 + eLength), 2); //注意这里要加上第二个参数2,不然默认十进制转化
int Yexponent = Integer.valueOf(operand2.substring(1, 1 + eLength), 2);
if (Xexponent == 0) Xexponent++; //如果指数是全0,那么指数的真实值为1,因为阶码已经考虑了隐藏位
if (Yexponent == 0) Yexponent++;
if (Xexponent == 255) {
return "0" + operand1;
}
if (Yexponent == 255) {
return "0" + operand2;
}
//处理尾数:前面加了1位是规格化的
StringBuilder Xsig = new StringBuilder(getSignificand(operand1, eLength, sLength)); //get the two significands including the implicit bit
StringBuilder Ysig = new StringBuilder(getSignificand(operand2, eLength, sLength));
//加上保护胃,1+23+3=27
for (int i = 0; i < gLength; i++) { //add the guard bits
Xsig.append("0");
Ysig.append("0");
}
//现在Xsig组成为 隐藏位+sLength+保护位
int exponent = Math.max(Xexponent, Yexponent); //choose the bigger exponent and right shift the smaller one
//提取大的然后右移动
if (Xexponent != Yexponent) {
if (Xexponent > Yexponent) {
Ysig = new StringBuilder(rightShift(Ysig.toString(), Xexponent - Yexponent));
} else {
Xsig = new StringBuilder(rightShift(Xsig.toString(), Yexponent - Xexponent));
}
}
//尾码全部0就可以直接输出了
if (allZero(Xsig.toString())) {
return "0" + operand2;
}
if (allZero(Ysig.toString())) {
return "0" + operand1;
}
String temp = signedAddition(operand1.charAt(0) + Xsig.toString(), operand2.charAt(0) + Ysig.toString(), Xsig.length()); //需要传入符号。这里直接设置寄存器长度为 Xsig.length() 就可以检测是否溢出,传入参数结构为 符号位+隐藏位+sLength+保护位
//temp结构为 溢出位+符号位+隐藏位+sLength+保护位
boolean overflow = temp.charAt(0) != '0';
char sign = temp.charAt(1);
String sig = temp.substring(2); //sig结构为 隐藏位+sLength+保护位
StringBuilder answer = new StringBuilder();
//溢出情况
if (overflow) {
sig = "1" + sig.substring(0, sig.length() - 1); //右移一位(不可能要移动两次)
exponent++;
if (exponent == maxValue(eLength)) { //指数上溢
answer.append("1").append(sign);
answer.append(Integer.toBinaryString(exponent));
for (int i = 0; i < sLength; i++) answer.append("0"); //无穷要求阶码全为0
return answer.toString();
}
}
if (allZero(sig)) {
for (int i = 0; i < eLength + sLength + 2; i++) answer.append("0");
return answer.toString();
}
while (sig.charAt(0) != '1' && exponent > 0) { //规格化
sig = leftShift(sig, "1");
exponent--;
}
if (exponent == 0) { //非规格化数
sig = rightShift(sig, 1);
}
StringBuilder ans_exponent = new StringBuilder(Integer.toBinaryString(exponent));
int len = ans_exponent.length(); //注意要先把长度单独写出来,不能写在循环里面
for (int i = 0; i < eLength - len; i++) ans_exponent.insert(0, "0"); //补齐到eLength长度
return "0" + round(sign, ans_exponent.toString(), sig);
}
Another Version
public DataType add(DataType src, DataType dest) {
String destStr = dest.toString();
String srcStr = src.toString();
if (destStr.matches(IEEE754Float.NaN_Regular) || srcStr.matches(IEEE754Float.NaN_Regular)) {
return new DataType(IEEE754Float.NaN);
}
String cornerCondition = cornerCheck(addCorner, destStr, srcStr);
if (null != cornerCondition)
return new DataType(cornerCondition);
//corner cases
String exp1 = destStr.substring(1, 9);
String exp2 = srcStr.substring(1, 9);
//get the exp value
int expVal_1 = Integer.valueOf(exp1, 2);
int expVal_2 = Integer.valueOf(exp2, 2);
if (expVal_1 == 255)
return dest;
if (expVal_2 == 255)
return src;
if (destStr.substring(1).equals(IEEE754Float.P_ZERO.substring(1)))
return src;
if (srcStr.substring(1).equals(IEEE754Float.P_ZERO.substring(1)))
return dest;
//some other corner cases
String sig1 = "";
String sig2 = "";
if (expVal_1 == 0) {
expVal_1++;
sig1 += "0";
} else sig1 += "1";
if (expVal_2 == 0) {
expVal_2++;
sig2 += "0";
} else sig2 += "1";
sig1 = sig1 + destStr.substring(9) + "000";
sig2 = sig2 + srcStr.substring(9) + "000";
int expVal = Math.max(expVal_1, expVal_2);
if (expVal_1 > expVal_2) {
sig2 = rightShift(sig2, expVal - expVal_2);
} else {
sig1 = rightShift(sig1, expVal - expVal_1);
}
String temp = signedADD(sig1, sig2, destStr.charAt(0), srcStr.charAt(0));
String sig = temp.substring(1);
String sign = temp.substring(0, 1);
expVal++;//因为转换成规格化之后位数+1但实际大小没变动
while (sig.charAt(0) == '0' && expVal > 0) {
expVal--;
sig = sig.substring(1) + '0';
}
while (!sig.startsWith("00000000000000000000000000") && expVal < 0) {
expVal++;
sig = rightShift(sig, 1);
}
if (expVal >= 255)
return new DataType(sign + "1111111100000000000000000000000");
if (expVal == 0)
sig = rightShift(sig, 1);
String exp = Transformer.intToBinary(String.valueOf(expVal)).substring(32 - 8);
String round = round(sign.charAt(0), exp, sig);
return new DataType(round);
}
private String signedADD(String sig1, String sig2, char sign1, char sign2) {
boolean sameSign = sign1 == sign2;
if (sig1.equals("000000000000000000000000000"))
return sign2 + sig2;
if (sig2.equals("000000000000000000000000000"))
return sign1 + sig1;
if (sameSign) {
int intSig1 = Integer.valueOf(sig1, 2);
int intSig2 = Integer.valueOf(sig2, 2);
String intToBinary = Transformer.intToBinary(String.valueOf(intSig1 + intSig2));
String ansSig = intToBinary.substring(32 - 28);
return sign1 + ansSig;
} else {
int intSig1 = Integer.valueOf(sig1, 2);
int intSig2 = Integer.valueOf(sig2, 2);
if (sig1.equals(sig2))
return "00000000000000000000000000000";
String intToBinary = Transformer.intToBinary(String.valueOf(Math.abs(intSig1 - intSig2)));
String ansSig = intToBinary.substring(32 - 28);
return intSig1 > intSig2 ? (sign1 + ansSig) : (sign2 + ansSig);
}
}
Another version of div
package cpu.fpu;
import util.DataType;
import util.IEEE754Float;
import java.util.Collections;
import static util.Transformer.oneAdder;
import static util.Transformer.negation;
public class FPU {
final int eLength = 8;
final int sLength = 23;
final int gLength = 3;
class FLOAT {
char sign;
int exp;
String sig;
FLOAT(String v) {
sign = v.charAt(0);
exp = Integer.parseInt(v.substring(1, 9), 2);
sig = exp == 0 ? "0" : "1";
if (exp == 0)
exp++;
sig += v.substring(9) + "000";
}
}
private final String[][] addCorner = new String[][]{
{IEEE754Float.P_ZERO, IEEE754Float.P_ZERO, IEEE754Float.P_ZERO},
{IEEE754Float.N_ZERO, IEEE754Float.P_ZERO, IEEE754Float.P_ZERO},
{IEEE754Float.P_ZERO, IEEE754Float.N_ZERO, IEEE754Float.P_ZERO},
{IEEE754Float.N_ZERO, IEEE754Float.N_ZERO, IEEE754Float.N_ZERO},
{IEEE754Float.P_INF, IEEE754Float.N_INF, IEEE754Float.NaN},
{IEEE754Float.N_INF, IEEE754Float.P_INF, IEEE754Float.NaN}
};
private final String[][] subCorner = new String[][]{
{IEEE754Float.P_ZERO, IEEE754Float.P_ZERO, IEEE754Float.P_ZERO},
{IEEE754Float.N_ZERO, IEEE754Float.P_ZERO, IEEE754Float.N_ZERO},
{IEEE754Float.P_ZERO, IEEE754Float.N_ZERO, IEEE754Float.P_ZERO},
{IEEE754Float.N_ZERO, IEEE754Float.N_ZERO, IEEE754Float.P_ZERO},
{IEEE754Float.P_INF, IEEE754Float.P_INF, IEEE754Float.NaN},
{IEEE754Float.N_INF, IEEE754Float.N_INF, IEEE754Float.NaN}
};
private final String[][] mulCorner = new String[][]{
{IEEE754Float.P_ZERO, IEEE754Float.N_ZERO, IEEE754Float.N_ZERO},
{IEEE754Float.N_ZERO, IEEE754Float.P_ZERO, IEEE754Float.N_ZERO},
{IEEE754Float.P_ZERO, IEEE754Float.P_ZERO, IEEE754Float.P_ZERO},
{IEEE754Float.N_ZERO, IEEE754Float.N_ZERO, IEEE754Float.P_ZERO},
{IEEE754Float.P_ZERO, IEEE754Float.P_INF, IEEE754Float.NaN},
{IEEE754Float.P_ZERO, IEEE754Float.N_INF, IEEE754Float.NaN},
{IEEE754Float.N_ZERO, IEEE754Float.P_INF, IEEE754Float.NaN},
{IEEE754Float.N_ZERO, IEEE754Float.N_INF, IEEE754Float.NaN},
{IEEE754Float.P_INF, IEEE754Float.P_ZERO, IEEE754Float.NaN},
{IEEE754Float.P_INF, IEEE754Float.N_ZERO, IEEE754Float.NaN},
{IEEE754Float.N_INF, IEEE754Float.P_ZERO, IEEE754Float.NaN},
{IEEE754Float.N_INF, IEEE754Float.N_ZERO, IEEE754Float.NaN}
};
private final String[][] divCorner = new String[][]{
{IEEE754Float.P_ZERO, IEEE754Float.P_ZERO, IEEE754Float.NaN},
{IEEE754Float.N_ZERO, IEEE754Float.N_ZERO, IEEE754Float.NaN},
{IEEE754Float.P_ZERO, IEEE754Float.N_ZERO, IEEE754Float.NaN},
{IEEE754Float.N_ZERO, IEEE754Float.P_ZERO, IEEE754Float.NaN},
{IEEE754Float.P_INF, IEEE754Float.P_INF, IEEE754Float.NaN},
{IEEE754Float.N_INF, IEEE754Float.N_INF, IEEE754Float.NaN},
{IEEE754Float.P_INF, IEEE754Float.N_INF, IEEE754Float.NaN},
{IEEE754Float.N_INF, IEEE754Float.P_INF, IEEE754Float.NaN},
};
//dest + src
public DataType add(DataType src, DataType dest) {
String a = src.toString();
String b = dest.toString();
if (Corner(a, b, addCorner) != null) {
return Corner(a, b, addCorner);
}
return new DataType(floatAddition(new FLOAT(a), new FLOAT(b)));
}
//dest - src
public DataType sub(DataType src, DataType dest) {
String a = src.toString();
String b = dest.toString();
if (Corner(a, b, subCorner) != null) {
return Corner(a, b, subCorner);
}
a = negation("" + a.charAt(0)) + a.substring(1);
return new DataType(floatAddition(new FLOAT(a), new FLOAT(b)));
}
//dest * src
public DataType mul(DataType src, DataType dest) {
String a = src.toString();
String b = dest.toString();
if (Corner(a, b, mulCorner) != null) {
return Corner(a, b, mulCorner);
}
return new DataType(floatMul(new FLOAT(a), new FLOAT(b)));
}
//dest / src
public DataType div(DataType src, DataType dest) {
String a = src.toString();
String b = dest.toString();
if (Corner(a, b, divCorner) != null) {
return Corner(a, b, divCorner);
}
boolean sameSign = a.charAt(0) == b.charAt(0);
if (isZero(b.substring(1))) {
return sameSign ? new DataType(IEEE754Float.P_ZERO) : new DataType(IEEE754Float.N_ZERO);
}
if (isZero(a.substring(1))) throw new ArithmeticException();
return new DataType(floatDiv(new FLOAT(b), new FLOAT(a)));
}
public String floatAddition(FLOAT a, FLOAT b) {
int exp = Math.max(a.exp, b.exp);
b.sig = rightShift(b.sig, exp - b.exp);
a.sig = rightShift(a.sig, exp - a.exp);
String temp = signedAdder(a.sign + a.sig, b.sign + b.sig);
boolean overFlow = temp.charAt(0) == '1';
char sign = temp.charAt(1);
String sig = temp.substring(2);
if (overFlow) {
exp++;
sig = "1" + sig.substring(0, sig.length() - 1);
}
if (exp >= 255) {
return "" + sign + "11111111" + String.join("", Collections.nCopies(sLength, "0"));
}
return NormalTreatment1(sig, exp, sign, sig.length());
}
public String floatMul(FLOAT a, FLOAT b) {
int exp = a.exp + b.exp - 127;
char sign = (char) ((a.sign - '0') ^ (b.sign - '0') + '0');
if (a.exp == 255 || b.exp == 255) {
return "" + sign + "11111111" + String.join("", Collections.nCopies(sLength, "0"));
}
String sig = unsignedMul(a.sig, b.sig, a.sig.length());
//乘积的隐藏位是2位
exp++;
return NormalTreatment2(sig, exp, sign, a.sig.length());
}
// a / b
public String floatDiv(FLOAT a, FLOAT b) {
char sign = (char) ((a.sign - '0') ^ (b.sign - '0') + '0');
int exp = a.exp - b.exp + 127;
if (a.exp == 255) {
return "" + sign + "11111111" + String.join("", Collections.nCopies(sLength, "0"));
}
if (b.exp == 255) {
return "" + sign + "00000000" + String.join("", Collections.nCopies(sLength, "0"));
}
String temp = unsignedDiv(a.sig, b.sig, a.sig.length());
return NormalTreatment2(temp, exp, sign, a.sig.length());
}
public String NormalTreatment1(String sig, int exp, char sign, int length) {
while (sig.charAt(0) != '1' && exp > 0) { //加减乘除均需要
sig = leftShift(sig, 1);
exp--;
}
if (exp == 0) {
sig = rightShift(sig, 1);
}
if (isZero(sig)) { //仅在加减中需要注意符号位设为0
return "" + String.join("", Collections.nCopies(eLength + sLength + 1, "0"));
}
String expStr = String.format("%8s", Integer.toBinaryString(exp)).replaceAll(" ", "0");
return round(sign, expStr, sig);
}
public String NormalTreatment2(String sig, int exp, char sign, int length) {
while (sig.charAt(0) != '1' && exp > 0) { //加减乘除均需要
sig = leftShift(sig, 1);
exp--;
}
while (!isZero(sig.substring(0, length)) && exp < 0) { //仅在乘除中需要
sig = rightShift(sig, 1);
exp++;
}
if (exp >= 255) { //乘除中需要
return "" + sign + "11111111" + String.join("", Collections.nCopies(sLength, "0"));
}
if (exp == 0) {
sig = rightShift(sig, 1);
}
if (exp < 0) { //仅在乘除中需要,注意符号位设为sign
return "" + sign + String.join("", Collections.nCopies(eLength + sLength, "0"));
}
String expStr = String.format("%8s", Integer.toBinaryString(exp)).replaceAll(" ", "0");
return round(sign, expStr, sig);
}
//第一位是符号位的加法
public String signedAdder(String a, String b) {
char signA = a.charAt(0);
char signB = b.charAt(0);
//对于零情况的特殊判断
if (isZero(a.substring(1))) return "0" + b;
if (isZero(b.substring(1))) return "0" + a;
a = a.substring(1);
b = b.substring(1);
if (signA == signB) { //用 temp.charAt(0) 判断是否溢出
String temp = carry_adder(a, b, 0, a.length());
return "" + temp.charAt(0) + signA + temp.substring(1);
} else {
b = oneAdder(negation(b)).substring(1);
String temp = carry_adder(a, b, 0, a.length());
if (temp.charAt(0) == '1') return "0" + signA + temp.substring(1);
return "0" + negation("" + signA) + oneAdder(negation(temp.substring(1))).substring(1);
}
}
String unsignedMul(String x, String y, int length) {
String ans = String.join("", Collections.nCopies(length, "0")) + y;
for (int i = 0; i < length; i++) {
char carry = '0';
if (ans.charAt(2 * length - 1) == '1') {
String temp = carry_adder(x, ans.substring(0, length), 0, length);
carry = temp.charAt(0);
ans = temp.substring(1) + ans.substring(length);
}
ans = carry + ans.substring(0, 2 * length - 1);
}
return ans;
}
// x / y
String unsignedDiv(String x, String y, int length) {
String Qstr = "";
x += String.join("", Collections.nCopies(length, "0"));
String negy = oneAdder(negation(y)).substring(1);
for (int i = 0; i < length; i++) {
String temp = carry_adder(x.substring(0, length), negy, 0, length).substring(1);
if (temp.charAt(0) == '0') {
x = temp.substring(1) + x.substring(length) + "1";
} else {
x = leftShift(x, 1);
}
}
Qstr = x.substring(length);
return Qstr;
}
public String carry_adder(String add1, String add2, int carry, int length) {
StringBuilder ans = new StringBuilder();
int x, y;
for (int i = length - 1; i >= 0; i--) {//顺序是从低位到高位
x = add1.charAt(i) - '0';
y = add2.charAt(i) - '0';
ans.insert(0, x ^ y ^ carry);
carry = x & carry | y & carry | x & y;
}
return "" + carry + ans;
}
public boolean isZero(String s) {
for (char c : s.toCharArray()) {
if (c != '0') return false;
}
return true;
}
public String leftShift(String s, int n) {
StringBuilder res = new StringBuilder(s.substring(n));
for (int i = 0; i < n; i++) {
res.append(0);
}
return res.toString();
}
public DataType Corner(String a, String b, String[][] corner) {
String check = cornerCheck(corner, a, b);
if (check != null) return new DataType(check);
if (a.matches(IEEE754Float.NaN_Regular) || b.matches(IEEE754Float.NaN_Regular)) {
return new DataType(IEEE754Float.NaN);
}
return null;
}
private String cornerCheck(String[][] cornerMatrix, String oprA, String oprB) {
for (String[] matrix : cornerMatrix) {
if (oprA.equals(matrix[0]) && oprB.equals(matrix[1])) {
return matrix[2];
}
}
return null;
}
private String rightShift(String operand, int n) {
StringBuilder result = new StringBuilder(operand); //保证位数不变
boolean sticky = false;
for (int i = 0; i < n; i++) {
sticky = sticky || result.toString().endsWith("1");
result.insert(0, "0");
result.deleteCharAt(result.length() - 1);
}
if (sticky) {
result.replace(operand.length() - 1, operand.length(), "1");
}
return result.substring(0, operand.length());
}
/**
* 对GRS保护位进行舍入
*
* @param sign 符号位
* @param exp 阶码
* @param sig_grs 带隐藏位和保护位的尾数
* @return 舍入后的结果
*/
private String round(char sign, String exp, String sig_grs) {
int grs = Integer.parseInt(sig_grs.substring(24, 27), 2);
if ((sig_grs.substring(27).contains("1")) && (grs % 2 == 0)) {
grs++;
}
String sig = sig_grs.substring(0, 24); // 隐藏位+23位
if (grs > 4 || (grs == 4 && sig.endsWith("1"))) {
sig = oneAdder(sig);
if (sig.charAt(0) == '1') {
exp = oneAdder(exp).substring(1);
sig = sig.substring(1);
}
}
if (Integer.parseInt(sig.substring(0, sig.length() - 23), 2) > 1) {
sig = rightShift(sig, 1);
exp = oneAdder(exp).substring(1);
}
if (exp.equals("11111111")) {
return sign == '0' ? IEEE754Float.P_INF : IEEE754Float.N_INF;
}
return sign + exp + sig.substring(sig.length() - 23);
}
}