m68kmac: Add a port to m68k Macintosh.
This is lightly tested with umac emulating a 4MB Macintosh Plus.
This commit is contained in:
parent
536f6fbfcc
commit
ee591eb58b
55 changed files with 3531 additions and 1250 deletions
16
.github/workflows/biome.yml
vendored
16
.github/workflows/biome.yml
vendored
|
|
@ -1,16 +0,0 @@
|
|||
name: JavaScript code lint and formatting with Biome
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
eslint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Biome
|
||||
uses: biomejs/setup-biome@v2
|
||||
with:
|
||||
version: 1.5.3
|
||||
- name: Run Biome
|
||||
run: biome ci --indent-style=space --indent-width=4 tests/ ports/webassembly
|
||||
50
.github/workflows/code_size.yml
vendored
50
.github/workflows/code_size.yml
vendored
|
|
@ -1,50 +0,0 @@
|
|||
name: Check code size
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'ports/bare-arm/**'
|
||||
- 'ports/mimxrt/**'
|
||||
- 'ports/minimal/**'
|
||||
- 'ports/rp2/**'
|
||||
- 'ports/samd/**'
|
||||
- 'ports/stm32/**'
|
||||
- 'ports/unix/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 100
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_code_size_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_code_size_build
|
||||
- name: Compute code size difference
|
||||
run: tools/metrics.py diff ~/size0 ~/size1 | tee diff
|
||||
- name: Save PR number
|
||||
if: github.event_name == 'pull_request'
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
run: echo $PR_NUMBER > pr_number
|
||||
- name: Upload diff
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: code-size-report
|
||||
path: |
|
||||
diff
|
||||
pr_number
|
||||
retention-days: 1
|
||||
105
.github/workflows/code_size_comment.yml
vendored
105
.github/workflows/code_size_comment.yml
vendored
|
|
@ -1,105 +0,0 @@
|
|||
name: Code size comment
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [Check code size]
|
||||
types: [completed]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: 'Download artifact'
|
||||
id: download-artifact
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
|
||||
const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: context.payload.workflow_run.id,
|
||||
});
|
||||
|
||||
const matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
|
||||
return artifact.name == "code-size-report"
|
||||
});
|
||||
|
||||
if (matchArtifact.length === 0) {
|
||||
console.log('no matching artifact found');
|
||||
console.log('result: "skip"');
|
||||
|
||||
return 'skip';
|
||||
}
|
||||
|
||||
const download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact[0].id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
|
||||
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/code-size-report.zip`, Buffer.from(download.data));
|
||||
|
||||
console.log('artifact downloaded to `code-size-report.zip`');
|
||||
console.log('result: "ok"');
|
||||
|
||||
return 'ok';
|
||||
- name: 'Unzip artifact'
|
||||
if: steps.download-artifact.outputs.result == 'ok'
|
||||
run: unzip code-size-report.zip
|
||||
- name: Post comment to pull request
|
||||
if: steps.download-artifact.outputs.result == 'ok'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
|
||||
const prNumber = Number(fs.readFileSync('pr_number'));
|
||||
const codeSizeReport = `Code size report:
|
||||
|
||||
\`\`\`
|
||||
${fs.readFileSync('diff')}
|
||||
\`\`\`
|
||||
`;
|
||||
|
||||
const comments = await github.paginate(
|
||||
github.rest.issues.listComments,
|
||||
{
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
}
|
||||
);
|
||||
|
||||
comments.reverse();
|
||||
|
||||
const previousComment = comments.find(comment =>
|
||||
comment.user.login === 'github-actions[bot]'
|
||||
)
|
||||
|
||||
// if github-actions[bot] already made a comment, update it,
|
||||
// otherwise create a new comment.
|
||||
|
||||
if (previousComment) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: previousComment.id,
|
||||
body: codeSizeReport,
|
||||
});
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body: codeSizeReport,
|
||||
});
|
||||
}
|
||||
18
.github/workflows/commit_formatting.yml
vendored
18
.github/workflows/commit_formatting.yml
vendored
|
|
@ -1,18 +0,0 @@
|
|||
name: Check commit message formatting
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 100
|
||||
- uses: actions/setup-python@v5
|
||||
- name: Check commit message formatting
|
||||
run: source tools/ci.sh && ci_commit_formatting_run
|
||||
23
.github/workflows/docs.yml
vendored
23
.github/workflows/docs.yml
vendored
|
|
@ -1,23 +0,0 @@
|
|||
name: Build docs
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- docs/**
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
- name: Install Python packages
|
||||
run: pip install -r docs/requirements.txt
|
||||
- name: Build docs
|
||||
run: make -C docs/ html
|
||||
25
.github/workflows/examples.yml
vendored
25
.github/workflows/examples.yml
vendored
|
|
@ -1,25 +0,0 @@
|
|||
name: Check examples
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'examples/**'
|
||||
- 'ports/unix/**'
|
||||
- 'py/**'
|
||||
- 'shared/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
embedding:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: make -C examples/embedding -f micropython_embed.mk && make -C examples/embedding
|
||||
- name: Run
|
||||
run: ./examples/embedding/embed | grep "hello world"
|
||||
29
.github/workflows/mpremote.yml
vendored
29
.github/workflows/mpremote.yml
vendored
|
|
@ -1,29 +0,0 @@
|
|||
name: Package mpremote
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Setting this to zero means fetch all history and tags,
|
||||
# which hatch-vcs can use to discover the version tag.
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v5
|
||||
- name: Install build tools
|
||||
run: pip install build
|
||||
- name: Build mpremote wheel
|
||||
run: cd tools/mpremote && python -m build --wheel
|
||||
- name: Archive mpremote wheel
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mpremote
|
||||
path: |
|
||||
tools/mpremote/dist/mpremote*.whl
|
||||
24
.github/workflows/mpy_format.yml
vendored
24
.github/workflows/mpy_format.yml
vendored
|
|
@ -1,24 +0,0 @@
|
|||
name: .mpy file format and tools
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'examples/**'
|
||||
- 'tests/**'
|
||||
- 'tools/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-22.04 # use 22.04 to get python2
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_mpy_format_setup
|
||||
- name: Test mpy-tool.py
|
||||
run: source tools/ci.sh && ci_mpy_format_test
|
||||
22
.github/workflows/ports.yml
vendored
22
.github/workflows/ports.yml
vendored
|
|
@ -1,22 +0,0 @@
|
|||
name: Build ports metadata
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- ports/**
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build ports download metadata
|
||||
run: mkdir boards && ./tools/autobuild/build-downloads.py . ./boards
|
||||
33
.github/workflows/ports_alif.yml
vendored
33
.github/workflows/ports_alif.yml
vendored
|
|
@ -1,33 +0,0 @@
|
|||
name: alif port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'drivers/**'
|
||||
- 'ports/alif/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build_alif:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ci_func: # names are functions in ci.sh
|
||||
- alif_ae3_build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_alif_setup
|
||||
- name: Build ci_${{matrix.ci_func }}
|
||||
run: source tools/ci.sh && ci_${{ matrix.ci_func }}
|
||||
28
.github/workflows/ports_cc3200.yml
vendored
28
.github/workflows/ports_cc3200.yml
vendored
|
|
@ -1,28 +0,0 @@
|
|||
name: cc3200 port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'drivers/**'
|
||||
- 'ports/cc3200/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_cc3200_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_cc3200_build
|
||||
57
.github/workflows/ports_esp32.yml
vendored
57
.github/workflows/ports_esp32.yml
vendored
|
|
@ -1,57 +0,0 @@
|
|||
name: esp32 port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'drivers/**'
|
||||
- 'ports/esp32/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build_idf:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ci_func: # names are functions in ci.sh
|
||||
- esp32_build_cmod_spiram_s2
|
||||
- esp32_build_s3_c3
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- id: idf_ver
|
||||
name: Read the ESP-IDF version (including Python version)
|
||||
run: source tools/ci.sh && echo "IDF_VER=${IDF_VER}-py${PYTHON_VER}" | tee "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Cached ESP-IDF install
|
||||
id: cache_esp_idf
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
./esp-idf/
|
||||
~/.espressif/
|
||||
!~/.espressif/dist/
|
||||
~/.cache/pip/
|
||||
key: esp-idf-${{ steps.idf_ver.outputs.IDF_VER }}
|
||||
|
||||
- name: Install ESP-IDF packages
|
||||
if: steps.cache_esp_idf.outputs.cache-hit != 'true'
|
||||
run: source tools/ci.sh && ci_esp32_idf_setup
|
||||
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: esp32-${{ matrix.ci_func }}
|
||||
|
||||
- name: Build ci_${{matrix.ci_func }}
|
||||
run: source tools/ci.sh && ci_${{ matrix.ci_func }}
|
||||
28
.github/workflows/ports_esp8266.yml
vendored
28
.github/workflows/ports_esp8266.yml
vendored
|
|
@ -1,28 +0,0 @@
|
|||
name: esp8266 port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'drivers/**'
|
||||
- 'ports/esp8266/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_esp8266_setup && ci_esp8266_path >> $GITHUB_PATH
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_esp8266_build
|
||||
27
.github/workflows/ports_m68kmac.yml
vendored
Normal file
27
.github/workflows/ports_m68kmac.yml
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
name: m68k macintosh port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/autc04/retro68
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: |
|
||||
git config --global --add safe.directory $(pwd)
|
||||
make -C mpy-cross -j$(nproc)
|
||||
make -C ports/m68kmac submodules
|
||||
make -C ports/m68kmac -j$(nproc)
|
||||
- name: Upload disk image
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: ports/m68kmac/build/micropython.dsk
|
||||
|
||||
33
.github/workflows/ports_mimxrt.yml
vendored
33
.github/workflows/ports_mimxrt.yml
vendored
|
|
@ -1,33 +0,0 @@
|
|||
name: mimxrt port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'drivers/**'
|
||||
- 'ports/mimxrt/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: 'micropython repo' # test build with space in path
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: 'micropython repo'
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_mimxrt_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_mimxrt_build
|
||||
28
.github/workflows/ports_nrf.yml
vendored
28
.github/workflows/ports_nrf.yml
vendored
|
|
@ -1,28 +0,0 @@
|
|||
name: nrf port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'drivers/**'
|
||||
- 'ports/nrf/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_nrf_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_nrf_build
|
||||
28
.github/workflows/ports_powerpc.yml
vendored
28
.github/workflows/ports_powerpc.yml
vendored
|
|
@ -1,28 +0,0 @@
|
|||
name: powerpc port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'drivers/**'
|
||||
- 'ports/powerpc/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_powerpc_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_powerpc_build
|
||||
51
.github/workflows/ports_qemu.yml
vendored
51
.github/workflows/ports_qemu.yml
vendored
|
|
@ -1,51 +0,0 @@
|
|||
name: qemu port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'drivers/**'
|
||||
- 'ports/qemu/**'
|
||||
- 'tests/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build_and_test_arm:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ci_func: # names are functions in ci.sh
|
||||
- bigendian
|
||||
- sabrelite
|
||||
- thumb
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_qemu_setup_arm
|
||||
- name: Build and run test suite ci_qemu_build_arm_${{ matrix.ci_func }}
|
||||
run: source tools/ci.sh && ci_qemu_build_arm_${{ matrix.ci_func }}
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
build_and_test_rv32:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_qemu_setup_rv32
|
||||
- name: Build and run test suite
|
||||
run: source tools/ci.sh && ci_qemu_build_rv32
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
29
.github/workflows/ports_renesas-ra.yml
vendored
29
.github/workflows/ports_renesas-ra.yml
vendored
|
|
@ -1,29 +0,0 @@
|
|||
name: renesas-ra port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'drivers/**'
|
||||
- 'ports/renesas-ra/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build_renesas_ra_board:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_renesas_ra_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_renesas_ra_board_build
|
||||
|
||||
33
.github/workflows/ports_rp2.yml
vendored
33
.github/workflows/ports_rp2.yml
vendored
|
|
@ -1,33 +0,0 @@
|
|||
name: rp2 port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'drivers/**'
|
||||
- 'ports/rp2/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: 'micropython repo' # test build with space in path
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: 'micropython repo'
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_rp2_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_rp2_build
|
||||
28
.github/workflows/ports_samd.yml
vendored
28
.github/workflows/ports_samd.yml
vendored
|
|
@ -1,28 +0,0 @@
|
|||
name: samd port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'drivers/**'
|
||||
- 'ports/samd/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_samd_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_samd_build
|
||||
36
.github/workflows/ports_stm32.yml
vendored
36
.github/workflows/ports_stm32.yml
vendored
|
|
@ -1,36 +0,0 @@
|
|||
name: stm32 port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'drivers/**'
|
||||
- 'ports/stm32/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build_stm32:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ci_func: # names are functions in ci.sh
|
||||
- stm32_pyb_build
|
||||
- stm32_nucleo_build
|
||||
- stm32_misc_build
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_stm32_setup
|
||||
- name: Build ci_${{matrix.ci_func }}
|
||||
run: source tools/ci.sh && ci_${{ matrix.ci_func }}
|
||||
|
||||
284
.github/workflows/ports_unix.yml
vendored
284
.github/workflows/ports_unix.yml
vendored
|
|
@ -1,284 +0,0 @@
|
|||
name: unix port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'examples/**'
|
||||
- 'mpy-cross/**'
|
||||
- 'ports/unix/**'
|
||||
- 'tests/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
minimal:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_minimal_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_minimal_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
reproducible:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build with reproducible date
|
||||
run: source tools/ci.sh && ci_unix_minimal_build
|
||||
env:
|
||||
SOURCE_DATE_EPOCH: 1234567890
|
||||
- name: Check reproducible build date
|
||||
run: echo | ports/unix/build-minimal/micropython -i | grep 'on 2009-02-13;'
|
||||
|
||||
standard:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_standard_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_standard_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
standard_v2:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_standard_v2_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_standard_v2_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_coverage_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_coverage_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_coverage_run_tests
|
||||
- name: Test merging .mpy files
|
||||
run: source tools/ci.sh && ci_unix_coverage_run_mpy_merge_tests
|
||||
- name: Build native mpy modules
|
||||
run: source tools/ci.sh && ci_native_mpy_modules_build
|
||||
- name: Test importing .mpy generated by mpy_ld.py
|
||||
run: source tools/ci.sh && ci_unix_coverage_run_native_mpy_tests
|
||||
- name: Run gcov coverage analysis
|
||||
run: |
|
||||
(cd ports/unix && gcov -o build-coverage/py ../../py/*.c || true)
|
||||
(cd ports/unix && gcov -o build-coverage/extmod ../../extmod/*.c || true)
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
coverage_32bit:
|
||||
runs-on: ubuntu-22.04 # use 22.04 to get libffi-dev:i386
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_32bit_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_coverage_32bit_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_coverage_32bit_run_tests
|
||||
- name: Build native mpy modules
|
||||
run: source tools/ci.sh && ci_native_mpy_modules_32bit_build
|
||||
- name: Test importing .mpy generated by mpy_ld.py
|
||||
run: source tools/ci.sh && ci_unix_coverage_32bit_run_native_mpy_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
nanbox:
|
||||
runs-on: ubuntu-22.04 # use 22.04 to get python2, and libffi-dev:i386
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_32bit_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_nanbox_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_nanbox_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
float:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_float_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_float_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
stackless_clang:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_clang_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_stackless_clang_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_stackless_clang_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
float_clang:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_clang_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_float_clang_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_float_clang_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
settrace:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
# Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests.
|
||||
# Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default.
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_settrace_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_settrace_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
settrace_stackless:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
# Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests.
|
||||
# Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default.
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_settrace_stackless_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_settrace_stackless_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
macos:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_macos_build
|
||||
- name: Run tests
|
||||
run: source tools/ci.sh && ci_unix_macos_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
qemu_mips:
|
||||
# ubuntu-22.04 is needed for older libffi.
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_qemu_mips_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_qemu_mips_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_qemu_mips_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
qemu_arm:
|
||||
# ubuntu-22.04 is needed for older libffi.
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_qemu_arm_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_qemu_arm_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_qemu_arm_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
qemu_riscv64:
|
||||
# ubuntu-22.04 is needed for older libffi.
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_qemu_riscv64_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_qemu_riscv64_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_qemu_riscv64_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
sanitize_undefined:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_coverage_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_sanitize_undefined_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_sanitize_undefined_run_tests
|
||||
- name: Test merging .mpy files
|
||||
run: source tools/ci.sh && ci_unix_coverage_run_mpy_merge_tests
|
||||
- name: Build native mpy modules
|
||||
run: source tools/ci.sh && ci_native_mpy_modules_build
|
||||
- name: Test importing .mpy generated by mpy_ld.py
|
||||
run: source tools/ci.sh && ci_unix_coverage_run_native_mpy_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
32
.github/workflows/ports_webassembly.yml
vendored
32
.github/workflows/ports_webassembly.yml
vendored
|
|
@ -1,32 +0,0 @@
|
|||
name: webassembly port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'ports/webassembly/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_webassembly_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_webassembly_build
|
||||
- name: Run tests
|
||||
run: source tools/ci.sh && ci_webassembly_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
150
.github/workflows/ports_windows.yml
vendored
150
.github/workflows/ports_windows.yml
vendored
|
|
@ -1,150 +0,0 @@
|
|||
name: windows port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'ports/unix/**'
|
||||
- 'ports/windows/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-vs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [x86, x64]
|
||||
configuration: [Debug, Release]
|
||||
variant: [dev, standard]
|
||||
visualstudio: ['2017', '2019', '2022']
|
||||
include:
|
||||
- visualstudio: '2017'
|
||||
vs_version: '[15, 16)'
|
||||
- visualstudio: '2019'
|
||||
vs_version: '[16, 17)'
|
||||
- visualstudio: '2022'
|
||||
vs_version: '[17, 18)'
|
||||
# trim down the number of jobs in the matrix
|
||||
exclude:
|
||||
- variant: standard
|
||||
configuration: Debug
|
||||
- visualstudio: '2019'
|
||||
configuration: Debug
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
CI_BUILD_CONFIGURATION: ${{ matrix.configuration }}
|
||||
steps:
|
||||
- name: Install Visual Studio 2017
|
||||
if: matrix.visualstudio == '2017'
|
||||
run: |
|
||||
choco install visualstudio2017buildtools
|
||||
choco install visualstudio2017-workload-vctools
|
||||
choco install windows-sdk-8.1
|
||||
- name: Install Visual Studio 2019
|
||||
if: matrix.visualstudio == '2019'
|
||||
run: |
|
||||
choco install visualstudio2019buildtools
|
||||
choco install visualstudio2019-workload-vctools
|
||||
choco install windows-sdk-8.1
|
||||
- uses: microsoft/setup-msbuild@v2
|
||||
with:
|
||||
vs-version: ${{ matrix.vs_version }}
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build mpy-cross.exe
|
||||
run: msbuild mpy-cross\mpy-cross.vcxproj -maxcpucount -property:Configuration=${{ matrix.configuration }} -property:Platform=${{ matrix.platform }}
|
||||
- name: Update submodules
|
||||
run: git submodule update --init lib/micropython-lib
|
||||
- name: Build micropython.exe
|
||||
run: msbuild ports\windows\micropython.vcxproj -maxcpucount -property:Configuration=${{ matrix.configuration }} -property:Platform=${{ matrix.platform }} -property:PyVariant=${{ matrix.variant }}
|
||||
- name: Get micropython.exe path
|
||||
id: get_path
|
||||
run: |
|
||||
$exePath="$(msbuild ports\windows\micropython.vcxproj -nologo -v:m -t:ShowTargetPath -property:Configuration=${{ matrix.configuration }} -property:Platform=${{ matrix.platform }} -property:PyVariant=${{ matrix.variant }})"
|
||||
echo ("micropython=" + $exePath.Trim()) >> $env:GITHUB_OUTPUT
|
||||
- name: Run tests
|
||||
id: test
|
||||
env:
|
||||
MICROPY_MICROPYTHON: ${{ steps.get_path.outputs.micropython }}
|
||||
working-directory: tests
|
||||
run: python run-tests.py
|
||||
- name: Print failures
|
||||
if: failure() && steps.test.conclusion == 'failure'
|
||||
working-directory: tests
|
||||
run: python run-tests.py --print-failures
|
||||
- name: Run mpy tests
|
||||
id: test_mpy
|
||||
env:
|
||||
MICROPY_MICROPYTHON: ${{ steps.get_path.outputs.micropython }}
|
||||
working-directory: tests
|
||||
run: python run-tests.py --via-mpy -d basics float micropython
|
||||
- name: Print mpy failures
|
||||
if: failure() && steps.test_mpy.conclusion == 'failure'
|
||||
working-directory: tests
|
||||
run: python run-tests.py --print-failures
|
||||
|
||||
build-mingw:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
variant: [dev, standard]
|
||||
sys: [mingw32, mingw64]
|
||||
include:
|
||||
- sys: mingw32
|
||||
env: i686
|
||||
- sys: mingw64
|
||||
env: x86_64
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
CHERE_INVOKING: enabled_from_arguments
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: actions/setup-python@v5
|
||||
# note: can go back to installing mingw-w64-${{ matrix.env }}-python after
|
||||
# MSYS2 updates to Python >3.12 (due to settrace compatibility issue)
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{ matrix.sys }}
|
||||
update: true
|
||||
install: >-
|
||||
make
|
||||
mingw-w64-${{ matrix.env }}-gcc
|
||||
pkg-config
|
||||
git
|
||||
diffutils
|
||||
path-type: inherit # Remove when setup-python is removed
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build mpy-cross.exe
|
||||
run: make -C mpy-cross -j2
|
||||
- name: Update submodules
|
||||
run: make -C ports/windows VARIANT=${{ matrix.variant }} submodules
|
||||
- name: Build micropython.exe
|
||||
run: make -C ports/windows -j2 VARIANT=${{ matrix.variant }}
|
||||
- name: Run tests
|
||||
id: test
|
||||
run: make -C ports/windows test_full VARIANT=${{ matrix.variant }}
|
||||
- name: Print failures
|
||||
if: failure() && steps.test.conclusion == 'failure'
|
||||
working-directory: tests
|
||||
run: python run-tests.py --print-failures
|
||||
|
||||
cross-build-on-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_windows_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_windows_build
|
||||
60
.github/workflows/ports_zephyr.yml
vendored
60
.github/workflows/ports_zephyr.yml
vendored
|
|
@ -1,60 +0,0 @@
|
|||
name: zephyr port
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/ports_zephyr.yml'
|
||||
- 'tools/**'
|
||||
- 'py/**'
|
||||
- 'extmod/**'
|
||||
- 'shared/**'
|
||||
- 'lib/**'
|
||||
- 'ports/zephyr/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# Only free up a few things so this step runs quickly.
|
||||
android: false
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: false
|
||||
docker-images: false
|
||||
swap-storage: false
|
||||
- uses: actions/checkout@v4
|
||||
- id: versions
|
||||
name: Read Zephyr version
|
||||
run: source tools/ci.sh && echo "ZEPHYR=$ZEPHYR_VERSION" | tee "$GITHUB_OUTPUT"
|
||||
- name: Cached Zephyr Workspace
|
||||
id: cache_workspace
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
# note that the Zephyr CI docker image is 15GB. At time of writing
|
||||
# GitHub caches are limited to 10GB total for a project. So we only
|
||||
# cache the "workspace"
|
||||
path: ./zephyrproject
|
||||
key: zephyr-workspace-${{ steps.versions.outputs.ZEPHYR }}
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: zephyr
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_zephyr_setup
|
||||
- name: Install Zephyr
|
||||
if: steps.cache_workspace.outputs.cache-hit != 'true'
|
||||
run: source tools/ci.sh && ci_zephyr_install
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_zephyr_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_zephyr_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
1
esp-idf
Submodule
1
esp-idf
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 8153bfe4125e6a608abccf1561fd10285016c90a
|
||||
107
ports/m68kmac/Makefile
Normal file
107
ports/m68kmac/Makefile
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
include ../../py/mkenv.mk
|
||||
|
||||
CROSS_COMPILE=m68k-apple-macos-
|
||||
|
||||
# qstr definitions (must come before including py.mk)
|
||||
QSTR_DEFS = qstrdefsport.h
|
||||
|
||||
# MicroPython feature configurations
|
||||
MICROPY_ROM_TEXT_COMPRESSION ?= 1
|
||||
|
||||
FROZEN_MANIFEST = manifest.py
|
||||
|
||||
# include py core make definitions
|
||||
include $(TOP)/py/py.mk
|
||||
include $(TOP)/extmod/extmod.mk
|
||||
|
||||
INC += -I.
|
||||
INC += -I$(TOP)
|
||||
INC += -I$(BUILD)
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
LD = $(CXX)
|
||||
CFLAGS += $(INC) -Wall -Wdouble-promotion -Wfloat-conversion -std=c99 $(COPT)
|
||||
CFLAGS += --param=min-pagesize=0
|
||||
CSUPEROPT = -Os # save some code space
|
||||
|
||||
# Tune for Debugging or Optimization
|
||||
#CFLAGS += -g # always include debug info in the ELF
|
||||
ifeq ($(DEBUG), 1)
|
||||
CFLAGS += -O0
|
||||
else
|
||||
CFLAGS += -Os -DNDEBUG
|
||||
CFLAGS += -fdata-sections -ffunction-sections
|
||||
endif
|
||||
|
||||
# Flags for optional C++ source code
|
||||
CXXFLAGS += $(filter-out -std=c99,$(CFLAGS)) -fno-rtti -fno-exceptions
|
||||
|
||||
LIBS =
|
||||
|
||||
SRC_C = \
|
||||
main.c \
|
||||
vfs_mac.c \
|
||||
macutil.c \
|
||||
|
||||
SRC_C += \
|
||||
shared/readline/readline.c \
|
||||
shared/runtime/gchelper_native.c \
|
||||
shared/runtime/interrupt_char.c \
|
||||
shared/runtime/pyexec.c \
|
||||
shared/runtime/stdout_helpers.c \
|
||||
|
||||
SRC_CXX += \
|
||||
uart_core.cpp \
|
||||
retro/Console.cpp \
|
||||
retro/ConsoleWindow.cpp \
|
||||
retro/InitConsole.cpp \
|
||||
|
||||
SRC_S += \
|
||||
shared/runtime/gchelper_m68k.s \
|
||||
|
||||
SRC_QSTR += \
|
||||
vfs_mac.c \
|
||||
shared/readline/readline.c \
|
||||
shared/runtime/pyexec.c \
|
||||
|
||||
OBJ += $(PY_CORE_O) $(PY_O)
|
||||
OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
|
||||
OBJ += $(addprefix $(BUILD)/, $(SRC_CXX:.cpp=.o))
|
||||
OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o))
|
||||
|
||||
all: $(BUILD)/micropython.bin
|
||||
|
||||
$(BUILD)/micropython.code.bin: $(OBJ)
|
||||
$(ECHO) "LINK $@"
|
||||
$(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
$(SIZE) $@.gdb
|
||||
|
||||
REZ=Rez
|
||||
RINCLUDES=/Retro68-build/toolchain/RIncludes
|
||||
REZFLAGS=-I$(RINCLUDES)
|
||||
|
||||
$(BUILD)/micropython.bin $(BUILD)/micropython.APPL $(BUILD)/micropython.dsk: $(BUILD)/micropython.code.bin
|
||||
$(REZ) $(REZFLAGS) \
|
||||
--copy "$(BUILD)/micropython.code.bin" \
|
||||
"$(RINCLUDES)/Retro68APPL.r" \
|
||||
-t "APPL" -c "mupy" \
|
||||
-o $(BUILD)/micropython.bin --cc $(BUILD)/micropython.APPL --cc $(BUILD)/micropython.dsk
|
||||
|
||||
.PHONY: docker-build
|
||||
docker-build:
|
||||
docker run --rm --mount type=bind,source=$(abspath $(TOP)),destination=/work ghcr.io/autc04/retro68 make -C /work/ports/m68kmac -j$(shell nproc)
|
||||
|
||||
.PHONY: docker-build-%
|
||||
docker-build-%:
|
||||
docker run --rm --mount type=bind,source=$(abspath $(TOP)),destination=/work ghcr.io/autc04/retro68 make -C /work/ports/m68kmac $*
|
||||
|
||||
BASE_IMG ?= base.img
|
||||
UMAC=$(HOME)/src/umac/main -w -r ~/src/umac/rom.bin
|
||||
.PHONY: run
|
||||
run:
|
||||
cp $(BASE_IMG) $(BUILD)/run.dsk
|
||||
hmount $(BUILD)/run.dsk
|
||||
hcopy $(BUILD)/micropython.bin ":Desktop Folder"
|
||||
$(UMAC) -d build/run.dsk
|
||||
|
||||
include $(TOP)/py/mkrules.mk
|
||||
47
ports/m68kmac/README.md
Normal file
47
ports/m68kmac/README.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# The m68k Mac port
|
||||
|
||||
This port runs on m68k macs. The author tests it in a modified umac emulating a
|
||||
4MB "mac plus" with System 7, though it may also run on System 6.
|
||||
|
||||
## Building and running
|
||||
|
||||
The build assumes it will occur inside the Retro68 docker image:
|
||||
|
||||
$ docker run --rm --mount type=bind,source=.,destination=/work -it ghcr.io/autc04/retro68 make -C /work/ports/m68kmac
|
||||
|
||||
A modified version of umac with multi disc image support is required.
|
||||
(It hopefully works in other emulators but this is what I use.)
|
||||
To run the executable and get a basic working REPL do:
|
||||
|
||||
$ /path/to/umac/main -r rom.bin -d HyperCardBootSystem7.img -d build/micropython.dsk
|
||||
|
||||
.. then when the micropython disk is mounted, double click it and then the
|
||||
micropython application icon. It will open up with a repl window that supports
|
||||
minimal ANSI-style escape codes.
|
||||
|
||||
## Built in editor
|
||||
|
||||
There's a built in editor:
|
||||
```py
|
||||
>>> import editor
|
||||
>>> editor.edit() ## OR
|
||||
>>> editor.edit("filename.py")
|
||||
```
|
||||
umac doesn't support arrow keys. Emacs-style ctrl+p/n f/b work to move cursor instead.
|
||||
Probably doesn't work right now on a keyboard with no control key.
|
||||
|
||||
## Key TODOs
|
||||
|
||||
* Finish `VfsMac`
|
||||
* a few more methods
|
||||
* chdir may affect a lot
|
||||
* handling `.` and `..` in paths
|
||||
* Add sys.stdin.readable & polling of sys.stdin for editor
|
||||
* auto-launch `code.py` (with bypass via shift key or something?)
|
||||
* OR double-clickable ".py" files
|
||||
* "Mac Roman" encoding support in terminal, text I/O & filenames
|
||||
* Mac API support (e.g., quickdraw, maybe arbitrary traps)
|
||||
* Support larger heap (via split heap? how does heap allocation work?)
|
||||
* Decide whether RetroConsole needs to be replaced (is GPL a problem for upstream??)
|
||||
* ctrl-c interrupt char handling
|
||||
* Any other issues that might ease upstream inclusion.
|
||||
14
ports/m68kmac/debug_print.h
Normal file
14
ports/m68kmac/debug_print.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct _mp_print_t mp_print_t;
|
||||
extern mp_print_t debug_print;
|
||||
int mp_printf(const mp_print_t *print, const char *fmt, ...);
|
||||
#define DPRINTF(fmt, ...) mp_printf(&debug_print, fmt "\n",##__VA_ARGS__)
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
4
ports/m68kmac/docker-run.sh
Executable file
4
ports/m68kmac/docker-run.sh
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
HERE="$(dirname "$0")"
|
||||
TOP="$(readlink -f "${HERE}/../..")"
|
||||
docker run -w /work/ports/m68kmac --rm --mount type=bind,source=${TOP},destination=/work -it ghcr.io/autc04/retro68 "$@"
|
||||
118
ports/m68kmac/macutil.c
Normal file
118
ports/m68kmac/macutil.c
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#include <py/runtime.h>
|
||||
#include <py/objstr.h>
|
||||
#include <py/mperrno.h>
|
||||
#include "macutil.h"
|
||||
|
||||
mp_obj_t new_bytes_from_pstr(Byte *pStr) {
|
||||
return mp_obj_new_bytes(pStr + 1, *pStr);
|
||||
}
|
||||
|
||||
mp_obj_t new_str_from_pstr(Byte *pStr) {
|
||||
return mp_obj_new_str((const char *)pStr + 1, *pStr);
|
||||
}
|
||||
|
||||
Byte *pstr_from_data(Byte *pStr, size_t pStr_size, const char *str_data, size_t str_len) {
|
||||
pStr[0] = 0;
|
||||
return pstr_cat_data(pStr, pStr_size, str_data, str_len);
|
||||
}
|
||||
|
||||
Byte *pstr_cat_data(Byte *pStr, size_t pStr_size, const char *str_data, size_t str_len) {
|
||||
int orig_len = pStr[0];
|
||||
if (str_len + 1 + orig_len >= pStr_size) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("buffer too long"));
|
||||
}
|
||||
*pStr += str_len;
|
||||
memcpy(pStr + 1 + orig_len, str_data, str_len);
|
||||
return pStr;
|
||||
}
|
||||
|
||||
Byte *pstr_from_str(Byte *pStr, size_t pStr_size, mp_obj_t obj) {
|
||||
GET_STR_DATA_LEN(obj, str_data, str_len);
|
||||
return pstr_from_data(pStr, pStr_size, (const char *)str_data, str_len);
|
||||
}
|
||||
|
||||
Byte *pstr_from_cstr(Byte *pStr, size_t pStr_size, const char *str_data) {
|
||||
return pstr_from_data(pStr, pStr_size, str_data, strlen(str_data));
|
||||
}
|
||||
|
||||
Byte *pstr_cat_str(Byte *pStr, size_t pStr_size, mp_obj_t obj) {
|
||||
GET_STR_DATA_LEN(obj, str_data, str_len);
|
||||
return pstr_cat_data(pStr, pStr_size, (const char *)str_data, str_len);
|
||||
}
|
||||
|
||||
Byte *pstr_cat_cstr(Byte *pStr, size_t pStr_size, const char *str_data) {
|
||||
return pstr_cat_data(pStr, pStr_size, str_data, strlen(str_data));
|
||||
}
|
||||
|
||||
int convert_mac_err(OSErr e) {
|
||||
int err = MP_EINVAL;
|
||||
switch (e) {
|
||||
case nsvErr: // "No such volume"
|
||||
case noMacDskErr:
|
||||
case extFSErr:
|
||||
case badMDBErr:
|
||||
err = MP_ENODEV;
|
||||
break;
|
||||
case gfpErr: // "Error during GetFPos"
|
||||
case fsRnErr: // "Problem during rename"
|
||||
case posErr:
|
||||
case bdNamErr:
|
||||
case paramErr:
|
||||
case rfNumErr:
|
||||
err = MP_EINVAL;
|
||||
break;
|
||||
case dirFulErr:
|
||||
err = MP_ENFILE;
|
||||
break;
|
||||
case dskFulErr:
|
||||
err = MP_EPIPE;
|
||||
break;
|
||||
case dupFNErr:
|
||||
err = MP_EEXIST;
|
||||
break;
|
||||
case ioErr:
|
||||
case eofErr:
|
||||
err = MP_EIO;
|
||||
break;
|
||||
case fBsyErr:
|
||||
err = MP_EBUSY;
|
||||
break;
|
||||
case volOffLinErr:
|
||||
err = MP_EISCONN;
|
||||
break;
|
||||
case fLckdErr:
|
||||
err = MP_EWOULDBLOCK;
|
||||
break;
|
||||
case dirNFErr:
|
||||
case fnfErr:
|
||||
err = MP_ENOENT;
|
||||
break;
|
||||
case fnOpnErr:
|
||||
err = MP_EBADF;
|
||||
break;
|
||||
case wPrErr:
|
||||
case wrPermErr:
|
||||
case vLckdErr:
|
||||
case permErr:
|
||||
case opWrErr:
|
||||
err = MP_EPERM;
|
||||
break;
|
||||
case tmwdoErr:
|
||||
case tmfoErr:
|
||||
err = MP_EMFILE;
|
||||
break;
|
||||
case wrgVolTypErr:
|
||||
err = MP_EINVAL;
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
MP_NORETURN void raise_mac_err(OSErr e) {
|
||||
mp_raise_OSError(convert_mac_err(e));
|
||||
}
|
||||
|
||||
void check_mac_err(OSErr e) {
|
||||
if (e != noErr) {
|
||||
raise_mac_err(e);
|
||||
}
|
||||
}
|
||||
21
ports/m68kmac/macutil.h
Normal file
21
ports/m68kmac/macutil.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#include <py/obj.h>
|
||||
#include "Multiverse.h"
|
||||
|
||||
void check_mac_err(OSErr e);
|
||||
MP_NORETURN void raise_mac_err(OSErr e);
|
||||
int convert_mac_err(OSErr e);
|
||||
mp_obj_t new_str_from_pstr(Byte *pStr);
|
||||
mp_obj_t new_bytes_from_pstr(Byte *pStr);
|
||||
Byte *pstr_from_str(Byte *pStr, size_t pStr_size, mp_obj_t obj);
|
||||
Byte *pstr_from_data(Byte *pStr, size_t pStr_size, const char *str_data, size_t str_len);
|
||||
Byte *pstr_from_cstr(Byte *pStr, size_t pStr_size, const char *str_data);
|
||||
Byte *pstr_cat_str(Byte *pStr, size_t pStr_size, mp_obj_t obj);
|
||||
Byte *pstr_cat_cstr(Byte *pStr, size_t pStr_size, const char *str_data);
|
||||
Byte *pstr_cat_data(Byte *pStr, size_t pStr_size, const char *str_data, size_t str_len);
|
||||
|
||||
#define PSTR_FROM_STR(pStr, obj) pstr_from_str(pStr, MP_ARRAY_SIZE(pStr), obj)
|
||||
#define PSTR_FROM_CSTR(pStr, str) pstr_from_cstr(pStr, MP_ARRAY_SIZE(pStr), str)
|
||||
#define PSTR_FROM_DATA(pStr, ptr, len) pstr_from_data(pStr, MP_ARRAY_SIZE(pStr), ptr, len)
|
||||
|
||||
#define PSTR_LEN(p) (*(p))
|
||||
#define PSTR_DATA(p) ((char *)((p) + 1))
|
||||
177
ports/m68kmac/main.c
Normal file
177
ports/m68kmac/main.c
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Multiverse.h"
|
||||
#include "macutil.h"
|
||||
#include "uart_core.h"
|
||||
|
||||
#include "py/builtin.h"
|
||||
#include "py/compile.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/repl.h"
|
||||
#include "py/gc.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "py/stackctrl.h"
|
||||
#include "shared/runtime/pyexec.h"
|
||||
#include "shared/runtime/gchelper.h"
|
||||
#include "shared/readline/readline.h"
|
||||
#include "shared/runtime/interrupt_char.h"
|
||||
#include "extmod/vfs.h"
|
||||
#include "extmod/vfs_posix.h"
|
||||
|
||||
#if MICROPY_ENABLE_COMPILER
|
||||
static int execute_from_lexer(mp_obj_t source, mp_parse_input_kind_t input_kind, bool is_repl) {
|
||||
mp_hal_set_interrupt_char(CHAR_CTRL_C);
|
||||
|
||||
nlr_buf_t nlr;
|
||||
if (nlr_push(&nlr) == 0) {
|
||||
// create lexer based on source kind
|
||||
mp_lexer_t *lex;
|
||||
lex = mp_lexer_new_from_file(mp_obj_str_get_qstr(source));
|
||||
qstr source_name = lex->source_name;
|
||||
mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
|
||||
mp_obj_t module_fun = mp_compile(&parse_tree, source_name, is_repl);
|
||||
mp_call_function_0(module_fun);
|
||||
mp_hal_set_interrupt_char(-1);
|
||||
mp_handle_pending(true);
|
||||
nlr_pop();
|
||||
return 0;
|
||||
} else {
|
||||
// uncaught exception
|
||||
mp_hal_set_interrupt_char(-1);
|
||||
mp_handle_pending(false);
|
||||
mp_obj_base_t *exc = nlr.ret_val;
|
||||
mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(exc));
|
||||
(void)mp_hal_stdin_rx_chr();
|
||||
return 1;
|
||||
}
|
||||
mp_printf(&mp_plat_print, "Press any key to exit");
|
||||
}
|
||||
#endif
|
||||
|
||||
void LMSetApplLimit_checked(Ptr val) {
|
||||
if (val > LMGetHeapEnd()) {
|
||||
LMSetApplLimit(val);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Enlarge stack to at least 32kB
|
||||
LMSetApplLimit_checked(LMGetCurStackBase() - 32768);
|
||||
|
||||
// Set MP stack limits, reserving 1kB stack for internal use
|
||||
mp_stack_set_top(LMGetCurStackBase());
|
||||
mp_stack_set_limit(LMGetApplLimit() + 1024);
|
||||
|
||||
// Use half the largest possible block as MicroPython heap
|
||||
Size growmax;
|
||||
(void)MaxMem(&growmax);
|
||||
size_t heap_size = growmax / 2;
|
||||
unsigned char *heap = (unsigned char *)NewPtr(growmax / 2);
|
||||
|
||||
// Check if we were given a file ...
|
||||
short message, count;
|
||||
CountAppFiles(&message, &count);
|
||||
AppFile file = {};
|
||||
if (count) {
|
||||
GetAppFiles(1, &file);
|
||||
}
|
||||
|
||||
gc_init(heap, heap + heap_size);
|
||||
mp_init();
|
||||
|
||||
{
|
||||
// Mount the host FS at the root of our internal VFS
|
||||
mp_obj_t args[2] = {
|
||||
MP_OBJ_TYPE_GET_SLOT(&mp_type_vfs_mac, make_new)(&mp_type_vfs_mac, 0, 0, NULL),
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR__slash_),
|
||||
};
|
||||
mp_vfs_mount(2, args, (mp_map_t *)&mp_const_empty_map);
|
||||
|
||||
// Make sure the root that was just mounted is the current VFS (it's always at
|
||||
// the end of the linked list). Can't use chdir('/') because that will change
|
||||
// the current path within the VfsPosix object.
|
||||
MP_STATE_VM(vfs_cur) = MP_STATE_VM(vfs_mount_table);
|
||||
while (MP_STATE_VM(vfs_cur)->next != NULL) {
|
||||
MP_STATE_VM(vfs_cur) = MP_STATE_VM(vfs_cur)->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (count) {
|
||||
mp_obj_t filename = new_str_from_pstr(file.fName);
|
||||
execute_from_lexer(filename, MP_PARSE_FILE_INPUT, false);
|
||||
} else {
|
||||
#if MICROPY_REPL_EVENT_DRIVEN
|
||||
pyexec_event_repl_init();
|
||||
for (;;) {
|
||||
int c = mp_hal_stdin_rx_chr();
|
||||
if (pyexec_event_repl_process_char(c)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
pyexec_friendly_repl();
|
||||
#endif
|
||||
}
|
||||
mp_deinit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MP_NORETURN __fatal_error(const char *msg);
|
||||
|
||||
void gc_collect(void) {
|
||||
// WARNING: This gc_collect implementation doesn't try to get root
|
||||
// pointers from CPU registers, and thus may function incorrectly.
|
||||
gc_collect_start();
|
||||
gc_helper_collect_regs_and_stack();
|
||||
#if MICROPY_PY_THREAD
|
||||
mp_thread_gc_others();
|
||||
#endif
|
||||
gc_collect_end();
|
||||
}
|
||||
|
||||
void nlr_jump_fail(void *val) {
|
||||
__fatal_error("nlr jump fail");
|
||||
}
|
||||
|
||||
void MP_NORETURN __fatal_error(const char *msg) {
|
||||
mp_printf(&mp_plat_print, "Fatal: %s\n", msg);
|
||||
while (1) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr) {
|
||||
printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line);
|
||||
__fatal_error("Assertion failed");
|
||||
}
|
||||
#endif
|
||||
|
||||
#define TICK_TO_MS(x) ((x + 2) * 50 / 3)
|
||||
#define TICK_TO_US(x) ((x + 2) * 50000 / 3)
|
||||
#define MS_TO_TICK(x) ((x + 25) * 3 / 50)
|
||||
#define US_TO_TICK(x) ((x + 25000) * 3 / 50000)
|
||||
|
||||
void mp_hal_delay_ms(mp_uint_t delay) {
|
||||
long unused;
|
||||
Delay(MS_TO_TICK(delay), &unused);
|
||||
}
|
||||
|
||||
void mp_hal_delay_us(mp_uint_t delay) {
|
||||
long unused;
|
||||
Delay(US_TO_TICK(delay), &unused);
|
||||
}
|
||||
|
||||
mp_uint_t mp_hal_ticks_ms(void) {
|
||||
return TICK_TO_MS(TickCount());
|
||||
}
|
||||
|
||||
mp_uint_t mp_hal_ticks_us(void) {
|
||||
return TICK_TO_US(TickCount());
|
||||
}
|
||||
|
||||
mp_uint_t mp_hal_ticks_cpu(void) {
|
||||
return 0;
|
||||
}
|
||||
2
ports/m68kmac/manifest.py
Normal file
2
ports/m68kmac/manifest.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
freeze("$(PORT_DIR)/modules")
|
||||
include("$(MPY_DIR)/extmod/asyncio")
|
||||
8
ports/m68kmac/modules/editor/__init__.py
Normal file
8
ports/m68kmac/modules/editor/__init__.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
def edit(filename=None):
|
||||
if filename is None:
|
||||
from . import picker
|
||||
|
||||
filename = picker.pick_file()
|
||||
from . import editor
|
||||
|
||||
editor.edit(filename)
|
||||
151
ports/m68kmac/modules/editor/dang.py
Normal file
151
ports/m68kmac/modules/editor/dang.py
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
try:
|
||||
import select
|
||||
except:
|
||||
select = None
|
||||
|
||||
import sys
|
||||
# pylint: disable=no-self-use
|
||||
|
||||
try:
|
||||
import termios
|
||||
|
||||
_orig_attr = None # pylint: disable=invalid-name
|
||||
|
||||
def _nonblocking():
|
||||
global _orig_attr # pylint: disable=global-statement
|
||||
_orig_attr = termios.tcgetattr(sys.stdin)
|
||||
attr = termios.tcgetattr(sys.stdin)
|
||||
attr[3] &= ~(termios.ECHO | termios.ICANON)
|
||||
attr[6][termios.VMIN] = 1
|
||||
attr[6][termios.VTIME] = 0
|
||||
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, attr)
|
||||
|
||||
def _blocking():
|
||||
if _orig_attr is not None:
|
||||
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, _orig_attr)
|
||||
|
||||
except ImportError:
|
||||
|
||||
def _nonblocking():
|
||||
pass
|
||||
|
||||
def _blocking():
|
||||
pass
|
||||
|
||||
|
||||
LINES = 24
|
||||
COLS = 80
|
||||
|
||||
|
||||
def CTRL(c):
|
||||
return chr(ord(c) & 0x1F)
|
||||
|
||||
|
||||
special_keys = {
|
||||
# Some emacs style bindings for Mac w/o arrow keys
|
||||
CTRL('a'): "KEY_HOME",
|
||||
CTRL('e'): "KEY_END",
|
||||
CTRL('p'): "KEY_UP",
|
||||
CTRL('n'): "KEY_DOWN",
|
||||
CTRL('b'): "KEY_LEFT",
|
||||
CTRL('f'): "KEY_RIGHT",
|
||||
CTRL('v'): "KEY_PGDN",
|
||||
"\x1bv": "KEY_PGUP",
|
||||
CTRL('d'): "KEY_DELETE",
|
||||
"\x1b": ..., # all prefixes of special keys must be entered as Ellipsis
|
||||
"\x1b[": ...,
|
||||
"\x1b[5": ...,
|
||||
"\x1b[6": ...,
|
||||
"\x1b[A": "KEY_UP",
|
||||
"\x1b[B": "KEY_DOWN",
|
||||
"\x1b[C": "KEY_RIGHT",
|
||||
"\x1b[D": "KEY_LEFT",
|
||||
"\x1b[H": "KEY_HOME",
|
||||
"\x1b[F": "KEY_END",
|
||||
"\x1b[5~": "KEY_PGUP",
|
||||
"\x1b[6~": "KEY_PGDN",
|
||||
"\x1b[3~": "KEY_DELETE",
|
||||
}
|
||||
|
||||
|
||||
class FakePoll:
|
||||
def poll(self, timeout):
|
||||
return True
|
||||
|
||||
|
||||
class Screen:
|
||||
def __init__(self):
|
||||
if select is None:
|
||||
self._poll = select.poll()
|
||||
self._poll.register(sys.stdin, select.POLLIN)
|
||||
else:
|
||||
self._poll = FakePoll()
|
||||
self._pending = ""
|
||||
|
||||
def _sys_stdin_readable(self):
|
||||
return hasattr(sys.stdin, "readable") and sys.stdin.readable()
|
||||
|
||||
def _sys_stdout_flush(self):
|
||||
if hasattr(sys, 'stdout') and hasattr(sys.stdout, "flush"):
|
||||
sys.stdout.flush()
|
||||
|
||||
def _terminal_read_blocking(self):
|
||||
return sys.stdin.read(1)
|
||||
|
||||
def _terminal_read_timeout(self, timeout):
|
||||
if self._sys_stdin_readable() or self._poll.poll(timeout):
|
||||
r = sys.stdin.read(1)
|
||||
return r
|
||||
return None
|
||||
|
||||
def move(self, y, x):
|
||||
print(end=f"\033[{y + 1};{x + 1}H")
|
||||
|
||||
def erase(self):
|
||||
print(end="\033H\033[2J")
|
||||
|
||||
def addstr(self, y, x, text):
|
||||
self.move(y, x)
|
||||
print(end=text)
|
||||
|
||||
def getkey(self):
|
||||
self._sys_stdout_flush()
|
||||
pending = self._pending
|
||||
if pending and (code := special_keys.get(pending)) is None:
|
||||
self._pending = pending[1:]
|
||||
return pending[0]
|
||||
|
||||
while True:
|
||||
if pending:
|
||||
c = self._terminal_read_timeout(50)
|
||||
if c is None:
|
||||
self._pending = pending[1:]
|
||||
return pending[0]
|
||||
else:
|
||||
c = self._terminal_read_blocking()
|
||||
c = pending + c
|
||||
|
||||
code = special_keys.get(c)
|
||||
|
||||
if code is None:
|
||||
self._pending = c[1:]
|
||||
return c[0]
|
||||
if code is not Ellipsis:
|
||||
return code
|
||||
|
||||
pending = c
|
||||
|
||||
|
||||
def wrapper(func, *args, **kwds):
|
||||
stdscr = Screen()
|
||||
try:
|
||||
_nonblocking()
|
||||
return func(stdscr, *args, **kwds)
|
||||
finally:
|
||||
_blocking()
|
||||
stdscr.move(LINES - 1, 0)
|
||||
print("\n")
|
||||
284
ports/m68kmac/modules/editor/editor.py
Normal file
284
ports/m68kmac/modules/editor/editor.py
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
# SPDX-FileCopyrightText: 2020 Wasim Lorgat
|
||||
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
|
||||
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import gc
|
||||
import os
|
||||
from . import dang as curses
|
||||
# pylint: disable=redefined-builtin
|
||||
|
||||
|
||||
class MaybeDisableReload:
|
||||
def __enter__(self):
|
||||
try:
|
||||
from supervisor import runtime # pylint: disable=import-outside-toplevel
|
||||
except ImportError:
|
||||
return
|
||||
|
||||
self._old_autoreload = ( # pylint: disable=attribute-defined-outside-init
|
||||
runtime.autoreload
|
||||
)
|
||||
runtime.autoreload = False
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
try:
|
||||
from supervisor import runtime # pylint: disable=import-outside-toplevel
|
||||
except ImportError:
|
||||
return
|
||||
|
||||
runtime.autoreload = self._old_autoreload
|
||||
|
||||
|
||||
def os_exists(filename):
|
||||
try:
|
||||
with open(filename, "r"):
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def gc_mem_free_hint():
|
||||
if hasattr(gc, "mem_free"):
|
||||
gc.collect()
|
||||
return f" | free: {gc.mem_free()}"
|
||||
return ""
|
||||
|
||||
|
||||
class Buffer:
|
||||
def __init__(self, lines):
|
||||
self.lines = lines
|
||||
|
||||
def __len__(self):
|
||||
return len(self.lines)
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.lines[index]
|
||||
|
||||
@property
|
||||
def bottom(self):
|
||||
return len(self) - 1
|
||||
|
||||
def insert(self, cursor, string):
|
||||
row, col = cursor.row, cursor.col
|
||||
try:
|
||||
current = self.lines.pop(row)
|
||||
except IndexError:
|
||||
current = ""
|
||||
new = current[:col] + string + current[col:]
|
||||
self.lines.insert(row, new)
|
||||
|
||||
def split(self, cursor):
|
||||
row, col = cursor.row, cursor.col
|
||||
current = self.lines.pop(row)
|
||||
self.lines.insert(row, current[:col])
|
||||
self.lines.insert(row + 1, current[col:])
|
||||
|
||||
def delete(self, cursor):
|
||||
row, col = cursor.row, cursor.col
|
||||
if (row, col) < (self.bottom, len(self[row])):
|
||||
current = self.lines.pop(row)
|
||||
if col < len(current):
|
||||
new = current[:col] + current[col + 1 :]
|
||||
self.lines.insert(row, new)
|
||||
else:
|
||||
nextline = self.lines.pop(row)
|
||||
new = current + nextline
|
||||
self.lines.insert(row, new)
|
||||
|
||||
|
||||
def clamp(x, lower, upper):
|
||||
if x < lower:
|
||||
return lower
|
||||
if x > upper:
|
||||
return upper
|
||||
return x
|
||||
|
||||
|
||||
class Cursor:
|
||||
def __init__(self, row=0, col=0, col_hint=None):
|
||||
self.row = row
|
||||
self._col = col
|
||||
self._col_hint = col if col_hint is None else col_hint
|
||||
|
||||
@property
|
||||
def col(self):
|
||||
return self._col
|
||||
|
||||
@col.setter
|
||||
def col(self, col):
|
||||
self._col = col
|
||||
self._col_hint = col
|
||||
|
||||
def _clamp_col(self, buffer):
|
||||
self._col = min(self._col_hint, len(buffer[self.row]))
|
||||
|
||||
def up(self, buffer): # pylint: disable=invalid-name
|
||||
if self.row > 0:
|
||||
self.row -= 1
|
||||
self._clamp_col(buffer)
|
||||
|
||||
def down(self, buffer):
|
||||
if self.row < len(buffer) - 1:
|
||||
self.row += 1
|
||||
self._clamp_col(buffer)
|
||||
|
||||
def left(self, buffer):
|
||||
if self.col > 0:
|
||||
self.col -= 1
|
||||
elif self.row > 0:
|
||||
self.row -= 1
|
||||
self.col = len(buffer[self.row])
|
||||
|
||||
def right(self, buffer):
|
||||
if len(buffer) > 0 and self.col < len(buffer[self.row]):
|
||||
self.col += 1
|
||||
elif self.row < len(buffer) - 1:
|
||||
self.row += 1
|
||||
self.col = 0
|
||||
|
||||
def end(self, buffer):
|
||||
self.col = len(buffer[self.row])
|
||||
|
||||
|
||||
class Window:
|
||||
def __init__(self, n_rows, n_cols, row=0, col=0):
|
||||
self.n_rows = n_rows
|
||||
self.n_cols = n_cols
|
||||
self.row = row
|
||||
self.col = col
|
||||
|
||||
@property
|
||||
def bottom(self):
|
||||
return self.row + self.n_rows - 1
|
||||
|
||||
def up(self, cursor): # pylint: disable=invalid-name
|
||||
if cursor.row == self.row - 1 and self.row > 0:
|
||||
self.row -= 1
|
||||
|
||||
def down(self, buffer, cursor):
|
||||
if cursor.row == self.bottom + 1 and self.bottom < len(buffer) - 1:
|
||||
self.row += 1
|
||||
|
||||
def horizontal_scroll(self, cursor, left_margin=5, right_margin=2):
|
||||
n_pages = cursor.col // (self.n_cols - right_margin)
|
||||
self.col = max(n_pages * self.n_cols - right_margin - left_margin, 0)
|
||||
|
||||
def translate(self, cursor):
|
||||
return cursor.row - self.row, cursor.col - self.col
|
||||
|
||||
|
||||
def left(window, buffer, cursor):
|
||||
cursor.left(buffer)
|
||||
window.up(cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
|
||||
|
||||
def right(window, buffer, cursor):
|
||||
cursor.right(buffer)
|
||||
window.down(buffer, cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
|
||||
|
||||
def home(window, buffer, cursor): # pylint: disable=unused-argument
|
||||
cursor.col = 0
|
||||
window.horizontal_scroll(cursor)
|
||||
|
||||
|
||||
def end(window, buffer, cursor):
|
||||
cursor.end(buffer)
|
||||
window.horizontal_scroll(cursor)
|
||||
|
||||
|
||||
def editor(stdscr, filename): # pylint: disable=too-many-branches,too-many-statements
|
||||
if os_exists(filename):
|
||||
with open(filename, "r", encoding="utf-8") as f:
|
||||
buffer = Buffer(f.read().splitlines())
|
||||
else:
|
||||
buffer = Buffer([""])
|
||||
|
||||
window = Window(curses.LINES - 1, curses.COLS - 1)
|
||||
cursor = Cursor()
|
||||
|
||||
stdscr.erase()
|
||||
|
||||
img = [None] * curses.LINES
|
||||
|
||||
def setline(row, line):
|
||||
if img[row] == line:
|
||||
return
|
||||
img[row] = line
|
||||
line += " " * (window.n_cols - len(line))
|
||||
stdscr.addstr(row, 0, line)
|
||||
|
||||
while True:
|
||||
lastrow = 0
|
||||
for row, line in enumerate(buffer[window.row : window.row + window.n_rows]):
|
||||
lastrow = row
|
||||
if row == cursor.row - window.row and window.col > 0:
|
||||
line = "«" + line[window.col + 1 :]
|
||||
if len(line) > window.n_cols:
|
||||
line = line[: window.n_cols - 1] + "»"
|
||||
setline(row, line)
|
||||
for row in range(lastrow + 1, window.n_rows):
|
||||
setline(row, "~~ EOF ~~")
|
||||
row = curses.LINES - 1
|
||||
line = f"{filename:12} | ^X: write & exit | ^C: quit w/o save{gc_mem_free_hint()}"
|
||||
setline(row, line)
|
||||
|
||||
stdscr.move(*window.translate(cursor))
|
||||
|
||||
k = stdscr.getkey()
|
||||
if len(k) == 1 and " " <= k <= "~":
|
||||
buffer.insert(cursor, k)
|
||||
for _ in k:
|
||||
right(window, buffer, cursor)
|
||||
elif k == "\x18": # ctrl-x
|
||||
with open(filename, "w", encoding="utf-8") as f:
|
||||
for row in buffer:
|
||||
f.write(f"{row}\n")
|
||||
return
|
||||
elif k == "\x11": # Ctrl-Q
|
||||
for row in buffer:
|
||||
print(row)
|
||||
elif k == "KEY_HOME":
|
||||
home(window, buffer, cursor)
|
||||
elif k == "KEY_END":
|
||||
end(window, buffer, cursor)
|
||||
elif k == "KEY_LEFT":
|
||||
left(window, buffer, cursor)
|
||||
elif k == "KEY_DOWN":
|
||||
cursor.down(buffer)
|
||||
window.down(buffer, cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
elif k == "KEY_PGDN":
|
||||
for _ in range(window.n_rows):
|
||||
cursor.down(buffer)
|
||||
window.down(buffer, cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
elif k == "KEY_UP":
|
||||
cursor.up(buffer)
|
||||
window.up(cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
elif k == "KEY_PGUP":
|
||||
for _ in range(window.n_rows):
|
||||
cursor.up(buffer)
|
||||
window.up(cursor)
|
||||
window.horizontal_scroll(cursor)
|
||||
elif k == "KEY_RIGHT":
|
||||
right(window, buffer, cursor)
|
||||
elif k == "\n" or k == "\r":
|
||||
buffer.split(cursor)
|
||||
right(window, buffer, cursor)
|
||||
elif k in ("KEY_DELETE", "\x04"):
|
||||
buffer.delete(cursor)
|
||||
|
||||
elif k in ("KEY_BACKSPACE", "\x7f", "\x08"):
|
||||
if (cursor.row, cursor.col) > (0, 0):
|
||||
left(window, buffer, cursor)
|
||||
buffer.delete(cursor)
|
||||
|
||||
|
||||
def edit(filename):
|
||||
return curses.wrapper(editor, filename)
|
||||
72
ports/m68kmac/modules/editor/picker.py
Normal file
72
ports/m68kmac/modules/editor/picker.py
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import os
|
||||
|
||||
from . import dang as curses
|
||||
|
||||
always = ["code.py", "boot.py", "settings.toml", "boot_out.txt"]
|
||||
good_extensions = [".py", ".toml", ".txt", ".json"]
|
||||
|
||||
|
||||
def os_exists(filename):
|
||||
try:
|
||||
os.stat(filename)
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def isdir(filename):
|
||||
st_mode = os.stat(filename)[0]
|
||||
r = st_mode & 0o40_000
|
||||
print(f"isdir {filename} {st_mode=:o} {r=}")
|
||||
return r
|
||||
|
||||
|
||||
def has_good_extension(filename):
|
||||
for g in good_extensions:
|
||||
if filename.endswith(g):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def picker(stdscr, options, notes=(), start_idx=0):
|
||||
stdscr.erase()
|
||||
stdscr.addstr(curses.LINES - 1, 0, "Enter: select | ^C: quit")
|
||||
del options[curses.LINES - 1 :]
|
||||
for row, option in enumerate(options):
|
||||
if row < len(notes) and (note := notes[row]):
|
||||
option = f"{option} {note}"
|
||||
|
||||
stdscr.addstr(row, 3, option)
|
||||
|
||||
old_idx = None
|
||||
idx = start_idx
|
||||
while True:
|
||||
if idx != old_idx:
|
||||
if old_idx is not None:
|
||||
stdscr.addstr(old_idx, 0, " ")
|
||||
stdscr.addstr(idx, 0, "=>")
|
||||
old_idx = idx
|
||||
|
||||
k = stdscr.getkey()
|
||||
|
||||
if k == "KEY_DOWN":
|
||||
idx = min(idx + 1, len(options) - 1)
|
||||
elif k == "KEY_UP":
|
||||
idx = max(idx - 1, 0)
|
||||
elif k in ("\r", "\n"):
|
||||
return options[idx]
|
||||
elif k == "\3":
|
||||
raise SystemExit
|
||||
|
||||
|
||||
def pick_file():
|
||||
options = always[:] + sorted(
|
||||
(g for g in os.listdir("") if g not in always and not isdir(g) and not g.startswith(".")),
|
||||
key=lambda filename: (not has_good_extension(filename), filename),
|
||||
)
|
||||
notes = [None if os_exists(filename) else "(NEW)" for filename in options]
|
||||
return curses.wrapper(picker, options, notes)
|
||||
55
ports/m68kmac/mpconfigport.h
Normal file
55
ports/m68kmac/mpconfigport.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#include <stdint.h>
|
||||
|
||||
// options to control how MicroPython is built
|
||||
|
||||
#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_B)
|
||||
|
||||
// Use the minimal starting configuration (disables all optional features).
|
||||
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES)
|
||||
|
||||
// You can disable the built-in MicroPython compiler by setting the following
|
||||
// config option to 0. If you do this then you won't get a REPL prompt, but you
|
||||
// will still be able to execute pre-compiled scripts, compiled with mpy-cross.
|
||||
#define MICROPY_ENABLE_COMPILER (1)
|
||||
|
||||
#define MICROPY_QSTR_EXTRA_POOL mp_qstr_frozen_const_pool
|
||||
#define MICROPY_ENABLE_GC (1)
|
||||
#define MICROPY_HELPER_REPL (1)
|
||||
#define MICROPY_ENABLE_EXTERNAL_IMPORT (1)
|
||||
#define MICROPY_PY_UCTYPES (0)
|
||||
#define MICROPY_PY_SYS_STDFILES (1)
|
||||
#define MICROPY_STACK_CHECK (0)
|
||||
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
|
||||
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
|
||||
#define MICROPY_READER_POSIX (0)
|
||||
#define MICROPY_READER_VFS (1)
|
||||
#define MICROPY_VFS (1)
|
||||
#define MICROPY_PY_OS_STATVFS (0)
|
||||
|
||||
#define MP_SSIZE_MAX LONG_MAX
|
||||
|
||||
#define MICROPY_ALLOC_PATH_MAX (256)
|
||||
|
||||
// Use the minimum headroom in the chunk allocator for parse nodes.
|
||||
#define MICROPY_ALLOC_PARSE_CHUNK_INIT (16)
|
||||
|
||||
// type definitions for the specific machine
|
||||
|
||||
typedef intptr_t mp_int_t; // must be pointer size
|
||||
typedef uintptr_t mp_uint_t; // must be pointer size
|
||||
typedef long mp_off_t;
|
||||
|
||||
// We need to provide a declaration/definition of alloca()
|
||||
#include <alloca.h>
|
||||
|
||||
#define MICROPY_HW_BOARD_NAME "macplus"
|
||||
#define MICROPY_HW_MCU_NAME "m68000"
|
||||
|
||||
#define MICROPY_MIN_USE_STDOUT (0)
|
||||
#define MICROPY_HEAP_SIZE (100 * 1024)
|
||||
|
||||
#define MP_STATE_PORT MP_STATE_VM
|
||||
|
||||
typedef struct _mp_obj_type_t mp_obj_type_t;
|
||||
extern const mp_obj_type_t mp_type_vfs_mac;
|
||||
#define MICROPY_VFS_PORT { MP_ROM_QSTR(MP_QSTR_VfsMac), MP_ROM_PTR(&mp_type_vfs_mac) }
|
||||
20
ports/m68kmac/mphalport.h
Normal file
20
ports/m68kmac/mphalport.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
static inline void mp_hal_set_interrupt_char(char c) {
|
||||
}
|
||||
|
||||
// This macro is used to implement PEP 475 to retry specified syscalls on EINTR
|
||||
#define MP_HAL_RETRY_SYSCALL(ret, syscall, raise) { \
|
||||
for (;;) { \
|
||||
MP_THREAD_GIL_EXIT(); \
|
||||
ret = syscall; \
|
||||
MP_THREAD_GIL_ENTER(); \
|
||||
if (ret == -1) { \
|
||||
int err = errno; \
|
||||
if (err == EINTR) { \
|
||||
mp_handle_pending(true); \
|
||||
continue; \
|
||||
} \
|
||||
raise; \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
2
ports/m68kmac/qstrdefsport.h
Normal file
2
ports/m68kmac/qstrdefsport.h
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// qstrs specific to this port
|
||||
// *FORMAT-OFF*
|
||||
900
ports/m68kmac/retro/Console.cpp
Normal file
900
ports/m68kmac/retro/Console.cpp
Normal file
|
|
@ -0,0 +1,900 @@
|
|||
/*
|
||||
Copyright 2012-2020 Wolfgang Thaller, Davide Bucci
|
||||
|
||||
This file is part of Retro68.
|
||||
|
||||
Retro68 is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Retro68 is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Under Section 7 of GPL version 3, you are granted additional
|
||||
permissions described in the GCC Runtime Library Exception, version
|
||||
3.1, as published by the Free Software Foundation.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Retro68. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Console.h"
|
||||
#include "MacUtils.h"
|
||||
#include "Fonts.h"
|
||||
#include "Processes.h"
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
#include <stack>
|
||||
#include <sstream>
|
||||
|
||||
using namespace retro;
|
||||
|
||||
const char BEL = 7;
|
||||
const char MAX_LEN = 250;
|
||||
|
||||
Console *Console::currentInstance = NULL;
|
||||
|
||||
Attributes::Attributes(void)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
void Attributes::reset(void)
|
||||
{
|
||||
cBold=false;
|
||||
cUnderline=false;
|
||||
cItalic=false;
|
||||
}
|
||||
|
||||
bool Attributes::isBold(void) const
|
||||
{
|
||||
return cBold;
|
||||
}
|
||||
|
||||
|
||||
bool Attributes::isUnderline(void) const
|
||||
{
|
||||
return cUnderline;
|
||||
}
|
||||
|
||||
bool Attributes::isItalic(void) const
|
||||
{
|
||||
return cItalic;
|
||||
}
|
||||
|
||||
void Attributes::setBold(const bool v)
|
||||
{
|
||||
cBold=v;
|
||||
}
|
||||
|
||||
void Attributes::setItalic(const bool v)
|
||||
{
|
||||
cItalic=v;
|
||||
}
|
||||
|
||||
void Attributes::setUnderline(const bool v)
|
||||
{
|
||||
cUnderline=v;
|
||||
}
|
||||
|
||||
inline bool operator==(const Attributes& lhs, const Attributes& rhs)
|
||||
{
|
||||
return lhs.isBold()==rhs.isBold() && lhs.isUnderline()==rhs.isUnderline() && lhs.isItalic()==rhs.isItalic();
|
||||
}
|
||||
|
||||
inline bool operator!=(const Attributes& lhs, const Attributes& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
inline bool operator==(const AttributedChar& lhs, const AttributedChar& rhs)
|
||||
{
|
||||
return lhs.c==rhs.c && lhs.attrs==rhs.attrs;
|
||||
}
|
||||
|
||||
inline bool operator!=(const AttributedChar& lhs, const AttributedChar& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class FontSetup
|
||||
{
|
||||
short saveFont, saveSize, saveFace;
|
||||
public:
|
||||
FontSetup()
|
||||
{
|
||||
#if TARGET_API_MAC_CARBON
|
||||
GrafPtr port;
|
||||
GetPort(&port);
|
||||
saveFont = GetPortTextFont(port);
|
||||
saveSize = GetPortTextSize(port);
|
||||
#else
|
||||
saveFont = qd.thePort->txFont;
|
||||
saveSize = qd.thePort->txSize;
|
||||
saveFace = qd.thePort->txFace;
|
||||
#endif
|
||||
TextFont(kFontIDMonaco);
|
||||
TextSize(9);
|
||||
TextFace(normal);
|
||||
}
|
||||
|
||||
~FontSetup()
|
||||
{
|
||||
TextFont(saveFont);
|
||||
TextSize(saveSize);
|
||||
TextFace(saveFace);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Console::Console()
|
||||
{
|
||||
InitEscapeSequenceMap();
|
||||
}
|
||||
|
||||
Console::Console(GrafPtr port, Rect r)
|
||||
{
|
||||
Init(port, r);
|
||||
InitEscapeSequenceMap();
|
||||
}
|
||||
|
||||
Console::~Console()
|
||||
{
|
||||
if(currentInstance == this)
|
||||
currentInstance = NULL;
|
||||
}
|
||||
|
||||
void Console::Init(GrafPtr port, Rect r)
|
||||
{
|
||||
consolePort = port;
|
||||
bounds = r;
|
||||
|
||||
PortSetter setport(consolePort);
|
||||
FontSetup fontSetup;
|
||||
|
||||
InsetRect(&bounds, 2,2);
|
||||
|
||||
cellSizeY = 12;
|
||||
cellSizeX = CharWidth('M');
|
||||
|
||||
rows = (bounds.bottom - bounds.top) / cellSizeY;
|
||||
cols = (bounds.right - bounds.left) / cellSizeX;
|
||||
|
||||
chars = std::vector<AttributedChar>(rows*cols, AttributedChar(' ',currentAttr));
|
||||
|
||||
onscreen = chars;
|
||||
|
||||
cursorX = cursorY = 0;
|
||||
sequenceState=State::noSequence;
|
||||
}
|
||||
|
||||
void Console::SetAttributes(Attributes aa)
|
||||
{
|
||||
TextFace(aa.isBold()?bold+condense:0 + aa.isUnderline()?underline:0 + aa.isItalic()?italic:0);
|
||||
}
|
||||
|
||||
Rect Console::CellRect(short x, short y)
|
||||
{
|
||||
return { (short) (bounds.top + y * cellSizeY), (short) (bounds.left + x * cellSizeX),
|
||||
(short) (bounds.top + (y+1) * cellSizeY), (short) (bounds.left + (x+1) * cellSizeX) };
|
||||
}
|
||||
void Console::DrawCell(short x, short y, bool erase)
|
||||
{
|
||||
Rect r = CellRect(x,y);
|
||||
|
||||
if(cursorDrawn)
|
||||
{
|
||||
if(y == cursorY && x == cursorX)
|
||||
{
|
||||
erase = true;
|
||||
cursorDrawn = false;
|
||||
}
|
||||
}
|
||||
|
||||
Attributes a=chars[y * cols + x].attrs;
|
||||
SetAttributes(a);
|
||||
if(erase)
|
||||
EraseRect(&r);
|
||||
MoveTo(r.left, r.bottom - 2);
|
||||
DrawChar(chars[y * cols + x].c);
|
||||
}
|
||||
|
||||
void Console::DrawCells(short x1, short x2, short y, bool erase)
|
||||
{
|
||||
Rect r = { (short) (bounds.top + y * cellSizeY), (short) (bounds.left + x1 * cellSizeX),
|
||||
(short) (bounds.top + (y+1) * cellSizeY), (short) (bounds.left + x2 * cellSizeX) };
|
||||
if(cursorDrawn)
|
||||
{
|
||||
if(y == cursorY && x1 <= cursorX && x2 > cursorX)
|
||||
{
|
||||
erase = true;
|
||||
cursorDrawn = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(erase)
|
||||
EraseRect(&r);
|
||||
MoveTo(r.left, r.bottom - 2);
|
||||
|
||||
Attributes a=chars[y * cols + x1].attrs;
|
||||
SetAttributes(a);
|
||||
for(int i=x1; i<x2; ++i)
|
||||
{
|
||||
if(a!=chars[y * cols + i].attrs) {
|
||||
a=chars[y * cols + i].attrs;
|
||||
SetAttributes(a);
|
||||
}
|
||||
DrawChar(chars[y * cols + i].c);
|
||||
}
|
||||
}
|
||||
|
||||
void Console::Draw(Rect r)
|
||||
{
|
||||
if(!consolePort)
|
||||
return;
|
||||
PortSetter setport(consolePort);
|
||||
FontSetup fontSetup;
|
||||
|
||||
SectRect(&r, &bounds, &r);
|
||||
|
||||
short minRow = std::max(0, (r.top - bounds.top) / cellSizeY);
|
||||
short maxRow = std::min((int)rows, (r.bottom - bounds.top + cellSizeY - 1) / cellSizeY);
|
||||
|
||||
short minCol = std::max(0, (r.left - bounds.left) / cellSizeX);
|
||||
short maxCol = std::min((int)cols, (r.right - bounds.left + cellSizeX - 1) / cellSizeX);
|
||||
|
||||
EraseRect(&r);
|
||||
for(short row = minRow; row < maxRow; ++row)
|
||||
{
|
||||
DrawCells(minCol, maxCol, row, false);
|
||||
}
|
||||
if(cursorDrawn)
|
||||
{
|
||||
Rect cursor = CellRect(cursorX, cursorY);
|
||||
InvertRect(&cursor);
|
||||
}
|
||||
onscreen = chars;
|
||||
}
|
||||
|
||||
void Console::ScrollUp(short n)
|
||||
{
|
||||
PortSetter setport(consolePort);
|
||||
cursorY--;
|
||||
std::copy(chars.begin() + cols, chars.end(), chars.begin());
|
||||
std::fill(chars.end() - cols, chars.end(), AttributedChar(' ', currentAttr));
|
||||
std::copy(onscreen.begin() + cols, onscreen.end(), onscreen.begin());
|
||||
std::fill(onscreen.end() - cols, onscreen.end(), AttributedChar(' ', currentAttr));
|
||||
RgnHandle rgn = NewRgn();
|
||||
ScrollRect(&bounds, 0, -cellSizeY, rgn);
|
||||
DisposeRgn(rgn);
|
||||
dirtyRect.top = dirtyRect.top > 0 ? dirtyRect.top - 1 : 0;
|
||||
dirtyRect.bottom = dirtyRect.bottom > 0 ? dirtyRect.bottom - 1 : 0;
|
||||
}
|
||||
|
||||
bool Console::ProcessEscSequence(char c)
|
||||
{
|
||||
switch(sequenceState)
|
||||
{
|
||||
case State::noSequence:
|
||||
return false; // Break is not needed there.
|
||||
case State::waitingForSequenceStart:
|
||||
if(c=='[') {
|
||||
sequenceState=State::waitingForControlSequence;
|
||||
got_something = false;
|
||||
cur_arg = 0;
|
||||
} else if(c==']')
|
||||
sequenceState=State::waitingForOSCStart;
|
||||
else
|
||||
sequenceState=State::noSequence; // Unrecognized sequence
|
||||
break;
|
||||
case State::waitingForControlSequence:
|
||||
HandleControlSequence(c);
|
||||
break;
|
||||
case State::waitingForOSCStart:
|
||||
if(c=='0')
|
||||
sequenceState=State::waitingForSemicolon;
|
||||
else
|
||||
sequenceState=State::noSequence; // Normal end of sequence
|
||||
break;
|
||||
case State::waitingForSemicolon:
|
||||
if(c==';')
|
||||
{
|
||||
sequenceState=State::inWindowName;
|
||||
title.clear();
|
||||
}
|
||||
else
|
||||
sequenceState=State::noSequence; // Normal end of sequence
|
||||
break;
|
||||
case State::inWindowName:
|
||||
if(c==BEL)
|
||||
{
|
||||
title.push_back(0);
|
||||
setWindowName(&title[0]);
|
||||
sequenceState=State::noSequence; // Normal end of sequence
|
||||
}
|
||||
else
|
||||
{
|
||||
if(title.size() < (unsigned)MAX_LEN) // Ignore subsequent characters
|
||||
title.push_back(c);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sequenceState=State::noSequence;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Console::PutCharNoUpdate(char c)
|
||||
{
|
||||
if(ProcessEscSequence(c))
|
||||
return;
|
||||
|
||||
InvalidateCursor();
|
||||
switch(c)
|
||||
{
|
||||
case '\033': // Begin of an ANSI escape sequence
|
||||
sequenceState=State::waitingForSequenceStart;
|
||||
break;
|
||||
case '\b':
|
||||
cursorX--;
|
||||
break;
|
||||
case '\r':
|
||||
cursorX = 0;
|
||||
break;
|
||||
case '\n':
|
||||
cursorY++;
|
||||
cursorX = 0;
|
||||
if(cursorY >= rows)
|
||||
ScrollUp();
|
||||
break;
|
||||
default:
|
||||
chars[cursorY * cols + cursorX].c = c;
|
||||
chars[cursorY * cols + cursorX].attrs = currentAttr;
|
||||
|
||||
if(dirtyRect.right == 0)
|
||||
{
|
||||
dirtyRect.right = (dirtyRect.left = cursorX) + 1;
|
||||
dirtyRect.bottom = (dirtyRect.top = cursorY) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
dirtyRect.left = std::min(dirtyRect.left, cursorX);
|
||||
dirtyRect.top = std::min(dirtyRect.top, cursorY);
|
||||
dirtyRect.right = std::max(dirtyRect.right, short(cursorX + 1));
|
||||
dirtyRect.bottom = std::max(dirtyRect.bottom, short(cursorY + 1));
|
||||
}
|
||||
|
||||
cursorX++;
|
||||
if(cursorX >= cols)
|
||||
PutCharNoUpdate('\n');
|
||||
}
|
||||
}
|
||||
|
||||
void Console::Update()
|
||||
{
|
||||
PortSetter setport(consolePort);
|
||||
FontSetup fontSetup;
|
||||
|
||||
for(short row = dirtyRect.top; row < dirtyRect.bottom; ++row)
|
||||
{
|
||||
short start = -1;
|
||||
bool needclear = false;
|
||||
for(short col = dirtyRect.left; col < dirtyRect.right; ++col)
|
||||
{
|
||||
AttributedChar old = onscreen[row * cols + col];
|
||||
if(chars[row * cols + col] != old)
|
||||
{
|
||||
if(start == -1)
|
||||
start = col;
|
||||
if(old.c != ' ')
|
||||
needclear = true;
|
||||
onscreen[row * cols + col] = chars[row * cols + col];
|
||||
}
|
||||
else
|
||||
{
|
||||
if(start != -1)
|
||||
DrawCells(start, col, row, needclear);
|
||||
start = -1;
|
||||
needclear = false;
|
||||
}
|
||||
}
|
||||
if(start != -1)
|
||||
DrawCells(start, dirtyRect.right, row, needclear);
|
||||
}
|
||||
dirtyRect = Rect();
|
||||
|
||||
if(cursorVisible != cursorDrawn)
|
||||
{
|
||||
Rect r = CellRect(cursorX, cursorY);
|
||||
if(cursorDrawn)
|
||||
DrawCell(cursorX, cursorY, true);
|
||||
else if(cursorRequestedHidden == false)
|
||||
InvertRect(&r);
|
||||
cursorDrawn = !cursorDrawn;
|
||||
}
|
||||
|
||||
#if TARGET_API_MAC_CARBON
|
||||
QDFlushPortBuffer(consolePort,NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Console::putch(char c)
|
||||
{
|
||||
if(!rows)
|
||||
return;
|
||||
PutCharNoUpdate(c);
|
||||
Update();
|
||||
}
|
||||
|
||||
void Console::write(const char *p, int n)
|
||||
{
|
||||
if(!rows)
|
||||
return;
|
||||
|
||||
for(int i = 0; i < n; i++)
|
||||
Console::currentInstance->PutCharNoUpdate(*p++);
|
||||
Update();
|
||||
}
|
||||
|
||||
|
||||
void Console::InvalidateCursor()
|
||||
{
|
||||
if(cursorDrawn)
|
||||
{
|
||||
PortSetter setport(consolePort);
|
||||
FontSetup fontSetup;
|
||||
DrawCell(cursorX, cursorY, true);
|
||||
cursorDrawn = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Console::Idle()
|
||||
{
|
||||
long ticks = TickCount();
|
||||
if(ticks - blinkTicks > 60)
|
||||
{
|
||||
cursorVisible = !cursorVisible;
|
||||
blinkTicks = ticks;
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
void Console::Reshape(Rect newBounds)
|
||||
{
|
||||
if(!consolePort)
|
||||
return;
|
||||
|
||||
|
||||
bounds = newBounds;
|
||||
InsetRect(&bounds, 2,2);
|
||||
|
||||
short newRows = (bounds.bottom - bounds.top) / cellSizeY;
|
||||
short newCols = (bounds.right - bounds.left) / cellSizeX;
|
||||
|
||||
short upshift = 0;
|
||||
if(cursorY >= newRows)
|
||||
{
|
||||
upshift = cursorY - (newRows - 1);
|
||||
|
||||
InvalidateCursor();
|
||||
cursorY = std::max(newRows - 1, 0) ;
|
||||
}
|
||||
|
||||
std::vector<AttributedChar> newChars(newRows*newCols, AttributedChar(' ', currentAttr));
|
||||
for(short row = 0; row < newRows && row + upshift < rows; row++)
|
||||
{
|
||||
AttributedChar *src = &chars[(row+upshift) * cols];
|
||||
AttributedChar *dst = &newChars[row * newCols];
|
||||
std::copy(src, src + std::min(cols, newCols), dst);
|
||||
}
|
||||
chars.swap(newChars);
|
||||
|
||||
onscreen = newChars;
|
||||
|
||||
rows = newRows;
|
||||
cols = newCols;
|
||||
|
||||
|
||||
dirtyRect = Rect { 0, 0, rows, cols };
|
||||
EraseRect(&newBounds);
|
||||
Update();
|
||||
Draw(newBounds);
|
||||
}
|
||||
|
||||
int Console::WaitNextChar(unsigned long timeout)
|
||||
{
|
||||
return EOF;
|
||||
}
|
||||
|
||||
bool Console::Available(unsigned long timeout)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Map a letter to a function
|
||||
void Console::InitEscapeSequenceMap()
|
||||
{
|
||||
escapeSequenceMap['A'] = &Console::MoveCursorUp;
|
||||
escapeSequenceMap['B'] = &Console::MoveCursorDown;
|
||||
escapeSequenceMap['C'] = &Console::MoveCursorForward;
|
||||
escapeSequenceMap['D'] = &Console::MoveCursorBack;
|
||||
escapeSequenceMap['E'] = &Console::MoveCursorNextLine;
|
||||
escapeSequenceMap['F'] = &Console::MoveCursorPreviousLine;
|
||||
escapeSequenceMap['G'] = &Console::MoveCursorHorizonalAbsolute;
|
||||
escapeSequenceMap['H'] = &Console::SetCursorPosition;
|
||||
escapeSequenceMap['J'] = &Console::EraseInDisplay;
|
||||
escapeSequenceMap['K'] = &Console::EraseInLine;
|
||||
escapeSequenceMap['h'] = &Console::ShowCursor;
|
||||
escapeSequenceMap['l'] = &Console::HideCursor;
|
||||
escapeSequenceMap['m'] = &Console::SetDisplayAttributes;
|
||||
escapeSequenceMap['s'] = &Console::SaveCursorPosition;
|
||||
escapeSequenceMap['u'] = &Console::RestoreCursorPosition;
|
||||
}
|
||||
|
||||
// Bound to ANSI escape code H
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Control Sequence Introducer commands
|
||||
// Name: Cursor Position
|
||||
void Console::SetCursorPosition()
|
||||
{
|
||||
// possible formats the arguments can be in:
|
||||
// n -> (1,n)
|
||||
// n;m. -> (m,n)
|
||||
// n;m; -> (m,n)
|
||||
// n; -> (1,n)
|
||||
// ;m -> (m,1)
|
||||
// -> (1,1)
|
||||
|
||||
SetCursorX(getArgDefault(1, 1));
|
||||
SetCursorY(getArgDefault(0, 1));
|
||||
Update();
|
||||
}
|
||||
|
||||
// Bound to ANSI escape code J
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Control Sequence Introducer commands
|
||||
// Name: Erase in Display
|
||||
void Console::EraseInDisplay()
|
||||
{
|
||||
int n = getArgDefault(0, 1);
|
||||
|
||||
switch(n) {
|
||||
case 0: // clear from cursor to end of window
|
||||
ClearFromCursorToEndOfWindow();
|
||||
break;
|
||||
|
||||
case 1: // clear from cursor to beginning of the window
|
||||
ClearFromTopOfWindowToCursor();
|
||||
break;
|
||||
|
||||
case 2: // clear entire screen
|
||||
ClearWindow();
|
||||
break;
|
||||
|
||||
case 3: // clear entire screen and delete all lines saved in the scrollback buffer
|
||||
ClearWindow();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Sets attributes of characters
|
||||
// Note: only a few attributes are currently implemented
|
||||
// Bound to ANSI escape code m
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Select Graphic Rendition parameters
|
||||
void Console::SetDisplayAttributes()
|
||||
{
|
||||
int c = getArgDefault(0, 0);
|
||||
switch(c)
|
||||
{
|
||||
case '0': // Normal character
|
||||
currentAttr.reset();
|
||||
break;
|
||||
case '1': // Bold
|
||||
currentAttr.setBold(true);
|
||||
break;
|
||||
case '3': // Italic
|
||||
currentAttr.setItalic(true);
|
||||
break;
|
||||
case '4': // Underline
|
||||
currentAttr.setUnderline(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Clears the window of all text
|
||||
void Console::ClearWindow()
|
||||
{
|
||||
// Fill the buffer with blank spaces
|
||||
std::fill(chars.begin(), chars.end(), AttributedChar(' ', currentAttr));
|
||||
std::fill(onscreen.begin(), onscreen.end(), AttributedChar(' ', currentAttr));
|
||||
|
||||
// Erase the window
|
||||
EraseRect(&bounds);
|
||||
Update();
|
||||
Draw(bounds);
|
||||
}
|
||||
|
||||
// Clears the window of text from the current cursor position to the bottom of the window
|
||||
void Console::ClearFromCursorToEndOfWindow()
|
||||
{
|
||||
int newPosition = GetCursorY() * cols + GetCursorX() - 1;
|
||||
|
||||
// Fill the buffer with blank spaces
|
||||
std::fill(chars.begin() + newPosition, chars.end(), AttributedChar(' ', currentAttr));
|
||||
std::fill(onscreen.begin() + newPosition, onscreen.end(), AttributedChar(' ', currentAttr));
|
||||
|
||||
// Erase the window
|
||||
EraseRect(&bounds);
|
||||
Update();
|
||||
Draw(bounds);
|
||||
}
|
||||
|
||||
// Clears the window from the top to the current cursor position
|
||||
void Console::ClearFromTopOfWindowToCursor()
|
||||
{
|
||||
int newPosition = GetCursorY() * cols + GetCursorX();
|
||||
|
||||
// Fill the buffer with blank spaces
|
||||
std::fill(chars.begin(), chars.begin() + newPosition, AttributedChar(' ', currentAttr));
|
||||
std::fill(onscreen.begin(), onscreen.begin() + newPosition, AttributedChar(' ', currentAttr));
|
||||
|
||||
// Erase the window
|
||||
EraseRect(&bounds);
|
||||
Update();
|
||||
Draw(bounds);
|
||||
}
|
||||
|
||||
// handles the waitingForControlSequence state
|
||||
void Console::HandleControlSequence(char c)
|
||||
{
|
||||
if (isalpha(c))
|
||||
{
|
||||
if(got_something) args.push_back(cur_arg);
|
||||
EscapeSequenceFunction escFunc = escapeSequenceMap[(unsigned char)c];
|
||||
if (escFunc)
|
||||
(this->*(escFunc))();
|
||||
sequenceState=State::noSequence;
|
||||
args.clear();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (c >= '0' && c <= '9') {
|
||||
got_something = true;
|
||||
cur_arg = cur_arg * 10 + (c - '0');
|
||||
} else if(c == ';') {
|
||||
args.push_back(cur_arg);
|
||||
got_something = false;
|
||||
cur_arg = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bound to ANSI escape code A
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Some ANSI control sequences
|
||||
// Name: Cursor Up
|
||||
void Console::MoveCursorUp() {
|
||||
int lines = getArgDefault(2, 1);
|
||||
SetCursorY(GetCursorY() - lines);
|
||||
Update();
|
||||
}
|
||||
|
||||
// Bound to ANSI escape code B
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Some ANSI control sequences
|
||||
// Name: Cursor Down
|
||||
void Console::MoveCursorDown()
|
||||
{
|
||||
int lines = getArgDefault(0, 1);
|
||||
SetCursorY(GetCursorY() + lines);
|
||||
Update();
|
||||
}
|
||||
|
||||
// Bound to ANSI escape code C
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Some ANSI control sequences
|
||||
// Name: Cursor Forward
|
||||
void Console::MoveCursorForward()
|
||||
{
|
||||
int columns = getArgDefault(0, 1);
|
||||
SetCursorX(GetCursorX() + columns);
|
||||
Update();
|
||||
}
|
||||
|
||||
// Bound to ANSI escape code D
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Some ANSI control sequences
|
||||
// Name: Cursor Back
|
||||
void Console::MoveCursorBack()
|
||||
{
|
||||
int columns = getArgDefault(0, 1);
|
||||
SetCursorX(GetCursorX() - columns);
|
||||
Update();
|
||||
}
|
||||
|
||||
// Bound to ANSI escape code E
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Some ANSI control sequences
|
||||
// Name: Cursor Next Line
|
||||
void Console::MoveCursorNextLine()
|
||||
{
|
||||
int lines = getArgDefault(0, 1);
|
||||
SetCursorX(1);
|
||||
SetCursorY(GetCursorY() + lines);
|
||||
Update();
|
||||
}
|
||||
|
||||
// Bound to ANSI escape code F
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Some ANSI control sequences
|
||||
// Name: Cursor Previous Line
|
||||
void Console::MoveCursorPreviousLine()
|
||||
{
|
||||
int lines = getArgDefault(0, 1);
|
||||
SetCursorX(1);
|
||||
SetCursorY(GetCursorY() - lines);
|
||||
Update();
|
||||
}
|
||||
|
||||
// Bound to ANSI escape code G
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Some ANSI control sequences
|
||||
// Name: Cursor Horizontal Absolute
|
||||
void Console::MoveCursorHorizonalAbsolute()
|
||||
{
|
||||
int newPosition = getArgDefault(0, 1);
|
||||
SetCursorX(newPosition);
|
||||
Update();
|
||||
}
|
||||
|
||||
// Bound to ANSI escape code K
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Some ANSI control sequences
|
||||
// Name: Erase in Line
|
||||
void Console::EraseInLine()
|
||||
{
|
||||
int newPosition = getArgDefault(0, 0);
|
||||
switch(newPosition)
|
||||
{
|
||||
case 0:
|
||||
ClearFromCursorToEndOfLine();
|
||||
break;
|
||||
case 1:
|
||||
ClearFromBeginningOfLineToCursor();
|
||||
break;
|
||||
case 2:
|
||||
ClearEntireLine();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Erases from the current cursor position to the end of the line
|
||||
void Console::ClearFromCursorToEndOfLine()
|
||||
{
|
||||
int currentPosition = (GetCursorY() - 1) * cols + GetCursorX() - 1;
|
||||
int endOfLinePosition = GetCursorY() * cols;
|
||||
|
||||
// Fill part of the buffer with blank spaces
|
||||
std::fill(chars.begin() + currentPosition, chars.begin() + endOfLinePosition, AttributedChar(' ', currentAttr));
|
||||
std::fill(onscreen.begin() + currentPosition, onscreen.begin() + endOfLinePosition, AttributedChar(' ', currentAttr));
|
||||
|
||||
Update();
|
||||
|
||||
// Erase only on the line the cursor is on
|
||||
PortSetter setport(consolePort);
|
||||
Rect rect;
|
||||
rect = CellRect(cursorX, cursorY);
|
||||
rect.right = cols * cellSizeX;
|
||||
EraseRect(&rect);
|
||||
}
|
||||
|
||||
// Erases from the beginning of the line to the cursor's position
|
||||
void Console::ClearFromBeginningOfLineToCursor()
|
||||
{
|
||||
int currentPosition = (GetCursorY() - 1) * cols + GetCursorX();
|
||||
int beginningOfLinePosition = (GetCursorY() - 1) * cols;
|
||||
|
||||
// Fill part of the buffer with blank spaces
|
||||
std::fill(chars.begin() + beginningOfLinePosition, chars.begin() + currentPosition, AttributedChar(' ', currentAttr));
|
||||
std::fill(onscreen.begin() + beginningOfLinePosition, onscreen.begin() + currentPosition, AttributedChar(' ', currentAttr));
|
||||
|
||||
Update();
|
||||
|
||||
// Erase only on the line the cursor is on
|
||||
Rect rect;
|
||||
rect = CellRect(0, cursorY);
|
||||
rect.right = GetCursorX() * cellSizeX;
|
||||
EraseRect(&rect);
|
||||
}
|
||||
|
||||
// Erases the entire line the cursor is on
|
||||
void Console::ClearEntireLine()
|
||||
{
|
||||
int beginningOfLinePosition = (GetCursorY() - 1) * cols;
|
||||
int endOfLinePosition = GetCursorY() * cols;
|
||||
|
||||
// Fill part of the buffer with blank spaces
|
||||
std::fill(chars.begin() + beginningOfLinePosition, chars.begin() + endOfLinePosition, AttributedChar(' ', currentAttr));
|
||||
std::fill(onscreen.begin() + beginningOfLinePosition, onscreen.begin() + endOfLinePosition, AttributedChar(' ', currentAttr));
|
||||
|
||||
Update();
|
||||
|
||||
// Erase only the line the cursor is on
|
||||
Rect rect;
|
||||
rect = CellRect(0, cursorY);
|
||||
rect.right = cols * cellSizeX;
|
||||
EraseRect(&rect);
|
||||
}
|
||||
|
||||
// Bound to ANSI escape code h
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Some popular private sequences
|
||||
// Description: Sets a variable to indicate the cursor should be shown
|
||||
void Console::ShowCursor()
|
||||
{
|
||||
cursorRequestedHidden = false;
|
||||
}
|
||||
|
||||
// Bound to ANSI escape code l
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Some popular private sequences
|
||||
// Description: Sets a variable to indicate the cursor should be hidden
|
||||
void Console::HideCursor()
|
||||
{
|
||||
cursorRequestedHidden = true;
|
||||
}
|
||||
|
||||
// Bound to ANSI escape code s
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Some popular private sequences
|
||||
// Description: Saves the current cursor position
|
||||
void Console::SaveCursorPosition()
|
||||
{
|
||||
savedCursorX = cursorX;
|
||||
savedCursorY = cursorY;
|
||||
}
|
||||
|
||||
// Bound to ANSI escape code u
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Some popular private sequences
|
||||
// Description: Restores the cursor's position to the saved value
|
||||
void Console::RestoreCursorPosition()
|
||||
{
|
||||
cursorX = savedCursorX;
|
||||
cursorY = savedCursorY;
|
||||
}
|
||||
|
||||
/*
|
||||
These setter and getter functions fix a problem where ANSI escape codes expect
|
||||
an origin of (1,1) while the Console class expects an origin of (0,0).
|
||||
Only functions that work with ANSI escape codes should use these functions.
|
||||
*/
|
||||
|
||||
void Console::SetCursorX(int newX)
|
||||
{
|
||||
cursorX = newX - 1;
|
||||
cursorX = cursorX < 0 ? 0 : cursorX; // Terminal.app does this so we will too
|
||||
}
|
||||
|
||||
int Console::GetCursorX()
|
||||
{
|
||||
return cursorX + 1;
|
||||
}
|
||||
|
||||
void Console::SetCursorY(int newY)
|
||||
{
|
||||
cursorY = newY - 1;
|
||||
cursorY = cursorY < 0 ? 0 : cursorY; // Terminal.app does this so we will too
|
||||
}
|
||||
|
||||
int Console::GetCursorY()
|
||||
{
|
||||
return cursorY + 1;
|
||||
}
|
||||
205
ports/m68kmac/retro/Console.h
Normal file
205
ports/m68kmac/retro/Console.h
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
Copyright 2012-2020 Wolfgang Thaller, Davide Bucci
|
||||
|
||||
This file is part of Retro68.
|
||||
|
||||
Retro68 is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Retro68 is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Under Section 7 of GPL version 3, you are granted additional
|
||||
permissions described in the GCC Runtime Library Exception, version
|
||||
3.1, as published by the Free Software Foundation.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Retro68. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef RETRO68_CONSOLE_H_
|
||||
#define RETRO68_CONSOLE_H_
|
||||
|
||||
#include <Quickdraw.h>
|
||||
#include <vector>
|
||||
|
||||
namespace retro
|
||||
{
|
||||
class Console;
|
||||
typedef void (Console::*EscapeSequenceFunction)();
|
||||
|
||||
class Attributes
|
||||
{
|
||||
public:
|
||||
|
||||
bool isBold(void) const;
|
||||
bool isUnderline(void) const;
|
||||
bool isItalic(void) const;
|
||||
|
||||
void setBold(const bool v);
|
||||
void setUnderline(const bool v);
|
||||
void setItalic(const bool v);
|
||||
|
||||
Attributes(void);
|
||||
void reset(void);
|
||||
|
||||
private:
|
||||
|
||||
bool cBold;
|
||||
bool cUnderline;
|
||||
bool cItalic;
|
||||
};
|
||||
|
||||
class AttributedChar
|
||||
{
|
||||
public:
|
||||
char c;
|
||||
Attributes attrs;
|
||||
AttributedChar(char cc, Attributes aa) {
|
||||
c = cc;
|
||||
attrs = aa;
|
||||
}
|
||||
};
|
||||
|
||||
enum class State
|
||||
{
|
||||
noSequence,
|
||||
waitingForSequenceStart,
|
||||
waitingForControlSequence,
|
||||
waitingForM,
|
||||
waitingForOSCStart,
|
||||
waitingForSemicolon,
|
||||
inWindowName
|
||||
};
|
||||
|
||||
class Console
|
||||
{
|
||||
public:
|
||||
Console();
|
||||
Console(GrafPtr port, Rect r);
|
||||
~Console();
|
||||
|
||||
void Reshape(Rect newBounds);
|
||||
|
||||
void Draw(Rect r);
|
||||
void Draw() {
|
||||
Draw(bounds);
|
||||
}
|
||||
void putch(char c);
|
||||
|
||||
void write(const char *s, int n);
|
||||
|
||||
static Console *currentInstance;
|
||||
|
||||
short GetRows() const {
|
||||
return rows;
|
||||
}
|
||||
short GetCols() const {
|
||||
return cols;
|
||||
}
|
||||
|
||||
virtual void setWindowName(const char *newName) {
|
||||
};
|
||||
|
||||
|
||||
void Idle();
|
||||
|
||||
bool IsEOF() const {
|
||||
return eof;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
State sequenceState;
|
||||
GrafPtr consolePort = nullptr;
|
||||
Rect bounds;
|
||||
Attributes currentAttr;
|
||||
EscapeSequenceFunction escapeSequenceMap[256] = {};
|
||||
|
||||
std::vector < AttributedChar > chars, onscreen;
|
||||
|
||||
short cellSizeX;
|
||||
short cellSizeY;
|
||||
|
||||
short rows = 0, cols = 0;
|
||||
|
||||
short cursorX, cursorY;
|
||||
short savedCursorX, savedCursorY;
|
||||
|
||||
Rect dirtyRect = {};
|
||||
|
||||
long blinkTicks = 0;
|
||||
bool cursorDrawn = false;
|
||||
bool cursorVisible = true;
|
||||
bool cursorRequestedHidden = false;
|
||||
bool eof = false;
|
||||
|
||||
public:
|
||||
void PutCharNoUpdate(char c);
|
||||
void Update();
|
||||
|
||||
private:
|
||||
short CalcStartX(short x, short y);
|
||||
Rect CellRect(short x, short y);
|
||||
void DrawCell(short x, short y, bool erase = true);
|
||||
void DrawCells(short x1, short x2, short y, bool erase = true);
|
||||
void ScrollUp(short n = 1);
|
||||
bool ProcessEscSequence(char c);
|
||||
void SetAttributes(Attributes aa);
|
||||
|
||||
void InvalidateCursor();
|
||||
|
||||
public:
|
||||
virtual int WaitNextChar(unsigned long timeout = ~-0UL);
|
||||
virtual bool Available(unsigned long timeout = ~-0UL);
|
||||
private:
|
||||
void InitEscapeSequenceMap();
|
||||
void SetCursorPosition();
|
||||
void EraseInDisplay();
|
||||
void SetDisplayAttributes();
|
||||
void ClearWindow();
|
||||
void ClearFromCursorToEndOfWindow();
|
||||
void ClearFromTopOfWindowToCursor();
|
||||
void HandleControlSequence(char);
|
||||
void MoveCursorUp();
|
||||
void MoveCursorDown();
|
||||
void MoveCursorForward();
|
||||
void MoveCursorBack();
|
||||
void MoveCursorNextLine();
|
||||
void MoveCursorPreviousLine();
|
||||
void MoveCursorHorizonalAbsolute();
|
||||
void EraseInLine();
|
||||
void ClearFromCursorToEndOfLine();
|
||||
void ClearFromBeginningOfLineToCursor();
|
||||
void ClearEntireLine();
|
||||
void ShowCursor();
|
||||
void HideCursor();
|
||||
void SaveCursorPosition();
|
||||
void RestoreCursorPosition();
|
||||
void SetCursorX(int newX);
|
||||
int GetCursorX();
|
||||
void SetCursorY(int newY);
|
||||
int GetCursorY();
|
||||
|
||||
int getArgDefault(size_t i, int defval) {
|
||||
if (i < args.size()) {
|
||||
return args[i];
|
||||
}
|
||||
return defval;
|
||||
}
|
||||
|
||||
std::vector < char > title;
|
||||
std::vector < int > args;
|
||||
int cur_arg;
|
||||
bool got_something;
|
||||
protected:
|
||||
void Init(GrafPtr port, Rect r);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif /* RETRO68_CONSOLE_H_ */
|
||||
274
ports/m68kmac/retro/ConsoleWindow.cpp
Normal file
274
ports/m68kmac/retro/ConsoleWindow.cpp
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
Copyright 2012-2020 Wolfgang Thaller, Davide Bucci
|
||||
|
||||
This file is part of Retro68.
|
||||
|
||||
Retro68 is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Retro68 is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Under Section 7 of GPL version 3, you are granted additional
|
||||
permissions described in the GCC Runtime Library Exception, version
|
||||
3.1, as published by the Free Software Foundation.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Retro68. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ConsoleWindow.h"
|
||||
#include "Events.h"
|
||||
#include <unordered_map>
|
||||
#include <cstring>
|
||||
#include <TextUtils.h>
|
||||
#include <functional>
|
||||
#include <OSUtils.h>
|
||||
#include <Traps.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace retro;
|
||||
|
||||
namespace
|
||||
{
|
||||
std::unordered_map<WindowPtr, ConsoleWindow*> *windows = NULL;
|
||||
function<bool(EventRecord *)> getEvent;
|
||||
}
|
||||
static void setupEventFunction();
|
||||
|
||||
ConsoleWindow::ConsoleWindow(Rect r, ConstStr255Param title)
|
||||
{
|
||||
GrafPtr port;
|
||||
win = NewWindow(NULL, &r, title, true, 0, (WindowPtr)-1, true, 0);
|
||||
|
||||
#if !TARGET_API_MAC_CARBON
|
||||
port = win;
|
||||
Rect portRect = port->portRect;
|
||||
#else
|
||||
port = GetWindowPort(win);
|
||||
Rect portRect;
|
||||
GetPortBounds(port, &portRect);
|
||||
#endif
|
||||
|
||||
SetPort(port);
|
||||
EraseRect(&portRect);
|
||||
|
||||
if(!windows)
|
||||
windows = new std::unordered_map<WindowPtr, ConsoleWindow*>();
|
||||
(*windows)[win] = this;
|
||||
|
||||
Init(port, portRect);
|
||||
setupEventFunction();
|
||||
}
|
||||
|
||||
ConsoleWindow::~ConsoleWindow()
|
||||
{
|
||||
windows->erase(win);
|
||||
DisposeWindow(win);
|
||||
}
|
||||
|
||||
void ConsoleWindow::setWindowName(std::string newName)
|
||||
{
|
||||
Str255 pname;
|
||||
#if TARGET_API_MAC_CARBON
|
||||
// Carbon has the new, sane version.
|
||||
c2pstrcpy(pname,newName.c_str());
|
||||
#else
|
||||
// It is also available in various glue code libraries and
|
||||
// in some versions of InterfaceLib, but it's confusing.
|
||||
// Using the inplace variant, c2pstr, isn't much better than
|
||||
// doing things by hand:
|
||||
strncpy((char *)&pname[1],newName.c_str(),255);
|
||||
pname[0] = newName.length();
|
||||
#endif
|
||||
|
||||
SetWTitle(win, pname);
|
||||
}
|
||||
|
||||
int ConsoleWindow::WaitNextChar(unsigned long timeout) {
|
||||
if (pending.empty()) {
|
||||
if (!Available(timeout) || pending.empty()) return EOF;
|
||||
}
|
||||
int c = pending.front();
|
||||
pending.pop_front();
|
||||
return c;
|
||||
}
|
||||
|
||||
bool ConsoleWindow::Available(unsigned long timeout)
|
||||
{
|
||||
if(!pending.empty()) return true;
|
||||
|
||||
unsigned long start = TickCount();
|
||||
|
||||
EventRecord event;
|
||||
WindowPtr eventWin;
|
||||
ConsoleWindow *realConsole;
|
||||
#if TARGET_API_MAC_CARBON
|
||||
Rect *boundsPtr = NULL;
|
||||
#else
|
||||
Rect *boundsPtr = &qd.screenBits.bounds;
|
||||
#endif
|
||||
|
||||
do
|
||||
{
|
||||
#if TARGET_API_MAC_CARBON
|
||||
#define SystemTask()
|
||||
#endif
|
||||
SystemTask();
|
||||
Idle();
|
||||
while(!getEvent(&event))
|
||||
{
|
||||
SystemTask();
|
||||
Idle();
|
||||
unsigned long now = TickCount();
|
||||
if((now-start) > timeout) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
switch(event.what)
|
||||
{
|
||||
case updateEvt:
|
||||
eventWin = (WindowPtr)event.message;
|
||||
realConsole = (*windows)[(WindowPtr)event.message];
|
||||
if(realConsole)
|
||||
{
|
||||
Rect updateRect;
|
||||
BeginUpdate(eventWin);
|
||||
#if TARGET_API_MAC_CARBON
|
||||
RgnHandle rgn = NewRgn();
|
||||
GetPortVisibleRegion(GetWindowPort(eventWin), rgn);
|
||||
GetRegionBounds(rgn, &updateRect);
|
||||
DisposeRgn(rgn);
|
||||
#else
|
||||
updateRect = (*qd.thePort->visRgn)->rgnBBox; // Life was simple back then.
|
||||
#endif
|
||||
realConsole->Draw(updateRect);
|
||||
EndUpdate(eventWin);
|
||||
}
|
||||
break;
|
||||
case mouseDown:
|
||||
switch(FindWindow(event.where, &eventWin))
|
||||
{
|
||||
case inDrag:
|
||||
DragWindow(eventWin, event.where, boundsPtr);
|
||||
break;
|
||||
case inGrow:
|
||||
{
|
||||
long growResult = GrowWindow(eventWin, event.where, boundsPtr);
|
||||
SizeWindow(eventWin, growResult & 0xFFFF, growResult >> 16, false);
|
||||
Reshape(Rect {0, 0, (short) (growResult >> 16), (short) (growResult & 0xFFFF) });
|
||||
}
|
||||
break;
|
||||
case inGoAway:
|
||||
{
|
||||
if (TrackGoAway(eventWin,event.where))
|
||||
exit(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while(event.what != keyDown && event.what != autoKey);
|
||||
|
||||
int c = event.message & charCodeMask;
|
||||
if (event.modifiers & ControlKey) {
|
||||
// treat option like ctrl
|
||||
c = c & 31;
|
||||
}
|
||||
|
||||
if (event.modifiers & optionKey) {
|
||||
// treat cmd like alt
|
||||
pending.push_back(c);
|
||||
c = 27;
|
||||
}
|
||||
|
||||
// umac doesn't seem to ever generate these, so this code is untested....
|
||||
int keycode = (event.message & keyCodeMask) >> 8;
|
||||
static const struct { int keycode; const char *str; } keycodes[] = {
|
||||
#define MKC_Left /* 0x46 */ 0x7B
|
||||
#define MKC_Right /* 0x42 */ 0x7C
|
||||
#define MKC_Down /* 0x48 */ 0x7D
|
||||
#define MKC_Up /* 0x4D */ 0x7E
|
||||
{MKC_Left, "\x1b[D"},
|
||||
{MKC_Right, "\x1b[C"},
|
||||
{MKC_Up, "\x1b[A"},
|
||||
{MKC_Down, "\x1b[B"},
|
||||
};
|
||||
|
||||
for(auto &i : keycodes) {
|
||||
if(keycode == i.keycode) {
|
||||
for(auto p = i.str; *p; p++) {
|
||||
pending.push_back(*p);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// if keycode not found in map...
|
||||
pending.push_back(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Wrapper for the WaitNextEvent() function
|
||||
static bool waitNextEventWrapper(EventRecord *event)
|
||||
{
|
||||
const int sleepValue = 5;
|
||||
const RgnHandle mouseRegion = nil;
|
||||
return WaitNextEvent(everyEvent, event, sleepValue, mouseRegion);
|
||||
}
|
||||
|
||||
#if !(TARGET_API_MAC_CARBON)
|
||||
|
||||
// Wrapper for the GetNextEvent() function
|
||||
static bool getNextEventWrapper(EventRecord *event)
|
||||
{
|
||||
return GetNextEvent(everyEvent, event);
|
||||
}
|
||||
|
||||
// Determines if a Toolbox routine is available
|
||||
static bool routineAvailable(int trapWord) {
|
||||
TrapType trType;
|
||||
int OSTrap = 0;
|
||||
int ToolTrap = 1;
|
||||
|
||||
// Determine whether it is an Operating System or Toolbox routine
|
||||
if ((trapWord & 0x0800) == 0) {
|
||||
trType = OSTrap;
|
||||
}
|
||||
else {
|
||||
trType = ToolTrap;
|
||||
}
|
||||
|
||||
// Filter cases where older systems mask with 0x1FF rather than 0x3FF
|
||||
if ((trType == ToolTrap) &&
|
||||
((trapWord & 0x03FF) >= 0x200) &&
|
||||
(GetToolboxTrapAddress(0xA86E) == GetToolboxTrapAddress(0xAA6E))) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return (NGetTrapAddress(trapWord, trType) != GetToolboxTrapAddress(_Unimplemented));
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* TARGET_API_MAC_CARBON */
|
||||
|
||||
|
||||
// Decides which event retrieving function to use
|
||||
static void setupEventFunction()
|
||||
{
|
||||
#if TARGET_API_MAC_CARBON
|
||||
getEvent = waitNextEventWrapper;
|
||||
#else
|
||||
if (routineAvailable(_WaitNextEvent) == true) {
|
||||
getEvent = waitNextEventWrapper;
|
||||
}
|
||||
else {
|
||||
getEvent = getNextEventWrapper;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
46
ports/m68kmac/retro/ConsoleWindow.h
Normal file
46
ports/m68kmac/retro/ConsoleWindow.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
Copyright 2012-2020 Wolfgang Thaller, Davide Bucci
|
||||
|
||||
This file is part of Retro68.
|
||||
|
||||
Retro68 is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Retro68 is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Under Section 7 of GPL version 3, you are granted additional
|
||||
permissions described in the GCC Runtime Library Exception, version
|
||||
3.1, as published by the Free Software Foundation.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Retro68. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Windows.h>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
||||
#include "Console.h"
|
||||
|
||||
namespace retro
|
||||
{
|
||||
class ConsoleWindow: public Console
|
||||
{
|
||||
public:
|
||||
ConsoleWindow(Rect r, ConstStr255Param title);
|
||||
~ConsoleWindow();
|
||||
void setWindowName(std::string newName);
|
||||
|
||||
private:
|
||||
WindowPtr win;
|
||||
|
||||
int WaitNextChar(unsigned long timeout = 0UL) override;
|
||||
bool Available(unsigned long timeout = 0UL) override;
|
||||
std::deque < char > pending;
|
||||
};
|
||||
}
|
||||
115
ports/m68kmac/retro/InitConsole.cpp
Normal file
115
ports/m68kmac/retro/InitConsole.cpp
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
Copyright 2014 Wolfgang Thaller.
|
||||
|
||||
This file is part of Retro68.
|
||||
|
||||
Retro68 is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Retro68 is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Under Section 7 of GPL version 3, you are granted additional
|
||||
permissions described in the GCC Runtime Library Exception, version
|
||||
3.1, as published by the Free Software Foundation.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Retro68. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <Quickdraw.h>
|
||||
#include <MacMemory.h>
|
||||
#include <Sound.h>
|
||||
#include <Events.h>
|
||||
#include <Fonts.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "MacUtils.h"
|
||||
#include "Console.h"
|
||||
#include "ConsoleWindow.h"
|
||||
|
||||
namespace retro
|
||||
{
|
||||
void InitConsole();
|
||||
}
|
||||
|
||||
using namespace retro;
|
||||
|
||||
void retro::InitConsole()
|
||||
{
|
||||
if(Console::currentInstance)
|
||||
return;
|
||||
Console::currentInstance = (Console*) -1;
|
||||
|
||||
#if !TARGET_API_MAC_CARBON
|
||||
InitGraf(&qd.thePort);
|
||||
InitFonts();
|
||||
InitWindows();
|
||||
InitMenus();
|
||||
|
||||
Rect r = qd.screenBits.bounds;
|
||||
#else
|
||||
Rect r = (*GetMainDevice())->gdRect;
|
||||
#endif
|
||||
{
|
||||
// give MultiFinder a chance to bring the App to front
|
||||
// see Technote TB 35 - MultiFinder Miscellanea
|
||||
// "If your application [...] has the canBackground bit set in the
|
||||
// size resource, then it should call _EventAvail several times
|
||||
// (or _WaitNextEvent or _GetNextEvent) before putting up the splash
|
||||
// screen, or the splash screen will come up behind the frontmost
|
||||
// layer. If the canBackground bit is set, MultiFinder will not move
|
||||
// your layer to the front until you call _GetNextEvent,
|
||||
// _WaitNextEvent, or _EventAvail."
|
||||
|
||||
EventRecord event;
|
||||
for(int i = 0; i < 5; i++)
|
||||
EventAvail(everyEvent, &event);
|
||||
}
|
||||
|
||||
r.top += 40;
|
||||
InsetRect(&r, 5,5);
|
||||
|
||||
Console::currentInstance = new ConsoleWindow(r, "\pRetro68 Console");
|
||||
InitCursor();
|
||||
}
|
||||
|
||||
extern "C" ssize_t _consolewrite(int fd, const void *buf, size_t count)
|
||||
{
|
||||
if(!Console::currentInstance)
|
||||
InitConsole();
|
||||
if(Console::currentInstance == (Console*)-1)
|
||||
return 0;
|
||||
|
||||
Console::currentInstance->write((const char*)buf, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
extern "C" ssize_t _consoleread(int fd, void *buf, size_t count)
|
||||
{
|
||||
#if 0
|
||||
if(!Console::currentInstance)
|
||||
InitConsole();
|
||||
if(Console::currentInstance == (Console*)-1)
|
||||
return 0;
|
||||
|
||||
static std::string consoleBuf;
|
||||
if(consoleBuf.size() == 0)
|
||||
consoleBuf = Console::currentInstance->ReadLine();
|
||||
|
||||
if(count > consoleBuf.size())
|
||||
count = consoleBuf.size();
|
||||
memcpy(buf, consoleBuf.data(), count);
|
||||
consoleBuf = consoleBuf.substr(count);
|
||||
return count;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
44
ports/m68kmac/retro/MacUtils.h
Normal file
44
ports/m68kmac/retro/MacUtils.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
Copyright 2012 Wolfgang Thaller.
|
||||
|
||||
This file is part of Retro68.
|
||||
|
||||
Retro68 is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Retro68 is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
Under Section 7 of GPL version 3, you are granted additional
|
||||
permissions described in the GCC Runtime Library Exception, version
|
||||
3.1, as published by the Free Software Foundation.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Retro68. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Quickdraw.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace retro
|
||||
{
|
||||
class PortSetter
|
||||
{
|
||||
GrafPtr save;
|
||||
public:
|
||||
PortSetter(GrafPtr port)
|
||||
{
|
||||
::GetPort(&save);
|
||||
::SetPort(port);
|
||||
}
|
||||
|
||||
~PortSetter()
|
||||
{
|
||||
::SetPort(save);
|
||||
}
|
||||
};
|
||||
}
|
||||
80
ports/m68kmac/uart_core.cpp
Normal file
80
ports/m68kmac/uart_core.cpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
extern "C" {
|
||||
#include "uart_core.h"
|
||||
}
|
||||
|
||||
|
||||
#include "retro/Console.h"
|
||||
#include "retro/ConsoleWindow.h"
|
||||
/*
|
||||
* Core UART functions to implement for a port
|
||||
*/
|
||||
|
||||
using namespace retro;
|
||||
namespace retro
|
||||
{
|
||||
void InitConsole();
|
||||
}
|
||||
|
||||
#define USE_CONSOLE (1)
|
||||
|
||||
// Receive single character
|
||||
extern "C"
|
||||
int mp_hal_stdin_rx_chr(void);
|
||||
int mp_hal_stdin_rx_chr(void) {
|
||||
#if USE_CONSOLE
|
||||
if(!Console::currentInstance)
|
||||
InitConsole();
|
||||
if(Console::currentInstance == (Console*)-1)
|
||||
return EOF;
|
||||
int c = Console::currentInstance->WaitNextChar();
|
||||
#else
|
||||
int c = *(char*)0xc0006a;
|
||||
#endif
|
||||
return c;
|
||||
}
|
||||
|
||||
bool mp_hal_stdin_available(void) {
|
||||
if(!Console::currentInstance)
|
||||
InitConsole();
|
||||
if(Console::currentInstance == (Console*)-1)
|
||||
return false;
|
||||
return Console::currentInstance->Available(1);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
mp_uint_t debug_uart_tx_strn(const char *str, mp_uint_t len);
|
||||
mp_uint_t debug_uart_tx_strn(const char *str, mp_uint_t len) {
|
||||
mp_uint_t result = len;
|
||||
// debug hack, needs patched umac
|
||||
while(len--) {
|
||||
*(char*)0xc0006a = *str++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void debug_print_fn(void *data, const char *str, size_t len) {
|
||||
debug_uart_tx_strn(str, len);
|
||||
}
|
||||
|
||||
mp_print_t debug_print = { NULL, debug_print_fn };
|
||||
|
||||
// Send string of given length
|
||||
extern "C"
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len);
|
||||
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
debug_uart_tx_strn(str, len);
|
||||
|
||||
#if USE_CONSOLE
|
||||
if(!Console::currentInstance)
|
||||
InitConsole();
|
||||
if(Console::currentInstance == (Console*)-1)
|
||||
return 0;
|
||||
|
||||
Console::currentInstance->write(str, (size_t)len);
|
||||
#endif
|
||||
|
||||
return len;
|
||||
}
|
||||
16
ports/m68kmac/uart_core.h
Normal file
16
ports/m68kmac/uart_core.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "py/mpprint.h"
|
||||
|
||||
bool mp_hal_stdin_available(void);
|
||||
int mp_hal_stdin_rx_chr(void);
|
||||
extern mp_print_t debug_print;
|
||||
#define DEBUG_PRINT(...) mp_printf(&debug_print, __VA_ARGS__)
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
677
ports/m68kmac/vfs_mac.c
Normal file
677
ports/m68kmac/vfs_mac.c
Normal file
|
|
@ -0,0 +1,677 @@
|
|||
#include "py/mpconfig.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/objstr.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "py/mphal.h"
|
||||
#include "py/stream.h"
|
||||
#include "extmod/vfs.h"
|
||||
|
||||
#include "macutil.h"
|
||||
#include "vfs_mac.h"
|
||||
#include "debug_print.h"
|
||||
#include "uart_core.h"
|
||||
|
||||
#define MAC_O_RDONLY (1)
|
||||
#define MAC_O_WRONLY (2)
|
||||
#define MAC_O_RDWR (3)
|
||||
#define MAC_O_TRUNC (4)
|
||||
#define MAC_O_APPEND (8)
|
||||
#define MAC_O_CREAT (16)
|
||||
|
||||
// I have no idea whether mac is guaranteed not to generate these as file handles...
|
||||
#define STDIN_FILENO (0)
|
||||
#define STDOUT_FILENO (1)
|
||||
#define STDERR_FILENO (2)
|
||||
|
||||
typedef struct _mp_obj_vfs_mac_t {
|
||||
mp_obj_base_t base;
|
||||
INTEGER volRefNum;
|
||||
size_t root_len;
|
||||
bool readonly;
|
||||
} mp_obj_vfs_mac_t;
|
||||
|
||||
typedef struct _mp_obj_vfs_mac_file_t {
|
||||
mp_obj_base_t base;
|
||||
INTEGER fd;
|
||||
INTEGER volRefNum;
|
||||
int mode;
|
||||
} mp_obj_vfs_mac_file_t;
|
||||
|
||||
static Byte *vfs_mac_get_path_cstr(Byte *pName, size_t pName_size, const char *path) {
|
||||
while (*path == '/') {
|
||||
path++;
|
||||
}
|
||||
pstr_from_cstr(pName, pName_size, "/");
|
||||
pstr_cat_cstr(pName, pName_size, path);
|
||||
for (int i = 1; i <= pName[0]; i++) {
|
||||
if (pName[i] == '/') {
|
||||
pName[i] = ':';
|
||||
}
|
||||
}
|
||||
return pName;
|
||||
}
|
||||
|
||||
static Byte *vfs_mac_get_path_str(Byte *pName, size_t pName_size, mp_obj_t obj) {
|
||||
return vfs_mac_get_path_cstr(pName, pName_size, mp_obj_str_get_str(obj));
|
||||
}
|
||||
|
||||
static VCB *getVolumeByName(mp_obj_t name) {
|
||||
size_t str_len;
|
||||
const char *str_data = mp_obj_str_get_data(name, &str_len);
|
||||
VCB *vol = (VCB *)LMGetVCBQHdr().qHead;
|
||||
while (vol) {
|
||||
if (PSTR_LEN(vol->vcbVN) == str_len &&
|
||||
memcmp(PSTR_DATA(vol->vcbVRefNum), str_data, str_len) == 0) {
|
||||
return vol;
|
||||
}
|
||||
vol = (VCB *)vol->qLink;
|
||||
}
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("volume not found (by name)"));
|
||||
}
|
||||
|
||||
static VCB *getVolumeByVolumeReference(INTEGER vn) {
|
||||
VCB *vol = (VCB *)LMGetVCBQHdr().qHead;
|
||||
while (vol) {
|
||||
if (vol->vcbVRefNum == vn) {
|
||||
return vol;
|
||||
}
|
||||
vol = (VCB *)vol->qLink;
|
||||
}
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("volume not found (by ref)"));
|
||||
}
|
||||
|
||||
|
||||
static void check_fd_is_open(const mp_obj_vfs_mac_file_t *o) {
|
||||
if (o->fd < 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("I/O operation on closed file"));
|
||||
}
|
||||
}
|
||||
|
||||
static void vfs_mac_file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
(void)kind;
|
||||
mp_obj_vfs_mac_file_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (self->fd <= STDERR_FILENO) {
|
||||
mp_printf(print, "<io.%s %d>",
|
||||
mp_obj_get_type_str(self_in), self->fd);
|
||||
} else {
|
||||
VCB *vol = getVolumeByVolumeReference(self->volRefNum);
|
||||
mp_printf(print, "<io.%s %d on %.*s>",
|
||||
mp_obj_get_type_str(self_in), self->fd,
|
||||
(int)PSTR_LEN(vol->vcbVN), PSTR_DATA(vol->vcbVN));
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t mp_vfs_mac_file_open(const mp_obj_vfs_mac_t *fs, const mp_obj_type_t *type, mp_obj_t file_in, mp_obj_t mode_in) {
|
||||
const char *mode_s = mp_obj_str_get_str(mode_in);
|
||||
|
||||
int mode_rw = 0, mode_x = 0;
|
||||
while (*mode_s) {
|
||||
switch (*mode_s++) {
|
||||
case 'r':
|
||||
mode_rw = MAC_O_RDONLY;
|
||||
break;
|
||||
case 'w':
|
||||
mode_rw = MAC_O_WRONLY;
|
||||
mode_x = MAC_O_CREAT | MAC_O_TRUNC;
|
||||
break;
|
||||
case 'a':
|
||||
mode_rw = MAC_O_WRONLY;
|
||||
mode_x = MAC_O_CREAT | MAC_O_APPEND;
|
||||
break;
|
||||
case '+':
|
||||
mode_rw = MAC_O_RDWR;
|
||||
break;
|
||||
case 'b':
|
||||
type = &mp_type_vfs_mac_fileio;
|
||||
break;
|
||||
case 't':
|
||||
type = &mp_type_vfs_mac_textio;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_vfs_mac_file_t *o = mp_obj_malloc_with_finaliser(mp_obj_vfs_mac_file_t, type);
|
||||
o->fd = -1; // In case open() fails below, initialise this as a "closed" file object.
|
||||
|
||||
mp_obj_t fid = file_in;
|
||||
|
||||
if (mp_obj_is_small_int(fid)) {
|
||||
o->fd = MP_OBJ_SMALL_INT_VALUE(fid);
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
|
||||
Str255 pName;
|
||||
vfs_mac_get_path_str(pName, sizeof(pName), fid);
|
||||
INTEGER fd;
|
||||
OSErr err = noErr;
|
||||
|
||||
while (true) {
|
||||
OSErr err = FSOpen(pName, fs->volRefNum, &fd);
|
||||
if (err == fnfErr && (mode_x & MAC_O_CREAT)) {
|
||||
err = Create(pName, fs->volRefNum, 'mupy', type == &mp_type_vfs_mac_textio ? 'text' : 'bin ');
|
||||
check_mac_err(err);
|
||||
mode_x &= ~MAC_O_CREAT;
|
||||
continue;
|
||||
}
|
||||
check_mac_err(err);
|
||||
break;
|
||||
}
|
||||
|
||||
if (mode_x & MAC_O_TRUNC) {
|
||||
err = SetEOF(fd, 0);
|
||||
} else if (mode_x & MAC_O_APPEND) {
|
||||
err = SetFPos(fd, fsFromLEOF, 0);
|
||||
}
|
||||
if (err != noErr) {
|
||||
FSClose(fd);
|
||||
raise_mac_err(err);
|
||||
}
|
||||
o->fd = fd;
|
||||
o->mode = mode_rw;
|
||||
o->volRefNum = fs->volRefNum;
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
|
||||
static mp_obj_t vfs_mac_file_fileno(mp_obj_t self_in) {
|
||||
mp_obj_vfs_mac_file_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return MP_OBJ_NEW_SMALL_INT(self->fd);
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(vfs_mac_file_fileno_obj, vfs_mac_file_fileno);
|
||||
|
||||
static mp_uint_t vfs_mac_file_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) {
|
||||
mp_obj_vfs_mac_file_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
|
||||
check_fd_is_open(o);
|
||||
long count = (long)size;
|
||||
|
||||
if (o->fd == STDIN_FILENO) {
|
||||
mp_uint_t result = size;
|
||||
char *data = buf;
|
||||
while (size--) {
|
||||
*data++ = mp_hal_stdin_rx_chr();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (o->fd <= STDERR_FILENO) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// As far as I could tell, reading past EOF is an error. It leaves the file advanced
|
||||
// to the EOF position so "try again if EOF is signaled, only using the right length"
|
||||
// doesn't work. Instead, check the current position & EOF position, limiting the size
|
||||
// argument every time.
|
||||
LONGINT pos_cur, pos_eof;
|
||||
OSErr err = GetFPos(o->fd, &pos_cur);
|
||||
if (err != noErr) {
|
||||
goto out_err;
|
||||
}
|
||||
err = GetEOF(o->fd, &pos_eof);
|
||||
if (err != noErr) {
|
||||
goto out_err;
|
||||
}
|
||||
LONGINT max_count = pos_eof - pos_cur;
|
||||
if (max_count < count) {
|
||||
count = max_count;
|
||||
}
|
||||
|
||||
err = FSRead(o->fd, &count, buf);
|
||||
|
||||
if (err == noErr) {
|
||||
return count;
|
||||
}
|
||||
out_err:
|
||||
*errcode = convert_mac_err(err);
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
|
||||
static mp_uint_t vfs_mac_file_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) {
|
||||
mp_obj_vfs_mac_file_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
|
||||
if (o->fd <= STDERR_FILENO) {
|
||||
mp_hal_stdout_tx_strn(buf, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
check_fd_is_open(o);
|
||||
long count = (long)size;
|
||||
OSErr err = FSWrite(o->fd, &count, buf);
|
||||
|
||||
if (err == noErr) {
|
||||
return count;
|
||||
}
|
||||
*errcode = convert_mac_err(err);
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
static mp_uint_t vfs_mac_file_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
|
||||
mp_obj_vfs_mac_file_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
|
||||
if (request != MP_STREAM_CLOSE) {
|
||||
check_fd_is_open(o);
|
||||
}
|
||||
|
||||
switch (request) {
|
||||
case MP_STREAM_POLL: {
|
||||
if (o->fd != STDIN_FILENO) {
|
||||
*errcode = EINVAL;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
mp_uint_t ret = 0;
|
||||
if (mp_hal_stdin_available()) {
|
||||
ret |= arg & MP_STREAM_POLL_RD;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
case MP_STREAM_FLUSH: {
|
||||
if (o->fd <= STDERR_FILENO) {
|
||||
return 0;
|
||||
}
|
||||
INTEGER err = FlushVol(NULL, o->volRefNum);
|
||||
if (err == noErr) {
|
||||
*errcode = convert_mac_err(err);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case MP_STREAM_SEEK: {
|
||||
struct mp_stream_seek_t *s = (struct mp_stream_seek_t *)arg;
|
||||
INTEGER posMode =
|
||||
s->whence == MP_SEEK_CUR ? fsFromMark :
|
||||
s->whence == MP_SEEK_SET ? fsFromStart : fsFromMark;
|
||||
OSErr err = SetFPos(o->fd, posMode, (long)s->offset);
|
||||
// TODO: seek that enlarges a file [need to call Allocate()?]
|
||||
if (err == noErr) {
|
||||
*errcode = convert_mac_err(err);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case MP_STREAM_CLOSE:
|
||||
if (o->fd >= 0) {
|
||||
int fd = o->fd;
|
||||
o->fd = -1;
|
||||
MP_THREAD_GIL_EXIT();
|
||||
OSErr err = FSClose(fd);
|
||||
OSErr err1 = FlushVol(NULL, o->volRefNum);
|
||||
if (err != noErr) {
|
||||
*errcode = convert_mac_err(err);
|
||||
break;
|
||||
}
|
||||
if (err1 != noErr) {
|
||||
*errcode = convert_mac_err(err1);
|
||||
break;
|
||||
}
|
||||
MP_THREAD_GIL_ENTER();
|
||||
}
|
||||
return 0;
|
||||
case MP_STREAM_GET_FILENO:
|
||||
return o->fd;
|
||||
|
||||
default:
|
||||
*errcode = EINVAL;
|
||||
}
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
|
||||
static const mp_rom_map_elem_t vfs_mac_rawfile_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_fileno), MP_ROM_PTR(&vfs_mac_file_fileno_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mp_stream_tell_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&mp_stream___exit___obj) },
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(vfs_mac_rawfile_locals_dict, vfs_mac_rawfile_locals_dict_table);
|
||||
|
||||
static const mp_stream_p_t vfs_mac_fileio_stream_p = {
|
||||
.read = vfs_mac_file_read,
|
||||
.write = vfs_mac_file_write,
|
||||
.ioctl = vfs_mac_file_ioctl,
|
||||
};
|
||||
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
mp_type_vfs_mac_fileio,
|
||||
MP_QSTR_FileIO,
|
||||
MP_TYPE_FLAG_ITER_IS_STREAM,
|
||||
print, vfs_mac_file_print,
|
||||
protocol, &vfs_mac_fileio_stream_p,
|
||||
locals_dict, &vfs_mac_rawfile_locals_dict
|
||||
);
|
||||
|
||||
static const mp_stream_p_t vfs_mac_textio_stream_p = {
|
||||
.read = vfs_mac_file_read,
|
||||
.write = vfs_mac_file_write,
|
||||
.ioctl = vfs_mac_file_ioctl,
|
||||
.is_text = true,
|
||||
};
|
||||
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
mp_type_vfs_mac_textio,
|
||||
MP_QSTR_TextIOWrapper,
|
||||
MP_TYPE_FLAG_ITER_IS_STREAM,
|
||||
print, &vfs_mac_file_print,
|
||||
protocol, &vfs_mac_textio_stream_p,
|
||||
locals_dict, &vfs_mac_rawfile_locals_dict
|
||||
);
|
||||
|
||||
static mp_obj_t vfs_mac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_arg_check_num(n_args, n_kw, 0, 1, false);
|
||||
INTEGER volRefNum;
|
||||
|
||||
if (n_args == 0) {
|
||||
GetVol(NULL, &volRefNum);
|
||||
} else if (mp_obj_is_small_int(args[0])) {
|
||||
volRefNum = MP_OBJ_SMALL_INT_VALUE(args[0]);
|
||||
} else {
|
||||
volRefNum = getVolumeByName(args[0])->vcbVRefNum;
|
||||
}
|
||||
|
||||
// create new object
|
||||
mp_obj_vfs_mac_t *vfs = mp_obj_malloc(mp_obj_vfs_mac_t, type);
|
||||
vfs->volRefNum = volRefNum;
|
||||
|
||||
return MP_OBJ_FROM_PTR(vfs);
|
||||
}
|
||||
|
||||
static mp_obj_t volumes(mp_obj_t self_in) {
|
||||
mp_obj_t result = mp_obj_list_make_new(&mp_type_list, 0, 0, NULL);
|
||||
VCB *vol = (VCB *)LMGetVCBQHdr().qHead;
|
||||
mp_obj_t args[3];
|
||||
mp_load_method(result, MP_QSTR_append, args);
|
||||
while (vol) {
|
||||
args[2] = new_str_from_pstr(vol->vcbVN);
|
||||
mp_call_method_n_kw(1, 0, args);
|
||||
vol = (VCB *)vol->qLink;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(volumes_obj, volumes);
|
||||
|
||||
static mp_obj_t vfs_mac_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) {
|
||||
mp_obj_vfs_mac_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (mp_obj_is_true(readonly)) {
|
||||
self->readonly = true;
|
||||
}
|
||||
if (mp_obj_is_true(mkfs)) {
|
||||
mp_raise_OSError(MP_EPERM);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_3(vfs_mac_mount_obj, vfs_mac_mount);
|
||||
|
||||
static mp_obj_t vfs_mac_umount(mp_obj_t self_in) {
|
||||
(void)self_in;
|
||||
return mp_const_none;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(vfs_mac_umount_obj, vfs_mac_umount);
|
||||
|
||||
static mp_obj_t vfs_mac_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in) {
|
||||
mp_obj_vfs_mac_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
const char *mode = mp_obj_str_get_str(mode_in);
|
||||
if (self->readonly
|
||||
&& (strchr(mode, 'w') != NULL || strchr(mode, 'a') != NULL || strchr(mode, '+') != NULL)) {
|
||||
mp_raise_OSError(MP_EROFS);
|
||||
}
|
||||
|
||||
return mp_vfs_mac_file_open(self, &mp_type_vfs_mac_textio, path_in, mode_in);
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_3(vfs_mac_open_obj, vfs_mac_open);
|
||||
|
||||
static mp_import_stat_t mp_vfs_mac_import_stat(void *self_in, const char *path) {
|
||||
mp_obj_vfs_mac_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
Str255 pName;
|
||||
vfs_mac_get_path_cstr(pName, sizeof(pName), path);
|
||||
CInfoPBRec pb = {
|
||||
.hFileInfo.ioVRefNum = self->volRefNum,
|
||||
.hFileInfo.ioNamePtr = (StringPtr)pName,
|
||||
};
|
||||
OSErr err;
|
||||
err = PBGetCatInfoSync(&pb);
|
||||
MP_THREAD_GIL_ENTER();
|
||||
|
||||
|
||||
if (err != noErr) {
|
||||
return MP_IMPORT_STAT_NO_EXIST;
|
||||
}
|
||||
bool is_dir = pb.hFileInfo.ioFlAttrib & (1 << 4);
|
||||
if (is_dir) {
|
||||
return MP_IMPORT_STAT_DIR;
|
||||
} else {
|
||||
return MP_IMPORT_STAT_FILE;
|
||||
}
|
||||
}
|
||||
|
||||
static mp_obj_t vfs_mac_chdir(mp_obj_t self_in, mp_obj_t path_in) {
|
||||
mp_raise_NotImplementedError(NULL); // TODO
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_2(vfs_mac_chdir_obj, vfs_mac_chdir);
|
||||
|
||||
static mp_obj_t vfs_mac_getcwd(mp_obj_t self_in) {
|
||||
mp_raise_NotImplementedError(NULL); // TODO
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(vfs_mac_getcwd_obj, vfs_mac_getcwd);
|
||||
|
||||
typedef struct _vfs_mac_ilistdir_it_t {
|
||||
mp_obj_base_t base;
|
||||
mp_fun_1_t iternext;
|
||||
bool is_str;
|
||||
INTEGER volRefNum;
|
||||
INTEGER index;
|
||||
INTEGER dirId;
|
||||
} vfs_mac_ilistdir_it_t;
|
||||
|
||||
INTEGER get_dirid(INTEGER volRefNum, Byte *dirname) {
|
||||
MP_THREAD_GIL_EXIT();
|
||||
CInfoPBRec pb = {
|
||||
.hFileInfo.ioVRefNum = volRefNum,
|
||||
.hFileInfo.ioNamePtr = (StringPtr)dirname,
|
||||
};
|
||||
OSErr err;
|
||||
err = PBGetCatInfoSync(&pb);
|
||||
MP_THREAD_GIL_ENTER();
|
||||
|
||||
check_mac_err(err);
|
||||
bool is_dir = pb.hFileInfo.ioFlAttrib & (1 << 4);
|
||||
|
||||
if (is_dir) {
|
||||
return pb.hFileInfo.ioDirID;
|
||||
}
|
||||
mp_raise_OSError(MP_ENOTDIR);
|
||||
}
|
||||
|
||||
static mp_obj_t vfs_mac_ilistdir_it_iternext(mp_obj_t self_in) {
|
||||
vfs_mac_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
while (self->index != 0) {
|
||||
int index = self->index++;
|
||||
MP_THREAD_GIL_EXIT();
|
||||
Str255 pName;
|
||||
CInfoPBRec pb = {
|
||||
.hFileInfo.ioVRefNum = self->volRefNum,
|
||||
.hFileInfo.ioNamePtr = (StringPtr)pName,
|
||||
.hFileInfo.ioFVersNum = 0,
|
||||
.hFileInfo.ioFDirIndex = index,
|
||||
.hFileInfo.ioDirID = self->dirId,
|
||||
};
|
||||
OSErr err;
|
||||
err = PBGetCatInfoSync(&pb);
|
||||
MP_THREAD_GIL_ENTER();
|
||||
|
||||
DPRINTF("PBGetCatInfoSync(%d) -> %d", index, err);
|
||||
|
||||
if (err == fnfErr) {
|
||||
self->index = 0;
|
||||
break;
|
||||
}
|
||||
if (err != noErr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool is_dir = pb.hFileInfo.ioFlAttrib & (1 << 4);
|
||||
|
||||
// make 3-tuple with info about this entry
|
||||
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
|
||||
|
||||
if (self->is_str) {
|
||||
t->items[0] = new_str_from_pstr(pName);
|
||||
} else {
|
||||
t->items[0] = new_bytes_from_pstr(pName);
|
||||
}
|
||||
|
||||
if (is_dir) {
|
||||
t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR);
|
||||
} else {
|
||||
t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFREG);
|
||||
}
|
||||
|
||||
t->items[2] = MP_OBJ_NEW_SMALL_INT(index);
|
||||
|
||||
return MP_OBJ_FROM_PTR(t);
|
||||
}
|
||||
return MP_OBJ_STOP_ITERATION;
|
||||
}
|
||||
|
||||
static mp_obj_t vfs_mac_ilistdir(mp_obj_t self_in, mp_obj_t path_in) {
|
||||
mp_obj_vfs_mac_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
vfs_mac_ilistdir_it_t *iter = mp_obj_malloc(vfs_mac_ilistdir_it_t, &mp_type_polymorph_iter);
|
||||
iter->iternext = vfs_mac_ilistdir_it_iternext;
|
||||
iter->is_str = mp_obj_get_type(path_in) == &mp_type_str;
|
||||
iter->volRefNum = self->volRefNum;
|
||||
iter->index = 1;
|
||||
|
||||
Str255 pName;
|
||||
vfs_mac_get_path_str(pName, sizeof(pName), path_in);
|
||||
iter->dirId = get_dirid(self->volRefNum, pName);
|
||||
|
||||
return MP_OBJ_FROM_PTR(iter);
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_2(vfs_mac_ilistdir_obj, vfs_mac_ilistdir);
|
||||
|
||||
static mp_obj_t vfs_mac_mkdir(mp_obj_t self_in, mp_obj_t path_in) {
|
||||
mp_raise_NotImplementedError(NULL); // TODO
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_2(vfs_mac_mkdir_obj, vfs_mac_mkdir);
|
||||
|
||||
static mp_obj_t vfs_mac_remove(mp_obj_t self_in, mp_obj_t path_in) {
|
||||
mp_raise_NotImplementedError(NULL); // TODO
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_2(vfs_mac_remove_obj, vfs_mac_remove);
|
||||
|
||||
static mp_obj_t vfs_mac_rename(mp_obj_t self_in, mp_obj_t old_path_in, mp_obj_t new_path_in) {
|
||||
mp_raise_NotImplementedError(NULL); // TODO
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_3(vfs_mac_rename_obj, vfs_mac_rename);
|
||||
|
||||
|
||||
static mp_obj_t vfs_mac_rmdir(mp_obj_t self_in, mp_obj_t path_in) {
|
||||
mp_raise_NotImplementedError(NULL); // TODO
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_2(vfs_mac_rmdir_obj, vfs_mac_rmdir);
|
||||
|
||||
static mp_obj_t vfs_mac_stat(mp_obj_t self_in, mp_obj_t path_in) {
|
||||
mp_obj_vfs_mac_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
Str255 pName;
|
||||
vfs_mac_get_path_str(pName, sizeof(pName), path_in);
|
||||
|
||||
MP_THREAD_GIL_EXIT();
|
||||
CInfoPBRec pb = {
|
||||
.hFileInfo.ioVRefNum = self->volRefNum,
|
||||
.hFileInfo.ioNamePtr = (StringPtr)pName,
|
||||
};
|
||||
OSErr err;
|
||||
err = PBGetCatInfoSync(&pb);
|
||||
MP_THREAD_GIL_ENTER();
|
||||
check_mac_err(err);
|
||||
|
||||
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
|
||||
bool is_dir = pb.hFileInfo.ioFlAttrib & (1 << 4);
|
||||
t->items[0] = MP_OBJ_NEW_SMALL_INT(is_dir ? MP_S_IFDIR | 0777 : MP_S_IFREG | 0666);
|
||||
t->items[1] = mp_obj_new_int_from_uint(pb.hFileInfo.ioFDirIndex);
|
||||
t->items[2] = mp_obj_new_int_from_uint(self->volRefNum);
|
||||
t->items[3] = mp_obj_new_int_from_uint(is_dir ? 2 : 1);
|
||||
t->items[4] = mp_obj_new_int_from_uint(0); // uid
|
||||
t->items[5] = mp_obj_new_int_from_uint(0); // gid
|
||||
t->items[6] = mp_obj_new_int_from_uint(is_dir ? 512 : pb.hFileInfo.ioFlPyLen); // data fork only
|
||||
t->items[7] = mp_obj_new_int_from_uint(0); // atime
|
||||
t->items[8] = mp_obj_new_int_from_uint(0); // mtime
|
||||
t->items[9] = mp_obj_new_int_from_uint(0); // ctime
|
||||
return MP_OBJ_FROM_PTR(t);
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_2(vfs_mac_stat_obj, vfs_mac_stat);
|
||||
|
||||
static mp_obj_t vfs_mac_statvfs(mp_obj_t self_in, mp_obj_t path_in) {
|
||||
mp_raise_NotImplementedError(NULL); // TODO
|
||||
#if 0
|
||||
mp_obj_vfs_mac_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
const char *path = vfs_mac_get_path_cstr(self, path_in);
|
||||
STRUCT_STATVFS sb;
|
||||
int ret;
|
||||
MP_HAL_RETRY_SYSCALL(ret, STATVFS(path, &sb), mp_raise_OSError(err));
|
||||
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
|
||||
t->items[0] = MP_OBJ_NEW_SMALL_INT(sb.f_bsize);
|
||||
t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.f_frsize);
|
||||
t->items[2] = MP_OBJ_NEW_SMALL_INT(sb.f_blocks);
|
||||
t->items[3] = MP_OBJ_NEW_SMALL_INT(sb.f_bfree);
|
||||
t->items[4] = MP_OBJ_NEW_SMALL_INT(sb.f_bavail);
|
||||
t->items[5] = MP_OBJ_NEW_SMALL_INT(sb.f_files);
|
||||
t->items[6] = MP_OBJ_NEW_SMALL_INT(sb.f_ffree);
|
||||
t->items[7] = MP_OBJ_NEW_SMALL_INT(F_FAVAIL);
|
||||
t->items[8] = MP_OBJ_NEW_SMALL_INT(F_FLAG);
|
||||
t->items[9] = MP_OBJ_NEW_SMALL_INT(F_NAMEMAX);
|
||||
return MP_OBJ_FROM_PTR(t);
|
||||
#endif
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_2(vfs_mac_statvfs_obj, vfs_mac_statvfs);
|
||||
|
||||
|
||||
static const mp_rom_map_elem_t vfs_mac_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_volumes), MP_ROM_PTR(&volumes_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&vfs_mac_mount_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&vfs_mac_umount_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&vfs_mac_open_obj) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&vfs_mac_chdir_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&vfs_mac_getcwd_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&vfs_mac_ilistdir_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&vfs_mac_mkdir_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&vfs_mac_remove_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&vfs_mac_rename_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&vfs_mac_rmdir_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&vfs_mac_stat_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&vfs_mac_statvfs_obj) },
|
||||
};
|
||||
static MP_DEFINE_CONST_DICT(vfs_mac_locals_dict, vfs_mac_locals_dict_table);
|
||||
|
||||
static const mp_vfs_proto_t vfs_mac_proto = {
|
||||
.import_stat = mp_vfs_mac_import_stat,
|
||||
};
|
||||
|
||||
static void vfs_mac_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
mp_obj_vfs_mac_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
VCB *vol = getVolumeByVolumeReference(self->volRefNum);
|
||||
mp_printf(print, "<VfsMac %d %.*s>",
|
||||
self->volRefNum, (int)PSTR_LEN(vol->vcbVN), PSTR_DATA(vol->vcbVN));
|
||||
}
|
||||
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
mp_type_vfs_mac,
|
||||
MP_QSTR_VfsMac,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
make_new, vfs_mac_make_new,
|
||||
protocol, &vfs_mac_proto,
|
||||
locals_dict, &vfs_mac_locals_dict,
|
||||
print, &vfs_mac_print
|
||||
);
|
||||
|
||||
mp_obj_vfs_mac_file_t mp_sys_stdin_obj = {{&mp_type_vfs_mac_textio}, STDIN_FILENO};
|
||||
mp_obj_vfs_mac_file_t mp_sys_stdout_obj = {{&mp_type_vfs_mac_textio}, STDOUT_FILENO};
|
||||
mp_obj_vfs_mac_file_t mp_sys_stderr_obj = {{&mp_type_vfs_mac_textio}, STDERR_FILENO};
|
||||
10
ports/m68kmac/vfs_mac.h
Normal file
10
ports/m68kmac/vfs_mac.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
typedef struct _mp_obj_vfs_mac_t mp_obj_vfs_mac_t;
|
||||
typedef struct _mp_obj_vfs_mac_file_t mp_obj_vfs_mac_file_t;
|
||||
|
||||
extern const mp_obj_type_t mp_type_vfs_mac;
|
||||
extern const mp_obj_type_t mp_type_vfs_mac_fileio;
|
||||
extern const mp_obj_type_t mp_type_vfs_mac_textio;
|
||||
|
||||
mp_obj_t mp_vfs_mac_file_open(const mp_obj_vfs_mac_t *fs, const mp_obj_type_t *type, mp_obj_t file_in, mp_obj_t mode_in);
|
||||
9
py/gc.c
9
py/gc.c
|
|
@ -420,6 +420,9 @@ static void gc_collect_start_common(void) {
|
|||
}
|
||||
|
||||
void gc_collect_root(void **ptrs, size_t len) {
|
||||
#if __m68k__
|
||||
len *= 2;
|
||||
#endif
|
||||
#if !MICROPY_GC_SPLIT_HEAP
|
||||
mp_state_mem_area_t *area = &MP_STATE_MEM(area);
|
||||
#endif
|
||||
|
|
@ -676,6 +679,12 @@ static void gc_sweep_free_blocks(void) {
|
|||
__attribute__((no_sanitize_address))
|
||||
#endif
|
||||
static void *gc_get_ptr(void **ptrs, int i) {
|
||||
#if __m68k__
|
||||
char *ptr = (char *)ptrs;
|
||||
ptr += i * 2;
|
||||
ptrs = (void **)ptr;
|
||||
i = 0;
|
||||
#endif
|
||||
#if MICROPY_DEBUG_VALGRIND
|
||||
if (!VALGRIND_CHECK_MEM_IS_ADDRESSABLE(&ptrs[i], sizeof(*ptrs))) {
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ typedef uintptr_t gc_helper_regs_t[10];
|
|||
typedef uintptr_t gc_helper_regs_t[11]; // x19-x29
|
||||
#elif defined(__riscv) && (__riscv_xlen <= 64)
|
||||
typedef uintptr_t gc_helper_regs_t[12]; // S0-S11
|
||||
#elif defined(__m68k__)
|
||||
typedef uintptr_t gc_helper_regs_t[8]; // a0-a4/d3-d7
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
43
shared/runtime/gchelper_m68k.s
Normal file
43
shared/runtime/gchelper_m68k.s
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2024 Alessandro Gatti
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
.global gc_helper_get_regs_and_sp
|
||||
.type gc_helper_get_regs_and_sp, @function
|
||||
|
||||
gc_helper_get_regs_and_sp:
|
||||
|
||||
/* Store registers into the given array (address on stack). */
|
||||
|
||||
move.l 4(%sp),%a0
|
||||
lea.l 32(%a0), %a0
|
||||
movem.l %a2-%a4/%d3-%d7, -(%a0)
|
||||
|
||||
/* Return the stack pointer in d0. */
|
||||
move.l %sp, %d0
|
||||
|
||||
rts
|
||||
|
||||
.size gc_helper_get_regs_and_sp, .-gc_helper_get_regs_and_sp
|
||||
Loading…
Reference in a new issue