Files
homeclaw/web-ui/vendor/avr8js/peripherals/usi.js
T
kim 209f3a3aef v3.0.6: RP2040 에뮬레이터 개선 — USB CDC, vendor화, 부팅 감지
- USB CDC 경로 복원: #define Serial Serial1 우회 제거
- rp2040js/avr8js/codemirror/xterm/monaco-editor vendor화
- /vendor 정적 라우팅 추가 (Express 5 catch-all 우회)
- 부팅 완료 타임아웃 감지 (Serial 출력 없어도 8초 후 RAF 전환)
- 리셋 시 Simulator 재생성으로 주변 장치 상태 초기화
- 시리얼 패널의 내부 메시지를 console.log로 이동
2026-06-19 15:49:36 +09:00

119 lines
4.2 KiB
JavaScript

// SPDX-License-Identifier: MIT
// Copyright (c) Uri Shaked and contributors
const USICR = 0x2d;
const USISR = 0x2e;
const USIDR = 0x2f;
const USIBR = 0x30;
// USISR bits
const USICNT_MASK = 0xf;
const USIDC = 1 << 4;
const USIPF = 1 << 5;
const USIOIF = 1 << 6;
const USISIF = 1 << 7;
// USICR bits
const USITC = 1 << 0;
const USICLK = 1 << 1;
const USICS0 = 1 << 2;
const USICS1 = 1 << 3;
const USIWM0 = 1 << 4;
const USIWM1 = 1 << 5;
const USIOIE = 1 << 6;
const USISIE = 1 << 7;
export class AVRUSI {
constructor(cpu, port, portPin, dataPin, clockPin) {
// Interrupts
this.START = {
address: 0xd,
flagRegister: USISR,
flagMask: USISIF,
enableRegister: USICR,
enableMask: USISIE,
};
this.OVF = {
address: 0xe,
flagRegister: USISR,
flagMask: USIOIF,
enableRegister: USICR,
enableMask: USIOIE,
};
const PIN = portPin;
const PORT = PIN + 2;
port.addListener((value) => {
const twoWire = (cpu.data[USICR] & USIWM1) === USIWM1;
if (twoWire) {
if (value & (1 << clockPin) && !(value & (1 << dataPin))) {
// Start condition detected
cpu.setInterruptFlag(this.START);
}
if (value & (1 << clockPin) && value & (1 << dataPin)) {
// Stop condition detected
cpu.data[USISR] |= USIPF;
}
}
});
const updateOutput = () => {
const oldValue = cpu.data[PORT];
const newValue = cpu.data[USIDR] & 0x80 ? oldValue | (1 << dataPin) : oldValue & ~(1 << dataPin);
cpu.writeHooks[PORT](newValue, oldValue, PORT, 0xff);
if (newValue & 0x80 && !(cpu.data[PIN] & 0x80)) {
cpu.data[USISR] |= USIDC; // Shout output HIGH (pulled-up), but input is LOW
}
else {
cpu.data[USISR] &= ~USIDC;
}
};
const count = () => {
const counter = (cpu.data[USISR] + 1) & USICNT_MASK;
cpu.data[USISR] = (cpu.data[USISR] & ~USICNT_MASK) | counter;
if (!counter) {
cpu.data[USIBR] = cpu.data[USIDR];
cpu.setInterruptFlag(this.OVF);
}
};
const shift = (inputValue) => {
cpu.data[USIDR] = (cpu.data[USIDR] << 1) | inputValue;
updateOutput();
};
cpu.writeHooks[USIDR] = (value) => {
cpu.data[USIDR] = value;
updateOutput();
return true;
};
cpu.writeHooks[USISR] = (value) => {
const writeClearMask = USISIF | USIOIF | USIPF;
cpu.data[USISR] = (cpu.data[USISR] & writeClearMask & ~value) | (value & 0xf);
cpu.clearInterruptByFlag(this.START, value);
cpu.clearInterruptByFlag(this.OVF, value);
return true;
};
cpu.writeHooks[USICR] = (value) => {
cpu.data[USICR] = value & ~(USICLK | USITC);
cpu.updateInterruptEnable(this.START, value);
cpu.updateInterruptEnable(this.OVF, value);
const clockSrc = value & ((USICS1 | USICS0) >> 2);
const mode = value & ((USIWM1 | USIWM0) >> 4);
const usiClk = value & USICLK;
port.openCollector = mode >= 2 ? 1 << dataPin : 0;
const inputValue = cpu.data[PIN] & (1 << dataPin) ? 1 : 0;
if (usiClk && !clockSrc) {
shift(inputValue);
count();
}
if (value & USITC) {
cpu.writeHooks[PIN](1 << clockPin, cpu.data[PIN], PIN, 0xff);
const newValue = cpu.data[PIN] & (1 << clockPin);
if (usiClk && (clockSrc === 2 || clockSrc === 3)) {
if (clockSrc === 2 && newValue) {
shift(inputValue);
}
if (clockSrc === 3 && !newValue) {
shift(inputValue);
}
count();
}
return true;
}
};
}
}