VicturalMachine(2)

the stack from the machine


Definition stack

When we call the function, we need to pointer to protocal the stack from original

  • stack pointer
  • frame pointer

Difintion in CPU class

  • length - 1 make sure index from 0, another minus one because we need 2 bytes to store the bits word
class CPU {   
  constructor(memory){
 
        this.registerNames = [
            'sp','fp'
        ]
        // ...
        this.setRegister('sp',this.getRegister('sp').length - 1 - 1)
        this.setRegister('fp',this.getRegister('fp'.length - 1 -1))
    }
  // ...
}

Difintion in instructions set

const PSH_LIT = 0x17
const PSH_REG = 0x18

Implement pop and push

case PSH_LIT: {
                const value = this.fetch16()
                this.push(value)
                return
            }
 
case PSH_REG: {
                const registerIndex = this.fetchRegisterIndex()
                this.push(this.registers.getUint16(registerIndex))
                return
            }
 
case POP: {
                const registerIndex = this.fetchRegisterIndex()
                const value = this.pop()
                this.registers.setUint16(registerIndex,value)
                return
            }

Test the push and pop

 
writeBytes[i++] = MOVE_LIT_REG
writeBytes[i++] = 0x51
writeBytes[i++] = 0x51
writeBytes[i++] = R1
 
writeBytes[i++] = MOVE_LIT_REG
writeBytes[i++] = 0x42
writeBytes[i++] = 0x42
writeBytes[i++] = R2
 
writeBytes[i++] = PSH_REG
writeBytes[i++] = R1
 
writeBytes[i++] = PSH_REG
writeBytes[i++] = R2
 
writeBytes[i++] = POP
writeBytes[i++] = R1
 
writeBytes[i++] = POP
writeBytes[i++] = R2
 
cpu.debug()
cpu.viewMemoryAt(cpu.getRegister('ip'))
/** 0xffff-1 為記憶體開頭
    而往下還有7個字節因此減6 (從0開始)
 **/
cpu.viewMemoryAt(0xffff - 1 - 6)
 
const rl = readline.createInterface({
    input: process.stdin,
    output:process.stdout
})
 
rl.on('line',()=>{
    cpu.step()
    cpu.debug()
    cpu.viewMemoryAt(cpu.getRegister('ip'))
    cpu.viewMemoryAt(0xffff - 1 - 6)
})

CAL and RET

Definition of the instruction

const CAL_LIT = 0x5e
const CAL_REG = 0x5f
const RET = 0x60

Implement the function of instruction

CAL function

case CAL_LIT: {
                const address = this.fetch16()
                this.pushState()
                this.setRegister('fp',this.getRegister('sp'))
                this.stackFrameSize = 0
                this.setRegister('ip',address)
                return
            }
 
case CAL_REG: {
                const registerIndex = this.fetchRegisterIndex()
                const address = this.registers.getUint16(registerIndex)
                this.pushState()
                this.setRegister('ip',address)
                return
            }
push state function
pushState(){
        this.push(this.getRegister('r1'))
        this.push(this.getRegister('r2'))
        this.push(this.getRegister('r3'))
        this.push(this.getRegister('r4'))
        this.push(this.getRegister('r5'))
        this.push(this.getRegister('r6'))
        this.push(this.getRegister('r7'))
        this.push(this.getRegister('r8'))
        this.push(this.getRegister('ip'))
        this.push(this.stackFrameSize + 2) //last item
    }

RET function

case RET: {
                this.popState()
                return
            }
pop state
  • pop state is inverse of the push state beacause the stack is FILO
 
    popState(){
        const framePointerAddress = this.getRegister('fp')
        this.setRegister('sp',framePointerAddress)
 
        this.stackFrameSize = this.pop() // get the last item which is stackFrameSize size
        const stackFrameSize = this.stackFrameSize
 
            this.setRegister('ip', this.pop())
            this.setRegister('r8', this.pop())
            this.setRegister('r7', this.pop())
            this.setRegister('r6', this.pop())
            this.setRegister('r5', this.pop())
            this.setRegister('r4', this.pop())
            this.setRegister('r3', this.pop())
            this.setRegister('r2', this.pop())
            this.setRegister('r1', this.pop())
 
        const nArgs = this.pop()
        for(let i = 0;i < nArgs;i++){
            this.pop()
        }
 
        this.setRegister('fp',framePointerAddress + stackFrameSize)
    }

Change the View Memory function to check the more address in memory

viewMemoryAt(address,n = 8) {
        // 0x0f01: 0x04 0x05 0xA3 0xFE 0x13 0x0D 0x44 0x0F
        const nextNBytes = Array.from({length: n}, (_, i) =>
            this.memory.getUint8(address + i)
        ).map(v => `0x${v.toString(16).padStart(2, '0')}`);
 
        console.log(`0x${address.toString(16).padStart(4, '0')}: ${nextNBytes.join(' ')}`);
    }

TEST the CAL and RET

 
# define the subfunction started address
 
const subroutineAddress = 0x3000
 
let i = 0
 
writeBytes[i++] = PSH_LIT
writeBytes[i++] = 0x33
writeBytes[i++] = 0x33
 
writeBytes[i++] = PSH_LIT
writeBytes[i++] = 0x22
writeBytes[i++] = 0x22
 
writeBytes[i++] = PSH_LIT
writeBytes[i++] = 0x11
writeBytes[i++] = 0x11
 
writeBytes[i++] = MOVE_LIT_REG
writeBytes[i++] = 0x12
writeBytes[i++] = 0x34
writeBytes[i++] = R1
 
writeBytes[i++] = MOVE_LIT_REG
writeBytes[i++] = 0x56
writeBytes[i++] = 0x78
writeBytes[i++] = R4
 
writeBytes[i++] = PSH_LIT
writeBytes[i++] = 0x00
writeBytes[i++] = 0x00
 
writeBytes[i++] = CAL_LIT
writeBytes[i++] = (subroutineAddress & 0xff00) >> 8
writeBytes[i++] = (subroutineAddress & 0x00ff)
 
writeBytes[i++] = PSH_LIT
writeBytes[i++] = 0x44
writeBytes[i++] = 0x44
 
i = subroutineAddress
writeBytes[i++] = PSH_LIT
writeBytes[i++] = 0x01
writeBytes[i++] = 0x02
 
writeBytes[i++] = PSH_LIT
writeBytes[i++] = 0x03
writeBytes[i++] = 0x04
 
writeBytes[i++] = PSH_LIT
writeBytes[i++] = 0x05
writeBytes[i++] = 0x06
 
writeBytes[i++] = MOVE_LIT_REG
writeBytes[i++] = 0x07
writeBytes[i++] = 0x08
writeBytes[i++] = R1
 
writeBytes[i++] = MOVE_LIT_REG
writeBytes[i++] = 0x09
writeBytes[i++] = 0x0A
writeBytes[i++] = R8
 
writeBytes[i++] = RET
 
cpu.debug()
 
// ...
 
/** 0xffff-1 為記憶體開頭
    而往下還有44個字節因此減42 (從0開始)
 **/
cpu.viewMemoryAt(0xffff - 1 - 42,44)
 
// ...
 
rl.on('line',()=>{
    // ...
    cpu.viewMemoryAt(0xffff - 1 - 42,44)
})