Compare commits
2 commits
master
...
sic_sim_de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e9039af05 | ||
|
|
7c6379c62d |
57 changed files with 1051 additions and 5401 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -19,8 +19,7 @@ node_modules/
|
|||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
|
||||
*.dev
|
||||
autotester
|
||||
sictools.jar
|
||||
simulator_SIC_XE/CMakeLists.txt.user
|
||||
|
||||
/build/
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
arith START 0
|
||||
|
||||
. seštevek: sum = x + y
|
||||
LDA x
|
||||
ADD y
|
||||
STA sum
|
||||
|
||||
. razlika: diff = x - y
|
||||
LDA x
|
||||
SUB y
|
||||
STA diff
|
||||
|
||||
. produkt: prod = x * y
|
||||
LDA x
|
||||
MUL y
|
||||
STA prod
|
||||
|
||||
. količnik: quot = x / y
|
||||
LDA x
|
||||
DIV y .
|
||||
STA quot
|
||||
|
||||
. ostanek: mod = x - y * (x / y)
|
||||
STA qtemp
|
||||
|
||||
LDA y
|
||||
MUL qtemp
|
||||
STA prodtmp
|
||||
|
||||
LDA x
|
||||
SUB prodtmp
|
||||
STA mod
|
||||
|
||||
HALT J HALT
|
||||
|
||||
.data
|
||||
x WORD 5
|
||||
y WORD 2
|
||||
|
||||
sum RESW 1
|
||||
diff RESW 1
|
||||
prod RESW 1
|
||||
quot RESW 1
|
||||
mod RESW 1
|
||||
|
||||
qtemp RESW 1
|
||||
prodtmp RESW 1
|
||||
|
||||
END arith
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
.code
|
||||
arithr START 0
|
||||
|
||||
LDA x ; A = x
|
||||
LDB y ; B = y
|
||||
|
||||
RMO A,S ; S = x
|
||||
RMO B,T ; T = y
|
||||
|
||||
. sum = x + y
|
||||
RMO S,A ; A = x
|
||||
ADDR T,A ; A = x + y
|
||||
STA sum
|
||||
|
||||
. diff = x - y
|
||||
RMO S,A ; A = x
|
||||
SUBR T,A ; A = x - y
|
||||
STA diff
|
||||
|
||||
. prod = x * y
|
||||
RMO S,A ; A = x
|
||||
RMO T,B ; B = y
|
||||
MULR B,A ; A = x * y
|
||||
STA prod
|
||||
|
||||
. quot = x / y
|
||||
RMO S,A ; A = x
|
||||
RMO T,B ; B = y
|
||||
DIVR B,A ; A = x / y
|
||||
STA quot
|
||||
|
||||
|
||||
. mod = x % y = x - (x / y)*y
|
||||
RMO S,A
|
||||
RMO T,B
|
||||
DIVR B,A ; A = q = x/y
|
||||
RMO A,L ; L = q (shrani)
|
||||
|
||||
RMO T,A ; A = y
|
||||
RMO L,B ; B = q
|
||||
MULR B,A ; A = y*q = produkt
|
||||
|
||||
RMO S,B ; B = x
|
||||
SUBR A,B ; B = x - (y*q)
|
||||
STB mod
|
||||
|
||||
HALT J HALT
|
||||
|
||||
.data
|
||||
x WORD 5
|
||||
y WORD 2
|
||||
|
||||
sum RESW 1
|
||||
diff RESW 1
|
||||
prod RESW 1
|
||||
quot RESW 1
|
||||
mod RESW 1
|
||||
|
||||
END arithr
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
poly START 0
|
||||
|
||||
. potence x^1 .. x^4
|
||||
LDA x
|
||||
STA x1
|
||||
|
||||
MUL x
|
||||
STA x2
|
||||
|
||||
MUL x
|
||||
STA x3
|
||||
|
||||
MUL x
|
||||
STA x4
|
||||
|
||||
|
||||
. x3 = 2 * x^3
|
||||
LDA x3
|
||||
LDB #2
|
||||
MULR A,B
|
||||
STB x3
|
||||
|
||||
. x2 = 3 * x^2
|
||||
LDA x2
|
||||
LDB #3
|
||||
MULR A,B
|
||||
STB x2
|
||||
|
||||
. x1 = 4 * x
|
||||
LDA x1
|
||||
LDB #4
|
||||
MULR A,B
|
||||
STB x1
|
||||
|
||||
. vsota vseh
|
||||
LDA x0
|
||||
ADD x1
|
||||
ADD x2
|
||||
ADD x3
|
||||
ADD x4
|
||||
STA result
|
||||
|
||||
|
||||
HALT J HALT
|
||||
END poly
|
||||
.data
|
||||
x WORD 2 ; vrednost x
|
||||
|
||||
x4 RESW 1
|
||||
x3 RESW 1
|
||||
x2 RESW 1
|
||||
x1 RESW 1
|
||||
x0 WORD 5 ; konstanta
|
||||
|
||||
result RESW 1
|
||||
|
||||
END poly
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
.code
|
||||
prog START 0
|
||||
|
||||
LDX #0
|
||||
|
||||
loop LDCH msg,X
|
||||
WD #0xAA
|
||||
|
||||
TIX msglen
|
||||
JLT loop
|
||||
|
||||
LDA #0x0D
|
||||
WD #0xAA
|
||||
LDA #0x0A
|
||||
WD #0xAA
|
||||
|
||||
halt J halt
|
||||
|
||||
.data
|
||||
msg BYTE C'SIC/XE'
|
||||
msglen WORD 6
|
||||
|
||||
END prog
|
||||
223
ass1/rec.asm
223
ass1/rec.asm
|
|
@ -1,223 +0,0 @@
|
|||
prog START 0
|
||||
|
||||
.-------------------------------------------
|
||||
. MAIN LOOP
|
||||
.
|
||||
. Psevdo:
|
||||
. sp = 0
|
||||
. while true:
|
||||
. n = readFA()
|
||||
. if n == 0: halt
|
||||
. acc = 1
|
||||
. fact() ; rekurzivno: acc = n!
|
||||
. printStdout(acc)
|
||||
.-------------------------------------------
|
||||
CLEAR A
|
||||
STA sp
|
||||
|
||||
loop JSUB readFA
|
||||
COMP #0
|
||||
JEQ halt
|
||||
|
||||
STA n
|
||||
LDA #1
|
||||
STA acc
|
||||
|
||||
JSUB fact
|
||||
LDA acc
|
||||
JSUB printStdout
|
||||
|
||||
J loop
|
||||
|
||||
halt J halt
|
||||
|
||||
.-------------------------------------------
|
||||
. readFA
|
||||
.
|
||||
. Psevdo:
|
||||
. B = 0
|
||||
. while true:
|
||||
. ch = RD(FA)
|
||||
. if ch == CR or ch == LF: break
|
||||
. digit = ch - '0'
|
||||
. B = B * 10 + digit
|
||||
. return B
|
||||
.-------------------------------------------
|
||||
readFA CLEAR B
|
||||
LDS #10
|
||||
|
||||
rd_loopFA RD #0xFA
|
||||
COMP #0x0D . CR?
|
||||
JEQ rd_doneCR_FA
|
||||
COMP #0x0A . LF?
|
||||
JEQ rd_doneFA
|
||||
|
||||
SUB #0x30
|
||||
MULR S,B . B = B * 10
|
||||
ADDR A,B . B = B + digit
|
||||
J rd_loopFA
|
||||
|
||||
rd_doneCR_FA RD #0xFA . pogoltni LF po CR
|
||||
rd_doneFA CLEAR A
|
||||
RMO B,A
|
||||
RSUB
|
||||
|
||||
.-------------------------------------------
|
||||
. fact
|
||||
.
|
||||
. Psevdo (globalni n, acc, sklad L):
|
||||
. fact():
|
||||
. push(L)
|
||||
. if n <= 1:
|
||||
. pop(L); return
|
||||
. acc = acc * n
|
||||
. n = n - 1
|
||||
. fact()
|
||||
. pop(L); return
|
||||
.-------------------------------------------
|
||||
fact . push L
|
||||
LDA sp
|
||||
ADD #3
|
||||
STA sp
|
||||
LDX sp
|
||||
STL stackL,X
|
||||
|
||||
LDA n
|
||||
COMP #1
|
||||
JGT fact_rec
|
||||
|
||||
. base case: n <= 1
|
||||
LDX sp
|
||||
LDL stackL,X
|
||||
LDA sp
|
||||
SUB #3
|
||||
STA sp
|
||||
RSUB
|
||||
|
||||
fact_rec . recursive case: acc *= n; n--; fact()
|
||||
|
||||
LDB acc
|
||||
LDS n
|
||||
MULR S,B
|
||||
STB acc
|
||||
|
||||
LDA n
|
||||
SUB #1
|
||||
STA n
|
||||
|
||||
JSUB fact
|
||||
|
||||
. pop L in return to caller
|
||||
LDX sp
|
||||
LDL stackL,X
|
||||
LDA sp
|
||||
SUB #3
|
||||
STA sp
|
||||
RSUB
|
||||
|
||||
.-------------------------------------------
|
||||
. printStdout
|
||||
.
|
||||
. Psevdo:
|
||||
. if A == 0:
|
||||
. print "0\n"
|
||||
. return
|
||||
. ps_val = A
|
||||
. ps_len = 0
|
||||
. while ps_val > 0:
|
||||
. q = ps_val / 10
|
||||
. r = ps_val % 10
|
||||
. buf[ps_len] = '0' + r
|
||||
. ps_len++
|
||||
. ps_val = q
|
||||
. for i = ps_len-1 .. 0:
|
||||
. print buf[i]
|
||||
. print "\r\n"
|
||||
.-------------------------------------------
|
||||
printStdout COMP #0
|
||||
JEQ ps_zero
|
||||
|
||||
STA ps_val
|
||||
LDA #0
|
||||
STA ps_len
|
||||
LDS #10
|
||||
LDT #0x30 . '0'
|
||||
|
||||
ps_div LDA ps_val
|
||||
COMP #0
|
||||
JEQ ps_divdone
|
||||
|
||||
RMO A,B
|
||||
DIVR S,B . kvocient v B
|
||||
RMO B,X . X = kvocient
|
||||
|
||||
MULR S,B
|
||||
SUBR B,A . A = ostanek
|
||||
ADDR T,A . A = '0' + ostanek
|
||||
STA psdigit
|
||||
|
||||
LDA ps_len
|
||||
STA ps_idx
|
||||
LDA #psbuf
|
||||
ADD ps_idx
|
||||
STA ps_ptr
|
||||
LDA psdigit
|
||||
STCH @ps_ptr
|
||||
|
||||
LDA ps_len
|
||||
ADD #1
|
||||
STA ps_len
|
||||
|
||||
RMO X,A
|
||||
STA ps_val
|
||||
J ps_div
|
||||
|
||||
ps_divdone LDA ps_len
|
||||
SUB #1
|
||||
STA ps_idx
|
||||
|
||||
ps_print LDA ps_idx
|
||||
COMP #0
|
||||
JLT ps_end
|
||||
|
||||
LDA #psbuf
|
||||
ADD ps_idx
|
||||
STA ps_ptr
|
||||
LDCH @ps_ptr
|
||||
WD #1
|
||||
|
||||
LDA ps_idx
|
||||
SUB #1
|
||||
STA ps_idx
|
||||
J ps_print
|
||||
|
||||
ps_end LDA #0x0D . CR
|
||||
WD #1
|
||||
LDA #0x0A . LF
|
||||
WD #1
|
||||
RSUB
|
||||
|
||||
ps_zero LDA #0x30 . "0"
|
||||
WD #1
|
||||
LDA #0x0D
|
||||
WD #1
|
||||
LDA #0x0A
|
||||
WD #1
|
||||
RSUB
|
||||
|
||||
.data
|
||||
. rekurzija faktoriala
|
||||
sp WORD 0 . stack pointer
|
||||
n WORD 0
|
||||
acc WORD 0 . akumulator za faktorial
|
||||
stackL RESB 60
|
||||
|
||||
. printStdout
|
||||
ps_val WORD 0
|
||||
ps_len WORD 0
|
||||
ps_idx WORD 0
|
||||
psdigit WORD 0
|
||||
ps_ptr WORD 0
|
||||
psbuf RESB 12
|
||||
|
||||
END prog
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
.code
|
||||
|
||||
stack START 0
|
||||
|
||||
LDA #9
|
||||
STA @stackptr
|
||||
JSUB stackpush
|
||||
|
||||
JSUB stackpop
|
||||
LDA @stackptr
|
||||
|
||||
halt J halt
|
||||
|
||||
|
||||
stackinit
|
||||
STA stacktmp
|
||||
|
||||
LDA #STACK
|
||||
STA stackptr
|
||||
|
||||
LDA stacktmp
|
||||
RSUB
|
||||
|
||||
|
||||
stackpush
|
||||
STA stacktmp
|
||||
|
||||
LDA stackptr
|
||||
ADD #3
|
||||
STA stackptr
|
||||
|
||||
LDA stacktmp
|
||||
RSUB
|
||||
|
||||
|
||||
stackpop
|
||||
STA stacktmp
|
||||
|
||||
LDA stackptr
|
||||
SUB #3
|
||||
STA stackptr
|
||||
|
||||
LDA stacktmp
|
||||
RSUB
|
||||
|
||||
|
||||
.data
|
||||
|
||||
stackptr WORD 0
|
||||
|
||||
stacktmp WORD 0
|
||||
|
||||
STACKSIZE EQU 50
|
||||
STACK RESW STACKSIZE
|
||||
|
||||
END stack
|
||||
95
ass2/simulator_SIC_XE/.gitignore
vendored
95
ass2/simulator_SIC_XE/.gitignore
vendored
|
|
@ -1,95 +0,0 @@
|
|||
# Build directories
|
||||
build/
|
||||
target/
|
||||
|
||||
# CMake generated files
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
CMakeScripts/
|
||||
cmake_install.cmake
|
||||
*.cmake
|
||||
!CMakeLists.txt
|
||||
|
||||
# Compiled Object files
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# VS Code
|
||||
.vscode/
|
||||
*.code-workspace
|
||||
|
||||
# CLion
|
||||
.idea/
|
||||
cmake-build-*/
|
||||
|
||||
# Xcode
|
||||
*.pbxuser
|
||||
*.mode1v3
|
||||
*.mode2v3
|
||||
*.perspectivev3
|
||||
*.xcuserstate
|
||||
project.xcworkspace/
|
||||
xcuserdata/
|
||||
|
||||
# Qt Creator
|
||||
*.pro.user
|
||||
*.pro.user.*
|
||||
*.qbs.user
|
||||
*.qbs.user.*
|
||||
*.moc
|
||||
*.moc.cpp
|
||||
*.qm
|
||||
*.prl
|
||||
CMakeLists.txt.user
|
||||
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# Temporary files
|
||||
*~
|
||||
*.swp
|
||||
*.swo
|
||||
*.tmp
|
||||
*.bak
|
||||
|
||||
# Log files
|
||||
*.log
|
||||
|
||||
# Core dumps
|
||||
core
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
project(simulator_SIC_XE VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Put all build outputs under target/bin
|
||||
set(OUTPUT_DIR ${CMAKE_SOURCE_DIR}/target/bin)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR})
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIR})
|
||||
|
||||
# Collect all .cpp sources under src/
|
||||
file(GLOB_RECURSE SOURCES "${PROJECT_SOURCE_DIR}/src/*.cpp")
|
||||
|
||||
if(NOT SOURCES)
|
||||
message(WARNING "No source files found in ${PROJECT_SOURCE_DIR}/src — the build will create an empty library")
|
||||
endif()
|
||||
|
||||
# Build a static library from all sources
|
||||
add_library(simulator_lib STATIC ${SOURCES})
|
||||
target_include_directories(simulator_lib PUBLIC ${PROJECT_SOURCE_DIR}/include)
|
||||
set_target_properties(simulator_lib PROPERTIES OUTPUT_NAME "simulator")
|
||||
|
||||
# If a main.cpp exists, create an executable that links the library.
|
||||
if(EXISTS "${PROJECT_SOURCE_DIR}/src/main.cpp")
|
||||
add_executable(simulator_exec "${PROJECT_SOURCE_DIR}/src/main.cpp")
|
||||
target_link_libraries(simulator_exec PRIVATE simulator_lib)
|
||||
endif()
|
||||
|
||||
|
||||
if(TARGET simulator_exec)
|
||||
add_custom_target(run
|
||||
DEPENDS simulator_exec
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Running simulator_exec..."
|
||||
COMMAND $<TARGET_FILE:simulator_exec>
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
COMMENT "Builds and runs simulator_exec"
|
||||
)
|
||||
endif()
|
||||
|
||||
message(STATUS "Project: ${PROJECT_NAME}")
|
||||
message(STATUS "Sources found: ${SOURCES}")
|
||||
message(STATUS "Output directory: ${OUTPUT_DIR}")
|
||||
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/gui/qt/CMakeLists.txt")
|
||||
add_subdirectory(gui/qt)
|
||||
endif()
|
||||
|
||||
# Copy resources directory (if present) to target/res so build output includes them
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/res")
|
||||
add_custom_target(copy_resources
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_SOURCE_DIR}/target/res
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/res ${CMAKE_SOURCE_DIR}/target/res
|
||||
COMMENT "Copying resources from res/ to target/res/"
|
||||
)
|
||||
if(TARGET simulator_exec)
|
||||
add_dependencies(simulator_exec copy_resources)
|
||||
endif()
|
||||
endif()
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
# Simple Makefile wrapper to configure, build and run the CMake project.
|
||||
# Usage:
|
||||
# make # configure + build with all cores
|
||||
# make build # configure + build with all cores
|
||||
# make all # clean + configure + build + run
|
||||
# make run # just run the executable (no build)
|
||||
# make clean # run CMake clean (or remove build files)
|
||||
# make distclean # remove build dir and generated targets
|
||||
|
||||
CMAKE ?= cmake
|
||||
BUILD_DIR := build
|
||||
CMAKE_BUILD_TYPE ?= Release
|
||||
TARGET := target/bin/simulator_exec
|
||||
GUI_TARGET := target/bin/simulator_qt
|
||||
NPROC := $(shell nproc)
|
||||
|
||||
.PHONY: all configure build run clean distclean
|
||||
|
||||
# Default target: just build
|
||||
default: build
|
||||
|
||||
# make all: clean, build, then run
|
||||
all: clean build run
|
||||
|
||||
configure:
|
||||
@echo "Configuring (build dir: $(BUILD_DIR), type: $(CMAKE_BUILD_TYPE))"
|
||||
$(CMAKE) -S . -B $(BUILD_DIR) -DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE)
|
||||
|
||||
build: configure
|
||||
@echo "Building with $(NPROC) cores..."
|
||||
$(CMAKE) --build $(BUILD_DIR) -j$(NPROC)
|
||||
|
||||
# make run: just launch the executable (no build)
|
||||
run:
|
||||
@echo "Running primary target..."
|
||||
# Prefer GUI if available, otherwise fall back to console executable
|
||||
@if [ -x "$(GUI_TARGET)" ]; then \
|
||||
echo "Launching GUI: $(GUI_TARGET)"; \
|
||||
./$(GUI_TARGET); \
|
||||
elif [ -x "$(TARGET)" ]; then \
|
||||
./$(TARGET); \
|
||||
else \
|
||||
echo "No runnable target found (tried $(GUI_TARGET) and $(TARGET))."; exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: run-gui
|
||||
run-gui: build
|
||||
@echo "Running GUI target ($(GUI_TARGET))"
|
||||
@if [ -x "$(GUI_TARGET)" ]; then \
|
||||
echo "Starting GUI..."; ./$(GUI_TARGET) -platform xcb; \
|
||||
else \
|
||||
echo "GUI executable not found: $(GUI_TARGET)"; exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: build-gui
|
||||
build-gui: configure
|
||||
@echo "Building GUI (and core)..."
|
||||
$(CMAKE) --build $(BUILD_DIR) -j$(shell nproc) --target simulator_qt || true
|
||||
|
||||
clean:
|
||||
@echo "Cleaning build (CMake clean)..."
|
||||
-$(CMAKE) --build $(BUILD_DIR) --target clean || true
|
||||
@echo "Removing target directory..."
|
||||
-rm -rf target/
|
||||
|
||||
distclean:
|
||||
@echo "Removing build artifacts and generated files..."
|
||||
-rm -rf $(BUILD_DIR) CMakeFiles CMakeCache.txt cmake_install.cmake target/
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
# SIC/XE Simulator
|
||||
|
||||
A complete SIC/XE architecture simulator with instruction execution, device I/O, and memory management.
|
||||
|
||||
## Quick Start
|
||||
|
||||
The easiest way to build and run the simulator:
|
||||
|
||||
```bash
|
||||
make run
|
||||
```
|
||||
|
||||
This single command will:
|
||||
- Configure the build system (if needed)
|
||||
- Compile all source files
|
||||
- Link the executable
|
||||
- Run the simulator
|
||||
|
||||
## Build Commands
|
||||
|
||||
| Command | Description |
|
||||
|--------------|----------------------------------------------------|
|
||||
| `make` | Build the project |
|
||||
| `make build` | Build the project |
|
||||
| `make run` | Build run the simulator |
|
||||
| `make clean` | Clean build artifacts |
|
||||
| `make run` | Clean build artifacts, build and run the simulator |
|
||||
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
simulator_SIC_XE/
|
||||
├── include/ # Header files (.h)
|
||||
├── src/ # Source files (.cpp)
|
||||
├── target/bin/ # Build output (executables, libraries)
|
||||
└── build/ # CMake build directory
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- **SIC/XE Architecture**: Complete register set (A, X, L, B, S, T, F, PC, SW)
|
||||
- **Instruction Execution**: Format 1, 2, and 3/4 instruction support
|
||||
- **Device I/O**: Input, output, and file device management
|
||||
- **Memory Management**: 24-bit address space with proper bounds checking
|
||||
|
||||
## Development
|
||||
|
||||
The project uses CMake with a convenient Makefile wrapper. All build artifacts are placed in `target/bin/` for easy access.
|
||||
|
||||
For manual CMake usage:
|
||||
```bash
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build -j
|
||||
```
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.16)
|
||||
project(simulator_qt LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
# Prefer Qt6, fall back to Qt5
|
||||
find_package(Qt6 COMPONENTS Widgets QUIET)
|
||||
if(NOT Qt6_FOUND)
|
||||
# Try explicitly the system Qt6 cmake prefix on Debian/Ubuntu
|
||||
find_package(Qt6 COMPONENTS Widgets QUIET PATHS /usr/lib/x86_64-linux-gnu)
|
||||
endif()
|
||||
|
||||
if(NOT Qt6_FOUND)
|
||||
# Fallback: try Qt5 if Qt6 is unavailable
|
||||
find_package(Qt5 COMPONENTS Widgets QUIET)
|
||||
endif()
|
||||
|
||||
if(Qt6_FOUND)
|
||||
set(QT_LIB Qt6::Widgets)
|
||||
elseif(Qt5_FOUND)
|
||||
set(QT_LIB Qt5::Widgets)
|
||||
else()
|
||||
message(FATAL_ERROR "Qt6 or Qt5 not found. Install Qt development packages or set CMAKE_PREFIX_PATH to your Qt installation.")
|
||||
endif()
|
||||
|
||||
set(GUI_SRCS
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
MachineController.cpp
|
||||
)
|
||||
|
||||
set(GUI_HDRS
|
||||
mainwindow.h
|
||||
MachineController.h
|
||||
)
|
||||
|
||||
add_executable(simulator_qt ${GUI_SRCS} ${GUI_HDRS})
|
||||
|
||||
# Allow the generated UI headers (from AUTOUIC) to be found in the build dir
|
||||
# and also include the top-level include folder (works when added with add_subdirectory)
|
||||
target_include_directories(simulator_qt PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/include)
|
||||
|
||||
# Link to core library target (must be defined by top-level CMake)
|
||||
target_link_libraries(simulator_qt PRIVATE simulator_lib ${QT_LIB})
|
||||
|
||||
# Place runtime binary under repo/target/bin to match project layout
|
||||
set_target_properties(simulator_qt PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/target/bin
|
||||
)
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
#include "MachineController.h"
|
||||
#include "../../include/machine.h"
|
||||
#include <chrono>
|
||||
#include <QDebug>
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
MachineController::MachineController(std::shared_ptr<Machine> machine, QObject *parent)
|
||||
: QObject(parent), m_machine(std::move(machine))
|
||||
{
|
||||
if (!m_machine) {
|
||||
m_machine = std::make_shared<Machine>();
|
||||
}
|
||||
m_lastUpdateTime = steady_clock::now();
|
||||
}
|
||||
|
||||
MachineController::~MachineController() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void MachineController::start() {
|
||||
if (m_running.exchange(true)) return;
|
||||
m_thread = std::thread([this]{ runLoop(); });
|
||||
}
|
||||
|
||||
void MachineController::stop() {
|
||||
if (!m_running.exchange(false)) return;
|
||||
if (m_thread.joinable()) m_thread.join();
|
||||
}
|
||||
|
||||
void MachineController::step() {
|
||||
try {
|
||||
if (m_machine) {
|
||||
m_machine->execute();
|
||||
m_machine->tick();
|
||||
emit tick();
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
emit error(QString::fromStdString(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void MachineController::runLoop() {
|
||||
const auto minUpdateInterval = milliseconds(16);
|
||||
|
||||
while (m_running.load()) {
|
||||
try {
|
||||
if (m_machine) {
|
||||
m_machine->execute();
|
||||
m_machine->tick();
|
||||
m_ticksSinceLastUpdate++;
|
||||
|
||||
// Throttle GUI updates to 60 Hz
|
||||
auto now = steady_clock::now();
|
||||
auto elapsed = duration_cast<milliseconds>(now - m_lastUpdateTime);
|
||||
|
||||
if (elapsed >= minUpdateInterval) {
|
||||
emit tick();
|
||||
m_lastUpdateTime = now;
|
||||
m_ticksSinceLastUpdate = 0;
|
||||
}
|
||||
|
||||
if (m_machine->isStopped()) {
|
||||
emit tick();
|
||||
m_running.store(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
emit error(QString::fromStdString(e.what()));
|
||||
// Stop on fatal error
|
||||
m_running.store(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
#ifndef MACHINECONTROLLER_H
|
||||
#define MACHINECONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
|
||||
class Machine;
|
||||
|
||||
class MachineController : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MachineController(std::shared_ptr<Machine> machine = nullptr, QObject *parent = nullptr);
|
||||
~MachineController() override;
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void step();
|
||||
|
||||
signals:
|
||||
void tick();
|
||||
void error(const QString &msg);
|
||||
|
||||
private:
|
||||
void runLoop();
|
||||
std::atomic<bool> m_running{false};
|
||||
std::thread m_thread;
|
||||
std::shared_ptr<Machine> m_machine;
|
||||
std::atomic<int> m_ticksSinceLastUpdate{0};
|
||||
std::chrono::steady_clock::time_point m_lastUpdateTime;
|
||||
};
|
||||
|
||||
#endif // MACHINECONTROLLER_H
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#include <QApplication>
|
||||
#include "mainwindow.h"
|
||||
#include "../../include/opcode.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
loadInstructionSet();
|
||||
|
||||
qputenv("QT_QPA_PLATFORM", "xcb");
|
||||
|
||||
QApplication app(argc, argv);
|
||||
MainWindow w;
|
||||
w.show();
|
||||
return app.exec();
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,85 +0,0 @@
|
|||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <memory>
|
||||
|
||||
class MachineController;
|
||||
class Machine;
|
||||
class QLineEdit;
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
std::shared_ptr<Machine> machine() const { return m_machine; }
|
||||
MachineController* controller() const { return m_controller.get(); }
|
||||
|
||||
void startExecution();
|
||||
void stopExecution();
|
||||
void stepExecution();
|
||||
|
||||
void setTestRegisterValues();
|
||||
|
||||
private slots:
|
||||
void updateRegisterDisplays();
|
||||
void updateMemoryDisplay();
|
||||
void updateDisassemblyDisplay();
|
||||
void onRegisterFieldChanged();
|
||||
void onMemoryInc256();
|
||||
void onMemoryInc4096();
|
||||
void onMemoryInc65536();
|
||||
void onMemoryDec256();
|
||||
void onMemoryDec4096();
|
||||
void onMemoryDec65536();
|
||||
void onMemoryGoToStart();
|
||||
void onMemoryGoToEnd();
|
||||
void onDisassemblyInc();
|
||||
void onDisassemblyInc16();
|
||||
void onDisassemblyInc256();
|
||||
void onDisassemblyDec();
|
||||
void onDisassemblyDec16();
|
||||
void onDisassemblyDec256();
|
||||
void onDisassemblyGoToStart();
|
||||
void onDisassemblyGoToEnd();
|
||||
void loadObjectFile();
|
||||
void showAboutDialog();
|
||||
void showFrequencyDialog();
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
std::shared_ptr<Machine> m_machine;
|
||||
std::unique_ptr<MachineController> m_controller;
|
||||
int m_memoryOffset = 0;
|
||||
int m_disassemblyOffset = 0;
|
||||
|
||||
void connectRegisterFields();
|
||||
void updateSingleRegisterDisplay(const QString& fieldName, int value);
|
||||
void updateAllFormatsForRegister(const QString& regPrefix, int value);
|
||||
void updateFloatRegisterFormats(const QString& regPrefix, double value);
|
||||
void handleFloatRegisterFieldChanged(QLineEdit* field, const QString& objectName);
|
||||
void loadDemoProgram();
|
||||
void setupMemoryDisplay();
|
||||
void setupDisassemblyDisplay();
|
||||
|
||||
struct DisassembledInstruction {
|
||||
int address;
|
||||
int size;
|
||||
QString mnemonic;
|
||||
QString operand;
|
||||
int effectiveAddr;
|
||||
bool isImmediate;
|
||||
bool isIndirect;
|
||||
};
|
||||
DisassembledInstruction disassembleAt(int address);
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
|
|
@ -1,930 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1172</width>
|
||||
<height>649</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>0</y>
|
||||
<width>431</width>
|
||||
<height>601</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>80</y>
|
||||
<width>431</width>
|
||||
<height>321</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Register values</string>
|
||||
</property>
|
||||
<widget class="QLineEdit" name="regA_bin_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>70</x>
|
||||
<y>40</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>40</y>
|
||||
<width>57</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reg A</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regA_hex_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>190</x>
|
||||
<y>40</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regA_dec_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>310</x>
|
||||
<y>40</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>110</x>
|
||||
<y>20</y>
|
||||
<width>57</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Bin</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>230</x>
|
||||
<y>20</y>
|
||||
<width>57</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Hex</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>350</x>
|
||||
<y>20</y>
|
||||
<width>57</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Dec</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regB_dec_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>310</x>
|
||||
<y>70</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>70</y>
|
||||
<width>57</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reg B</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regB_hex_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>190</x>
|
||||
<y>70</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regB_bin_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>70</x>
|
||||
<y>70</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regX_dec_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>310</x>
|
||||
<y>100</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>100</y>
|
||||
<width>57</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reg X</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regX_hex_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>190</x>
|
||||
<y>100</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regX_bin_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>70</x>
|
||||
<y>100</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regS_dec_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>310</x>
|
||||
<y>130</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>130</y>
|
||||
<width>57</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reg S</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regS_hex_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>190</x>
|
||||
<y>130</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regS_bin_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>70</x>
|
||||
<y>130</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regT_dec_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>310</x>
|
||||
<y>160</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>160</y>
|
||||
<width>57</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reg T</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regT_hex_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>190</x>
|
||||
<y>160</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regT_bin_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>70</x>
|
||||
<y>160</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regL_hex_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>190</x>
|
||||
<y>190</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regL_bin_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>70</x>
|
||||
<y>190</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>190</y>
|
||||
<width>57</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reg L</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regL_dec_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>310</x>
|
||||
<y>190</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>220</y>
|
||||
<width>57</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reg PC</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regPC_dec_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>310</x>
|
||||
<y>220</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regPC_bin_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>70</x>
|
||||
<y>220</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regPC_hex_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>190</x>
|
||||
<y>220</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>249</y>
|
||||
<width>57</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reg SW</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regSW_dec_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>310</x>
|
||||
<y>249</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regSW_bin_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>70</x>
|
||||
<y>249</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regSW_hex_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>190</x>
|
||||
<y>249</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>280</y>
|
||||
<width>57</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reg L</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regF_dec_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>310</x>
|
||||
<y>280</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regF_bin_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>70</x>
|
||||
<y>280</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="regF_hex_field">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>190</x>
|
||||
<y>280</y>
|
||||
<width>113</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>431</width>
|
||||
<height>81</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Control</string>
|
||||
</property>
|
||||
<widget class="QPushButton" name="StartBtn">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>40</y>
|
||||
<width>80</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="StopBtn">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>170</x>
|
||||
<y>40</y>
|
||||
<width>80</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stop</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="StepBtn">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>310</x>
|
||||
<y>40</y>
|
||||
<width>80</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Step</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="widget_2" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>450</x>
|
||||
<y>0</y>
|
||||
<width>721</width>
|
||||
<height>601</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QGroupBox" name="MemorygroupBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>711</width>
|
||||
<height>291</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Memory</string>
|
||||
</property>
|
||||
<widget class="QScrollArea" name="MemoryScrollArea">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>20</y>
|
||||
<width>711</width>
|
||||
<height>221</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>709</width>
|
||||
<height>219</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="MemoryInc4096Btn">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>450</x>
|
||||
<y>250</y>
|
||||
<width>71</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>>></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="MemoryInc256Btn">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>370</x>
|
||||
<y>250</y>
|
||||
<width>71</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="MemoryInc65536Btn">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>530</x>
|
||||
<y>250</y>
|
||||
<width>71</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>>>></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="MemoryDec256Btn">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>290</x>
|
||||
<y>250</y>
|
||||
<width>71</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="MemoryDec4096Btn">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>210</x>
|
||||
<y>250</y>
|
||||
<width>71</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><<</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="MemoryDec65536Btn">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>130</x>
|
||||
<y>250</y>
|
||||
<width>71</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><<<</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="MemoryGoToStart_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>50</x>
|
||||
<y>250</y>
|
||||
<width>71</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>O</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="MemoryGoToEnd">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>610</x>
|
||||
<y>250</y>
|
||||
<width>71</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>|</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QGroupBox" name="MemorygroupBox_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>300</y>
|
||||
<width>711</width>
|
||||
<height>301</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Disasembly</string>
|
||||
</property>
|
||||
<widget class="QScrollArea" name="DisasemblyScrollArea">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>20</y>
|
||||
<width>711</width>
|
||||
<height>221</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>709</width>
|
||||
<height>219</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="DisasmInc4096Btn">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>440</x>
|
||||
<y>250</y>
|
||||
<width>71</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>>></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="DisasmInc256Btn">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>360</x>
|
||||
<y>250</y>
|
||||
<width>71</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="DisasmInc65536Btn">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>520</x>
|
||||
<y>250</y>
|
||||
<width>71</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>>>></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="DisasmDec256Btn">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>280</x>
|
||||
<y>250</y>
|
||||
<width>71</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="DisasmDec4096Btn">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>200</x>
|
||||
<y>250</y>
|
||||
<width>71</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><<</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="DisasmDec65536Btn">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>120</x>
|
||||
<y>250</y>
|
||||
<width>71</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><<<</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="DisasmGoToStart">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>40</x>
|
||||
<y>250</y>
|
||||
<width>71</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>O</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="DisasmGoToEnd">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>600</x>
|
||||
<y>250</y>
|
||||
<width>71</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>|</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<widget class="QToolBar" name="toolBar">
|
||||
<property name="windowTitle">
|
||||
<string>toolBar</string>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1172</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
<property name="title">
|
||||
<string>File</string>
|
||||
</property>
|
||||
<addaction name="actionLoad_Object_File"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuMachine">
|
||||
<property name="title">
|
||||
<string>Machine</string>
|
||||
</property>
|
||||
<addaction name="actionFrequency"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuHelp">
|
||||
<property name="title">
|
||||
<string>Help</string>
|
||||
</property>
|
||||
<addaction name="actionAbout"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuMachine"/>
|
||||
<addaction name="menuHelp"/>
|
||||
</widget>
|
||||
<action name="actionLoad_Object_File">
|
||||
<property name="text">
|
||||
<string>Load Object File</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFrequency">
|
||||
<property name="text">
|
||||
<string>Frequency</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAbout">
|
||||
<property name="text">
|
||||
<string>About</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
#ifndef CONSTANTS_H
|
||||
#define CONSTANTS_H
|
||||
|
||||
#include <string>
|
||||
// ==============================
|
||||
// SIC/XE Architecture Constants
|
||||
// ==============================
|
||||
|
||||
// Memory and system constants
|
||||
constexpr int MEMORY_SIZE = 1 << 20; // 1 MB memory
|
||||
constexpr int NUM_DEVICES = 256;
|
||||
constexpr int WORD_SIZE = 24;
|
||||
constexpr int WORD_MASK = 0xFFFFFF;
|
||||
|
||||
// SIC/XE floating point constants
|
||||
constexpr int SICF_BITS = 48;
|
||||
constexpr int SICF_FRAC_BITS = 40;
|
||||
constexpr int SICF_EXP_BITS = 7;
|
||||
constexpr int SICF_EXP_BIAS = 64;
|
||||
constexpr unsigned long long SICF_FRAC_MASK = (1ULL << SICF_FRAC_BITS) - 1;
|
||||
|
||||
// SW register condition codes
|
||||
constexpr int CC_LT = 0x0; // 00
|
||||
constexpr int CC_EQ = 0x1; // 01
|
||||
constexpr int CC_GT = 0x2; // 10
|
||||
constexpr int CC_MASK = 0x3; // mask for 2 bits
|
||||
|
||||
// Instruction format bit masks
|
||||
constexpr int TYPE3_4_SIC_MASK = 0xFC;
|
||||
constexpr int NI_MASK = 0x03; // mask for n and i bits
|
||||
constexpr int NI_SIC = 0x0;
|
||||
|
||||
constexpr int BP_BASE_REL_MASK = 0b10;
|
||||
constexpr int BP_PC_REL_MASK = 0b01;
|
||||
constexpr int BP_DIRECT_MASK = 0b00;
|
||||
|
||||
constexpr int BIT_E_MASK = 0x10; // mask for e bit in F4 and F3 instructions
|
||||
|
||||
//SIC/XE/XE
|
||||
constexpr bool USE_EXTENDED_MODE = true;
|
||||
constexpr int VECTOR_REG_SIZE = 4;
|
||||
|
||||
/* if structure is
|
||||
/target/
|
||||
|-> bin/simulator_exec
|
||||
|-> res/
|
||||
*/
|
||||
// When running from project root (./target/bin/simulator_exec), resources are in ./target/res/
|
||||
constexpr char PATH_RESOURCES[] = "./target/res/";
|
||||
constexpr bool FILE_CONTAINS_WHITE_SPACES = true;
|
||||
|
||||
#endif // CONSTANTS_H
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
#ifndef DEVICE_H
|
||||
#define DEVICE_H
|
||||
|
||||
|
||||
class Device {
|
||||
public:
|
||||
Device();
|
||||
|
||||
bool test();
|
||||
virtual unsigned char read();
|
||||
virtual void write(unsigned char value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // DEVICE_H
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
#ifndef FILE_DEVICE_H
|
||||
#define FILE_DEVICE_H
|
||||
|
||||
#include "device.h"
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
class FileDevice : public Device {
|
||||
public:
|
||||
explicit FileDevice(const std::string &filename);
|
||||
~FileDevice();
|
||||
unsigned char read() override;
|
||||
void write(unsigned char value) override;
|
||||
|
||||
private:
|
||||
void ensureFileOpen();
|
||||
std::fstream fileStream;
|
||||
std::string filename;
|
||||
bool fileCreated;
|
||||
std::streampos readPosition;
|
||||
};
|
||||
|
||||
#endif // FILE_DEVICE_H
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
#ifndef FILE_READER_H
|
||||
#define FILE_READER_H
|
||||
|
||||
#include "reader.h"
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
class FileReader : public Reader {
|
||||
public:
|
||||
explicit FileReader(const std::string &path, std::ios::openmode m = std::ios::binary);
|
||||
~FileReader() override;
|
||||
|
||||
int readByte() override;
|
||||
|
||||
bool readBytes(uint8_t* buf, size_t len) override;
|
||||
std::string readString(size_t len) override;
|
||||
std::string readLine() override;
|
||||
|
||||
bool good() const;
|
||||
|
||||
private:
|
||||
std::ifstream in;
|
||||
};
|
||||
|
||||
#endif // FILE_READER_H
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
#ifndef INPUT_DEVICE_H
|
||||
#define INPUT_DEVICE_H
|
||||
|
||||
#include "device.h"
|
||||
#include <istream>
|
||||
|
||||
class InputDevice : public Device {
|
||||
public:
|
||||
explicit InputDevice(std::istream &in);
|
||||
~InputDevice();
|
||||
|
||||
unsigned char read();
|
||||
|
||||
private:
|
||||
std::istream &inStream;
|
||||
};
|
||||
|
||||
#endif // INPUT_DEVICE_H
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
#ifndef INSTRUCTIONS_H
|
||||
#define INSTRUCTIONS_H
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
class Machine; // forward declaration
|
||||
|
||||
// Type 1 instruction handlers
|
||||
void fix_handler(Machine& m);
|
||||
void float_handler(Machine& m);
|
||||
void hio_handler(Machine& m);
|
||||
void norm_handler(Machine& m);
|
||||
void sio_handler(Machine& m);
|
||||
void tio_handler(Machine& m);
|
||||
void nop_handler(Machine& m);
|
||||
|
||||
/* IDEJE ZA SIC_XE_XE :)*/
|
||||
// void nop(Machine& m);
|
||||
|
||||
// Type 2 instruction handlers
|
||||
void addr_handler(Machine& m, int r1, int r2);
|
||||
void clear_handler(Machine& m, int r, int unused);
|
||||
void compr_handler(Machine& m, int r1, int r2);
|
||||
void divr_handler(Machine& m, int r1, int r2);
|
||||
void mulr_handler(Machine& m, int r1, int r2);
|
||||
void rmo_handler(Machine& m, int r1, int r2);
|
||||
void shiftl_handler(Machine& m, int r1, int n);
|
||||
void shiftr_handler(Machine& m, int r1, int n);
|
||||
void subr_handler(Machine& m, int r1, int r2);
|
||||
void svc_handler(Machine& m, int n, int unused);
|
||||
void tixr_handler(Machine& m, int r1, int unused);
|
||||
|
||||
|
||||
// Type 3/4 instruction handlers
|
||||
void add_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void addf_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void and_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void comp_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void compf_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void div_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void divf_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void j_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void jeq_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void jgt_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void jlt_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void jsub_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void lda_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void ldb_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void ldch_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void ldf_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void ldl_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void lds_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void ldt_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void ldx_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void lps_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void mul_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void mulf_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void or_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void rd_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void rsub_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void ssk_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void sta_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void stb_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void stch_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void stf_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void sti_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void stl_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void sts_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void stsw_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void stt_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void stx_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void sub_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void subf_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void td_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void tix_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void wd_handler(Machine& m, int ea, AddressingMode mode);
|
||||
|
||||
|
||||
// SIC/XE/XE Extended instruction handlers
|
||||
void xexe_handler(Machine& m);
|
||||
void halt_handler(Machine& m);
|
||||
void nop_handler(Machine& m);
|
||||
|
||||
void vaddr_handler(Machine& m, int r1, int r2);
|
||||
void vsubr_handler(Machine& m, int r1, int r2);
|
||||
void vmulr_handler(Machine& m, int r1, int r2);
|
||||
void vdivr_handler(Machine& m, int r1, int r2);
|
||||
|
||||
void vadd_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void vsub_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void vmul_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void vdiv_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void stva_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void stvs_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void stvt_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void ldva_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void ldvs_handler(Machine& m, int ea, AddressingMode mode);
|
||||
void ldvt_handler(Machine& m, int ea, AddressingMode mode);
|
||||
|
||||
|
||||
#endif // INSTRUCTIONS_H
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
#ifndef LOADER_H
|
||||
#define LOADER_H
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "file_reader.h"
|
||||
#include <stdexcept>
|
||||
|
||||
class Machine;
|
||||
|
||||
using std::shared_ptr;
|
||||
using std::string;
|
||||
|
||||
|
||||
class Loader {
|
||||
public:
|
||||
Loader( shared_ptr<Machine> machine, string filename) : _machine(machine), _filename(filename) {
|
||||
_file_reader = std::make_shared<FileReader>(filename, std::ios::in);
|
||||
if (!_file_reader->good()) {
|
||||
throw std::runtime_error("Loader: failed to open file: " + filename);
|
||||
}
|
||||
}
|
||||
~Loader();
|
||||
|
||||
|
||||
enum class RecordType {
|
||||
HEADER,
|
||||
TEXT,
|
||||
END,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
struct HeaderMetadata {
|
||||
string program_name;
|
||||
int start_address;
|
||||
int length;
|
||||
};
|
||||
struct TextRecord {
|
||||
int start_address;
|
||||
std::vector<uint8_t> data;
|
||||
};
|
||||
struct EndRecord {
|
||||
int execution_start_address;
|
||||
};
|
||||
|
||||
void load();
|
||||
|
||||
private :
|
||||
|
||||
static RecordType parseRecordType(char c);
|
||||
|
||||
|
||||
shared_ptr<Machine> _machine;
|
||||
string _filename;
|
||||
shared_ptr<FileReader> _file_reader;
|
||||
HeaderMetadata readHeader();
|
||||
TextRecord readTextRecord();
|
||||
EndRecord readEndRecord();
|
||||
bool load_into_memory(int start_address, const std::vector<uint8_t>& data);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // LOADER_H
|
||||
|
|
@ -1,149 +0,0 @@
|
|||
#ifndef MACHINE_H
|
||||
#define MACHINE_H
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
#include "constants.h"
|
||||
#include "device.h"
|
||||
#include "input_device.h"
|
||||
#include "output_device.h"
|
||||
#include "file_device.h"
|
||||
#include "opcode.h"
|
||||
#include "utils.h"
|
||||
|
||||
using std::string;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
using std::cout;
|
||||
|
||||
|
||||
class Machine {
|
||||
public:
|
||||
Machine();
|
||||
Machine(int speedHz) : Machine() { this->speedHz = speedHz; _instructionsTable = instructions; }
|
||||
~Machine();
|
||||
|
||||
int getA() const { return A; }
|
||||
void setA(int value) { A = toSIC24(value); }
|
||||
|
||||
int getB() const { return B; }
|
||||
void setB(int value) { B = toSIC24(value); }
|
||||
|
||||
int getX() const { return X; }
|
||||
void setX(int value) { X = toSIC24(value); }
|
||||
|
||||
int getL() const { return L; }
|
||||
void setL(int value) { L = toSIC24(value); }
|
||||
|
||||
int getS() const { return S; }
|
||||
void setS(int value) { S = toSIC24(value); }
|
||||
|
||||
int getT() const { return T; }
|
||||
void setT(int value) { T = toSIC24(value); }
|
||||
|
||||
// PC is an address → don't mask to 24 unless you want 24-bit addressing
|
||||
int getPC() const { return PC; }
|
||||
void setPC(int value) { PC = value; }
|
||||
|
||||
// status word: keep as-is
|
||||
int getSW() const { return SW; }
|
||||
void setSW(int value) { SW = value; }
|
||||
|
||||
double getF() const { return F; }
|
||||
void setF(double value) { F = value; }
|
||||
|
||||
int getReg(int regNum) const;
|
||||
void setReg(int regNum, int value);
|
||||
|
||||
// Memory access methods
|
||||
int getByte(int address);
|
||||
void setByte(int address, int value);
|
||||
|
||||
int getWord(int address);
|
||||
void setWord(int address, int value);
|
||||
|
||||
double getFloat(int address);
|
||||
void setFloat(int address, double value);
|
||||
|
||||
|
||||
// Device access methods
|
||||
Device& getDevice(int num);
|
||||
void setDevice(int num, std::shared_ptr<Device> device);
|
||||
// Set a file device at index `num` using the provided filename.
|
||||
void setFileDevice(int num, const std::string &filename);
|
||||
|
||||
// Fetch and execute instructions
|
||||
int fetch();
|
||||
void execute();
|
||||
|
||||
// Execution and speed control
|
||||
int getSpeed() const;
|
||||
void setSpeed(int Hz);
|
||||
void start();
|
||||
void stop();
|
||||
void tick();
|
||||
void halt();
|
||||
bool isStopped() const { return _stopped; }
|
||||
void reset();
|
||||
|
||||
// error handling methods
|
||||
void notImplemented(string mnemonic);
|
||||
void invalidOpcode(int opcode);
|
||||
void invalidAddressing();
|
||||
void divisionByZero(int opcode);
|
||||
void undefinedHandler(int opcode);
|
||||
|
||||
bool getExtendedMode() const { return _exex_mode; }
|
||||
void enableExtendedMode();
|
||||
void disableExtendedMode();
|
||||
|
||||
|
||||
int* getVectorRegister(int regNum);
|
||||
void setVectorRegister(int regNum, const int* values);
|
||||
|
||||
const int* getVA() const { return VA; }
|
||||
const int* getVS() const { return VS; }
|
||||
const int* getVT() const { return VT; }
|
||||
void setVA(const int* values);
|
||||
void setVS(const int* values);
|
||||
void setVT(const int* values);
|
||||
|
||||
|
||||
private:
|
||||
// registers
|
||||
int A, B, X, L, S, T, PC, SW;
|
||||
double F;
|
||||
|
||||
// memory
|
||||
unsigned char memory[MEMORY_SIZE];
|
||||
|
||||
// devices
|
||||
std::vector<std::shared_ptr<Device>> devices;
|
||||
// fallback device returned when device slot is empty/invalid
|
||||
Device fallbackDevice;
|
||||
|
||||
// Execution control
|
||||
std::atomic<bool> running{false};
|
||||
std::atomic<int> speedHz{10}; // Default 10 Hz
|
||||
|
||||
bool execF1(int opcode);
|
||||
bool execF2(int opcode, int operand);
|
||||
bool execSICF3F4(int opcode, int ni, int x, int b, int p, int e, int operand);
|
||||
|
||||
|
||||
// Extended mode
|
||||
bool _stopped{false};
|
||||
bool _exex_mode{false};
|
||||
InstructionInfo* _instructionsTable;
|
||||
int VA[VECTOR_REG_SIZE], VS[VECTOR_REG_SIZE], VT[VECTOR_REG_SIZE]; // vector operation registers
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // MACHINE_H
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
#ifndef OPCODE_H
|
||||
#define OPCODE_H
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
// ==============================
|
||||
// Opcode definitions (SIC/XE)
|
||||
// ==============================
|
||||
#define ADD 0x18
|
||||
#define ADDF 0x58
|
||||
#define ADDR 0x90
|
||||
#define AND 0x40
|
||||
#define CLEAR 0xB4
|
||||
#define COMP 0x28
|
||||
#define COMPF 0x88
|
||||
#define COMPR 0xA0
|
||||
#define DIV 0x24
|
||||
#define DIVF 0x64
|
||||
#define DIVR 0x9C
|
||||
#define FIX 0xC4
|
||||
#define FLOAT 0xC0
|
||||
#define HIO 0xF4
|
||||
#define J 0x3C
|
||||
#define JEQ 0x30
|
||||
#define JGT 0x34
|
||||
#define JLT 0x38
|
||||
#define JSUB 0x48
|
||||
#define LDA 0x00
|
||||
#define LDB 0x68
|
||||
#define LDCH 0x50
|
||||
#define LDF 0x70
|
||||
#define LDL 0x08
|
||||
#define LDS 0x6C
|
||||
#define LDT 0x74
|
||||
#define LDX 0x04
|
||||
#define LPS 0xD0
|
||||
#define MUL 0x20
|
||||
#define MULF 0x60
|
||||
#define MULR 0x98
|
||||
#define NORM 0xC8
|
||||
#define OR 0x44
|
||||
#define RD 0xD8
|
||||
#define RMO 0xAC
|
||||
#define RSUB 0x4C
|
||||
#define SHIFTL 0xA4
|
||||
#define SHIFTR 0xA8
|
||||
#define SIO 0xF0
|
||||
#define SSK 0xEC
|
||||
#define STA 0x0C
|
||||
#define STB 0x78
|
||||
#define STCH 0x54
|
||||
#define STF 0x80
|
||||
#define STI 0xD4
|
||||
#define STL 0x14
|
||||
#define STS 0x7C
|
||||
#define STSW 0xE8
|
||||
#define STT 0x84
|
||||
#define STX 0x10
|
||||
#define SUB 0x1C
|
||||
#define SUBF 0x5C
|
||||
#define SUBR 0x94
|
||||
#define SVC 0xB0
|
||||
#define TD 0xE0
|
||||
#define TIO 0xF8
|
||||
#define TIX 0x2C
|
||||
#define TIXR 0xB8
|
||||
#define WD 0xDC
|
||||
|
||||
// ==============================
|
||||
// Extended opcodes (SIC/XE/XE)
|
||||
// ==============================
|
||||
#define NOP 0xF1
|
||||
#define HALT 0xF2
|
||||
#define XEXE 0xEE // Enable extended mode
|
||||
#define VADD 0x18
|
||||
#define VADDR 0x90
|
||||
#define VSUB 0x1C
|
||||
#define VSUBR 0x94
|
||||
#define VMUL 0x20
|
||||
#define VMULR 0x98
|
||||
#define VDIV 0x24
|
||||
#define VDIVR 0x9C
|
||||
#define STVA 0x0C
|
||||
#define STVS 0x7C
|
||||
#define STVT 0x84
|
||||
#define LDVA 0x00
|
||||
#define LDVS 0x68
|
||||
#define LDVT 0x04
|
||||
|
||||
|
||||
|
||||
enum class InstructionType {
|
||||
TYPE1,
|
||||
TYPE2,
|
||||
TYPE3_4,
|
||||
INVALID
|
||||
};
|
||||
|
||||
class Machine; // forward
|
||||
|
||||
// Store raw function pointer (void*) to allow different handler signatures
|
||||
using RawHandler = void*;
|
||||
|
||||
struct InstructionInfo {
|
||||
const char* name;
|
||||
InstructionType type;
|
||||
RawHandler handler;
|
||||
};
|
||||
|
||||
extern InstructionInfo instructions[];
|
||||
extern InstructionInfo instructionsEXEX[];
|
||||
|
||||
// Initialize the instruction table
|
||||
void loadInstructionSet();
|
||||
|
||||
#endif // OPCODE_H
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
#ifndef OUTPUT_DEVICE_H
|
||||
#define OUTPUT_DEVICE_H
|
||||
|
||||
#include "device.h"
|
||||
#include <ostream>
|
||||
|
||||
class OutputDevice : public Device {
|
||||
public:
|
||||
explicit OutputDevice(std::ostream &out);
|
||||
~OutputDevice();
|
||||
|
||||
void write(unsigned char value) override;
|
||||
|
||||
private:
|
||||
std::ostream &outStream;
|
||||
};
|
||||
|
||||
#endif // OUTPUT_DEVICE_H
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
#ifndef READER_H
|
||||
#define READER_H
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
// Abstract Reader class: read bytes/strings from a source (file, string, etc.)
|
||||
class Reader {
|
||||
public:
|
||||
virtual ~Reader() = default;
|
||||
// return 0..255 on success, -1 on EOF/error
|
||||
virtual int readByte() = 0;
|
||||
// read exactly len bytes into buf; return true on success
|
||||
virtual bool readBytes(uint8_t* buf, size_t len) = 0;
|
||||
// read up to len bytes into a std::string; may return shorter string on EOF
|
||||
virtual std::string readString(size_t len) = 0;
|
||||
// read a line (up to newline), return empty string on EOF
|
||||
virtual std::string readLine() = 0;
|
||||
};
|
||||
|
||||
#endif // READER_H
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#ifndef STRING_READER_H
|
||||
#define STRING_READER_H
|
||||
|
||||
#include "reader.h"
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
class StringReader : public Reader {
|
||||
public:
|
||||
explicit StringReader(const std::string &s);
|
||||
~StringReader() override;
|
||||
|
||||
int readByte() override;
|
||||
bool readBytes(uint8_t* buf, size_t len) override;
|
||||
std::string readString(size_t len) override;
|
||||
std::string readLine() override;
|
||||
|
||||
private:
|
||||
std::istringstream in;
|
||||
};
|
||||
|
||||
#endif // STRING_READER_H
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
// ==============================
|
||||
// SIC/XE Utility Functions
|
||||
// ==============================
|
||||
|
||||
// Instruction bit extraction utilities
|
||||
inline int getXBit(int b2) {
|
||||
return (b2 & 0x80) ? 1 : 0;
|
||||
}
|
||||
|
||||
inline int getBPBits(int b2) {
|
||||
return (b2 >> 5) & 0x03;
|
||||
}
|
||||
|
||||
enum class AddressingMode {
|
||||
IMMEDIATE,
|
||||
INDIRECT,
|
||||
SIMPLE,
|
||||
SIC_DIRECT,
|
||||
INVALID
|
||||
};
|
||||
|
||||
// Get addressing mode from ni bits
|
||||
AddressingMode getAddressingMode(int ni);
|
||||
|
||||
|
||||
// convert to signed 24-bit integer
|
||||
inline int toSIC24(int value) {
|
||||
value &= 0xFFFFFF;
|
||||
if (value & 0x800000) {
|
||||
value -= 0x1000000;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
inline int setCC(int sw, int cc) {
|
||||
sw &= ~CC_MASK;
|
||||
sw |= (cc & CC_MASK);
|
||||
return sw;
|
||||
}
|
||||
|
||||
inline int sic_comp(int a, int b, int sw) {
|
||||
int sa = toSIC24(a);
|
||||
int sb = toSIC24(b);
|
||||
|
||||
int cc;
|
||||
if (sa < sb) {
|
||||
cc = CC_LT;
|
||||
} else if (sa == sb) {
|
||||
cc = CC_EQ;
|
||||
} else {
|
||||
cc = CC_GT;
|
||||
}
|
||||
|
||||
return setCC(sw, cc);
|
||||
}
|
||||
|
||||
inline int sic_comp(double a, double b, int sw) {
|
||||
int cc;
|
||||
if (a < b) {
|
||||
cc = CC_LT;
|
||||
} else if (a == b) {
|
||||
cc = CC_EQ;
|
||||
} else {
|
||||
cc = CC_GT;
|
||||
}
|
||||
|
||||
return setCC(sw, cc);
|
||||
}
|
||||
|
||||
|
||||
inline int getCC(int sw) {
|
||||
return sw & CC_MASK;
|
||||
}
|
||||
|
||||
inline double normaliseFloat(double value)
|
||||
{
|
||||
if (value == 0.0 )return 0.0;
|
||||
if (!std::isfinite(value)) return value;
|
||||
double mantissa = value;
|
||||
while (std::fabs(mantissa) >= 10.0) mantissa /= 10.0;
|
||||
while (std::fabs(mantissa) < 1.0) mantissa *= 10.0;
|
||||
return mantissa;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // UTILS_H
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
#include "device.h"
|
||||
|
||||
Device::Device()
|
||||
{
|
||||
}
|
||||
|
||||
bool Device::test()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char Device::read()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Device::write(unsigned char value)
|
||||
{
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
#include "file_device.h"
|
||||
#include <stdexcept>
|
||||
#include <fstream>
|
||||
|
||||
FileDevice::FileDevice(const std::string &filename)
|
||||
: filename(filename), fileCreated(false), readPosition(0)
|
||||
{
|
||||
}
|
||||
|
||||
FileDevice::~FileDevice()
|
||||
{
|
||||
if (fileStream.is_open()) {
|
||||
fileStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
void FileDevice::ensureFileOpen()
|
||||
{
|
||||
if (!fileStream.is_open()) {
|
||||
// Check if file exists
|
||||
std::ifstream checkFile(filename);
|
||||
bool fileExists = checkFile.good();
|
||||
checkFile.close();
|
||||
|
||||
if (fileExists) {
|
||||
fileStream.open(filename, std::ios::in | std::ios::out | std::ios::ate);
|
||||
fileCreated = true;
|
||||
} else {
|
||||
// Create new file
|
||||
std::ofstream create(filename);
|
||||
if (!create) {
|
||||
throw std::runtime_error("Failed to create file: " + filename);
|
||||
}
|
||||
create.close();
|
||||
fileCreated = true;
|
||||
|
||||
fileStream.open(filename, std::ios::in | std::ios::out);
|
||||
if (!fileStream.is_open()) {
|
||||
throw std::runtime_error("Failed to open file after creating: " + filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char FileDevice::read()
|
||||
{
|
||||
unsigned char value = 0;
|
||||
ensureFileOpen();
|
||||
if (fileStream.is_open()) {
|
||||
fileStream.seekg(readPosition);
|
||||
char ch;
|
||||
if (fileStream.get(ch)) {
|
||||
value = static_cast<unsigned char>(ch);
|
||||
readPosition = fileStream.tellg();
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void FileDevice::write(unsigned char value)
|
||||
{
|
||||
ensureFileOpen();
|
||||
if (fileStream.is_open()) {
|
||||
fileStream.seekp(0, std::ios::end);
|
||||
fileStream.put(static_cast<char>(value));
|
||||
fileStream.flush();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
#include "file_reader.h"
|
||||
|
||||
FileReader::FileReader(const std::string &path, std::ios::openmode m)
|
||||
: in(path, m)
|
||||
{}
|
||||
|
||||
FileReader::~FileReader() = default;
|
||||
|
||||
int FileReader::readByte() {
|
||||
char c;
|
||||
if (!in.get(c)) return -1;
|
||||
return static_cast<unsigned char>(c);
|
||||
}
|
||||
|
||||
|
||||
bool FileReader::readBytes(uint8_t* buf, size_t len) {
|
||||
in.read(reinterpret_cast<char*>(buf), static_cast<std::streamsize>(len));
|
||||
return static_cast<size_t>(in.gcount()) == len;
|
||||
}
|
||||
|
||||
std::string FileReader::readString(size_t len) {
|
||||
std::string s;
|
||||
s.resize(len);
|
||||
in.read(reinterpret_cast<char*>(&s[0]), static_cast<std::streamsize>(len));
|
||||
std::streamsize got = in.gcount();
|
||||
if (static_cast<size_t>(got) < len) s.resize(static_cast<size_t>(got));
|
||||
return s;
|
||||
}
|
||||
|
||||
bool FileReader::good() const { return static_cast<bool>(in); }
|
||||
|
||||
std::string FileReader::readLine() {
|
||||
std::string s;
|
||||
if (!std::getline(in, s)) return std::string();
|
||||
return s;
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#include "input_device.h"
|
||||
|
||||
InputDevice::InputDevice(std::istream &in)
|
||||
: inStream(in)
|
||||
{
|
||||
}
|
||||
|
||||
InputDevice::~InputDevice()
|
||||
{
|
||||
}
|
||||
|
||||
unsigned char InputDevice::read()
|
||||
{
|
||||
char c;
|
||||
if (!inStream.get(c)) {
|
||||
// If stream is at EOF or error, return 0
|
||||
return 0;
|
||||
}
|
||||
return static_cast<unsigned char>(c);
|
||||
}
|
||||
|
|
@ -1,603 +0,0 @@
|
|||
#include "instructions.h"
|
||||
#include "machine.h"
|
||||
#include "utils.h"
|
||||
|
||||
inline int resolveWordOperand(Machine& m, int ea, AddressingMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case AddressingMode::IMMEDIATE: return ea;
|
||||
case AddressingMode::SIMPLE:
|
||||
case AddressingMode::SIC_DIRECT: return m.getWord(ea);
|
||||
case AddressingMode::INDIRECT: return m.getWord(m.getWord(ea));
|
||||
default: m.invalidAddressing(); return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline double resolveFloatOperand(Machine& m, int ea, AddressingMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case AddressingMode::IMMEDIATE: return static_cast<double>(ea);
|
||||
case AddressingMode::SIMPLE:
|
||||
case AddressingMode::SIC_DIRECT: return m.getFloat(ea);
|
||||
case AddressingMode::INDIRECT: return m.getFloat(m.getWord(ea));
|
||||
default: m.invalidAddressing(); return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline void writeWordOperand(Machine& m, int ea, AddressingMode mode, int value)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case AddressingMode::SIMPLE:
|
||||
case AddressingMode::SIC_DIRECT: m.setWord(ea, value); break; // direct store
|
||||
case AddressingMode::INDIRECT: m.setWord(m.getWord(ea), value); break; // store via pointer
|
||||
default: m.invalidAddressing(); break;
|
||||
}
|
||||
}
|
||||
|
||||
inline void writeFloatOperand(Machine& m, int ea, AddressingMode mode, double value)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case AddressingMode::SIMPLE:
|
||||
case AddressingMode::SIC_DIRECT: m.setFloat(ea, value); break;
|
||||
case AddressingMode::INDIRECT: m.setFloat(m.getWord(ea), value); break;
|
||||
default: m.invalidAddressing(); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// For jump-like ops: what PC should become?
|
||||
inline int resolveJumpTarget(Machine& m, int ea, AddressingMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case AddressingMode::IMMEDIATE:
|
||||
case AddressingMode::SIMPLE:
|
||||
case AddressingMode::SIC_DIRECT: return ea; // jump to EA (normal case)
|
||||
case AddressingMode::INDIRECT: return m.getWord(ea); // jump via pointer
|
||||
default: m.invalidAddressing(); return m.getPC();
|
||||
}
|
||||
}
|
||||
|
||||
void fix_handler(Machine &m)
|
||||
{
|
||||
m.setA(static_cast<int>(m.getF()));
|
||||
}
|
||||
|
||||
void float_handler(Machine &m)
|
||||
{
|
||||
m.setF(static_cast<double>(m.getA()));
|
||||
}
|
||||
|
||||
void norm_handler(Machine &m)
|
||||
{
|
||||
m.setF(normaliseFloat(m.getF()));
|
||||
}
|
||||
|
||||
void addr_handler(Machine &m, int r1, int r2)
|
||||
{
|
||||
m.setReg(r2, m.getReg(r1) + m.getReg(r2));
|
||||
}
|
||||
|
||||
void clear_handler(Machine& m, int r, int unused) {
|
||||
m.setReg(r, 0);
|
||||
}
|
||||
|
||||
void compr_handler(Machine &m, int r1, int r2)
|
||||
{
|
||||
m.setSW(sic_comp(m.getReg(r1), m.getReg(r2), m.getSW()));
|
||||
}
|
||||
|
||||
void divr_handler(Machine& m, int r1, int r2) {
|
||||
|
||||
if (m.getReg(r2) == 0) {
|
||||
m.invalidOpcode(DIVR);
|
||||
return;
|
||||
}
|
||||
m.setReg(r2, m.getReg(r2) / m.getReg(r1));
|
||||
}
|
||||
|
||||
void mulr_handler(Machine &m, int r1, int r2)
|
||||
{
|
||||
m.setReg(r2, m.getReg(r1) * m.getReg(r2));
|
||||
}
|
||||
|
||||
void rmo_handler(Machine &m, int r1, int r2)
|
||||
{
|
||||
m.setReg(r2, m.getReg(r1));
|
||||
}
|
||||
|
||||
|
||||
// SHIFTL r1, n → left *circular* shift n bits
|
||||
void shiftl_handler(Machine &m, int r1, int n)
|
||||
{
|
||||
unsigned int v = m.getReg(r1) & WORD_MASK;
|
||||
n %= WORD_SIZE;
|
||||
unsigned int res = ((v << n) | (v >> (WORD_SIZE - n))) & WORD_MASK;
|
||||
m.setReg(r1, res);
|
||||
}
|
||||
|
||||
// SHIFTR r1, n → right shift n bits, fill with original leftmost bit
|
||||
void shiftr_handler(Machine &m, int r1, int n)
|
||||
{
|
||||
unsigned int v = m.getReg(r1) & WORD_MASK;
|
||||
n %= WORD_SIZE;
|
||||
unsigned int msb = (v & 0x800000) ? 1u : 0u;
|
||||
unsigned int shifted = v >> n;
|
||||
unsigned int fill = 0;
|
||||
if (msb) {
|
||||
fill = (~0u) << (WORD_SIZE - n);
|
||||
fill &= WORD_MASK;
|
||||
}
|
||||
|
||||
unsigned int res = (shifted | fill) & WORD_MASK;
|
||||
m.setReg(r1, res);
|
||||
}
|
||||
|
||||
void subr_handler(Machine &m, int r1, int r2)
|
||||
{
|
||||
m.setReg(r2, m.getReg(r2) - m.getReg(r1));
|
||||
}
|
||||
|
||||
// TODO: implement SVC functionality
|
||||
void svc_handler(Machine &m, int n, int unused)
|
||||
{
|
||||
m.notImplemented("SVC");
|
||||
}
|
||||
|
||||
void tixr_handler(Machine &m, int r1, int unused)
|
||||
{
|
||||
m.setX(m.getX() + 1);
|
||||
int valX = m.getX();
|
||||
int valR1 = m.getReg(r1);
|
||||
m.setSW(sic_comp(valX, valR1, m.getSW()));
|
||||
}
|
||||
|
||||
void add_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int val = resolveWordOperand(m, ea, mode);
|
||||
m.setA(m.getA() + val);
|
||||
}
|
||||
|
||||
void addf_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
double val = resolveFloatOperand(m, ea, mode);
|
||||
m.setA(m.getA() + val);
|
||||
}
|
||||
|
||||
void and_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int val = resolveWordOperand(m, ea, mode);
|
||||
m.setA(m.getA() & val);
|
||||
}
|
||||
|
||||
void comp_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int operand = resolveWordOperand(m, ea, mode);
|
||||
m.setSW(sic_comp(m.getA(), operand, m.getSW()));
|
||||
}
|
||||
|
||||
void compf_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
double operand = resolveFloatOperand(m, ea, mode);
|
||||
m.setSW(sic_comp(m.getF(), operand, m.getSW()));
|
||||
}
|
||||
|
||||
void div_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int divisor = resolveWordOperand(m, ea, mode);
|
||||
if (divisor == 0) {
|
||||
m.divisionByZero(DIV);
|
||||
return;
|
||||
}
|
||||
m.setA(m.getA() / divisor);
|
||||
}
|
||||
|
||||
void divf_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
double divisor = resolveFloatOperand(m, ea, mode);
|
||||
if (divisor == 0.0) {
|
||||
m.divisionByZero(DIVF);
|
||||
return;
|
||||
}
|
||||
m.setF(m.getF() / divisor);
|
||||
}
|
||||
|
||||
void j_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int target = resolveJumpTarget(m, ea, mode);
|
||||
|
||||
int instrSize = 3;
|
||||
int instrAddr = m.getPC() - instrSize;
|
||||
// Check if jumping to itself (halt pattern)
|
||||
if (target == instrAddr) {
|
||||
m.halt();
|
||||
}
|
||||
|
||||
m.setPC(target);
|
||||
}
|
||||
|
||||
void jeq_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int sw = m.getSW();
|
||||
int cc = getCC(sw);
|
||||
if (cc == CC_EQ) {
|
||||
int target = resolveJumpTarget(m, ea, mode);
|
||||
m.setPC(target);
|
||||
}
|
||||
}
|
||||
|
||||
void jgt_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int sw = m.getSW();
|
||||
int cc = getCC(sw);
|
||||
if (cc == CC_GT) {
|
||||
int target = resolveJumpTarget(m, ea, mode);
|
||||
m.setPC(target);
|
||||
}
|
||||
}
|
||||
|
||||
void jlt_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int sw = m.getSW();
|
||||
int cc = getCC(sw);
|
||||
if (cc == CC_LT) {
|
||||
int target = resolveJumpTarget(m, ea, mode);
|
||||
m.setPC(target);
|
||||
}
|
||||
}
|
||||
|
||||
void jsub_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int target = resolveJumpTarget(m, ea, mode);
|
||||
m.setL(m.getPC());
|
||||
m.setPC(target);
|
||||
}
|
||||
|
||||
void lda_handler(Machine& m, int ea, AddressingMode mode)
|
||||
{
|
||||
m.setA(resolveWordOperand(m, ea, mode));
|
||||
}
|
||||
|
||||
void ldb_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
m.setB(resolveWordOperand(m, ea, mode));
|
||||
}
|
||||
|
||||
void ldch_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int val;
|
||||
if (mode == AddressingMode::IMMEDIATE) {
|
||||
val = ea & 0xFF;
|
||||
} else if (mode == AddressingMode::INDIRECT) {
|
||||
val = m.getByte(m.getWord(ea));
|
||||
} else {
|
||||
val = m.getByte(ea);
|
||||
}
|
||||
m.setA((m.getA() & 0xFFFF00) | (val & 0xFF));
|
||||
}
|
||||
|
||||
void ldf_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
m.setF(resolveFloatOperand(m, ea, mode));
|
||||
}
|
||||
|
||||
void ldl_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
m.setL(resolveWordOperand(m, ea, mode));
|
||||
}
|
||||
|
||||
void lds_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
m.setS(resolveWordOperand(m, ea, mode));
|
||||
}
|
||||
|
||||
void ldt_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
m.setT(resolveWordOperand(m, ea, mode));
|
||||
}
|
||||
|
||||
void ldx_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
m.setX(resolveWordOperand(m, ea, mode));
|
||||
}
|
||||
|
||||
void mul_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int val = resolveWordOperand(m, ea, mode);
|
||||
m.setA(m.getA() * val);
|
||||
}
|
||||
|
||||
void mulf_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
m.setF(m.getF() * resolveFloatOperand(m, ea, mode));
|
||||
}
|
||||
|
||||
void or_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int val = resolveWordOperand(m, ea, mode);
|
||||
m.setA(m.getA() | val);
|
||||
}
|
||||
|
||||
void rd_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int deviceNum = resolveWordOperand(m, ea, mode);
|
||||
Device& device = m.getDevice(deviceNum);
|
||||
// Load byte into rightmost byte of A register
|
||||
m.setA((m.getA() & 0xFFFF00) | device.read());
|
||||
}
|
||||
|
||||
void rsub_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
m.setPC(m.getL());
|
||||
}
|
||||
|
||||
void sta_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
writeWordOperand(m, ea, mode, m.getA());
|
||||
}
|
||||
|
||||
void stb_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
writeWordOperand(m, ea, mode, m.getB());
|
||||
}
|
||||
// Rightmost byte of A register is stored
|
||||
void stch_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int val = m.getA() & 0xFF;
|
||||
switch (mode)
|
||||
{
|
||||
case AddressingMode::SIMPLE:
|
||||
case AddressingMode::SIC_DIRECT: m.setByte(ea, val); break; // direct store
|
||||
case AddressingMode::INDIRECT: m.setByte(m.getWord(ea), val); break; // store via pointer
|
||||
default: m.invalidAddressing(); break;
|
||||
}
|
||||
}
|
||||
|
||||
void stf_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
writeFloatOperand(m, ea, mode, m.getF());
|
||||
}
|
||||
|
||||
void stl_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
writeWordOperand(m, ea, mode, m.getL());
|
||||
}
|
||||
|
||||
void sts_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
writeWordOperand(m, ea, mode, m.getS());
|
||||
}
|
||||
|
||||
void stsw_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
writeWordOperand(m, ea, mode, m.getSW());
|
||||
}
|
||||
|
||||
void stt_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
writeWordOperand(m, ea, mode, m.getT());
|
||||
}
|
||||
|
||||
void stx_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
writeWordOperand(m, ea, mode, m.getX());
|
||||
}
|
||||
|
||||
void sub_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int val = resolveWordOperand(m, ea, mode);
|
||||
m.setA(m.getA() - val);
|
||||
}
|
||||
|
||||
void subf_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
double val = resolveFloatOperand(m, ea, mode);
|
||||
m.setF(m.getF() - val);
|
||||
}
|
||||
|
||||
void td_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int deviceNum = resolveWordOperand(m, ea, mode);
|
||||
Device& device = m.getDevice(deviceNum);
|
||||
// Test device and set SW accordingly
|
||||
if (device.test()) {
|
||||
m.setSW(setCC(m.getSW(), CC_EQ));
|
||||
} else {
|
||||
m.setSW(setCC(m.getSW(), CC_LT));
|
||||
}
|
||||
}
|
||||
|
||||
void tix_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
m.setX(m.getX() + 1);
|
||||
int valX = m.getX();
|
||||
int memVal = resolveWordOperand(m, ea, mode);
|
||||
m.setSW(sic_comp(valX, memVal, m.getSW()));
|
||||
}
|
||||
|
||||
void wd_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int deviceNum = resolveWordOperand(m, ea, mode);
|
||||
Device& device = m.getDevice(deviceNum);
|
||||
// Write rightmost byte of A register to device
|
||||
device.write(static_cast<unsigned char>(m.getA() & 0xFF));
|
||||
}
|
||||
|
||||
void xexe_handler(Machine &m)
|
||||
{
|
||||
m.enableExtendedMode();
|
||||
m.execute();
|
||||
m.disableExtendedMode();
|
||||
}
|
||||
|
||||
void halt_handler(Machine &m)
|
||||
{
|
||||
m.halt();
|
||||
}
|
||||
|
||||
void nop_handler(Machine &m)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
void vaddr_handler(Machine &m, int r1, int r2)
|
||||
{
|
||||
int result[VECTOR_REG_SIZE];
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
result[i] = m.getVectorRegister(r1)[i] + m.getVectorRegister(r2)[i];
|
||||
}
|
||||
m.setVectorRegister(r2, result);
|
||||
}
|
||||
|
||||
void vsubr_handler(Machine &m, int r1, int r2)
|
||||
{
|
||||
int result[VECTOR_REG_SIZE];
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
result[i] = m.getVectorRegister(r2)[i] - m.getVectorRegister(r1)[i];
|
||||
}
|
||||
m.setVectorRegister(r2, result);
|
||||
}
|
||||
|
||||
void vmulr_handler(Machine &m, int r1, int r2)
|
||||
{
|
||||
int result[VECTOR_REG_SIZE];
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
result[i] = m.getVectorRegister(r1)[i] * m.getVectorRegister(r2)[i];
|
||||
}
|
||||
m.setVectorRegister(r2, result);
|
||||
}
|
||||
|
||||
void vdivr_handler(Machine &m, int r1, int r2)
|
||||
{
|
||||
int result[VECTOR_REG_SIZE];
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
if (m.getVectorRegister(r1)[i] == 0) {
|
||||
m.divisionByZero(VDIVR);
|
||||
return;
|
||||
}
|
||||
result[i] = m.getVectorRegister(r2)[i] / m.getVectorRegister(r1)[i];
|
||||
}
|
||||
m.setVT(result);
|
||||
}
|
||||
|
||||
void vadd_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||
int vec[VECTOR_REG_SIZE];
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
vec[i] = m.getWord(baseAddr + i * 3);
|
||||
}
|
||||
int result[VECTOR_REG_SIZE];
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
result[i] = m.getVA()[i] + vec[i];
|
||||
}
|
||||
m.setVA(result);
|
||||
}
|
||||
|
||||
void vsub_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||
int vec[VECTOR_REG_SIZE];
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
vec[i] = m.getWord(baseAddr + i * 3);
|
||||
}
|
||||
int result[VECTOR_REG_SIZE];
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
result[i] = m.getVA()[i] - vec[i];
|
||||
}
|
||||
m.setVA(result);
|
||||
}
|
||||
|
||||
void vmul_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||
int vec[VECTOR_REG_SIZE];
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
vec[i] = m.getWord(baseAddr + i * 3);
|
||||
}
|
||||
int result[VECTOR_REG_SIZE];
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
result[i] = m.getVA()[i] * vec[i];
|
||||
}
|
||||
m.setVA(result);
|
||||
}
|
||||
|
||||
void vdiv_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||
int vec[VECTOR_REG_SIZE];
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
vec[i] = m.getWord(baseAddr + i * 3);
|
||||
}
|
||||
int result[VECTOR_REG_SIZE];
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
if (vec[i] == 0) {
|
||||
m.divisionByZero(VDIV);
|
||||
return;
|
||||
}
|
||||
result[i] = m.getVA()[i] / vec[i];
|
||||
}
|
||||
m.setVA(result);
|
||||
}
|
||||
|
||||
void stva_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||
const int* vec = m.getVA();
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
m.setWord(baseAddr + i * 3, vec[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void stvs_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||
const int* vec = m.getVS();
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
m.setWord(baseAddr + i * 3, vec[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void stvt_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||
const int* vec = m.getVT();
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
m.setWord(baseAddr + i * 3, vec[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void ldva_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||
int vec[VECTOR_REG_SIZE];
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
vec[i] = m.getWord(baseAddr + i * 3);
|
||||
}
|
||||
m.setVA(vec);
|
||||
}
|
||||
|
||||
void ldvs_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||
int vec[VECTOR_REG_SIZE];
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
vec[i] = m.getWord(baseAddr + i * 3);
|
||||
}
|
||||
m.setVS(vec);
|
||||
}
|
||||
|
||||
void ldvt_handler(Machine &m, int ea, AddressingMode mode)
|
||||
{
|
||||
int baseAddr = resolveWordOperand(m, ea, mode);
|
||||
int vec[VECTOR_REG_SIZE];
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
vec[i] = m.getWord(baseAddr + i * 3);
|
||||
}
|
||||
m.setVT(vec);
|
||||
}
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
#include "loader.h"
|
||||
#include "file_reader.h"
|
||||
#include "string_reader.h"
|
||||
#include "machine.h"
|
||||
#include "constants.h"
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
|
||||
Loader::~Loader()
|
||||
{
|
||||
_machine.reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Loader::load()
|
||||
{
|
||||
HeaderMetadata header = readHeader();
|
||||
|
||||
while(true) {
|
||||
RecordType type = parseRecordType(static_cast<char>(_file_reader->readByte()));
|
||||
switch (type) {
|
||||
case RecordType::TEXT: {
|
||||
TextRecord textRecord = readTextRecord();
|
||||
if (!load_into_memory(textRecord.start_address, textRecord.data)) {
|
||||
throw std::runtime_error("Failed to load text record into memory");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RecordType::END: {
|
||||
EndRecord endRecord = readEndRecord();
|
||||
_machine->setPC(endRecord.execution_start_address);
|
||||
return; // Loading complete
|
||||
}
|
||||
case RecordType::UNKNOWN:
|
||||
default:
|
||||
throw std::runtime_error("Unknown record type encountered");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader::RecordType Loader::parseRecordType(char c)
|
||||
{
|
||||
switch (c) {
|
||||
case 'H': return RecordType::HEADER;
|
||||
case 'T': return RecordType::TEXT;
|
||||
case 'E': return RecordType::END;
|
||||
default: return RecordType::UNKNOWN; // fallback; adjust as needed
|
||||
}
|
||||
}
|
||||
|
||||
Loader::HeaderMetadata Loader::readHeader()
|
||||
{
|
||||
|
||||
RecordType type = parseRecordType(static_cast<char>(_file_reader->readByte()));
|
||||
if (type != RecordType::HEADER) {
|
||||
throw std::runtime_error("Expected HEADER record");
|
||||
}
|
||||
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
|
||||
|
||||
HeaderMetadata header;
|
||||
// Read program name (6 bytes)
|
||||
header.program_name = _file_reader->readString(6);
|
||||
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
|
||||
|
||||
// Read start address (6 hex digits)
|
||||
header.start_address = std::stoi(_file_reader->readString(6), nullptr, 16);
|
||||
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
|
||||
|
||||
// Read length (6 hex digits)
|
||||
header.length = std::stoi(_file_reader->readString(6), nullptr, 16);
|
||||
// consume newline
|
||||
_file_reader->readLine();
|
||||
return header;
|
||||
}
|
||||
|
||||
Loader::TextRecord Loader::readTextRecord()
|
||||
{
|
||||
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
|
||||
TextRecord record;
|
||||
// Assume 'T' has already been read
|
||||
record.start_address = std::stoi(_file_reader->readString(6), nullptr, 16);
|
||||
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
|
||||
|
||||
// Read length (1 byte, 2 hex digits)
|
||||
std::string lengthStr = _file_reader->readString(2);
|
||||
int length = std::stoi(lengthStr, nullptr, 16);
|
||||
|
||||
// Read the rest of the line (data bytes with spaces)
|
||||
std::string dataLine = _file_reader->readLine();
|
||||
|
||||
// Remove all whitespace from the data line
|
||||
dataLine.erase(std::remove_if(dataLine.begin(), dataLine.end(), ::isspace), dataLine.end());
|
||||
|
||||
// Now use StringReader to parse the hex bytes
|
||||
StringReader stringReader(dataLine);
|
||||
record.data.resize(length);
|
||||
|
||||
for (int i = 0; i < length; ++i) {
|
||||
std::string byteHex = stringReader.readString(2);
|
||||
record.data[i] = static_cast<uint8_t>(std::stoi(byteHex, nullptr, 16));
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
Loader::EndRecord Loader::readEndRecord()
|
||||
{
|
||||
EndRecord record;
|
||||
if(FILE_CONTAINS_WHITE_SPACES) _file_reader->readByte();
|
||||
// Assume 'E' has already been read
|
||||
std::string addrStr = _file_reader->readString(6);
|
||||
if (!addrStr.empty()) {
|
||||
record.execution_start_address = std::stoi(addrStr, nullptr, 16);
|
||||
} else {
|
||||
record.execution_start_address = 0;
|
||||
}
|
||||
// consume newline
|
||||
_file_reader->readLine();
|
||||
return record;
|
||||
}
|
||||
|
||||
bool Loader::load_into_memory(int start_address, const std::vector<uint8_t> &data)
|
||||
{
|
||||
for(size_t i = 0; i < data.size(); ++i) {
|
||||
int addr = start_address + static_cast<int>(i);
|
||||
if (addr < 0 || addr >= MEMORY_SIZE) {
|
||||
return false;
|
||||
}
|
||||
_machine->setByte(addr, data[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,525 +0,0 @@
|
|||
#include "machine.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "opcode.h"
|
||||
#include "instructions.h"
|
||||
#include <cmath>
|
||||
#include <thread>
|
||||
|
||||
using std::make_shared;
|
||||
|
||||
string prefix = "Machine error: ";
|
||||
|
||||
|
||||
Machine::Machine()
|
||||
{
|
||||
// Initialize registers and memory to zero
|
||||
A = B = X = L = S = T = PC = SW = 0;
|
||||
F = 0.0;
|
||||
for (int i = 0; i < MEMORY_SIZE; i++) {
|
||||
memory[i] = 0;
|
||||
}
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; i++) {
|
||||
VA[i] = VS[i] = VT[i] = 0;
|
||||
}
|
||||
_stopped = false;
|
||||
|
||||
devices.resize(NUM_DEVICES);
|
||||
// device 0: standard input
|
||||
devices[0] = make_shared<InputDevice>(std::cin);
|
||||
// device 1: standard output
|
||||
devices[1] = make_shared<OutputDevice>(std::cout);
|
||||
|
||||
// Initialize devices >= 2 as FileDevice with hex names in devices directory
|
||||
for (int i = 2; i < NUM_DEVICES; i++) {
|
||||
char hex[3];
|
||||
snprintf(hex, sizeof(hex), "%02X", i);
|
||||
std::string filename = "devices/" + std::string(hex) + ".dev";
|
||||
try {
|
||||
devices[i] = std::make_shared<FileDevice>(filename);
|
||||
} catch (const std::exception &e) {
|
||||
cerr << prefix << "Warning: Failed to initialize FileDevice for device " << i << ": " << e.what() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
_exex_mode = false;
|
||||
_instructionsTable = instructions;
|
||||
}
|
||||
|
||||
Machine::~Machine()
|
||||
{
|
||||
for (auto& device : devices) {
|
||||
device.reset();
|
||||
}
|
||||
}
|
||||
|
||||
int Machine::getSpeed() const
|
||||
{
|
||||
return speedHz.load();
|
||||
}
|
||||
|
||||
void Machine::setSpeed(int Hz)
|
||||
{
|
||||
speedHz.store(Hz);
|
||||
}
|
||||
|
||||
// TODO: implement errors
|
||||
void Machine::notImplemented(string mnemonic)
|
||||
{
|
||||
cout << prefix << "Not implemented: " << mnemonic << endl;
|
||||
}
|
||||
|
||||
void Machine::invalidOpcode(int opcode)
|
||||
{
|
||||
cout << prefix << "Invalid opcode: " << opcode << endl;
|
||||
}
|
||||
|
||||
|
||||
void Machine::invalidAddressing()
|
||||
{
|
||||
cout << prefix << "Invalid addressing mode" << endl;
|
||||
}
|
||||
|
||||
void Machine::divisionByZero(int opcode)
|
||||
{
|
||||
cout << prefix << "Division by zero error in opcode: " << opcode << endl;
|
||||
}
|
||||
|
||||
void Machine::undefinedHandler(int opcode)
|
||||
{
|
||||
cout << prefix << "Undefined handler for opcode: " << opcode << endl;
|
||||
}
|
||||
|
||||
void Machine::enableExtendedMode()
|
||||
{
|
||||
if(!USE_EXTENDED_MODE) return;
|
||||
_exex_mode = true;
|
||||
_instructionsTable = instructionsEXEX;
|
||||
}
|
||||
|
||||
void Machine::disableExtendedMode()
|
||||
{
|
||||
if(!USE_EXTENDED_MODE) return;
|
||||
_exex_mode = false;
|
||||
_instructionsTable = instructions;
|
||||
}
|
||||
|
||||
int *Machine::getVectorRegister(int regNum)
|
||||
{
|
||||
switch (regNum) {
|
||||
case 0: return VA;
|
||||
case 4: return VS;
|
||||
case 5: return VT;
|
||||
default:
|
||||
cerr << prefix << "Invalid register number: " << regNum << endl;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::setVectorRegister(int regNum, const int *values)
|
||||
{
|
||||
int* targetReg = getVectorRegister(regNum);
|
||||
if (targetReg == nullptr) return;
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; i++) {
|
||||
targetReg[i] = toSIC24(values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::setVA(const int *values)
|
||||
{
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; i++) {
|
||||
VA[i] = toSIC24(values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::setVS(const int *values)
|
||||
{
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; i++) {
|
||||
VS[i] = toSIC24(values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::setVT(const int *values)
|
||||
{
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; i++) {
|
||||
VT[i] = toSIC24(values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::tick()
|
||||
{
|
||||
const int speed = speedHz.load();
|
||||
if (speed <= 0) throw std::runtime_error("Invalid speed setting in Machine::tick");
|
||||
|
||||
const auto delay = std::chrono::milliseconds(1000 / speed);
|
||||
std::this_thread::sleep_for(delay);
|
||||
}
|
||||
|
||||
void Machine::halt()
|
||||
{
|
||||
_stopped = true;
|
||||
}
|
||||
|
||||
void Machine::reset()
|
||||
{
|
||||
// Reset all registers
|
||||
A = B = X = L = S = T = PC = SW = 0;
|
||||
F = 0.0;
|
||||
|
||||
// Clear memory
|
||||
for (int i = 0; i < MEMORY_SIZE; i++) {
|
||||
memory[i] = 0;
|
||||
}
|
||||
|
||||
// Reset execution state
|
||||
_stopped = false;
|
||||
running.store(false);
|
||||
|
||||
// Reset vector registers
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; i++) {
|
||||
VA[i] = VS[i] = VT[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int Machine::getReg(int regNum) const
|
||||
{
|
||||
switch (regNum) {
|
||||
case 0: return A;
|
||||
case 1: return X;
|
||||
case 2: return L;
|
||||
case 3: return B;
|
||||
case 4: return S;
|
||||
case 5: return T;
|
||||
case 6: return F;
|
||||
case 8: return PC;
|
||||
case 9: return SW;
|
||||
default:
|
||||
cerr << prefix << "Invalid register number: " << regNum << endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: handle double for F register
|
||||
void Machine::setReg(int regNum, int value)
|
||||
{
|
||||
value = toSIC24(value);
|
||||
switch (regNum) {
|
||||
case 0: A = value; break;
|
||||
case 1: X = value; break;
|
||||
case 2: L = value; break;
|
||||
case 3: B = value; break;
|
||||
case 4: S = value; break;
|
||||
case 5: T = value; break;
|
||||
case 6: F = value; break;
|
||||
case 8: PC = value; break;
|
||||
case 9: SW = value; break;
|
||||
default:
|
||||
cerr << prefix << "Invalid register number: " << regNum << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int Machine::getByte(int address)
|
||||
{
|
||||
if (address < 0 || address >= MEMORY_SIZE) {
|
||||
cerr << prefix << "Invalid memory address: " << address << endl;
|
||||
return -1;
|
||||
}
|
||||
return static_cast<int>(memory[address]);
|
||||
}
|
||||
|
||||
void Machine::setByte(int address, int value)
|
||||
{
|
||||
if(address < 0 || address >= MEMORY_SIZE) {
|
||||
cerr << prefix << "Invalid memory address: " << address << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
memory[address] = static_cast<unsigned char>(value);
|
||||
}
|
||||
|
||||
// Assuming word is 3 bytes
|
||||
|
||||
int Machine::getWord(int address)
|
||||
{
|
||||
if (address < 0 || address + 2 >= MEMORY_SIZE) {
|
||||
cerr << prefix << "Invalid memory address: " << address << endl;
|
||||
return -1;
|
||||
}
|
||||
// Big-endian: high byte first
|
||||
return (static_cast<int>(memory[address]) << 16) | (static_cast<int>(memory[address + 1]) << 8) | static_cast<int>(memory[address + 2]);
|
||||
}
|
||||
|
||||
// Assuming word is 3 bytes
|
||||
void Machine::setWord(int address, int value)
|
||||
{
|
||||
if(address < 0 || address + 2 >= MEMORY_SIZE) {
|
||||
cerr << prefix << "Invalid memory address: " << address << endl;
|
||||
return;
|
||||
}
|
||||
value &= 0xFFFFFF;
|
||||
|
||||
// Big-endian: high byte first
|
||||
memory[address] = static_cast<unsigned char>((value >> 16) & 0xFF);
|
||||
memory[address + 1] = static_cast<unsigned char>((value >> 8) & 0xFF);
|
||||
memory[address + 2] = static_cast<unsigned char>(value & 0xFF);
|
||||
}
|
||||
|
||||
double Machine::getFloat(int address)
|
||||
{
|
||||
if (address < 0 || address + 5 >= MEMORY_SIZE) {
|
||||
cerr << prefix << "Invalid float address: " << address << endl;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// load 6 bytes, big-endian → 48-bit word
|
||||
unsigned long long raw =
|
||||
((unsigned long long)memory[address] << 40) |
|
||||
((unsigned long long)memory[address+1] << 32) |
|
||||
((unsigned long long)memory[address+2] << 24) |
|
||||
((unsigned long long)memory[address+3] << 16) |
|
||||
((unsigned long long)memory[address+4] << 8) |
|
||||
(unsigned long long)memory[address+5];
|
||||
|
||||
int sign = (raw >> 47) & 0x1;
|
||||
int exponent = (raw >> 40) & 0x7F;
|
||||
unsigned long long frac = raw & SICF_FRAC_MASK; // 40 bits
|
||||
|
||||
if (raw == 0) return 0.0;
|
||||
|
||||
// value = (1 + frac/2^40) * 2^(exp - 64)
|
||||
double mant = 1.0 + (double)frac / (double)(1ULL << SICF_FRAC_BITS);
|
||||
int e = exponent - SICF_EXP_BIAS;
|
||||
double val = std::ldexp(mant, e); // ldexp is fast enough here
|
||||
return sign ? -val : val;
|
||||
}
|
||||
|
||||
void Machine::setFloat(int address, double value)
|
||||
{
|
||||
if (address < 0 || address + 5 >= MEMORY_SIZE) {
|
||||
cerr << prefix << "Invalid float address: " << address << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (value == 0.0) {
|
||||
memory[address] = 0;
|
||||
memory[address+1] = 0;
|
||||
memory[address+2] = 0;
|
||||
memory[address+3] = 0;
|
||||
memory[address+4] = 0;
|
||||
memory[address+5] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
int sign = value < 0;
|
||||
double x = sign ? -value : value;
|
||||
|
||||
// normalize x to [1, 2)
|
||||
int exp2 = 0;
|
||||
x = std::frexp(x, &exp2);
|
||||
x *= 2.0;
|
||||
exp2 -= 1;
|
||||
|
||||
int exp_field = exp2 + SICF_EXP_BIAS;
|
||||
if (exp_field < 0) exp_field = 0;
|
||||
if (exp_field > 127) exp_field = 127;
|
||||
|
||||
// mantissa = (x - 1) * 2^40
|
||||
double frac_d = (x - 1.0) * (double)(1ULL << SICF_FRAC_BITS);
|
||||
unsigned long long frac = (unsigned long long)(frac_d + 0.5); // round
|
||||
frac &= SICF_FRAC_MASK;
|
||||
|
||||
unsigned long long raw =
|
||||
((unsigned long long)sign << 47) |
|
||||
((unsigned long long)exp_field << 40) |
|
||||
frac;
|
||||
|
||||
// store 6 bytes big-endian
|
||||
memory[address] = (unsigned char)((raw >> 40) & 0xFF);
|
||||
memory[address+1] = (unsigned char)((raw >> 32) & 0xFF);
|
||||
memory[address+2] = (unsigned char)((raw >> 24) & 0xFF);
|
||||
memory[address+3] = (unsigned char)((raw >> 16) & 0xFF);
|
||||
memory[address+4] = (unsigned char)((raw >> 8) & 0xFF);
|
||||
memory[address+5] = (unsigned char)( raw & 0xFF);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Device &Machine::getDevice(int num)
|
||||
{
|
||||
if(num < 0 || num >= static_cast<int>(devices.size()) || !devices[num]) {
|
||||
cerr << prefix << "Invalid device number: " << num << endl;
|
||||
return fallbackDevice;
|
||||
}
|
||||
return *devices[num];
|
||||
}
|
||||
|
||||
void Machine::setDevice(int num, std::shared_ptr<Device> device)
|
||||
{
|
||||
if(num < 0 || num >= NUM_DEVICES) {
|
||||
cerr << prefix << "Invalid device number: " << num << endl;
|
||||
return;
|
||||
}
|
||||
if(static_cast<int>(devices.size()) != NUM_DEVICES) {
|
||||
devices.resize(NUM_DEVICES);
|
||||
}
|
||||
// Enforce: devices with index >= 2 must be FileDevice instances
|
||||
if (num >= 2) {
|
||||
// try dynamic cast
|
||||
if (std::dynamic_pointer_cast<FileDevice>(device) == nullptr) {
|
||||
cerr << prefix << "Device at index " << num << " must be a FileDevice." << endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
devices[num] = device;
|
||||
}
|
||||
|
||||
void Machine::setFileDevice(int num, const std::string &filename)
|
||||
{
|
||||
if(num < 0 || num >= NUM_DEVICES) {
|
||||
cerr << prefix << "Invalid device number: " << num << endl;
|
||||
return;
|
||||
}
|
||||
if(static_cast<int>(devices.size()) != NUM_DEVICES) {
|
||||
devices.resize(NUM_DEVICES);
|
||||
}
|
||||
try {
|
||||
devices[num] = std::make_shared<FileDevice>(filename);
|
||||
} catch (const std::exception &e) {
|
||||
cerr << prefix << "Failed to create FileDevice for index " << num << ": " << e.what() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
int Machine::fetch()
|
||||
{
|
||||
return getByte(PC++);
|
||||
}
|
||||
|
||||
void Machine::execute() {
|
||||
if (_stopped) return;
|
||||
|
||||
int b1 = fetch();
|
||||
|
||||
InstructionInfo &info = _instructionsTable[b1];
|
||||
|
||||
if (info.type == InstructionType::TYPE1) { execF1(b1); return; }
|
||||
if (info.type == InstructionType::TYPE2) { execF2(b1, fetch()); return; }
|
||||
|
||||
int opcode = b1 & TYPE3_4_SIC_MASK;
|
||||
InstructionInfo &info34 = _instructionsTable[opcode];
|
||||
int ni = b1 & NI_MASK;
|
||||
|
||||
if (info34.type == InstructionType::TYPE3_4) {
|
||||
int b2 = fetch(), b3 = fetch();
|
||||
int x = (b2 & 0x80) ? 1 : 0;
|
||||
int b = (b2 & 0x40) ? 1 : 0;
|
||||
int p = (b2 & 0x20) ? 1 : 0;
|
||||
int e = (b2 & 0x10) ? 1 : 0;
|
||||
|
||||
int operand;
|
||||
if (ni == NI_SIC) {
|
||||
// PURE SIC
|
||||
operand = ((b2 & 0x7F) << 8) | b3;
|
||||
} else {
|
||||
// SIC/XE
|
||||
operand = e
|
||||
? (((b2 & 0x0F) << 16) | (b3 << 8) | fetch()) // F4: 20-bit
|
||||
: (((b2 & 0x0F) << 8) | b3); // F3: 12-bit
|
||||
}
|
||||
|
||||
execSICF3F4(opcode, ni, x, b, p, e, operand);
|
||||
return;
|
||||
}
|
||||
|
||||
invalidOpcode(b1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool Machine::execF1(int opcode)
|
||||
{
|
||||
if (_instructionsTable[opcode].handler) {
|
||||
auto handler = reinterpret_cast<void(*)(Machine&)>(_instructionsTable[opcode].handler);
|
||||
handler(*this);
|
||||
return true;
|
||||
}
|
||||
undefinedHandler(opcode);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Machine::execF2(int opcode, int operand)
|
||||
{
|
||||
int r1 = (operand >> 4) & 0xF;
|
||||
int r2 = operand & 0xF;
|
||||
|
||||
if (_instructionsTable[opcode].handler) {
|
||||
auto handler = reinterpret_cast<void(*)(Machine&, int, int)>(_instructionsTable[opcode].handler);
|
||||
handler(*this, r1, r2);
|
||||
return true;
|
||||
}
|
||||
undefinedHandler(opcode);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Machine::execSICF3F4(int opcode, int ni, int x, int b, int p, int e, int operand)
|
||||
{
|
||||
int ea_part = operand;
|
||||
int base = 0;
|
||||
AddressingMode mode = getAddressingMode(ni);
|
||||
|
||||
// --- PURE SIC ---
|
||||
if (mode == AddressingMode::SIC_DIRECT) {
|
||||
int ea = ea_part + (x ? getX() : 0);
|
||||
if (_instructionsTable[opcode].handler) {
|
||||
auto h = reinterpret_cast<void(*)(Machine&, int, AddressingMode)>(_instructionsTable[opcode].handler);
|
||||
h(*this, ea, mode);
|
||||
return true;
|
||||
}
|
||||
undefinedHandler(opcode);
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- SIC/XE EA calc ---
|
||||
|
||||
if (!e) { // format 3
|
||||
if (b && !p) {
|
||||
base = getB(); // base-relative, unsigned 12-bit
|
||||
} else if (p && !b) {
|
||||
// PC-relative, signed 12-bit
|
||||
if (ea_part & 0x800) // bit 11 set?
|
||||
ea_part |= 0xFFFFF000; // sign-extend
|
||||
base = getPC();
|
||||
}
|
||||
}
|
||||
// format 4 (e=1): b/p ignored, ea_part is 20-bit absolute
|
||||
int ea = base + ea_part + (x ? getX() : 0);
|
||||
|
||||
if (_instructionsTable[opcode].handler) {
|
||||
auto h = reinterpret_cast<void(*)(Machine&, int, AddressingMode)>(_instructionsTable[opcode].handler);
|
||||
h(*this, ea, mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
undefinedHandler(opcode);
|
||||
return false;
|
||||
}
|
||||
|
||||
void Machine::start()
|
||||
{
|
||||
running.store(true);
|
||||
|
||||
// Main execution loop
|
||||
// TODO: consider running in separate thread
|
||||
while (running.load()) {
|
||||
execute();
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::stop()
|
||||
{
|
||||
running.store(false);
|
||||
}
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include "machine.h"
|
||||
#include "file_device.h"
|
||||
#include "opcode.h"
|
||||
#include "instructions.h"
|
||||
#include "constants.h"
|
||||
#include "loader.h"
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
struct VectorAddProgram {
|
||||
int x, y;
|
||||
};
|
||||
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
/*
|
||||
loadInstructionSet();
|
||||
Machine machine;
|
||||
cout << "SIC/XE Program: Accumulator Loop" << endl;
|
||||
|
||||
const int TEMP_ADDR = 0x50;
|
||||
const int LOOP_ADDR = 0x03;
|
||||
|
||||
// clear TEMP
|
||||
machine.setByte(TEMP_ADDR, 0);
|
||||
loadInstructionSet();
|
||||
|
||||
cout << "SIC/XE Program: Vector add test" << endl;
|
||||
|
||||
const int VA_ADDR = 0x100; // source vector A
|
||||
const int VB_ADDR = 0x200; // source vector B
|
||||
const int VR_ADDR = 0x300; // result store (STVA)
|
||||
|
||||
// Prepare two 4-element vectors (WORD = 3 bytes) for VA and VB
|
||||
int a_vals[VECTOR_REG_SIZE] = {1,2,3,4};
|
||||
int b_vals[VECTOR_REG_SIZE] = {5,6,7,8};
|
||||
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
machine.setWord(VA_ADDR + i * 3, a_vals[i]);
|
||||
machine.setWord(VB_ADDR + i * 3, b_vals[i]);
|
||||
}
|
||||
|
||||
// Assemble program at address 0x000 (we use XEXE before each extended op)
|
||||
// Offsets and bytes (hex):
|
||||
// 0x00: XEXE -> 0xEE
|
||||
// 0x01: LDVA (format 4) -> b1=0x03 (LDVA|ni=0x00|0x03), b2=0x10 (e=1), b3=0x01, b4=0x00 (addr 0x100)
|
||||
// 0x05: XEXE -> 0xEE
|
||||
// 0x06: LDVS (format 4) -> b1=0x6B (0x68|0x03), b2=0x10, b3=0x02, b4=0x00 (addr 0x200)
|
||||
// 0x0A: XEXE -> 0xEE
|
||||
// 0x0B: VADDR B->A (type 2) -> opcode 0x90, operand r1=4 (VS), r2=0 (VA) => operand=(4<<4)|0=0x40
|
||||
// 0x0D: XEXE -> 0xEE
|
||||
// 0x0E: STVA (format4) -> b1=0x0F (0x0C|0x03), b2=0x10, b3=0x03, b4=0x00 (addr 0x300)
|
||||
// 0x12: J (format4) to self -> b1=0x3F (0x3C|0x03), b2=0x10, b3=0x00, b4=0x12
|
||||
|
||||
unsigned char prog[] = {
|
||||
0xEE,
|
||||
0x01, 0x10, 0x01, 0x00, // LDVA (format 4) with ni=IMMEDIATE -> b1=0x01
|
||||
0xEE,
|
||||
0x69, 0x10, 0x02, 0x00, // LDVS (format 4) with ni=IMMEDIATE -> b1=0x69 (0x68|0x01)
|
||||
0xEE,
|
||||
0x90, 0x40, // VADDR VS->VA (type2)
|
||||
0xEE,
|
||||
0x0D, 0x10, 0x03, 0x00, // STVA (format4) with ni=IMMEDIATE -> b1=0x0D
|
||||
0x3F, 0x10, 0x00, 0x12 // J (format4) loop to 0x12
|
||||
};
|
||||
|
||||
const int PROG_START = 0x00;
|
||||
for (size_t i = 0; i < sizeof(prog); ++i) {
|
||||
machine.setByte(PROG_START + static_cast<int>(i), prog[i]);
|
||||
}
|
||||
|
||||
machine.setPC(PROG_START);
|
||||
|
||||
cout << "Program loaded. VA@0x" << std::hex << VA_ADDR << " VB@0x" << VB_ADDR << " -> store@0x" << VR_ADDR << std::dec << endl;
|
||||
|
||||
|
||||
const int MAX_STEPS = 100;
|
||||
for (int i = 0; i < MAX_STEPS; ++i) {
|
||||
machine.execute();
|
||||
}
|
||||
|
||||
// Read back result vector stored at VR_ADDR
|
||||
cout << "Result vector at 0x" << std::hex << VR_ADDR << std::dec << ": ";
|
||||
for (int i = 0; i < VECTOR_REG_SIZE; ++i) {
|
||||
int val = machine.getWord(VR_ADDR + i * 3);
|
||||
cout << val << (i + 1 < VECTOR_REG_SIZE ? ", " : "\n");
|
||||
}
|
||||
*/
|
||||
|
||||
loadInstructionSet();
|
||||
std::shared_ptr<Machine> machine = std::make_shared<Machine>();
|
||||
Loader loader(machine, std::string(PATH_RESOURCES) + "rec.obj");
|
||||
loader.load();
|
||||
|
||||
cout << "=== Starting execution of rec.obj ===" << endl;
|
||||
cout << "Initial PC: 0x" << std::hex << machine->getPC() << std::dec << endl;
|
||||
|
||||
int maxSteps = 10000;
|
||||
for (int step = 0; step < maxSteps; step++) {
|
||||
int pc = machine->getPC();
|
||||
int opcode = machine->getByte(pc) & 0xFC;
|
||||
const char* instName = (opcode < 256 && instructions[opcode].type != InstructionType::INVALID)
|
||||
? instructions[opcode].name : "???";
|
||||
|
||||
int regA = machine->getA();
|
||||
int regB = machine->getB();
|
||||
int regX = machine->getX();
|
||||
int regL = machine->getL();
|
||||
int sw = machine->getSW();
|
||||
|
||||
printf("Step %4d: PC=0x%05X %-6s A=0x%06X B=0x%06X X=0x%06X L=0x%06X SW=0x%06X\n",
|
||||
step, pc, instName, regA, regB, regX, regL, sw);
|
||||
|
||||
machine->execute();
|
||||
|
||||
if (machine->isStopped()) {
|
||||
cout << "Machine halted at step " << step << endl;
|
||||
break;
|
||||
}
|
||||
|
||||
if (step > 100 && machine->getPC() == pc) {
|
||||
cout << "ERROR: Infinite loop detected at PC=0x" << std::hex << pc << std::dec << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cout << "\n=== Final state ===" << endl;
|
||||
cout << "A: " << machine->getA() << endl;
|
||||
cout << "B: " << machine->getB() << endl;
|
||||
cout << "X: " << machine->getX() << endl;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
#include "opcode.h"
|
||||
#include "instructions.h"
|
||||
#include "utils.h"
|
||||
#include <utility>
|
||||
|
||||
InstructionInfo instructions[0xff];
|
||||
InstructionInfo instructionsEXEX[0xff];
|
||||
|
||||
void loadInstructionSet()
|
||||
{
|
||||
instructions[ADD] = {"ADD", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(add_handler)};
|
||||
instructions[ADDF] = {"ADDF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(addf_handler)};
|
||||
instructions[ADDR] = {"ADDR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(addr_handler)};
|
||||
instructions[AND] = {"AND", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(and_handler)};
|
||||
instructions[CLEAR] = {"CLEAR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(clear_handler)};
|
||||
instructions[COMP] = {"COMP", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(comp_handler)};
|
||||
instructions[COMPF] = {"COMPF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(compf_handler)};
|
||||
instructions[COMPR] = {"COMPR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(compr_handler)};
|
||||
instructions[DIV] = {"DIV", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(div_handler)};
|
||||
instructions[DIVF] = {"DIVF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(divf_handler)};
|
||||
instructions[DIVR] = {"DIVR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(divr_handler)};
|
||||
instructions[FIX] = {"FIX", InstructionType::TYPE1, reinterpret_cast<RawHandler>(fix_handler)};
|
||||
instructions[FLOAT] = {"FLOAT", InstructionType::TYPE1, reinterpret_cast<RawHandler>(float_handler)};
|
||||
instructions[HIO] = {"HIO", InstructionType::TYPE1, nullptr};
|
||||
instructions[J] = {"J", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(j_handler)};
|
||||
instructions[JEQ] = {"JEQ", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(jeq_handler)};
|
||||
instructions[JGT] = {"JGT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(jgt_handler)};
|
||||
instructions[JLT] = {"JLT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(jlt_handler)};
|
||||
instructions[JSUB] = {"JSUB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(jsub_handler)};
|
||||
instructions[LDA] = {"LDA", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(lda_handler)};
|
||||
instructions[LDB] = {"LDB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldb_handler)};
|
||||
instructions[LDCH] = {"LDCH", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldch_handler)};
|
||||
instructions[LDF] = {"LDF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldf_handler)};
|
||||
instructions[LDL] = {"LDL", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldl_handler)};
|
||||
instructions[LDS] = {"LDS", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(lds_handler)};
|
||||
instructions[LDT] = {"LDT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldt_handler)};
|
||||
instructions[LDX] = {"LDX", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldx_handler)};
|
||||
instructions[LPS] = {"LPS", InstructionType::TYPE3_4, nullptr};
|
||||
instructions[MUL] = {"MUL", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(mul_handler)};
|
||||
instructions[MULF] = {"MULF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(mulf_handler)};
|
||||
instructions[MULR] = {"MULR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(mulr_handler)};
|
||||
instructions[NORM] = {"NORM", InstructionType::TYPE1, reinterpret_cast<RawHandler>(norm_handler)};
|
||||
instructions[OR] = {"OR", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(or_handler)};
|
||||
instructions[RD] = {"RD", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(rd_handler)};
|
||||
instructions[RMO] = {"RMO", InstructionType::TYPE2, reinterpret_cast<RawHandler>(rmo_handler)};
|
||||
instructions[RSUB] = {"RSUB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(rsub_handler)};
|
||||
instructions[SHIFTL] = {"SHIFTL", InstructionType::TYPE2, reinterpret_cast<RawHandler>(shiftl_handler)};
|
||||
instructions[SHIFTR] = {"SHIFTR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(shiftr_handler)};
|
||||
instructions[SIO] = {"SIO", InstructionType::TYPE1, nullptr};
|
||||
instructions[SSK] = {"SSK", InstructionType::TYPE3_4, nullptr};
|
||||
instructions[STA] = {"STA", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(sta_handler)};
|
||||
instructions[STB] = {"STB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stb_handler)};
|
||||
instructions[STCH] = {"STCH", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stch_handler)};
|
||||
instructions[STF] = {"STF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stf_handler)};
|
||||
instructions[STI] = {"STI", InstructionType::TYPE3_4, nullptr};
|
||||
instructions[STL] = {"STL", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stl_handler)};
|
||||
instructions[STS] = {"STS", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(sts_handler)};
|
||||
instructions[STSW] = {"STSW", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stsw_handler)};
|
||||
instructions[STT] = {"STT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stt_handler)};
|
||||
instructions[STX] = {"STX", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stx_handler)};
|
||||
instructions[SUB] = {"SUB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(sub_handler)};
|
||||
instructions[SUBF] = {"SUBF", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(subf_handler)};
|
||||
instructions[SUBR] = {"SUBR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(subr_handler)};
|
||||
instructions[SVC] = {"SVC", InstructionType::TYPE2, reinterpret_cast<RawHandler>(svc_handler)};
|
||||
instructions[TIXR] = {"TIXR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(tixr_handler)};
|
||||
instructions[TD] = {"TD", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(td_handler)};
|
||||
instructions[TIX] = {"TIX", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(tix_handler)};
|
||||
instructions[TIO] = {"TIO", InstructionType::TYPE1, nullptr};
|
||||
instructions[WD] = {"WD", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(wd_handler)};
|
||||
|
||||
// Load SIC/XE/XE extended instructions
|
||||
if (USE_EXTENDED_MODE) {
|
||||
// Still in main table
|
||||
instructions[NOP] = {"NOP", InstructionType::TYPE1, reinterpret_cast<RawHandler>(nop_handler)};
|
||||
instructions[HALT] = {"HALT", InstructionType::TYPE1, reinterpret_cast<RawHandler>(halt_handler)};
|
||||
instructions[XEXE] = {"XEXE", InstructionType::TYPE1, reinterpret_cast<RawHandler>(xexe_handler)};
|
||||
|
||||
instructionsEXEX[VADD] = {"VADD", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(vadd_handler)};
|
||||
instructionsEXEX[VADDR] = {"VADDR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(vaddr_handler)};
|
||||
instructionsEXEX[VSUB] = {"VSUB", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(vsub_handler)};
|
||||
instructionsEXEX[VSUBR] = {"VSUBR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(vsubr_handler)};
|
||||
instructionsEXEX[VMUL] = {"VMUL", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(vmul_handler)};
|
||||
instructionsEXEX[VMULR] = {"VMULR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(vmulr_handler)};
|
||||
instructionsEXEX[VDIV] = {"VDIV", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(vdiv_handler)};
|
||||
instructionsEXEX[VDIVR] = {"VDIVR", InstructionType::TYPE2, reinterpret_cast<RawHandler>(vdivr_handler)};
|
||||
instructionsEXEX[STVA] = {"STVA", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stva_handler)};
|
||||
instructionsEXEX[STVS] = {"STVS", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stvs_handler)};
|
||||
instructionsEXEX[STVT] = {"STVT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(stvt_handler)};
|
||||
instructionsEXEX[LDVA] = {"LDVA", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldva_handler)};
|
||||
instructionsEXEX[LDVS] = {"LDVS", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldvs_handler)};
|
||||
instructionsEXEX[LDVT] = {"LDVT", InstructionType::TYPE3_4, reinterpret_cast<RawHandler>(ldvt_handler)};
|
||||
}
|
||||
// Mark uninitialized opcodes as INVALID
|
||||
for (int i = 0; i < 0xff; ++i) {
|
||||
if (instructions[i].name == nullptr) instructions[i] = {"INVALID", InstructionType::INVALID, nullptr};
|
||||
if (instructionsEXEX[i].name == nullptr) instructionsEXEX[i] = {"INVALID", InstructionType::INVALID, nullptr};
|
||||
}
|
||||
}
|
||||
|
||||
AddressingMode getAddressingMode(int ni)
|
||||
{
|
||||
switch (ni) {
|
||||
case 0x0: return AddressingMode::SIC_DIRECT;
|
||||
case 0x1: return AddressingMode::IMMEDIATE;
|
||||
case 0x2: return AddressingMode::INDIRECT;
|
||||
case 0x3: return AddressingMode::SIMPLE;
|
||||
default: return AddressingMode::INVALID; // Should not happen
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
#include "output_device.h"
|
||||
|
||||
OutputDevice::OutputDevice(std::ostream &out)
|
||||
: outStream(out)
|
||||
{
|
||||
}
|
||||
|
||||
OutputDevice::~OutputDevice()
|
||||
{
|
||||
}
|
||||
|
||||
void OutputDevice::write(unsigned char value)
|
||||
{
|
||||
outStream.put(static_cast<char>(value));
|
||||
outStream.flush();
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
#include "string_reader.h"
|
||||
|
||||
StringReader::StringReader(const std::string &s)
|
||||
: in(s)
|
||||
{}
|
||||
|
||||
StringReader::~StringReader() = default;
|
||||
|
||||
int StringReader::readByte() {
|
||||
char c;
|
||||
if (!in.get(c)) return -1;
|
||||
return static_cast<unsigned char>(c);
|
||||
}
|
||||
|
||||
bool StringReader::readBytes(uint8_t* buf, size_t len) {
|
||||
in.read(reinterpret_cast<char*>(buf), static_cast<std::streamsize>(len));
|
||||
return static_cast<size_t>(in.gcount()) == len;
|
||||
}
|
||||
|
||||
std::string StringReader::readString(size_t len) {
|
||||
std::string s;
|
||||
s.resize(len);
|
||||
in.read(reinterpret_cast<char*>(&s[0]), static_cast<std::streamsize>(len));
|
||||
std::streamsize got = in.gcount();
|
||||
if (static_cast<size_t>(got) < len) s.resize(static_cast<size_t>(got));
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string StringReader::readLine() {
|
||||
std::string s;
|
||||
if (!std::getline(in, s)) return std::string();
|
||||
return s;
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
project(simulator_SIC_XE VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Put all build outputs under target/bin
|
||||
|
|
@ -13,6 +13,10 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIR})
|
|||
# Collect all .cpp sources under src/
|
||||
file(GLOB_RECURSE SOURCES "${PROJECT_SOURCE_DIR}/src/*.cpp")
|
||||
|
||||
|
||||
set(MAIN_SRC "${PROJECT_SOURCE_DIR}/src/main.cpp")
|
||||
list(REMOVE_ITEM SOURCES ${MAIN_SRC})
|
||||
|
||||
if(NOT SOURCES)
|
||||
message(WARNING "No source files found in ${PROJECT_SOURCE_DIR}/src — the build will create an empty library")
|
||||
endif()
|
||||
|
|
|
|||
2
simulator_SIC_XE/devices/FA.dev
Normal file
2
simulator_SIC_XE/devices/FA.dev
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
5
|
||||
0
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
#include "../../include/instructions.h"
|
||||
#include "../../include/opcode.h"
|
||||
#include "../../include/constants.h"
|
||||
#include "../../../include/loader.h"
|
||||
#include "../../include/loader.h"
|
||||
|
||||
#include <QIntValidator>
|
||||
#include <QLineEdit>
|
||||
|
|
|
|||
26
simulator_SIC_XE/include/code.h
Normal file
26
simulator_SIC_XE/include/code.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef CODE_H
|
||||
#define CODE_H
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "node.h"
|
||||
|
||||
class Code {
|
||||
|
||||
public:
|
||||
Code() = default;
|
||||
|
||||
void addLine(const std::shared_ptr<Node>& line);
|
||||
|
||||
const std::vector<std::shared_ptr<Node>>& getLines() const;
|
||||
|
||||
const string toString() const;
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Node>> _lines;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // CODE_H
|
||||
55
simulator_SIC_XE/include/lexer.h
Normal file
55
simulator_SIC_XE/include/lexer.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#ifndef LEXER_H
|
||||
#define LEXER_H
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <cstddef>
|
||||
|
||||
class SyntaxError : public std::runtime_error {
|
||||
public:
|
||||
int row;
|
||||
int col;
|
||||
|
||||
SyntaxError(const std::string& msg, int row_, int col_)
|
||||
: std::runtime_error(msg), row(row_), col(col_) {}
|
||||
};
|
||||
|
||||
|
||||
class Lexer {
|
||||
public:
|
||||
int row;
|
||||
int col;
|
||||
|
||||
explicit Lexer(std::string input);
|
||||
|
||||
Lexer& mark();
|
||||
|
||||
std::string extract(int ofs);
|
||||
std::string extract();
|
||||
|
||||
char peek(int ahead) const;
|
||||
char peek() const;
|
||||
|
||||
char advance();
|
||||
|
||||
bool advanceIf(char ch);
|
||||
void advance(char ch);
|
||||
|
||||
|
||||
bool skipWhitespace();
|
||||
|
||||
std::string readTo(char delimiter);
|
||||
|
||||
std::string readAlphanumeric();
|
||||
|
||||
std::string readDigits(int radix);
|
||||
|
||||
private:
|
||||
std::string input_;
|
||||
std::size_t pos_;
|
||||
std::size_t start_;
|
||||
|
||||
static int digitValue(char c, int radix);
|
||||
};
|
||||
|
||||
#endif // LEXER_H
|
||||
45
simulator_SIC_XE/include/mnemonic.h
Normal file
45
simulator_SIC_XE/include/mnemonic.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// mnemonic.h
|
||||
#ifndef MNEMONIC_H
|
||||
#define MNEMONIC_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
|
||||
#include "opcode.h"
|
||||
|
||||
struct Empty {};
|
||||
struct Register { int num; };
|
||||
struct Immediate { int value; };
|
||||
struct SymbolRef {
|
||||
std::string name;
|
||||
bool indexed = false;
|
||||
bool immediate = false;
|
||||
bool indirect = false;
|
||||
};
|
||||
|
||||
using Operand = std::variant<Empty, Register, Immediate, SymbolRef>;
|
||||
|
||||
class Mnemonic {
|
||||
public:
|
||||
Mnemonic(std::uint8_t opcode, InstructionType type, bool extended)
|
||||
: _opcode(opcode), _extended(extended), _type(type) {}
|
||||
|
||||
std::uint8_t opcode() const { return _opcode; }
|
||||
bool extended() const { return _extended; }
|
||||
InstructionType type() const { return _type; }
|
||||
|
||||
std::vector<Operand>& operands() { return _operands; }
|
||||
const std::vector<Operand>& operands() const { return _operands; }
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
private:
|
||||
std::uint8_t _opcode;
|
||||
bool _extended;
|
||||
InstructionType _type;
|
||||
std::vector<Operand> _operands;
|
||||
};
|
||||
|
||||
#endif // MNEMONIC_H
|
||||
98
simulator_SIC_XE/include/node.h
Normal file
98
simulator_SIC_XE/include/node.h
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#ifndef NODE_H
|
||||
#define NODE_H
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include <cstdint>
|
||||
#include "mnemonic.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
class Node {
|
||||
public:
|
||||
virtual ~Node() = default;
|
||||
|
||||
string getLabel() const { return _label; }
|
||||
string getComment() const { return _comment; }
|
||||
std::shared_ptr<Mnemonic> getMnemonic() const { return _mnemonic; }
|
||||
|
||||
virtual string toString() const;
|
||||
|
||||
protected:
|
||||
string _label;
|
||||
std::shared_ptr<Mnemonic> _mnemonic;
|
||||
string _comment;
|
||||
};
|
||||
|
||||
class InstructionNode : public Node {
|
||||
public:
|
||||
InstructionNode(string label,
|
||||
std::shared_ptr<Mnemonic> mnemonic,
|
||||
string comment) {
|
||||
_label = std::move(label);
|
||||
_mnemonic = std::move(mnemonic);
|
||||
_comment = std::move(comment);
|
||||
}
|
||||
|
||||
string toString() const override;
|
||||
};
|
||||
|
||||
class CommentNode : public Node {
|
||||
public:
|
||||
explicit CommentNode(string text) {
|
||||
_comment = std::move(text);
|
||||
}
|
||||
|
||||
string toString() const override;
|
||||
};
|
||||
|
||||
enum class DirectiveKind {
|
||||
START, END, BASE, NOBASE, EQU, ORG, LTORG,
|
||||
EXTDEF, EXTREF, CSECT
|
||||
};
|
||||
|
||||
using DirectiveArg = std::variant<std::monostate, int, std::string, std::vector<std::string>>;
|
||||
|
||||
class DirectiveNode : public Node {
|
||||
public:
|
||||
DirectiveNode(string label, DirectiveKind kind, DirectiveArg arg, string comment)
|
||||
: _kind(kind), _arg(std::move(arg)) {
|
||||
_label = std::move(label);
|
||||
_comment = std::move(comment);
|
||||
}
|
||||
|
||||
DirectiveKind kind() const { return _kind; }
|
||||
const DirectiveArg& arg() const { return _arg; }
|
||||
|
||||
string toString() const override;
|
||||
|
||||
private:
|
||||
DirectiveKind _kind;
|
||||
DirectiveArg _arg;
|
||||
};
|
||||
|
||||
enum class DataKind { WORD, BYTE, RESW, RESB };
|
||||
|
||||
using DataValue = std::variant<std::monostate, int, std::vector<uint8_t>>;
|
||||
|
||||
class DataNode : public Node {
|
||||
public:
|
||||
DataNode(string label, DataKind kind, DataValue value, string comment)
|
||||
: _kind(kind), _value(std::move(value)) {
|
||||
_label = std::move(label);
|
||||
_comment = std::move(comment);
|
||||
}
|
||||
|
||||
DataKind kind() const { return _kind; }
|
||||
const DataValue& value() const { return _value; }
|
||||
|
||||
string toString() const override;
|
||||
|
||||
private:
|
||||
DataKind _kind;
|
||||
DataValue _value;
|
||||
};
|
||||
|
||||
#endif // NODE_H
|
||||
|
|
@ -3,6 +3,10 @@
|
|||
|
||||
#include "utils.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string_view>
|
||||
#include <optional>
|
||||
|
||||
// ==============================
|
||||
// Opcode definitions (SIC/XE)
|
||||
// ==============================
|
||||
|
|
@ -87,6 +91,8 @@
|
|||
#define LDVS 0x68
|
||||
#define LDVT 0x04
|
||||
|
||||
static std::unordered_map<std::string_view, uint8_t> mnemonicToOpcode;
|
||||
static bool opcodeTablesInitialized = false;
|
||||
|
||||
|
||||
enum class InstructionType {
|
||||
|
|
@ -110,6 +116,10 @@ struct InstructionInfo {
|
|||
extern InstructionInfo instructions[];
|
||||
extern InstructionInfo instructionsEXEX[];
|
||||
|
||||
extern std::optional<uint8_t> findOpcodeByMnemonic(std::string_view name);
|
||||
extern const InstructionInfo& getInstructionInfo(uint8_t opcode);
|
||||
|
||||
|
||||
// Initialize the instruction table
|
||||
void loadInstructionSet();
|
||||
|
||||
|
|
|
|||
52
simulator_SIC_XE/include/parser.h
Normal file
52
simulator_SIC_XE/include/parser.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// parser.h
|
||||
#ifndef PARSER_H
|
||||
#define PARSER_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <cstdint>
|
||||
|
||||
#include "lexer.h"
|
||||
#include "code.h"
|
||||
#include "opcode.h"
|
||||
#include "mnemonic.h"
|
||||
|
||||
class Parser {
|
||||
public:
|
||||
Parser() = default;
|
||||
|
||||
Code parse(const std::string& input);
|
||||
|
||||
private:
|
||||
std::string parseLabel();
|
||||
std::shared_ptr<Mnemonic> parseMnemonic();
|
||||
std::string parseSymbol();
|
||||
int parseRegister();
|
||||
void parseComma();
|
||||
bool parseIndexed();
|
||||
int parseNumber(int lo, int hi);
|
||||
std::vector<std::uint8_t> parseData();
|
||||
|
||||
void parseOperands(Mnemonic& m);
|
||||
|
||||
bool isDirective(const std::string& name);
|
||||
bool isDataDirective(const std::string& name);
|
||||
std::shared_ptr<Node> parseDirective(const std::string& label, const std::string& directive);
|
||||
std::shared_ptr<Node> parseDataDirective(const std::string& label, const std::string& directive);
|
||||
|
||||
std::shared_ptr<Node> parseInstruction();
|
||||
Code parseCode();
|
||||
|
||||
std::shared_ptr<Mnemonic> makeMnemonic(const std::string& name, bool extended);
|
||||
static void initMnemonicMap();
|
||||
|
||||
private:
|
||||
Lexer lexer_{""};
|
||||
|
||||
static inline std::unordered_map<std::string, std::uint8_t> s_nameToOpcode{};
|
||||
static inline bool s_mnemonicMapInitialized = false;
|
||||
};
|
||||
|
||||
#endif // PARSER_H
|
||||
20
simulator_SIC_XE/src/code.cpp
Normal file
20
simulator_SIC_XE/src/code.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#include "code.h"
|
||||
|
||||
void Code::addLine(const std::shared_ptr<Node> &line)
|
||||
{
|
||||
_lines.emplace_back(line);
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<Node>> &Code::getLines() const
|
||||
{
|
||||
return _lines;
|
||||
}
|
||||
|
||||
const string Code::toString() const
|
||||
{
|
||||
string result;
|
||||
for (const auto& line : _lines) {
|
||||
result += line->toString() + "\n";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
138
simulator_SIC_XE/src/lexer.cpp
Normal file
138
simulator_SIC_XE/src/lexer.cpp
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
#include "lexer.h"
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
|
||||
Lexer::Lexer(std::string input)
|
||||
: input_(std::move(input)),
|
||||
pos_(0),
|
||||
start_(0),
|
||||
row(1),
|
||||
col(1)
|
||||
{
|
||||
}
|
||||
|
||||
Lexer& Lexer::mark() {
|
||||
start_ = pos_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string Lexer::extract(int ofs) {
|
||||
std::size_t end = pos_ + static_cast<std::size_t>(ofs);
|
||||
if (end > input_.size()) {
|
||||
end = input_.size();
|
||||
}
|
||||
if (end < start_) {
|
||||
end = start_;
|
||||
}
|
||||
return input_.substr(start_, end - start_);
|
||||
}
|
||||
|
||||
std::string Lexer::extract() {
|
||||
return extract(0);
|
||||
}
|
||||
|
||||
char Lexer::peek(int ahead) const {
|
||||
std::size_t idx = pos_ + static_cast<std::size_t>(ahead);
|
||||
if (idx < input_.size()) {
|
||||
return input_[idx];
|
||||
}
|
||||
return '\0'; // sentinel for "no more chars"
|
||||
}
|
||||
|
||||
char Lexer::peek() const {
|
||||
return peek(0);
|
||||
}
|
||||
|
||||
char Lexer::advance() {
|
||||
char ch = peek();
|
||||
if (ch == '\0') {
|
||||
return '\0'; // don't move past end
|
||||
}
|
||||
|
||||
++pos_;
|
||||
|
||||
// update logical location
|
||||
if (ch == '\n') {
|
||||
++row;
|
||||
col = 1;
|
||||
} else if (ch == '\t') {
|
||||
col = ((col - 1) / 4) * 4 + 5;
|
||||
} else {
|
||||
++col;
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
bool Lexer::advanceIf(char ch) {
|
||||
if (peek() != ch) {
|
||||
return false;
|
||||
}
|
||||
advance();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Lexer::advance(char ch) {
|
||||
if (!advanceIf(ch)) {
|
||||
throw SyntaxError(std::string("'") + ch + "' expected", row, col);
|
||||
}
|
||||
}
|
||||
|
||||
bool Lexer::skipWhitespace() {
|
||||
while (true) {
|
||||
char p = peek();
|
||||
if (p == ' ' || p == '\t') {
|
||||
advance();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
char p = peek();
|
||||
return (p == '\n' || p == '\0');
|
||||
}
|
||||
|
||||
std::string Lexer::readTo(char delimiter) {
|
||||
mark();
|
||||
while (peek() > 0 && peek() != delimiter) {
|
||||
advance();
|
||||
}
|
||||
if (peek() == delimiter) {
|
||||
advance(); // consume delimiter
|
||||
}
|
||||
// exclude delimiter itself (like Java's extract(-1))
|
||||
return extract(-1);
|
||||
}
|
||||
|
||||
std::string Lexer::readAlphanumeric() {
|
||||
mark();
|
||||
while (true) {
|
||||
char c = peek();
|
||||
if (std::isalnum(static_cast<unsigned char>(c)) || c == '_') {
|
||||
advance();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return extract();
|
||||
}
|
||||
|
||||
int Lexer::digitValue(char c, int radix) {
|
||||
if (radix < 2 || radix > 36) return -1;
|
||||
int v = -1;
|
||||
if (c >= '0' && c <= '9') {
|
||||
v = c - '0';
|
||||
} else if (c >= 'A' && c <= 'Z') {
|
||||
v = c - 'A' + 10;
|
||||
} else if (c >= 'a' && c <= 'z') {
|
||||
v = c - 'a' + 10;
|
||||
}
|
||||
if (v >= 0 && v < radix) return v;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string Lexer::readDigits(int radix) {
|
||||
mark();
|
||||
while (digitValue(peek(), radix) != -1) {
|
||||
advance();
|
||||
}
|
||||
return extract();
|
||||
}
|
||||
120
simulator_SIC_XE/src/node.cpp
Normal file
120
simulator_SIC_XE/src/node.cpp
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
#include "node.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
string Node::toString() const {
|
||||
std::ostringstream oss;
|
||||
if (!_label.empty()) oss << _label << " ";
|
||||
if (_mnemonic) oss << _mnemonic->toString() << " ";
|
||||
if (!_comment.empty()) oss << "." << _comment;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string Mnemonic::toString() const {
|
||||
std::ostringstream oss;
|
||||
oss << "[OP:" << std::hex << (int)_opcode << "]";
|
||||
if (_extended) oss << "+";
|
||||
// Print operands
|
||||
for (size_t i = 0; i < _operands.size(); ++i) {
|
||||
if (i > 0) oss << ",";
|
||||
std::visit([&](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, Empty>) {
|
||||
// nothing
|
||||
} else if constexpr (std::is_same_v<T, Register>) {
|
||||
oss << "R" << arg.num;
|
||||
} else if constexpr (std::is_same_v<T, Immediate>) {
|
||||
oss << "#" << arg.value;
|
||||
} else if constexpr (std::is_same_v<T, SymbolRef>) {
|
||||
oss << arg.name;
|
||||
if (arg.indexed) oss << ",X";
|
||||
}
|
||||
}, _operands[i]);
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
string InstructionNode::toString() const {
|
||||
std::ostringstream oss;
|
||||
if (!_label.empty()) oss << _label << " ";
|
||||
if (_mnemonic) oss << _mnemonic->toString();
|
||||
if (!_comment.empty()) oss << " ." << _comment;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
string CommentNode::toString() const {
|
||||
return "." + _comment;
|
||||
}
|
||||
|
||||
string DirectiveNode::toString() const {
|
||||
std::ostringstream oss;
|
||||
if (!_label.empty()) oss << _label << " ";
|
||||
switch (_kind) {
|
||||
case DirectiveKind::START: oss << "START"; break;
|
||||
case DirectiveKind::END: oss << "END"; break;
|
||||
case DirectiveKind::BASE: oss << "BASE"; break;
|
||||
case DirectiveKind::NOBASE: oss << "NOBASE"; break;
|
||||
case DirectiveKind::EQU: oss << "EQU"; break;
|
||||
case DirectiveKind::ORG: oss << "ORG"; break;
|
||||
case DirectiveKind::LTORG: oss << "LTORG"; break;
|
||||
case DirectiveKind::EXTDEF: oss << "EXTDEF"; break;
|
||||
case DirectiveKind::EXTREF: oss << "EXTREF"; break;
|
||||
case DirectiveKind::CSECT: oss << "CSECT"; break;
|
||||
}
|
||||
std::visit([&](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, std::monostate>) {
|
||||
// no arg
|
||||
} else if constexpr (std::is_same_v<T, int>) {
|
||||
oss << " " << std::hex << arg;
|
||||
} else if constexpr (std::is_same_v<T, std::string>) {
|
||||
oss << " " << arg;
|
||||
} else if constexpr (std::is_same_v<T, std::vector<std::string>>) {
|
||||
for (size_t i = 0; i < arg.size(); ++i) {
|
||||
if (i > 0) oss << ",";
|
||||
oss << arg[i];
|
||||
}
|
||||
}
|
||||
}, _arg);
|
||||
if (!_comment.empty()) oss << " ." << _comment;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
string DataNode::toString() const {
|
||||
std::ostringstream oss;
|
||||
if (!_label.empty()) oss << _label << " ";
|
||||
switch (_kind) {
|
||||
case DataKind::WORD: oss << "WORD"; break;
|
||||
case DataKind::BYTE: oss << "BYTE"; break;
|
||||
case DataKind::RESW: oss << "RESW"; break;
|
||||
case DataKind::RESB: oss << "RESB"; break;
|
||||
}
|
||||
std::visit([&](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, std::monostate>) {
|
||||
// no value
|
||||
} else if constexpr (std::is_same_v<T, int>) {
|
||||
oss << " " << arg;
|
||||
} else if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
|
||||
// Try to display as string if all printable ASCII
|
||||
bool isPrintable = !arg.empty() && std::all_of(arg.begin(), arg.end(),
|
||||
[](uint8_t b) { return b >= 32 && b <= 126; });
|
||||
|
||||
if (isPrintable) {
|
||||
oss << " C'";
|
||||
for (uint8_t b : arg) oss << static_cast<char>(b);
|
||||
oss << "'";
|
||||
} else {
|
||||
// Display as hex
|
||||
oss << " X'";
|
||||
for (uint8_t b : arg) {
|
||||
oss << std::hex << std::setw(2) << std::setfill('0') << (int)b;
|
||||
}
|
||||
oss << "'";
|
||||
}
|
||||
}
|
||||
}, _value);
|
||||
if (!_comment.empty()) oss << " ." << _comment;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
|
|
@ -95,8 +95,36 @@ void loadInstructionSet()
|
|||
if (instructions[i].name == nullptr) instructions[i] = {"INVALID", InstructionType::INVALID, nullptr};
|
||||
if (instructionsEXEX[i].name == nullptr) instructionsEXEX[i] = {"INVALID", InstructionType::INVALID, nullptr};
|
||||
}
|
||||
|
||||
// Initialize mnemonicToOpcode map
|
||||
for (int i = 0; i < 0xff; ++i) {
|
||||
if (instructions[i].type != InstructionType::INVALID) {
|
||||
mnemonicToOpcode.emplace(instructions[i].name, static_cast<uint8_t>(i));
|
||||
}
|
||||
if (instructionsEXEX[i].type != InstructionType::INVALID) {
|
||||
mnemonicToOpcode.emplace(instructionsEXEX[i].name, static_cast<uint8_t>(i));
|
||||
}
|
||||
}
|
||||
opcodeTablesInitialized = true;
|
||||
}
|
||||
|
||||
std::optional<uint8_t> findOpcodeByMnemonic(std::string_view name)
|
||||
{
|
||||
auto it = mnemonicToOpcode.find(name);
|
||||
if (it == mnemonicToOpcode.end())
|
||||
return std::nullopt;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
const InstructionInfo& getInstructionInfo(uint8_t opcode)
|
||||
{
|
||||
if (instructions[opcode].type != InstructionType::INVALID)
|
||||
return instructions[opcode];
|
||||
return instructionsEXEX[opcode];
|
||||
}
|
||||
|
||||
|
||||
|
||||
AddressingMode getAddressingMode(int ni)
|
||||
{
|
||||
switch (ni) {
|
||||
|
|
|
|||
449
simulator_SIC_XE/src/parser.cpp
Normal file
449
simulator_SIC_XE/src/parser.cpp
Normal file
|
|
@ -0,0 +1,449 @@
|
|||
// parser.cpp
|
||||
#include "parser.h"
|
||||
#include <cctype>
|
||||
#include <limits>
|
||||
#include <string_view>
|
||||
|
||||
void Parser::initMnemonicMap() {
|
||||
if (s_mnemonicMapInitialized) return;
|
||||
|
||||
loadInstructionSet();
|
||||
|
||||
for (int op = 0; op < 0xFF; ++op) {
|
||||
const auto& info = instructions[op];
|
||||
if (info.name && info.type != InstructionType::INVALID) {
|
||||
s_nameToOpcode.emplace(info.name, static_cast<std::uint8_t>(op));
|
||||
}
|
||||
const auto& ex = instructionsEXEX[op];
|
||||
if (ex.name && ex.type != InstructionType::INVALID) {
|
||||
s_nameToOpcode.emplace(ex.name, static_cast<std::uint8_t>(op));
|
||||
}
|
||||
}
|
||||
|
||||
s_mnemonicMapInitialized = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<Mnemonic> Parser::makeMnemonic(const std::string& name, bool extended) {
|
||||
initMnemonicMap();
|
||||
|
||||
auto it = s_nameToOpcode.find(name);
|
||||
if (it == s_nameToOpcode.end()) {
|
||||
throw SyntaxError("Invalid mnemonic '" + name + "'", lexer_.row, lexer_.col);
|
||||
}
|
||||
|
||||
std::uint8_t opcode = it->second;
|
||||
const InstructionInfo* info = nullptr;
|
||||
|
||||
if (instructions[opcode].type != InstructionType::INVALID) {
|
||||
info = &instructions[opcode];
|
||||
} else if (instructionsEXEX[opcode].type != InstructionType::INVALID) {
|
||||
info = &instructionsEXEX[opcode];
|
||||
}
|
||||
|
||||
if (!info) {
|
||||
throw SyntaxError("Invalid mnemonic '" + name + "'", lexer_.row, lexer_.col);
|
||||
}
|
||||
|
||||
if (extended && info->type != InstructionType::TYPE3_4) {
|
||||
throw SyntaxError(
|
||||
"Extended format not allowed for mnemonic '" + name + "'",
|
||||
lexer_.row,
|
||||
lexer_.col
|
||||
);
|
||||
}
|
||||
|
||||
return std::make_shared<Mnemonic>(opcode, info->type, extended);
|
||||
}
|
||||
|
||||
std::string Parser::parseLabel() {
|
||||
if (lexer_.col == 1 && std::isalpha(static_cast<unsigned char>(lexer_.peek()))) {
|
||||
return std::string(lexer_.readAlphanumeric());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::shared_ptr<Mnemonic> Parser::parseMnemonic() {
|
||||
bool isExtended = lexer_.advanceIf('+');
|
||||
std::string name(lexer_.readAlphanumeric());
|
||||
if (name.empty()) {
|
||||
throw SyntaxError("Mnemonic expected", lexer_.row, lexer_.col);
|
||||
}
|
||||
return makeMnemonic(name, isExtended);
|
||||
}
|
||||
|
||||
std::string Parser::parseSymbol() {
|
||||
return std::string(lexer_.readAlphanumeric());
|
||||
}
|
||||
|
||||
int Parser::parseRegister() {
|
||||
char ch = lexer_.advance();
|
||||
constexpr std::string_view regs = "AXLBSTF";
|
||||
auto pos = regs.find(ch);
|
||||
if (pos == std::string_view::npos) {
|
||||
throw SyntaxError(std::string("Invalid register '") + ch + "'", lexer_.row, lexer_.col);
|
||||
}
|
||||
return static_cast<int>(pos);
|
||||
}
|
||||
|
||||
void Parser::parseComma() {
|
||||
lexer_.skipWhitespace();
|
||||
lexer_.advance(',');
|
||||
lexer_.skipWhitespace();
|
||||
}
|
||||
|
||||
bool Parser::parseIndexed() {
|
||||
lexer_.skipWhitespace();
|
||||
if (lexer_.advanceIf(',')) {
|
||||
lexer_.skipWhitespace();
|
||||
lexer_.advance('X');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int digitValue(char c, int radix) {
|
||||
if (radix < 2 || radix > 36) return -1;
|
||||
int v = -1;
|
||||
if (c >= '0' && c <= '9') v = c - '0';
|
||||
else if (c >= 'A' && c <= 'Z') v = c - 'A' + 10;
|
||||
else if (c >= 'a' && c <= 'z') v = c - 'a' + 10;
|
||||
if (v >= 0 && v < radix) return v;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Parser::parseNumber(int lo, int hi) {
|
||||
auto parseDigits = [&](int radix) -> int {
|
||||
std::string digits(lexer_.readDigits(radix));
|
||||
if (digits.empty()) {
|
||||
throw SyntaxError("Invalid number", lexer_.row, lexer_.col);
|
||||
}
|
||||
|
||||
long long value = 0;
|
||||
for (char c : digits) {
|
||||
int d = digitValue(c, radix);
|
||||
if (d < 0) throw SyntaxError("Invalid number", lexer_.row, lexer_.col);
|
||||
value = value * radix + d;
|
||||
if (value > std::numeric_limits<int>::max()) {
|
||||
throw SyntaxError("Invalid number", lexer_.row, lexer_.col);
|
||||
}
|
||||
}
|
||||
return static_cast<int>(value);
|
||||
};
|
||||
|
||||
int num = 0;
|
||||
|
||||
if (lexer_.peek() == '0') {
|
||||
int radix = -1;
|
||||
switch (lexer_.peek(1)) {
|
||||
case 'b': radix = 2; break;
|
||||
case 'o': radix = 8; break;
|
||||
case 'x': radix = 16; break;
|
||||
default: break;
|
||||
}
|
||||
if (radix != -1) {
|
||||
lexer_.advance();
|
||||
lexer_.advance();
|
||||
num = parseDigits(radix);
|
||||
} else {
|
||||
num = parseDigits(10);
|
||||
}
|
||||
} else if (std::isdigit(static_cast<unsigned char>(lexer_.peek()))) {
|
||||
num = parseDigits(10);
|
||||
} else {
|
||||
throw SyntaxError("Number expected", lexer_.row, lexer_.col);
|
||||
}
|
||||
|
||||
if (std::isalnum(static_cast<unsigned char>(lexer_.peek()))) {
|
||||
throw SyntaxError(
|
||||
std::string("invalid digit '") + lexer_.peek() + "'",
|
||||
lexer_.row,
|
||||
lexer_.col
|
||||
);
|
||||
}
|
||||
|
||||
if (num < lo || num > hi) {
|
||||
throw SyntaxError(
|
||||
"Number '" + std::to_string(num) + "' out of range [" +
|
||||
std::to_string(lo) + ".." + std::to_string(hi) + "]",
|
||||
lexer_.row,
|
||||
lexer_.col
|
||||
);
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> Parser::parseData() {
|
||||
if (lexer_.advanceIf('C')) {
|
||||
lexer_.advance('\'');
|
||||
std::string s(lexer_.readTo('\''));
|
||||
std::vector<std::uint8_t> data;
|
||||
data.reserve(s.size());
|
||||
for (unsigned char c : s) {
|
||||
data.push_back(static_cast<std::uint8_t>(c));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
if (lexer_.advanceIf('X')) {
|
||||
lexer_.advance('\'');
|
||||
std::string s(lexer_.readTo('\''));
|
||||
if (s.size() % 2 != 0) {
|
||||
throw SyntaxError("Invalid hex literal length", lexer_.row, lexer_.col);
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> data;
|
||||
data.reserve(s.size() / 2);
|
||||
|
||||
auto hexVal = [](char c) -> int {
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
return -1;
|
||||
};
|
||||
|
||||
for (std::size_t i = 0; i < s.size(); i += 2) {
|
||||
int hi = hexVal(s[i]);
|
||||
int lo = hexVal(s[i + 1]);
|
||||
if (hi < 0 || lo < 0) {
|
||||
throw SyntaxError("Invalid hex digit in literal", lexer_.row, lexer_.col);
|
||||
}
|
||||
data.push_back(static_cast<std::uint8_t>((hi << 4) | lo));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
if (std::isdigit(static_cast<unsigned char>(lexer_.peek()))) {
|
||||
constexpr int MAX_WORD = 0xFFFFFF;
|
||||
int num = parseNumber(0, MAX_WORD);
|
||||
return {
|
||||
static_cast<std::uint8_t>((num >> 16) & 0xFF),
|
||||
static_cast<std::uint8_t>((num >> 8) & 0xFF),
|
||||
static_cast<std::uint8_t>(num & 0xFF)
|
||||
};
|
||||
}
|
||||
|
||||
throw SyntaxError(
|
||||
std::string("Invalid storage specifier '") + lexer_.peek() + "'",
|
||||
lexer_.row,
|
||||
lexer_.col
|
||||
);
|
||||
}
|
||||
|
||||
void Parser::parseOperands(Mnemonic& m) {
|
||||
InstructionType t = m.type();
|
||||
char c = lexer_.peek();
|
||||
|
||||
if (t == InstructionType::TYPE1) {
|
||||
// TYPE1 has no operands
|
||||
return;
|
||||
}
|
||||
|
||||
if (t == InstructionType::TYPE2) {
|
||||
// TYPE2: r1 or r1,r2 or r1,n
|
||||
if (c == '\n' || c == '\0') return;
|
||||
|
||||
int r1 = parseRegister();
|
||||
m.operands().emplace_back(Register{r1});
|
||||
lexer_.skipWhitespace();
|
||||
|
||||
if (lexer_.peek() == ',') {
|
||||
parseComma();
|
||||
char c2 = lexer_.peek();
|
||||
if (std::isalpha(static_cast<unsigned char>(c2))) {
|
||||
int r2 = parseRegister();
|
||||
m.operands().emplace_back(Register{r2});
|
||||
} else if (std::isdigit(static_cast<unsigned char>(c2))) {
|
||||
int n = parseNumber(0, 0xFFFF);
|
||||
m.operands().emplace_back(Immediate{n});
|
||||
} else {
|
||||
throw SyntaxError("Invalid second operand", lexer_.row, lexer_.col);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (t == InstructionType::TYPE3_4) {
|
||||
lexer_.skipWhitespace();
|
||||
char c0 = lexer_.peek();
|
||||
if (c0 == '\n' || c0 == '\0') {
|
||||
// No operand (e.g., RSUB)
|
||||
return;
|
||||
}
|
||||
|
||||
bool immediate = false;
|
||||
bool indirect = false;
|
||||
|
||||
if (lexer_.advanceIf('#')) {
|
||||
immediate = true;
|
||||
} else if (lexer_.advanceIf('@')) {
|
||||
indirect = true;
|
||||
}
|
||||
|
||||
char c1 = lexer_.peek();
|
||||
if (std::isdigit(static_cast<unsigned char>(c1))) {
|
||||
int num = parseNumber(0, 0x7FFFFF);
|
||||
if (immediate) {
|
||||
m.operands().emplace_back(Immediate{num});
|
||||
} else {
|
||||
// Direct numeric addressing (rare, treat as immediate)
|
||||
m.operands().emplace_back(Immediate{num});
|
||||
}
|
||||
} else if (std::isalpha(static_cast<unsigned char>(c1))) {
|
||||
std::string symbol = parseSymbol();
|
||||
bool indexed = parseIndexed();
|
||||
m.operands().emplace_back(SymbolRef{symbol, indexed, immediate, indirect});
|
||||
} else {
|
||||
throw SyntaxError("Invalid operand", lexer_.row, lexer_.col);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool Parser::isDirective(const std::string& name) {
|
||||
return name == "START" || name == "END" || name == "BASE" || name == "NOBASE" ||
|
||||
name == "EQU" || name == "ORG" || name == "LTORG" ||
|
||||
name == "EXTDEF" || name == "EXTREF" || name == "CSECT";
|
||||
}
|
||||
|
||||
bool Parser::isDataDirective(const std::string& name) {
|
||||
return name == "WORD" || name == "BYTE" || name == "RESW" || name == "RESB";
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> Parser::parseDirective(const std::string& label, const std::string& directive) {
|
||||
lexer_.skipWhitespace();
|
||||
|
||||
DirectiveArg argValue;
|
||||
char c = lexer_.peek();
|
||||
|
||||
// Parse argument based on first character
|
||||
if (std::isalpha(c)) {
|
||||
std::string arg = std::string(lexer_.readAlphanumeric());
|
||||
argValue = arg;
|
||||
} else if (std::isdigit(c) || c == '0') {
|
||||
int num = parseNumber(0, 0xFFFFFF);
|
||||
argValue = num;
|
||||
} else {
|
||||
// No argument
|
||||
argValue = std::monostate{};
|
||||
}
|
||||
|
||||
lexer_.skipWhitespace();
|
||||
std::string comment = std::string(lexer_.readTo('\n'));
|
||||
|
||||
DirectiveKind kind;
|
||||
if (directive == "START") kind = DirectiveKind::START;
|
||||
else if (directive == "END") kind = DirectiveKind::END;
|
||||
else if (directive == "BASE") kind = DirectiveKind::BASE;
|
||||
else if (directive == "NOBASE") kind = DirectiveKind::NOBASE;
|
||||
else if (directive == "EQU") kind = DirectiveKind::EQU;
|
||||
else if (directive == "ORG") kind = DirectiveKind::ORG;
|
||||
else if (directive == "LTORG") kind = DirectiveKind::LTORG;
|
||||
else if (directive == "EXTDEF") kind = DirectiveKind::EXTDEF;
|
||||
else if (directive == "EXTREF") kind = DirectiveKind::EXTREF;
|
||||
else if (directive == "CSECT") kind = DirectiveKind::CSECT;
|
||||
else throw SyntaxError("Unknown directive", lexer_.row, lexer_.col);
|
||||
|
||||
return std::make_shared<DirectiveNode>(label, kind, argValue, comment);
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> Parser::parseDataDirective(const std::string& label, const std::string& directive) {
|
||||
lexer_.skipWhitespace();
|
||||
|
||||
DataKind kind;
|
||||
if (directive == "WORD") kind = DataKind::WORD;
|
||||
else if (directive == "BYTE") kind = DataKind::BYTE;
|
||||
else if (directive == "RESW") kind = DataKind::RESW;
|
||||
else if (directive == "RESB") kind = DataKind::RESB;
|
||||
else throw SyntaxError("Unknown data directive", lexer_.row, lexer_.col);
|
||||
|
||||
DataValue value;
|
||||
if (kind == DataKind::WORD || kind == DataKind::RESW || kind == DataKind::RESB) {
|
||||
int num = parseNumber(0, 0xFFFFFF);
|
||||
value = num;
|
||||
} else { // BYTE
|
||||
auto bytes = parseData();
|
||||
value = bytes;
|
||||
}
|
||||
|
||||
lexer_.skipWhitespace();
|
||||
std::string comment = std::string(lexer_.readTo('\n'));
|
||||
|
||||
return std::make_shared<DataNode>(label, kind, value, comment);
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> Parser::parseInstruction() {
|
||||
if (lexer_.col == 1 && lexer_.peek() == '.') {
|
||||
return std::make_shared<CommentNode>(
|
||||
std::string(lexer_.readTo('\n'))
|
||||
);
|
||||
}
|
||||
|
||||
std::string label = parseLabel();
|
||||
|
||||
if (lexer_.skipWhitespace() && label.empty()) {
|
||||
lexer_.advance();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
lexer_.skipWhitespace();
|
||||
|
||||
// Check for extended format prefix
|
||||
bool isExtended = lexer_.peek() == '+';
|
||||
if (isExtended) {
|
||||
lexer_.advance();
|
||||
}
|
||||
|
||||
std::string name = std::string(lexer_.readAlphanumeric());
|
||||
|
||||
if (name.empty()) {
|
||||
throw SyntaxError("Mnemonic or directive expected", lexer_.row, lexer_.col);
|
||||
}
|
||||
|
||||
// Check if it's a directive or data directive
|
||||
if (isDirective(name)) {
|
||||
return parseDirective(label, name);
|
||||
}
|
||||
|
||||
if (isDataDirective(name)) {
|
||||
return parseDataDirective(label, name);
|
||||
}
|
||||
|
||||
// It's an instruction - create mnemonic
|
||||
auto mnemonic = makeMnemonic(name, isExtended);
|
||||
lexer_.skipWhitespace();
|
||||
|
||||
parseOperands(*mnemonic);
|
||||
lexer_.skipWhitespace();
|
||||
|
||||
std::string comment(lexer_.readTo('\n'));
|
||||
|
||||
return std::make_shared<InstructionNode>(
|
||||
std::move(label),
|
||||
std::move(mnemonic),
|
||||
std::move(comment)
|
||||
);
|
||||
}
|
||||
|
||||
Code Parser::parseCode() {
|
||||
Code code;
|
||||
|
||||
while (lexer_.peek() > 0) {
|
||||
while (lexer_.peek() > 0 && lexer_.col > 1) {
|
||||
lexer_.readTo('\n');
|
||||
}
|
||||
|
||||
if (auto node = parseInstruction()) {
|
||||
code.addLine(node);
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
Code Parser::parse(const std::string& input) {
|
||||
lexer_ = Lexer(input);
|
||||
return parseCode();
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
cat START 0
|
||||
|
||||
CLEAR X
|
||||
|
||||
|
||||
read RD #0
|
||||
STCH BUFF, X
|
||||
Loading…
Add table
Add a link
Reference in a new issue