First commit
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.phpdoc/
|
||||
.phplint.cache/
|
||||
.phpunit.cache/
|
||||
output/
|
||||
vendor/
|
50
.phpcs.xml
Normal file
50
.phpcs.xml
Normal file
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="PHP_CodeSniffer" xsi:noNamespaceSchemaLocation="phpcs.xsd">
|
||||
<description>Coding standard</description>
|
||||
|
||||
<file>src/</file>
|
||||
|
||||
<rule ref="PSR1">
|
||||
<exclude name="Generic.Files.LineLength"/>
|
||||
</rule>
|
||||
<rule ref="PSR2"></rule>
|
||||
<rule ref="PSR12"></rule>
|
||||
|
||||
<rule ref="Generic">
|
||||
<exclude name="Generic.WhiteSpace.DisallowSpaceIndent.SpacesUsed"/>
|
||||
<exclude name="Generic.Files.LowercasedFilename.NotFound"/>
|
||||
<exclude name="Generic.PHP.ClosingPHPTag.NotFound"/>
|
||||
<exclude name="Generic.Files.EndFileNoNewline.Found"/>
|
||||
<exclude name="Generic.Files.EndFileNoNewline.Found"/>
|
||||
<exclude name="Generic.Arrays.DisallowShortArraySyntax.Found"/>
|
||||
<exclude name="Generic.Functions.OpeningFunctionBraceKernighanRitchie.BraceOnNewLine"/>
|
||||
<exclude name="Generic.Classes.OpeningBraceSameLine.BraceOnNewLine"/>
|
||||
<exclude name="Generic.PHP.LowerCaseConstant.Found"/>
|
||||
<exclude name="Generic.Formatting.SpaceAfterCast"/>
|
||||
<exclude name="Generic.Formatting.MultipleStatementAlignment.NotSameWarning"/>
|
||||
<exclude name="Generic.Commenting.DocComment.MissingShort"/>
|
||||
<exclude name="Generic.NamingConventions.AbstractClassNamePrefix.Missing"/>
|
||||
<exclude name="Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed"/>
|
||||
<exclude name="Generic.NamingConventions.InterfaceNameSuffix.Missing"/>
|
||||
<exclude name="Generic.Commenting.Todo.TaskFound"/>
|
||||
<exclude name="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUse"/>
|
||||
<exclude name="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed"/>
|
||||
<exclude name="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed"/>
|
||||
<exclude name="Generic.Formatting.SpaceBeforeCast.NoSpace"/>
|
||||
<exclude name="Generic.CodeAnalysis.UselessOverridingMethod.Found"/>
|
||||
<exclude name="Squiz.Functions.MultiLineFunctionDeclaration.NewlineBeforeOpenBrace"/>
|
||||
</rule>
|
||||
|
||||
<!-- Ban some functions -->
|
||||
<rule ref="Generic.PHP.ForbiddenFunctions">
|
||||
<properties>
|
||||
<property name="forbiddenFunctions" type="array">
|
||||
<element key="sizeof" value="count"/>
|
||||
<element key="delete" value="unset"/>
|
||||
<element key="print" value="echo"/>
|
||||
<element key="is_null" value="null"/>
|
||||
<element key="create_function" value="null"/>
|
||||
</property>
|
||||
</properties>
|
||||
</rule>
|
||||
</ruleset>
|
0
CHANGELOG.md
Normal file
0
CHANGELOG.md
Normal file
0
CONTRIBUTING.md
Normal file
0
CONTRIBUTING.md
Normal file
0
LICENSE.md
Normal file
0
LICENSE.md
Normal file
7
README.md
Normal file
7
README.md
Normal file
@ -0,0 +1,7 @@
|
||||
SIMAVRPHP
|
||||
==========
|
||||
|
||||
|
||||
What
|
||||
-----
|
||||
Implementing an ATmega328p simulator in PHP. Just because.
|
20
bin/simavr.php
Normal file
20
bin/simavr.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
require_once("vendor/autoload.php");
|
||||
|
||||
|
||||
use Monolog\Level;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Handler\FirePHPHandler;
|
||||
|
||||
// Create the logger
|
||||
$logger = new Logger('simavrphp');
|
||||
// Now add some handlers
|
||||
$logger->pushHandler(new StreamHandler('php://stderr'));
|
||||
|
||||
$avr = new SimAVRPHP\AVR($logger);
|
||||
$mem = file_get_contents($argv[1]);
|
||||
$avr->setMemory($mem);
|
||||
while(true){
|
||||
$avr->step();
|
||||
}
|
48
composer.json
Normal file
48
composer.json
Normal file
@ -0,0 +1,48 @@
|
||||
{
|
||||
"require": {
|
||||
"php": "^8.3",
|
||||
"monolog/monolog": "^3.7"
|
||||
},
|
||||
"require-dev": {
|
||||
"vimeo/psalm": "^5.15",
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"phpunit/phpunit": "^10.3",
|
||||
"phpmetrics/phpmetrics": "^2.8",
|
||||
"rector/rector": "^1.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SimAVRPHP\\": "src/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpunit",
|
||||
"document": "phpDocumentor",
|
||||
"benchmark": "phpbench run --report=default --output=build-artifact",
|
||||
"metrics": "vendor/bin/phpmetrics --config=phpmetrics.json",
|
||||
"lint": [
|
||||
"phplint",
|
||||
"phpcs",
|
||||
"phpmd src/,tests/ text phpmd.ruleset.xml"
|
||||
],
|
||||
"analyze": [
|
||||
"@analyze-phpstan",
|
||||
"@analyze-psalm",
|
||||
"@analyze-rector"
|
||||
],
|
||||
"analyze-phpstan":"vendor/bin/phpstan analyze --error-format=raw",
|
||||
"analyze-psalm": "vendor/bin/psalm --no-cache --show-info=true",
|
||||
"analyze-rector": "vendor/bin/rector --dry-run",
|
||||
"html": [
|
||||
"pandoc -s README.md -o output/README.html"
|
||||
],
|
||||
"all": [
|
||||
"@test",
|
||||
"@lint",
|
||||
"@analyze",
|
||||
"@document",
|
||||
"@metrics",
|
||||
"@html"
|
||||
]
|
||||
}
|
||||
}
|
3880
composer.lock
generated
Normal file
3880
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
BIN
composer.phar
Normal file
BIN
composer.phar
Normal file
Binary file not shown.
20
phpdoc.xml
Normal file
20
phpdoc.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<phpdocumentor
|
||||
configVersion="3"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://www.phpdoc.org"
|
||||
xsi:noNamespaceSchemaLocation="https://docs.phpdoc.org/latest/phpdoc.xsd"
|
||||
>
|
||||
<title>trueskill</title>
|
||||
<paths>
|
||||
<output>output/docs</output>
|
||||
</paths>
|
||||
<version number="latest">
|
||||
<api>
|
||||
<source>
|
||||
<path>src</path>
|
||||
</source>
|
||||
</api>
|
||||
</version>
|
||||
<!--setting name="graphs.enabled" value="true"/-->
|
||||
</phpdocumentor>
|
47
phpmd.ruleset.xml
Normal file
47
phpmd.ruleset.xml
Normal file
@ -0,0 +1,47 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset name="Custom PHPMD rules"
|
||||
xmlns="http://pmd.sf.net/ruleset/1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0
|
||||
http://pmd.sf.net/ruleset_xml_schema.xsd"
|
||||
xsi:noNamespaceSchemaLocation="
|
||||
http://pmd.sf.net/ruleset_xml_schema.xsd">
|
||||
<description>
|
||||
Custom PHPMD rules
|
||||
</description>
|
||||
|
||||
<rule ref="rulesets/cleancode.xml">
|
||||
<exclude name="StaticAccess" />
|
||||
<exclude name="ElseExpression" />
|
||||
</rule>
|
||||
<rule ref="rulesets/codesize.xml" >
|
||||
<exclude name="TooManyMethods" />
|
||||
<exclude name="TooManyPublicMethods" />
|
||||
</rule>
|
||||
<rule ref="rulesets/codesize.xml/TooManyMethods">
|
||||
<priority>1</priority>
|
||||
<properties>
|
||||
<property name="ignorepattern" value="#^(set|get|test)|test$#i" />
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="rulesets/codesize.xml/TooManyPublicMethods">
|
||||
<priority>1</priority>
|
||||
<properties>
|
||||
<property name="ignorepattern" value="#^(set|get|test)|test$#i" />
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
|
||||
<rule ref="rulesets/design.xml" />
|
||||
<rule ref="rulesets/naming.xml" >
|
||||
<exclude name="LongClassName" />
|
||||
<exclude name="ShortClassName" />
|
||||
<exclude name="ShortVariable" />
|
||||
<exclude name="LongVariable" />
|
||||
<exclude name="ShortMethodName" />
|
||||
</rule>
|
||||
<rule ref="rulesets/unusedcode.xml" />
|
||||
|
||||
|
||||
|
||||
</ruleset>
|
21
phpmetrics.json
Normal file
21
phpmetrics.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"composer": true,
|
||||
"includes": [
|
||||
"src"
|
||||
],
|
||||
"excludes": [
|
||||
"tests"
|
||||
],
|
||||
"report": {
|
||||
"html": "output/metrics/",
|
||||
"json": "output/metrics/report.json"
|
||||
},
|
||||
"plugins": {
|
||||
"git": {
|
||||
"binary": "git"
|
||||
},
|
||||
"junit": {
|
||||
"file": "output/test.xml"
|
||||
}
|
||||
}
|
||||
}
|
5
phpstan.neon
Normal file
5
phpstan.neon
Normal file
@ -0,0 +1,5 @@
|
||||
parameters:
|
||||
level: 6
|
||||
paths:
|
||||
- src
|
||||
- tests
|
24
phpunit.xml
Normal file
24
phpunit.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.2/phpunit.xsd" backupGlobals="false" bootstrap="vendor/autoload.php" colors="true" processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" backupStaticProperties="false" displayDetailsOnTestsThatTriggerWarnings="true" beStrictAboutCoverageMetadata="true" requireCoverageMetadata="true">
|
||||
<testsuites>
|
||||
<testsuite name="Test Suite">
|
||||
<directory>./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<source>
|
||||
<include>
|
||||
<directory suffix=".php">src/</directory>
|
||||
</include>
|
||||
</source>
|
||||
<logging>
|
||||
<junit outputFile="output/test/junit.xml"/>
|
||||
<testdoxHtml outputFile="output/test/index.html"/>
|
||||
</logging>
|
||||
|
||||
<coverage>
|
||||
<report>
|
||||
<html outputDirectory="output/coverage" />
|
||||
<clover outputFile ="output/coverage/clover.xml" />
|
||||
</report>
|
||||
</coverage>
|
||||
</phpunit>
|
18
psalm.xml
Normal file
18
psalm.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
errorLevel="3"
|
||||
resolveFromConfigFile="true"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
findUnusedBaselineEntry="true"
|
||||
findUnusedCode="false"
|
||||
>
|
||||
<projectFiles>
|
||||
<directory name="src" />
|
||||
<ignoreFiles>
|
||||
<directory name="vendor" />
|
||||
</ignoreFiles>
|
||||
<file name="bin/simavr.php" />
|
||||
</projectFiles>
|
||||
</psalm>
|
18
rector.php
Normal file
18
rector.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Config\RectorConfig;
|
||||
use Rector\CodeQuality\Rector\ClassMethod\LocallyCalledStaticMethodToNonStaticRector;
|
||||
|
||||
return RectorConfig::configure()
|
||||
->withPaths([
|
||||
__DIR__ . '/src',
|
||||
__DIR__ . '/tests',
|
||||
])
|
||||
// uncomment to reach your current PHP version
|
||||
->withPhpSets()
|
||||
->withPreparedSets(deadCode: true, codeQuality: true, codingStyle: true, typeDeclarations : true, privatization: true, naming: false, instanceOf: true, earlyReturn: true, strictBooleans: true)
|
||||
->withSkip([
|
||||
LocallyCalledStaticMethodToNonStaticRector::class
|
||||
]);;
|
BIN
resources/blinky/blink.bin
Normal file
BIN
resources/blinky/blink.bin
Normal file
Binary file not shown.
17
resources/blinky/blink.c
Normal file
17
resources/blinky/blink.c
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef F_CPU
|
||||
#define F_CPU 1000000UL
|
||||
#endif
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <util/delay.h>
|
||||
|
||||
int main(void) {
|
||||
DDRB = 0b00000001;
|
||||
|
||||
while (1) {
|
||||
PORTB = 0b00000001;
|
||||
PORTB = 0b00000000;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
69
resources/blinky/blink.dump
Normal file
69
resources/blinky/blink.dump
Normal file
@ -0,0 +1,69 @@
|
||||
|
||||
blink.elf: file format elf32-avr
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
00000000 <__vectors>:
|
||||
0: 0c 94 34 00 jmp 0x68 ; 0x68 <__ctors_end>
|
||||
4: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
8: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
10: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
14: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
18: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
1c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
20: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
24: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
28: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
2c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
30: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
34: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
38: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
3c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
40: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
44: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
48: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
4c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
50: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
54: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
58: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
5c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
60: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
64: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
|
||||
00000068 <__ctors_end>:
|
||||
68: 11 24 eor r1, r1
|
||||
6a: 1f be out 0x3f, r1 ; 63
|
||||
6c: cf ef ldi r28, 0xFF ; 255
|
||||
6e: d8 e0 ldi r29, 0x08 ; 8
|
||||
70: de bf out 0x3e, r29 ; 62
|
||||
72: cd bf out 0x3d, r28 ; 61
|
||||
74: 0e 94 40 00 call 0x80 ; 0x80 <main>
|
||||
78: 0c 94 45 00 jmp 0x8a ; 0x8a <_exit>
|
||||
|
||||
0000007c <__bad_interrupt>:
|
||||
7c: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>
|
||||
|
||||
00000080 <main>:
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <util/delay.h>
|
||||
|
||||
int main(void) {
|
||||
DDRB = 0b00000001;
|
||||
80: 81 e0 ldi r24, 0x01 ; 1
|
||||
82: 84 b9 out 0x04, r24 ; 4
|
||||
|
||||
while (1) {
|
||||
PORTB = 0b00000001;
|
||||
84: 85 b9 out 0x05, r24 ; 5
|
||||
PORTB = 0b00000000;
|
||||
86: 15 b8 out 0x05, r1 ; 5
|
||||
88: fd cf rjmp .-6 ; 0x84 <main+0x4>
|
||||
|
||||
0000008a <_exit>:
|
||||
8a: f8 94 cli
|
||||
|
||||
0000008c <__stop_program>:
|
||||
8c: ff cf rjmp .-2 ; 0x8c <__stop_program>
|
BIN
resources/blinky/blink.elf
Executable file
BIN
resources/blinky/blink.elf
Executable file
Binary file not shown.
10
resources/blinky/blink.hex
Normal file
10
resources/blinky/blink.hex
Normal file
@ -0,0 +1,10 @@
|
||||
:100000000C9434000C943E000C943E000C943E0082
|
||||
:100010000C943E000C943E000C943E000C943E0068
|
||||
:100020000C943E000C943E000C943E000C943E0058
|
||||
:100030000C943E000C943E000C943E000C943E0048
|
||||
:100040000C943E000C943E000C943E000C943E0038
|
||||
:100050000C943E000C943E000C943E000C943E0028
|
||||
:100060000C943E000C943E0011241FBECFEFD8E04C
|
||||
:10007000DEBFCDBF0E9440000C9445000C940000F0
|
||||
:0E00800081E084B985B915B8FDCFF894FFCFA3
|
||||
:00000001FF
|
BIN
resources/blinky/blink.o
Normal file
BIN
resources/blinky/blink.o
Normal file
Binary file not shown.
7
resources/blinky/compile.sh
Executable file
7
resources/blinky/compile.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
avr-gcc -g -Os -mmcu=atmega328p -c blink.c
|
||||
avr-gcc -g -mmcu=atmega328p -o blink.elf blink.o
|
||||
avr-objcopy -j .text -j .data -O ihex blink.elf blink.hex
|
||||
avr-size --format=avr --mcu=atmega328p blink.elf
|
||||
objcopy --input-target=ihex --output-target=binary blink.hex blink.bin
|
||||
avr-objdump -d -S -m avr5 blink.elf > blink.dump
|
BIN
resources/blinky2/blink.bin
Normal file
BIN
resources/blinky2/blink.bin
Normal file
Binary file not shown.
19
resources/blinky2/blink.c
Normal file
19
resources/blinky2/blink.c
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef F_CPU
|
||||
#define F_CPU 1000000UL
|
||||
#endif
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <util/delay.h>
|
||||
|
||||
int main(void) {
|
||||
DDRB = 0b00000001;
|
||||
|
||||
while (1) {
|
||||
PORTB = 0b00000001;
|
||||
_delay_ms(500);
|
||||
PORTB = 0b00000000;
|
||||
_delay_ms(500);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
94
resources/blinky2/blink.dump
Normal file
94
resources/blinky2/blink.dump
Normal file
@ -0,0 +1,94 @@
|
||||
|
||||
blink.elf: file format elf32-avr
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
00000000 <__vectors>:
|
||||
0: 0c 94 34 00 jmp 0x68 ; 0x68 <__ctors_end>
|
||||
4: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
8: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
10: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
14: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
18: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
1c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
20: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
24: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
28: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
2c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
30: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
34: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
38: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
3c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
40: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
44: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
48: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
4c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
50: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
54: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
58: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
5c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
60: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
64: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
|
||||
|
||||
00000068 <__ctors_end>:
|
||||
68: 11 24 eor r1, r1
|
||||
6a: 1f be out 0x3f, r1 ; 63
|
||||
6c: cf ef ldi r28, 0xFF ; 255
|
||||
6e: d8 e0 ldi r29, 0x08 ; 8
|
||||
70: de bf out 0x3e, r29 ; 62
|
||||
72: cd bf out 0x3d, r28 ; 61
|
||||
74: 0e 94 40 00 call 0x80 ; 0x80 <main>
|
||||
78: 0c 94 57 00 jmp 0xae ; 0xae <_exit>
|
||||
|
||||
0000007c <__bad_interrupt>:
|
||||
7c: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>
|
||||
|
||||
00000080 <main>:
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <util/delay.h>
|
||||
|
||||
int main(void) {
|
||||
DDRB = 0b00000001;
|
||||
80: 81 e0 ldi r24, 0x01 ; 1
|
||||
82: 84 b9 out 0x04, r24 ; 4
|
||||
|
||||
while (1) {
|
||||
PORTB = 0b00000001;
|
||||
84: 85 b9 out 0x05, r24 ; 5
|
||||
#else
|
||||
//round up by default
|
||||
__ticks_dc = (uint32_t)(ceil(fabs(__tmp)));
|
||||
#endif
|
||||
|
||||
__builtin_avr_delay_cycles(__ticks_dc);
|
||||
86: 2f e9 ldi r18, 0x9F ; 159
|
||||
88: 36 e8 ldi r19, 0x86 ; 134
|
||||
8a: 91 e0 ldi r25, 0x01 ; 1
|
||||
8c: 21 50 subi r18, 0x01 ; 1
|
||||
8e: 30 40 sbci r19, 0x00 ; 0
|
||||
90: 90 40 sbci r25, 0x00 ; 0
|
||||
92: e1 f7 brne .-8 ; 0x8c <main+0xc>
|
||||
94: 00 c0 rjmp .+0 ; 0x96 <main+0x16>
|
||||
96: 00 00 nop
|
||||
_delay_ms(500);
|
||||
PORTB = 0b00000000;
|
||||
98: 15 b8 out 0x05, r1 ; 5
|
||||
9a: 2f e9 ldi r18, 0x9F ; 159
|
||||
9c: 36 e8 ldi r19, 0x86 ; 134
|
||||
9e: 91 e0 ldi r25, 0x01 ; 1
|
||||
a0: 21 50 subi r18, 0x01 ; 1
|
||||
a2: 30 40 sbci r19, 0x00 ; 0
|
||||
a4: 90 40 sbci r25, 0x00 ; 0
|
||||
a6: e1 f7 brne .-8 ; 0xa0 <main+0x20>
|
||||
a8: 00 c0 rjmp .+0 ; 0xaa <main+0x2a>
|
||||
aa: 00 00 nop
|
||||
ac: eb cf rjmp .-42 ; 0x84 <main+0x4>
|
||||
|
||||
000000ae <_exit>:
|
||||
ae: f8 94 cli
|
||||
|
||||
000000b0 <__stop_program>:
|
||||
b0: ff cf rjmp .-2 ; 0xb0 <__stop_program>
|
BIN
resources/blinky2/blink.elf
Executable file
BIN
resources/blinky2/blink.elf
Executable file
Binary file not shown.
13
resources/blinky2/blink.hex
Normal file
13
resources/blinky2/blink.hex
Normal file
@ -0,0 +1,13 @@
|
||||
:100000000C9434000C943E000C943E000C943E0082
|
||||
:100010000C943E000C943E000C943E000C943E0068
|
||||
:100020000C943E000C943E000C943E000C943E0058
|
||||
:100030000C943E000C943E000C943E000C943E0048
|
||||
:100040000C943E000C943E000C943E000C943E0038
|
||||
:100050000C943E000C943E000C943E000C943E0028
|
||||
:100060000C943E000C943E0011241FBECFEFD8E04C
|
||||
:10007000DEBFCDBF0E9440000C9457000C940000DE
|
||||
:1000800081E084B985B92FE936E891E0215030400C
|
||||
:100090009040E1F700C0000015B82FE936E891E084
|
||||
:1000A000215030409040E1F700C00000EBCFF894C1
|
||||
:0200B000FFCF80
|
||||
:00000001FF
|
BIN
resources/blinky2/blink.o
Normal file
BIN
resources/blinky2/blink.o
Normal file
Binary file not shown.
7
resources/blinky2/compile.sh
Executable file
7
resources/blinky2/compile.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
avr-gcc -g -Os -mmcu=atmega328p -c blink.c
|
||||
avr-gcc -g -mmcu=atmega328p -o blink.elf blink.o
|
||||
avr-objcopy -j .text -j .data -O ihex blink.elf blink.hex
|
||||
avr-size --format=avr --mcu=atmega328p blink.elf
|
||||
avr-objcopy --input-target=ihex --output-target=binary blink.hex blink.bin
|
||||
avr-objdump -d -S -m avr5 blink.elf > blink.dump
|
59
src/AVR.php
Normal file
59
src/AVR.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP;
|
||||
|
||||
use SimAVRPHP\Exceptions\InvalidAddressException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class AVR
|
||||
{
|
||||
public ProgramCounter $pc;
|
||||
|
||||
public Registers $registers;
|
||||
|
||||
/**
|
||||
* @var Stack Contains the CPU stack
|
||||
*/
|
||||
public Stack $stack;
|
||||
|
||||
public string $memory = "";
|
||||
|
||||
public function __construct(public LoggerInterface $logger)
|
||||
{
|
||||
$this->pc = new ProgramCounter();
|
||||
$this->registers = new Registers();
|
||||
$this->stack = new Stack();
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
public function setMemory(string $memory): void
|
||||
{
|
||||
$this->memory = $memory;
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->pc->reset();
|
||||
}
|
||||
|
||||
public function step(): void
|
||||
{
|
||||
$rawOperation = $this->getOperation();
|
||||
$operation = Opcode\Instruction::createInstructionFromOpcode($this, $rawOperation);
|
||||
|
||||
$this->logger->debug("Prepare instruction for execution", ['PC' => $this->pc->get(), 'Class' => $operation::class]);
|
||||
$operation->execute();
|
||||
}
|
||||
|
||||
private function getOperation(): string
|
||||
{
|
||||
$opcode = unpack("v2", $this->memory, $this->pc->get());
|
||||
if ($opcode == FALSE) {
|
||||
throw new InvalidAddressException();
|
||||
}
|
||||
|
||||
return str_pad(decbin((int)$opcode[1]), 16, "0", STR_PAD_LEFT) . str_pad(decbin((int)$opcode[2]), 16, "0", STR_PAD_LEFT);
|
||||
}
|
||||
}
|
11
src/Exceptions/InvalidAddressException.php
Normal file
11
src/Exceptions/InvalidAddressException.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidAddressException extends Exception
|
||||
{
|
||||
}
|
11
src/Exceptions/UnsupportedInstructionException.php
Normal file
11
src/Exceptions/UnsupportedInstructionException.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class UnsupportedInstructionException extends Exception
|
||||
{
|
||||
}
|
29
src/Opcode/BRNE.php
Normal file
29
src/Opcode/BRNE.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP\Opcode;
|
||||
|
||||
class BRNE extends Instruction
|
||||
{
|
||||
public const string OPCODE = "111101kkkkkkk001";
|
||||
|
||||
public const int WORDS = 1;
|
||||
|
||||
public function do(int $k): void
|
||||
{
|
||||
$k = Instruction::signed($k, -64, 63);
|
||||
$this->debug('Branch if Not Equal: ' . $k);
|
||||
$return_address = $this->mcu->pc->get() + (self::WORDS << 1);
|
||||
$this->mcu->stack->push($return_address);
|
||||
|
||||
$newPC = $this->mcu->pc->get();
|
||||
if ($this->mcu->registers->getZ()) {
|
||||
$newPC += (self::WORDS << 1);
|
||||
} else {
|
||||
$newPC += ($k << 1) + 2;
|
||||
}
|
||||
|
||||
$this->mcu->pc->jump($newPC);
|
||||
}
|
||||
}
|
22
src/Opcode/CALL.php
Normal file
22
src/Opcode/CALL.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP\Opcode;
|
||||
|
||||
class CALL extends Instruction
|
||||
{
|
||||
public const string OPCODE = "1001010kkkkk111kkkkkkkkkkkkkkkkk";
|
||||
|
||||
public const int WORDS = 2;
|
||||
|
||||
public const int CYCLES = 4;
|
||||
|
||||
public function do(int $k): void
|
||||
{
|
||||
$this->debug("Call", ['k' => $k]);
|
||||
$return_address = $this->mcu->pc->get() + (self::WORDS << 1);
|
||||
$this->mcu->stack->push($return_address);
|
||||
$this->mcu->pc->jump($k << 1);
|
||||
}
|
||||
}
|
19
src/Opcode/CLI.php
Normal file
19
src/Opcode/CLI.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP\Opcode;
|
||||
|
||||
class CLI extends Instruction
|
||||
{
|
||||
public const string OPCODE = "1001010011111000";
|
||||
|
||||
public const int WORDS = 1;
|
||||
|
||||
public const int CYCLES = 1;
|
||||
|
||||
public function do(): void
|
||||
{
|
||||
$this->mcu->registers->setI(FALSE);
|
||||
}
|
||||
}
|
26
src/Opcode/EOR.php
Normal file
26
src/Opcode/EOR.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP\Opcode;
|
||||
|
||||
class EOR extends Instruction
|
||||
{
|
||||
public const string OPCODE = "001001rdddddrrrr";
|
||||
|
||||
public const int WORDS = 1;
|
||||
|
||||
public const int CYCLES = 1;
|
||||
|
||||
public function do(int $r, int $d): void
|
||||
{
|
||||
$Rd = $this->mcu->registers->get($d);
|
||||
$Rr = $this->mcu->registers->get($r);
|
||||
|
||||
$result = $Rd ^ $Rr;
|
||||
|
||||
$this->mcu->registers->set($d, $result);
|
||||
$this->debug(sprintf('Exclusive OR: %d xor %d = %d', $Rd, $Rr, $result));
|
||||
$this->mcu->pc->stepWords(self::WORDS);
|
||||
}
|
||||
}
|
117
src/Opcode/Instruction.php
Normal file
117
src/Opcode/Instruction.php
Normal file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP\Opcode;
|
||||
|
||||
use SimAVRPHP\AVR;
|
||||
use SimAVRPHP\Exceptions\UnsupportedInstructionException;
|
||||
use Exception;
|
||||
|
||||
class Instruction
|
||||
{
|
||||
/**
|
||||
* @var class-string[]
|
||||
*/
|
||||
protected static $AVRInstructions = [
|
||||
\SimAVRPHP\Opcode\BRNE::class,
|
||||
\SimAVRPHP\Opcode\CALL::class,
|
||||
\SimAVRPHP\Opcode\CLI::class,
|
||||
\SimAVRPHP\Opcode\EOR::class,
|
||||
\SimAVRPHP\Opcode\JMP::class,
|
||||
\SimAVRPHP\Opcode\LDI::class,
|
||||
\SimAVRPHP\Opcode\NOP::class,
|
||||
\SimAVRPHP\Opcode\OUT::class,
|
||||
\SimAVRPHP\Opcode\RJMP::class,
|
||||
\SimAVRPHP\Opcode\SBCI::class,
|
||||
\SimAVRPHP\Opcode\SUBI::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param array<string,int> $operands
|
||||
*/
|
||||
protected function __construct(protected AVR $mcu, protected array $operands)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|array<string,int>
|
||||
*/
|
||||
private static function decode(string $opcode, string $pattern): array|bool
|
||||
{
|
||||
$data = [];
|
||||
$pattern_length = strlen($pattern);
|
||||
if ($pattern_length != 16 && $pattern_length != 32) {
|
||||
throw new Exception('Pattern length invalid ' . $pattern_length);
|
||||
}
|
||||
|
||||
for ($c = 0; $c < $pattern_length; $c++) {
|
||||
$token = $pattern[$c];
|
||||
$instructionBit = $opcode[$c];
|
||||
if ($token === "1" || $token === "0") {
|
||||
if ($instructionBit !== $token) {
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
if (! isset($data[$token])) {
|
||||
$data[$token] = "";
|
||||
}
|
||||
|
||||
$data[$token] .= $instructionBit;
|
||||
}
|
||||
}
|
||||
|
||||
return array_map(
|
||||
fn(string $element): int => (int)bindec($element),
|
||||
$data
|
||||
);
|
||||
}
|
||||
|
||||
public function execute(): void
|
||||
{
|
||||
if (is_callable([$this,'do'])) {
|
||||
call_user_func_array([$this,'do'], $this->operands);
|
||||
}
|
||||
}
|
||||
|
||||
public static function createInstructionFromOpcode(AVR $mcu, string $opcode): Instruction
|
||||
{
|
||||
foreach (self::$AVRInstructions as $i) {
|
||||
$data = self::decode($opcode, constant($i . "::OPCODE"));
|
||||
if ($data !== FALSE) {
|
||||
$obj = new $i($mcu, $data);
|
||||
if ($obj instanceof Instruction) {
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new UnsupportedInstructionException("Unsupported instruction", (int)bindec($opcode));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,int> $param
|
||||
*/
|
||||
public function debug(string $string, array $param = []): void
|
||||
{
|
||||
$this->mcu->logger->debug($string, $param);
|
||||
}
|
||||
|
||||
protected function getRegister(int $reg): int
|
||||
{
|
||||
return $this->mcu->registers->get($reg);
|
||||
}
|
||||
|
||||
public static function signed(int $v, int $min, int $max): int
|
||||
{
|
||||
if ($v > $max + abs($min)) {
|
||||
throw new Exception("Not valid within range");
|
||||
}
|
||||
|
||||
if ($v > $max) {
|
||||
return $min - 1 + $v - $max;
|
||||
}
|
||||
|
||||
return $v;
|
||||
}
|
||||
}
|
20
src/Opcode/JMP.php
Normal file
20
src/Opcode/JMP.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP\Opcode;
|
||||
|
||||
class JMP extends Instruction
|
||||
{
|
||||
public const string OPCODE = "1001010kkkkk110kkkkkkkkkkkkkkkkk";
|
||||
|
||||
public const int WORDS = 2;
|
||||
|
||||
public const int CYCLES = 3;
|
||||
|
||||
public function do(int $k): void
|
||||
{
|
||||
$this->debug("Jump to: " . $k);
|
||||
$this->mcu->pc->jump($k << 1);
|
||||
}
|
||||
}
|
23
src/Opcode/LDI.php
Normal file
23
src/Opcode/LDI.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP\Opcode;
|
||||
|
||||
class LDI extends Instruction
|
||||
{
|
||||
public const string OPCODE = "1110KKKKddddKKKK";
|
||||
|
||||
public const int WORDS = 1;
|
||||
|
||||
public const int CYCLES = 1;
|
||||
|
||||
|
||||
public function do(int $K, int $d): void
|
||||
{
|
||||
$reg = $d + 16;
|
||||
$this->mcu->registers->set($reg, $K);
|
||||
$this->debug(sprintf('Load Immediate: %d to R%d', $K, $reg));
|
||||
$this->mcu->pc->stepWords(self::WORDS);
|
||||
}
|
||||
}
|
19
src/Opcode/NOP.php
Normal file
19
src/Opcode/NOP.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP\Opcode;
|
||||
|
||||
class NOP extends Instruction
|
||||
{
|
||||
public const string OPCODE = "0000000000000000";
|
||||
|
||||
public const int WORDS = 1;
|
||||
|
||||
public const int CYCLES = 1;
|
||||
|
||||
public function do(): void
|
||||
{
|
||||
$this->mcu->pc->stepWords(self::WORDS);
|
||||
}
|
||||
}
|
24
src/Opcode/OUT.php
Normal file
24
src/Opcode/OUT.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP\Opcode;
|
||||
|
||||
class OUT extends Instruction
|
||||
{
|
||||
public const string OPCODE = "10111AArrrrrAAAA";
|
||||
|
||||
public const int WORDS = 1;
|
||||
|
||||
public const int CYCLES = 1;
|
||||
|
||||
|
||||
public function do(int $r, int $A): void
|
||||
{
|
||||
$Rr = $this->mcu->registers->get($r);
|
||||
|
||||
$this->debug(sprintf('OUT: R%d Value %d to A: %d', $r, $Rr, $A));
|
||||
$this->mcu->registers->setA($A, $Rr);
|
||||
$this->mcu->pc->stepWords(self::WORDS);
|
||||
}
|
||||
}
|
23
src/Opcode/RJMP.php
Normal file
23
src/Opcode/RJMP.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP\Opcode;
|
||||
|
||||
class RJMP extends Instruction
|
||||
{
|
||||
public const string OPCODE = "1100kkkkkkkkkkkk";
|
||||
|
||||
public const int WORDS = 1;
|
||||
|
||||
public const int CYCLES = 2;
|
||||
|
||||
public function do(int $k): void
|
||||
{
|
||||
$k = $this->signed($k, -2048, 2047);
|
||||
$new_pc = $this->mcu->pc->get() + ($k * 2) + 2;
|
||||
$this->debug("Relative Jump to: " . sprintf("0x%X", $new_pc));
|
||||
|
||||
$this->mcu->pc->jump($new_pc);
|
||||
}
|
||||
}
|
25
src/Opcode/SBCI.php
Normal file
25
src/Opcode/SBCI.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP\Opcode;
|
||||
|
||||
class SBCI extends Instruction
|
||||
{
|
||||
public const string OPCODE = "0100KKKKddddKKKK";
|
||||
|
||||
public const int WORDS = 1;
|
||||
|
||||
public const int CYCLES = 1;
|
||||
|
||||
|
||||
public function do(int $K, int $d): void
|
||||
{
|
||||
$reg = $d + 16;
|
||||
$value = $this->mcu->registers->get($reg);
|
||||
$result = $value - $K;
|
||||
$this->mcu->registers->set($reg, $result);
|
||||
$this->debug("Subtract Immediate with Carry SBI : R" . $reg . " - " . $K);
|
||||
$this->mcu->pc->stepWords(self::WORDS);
|
||||
}
|
||||
}
|
25
src/Opcode/SUBI.php
Normal file
25
src/Opcode/SUBI.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP\Opcode;
|
||||
|
||||
class SUBI extends Instruction
|
||||
{
|
||||
public const string OPCODE = "0101KKKKddddKKKK";
|
||||
|
||||
public const int WORDS = 1;
|
||||
|
||||
public const int CYCLES = 1;
|
||||
|
||||
|
||||
public function do(int $K, int $d): void
|
||||
{
|
||||
$reg = $d + 16;
|
||||
$value = $this->mcu->registers->get($reg);
|
||||
$result = $value - $K;
|
||||
$this->mcu->registers->set($reg, $result);
|
||||
$this->debug("Subtract Immediate: R" . $reg . " - " . $K);
|
||||
$this->mcu->pc->stepWords(self::WORDS);
|
||||
}
|
||||
}
|
44
src/ProgramCounter.php
Normal file
44
src/ProgramCounter.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP;
|
||||
|
||||
use SimAVRPHP\Exceptions\InvalidAddressException;
|
||||
|
||||
class ProgramCounter
|
||||
{
|
||||
private const int RESET_PC_VALUE = 0;
|
||||
|
||||
private int $programCounter = self::RESET_PC_VALUE;
|
||||
|
||||
public function get(): int
|
||||
{
|
||||
return $this->programCounter;
|
||||
}
|
||||
|
||||
public function jump(int $address): void
|
||||
{
|
||||
if (($address % 2) != 0) {
|
||||
throw new InvalidAddressException("Invalid address", $address);
|
||||
}
|
||||
|
||||
$this->programCounter = $address;
|
||||
}
|
||||
|
||||
public function relativeJump(int $address): void
|
||||
{
|
||||
$this->jump($this->programCounter + $address);
|
||||
}
|
||||
|
||||
public function stepWords(int $word): int
|
||||
{
|
||||
$this->programCounter += ($word << 1);
|
||||
return $this->programCounter;
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->programCounter = self::RESET_PC_VALUE;
|
||||
}
|
||||
}
|
69
src/Registers.php
Normal file
69
src/Registers.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP;
|
||||
|
||||
class Registers
|
||||
{
|
||||
private bool $Z = FALSE;
|
||||
|
||||
private bool $I = FALSE;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
private $registers = [];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
private $A = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->Z = FALSE;
|
||||
$this->I = FALSE;
|
||||
$this->registers = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
}
|
||||
|
||||
public function get(int $i): int
|
||||
{
|
||||
return $this->registers[$i];
|
||||
}
|
||||
|
||||
public function set(int $i, int $value): void
|
||||
{
|
||||
$this->registers[$i] = $value;
|
||||
}
|
||||
|
||||
public function setA(int $A, int $value): void
|
||||
{
|
||||
$this->A[$A] = $value;
|
||||
}
|
||||
|
||||
public function getA(int $A): int
|
||||
{
|
||||
return $this->A[$A];
|
||||
}
|
||||
|
||||
public function getZ(): bool
|
||||
{
|
||||
return $this->Z;
|
||||
}
|
||||
|
||||
public function setI(bool $I): void
|
||||
{
|
||||
$this->$I = $I;
|
||||
}
|
||||
|
||||
public function getI(): bool
|
||||
{
|
||||
return $this->I;
|
||||
}
|
||||
}
|
34
src/Stack.php
Normal file
34
src/Stack.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP;
|
||||
|
||||
use Exception;
|
||||
|
||||
class Stack
|
||||
{
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
private array $stack = [];
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->stack = [];
|
||||
}
|
||||
|
||||
public function push(int $value): void
|
||||
{
|
||||
$this->stack[] = $value;
|
||||
}
|
||||
|
||||
public function pop(): int
|
||||
{
|
||||
if (count($this->stack) >= 1) {
|
||||
return array_pop($this->stack);
|
||||
}
|
||||
|
||||
throw new Exception("Stack Underflow");
|
||||
}
|
||||
}
|
26
tests/AVRTest.php
Normal file
26
tests/AVRTest.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP\Tests;
|
||||
|
||||
use SimAVRPHP\AVR;
|
||||
|
||||
use Psr\Log\NullLogger;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
|
||||
|
||||
#[CoversClass(AVR::class)]
|
||||
class AVRTest extends TestCase
|
||||
{
|
||||
public function testStepNOP(): void
|
||||
{
|
||||
$avr = new AVR(new NullLogger);
|
||||
$avr->setMemory("\00\00\00\00");
|
||||
$avr->step();
|
||||
}
|
||||
|
||||
|
||||
}
|
40
tests/ProgramCounterTest.php
Normal file
40
tests/ProgramCounterTest.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimAVRPHP\Tests;
|
||||
|
||||
use SimAVRPHP\ProgramCounter;
|
||||
use SimAVRPHP\Exceptions\InvalidAddressException;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
|
||||
|
||||
#[CoversClass(ProgramCounter::class)]
|
||||
class ProgramCounterTest extends TestCase
|
||||
{
|
||||
public function testStepAndSet(): void
|
||||
{
|
||||
$pc = new ProgramCounter();
|
||||
$this->assertEquals(0, $pc->get());
|
||||
|
||||
$pc->stepWords(1); //Step 1 word
|
||||
|
||||
$this->assertEquals(2, $pc->get());
|
||||
|
||||
$pc->jump(0x100);
|
||||
$this->assertEquals(0x100, $pc->get());
|
||||
|
||||
$pc->reset();
|
||||
$this->assertEquals(0, $pc->get());
|
||||
}
|
||||
|
||||
public function testSetinvalid(): void
|
||||
{
|
||||
$pc = new ProgramCounter();
|
||||
$this->expectException(InvalidAddressException::class);
|
||||
$pc->jump(3); //Uneven address
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user