2023-12-07

计组机试盘点

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.
      1. If Qn Qn+1 = 01 do A= A + BR and perform arithmetic shift by 1 bit.
      1. 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);
}

}