清丰网站建设,怎么做网站运营,wordpress后台怎么进,南京手机网站制作文章目录CPU 架构EU#xff08;执行单元#xff09;BIU#xff08;总线接口单元#xff09;小结一下模拟内存模拟 BIU模拟 EU模拟 CPU总结要模拟 8086 CPU 运行#xff0c;必须知道 CPU 的一些知识。下文的知识点都来自《Intel_8086_Family_Users_Manual 》。CPU 架构
微…
文章目录CPU 架构EU执行单元BIU总线接口单元小结一下模拟内存模拟 BIU模拟 EU模拟 CPU总结要模拟 8086 CPU 运行必须知道 CPU 的一些知识。下文的知识点都来自《Intel_8086_Family_Users_Manual 》。CPU 架构
微处理器通常通过重复循环执行以下步骤来执行程序此描述有所简化
从内存中取出下一条指令。读取操作数如果指令需要。执行指令。写入结果如果指令需要。
在以前的 CPU 中大多数这些步骤都是串行执行的或者只有一个总线周期取指重叠。 8086 和 8088 CPU 的架构也执行相同步骤但是是将它们分配给 CPU 内两个独立的处理单元。执行单元EU执行指令总线接口单元 (BIU) 获取指令、读取操作数并写入结果。
这两个单元可以彼此独立运行并且在大多数情况下能够广泛地重叠取指令和执行。结果是在大多数情况下通常获取指令所需的时间“消失”了因为 EU 执行的指令已经被 BIU 提前预取了。
8086 CPU 内部组成如下 更详细的结构如下
EU执行单元
EU 中的一个 16 位算术/逻辑单元 (ALU) 维护 CPU 状态和控制标志并操纵通用寄存器和指令操作数。 EU 中的所有寄存器和数据通路都是 16 位宽的用于快速内部传输。EU与系统总线即“外面的世界”没有任何联系。它从 BIU 维护的队列中获取指令。当指令需要访问内存或外围设备时EU 会请求 BIU 获取或存储数据。EU 使用的所有地址都是 16 位宽。然而BIU 执行地址重定位【段地址左移4位加上偏移地址】使 EU 可以访问完整的1 M字节的内存空间。
BIU总线接口单元
BIU 为 EU 执行所有总线操作。数据在 CPU 和内存或 I/O 设备之间传输。
此外在 EU 忙于执行指令期间BIU 会“向前看”并从内存中获取更多指令【指令预取】。指令存储在称为指令流队列的内部 RAM 阵列中。 8086 队列最多可存放六个指令字节。 这些队列大小允许 BIU 在大多数情况下为 EU 提供预取的指令而无需独占系统总线。
8088 BIU在指令队列中还剩余 1 字节空间并且没有来自 EU 的总线请求访问时才获取下一个字节指令。8086 BIU 的操作与此类似只是它直到指令队列中还剩余 2 字节空间时才开始读取操作。
在大多数情况下指令队列中至少包含指令流的一个字节并且 EU 无需等待就可获取。队列中的指令是存储在紧邻且高于当前正在执行的指令的内存位置中的指令。也就是说只要执行顺序进行它们就是下一条要被执行的指令。如果 EU 执行一个指令时将控制转移到另一个位置BIU 将重置队列从新地址获取指令立即将其传递给 EU然后开始从新位置重新填充队列。此外只要 EU 请求内存或 I/O 读取或写入BIU 就会暂停获取指令。
小结一下
BIU
包含 ES,CS,SS,DS 这4个段寄存器和 IP 寄存器可以读取和修改这些寄存器包含一个指令队列可从内存获取指令放入队列可以读写内存【可以向内存芯片发起读或写请求】可以接收 EU 的请求并处理
EU
包含 AX,CX,DX,BX,SP,BP,SI,DI 这 8 个16 位通用寄存器和 16 位的 EFLAGS 寄存器可以读取和修改这些寄存器从 BIU 获取指令【可以向 BIU 发起获取指令的请求】可以向 BIU 发起读写内存的请求可以向 BIU 发起读写 4 个段寄存器和 IP 寄存器的请求可以将二进制格式的指令解码成 EU 可以识别的指令格式并执行指令 模拟内存
由于 BIU 要和内存打交道先用代码来模拟下内存芯片。
简单地说内存就是一个大数组。但是为了模拟它是一个芯片且有与 BIU 的交互行为自然地就可以把它实现为可以接收 BIU 请求的对象请求和数据都通过 channel 发送
type memoryOpType uint8// 支持的操作
const (// 读一个字节memoryOpReadByte memoryOpType iota// 读一个字memoryOpReadWord// 写一个字节memoryOpWriteByte// 写一个字memoryOpWriteWord
)// 8086 CPU 地址总线为20位最大支持1M内存
const maxMemorySize 1 20 type Memory struct {size uint32memory []byte// 与 BIU 的通信接口CtrlBus chan memoryOpTypeAddrBus chan uint32DataBus chan uint16
}func (m *Memory) Init(size uint32) {if m.size ! 0 {log.Fatal(Memory reinit!)}if size maxMemorySize {log.Fatal(memory size exceeds max!)}m.memory make([]byte, size)m.size sizem.CtrlBus make(chan memoryOpType)m.AddrBus make(chan uint32)m.DataBus make(chan uint16)// 内存的工作函数go func() {for op : range m.CtrlBus {switch op {case memoryOpReadByte:addr : -m.AddrBusm.DataBus - uint16(m.memory[addr])case memoryOpReadWord:addr : -m.AddrBusm.DataBus - uint16(m.memory[addr]) | uint16(m.memory[addr1])8case memoryOpWriteByte:addr : -m.AddrBusdata : -m.DataBusm.memory[addr] byte(data 0xff)case memoryOpWriteWord:addr : -m.AddrBusdata : -m.DataBusm.memory[addr] byte(data)m.memory[addr1] byte(data 8)}}}()
}
CtrlBus 表示控制总线AddrBus 表示地址总线DataBus 表示数据总线。这些总线与下文的 BIU “相连”。
从控制总线获取操作命令从地址总线获取地址当为读操作时数据通过数据总线返回当为写操作时要写入的数据从数据总线获取。
内存对象的使用也很简单 m : Memory{}m.Init(1 20)模拟 BIU
模拟 BIU 也是类似的它主要包含一个指令队列与内存芯片的通信接口与 EU的通信接口
// 表示无效的段前缀
const invalidSegPrefix uint8 0xff// 段寄存器ID
const (ES uint8 iotaCSSSDS
)// 指令队列大小
const instructionQueueSize 6
const instructionQueueEmptySize 2type BIU struct {es, cs, ss, ds uint16ip uint16// 指令队列instructionQueue [instructionQueueSize]byte// 指令队列剩余空间大小nEmpty uint8// 取指令索引// 存指令索引getIndex, putIndex uint8// 与内存芯片的通信接口ctrlBus chan memoryOpTypeaddrBus chan uint32dataBus chan uint16// 与EU的通信接口InnerCtrlBus chan BIURequestInnerDataBus chan uint16// 虚拟IP指针virtIP uint16// 内存操作使用的段前缀segPrefix uint8
}再把它支持的操作定义出来
type BIURequest uint16// 支持的操作
const (// 取指令FetchInstruction BIURequest iota// 读内存ReadMemory// 读写段寄存器ReadSegRegWriteSegReg// 读写IP寄存器ReadIPRegWriteIPReg// 读写栈内存ReadStackMemoryWriteStackMemoryReadVariableWriteVariable// 改变段前缀ChangeSegPrefix// 未实现// StringSource// StringDestination// BPAsBaseRegister
)这些操作要满足 EU 的需求比如需要有读写段寄存器和读写 IP 寄存器的操作需要有读写内存的操作等等。 ReadStackMemoryWriteStackMemoryReadVariableWriteVariable// 未实现// StringSource// StringDestination// BPAsBaseRegister这几种是手册中定义的不同的内存访问类型 因为不同的内存访问操作使用不同的段寄存器和偏移地址【逻辑地址】。
比如读取指令使用的是 CS 段寄存器偏移地址放在 IP 寄存器中。栈操作使用的 SS 寄存器偏移地址放在 SP 寄存器中。普通的数据访问使用的 DS 段寄存器偏移地址是 EU 传入的地址。
16 位段寄存器地址左移 4 位加上偏移地址就得到 20 位的物理地址。所以 BIU 在这里要根据访问类型计算物理地址。
我在实现时偏移地址是由 EU 根据内存类型操作传入的。举例来说EU 要执行栈操作时传入的偏移地址就是 SP 寄存器的值BIU 并不关心偏移地址的来源它只关心如果是栈操作那么段寄存器就使用 SS。
读写内存的实现如下
func (b *BIU) readMemoryByte(addr uint32) byte {b.ctrlBus - memoryOpReadByteb.addrBus - addrreturn byte(-b.dataBus)
}func (b *BIU) readMemoryWord(addr uint32) uint16 {b.ctrlBus - memoryOpReadWordb.addrBus - addrreturn -b.dataBus
}func (b *BIU) writeMemoryByte(addr uint32, data byte) {b.ctrlBus - memoryOpWriteByteb.addrBus - addrb.dataBus - uint16(data)
}func (b *BIU) writeMemoryWord(addr uint32, data uint16) {b.ctrlBus - memoryOpWriteWordb.addrBus - addrb.dataBus - data
}就是通过通信接口向内存芯片发起请求。
与取指令相关的几个函数实现如下
// 清空指令队列
func (b *BIU) emptyInstructionQueue() {b.nEmpty instructionQueueSizeb.getIndex 0b.putIndex 0
}// 尝试预取指令
func (b *BIU) prefetchInstructions() {for b.nEmpty instructionQueueEmptySize {phyAddress : uint32(b.cs)4 uint32(b.virtIP)instruction : b.readMemoryByte(phyAddress)b.instructionQueue[b.putIndex] instructionb.nEmpty--b.putIndexif b.putIndex instructionQueueSize {b.putIndex 0}// 虚拟IP指针加1b.virtIP}
}// 从指令队列取出一个字节的指令给EU
func (b *BIU) fetchOneInstruction() byte {if b.nEmpty instructionQueueSize {return 0x0f // 未使用的指令}instruction : b.instructionQueue[b.getIndex]b.nEmptyb.getIndexif b.getIndex instructionQueueSize {b.getIndex 0}// 取得一个字节指令后IP指针加1b.ipreturn instruction
}我在实现时 EU 从 BIU 获取的是一条指令中的 1 字节数据而不是完整的一条指令。因为 BIU 每次都是从内存读取 1 字节指令BIU 做不到每次从内存读取一条完整的指令。 而 BIU 做指令解码的工作又很奇怪所以我将指令的解码工作放在 EU 里实现。
BIU 的工作流程就是不停地
尝试预取指令处理来自EU的各种请求
实现如下
func (b *BIU) run() {if b.nEmpty 0 {log.Fatal(not init!!!)}go func() {for {// 预取指令b.prefetchInstructions()// 处理EU的请求req : -b.InnerCtrlBusswitch req {// 取指令case FetchInstruction:b.InnerDataBus - uint16(b.fetchOneInstruction())// 读内存case ReadMemory:addrLow : -b.InnerDataBusaddrHigh : -b.InnerDataBussize : -b.InnerDataBusphyAddr : uint32(addrHigh)16 | uint32(addrLow)if size 8 {b.InnerDataBus - uint16(b.readMemoryByte(phyAddr))} else {b.InnerDataBus - b.readMemoryWord(phyAddr)}// 读段寄存器case ReadSegReg:reg : uint8(-b.InnerDataBus)switch reg {case ES:b.InnerDataBus - b.escase CS:b.InnerDataBus - b.cscase SS:b.InnerDataBus - b.sscase DS:b.InnerDataBus - b.dsdefault:log.Fatal(error)}// 读IP寄存器case ReadIPReg:b.InnerDataBus - b.ip// 写段寄存器case WriteSegReg:reg : uint8(-b.InnerDataBus)val : -b.InnerDataBusswitch reg {case ES:b.es valcase CS:b.cs val// 先修改IP再修改CS可能从旧的代码段取了指令所以需要清空指令队列b.virtIP b.ipb.emptyInstructionQueue()case SS:b.ss valcase DS:b.ds valdefault:log.Fatal(error)}// 写IP寄存器case WriteIPReg:val : -b.InnerDataBusb.ip valb.virtIP valb.emptyInstructionQueue()fmt.Printf(change Ip to 0x%X\n, val)// 读栈内存读普通数据内存case ReadStackMemory, ReadVariable:offset : -b.InnerDataBussize : -b.InnerDataBusvar phyAddress uint32if req ReadStackMemory {phyAddress uint32(b.ss)4 uint32(offset)} else {//cs es ssif b.segPrefix invalidSegPrefix {phyAddress uint32(b.ds)4 uint32(offset)} else {switch b.segPrefix {case ES:phyAddress uint32(b.es)4 uint32(offset)case CS:phyAddress uint32(b.cs)4 uint32(offset)case SS:phyAddress uint32(b.ss)4 uint32(offset)case DS:phyAddress uint32(b.ds)4 uint32(offset)default:log.Fatal(error)}}}if size 8 {b.InnerDataBus - uint16(b.readMemoryByte(phyAddress))} else {b.InnerDataBus - b.readMemoryWord(phyAddress)}if req ReadVariable b.segPrefix ! invalidSegPrefix {b.segPrefix invalidSegPrefix}// 写栈内存写普通数据内存case WriteStackMemory, WriteVariable:offset : -b.InnerDataBussize : -b.InnerDataBusval : -b.InnerDataBusvar phyAddress uint32if req WriteStackMemory {phyAddress uint32(b.ss)4 uint32(offset)} else {//cs es ssif b.segPrefix invalidSegPrefix {phyAddress uint32(b.ds)4 uint32(offset)} else {switch b.segPrefix {case ES:phyAddress uint32(b.es)4 uint32(offset)case CS:phyAddress uint32(b.cs)4 uint32(offset)case SS:phyAddress uint32(b.ss)4 uint32(offset)case DS:phyAddress uint32(b.ds)4 uint32(offset)default:log.Fatal(error)}}}if size 8 {b.writeMemoryByte(phyAddress, byte(val))} else {b.writeMemoryWord(phyAddress, val)}if req WriteVariable b.segPrefix ! invalidSegPrefix {b.segPrefix invalidSegPrefix}// case StringSource:// phyAddress uint32(b.ds4) uint32(req.Offset)// case StringDestination:// phyAddress uint32(b.es4) uint32(req.Offset)// case BPAsBaseRegister:// phyAddress uint32(b.ss4) uint32(req.Offset)// 改变段前缀case ChangeSegPrefix:if b.segPrefix ! invalidSegPrefix {log.Fatal(error: invalid )}b.segPrefix uint8(-b.InnerDataBus)default:log.Fatal(erer)}}}()
}模拟 EU
模拟 EU 也是和模拟 BIU 类似可用如下的结构体表示
type EU struct {// 8 个 16位通用寄存器ax uint16cx uint16dx uint16bx uint16sp uint16bp uint16si uint16di uint16eflags uint16//与 BIU 的通信接口biuCtrl chan BIURequestbiuData chan uint16// 当前正在执行的指令currentInstruction byte// 是否停止执行的标志位stop bool// 略去了与中断相关的字段
}它定义了如下读写寄存器的方法 // 16位通用寄存器的ID
const (AL uint8 iotaCLDLBLAHCHDHBH
)// 8 位通用寄存器的ID
const (AX uint8 iotaCXDXBXSPBPSIDI
)// 标志寄存器的各种标志位
const (cfFlag uint8 0 //pfFlag uint8 2 //afFlag uint8 4zfFlag uint8 6 //sfFlag uint8 7 //tfFlag uint8 8ifFlag uint8 9dfFlag uint8 10ofFlag uint8 11 //
)// 写16位通用寄存器
func (e *EU) writeReg16(reg uint8, value uint16) {switch reg {case AX:e.ax valuecase CX:e.cx valuecase DX:e.dx valuecase BX:e.bx valuecase SP:e.sp valuecase BP:e.bp valuecase SI:e.si valuecase DI:e.di valuedefault:log.Fatal()}
}// 读16位通用寄存器
func (e *EU) readReg16(reg uint8) uint16 {var value uint16switch reg {case AX:value e.axcase CX:value e.cxcase DX:value e.dxcase BX:value e.bxcase SP:value e.spcase BP:value e.bpcase SI:value e.sicase DI:value e.didefault:log.Fatal()}return value
}// 写 8 位通用寄存器
func (e *EU) writeReg8(reg uint8, value uint8) {switch reg {case AL:e.ax 0xff00e.ax | uint16(value)case CL:e.cx 0xff00e.cx | uint16(value)case DL:e.dx 0xff00e.dx | uint16(value)case BL:e.bx 0xff00e.bx | uint16(value)case AH:e.ax 0x00ffe.ax | uint16(value) 8case CH:e.cx 0x00ffe.cx | uint16(value) 8case DH:e.dx 0x00ffe.dx | uint16(value) 8case BH:e.bx 0x00ffe.bx | uint16(value) 8default:log.Fatal()}
}// 读 8 位通用寄存器
func (e *EU) readReg8(reg uint8) uint8 {var value uint8switch reg {case AL:value uint8(e.ax)case CL:value uint8(e.cx)case DL:value uint8(e.dx)case BL:value uint8(e.bx)case AH:value uint8(e.ax 8)case CH:value uint8(e.cx 8)case DH:value uint8(e.dx 8)case BH:value uint8(e.bx 8)}return value
}// 设置标志寄存器的某一位
func (e *EU) writeEFLAGS(bitOffset uint8, value uint8) {if value 0 {e.eflags ^uint16(1 bitOffset)} else {e.eflags | uint16(1 bitOffset)}
}// 读取标志寄存器的某一位
func (e *EU) readEFLAGS(bitOffset uint8) uint8 {value : uint8(e.eflagsbitOffset) 0x1return value
}定义了如下和 BIU 通信的方法【比如读写段寄存器读写 IP 寄存器读写内存等】
func (e *EU) writeIP(val uint16) {e.biuCtrl - WriteIPRege.biuData - val
}func (e *EU) readIP() uint16 {e.biuCtrl - ReadIPRegreturn -e.biuData
}func (e *EU) readSeg(reg uint8) uint16 {e.biuCtrl - ReadSegRege.biuData - uint16(reg)return -e.biuData
}func (e *EU) writeSeg(reg uint8, val uint16) {e.biuCtrl - WriteSegRege.biuData - uint16(reg)e.biuData - val
}func (e *EU) readMemoryWord(phyAddr uint32) uint16 {e.biuCtrl - ReadMemorye.biuData - uint16(phyAddr)e.biuData - uint16(phyAddr 16)e.biuData - 16return -e.biuData
}func (e *EU) readDataMemmoryByte(effectiveAddr uint16) uint8 {e.biuCtrl - ReadVariablee.biuData - effectiveAddre.biuData - 8return uint8(-e.biuData)
}func (e *EU) readDataMemmoryWord(effectiveAddr uint16) uint16 {e.biuCtrl - ReadVariablee.biuData - effectiveAddre.biuData - 16return -e.biuData
}func (e *EU) writeDataMemmoryByte(effectiveAddr uint16, val uint8) {e.biuCtrl - WriteVariablee.biuData - effectiveAddre.biuData - 8e.biuData - uint16(val)
}func (e *EU) writeDataMemmoryWord(effectiveAddr uint16, val uint16) {e.biuCtrl - WriteVariablee.biuData - effectiveAddre.biuData - 16e.biuData - val
}func (e *EU) readStackMemory() uint16 {e.biuCtrl - ReadStackMemorye.biuData - e.spe.biuData - 16return -e.biuData
}func (e *EU) writeStackMemory(val uint16) {e.biuCtrl - WriteStackMemorye.biuData - e.spe.biuData - 16e.biuData - val
}func (e *EU) changeSegPrefix(newPrefix uint8) {e.biuCtrl - ChangeSegPrefixe.biuData - uint16(newPrefix)
}定义了最关键的执行指令的方法
func (e *EU) execute(instructions []byte) {// 指令格式的第一字节表示指令类型instruction : instructions[0]e.currentInstruction instruction// 根据指令类型执行不同的操作switch instruction {case InstructionMov:e.executeMov(instructions[1:])case InstructionAdd, InstructionOr, InstructionAdc, InstructionSbb,InstructionAnd, InstructionSub, InstructionXor, InstructionCmp:e.executeAddEtc(instructions[1:])case InstructionInc, InstructionDec, InstructionNot, InstructionNeg,InstructionMul, InstructionImul, InstructionDiv, InstructionIdiv:e.executeIncEtc(instructions[1:])case InstructionSegPrefix:e.executeSegPrefix(instructions[1:])case InstructionPush:e.executePush(instructions[1:])case InstructionPop:e.executePop(instructions[1:])case InstructionJmp:e.executeJmp(instructions[1:])case InstructionCall:e.executeCall(instructions[1:])case InstructionRet:e.executeRet(instructions[1:])case InstructionLoop:e.executeLoop(instructions[1:])case InstructionInt:e.executeInt(instructions[1:])case InstructionNop:e.executeNop(instructions[1:])default:log.Fatal(unsupported inssss---)}
}EU 的工作流程就是不停地从 BIU 获取指令执行直到程序终止。
实现如下
func (e *EU) run() {var instructions []bytefor {// 从BIU获取一个字节指令e.biuCtrl - FetchInstructioninstruction : byte(-e.biuData)// 拼接指令instructions append(instructions, instruction)// 解码当前的指令字节序列decodedInstructions : Decode(instructions)// 当前是一条有效的指令if decodedInstructions ! nil {// 执行指令e.execute(decodedInstructions)// 清空指令字节序列instructions instructions[:0]// 如果要求程序终止则退出循环if e.stop {e.stop falsebreak}}}
}前面说过一条指令通常包含多个字节而从 BIU 获取的只是指令中的一个字节所以需要拼接起来解码看能否形成一条完整的指令
模拟 CPU
模拟完 BIU 和 EU 之后模拟 CPU 就很简单了
type CPU struct {eu EUbiu BIU
}为了将程序写入内存为它实现了如下读写内存的方法
func (c *CPU) writeMemory(addr uint32, data []byte) {for i, v : range data {c.biu.writeMemoryByte(addruint32(i), v)}
}func (c *CPU) readMemory(addr uint32, data []byte) {for i, _ : range data {data[i] c.biu.readMemoryByte(addr uint32(i))}
}为了将它与内存芯片相连实现了 ConnectMemory 方法
func (c *CPU) ConnectMemory(m *Memory) {c.biu.connectMemory(m)
}
它就是调用 BIU 的connectMemory方法它实现如下
func (b *BIU) connectMemory(m *Memory) {b.ctrlBus m.CtrlBusb.addrBus m.AddrBusb.dataBus m.DataBus
}就是将 BIU 的控制总线、地址总线、数据总线与内存芯片的相连
CPU 的初始化方法实现如下
func (c *CPU) Init() {c.biu.Init()// 将 EU 的控制总线和 BIU 相连c.eu.biuCtrl c.biu.InnerCtrlBusc.eu.biuData c.biu.InnerDataBus
}其中 BIU 的初始化方法实现如下
func (b *BIU) Init() {// 情况指令序列b.emptyInstructionQueue()// 设置段前缀为无效b.segPrefix invalidSegPrefix// 创建控制总线和数据总线b.InnerCtrlBus make(chan BIURequest)b.InnerDataBus make(chan uint16)
}
在 CPU 初始化并与内存相连后它们之间的通信数据流如下
CPU 的工作函数实现如下
func (c *CPU) Run(cs, ip uint16, debug bool) {// 让 BIU 开始工作c.biu.run()if debug {c.eu.writeEFLAGS(tfFlag, 1)}// 设置CS和IP寄存器的值c.eu.writeSeg(CS, cs)c.eu.writeIP(ip)// 让 EU 开始工作c.eu.run()
}再回头看上篇文章 main 函数里面的加载并执行程序这段代码就很容易明白了 // 4. 初始化一个CPU和内存芯片// 初始化一个内存芯片大小为1Mm : Memory{}m.Init(1 20)// 初始化一个CPUc : CPU{}c.Init()// 将CPU与内存相连c.ConnectMemory(m)// 5. 将程序写入内存// 计算出程序在内存中的起始地址var phyAddr uint32 uint32(cs)4 - programHeader.codeSegProgOffset// 将程序加载到内存c.writeMemory(phyAddr, program)// 6. CPU 开始执行程序// 第一个参数是CPU开始执行时CS寄存器的值第二个参数是IP寄存器的值c.Run(uint16(cs), uint16(programHeader.codeEntryProgOffset))
总结
本文介绍了怎样使用程序模拟 CPU 和内存至此读者可以窥见 8086 虚拟机内部详细工作原理。 后续文章将介绍 EU 中最核心的部分——指令解码和执行的实现。 文章转载自: http://www.morning.jrwbl.cn.gov.cn.jrwbl.cn http://www.morning.wztnh.cn.gov.cn.wztnh.cn http://www.morning.psdbf.cn.gov.cn.psdbf.cn http://www.morning.zmbzl.cn.gov.cn.zmbzl.cn http://www.morning.txqgd.cn.gov.cn.txqgd.cn http://www.morning.ljqd.cn.gov.cn.ljqd.cn http://www.morning.fgkrh.cn.gov.cn.fgkrh.cn http://www.morning.thrgp.cn.gov.cn.thrgp.cn http://www.morning.wdykx.cn.gov.cn.wdykx.cn http://www.morning.kyjyt.cn.gov.cn.kyjyt.cn http://www.morning.rhnn.cn.gov.cn.rhnn.cn http://www.morning.wdprz.cn.gov.cn.wdprz.cn http://www.morning.sypzg.cn.gov.cn.sypzg.cn http://www.morning.xdmsq.cn.gov.cn.xdmsq.cn http://www.morning.qsmmq.cn.gov.cn.qsmmq.cn http://www.morning.xqspn.cn.gov.cn.xqspn.cn http://www.morning.leyuhh.com.gov.cn.leyuhh.com http://www.morning.xnyfn.cn.gov.cn.xnyfn.cn http://www.morning.ncfky.cn.gov.cn.ncfky.cn http://www.morning.kfyqd.cn.gov.cn.kfyqd.cn http://www.morning.ryznd.cn.gov.cn.ryznd.cn http://www.morning.tgwfn.cn.gov.cn.tgwfn.cn http://www.morning.qgcfb.cn.gov.cn.qgcfb.cn http://www.morning.sldrd.cn.gov.cn.sldrd.cn http://www.morning.nrrzw.cn.gov.cn.nrrzw.cn http://www.morning.jbshh.cn.gov.cn.jbshh.cn http://www.morning.ckwrn.cn.gov.cn.ckwrn.cn http://www.morning.nwtmy.cn.gov.cn.nwtmy.cn http://www.morning.zckhn.cn.gov.cn.zckhn.cn http://www.morning.jxmjr.cn.gov.cn.jxmjr.cn http://www.morning.mjgxl.cn.gov.cn.mjgxl.cn http://www.morning.buyid.com.cn.gov.cn.buyid.com.cn http://www.morning.qbkw.cn.gov.cn.qbkw.cn http://www.morning.lblsx.cn.gov.cn.lblsx.cn http://www.morning.nqfxq.cn.gov.cn.nqfxq.cn http://www.morning.nlywq.cn.gov.cn.nlywq.cn http://www.morning.hrkth.cn.gov.cn.hrkth.cn http://www.morning.rnwmp.cn.gov.cn.rnwmp.cn http://www.morning.grpbt.cn.gov.cn.grpbt.cn http://www.morning.gqddl.cn.gov.cn.gqddl.cn http://www.morning.sblgt.cn.gov.cn.sblgt.cn http://www.morning.jjhng.cn.gov.cn.jjhng.cn http://www.morning.zyslyq.cn.gov.cn.zyslyq.cn http://www.morning.phgz.cn.gov.cn.phgz.cn http://www.morning.wlddq.cn.gov.cn.wlddq.cn http://www.morning.mjgxl.cn.gov.cn.mjgxl.cn http://www.morning.fpzpb.cn.gov.cn.fpzpb.cn http://www.morning.yzygj.cn.gov.cn.yzygj.cn http://www.morning.pzbqm.cn.gov.cn.pzbqm.cn http://www.morning.jbgzy.cn.gov.cn.jbgzy.cn http://www.morning.lnmby.cn.gov.cn.lnmby.cn http://www.morning.smrkf.cn.gov.cn.smrkf.cn http://www.morning.czgfn.cn.gov.cn.czgfn.cn http://www.morning.fnwny.cn.gov.cn.fnwny.cn http://www.morning.dmzqd.cn.gov.cn.dmzqd.cn http://www.morning.wpydf.cn.gov.cn.wpydf.cn http://www.morning.ndngj.cn.gov.cn.ndngj.cn http://www.morning.xpmwt.cn.gov.cn.xpmwt.cn http://www.morning.tmfhx.cn.gov.cn.tmfhx.cn http://www.morning.jppb.cn.gov.cn.jppb.cn http://www.morning.zbnts.cn.gov.cn.zbnts.cn http://www.morning.nwfpl.cn.gov.cn.nwfpl.cn http://www.morning.flchj.cn.gov.cn.flchj.cn http://www.morning.lqffg.cn.gov.cn.lqffg.cn http://www.morning.lwdzt.cn.gov.cn.lwdzt.cn http://www.morning.nypgb.cn.gov.cn.nypgb.cn http://www.morning.cbnlg.cn.gov.cn.cbnlg.cn http://www.morning.qxljc.cn.gov.cn.qxljc.cn http://www.morning.yzzfl.cn.gov.cn.yzzfl.cn http://www.morning.lxqyf.cn.gov.cn.lxqyf.cn http://www.morning.jqjnx.cn.gov.cn.jqjnx.cn http://www.morning.lprfk.cn.gov.cn.lprfk.cn http://www.morning.hqmfn.cn.gov.cn.hqmfn.cn http://www.morning.jtfcd.cn.gov.cn.jtfcd.cn http://www.morning.kfqzd.cn.gov.cn.kfqzd.cn http://www.morning.mpsnb.cn.gov.cn.mpsnb.cn http://www.morning.syznh.cn.gov.cn.syznh.cn http://www.morning.hpcpp.cn.gov.cn.hpcpp.cn http://www.morning.addai.cn.gov.cn.addai.cn http://www.morning.hcbky.cn.gov.cn.hcbky.cn