m_genesis.kv 1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
m_genesis.kv 2 ;; ;;
m_genesis.kv 3 ;; This file is part of 'M', a MIPS system emulator. ;;
m_genesis.kv 4 ;; ;;
m_genesis.kv 5 ;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;;
m_genesis.kv 6 ;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;;
m_genesis.kv 7 ;; ;;
m_genesis.kv 8 ;; You do not have, nor can you ever acquire the right to use, copy or ;;
m_genesis.kv 9 ;; distribute this software ; Should you use this software for any purpose, ;;
m_genesis.kv 10 ;; or copy and distribute it to anyone or in any manner, you are breaking ;;
m_genesis.kv 11 ;; the laws of whatever soi-disant jurisdiction, and you promise to ;;
m_genesis.kv 12 ;; continue doing so for the indefinite future. In any case, please ;;
m_genesis.kv 13 ;; always : read and understand any software ; verify any PGP signatures ;;
m_genesis.kv 14 ;; that you use - for any purpose. ;;
m_genesis.kv 15 ;; ;;
m_genesis.kv 16 ;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;;
m_genesis.kv 17 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
m_genesis.kv 18
m_genesis.kv 19 ;-----------------------------------------------------------------------------
m_genesis.kv 20 ;; State
m_genesis.kv 21 ;-----------------------------------------------------------------------------
m_genesis.kv 22 section .bss
m_genesis.kv 23 M resb 8 ; Addr mmap'd M (message) buffer
m_genesis.kv 24 Mbytes resb 8 ; Length of image (bytes)
m_genesis.kv 25 RAMbytes resb 8 ; Total size of RAM (bytes)
m_genesis.kv 26 ;-----------------------------------------------------------------------------
m_genesis.kv 27
m_genesis.kv 28 section .text
m_genesis.kv 29
m_genesis.kv 30 ;-----------------------------------------------------------------------------
m_genesis.kv 31 ; Jump if register value resides in given range
m_genesis.kv 32 ;-----------------------------------------------------------------------------
m_genesis.kv 33 %macro JMP_If_In 4 ; %1: register, %2: r.bottom, %3: r.top, %4: dest.
m_genesis.kv 34 cmp %1, %3 ; Compare register to TOP of range
m_genesis.kv 35 ja %%nope ; If above top -- skip.
m_genesis.kv 36 cmp %1, %2 ; Compare register to BOTTOM of range
m_genesis.kv 37 jb %%nope ; If below bottom -- skip.
m_genesis.kv 38 jmp %4 ; ... Else, register is in the range, and jump.
m_genesis.kv 39 %%nope
m_genesis.kv 40 %endmacro
m_genesis.kv 41 ;-----------------------------------------------------------------------------
m_genesis.kv 42
m_genesis.kv 43 ;-----------------------------------------------------------------------------
m_genesis.kv 44 ; Allocate RAM:
m_genesis.kv 45 ;-----------------------------------------------------------------------------
m_genesis.kv 46 _ram_allocate:
m_genesis.kv 47 ;; Allocate memory for sim RAM:
m_genesis.kv 48 mov rax, SYS_MMAP
m_genesis.kv 49 xor r9, r9 ; offset=0
m_genesis.kv 50 xor r8, r8
m_genesis.kv 51 mov r10, MAP_PRIVATE | MAP_ANONYMOUS
m_genesis.kv 52 mov rdx, PROT_READ | PROT_WRITE
m_genesis.kv 53 mov rsi, [RAMbytes] ; # of bytes to allocate
m_genesis.kv 54 xor rdi, rdi ; os will give addr
m_genesis.kv 55 syscall ; invoke mmap
m_genesis.kv 56 ;; Verify whether we actually got our memory:
m_genesis.kv 57 cmp rax, 0
m_genesis.kv 58 jg _mmap_ok
m_genesis.kv 59 ;; ... if not:
m_genesis.kv 60 EGGOG "Could not allocate memory for sim RAM!"
m_genesis.kv 61 _mmap_ok: ;; we got the memory;
m_genesis.kv 62 ret
m_genesis.kv 63 ;-----------------------------------------------------------------------------
m_genesis.kv 64
m_genesis.kv 65 ;-----------------------------------------------------------------------------
m_genesis.kv 66 ; Free RAM:
m_genesis.kv 67 ;-----------------------------------------------------------------------------
m_genesis.kv 68 _ram_deallocate:
m_genesis.kv 69 mov rax, SYS_MUNMAP
m_genesis.kv 70 mov rsi, [RAMbytes]
m_genesis.kv 71 mov rdi, [M]
m_genesis.kv 72 syscall ; munmap(M, Mbytes)
m_genesis.kv 73 ret
m_genesis.kv 74 ;-----------------------------------------------------------------------------
m_genesis.kv 75
m_genesis.kv 76 ;-----------------------------------------------------------------------------
m_genesis.kv 77 ; Init RAM:
m_genesis.kv 78 ;-----------------------------------------------------------------------------
m_genesis.kv 79 _ram_init:
m_genesis.kv 80 mov RAM_Floor, [M] ; Set 'floor' of RAM
m_genesis.kv 81 mov RAM_Ceiling, RAM_Floor ; 'ceiling'
m_genesis.kv 82 add RAM_Ceiling, [RAMbytes]; ... size of the mmap
m_genesis.kv 83 ;; TODO: test for unaligned floor or ceiling
m_genesis.kv 84 ret
m_genesis.kv 85 ;-----------------------------------------------------------------------------
m_genesis.kv 86
m_genesis.kv 87 ;-----------------------------------------------------------------------------
m_genesis.kv 88 ; Translate_Address : virtual address in eax; output (physical addr) in eax
m_genesis.kv 89 ;-----------------------------------------------------------------------------
m_genesis.kv 90 align GRAIN, db 0x90
m_genesis.kv 91 _Virt_To_Phys:
m_genesis.kv 92 bt eax, 31 ; CF := 31st (top) bit of vAddr
m_genesis.kv 93 jc _Above_7FFFFFFF ; If 31st bit = 1, kseg 0/1/2; else:
m_genesis.kv 94 ;; 0x00000000 <= vAddr <= 0x7FFFFFFF (kuseg) :
m_genesis.kv 95 bt CP0_Status, CP0St_ERL ; CF := CP0St_ERL Flag
m_genesis.kv 96 jnc _TLB_Lookup ; If ERL = 0: TLB Lookup required; else:
m_genesis.kv 97 jmp _No_Tlb_Lookup ; pAddr is equal to vAddr, return.
m_genesis.kv 98 _Above_7FFFFFFF:
m_genesis.kv 99 bt eax, 30 ; CF := 30th (2nd from top) bt of vAddr
m_genesis.kv 100 jc _Above_BFFFFFFF ; If 30th bit = 1 : kseg2; else:
m_genesis.kv 101 ;; 0x80000000 <= vAddr <= 0x9FFFFFFF (kseg0) :
m_genesis.kv 102 ;; 0xA0000000 <= vAddr <= 0xBFFFFFFF (kseg1) :
m_genesis.kv 103 and eax, 0x1FFFFFFF ; kseg0 and kseg1: clear top 3 bits,
m_genesis.kv 104 jmp _No_Tlb_Lookup ; i.e. pAddr := bottom 29 bts of vAddr.
m_genesis.kv 105 _Above_BFFFFFFF: ;; 0xC0000000 <= vAddr <= 0xFFFFFFFF (kseg2) :
m_genesis.kv 106 bt CP0_Status, CP0St_UM ; CF := CP0St_UM Flag
m_genesis.kv 107 jnc _TLB_Lookup ; If UM = 0: K. Mode, so do TLB; else:
m_genesis.kv 108 test CP0_Status, (1 << CP0St_EXL) | (1 << CP0St_ERL) ; EXL or ERL
m_genesis.kv 109 jnz _TLB_Lookup ; If EXL && ERL, K. Mode, do TLB
m_genesis.kv 110 ;; Else: vAddr is in kseg2, but we are NOT in Kernel Mode:
m_genesis.kv 111 Flg_Get IsWriting ; Is Writing?
m_genesis.kv 112 jc _V2P_Eggog_Wr ; If so, we want to set AdES;
m_genesis.kv 113 _V2P_Eggog_Rd: ; ... otherwise, set AdEL.
m_genesis.kv 114 SetEXC EXC_AdEL ; Fetch address error.
m_genesis.kv 115 jmp _V2P_Eggog_Fin ; Proceed to abort.
m_genesis.kv 116 _V2P_Eggog_Wr:
m_genesis.kv 117 SetEXC EXC_AdES ; Store address error.
m_genesis.kv 118 _V2P_Eggog_Fin:
m_genesis.kv 119 ;; Will go into exception handler instead of back to _Virt_xxx etc
m_genesis.kv 120 add rsp, 16 ; squelch return to _Virt_xxx and its caller
m_genesis.kv 121 push _Handle_Exception ; 'return' directly to exc handler.
m_genesis.kv 122 _No_Tlb_Lookup:
m_genesis.kv 123 Flg_Off IsWriting ; Consume 'is writing' flag.
m_genesis.kv 124 ret ; Done.
m_genesis.kv 125 _TLB_Lookup: ; TLB Lookup Required:
m_genesis.kv 126 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
m_genesis.kv 127 ;; Save ebx, ecx, edx, AUX, to xmm ;;
m_genesis.kv 128 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
m_genesis.kv 129 movd xmm0, ebx
m_genesis.kv 130 movd xmm1, ecx
m_genesis.kv 131 movd xmm2, edx
m_genesis.kv 132 movd xmm3, AUX
m_genesis.kv 133 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
m_genesis.kv 134 Flg_Off ExcWasTLBNoMatch ; Clear the ExcWasTLBNoMatch Flag
m_genesis.kv 135 ;; Get the active ASID:
m_genesis.kv 136 mov edx, Sr(CP0_EntryHi) ; edx := CP0_EntryHi
m_genesis.kv 137 and edx, 0xFF ; edx := edx & 0xFF (get current ASID)
m_genesis.kv 138 ;; For each slot in table (0 .. 15), attempt lookup
m_genesis.kv 139 xor AUX, AUX ; Start with the 0-th entry in table
m_genesis.kv 140 _Lookup_TLB_E:
m_genesis.kv 141 mov ecx, eax ; ecx := eax (vAddr)
m_genesis.kv 142 and ecx, 0xFFFFF000 ; ecx := ecx & 0xFFFFF000
m_genesis.kv 143 shr ecx, 13 ; ecx := ecx >> 13 (get vAddr's Tag)
m_genesis.kv 144 mov ebx, TLB_E(AUX) ; ebx := current TLB entry
m_genesis.kv 145 and ebx, TLB_VPN2_Mask ; get VPN2 of this entry
m_genesis.kv 146 cmp ebx, ecx ; cmp(entry.VPN2, vAddr.tag)
m_genesis.kv 147 jne _Lookup_TLB_E_Not_Here ; if entry.VPN2 != vAddr.tag: no match
m_genesis.kv 148 mov ebx, TLB_E(AUX) ; ebx := current TLB entry
m_genesis.kv 149 bt ebx, TLB_G ; is entry.G = 1?
m_genesis.kv 150 jc _Lookup_TLB_E_Match ; then match.
m_genesis.kv 151 shr ebx, TLB_ASID_Shift ; ebx := ebx >> TLB_ASID_Shift
m_genesis.kv 152 and ebx, TLB_ASID_Mask ; ebx := entry.ASID
m_genesis.kv 153 cmp ebx, edx ; entry.ASID = current ASID ?
m_genesis.kv 154 jne _Lookup_TLB_E_Not_Here ; if neither G=1 nor ASID match.
m_genesis.kv 155 mov ebx, TLB_E(AUX) ; ebx := current TLB entry
m_genesis.kv 156 _Lookup_TLB_E_Match: ; TLB Match:
m_genesis.kv 157 bt eax, 12 ; Test odd/even junior bit
m_genesis.kv 158 jc _Lookup_TLB_E_Match_Odd ; If odd: test V1, D1
m_genesis.kv 159 _Lookup_TLB_E_Match_Even: ; If even: test V0, D0
m_genesis.kv 160 lea ecx, TLB_PFN_E(AUX) ; prepare to load even PFN entry
m_genesis.kv 161 bt ebx, TLB_V0 ; Is entry.V0=1 ?
m_genesis.kv 162 jnc _Lookup_TLB_E_Invalid ; If not, TLBRET_INVALID
m_genesis.kv 163 bt ebx, TLB_D0 ; Is entry.D0=1 ?
m_genesis.kv 164 jc _Lookup_TLB_E_Match_Yes ; If entry.D0=1, then Match Yes
m_genesis.kv 165 jmp _Lookup_TLB_E_Match_Wr ; else, go to 'is writing?'
m_genesis.kv 166 _Lookup_TLB_E_Match_Odd: ; Odd bit:
m_genesis.kv 167 lea ecx, TLB_PFN_O(AUX) ; prepare to load odd PFN entry
m_genesis.kv 168 bt ebx, TLB_V1 ; Is entry.V1=1 ?
m_genesis.kv 169 jnc _Lookup_TLB_E_Invalid ; If not, TLBRET_INVALID
m_genesis.kv 170 bt ebx, TLB_D1 ; Is entry.D1=1 ?
m_genesis.kv 171 jc _Lookup_TLB_E_Match_Yes ; If entry.D1=1, then Match Yes
m_genesis.kv 172 _Lookup_TLB_E_Match_Wr:
m_genesis.kv 173 Flg_Get IsWriting ; Is Writing?
m_genesis.kv 174 jnc _Lookup_TLB_E_Match_Yes ; If not writing, go to Match Yes
m_genesis.kv 175 _Lookup_TLB_E_Dirty: ; ... else, Dirty:
m_genesis.kv 176 SetEXC EXC_Mod ; Set the EXC_Mod Exception
m_genesis.kv 177 jmp _Lookup_TLB_E_WriteExtr ; Write the 'extra data' and finish.
m_genesis.kv 178 _Lookup_TLB_E_Match_Yes: ; This is the 'success' case
m_genesis.kv 179 mov ebx, dword [ecx] ; Actually load the current PFN entry
m_genesis.kv 180 and eax, 0xFFF ; vAddr := vAddr & 0xFFF
m_genesis.kv 181 or eax, ebx ; vAddr := vAddr | entry.PFN[lowbit]
m_genesis.kv 182 jmp _Lookup_TLB_Done ; vAddr is now correct pAddr, done.
m_genesis.kv 183 _Lookup_TLB_E_Not_Here: ; try next one in the table, if any
m_genesis.kv 184 inc AUX ; index := index + 1
m_genesis.kv 185 cmp AUX, TLB_ENTRIES_COUNT ; see if still in range 0 .. n-1
m_genesis.kv 186 jb _Lookup_TLB_E ; if in range, go to next entry
m_genesis.kv 187 ;; ... else:
m_genesis.kv 188 Flg_On ExcWasTLBNoMatch ; Set the ExcWasTLBNoMatch Flag
m_genesis.kv 189 ;; ... now drop down into 'invalid' :
m_genesis.kv 190 _Lookup_TLB_E_Invalid:
m_genesis.kv 191 Flg_Get IsWriting ; Was Writing?
m_genesis.kv 192 jc _Lookup_TLB_E_Invalid_W ; If so, we want to set EXC_TLBS
m_genesis.kv 193 _Lookup_TLB_E_Invalid_R: ; Otherwise, set EXC_TLBL exception
m_genesis.kv 194 SetEXC EXC_TLBL ; Set the EXC_TLBL Exception
m_genesis.kv 195 jmp _Lookup_TLB_E_WriteExtr ; Write the 'extra data' and finish.
m_genesis.kv 196 _Lookup_TLB_E_Invalid_W:
m_genesis.kv 197 SetEXC EXC_TLBS ; Set the EXC_TLBS Exception
m_genesis.kv 198 ;; then drop down to 'write extra' :
m_genesis.kv 199 _Lookup_TLB_E_WriteExtr: ; Write the 'extra data' and finish
m_genesis.kv 200 mov Sr(CP0_BadVAddr), eax ; CP0_BadVAddr := vAddr
m_genesis.kv 201 mov ecx, eax ; ecx := vAddr
m_genesis.kv 202 mov ebx, Sr(CP0_Context) ; ebx := CP0_Context
m_genesis.kv 203 and ebx, ~0x007FFFFF ; ebx := ebx & ~0x007FFFFF
m_genesis.kv 204 shr ecx, 9 ; ecx := ecx >> 9
m_genesis.kv 205 and ecx, 0x007FFFF0 ; ecx := ecx & 0x007FFFF0
m_genesis.kv 206 or ebx, ecx ; ebx := ebx | ecx
m_genesis.kv 207 mov Sr(CP0_Context), ebx ; CP0_Context := ebx
m_genesis.kv 208 mov ecx, eax ; ecx := vAddr
m_genesis.kv 209 mov ebx, Sr(CP0_EntryHi) ; ebx := CP0_EntryHi
m_genesis.kv 210 and ebx, 0xFF ; ebx := ebx & 0xFF
m_genesis.kv 211 and ecx, 0xFFFFE000 ; ecx := ecx & 0xFFFFE000
m_genesis.kv 212 or ebx, ecx ; ebx := ebx | ecx
m_genesis.kv 213 mov Sr(CP0_EntryHi), ebx ; CP0_EntryHi := ebx
m_genesis.kv 214 ;; Will go into exception handler instead of back to _Virt_xxx etc
m_genesis.kv 215 add rsp, 16 ; squelch return to _Virt_xxx and its caller
m_genesis.kv 216 push _Handle_Exception ; 'return' directly to exc handler.
m_genesis.kv 217 ;; and drop into 'done' :
m_genesis.kv 218 _Lookup_TLB_Done:
m_genesis.kv 219 Flg_Off IsWriting ; Consume 'is writing' flag.
m_genesis.kv 220 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
m_genesis.kv 221 ;; Restore ebx, ecx, edx, AUX, from xmm ;;
m_genesis.kv 222 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
m_genesis.kv 223 movd ebx, xmm0
m_genesis.kv 224 movd ecx, xmm1
m_genesis.kv 225 movd edx, xmm2
m_genesis.kv 226 movd AUX, xmm3
m_genesis.kv 227 ret
m_genesis.kv 228 ;-----------------------------------------------------------------------------
m_genesis.kv 229
m_genesis.kv 230 ;-----------------------------------------------------------------------------
m_genesis.kv 231 ; ReadWord - Source Address (Virtual) is EAX; Resulting Word Read is EAX.
m_genesis.kv 232 ;-----------------------------------------------------------------------------
m_genesis.kv 233 ; TODO: 1) is the 'unaligned' case in fact physically possible?
m_genesis.kv 234 ; 2) would go faster if used tmp instead of self-clobbering rax ?
m_genesis.kv 235 align GRAIN, db 0x90
m_genesis.kv 236 _Virt_Read_Word:
m_genesis.kv 237 call _Virt_To_Phys ; Transform vAddr to pAddr
m_genesis.kv 238 test eax, 0x3 ; Are any of the bottom 2 bits set?
m_genesis.kv 239 jnz _V_Rd_Word_Unaligned ; If yes, go to eggog. Else:
m_genesis.kv 240 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
m_genesis.kv 241 ;; If pAddr is in Memory-Mapped Device space:
m_genesis.kv 242 JMP_If_In eax, MMIO_BASE, MMIO_TOP, _Phys_Device_Read_Word
m_genesis.kv 243 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
m_genesis.kv 244 add rax, RAM_Floor ; Calculate the target PC RAM addr
m_genesis.kv 245 cmp rax, RAM_Ceiling ; compare the above with RAM ceiling
m_genesis.kv 246 jnb _V_Rd_Word_Hit_Ceiling ; if not below: RAM ceiling eggog!
m_genesis.kv 247 ;; Address is valid, so load the word:
m_genesis.kv 248 mov eax, dword [rax] ; eax := dword [rax]
m_genesis.kv 249 ret ; Done, return with read result in eax.
m_genesis.kv 250 _V_Rd_Word_Unaligned: ;; TODO: print address
m_genesis.kv 251 ACHTUNG "Virt_Read_Word: Unaligned Physical Address!"
m_genesis.kv 252 jmp _V_Rd_Word_Eggog ; Go to eggogology
m_genesis.kv 253 _V_Rd_Word_Hit_Ceiling: ;; TODO: print address
m_genesis.kv 254 ACHTUNG "Virt_Read_Word: Hit RAM Ceiling!"
m_genesis.kv 255 _V_Rd_Word_Eggog:
m_genesis.kv 256 SetEXC EXC_AdEL ; Fetch address error.
m_genesis.kv 257 ;; Will go into exception handler instead of back to caller
m_genesis.kv 258 add rsp, 8 ; squelch return to original caller
m_genesis.kv 259 push _Handle_Exception ; 'return' directly to exc handler.
m_genesis.kv 260 ret ; Go there.
m_genesis.kv 261 ;-----------------------------------------------------------------------------
m_genesis.kv 262
m_genesis.kv 263 ;-----------------------------------------------------------------------------
m_genesis.kv 264 ; WriteWord - Destination Address (Virtual) is EAX; Word to be Written is TMP.
m_genesis.kv 265 ;-----------------------------------------------------------------------------
m_genesis.kv 266 ; TODO: 1) is the 'unaligned' case in fact physically possible?
m_genesis.kv 267 ; 2) would go faster if used tmp instead of self-clobbering rax ?
m_genesis.kv 268 ; 3) do we need to explicitly zero-extend rax here?
m_genesis.kv 269 align GRAIN, db 0x90
m_genesis.kv 270 _Virt_Write_Word:
m_genesis.kv 271 Flg_On IsWriting ; Tell the translator that we're writing
m_genesis.kv 272 call _Virt_To_Phys ; Transform vAddr to pAddr
m_genesis.kv 273 test eax, 0x3 ; Are any of the bottom 2 bits set?
m_genesis.kv 274 jnz _V_Wr_Word_Unaligned ; If yes, go to eggog. Else:
m_genesis.kv 275 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
m_genesis.kv 276 ;; If pAddr is in Memory-Mapped Device space:
m_genesis.kv 277 JMP_If_In eax, MMIO_BASE, MMIO_TOP, _Phys_Device_Write_Word
m_genesis.kv 278 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
m_genesis.kv 279 add rax, RAM_Floor ; Calculate the target PC RAM addr
m_genesis.kv 280 cmp rax, RAM_Ceiling ; compare the above with RAM ceiling
m_genesis.kv 281 jnb _V_Wr_Word_Hit_Ceiling ; if not below: RAM ceiling eggog!
m_genesis.kv 282 ;; Address is valid, so write the word:
m_genesis.kv 283 mov dword [rax], TMP ; dword [rax] := TMP
m_genesis.kv 284 ret ; Done.
m_genesis.kv 285 _V_Wr_Word_Unaligned: ;; TODO: print address
m_genesis.kv 286 ACHTUNG "Virt_Write_Word: Unaligned Physical Address!"
m_genesis.kv 287 jmp _V_Wr_Word_Eggog
m_genesis.kv 288 _V_Wr_Word_Hit_Ceiling: ;; TODO: print address
m_genesis.kv 289 ACHTUNG "Virt_Write_Word: Hit RAM Ceiling!"
m_genesis.kv 290 _V_Wr_Word_Eggog:
m_genesis.kv 291 SetEXC EXC_AdES ; Store address error.
m_genesis.kv 292 ;; Will go into exception handler instead of back to caller
m_genesis.kv 293 add rsp, 8 ; squelch return to original caller
m_genesis.kv 294 push _Handle_Exception ; 'return' directly to exc handler.
m_genesis.kv 295 ret ; Go there.
m_genesis.kv 296 ;-----------------------------------------------------------------------------
m_genesis.kv 297
m_genesis.kv 298 ;-----------------------------------------------------------------------------
m_genesis.kv 299 ; ReadByte - Source Address (Virtual) is EAX; Resulting Byte Read is EAX.
m_genesis.kv 300 ;-----------------------------------------------------------------------------
m_genesis.kv 301 align GRAIN, db 0x90
m_genesis.kv 302 _Virt_Read_Byte:
m_genesis.kv 303 call _Virt_To_Phys ; Transform vAddr to pAddr
m_genesis.kv 304 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
m_genesis.kv 305 ;; If pAddr is in Memory-Mapped Device space:
m_genesis.kv 306 JMP_If_In eax, MMIO_BASE, MMIO_TOP, _Phys_Device_Read_Byte
m_genesis.kv 307 ;;;;;;;;;;;;;;;;;; ENDIANISM ;;;;;;;;;;;;;;;;;;
m_genesis.kv 308 %ifdef LITTLE_ENDIAN
m_genesis.kv 309 ;; little -- do nothing
m_genesis.kv 310 %else
m_genesis.kv 311 ;; big -- flip :
m_genesis.kv 312 xor eax, 0x3 ; Flip the 3 unalignment bits.
m_genesis.kv 313 %endif
m_genesis.kv 314 ;;;;;;;;;;;;;;;;;; ENDIANISM ;;;;;;;;;;;;;;;;;;
m_genesis.kv 315 add rax, RAM_Floor ; Calculate the target PC RAM addr
m_genesis.kv 316 cmp rax, RAM_Ceiling ; compare the above with RAM ceiling
m_genesis.kv 317 jnb _V_Rd_Byte_Hit_Ceiling ; if not below: RAM ceiling eggog!
m_genesis.kv 318 ;; Address is valid, so proceed to load:
m_genesis.kv 319 movzx ax, byte [rax] ; ax := byte [rax]
m_genesis.kv 320 and eax, 0xFF ; keep only bottom byte
m_genesis.kv 321 ret ; Done, return with read result in eax.
m_genesis.kv 322 _V_Rd_Byte_Hit_Ceiling:
m_genesis.kv 323 ACHTUNG "Virt_Read_Byte: Hit RAM Ceiling!" ;; TODO: print address
m_genesis.kv 324 SetEXC EXC_AdEL ; Fetch address error.
m_genesis.kv 325 ;; Will go into exception handler instead of back to caller
m_genesis.kv 326 add rsp, 8 ; squelch return to original caller
m_genesis.kv 327 push _Handle_Exception ; 'return' directly to exc handler.
m_genesis.kv 328 ret ; Go there.
m_genesis.kv 329 ;-----------------------------------------------------------------------------
m_genesis.kv 330
m_genesis.kv 331 ;-----------------------------------------------------------------------------
m_genesis.kv 332 ; WriteByte - Destination Address (Virtual) is EAX; Byte to be Written is DL.
m_genesis.kv 333 ;-----------------------------------------------------------------------------
m_genesis.kv 334 align GRAIN, db 0x90
m_genesis.kv 335 _Virt_Write_Byte:
m_genesis.kv 336 Flg_On IsWriting ; Tell the translator that we're writing
m_genesis.kv 337 call _Virt_To_Phys ; Transform vAddr to pAddr
m_genesis.kv 338 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
m_genesis.kv 339 ;; If pAddr is in Memory-Mapped Device space:
m_genesis.kv 340 JMP_If_In eax, MMIO_BASE, MMIO_TOP, _Phys_Device_Write_Byte
m_genesis.kv 341 ;;;;;;;;;;;;;;;;;; ENDIANISM ;;;;;;;;;;;;;;;;;;
m_genesis.kv 342 %ifdef LITTLE_ENDIAN
m_genesis.kv 343 ;; little -- do nothing
m_genesis.kv 344 %else
m_genesis.kv 345 ;; big -- flip :
m_genesis.kv 346 xor eax, 0x3 ; Flip the 3 unalignment bits.
m_genesis.kv 347 %endif
m_genesis.kv 348 ;;;;;;;;;;;;;;;;;; ENDIANISM ;;;;;;;;;;;;;;;;;;
m_genesis.kv 349 add rax, RAM_Floor ; Calculate the target PC RAM addr
m_genesis.kv 350 cmp rax, RAM_Ceiling ; compare the above with RAM ceiling
m_genesis.kv 351 jnb _V_Wr_Byte_Hit_Ceiling ; if not below: RAM ceiling eggog!
m_genesis.kv 352 ;; Address is valid, so write the byte:
m_genesis.kv 353 mov byte [rax], DL ; dword [rax] := TMP
m_genesis.kv 354 ret ; Done.
m_genesis.kv 355 _V_Wr_Byte_Hit_Ceiling:
m_genesis.kv 356 ACHTUNG "Virt_Write_Byte: Hit RAM Ceiling!" ;; TODO: print address
m_genesis.kv 357 SetEXC EXC_AdES ; Store address error.
m_genesis.kv 358 ;; Will go into exception handler instead of back to caller
m_genesis.kv 359 add rsp, 8 ; squelch return to original caller
m_genesis.kv 360 push _Handle_Exception ; 'return' directly to exc handler.
m_genesis.kv 361 ret ; Go there.
m_genesis.kv 362 ;-----------------------------------------------------------------------------
m_genesis.kv 363
m_genesis.kv 364 ;-----------------------------------------------------------------------------
m_genesis.kv 365 ; Write TLB Entry at the given index. Index (0 .. 15) is in AUX.
m_genesis.kv 366 ; Kills eax, ebx, ecx, edx.
m_genesis.kv 367 ;-----------------------------------------------------------------------------
m_genesis.kv 368 _write_tlb_entry:
m_genesis.kv 369 mov edx, Sr(CP0_EntryHi) ; edx := CP0_EntryHi
m_genesis.kv 370 mov ecx, edx ; ecx := edx
m_genesis.kv 371 shr ecx, 13 ; ecx := ecx >> 13 to get VPN2
m_genesis.kv 372 and edx, TLB_ASID_Mask ; edx := edx & 0xFF to get ASID
m_genesis.kv 373 shl edx, TLB_ASID_Shift ; edx := edx << 19 to put ASID in place
m_genesis.kv 374 or ecx, edx ; now we have VPN2 and ASID in ecx
m_genesis.kv 375 ;; done with edx, can reuse
m_genesis.kv 376 mov edx, Sr(CP0_EntryLo0) ; edx := CP0_EntryLo0
m_genesis.kv 377 mov ebx, Sr(CP0_EntryLo1) ; ebx := CP0_EntryLo1
m_genesis.kv 378 ;; get G:
m_genesis.kv 379 mov eax, edx ; eax := CP0_EntryLo0
m_genesis.kv 380 and eax, ebx ; eax := eax & CP0_EntryLo1
m_genesis.kv 381 and eax, 0x1 ; eax := eax & 1 to get G
m_genesis.kv 382 shl eax, TLB_G ; move G bit into position
m_genesis.kv 383 or ecx, eax ; ecx := ecx | eax to put in G bit
m_genesis.kv 384 ;; now ecx contains VPN2, ASID, G
m_genesis.kv 385 ;; Get V0 from CP0_EntryLo0 and put in ecx where belongs:
m_genesis.kv 386 mov eax, edx ; eax := CP0_EntryLo0
m_genesis.kv 387 and eax, 0x2 ; eax := eax & 0x2 to get V0 bit
m_genesis.kv 388 shl eax, (TLB_V0 - 1) ; put V0 bit in position
m_genesis.kv 389 or ecx, eax ; ecx := ecx | eax to put in V0 bit
m_genesis.kv 390 ;; Get D0 from CP0_EntryLo0 and put in ecx where belongs:
m_genesis.kv 391 mov eax, edx ; eax := CP0_EntryLo0
m_genesis.kv 392 and eax, 0x4 ; eax := eax & 0x4 to get D0 bit
m_genesis.kv 393 shl eax, (TLB_D0 - 2) ; put D0 bit in position
m_genesis.kv 394 or ecx, eax ; ecx := ecx | eax to put in D0 bit
m_genesis.kv 395 ;; Get V1 from CP0_EntryLo1 and put in ecx where belongs:
m_genesis.kv 396 mov eax, ebx ; eax := CP0_EntryLo1
m_genesis.kv 397 and eax, 0x2 ; eax := eax & 0x2 to get V1 bit
m_genesis.kv 398 shl eax, (TLB_V1 - 1) ; put V1 bit in position
m_genesis.kv 399 or ecx, eax ; ecx := ecx | eax to put in V1 bit
m_genesis.kv 400 ;; Get D1 from CP0_EntryLo1 and put in ecx where belongs:
m_genesis.kv 401 mov eax, ebx ; eax := CP0_EntryLo1
m_genesis.kv 402 and eax, 0x4 ; eax := eax & 0x4 to get D1 bit
m_genesis.kv 403 shl eax, (TLB_D1 - 2) ; put D1 bit in position
m_genesis.kv 404 or ecx, eax ; ecx := ecx | eax to put in D1 bit
m_genesis.kv 405 ;; Write the TLB entry to the given index (in AUX) :
m_genesis.kv 406 and AUX, 0xF ; Index of TLB entry is bottom 4 bits
m_genesis.kv 407 mov TLB_E(AUX), ecx ; Write TLB entry in ecx to n-th slot.
m_genesis.kv 408 ;; Transform CP0_EntryLo0 (edx) into PFN0:
m_genesis.kv 409 shr edx, 6
m_genesis.kv 410 and edx, 0xFFFFF
m_genesis.kv 411 shl edx, 12
m_genesis.kv 412 ;; Transform CP0_EntryLo1 (ebx) into PFN1:
m_genesis.kv 413 shr ebx, 6
m_genesis.kv 414 and ebx, 0xFFFFF
m_genesis.kv 415 shl ebx, 12
m_genesis.kv 416 ;; Store PFN:
m_genesis.kv 417 mov TLB_PFN_E(AUX), edx ; Store PFN0
m_genesis.kv 418 mov TLB_PFN_O(AUX), ebx ; Store PFN1
m_genesis.kv 419 ret ; Done.
m_genesis.kv 420 ;-----------------------------------------------------------------------------