Compare commits

...

113 Commits

Author SHA1 Message Date
laubzega f18b579ce2
Merge 122461a08c into b993d88339 2024-04-01 21:49:29 -04:00
mrdudz b993d88339 second half of #2420 - don't use the loop macro. Fixes -j13 for me 2024-03-17 17:19:42 +01:00
Bob Andrews 25967e65b5
Merge pull request #2424 from acqn/Cleanup
[cc65] Cleanups in src/cc65/declare.c
2024-03-10 02:39:14 +01:00
Bob Andrews a372ead4de
Merge pull request #2334 from carlo-bramini/fix-sim65-1
[SIM65] Support undocumented opcodes for 6502
2024-03-10 00:37:18 +01:00
Bob Andrews 081d18f7d7
Merge pull request #2422 from acqn/SubFix
[cc65] Fixed potential errors with subtraction evaluation of identifiers at different memory locations
2024-03-09 13:02:27 +01:00
Bob Andrews a293920fb3
Merge pull request #2423 from acqn/BitfieldFix
[cc65] Fixed the error recovery integer type used for bit-fields
2024-03-09 12:05:20 +01:00
Bob Andrews 60c75bdb54
Merge pull request #2414 from SvenMichaelKlose/master
vic-asm.cfg: Fix size of MAIN to end at $1E00.
2024-03-09 12:00:42 +01:00
Sven Michael Klose 1fe12f112e
Merge branch 'cc65:master' into master 2024-03-08 17:41:06 +01:00
Sven Michael Klose a887b29ffb Revert "Test strtok()."
This reverts commit 3a7bd53956.
2024-03-05 07:04:59 +01:00
acqn 731f349b24 Removed ParamTypeCvt(). 2024-02-29 18:24:22 +08:00
acqn 98767741ce Reorganized stuff in src/cc65/declare.c. 2024-02-29 18:24:22 +08:00
acqn 9b2d27d1e1 Fixed the error recovery integer type used for bit-fields. 2024-02-29 18:23:04 +08:00
acqn 23aa562094 Fixed potential errors with subtraction evaluation of identifiers at different memory locations. 2024-02-24 15:34:38 +08:00
Bob Andrews 5c3ff714ae
Merge pull request #2415 from polluks/patch-13
[grc65] Fixed segv of empty resource file
2024-02-23 16:16:46 +01:00
Colin Leroy-Mira 86317711e0 IIgs SCC: Rework branches to X-indexed variables
and general cleanup/commenting
2024-02-23 01:20:47 +01:00
Colin Leroy-Mira 8b71fafb84 IIgs SCC: Allow choosing 115200bps as the card allows it
Of course, that won't work full speed with the standard
IRQ-based RX. But that will allow users to setup the port
at this speed without duplicating the setup part of the
code. Up to them to add hooks to disable IRQs and read
directly in a tight asm loop.
2024-02-23 01:20:47 +01:00
Colin Leroy-Mira 3fd78208ba Disable IRQ if opening at 115200 bps 2024-02-19 19:31:47 +01:00
Colin Leroy-Mira 7a12399b39 Allow choosing 115200bps as the card allows it
Of course, that won't work full speed with the
standard IRQ-based RX. But that will allow users
to setup the port at this speed without duplicating
the setup part of the code. Up to them to add hooks
to disable IRQs and read directly in a tight asm
loop.
2024-02-19 19:31:47 +01:00
Sven Michael Klose 294b034920 Add configuration files for expanded VICs. 2024-02-15 17:32:44 +01:00
Stefan ab0eb4fe58
oops 2024-02-15 09:03:46 +01:00
Stefan 8d4946b3f4
Fixed segv
touch /tmp/xx
grc65 /tmp/xx
2024-02-15 07:52:42 +01:00
Sven Michael Klose 3a7bd53956 Test strtok(). 2024-02-15 01:05:35 +01:00
Sven Michael Klose 8173c850fd Fix size of MAIN to end at $1E00.
Caused negative size of MAIN in cc65-contrib/quikmans2k8.
2024-02-15 00:00:46 +01:00
Bob Andrews 4bde3afd80
Merge pull request #2410 from alexthissen/serial
Improvements and fixes in serial support for Atari Lynx
2024-02-12 12:50:02 +01:00
Alex Thissen 7d6f3d24d4 Changed sta (ptr1,x) to sta (ptr1)
Reset serial status on ser_close
Fixed error for saving serial state
2024-02-11 23:12:27 +00:00
Alex Thissen 8b172e05bc Applied optimization as per review 42Bastian 2024-02-11 20:59:08 +00:00
Alex Thissen 1deb9e52ae Replaced last literal value for SERCTL 2024-02-11 15:46:23 +00:00
Alex Thissen acff429eb8 Added redeye check for SER_HS_SW handshake 2024-02-11 15:33:22 +00:00
Alex Thissen 6cf8ee8eb5 Removed baud rates from 150 and lower.
Fixed tab
Replaced uploader references to SERIAL_INTERRUPT
2024-02-10 23:19:05 +00:00
Alex Thissen 65bce9ecde Implemented mark and space checks. 2024-02-10 23:19:04 +00:00
Alex Thissen 014f85f226 Fixed baud rates 2024-02-10 23:19:04 +00:00
Alex Thissen 788ae82d30 Fixes to serial driver implementation 2024-02-10 23:19:02 +00:00
Carlo Bramini b04d79b1da [SIM65] Support undocumented opcodes for 6502
This PR is mostly a complete rewrite of the emulator for 6502/65c02 opcodes.
It provides an easier to maintain implementation of the instructions, by using few macros rather than having hand-written code for each function.
All undocumented, previously missing opcodes for 6502 are also implemented.
The patch also includes a detailed documentation of those opcodes, for reference to developers.
This PR should fix one of the milestones listed here for the next version of CC65:

https://github.com/cc65/wiki/wiki/Before-the-next-release
2024-02-08 12:13:17 +01:00
mrdudz 3dfe033000 update actions/upload-artifact@v3->actions/upload-artifact@v4, actions/cache@v3->actions/cache@v4 2024-02-03 17:02:08 +01:00
mrdudz 5acfb02794 update actions/checkout@v3 -> actions/checkout@v4 and microsoft/setup-msbuild@v1.1 -> microsoft/setup-msbuild@v2. lets see what happens :) 2024-02-03 16:20:17 +01:00
mrdudz 934de685bc this is pr #2194 - removed ramfont.o 2024-02-03 01:34:21 +01:00
Bob Andrews ce14f0f8ad
Merge pull request #2401 from colinleroy/enabled-windows-tests-on-PR
Enable Windows tests on pull requests
2024-02-02 21:59:09 +01:00
Bob Andrews a06431a8f9
Merge pull request #2402 from colinleroy/a-bit-of-65c02-optimisations
a BIT of 65C02 optimisations
2024-02-02 21:58:04 +01:00
Bob Andrews eb503cc542
Merge pull request #2390 from colinleroy/implement-shifts-by-7
Implement aslax7/shlax7/asrax7/shrax7
2024-02-02 20:17:48 +01:00
Colin Leroy-Mira 1fab179cb4 a BIT of 65C02 optimisations
Use BIT immediate instead of AND when reloading A is required
afterwards.
Add an fread unit test as the optimisation touches fread.
Sprinkle a few zero page indexed while we're at it.
2024-02-02 19:45:49 +01:00
Colin Leroy-Mira c4c6967e4a Enable Windows tests on pull requests 2024-02-02 19:45:17 +01:00
Bob Andrews 2a03e5d8c9
Merge pull request #2392 from colinleroy/asm-fputc-fputs
Asm fputc fputs
2024-02-02 19:41:23 +01:00
Bob Andrews 128f261c3a
Merge pull request #2389 from colinleroy/fix-2388
Fix #2388
2024-02-02 18:25:39 +01:00
Bob Andrews d5c84e7828
Merge pull request #2398 from acqn/OptFix
[cc65] Fixed optimization for char-size bitwise XOR/OR/AND when the rhs operand is complicated
2024-02-02 17:20:31 +01:00
Bob Andrews 54b423a99e
fix test 2024-02-02 13:13:57 +01:00
acqn 96d55e3703 Fixed optimization for char-size bitwise XOR/OR/AND when the rhs operand is complicated. 2024-02-02 19:00:33 +08:00
Bob Andrews a7ac9b7ef2
Merge pull request #2397 from colinleroy/bug-2395-test
Add test case for issue #2395
2024-02-01 23:09:12 +01:00
Colin Leroy-Mira 1e300bf768 Add test case for issue #2395 2024-02-01 22:06:56 +01:00
Colin Leroy-Mira ce606bb19e Fix tests... 2024-01-31 08:40:36 +01:00
Colin Leroy-Mira afd8ee627e Remove useless branching code in fgets 2024-01-30 19:52:48 +01:00
Colin Leroy-Mira 2b97735d5d Factorize file pointer check 2024-01-30 19:40:28 +01:00
Colin Leroy-Mira 1a5a7d67a7 Rewrite fputc in assembly
-36 bytes, -12% cycles
2024-01-30 17:19:13 +01:00
Colin Leroy-Mira ba36071022 Rewrite fputs in assembly
-28 bytes, -1% cycles
2024-01-30 17:12:52 +01:00
Colin Leroy-Mira 7594af553a Fix #2388
Reopen stdin in binary mode instead of closing/opening
2024-01-30 09:39:44 +01:00
Colin Leroy-Mira 7d7cf2d1e0 Implement aslax7/shlax7/asrax7/shrax7 2024-01-29 07:59:11 +01:00
Bob Andrews 51b946bf25
Merge pull request #2387 from colinleroy/sim65-implement-remove
Implement __sysremove for sim65
2024-01-28 16:53:29 +01:00
Colin Leroy-Mira 0dd7b0c3a5 Implement __sysremove for sim65
This will allow using unlink()/remove() in sim65 programs
Use it to unlink fgets' test output file
2024-01-28 13:58:57 +01:00
Bob Andrews 8682095f9f
Merge pull request #2384 from colinleroy/asm-realloc
Rewrite realloc in asm
2024-01-27 00:28:17 +01:00
Bob Andrews 782dad23da
Merge pull request #2382 from colinleroy/asm-gets
Rewrite gets in asm
2024-01-27 00:26:12 +01:00
Colin Leroy-Mira aa6f850b8d Rewrite gets in assembler
+19 bytes if used alone, because it pulls in fgets, but as code is
factorized, -128 bytes in programs using both fgets and gets.
2024-01-26 20:29:17 +01:00
Colin Leroy-Mira a8b870555e Rewrite realloc in asm
-80 bytes, -39% cycles
2024-01-26 20:25:59 +01:00
Bob Andrews 65937684a0
Merge pull request #2380 from colinleroy/asm-fgetc
Rewrite fgetc in asm
2024-01-26 20:12:41 +01:00
Bob Andrews ba6f9f5d3f
Merge pull request #2381 from colinleroy/asm-fgets
Rewrite fgets in asm
2024-01-26 19:36:57 +01:00
Bob Andrews 4b9bf9bb98
Merge pull request #2386 from polluks/patch-12
Added link for your interest
2024-01-25 14:36:51 +01:00
Stefan f1d95f1f07
Added link for your interest 2024-01-25 11:27:54 +01:00
Colin Leroy-Mira 476591e8b7 Rewrite fgetc in asm
-82 bytes, -20% cycles
2024-01-25 09:13:39 +01:00
Colin Leroy-Mira f7388cfb79 add fgets/fgetc test 2024-01-25 09:13:35 +01:00
Bob Andrews ee3a542539
Merge pull request #2383 from colinleroy/pmemalign-size-opt
Optimize -36 bytes out of posix_memalign
2024-01-24 21:26:50 +01:00
Bob Andrews a2d722639f
Merge pull request #2385 from polluks/master
Fixed comment
2024-01-24 21:17:19 +01:00
Bob Andrews 9ba270d97b
Merge pull request #2378 from colinleroy/fix-overflow-in-malloc-and-realloc
Fix malloc and realloc overflow
2024-01-24 21:15:31 +01:00
Stefan 34f37c873e
Fixed comment 2024-01-24 16:32:45 +01:00
Bob Andrews ebf43ddb05
Merge pull request #2379 from colinleroy/add-return-minus-1-helper
add a return -1 helper
2024-01-24 15:53:19 +01:00
Colin Leroy-Mira f0b5b02966 Swap in LC before destructors, ROM after 2024-01-24 12:28:57 +01:00
Colin Leroy-Mira b87df9e1c6 Optimize -36 bytes out of posix_memalign
And add a unit test
2024-01-24 10:23:53 +01:00
Colin Leroy-Mira 1f820d0ae8 Rewrite fgets in asm
-104 bytes, -1% cycles
2024-01-24 10:16:26 +01:00
Colin Leroy-Mira 5d49fde788 add a return -1 helper 2024-01-24 09:58:09 +01:00
Colin Leroy-Mira 3e01ac9b04 Fix malloc and realloc overflow
If user requests a size >= 65532, adding the heap admin size
overflows size. Fixes #2358.
2024-01-24 09:54:54 +01:00
Bob Andrews 57e65a6bf6
Merge pull request #2348 from colinleroy/time-functions-pass-2
Convert more time functions to asm
2024-01-23 23:45:10 +01:00
Bob Andrews 13ddd734db
Add note about contacting the devs when starting to work on huge patches 2024-01-23 23:26:26 +01:00
Colin Leroy-Mira 2ba176372e Add beep for apple2 2024-01-23 23:04:50 +01:00
Bob Andrews 6c7106c9d9
Merge pull request #2377 from acqn/Diagnostics
[cc65] Diagnosis improvements
2024-01-23 22:16:35 +01:00
Bob Andrews ea83b9fa53
Merge pull request #2347 from acqn/SrcFile
[cc65] fixes and enhancements for source file info in diagnosis and debug output
2024-01-23 22:15:31 +01:00
acqn ba75a2ac26 Added missing checks for forward declarations of the main() function.
More accurate diagnosis on implicit 'int' type specifiers.
2024-01-23 14:33:05 +08:00
Bob Andrews bea2e86210
Merge pull request #2354 from acqn/Diagnostics
[cc65] Improved diagnosis
2024-01-22 19:09:51 +01:00
Bob Andrews 998dfd6fa7
Merge pull request #2370 from acqn/VisibilityFix
[cc65] Fixed visibility of undeclared functions and objects
2024-01-22 18:31:47 +01:00
Bob Andrews deaf27d6a8
Merge pull request #2372 from acqn/C99Inline
[cc65] Added primitive support for the ISO C99 inline feature as well as the __inline__ extension
2024-01-22 17:30:09 +01:00
Colin Leroy-Mira 01ee903cdf Fixup gmtime/localtime/mktime tests with all cases 2024-01-22 16:38:39 +01:00
Bob Andrews 7a7e98d799
Merge pull request #2371 from alexthissen/lynx-upload
Lynx upload fixes for IRQ and configuration
2024-01-22 15:50:40 +01:00
Bob Andrews 6a03fda453
Merge pull request #2374 from colinleroy/save-two-bytes
Save two cycles in pushax and popptr1
2024-01-22 15:48:49 +01:00
Colin Leroy-Mira b23a7ec407 Save two bytes in pushax and popptr1
It's not because Y must equal zero on rts that we should'nt spare one byte
and one cycle.
2024-01-19 21:14:47 +01:00
Alex Thissen 83691f30c1 Missed a tab in config 2024-01-19 10:52:42 +00:00
Alex Thissen 6bab3f921b
Merge branch 'cc65:master' into lynx-upload 2024-01-19 09:49:20 +01:00
Alex Thissen ad90a3a421 Replaced references to __BLOCKSIZE__ with __BANK0BLOCKSIZE__ 2024-01-18 18:57:57 +00:00
Bob Andrews a173428fab
Merge pull request #2367 from acqn/PragmaFix
[cc65] Fixed segname pragmas right after a function definition
2024-01-18 18:44:39 +01:00
Alex Thissen 2e56dcc521 Fix for mising import 2024-01-18 18:13:39 +01:00
Alex Thissen acce24fedc Switched to __BANK0BLOCKSIZE__ instead of __BLOCKSIZE__ to make current lynx config files work 2024-01-18 18:13:02 +01:00
Alex Thissen 93f9cb6e48 Adjusted uploader configuration. Split into two MEMORY areas, so it can be just below video memory. 2024-01-18 18:06:10 +01:00
Alex Thissen d906748691 Fix uploader implementation to reset IRQ bit for timer 4 (serial) interrupt 2024-01-18 17:37:09 +01:00
Colin Leroy-Mira 166a4b25f7 Apple2: implement sleep using MONWAIT
Also publish detect_iigs(), set_iigs_speed() and get_iigs_speed(). Refactor to only store one ostype variable.
2024-01-18 16:17:10 +01:00
acqn 9471e128b5 Fixed segname pragmas right after a function definition. 2024-01-18 20:59:46 +08:00
Colin Leroy-Mira 10282a9b74 Rewrite asctime() in assembler (-7 bytes) 2024-01-18 09:09:38 +01:00
Colin Leroy-Mira 03d5e5fba0 Rewrite mktime in assembly
-415 bytes,
-39% cycles,
Unit test expanded to cover more cases (there was a bug in 2100 before!)
2024-01-18 09:09:38 +01:00
Colin Leroy-Mira 348a9048b7 Convert _time_t_to_tm to asm
46 bytes size gain, -8% cycles on the unit tests
2024-01-18 09:09:31 +01:00
acqn e9bd9330c0 Added warning on some code patterns of faulty attempt to declare anonymous structs/unions.
Removed unnecessary warning on tagless enum/struct/unions that would be invisible out of a function declaration.
2024-01-15 23:56:42 +08:00
acqn 07e349c517 Skipped anonymous tag names in diagnosis on empty structs/unions. 2024-01-15 23:56:39 +08:00
acqn afdf398a0b Fixed repeated diagnosis when reading EOF in certain cases. 2024-01-15 23:56:11 +08:00
acqn de3087a7e9 Removed the extra "unused parameter" warning when the parameter had an duplicated identifier error. 2024-01-14 00:19:11 +08:00
acqn 0b06c34dfc Added primitive support for the ISO C99 inline feature as well as the __inline__ extension.
No inlining is actually done but that part is not required by the standard.
2024-01-14 00:08:41 +08:00
acqn 7e80e55b6d Added a warning on implicit int in typedefs. 2024-01-13 16:40:44 +08:00
acqn 3d0dc58153 Fixed visibility of undeclared functions and objects. 2024-01-13 00:46:14 +08:00
acqn 8e43c4706f Added hierarchy info about source file inclusion in diagnostic output.
Fixed presumed names of source files in disgnosis.
Fixed line number of source files in debug output.
2024-01-10 04:50:42 +08:00
Bob Andrews 122461a08c
Merge branch 'master' into pr-1056 2021-12-11 22:36:51 +01:00
laubzega 477adc23fb Fix #1056. 2020-06-30 20:36:13 -07:00
145 changed files with 9023 additions and 3411 deletions

View File

@ -19,7 +19,7 @@ jobs:
- shell: bash
run: git config --global core.autocrlf input
- name: Checkout Source
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Do some simple style checks
shell: bash
@ -44,7 +44,7 @@ jobs:
shell: bash
run: make -j2 doc
- name: Upload a documents snapshot.
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: docs
path: ./html
@ -54,7 +54,7 @@ jobs:
make -j2 bin USER_CFLAGS=-Werror CROSS_COMPILE=x86_64-w64-mingw32-
build_windows:
name: Build (Windows)
name: Build and Test (Windows)
runs-on: windows-latest
steps:
@ -62,10 +62,10 @@ jobs:
run: git config --global core.autocrlf input
- name: Checkout Source
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.1
uses: microsoft/setup-msbuild@v2
- name: Build app (x86 debug)
run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug -property:Platform=Win32
@ -79,4 +79,14 @@ jobs:
- name: Build app (x64 release)
run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Release -property:Platform=x64
- name: Build utils (MinGW)
shell: cmd
run: make -j2 util SHELL=cmd
- name: Build the platform libraries (make lib)
shell: cmd
run: make -j2 lib QUIET=1 SHELL=cmd
- name: Run the regression tests (make test)
shell: cmd
run: make test QUIET=1 SHELL=cmd

View File

@ -18,10 +18,10 @@ jobs:
run: git config --global core.autocrlf input
- name: Checkout Source
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.1
uses: microsoft/setup-msbuild@v2
- name: Build app (debug)
run: msbuild src\cc65.sln -t:rebuild -property:Configuration=Debug
@ -44,7 +44,7 @@ jobs:
- shell: bash
run: git config --global core.autocrlf input
- name: Checkout Source
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Do some simple style checks
shell: bash
@ -86,18 +86,18 @@ jobs:
mv cc65.zip cc65-snapshot-win32.zip
- name: Upload a 32-bit Snapshot Zip
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: cc65-snapshot-win32
path: cc65-snapshot-win32.zip
- name: Upload a 64-bit Snapshot Zip
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: cc65-snapshot-win64
path: cc65-snapshot-win64.zip
- name: Get the online documents repo.
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: cc65/doc
# this token will expire, if it does, generate a new one as decribed in https://github.com/cc65/cc65/issues/2065
@ -120,7 +120,7 @@ jobs:
- name: Package offline documents.
run: 7z a cc65-snapshot-docs.zip ./html/*.*
- name: Upload a Documents Snapshot Zip
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: cc65-snapshot-docs
path: cc65-snapshot-docs.zip

View File

@ -30,7 +30,7 @@ jobs:
run: mkdir ~/.cache-sha
- name: Cache SHA
uses: actions/cache@v3
uses: actions/cache@v4
id: check-sha
with:
path: ~/.cache-sha
@ -43,11 +43,11 @@ jobs:
- name: Checkout source
if: steps.check-sha.outputs.cache-hit != 'true'
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Add msbuild to PATH
if: steps.check-sha.outputs.cache-hit != 'true'
uses: microsoft/setup-msbuild@v1.1
uses: microsoft/setup-msbuild@v2
- name: Build app (MSVC debug)
if: steps.check-sha.outputs.cache-hit != 'true'

View File

@ -1,4 +1,6 @@
This document contains all kinds of information that you should know if you want to contribute to the cc65 project. Before you start, please read all of it. If something is not clear to you, please ask - this document is an ongoing effort and may well be incomplete.
This document contains all kinds of information that you should know if you want to contribute to the cc65 project. Before you start, please read all of it. If something is not clear to you, please ask - this document is an ongoing effort and may well be incomplete.
Also, before you put a lot of work into implementing something you want to contribute, please get in touch with one of the developers and ask if what you are going to do is actually wanted and has a chance of being merged. Perhaps someone else is already working on it, or perhaps what you have in mind is not how we'd expect it to be - talking to us before you start might save you a lot of work in those cases.
(''Note:'' The word "must" indicates a requirement. The word "should" indicates a recomendation.)

View File

@ -9,7 +9,7 @@ For details look at the [Website](https://cc65.github.io).
Project founders:
* John R. Dunning: original implementation of the C compiler and runtime library, Atari hosted
* John R. Dunning: [original implementation](https://public.websites.umich.edu/~archive/atari/8bit/Languages/Cc65/) of the C compiler and runtime library, Atari hosted
* Ullrich von Bassewitz:
* move the code to modern systems
* rewrite most parts of the compiler

View File

@ -15,7 +15,7 @@ CPU_ISET_4510 = $0400
CPU_NONE = CPU_ISET_NONE
CPU_6502 = CPU_ISET_6502
CPU_6502X = CPU_ISET_6502|CPU_ISET_6502X
CPU_6502DTV = CPU_ISET_6502|CPU_ISET_6502X|CPU_ISET_6502DTV
CPU_6502DTV = CPU_ISET_6502|CPU_ISET_6502DTV
CPU_65SC02 = CPU_ISET_6502|CPU_ISET_65SC02
CPU_65C02 = CPU_ISET_6502|CPU_ISET_65SC02|CPU_ISET_65C02
CPU_65816 = CPU_ISET_6502|CPU_ISET_65SC02|CPU_ISET_65816

View File

@ -259,30 +259,44 @@ SND_INTERRUPT = TIMER7_INTERRUPT
INTRST = $FD80
INTSET = $FD81
MAGRDY0 = $FD84
MAGRDY1 = $FD85
AUDIN = $FD86
SYSCTL1 = $FD87
MIKEYHREV = $FD88
MIKEYSREV = $FD89
IODIR = $FD8A
IODAT = $FD8B
TxIntEnable = %10000000
RxIntEnable = %01000000
TxParEnable = %00010000
ResetErr = %00001000
TxOpenColl = %00000100
TxBreak = %00000010
ParEven = %00000001
TxReady = %10000000
RxReady = %01000000
TxEmpty = %00100000
RxParityErr = %00010000
RxOverrun = %00001000
RxFrameErr = %00000100
RxBreak = %00000010
ParityBit = %00000001
SERCTL = $FD8C
IODIR = $FD8A
IODAT = $FD8B
; IODIR and IODAT bit definitions
AUDIN_BIT = $10 ; Note that there is also the address AUDIN
READ_ENABLE = $10 ; Same bit for AUDIN_BIT
RESTLESS = $08
NOEXP = $04 ; If set, redeye is not connected
CART_ADDR_DATA = $02
CART_POWER_OFF = $02 ; Same bit for CART_ADDR_DATA
EXTERNAL_POWER = $01
SERCTL = $FD8C
; SERCTL bit definitions for write operations
TXINTEN = $80
RXINTEN = $40
PAREN = $10
RESETERR = $08
TXOPEN = $04
TXBRK = $02
PAREVEN = $01
; SERCTL bit definitions for read operations
TXRDY = $80
RXRDY = $40
TXEMPTY = $20
PARERR = $10
OVERRUN = $08
FRAMERR = $04
RXBRK = $02
PARBIT = $01
SERDAT = $FD8D
SDONEACK = $FD90
CPUSLEEP = $FD91

41
cfg/kim1-mtu60k.cfg Normal file
View File

@ -0,0 +1,41 @@
# kim1-mtu60k.cfg (4k)
#
# for expanded KIM-1 w/ K-1008 Graphics and 60K RAM
#
# ld65 --config kim1-mtu60k.cfg -o <prog>.bin <prog>.o
FEATURES {
STARTADDRESS: default = $2000;
CONDES: segment = STARTUP,
type = constructor,
label = __CONSTRUCTOR_TABLE__,
count = __CONSTRUCTOR_COUNT__;
CONDES: segment = STARTUP,
type = destructor,
label = __DESTRUCTOR_TABLE__,
count = __DESTRUCTOR_COUNT__;
}
SYMBOLS {
__STACKSIZE__: type = weak, value = $0080; # 128 byte program stack
__STARTADDRESS__: type = export, value = %S;
}
MEMORY {
ZP: file = %O, define = yes, start = $0000, size = $00EE;
CPUSTACK: file = "", define = yes, start = $0100, size = $0100;
RAM: file = %O, define = yes, start = %S, size = $E000 - %S - __STACKSIZE__;
MAINROM: file = "", define = yes, start = $E000, size = $1000;
TOP: file = "", define = yes, start = $F000, size = $1000;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp, define = yes;
STARTUP: load = RAM, type = ro, define = yes;
CODE: load = RAM, type = ro, define = yes;
RODATA: load = RAM, type = ro, define = yes;
ONCE: load = RAM, type = ro, define = yes;
DATA: load = RAM, type = rw, define = yes;
BSS: load = RAM, type = bss, define = yes;
}

41
cfg/kim1-mtuE000.cfg Normal file
View File

@ -0,0 +1,41 @@
# kim1-mtu60k.cfg (4k)
#
# for expanded KIM-1 w/ K-1008 Graphics and 60K RAM
#
# ld65 --config kim1-mtu60k.cfg -o <prog>.bin <prog>.o
FEATURES {
STARTADDRESS: default = $E000;
CONDES: segment = STARTUP,
type = constructor,
label = __CONSTRUCTOR_TABLE__,
count = __CONSTRUCTOR_COUNT__;
CONDES: segment = STARTUP,
type = destructor,
label = __DESTRUCTOR_TABLE__,
count = __DESTRUCTOR_COUNT__;
}
SYMBOLS {
__STACKSIZE__: type = weak, value = $0080; # 128 byte program stack
__STARTADDRESS__: type = export, value = %S;
}
MEMORY {
ZP: file = %O, define = yes, start = $0000, size = $00EE;
CPUSTACK: file = "", define = yes, start = $0100, size = $0100;
RAM: file = %O, define = yes, start = $2000, size = $E000 - $2000 - __STACKSIZE__;
MAINROM: file = "", define = yes, start = $E000, size = $1000;
TOP: file = "", define = yes, start = $F000, size = $1000;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp, define = yes;
STARTUP: load = RAM, type = ro, define = yes;
CODE: load = RAM, type = ro, define = yes;
RODATA: load = RAM, type = ro, define = yes;
ONCE: load = RAM, type = ro, define = yes;
DATA: load = RAM, type = rw, define = yes;
BSS: load = RAM, type = bss, define = yes;
}

View File

@ -5,16 +5,17 @@ SYMBOLS {
__BANK1BLOCKSIZE__: type = weak, value = $0000; # bank 1 block size
__EXEHDR__: type = import;
__BOOTLDR__: type = import;
__DEFDIR__: type = import;
__UPLOADER__: type = import;
__UPLOADERSIZE__: type = export, value = $61;
__HEADERSIZE__: type = export, value = 64;
}
MEMORY {
ZP: file = "", define = yes, start = $0000, size = $0100;
HEADER: file = %O, start = $0000, size = $0040;
HEADER: file = %O, start = $0000, size = __HEADERSIZE__;
BOOT: file = %O, start = $0200, size = __STARTOFDIRECTORY__;
DIR: file = %O, start = $0000, size = 8;
MAIN: file = %O, define = yes, start = $0200, size = $BD38 - __STACKSIZE__;
UPLDR: file = %O, define = yes, start = $BFDC, size = $005C;
DIR: file = %O, start = $0000, size = 16;
MAIN: file = %O, define = yes, start = $0200, size = $C038 - __UPLOADERSIZE__ - $200 - __STACKSIZE__;
UPLOAD: file = %O, define = yes, start = $C038 - __UPLOADERSIZE__, size = $0061;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp;
@ -30,8 +31,8 @@ SEGMENTS {
RODATA: load = MAIN, type = ro, define = yes;
DATA: load = MAIN, type = rw, define = yes;
BSS: load = MAIN, type = bss, define = yes;
UPCODE: load = UPLDR, type = ro, define = yes;
UPDATA: load = UPLDR, type = rw, define = yes;
UPCODE: load = UPLOAD, type = ro, define = yes;
UPDATA: load = UPLOAD, type = rw, define = yes;
}
FEATURES {
CONDES: type = constructor,

21
cfg/vic20-asm-32k.cfg Normal file
View File

@ -0,0 +1,21 @@
# Assembly program configuration for expanded VICs (>= +8K).
FEATURES {
STARTADDRESS: default = $1201;
}
SYMBOLS {
__LOADADDR__: type = import;
}
MEMORY {
ZP: file = "", start = $0002, size = $001A, define = yes;
LOADADDR: file = %O, start = %S - 2, size = $0002;
MAIN: file = %O, start = %S, size = $8000 - %S;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp, optional = yes;
LOADADDR: load = LOADADDR, type = ro;
CODE: load = MAIN, type = ro;
RODATA: load = MAIN, type = ro;
DATA: load = MAIN, type = rw;
BSS: load = MAIN, type = bss, optional = yes, define = yes;
}

21
cfg/vic20-asm-3k.cfg Normal file
View File

@ -0,0 +1,21 @@
# Assembly program configuration for expanded VICs (+3K only).
FEATURES {
STARTADDRESS: default = $0401;
}
SYMBOLS {
__LOADADDR__: type = import;
}
MEMORY {
ZP: file = "", start = $0002, size = $001A, define = yes;
LOADADDR: file = %O, start = %S - 2, size = $0002;
MAIN: file = %O, start = %S, size = $1E00 - %S;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp, optional = yes;
LOADADDR: load = LOADADDR, type = ro;
CODE: load = MAIN, type = ro;
RODATA: load = MAIN, type = ro;
DATA: load = MAIN, type = rw;
BSS: load = MAIN, type = bss, optional = yes, define = yes;
}

View File

@ -1,3 +1,5 @@
# Assembly program configuration for unexpanded VICs.
FEATURES {
STARTADDRESS: default = $1001;
}
@ -7,7 +9,7 @@ SYMBOLS {
MEMORY {
ZP: file = "", start = $0002, size = $001A, define = yes;
LOADADDR: file = %O, start = %S - 2, size = $0002;
MAIN: file = %O, start = %S, size = $0DF3 - %S;
MAIN: file = %O, start = %S, size = $1E00 - %S;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp, optional = yes;

View File

@ -62,7 +62,7 @@ Special locations:
</descrip><p>
While running <tt/main()/ the Language Card bank 2 is enabled for read access.
However while running module constructors/destructors the Language Card is disabled.
However while running module constructors the Language Card is disabled.
Enabling the Language Card allows to use it as additional memory for cc65
generated code. However code is never automatically placed there. Rather code
@ -330,13 +330,30 @@ usage.
<item>_dos_type
<item>_filetype
<item>_datetime
<item>beep
<item>get_ostype
<item>gmtime_dt
<item>mktime_dt
<item>rebootafterexit
<item>ser_apple2_slot
<item>tgi_apple2_mix
</itemize>
<sect1>Apple IIgs specific functions in accelerator.h<p>
In addition to those, the <tt/accelerator.h/ header file contains three functions
to help determine whether the program is running on a IIgs, and change the IIgs
CPU speed. See the <url url="funcref.html" name="function reference"> for declaration and
usage.
<itemize>
<item>detect_iigs
<item>get_iigs_speed
<item>set_iigs_speed
</itemize>
<sect1>Hardware access<p>
There's currently no support for direct hardware access. This does not mean
@ -435,10 +452,15 @@ The names in the parentheses denote the symbols to be used for static linking of
(RTS/CTS) and does interrupt driven receives. Speeds faster than 9600 baud
aren't reachable because the ROM and ProDOS IRQ handlers are too slow.
Software flow control (XON/XOFF) is not supported.
Note that because of the peculiarities of the 6551 chip transmits are not
interrupt driven, and the transceiver blocks if the receiver asserts
flow control because of a full buffer.
Note that using the driver at SER_BAUD_115200 will disable IRQs. It will be up
to the users to use the serial port, either by re-enabling IRQs themselves,
or by directly poll-reading the ACIA DATA register without the help of ser_get().
The driver defaults to slot 2. Call <tt/ser_apple2_slot()/ prior to
<tt/ser_open()/ in order to select a different slot. <tt/ser_apple2_slot()/
succeeds for all Apple&nbsp;II slots, but <tt/ser_open()/ fails with

View File

@ -63,7 +63,7 @@ Special locations:
</descrip><p>
While running <tt/main()/ the Language Card bank 2 is enabled for read access.
However while running module constructors/destructors the Language Card is disabled.
However while running module constructors the Language Card is disabled.
Enabling the Language Card allows to use it as additional memory for cc65
generated code. However code is never automatically placed there. Rather code
@ -331,7 +331,10 @@ usage.
<item>_dos_type
<item>_filetype
<item>_datetime
<item>beep
<item>get_ostype
<item>gmtime_dt
<item>mktime_dt
<item>rebootafterexit
<item>ser_apple2_slot
<item>tgi_apple2_mix
@ -340,6 +343,20 @@ usage.
</itemize>
<sect1>Apple IIgs specific functions in accelerator.h<p>
In addition to those, the <tt/accelerator.h/ header file contains three functions
to help determine whether the program is running on a IIgs, and change the IIgs
CPU speed. See the <url url="funcref.html" name="function reference"> for declaration and
usage.
<itemize>
<item>detect_iigs
<item>get_iigs_speed
<item>set_iigs_speed
</itemize>
<sect1>Hardware access<p>
There's currently no support for direct hardware access. This does not mean
@ -436,10 +453,15 @@ The names in the parentheses denote the symbols to be used for static linking of
(RTS/CTS) and does interrupt driven receives. Speeds faster than 9600 baud
aren't reachable because the ROM and ProDOS IRQ handlers are too slow.
Software flow control (XON/XOFF) is not supported.
Note that because of the peculiarities of the 6551 chip transmits are not
interrupt driven, and the transceiver blocks if the receiver asserts
flow control because of a full buffer.
Note that using the driver at SER_BAUD_115200 will disable IRQs. It will be up
to the users to use the serial port, either by re-enabling IRQs themselves,
or by directly poll-reading the ACIA DATA register without the help of ser_get().
The driver defaults to slot 2. Call <tt/ser_apple2_slot()/ prior to
<tt/ser_open()/ in order to select a different slot. <tt/ser_apple2_slot()/
succeeds for all Apple&nbsp;II slots, but <tt/ser_open()/ fails with

View File

@ -71,18 +71,21 @@ function.
<item><ref id="detect_c64dtv" name="detect_c64dtv">
<item><ref id="detect_c65" name="detect_c65">
<item><ref id="detect_chameleon" name="detect_chameleon">
<item><ref id="detect_iigs" name="detect_iigs">
<item><ref id="detect_scpu" name="detect_scpu">
<item><ref id="detect_turbomaster" name="detect_turbomaster">
<item><ref id="get_c128_speed" name="get_c128_speed">
<item><ref id="get_c64dtv_speed" name="get_c64dtv_speed">
<item><ref id="get_c65_speed" name="get_c65_speed">
<item><ref id="get_chameleon_speed" name="get_chameleon_speed">
<item><ref id="get_iigs_speed" name="get_iigs_speed">
<item><ref id="get_scpu_speed" name="get_scpu_speed">
<item><ref id="get_turbomaster_speed" name="get_turbomaster_speed">
<item><ref id="set_c128_speed" name="set_c128_speed">
<item><ref id="set_c64dtv_speed" name="set_c64dtv_speed">
<item><ref id="set_c65_speed" name="set_c65_speed">
<item><ref id="set_chameleon_speed" name="set_chameleon_speed">
<item><ref id="set_iigs_speed" name="set_iigs_speed">
<item><ref id="set_scpu_speed" name="set_scpu_speed">
<item><ref id="set_turbomaster_speed" name="set_turbomaster_speed">
</itemize>
@ -92,6 +95,7 @@ function.
<itemize>
<item>_dos_type
<item><ref id="beep" name="beep">
<item><ref id="get_ostype" name="get_ostype">
<item><ref id="gmtime_dt" name="gmtime_dt">
<item><ref id="mktime_dt" name="mktime_dt">
@ -103,7 +107,10 @@ function.
<itemize>
<item>_dos_type
<item><ref id="beep" name="beep">
<item><ref id="get_ostype" name="get_ostype">
<item><ref id="gmtime_dt" name="gmtime_dt">
<item><ref id="mktime_dt" name="mktime_dt">
<item>rebootafterexit
<item><ref id="videomode" name="videomode">
</itemize>
@ -1766,10 +1773,11 @@ used in presence of a prototype.
<descrip>
<tag/Function/Beep sound.
<tag/Header/<tt/<ref id="sym1.h" name="sym1.h">/
<tag/Header/<tt/<ref id="apple2.h" name="apple2.h">/
<tag/Declaration/<tt/void beep(void);/
<tag/Description/<tt/beep/ makes a brief tone.
<tag/Notes/<itemize>
<item>The function is specific to the Sym-1.
<item>The function is specific to the Sym-1 and Apple2 platforms.
</itemize>
<tag/Availability/cc65
<tag/See also/
@ -3453,6 +3461,26 @@ used in presence of a prototype.
</quote>
<sect1>detect_iigs<label id="detect_iigs"><p>
<quote>
<descrip>
<tag/Function/Check whether we are running on an Apple IIgs..
<tag/Header/<tt/<ref id="accelerator.h" name="accelerator.h">/
<tag/Declaration/<tt/unsigned char detect_iigs (void);/
<tag/Description/The function returns a 1 if running on an Apple IIgs.
<tag/Notes/<itemize>
<item>The function is specific to the Apple2 and Apple2enh platforms.
</itemize>
<tag/Availability/cc65 (not all platforms)
<tag/See also/
<ref id="get_iigs_speed" name="get_iigs_speed">,
<ref id="set_iigs_speed" name="set_iigs_speed">,
<tag/Example/None.
</descrip>
</quote>
<sect1>detect_scpu<label id="detect_scpu"><p>
<quote>
@ -4167,6 +4195,27 @@ header files define constants that can be used to check the return code.
</quote>
<sect1>get_iigs_speed<label id="get_iigs_speed"><p>
<quote>
<descrip>
<tag/Function/Get the current speed of the Apple IIgs.
<tag/Header/<tt/<ref id="accelerator.h" name="accelerator.h">/
<tag/Declaration/<tt/unsigned char get_iigs_speed (void);/
<tag/Description/The function returns the current speed of the Apple IIgs.
<tag/Notes/<itemize>
<item>The function is specific to the Apple2 and Apple2enh platforms.
<item>See the accelerator.h header for the speed definitions.
</itemize>
<tag/Availability/cc65 (not all platforms)
<tag/See also/
<ref id="detect_iigs" name="detect_iigs">,
<ref id="set_iigs_speed" name="set_iigs_speed">,
<tag/Example/None.
</descrip>
</quote>
<sect1>get_scpu_speed<label id="get_scpu_speed"><p>
<quote>
@ -6985,6 +7034,30 @@ clean-up when exiting the program.
</quote>
<sect1>set_iigs_speed<label id="set_iigs_speed"><p>
<quote>
<descrip>
<tag/Function/Set the current speed of the Apple IIgs.
<tag/Header/<tt/<ref id="accelerator.h" name="accelerator.h">/
<tag/Declaration/<tt/unsigned char __fastcall__ set_iigs_speed (unsigned char speed);/
<tag/Description/The function sets the speed of the Apple IIgs CPU (and returns
the new speed).
<tag/Notes/<itemize>
<item>The function is specific to the Apple2 and Apple2enh platforms.
<item>See the accelerator.h header for the speed definitions.
<item>Accepted parameters are SPEED_SLOW and SPEED_FAST (all other values are
considered SPEED_FAST).
</itemize>
<tag/Availability/cc65 (not all platforms)
<tag/See also/
<ref id="detect_iigs" name="detect_iigs">,
<ref id="get_iigs_speed" name="get_iigs_speed">,
<tag/Example/None.
</descrip>
</quote>
<sect1>set_scpu_speed<label id="set_scpu_speed"><p>
<quote>

View File

@ -304,6 +304,36 @@ unsigned char detect_turbomaster (void);
* 0x01 : C64 Turbo Master cartridge present
*/
unsigned char __fastcall__ set_iigs_speed (unsigned char speed);
/* Set the speed of the Apple IIgs CPU.
*
* Possible values:
* SPEED_SLOW : 1 Mhz mode
* SPEED_FAST : Fast mode (2.8MHz or more, depending on the presence of
* an accelerator)
*
* Any other value will be interpreted as SPEED_FAST.
*/
unsigned char get_iigs_speed (void);
/* Get the speed of the Apple IIgs CPU.
*
* Possible return values:
* SPEED_SLOW : 1 Mhz mode
* SPEED_FAST : Fast mode (2.8MHz or more, depending on the presence of
* an accelerator)
*/
unsigned char detect_iigs (void);
/* Check whether we are running on an Apple IIgs.
*
* Possible return values:
* 0x00 : No
* 0x01 : Yes
*/
/* End of accelerator.h */
#endif

View File

@ -197,6 +197,9 @@ extern void a2_lo_tgi[];
void beep (void);
/* Beep beep. */
unsigned char get_ostype (void);
/* Get the machine type. Returns one of the APPLE_xxx codes. */

View File

@ -86,6 +86,10 @@ extern FILE* stderr;
# define FILENAME_MAX (80+1)
#elif defined(__TELESTRAT__)
# define FILENAME_MAX (50+1)
#elif defined(__SIM6502__)
# define FILENAME_MAX (1024+1)
#elif defined(__SIM65C02__)
# define FILENAME_MAX (1024+1)
#else
# define FILENAME_MAX (16+1)
#endif

20
libsrc/apple2/beep.s Normal file
View File

@ -0,0 +1,20 @@
;
; Colin Leroy-Mira, 2024
;
; void beep(void)
;
.export _beep
.import BELL
.include "apple2.inc"
.segment "LOWCODE"
_beep:
lda CH ; Bell scrambles CH in 80col mode on IIgs, storing
pha ; it in OURCH and resetting CH to 0. Save it.
jsr BELL
pla
sta CH ; Restore CH
rts

20
libsrc/apple2/bell.s Normal file
View File

@ -0,0 +1,20 @@
;
; Colin Leroy-Mira, 2024
;
; BELL routine
;
.export BELL
.include "apple2.inc"
.segment "LOWCODE"
BELL:
; Switch in ROM and call BELL
bit $C082
jsr $FF3A ; BELL
; Switch in LC bank 2 for R/O and return
bit $C080
rts

View File

@ -40,12 +40,15 @@ _exit: ldx #<exit
lda #>exit
jsr reset ; Setup RESET vector
; Switch in ROM, in case it wasn't already switched in by a RESET.
bit $C082
; Switch in LC bank 2 for R/O in case it was switched out by a RESET.
bit $C080
; Call the module destructors.
jsr donelib
; Switch in ROM.
bit $C082
; Restore the original RESET vector.
exit: ldx #$02
: lda rvsave,x

View File

@ -0,0 +1,17 @@
;
; Colin Leroy-Mira <colin@colino.net>, 2024
;
; void __fastcall__ detect_iigs(void)
;
.export _detect_iigs
.import ostype, return0, return1
.include "apple2.inc"
; Returns 1 if running on IIgs, 0 otherwise
_detect_iigs:
lda ostype
bpl :+
jmp return1
: jmp return0

View File

@ -0,0 +1,22 @@
;
; Colin Leroy-Mira <colin@colino.net>, 2024
;
; unsigned char __fastcall__ get_iigs_speed(void)
;
.export _get_iigs_speed
.import ostype, return0
.include "apple2.inc"
.include "accelerator.inc"
_get_iigs_speed:
lda ostype ; Return SLOW if not IIgs
bpl :+
lda CYAREG ; Check current setting
bpl :+
lda #SPEED_FAST
ldx #$00
rts
.assert SPEED_SLOW = 0, error
: jmp return0 ; SPEED_SLOW

View File

@ -5,7 +5,7 @@
;
.constructor initostype, 9
.export _get_ostype
.export _get_ostype, ostype
; Identify machine according to:
; Apple II Miscellaneous TechNote #7, Apple II Family Identification

View File

@ -66,34 +66,16 @@ HSType: .res 1 ; Flow-control type
RecvBuf: .res 256 ; Receive buffers: 256 bytes
SendBuf: .res 256 ; Send buffers: 256 bytes
CurClockSource: .res 1 ; Whether to use BRG or RTxC for clock
.data
Opened: .byte $00 ; 1 when opened
Channel: .byte $00 ; Channel B by default
CurChanIrqFlags:.byte INTR_PENDING_RX_EXT_B
CurChanIrqFlags:.byte $00
SerFlagOrig: .byte $00
; Tables used to translate cc65 RS232 params into register values
; (Ref page 5-18 and 5-19)
BaudLowTable: .byte $7E ; SER_BAUD_300
.byte $5E ; SER_BAUD_1200
.byte $2E ; SER_BAUD_2400
.byte $16 ; SER_BAUD_4800
.byte $0A ; SER_BAUD_9600
.byte $04 ; SER_BAUD_19200
.byte $01 ; SER_BAUD_38400
.byte $00 ; SER_BAUD_57600
BaudHighTable: .byte $01 ; SER_BAUD_300
.byte $00 ; SER_BAUD_1200
.byte $00 ; SER_BAUD_2400
.byte $00 ; SER_BAUD_4800
.byte $00 ; SER_BAUD_9600
.byte $00 ; SER_BAUD_19200
.byte $00 ; SER_BAUD_38400
.byte $00 ; SER_BAUD_57600
RxBitTable: .byte %00000000 ; SER_BITS_5, in WR_RX_CTRL (WR3)
.byte %10000000 ; SER_BITS_6 (Ref page 5-7)
.byte %01000000 ; SER_BITS_7
@ -106,29 +88,65 @@ TxBitTable: .byte %00000000 ; SER_BITS_5, in WR_TX_CTRL (WR5)
.rodata
ClockMultiplier:.byte %01000000 ; Clock x16 (300-57600bps, WR4, ref page 5-8)
.byte %10000000 ; Clock x32 (115200bps, ref page 5-8)
ClockSource: .byte %01010000 ; Use baud rate generator (ch. B) (WR11, page 5-17)
.byte %00000000 ; Use RTxC (115200bps) (ch. B)
.byte %11010000 ; Use baud rate generator (ch. A)
.byte %10000000 ; Use RTxC (115200bps) (ch. A)
BrgEnabled: .byte %00000001 ; Baud rate generator on (WR14, page 5-19)
.byte %00000000 ; BRG Off
ChanIrqFlags: .byte %00000101 ; ANDed (RX/special IRQ, ch. B) (page 5-25)
.byte %00101000 ; ANDed (RX/special IRQ, ch. A)
ChanIrqMask: .byte %00000111 ; Ch. B IRQ flags mask
.byte %00111000 ; Ch. A IRQ flags mask
BaudTable: ; bit7 = 1 means setting is invalid
; Otherwise refers to the index in
; Baud(Low/High)Table
.byte $FF ; SER_BAUD_45_5
.byte $FF ; SER_BAUD_50
.byte $FF ; SER_BAUD_75
.byte $FF ; SER_BAUD_110
.byte $FF ; SER_BAUD_134_5
.byte $FF ; SER_BAUD_150
.byte $00 ; SER_BAUD_300
.byte $FF ; SER_BAUD_600
.byte $01 ; SER_BAUD_1200
.byte $FF ; SER_BAUD_1800
.byte $02 ; SER_BAUD_2400
.byte $FF ; SER_BAUD_3600
.byte $03 ; SER_BAUD_4800
.byte $FF ; SER_BAUD_7200
.byte $04 ; SER_BAUD_9600
.byte $05 ; SER_BAUD_19200
.byte $06 ; SER_BAUD_38400
.byte $07 ; SER_BAUD_57600
.byte $FF ; SER_BAUD_115200
.byte $FF ; SER_BAUD_230400
; Indexes cc65 RS232 SER_BAUD enum
; into WR12/13 register values
; (Ref page 5-18 and 5-19)
.word $FFFF ; SER_BAUD_45_5
.word $FFFF ; SER_BAUD_50
.word $FFFF ; SER_BAUD_75
.word $FFFF ; SER_BAUD_110
.word $FFFF ; SER_BAUD_134_5
.word $FFFF ; SER_BAUD_150
.word $017E ; SER_BAUD_300
.word $FFFF ; SER_BAUD_600
.word $005E ; SER_BAUD_1200
.word $FFFF ; SER_BAUD_1800
.word $002E ; SER_BAUD_2400
.word $FFFF ; SER_BAUD_3600
.word $0016 ; SER_BAUD_4800
.word $FFFF ; SER_BAUD_7200
.word $000A ; SER_BAUD_9600
.word $0004 ; SER_BAUD_19200
.word $0001 ; SER_BAUD_38400
.word $0000 ; SER_BAUD_57600
.word $0000 ; SER_BAUD_115200 (constant unused at that speed)
.word $FFFF ; SER_BAUD_230400
; About the speed selection: either we use the baud rate generator:
; - Load the time constants from BaudTable into WR12/WR13
; - Setup the TX/RX clock source to BRG (ClockSource into WR11)
; - Setup the clock multiplier (WR4)
; - Enable the baud rate generator (WR14)
; In this case, the baud rate will be:
; rate = crystal_clock/(2+BRG_time_constant))/(2*clock_multiplier)
; Example: (3686400/(2+0x0004)) / (2*16) = 19200 bps
;
; Or we don't use the baud rate generator:
; - Setup the TX/RX clock source to RTxC
; - Setup the clock multiplier
; - Disable the baud rate generator
; - WR12 and 13 are ignored
; In this case, the baud rate will be:
; rate = crystal_clock/clock_multiplier
; Example: 3686400/32 = 115200 bps
StopTable: .byte %00000100 ; SER_STOP_1, in WR_TX_RX_CTRL (WR4)
.byte %00001100 ; SER_STOP_2 (Ref page 5-8)
@ -156,6 +174,7 @@ SER_FLAG := $E10104
; ------------------------------------------------------------------------
; Channels
CHANNEL_B = 0
CHANNEL_A = 1
@ -180,7 +199,6 @@ RX_CTRL_OFF = %11111110 ; ANDed,Rx disabled
WR_TX_RX_CTRL = 4
RR_TX_RX_STATUS = 4
TX_RX_CLOCK_MUL = %01000000 ; Clock x16 (Ref page 5-8)
WR_TX_CTRL = 5 ; (Ref page 5-9)
RR_TX_STATUS = 5 ; Corresponding status register
@ -197,15 +215,11 @@ MASTER_IRQ_MIE_RST = %00001010 ; STA'd
MASTER_IRQ_SET = %00011001 ; STA'd
WR_CLOCK_CTRL = 11 ; (Ref page 5-17)
CLOCK_CTRL_CH_A = %11010000
CLOCK_CTRL_CH_B = %01010000
WR_BAUDL_CTRL = 12 ; (Ref page 5-18)
WR_BAUDH_CTRL = 13 ; (Ref page 5-19)
WR_MISC_CTRL = 14 ; (Ref page 5-19)
MISC_CTRL_RATE_GEN_ON = %00000001 ; ORed
MISC_CTRL_RATE_GEN_OFF = %11111110 ; ANDed
WR_IRQ_CTRL = 15 ; (Ref page 5-20)
IRQ_CLEANUP_EIRQ = %00001000
@ -220,13 +234,8 @@ IRQ_RX = %00100000
IRQ_SPECIAL = %01100000
RR_INTR_PENDING_STATUS = 3 ; (Ref page 5-25)
INTR_PENDING_RX_EXT_A = %00101000 ; ANDed (RX or special IRQ)
INTR_PENDING_RX_EXT_B = %00000101 ; ANDed (RX or special IRQ)
INTR_IS_RX = %00100100 ; ANDed (RX IRQ, channel A or B)
SER_FLAG_CH_A = %00111000
SER_FLAG_CH_B = %00000111
.code
; Read register value to A.
@ -329,6 +338,15 @@ IIgs:
: txa ; Promote char return value
rts
getClockSource:
.assert SER_PARAMS::BAUDRATE = 0, error
lda (ptr1) ; Baudrate index - cc65 value
cmp #SER_BAUD_115200
lda #$00
adc #$00
sta CurClockSource ; 0 = BRG, 1 = RTxC
rts
;----------------------------------------------------------------------------
; SER_OPEN: A pointer to a ser_params structure is passed in ptr1.
; Must return an SER_ERR_xx code in a/x.
@ -360,11 +378,13 @@ SER_OPEN:
ldy #RR_INIT_STATUS ; Hit rr0 once to sync up
jsr readSSCReg
ldy #WR_MISC_CTRL ; Turn everything off
ldy #WR_MISC_CTRL ; WR14: Turn everything off
lda #$00
jsr writeSCCReg
ldy #SER_PARAMS::STOPBITS
jsr getClockSource ; Should we use BRG or RTxC?
ldy #SER_PARAMS::STOPBITS ; WR4 setup: clock mult., stop & parity
lda (ptr1),y ; Stop bits
tay
lda StopTable,y ; Get value
@ -377,36 +397,33 @@ SER_OPEN:
ora ParityTable,y ; Get value
bmi InvParam
ora #TX_RX_CLOCK_MUL
ldy CurClockSource ; Clock multiplier
ora ClockMultiplier,y
ldy #WR_TX_RX_CTRL ; Setup stop & parity bits
jsr writeSCCReg
ldy #WR_TX_RX_CTRL
jsr writeSCCReg ; End of WR4 setup
ldy CurClockSource ; WR11 setup: clock source
cpx #CHANNEL_B
bne ClockA
ClockB:
beq SetClock
iny ; Shift to get correct ClockSource val
iny ; depending on our channel
SetClock:
lda ClockSource,y
ldy #WR_CLOCK_CTRL
lda #CLOCK_CTRL_CH_B
jsr writeSCCReg
jsr writeSCCReg ; End of WR11 setup
lda #INTR_PENDING_RX_EXT_B ; Store which IRQ bits we'll check
sta CurChanIrqFlags
bra SetBaud
ClockA:
ldy #WR_CLOCK_CTRL
lda #CLOCK_CTRL_CH_A
jsr writeSCCReg
lda #INTR_PENDING_RX_EXT_A ; Store which IRQ bits we'll check
lda ChanIrqFlags,x ; Store which IRQ bits we'll check
sta CurChanIrqFlags
SetBaud:
ldy #SER_PARAMS::BAUDRATE
lda (ptr1),y ; Baudrate index - cc65 value
.assert SER_PARAMS::BAUDRATE = 0, error
lda (ptr1) ; Baudrate index - cc65 value
asl
tay
lda BaudTable,y ; Get chip value from Low/High tables
lda BaudTable,y ; Get low byte of register value
bpl BaudOK ; Verify baudrate is supported
InvParam:
@ -415,59 +432,57 @@ InvParam:
bra SetupOut
BaudOK:
tay
lda BaudLowTable,y ; Get low byte
phy
ldy #WR_BAUDL_CTRL
jsr writeSCCReg
phy ; WR12 setup: BRG time constant, low byte
ldy #WR_BAUDL_CTRL ; Setting WR12 & 13 is useless if we're using
jsr writeSCCReg ; RTxC, but doing it anyway makes code smaller
ply
lda BaudHighTable,y ; Get high byte
iny
lda BaudTable,y ; WR13 setup: BRG time constant, high byte
ldy #WR_BAUDH_CTRL
jsr writeSCCReg
ldy CurClockSource ; WR14 setup: BRG enabling
lda BrgEnabled,y
ldy #WR_MISC_CTRL ; Time to turn this thing on
lda #MISC_CTRL_RATE_GEN_ON
jsr writeSCCReg
ldy #SER_PARAMS::DATABITS
lda (ptr1),y ; Data bits
ldy #SER_PARAMS::DATABITS ; WR3 setup: RX data bits
lda (ptr1),y
tay
lda RxBitTable,y ; Data bits for RX
ora #RX_CTRL_ON ; and turn RX on
lda RxBitTable,y
ora #RX_CTRL_ON ; and turn receiver on
phy
ldy #WR_RX_CTRL
jsr writeSCCReg
jsr writeSCCReg ; End of WR3 setup
ply
lda TxBitTable,y ; Data bits for TX
ora #TX_CTRL_ON ; and turn TX on
and #TX_DTR_ON
lda TxBitTable,y ; WR5 setup: TX data bits
ora #TX_CTRL_ON ; and turn transmitter on
and #TX_DTR_ON ; and turn DTR on
sta RtsOff ; Save value for flow control
ora #TX_RTS_ON
ora #TX_RTS_ON ; and turn RTS on
ldy #WR_TX_CTRL
jsr writeSCCReg
jsr writeSCCReg ; End of WR5 setup
ldy #WR_IRQ_CTRL
ldy #WR_IRQ_CTRL ; WR15 setup: IRQ
lda #IRQ_CLEANUP_EIRQ
jsr writeSCCReg
ldy #WR_INIT_CTRL ; Clear ext status (write twice)
ldy #WR_INIT_CTRL ; WR0 setup: clear existing IRQs
lda #INIT_CTRL_CLEAR_EIRQ
jsr writeSCCReg
jsr writeSCCReg ; Clear (write twice)
jsr writeSCCReg
ldy #WR_TX_RX_MODE_CTRL ; Activate RX IRQ
ldy #WR_TX_RX_MODE_CTRL ; WR1 setup: Activate RX IRQ
lda #TX_RX_MODE_RXIRQ
jsr writeSCCReg
lda SCCBREG ; Activate master IRQ
lda SCCBREG ; WR9 setup: Activate master IRQ
ldy #WR_MASTER_IRQ_RST
lda #MASTER_IRQ_SET
jsr writeSCCReg
@ -475,14 +490,7 @@ BaudOK:
lda SER_FLAG ; Get SerFlag's current value
sta SerFlagOrig ; and save it
cpx #CHANNEL_B
bne IntA
IntB:
ora #SER_FLAG_CH_B ; Inform firmware we want channel B IRQs
bra StoreFlag
IntA:
ora #SER_FLAG_CH_A ; Inform firmware we want channel A IRQs
StoreFlag:
ora ChanIrqMask,x ; Tell firmware which channel IRQs we want
sta SER_FLAG
ldy #$01 ; Mark port opened

View File

@ -121,7 +121,7 @@ BaudTable: ; Table used to translate RS232 baudrate param
.byte $0F ; SER_BAUD_19200
.byte $FF ; SER_BAUD_38400
.byte $FF ; SER_BAUD_57600
.byte $FF ; SER_BAUD_115200
.byte $00 ; SER_BAUD_115200
.byte $FF ; SER_BAUD_230400
BitTable: ; Table used to translate RS232 databits param
@ -302,6 +302,7 @@ HandshakeOK:
lda (ptr1),y ; Baudrate index
tay
lda BaudTable,y ; Get 6551 value
sta tmp2 ; Backup for IRQ setting
bpl BaudOK ; Check that baudrate is supported
lda #SER_ERR_BAUD_UNAVAIL
@ -332,8 +333,13 @@ BaudOK: sta tmp1
ora #%00000001 ; Set DTR active
sta RtsOff ; Store value to easily handle flow control later
ora #%00001000 ; Enable receive interrupts (RTS low)
sta ACIA_CMD,x
ora #%00001010 ; Disable interrupts and set RTS low
ldy tmp2 ; Don't enable IRQs if 115200bps
beq :+
and #%11111101 ; Enable receive IRQs
: sta ACIA_CMD,x
; Done
stx Index ; Mark port as open

View File

@ -0,0 +1,29 @@
;
; Colin Leroy-Mira <colin@colino.net>, 2024
;
; unsigned char __fastcall__ detect_iigs(unsigned char speed)
;
.export _set_iigs_speed
.import ostype, return0
.include "apple2.inc"
.include "accelerator.inc"
_set_iigs_speed:
tax ; Keep parameter
lda ostype ; Return if not IIgs
bmi :+
jmp return0
: lda CYAREG
cpx #SPEED_SLOW
beq :+
ora #%10000000
bne set_speed
: and #%01111111
set_speed:
sta CYAREG
txa
ldx #$00
rts

54
libsrc/apple2/sleep.s Normal file
View File

@ -0,0 +1,54 @@
;
; Colin Leroy-Mira <colin@colino.net>, 2024
;
; void __fastcall__ sleep(unsigned s)
;
;
.export _sleep
.import _get_iigs_speed
.import _set_iigs_speed
.import WAIT
.importzp tmp1
.include "accelerator.inc"
; This functions uses the Apple2 WAIT ROM routine to waste a certain
; amount of cycles and returns approximately after the numbers of
; seconds passed in AX.
;
; It takes 1023730 cycles when called with AX=1 (1,0007s),
; 10236364 cycles when called with AX=10 (10,006 seconds),
; 306064298 cycles with AX=300 (299.2 seconds).
;
; Caveat: IRQs firing during calls to sleep will make the sleep longer
; by the amount of cycles it takes to handle the IRQ.
;
_sleep:
stx tmp1 ; High byte of s in X
tay ; Low byte in A
ora tmp1
bne :+
rts
: jsr _get_iigs_speed ; Save current CPU speed
pha
lda #SPEED_SLOW ; Down to 1MHz for consistency around WAIT
jsr _set_iigs_speed
sleep_1s:
ldx #$0A ; Loop 10 times
sleep_100ms:
lda #$C7 ; Sleep about 99ms
jsr WAIT
lda #$0D ; About 1ms
jsr WAIT
dex
bne sleep_100ms
dey
bne sleep_1s
dec tmp1
bmi done
dey ; Down to #$FF
bne sleep_1s
done:
pla ; Restore CPU speed
jmp _set_iigs_speed

View File

@ -6,7 +6,7 @@
.export _statvfs
.import _dio_query_sectsize
.import mli_file_info, pushax, popax, popptr1
.import mli_file_info, pushax, popax, popptr1, pushptr1
.include "zeropage.inc"
.include "apple2.inc"
.include "errno.inc"
@ -45,9 +45,7 @@ _statvfs:
sty vol_sep ; Register '/' index
lda #$00
sta (ptr1),y ; Cut pathname at first slash
: lda ptr1
ldx ptr1+1
jsr pushax
: jsr pushptr1
jsr mli_file_info

20
libsrc/apple2/wait.s Normal file
View File

@ -0,0 +1,20 @@
;
; Colin Leroy-Mira, 2024
;
; WAIT routine
;
.export WAIT
.include "apple2.inc"
.segment "LOWCODE"
WAIT:
; Switch in ROM and call WAIT
bit $C082
jsr $FCA8 ; Vector to WAIT routine
; Switch in LC bank 2 for R/O and return
bit $C080
rts

View File

@ -5,21 +5,11 @@
;
.ifdef __APPLE2ENH__
.constructor initvsync
.export _waitvsync
.import _get_ostype
.import ostype
.include "apple2.inc"
.segment "ONCE"
initvsync:
jsr _get_ostype
sta ostype
rts
.code
_waitvsync:
bit ostype
bmi iigs ; $8x
@ -53,8 +43,4 @@ iic: sei
cli
rts
.segment "INIT"
ostype: .res 1
.endif ; __APPLE2ENH__

View File

@ -19,7 +19,7 @@
.import findfreeiocb
.import incsp4
.import ldaxysp,addysp
.import ___oserror
.import ___oserror, returnFFFF
.ifdef UCASE_FILENAME
.import ucase_fn
.endif
@ -39,9 +39,7 @@ parmok: jsr findfreeiocb
lda #<EMFILE ; "too many open files"
seterr: jsr ___directerrno
jsr incsp4 ; clean up stack
lda #$FF
tax
rts ; return -1
jmp returnFFFF
; process the mode argument

View File

@ -40,7 +40,7 @@
.export _cbm_read
.importzp ptr1, ptr2, ptr3, tmp1
.import popax, popa
.import popax, popa, returnFFFF
.import ___oserror
@ -107,7 +107,4 @@ _cbm_read:
; CHKIN failed
@E1: sta ___oserror
lda #$FF
tax
rts ; return -1
jmp returnFFFF

View File

@ -32,7 +32,7 @@
.export _cbm_write
.importzp ptr1, ptr2, ptr3
.import popax, popa
.import popax, popa, returnFFFF
.import ___oserror
@ -88,7 +88,4 @@ _cbm_write:
; Error entry, error code is in A
@E2: sta ___oserror
lda #$FF
tax
rts ; return -1
jmp returnFFFF

View File

@ -1,22 +0,0 @@
/*
** _is_leap_year.h
**
** (C) Copyright 2024, Colin Leroy-Mira <colin@colino.net>
**
*/
#ifndef __IS_LEAP_YEAR_H
#define __IS_LEAP_YEAR_H
unsigned char __fastcall__ IsLeapYear (unsigned char Year);
/* Returns 1 if the given year is a leap year. Expects a year from 0 to 206,
* without 1900 added */
/* End of _is_leap_year.h */
#endif

View File

@ -1,23 +0,0 @@
;
; Colin Leroy-Mira, 2024
;
; unsigned char __fastcall__ IsLeapYear (unsigned char Year)
; Returns 1 in A if the given year is a leap year. Expects a year from 0 to 206,
; without 1900 added.
;
.export _IsLeapYear
_IsLeapYear:
ldx #$00 ; Prepare X for rts
cmp #$00 ; Y 0 (1900) is not a leap year
beq NotLeap
cmp #$C8 ; Y 200 (2100) is not a leap year
beq NotLeap
and #$03 ; Year % 4 == 0 means leap year
bne NotLeap
lda #$01 ; Return 1
rts
NotLeap:
lda #$00 ; Return 0
rts

View File

@ -13,6 +13,7 @@
.import _strlower, _strlen
.macpack generic
.macpack cpu
; ----------------------------------------------------------------------------
; We will store variables into the register bank in the zeropage. Define
@ -37,7 +38,11 @@ FCount = ptr2
GetFormatChar:
ldy #0
.if (.cpu .bitand ::CPU_ISET_65SC02)
lda (Format)
.else
lda (Format),y
.endif
IncFormatPtr:
inc Format
bne @L1
@ -110,7 +115,11 @@ GetIntArg:
lda (ArgList),y
tax
dey
.if (.cpu .bitand ::CPU_ISET_65SC02)
lda (ArgList)
.else
lda (ArgList),y
.endif
rts
; ----------------------------------------------------------------------------
@ -135,9 +144,9 @@ ReadInt:
pha ; Save digit value
lda ptr1
ldx ptr1+1
asl ptr1
asl a
rol ptr1+1 ; * 2
asl ptr1
asl a
rol ptr1+1 ; * 4, assume carry clear
adc ptr1
sta ptr1
@ -265,10 +274,16 @@ Save: lda regbank,y
; Initialize the output counter in the output descriptor to zero
lda #0
.if (.cpu .bitand ::CPU_ISET_65SC02)
sta (OutData)
ldy #$01
sta (OutData),y
.else
tay
sta (OutData),y
iny
sta (OutData),y
.endif
; Get the output function from the output descriptor and remember it
@ -338,7 +353,11 @@ MainLoop:
sta (sp),y
dey
lda FCount
.if (.cpu .bitand ::CPU_ISET_65SC02)
sta (sp)
.else
sta (sp),y
.endif
jsr CallOutFunc ; Call the output function
; We're back from out(), or we didn't call it. Check for end of string.
@ -551,10 +570,16 @@ CheckCount:
jsr GetIntArg
sta ptr1
stx ptr1+1 ; Get user supplied pointer
.if (.cpu .bitand ::CPU_ISET_65SC02)
lda (OutData) ; Low byte of OutData->ccount
sta (ptr1)
ldy #1
.else
ldy #0
lda (OutData),y ; Low byte of OutData->ccount
sta (ptr1),y
iny
.endif
lda (OutData),y ; High byte of OutData->ccount
sta (ptr1),y
jmp MainLoop ; Done

View File

@ -1,64 +0,0 @@
/*****************************************************************************/
/* */
/* gmtime.c */
/* */
/* Convert calendar time into broken down time in UTC */
/* */
/* */
/* */
/* (C) 2002 Ullrich von Bassewitz */
/* Wacholderweg 14 */
/* D-70597 Stuttgart */
/* EMail: uz@musoftware.de */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#include <time.h>
/*****************************************************************************/
/* Code */
/*****************************************************************************/
struct tm* __fastcall__ _time_t_to_tm (const time_t t)
{
static struct tm timebuf;
/* Since our ints are just 16 bits, split the given time into seconds,
** hours and days. Each of the values will fit in a 16 bit variable.
** The mktime routine will then do the rest.
*/
timebuf.tm_sec = t % 3600;
timebuf.tm_min = 0;
timebuf.tm_hour = (t / 3600) % 24;
timebuf.tm_mday = (t / (3600UL * 24UL)) + 1;
timebuf.tm_mon = 0;
timebuf.tm_year = 70; /* Base value is 1/1/1970 */
/* Call mktime to do the final conversion */
mktime (&timebuf);
/* Return the result */
return &timebuf;
}

View File

@ -0,0 +1,129 @@
;
; Colin Leroy-Mira, 2024
;
; struct tm* __fastcall__ _time_t_to_tm (const time_t t)
;
; Helper to gmtime and localtime. Breaks down a number of
; seconds since Jan 1, 1970 into days, hours and seconds,
; so that each of them fits in 16 bits; passes the
; result to _mktime which fixes all values in the struct,
; and returns a pointer to the struct to callers.
;
.export __time_t_to_tm
.import udiv32, _mktime
.importzp sreg, tmp3, ptr1, ptr2, ptr3, ptr4
.include "time.inc"
.macpack cpu
__time_t_to_tm:
; Divide number of seconds since epoch, in ptr1:sreg,
; by 86400 to get the number of days since epoch, and
; the number of seconds today in the remainder.
; Load t as dividend (sreg is already set by the caller)
sta ptr1
stx ptr1+1
; Load 86400 as divisor
lda #$80
sta ptr3
lda #$51
sta ptr3+1
lda #$01
sta ptr4
lda #$00
sta ptr4+1
; Clear TM buf while we have zero in A
ldx #.sizeof(tm)-1
: sta TM,x
dex
bpl :-
; Divide t/86400
jsr udiv32
; Store the quotient (the number of full days), and increment
; by one as epoch starts at day 1.
clc
lda ptr1
adc #1
sta TM + tm::tm_mday
lda ptr1+1
adc #0
sta TM + tm::tm_mday+1
; Now divide the number of remaining seconds by 3600,
; to get the number of hours, and the seconds in the
; current hour, in neat 16-bit integers.
; Load the previous division's remainder (in ptr2:tmp3:tmp4)
; as dividend
lda ptr2
sta ptr1
lda ptr2+1
sta ptr1+1
lda tmp3
sta sreg
; We ignore the high byte stored in tmp4 because it will be
; zero. We'll zero sreg+1 right below, when we'll have
; a convenient zero already in A.
; Load divisor
lda #<3600
sta ptr3
lda #>3600
sta ptr3+1
; Zero the two high bytes of the divisor and the high byte
; of the dividend.
.if .cpu .bitand CPU_ISET_65SC02
stz ptr4
stz ptr4+1
stz sreg+1
.else
lda #$00
sta ptr4
sta ptr4+1
sta sreg+1
.endif
; Do the division
jsr udiv32
; Store year
lda #70
sta TM + tm::tm_year
; Store hours (the quotient of the last division)
lda ptr1
sta TM + tm::tm_hour
lda ptr1+1
sta TM + tm::tm_hour+1
; Store seconds (the remainder of the last division)
lda ptr2
sta TM + tm::tm_sec
lda ptr2+1
sta TM + tm::tm_sec+1
; The rest of the struct tm fields are zero. mktime
; will take care of shifting extra seconds to minutes,
; and extra days to months and years.
; Call mktime
lda #<TM
ldx #>TM
jsr _mktime
; And return our pointer
lda #<TM
ldx #>TM
rts
.bss
TM: .tag tm

View File

@ -1,59 +0,0 @@
/*****************************************************************************/
/* */
/* asctime.c */
/* */
/* Convert a broken down time into a string */
/* */
/* */
/* */
/* (C) 2002 Ullrich von Bassewitz */
/* Wacholderweg 14 */
/* D-70597 Stuttgart */
/* EMail: uz@musoftware.de */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#include <stdio.h>
#include <time.h>
/*****************************************************************************/
/* Code */
/*****************************************************************************/
/*
CAUTION: we need to reserve enough space to be able to hold the maximum
length string:
1234567890123456789012345678901234567
"Wednesday September ..1 00:00:00 1970"
*/
char* __fastcall__ asctime (const struct tm* timep)
{
static char buf[38];
/* Format into given buffer and return the result */
return strftime (buf, sizeof (buf), "%c\n", timep)? buf : 0;
}

81
libsrc/common/asctime.s Normal file
View File

@ -0,0 +1,81 @@
;
; Colin Leroy-Mira, 2024
;
; char* __fastcall__ asctime (const struct tm* timep)
;
.export _asctime
.import _strftime, pushax
.importzp ptr1
.include "time.inc"
.macpack cpu
; ------------------------------------------------------------------------
; Special values
; We need to be able to store up to 38 bytes:
; 1234567890123456789012345678901234567
; "Wednesday September ..1 00:00:00 1970"
MAX_BUF_LEN = 38
; ------------------------------------------------------------------------
; Code
_asctime:
; Backup timep
.if (.cpu .bitand ::CPU_ISET_65SC02)
pha
phx
.else
sta ptr1
stx ptr1+1
.endif
; Push buf
lda #<buf
ldx #>buf
jsr pushax
; Push sizeof(buf)
lda #<MAX_BUF_LEN
ldx #>MAX_BUF_LEN
jsr pushax
; Push format string
lda #<fmt
ldx #>fmt
jsr pushax
; Restore timep
.if (.cpu .bitand ::CPU_ISET_65SC02)
plx
pla
.else
lda ptr1
ldx ptr1+1
.endif
; Call formatter
jsr _strftime
; Check return status
bne :+
cpx #$00
bne :+
rts
: lda #<buf
ldx #>buf
rts
.data
fmt: .byte '%'
.byte 'c'
.byte $0A
.byte $00
.bss
buf: .res MAX_BUF_LEN

View File

@ -0,0 +1,24 @@
;
; Colin Leroy-Mira, 2024
;
; Helper to check for file opened, not eof, not ferror
; Expects file pointer in ptr1,
; Returns with Z flag set if everything is OK,
; Destroys A, X, Y,
; Sets file flags in A
;
.export checkferror
.importzp ptr1
.include "_file.inc"
checkferror:
ldy #_FILE::f_flags
lda (ptr1),y
tax
and #(_FOPEN|_FERROR|_FEOF); Check for file open, error/eof
tay
txa
cpy #_FOPEN
rts

View File

@ -3,7 +3,7 @@
; 2002-10-22, Greg King
;
; This signed-division function returns both the quotient and the remainder,
; in this structure:
; in this structure: (quotient in sreg, remainder in AX)
;
; typedef struct {
; int rem, quot;

View File

@ -7,7 +7,7 @@
.export _fclose
.import _close
.import _close, ___directerrno
.importzp ptr1
.include "errno.inc"
@ -31,10 +31,7 @@
; File is not open
lda #EINVAL
jsr ___seterrno
lda #$FF ; Return -1
tax
rts
jmp ___directerrno
; File is open. Reset the flags and close the file.
@ -47,4 +44,3 @@
jmp _close ; Will set errno and return an error flag
.endproc

View File

@ -1,58 +0,0 @@
/*
** fgetc.c
**
** (C) Copyright 1998, 2002 Ullrich von Bassewitz (uz@cc65.org)
**
*/
#include <stdio.h>
#include <unistd.h>
#include "_file.h"
/*****************************************************************************/
/* Code */
/*****************************************************************************/
int __fastcall__ fgetc (register FILE* f)
{
unsigned char c;
/* Check if the file is open or if there is an error condition */
if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) {
return EOF;
}
/* If we have a pushed back character, return it */
if (f->f_flags & _FPUSHBACK) {
f->f_flags &= ~_FPUSHBACK;
return f->f_pushback;
}
/* Read one byte */
switch (read (f->f_fd, &c, 1)) {
case -1:
/* Error */
f->f_flags |= _FERROR;
return EOF;
case 0:
/* EOF */
f->f_flags |= _FEOF;
return EOF;
default:
/* Char read */
return c;
}
}

94
libsrc/common/fgetc.s Normal file
View File

@ -0,0 +1,94 @@
;
; Colin Leroy-Mira, 2024
;
; int __fastcall__ fgetc (register FILE* f)
;
.export _fgetc
.import _read, checkferror
.import pusha0, pushax, popptr1, incsp2, returnFFFF
.importzp ptr1
.include "stdio.inc"
.include "_file.inc"
.macpack cpu
_fgetc:
sta ptr1
stx ptr1+1
jsr pushax ; Backup our ptr
jsr checkferror
bne ret_eof
.if (.cpu .bitand ::CPU_ISET_65SC02)
bit #_FPUSHBACK ; Check for pushed back char
beq do_read
.else
tax
and #_FPUSHBACK ; Check for pushed back char
beq do_read
txa
.endif
and #<(~_FPUSHBACK) ; Reset flag
sta (ptr1),y
.assert _FILE::f_pushback = _FILE::f_flags+1, error
iny
jsr incsp2 ; Drop our ptr copy
lda (ptr1),y ; Return pushed back char
ldx #$00
rts
do_read:
; Push _read parameters
ldy #_FILE::f_fd
lda (ptr1),y
jsr pusha0
lda #<c
ldx #>c
jsr pushax
lda #$01
ldx #$00
; Read
jsr _read
; Check for errors
cmp #$00
beq set_feof
cmp #<(-1)
beq set_ferror
jsr incsp2
; Return char
ldx #$00
lda c
rts
ret_eof:
jsr incsp2
jmp returnFFFF
set_ferror:
lda #_FERROR
bne set_err
set_feof:
lda #_FEOF
set_err:
pha
jsr popptr1
pla
ldy #_FILE::f_flags
ora (ptr1),y
sta (ptr1),y
jmp returnFFFF
.bss
c: .res 1

View File

@ -1,68 +0,0 @@
/*
** Ullrich von Bassewitz, 11.08.1998
**
** char* fgets (char* s, int size, FILE* f);
*/
#include <stdio.h>
#include <errno.h>
#include "_file.h"
/*****************************************************************************/
/* Code */
/*****************************************************************************/
char* __fastcall__ fgets (char* s, unsigned size, register FILE* f)
{
register char* p = s;
unsigned i;
int c;
if (size == 0) {
/* Invalid size */
return (char*) _seterrno (EINVAL);
}
/* Read input */
i = 0;
while (--size) {
/* Get next character */
if ((c = fgetc (f)) == EOF) {
/* Error or EOF */
if ((f->f_flags & _FERROR) != 0 || i == 0) {
/* ERROR or EOF on first char */
*p = '\0';
return 0;
} else {
/* EOF with data already read */
break;
}
}
/* One char more */
*p = c;
++p;
++i;
/* Stop at end of line */
if ((char)c == '\n') {
break;
}
}
/* Terminate the string */
*p = '\0';
/* Done */
return s;
}

117
libsrc/common/fgets.s Normal file
View File

@ -0,0 +1,117 @@
;
; Colin Leroy-Mira, 2024
;
; char* __fastcall__ fgets (char* s, unsigned size, register FILE* f)
;
.export _fgets
.import _fgetc, popptr1, pushptr1, popax, pushax, return0, ___errno
.importzp ptr1, ptr4
.include "errno.inc"
.include "stdio.inc"
.include "_file.inc"
.macpack cpu
terminate_ptr:
lda #$00
tax
.if (.cpu .bitand ::CPU_ISET_65SC02)
sta (ptr4)
.else
tay
sta (ptr4),y
.endif
rts
_fgets:
sta ptr1
stx ptr1+1
jsr popax
sta size
stx size+1
jsr popax
sta ptr4
stx ptr4+1
sta buf
stx buf+1
.if (.cpu .bitand ::CPU_ISET_65SC02)
stz didread
.else
lda #$00 ; We have read nothing yet
sta didread
.endif
; Check size
lda size
ora size+1
bne read_loop
lda #EINVAL
sta ___errno
jmp return0
read_loop:
lda size ; Dec size
bne :+
dec size+1
: dec size
bne :+ ; Check bound
ldx size+1
beq done
: jsr pushptr1 ; Push ptr1 for backup and load it to AX for fgetc
jsr _fgetc ; Read a char
pha
jsr popptr1 ; Get ptr1 back
pla
cpx #<EOF
beq got_eof
ldy #$01
sty didread ; We read at least one char
.if (.cpu .bitand ::CPU_ISET_65SC02)
sta (ptr4)
.else
dey
sta (ptr4),y
.endif
inc ptr4
bne :+
inc ptr4+1
: cmp #$0A ; Stop at \n
beq done
bne read_loop
got_eof:
lda didread
beq stopped_at_first_char
ldy #_FILE::f_flags
lda (ptr1),y
and #_FERROR
bne stopped_at_first_char
done:
jsr terminate_ptr
ldx #>buf
lda #<buf
rts
stopped_at_first_char:
jmp terminate_ptr
.bss
c: .res 1
buf: .res 2
size: .res 2
didread:.res 1

View File

@ -5,7 +5,7 @@
;
.export _clearerr, _feof, _ferror, _fileno, _fflush
.import return0
.import return0, ___directerrno
.importzp ptr1
.include "_file.inc"
@ -78,10 +78,7 @@ err: rts
; If the file is not valid, fileno must set errno and return -1
error: lda #<EBADF
jsr ___seterrno
lda #$FF
tax
rts
jmp ___directerrno
.endproc
;
@ -89,5 +86,3 @@ error: lda #<EBADF
;
_fflush = return0

View File

@ -1,41 +0,0 @@
/*
** fputc.c
**
** Ullrich von Bassewitz, 02.06.1998
*/
#include <stdio.h>
#include <unistd.h>
#include "_file.h"
/*****************************************************************************/
/* Code */
/*****************************************************************************/
int __fastcall__ fputc (int c, register FILE* f)
{
/* Check if the file is open or if there is an error condition */
if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) {
goto ReturnEOF;
}
/* Write the byte */
if (write (f->f_fd, &c, 1) != 1) {
/* Error */
f->f_flags |= _FERROR;
ReturnEOF:
return EOF;
}
/* Return the byte written */
return c & 0xFF;
}

66
libsrc/common/fputc.s Normal file
View File

@ -0,0 +1,66 @@
;
; Colin Leroy-Mira, 2024
;
; int __fastcall__ fputc (int c, FILE* f);
;
.export _fputc
.importzp ptr1
.import _write, checkferror
.import pushax, pusha0, popax, incsp2
.import pushptr1, popptr1, returnFFFF
.include "stdio.inc"
.include "_file.inc"
_fputc:
sta ptr1
stx ptr1+1
jsr popax ; Get char, as we'll have
sta c ; to return it anyway
stx c+1
jsr checkferror
bne ret_eof
jsr pushptr1 ; Backup fp pointer
; Push _write parameters
ldy #_FILE::f_fd
lda (ptr1),y
jsr pusha0
lda #<c
ldx #>c
jsr pushax
lda #$01
ldx #$00
; Write
jsr _write
; Check for errors
cmp #$01
bne set_ferror
; Return char
lda c
ldx #$00
jmp incsp2 ; Drop fp pointer copy
ret_eof:
jmp returnFFFF
set_ferror:
jsr popptr1
lda #_FERROR
ldy #_FILE::f_flags
ora (ptr1),y
sta (ptr1),y
jmp returnFFFF
.bss
c: .res 2

View File

@ -1,28 +0,0 @@
/*
** int fputs (const char* s, FILE* f);
**
** Ullrich von Bassewitz, 11.08.1998
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "_file.h"
int __fastcall__ fputs (const char* s, register FILE* f)
{
/* Check if the file is open or if there is an error condition */
if ((f->f_flags & _FOPEN) == 0 || (f->f_flags & (_FERROR | _FEOF)) != 0) {
return EOF;
}
/* Write the string */
return write (f->f_fd, s, strlen (s));
}

36
libsrc/common/fputs.s Normal file
View File

@ -0,0 +1,36 @@
;
; Colin Leroy-Mira, 2024
;
; int __fastcall__ fputs (const char* s, register FILE* f)
;
.export _fputs
.importzp ptr1, ptr2
.import _write, _strlen, checkferror
.import swapstk, pushax, returnFFFF
.include "stdio.inc"
.include "_file.inc"
_fputs:
sta ptr1
stx ptr1+1
jsr checkferror
bne ret_eof
; Push _write parameters
ldy #_FILE::f_fd
lda (ptr1),y
ldx #$00
jsr swapstk ; Push fd, get s
jsr pushax ; Push s
jsr _strlen ; Get length
; Write
jmp _write
ret_eof:
jmp returnFFFF

View File

@ -20,6 +20,7 @@
.include "_file.inc"
.macpack generic
.macpack cpu
; ------------------------------------------------------------------------
; Code
@ -47,13 +48,21 @@
ldy #_FILE::f_flags
lda (file),y
.if (.cpu .bitand ::CPU_ISET_65SC02)
bit #_FOPEN ; Is the file open?
.else
and #_FOPEN ; Is the file open?
.endif
beq @L1 ; Branch if no
; Check if the stream is in an error state
.if (.cpu .bitand ::CPU_ISET_65SC02)
bit #_FERROR
.else
lda (file),y ; get file->f_flags again
and #_FERROR
.endif
beq @L2
; File not open or in error state
@ -65,11 +74,19 @@
; Remember if we have a pushed back character and reset the flag.
@L2: tax ; X = 0
@L2: .if (.cpu .bitand ::CPU_ISET_65SC02)
ldx #$00
bit #_FPUSHBACK
.else
tax ; X = 0
lda (file),y
and #_FPUSHBACK
.endif
beq @L3
.if (.not .cpu .bitand ::CPU_ISET_65SC02)
lda (file),y
.endif
and #<~_FPUSHBACK
sta (file),y ; file->f_flags &= ~_FPUSHBACK;
inx ; X = 1
@ -118,12 +135,20 @@
; Copy the buffer pointer into ptr1, and increment the pointer value passed
; to read() by one, so read() starts to store data at buf+1.
.if (.cpu .bitand ::CPU_ISET_65SC02)
lda (sp)
sta ptr1
add #1
sta (sp)
ldy #1
.else
ldy #0
lda (sp),y
sta ptr1
add #1
sta (sp),y
iny
.endif
lda (sp),y
sta ptr1+1
adc #0
@ -134,8 +159,12 @@
ldy #_FILE::f_pushback
lda (file),y
.if (.cpu .bitand ::CPU_ISET_65SC02)
sta (ptr1) ; *buf = file->f_pushback;
.else
ldy #0
sta (ptr1),y ; *buf = file->f_pushback;
.endif
; Restore the low byte of count and decrement count by one. This may result
; in count being zero, so check for that.
@ -210,4 +239,3 @@
.bss
save: .res 2
pb: .res 1

View File

@ -8,7 +8,7 @@
.export _fwrite
.import _write
.import pushax, incsp6, addysp, ldaxysp, pushwysp, return0
.import pushax, pusha0, incsp6, addysp, ldaxysp, pushwysp, return0
.import tosumulax, tosudivax
.importzp ptr1
@ -16,6 +16,7 @@
.include "errno.inc"
.include "_file.inc"
.macpack cpu
; ------------------------------------------------------------------------
; Code
@ -33,7 +34,11 @@
ldy #_FILE::f_flags
lda (ptr1),y
.if (.cpu .bitand ::CPU_ISET_65SC02)
bit #_FOPEN
.else
and #_FOPEN ; Is the file open?
.endif
bne @L2 ; Branch if yes
; File not open
@ -45,7 +50,9 @@
; Check if the stream is in an error state
@L2: lda (ptr1),y ; get file->f_flags again
@L2: .if (.not .cpu .bitand ::CPU_ISET_65SC02)
lda (ptr1),y ; get file->f_flags again
.endif
and #_FERROR
bne @L1
@ -53,8 +60,7 @@
ldy #_FILE::f_fd
lda (ptr1),y
ldx #$00
jsr pushax ; file->f_fd
jsr pusha0 ; file->f_fd
ldy #9
jsr pushwysp ; buf
@ -123,4 +129,3 @@
.bss
file: .res 2

View File

@ -1,63 +0,0 @@
/*
** gets.c
**
** Ullrich von Bassewitz, 11.08.1998
*/
#include <stdio.h>
#include "_file.h"
/*****************************************************************************/
/* Code */
/*****************************************************************************/
char* __fastcall__ gets (char* s)
{
register char* p = s;
int c;
unsigned i = 0;
while (1) {
/* Get next character */
if ((c = fgetc (stdin)) == EOF) {
/* Error or EOF */
*p = '\0';
if (stdin->f_flags & _FERROR) {
/* ERROR */
return 0;
} else {
/* EOF */
if (i) {
return s;
} else {
return 0;
}
}
}
/* One char more. Newline ends the input */
if ((char) c == '\n') {
*p = '\0';
break;
} else {
*p = c;
++p;
++i;
}
}
/* Done */
return s;
}

47
libsrc/common/gets.s Normal file
View File

@ -0,0 +1,47 @@
;
; Colin Leroy-Mira, 2024
;
; char* __fastcall__ gets (char* s)
;
.export _gets
.import _fgets, _stdin, popax, pushax
.importzp ptr4
_gets:
; Push buffer
sta ptr4
stx ptr4+1
jsr pushax
; Push size (there's no limit!)
lda #$FF
tax
jsr pushax
lda _stdin
ldx _stdin+1
jsr _fgets
; Check return value
bne :+
cpx #$00
bne :+
rts
: ; At least one byte written.
jsr pushax ; Store returned pointer
; Remove \n if there is one.
lda ptr4 ; _fgets returns with ptr4 at
bne :+ ; end of buffer
dec ptr4+1
: dec ptr4
lda (ptr4),y ; _fgets returns with Y=0
cmp #$0A
bne :+
tya
sta (ptr4),y ; Set terminator over \n
: jmp popax

View File

@ -131,6 +131,7 @@ _malloc:
sta ptr1
bcc @L1
inc ptr1+1
beq OutOfHeapSpace ; if high byte's 0, we overflowed!
@L1: ldx ptr1+1
bne @L2
cmp #HEAP_MIN_BLOCKSIZE+1
@ -336,4 +337,3 @@ RetUserPtr:
bcc @L9
inx
@L9: rts

View File

@ -1,184 +0,0 @@
/*****************************************************************************/
/* */
/* mktime.c */
/* */
/* Make calendar time from broken down time and cleanup */
/* */
/* */
/* */
/* (C) 2002 Ullrich von Bassewitz */
/* Wacholderweg 14 */
/* D-70597 Stuttgart */
/* EMail: uz@musoftware.de */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#include <limits.h>
#include <stdlib.h>
#include <time.h>
#include "_is_leap_year.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
#define JANUARY 0
#define FEBRUARY 1
#define DECEMBER 11
#define JAN_1_1970 4 /* 1/1/1970 is a thursday */
static const unsigned char MonthLength [] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static const unsigned MonthDays [] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
};
/*****************************************************************************/
/* Code */
/*****************************************************************************/
time_t __fastcall__ mktime (register struct tm* TM)
/* Make a time in seconds since 1/1/1970 from the broken down time in TM.
** A call to mktime does also correct the time in TM to contain correct
** values.
*/
{
register div_t D;
static int Max;
static unsigned DayCount;
/* Check if TM is valid */
if (TM == 0) {
/* Invalid data */
return (time_t) -1L;
}
/* Adjust seconds. */
D = div (TM->tm_sec, 60);
TM->tm_sec = D.rem;
/* Adjust minutes */
TM->tm_min += D.quot;
D = div (TM->tm_min, 60);
TM->tm_min = D.rem;
/* Adjust hours */
TM->tm_hour += D.quot;
D = div (TM->tm_hour, 24);
TM->tm_hour = D.rem;
/* Adjust days */
TM->tm_mday += D.quot;
/* Adjust year */
while (1) {
Max = 365UL + IsLeapYear (TM->tm_year);
if ((unsigned int)TM->tm_mday > Max) {
++TM->tm_year;
TM->tm_mday -= Max;
} else {
break;
}
}
/* Adjust month and year. This is an iterative process, since changing
** the month will change the allowed days for this month.
*/
while (1) {
/* Make sure, month is in the range 0..11 */
D = div (TM->tm_mon, 12);
TM->tm_mon = D.rem;
TM->tm_year += D.quot;
/* Now check if mday is in the correct range, if not, correct month
** and eventually year and repeat the process.
*/
if (TM->tm_mon == FEBRUARY && IsLeapYear (TM->tm_year)) {
Max = 29;
} else {
Max = MonthLength[TM->tm_mon];
}
if ((unsigned int)TM->tm_mday > Max) {
/* Must correct month and eventually, year */
if (TM->tm_mon == DECEMBER) {
TM->tm_mon = JANUARY;
++TM->tm_year;
} else {
++TM->tm_mon;
}
TM->tm_mday -= Max;
} else {
/* Done */
break;
}
}
/* Ok, all time/date fields are now correct. Calculate the days in this
** year.
*/
TM->tm_yday = MonthDays[TM->tm_mon] + TM->tm_mday - 1;
if (TM->tm_mon > FEBRUARY && IsLeapYear (TM->tm_year)) {
++TM->tm_yday;
}
/* Calculate days since 1/1/1970. In the complete epoch (1/1/1970 to
** somewhere in 2106) all years dividable by 4 are leap years(1),
** so dividing by 4 gives the days that must be added because of leap years.
** (and the last leap year before 1970 was 1968)
** (1): Exception on 2100, which is not leap, and handled just after.
*/
DayCount = ((unsigned) (TM->tm_year-70)) * 365U +
(((unsigned) (TM->tm_year-(68+1))) / 4) +
TM->tm_yday;
/* Handle the 2100 exception */
if (TM->tm_year == 200 && TM->tm_mon > FEBRUARY) {
DayCount--;
} else if (TM->tm_year > 200) {
DayCount--;
}
/* Calculate the weekday */
TM->tm_wday = (JAN_1_1970 + DayCount) % 7;
/* No (US) daylight saving (for now) */
TM->tm_isdst = 0;
/* Return seconds since 1970 */
return DayCount * 86400UL +
((unsigned) TM->tm_hour) * 3600UL +
((unsigned) TM->tm_min) * 60U +
((unsigned) TM->tm_sec) -
_tz.timezone;
}

476
libsrc/common/mktime.s Normal file
View File

@ -0,0 +1,476 @@
;
; Colin Leroy-Mira, 2024
;
; time_t __fastcall__ mktime (register struct tm* TM)
;
; Converts a struct tm to a time_t timestamp, making sure
; day, month, year, hour, minute and seconds are in the
; correct range.
;
.export _mktime
.import __tz
.import pushax, pusha0, pusheax
.import shrax2, _div, tosumulax, tosumodax, tossubeax, tosaddeax, tosumuleax
.importzp ptr2, tmp3, sreg
.include "time.inc"
; ------------------------------------------------------------------------
; Special values
FEBRUARY = 1
MARCH = 2
JAN_1_1970 = 4
N_SEC = 60
N_MIN = 60
N_HOUR = 24
N_MON = 12
N_DAY_YEAR = 365
; ------------------------------------------------------------------------
; Helpers
; Helper to shift overflows from one field to the next
; Current field in Y, divisor in A
; Keeps remainder in current field, and adds the quotient
; to the next one
adjust_field:
pha ; Push divisor
iny ; Point to high byte of current field
lda (ptr2),y
tax
dey
sty tmp3 ; Store current field (_div will mess with
lda (ptr2),y ; tmp1 and tmp2)
jsr pushax
pla ; Load divisor
ldx #$00
jsr _div
ldy tmp3 ; Store remainder in current field
sta (ptr2),y
iny
txa
sta (ptr2),y
lda sreg ; Add quotient to next field
iny
clc
adc (ptr2),y
sta (ptr2),y
iny
lda sreg+1
adc (ptr2),y
sta (ptr2),y
rts
; Returns 1 in A if the given year is a leap year. Expects a year
; from 0 to 206, without 1900 added.
is_leap_year:
cmp #$00 ; Y 0 (1900) is not a leap year
beq not_leap
cmp #$C8 ; Y 200 (2100) is not a leap year
beq not_leap
and #$03 ; Year % 4 == 0 means leap year
bne not_leap
lda #$01 ; Return 1
rts
not_leap:
lda #$00 ; Return 0
rts
; Returns the number of days in the current month/year in A
get_days_in_month:
ldy #tm::tm_mon
lda (ptr2),y
tax
lda months_len,x
cpx #FEBRUARY
beq :+
rts
: tax
ldy #tm::tm_year ; Adjust for leap years
lda (ptr2),y
jsr is_leap_year
beq :+
inx
: txa
rts
; Add AX to counter
addaxcounter:
clc
adc Counter
sta Counter ; Store in Counter
txa
adc Counter+1
sta Counter+1
rts
; Helpers for long chain of arithmetic on day counter.
; Reload Counter and push it on the stack
load_and_push_counter:
lda Counter+3
sta sreg+1
lda Counter+2
sta sreg
lda Counter
ldx Counter+1
jsr pusheax
rts
; Store result in AX:sreg to Counter
store_counter:
sta Counter
stx Counter+1
lda sreg
sta Counter+2
lda sreg+1
sta Counter+3
rts
; ------------------------------------------------------------------------
; Code
_mktime:
sta ptr2 ; Store struct to ptr2, which arithmetic
stx ptr2+1 ; functions won't touch
; Check pointer validity
ora ptr2+1
bne :+
lda #$FF
tax
sta sreg
sta sreg+1
rts
; Adjust seconds
: ldy #tm::tm_sec
lda #N_SEC
jsr adjust_field
; Adjust minutes
ldy #tm::tm_min
lda #N_MIN
jsr adjust_field
; Adjust hours
ldy #tm::tm_hour
lda #N_HOUR
jsr adjust_field
;Shift one year as long as tm_mday is more than a year
ldy #tm::tm_year
lda (ptr2),y
dec_by_year:
jsr is_leap_year ; Compute max numbers of days in year
clc
adc #<N_DAY_YEAR ; No care about carry,
sta Max ; 365+1 doesn't overflow low byte
ldy #tm::tm_mday+1 ; Do we have more days in store?
lda (ptr2),y
cmp #>N_DAY_YEAR
beq :+ ; High byte equal, check low byte
bcs do_year_dec ; High byte greater, decrement
bcc dec_by_month ; Low byte lower, we're done
: dey
lda (ptr2),y
cmp Max
bcc dec_by_month
beq dec_by_month
do_year_dec:
; Decrement days
ldy #tm::tm_mday
lda (ptr2),y
sbc Max ; Carry already set
sta (ptr2),y
iny
lda (ptr2),y
sbc #>N_DAY_YEAR
sta (ptr2),y
; Increment year
ldy #tm::tm_year
lda (ptr2),y
clc
adc #1
sta (ptr2),y ; No carry possible here either
bcc dec_by_year ; bra, go check next year
dec_by_month:
; We're done decrementing days by full years, now do it
; month per month.
ldy #tm::tm_mon
lda #N_MON
jsr adjust_field
; Get max day for this month
jsr get_days_in_month
sta Max
; So, do we have more days than this month?
ldy #tm::tm_mday+1
lda (ptr2),y
bne do_month_dec ; High byte not zero, sure we do
dey
lda (ptr2),y
cmp Max
bcc calc_tm_yday ; No
beq calc_tm_yday
do_month_dec:
; Decrement days
ldy #tm::tm_mday
lda (ptr2),y
sec
sbc Max
sta (ptr2),y
iny
lda (ptr2),y
sbc #$00
sta (ptr2),y
; Increment month
ldy #tm::tm_mon
lda (ptr2),y
clc
adc #1
sta (ptr2),y
bne dec_by_month ; Check next month
calc_tm_yday:
; We finished decrementing tm_mday and have put it in the correct
; year/month range. Now compute the day of the year.
ldy #tm::tm_mday ; Get current day of month
lda (ptr2),y
sta Counter ; Store it in Counter
lda #$00 ; Init counter high bytes
sta Counter+1
sta Counter+2
sta Counter+3
ldy #tm::tm_mon ; Get current month
lda (ptr2),y
asl
tax
clc
lda yday_by_month,x ; Get yday for this month's start
adc Counter ; Add it to counter
sta Counter
inx
lda yday_by_month,x
adc Counter+1
sta Counter+1
ldy #tm::tm_year ; Adjust for leap years (if after feb)
lda (ptr2),y
jsr is_leap_year
beq dec_counter
ldy #tm::tm_mon ; Leap year, get current month
lda (ptr2),y
cmp #MARCH
bcs store_yday
dec_counter:
lda Counter ; Decrease counter by one (yday starts at 0),
bne :+ ; unless we're after february in a leap year
dec Counter+1
: dec Counter
store_yday:
ldy #tm::tm_yday ; Store tm_yday
lda Counter
sta (ptr2),y
iny
lda Counter+1
sta (ptr2),y
; Now calculate total day count since epoch with the formula:
; ((unsigned) (TM->tm_year-70)) * 365U + (number of days per year since 1970)
; (((unsigned) (TM->tm_year-(68+1))) / 4) + (one extra day per leap year since 1970)
; TM->tm_yday (number of days in this year)
ldy #tm::tm_year ; Get full years
lda (ptr2),y
sec
sbc #70
ldx #0
jsr pushax
lda #<N_DAY_YEAR
ldx #>N_DAY_YEAR
jsr tosumulax
jsr addaxcounter
; Add one day per leap year
ldy #tm::tm_year ; Get full years
lda (ptr2),y
sec
sbc #69
ldx #0
jsr shrax2 ; Divide by 4
jsr addaxcounter
; Handle the 2100 exception (which was considered leap by "Add one day
; per leap year" just before)
ldy #tm::tm_year ; Get full years
lda (ptr2),y
cmp #201
bcc finish_calc ; <= 200, nothing to do
lda Counter
bne :+
dec Counter+1
: dec Counter
finish_calc:
; Now we can compute the weekday.
lda Counter
clc
adc #JAN_1_1970
pha
lda Counter+1
adc #0
tax
pla
jsr pushax
lda #7 ; Modulo 7
ldx #0
jsr tosumodax
ldy #tm::tm_wday ; Store tm_wday
sta (ptr2),y
iny
txa
sta (ptr2),y
; DST
lda #$00 ; Store tm_isdst
ldy #tm::tm_isdst
sta (ptr2),y
iny
sta (ptr2),y
; Our struct tm is all fixed and every field calculated.
; We can finally count seconds according to this formula:
; seconds = (full days since epoch) * 86400UL +
; ((unsigned) TM->tm_hour) * 3600UL +
; ((unsigned) TM->tm_min) * 60U +
; ((unsigned) TM->tm_sec) -
; _tz.timezone;
; We already have the number of days since epoch in our counter,
; from just before when we computed tm_wday. Reuse it.
jsr load_and_push_counter
lda #$00 ; Multiply by 86400
sta sreg+1
lda #$01
sta sreg
lda #$80
ldx #$51
jsr tosumuleax
jsr store_counter ; Store into counter
; Push counter to add 3600 * hours to it
jsr load_and_push_counter
ldx #$00 ; Load hours
stx sreg
stx sreg+1
ldy #tm::tm_hour
lda (ptr2),y
jsr pusheax ; Push
ldx #$00 ; Load 3600
stx sreg
stx sreg+1
lda #<3600
ldx #>3600
jsr tosumuleax ; Multiply (pops the pushed hours)
jsr tosaddeax ; Add to counter (pops the pushed counter)
jsr store_counter ; Store counter
; Push counter to add 60 * min to it
jsr load_and_push_counter
ldy #tm::tm_min ; Load minutes
lda (ptr2),y
jsr pusha0 ; Push
lda #N_MIN
ldx #0
stx sreg
stx sreg+1
jsr tosumulax ; Multiply
jsr tosaddeax ; Add to pushed counter
jsr store_counter ; Store
; Add seconds
jsr load_and_push_counter
ldy #tm::tm_sec ; Load seconds
lda (ptr2),y
ldx #0
stx sreg
stx sreg+1
jsr tosaddeax ; Simple addition there
; No need to store/load/push the counter here, simply to push it
; for the last substraction
jsr pusheax
; Substract timezone
lda __tz+1+3
sta sreg+1
lda __tz+1+2
sta sreg
ldx __tz+1+1
lda __tz+1
jsr tossubeax
; And we're done!
rts
.data
months_len:
.byte 31
.byte 28
.byte 31
.byte 30
.byte 31
.byte 30
.byte 31
.byte 31
.byte 30
.byte 31
.byte 30
.byte 31
yday_by_month:
.word 0
.word 31
.word 59
.word 90
.word 120
.word 151
.word 181
.word 212
.word 243
.word 273
.word 304
.word 334
.bss
Max: .res 1 ; We won't need a high byte
Counter:
.res 4

View File

@ -50,7 +50,6 @@
*/
int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size)
/* Allocate a block of memory with the given "size", which is aligned to a
** memory address that is a multiple of "alignment". "alignment" MUST NOT be
@ -64,20 +63,27 @@ int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size)
size_t rawsize;
size_t uppersize;
size_t lowersize;
char err;
register struct usedblock* b; /* points to raw Block */
register struct usedblock* u; /* points to User block */
register struct usedblock* p; /* Points to upper block */
/* Handle requests for zero-sized blocks */
if (size == 0) {
err_einval:
err = EINVAL;
err_out:
*memptr = NULL;
return EINVAL;
return err;
}
/* Test alignment: is it a power of two? There must be only one bit set. */
if (alignment == 0 || (alignment & (alignment - 1)) != 0) {
*memptr = NULL;
return EINVAL;
/* Test alignment: is it a power of two? There must be one and only one bit set. */
if (alignment == 0) {
goto err_einval;
}
if (alignment & (alignment - 1)) {
goto err_einval;
}
/* Augment the block size up to the alignment, and allocate memory.
@ -90,8 +96,8 @@ int __fastcall__ posix_memalign (void** memptr, size_t alignment, size_t size)
/* Handle out-of-memory */
if (b == NULL) {
*memptr = NULL;
return ENOMEM;
err = ENOMEM;
goto err_out;
}
/* Create (and return) a new pointer that points to the user-visible

View File

@ -10,7 +10,7 @@
.import _malloc, _free
.import searchenv, copyenvptr
.import __environ, __envcount, __envsize
.import return0
.import return0, ___directerrno
.import ptr1:zp, ptr2:zp, ptr3:zp, tmp1:zp
.include "errno.inc"
@ -169,10 +169,7 @@ addentry:
; Error entries
nomem: lda #ENOMEM
error: jsr ___seterrno
lda #$FF ; Return -1
tax
rts
error: jmp ___directerrno
.endproc
@ -184,5 +181,3 @@ error: jsr ___seterrno
name: .addr 0 ; Pointer to name
newsize: .byte 0 ; New environment size

View File

@ -1,112 +0,0 @@
/*****************************************************************************/
/* */
/* realloc.c */
/* */
/* Change the size of an allocated memory block */
/* */
/* */
/* */
/* (C) 1998-2004 Ullrich von Bassewitz */
/* Wacholderweg 14 */
/* D-70597 Stuttgart */
/* EMail: uz@musoftware.de */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#include <stdlib.h>
#include <string.h>
#include <_heap.h>
void* __fastcall__ realloc (void* block, register size_t size)
{
register struct usedblock* b;
struct usedblock* newblock;
unsigned oldsize;
unsigned newhptr;
/* Check the block parameter */
if (!block) {
/* Block is NULL, same as malloc */
return malloc (size);
}
/* Check the size parameter */
if (size == 0) {
/* Block is not NULL, but size is: free the block */
free (block);
return 0;
}
/* Make the internal used size from the given size */
size += HEAP_ADMIN_SPACE;
if (size < sizeof (struct freeblock)) {
size = sizeof (struct freeblock);
}
/* The word below the user block contains a pointer to the start of the
** raw memory block. The first word of this raw memory block is the full
** size of the block. Get a pointer to the real block, get the old block
** size.
*/
b = (((struct usedblock*) block) - 1)->start;
oldsize = b->size;
/* Is the block at the current heap top? */
if (((unsigned) b) + oldsize == ((unsigned) __heapptr)) {
/* Check if we've enough memory at the heap top */
newhptr = ((unsigned) __heapptr) - oldsize + size;
if (newhptr <= ((unsigned) __heapend)) {
/* Ok, there's space enough */
__heapptr = (unsigned*) newhptr;
b->size = size;
b->start = b;
return block;
}
}
/* The given block was not located on top of the heap, or there's no
** room left. Try to allocate a new block and copy the data.
*/
if (newblock = malloc (size)) {
/* Adjust the old size to the user visible portion */
oldsize -= HEAP_ADMIN_SPACE;
/* If the new block is larger than the old one, copy the old
** data only
*/
if (size > oldsize) {
size = oldsize;
}
/* Copy the block data */
memcpy (newblock, block, size);
free (block);
}
return newblock;
}

213
libsrc/common/realloc.s Normal file
View File

@ -0,0 +1,213 @@
;
; Colin Leroy-Mira, 2024
;
; void* __fastcall__ realloc (void* block, register size_t size)
;
.importzp ptr1, ptr2, ptr3, ptr4, tmp1, tmp2, tmp3, tmp4, sp
.import _malloc, _memcpy, _free
.import pushax, popptr1, return0
.import incsp2, decsp2
.export _realloc
.include "_heap.inc"
.macpack generic
;----------------------------------------------------------------------------
; Aliases for clarity
block = ptr1
size = ptr2
ublock = ptr3
oldsize = ptr4
newblock = tmp1 ; (and tmp2)
orgblock = tmp3 ; (and tmp4)
;----------------------------------------------------------------------------
; Code
_realloc:
sta size ; Store size
stx size+1
jsr popptr1 ; Pop block
lda block+1 ; Is block null?
tax
ora block
bne :+
lda size ; Block is null, just malloc
ldx size+1
jmp _malloc
: lda size ; Is size 0?
ora size+1
bne :+
lda block ; It is: free block (high byte already in X)
jsr _free
jmp return0
: clc ; Add internal used size
lda size
adc #HEAP_ADMIN_SPACE
sta size
bcc :+
inc size+1
bne :+
lda #$00 ; Size high byte now 0: We overflowed!
tax
rts
: ldx size+1 ; Should we round size up?
bne :+
cmp #.sizeof (freeblock)
bcs :+
lda #.sizeof (freeblock)
sta size ; (we presuppose that sizeof (freeblock) is < 256)
: lda block ; Get pointer to raw memory block
sta orgblock ; Store original pointer
sec
sbc #.sizeof(usedblock)
sta ublock
lda block+1
sta orgblock+1 ; Finish storing original pointer
sbc #0
sta ublock+1 ; We have our usedblock struct
; Get block start
ldy #usedblock::start+1
lda (ublock),y
tax ; Backup ublock high
dey
lda (ublock),y
sta ublock ; Store ublock
stx ublock+1
; Remember oldsize
ldy #usedblock::size+1
lda (ublock),y
sta oldsize+1
dey
lda (ublock),y
sta oldsize
clc ; Is the block at heap top?
adc ublock
tay
lda ublock+1
adc oldsize+1
cmp ___heapptr+1
bne must_malloc_new
cpy ___heapptr
bne must_malloc_new
tya ; Put ___heapptr back in A
sec ; Check if we have enough memory at heap top
sbc oldsize ; Substract oldsize
sta newblock
lda ___heapptr+1
sbc oldsize+1
sta newblock+1
clc
lda newblock ; And add size
adc size
sta newblock
lda newblock+1
adc size+1
sta newblock+1
bcs must_malloc_new ; If we have a carry there we overflowed
cmp ___heapend+1
bne :+
lda newblock
cmp ___heapend
: bcc :+
bne must_malloc_new
: lda newblock ; There is enough space
sta ___heapptr ; Update heapptr
lda newblock+1
sta ___heapptr+1
ldy #usedblock::start+1
lda ublock+1
sta (ublock),y ; Update block start
dey
lda ublock
sta (ublock),y
dey
.assert usedblock::size = usedblock::start-2, error
lda size+1
sta (ublock),y ; Update block size
dey
lda size
sta (ublock),y
lda orgblock ; Return original block
ldx orgblock+1
rts
must_malloc_new: ; The block is not at heap top, or too big
lda size+1
pha ; Backup new size (at this point the only ptr
tax ; we'll need after malloc). tmp* are safe
lda size ; from malloc, memcpy and free.
pha
jsr _malloc
cmp #$00 ; Did malloc succeed?
bne :+
cpx #$00
bne :+
pla ; Pop size backup and return NULL
pla
txa ; X already 0
rts ; No
: sta newblock ; Yes, store newblock
stx newblock+1
jsr pushax ; Push newblock for memcpy
lda orgblock ; Push orgblock for memcpy
ldx orgblock+1
jsr pushax
sec ; Remove admin space from oldsize
lda oldsize
sbc #<HEAP_ADMIN_SPACE
sta oldsize
lda oldsize+1
sbc #>HEAP_ADMIN_SPACE
sta oldsize+1
pla ; Restore new size to AX
tay
pla
tax
tya
cmp oldsize ; Find the smallest size
bcc :+
cpx oldsize+1
bcc :+
lda oldsize
ldx oldsize+1
: jsr _memcpy ; And copy data
lda orgblock ; Free old block
ldx orgblock+1
jsr _free
lda newblock ; Return new block
ldx newblock+1
rts

View File

@ -62,10 +62,6 @@
; File is not open or the character is invalid
error: lda #EINVAL
jsr ___seterrno
lda #$FF ; Return -1
tax
rts
jmp ___directerrno
.endproc

View File

@ -6,7 +6,7 @@
; void BitOtherClip (void *proc1, void* proc2, char skipl, char skipr, int skipy,
; struct iconpic *myGfx);
; both proc1, proc2 should be: char __fastcall something (void);
; both proc1, proc2 should be: char foo (void);
; proc1 is called before reading a byte (.A returns next data)
; proc2 is called before reading each byte which is not pattern (code >219)

View File

@ -68,7 +68,7 @@ MikeyInitData: .byte $9e,$18,$68,$1f,$00,$00,$00,$00,$00,$ff,$1a,$1b,$04,$0d,$2
; Disable the TX/RX IRQ; set to 8E1.
lda #%00011101
lda #PAREN|RESETERR|TXOPEN|PAREVEN ; #%00011101
sta SERCTL
; Clear all pending interrupts.

View File

@ -18,7 +18,7 @@
.import ldeaxysp, decsp2, pushax, incsp8
.import tosandeax,decax1,tosdiveax,axlong,ldaxysp
.import lynxskip0, lynxblock,tosasreax
.import __BLOCKSIZE__
.import __BANK0BLOCKSIZE__
.importzp _FileCurrBlock
.segment "CODE"
@ -32,15 +32,15 @@
jsr ldeaxysp
jsr pusheax
ldx #$00
lda #<(__BLOCKSIZE__/1024 + 9)
lda #<(__BANK0BLOCKSIZE__/1024 + 9)
jsr tosasreax
sta _FileCurrBlock
jsr lynxblock
ldy #$05
jsr ldeaxysp
jsr pusheax
lda #<(__BLOCKSIZE__-1)
ldx #>(__BLOCKSIZE__-1)
lda #<(__BANK0BLOCKSIZE__-1)
ldx #>(__BANK0BLOCKSIZE__-1)
jsr axlong
jsr tosandeax
eor #$FF

View File

@ -17,7 +17,7 @@
.include "extzp.inc"
.export lynxskip0, lynxread0
.export lynxblock
.import __BLOCKSIZE__
.import __BANK0BLOCKSIZE__
.code
@ -88,7 +88,7 @@ lynxblock:
lda __iodat
sta IODAT
stz _FileBlockByte
lda #<($100-(>__BLOCKSIZE__))
lda #<($100-(>__BANK0BLOCKSIZE__))
sta _FileBlockByte+1
ply
plx

View File

@ -73,7 +73,12 @@ SER_UNINSTALL:
; Must return an SER_ERR_xx code in a/x.
SER_CLOSE:
; Disable interrupts
; Disable interrupts and stop timer 4 (serial)
lda #TXOPEN|RESETERR
sta SERCTL
stz TIM4CTLA ; Disable count and no reload
stz SerialStat ; Reset status
; Done, return an error code
lda #SER_ERR_OK
.assert SER_ERR_OK = 0, error
@ -108,17 +113,17 @@ SER_OPEN:
stz TxPtrIn
stz TxPtrOut
; clock = 8 * 15625
lda #%00011000
sta TIM4CTLA
ldy #SER_PARAMS::BAUDRATE
lda (ptr1),y
; Source period is 1 us
ldy #%00011000 ; ENABLE_RELOAD|ENABLE_COUNT|AUD_1
ldx #1
cmp #SER_BAUD_62500
beq setbaudrate
ldx #2
ldx #3
cmp #SER_BAUD_31250
beq setbaudrate
@ -134,6 +139,10 @@ SER_OPEN:
cmp #SER_BAUD_2400
beq setbaudrate
ldx #68
cmp #SER_BAUD_1800
beq setbaudrate
ldx #103
cmp #SER_BAUD_1200
beq setbaudrate
@ -142,65 +151,22 @@ SER_OPEN:
cmp #SER_BAUD_600
beq setbaudrate
; clock = 6 * 15625
ldx #%00011010
stx TIM4CTLA
; Source period is 8 us
ldy #%00011011 ; ENABLE_RELOAD|ENABLE_COUNT|AUD_8
ldx #12
cmp #SER_BAUD_7200
beq setbaudrate
ldx #25
cmp #SER_BAUD_3600
beq setbaudrate
ldx #207
stx TIM4BKUP
; clock = 4 * 15625
ldx #%00011100
ldx #51
cmp #SER_BAUD_300
beq setprescaler
; clock = 6 * 15625
ldx #%00011110
cmp #SER_BAUD_150
beq setprescaler
; clock = 1 * 15625
ldx #%00011111
stx TIM4CTLA
cmp #SER_BAUD_75
beq baudsuccess
ldx #141
cmp #SER_BAUD_110
beq setbaudrate
; clock = 2 * 15625
ldx #%00011010
stx TIM4CTLA
ldx #68
cmp #SER_BAUD_1800
beq setbaudrate
; clock = 6 * 15625
ldx #%00011110
stx TIM4CTLA
ldx #231
cmp #SER_BAUD_134_5
beq setbaudrate
lda #SER_ERR_BAUD_UNAVAIL
ldx #0 ; return value is char
rts
setprescaler:
stx TIM4CTLA
bra baudsuccess
setbaudrate:
sty TIM4CTLA
stx TIM4BKUP
baudsuccess:
ldx #TxOpenColl|ParEven
ldx #TXOPEN|PAREVEN
stx contrl
ldy #SER_PARAMS::DATABITS ; Databits
lda (ptr1),y
@ -218,15 +184,15 @@ baudsuccess:
beq checkhs
cmp #SER_PAR_SPACE
bne @L0
ldx #TxOpenColl
ldx #TXOPEN
stx contrl
bra checkhs
@L0:
ldx #TxParEnable|TxOpenColl|ParEven
ldx #PAREN|TXOPEN|PAREVEN
stx contrl
cmp #SER_PAR_EVEN
beq checkhs
ldx #TxParEnable|TxOpenColl
ldx #PAREN|TXOPEN
stx contrl
checkhs:
ldx contrl
@ -234,15 +200,27 @@ checkhs:
ldy #SER_PARAMS::HANDSHAKE ; Handshake
lda (ptr1),y
cmp #SER_HS_NONE
beq redeye_ok
cmp #SER_HS_SW ; Software handshake will check for connected redeye
bne invparameter
lda IODAT
and #NOEXP ; Check if redeye bit flag is unset
beq redeye_ok
lda #SER_ERR_NO_DEVICE ; ComLynx cable is not inserted
ldx #0
rts
redeye_ok:
lda SERDAT
lda contrl
ora #RxIntEnable|ResetErr
ora #RXINTEN|RESETERR ; Turn on interrupts for receive
sta SERCTL
lda #SER_ERR_OK
.assert SER_ERR_OK = 0, error
tax
rts
invparameter:
lda #SER_ERR_INIT_FAILED
ldx #0 ; return value is char
@ -264,8 +242,8 @@ GetByte:
ldy RxPtrOut
lda RxBuffer,y
inc RxPtrOut
sta (ptr1)
ldx #$00
sta (ptr1,x)
txa ; Return code = 0
rts
@ -279,24 +257,26 @@ SER_PUT:
ina
cmp TxPtrOut
bne PutByte
lda #SER_ERR_OVERFLOW
ldx #0 ; return value is char
rts
PutByte:
ldy TxPtrIn
txa
sta TxBuffer,y
inc TxPtrIn
bit TxDone
bmi @L1
bit TxDone ; Check bit 7 of TxDone (TXINTEN)
bmi @L1 ; Was TXINTEN already set?
php
sei
lda contrl
ora #TxIntEnable|ResetErr
sta SERCTL ; Allow TX-IRQ to hang RX-IRQ
lda contrl ; contrl does not include RXINTEN setting
ora #TXINTEN|RESETERR
sta SERCTL ; Allow TX-IRQ to hang RX-IRQ (no receive while transmitting)
sta TxDone
plp
plp ; Restore processor and interrupt enable
@L1:
lda #SER_ERR_OK
.assert SER_ERR_OK = 0, error
@ -308,9 +288,9 @@ PutByte:
; Must return an SER_ERR_xx code in a/x.
SER_STATUS:
ldy SerialStat
lda SerialStat
sta (ptr1)
ldx #$00
sta (ptr1,x)
txa ; Return code = 0
rts
@ -342,48 +322,56 @@ SER_IRQ:
@L0:
bit TxDone
bmi @tx_irq ; Transmit in progress
ldx SERDAT
lda SERCTL
and #RxParityErr|RxOverrun|RxFrameErr|RxBreak
beq @rx_irq
ldx SERDAT ; Read received data
lda contrl
and #PAREN ; Parity enabled implies SER_PAR_EVEN or SER_PAR_ODD
tay
ora #OVERRUN|FRAMERR|RXBRK
and SERCTL ; Check presence of relevant error flags in SERCTL
beq @rx_irq ; No errors so far
tsb SerialStat ; Save error condition
bit #RxBreak
bit #RXBRK ; Check for break signal
beq @noBreak
stz TxPtrIn ; Break received - drop buffers
stz TxPtrOut
stz RxPtrIn
stz RxPtrOut
@noBreak:
lda contrl
ora #RxIntEnable|ResetErr
sta SERCTL
lda #$10
sta INTRST
bra @IRQexit
bra @exit0
@rx_irq:
tya
bne @2 ; Parity was enabled so no marker bit check needed
lda contrl
ora #RxIntEnable|ResetErr
sta SERCTL
eor SERCTL ; Should match current parity bit
and #PARBIT ; Check for mark or space value
bne @exit0
@2:
txa
ldx RxPtrIn
sta RxBuffer,x
txa
inx
@cont0:
cpx RxPtrOut
beq @1
stx RxPtrIn
lda #SERIAL_INTERRUPT
sta INTRST
bra @IRQexit
@1:
sta RxPtrIn
lda #$80
tsb SerialStat
bra @exit0
@tx_irq:
ldx TxPtrOut ; Has all bytes been sent?
ldx TxPtrOut ; Have all bytes been sent?
cpx TxPtrIn
beq @allSent
@ -393,24 +381,24 @@ SER_IRQ:
@exit1:
lda contrl
ora #TxIntEnable|ResetErr
ora #TXINTEN|RESETERR
sta SERCTL
lda #SERIAL_INTERRUPT
sta INTRST
bra @IRQexit
@allSent:
lda SERCTL ; All bytes sent
bit #TxEmpty
bit #TXEMPTY
beq @exit1
bvs @exit1
stz TxDone
@exit0:
lda contrl
ora #RxIntEnable|ResetErr
ora #RXINTEN|RESETERR ; Re-enable receive interrupt
sta SERCTL
@IRQexit:
lda #SERIAL_INTERRUPT
sta INTRST
@IRQexit:
clc
rts

View File

@ -33,21 +33,21 @@ loop1:
cont1:
jsr read_byte
sta (load_ptr2),y
sta PALETTE ; feedback ;-)
sta PALETTE + 1 ; feedback ;-)
iny
bne loop1
inc load_ptr2+1
bra loop1
read_byte:
bit SERCTL
bit SERCTL ; Check for RXRDY ($40)
bvc read_byte
lda SERDAT
rts
_UpLoaderIRQ:
lda INTSET
and #$10
and #SERIAL_INTERRUPT
bne @L0
clc
rts
@ -69,6 +69,8 @@ again:
; last action : clear interrupt
;
exit:
lda #SERIAL_INTERRUPT
sta INTRST
clc
rts

21
libsrc/runtime/aslax7.s Normal file
View File

@ -0,0 +1,21 @@
;
; Miloslaw Smyk, 2024
;
; CC65 runtime: Scale the primary register by 128, unsigned
;
.export shlax7, aslax7
aslax7:
shlax7: ; XXXXXXXL AAAAAAAl
tay
txa
lsr ; XXXXXXXL -> 0XXXXXXX, L->C
tya
ror ; AAAAAAAl -> LAAAAAAA, l->C
tax
lda #$00 ; LAAAAAAA 00000000
ror ; LAAAAAAA l0000000
rts
; 10 bytes, 16 cycles + rts

18
libsrc/runtime/asrax7.s Normal file
View File

@ -0,0 +1,18 @@
;
; Miloslaw Smyk, 2024
;
; CC65 runtime: Scale the primary register by 128, signed
;
.export asrax7
asrax7: ; HXXXXXXL hAAAAAAl
asl ; AAAAAAA0, h->C
txa
rol ; XXXXXXLh, H->C
ldx #$00 ; 00000000 XXXXXXLh
bcc :+
dex ; 11111111 XXXXXXLh if C
: rts
; 12 cycles max, 9 bytes

View File

@ -8,12 +8,18 @@
.import incsp2
.importzp sp, ptr1
.macpack cpu
.proc popptr1 ; 14 bytes (four usages = at least 2 bytes saved)
ldy #1
lda (sp),y ; get hi byte
sta ptr1+1 ; into ptr hi
dey ; no optimization for 65C02 here to have Y=0 at exit!
dey ; dey even for for 65C02 here to have Y=0 at exit!
.if (.cpu .bitand ::CPU_ISET_65SC02)
lda (sp) ; get lo byte
.else
lda (sp),y ; get lo byte
.endif
sta ptr1 ; to ptr lo
jmp incsp2
.endproc

View File

@ -7,6 +7,8 @@
.export push0, pusha0, pushax
.importzp sp
.macpack cpu
push0: lda #0
pusha0: ldx #0
@ -29,7 +31,11 @@ pusha0: ldx #0
sta (sp),y ; (27)
pla ; (31)
dey ; (33)
.if (.cpu .bitand ::CPU_ISET_65SC02)
sta (sp) ; (37)
.else
sta (sp),y ; (38)
rts ; (44)
.endif
rts ; (44/43)
.endproc

14
libsrc/runtime/pushptr1.s Normal file
View File

@ -0,0 +1,14 @@
;
; Colin Leroy-Mira, 2024
;
; CC65 runtime: Push ptr1 to stack.
; A/X destroyed (set to ptr1)
.export pushptr1
.import pushax
.importzp ptr1
pushptr1:
lda ptr1
ldx ptr1+1
jmp pushax

View File

@ -0,0 +1,15 @@
;
; Ullrich von Bassewitz, 25.10.2000
;
; CC65 runtime: Return -1 in a/x
;
.export returnFFFF
.proc returnFFFF
lda #$FF
tax
rts
.endproc

18
libsrc/runtime/shrax7.s Normal file
View File

@ -0,0 +1,18 @@
;
; Miloslaw Smyk, 2024
;
; CC65 runtime: Scale the primary register by 128, unsigned
;
.export shrax7
shrax7: ; HXXXXXXL hAAAAAAl
asl ; AAAAAAA0, h->C
txa
rol ; XXXXXXLh, H->C
ldx #$00 ; 00000000 XXXXXXLh
bcc :+
inx ; 0000000H XXXXXXLh if C
: rts
; 12 cycles max, 9 bytes

View File

@ -9,11 +9,21 @@
.import __MAIN_START__
.import startup
.macpack cpu
.segment "EXEHDR"
.byte $73, $69, $6D, $36, $35 ; 'sim65'
.byte 2 ; header version
.byte .defined(__SIM65C02__) ; CPU type
.if (.cpu .bitand ::CPU_ISET_6502X)
.byte 2
.elseif (.cpu .bitand ::CPU_ISET_65C02)
.byte 1
.elseif (.cpu .bitand ::CPU_ISET_6502)
.byte 0
.else
.error Unknow CPU type.
.endif
.byte sp ; sp address
.addr __MAIN_START__ ; load address
.addr startup ; reset address

View File

@ -8,10 +8,15 @@
;
.export exit, args, _open, _close, _read, _write
.export __sysremove, ___osmaperrno
__sysremove := $FFF2
___osmaperrno := $FFF3
_open := $FFF4
_close := $FFF5
_read := $FFF6
_write := $FFF7
args := $FFF8
exit := $FFF9
; $FFFA-FFFF are hardware vectors, extend before not after!

View File

@ -31,9 +31,12 @@ else
LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65)
endif
EXELIST_kim1 = \
kimHello.bin \
kimSieve.bin
EXELIST_kim1 = \
kimHello.bin \
kimSieve.bin \
kimLife.bin \
kimTest.bin \
kimGFX.bin
ifneq ($(EXELIST_$(SYS)),)
samples: $(EXELIST_$(SYS))
@ -50,13 +53,65 @@ else
@echo > $(NULLDEV)
endif
subs.o: subs.asm
$(AS) subs.asm -o subs.o
ramfont.o: ramfont.asm
$(AS) ramfont.asm -o ramfont.o
kimLife.bin: kimLife.c
$(CL) -t kim1 -C kim1-60k.cfg -Oi -o kimLife.bin kimLife.c
kimTest.bin: kimTest.c
$(CL) -t kim1 -C kim1-60k.cfg -Oi -o kimTest.bin kimTest.c
kimGFX.bin: kimGFX.c subs.o ramfont.o
$(CL) -t kim1 --listing kimGFX.lst -C kim1-mtuE000.cfg -o kimGFX.bin kimGFX.c subs.o ramfont.o -Ln kimgfx.lbl
kimSieve.bin: kimSieve.c
$(CL) -t kim1 -C kim1-60k.cfg -O -o kimSieve.bin kimSieve.c
kimHello.bin: kimHello.c
$(CL) -t kim1 -O -o kimHello.bin kimHello.c
# To build an intel-format file for the CORSHAM SD card reader
kimLife.hex: kimLife.bin
srec_cat kimLife.bin -binary -offset 0x2000 -o kimLife.hex -Intel -address-length=2
kimTest.hex: kimTest.bin
srec_cat kimTest.bin -binary -offset 0x2000 -o kimTest.hex -Intel -address-length=2
kimGFX.hex: kimGFX.bin ramfont.o
srec_cat kimGFX.bin -binary -offset 0x2000 -o kimGFX.hex -Intel -address-length=2
# To build a paper tape file for uploading to the KIM-1 via terminal
kimLife.ptp: kimLife.bin
srec_cat kimLife.bin -binary -offset 0x2000 -o kimLife.ptp -MOS_Technologies
kimGFX.ptp: kimGFX.bin
srec_cat kimGFX.bin -binary -offset 0x2000 -o kimGFX.ptp -MOS_Technologies
kimTest.ptp: kimTest.bin
srec_cat kimTest.bin -binary -offset 0x2000 -o kimTest.ptp -MOS_Technologies
clean:
@$(DEL) kimSieve.bin 2>$(NULLDEV)
@$(DEL) kimHello.bin 2>$(NULLDEV)
@$(DEL) kimLife.bin 2>$(NULLDEV)
@$(DEL) kimLife.ptp 2>$(NULLDEV)
@$(DEL) kimLife.hex 2>$(NULLDEV)
@$(DEL) kimTest.bin 2>$(NULLDEV)
@$(DEL) kimTest.ptp 2>$(NULLDEV)
@$(DEL) kimTest.hex 2>$(NULLDEV)
@$(DEL) kimGFX.bin 2>$(NULLDEV)
@$(DEL) kimGFX.ptp 2>$(NULLDEV)
@$(DEL) kimGFX.hex 2>$(NULLDEV)
@$(DEL) kimgfx.lbl 2>$(NULLDEV)
@$(DEL) kimGFX.lst 2>$(NULLDEV)
@$(DEL) subs.o 2>$(NULLDEV)
@$(DEL) ramfont.o 2>$(NULLDEV)

0
samples/kim1/font.rom Normal file
View File

290
samples/kim1/kimGFX.c Normal file
View File

@ -0,0 +1,290 @@
// --------------------------------------------------------------------------
// Simple Graphics Test for KIM-1 with MTU Visible Memory Board
//
// Assumes the MTU Visible Memory Board mapped at 0xA000 for 8K of video RAM
//
// davepl@davepl.com
// --------------------------------------------------------------------------
#include <stdio.h> // For printf
#include <stdlib.h> // For rand, srand
#include <string.h> // For memcpy
#include <ctype.h>
typedef unsigned char byte;
extern void ClearScreen(void); // In subs.asm
extern void ScrollScreen(void);
extern void DrawCircle(void);
extern void SetPixel(void);
extern void ClearPixel(void);
extern void DrawChar(void);
extern void Demo(void);
extern void __fastcall__ Delay(byte loops);
extern void __fastcall__ DrawLine(byte bSet);
extern byte __fastcall__ AscToPet(byte in);
extern byte __fastcall__ PetToAsc(byte in);
extern byte __fastcall__ ReverseBits(byte in);
extern void __fastcall__ CharOut(byte asci_char);
extern byte __fastcall__ getch();
extern unsigned char font8x8_basic[256][8];
extern int x1cord;
extern int y1cord;
extern int x2cord;
extern int y2cord;
extern int cursorX;
extern int cursorY;
// If in zeropage:
//
// #pragma zpsym("x1cord")
// #pragma zpsym("x2cord")
// #pragma zpsym("y1cord")
// #pragma zpsym("y2cord")
// Screen memory is placed at A000-BFFF, 320x200 pixels, mapped right to left within each horizontal byte
byte * screen = (byte *) 0xA000;
// Cursor position
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 200
#define CHARWIDTH 8
#define CHARHEIGHT 8
#define BYTESPERROW (SCREEN_WIDTH / 8)
#define BYTESPERCHARROW (BYTESPERROW * 8)
#define CHARSPERROW (SCREEN_WIDTH / CHARWIDTH)
#define ROWSPERCOLUMN (SCREEN_HEIGHT / CHARHEIGHT)
// SETPIXEL
//
// 0 <= x < 320
// 0 <= y < 200
//
// Draws a pixel on the screen in white or black at pixel pos x, y
void SETPIXEL(int x, int y, byte b)
{
x1cord = x;
y1cord = y;
if (b)
SetPixel();
else
ClearPixel();
}
// DRAWPIXEL
//
// 0 <= x < 320
// 0 <= y < 200
//
// Turns on a screen pixel at pixel pos x,y
void DRAWPIXEL(int x, int y)
{
x1cord = x;
y1cord = y;
SetPixel();
}
int c;
void DrawText(char * psz)
{
while (*psz)
{
while (cursorX >= CHARSPERROW)
{
cursorX -= CHARSPERROW;
cursorY += 1;
}
// If we've gone off the bottom of the screen, we scroll the screen and back up to the last line again
if (cursorY >= ROWSPERCOLUMN)
{
cursorY = ROWSPERCOLUMN - 1;
ScrollScreen();
}
// If we output a newline we advanced the cursor down one line and reset it to the left
if (*psz == 0x0A)
{
cursorX = 0;
cursorY++;
psz++;
}
else
{
c = *psz;
__asm__ ("ldx %v", cursorX);
__asm__ ("ldy %v", cursorY);
__asm__ ("lda %v", c);
DrawChar();
cursorX++;
psz++;
}
}
}
void DrawTextAt(int x, int y, char * psz)
{
cursorX = x;
cursorY = y;
DrawText(psz);
}
// Something like Bresenham's algorithm for drawing a line
/*
void DrawLine(int x0, int y0, int x1, int y1, byte val)
{
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
int err = (dx > dy ? dx : -dy) / 2, e2;
while (1)
{
SETPIXEL(x0, y0, val);
if (x0 == x1 && y0 == y1)
break;
e2 = err;
if (e2 > -dx)
{
err -= dy;
x0 += sx;
}
if (e2 < dy)
{
err += dx;
y0 += sy;
}
}
}
*/
// DrawCircle
//
// Draw a circle without sin, cos, or floating point!
void DrawCircleC(int x0, int y0, int radius, byte)
{
x1cord = x0;
y1cord = y0;
y2cord = radius;
DrawCircle();
}
void DrawLineC(int x1, int y1, int x2, int y2, byte bSet)
{
x1cord = x1;
y1cord = y1;
x2cord = x2;
y2cord = y2;
DrawLine(bSet);
}
// MirrorFont
//
// RAM font is backwards left-right relative to the way memory is laid out on the KIM-1, so we swap all the
// bytes in place by reversing the order of the bits in every byte
void MirrorFont()
{
int c;
byte * pb = (byte *) font8x8_basic;
for (c = 0; c < 128 * 8; c++)
pb[c] = ReverseBits(pb[c]);
}
// DrawScreenMoire
//
// Draws a moire pattern on the screen without clearing it first
void DrawMoire(int left, int top, int right, int bottom, byte pixel)
{
int x, y;
for (x = left; x < right; x += 6)
DrawLineC(x, top, right - x + left, bottom, pixel);
for (y = top; y < bottom; y += 6)
DrawLineC(left, y, right, bottom - y + top, pixel);
}
void DrawScreenMoire(int left, int top, int right, int bottom)
{
int x, y;
DrawLineC(left, top, right, top, 1);
DrawLineC(left, bottom, right, bottom, 1);
DrawLineC(left, top, left, bottom, 1);
DrawLineC(right, top, right, bottom, 1);
left++; top++; right--; bottom--;
for (x = left; x < right; x += 6)
DrawLineC(x, top, right - x + left, bottom, 1);
for (y = top; y < bottom; y += 6)
DrawLineC(left, y, right, bottom - y + top, 1);
for (x = left; x < right; x += 6)
DrawLineC(x, top, right - x + left, bottom, 0);
for (y = top; y < bottom; y += 6)
DrawLineC(left, y, right, bottom - y + top, 0);
}
int main (void)
{
int i;
int c = 0;
Demo();
CharOut('R');
CharOut('E');
CharOut('A');
CharOut('D');
CharOut('Y');
CharOut('.');
CharOut('\n');
while(1)
{
c = toupper(getch());
if (c != EOF)
CharOut(c);
}
// Clear the screen memory
while(1)
{
Demo();
DrawScreenMoire(0,30, 319, 199);
Delay(10);
Demo();
for (i = 5; i < 80; i+=5)
{
DrawCircleC(SCREEN_WIDTH/2, SCREEN_HEIGHT/2 + 20, i, 1);
DrawCircleC(SCREEN_WIDTH/4, SCREEN_HEIGHT/2 + 20, i, 1);
DrawCircleC(SCREEN_WIDTH*3/4, SCREEN_HEIGHT/2 + 20, i, 1);
}
Delay(10);
}
printf("Done, exiting...\r\n");
return 0;
}

144
samples/kim1/kimLife.c Normal file
View File

@ -0,0 +1,144 @@
// --------------------------------------------------------------------------
// Conway's Game of Life for KIM-1
//
// Assumes the MTU Visible Memory Board mapped at 0x8000 for 8K of video RAM
//
// Dave Plummer on a rainy Thursday
//
// davepl@davepl.com
// --------------------------------------------------------------------------
#include <stdio.h> // For printf
#include <stdlib.h> // For rand, srand
#include <string.h> // For memcpy
typedef unsigned char byte;
// World size
#define WIDTH 320
#define HEIGHT 200
#define NUMBITS 64000
#define NUMBYTES 8000
#define DENSITY 50
// Screen memory is placed at 8000, our world copy at A000, and they use the same layout so
// that we can memcpy from one to the other without translating
byte * world = (byte *) 0x8000;
byte * new_world = (byte *) 0xA000;
// BITARRAY
//
// Access individual bits in a block of memory
// Access to the screen bitmap
byte GETBIT(byte *p, int n)
{
return (p[n >> 3] & (1 << (n & 7))) ? 1 : 0;
}
void SETBIT(byte *p, int n)
{
p[n >> 3] |= (1 << (n & 7));
}
void CLRBIT(byte *p, int n)
{
p[n >> 3] &= ~(1 << (n & 7));
}
void SETPIXEL(byte * p, int x, int y, byte b)
{
if (b)
SETBIT(p, y * WIDTH + x);
else
CLRBIT(p, y * WIDTH + x);
}
byte GETPIXEL(byte *p, int x, int y)
{
return GETBIT(p, y * WIDTH + x);
}
// RandomFillWorld
//
// Populates the initial world with random cells
void RandomFillWorld()
{
int x, y;
// I need a better way to see the RNG or it'll be the same game every time!
srand(0);
for (x = 0; x < WIDTH; x++)
{
for (y = 0; y < HEIGHT; y++)
{
byte b = ((rand() % 100) < DENSITY) ? 1 : 0;
SETPIXEL(world, x, y, b);
}
}
}
// CountNeighbors
//
// Count the number of live cells around the given spot, excluding the actual spot specified
int CountNeighbors(int x, int y)
{
int i, j, nx, ny, count = 0;
for (j = -1; j <= 1; j++)
{
for (i = -1; i <= 1; i++)
{
if (i != 0 || j != 0)
{
nx = (x + i + WIDTH) % WIDTH;
ny = (y + j + HEIGHT) % HEIGHT;
count += GETPIXEL(world, nx, ny) ? 1 : 0;
}
}
}
return count;
}
// UpdateWorld
//
// Applies the rules of Conway's Game of Life to the cells
void UpdateWorld()
{
int x, y;
for (y = 0; y < HEIGHT; y++)
{
for (x = 0; x < WIDTH; x++)
{
int neighbors = CountNeighbors(x, y);
if (GETPIXEL(world, x, y))
SETPIXEL(new_world, x, y, (neighbors == 2 || neighbors == 3));
else
SETPIXEL(new_world, x, y, (neighbors == 3));
}
}
}
int main (void)
{
printf("\r\nStarting Conway's Game of Life: Randomizing World...\r\n");
RandomFillWorld();
printf("World Ready, Running!\r\n");
for (;;)
{
UpdateWorld();
printf("[");
memcpy(world, new_world, NUMBYTES);
printf("]");
}
return 0;
}

262
samples/kim1/kimTest.c Normal file
View File

@ -0,0 +1,262 @@
// --------------------------------------------------------------------------
// Diagnostics Test for KIM-1
//
// Dave Plummer
// davepl@davepl.com
//
// Memory test examples by Michael Barr
//
// --------------------------------------------------------------------------
#include <stdio.h> // For printf
#include <stdlib.h> // For rand, srand
#include <string.h> // For memcpy
typedef unsigned char byte;
// RepeatChar
//
// Outputs a given character N times
void RepeatChar(char c, size_t count)
{
while (count--)
putc(c, stdout);
}
/**********************************************************************
*
* Function: memTestDataBus()
*
* Description: Test the data bus wiring in a memory region by
* performing a walking 1's test at a fixed address
* within that region. The address (and hence the
* memory region) is selected by the caller.
*
* Returns: 0 if the test succeeds.
* A non-zero result is the first pattern that failed.
*
**********************************************************************/
byte memTestDataBus(volatile byte * address)
{
byte pattern;
// Perform a walking 1's test at the given address.
for (pattern = 1; pattern != 0; pattern <<= 1)
{
// Write the test pattern.
*address = pattern;
// Read it back and check it
if (*address != pattern)
{
printf("\r\nmemTestDataBus: FAILED at %04x with pattern %02x\r\n", address, pattern);
return (pattern);
}
}
return (0);
}
/**********************************************************************
*
* Function: memTestAddressBus()
*
* Description: Test the address bus wiring in a memory region by
* performing a walking 1's test on the relevant bits
* of the address and checking for aliasing. This test
* will find single-bit address failures such as stuck
* -high, stuck-low, and shorted pins. The base address
* and size of the region are selected by the caller.
*
* Notes: For best results, the selected base address should
* have enough LSB 0's to guarantee single address bit
* changes. For example, to test a 64-Kbyte region,
* select a base address on a 64-Kbyte boundary. Also,
* select the region size as a power-of-two--if at all
* possible.
*
* Returns: NULL if the test succeeds.
* A non-zero result is the first address at which an
* aliasing problem was uncovered. By examining the
* contents of memory, it may be possible to gather
* additional information about the problem.
*
**********************************************************************/
byte * memTestAddressBus(volatile byte * baseAddress, unsigned long nBytes)
{
unsigned long addressMask = (nBytes/sizeof(byte) - 1);
unsigned long offset;
unsigned long testOffset;
byte pattern = (byte) 0xAAAAAAAA;
byte antipattern = (byte) 0x55555555;
//Write the default pattern at each of the power-of-two offsets.
for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
{
baseAddress[offset] = pattern;
}
// Check for address bits stuck high.
testOffset = 0;
baseAddress[testOffset] = antipattern;
for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
{
if (baseAddress[offset] != pattern)
{
printf("\r\nmemTestAddressBus: FAILED at %04x with pattern %02x\r\n", baseAddress+offset, pattern);
return ((byte *) &baseAddress[offset]);
}
if (offset % 1024 == 0)
printf(".");
}
baseAddress[testOffset] = pattern;
// Check for address bits stuck low or shorted.
for (testOffset = 1; (testOffset & addressMask) != 0; testOffset <<= 1)
{
baseAddress[testOffset] = antipattern;
if (baseAddress[0] != pattern)
{
return ((byte *) &baseAddress[testOffset]);
}
for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
{
if ((baseAddress[offset] != pattern) && (offset != testOffset))
{
printf("\r\nmemTestAddressBus: FAILED at %04x with pattern %02x\r\n", baseAddress+offset, pattern);
return ((byte *) &baseAddress[testOffset]);
}
}
baseAddress[testOffset] = pattern;
}
return (NULL);
}
/**********************************************************************
*
* Function: memTestDevice()
*
* Description: Test the integrity of a physical memory device by
* performing an increment/decrement test over the
* entire region. In the process every storage bit
* in the device is tested as a zero and a one. The
* base address and the size of the region are
* selected by the caller.
*
* Returns: NULL if the test succeeds.
*
* A non-zero result is the first address at which an
* incorrect value was read back. By examining the
* contents of memory, it may be possible to gather
* additional information about the problem.
*
**********************************************************************/
byte * memTestDevice(volatile byte * baseAddress, unsigned long nBytes)
{
unsigned long offset;
unsigned long nWords = nBytes / sizeof(byte);
byte pattern;
byte antipattern;
// Fill memory with a known pattern.
for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
baseAddress[offset] = pattern;
// Check each location and invert it for the second pass.
for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
{
if (offset % 1024 == 0)
printf("%04X ", (int) &baseAddress[offset]);
if (baseAddress[offset] != pattern)
{
printf("\r\nmemTestDevice: FAILED at %04x with pattern %02x\r\n", (int) &baseAddress[offset], pattern);
return ((byte *) &baseAddress[offset]);
}
antipattern = ~pattern;
baseAddress[offset] = antipattern;
}
// Check each location for the inverted pattern and zero it.
for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
{
if (offset % 1024 == 0)
printf("%04X ", (int) &baseAddress[offset]);
antipattern = ~pattern;
if (baseAddress[offset] != antipattern)
{
printf("\r\nmemTestDevice: FAILED at %04x with antipattern %02x\r\n", (int) &baseAddress[offset], pattern);
return ((byte *) &baseAddress[offset]);
}
}
return (NULL);
}
// TestMemory
//
// Run all three memory tests
byte TestMemory(byte * startAddress, unsigned long size)
{
if ((memTestDataBus(startAddress) != 0) ||
(memTestAddressBus(startAddress, size) != NULL) ||
(memTestDevice(startAddress, size) != NULL))
{
return (-1);
}
else
{
return (0);
}
}
int main (void)
{
printf("\r\nTesting KIM-1...\r\n");
RepeatChar('-', 39);
printf("\r\nTesting RIOT RAM: 1780-17BF\r\n");
if (TestMemory((byte *)0x1780, 0x17BF - 0x1780))
return 0;
printf("\r\nTesting RIOT RAM: 17C0-17E6\r\n");
if (TestMemory((byte *)0x17C0, 0x17E6 - 0x17C0))
return 0;
printf("\r\nTesting Memory: 0400-13FF\r\n");
if (TestMemory((byte *)0x0400, 0x13FF - 0x0400))
return 0;
printf("\r\nTesting Memory: 4000-DFFF\r\n");
if (TestMemory((byte *)0x4000, 0xDFFF - 0x4000))
return 0;
printf("\r\nPASS!\r\n");
return 1;
}

272
samples/kim1/ramfont.asm Normal file
View File

@ -0,0 +1,272 @@
;-----------------------------------------------------------------------------------
; KIMGFX: Simple pixel graphics for the MOS/Commodore KIM-1
;-----------------------------------------------------------------------------------
; (c) Plummer's Software Ltd, 04/25/2023 Created
; David Plummer
;-----------------------------------------------------------------------------------
;
; File: ramfont.s
; Magnetic OCR (check number style) Font data
;
;-----------------------------------------------------------------------------------
.segment "CODE"
.export _font8x8_basic
_font8x8_basic:
.byte $1c, $22, $4a, $56, $4c, $20, $1e, $00 ; PETSCII code 0
.byte $3c, $24, $24, $7e, $62, $62, $62, $00 ; PETSCII code 1
.byte $78, $44, $44, $7c, $62, $62, $7e, $00 ; PETSCII code 2
.byte $7e, $42, $40, $60, $60, $62, $7e, $00 ; PETSCII code 3
.byte $7c, $46, $42, $62, $62, $66, $7c, $00 ; PETSCII code 4
.byte $7e, $40, $40, $7c, $60, $60, $7e, $00 ; PETSCII code 5
.byte $7e, $40, $40, $7e, $60, $60, $60, $00 ; PETSCII code 6
.byte $7e, $42, $40, $6e, $62, $62, $7e, $00 ; PETSCII code 7
.byte $42, $42, $42, $7e, $62, $62, $62, $00 ; PETSCII code 8
.byte $08, $08, $08, $0c, $0c, $0c, $0c, $00 ; PETSCII code 9
.byte $04, $04, $04, $06, $06, $46, $7e, $00 ; PETSCII code 10
.byte $42, $44, $48, $7c, $62, $62, $62, $00 ; PETSCII code 11
.byte $40, $40, $40, $60, $60, $60, $7e, $00 ; PETSCII code 12
.byte $7e, $4a, $4a, $6a, $6a, $6a, $6a, $00 ; PETSCII code 13
.byte $7e, $42, $42, $62, $62, $62, $62, $00 ; PETSCII code 14
.byte $7e, $46, $42, $42, $42, $42, $7e, $00 ; PETSCII code 15
.byte $7e, $42, $42, $7e, $60, $60, $60, $00 ; PETSCII code 16
.byte $7e, $42, $42, $42, $4a, $4e, $7e, $00 ; PETSCII code 17
.byte $7c, $44, $44, $7c, $62, $62, $62, $00 ; PETSCII code 18
.byte $7e, $42, $40, $7e, $06, $46, $7e, $00 ; PETSCII code 19
.byte $3e, $10, $10, $18, $18, $18, $18, $00 ; PETSCII code 20
.byte $42, $42, $42, $62, $62, $62, $7e, $00 ; PETSCII code 21
.byte $62, $62, $62, $66, $24, $24, $3c, $00 ; PETSCII code 22
.byte $4a, $4a, $4a, $6a, $6a, $6a, $7e, $00 ; PETSCII code 23
.byte $42, $42, $66, $18, $66, $62, $62, $00 ; PETSCII code 24
.byte $22, $22, $22, $3e, $18, $18, $18, $00 ; PETSCII code 25
.byte $7e, $42, $06, $18, $60, $62, $7e, $00 ; PETSCII code 26
.byte $3c, $20, $20, $20, $20, $20, $3c, $00 ; PETSCII code 27
.byte $00, $40, $20, $10, $08, $04, $02, $00 ; PETSCII code 28
.byte $3c, $04, $04, $04, $04, $04, $3c, $00 ; PETSCII code 29
.byte $00, $08, $1c, $2a, $08, $08, $14, $14 ; PETSCII code 30
.byte $00, $00, $10, $20, $7f, $20, $10, $00 ; PETSCII code 31
.byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 32
.byte $08, $08, $08, $0c, $0c, $00, $0c, $00 ; PETSCII code 33
.byte $6c, $24, $6c, $00, $00, $00, $00, $00 ; PETSCII code 34
.byte $24, $24, $7e, $24, $7e, $24, $24, $00 ; PETSCII code 35
.byte $08, $3e, $20, $3e, $06, $3e, $08, $00 ; PETSCII code 36
.byte $00, $62, $64, $08, $10, $26, $46, $00 ; PETSCII code 37
.byte $3c, $20, $24, $7e, $64, $64, $7c, $00 ; PETSCII code 38
.byte $1c, $18, $10, $00, $00, $00, $00, $00 ; PETSCII code 39
.byte $04, $08, $10, $10, $10, $08, $04, $00 ; PETSCII code 40
.byte $20, $10, $08, $08, $08, $10, $20, $00 ; PETSCII code 41
.byte $08, $2a, $1c, $3e, $1c, $2a, $08, $00 ; PETSCII code 42
.byte $00, $08, $08, $3e, $08, $08, $00, $00 ; PETSCII code 43
.byte $00, $00, $00, $00, $00, $18, $18, $08 ; PETSCII code 44
.byte $00, $00, $00, $7e, $00, $00, $00, $00 ; PETSCII code 45
.byte $00, $00, $00, $00, $00, $18, $18, $00 ; PETSCII code 46
.byte $00, $02, $04, $08, $10, $20, $40, $00 ; PETSCII code 47
.byte $7e, $62, $52, $4a, $46, $46, $7e, $00 ; PETSCII code 48
.byte $18, $08, $08, $18, $18, $1a, $3e, $00 ; PETSCII code 49
.byte $7e, $42, $02, $7e, $60, $60, $7e, $00 ; PETSCII code 50
.byte $7c, $44, $04, $1e, $06, $46, $7e, $00 ; PETSCII code 51
.byte $44, $44, $44, $44, $7e, $0c, $0c, $00 ; PETSCII code 52
.byte $7e, $40, $7e, $06, $06, $46, $7e, $00 ; PETSCII code 53
.byte $7e, $42, $40, $7e, $46, $46, $7e, $00 ; PETSCII code 54
.byte $7e, $02, $02, $06, $06, $06, $06, $00 ; PETSCII code 55
.byte $3c, $24, $24, $7e, $46, $46, $7e, $00 ; PETSCII code 56
.byte $7e, $42, $42, $7e, $06, $06, $06, $00 ; PETSCII code 57
.byte $00, $00, $18, $00, $00, $18, $00, $00 ; PETSCII code 58
.byte $00, $00, $18, $00, $00, $18, $18, $08 ; PETSCII code 59
.byte $0e, $18, $30, $60, $30, $18, $0e, $00 ; PETSCII code 60
.byte $00, $00, $7e, $00, $7e, $00, $00, $00 ; PETSCII code 61
.byte $70, $18, $0c, $06, $0c, $18, $70, $00 ; PETSCII code 62
.byte $7e, $02, $02, $7e, $60, $00, $60, $00 ; PETSCII code 63
.byte $00, $00, $00, $00, $ff, $00, $00, $00 ; PETSCII code 64
.byte $08, $1c, $3e, $7f, $7f, $1c, $3e, $00 ; PETSCII code 65
.byte $10, $10, $10, $10, $10, $10, $10, $10 ; PETSCII code 66
.byte $00, $00, $00, $ff, $00, $00, $00, $00 ; PETSCII code 67
.byte $00, $00, $ff, $00, $00, $00, $00, $00 ; PETSCII code 68
.byte $00, $ff, $00, $00, $00, $00, $00, $00 ; PETSCII code 69
.byte $00, $00, $00, $00, $00, $ff, $00, $00 ; PETSCII code 70
.byte $20, $20, $20, $20, $20, $20, $20, $20 ; PETSCII code 71
.byte $04, $04, $04, $04, $04, $04, $04, $04 ; PETSCII code 72
.byte $00, $00, $00, $00, $e0, $10, $08, $08 ; PETSCII code 73
.byte $08, $08, $08, $04, $03, $00, $00, $00 ; PETSCII code 74
.byte $08, $08, $08, $10, $e0, $00, $00, $00 ; PETSCII code 75
.byte $80, $80, $80, $80, $80, $80, $80, $ff ; PETSCII code 76
.byte $80, $40, $20, $10, $08, $04, $02, $01 ; PETSCII code 77
.byte $01, $02, $04, $08, $10, $20, $40, $80 ; PETSCII code 78
.byte $ff, $80, $80, $80, $80, $80, $80, $80 ; PETSCII code 79
.byte $ff, $01, $01, $01, $01, $01, $01, $01 ; PETSCII code 80
.byte $00, $3c, $7e, $7e, $7e, $7e, $3c, $00 ; PETSCII code 81
.byte $00, $00, $00, $00, $00, $00, $ff, $00 ; PETSCII code 82
.byte $36, $7f, $7f, $7f, $3e, $1c, $08, $00 ; PETSCII code 83
.byte $40, $40, $40, $40, $40, $40, $40, $40 ; PETSCII code 84
.byte $00, $00, $00, $00, $03, $04, $08, $08 ; PETSCII code 85
.byte $81, $42, $24, $18, $18, $24, $42, $81 ; PETSCII code 86
.byte $00, $3c, $42, $42, $42, $42, $3c, $00 ; PETSCII code 87
.byte $08, $1c, $2a, $77, $2a, $08, $08, $00 ; PETSCII code 88
.byte $02, $02, $02, $02, $02, $02, $02, $02 ; PETSCII code 89
.byte $08, $1c, $3e, $7f, $3e, $1c, $08, $00 ; PETSCII code 90
.byte $08, $08, $08, $08, $ff, $08, $08, $08 ; PETSCII code 91
.byte $a0, $50, $a0, $50, $a0, $50, $a0, $50 ; PETSCII code 92
.byte $08, $08, $08, $08, $08, $08, $08, $08 ; PETSCII code 93
.byte $00, $00, $01, $3e, $54, $14, $14, $00 ; PETSCII code 94
.byte $ff, $7f, $3f, $1f, $0f, $07, $03, $01 ; PETSCII code 95
.byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 96
.byte $f0, $f0, $f0, $f0, $f0, $f0, $f0, $f0 ; PETSCII code 97
.byte $00, $00, $00, $00, $ff, $ff, $ff, $ff ; PETSCII code 98
.byte $ff, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 99
.byte $00, $00, $00, $00, $00, $00, $00, $ff ; PETSCII code 100
.byte $80, $80, $80, $80, $80, $80, $80, $80 ; PETSCII code 101
.byte $aa, $55, $aa, $55, $aa, $55, $aa, $55 ; PETSCII code 102
.byte $01, $01, $01, $01, $01, $01, $01, $01 ; PETSCII code 103
.byte $00, $00, $00, $00, $aa, $55, $aa, $55 ; PETSCII code 104
.byte $ff, $fe, $fc, $f8, $f0, $e0, $c0, $80 ; PETSCII code 105
.byte $03, $03, $03, $03, $03, $03, $03, $03 ; PETSCII code 106
.byte $08, $08, $08, $08, $0f, $08, $08, $08 ; PETSCII code 107
.byte $00, $00, $00, $00, $0f, $0f, $0f, $0f ; PETSCII code 108
.byte $08, $08, $08, $08, $0f, $00, $00, $00 ; PETSCII code 109
.byte $00, $00, $00, $00, $f8, $08, $08, $08 ; PETSCII code 110
.byte $00, $00, $00, $00, $00, $00, $ff, $ff ; PETSCII code 111
.byte $00, $00, $00, $00, $0f, $08, $08, $08 ; PETSCII code 112
.byte $08, $08, $08, $08, $ff, $00, $00, $00 ; PETSCII code 113
.byte $00, $00, $00, $00, $ff, $08, $08, $08 ; PETSCII code 114
.byte $08, $08, $08, $08, $f8, $08, $08, $08 ; PETSCII code 115
.byte $c0, $c0, $c0, $c0, $c0, $c0, $c0, $c0 ; PETSCII code 116
.byte $e0, $e0, $e0, $e0, $e0, $e0, $e0, $e0 ; PETSCII code 117
.byte $07, $07, $07, $07, $07, $07, $07, $07 ; PETSCII code 118
.byte $ff, $ff, $00, $00, $00, $00, $00, $00 ; PETSCII code 119
.byte $ff, $ff, $ff, $00, $00, $00, $00, $00 ; PETSCII code 120
.byte $00, $00, $00, $00, $00, $ff, $ff, $ff ; PETSCII code 121
.byte $01, $01, $01, $01, $01, $01, $01, $ff ; PETSCII code 122
.byte $00, $00, $00, $00, $f0, $f0, $f0, $f0 ; PETSCII code 123
.byte $0f, $0f, $0f, $0f, $00, $00, $00, $00 ; PETSCII code 124
.byte $08, $08, $08, $08, $f8, $00, $00, $00 ; PETSCII code 125
.byte $f0, $f0, $f0, $f0, $00, $00, $00, $00 ; PETSCII code 126
.byte $f0, $f0, $f0, $f0, $0f, $0f, $0f, $0f ; PETSCII code 127
.byte $1c, $22, $4a, $56, $4c, $20, $1e, $00 ; PETSCII code 128
.byte $00, $00, $3c, $04, $7c, $64, $7c, $00 ; PETSCII code 129
.byte $40, $40, $7e, $42, $62, $62, $7e, $00 ; PETSCII code 130
.byte $00, $00, $7e, $42, $60, $62, $7e, $00 ; PETSCII code 131
.byte $02, $02, $7e, $42, $62, $62, $7e, $00 ; PETSCII code 132
.byte $00, $00, $7e, $42, $7e, $60, $7e, $00 ; PETSCII code 133
.byte $1e, $12, $10, $7c, $18, $18, $18, $00 ; PETSCII code 134
.byte $00, $00, $7e, $42, $62, $7e, $02, $7e ; PETSCII code 135
.byte $40, $40, $7e, $42, $62, $62, $62, $00 ; PETSCII code 136
.byte $18, $00, $10, $10, $18, $18, $18, $00 ; PETSCII code 137
.byte $0c, $00, $08, $0c, $0c, $0c, $44, $7c ; PETSCII code 138
.byte $40, $40, $44, $48, $78, $64, $64, $00 ; PETSCII code 139
.byte $10, $10, $10, $10, $18, $18, $18, $00 ; PETSCII code 140
.byte $00, $00, $7f, $49, $6d, $6d, $6d, $00 ; PETSCII code 141
.byte $00, $00, $7e, $42, $62, $62, $62, $00 ; PETSCII code 142
.byte $00, $00, $7e, $42, $62, $62, $7e, $00 ; PETSCII code 143
.byte $00, $00, $7e, $42, $62, $7e, $40, $40 ; PETSCII code 144
.byte $00, $00, $7e, $42, $46, $7e, $02, $02 ; PETSCII code 145
.byte $00, $00, $7e, $40, $60, $60, $60, $00 ; PETSCII code 146
.byte $00, $00, $7e, $40, $7e, $06, $7e, $00 ; PETSCII code 147
.byte $10, $10, $7c, $10, $18, $18, $18, $00 ; PETSCII code 148
.byte $00, $00, $42, $42, $62, $62, $7e, $00 ; PETSCII code 149
.byte $00, $00, $62, $62, $66, $24, $3c, $00 ; PETSCII code 150
.byte $00, $00, $49, $49, $6d, $6d, $7f, $00 ; PETSCII code 151
.byte $00, $00, $42, $42, $3c, $62, $62, $00 ; PETSCII code 152
.byte $00, $00, $62, $62, $42, $7e, $02, $7e ; PETSCII code 153
.byte $00, $00, $7e, $06, $18, $60, $7e, $00 ; PETSCII code 154
.byte $3c, $20, $20, $20, $20, $20, $3c, $00 ; PETSCII code 155
.byte $00, $40, $20, $10, $08, $04, $02, $00 ; PETSCII code 156
.byte $3c, $04, $04, $04, $04, $04, $3c, $00 ; PETSCII code 157
.byte $00, $08, $1c, $2a, $08, $08, $14, $14 ; PETSCII code 158
.byte $00, $00, $10, $20, $7f, $20, $10, $00 ; PETSCII code 159
.byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 160
.byte $08, $08, $08, $0c, $0c, $00, $0c, $00 ; PETSCII code 161
.byte $6c, $24, $6c, $00, $00, $00, $00, $00 ; PETSCII code 162
.byte $24, $24, $7e, $24, $7e, $24, $24, $00 ; PETSCII code 163
.byte $08, $3e, $20, $3e, $06, $3e, $08, $00 ; PETSCII code 164
.byte $00, $62, $64, $08, $10, $26, $46, $00 ; PETSCII code 165
.byte $3c, $20, $24, $7e, $64, $64, $7c, $00 ; PETSCII code 166
.byte $1c, $18, $10, $00, $00, $00, $00, $00 ; PETSCII code 167
.byte $04, $08, $10, $10, $10, $08, $04, $00 ; PETSCII code 168
.byte $20, $10, $08, $08, $08, $10, $20, $00 ; PETSCII code 169
.byte $08, $2a, $1c, $3e, $1c, $2a, $08, $00 ; PETSCII code 170
.byte $00, $08, $08, $3e, $08, $08, $00, $00 ; PETSCII code 171
.byte $00, $00, $00, $00, $00, $18, $18, $08 ; PETSCII code 172
.byte $00, $00, $00, $7e, $00, $00, $00, $00 ; PETSCII code 173
.byte $00, $00, $00, $00, $00, $18, $18, $00 ; PETSCII code 174
.byte $00, $02, $04, $08, $10, $20, $40, $00 ; PETSCII code 175
.byte $7e, $62, $52, $4a, $46, $46, $7e, $00 ; PETSCII code 176
.byte $38, $08, $08, $18, $18, $1a, $3e, $00 ; PETSCII code 177
.byte $7e, $42, $02, $7e, $60, $60, $7e, $00 ; PETSCII code 178
.byte $7c, $44, $04, $1e, $06, $46, $7e, $00 ; PETSCII code 179
.byte $44, $44, $44, $44, $7e, $0c, $0c, $00 ; PETSCII code 180
.byte $7e, $40, $7e, $06, $06, $46, $7e, $00 ; PETSCII code 181
.byte $7e, $42, $40, $7e, $46, $46, $7e, $00 ; PETSCII code 182
.byte $7e, $02, $02, $06, $06, $06, $06, $00 ; PETSCII code 183
.byte $3c, $24, $24, $7e, $46, $46, $7e, $00 ; PETSCII code 184
.byte $7e, $42, $42, $7e, $06, $06, $06, $00 ; PETSCII code 185
.byte $00, $00, $18, $00, $00, $18, $00, $00 ; PETSCII code 186
.byte $00, $00, $18, $00, $00, $18, $18, $08 ; PETSCII code 187
.byte $0e, $18, $30, $60, $30, $18, $0e, $00 ; PETSCII code 188
.byte $00, $00, $7e, $00, $7e, $00, $00, $00 ; PETSCII code 189
.byte $70, $18, $0c, $06, $0c, $18, $70, $00 ; PETSCII code 190
.byte $7e, $02, $02, $7e, $60, $00, $60, $00 ; PETSCII code 191
.byte $00, $00, $00, $00, $ff, $00, $00, $00 ; PETSCII code 192
.byte $3c, $24, $24, $7e, $62, $62, $62, $00 ; PETSCII code 193
.byte $78, $44, $44, $7c, $62, $62, $7e, $00 ; PETSCII code 194
.byte $7e, $42, $40, $60, $60, $62, $7e, $00 ; PETSCII code 195
.byte $7c, $46, $42, $62, $62, $66, $7c, $00 ; PETSCII code 196
.byte $7e, $40, $40, $78, $60, $60, $7e, $00 ; PETSCII code 197
.byte $7e, $40, $40, $7e, $60, $60, $60, $00 ; PETSCII code 198
.byte $7e, $42, $40, $6e, $62, $62, $7e, $00 ; PETSCII code 199
.byte $42, $42, $42, $7e, $62, $62, $62, $00 ; PETSCII code 200
.byte $08, $08, $08, $0c, $0c, $0c, $0c, $00 ; PETSCII code 201
.byte $04, $04, $04, $06, $06, $46, $7e, $00 ; PETSCII code 202
.byte $42, $44, $48, $7c, $62, $62, $62, $00 ; PETSCII code 203
.byte $40, $40, $40, $60, $60, $60, $7e, $00 ; PETSCII code 204
.byte $7e, $4a, $4a, $6a, $6a, $6a, $6a, $00 ; PETSCII code 205
.byte $7e, $42, $42, $62, $62, $62, $62, $00 ; PETSCII code 206
.byte $7e, $46, $42, $42, $42, $42, $7e, $00 ; PETSCII code 207
.byte $7e, $42, $42, $7e, $60, $60, $60, $00 ; PETSCII code 208
.byte $7e, $42, $42, $42, $4a, $4e, $7e, $00 ; PETSCII code 209
.byte $7c, $44, $44, $7c, $62, $62, $62, $00 ; PETSCII code 210
.byte $7e, $42, $40, $7e, $06, $46, $7e, $00 ; PETSCII code 211
.byte $3e, $10, $10, $18, $18, $18, $18, $00 ; PETSCII code 212
.byte $42, $42, $42, $62, $62, $62, $7e, $00 ; PETSCII code 213
.byte $62, $62, $62, $66, $24, $24, $3c, $00 ; PETSCII code 214
.byte $4a, $4a, $4a, $6a, $6a, $6a, $7e, $00 ; PETSCII code 215
.byte $42, $42, $66, $3c, $66, $62, $62, $00 ; PETSCII code 216
.byte $22, $22, $22, $3e, $18, $18, $18, $00 ; PETSCII code 217
.byte $7e, $42, $06, $18, $60, $62, $7e, $00 ; PETSCII code 218
.byte $08, $08, $08, $08, $ff, $08, $08, $08 ; PETSCII code 219
.byte $a0, $50, $a0, $50, $a0, $50, $a0, $50 ; PETSCII code 220
.byte $08, $08, $08, $08, $08, $08, $08, $08 ; PETSCII code 221
.byte $cc, $cc, $33, $33, $cc, $cc, $33, $33 ; PETSCII code 222
.byte $cc, $66, $33, $99, $cc, $66, $33, $99 ; PETSCII code 223
.byte $00, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 224
.byte $f0, $f0, $f0, $f0, $f0, $f0, $f0, $f0 ; PETSCII code 225
.byte $00, $00, $00, $00, $ff, $ff, $ff, $ff ; PETSCII code 226
.byte $ff, $00, $00, $00, $00, $00, $00, $00 ; PETSCII code 227
.byte $00, $00, $00, $00, $00, $00, $00, $ff ; PETSCII code 228
.byte $80, $80, $80, $80, $80, $80, $80, $80 ; PETSCII code 229
.byte $aa, $55, $aa, $55, $aa, $55, $aa, $55 ; PETSCII code 230
.byte $01, $01, $01, $01, $01, $01, $01, $01 ; PETSCII code 231
.byte $00, $00, $00, $00, $aa, $55, $aa, $55 ; PETSCII code 232
.byte $99, $33, $66, $cc, $99, $33, $66, $cc ; PETSCII code 233
.byte $03, $03, $03, $03, $03, $03, $03, $03 ; PETSCII code 234
.byte $08, $08, $08, $08, $0f, $08, $08, $08 ; PETSCII code 235
.byte $00, $00, $00, $00, $0f, $0f, $0f, $0f ; PETSCII code 236
.byte $08, $08, $08, $08, $0f, $00, $00, $00 ; PETSCII code 237
.byte $00, $00, $00, $00, $f8, $08, $08, $08 ; PETSCII code 238
.byte $00, $00, $00, $00, $00, $00, $ff, $ff ; PETSCII code 239
.byte $00, $00, $00, $00, $0f, $08, $08, $08 ; PETSCII code 240
.byte $08, $08, $08, $08, $ff, $00, $00, $00 ; PETSCII code 241
.byte $00, $00, $00, $00, $ff, $08, $08, $08 ; PETSCII code 242
.byte $08, $08, $08, $08, $f8, $08, $08, $08 ; PETSCII code 243
.byte $c0, $c0, $c0, $c0, $c0, $c0, $c0, $c0 ; PETSCII code 244
.byte $e0, $e0, $e0, $e0, $e0, $e0, $e0, $e0 ; PETSCII code 245
.byte $07, $07, $07, $07, $07, $07, $07, $07 ; PETSCII code 246
.byte $ff, $ff, $00, $00, $00, $00, $00, $00 ; PETSCII code 247
.byte $ff, $ff, $ff, $00, $00, $00, $00, $00 ; PETSCII code 248
.byte $00, $00, $00, $00, $00, $ff, $ff, $ff ; PETSCII code 249
.byte $01, $02, $44, $48, $50, $60, $40, $00 ; PETSCII code 250
.byte $00, $00, $00, $00, $f0, $f0, $f0, $f0 ; PETSCII code 251
.byte $0f, $0f, $0f, $0f, $00, $00, $00, $00 ; PETSCII code 252
.byte $08, $08, $08, $08, $f8, $00, $00, $00 ; PETSCII code 253
.byte $f0, $f0, $f0, $f0, $00, $00, $00, $00 ; PETSCII code 254
.byte $f0, $f0, $f0, $f0, $0f, $0f, $0f, $0f ; PETSCII code 255

1140
samples/kim1/subs.asm Normal file

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,8 @@
/*****************************************************************************/
#include "addrsize.h"
#include "scopedefs.h"
/* ca65 */
#include "error.h"
@ -42,6 +44,7 @@
#include "symbol.h"
#include "symtab.h"
#include "condasm.h"
#include "pseudo.h"
@ -193,7 +196,7 @@ static void FreeIf (void)
int Done;
do {
IfDesc* ID = GetCurrentIf();
if (ID == 0) {
if (ID == 0 || strcmp(ID->Name, ".WEAKPROC") == 0) {
Error (" Unexpected .ENDIF");
Done = 1;
} else {
@ -205,6 +208,23 @@ static void FreeIf (void)
} while (!Done);
}
static void CheckNoWeak (void)
{
int Done = 0;
do {
IfDesc* ID = GetCurrentIf();
if (ID == 0) {
Done = 1;
} else {
if (strcmp(ID->Name, ".WEAKPROC") == 0) {
Error (".WEAKPROC nesting not allowed");
Done = 1;
}
}
} while (!Done);
}
/*****************************************************************************/
@ -282,6 +302,27 @@ void DoConditionals (void)
CalcOverallIfCond ();
break;
case TOK_ENDWEAK:
D = GetCurrentIf();
if (D == 0 || strcmp(D->Name, ".WEAKPROC") != 0) {
Error ("Unexpected .ENDWEAK");
} else {
ReleaseFullLineInfo (&D->LineInfos);
DoneCollection (&D->LineInfos);
--IfCount;
}
if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label == 0) {
} else {
SymLeaveLevel ();
}
NextTok ();
ExpectSep ();
/* Get the new overall condition */
CalcOverallIfCond ();
break;
case TOK_IF:
D = AllocIf (".IF", 1);
NextTok ();
@ -447,6 +488,23 @@ void DoConditionals (void)
CalcOverallIfCond ();
break;
case TOK_WEAKPROC:
CheckNoWeak();
D = AllocIf (".WEAKPROC", 1);
NextTok ();
if (IfCond) {
SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
SetIfCond (D, Sym != 0 && SymIsRef (Sym));
if (Sym != 0) {
SymDef (Sym, GenCurrentPC (), ADDR_SIZE_DEFAULT, SF_LABEL);
SymEnterLevel (&CurTok.SVal, SCOPE_SCOPE, ADDR_SIZE_DEFAULT, Sym);
}
ExpectSep ();
}
CalcOverallIfCond ();
break;
default:
/* Skip tokens */
NextTok ();
@ -468,6 +526,7 @@ int CheckConditionals (void)
case TOK_ELSE:
case TOK_ELSEIF:
case TOK_ENDIF:
case TOK_ENDWEAK:
case TOK_IF:
case TOK_IFBLANK:
case TOK_IFCONST:

View File

@ -2103,6 +2103,7 @@ static CtrlDesc CtrlCmdTab [] = {
{ ccNone, DoEndScope },
{ ccNone, DoUnexpected }, /* .ENDSTRUCT */
{ ccNone, DoUnexpected }, /* .ENDUNION */
{ ccKeepToken, DoConditionals }, /* .ENDWEAK */
{ ccNone, DoEnum },
{ ccNone, DoError },
{ ccNone, DoExitMacro },
@ -2203,6 +2204,7 @@ static CtrlDesc CtrlCmdTab [] = {
{ ccNone, DoUnion },
{ ccNone, DoUnexpected }, /* .VERSION */
{ ccNone, DoWarning },
{ ccKeepToken, DoConditionals }, /* .WEAKPROC */
{ ccNone, DoWord },
{ ccNone, DoUnexpected }, /* .XMATCH */
{ ccNone, DoZeropage },

View File

@ -187,6 +187,7 @@ struct DotKeyword {
{ ".ENDSCOPE", TOK_ENDSCOPE },
{ ".ENDSTRUCT", TOK_ENDSTRUCT },
{ ".ENDUNION", TOK_ENDUNION },
{ ".ENDWEAK", TOK_ENDWEAK },
{ ".ENUM", TOK_ENUM },
{ ".ERROR", TOK_ERROR },
{ ".EXITMAC", TOK_EXITMACRO },
@ -300,6 +301,7 @@ struct DotKeyword {
{ ".UNION", TOK_UNION },
{ ".VERSION", TOK_VERSION },
{ ".WARNING", TOK_WARNING },
{ ".WEAKPROC", TOK_WEAKPROC },
{ ".WORD", TOK_WORD },
{ ".XMATCH", TOK_XMATCH },
{ ".XOR", TOK_BOOLXOR },

View File

@ -165,6 +165,7 @@ typedef enum token_t {
TOK_ENDSCOPE,
TOK_ENDSTRUCT,
TOK_ENDUNION,
TOK_ENDWEAK,
TOK_ENUM,
TOK_ERROR,
TOK_EXITMACRO,
@ -265,6 +266,7 @@ typedef enum token_t {
TOK_UNION,
TOK_VERSION,
TOK_WARNING,
TOK_WEAKPROC,
TOK_WORD,
TOK_XMATCH,
TOK_ZEROPAGE,

View File

@ -3260,6 +3260,14 @@ void g_asr (unsigned flags, unsigned long val)
}
val -= 8;
}
if (val == 7) {
if (flags & CF_UNSIGNED) {
AddCodeLine ("jsr shrax7");
} else {
AddCodeLine ("jsr asrax7");
}
val = 0;
}
if (val >= 4) {
if (flags & CF_UNSIGNED) {
AddCodeLine ("jsr shrax4");
@ -3402,6 +3410,14 @@ void g_asl (unsigned flags, unsigned long val)
AddCodeLine ("lda #$00");
val -= 8;
}
if (val == 7) {
if (flags & CF_UNSIGNED) {
AddCodeLine ("jsr shlax7");
} else {
AddCodeLine ("jsr aslax7");
}
val = 0;
}
if (val >= 4) {
if (flags & CF_UNSIGNED) {
AddCodeLine ("jsr shlax4");

View File

@ -99,6 +99,7 @@ static const FuncInfo FuncInfoTable[] = {
{ "aslax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "aslax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "aslax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "aslax7", REG_AX, PSTATE_ALL | REG_AXY },
{ "aslaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 },
{ "asleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "asleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
@ -108,6 +109,7 @@ static const FuncInfo FuncInfoTable[] = {
{ "asrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "asrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "asrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "asrax7", REG_AX, PSTATE_ALL | REG_AX },
{ "asraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 },
{ "asreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "asreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
@ -245,6 +247,7 @@ static const FuncInfo FuncInfoTable[] = {
{ "shlax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "shlax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "shlax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "shlax7", REG_AX, PSTATE_ALL | REG_AXY },
{ "shlaxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 },
{ "shleax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "shleax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
@ -254,6 +257,7 @@ static const FuncInfo FuncInfoTable[] = {
{ "shrax2", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "shrax3", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "shrax4", REG_AX, PSTATE_ALL | REG_AX | REG_TMP1 },
{ "shrax7", REG_AX, PSTATE_ALL | REG_AX },
{ "shraxy", REG_AXY, PSTATE_ALL | REG_AXY | REG_TMP1 },
{ "shreax1", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },
{ "shreax2", REG_EAX, PSTATE_ALL | REG_EAX | REG_TMP1 },

View File

@ -1471,7 +1471,7 @@ void CS_Output (CodeSeg* S)
/* Add line debug info */
if (DebugInfo) {
WriteOutput ("\t.dbg\tline, \"%s\", %u\n",
GetInputName (LI), GetInputLine (LI));
GetActualFileName (LI), GetActualLineNum (LI));
}
}
/* Output the code */

View File

@ -121,7 +121,7 @@ static void Parse (void)
}
/* Read the declaration specifier */
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_NONE);
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT | TS_FUNCTION_SPEC, SC_NONE);
/* Don't accept illegal storage classes */
if ((Spec.StorageClass & SC_STORAGEMASK) == SC_AUTO ||
@ -163,19 +163,19 @@ static void Parse (void)
break;
}
/* Check if we must reserve storage for the variable. We do this,
**
** - if it is not a typedef or function,
** - if we don't had a storage class given ("int i")
** - if the storage class is explicitly specified as static,
** - or if there is an initialization.
**
** This means that "extern int i;" will not get storage allocated
** in this translation unit.
*/
/* The symbol is now visible in the file scope */
if ((Decl.StorageClass & SC_TYPEMASK) != SC_FUNC &&
(Decl.StorageClass & SC_TYPEMASK) != SC_TYPEDEF) {
/* The variable is visible in the file scope */
/* Check if we must reserve storage for the variable. We do this,
**
** - if it is not a typedef or function,
** - if we don't had a storage class given ("int i")
** - if the storage class is explicitly specified as static,
** - or if there is an initialization.
**
** This means that "extern int i;" will not get storage allocated
** in this translation unit.
*/
if ((Decl.StorageClass & SC_STORAGEMASK) == SC_NONE ||
(Decl.StorageClass & SC_STORAGEMASK) == SC_STATIC ||
((Decl.StorageClass & SC_STORAGEMASK) == SC_EXTERN &&
@ -189,7 +189,6 @@ static void Parse (void)
** or semicolon, it must be followed by a function body.
*/
if ((Decl.StorageClass & SC_TYPEMASK) == SC_FUNC) {
/* The function is now visible in the file scope */
if (CurTok.Tok == TOK_LCURLY) {
/* A definition */
Decl.StorageClass |= SC_DEF;
@ -560,6 +559,10 @@ void Compile (const char* FileName)
if ((Entry->Flags & SC_STORAGEMASK) == SC_STATIC && SymIsRef (Entry)) {
Warning ("Static function '%s' used but never defined",
Entry->Name);
} else if ((Entry->Flags & SC_INLINE) != 0) {
Warning ("Inline function '%s' %s but never defined",
Entry->Name,
SymIsRef (Entry) ? "used" : "declared");
}
}
}

View File

@ -1488,7 +1488,7 @@ static const OptFuncDesc FuncTable[] = {
};
static const OptFuncDesc FuncRegATable[] = {
{ "tosandax", Opt_a_tosand, REG_NONE, OP_NONE },
{ "tosandax", Opt_a_tosand, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT },
{ "toseqax", Opt_a_toseq, REG_NONE, OP_NONE },
{ "tosgeax", Opt_a_tosuge, REG_NONE, OP_NONE },
{ "tosgtax", Opt_a_tosugt, REG_NONE, OP_NONE },
@ -1496,13 +1496,13 @@ static const OptFuncDesc FuncRegATable[] = {
{ "tosleax", Opt_a_tosule, REG_NONE, OP_NONE },
{ "tosltax", Opt_a_tosult, REG_NONE, OP_NONE },
{ "tosneax", Opt_a_tosne, REG_NONE, OP_NONE },
{ "tosorax", Opt_a_tosor, REG_NONE, OP_NONE },
{ "tosorax", Opt_a_tosor, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT },
{ "tossubax", Opt_a_tossub, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT },
{ "tosugeax", Opt_a_tosuge, REG_NONE, OP_NONE },
{ "tosugtax", Opt_a_tosugt, REG_NONE, OP_NONE },
{ "tosuleax", Opt_a_tosule, REG_NONE, OP_NONE },
{ "tosultax", Opt_a_tosult, REG_NONE, OP_NONE },
{ "tosxorax", Opt_a_tosxor, REG_NONE, OP_NONE },
{ "tosxorax", Opt_a_tosxor, REG_NONE, OP_RHS_REMOVE_DIRECT | OP_RHS_LOAD_DIRECT },
};
#define FUNC_COUNT(Table) (sizeof(Table) / sizeof(Table[0]))

View File

@ -989,6 +989,24 @@ int IsIncompleteESUType (const Type* T)
int IsAnonESUType (const Type* T)
/* Return true if this is an anonymous ESU type */
{
SymEntry* TagSym = GetESUTagSym (T);
return TagSym != 0 && SymHasAnonName (TagSym);
}
int IsAnonStructClass (const Type* T)
/* Return true if this is an anonymous struct or union type */
{
return IsClassStruct (T) && IsAnonESUType (T);
}
int IsPassByRefType (const Type* T)
/* Return true if this is a large struct/union type that doesn't fit in the
** primary. This returns false for the void value extension type since it is

View File

@ -792,6 +792,12 @@ int IsESUType (const Type* T);
int IsIncompleteESUType (const Type* T);
/* Return true if this is an incomplete ESU type */
int IsAnonESUType (const Type* T);
/* Return true if this is an anonymous ESU type */
int IsAnonStructClass (const Type* T);
/* Return true if this is an anonymous struct or union type */
int IsPassByRefType (const Type* T);
/* Return true if this is a large struct/union type that doesn't fit in the
** primary. This returns false for the void value extension type since it is

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More